File Coverage

blib/lib/Crypt/Perl/Ed25519/PrivateKey.pm
Criterion Covered Total %
statement 85 85 100.0
branch 4 4 100.0
condition 4 6 66.6
subroutine 15 15 100.0
pod 0 5 0.0
total 108 115 93.9


line stmt bran cond sub pod time code
1             package Crypt::Perl::Ed25519::PrivateKey;
2              
3 3     3   62396 use strict;
  3         10  
  3         67  
4 3     3   12 use warnings;
  3         4  
  3         86  
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             # These return a hash reference, NOT a JSON string.
37             my $priv_hr = $key->get_struct_for_private_jwk();
38             my $pub_hr = $key->get_struct_for_public_jwk();
39              
40             =head1 DESCRIPTION
41              
42             This class implements Ed25519 signing and verification.
43              
44             =cut
45              
46 3         17 use parent qw(
47             Crypt::Perl::Ed25519::KeyBase
48 3     3   328 );
  3         208  
49              
50 3     3   95 use Digest::SHA ();
  3         4  
  3         36  
51              
52 3     3   11 use Crypt::Perl::Ed25519::Math;
  3         5  
  3         55  
53              
54 3         110 use constant _ASN1 => q<
55             FG_Key ::= SEQUENCE {
56             version INTEGER,
57             algorithmIdentifier AlgorithmIdentifier,
58             privateKey PrivateKey
59             }
60              
61             PrivateKey ::= OCTET STRING
62 3     3   9 >;
  3         5  
63              
64 3     3   10 use constant _PEM_HEADER => 'PRIVATE KEY';
  3         5  
  3         2028  
65              
66             sub new {
67 16     16 0 8228 my ($class, $priv, $pub) = @_;
68              
69 16 100 66     85 if (defined($priv) && length($priv)) {
70 8         31 $class->_verify_binary_key_part($priv);
71             }
72             else {
73 8         16 $priv = do {
74 8         614 require Crypt::Perl::RNG;
75 8         34 Crypt::Perl::RNG::bytes(32);
76             };
77             }
78              
79 16         4179 my ($pub_ar);
80              
81 16 100 66     59 if (defined($pub) && length($pub)) {
82 1         3 $class->_verify_binary_key_part($pub);
83              
84 1         2 $pub_ar = unpack 'C*', $pub;
85             }
86             else {
87 15         46 $pub_ar = _deduce_public_from_private($priv);
88 15         134 $pub = pack 'C*', @$pub_ar;
89             }
90              
91 16         289 return bless {
92             _public => $pub,
93             _public_ar => $pub_ar,
94             _private => $priv,
95             _private_ar => [ unpack 'C*', $priv ],
96             }, $class;
97             }
98              
99             sub get_struct_for_private_jwk {
100 1     1 0 4 my ($self) = @_;
101              
102 1         305 my $struct = $self->get_struct_for_public_jwk();
103              
104 1         17 require MIME::Base64;
105              
106 1         2 $struct->{'d'} = MIME::Base64::encode_base64url($self->{'_private'});
107              
108 1         60 return $struct;
109             }
110              
111             sub get_private {
112 2     2 0 12 my ($self) = @_;
113              
114 2         33 return $self->{'_private'};
115             }
116              
117             sub get_public_key {
118 1     1 0 70 my ($self) = @_;
119              
120 1         762 require Crypt::Perl::Ed25519::PublicKey;
121              
122 1         5 return Crypt::Perl::Ed25519::PublicKey->new( $self->{'_public'} );
123             }
124              
125             sub sign {
126 14     14 0 824 my ($self, $msg) = @_;
127              
128 14         67 my @x = (0) x 64;
129              
130 14         46 my @p = map { [ Crypt::Perl::Ed25519::Math::gf0() ] } 1 .. 4;
  56         136  
131              
132 14         94 my $digest_ar = _digest32( $self->{'_private'} );
133              
134 14         47 my @sm = (0) x 32;
135 14         35 push @sm, @{$digest_ar}[32 .. 63];
  14         52  
136 14         109 push @sm, unpack( 'C*', $msg );
137              
138 14         310 my @r = unpack 'C*', Digest::SHA::sha512( pack 'C*', @sm[32 .. $#sm] );
139 14         94 Crypt::Perl::Ed25519::Math::reduce(\@r);
140 14         79 Crypt::Perl::Ed25519::Math::scalarbase( \@p, \@r );
141 14         58 @sm[ 0 .. 31 ] = @{ Crypt::Perl::Ed25519::Math::pack(\@p) };
  14         67  
142              
143 14         43 @sm[32 .. 63] = @{$self->{'_public_ar'}};
  14         80  
144              
145 14         355 my @h = unpack 'C*', Digest::SHA::sha512( pack 'C*', @sm );
146 14         117 Crypt::Perl::Ed25519::Math::reduce( \@h );
147              
148 14         85 @x[0 .. 31] = @r[0 .. 31];
149              
150 14         33 for my $i ( 0 .. 31) {
151 448         461 for my $j ( 0 .. 31 ) {
152 14336         15172 $x[ $i + $j ] += $h[$i] * $digest_ar->[$j];
153             }
154             }
155              
156 14         121 my @latter_sm = @sm[32 .. $#sm];
157              
158 14         68 Crypt::Perl::Ed25519::Math::modL( \@latter_sm, \@x );
159              
160 14         83 @sm[32 .. $#sm] = @latter_sm;
161              
162 14         436 return pack 'C*', @sm[ 0 .. ($self->SIGN_BYTE_LENGTH - 1) ];
163             }
164              
165             #----------------------------------------------------------------------
166              
167             sub _to_der_args {
168 1     1   2 my ($self) = @_;
169              
170             return (
171              
172             # The leading bytes are the encoding of the inner CurvePrivateKey
173             # (i.e., OCTET STRING).
174 1         9 privateKey => "\x04\x20" . $self->{'_private'},
175             );
176             }
177              
178             sub _deduce_public_from_private {
179 15     15   41 my ($private) = @_;
180              
181 15         45 my $digest_ar = _digest32($private);
182              
183 15         41 my $p = [ map { [ Crypt::Perl::Ed25519::Math::gf0() ] } 0 .. 3 ];
  60         141  
184              
185             # private key is 32 bytes for private part
186             # plus 32 bytes for the public part
187              
188 15         62 Crypt::Perl::Ed25519::Math::scalarbase($p, $digest_ar);
189 15         91 my $pk = Crypt::Perl::Ed25519::Math::pack($p);
190              
191 15         116 return \@$pk;
192             }
193              
194             sub _digest32 {
195 29     29   67 my ($seed) = @_;
196              
197 29         391 my @digest = unpack 'C*', Digest::SHA::sha512($seed);
198 29         83 $digest[0] &= 0xf8; #248
199 29         48 $digest[31] &= 0x7f; #127
200 29         51 $digest[31] |= 0x40; # 64
201              
202 29         58 return \@digest;
203             }
204              
205             1;