1package t::Crypt::Perl::Ed25519::PrivateKey;
2
3use strict;
4use warnings;
5
6use Test::More;
7use Test::Deep;
8
9use Bytes::Random::Secure::Tiny ();
10
11use FindBin;
12use lib "$FindBin::Bin/../lib";
13
14use Crypt::Perl::Ed25519::PrivateKey;
15
16{
17    my $msg = 'test';
18
19    my $key = Crypt::Perl::Ed25519::PrivateKey->new('01234567890123456789012345678901');
20
21    my $signature = $key->sign($msg);
22
23    cmp_deeply(
24        [ unpack 'C*', $signature ],
25        [ 97,68,231,142,114,20,32,84,42,59,212,177,59,182,143,41,165,138,164,199,94,17,44,24,176,202,84,5,74,48,117,63,136,196,250,28,130,1,232,251,158,60,163,46,6,27,119,230,184,59,186,61,115,60,249,205,72,94,172,56,206,229,198,5 ],
26        'signature of “test”',
27    #) or diag "got: @signature";
28    ) or diag sprintf( "got: %v.02x", $signature );
29
30    is(
31        $key->verify( $msg, $signature ),
32        1,
33        'verify()',
34    );
35
36    substr( $signature, 0, 1, 'z' );
37
38    ok(
39        !$key->verify( $msg, $key->sign("$msg $msg") ),
40        'verify() - mismatch',
41    );
42}
43
44#----------------------------------------------------------------------
45my @pre_assigned_tests = (
46    {
47        private => [ 226, 85, 30, 181, 147, 126, 178, 234, 14, 82, 163, 108, 30, 146, 174, 101, 160, 27, 188, 20, 189, 13, 91, 33, 156, 147, 170, 24, 41, 250, 191, 143 ],
48        public => [ 149, 76, 21, 14, 234, 81, 92, 79, 160, 82, 8, 246, 69, 114, 70, 202, 242, 205, 147, 62, 245, 189, 87, 25, 230, 4, 106, 16, 135, 62, 147, 164 ],
49        message => '37.21.9a.9e.99.d9.53.47.cb.ca.3f.e9.48.11.3d.77.95.ff.a1.08.8f.72.21.89',
50        signature => 'b5.65.af.54.be.70.78.a3.87.82.f2.f6.ec.d3.f5.26.96.aa.7d.87.3e.c9.5c.e0.e4.d6.da.5b.88.07.bf.dc.a7.4f.80.30.f9.b3.f6.90.a4.30.6f.0e.88.59.4f.e6.3c.6a.f3.4b.3b.c1.c0.0d.57.61.12.49.78.d1.22.0a',
51    },
52    {
53        private => [ 147, 28, 170, 11, 118, 37, 231, 19, 158, 20, 105, 109, 36, 41, 131, 70, 145, 242, 5, 56, 236, 254, 172, 1, 254, 145, 81, 13, 59, 63, 98, 151 ],
54        public => [ 113, 214, 236, 64, 43, 34, 172, 89, 22, 8, 89, 127, 187, 195, 16, 170, 170, 149, 184, 173, 39, 192, 163, 139, 91, 88, 149, 88, 122, 106, 227, 56 ],
55        message => '64.3c.a0.34.ac.7e.46.19.7b.7a.a6.b5.50.08.40.8c.71.5f.d9.83.f1.7b.cf.bf',
56        signature => '50.a0.a1.86.2a.e4.40.b9.af.63.5f.cd.6e.41.c8.2d.a3.a2.00.8b.8f.b8.af.2e.a4.c5.23.42.75.84.d6.35.83.56.2f.71.5e.b5.41.fb.f0.55.93.4a.f9.68.af.af.1e.0d.d5.4a.07.32.b3.3f.65.b4.eb.9e.cc.f3.c6.0c',
57    },
58    {
59        private => [ 200, 101, 165, 21, 192, 50, 14, 180, 38, 210, 151, 69, 59, 93, 25, 218, 60, 211, 229, 151, 57, 3, 159, 148, 204, 54, 140, 86, 82, 237, 85, 34 ],
60        public => [ 239, 145, 206, 165, 6, 213, 190, 169, 99, 17, 132, 230, 4, 201, 139, 169, 47, 19, 240, 68, 159, 180, 218, 153, 158, 17, 250, 102, 215, 217, 30, 42 ],
61        message => '1c.96.78.ca.c4.1e.97.00.c4.d5.08.6e.93.91.11.f2.09.2c.68.12.c6.c1.bc.ef',
62        signature => '0b.d3.f5.3d.d3.0d.df.10.35.85.ff.7b.54.ed.29.c4.09.fe.2f.b6.46.a4.07.82.3b.62.bd.b7.61.03.e6.e2.c4.8c.ea.00.6c.78.d6.88.92.63.a1.50.ce.f4.5d.f4.70.1b.ae.a6.33.e3.ef.35.03.e7.bd.a0.e8.cf.cc.03',
63    },
64);
65
66for my $idx ( 0 .. $#pre_assigned_tests) {
67    note "PRE-DESIGNATED: " . (1 + $idx);
68
69    my ($priv_ar, $pub_ar, $msg_vec, $sig_vec) = @{ $pre_assigned_tests[$idx] }{ 'private', 'public', 'message', 'signature' };
70
71    my $pub_str = join q<.>, map { sprintf '%02x', $_ } @$pub_ar;
72
73    my $key = Crypt::Perl::Ed25519::PrivateKey->new( join q<>, map { chr } @$priv_ar );
74    is_deeply(
75        sprintf('%v.02x', $key->get_public() ),
76        $pub_str,
77        'correct public key determined',
78    );
79
80    my $msg = join q<>, map { chr hex } split m<\.>, $msg_vec;
81
82    my $sig = $key->sign($msg);
83
84    is(
85        sprintf('%v.02x', $sig),
86        $sig_vec,
87        'expected signature',
88    );
89
90    my $real_sig = join q<>, map { chr hex } split m<\.>, $sig_vec;
91    ok( $key->verify($msg, $real_sig), 'verify()' );
92}
93
94#----------------------------------------------------------------------
95
96my $rng = Bytes::Random::Secure::Tiny->new();
97
98for my $i ( 1 .. 8 ) {
99    my $key = Crypt::Perl::Ed25519::PrivateKey->new();
100
101    my $msg1 = $rng->bytes(24);
102    my $sig1 = $key->sign($msg1);
103
104    ok( $key->verify($msg1, $sig1), "round-trip ($i) - should verify" ) or do {
105        diag explain( {
106            key => $key,
107            message => sprintf('%v.02x', $msg1),
108            signature => sprintf('%v.02x', $sig1),
109        } );
110    };
111
112    my $msg2 = $rng->bytes(25);
113
114    ok( !$key->verify($msg2, $sig1), "round-trip ($i) - should mismatch" ) or do {
115        diag explain( {
116            key => $key,
117            message => sprintf('%v.02x', $msg2),
118            signature => sprintf('%v.02x', $sig1),
119        } );
120    };
121}
122
123#----------------------------------------------------------------------
124
125my $private = Crypt::Perl::Ed25519::PrivateKey->new(
126    join( q<>, map { chr hex } split m<\.>, '9d.61.b1.9d.ef.fd.5a.60.ba.84.4a.f4.92.ec.2c.c4.44.49.c5.69.7b.32.69.19.70.3b.ac.03.1c.ae.7f.60' ),
127);
128
129my $thumbprint = $private->get_jwk_thumbprint('sha256');
130is(
131    $thumbprint,
132    'kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k',
133    'JWK thumbprint (SHA-256)',
134);
135
136done_testing();
137