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 /src | |
parent | 862fed7c251b331ac953e8e7ef5cdfe3da7772e8 (diff) | |
download | zig-nkeys-f3232a9022b67db164b901272f4a8e47f46a74b3.tar.gz zig-nkeys-f3232a9022b67db164b901272f4a8e47f46a74b3.zip |
Add PrivateKey, use Ed25519.KeyPair in SeedKeyPair
Diffstat (limited to 'src')
-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 | } |