aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Rutger Broekhoff2021-05-25 17:41:21 +0200
committerLibravatar Rutger Broekhoff2021-05-25 17:41:21 +0200
commitd8597fe575644ddf5f1f8ef8cf901a446bc7acda (patch)
treef7f16966a74e60be6334ea10f1a277a434136de4 /src
parent9d03b59a409596d12c3ec7b49066e4eaf0f463e7 (diff)
downloadzig-nkeys-d8597fe575644ddf5f1f8ef8cf901a446bc7acda.tar.gz
zig-nkeys-d8597fe575644ddf5f1f8ef8cf901a446bc7acda.zip
Clean up janky prefix byte API
Diffstat (limited to 'src')
-rw-r--r--src/nkeys.zig229
-rw-r--r--src/znk.zig36
2 files changed, 134 insertions, 131 deletions
diff --git a/src/nkeys.zig b/src/nkeys.zig
index b7fd520..15605ca 100644
--- a/src/nkeys.zig
+++ b/src/nkeys.zig
@@ -19,67 +19,72 @@ pub const SeedDecodeError = DecodeError || InvalidSeedError || crypto.errors.Ide
19pub const PrivateKeyDecodeError = DecodeError || InvalidPrivateKeyError || crypto.errors.IdentityElementError; 19pub const PrivateKeyDecodeError = DecodeError || InvalidPrivateKeyError || crypto.errors.IdentityElementError;
20pub const SignError = crypto.errors.IdentityElementError || crypto.errors.WeakPublicKeyError || crypto.errors.KeyMismatchError; 20pub const SignError = crypto.errors.IdentityElementError || crypto.errors.WeakPublicKeyError || crypto.errors.KeyMismatchError;
21 21
22pub const KeyTypePrefixByte = enum(u8) { 22pub const prefix_byte_account = 0; // A
23 const Self = @This(); 23pub const prefix_byte_cluster = 2 << 3; // C
24 24pub const prefix_byte_operator = 14 << 3; // O
25 seed = 18 << 3, // S 25pub const prefix_byte_private = 15 << 3; // P
26 private = 15 << 3, // P 26pub const prefix_byte_seed = 18 << 3; // S
27 27pub const prefix_byte_server = 13 << 3; // N
28 pub fn char(self: Self) u8 { 28pub const prefix_byte_user = 20 << 3; // U
29 return switch (self) { 29
30 .seed => 'S', 30pub fn prefixByteLetter(prefix_byte: u8) ?u8 {
31 .private => 'P', 31 return switch (prefix_byte) {
32 }; 32 prefix_byte_account => 'A',
33 } 33 prefix_byte_cluster => 'C',
34 prefix_byte_operator => 'O',
35 prefix_byte_private => 'P',
36 prefix_byte_seed => 'S',
37 prefix_byte_server => 'N',
38 prefix_byte_user => 'U',
39 else => null,
40 };
41}
34 42
35 pub fn fromChar(c: u8) InvalidPrefixByteError!Self { 43pub fn prefixByteFromLetter(letter: u8) ?u8 {
36 return switch (c) { 44 return switch (letter) {
37 'S' => .seed, 45 'A' => prefix_byte_account,
38 'P' => .private, 46 'C' => prefix_byte_cluster,
39 else => error.InvalidPrefixByte, 47 'O' => prefix_byte_operator,
40 }; 48 'P' => prefix_byte_private,
41 } 49 'S' => prefix_byte_seed,
42}; 50 'N' => prefix_byte_server,
51 'U' => prefix_byte_user,
52 else => null,
53 };
54}
43 55
44pub const PublicPrefixByte = enum(u8) { 56pub const Role = enum(u8) {
45 const Self = @This(); 57 const Self = @This();
46 58
47 account = 0, // A 59 account,
48 cluster = 2 << 3, // C 60 cluster,
49 operator = 14 << 3, // O 61 operator,
50 server = 13 << 3, // N 62 server,
51 user = 20 << 3, // U 63 user,
52 64
53 pub fn fromU8(b: u8) InvalidPrefixByteError!PublicPrefixByte { 65 pub fn fromPublicPrefixByte(b: u8) ?Self {
54 return switch (b) { 66 return switch (b) {
55 @enumToInt(PublicPrefixByte.server) => .server, 67 prefix_byte_account => .account,
56 @enumToInt(PublicPrefixByte.cluster) => .cluster, 68 prefix_byte_cluster => .cluster,
57 @enumToInt(PublicPrefixByte.operator) => .operator, 69 prefix_byte_operator => .operator,
58 @enumToInt(PublicPrefixByte.account) => .account, 70 prefix_byte_server => .server,
59 @enumToInt(PublicPrefixByte.user) => .user, 71 prefix_byte_user => .user,
60 else => error.InvalidPrefixByte, 72 else => null,
61 }; 73 };
62 } 74 }
63 75
64 pub fn char(self: Self) u8 { 76 pub fn publicPrefixByte(self: Self) u8 {
65 return switch (self) { 77 return switch (self) {
66 .account => 'A', 78 .account => prefix_byte_account,
67 .cluster => 'C', 79 .cluster => prefix_byte_cluster,
68 .operator => 'O', 80 .operator => prefix_byte_operator,
69 .server => 'N', 81 .server => prefix_byte_server,
70 .user => 'U', 82 .user => prefix_byte_user,
71 }; 83 };
72 } 84 }
73 85
74 pub fn fromChar(c: u8) InvalidPrefixByteError!Self { 86 pub fn letter(self: Self) u8 {
75 return switch (c) { 87 return prefixByteLetter(self.publicPrefixByte()) orelse unreachable;
76 'A' => .account,
77 'C' => .cluster,
78 'O' => .operator,
79 'N' => .server,
80 'U' => .user,
81 else => error.InvalidPrefixByte,
82 };
83 } 88 }
84}; 89};
85 90
@@ -101,14 +106,14 @@ pub const text_seed = [text_seed_len]u8;
101pub const SeedKeyPair = struct { 106pub const SeedKeyPair = struct {
102 const Self = @This(); 107 const Self = @This();
103 108
104 prefix: PublicPrefixByte, 109 role: Role,
105 kp: Ed25519.KeyPair, 110 kp: Ed25519.KeyPair,
106 111
107 pub fn generate(prefix: PublicPrefixByte) crypto.errors.IdentityElementError!Self { 112 pub fn generate(role: Role) crypto.errors.IdentityElementError!Self {
108 var raw_seed: [Ed25519.seed_length]u8 = undefined; 113 var raw_seed: [Ed25519.seed_length]u8 = undefined;
109 crypto.random.bytes(&raw_seed); 114 crypto.random.bytes(&raw_seed);
110 defer wipeBytes(&raw_seed); 115 defer wipeBytes(&raw_seed);
111 return Self{ .prefix = prefix, .kp = try Ed25519.KeyPair.create(raw_seed) }; 116 return Self{ .role = role, .kp = try Ed25519.KeyPair.create(raw_seed) };
112 } 117 }
113 118
114 pub fn fromTextSeed(text: *const text_seed) SeedDecodeError!Self { 119 pub fn fromTextSeed(text: *const text_seed) SeedDecodeError!Self {
@@ -116,22 +121,22 @@ pub const SeedKeyPair = struct {
116 defer decoded.wipe(); // gets copied 121 defer decoded.wipe(); // gets copied
117 122
118 var key_ty_prefix = decoded.prefix[0] & 0b11111000; 123 var key_ty_prefix = decoded.prefix[0] & 0b11111000;
119 var entity_ty_prefix = (decoded.prefix[0] << 5) | (decoded.prefix[1] >> 3); 124 var role_prefix = (decoded.prefix[0] << 5) | (decoded.prefix[1] >> 3);
120 125
121 if (key_ty_prefix != @enumToInt(KeyTypePrefixByte.seed)) 126 if (key_ty_prefix != prefix_byte_seed)
122 return error.InvalidSeed; 127 return error.InvalidSeed;
123 128
124 return Self{ 129 return Self{
125 .prefix = try PublicPrefixByte.fromU8(entity_ty_prefix), 130 .role = Role.fromPublicPrefixByte(role_prefix) orelse return error.InvalidPrefixByte,
126 .kp = try Ed25519.KeyPair.create(decoded.data), 131 .kp = try Ed25519.KeyPair.create(decoded.data),
127 }; 132 };
128 } 133 }
129 134
130 pub fn fromRawSeed( 135 pub fn fromRawSeed(
131 prefix: PublicPrefixByte, 136 role: Role,
132 raw_seed: *const [Ed25519.seed_length]u8, 137 raw_seed: *const [Ed25519.seed_length]u8,
133 ) crypto.errors.IdentityElementError!Self { 138 ) crypto.errors.IdentityElementError!Self {
134 return Self{ .prefix = prefix, .kp = try Ed25519.KeyPair.create(raw_seed.*) }; 139 return Self{ .role = role, .kp = try Ed25519.KeyPair.create(raw_seed.*) };
135 } 140 }
136 141
137 pub fn sign( 142 pub fn sign(
@@ -150,25 +155,26 @@ pub const SeedKeyPair = struct {
150 } 155 }
151 156
152 pub fn seedText(self: *const Self) text_seed { 157 pub fn seedText(self: *const Self) text_seed {
158 const public_prefix = self.role.publicPrefixByte();
153 const full_prefix = &[_]u8{ 159 const full_prefix = &[_]u8{
154 @enumToInt(KeyTypePrefixByte.seed) | (@enumToInt(self.prefix) >> 5), 160 prefix_byte_seed | (public_prefix >> 5),
155 (@enumToInt(self.prefix) & 0b00011111) << 3, 161 (public_prefix & 0b00011111) << 3,
156 }; 162 };
157 const seed = self.kp.secret_key[0..Ed25519.seed_length]; 163 const seed = self.kp.secret_key[0..Ed25519.seed_length];
158 return encode(full_prefix.len, seed.len, full_prefix, seed); 164 return encode(full_prefix.len, seed.len, full_prefix, seed);
159 } 165 }
160 166
161 pub fn privateKeyText(self: *const Self) text_private { 167 pub fn privateKeyText(self: *const Self) text_private {
162 return encode(1, self.kp.secret_key.len, &.{@enumToInt(KeyTypePrefixByte.private)}, &self.kp.secret_key); 168 return encode(1, self.kp.secret_key.len, &.{prefix_byte_private}, &self.kp.secret_key);
163 } 169 }
164 170
165 pub fn publicKeyText(self: *const Self) text_public { 171 pub fn publicKeyText(self: *const Self) text_public {
166 return encode(1, self.kp.public_key.len, &.{@enumToInt(self.prefix)}, &self.kp.public_key); 172 return encode(1, self.kp.public_key.len, &.{self.role.publicPrefixByte()}, &self.kp.public_key);
167 } 173 }
168 174
169 pub fn intoPublicKey(self: *const Self) PublicKey { 175 pub fn intoPublicKey(self: *const Self) PublicKey {
170 return PublicKey{ 176 return PublicKey{
171 .prefix = self.prefix, 177 .role = self.role,
172 .key = self.kp.public_key, 178 .key = self.kp.public_key,
173 }; 179 };
174 } 180 }
@@ -178,7 +184,7 @@ pub const SeedKeyPair = struct {
178 } 184 }
179 185
180 pub fn wipe(self: *Self) void { 186 pub fn wipe(self: *Self) void {
181 self.prefix = .account; 187 self.role = .account;
182 wipeKeyPair(&self.kp); 188 wipeKeyPair(&self.kp);
183 } 189 }
184}; 190};
@@ -186,27 +192,27 @@ pub const SeedKeyPair = struct {
186pub const PublicKey = struct { 192pub const PublicKey = struct {
187 const Self = @This(); 193 const Self = @This();
188 194
189 prefix: PublicPrefixByte, 195 role: Role,
190 key: [Ed25519.public_length]u8, 196 key: [Ed25519.public_length]u8,
191 197
192 pub fn fromTextPublicKey(text: *const text_public) DecodeError!Self { 198 pub fn fromTextPublicKey(text: *const text_public) DecodeError!Self {
193 var decoded = try decode(1, Ed25519.public_length, text); 199 var decoded = try decode(1, Ed25519.public_length, text);
194 defer decoded.wipe(); // gets copied 200 defer decoded.wipe(); // gets copied
195 return PublicKey{ 201 return PublicKey{
196 .prefix = try PublicPrefixByte.fromU8(decoded.prefix[0]), 202 .role = Role.fromPublicPrefixByte(decoded.prefix[0]) orelse return error.InvalidPrefixByte,
197 .key = decoded.data, 203 .key = decoded.data,
198 }; 204 };
199 } 205 }
200 206
201 pub fn fromRawPublicKey( 207 pub fn fromRawPublicKey(
202 prefix: PublicPrefixByte, 208 role: Role,
203 raw_key: *const [Ed25519.public_length]u8, 209 raw_key: *const [Ed25519.public_length]u8,
204 ) Self { 210 ) Self {
205 return Self{ .prefix = prefix, .key = raw_key.* }; 211 return Self{ .role = role, .key = raw_key.* };
206 } 212 }
207 213
208 pub fn publicKeyText(self: *const Self) text_public { 214 pub fn publicKeyText(self: *const Self) text_public {
209 return encode(1, self.key.len, &.{@enumToInt(self.prefix)}, &self.key); 215 return encode(1, self.key.len, &.{self.role.publicPrefixByte()}, &self.key);
210 } 216 }
211 217
212 pub fn verify( 218 pub fn verify(
@@ -218,7 +224,7 @@ pub const PublicKey = struct {
218 } 224 }
219 225
220 pub fn wipe(self: *Self) void { 226 pub fn wipe(self: *Self) void {
221 self.prefix = .account; 227 self.role = .account;
222 wipeBytes(&self.key); 228 wipeBytes(&self.key);
223 } 229 }
224}; 230};
@@ -231,7 +237,7 @@ pub const PrivateKey = struct {
231 pub fn fromTextPrivateKey(text: *const text_private) PrivateKeyDecodeError!Self { 237 pub fn fromTextPrivateKey(text: *const text_private) PrivateKeyDecodeError!Self {
232 var decoded = try decode(1, Ed25519.secret_length, text); 238 var decoded = try decode(1, Ed25519.secret_length, text);
233 defer decoded.wipe(); // gets copied 239 defer decoded.wipe(); // gets copied
234 if (decoded.prefix[0] != @enumToInt(KeyTypePrefixByte.private)) 240 if (decoded.prefix[0] != prefix_byte_private)
235 return error.InvalidPrivateKey; 241 return error.InvalidPrivateKey;
236 return PrivateKey{ .kp = Ed25519.KeyPair.fromSecretKey(decoded.data) }; 242 return PrivateKey{ .kp = Ed25519.KeyPair.fromSecretKey(decoded.data) };
237 } 243 }
@@ -240,22 +246,22 @@ pub const PrivateKey = struct {
240 return Self{ .kp = Ed25519.KeyPair.fromSecretKey(raw_key.*) }; 246 return Self{ .kp = Ed25519.KeyPair.fromSecretKey(raw_key.*) };
241 } 247 }
242 248
243 pub fn intoSeedKeyPair(self: *const Self, prefix: PublicPrefixByte) SeedKeyPair { 249 pub fn intoSeedKeyPair(self: *const Self, role: Role) SeedKeyPair {
244 return SeedKeyPair{ 250 return SeedKeyPair{
245 .prefix = prefix, 251 .role = role,
246 .kp = self.kp, 252 .kp = self.kp,
247 }; 253 };
248 } 254 }
249 255
250 pub fn intoPublicKey(self: *const Self, prefix: PublicPrefixByte) PublicKey { 256 pub fn intoPublicKey(self: *const Self, role: Role) PublicKey {
251 return PublicKey{ 257 return PublicKey{
252 .prefix = prefix, 258 .role = role,
253 .key = self.kp.public_key, 259 .key = self.kp.public_key,
254 }; 260 };
255 } 261 }
256 262
257 pub fn privateKeyText(self: *const Self) text_private { 263 pub fn privateKeyText(self: *const Self) text_private {
258 return encode(1, self.kp.secret_key.len, &.{@enumToInt(KeyTypePrefixByte.private)}, &self.kp.secret_key); 264 return encode(1, self.kp.secret_key.len, &.{prefix_byte_private}, &self.kp.secret_key);
259 } 265 }
260 266
261 pub fn sign( 267 pub fn sign(
@@ -311,7 +317,7 @@ fn DecodedNkey(comptime prefix_len: usize, comptime data_len: usize) type {
311 data: [data_len]u8, 317 data: [data_len]u8,
312 318
313 pub fn wipe(self: *Self) void { 319 pub fn wipe(self: *Self) void {
314 self.prefix[0] = @enumToInt(PublicPrefixByte.account); 320 self.prefix[0] = Role.account.publicPrefixByte();
315 wipeBytes(&self.data); 321 wipeBytes(&self.data);
316 } 322 }
317 }; 323 };
@@ -356,18 +362,18 @@ pub fn isValidEncoding(text: []const u8) bool {
356 return made_crc == got_crc; 362 return made_crc == got_crc;
357} 363}
358 364
359pub fn isValidSeed(text: []const u8, with_type: ?PublicPrefixByte) bool { 365pub fn isValidSeed(text: []const u8, with_role: ?Role) bool {
360 if (text.len < text_seed_len) return false; 366 if (text.len < text_seed_len) return false;
361 var res = SeedKeyPair.fromTextSeed(text[0..text_seed_len]) catch return false; 367 var res = SeedKeyPair.fromTextSeed(text[0..text_seed_len]) catch return false;
362 defer res.wipe(); 368 defer res.wipe();
363 return if (with_type) |ty| res.prefix == ty else true; 369 return if (with_role) |role| res.role == role else true;
364} 370}
365 371
366pub fn isValidPublicKey(text: []const u8, with_type: ?PublicPrefixByte) bool { 372pub fn isValidPublicKey(text: []const u8, with_role: ?Role) bool {
367 if (text.len < text_public_len) return false; 373 if (text.len < text_public_len) return false;
368 var res = PublicKey.fromTextPublicKey(text[0..text_public_len]) catch return false; 374 var res = PublicKey.fromTextPublicKey(text[0..text_public_len]) catch return false;
369 defer res.wipe(); 375 defer res.wipe();
370 return if (with_type) |ty| res.prefix == ty else true; 376 return if (with_role) |role| res.role == role else true;
371} 377}
372 378
373pub fn isValidPrivateKey(text: []const u8) bool { 379pub fn isValidPrivateKey(text: []const u8) bool {
@@ -474,15 +480,14 @@ fn wipeBytes(bs: []u8) void {
474 480
475test { 481test {
476 testing.refAllDecls(@This()); 482 testing.refAllDecls(@This());
477 testing.refAllDecls(KeyTypePrefixByte); 483 testing.refAllDecls(Role);
478 testing.refAllDecls(PublicPrefixByte);
479 testing.refAllDecls(SeedKeyPair); 484 testing.refAllDecls(SeedKeyPair);
480 testing.refAllDecls(PublicKey); 485 testing.refAllDecls(PublicKey);
481 testing.refAllDecls(PrivateKey); 486 testing.refAllDecls(PrivateKey);
482} 487}
483 488
484test { 489test {
485 var key_pair = try SeedKeyPair.generate(PublicPrefixByte.server); 490 var key_pair = try SeedKeyPair.generate(.server);
486 var decoded_seed = try SeedKeyPair.fromTextSeed(&key_pair.seedText()); 491 var decoded_seed = try SeedKeyPair.fromTextSeed(&key_pair.seedText());
487 try testing.expect(isValidEncoding(&decoded_seed.seedText())); 492 try testing.expect(isValidEncoding(&decoded_seed.seedText()));
488 493
@@ -517,44 +522,42 @@ test "decode" {
517} 522}
518 523
519test "seed" { 524test "seed" {
520 inline for (@typeInfo(PublicPrefixByte).Enum.fields) |field| { 525 inline for (@typeInfo(Role).Enum.fields) |field| {
521 const prefix = @field(PublicPrefixByte, field.name); 526 const role = @field(Role, field.name);
522 const kp = try SeedKeyPair.generate(prefix); 527 const kp = try SeedKeyPair.generate(role);
523 const decoded = try SeedKeyPair.fromTextSeed(&kp.seedText()); 528 const decoded = try SeedKeyPair.fromTextSeed(&kp.seedText());
524 if (decoded.prefix != prefix) { 529 if (decoded.role != role) {
525 std.debug.print("expected prefix {}, found prefix {}\n", .{ prefix, decoded.prefix }); 530 std.debug.print("expected role {}, found role {}\n", .{ role, decoded.role });
526 return error.TestUnexpectedError; 531 return error.TestUnexpectedError;
527 } 532 }
528 } 533 }
529} 534}
530 535
531test "public key" { 536test "public key" {
532 inline for (@typeInfo(PublicPrefixByte).Enum.fields) |field| { 537 inline for (@typeInfo(Role).Enum.fields) |field| {
533 const prefix = @field(PublicPrefixByte, field.name); 538 const role = @field(Role, field.name);
534 const kp = try SeedKeyPair.generate(prefix); 539 const kp = try SeedKeyPair.generate(role);
535 const decoded_pub_key = try PublicKey.fromTextPublicKey(&kp.publicKeyText()); 540 const decoded_pub_key = try PublicKey.fromTextPublicKey(&kp.publicKeyText());
536 if (decoded_pub_key.prefix != prefix) { 541 if (decoded_pub_key.role != role) {
537 std.debug.print("expected prefix {}, found prefix {}\n", .{ prefix, decoded_pub_key.prefix }); 542 std.debug.print("expected role {}, found role {}\n", .{ role, decoded_pub_key.role });
538 return error.TestUnexpectedError; 543 return error.TestUnexpectedError;
539 } 544 }
540 } 545 }
541} 546}
542 547
543test "different key types" { 548test "different key types" {
544 inline for (@typeInfo(PublicPrefixByte).Enum.fields) |field| { 549 inline for (@typeInfo(Role).Enum.fields) |field| {
545 const prefix = @field(PublicPrefixByte, field.name); 550 const role = @field(Role, field.name);
546 551
547 const kp = try SeedKeyPair.generate(prefix); 552 const kp = try SeedKeyPair.generate(role);
548 _ = try SeedKeyPair.fromTextSeed(&kp.seedText()); 553 _ = try SeedKeyPair.fromTextSeed(&kp.seedText());
549 554
550 const pub_key_str = kp.publicKeyText(); 555 const pub_key_str = kp.publicKeyText();
551 const got_pub_key_prefix = try PublicPrefixByte.fromChar(pub_key_str[0]); 556 try testing.expect(pub_key_str[0] == role.letter());
552 try testing.expect(got_pub_key_prefix == prefix); 557 try testing.expect(isValidPublicKey(&pub_key_str, role));
553 try testing.expect(isValidPublicKey(&pub_key_str, prefix));
554 558
555 const priv_key_str = kp.privateKeyText(); 559 const priv_key_str = kp.privateKeyText();
556 const got_priv_key_prefix = try KeyTypePrefixByte.fromChar(priv_key_str[0]); 560 try testing.expect(priv_key_str[0] == 'P');
557 try testing.expect(got_priv_key_prefix == .private);
558 try testing.expect(isValidPrivateKey(&priv_key_str)); 561 try testing.expect(isValidPrivateKey(&priv_key_str));
559 562
560 const data = "Hello, world!"; 563 const data = "Hello, world!";
@@ -565,30 +568,30 @@ test "different key types" {
565} 568}
566 569
567test "validation" { 570test "validation" {
568 const prefixes = @typeInfo(PublicPrefixByte).Enum.fields; 571 const roles = @typeInfo(Role).Enum.fields;
569 inline for (prefixes) |field, i| { 572 inline for (roles) |field, i| {
570 const prefix = @field(PublicPrefixByte, field.name); 573 const role = @field(Role, field.name);
571 const next_prefix = next: { 574 const next_role = next: {
572 const next_field_i = if (i == prefixes.len - 1) 0 else i + 1; 575 const next_field_i = if (i == roles.len - 1) 0 else i + 1;
573 std.debug.assert(next_field_i != i); 576 std.debug.assert(next_field_i != i);
574 break :next @field(PublicPrefixByte, prefixes[next_field_i].name); 577 break :next @field(Role, roles[next_field_i].name);
575 }; 578 };
576 const kp = try SeedKeyPair.generate(prefix); 579 const kp = try SeedKeyPair.generate(role);
577 580
578 const seed_str = kp.seedText(); 581 const seed_str = kp.seedText();
579 const pub_key_str = kp.publicKeyText(); 582 const pub_key_str = kp.publicKeyText();
580 const priv_key_str = kp.privateKeyText(); 583 const priv_key_str = kp.privateKeyText();
581 584
582 try testing.expect(isValidSeed(&seed_str, prefix)); 585 try testing.expect(isValidSeed(&seed_str, role));
583 try testing.expect(isValidSeed(&seed_str, null)); 586 try testing.expect(isValidSeed(&seed_str, null));
584 try testing.expect(isValidPublicKey(&pub_key_str, null)); 587 try testing.expect(isValidPublicKey(&pub_key_str, null));
585 try testing.expect(isValidPublicKey(&pub_key_str, prefix)); 588 try testing.expect(isValidPublicKey(&pub_key_str, role));
586 try testing.expect(isValidPrivateKey(&priv_key_str)); 589 try testing.expect(isValidPrivateKey(&priv_key_str));
587 590
588 try testing.expect(!isValidSeed(&seed_str, next_prefix)); 591 try testing.expect(!isValidSeed(&seed_str, next_role));
589 try testing.expect(!isValidSeed(&pub_key_str, null)); 592 try testing.expect(!isValidSeed(&pub_key_str, null));
590 try testing.expect(!isValidSeed(&priv_key_str, null)); 593 try testing.expect(!isValidSeed(&priv_key_str, null));
591 try testing.expect(!isValidPublicKey(&pub_key_str, next_prefix)); 594 try testing.expect(!isValidPublicKey(&pub_key_str, next_role));
592 try testing.expect(!isValidPublicKey(&seed_str, null)); 595 try testing.expect(!isValidPublicKey(&seed_str, null));
593 try testing.expect(!isValidPublicKey(&priv_key_str, null)); 596 try testing.expect(!isValidPublicKey(&priv_key_str, null));
594 try testing.expect(!isValidPrivateKey(&seed_str)); 597 try testing.expect(!isValidPrivateKey(&seed_str));
@@ -602,7 +605,7 @@ test "validation" {
602 605
603test "from seed" { 606test "from seed" {
604 const kp = try SeedKeyPair.generate(.account); 607 const kp = try SeedKeyPair.generate(.account);
605 const kp_from_raw = try SeedKeyPair.fromRawSeed(kp.prefix, kp.kp.secret_key[0..Ed25519.seed_length]); 608 const kp_from_raw = try SeedKeyPair.fromRawSeed(kp.role, kp.kp.secret_key[0..Ed25519.seed_length]);
606 try testing.expect(std.meta.eql(kp, kp_from_raw)); 609 try testing.expect(std.meta.eql(kp, kp_from_raw));
607 610
608 const data = "Hello, World!"; 611 const data = "Hello, World!";
@@ -625,7 +628,7 @@ test "from public key" {
625 const pk = try PublicKey.fromTextPublicKey(&pk_text); 628 const pk = try PublicKey.fromTextPublicKey(&pk_text);
626 const pk_text_clone_2 = pk.publicKeyText(); 629 const pk_text_clone_2 = pk.publicKeyText();
627 try testing.expect(std.meta.eql(pk, kp.intoPublicKey())); 630 try testing.expect(std.meta.eql(pk, kp.intoPublicKey()));
628 try testing.expect(std.meta.eql(pk, PublicKey.fromRawPublicKey(kp.prefix, &kp.kp.public_key))); 631 try testing.expect(std.meta.eql(pk, PublicKey.fromRawPublicKey(kp.role, &kp.kp.public_key)));
629 try testing.expectEqualStrings(&pk_text, &pk_text_clone_2); 632 try testing.expectEqualStrings(&pk_text, &pk_text_clone_2);
630 633
631 const data = "Hello, world!"; 634 const data = "Hello, world!";
diff --git a/src/znk.zig b/src/znk.zig
index 4ab3077..8be7713 100644
--- a/src/znk.zig
+++ b/src/znk.zig
@@ -74,9 +74,9 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
74} 74}
75 75
76const usage_gen = 76const usage_gen =
77 \\Usage: znk gen [options] <type> 77 \\Usage: znk gen [options] <role>
78 \\ 78 \\
79 \\Supported Types: 79 \\Supported Roles:
80 \\ 80 \\
81 \\ account 81 \\ account
82 \\ cluster 82 \\ cluster
@@ -98,7 +98,7 @@ const usage_gen =
98pub fn cmdGen(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !void { 98pub fn cmdGen(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !void {
99 const stdout = io.getStdOut(); 99 const stdout = io.getStdOut();
100 100
101 var ty: ?nkeys.PublicPrefixByte = null; 101 var role: ?nkeys.Role = null;
102 var pub_out: bool = false; 102 var pub_out: bool = false;
103 var prefix: ?[]const u8 = null; 103 var prefix: ?[]const u8 = null;
104 104
@@ -120,34 +120,34 @@ pub fn cmdGen(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !voi
120 } else { 120 } else {
121 fatal("unrecognized parameter: '{s}'", .{arg}); 121 fatal("unrecognized parameter: '{s}'", .{arg});
122 } 122 }
123 } else if (ty != null) { 123 } else if (role != null) {
124 fatal("more than one type to generate provided", .{}); 124 fatal("more than one role to generate for provided", .{});
125 } else if (mem.eql(u8, arg, "account")) { 125 } else if (mem.eql(u8, arg, "account")) {
126 ty = .account; 126 role = .account;
127 } else if (mem.eql(u8, arg, "cluster")) { 127 } else if (mem.eql(u8, arg, "cluster")) {
128 ty = .cluster; 128 role = .cluster;
129 } else if (mem.eql(u8, arg, "operator")) { 129 } else if (mem.eql(u8, arg, "operator")) {
130 ty = .operator; 130 role = .operator;
131 } else if (mem.eql(u8, arg, "server")) { 131 } else if (mem.eql(u8, arg, "server")) {
132 ty = .server; 132 role = .server;
133 } else if (mem.eql(u8, arg, "user")) { 133 } else if (mem.eql(u8, arg, "user")) {
134 ty = .user; 134 role = .user;
135 } else { 135 } else {
136 fatal("unrecognized extra parameter: '{s}'", .{arg}); 136 fatal("unrecognized extra parameter: '{s}'", .{arg});
137 } 137 }
138 } 138 }
139 139
140 if (ty == null) { 140 if (role == null) {
141 info("{s}", .{usage_gen}); 141 info("{s}", .{usage_gen});
142 fatal("no type to generate seed for provided", .{}); 142 fatal("no role to generate seed for provided", .{});
143 } 143 }
144 144
145 if (prefix != null) { 145 if (prefix != null) {
146 const capitalized_prefix = try toUpper(arena, prefix.?); 146 const capitalized_prefix = try toUpper(arena, prefix.?);
147 147
148 try PrefixKeyGenerator.init(arena, ty.?, capitalized_prefix).generate(); 148 try PrefixKeyGenerator.init(arena, role.?, capitalized_prefix).generate();
149 } else { 149 } else {
150 var kp = try nkeys.SeedKeyPair.generate(ty.?); 150 var kp = try nkeys.SeedKeyPair.generate(role.?);
151 defer kp.wipe(); 151 defer kp.wipe();
152 try stdout.writeAll(&kp.seedText()); 152 try stdout.writeAll(&kp.seedText());
153 try stdout.writeAll("\n"); 153 try stdout.writeAll("\n");
@@ -356,16 +356,16 @@ pub fn cmdVerify(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !
356} 356}
357 357
358const PrefixKeyGenerator = struct { 358const PrefixKeyGenerator = struct {
359 ty: nkeys.PublicPrefixByte, 359 role: nkeys.Role,
360 prefix: []const u8, 360 prefix: []const u8,
361 allocator: *Allocator, 361 allocator: *Allocator,
362 done: std.atomic.Bool, 362 done: std.atomic.Bool,
363 363
364 const Self = @This(); 364 const Self = @This();
365 365
366 pub fn init(allocator: *Allocator, ty: nkeys.PublicPrefixByte, prefix: []const u8) Self { 366 pub fn init(allocator: *Allocator, role: nkeys.Role, prefix: []const u8) Self {
367 return .{ 367 return .{
368 .ty = ty, 368 .role = role,
369 .prefix = prefix, 369 .prefix = prefix,
370 .allocator = allocator, 370 .allocator = allocator,
371 .done = std.atomic.Bool.init(false), 371 .done = std.atomic.Bool.init(false),
@@ -376,7 +376,7 @@ const PrefixKeyGenerator = struct {
376 while (true) { 376 while (true) {
377 if (self.done.load(.SeqCst)) return; 377 if (self.done.load(.SeqCst)) return;
378 378
379 var kp = try nkeys.SeedKeyPair.generate(self.ty); 379 var kp = try nkeys.SeedKeyPair.generate(self.role);
380 defer kp.wipe(); 380 defer kp.wipe();
381 var public_key = kp.publicKeyText(); 381 var public_key = kp.publicKeyText();
382 if (!mem.startsWith(u8, public_key[1..], self.prefix)) continue; 382 if (!mem.startsWith(u8, public_key[1..], self.prefix)) continue;