aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Rutger Broekhoff2021-05-25 15:19:33 +0200
committerLibravatar Rutger Broekhoff2021-05-25 15:19:33 +0200
commit7914eed7b4a37b9c0b0da9aa9481d9666353b45e (patch)
treee8eba0f04ffbb469d04e55eebbe2605928f75bab /src
parent55b14e7fcfa3c340d27107f2b7cdf9836e7c4758 (diff)
downloadzig-nkeys-7914eed7b4a37b9c0b0da9aa9481d9666353b45e.tar.gz
zig-nkeys-7914eed7b4a37b9c0b0da9aa9481d9666353b45e.zip
Write a few more tests
Diffstat (limited to 'src')
-rw-r--r--src/base32.zig4
-rw-r--r--src/nkeys.zig140
2 files changed, 90 insertions, 54 deletions
diff --git a/src/base32.zig b/src/base32.zig
index 749c313..339566d 100644
--- a/src/base32.zig
+++ b/src/base32.zig
@@ -132,7 +132,7 @@ pub const Encoder = struct {
132 } 132 }
133}; 133};
134 134
135pub const DecodeError = error{CorruptInputError}; 135pub const DecodeError = error{CorruptInput};
136 136
137pub const Decoder = struct { 137pub const Decoder = struct {
138 const Self = @This(); 138 const Self = @This();
@@ -178,7 +178,7 @@ pub const Decoder = struct {
178 // '2' -> 26 178 // '2' -> 26
179 return @truncate(u5, c - @as(u8, '2') + 26); 179 return @truncate(u5, c - @as(u8, '2') + 26);
180 } else { 180 } else {
181 return error.CorruptInputError; 181 return error.CorruptInput;
182 } 182 }
183 } 183 }
184 184
diff --git a/src/nkeys.zig b/src/nkeys.zig
index 9645483..ac5ba1b 100644
--- a/src/nkeys.zig
+++ b/src/nkeys.zig
@@ -20,19 +20,37 @@ pub const PrivateKeyDecodeError = DecodeError || InvalidPrivateKeyError || crypt
20pub const SignError = crypto.errors.IdentityElementError || crypto.errors.WeakPublicKeyError || crypto.errors.KeyMismatchError; 20pub const SignError = crypto.errors.IdentityElementError || crypto.errors.WeakPublicKeyError || crypto.errors.KeyMismatchError;
21 21
22pub const KeyTypePrefixByte = enum(u8) { 22pub const KeyTypePrefixByte = enum(u8) {
23 const Self = @This();
24
23 seed = 18 << 3, // S 25 seed = 18 << 3, // S
24 private = 15 << 3, // P 26 private = 15 << 3, // P
25 unknown = 23 << 3, // U 27
28 fn char(self: Self) u8 {
29 switch (self) {
30 .seed => 'S',
31 .private => 'P',
32 }
33 }
34
35 fn fromChar(c: u8) InvalidPrefixByteError!Self {
36 return switch (c) {
37 'S' => .seed,
38 'P' => .private,
39 else => error.InvalidPrefixByte,
40 };
41 }
26}; 42};
27 43
28pub const PublicPrefixByte = enum(u8) { 44pub const PublicPrefixByte = enum(u8) {
45 const Self = @This();
46
29 account = 0, // A 47 account = 0, // A
30 cluster = 2 << 3, // C 48 cluster = 2 << 3, // C
31 operator = 14 << 3, // O 49 operator = 14 << 3, // O
32 server = 13 << 3, // N 50 server = 13 << 3, // N
33 user = 20 << 3, // U 51 user = 20 << 3, // U
34 52
35 fn fromU8(b: u8) error{InvalidPrefixByte}!PublicPrefixByte { 53 fn fromU8(b: u8) InvalidPrefixByteError!PublicPrefixByte {
36 return switch (b) { 54 return switch (b) {
37 @enumToInt(PublicPrefixByte.server) => .server, 55 @enumToInt(PublicPrefixByte.server) => .server,
38 @enumToInt(PublicPrefixByte.cluster) => .cluster, 56 @enumToInt(PublicPrefixByte.cluster) => .cluster,
@@ -42,6 +60,17 @@ pub const PublicPrefixByte = enum(u8) {
42 else => error.InvalidPrefixByte, 60 else => error.InvalidPrefixByte,
43 }; 61 };
44 } 62 }
63
64 fn fromChar(c: u8) InvalidPrefixByteError!Self {
65 return switch (c) {
66 'A' => .account,
67 'C' => .cluster,
68 'O' => .operator,
69 'N' => .server,
70 'U' => .user,
71 else => error.InvalidPrefixByte,
72 };
73 }
45}; 74};
46 75
47pub const SeedKeyPair = struct { 76pub const SeedKeyPair = struct {
@@ -105,11 +134,11 @@ pub const SeedKeyPair = struct {
105 } 134 }
106 135
107 pub fn privateKeyText(self: *const Self) text_private { 136 pub fn privateKeyText(self: *const Self) text_private {
108 return encode(1, self.kp.secret_key.len, &[_]u8{@enumToInt(KeyTypePrefixByte.private)}, &self.kp.secret_key); 137 return encode(1, self.kp.secret_key.len, &.{@enumToInt(KeyTypePrefixByte.private)}, &self.kp.secret_key);
109 } 138 }
110 139
111 pub fn publicKeyText(self: *const Self) text_public { 140 pub fn publicKeyText(self: *const Self) text_public {
112 return encode(1, self.kp.public_key.len, &[_]u8{@enumToInt(self.prefix)}, &self.kp.public_key); 141 return encode(1, self.kp.public_key.len, &.{@enumToInt(self.prefix)}, &self.kp.public_key);
113 } 142 }
114 143
115 pub fn intoPublicKey(self: *const Self) PublicKey { 144 pub fn intoPublicKey(self: *const Self) PublicKey {
@@ -161,7 +190,7 @@ pub const PublicKey = struct {
161 } 190 }
162 191
163 pub fn publicKeyText(self: *const Self) text_public { 192 pub fn publicKeyText(self: *const Self) text_public {
164 return encode(1, self.key.len, &[_]u8{@enumToInt(self.prefix)}, &self.key); 193 return encode(1, self.key.len, &.{@enumToInt(self.prefix)}, &self.key);
165 } 194 }
166 195
167 pub fn verify( 196 pub fn verify(
@@ -210,7 +239,7 @@ pub const PrivateKey = struct {
210 } 239 }
211 240
212 pub fn privateKeyText(self: *const Self) text_private { 241 pub fn privateKeyText(self: *const Self) text_private {
213 return encode(1, self.kp.secret_key.len, &[_]u8{@enumToInt(KeyTypePrefixByte.private)}, &self.kp.secret_key); 242 return encode(1, self.kp.secret_key.len, &.{@enumToInt(KeyTypePrefixByte.private)}, &self.kp.secret_key);
214 } 243 }
215 244
216 pub fn sign( 245 pub fn sign(
@@ -499,54 +528,28 @@ test "public key" {
499 } 528 }
500} 529}
501 530
502test "account" { 531test "different key types" {
503 const kp = try SeedKeyPair.generate(.account); 532 inline for (@typeInfo(PublicPrefixByte).Enum.fields) |field| {
504 _ = try SeedKeyPair.fromTextSeed(&kp.seedText()); 533 const prefix = @field(PublicPrefixByte, field.name);
505
506 const pub_key_str = kp.publicKeyText();
507 try testing.expect(pub_key_str[0] == 'A');
508 try testing.expect(isValidPublicKey(&pub_key_str, .account));
509
510 const priv_key_str = kp.privateKeyText();
511 try testing.expect(priv_key_str[0] == 'P');
512 try testing.expect(isValidPrivateKey(&priv_key_str));
513
514 const data = "Hello, world!";
515 const sig = try kp.sign(data);
516 try testing.expect(sig.len == Ed25519.signature_length);
517 try kp.verify(data, sig);
518}
519
520test "cluster" {
521 const kp = try SeedKeyPair.generate(.cluster);
522
523 const pub_key_str = kp.publicKeyText();
524 try testing.expect(pub_key_str[0] == 'C');
525 try testing.expect(isValidPublicKey(&pub_key_str, .cluster));
526}
527
528test "operator" {
529 const kp = try SeedKeyPair.generate(.operator);
530
531 const pub_key_str = kp.publicKeyText();
532 try testing.expect(pub_key_str[0] == 'O');
533 try testing.expect(isValidPublicKey(&pub_key_str, .operator));
534}
535 534
536test "server" { 535 const kp = try SeedKeyPair.generate(prefix);
537 const kp = try SeedKeyPair.generate(.server); 536 _ = try SeedKeyPair.fromTextSeed(&kp.seedText());
538 537
539 const pub_key_str = kp.publicKeyText(); 538 const pub_key_str = kp.publicKeyText();
540 try testing.expect(pub_key_str[0] == 'N'); 539 const got_pub_key_prefix = try PublicPrefixByte.fromChar(pub_key_str[0]);
541 try testing.expect(isValidPublicKey(&pub_key_str, .server)); 540 try testing.expect(got_pub_key_prefix == prefix);
542} 541 try testing.expect(isValidPublicKey(&pub_key_str, prefix));
543 542
544test "user" { 543 const priv_key_str = kp.privateKeyText();
545 const kp = try SeedKeyPair.generate(.user); 544 const got_priv_key_prefix = try KeyTypePrefixByte.fromChar(priv_key_str[0]);
545 try testing.expect(got_priv_key_prefix == .private);
546 try testing.expect(isValidPrivateKey(&priv_key_str));
546 547
547 const pub_key_str = kp.publicKeyText(); 548 const data = "Hello, world!";
548 try testing.expect(pub_key_str[0] == 'U'); 549 const sig = try kp.sign(data);
549 try testing.expect(isValidPublicKey(&pub_key_str, .user)); 550 try testing.expect(sig.len == Ed25519.signature_length);
551 try kp.verify(data, sig);
552 }
550} 553}
551 554
552test "validation" { 555test "validation" {
@@ -600,7 +603,6 @@ test "from seed" {
600 try kp2.verify(data, sig); 603 try kp2.verify(data, sig);
601} 604}
602 605
603// TODO(rutgerbrf): give test a better name
604test "from public key" { 606test "from public key" {
605 const kp = try SeedKeyPair.generate(.user); 607 const kp = try SeedKeyPair.generate(.user);
606 608
@@ -654,7 +656,41 @@ test "from private key" {
654 try testing.expectError(error.InvalidSignature, pk.verify(data, sig2)); 656 try testing.expectError(error.InvalidSignature, pk.verify(data, sig2));
655} 657}
656 658
657// TODO(rutgerbrf): bad decode, wipe, sign, (public/private/seed) verify 659test "bad decode" {
660 const kp = try SeedKeyPair.fromTextSeed("SAAHPQF3GOP4IP5SHKHCNBOHD5TMGSW4QQL6RTZAPEEYOQ2NRBIAKCCLQA");
661
662 var bad_seed = kp.seedText();
663 bad_seed[1] = 'S';
664 try testing.expectError(error.InvalidChecksum, SeedKeyPair.fromTextSeed(&bad_seed));
665
666 var bad_pub_key = kp.publicKeyText();
667 bad_pub_key[bad_pub_key.len - 1] = 'O';
668 bad_pub_key[bad_pub_key.len - 2] = 'O';
669 try testing.expectError(error.InvalidChecksum, PublicKey.fromTextPublicKey(&bad_pub_key));
670
671 var bad_priv_key = kp.privateKeyText();
672 bad_priv_key[bad_priv_key.len - 1] = 'O';
673 bad_priv_key[bad_priv_key.len - 2] = 'O';
674 try testing.expectError(error.InvalidChecksum, PrivateKey.fromTextPrivateKey(&bad_priv_key));
675}
676
677test "wipe" {
678 const kp = try SeedKeyPair.generate(.account);
679 const pub_key = kp.intoPublicKey();
680 const priv_key = kp.intoPrivateKey();
681
682 var kp_clone = kp;
683 kp_clone.wipe();
684 try testing.expect(!std.meta.eql(kp_clone.kp, kp.kp));
685
686 var pub_key_clone = pub_key;
687 pub_key_clone.wipe();
688 try testing.expect(!std.meta.eql(pub_key_clone.key, pub_key.key));
689
690 var priv_key_clone = priv_key;
691 priv_key_clone.wipe();
692 try testing.expect(!std.meta.eql(priv_key_clone.kp, priv_key.kp));
693}
658 694
659test "parse decorated JWT (bad)" { 695test "parse decorated JWT (bad)" {
660 try testing.expectEqualStrings("foo", parseDecoratedJwt("foo")); 696 try testing.expectEqualStrings("foo", parseDecoratedJwt("foo"));