diff options
| author | Rutger Broekhoff | 2022-10-31 23:54:06 +0100 | 
|---|---|---|
| committer | Rutger Broekhoff | 2022-10-31 23:54:06 +0100 | 
| commit | 523fff3ca4f50d859bdddd27c8343933a46bc926 (patch) | |
| tree | 9cbce064ad2f37f4cbf297fb950622c64564529e /src | |
| parent | 885a21d0f3226d1acc93aa16a6fe8ea37235c7e0 (diff) | |
| download | zig-nkeys-523fff3ca4f50d859bdddd27c8343933a46bc926.tar.gz zig-nkeys-523fff3ca4f50d859bdddd27c8343933a46bc926.zip | |
Make compatible with Zig 0.10.0
This does not build on unpatched Zig 0.10.0, as there is still an issue
with the revamped Ed25519 implementation in the standard library. I will
report an issue which should make this program buildable. There is still
some cleanup work to perform.
Diffstat (limited to 'src')
| -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 | ||