File Coverage

blib/lib/Crypt/Perl/Ed25519/PrivateKey.pm
Criterion Covered Total %
statement 75 78 96.1
branch n/a
condition 4 6 66.6
subroutine 14 15 93.3
pod 0 5 0.0
total 93 104 89.4


line stmt bran cond sub pod time code
1             package Crypt::Perl::Ed25519::PrivateKey;
2              
3 2     2   65316 use strict;
  2         11  
  2         44  
4 2     2   8 use warnings;
  2         4  
  2         53  
5              
6             =encoding utf-8
7              
8             =head1 NAME
9              
10             Crypt::Perl::Ed25519::PrivateKey
11              
12             =head1 SYNOPSIS
13              
14             my $new_key = Crypt::Perl::Ed25519::PrivateKey->new();
15              
16             # The passed-in string should contain ONLY the private pieces.
17             my $import_key = Crypt::Perl::Ed25519::PrivateKey->new( $priv_str );
18              
19             # … or do this if you’ve got the public component:
20             $import_key = Crypt::Perl::Ed25519::PrivateKey->new( $priv_str, $pub_str );
21              
22             # Returns an octet string
23             my $signature = $key->sign( $message );
24              
25             $key->verify( $message, $signature ) or die "Invalid sig for msg!";
26              
27             #----------------------------------------------------------------------
28              
29             # These return an octet string.
30             my $pub_str = $key->get_public();
31             my $priv_str = $key->get_private();
32              
33             # Returns an object
34             my $pub_obj = $key->get_public_key();
35              
36             =head1 DESCRIPTION
37              
38             This class implements Ed25519 signing and verification.
39              
40             =cut
41              
42 2         13 use parent qw(
43             Crypt::Perl::Ed25519::KeyBase
44 2     2   338 );
  2         258  
45              
46 2     2   65 use Digest::SHA ();
  2         3  
  2         23  
47              
48 2     2   18 use Crypt::Perl::Ed25519::Math;
  2         4  
  2         40  
49              
50 2         72 use constant _ASN1 => q<
51             FG_Key ::= SEQUENCE {
52             version INTEGER,
53             algorithmIdentifier AlgorithmIdentifier,
54             privateKey PrivateKey
55             }
56              
57             PrivateKey ::= OCTET STRING
58 2     2   7 >;
  2         4  
59              
60 2     2   8 use constant _PEM_HEADER => 'PRIVATE KEY';
  2         2  
  2         1377  
61              
62             sub _to_der_args {
63 1     1   2 my ($self) = @_;
64              
65             return (
66              
67             # The leading bytes are the encoding of the inner CurvePrivateKey
68             # (i.e., OCTET STRING).
69 1         8 privateKey => "\x04\x20" . $self->{'_private'},
70             );
71             }
72              
73             sub get_struct_for_private_jwk {
74 1     1 0 3 my ($self) = @_;
75              
76 1         8 my $struct = $self->get_struct_for_public_jwk();
77              
78 1         17 require MIME::Base64;
79              
80 1         3 $struct->{'d'} = MIME::Base64::encode_base64url($self->{'_private'});
81              
82 1         11 return $struct;
83             }
84              
85             sub new {
86 15     15 0 7259 my ($class, $priv, $pub) = @_;
87              
88 15   66     62 $priv ||= do {
89 8         692 require Crypt::Perl::RNG;
90 8         36 Crypt::Perl::RNG::bytes(32);
91             };
92              
93 15   66     4453 $pub ||= _deduce_public_from_private($priv);
94              
95 15         331 return bless {
96             _public => $pub,
97             _public_ar => [ unpack 'C*', $pub ],
98             _private => $priv,
99             _private_ar => [ unpack 'C*', $priv ],
100             }, $class;
101             }
102              
103             sub get_private {
104 2     2 0 13 my ($self) = @_;
105              
106 2         37 return $self->{'_private'};
107             }
108              
109             sub get_public_key {
110 0     0 0 0 my ($self) = @_;
111              
112 0         0 require Crypt::Perl::Ed25519::PublicKey;
113              
114 0         0 return Crypt::Perl::Ed25519::PublicKey->new( $self->{'_public'} );
115             }
116              
117             sub sign {
118 13     13 0 876 my ($self, $msg) = @_;
119              
120 13         59 my @x = (0) x 64;
121              
122 13         44 my @p = map { [ Crypt::Perl::Ed25519::Math::gf0() ] } 1 .. 4;
  52         126  
123              
124 13         69 my $digest_ar = _digest32( $self->{'_private'} );
125              
126 13         50 my @sm = (0) x 32;
127 13         32 push @sm, @{$digest_ar}[32 .. 63];
  13         54  
128 13         48 push @sm, unpack( 'C*', $msg );
129              
130 13         345 my @r = unpack 'C*', Digest::SHA::sha512( pack 'C*', @sm[32 .. $#sm] );
131 13         98 Crypt::Perl::Ed25519::Math::reduce(\@r);
132 13         73 Crypt::Perl::Ed25519::Math::scalarbase( \@p, \@r );
133 13         60 @sm[ 0 .. 31 ] = @{ Crypt::Perl::Ed25519::Math::pack(\@p) };
  13         55  
134              
135 13         40 @sm[32 .. 63] = @{$self->{'_public_ar'}};
  13         98  
136              
137 13         309 my @h = unpack 'C*', Digest::SHA::sha512( pack 'C*', @sm );
138 13         75 Crypt::Perl::Ed25519::Math::reduce( \@h );
139              
140 13         87 @x[0 .. 31] = @r[0 .. 31];
141              
142 13         37 for my $i ( 0 .. 31) {
143 416         448 for my $j ( 0 .. 31 ) {
144 13312         15135 $x[ $i + $j ] += $h[$i] * $digest_ar->[$j];
145             }
146             }
147              
148 13         119 my @latter_sm = @sm[32 .. $#sm];
149              
150 13         75 Crypt::Perl::Ed25519::Math::modL( \@latter_sm, \@x );
151              
152 13         73 @sm[32 .. $#sm] = @latter_sm;
153              
154 13         363 return pack 'C*', @sm[ 0 .. ($self->SIGN_BYTE_LENGTH - 1) ];
155             }
156              
157             #----------------------------------------------------------------------
158              
159             sub _deduce_public_from_private {
160 14     14   35 my ($private) = @_;
161              
162 14         56 my $digest_ar = _digest32($private);
163              
164 14         45 my $p = [ map { [ Crypt::Perl::Ed25519::Math::gf0() ] } 0 .. 3 ];
  56         177  
165              
166             # private key is 32 bytes for private part
167             # plus 32 bytes for the public part
168              
169 14         74 Crypt::Perl::Ed25519::Math::scalarbase($p, $digest_ar);
170 14         90 my $pk = Crypt::Perl::Ed25519::Math::pack($p);
171              
172 14         308 return pack 'C*', @$pk;
173             }
174              
175             sub _digest32 {
176 27     27   96 my ($seed) = @_;
177              
178 27         411 my @digest = unpack 'C*', Digest::SHA::sha512($seed);
179 27         73 $digest[0] &= 0xf8; #248
180 27         57 $digest[31] &= 0x7f; #127
181 27         49 $digest[31] |= 0x40; # 64
182              
183 27         83 return \@digest;
184             }
185              
186             1;