diff options
| author | Rutger Broekhoff | 2021-05-25 17:41:21 +0200 |
|---|---|---|
| committer | Rutger Broekhoff | 2021-05-25 17:41:21 +0200 |
| commit | d8597fe575644ddf5f1f8ef8cf901a446bc7acda (patch) | |
| tree | f7f16966a74e60be6334ea10f1a277a434136de4 | |
| parent | 9d03b59a409596d12c3ec7b49066e4eaf0f463e7 (diff) | |
| download | zig-nkeys-d8597fe575644ddf5f1f8ef8cf901a446bc7acda.tar.gz zig-nkeys-d8597fe575644ddf5f1f8ef8cf901a446bc7acda.zip | |
Clean up janky prefix byte API
| -rw-r--r-- | src/nkeys.zig | 229 | ||||
| -rw-r--r-- | src/znk.zig | 36 |
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 | |||
| 19 | pub const PrivateKeyDecodeError = DecodeError || InvalidPrivateKeyError || crypto.errors.IdentityElementError; | 19 | pub const PrivateKeyDecodeError = DecodeError || InvalidPrivateKeyError || crypto.errors.IdentityElementError; |
| 20 | pub const SignError = crypto.errors.IdentityElementError || crypto.errors.WeakPublicKeyError || crypto.errors.KeyMismatchError; | 20 | pub const SignError = crypto.errors.IdentityElementError || crypto.errors.WeakPublicKeyError || crypto.errors.KeyMismatchError; |
| 21 | 21 | ||
| 22 | pub const KeyTypePrefixByte = enum(u8) { | 22 | pub const prefix_byte_account = 0; // A |
| 23 | const Self = @This(); | 23 | pub const prefix_byte_cluster = 2 << 3; // C |
| 24 | 24 | pub const prefix_byte_operator = 14 << 3; // O | |
| 25 | seed = 18 << 3, // S | 25 | pub const prefix_byte_private = 15 << 3; // P |
| 26 | private = 15 << 3, // P | 26 | pub const prefix_byte_seed = 18 << 3; // S |
| 27 | 27 | pub const prefix_byte_server = 13 << 3; // N | |
| 28 | pub fn char(self: Self) u8 { | 28 | pub const prefix_byte_user = 20 << 3; // U |
| 29 | return switch (self) { | 29 | |
| 30 | .seed => 'S', | 30 | pub 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 { | 43 | pub 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 | ||
| 44 | pub const PublicPrefixByte = enum(u8) { | 56 | pub 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; | |||
| 101 | pub const SeedKeyPair = struct { | 106 | pub 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 { | |||
| 186 | pub const PublicKey = struct { | 192 | pub 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 | ||
| 359 | pub fn isValidSeed(text: []const u8, with_type: ?PublicPrefixByte) bool { | 365 | pub 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 | ||
| 366 | pub fn isValidPublicKey(text: []const u8, with_type: ?PublicPrefixByte) bool { | 372 | pub 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 | ||
| 373 | pub fn isValidPrivateKey(text: []const u8) bool { | 379 | pub fn isValidPrivateKey(text: []const u8) bool { |
| @@ -474,15 +480,14 @@ fn wipeBytes(bs: []u8) void { | |||
| 474 | 480 | ||
| 475 | test { | 481 | test { |
| 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 | ||
| 484 | test { | 489 | test { |
| 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 | ||
| 519 | test "seed" { | 524 | test "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 | ||
| 531 | test "public key" { | 536 | test "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 | ||
| 543 | test "different key types" { | 548 | test "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 | ||
| 567 | test "validation" { | 570 | test "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 | ||
| 603 | test "from seed" { | 606 | test "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 | ||
| 76 | const usage_gen = | 76 | const 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 = | |||
| 98 | pub fn cmdGen(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !void { | 98 | pub 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 | ||
| 358 | const PrefixKeyGenerator = struct { | 358 | const 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; |