diff options
Diffstat (limited to 'src/main.zig')
-rw-r--r-- | src/main.zig | 85 |
1 files changed, 44 insertions, 41 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}; | |||
14 | pub const InvalidSignatureError = error{InvalidSignature}; | 14 | pub const InvalidSignatureError = error{InvalidSignature}; |
15 | pub const NoNkeySeedFoundError = error{NoNkeySeedFound}; | 15 | pub const NoNkeySeedFoundError = error{NoNkeySeedFound}; |
16 | pub const NoNkeyUserSeedFoundError = error{NoNkeyUserSeedFound}; | 16 | pub const NoNkeyUserSeedFoundError = error{NoNkeyUserSeedFound}; |
17 | pub const DecodeError = InvalidPrefixByteError || base32.DecodeError || crc16.InvalidChecksumError; | 17 | pub const DecodeError = InvalidPrefixByteError || base32.DecodeError || crc16.InvalidChecksumError || crypto.errors.NonCanonicalError; |
18 | pub const SeedDecodeError = DecodeError || InvalidSeedError || crypto.errors.IdentityElementError; | 18 | pub const SeedDecodeError = DecodeError || InvalidSeedError || crypto.errors.IdentityElementError; |
19 | pub const PrivateKeyDecodeError = DecodeError || InvalidPrivateKeyError || crypto.errors.IdentityElementError; | 19 | pub const PrivateKeyDecodeError = DecodeError || InvalidPrivateKeyError || crypto.errors.EncodingError; |
20 | pub const SignError = crypto.errors.IdentityElementError || crypto.errors.WeakPublicKeyError || crypto.errors.KeyMismatchError; | 20 | pub const SignError = crypto.errors.IdentityElementError || crypto.errors.NonCanonicalError || crypto.errors.KeyMismatchError || crypto.errors.WeakPublicKeyError; |
21 | 21 | ||
22 | pub const prefix_byte_account = 0; // A | 22 | pub const prefix_byte_account = 0; // A |
23 | pub const prefix_byte_cluster = 2 << 3; // C | 23 | pub 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 |
92 | const binary_private_size = 1 + Ed25519.secret_length + 2; | 92 | const binary_private_size = 1 + Ed25519.SecretKey.encoded_length + 2; |
93 | // One prefix byte, two CRC bytes | 93 | // One prefix byte, two CRC bytes |
94 | const binary_public_size = 1 + Ed25519.public_length + 2; | 94 | const binary_public_size = 1 + Ed25519.PublicKey.encoded_length + 2; |
95 | // Two prefix bytes, two CRC bytes | 95 | // Two prefix bytes, two CRC bytes |
96 | const binary_seed_size = 2 + Ed25519.seed_length + 2; | 96 | const binary_seed_size = 2 + Ed25519.KeyPair.seed_length + 2; |
97 | 97 | ||
98 | pub const text_private_len = base32.Encoder.calcSize(binary_private_size); | 98 | pub const text_private_len = base32.Encoder.calcSize(binary_private_size); |
99 | pub const text_public_len = base32.Encoder.calcSize(binary_public_size); | 99 | pub 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 | ||
454 | fn wipeKeyPair(kp: *Ed25519.KeyPair) void { | 457 | fn 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 | ||
459 | fn wipeBytes(bs: []u8) void { | 462 | fn 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 | ||
588 | test "from seed" { | 591 | test "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 | ||