aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main.zig85
-rw-r--r--tool/znk.zig20
2 files changed, 55 insertions, 50 deletions
diff --git a/src/main.zig b/src/main.zig
index 3206f45..6c6e7c8 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -14,10 +14,10 @@ pub const InvalidSeedError = error{InvalidSeed};
14pub const InvalidSignatureError = error{InvalidSignature}; 14pub const InvalidSignatureError = error{InvalidSignature};
15pub const NoNkeySeedFoundError = error{NoNkeySeedFound}; 15pub const NoNkeySeedFoundError = error{NoNkeySeedFound};
16pub const NoNkeyUserSeedFoundError = error{NoNkeyUserSeedFound}; 16pub const NoNkeyUserSeedFoundError = error{NoNkeyUserSeedFound};
17pub const DecodeError = InvalidPrefixByteError || base32.DecodeError || crc16.InvalidChecksumError; 17pub const DecodeError = InvalidPrefixByteError || base32.DecodeError || crc16.InvalidChecksumError || crypto.errors.NonCanonicalError;
18pub const SeedDecodeError = DecodeError || InvalidSeedError || crypto.errors.IdentityElementError; 18pub const SeedDecodeError = DecodeError || InvalidSeedError || crypto.errors.IdentityElementError;
19pub const PrivateKeyDecodeError = DecodeError || InvalidPrivateKeyError || crypto.errors.IdentityElementError; 19pub const PrivateKeyDecodeError = DecodeError || InvalidPrivateKeyError || crypto.errors.EncodingError;
20pub const SignError = crypto.errors.IdentityElementError || crypto.errors.WeakPublicKeyError || crypto.errors.KeyMismatchError; 20pub const SignError = crypto.errors.IdentityElementError || crypto.errors.NonCanonicalError || crypto.errors.KeyMismatchError || crypto.errors.WeakPublicKeyError;
21 21
22pub const prefix_byte_account = 0; // A 22pub const prefix_byte_account = 0; // A
23pub const prefix_byte_cluster = 2 << 3; // C 23pub const prefix_byte_cluster = 2 << 3; // C
@@ -89,11 +89,11 @@ pub const Role = enum(u8) {
89}; 89};
90 90
91// One prefix byte, two CRC bytes 91// One prefix byte, two CRC bytes
92const binary_private_size = 1 + Ed25519.secret_length + 2; 92const binary_private_size = 1 + Ed25519.SecretKey.encoded_length + 2;
93// One prefix byte, two CRC bytes 93// One prefix byte, two CRC bytes
94const binary_public_size = 1 + Ed25519.public_length + 2; 94const binary_public_size = 1 + Ed25519.PublicKey.encoded_length + 2;
95// Two prefix bytes, two CRC bytes 95// Two prefix bytes, two CRC bytes
96const binary_seed_size = 2 + Ed25519.seed_length + 2; 96const binary_seed_size = 2 + Ed25519.KeyPair.seed_length + 2;
97 97
98pub const text_private_len = base32.Encoder.calcSize(binary_private_size); 98pub const text_private_len = base32.Encoder.calcSize(binary_private_size);
99pub const text_public_len = base32.Encoder.calcSize(binary_public_size); 99pub const text_public_len = base32.Encoder.calcSize(binary_public_size);
@@ -110,21 +110,21 @@ pub const SeedKeyPair = struct {
110 kp: Ed25519.KeyPair, 110 kp: Ed25519.KeyPair,
111 111
112 pub fn generate(role: Role) crypto.errors.IdentityElementError!Self { 112 pub fn generate(role: Role) crypto.errors.IdentityElementError!Self {
113 var raw_seed: [Ed25519.seed_length]u8 = undefined; 113 var raw_seed: [Ed25519.KeyPair.seed_length]u8 = undefined;
114 crypto.random.bytes(&raw_seed); 114 crypto.random.bytes(&raw_seed);
115 defer wipeBytes(&raw_seed); 115 defer wipeBytes(&raw_seed);
116 return Self{ .role = role, .kp = try Ed25519.KeyPair.create(raw_seed) }; 116 return Self{ .role = role, .kp = try Ed25519.KeyPair.create(raw_seed) };
117 } 117 }
118 118
119 pub fn generateWithCustomEntropy(role: Role, reader: anytype) !Self { 119 pub fn generateWithCustomEntropy(role: Role, reader: anytype) !Self {
120 var raw_seed: [Ed25519.seed_length]u8 = undefined; 120 var raw_seed: [Ed25519.KeyPair.seed_length]u8 = undefined;
121 try reader.readNoEof(&raw_seed); 121 try reader.readNoEof(&raw_seed);
122 defer wipeBytes(&raw_seed); 122 defer wipeBytes(&raw_seed);
123 return Self{ .role = role, .kp = try Ed25519.KeyPair.create(raw_seed) }; 123 return Self{ .role = role, .kp = try Ed25519.KeyPair.create(raw_seed) };
124 } 124 }
125 125
126 pub fn fromTextSeed(text: *const text_seed) SeedDecodeError!Self { 126 pub fn fromTextSeed(text: *const text_seed) SeedDecodeError!Self {
127 var decoded = try decode(2, Ed25519.seed_length, text); 127 var decoded = try decode(2, Ed25519.KeyPair.seed_length, text);
128 defer decoded.wipe(); // gets copied 128 defer decoded.wipe(); // gets copied
129 129
130 var key_ty_prefix = decoded.prefix[0] & 0b11111000; 130 var key_ty_prefix = decoded.prefix[0] & 0b11111000;
@@ -141,17 +141,17 @@ pub const SeedKeyPair = struct {
141 141
142 pub fn fromRawSeed( 142 pub fn fromRawSeed(
143 role: Role, 143 role: Role,
144 raw_seed: *const [Ed25519.seed_length]u8, 144 raw_seed: *const [Ed25519.KeyPair.seed_length]u8,
145 ) crypto.errors.IdentityElementError!Self { 145 ) crypto.errors.IdentityElementError!Self {
146 return Self{ .role = role, .kp = try Ed25519.KeyPair.create(raw_seed.*) }; 146 return Self{ .role = role, .kp = try Ed25519.KeyPair.create(raw_seed.*) };
147 } 147 }
148 148
149 pub fn sign(self: *const Self, msg: []const u8) SignError![Ed25519.signature_length]u8 { 149 pub fn sign(self: *const Self, msg: []const u8) SignError!Ed25519.Signature {
150 return Ed25519.sign(msg, self.kp, null); 150 return self.kp.sign(msg, null);
151 } 151 }
152 152
153 pub fn verify(self: *const Self, msg: []const u8, sig: [Ed25519.signature_length]u8) InvalidSignatureError!void { 153 pub fn verify(self: *const Self, msg: []const u8, sig: Ed25519.Signature) InvalidSignatureError!void {
154 Ed25519.verify(sig, msg, self.kp.public_key) catch return error.InvalidSignature; 154 sig.verify(msg, self.kp.public_key) catch return error.InvalidSignature;
155 } 155 }
156 156
157 pub fn seedText(self: *const Self) text_seed { 157 pub fn seedText(self: *const Self) text_seed {
@@ -160,16 +160,16 @@ pub const SeedKeyPair = struct {
160 prefix_byte_seed | (public_prefix >> 5), 160 prefix_byte_seed | (public_prefix >> 5),
161 (public_prefix & 0b00011111) << 3, 161 (public_prefix & 0b00011111) << 3,
162 }; 162 };
163 const seed = self.kp.secret_key[0..Ed25519.seed_length]; 163 const seed = self.kp.secret_key.seed();
164 return encode(full_prefix.len, seed.len, full_prefix, seed); 164 return encode(full_prefix.len, seed.len, full_prefix, &seed);
165 } 165 }
166 166
167 pub fn privateKeyText(self: *const Self) text_private { 167 pub fn privateKeyText(self: *const Self) text_private {
168 return encode(1, self.kp.secret_key.len, &.{prefix_byte_private}, &self.kp.secret_key); 168 return encode(1, Ed25519.SecretKey.encoded_length, &.{prefix_byte_private}, &self.kp.secret_key.toBytes());
169 } 169 }
170 170
171 pub fn publicKeyText(self: *const Self) text_public { 171 pub fn publicKeyText(self: *const Self) text_public {
172 return encode(1, self.kp.public_key.len, &.{self.role.publicPrefixByte()}, &self.kp.public_key); 172 return encode(1, Ed25519.PublicKey.encoded_length, &.{self.role.publicPrefixByte()}, &self.kp.public_key.toBytes());
173 } 173 }
174 174
175 pub fn intoPublicKey(self: *const Self) PublicKey { 175 pub fn intoPublicKey(self: *const Self) PublicKey {
@@ -193,32 +193,33 @@ pub const PublicKey = struct {
193 const Self = @This(); 193 const Self = @This();
194 194
195 role: Role, 195 role: Role,
196 key: [Ed25519.public_length]u8, 196 key: Ed25519.PublicKey,
197 197
198 pub fn fromTextPublicKey(text: *const text_public) DecodeError!Self { 198 pub fn fromTextPublicKey(text: *const text_public) DecodeError!Self {
199 var decoded = try decode(1, Ed25519.public_length, text); 199 var decoded = try decode(1, Ed25519.PublicKey.encoded_length, text);
200 defer decoded.wipe(); // gets copied 200 defer decoded.wipe(); // gets copied
201 return PublicKey{ 201 return PublicKey{
202 .role = Role.fromPublicPrefixByte(decoded.prefix[0]) orelse return error.InvalidPrefixByte, 202 .role = Role.fromPublicPrefixByte(decoded.prefix[0]) orelse return error.InvalidPrefixByte,
203 .key = decoded.data, 203 .key = try Ed25519.PublicKey.fromBytes(decoded.data),
204 }; 204 };
205 } 205 }
206 206
207 pub fn fromRawPublicKey(role: Role, raw_key: *const [Ed25519.public_length]u8) Self { 207 pub fn fromRawPublicKey(role: Role, raw_key: *const Ed25519.PublicKey) Self {
208 return .{ .role = role, .key = raw_key.* }; 208 return .{ .role = role, .key = raw_key.* };
209 } 209 }
210 210
211 pub fn publicKeyText(self: *const Self) text_public { 211 pub fn publicKeyText(self: *const Self) text_public {
212 return encode(1, self.key.len, &.{self.role.publicPrefixByte()}, &self.key); 212 return encode(1, Ed25519.PublicKey.encoded_length, &.{self.role.publicPrefixByte()}, &self.key.toBytes());
213 } 213 }
214 214
215 pub fn verify(self: *const Self, msg: []const u8, sig: [Ed25519.signature_length]u8) InvalidSignatureError!void { 215 pub fn verify(self: *const Self, msg: []const u8, sig: Ed25519.Signature) InvalidSignatureError!void {
216 Ed25519.verify(sig, msg, self.key) catch return error.InvalidSignature; 216 // TODO: maybe propagate errors better herer
217 sig.verify(msg, self.key) catch return error.InvalidSignature;
217 } 218 }
218 219
219 pub fn wipe(self: *Self) void { 220 pub fn wipe(self: *Self) void {
220 self.role = .account; 221 self.role = .account;
221 wipeBytes(&self.key); 222 wipeBytes(&self.key.bytes);
222 } 223 }
223}; 224};
224 225
@@ -228,15 +229,17 @@ pub const PrivateKey = struct {
228 kp: Ed25519.KeyPair, 229 kp: Ed25519.KeyPair,
229 230
230 pub fn fromTextPrivateKey(text: *const text_private) PrivateKeyDecodeError!Self { 231 pub fn fromTextPrivateKey(text: *const text_private) PrivateKeyDecodeError!Self {
231 var decoded = try decode(1, Ed25519.secret_length, text); 232 var decoded = try decode(1, Ed25519.SecretKey.encoded_length, text);
232 defer decoded.wipe(); // gets copied 233 defer decoded.wipe(); // gets copied
233 if (decoded.prefix[0] != prefix_byte_private) 234 if (decoded.prefix[0] != prefix_byte_private)
234 return error.InvalidPrivateKey; 235 return error.InvalidPrivateKey;
235 return Self{ .kp = Ed25519.KeyPair.fromSecretKey(decoded.data) }; 236
237 var secret_key = Ed25519.SecretKey.fromBytes(decoded.data) catch unreachable;
238 return Self{ .kp = try Ed25519.KeyPair.fromSecretKey(secret_key) };
236 } 239 }
237 240
238 pub fn fromRawPrivateKey(raw_key: *const [Ed25519.secret_length]u8) Self { 241 pub fn fromRawPrivateKey(raw_key: *const Ed25519.SecretKey) InvalidEncodingError!Self {
239 return .{ .kp = Ed25519.KeyPair.fromSecretKey(raw_key.*) }; 242 return .{ .kp = try Ed25519.KeyPair.fromSecretKey(raw_key.*) };
240 } 243 }
241 244
242 pub fn intoSeedKeyPair(self: *const Self, role: Role) SeedKeyPair { 245 pub fn intoSeedKeyPair(self: *const Self, role: Role) SeedKeyPair {
@@ -254,15 +257,15 @@ pub const PrivateKey = struct {
254 } 257 }
255 258
256 pub fn privateKeyText(self: *const Self) text_private { 259 pub fn privateKeyText(self: *const Self) text_private {
257 return encode(1, self.kp.secret_key.len, &.{prefix_byte_private}, &self.kp.secret_key); 260 return encode(1, Ed25519.SecretKey.encoded_length, &.{prefix_byte_private}, &self.kp.secret_key.toBytes());
258 } 261 }
259 262
260 pub fn sign(self: *const Self, msg: []const u8) SignError![Ed25519.signature_length]u8 { 263 pub fn sign(self: *const Self, msg: []const u8) SignError!Ed25519.Signature {
261 return Ed25519.sign(msg, self.kp, null); 264 return self.kp.sign(msg, null);
262 } 265 }
263 266
264 pub fn verify(self: *const Self, msg: []const u8, sig: [Ed25519.signature_length]u8) InvalidSignatureError!void { 267 pub fn verify(self: *const Self, msg: []const u8, sig: Ed25519.Signature) InvalidSignatureError!void {
265 Ed25519.verify(sig, msg, self.kp.public_key) catch return error.InvalidSignature; 268 sig.verify(msg, self.kp.public_key) catch return error.InvalidSignature;
266 } 269 }
267 270
268 pub fn wipe(self: *Self) void { 271 pub fn wipe(self: *Self) void {
@@ -452,8 +455,8 @@ fn findNkey(text: []const u8) ?[]const u8 {
452} 455}
453 456
454fn wipeKeyPair(kp: *Ed25519.KeyPair) void { 457fn wipeKeyPair(kp: *Ed25519.KeyPair) void {
455 wipeBytes(&kp.public_key); 458 wipeBytes(&kp.public_key.bytes);
456 wipeBytes(&kp.secret_key); 459 wipeBytes(&kp.secret_key.bytes);
457} 460}
458 461
459fn wipeBytes(bs: []u8) void { 462fn wipeBytes(bs: []u8) void {
@@ -544,7 +547,7 @@ test "different key types" {
544 547
545 const data = "Hello, world!"; 548 const data = "Hello, world!";
546 const sig = try kp.sign(data); 549 const sig = try kp.sign(data);
547 try testing.expect(sig.len == Ed25519.signature_length); 550 try testing.expect(sig.toBytes().len == Ed25519.Signature.encoded_length);
548 try kp.verify(data, sig); 551 try kp.verify(data, sig);
549 } 552 }
550} 553}
@@ -587,7 +590,7 @@ test "validation" {
587 590
588test "from seed" { 591test "from seed" {
589 const kp = try SeedKeyPair.generate(.account); 592 const kp = try SeedKeyPair.generate(.account);
590 const kp_from_raw = try SeedKeyPair.fromRawSeed(kp.role, kp.kp.secret_key[0..Ed25519.seed_length]); 593 const kp_from_raw = try SeedKeyPair.fromRawSeed(kp.role, &kp.kp.secret_key.seed());
591 try testing.expect(std.meta.eql(kp, kp_from_raw)); 594 try testing.expect(std.meta.eql(kp, kp_from_raw));
592 595
593 const data = "Hello, World!"; 596 const data = "Hello, World!";
@@ -636,14 +639,14 @@ test "from private key" {
636 const pk_text_clone_2 = pk.privateKeyText(); 639 const pk_text_clone_2 = pk.privateKeyText();
637 try testing.expect(std.meta.eql(pk, kp.intoPrivateKey())); 640 try testing.expect(std.meta.eql(pk, kp.intoPrivateKey()));
638 try testing.expect(std.meta.eql(kp, pk.intoSeedKeyPair(.account))); 641 try testing.expect(std.meta.eql(kp, pk.intoSeedKeyPair(.account)));
639 try testing.expect(std.meta.eql(pk, PrivateKey.fromRawPrivateKey(&kp.kp.secret_key))); 642 try testing.expect(std.meta.eql(pk, try PrivateKey.fromRawPrivateKey(&kp.kp.secret_key)));
640 try testing.expectEqualStrings(&pk_text, &pk_text_clone_2); 643 try testing.expectEqualStrings(&pk_text, &pk_text_clone_2);
641 644
642 const data = "Hello, World!"; 645 const data = "Hello, World!";
643 646
644 const sig0 = try kp.sign(data); 647 const sig0 = try kp.sign(data);
645 const sig1 = try pk.sign(data); 648 const sig1 = try pk.sign(data);
646 try testing.expectEqualSlices(u8, &sig0, &sig1); 649 try testing.expectEqualSlices(u8, &sig0.toBytes(), &sig1.toBytes());
647 try pk.verify(data, sig0); 650 try pk.verify(data, sig0);
648 try kp.verify(data, sig1); 651 try kp.verify(data, sig1);
649 652
diff --git a/tool/znk.zig b/tool/znk.zig
index e086879..fcd03ff 100644
--- a/tool/znk.zig
+++ b/tool/znk.zig
@@ -266,8 +266,8 @@ pub fn cmdSign(arena: Allocator, args: []const []const u8) !void {
266 defer nkey.wipe(); 266 defer nkey.wipe();
267 267
268 const sig = nkey.sign(content) catch fatal("could not generate signature", .{}); 268 const sig = nkey.sign(content) catch fatal("could not generate signature", .{});
269 var encoded_sig = try arena.alloc(u8, std.base64.standard.Encoder.calcSize(sig.len)); 269 var encoded_sig = try arena.alloc(u8, std.base64.standard.Encoder.calcSize(std.crypto.sign.Ed25519.Signature.encoded_length));
270 _ = std.base64.standard.Encoder.encode(encoded_sig, &sig); 270 _ = std.base64.standard.Encoder.encode(encoded_sig, &sig.toBytes());
271 try stdout.writeAll(encoded_sig); 271 try stdout.writeAll(encoded_sig);
272 try stdout.writeAll("\n"); 272 try stdout.writeAll("\n");
273} 273}
@@ -377,14 +377,16 @@ pub fn cmdVerify(arena: Allocator, args: []const []const u8) !void {
377 const decoded_len = std.base64.standard.Decoder.calcSizeForSlice(trimmed_signature_b64) catch { 377 const decoded_len = std.base64.standard.Decoder.calcSizeForSlice(trimmed_signature_b64) catch {
378 fatal("invalid signature encoding", .{}); 378 fatal("invalid signature encoding", .{});
379 }; 379 };
380 if (decoded_len != std.crypto.sign.Ed25519.signature_length) 380 if (decoded_len != std.crypto.sign.Ed25519.Signature.encoded_length)
381 fatal("invalid signature length", .{}); 381 fatal("invalid signature length", .{});
382 const signature = try arena.alloc(u8, decoded_len);
383 382
384 _ = std.base64.standard.Decoder.decode(signature, trimmed_signature_b64) catch { 383 var signature_bytes: [std.crypto.sign.Ed25519.Signature.encoded_length]u8 = undefined;
384 _ = std.base64.standard.Decoder.decode(&signature_bytes, trimmed_signature_b64) catch {
385 fatal("invalid signature encoding", .{}); 385 fatal("invalid signature encoding", .{});
386 }; 386 };
387 k.verify(content, signature[0..std.crypto.sign.Ed25519.signature_length].*) catch { 387
388 const signature = std.crypto.sign.Ed25519.Signature.fromBytes(signature_bytes);
389 k.verify(content, signature) catch {
388 fatal("bad signature", .{}); 390 fatal("bad signature", .{});
389 }; 391 };
390 392
@@ -462,7 +464,7 @@ fn PrefixKeyGenerator(comptime EntropyReaderType: type) type {
462 } else struct { 464 } else struct {
463 pub fn generate(self: *Self) !void { 465 pub fn generate(self: *Self) !void {
464 var cpu_count = try std.Thread.getCpuCount(); 466 var cpu_count = try std.Thread.getCpuCount();
465 var threads = try self.allocator.alloc(std.Thread, cpu_count*4); 467 var threads = try self.allocator.alloc(std.Thread, cpu_count * 4);
466 defer self.allocator.free(threads); 468 defer self.allocator.free(threads);
467 for (threads) |*thread| thread.* = try std.Thread.spawn(.{}, Self.generatePrivate, .{self}); 469 for (threads) |*thread| thread.* = try std.Thread.spawn(.{}, Self.generatePrivate, .{self});
468 for (threads) |thread| thread.join(); 470 for (threads) |thread| thread.join();
@@ -495,7 +497,7 @@ pub const Nkey = union(enum) {
495 pub fn verify( 497 pub fn verify(
496 self: *const Self, 498 self: *const Self,
497 msg: []const u8, 499 msg: []const u8,
498 sig: [std.crypto.sign.Ed25519.signature_length]u8, 500 sig: std.crypto.sign.Ed25519.Signature,
499 ) !void { 501 ) !void {
500 return switch (self.*) { 502 return switch (self.*) {
501 .seed_key_pair => |*kp| try kp.verify(msg, sig), 503 .seed_key_pair => |*kp| try kp.verify(msg, sig),
@@ -507,7 +509,7 @@ pub const Nkey = union(enum) {
507 pub fn sign( 509 pub fn sign(
508 self: *const Self, 510 self: *const Self,
509 msg: []const u8, 511 msg: []const u8,
510 ) ![std.crypto.sign.Ed25519.signature_length]u8 { 512 ) !std.crypto.sign.Ed25519.Signature {
511 return switch (self.*) { 513 return switch (self.*) {
512 .seed_key_pair => |*kp| try kp.sign(msg), 514 .seed_key_pair => |*kp| try kp.sign(msg),
513 .private_key => |*pk| try pk.sign(msg), 515 .private_key => |*pk| try pk.sign(msg),