diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/nkeys.zig | 262 | ||||
| -rw-r--r-- | src/znk.zig | 8 |
2 files changed, 234 insertions, 36 deletions
diff --git a/src/nkeys.zig b/src/nkeys.zig index b66865d..9645483 100644 --- a/src/nkeys.zig +++ b/src/nkeys.zig | |||
| @@ -95,7 +95,7 @@ pub const SeedKeyPair = struct { | |||
| 95 | Ed25519.verify(sig, msg, self.kp.public_key) catch return error.InvalidSignature; | 95 | Ed25519.verify(sig, msg, self.kp.public_key) catch return error.InvalidSignature; |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | pub fn textSeed(self: *const Self) text_seed { | 98 | pub fn seedText(self: *const Self) text_seed { |
| 99 | const full_prefix = &[_]u8{ | 99 | const full_prefix = &[_]u8{ |
| 100 | @enumToInt(KeyTypePrefixByte.seed) | (@enumToInt(self.prefix) >> 5), | 100 | @enumToInt(KeyTypePrefixByte.seed) | (@enumToInt(self.prefix) >> 5), |
| 101 | (@enumToInt(self.prefix) & 0b00011111) << 3, | 101 | (@enumToInt(self.prefix) & 0b00011111) << 3, |
| @@ -104,11 +104,11 @@ pub const SeedKeyPair = struct { | |||
| 104 | return encode(full_prefix.len, seed.len, full_prefix, seed); | 104 | return encode(full_prefix.len, seed.len, full_prefix, seed); |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | pub fn textPrivateKey(self: *const Self) text_private { | 107 | 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); | 108 | return encode(1, self.kp.secret_key.len, &[_]u8{@enumToInt(KeyTypePrefixByte.private)}, &self.kp.secret_key); |
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | pub fn textPublicKey(self: *const Self) text_public { | 111 | 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); | 112 | return encode(1, self.kp.public_key.len, &[_]u8{@enumToInt(self.prefix)}, &self.kp.public_key); |
| 113 | } | 113 | } |
| 114 | 114 | ||
| @@ -160,7 +160,7 @@ pub const PublicKey = struct { | |||
| 160 | return Self{ .prefix = prefix, .key = raw_key.* }; | 160 | return Self{ .prefix = prefix, .key = raw_key.* }; |
| 161 | } | 161 | } |
| 162 | 162 | ||
| 163 | pub fn textPublicKey(self: *const Self) text_public { | 163 | pub fn publicKeyText(self: *const Self) text_public { |
| 164 | return encode(1, self.key.len, &[_]u8{@enumToInt(self.prefix)}, &self.key); | 164 | return encode(1, self.key.len, &[_]u8{@enumToInt(self.prefix)}, &self.key); |
| 165 | } | 165 | } |
| 166 | 166 | ||
| @@ -209,7 +209,7 @@ pub const PrivateKey = struct { | |||
| 209 | }; | 209 | }; |
| 210 | } | 210 | } |
| 211 | 211 | ||
| 212 | pub fn textPrivateKey(self: *const Self) text_private { | 212 | 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); | 213 | return encode(1, self.kp.secret_key.len, &[_]u8{@enumToInt(KeyTypePrefixByte.private)}, &self.kp.secret_key); |
| 214 | } | 214 | } |
| 215 | 215 | ||
| @@ -326,17 +326,25 @@ pub fn isValidEncoding(text: []const u8) bool { | |||
| 326 | return made_crc == got_crc; | 326 | return made_crc == got_crc; |
| 327 | } | 327 | } |
| 328 | 328 | ||
| 329 | pub fn isValidSeed(text: *const text_seed) bool { | 329 | pub fn isValidSeed(text: []const u8, with_type: ?PublicPrefixByte) bool { |
| 330 | var res = SeedKeyPair.fromTextSeed(text) catch return false; | 330 | if (text.len < text_seed_len) return false; |
| 331 | res.wipe(); | 331 | var res = SeedKeyPair.fromTextSeed(text[0..text_seed_len]) catch return false; |
| 332 | return true; | 332 | defer res.wipe(); |
| 333 | return if (with_type) |ty| res.prefix == ty else true; | ||
| 333 | } | 334 | } |
| 334 | 335 | ||
| 335 | pub fn isValidPublicKey(text: *const text_public, with_type: ?PublicPrefixByte) bool { | 336 | pub fn isValidPublicKey(text: []const u8, with_type: ?PublicPrefixByte) bool { |
| 336 | var res = decode(1, Ed25519.public_length, text) catch return false; | 337 | if (text.len < text_public_len) return false; |
| 338 | var res = PublicKey.fromTextPublicKey(text[0..text_public_len]) catch return false; | ||
| 337 | defer res.wipe(); | 339 | defer res.wipe(); |
| 338 | const public = PublicPrefixByte.fromU8(res.data[0]) catch return false; | 340 | return if (with_type) |ty| res.prefix == ty else true; |
| 339 | return if (with_type) |ty| public == ty else true; | 341 | } |
| 342 | |||
| 343 | pub fn isValidPrivateKey(text: []const u8) bool { | ||
| 344 | if (text.len < text_private_len) return false; | ||
| 345 | var res = PrivateKey.fromTextPrivateKey(text[0..text_private_len]) catch return false; | ||
| 346 | res.wipe(); | ||
| 347 | return true; | ||
| 340 | } | 348 | } |
| 341 | 349 | ||
| 342 | // `line` must not contain CR or LF characters. | 350 | // `line` must not contain CR or LF characters. |
| @@ -420,7 +428,7 @@ pub fn parseDecoratedNkey(contents: []const u8) NoNkeySeedFoundError!SeedKeyPair | |||
| 420 | 428 | ||
| 421 | pub fn parseDecoratedUserNkey(contents: []const u8) (NoNkeySeedFoundError || NoNkeyUserSeedFoundError)!SeedKeyPair { | 429 | pub fn parseDecoratedUserNkey(contents: []const u8) (NoNkeySeedFoundError || NoNkeyUserSeedFoundError)!SeedKeyPair { |
| 422 | var key = try parseDecoratedNkey(contents); | 430 | var key = try parseDecoratedNkey(contents); |
| 423 | if (!mem.startsWith(u8, &key.textSeed(), "SU")) return error.NoNkeyUserSeedFound; | 431 | if (!mem.startsWith(u8, &key.seedText(), "SU")) return error.NoNkeyUserSeedFound; |
| 424 | defer key.wipe(); | 432 | defer key.wipe(); |
| 425 | return key; | 433 | return key; |
| 426 | } | 434 | } |
| @@ -434,29 +442,219 @@ test { | |||
| 434 | 442 | ||
| 435 | test { | 443 | test { |
| 436 | var key_pair = try SeedKeyPair.generate(PublicPrefixByte.server); | 444 | var key_pair = try SeedKeyPair.generate(PublicPrefixByte.server); |
| 437 | defer key_pair.wipe(); | 445 | var decoded_seed = try SeedKeyPair.fromTextSeed(&key_pair.seedText()); |
| 438 | 446 | try testing.expect(isValidEncoding(&decoded_seed.seedText())); | |
| 439 | var decoded_seed = try SeedKeyPair.fromTextSeed(&key_pair.textSeed()); | ||
| 440 | defer decoded_seed.wipe(); | ||
| 441 | try testing.expect(isValidEncoding(&decoded_seed.textSeed())); | ||
| 442 | 447 | ||
| 443 | var pub_key_str_a = key_pair.textPublicKey(); | 448 | var pub_key_str_a = key_pair.publicKeyText(); |
| 444 | defer wipeBytes(&pub_key_str_a); | 449 | var priv_key_str_a = key_pair.privateKeyText(); |
| 445 | var priv_key_str = key_pair.textPrivateKey(); | ||
| 446 | defer wipeBytes(&priv_key_str); | ||
| 447 | try testing.expect(pub_key_str_a.len != 0); | 450 | try testing.expect(pub_key_str_a.len != 0); |
| 448 | try testing.expect(priv_key_str.len != 0); | 451 | try testing.expect(priv_key_str_a.len != 0); |
| 449 | try testing.expect(isValidEncoding(&pub_key_str_a)); | 452 | try testing.expect(isValidEncoding(&pub_key_str_a)); |
| 450 | try testing.expect(isValidEncoding(&priv_key_str)); | 453 | try testing.expect(isValidEncoding(&priv_key_str_a)); |
| 451 | 454 | ||
| 452 | var pub_key = key_pair.intoPublicKey(); | 455 | var pub_key = key_pair.intoPublicKey(); |
| 453 | defer pub_key.wipe(); | 456 | var pub_key_str_b = pub_key.publicKeyText(); |
| 454 | var pub_key_str_b = pub_key.textPublicKey(); | 457 | try testing.expectEqualStrings(&pub_key_str_a, &pub_key_str_b); |
| 455 | defer wipeBytes(&pub_key_str_b); | 458 | |
| 456 | try testing.expectEqualSlices(u8, &pub_key_str_a, &pub_key_str_b); | 459 | var priv_key = key_pair.intoPrivateKey(); |
| 460 | var priv_key_str_b = priv_key.privateKeyText(); | ||
| 461 | try testing.expectEqualStrings(&priv_key_str_a, &priv_key_str_b); | ||
| 462 | } | ||
| 463 | |||
| 464 | test "decode" { | ||
| 465 | const kp = try SeedKeyPair.generate(.account); | ||
| 466 | const seed_text = kp.seedText(); | ||
| 467 | const pub_key_text = kp.publicKeyText(); | ||
| 468 | const priv_key_text = kp.privateKeyText(); | ||
| 469 | |||
| 470 | _ = try SeedKeyPair.fromTextSeed(&seed_text); | ||
| 471 | _ = try PublicKey.fromTextPublicKey(&pub_key_text); | ||
| 472 | _ = try PrivateKey.fromTextPrivateKey(&priv_key_text); | ||
| 473 | |||
| 474 | try testing.expectError(error.InvalidChecksum, PublicKey.fromTextPublicKey(seed_text[0..text_public_len])); | ||
| 475 | try testing.expectError(error.InvalidChecksum, SeedKeyPair.fromTextSeed(priv_key_text[0..text_seed_len])); | ||
| 476 | } | ||
| 477 | |||
| 478 | test "seed" { | ||
| 479 | inline for (@typeInfo(PublicPrefixByte).Enum.fields) |field| { | ||
| 480 | const prefix = @field(PublicPrefixByte, field.name); | ||
| 481 | const kp = try SeedKeyPair.generate(prefix); | ||
| 482 | const decoded = try SeedKeyPair.fromTextSeed(&kp.seedText()); | ||
| 483 | if (decoded.prefix != prefix) { | ||
| 484 | std.debug.print("expected prefix {}, found prefix {}\n", .{ prefix, decoded.prefix }); | ||
| 485 | return error.TestUnexpectedError; | ||
| 486 | } | ||
| 487 | } | ||
| 488 | } | ||
| 489 | |||
| 490 | test "public key" { | ||
| 491 | inline for (@typeInfo(PublicPrefixByte).Enum.fields) |field| { | ||
| 492 | const prefix = @field(PublicPrefixByte, field.name); | ||
| 493 | const kp = try SeedKeyPair.generate(prefix); | ||
| 494 | const decoded_pub_key = try PublicKey.fromTextPublicKey(&kp.publicKeyText()); | ||
| 495 | if (decoded_pub_key.prefix != prefix) { | ||
| 496 | std.debug.print("expected prefix {}, found prefix {}\n", .{ prefix, decoded_pub_key.prefix }); | ||
| 497 | return error.TestUnexpectedError; | ||
| 498 | } | ||
| 499 | } | ||
| 500 | } | ||
| 501 | |||
| 502 | test "account" { | ||
| 503 | const kp = try SeedKeyPair.generate(.account); | ||
| 504 | _ = try SeedKeyPair.fromTextSeed(&kp.seedText()); | ||
| 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 | |||
| 520 | test "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 | |||
| 528 | test "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 | |||
| 536 | test "server" { | ||
| 537 | const kp = try SeedKeyPair.generate(.server); | ||
| 538 | |||
| 539 | const pub_key_str = kp.publicKeyText(); | ||
| 540 | try testing.expect(pub_key_str[0] == 'N'); | ||
| 541 | try testing.expect(isValidPublicKey(&pub_key_str, .server)); | ||
| 542 | } | ||
| 543 | |||
| 544 | test "user" { | ||
| 545 | const kp = try SeedKeyPair.generate(.user); | ||
| 546 | |||
| 547 | const pub_key_str = kp.publicKeyText(); | ||
| 548 | try testing.expect(pub_key_str[0] == 'U'); | ||
| 549 | try testing.expect(isValidPublicKey(&pub_key_str, .user)); | ||
| 550 | } | ||
| 551 | |||
| 552 | test "validation" { | ||
| 553 | const prefixes = @typeInfo(PublicPrefixByte).Enum.fields; | ||
| 554 | inline for (prefixes) |field, i| { | ||
| 555 | const prefix = @field(PublicPrefixByte, field.name); | ||
| 556 | const next_prefix = next: { | ||
| 557 | const next_field_i = if (i == prefixes.len - 1) 0 else i + 1; | ||
| 558 | std.debug.assert(next_field_i != i); | ||
| 559 | break :next @field(PublicPrefixByte, prefixes[next_field_i].name); | ||
| 560 | }; | ||
| 561 | const kp = try SeedKeyPair.generate(prefix); | ||
| 562 | |||
| 563 | const seed_str = kp.seedText(); | ||
| 564 | const pub_key_str = kp.publicKeyText(); | ||
| 565 | const priv_key_str = kp.privateKeyText(); | ||
| 566 | |||
| 567 | try testing.expect(isValidSeed(&seed_str, prefix)); | ||
| 568 | try testing.expect(isValidSeed(&seed_str, null)); | ||
| 569 | try testing.expect(isValidPublicKey(&pub_key_str, null)); | ||
| 570 | try testing.expect(isValidPublicKey(&pub_key_str, prefix)); | ||
| 571 | try testing.expect(isValidPrivateKey(&priv_key_str)); | ||
| 572 | |||
| 573 | try testing.expect(!isValidSeed(&seed_str, next_prefix)); | ||
| 574 | try testing.expect(!isValidSeed(&pub_key_str, null)); | ||
| 575 | try testing.expect(!isValidSeed(&priv_key_str, null)); | ||
| 576 | try testing.expect(!isValidPublicKey(&pub_key_str, next_prefix)); | ||
| 577 | try testing.expect(!isValidPublicKey(&seed_str, null)); | ||
| 578 | try testing.expect(!isValidPublicKey(&priv_key_str, null)); | ||
| 579 | try testing.expect(!isValidPrivateKey(&seed_str)); | ||
| 580 | try testing.expect(!isValidPrivateKey(&pub_key_str)); | ||
| 581 | } | ||
| 582 | |||
| 583 | try testing.expect(!isValidSeed("seed", null)); | ||
| 584 | try testing.expect(!isValidPublicKey("public key", null)); | ||
| 585 | try testing.expect(!isValidPrivateKey("private key")); | ||
| 586 | } | ||
| 587 | |||
| 588 | test "from seed" { | ||
| 589 | const kp = try SeedKeyPair.generate(.account); | ||
| 590 | const kp_from_raw = try SeedKeyPair.fromRawSeed(kp.prefix, kp.kp.secret_key[0..Ed25519.seed_length]); | ||
| 591 | try testing.expect(std.meta.eql(kp, kp_from_raw)); | ||
| 592 | |||
| 593 | const data = "Hello, World!"; | ||
| 594 | const sig = try kp.sign(data); | ||
| 595 | |||
| 596 | const seed = kp.seedText(); | ||
| 597 | try testing.expect(mem.startsWith(u8, &seed, "SA")); | ||
| 598 | |||
| 599 | const kp2 = try SeedKeyPair.fromTextSeed(&seed); | ||
| 600 | try kp2.verify(data, sig); | ||
| 601 | } | ||
| 602 | |||
| 603 | // TODO(rutgerbrf): give test a better name | ||
| 604 | test "from public key" { | ||
| 605 | const kp = try SeedKeyPair.generate(.user); | ||
| 606 | |||
| 607 | const pk_text = kp.publicKeyText(); | ||
| 608 | const pk_text_clone = kp.publicKeyText(); | ||
| 609 | try testing.expectEqualStrings(&pk_text, &pk_text_clone); | ||
| 610 | |||
| 611 | const pk = try PublicKey.fromTextPublicKey(&pk_text); | ||
| 612 | const pk_text_clone_2 = pk.publicKeyText(); | ||
| 613 | try testing.expect(std.meta.eql(pk, kp.intoPublicKey())); | ||
| 614 | try testing.expect(std.meta.eql(pk, PublicKey.fromRawPublicKey(kp.prefix, &kp.kp.public_key))); | ||
| 615 | try testing.expectEqualStrings(&pk_text, &pk_text_clone_2); | ||
| 616 | |||
| 617 | const data = "Hello, world!"; | ||
| 618 | |||
| 619 | const sig = try kp.sign(data); | ||
| 620 | try pk.verify(data, sig); | ||
| 621 | |||
| 622 | // Create another user to sign and make sure verification fails | ||
| 623 | const kp2 = try SeedKeyPair.generate(.user); | ||
| 624 | const sig2 = try kp2.sign(data); | ||
| 625 | |||
| 626 | try testing.expectError(error.InvalidSignature, pk.verify(data, sig2)); | ||
| 627 | } | ||
| 628 | |||
| 629 | test "from private key" { | ||
| 630 | const kp = try SeedKeyPair.generate(.account); | ||
| 631 | |||
| 632 | const pk_text = kp.privateKeyText(); | ||
| 633 | const pk_text_clone = kp.privateKeyText(); | ||
| 634 | try testing.expectEqualStrings(&pk_text, &pk_text_clone); | ||
| 635 | |||
| 636 | const pk = try PrivateKey.fromTextPrivateKey(&pk_text); | ||
| 637 | const pk_text_clone_2 = pk.privateKeyText(); | ||
| 638 | try testing.expect(std.meta.eql(pk, kp.intoPrivateKey())); | ||
| 639 | try testing.expect(std.meta.eql(kp, pk.intoSeedKeyPair(.account))); | ||
| 640 | try testing.expect(std.meta.eql(pk, PrivateKey.fromRawPrivateKey(&kp.kp.secret_key))); | ||
| 641 | try testing.expectEqualStrings(&pk_text, &pk_text_clone_2); | ||
| 642 | |||
| 643 | const data = "Hello, World!"; | ||
| 644 | |||
| 645 | const sig0 = try kp.sign(data); | ||
| 646 | const sig1 = try pk.sign(data); | ||
| 647 | try testing.expectEqualSlices(u8, &sig0, &sig1); | ||
| 648 | try pk.verify(data, sig0); | ||
| 649 | try kp.verify(data, sig1); | ||
| 650 | |||
| 651 | const kp2 = try SeedKeyPair.generate(.account); | ||
| 652 | const sig2 = try kp2.sign(data); | ||
| 653 | |||
| 654 | try testing.expectError(error.InvalidSignature, pk.verify(data, sig2)); | ||
| 457 | } | 655 | } |
| 458 | 656 | ||
| 459 | // TODO(rutgerbrf): test decode (+bad), seed, account, user, operator, cluster, isValid*, from*, fromRaw*, wipe | 657 | // TODO(rutgerbrf): bad decode, wipe, sign, (public/private/seed) verify |
| 460 | 658 | ||
| 461 | test "parse decorated JWT (bad)" { | 659 | test "parse decorated JWT (bad)" { |
| 462 | try testing.expectEqualStrings("foo", parseDecoratedJwt("foo")); | 660 | try testing.expectEqualStrings("foo", parseDecoratedJwt("foo")); |
| @@ -486,10 +684,10 @@ test "parse decorated seed and JWT" { | |||
| 486 | const seed = "SUAGIEYODKBBTUMOB666Z5KA4FCWAZV7HWSGRHOD7MK6UM5IYLWLACH7DQ"; | 684 | const seed = "SUAGIEYODKBBTUMOB666Z5KA4FCWAZV7HWSGRHOD7MK6UM5IYLWLACH7DQ"; |
| 487 | 685 | ||
| 488 | var got_kp = try parseDecoratedUserNkey(creds); | 686 | var got_kp = try parseDecoratedUserNkey(creds); |
| 489 | try testing.expectEqualStrings(seed, &got_kp.textSeed()); | 687 | try testing.expectEqualStrings(seed, &got_kp.seedText()); |
| 490 | 688 | ||
| 491 | got_kp = try parseDecoratedNkey(creds); | 689 | got_kp = try parseDecoratedNkey(creds); |
| 492 | try testing.expectEqualStrings(seed, &got_kp.textSeed()); | 690 | try testing.expectEqualStrings(seed, &got_kp.seedText()); |
| 493 | 691 | ||
| 494 | var got_jwt = parseDecoratedJwt(creds); | 692 | var got_jwt = parseDecoratedJwt(creds); |
| 495 | try testing.expectEqualStrings(jwt, got_jwt); | 693 | try testing.expectEqualStrings(jwt, got_jwt); |
diff --git a/src/znk.zig b/src/znk.zig index 7837cc8..4ab3077 100644 --- a/src/znk.zig +++ b/src/znk.zig | |||
| @@ -149,10 +149,10 @@ pub fn cmdGen(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !voi | |||
| 149 | } else { | 149 | } else { |
| 150 | var kp = try 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.textSeed()); | 152 | try stdout.writeAll(&kp.seedText()); |
| 153 | try stdout.writeAll("\n"); | 153 | try stdout.writeAll("\n"); |
| 154 | 154 | ||
| 155 | var public_key = kp.textPublicKey(); | 155 | var public_key = kp.publicKeyText(); |
| 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"); |
| @@ -378,12 +378,12 @@ const PrefixKeyGenerator = struct { | |||
| 378 | 378 | ||
| 379 | var kp = try nkeys.SeedKeyPair.generate(self.ty); | 379 | var kp = try nkeys.SeedKeyPair.generate(self.ty); |
| 380 | defer kp.wipe(); | 380 | defer kp.wipe(); |
| 381 | var public_key = kp.textPublicKey(); | 381 | var public_key = kp.publicKeyText(); |
| 382 | if (!mem.startsWith(u8, public_key[1..], self.prefix)) continue; | 382 | if (!mem.startsWith(u8, public_key[1..], self.prefix)) continue; |
| 383 | 383 | ||
| 384 | if (self.done.xchg(true, .SeqCst)) return; // another thread is already done | 384 | if (self.done.xchg(true, .SeqCst)) return; // another thread is already done |
| 385 | 385 | ||
| 386 | info("{s}", .{kp.textSeed()}); | 386 | info("{s}", .{kp.seedText()}); |
| 387 | info("{s}", .{public_key}); | 387 | info("{s}", .{public_key}); |
| 388 | 388 | ||
| 389 | return; | 389 | return; |