diff options
| author | Rutger Broekhoff | 2021-05-24 22:44:21 +0200 |
|---|---|---|
| committer | Rutger Broekhoff | 2021-05-24 22:44:21 +0200 |
| commit | f3232a9022b67db164b901272f4a8e47f46a74b3 (patch) | |
| tree | 58f32f210024b32939053e5c8e13a7a52ddcb6fe | |
| parent | 862fed7c251b331ac953e8e7ef5cdfe3da7772e8 (diff) | |
| download | zig-nkeys-f3232a9022b67db164b901272f4a8e47f46a74b3.tar.gz zig-nkeys-f3232a9022b67db164b901272f4a8e47f46a74b3.zip | |
Add PrivateKey, use Ed25519.KeyPair in SeedKeyPair
| -rw-r--r-- | src/nkeys.zig | 271 | ||||
| -rw-r--r-- | src/znk.zig | 24 |
2 files changed, 160 insertions, 135 deletions
diff --git a/src/nkeys.zig b/src/nkeys.zig index 8493941..aac326c 100644 --- a/src/nkeys.zig +++ b/src/nkeys.zig | |||
| @@ -9,9 +9,15 @@ const testing = std.testing; | |||
| 9 | 9 | ||
| 10 | pub const InvalidPrefixByteError = error{InvalidPrefixByte}; | 10 | pub const InvalidPrefixByteError = error{InvalidPrefixByte}; |
| 11 | pub const InvalidEncodingError = error{InvalidEncoding}; | 11 | pub const InvalidEncodingError = error{InvalidEncoding}; |
| 12 | pub const InvalidPrivateKeyError = error{InvalidPrivateKey}; | ||
| 12 | pub const InvalidSeedError = error{InvalidSeed}; | 13 | pub const InvalidSeedError = error{InvalidSeed}; |
| 14 | pub const InvalidSignatureError = error{InvalidSignature}; | ||
| 13 | pub const NoNkeySeedFoundError = error{NoNkeySeedFound}; | 15 | pub const NoNkeySeedFoundError = error{NoNkeySeedFound}; |
| 14 | pub const NoNkeyUserSeedFoundError = error{NoNkeyUserSeedFound}; | 16 | pub const NoNkeyUserSeedFoundError = error{NoNkeyUserSeedFound}; |
| 17 | pub const DecodeError = InvalidPrefixByteError || base32.DecodeError || crc16.InvalidChecksumError; | ||
| 18 | pub const SeedDecodeError = DecodeError || InvalidSeedError || 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; | ||
| 15 | 21 | ||
| 16 | pub const KeyTypePrefixByte = enum(u8) { | 22 | pub const KeyTypePrefixByte = enum(u8) { |
| 17 | seed = 18 << 3, // S | 23 | seed = 18 << 3, // S |
| @@ -41,93 +47,93 @@ pub const PublicPrefixByte = enum(u8) { | |||
| 41 | pub const SeedKeyPair = struct { | 47 | pub const SeedKeyPair = struct { |
| 42 | const Self = @This(); | 48 | const Self = @This(); |
| 43 | 49 | ||
| 44 | seed: text_seed, | 50 | prefix: PublicPrefixByte, |
| 51 | kp: Ed25519.KeyPair, | ||
| 45 | 52 | ||
| 46 | pub fn generate(prefix: PublicPrefixByte) Self { | 53 | pub fn generate(prefix: PublicPrefixByte) crypto.errors.IdentityElementError!Self { |
| 47 | var raw_seed: [Ed25519.seed_length]u8 = undefined; | 54 | var raw_seed: [Ed25519.seed_length]u8 = undefined; |
| 48 | crypto.random.bytes(&raw_seed); | 55 | crypto.random.bytes(&raw_seed); |
| 49 | defer wipeBytes(&raw_seed); | 56 | defer wipeBytes(&raw_seed); |
| 50 | 57 | return Self{ .prefix = prefix, .kp = try Ed25519.KeyPair.create(raw_seed) }; | |
| 51 | return Self{ .seed = encodeSeed(prefix, &raw_seed) }; | ||
| 52 | } | ||
| 53 | |||
| 54 | pub fn fromTextSeed(seed: *const text_seed) SeedDecodeError!Self { | ||
| 55 | var decoded = try decodeSeed(seed); | ||
| 56 | decoded.wipe(); | ||
| 57 | return Self{ .seed = seed.* }; | ||
| 58 | } | ||
| 59 | |||
| 60 | pub fn fromRawSeed(prefix: PublicPrefixByte, raw_seed: *const [Ed25519.seed_length]u8) Self { | ||
| 61 | return Self{ .seed = encodeSeed(prefix, raw_seed) }; | ||
| 62 | } | 58 | } |
| 63 | 59 | ||
| 64 | fn rawSeed(self: *const Self) SeedDecodeError![Ed25519.seed_length]u8 { | 60 | pub fn fromTextSeed(text: *const text_seed) SeedDecodeError!Self { |
| 65 | return (try decodeSeed(&self.seed)).seed; | 61 | var decoded = try decode(2, Ed25519.seed_length, text); |
| 66 | } | 62 | defer decoded.wipe(); // gets copied |
| 67 | |||
| 68 | fn keys(self: *const Self) (SeedDecodeError || crypto.errors.IdentityElementError)!Ed25519.KeyPair { | ||
| 69 | return Ed25519.KeyPair.create(try rawSeed(self)); | ||
| 70 | } | ||
| 71 | 63 | ||
| 72 | pub fn privateKey(self: *const Self) (SeedDecodeError || crypto.errors.IdentityElementError)!text_private { | 64 | var key_ty_prefix = decoded.prefix[0] & 0b11111000; |
| 73 | var kp = try self.keys(); | 65 | var entity_ty_prefix = (decoded.prefix[0] << 5) | (decoded.prefix[1] >> 3); |
| 74 | defer wipeKeyPair(&kp); | ||
| 75 | return encodePrivate(&kp.secret_key); | ||
| 76 | } | ||
| 77 | 66 | ||
| 78 | pub fn publicKey(self: *const Self) (SeedDecodeError || crypto.errors.IdentityElementError)!text_public { | 67 | if (key_ty_prefix != @enumToInt(KeyTypePrefixByte.seed)) |
| 79 | var decoded = try decodeSeed(&self.seed); | 68 | return error.InvalidSeed; |
| 80 | defer decoded.wipe(); | ||
| 81 | var kp = try Ed25519.KeyPair.create(decoded.seed); | ||
| 82 | defer wipeKeyPair(&kp); | ||
| 83 | return encodePublic(decoded.prefix, &kp.public_key); | ||
| 84 | } | ||
| 85 | 69 | ||
| 86 | pub fn intoPublicKey(self: *const Self) (SeedDecodeError || crypto.errors.IdentityElementError)!PublicKey { | 70 | return Self{ |
| 87 | var decoded = try decodeSeed(&self.seed); | 71 | .prefix = try PublicPrefixByte.fromU8(entity_ty_prefix), |
| 88 | defer decoded.wipe(); | 72 | .kp = try Ed25519.KeyPair.create(decoded.data), |
| 89 | var kp = try Ed25519.KeyPair.create(decoded.seed); | ||
| 90 | defer wipeKeyPair(&kp); | ||
| 91 | return PublicKey{ | ||
| 92 | .prefix = decoded.prefix, | ||
| 93 | .key = kp.public_key, | ||
| 94 | }; | 73 | }; |
| 95 | } | 74 | } |
| 96 | 75 | ||
| 97 | pub const SignError = SeedDecodeError || crypto.errors.IdentityElementError || crypto.errors.WeakPublicKeyError; | 76 | pub fn fromRawSeed( |
| 77 | prefix: PublicPrefixByte, | ||
| 78 | raw_seed: *const [Ed25519.seed_length]u8, | ||
| 79 | ) crypto.errors.IdentityElementError!Self { | ||
| 80 | return Self{ .prefix = prefix, .kp = try Ed25519.KeyPair.create(raw_seed.*) }; | ||
| 81 | } | ||
| 98 | 82 | ||
| 99 | pub fn sign( | 83 | pub fn sign( |
| 100 | self: *const Self, | 84 | self: *const Self, |
| 101 | msg: []const u8, | 85 | msg: []const u8, |
| 102 | ) SignError![Ed25519.signature_length]u8 { | 86 | ) SignError![Ed25519.signature_length]u8 { |
| 103 | var kp = try self.keys(); | 87 | return Ed25519.sign(msg, self.kp, null); |
| 104 | defer wipeKeyPair(&kp); | ||
| 105 | return Ed25519.sign(msg, kp, null) catch |e| switch (e) { | ||
| 106 | error.KeyMismatch => unreachable, // would mean that self.keys() has an incorrect implementation | ||
| 107 | error.WeakPublicKey => error.WeakPublicKey, | ||
| 108 | error.IdentityElement => error.IdentityElement, | ||
| 109 | }; | ||
| 110 | } | 88 | } |
| 111 | 89 | ||
| 112 | pub fn verify( | 90 | pub fn verify( |
| 113 | self: *const Self, | 91 | self: *const Self, |
| 114 | msg: []const u8, | 92 | msg: []const u8, |
| 115 | sig: [Ed25519.signature_length]u8, | 93 | sig: [Ed25519.signature_length]u8, |
| 116 | ) !void { | 94 | ) InvalidSignatureError!void { |
| 117 | var kp = try self.keys(); | 95 | Ed25519.verify(sig, msg, self.kp.public_key) catch return error.InvalidSignature; |
| 118 | defer wipeKeyPair(&kp); | ||
| 119 | Ed25519.verify(sig, msg, kp.public_key) catch return error.InvalidSignature; | ||
| 120 | } | 96 | } |
| 121 | 97 | ||
| 122 | pub fn wipe(self: *Self) void { | 98 | pub fn asTextSeed(self: *const Self) text_seed { |
| 123 | wipeBytes(&self.seed); | 99 | const full_prefix = &[_]u8{ |
| 100 | @enumToInt(KeyTypePrefixByte.seed) | (@enumToInt(self.prefix) >> 5), | ||
| 101 | (@enumToInt(self.prefix) & 0b00011111) << 3, | ||
| 102 | }; | ||
| 103 | const seed = self.kp.secret_key[0..Ed25519.seed_length]; | ||
| 104 | return encode(full_prefix.len, seed.len, full_prefix, seed); | ||
| 105 | } | ||
| 106 | |||
| 107 | pub fn asTextPrivateKey(self: *const Self) text_private { | ||
| 108 | return encode(1, self.kp.secret_key.len, &[_]u8{@enumToInt(KeyTypePrefixByte.private)}, &self.kp.secret_key); | ||
| 109 | } | ||
| 110 | |||
| 111 | pub fn asTextPublicKey(self: *const Self) text_public { | ||
| 112 | return encode(1, self.kp.public_key.len, &[_]u8{@enumToInt(self.prefix)}, &self.kp.public_key); | ||
| 124 | } | 113 | } |
| 125 | 114 | ||
| 126 | fn wipeKeyPair(kp: *Ed25519.KeyPair) void { | 115 | pub fn asPublicKey(self: *const Self) PublicKey { |
| 127 | wipeBytes(&kp.secret_key); | 116 | return PublicKey{ |
| 117 | .prefix = self.prefix, | ||
| 118 | .key = self.kp.public_key, | ||
| 119 | }; | ||
| 120 | } | ||
| 121 | |||
| 122 | pub fn asPrivateKey(self: *const Self) PrivateKey { | ||
| 123 | return PrivateKey{ .kp = self.kp }; | ||
| 124 | } | ||
| 125 | |||
| 126 | pub fn wipe(self: *Self) void { | ||
| 127 | self.prefix = .account; | ||
| 128 | wipeKeyPair(&self.kp); | ||
| 128 | } | 129 | } |
| 129 | }; | 130 | }; |
| 130 | 131 | ||
| 132 | fn wipeKeyPair(kp: *Ed25519.KeyPair) void { | ||
| 133 | wipeBytes(&kp.public_key); | ||
| 134 | wipeBytes(&kp.secret_key); | ||
| 135 | } | ||
| 136 | |||
| 131 | fn wipeBytes(bs: []u8) void { | 137 | fn wipeBytes(bs: []u8) void { |
| 132 | for (bs) |*b| b.* = 0; | 138 | for (bs) |*b| b.* = 0; |
| 133 | } | 139 | } |
| @@ -138,7 +144,7 @@ pub const PublicKey = struct { | |||
| 138 | prefix: PublicPrefixByte, | 144 | prefix: PublicPrefixByte, |
| 139 | key: [Ed25519.public_length]u8, | 145 | key: [Ed25519.public_length]u8, |
| 140 | 146 | ||
| 141 | pub fn fromTextPublicKey(text: *const text_public) DecodeError!PublicKey { | 147 | pub fn fromTextPublicKey(text: *const text_public) DecodeError!Self { |
| 142 | var decoded = try decode(1, Ed25519.public_length, text); | 148 | var decoded = try decode(1, Ed25519.public_length, text); |
| 143 | defer decoded.wipe(); // gets copied | 149 | defer decoded.wipe(); // gets copied |
| 144 | return PublicKey{ | 150 | return PublicKey{ |
| @@ -147,15 +153,15 @@ pub const PublicKey = struct { | |||
| 147 | }; | 153 | }; |
| 148 | } | 154 | } |
| 149 | 155 | ||
| 150 | pub fn publicKey(self: *const Self) text_public { | 156 | pub fn asTextPublicKey(self: *const Self) text_public { |
| 151 | return encodePublic(self.prefix, &self.key); | 157 | return encode(1, self.key.len, &[_]u8{@enumToInt(self.prefix)}, &self.key); |
| 152 | } | 158 | } |
| 153 | 159 | ||
| 154 | pub fn verify( | 160 | pub fn verify( |
| 155 | self: *const Self, | 161 | self: *const Self, |
| 156 | msg: []const u8, | 162 | msg: []const u8, |
| 157 | sig: [Ed25519.signature_length]u8, | 163 | sig: [Ed25519.signature_length]u8, |
| 158 | ) !void { | 164 | ) InvalidSignatureError!void { |
| 159 | Ed25519.verify(sig, msg, self.key) catch return error.InvalidSignature; | 165 | Ed25519.verify(sig, msg, self.key) catch return error.InvalidSignature; |
| 160 | } | 166 | } |
| 161 | 167 | ||
| @@ -165,6 +171,57 @@ pub const PublicKey = struct { | |||
| 165 | } | 171 | } |
| 166 | }; | 172 | }; |
| 167 | 173 | ||
| 174 | pub const PrivateKey = struct { | ||
| 175 | const Self = @This(); | ||
| 176 | |||
| 177 | kp: Ed25519.KeyPair, | ||
| 178 | |||
| 179 | pub fn fromTextPrivateKey(text: *const text_private) PrivateKeyDecodeError!Self { | ||
| 180 | var decoded = try decode(1, Ed25519.secret_length, text); | ||
| 181 | defer decoded.wipe(); // gets copied | ||
| 182 | if (decoded.prefix[0] != @enumToInt(KeyTypePrefixByte.private)) | ||
| 183 | return error.InvalidPrivateKey; | ||
| 184 | return PrivateKey{ .kp = Ed25519.KeyPair.fromSecretKey(decoded.data) }; | ||
| 185 | } | ||
| 186 | |||
| 187 | pub fn asSeedKeyPair(self: *const Self, prefix: PublicPrefixByte) SeedKeyPair { | ||
| 188 | return SeedKeyPair{ | ||
| 189 | .prefix = prefix, | ||
| 190 | .kp = self.kp, | ||
| 191 | }; | ||
| 192 | } | ||
| 193 | |||
| 194 | pub fn asPublicKey(self: *const Self, prefix: PublicPrefixByte) PublicKey { | ||
| 195 | return PublicKey{ | ||
| 196 | .prefix = prefix, | ||
| 197 | .key = self.kp.public_key, | ||
| 198 | }; | ||
| 199 | } | ||
| 200 | |||
| 201 | pub fn asTextPrivateKey(self: *const Self) text_private { | ||
| 202 | return encode(1, self.kp.secret_key.len, &[_]u8{@enumToInt(KeyTypePrefixByte.private)}, &self.kp.secret_key); | ||
| 203 | } | ||
| 204 | |||
| 205 | pub fn sign( | ||
| 206 | self: *const Self, | ||
| 207 | msg: []const u8, | ||
| 208 | ) SignError![Ed25519.signature_length]u8 { | ||
| 209 | return Ed25519.sign(msg, self.kp, null); | ||
| 210 | } | ||
| 211 | |||
| 212 | pub fn verify( | ||
| 213 | self: *const Self, | ||
| 214 | msg: []const u8, | ||
| 215 | sig: [Ed25519.signature_length]u8, | ||
| 216 | ) InvalidSignatureError!void { | ||
| 217 | Ed25519.verify(sig, msg, self.kp.public_key) catch return error.InvalidSignature; | ||
| 218 | } | ||
| 219 | |||
| 220 | pub fn wipe(self: *Self) void { | ||
| 221 | wipeKeyPair(&self.kp); | ||
| 222 | } | ||
| 223 | }; | ||
| 224 | |||
| 168 | // One prefix byte, two CRC bytes | 225 | // One prefix byte, two CRC bytes |
| 169 | const binary_private_size = 1 + Ed25519.secret_length + 2; | 226 | const binary_private_size = 1 + Ed25519.secret_length + 2; |
| 170 | // One prefix byte, two CRC bytes | 227 | // One prefix byte, two CRC bytes |
| @@ -180,14 +237,6 @@ pub const text_private = [text_private_len]u8; | |||
| 180 | pub const text_public = [text_public_len]u8; | 237 | pub const text_public = [text_public_len]u8; |
| 181 | pub const text_seed = [text_seed_len]u8; | 238 | pub const text_seed = [text_seed_len]u8; |
| 182 | 239 | ||
| 183 | fn encodePublic(prefix: PublicPrefixByte, key: *const [Ed25519.public_length]u8) text_public { | ||
| 184 | return encode(1, key.len, &[_]u8{@enumToInt(prefix)}, key); | ||
| 185 | } | ||
| 186 | |||
| 187 | fn encodePrivate(key: *const [Ed25519.secret_length]u8) text_private { | ||
| 188 | return encode(1, key.len, &[_]u8{@enumToInt(KeyTypePrefixByte.private)}, key); | ||
| 189 | } | ||
| 190 | |||
| 191 | fn encoded_key(comptime prefix_len: usize, comptime data_len: usize) type { | 240 | fn encoded_key(comptime prefix_len: usize, comptime data_len: usize) type { |
| 192 | return [base32.Encoder.calcSize(prefix_len + data_len + 2)]u8; | 241 | return [base32.Encoder.calcSize(prefix_len + data_len + 2)]u8; |
| 193 | } | 242 | } |
| @@ -213,16 +262,6 @@ fn encode( | |||
| 213 | return text; | 262 | return text; |
| 214 | } | 263 | } |
| 215 | 264 | ||
| 216 | pub fn encodeSeed(prefix: PublicPrefixByte, src: *const [Ed25519.seed_length]u8) text_seed { | ||
| 217 | const full_prefix = &[_]u8{ | ||
| 218 | @enumToInt(KeyTypePrefixByte.seed) | (@enumToInt(prefix) >> 5), | ||
| 219 | (@enumToInt(prefix) & 0b00011111) << 3, | ||
| 220 | }; | ||
| 221 | return encode(full_prefix.len, src.len, full_prefix, src); | ||
| 222 | } | ||
| 223 | |||
| 224 | pub const DecodeError = InvalidPrefixByteError || base32.DecodeError || crc16.InvalidChecksumError; | ||
| 225 | |||
| 226 | fn DecodedNkey(comptime prefix_len: usize, comptime data_len: usize) type { | 265 | fn DecodedNkey(comptime prefix_len: usize, comptime data_len: usize) type { |
| 227 | return struct { | 266 | return struct { |
| 228 | const Self = @This(); | 267 | const Self = @This(); |
| @@ -255,36 +294,6 @@ fn decode( | |||
| 255 | }; | 294 | }; |
| 256 | } | 295 | } |
| 257 | 296 | ||
| 258 | pub const DecodedSeed = struct { | ||
| 259 | const Self = @This(); | ||
| 260 | |||
| 261 | prefix: PublicPrefixByte, | ||
| 262 | seed: [Ed25519.seed_length]u8, | ||
| 263 | |||
| 264 | pub fn wipe(self: *Self) void { | ||
| 265 | self.prefix = .account; | ||
| 266 | wipeBytes(&self.seed); | ||
| 267 | } | ||
| 268 | }; | ||
| 269 | |||
| 270 | pub const SeedDecodeError = DecodeError || InvalidSeedError; | ||
| 271 | |||
| 272 | pub fn decodeSeed(text: *const text_seed) SeedDecodeError!DecodedSeed { | ||
| 273 | var decoded = try decode(2, Ed25519.seed_length, text); | ||
| 274 | defer decoded.wipe(); // gets copied | ||
| 275 | |||
| 276 | var key_ty_prefix = decoded.prefix[0] & 0b11111000; | ||
| 277 | var entity_ty_prefix = (decoded.prefix[0] << 5) | (decoded.prefix[1] >> 3); | ||
| 278 | |||
| 279 | if (key_ty_prefix != @enumToInt(KeyTypePrefixByte.seed)) | ||
| 280 | return error.InvalidSeed; | ||
| 281 | |||
| 282 | return DecodedSeed{ | ||
| 283 | .prefix = try PublicPrefixByte.fromU8(entity_ty_prefix), | ||
| 284 | .seed = decoded.data, | ||
| 285 | }; | ||
| 286 | } | ||
| 287 | |||
| 288 | pub fn isValidEncoding(text: []const u8) bool { | 297 | pub fn isValidEncoding(text: []const u8) bool { |
| 289 | if (text.len < 4) return false; | 298 | if (text.len < 4) return false; |
| 290 | var made_crc: u16 = 0; | 299 | var made_crc: u16 = 0; |
| @@ -307,7 +316,7 @@ pub fn isValidEncoding(text: []const u8) bool { | |||
| 307 | } | 316 | } |
| 308 | 317 | ||
| 309 | pub fn isValidSeed(text: *const text_seed) bool { | 318 | pub fn isValidSeed(text: *const text_seed) bool { |
| 310 | var res = decodeSeed(text) catch return false; | 319 | var res = SeedKeyPair.fromTextSeed(text) catch return false; |
| 311 | res.wipe(); | 320 | res.wipe(); |
| 312 | return true; | 321 | return true; |
| 313 | } | 322 | } |
| @@ -400,7 +409,7 @@ pub fn parseDecoratedNkey(contents: []const u8) NoNkeySeedFoundError!SeedKeyPair | |||
| 400 | 409 | ||
| 401 | pub fn parseDecoratedUserNkey(contents: []const u8) (NoNkeySeedFoundError || NoNkeyUserSeedFoundError)!SeedKeyPair { | 410 | pub fn parseDecoratedUserNkey(contents: []const u8) (NoNkeySeedFoundError || NoNkeyUserSeedFoundError)!SeedKeyPair { |
| 402 | var key = try parseDecoratedNkey(contents); | 411 | var key = try parseDecoratedNkey(contents); |
| 403 | if (!mem.startsWith(u8, &key.seed, "SU")) return error.NoNkeyUserSeedFound; | 412 | if (!mem.startsWith(u8, &key.asTextSeed(), "SU")) return error.NoNkeyUserSeedFound; |
| 404 | defer key.wipe(); | 413 | defer key.wipe(); |
| 405 | return key; | 414 | return key; |
| 406 | } | 415 | } |
| @@ -409,36 +418,49 @@ test { | |||
| 409 | testing.refAllDecls(@This()); | 418 | testing.refAllDecls(@This()); |
| 410 | testing.refAllDecls(SeedKeyPair); | 419 | testing.refAllDecls(SeedKeyPair); |
| 411 | testing.refAllDecls(PublicKey); | 420 | testing.refAllDecls(PublicKey); |
| 421 | testing.refAllDecls(PrivateKey); | ||
| 412 | } | 422 | } |
| 413 | 423 | ||
| 414 | test { | 424 | test { |
| 415 | var key_pair = SeedKeyPair.generate(PublicPrefixByte.server); | 425 | var key_pair = try SeedKeyPair.generate(PublicPrefixByte.server); |
| 416 | defer key_pair.wipe(); | 426 | defer key_pair.wipe(); |
| 417 | 427 | ||
| 418 | var decoded_seed = try decodeSeed(&key_pair.seed); | 428 | var decoded_seed = try SeedKeyPair.fromTextSeed(&key_pair.asTextSeed()); |
| 419 | defer decoded_seed.wipe(); | 429 | defer decoded_seed.wipe(); |
| 420 | var encoded_second_time = encodeSeed(decoded_seed.prefix, &decoded_seed.seed); | 430 | try testing.expect(isValidEncoding(&decoded_seed.asTextSeed())); |
| 421 | defer wipeBytes(&encoded_second_time); | ||
| 422 | try testing.expectEqualSlices(u8, &key_pair.seed, &encoded_second_time); | ||
| 423 | try testing.expect(isValidEncoding(&key_pair.seed)); | ||
| 424 | 431 | ||
| 425 | var pub_key_str_a = try key_pair.publicKey(); | 432 | var pub_key_str_a = key_pair.asTextPublicKey(); |
| 426 | defer wipeBytes(&pub_key_str_a); | 433 | defer wipeBytes(&pub_key_str_a); |
| 427 | var priv_key_str = try key_pair.privateKey(); | 434 | var priv_key_str = key_pair.asTextPrivateKey(); |
| 428 | defer wipeBytes(&priv_key_str); | 435 | defer wipeBytes(&priv_key_str); |
| 429 | try testing.expect(pub_key_str_a.len != 0); | 436 | try testing.expect(pub_key_str_a.len != 0); |
| 430 | try testing.expect(priv_key_str.len != 0); | 437 | try testing.expect(priv_key_str.len != 0); |
| 431 | try testing.expect(isValidEncoding(&pub_key_str_a)); | 438 | try testing.expect(isValidEncoding(&pub_key_str_a)); |
| 432 | try testing.expect(isValidEncoding(&priv_key_str)); | 439 | try testing.expect(isValidEncoding(&priv_key_str)); |
| 433 | 440 | ||
| 434 | var pub_key = try key_pair.intoPublicKey(); | 441 | var pub_key = key_pair.asPublicKey(); |
| 435 | defer pub_key.wipe(); | 442 | defer pub_key.wipe(); |
| 436 | var pub_key_str_b = pub_key.publicKey(); | 443 | var pub_key_str_b = pub_key.asTextPublicKey(); |
| 437 | defer wipeBytes(&pub_key_str_b); | 444 | defer wipeBytes(&pub_key_str_b); |
| 438 | try testing.expectEqualSlices(u8, &pub_key_str_a, &pub_key_str_b); | 445 | try testing.expectEqualSlices(u8, &pub_key_str_a, &pub_key_str_b); |
| 439 | } | 446 | } |
| 440 | 447 | ||
| 441 | test { | 448 | test "encode" { |
| 449 | var raw_key: [32]u8 = undefined; | ||
| 450 | crypto.random.bytes(&raw_key); | ||
| 451 | |||
| 452 | // encode( | ||
| 453 | } | ||
| 454 | |||
| 455 | test "parse decorated JWT (bad)" { | ||
| 456 | try testing.expectEqualStrings("foo", parseDecoratedJwt("foo")); | ||
| 457 | } | ||
| 458 | |||
| 459 | test "parse decorated seed (bad)" { | ||
| 460 | try testing.expectError(error.NoNkeySeedFound, parseDecoratedNkey("foo")); | ||
| 461 | } | ||
| 462 | |||
| 463 | test "parse decorated seed and JWT" { | ||
| 442 | const creds = | 464 | const creds = |
| 443 | \\-----BEGIN NATS USER JWT----- | 465 | \\-----BEGIN NATS USER JWT----- |
| 444 | \\eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJUWEg1TUxDNTdPTUJUQURYNUJNU0RLWkhSQUtXUFM0TkdHRFFPVlJXRzUyRFdaUlFFVERBIiwiaWF0IjoxNjIxNTgyOTU1LCJpc3MiOiJBQ1ZUQVZMQlFKTklQRjdNWFZWSlpZUFhaTkdFQUZMWVpTUjJSNVRZNk9ESjNSTTRYV0FDNUVFRiIsIm5hbWUiOiJ0ZXN0Iiwic3ViIjoiVUJHSlhLRkVWUlFEM05LM0lDRVc1Q0lDSzM1NkdESVZORkhaRUU0SzdMMkRYWTdORVNQVlFVNEwiLCJuYXRzIjp7InB1YiI6e30sInN1YiI6e30sInN1YnMiOi0xLCJkYXRhIjotMSwicGF5bG9hZCI6LTEsInR5cGUiOiJ1c2VyIiwidmVyc2lvbiI6Mn19.OhPLDZflyJ_keg2xBRDHZZhG5x_Qf_Yb61k9eHLs9zLRf0_ETwMd0PNZI_isuBhXYevobXHVoYA3oxvMVGlDCQ | 466 | \\eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJUWEg1TUxDNTdPTUJUQURYNUJNU0RLWkhSQUtXUFM0TkdHRFFPVlJXRzUyRFdaUlFFVERBIiwiaWF0IjoxNjIxNTgyOTU1LCJpc3MiOiJBQ1ZUQVZMQlFKTklQRjdNWFZWSlpZUFhaTkdFQUZMWVpTUjJSNVRZNk9ESjNSTTRYV0FDNUVFRiIsIm5hbWUiOiJ0ZXN0Iiwic3ViIjoiVUJHSlhLRkVWUlFEM05LM0lDRVc1Q0lDSzM1NkdESVZORkhaRUU0SzdMMkRYWTdORVNQVlFVNEwiLCJuYXRzIjp7InB1YiI6e30sInN1YiI6e30sInN1YnMiOi0xLCJkYXRhIjotMSwicGF5bG9hZCI6LTEsInR5cGUiOiJ1c2VyIiwidmVyc2lvbiI6Mn19.OhPLDZflyJ_keg2xBRDHZZhG5x_Qf_Yb61k9eHLs9zLRf0_ETwMd0PNZI_isuBhXYevobXHVoYA3oxvMVGlDCQ |
| @@ -458,7 +480,10 @@ test { | |||
| 458 | const seed = "SUAGIEYODKBBTUMOB666Z5KA4FCWAZV7HWSGRHOD7MK6UM5IYLWLACH7DQ"; | 480 | const seed = "SUAGIEYODKBBTUMOB666Z5KA4FCWAZV7HWSGRHOD7MK6UM5IYLWLACH7DQ"; |
| 459 | 481 | ||
| 460 | var got_kp = try parseDecoratedUserNkey(creds); | 482 | var got_kp = try parseDecoratedUserNkey(creds); |
| 461 | try testing.expectEqualStrings(seed, &got_kp.seed); | 483 | try testing.expectEqualStrings(seed, &got_kp.asTextSeed()); |
| 484 | |||
| 485 | got_kp = try parseDecoratedNkey(creds); | ||
| 486 | try testing.expectEqualStrings(seed, &got_kp.asTextSeed()); | ||
| 462 | 487 | ||
| 463 | var got_jwt = parseDecoratedJwt(creds); | 488 | var got_jwt = parseDecoratedJwt(creds); |
| 464 | try testing.expectEqualStrings(jwt, got_jwt); | 489 | try testing.expectEqualStrings(jwt, got_jwt); |
diff --git a/src/znk.zig b/src/znk.zig index 5a5ab5e..7cb16c6 100644 --- a/src/znk.zig +++ b/src/znk.zig | |||
| @@ -147,12 +147,12 @@ pub fn cmdGen(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !voi | |||
| 147 | 147 | ||
| 148 | try PrefixKeyGenerator.init(arena, ty.?, capitalized_prefix).generate(); | 148 | try PrefixKeyGenerator.init(arena, ty.?, capitalized_prefix).generate(); |
| 149 | } else { | 149 | } else { |
| 150 | var kp = nkeys.SeedKeyPair.generate(ty.?); | 150 | var kp = try nkeys.SeedKeyPair.generate(ty.?); |
| 151 | defer kp.wipe(); | 151 | defer kp.wipe(); |
| 152 | try stdout.writeAll(&kp.seed); | 152 | try stdout.writeAll(&kp.asTextSeed()); |
| 153 | try stdout.writeAll("\n"); | 153 | try stdout.writeAll("\n"); |
| 154 | 154 | ||
| 155 | var public_key = kp.publicKey() catch |e| fatal("could not generate public key: {e}", .{e}); | 155 | var public_key = kp.asTextPublicKey(); |
| 156 | if (pub_out) { | 156 | if (pub_out) { |
| 157 | try stdout.writeAll(&public_key); | 157 | try stdout.writeAll(&public_key); |
| 158 | try stdout.writeAll("\n"); | 158 | try stdout.writeAll("\n"); |
| @@ -377,18 +377,18 @@ const PrefixKeyGenerator = struct { | |||
| 377 | }; | 377 | }; |
| 378 | } | 378 | } |
| 379 | 379 | ||
| 380 | fn generatePrivate(self: *Self) void { | 380 | fn generatePrivate(self: *Self) !void { |
| 381 | while (true) { | 381 | while (true) { |
| 382 | if (self.done.load(.SeqCst)) return; | 382 | if (self.done.load(.SeqCst)) return; |
| 383 | 383 | ||
| 384 | var kp = nkeys.SeedKeyPair.generate(self.ty); | 384 | var kp = try nkeys.SeedKeyPair.generate(self.ty); |
| 385 | defer kp.wipe(); | 385 | defer kp.wipe(); |
| 386 | var public_key = kp.publicKey() catch |e| fatal("could not generate public key: {e}", .{e}); | 386 | var public_key = kp.asTextPublicKey(); |
| 387 | if (!mem.startsWith(u8, public_key[1..], self.prefix)) continue; | 387 | if (!mem.startsWith(u8, public_key[1..], self.prefix)) continue; |
| 388 | 388 | ||
| 389 | if (self.done.xchg(true, .SeqCst)) return; // another thread is already done | 389 | if (self.done.xchg(true, .SeqCst)) return; // another thread is already done |
| 390 | 390 | ||
| 391 | info("{s}", .{kp.seed}); | 391 | info("{s}", .{kp.asTextSeed()}); |
| 392 | info("{s}", .{public_key}); | 392 | info("{s}", .{public_key}); |
| 393 | 393 | ||
| 394 | return; | 394 | return; |
| @@ -432,16 +432,16 @@ pub const Nkey = union(enum) { | |||
| 432 | 432 | ||
| 433 | const Self = @This(); | 433 | const Self = @This(); |
| 434 | 434 | ||
| 435 | pub fn publicKey(self: *const Self) !nkeys.text_public { | 435 | pub fn publicKey(self: *const Self) nkeys.text_public { |
| 436 | return switch (self.*) { | 436 | return switch (self.*) { |
| 437 | .seed_key_pair => |*kp| try kp.publicKey(), | 437 | .seed_key_pair => |*kp| kp.asTextPublicKey(), |
| 438 | .public_key => |*pk| pk.publicKey(), | 438 | .public_key => |*pk| pk.asTextPublicKey(), |
| 439 | }; | 439 | }; |
| 440 | } | 440 | } |
| 441 | 441 | ||
| 442 | pub fn intoPublicKey(self: *const Self) !nkeys.PublicKey { | 442 | pub fn intoPublicKey(self: *const Self) nkeys.PublicKey { |
| 443 | return switch (self.*) { | 443 | return switch (self.*) { |
| 444 | .seed_key_pair => |*kp| try kp.intoPublicKey(), | 444 | .seed_key_pair => |*kp| kp.asPublicKey(), |
| 445 | .public_key => |pk| pk, | 445 | .public_key => |pk| pk, |
| 446 | }; | 446 | }; |
| 447 | } | 447 | } |