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 4     4   79644 use strict;
  4         16  
  4         91  
4 4     4   17 use warnings;
  4         7  
  4         112  
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 4         20 use parent qw(
47             Crypt::Perl::Ed25519::KeyBase
48 4     4   338 );
  4         240  
49              
50 4     4   148 use Digest::SHA ();
  4         7  
  4         53  
51              
52 4     4   15 use Crypt::Perl::Ed25519::Math;
  4         6  
  4         75  
53              
54 4         150 use constant _ASN1 => q<
55             FG_Key ::= SEQUENCE {
56             version INTEGER,
57             algorithmIdentifier AlgorithmIdentifier,
58             privateKey PrivateKey
59             }
60              
61             PrivateKey ::= OCTET STRING
62 4     4   14 >;
  4         6  
63              
64 4     4   16 use constant _PEM_HEADER => 'PRIVATE KEY';
  4         5  
  4         2832  
65              
66             sub new {
67 16     16 0 8162 my ($class, $priv, $pub) = @_;
68              
69 16 100 66     87 if (defined($priv) && length($priv)) {
70 8         34 $class->_verify_binary_key_part($priv);
71             }
72             else {
73 8         15 $priv = do {
74 8         596 require Crypt::Perl::RNG;
75 8         38 Crypt::Perl::RNG::bytes(32);
76             };
77             }
78              
79 16         4338 my ($pub_ar);
80              
81 16 100 66     64 if (defined($pub) && length($pub)) {
82 1         3 $class->_verify_binary_key_part($pub);
83              
84 1         3 $pub_ar = unpack 'C*', $pub;
85             }
86             else {
87 15         51 $pub_ar = _deduce_public_from_private($priv);
88 15         138 $pub = pack 'C*', @$pub_ar;
89             }
90              
91 16         296 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 2 my ($self) = @_;
101              
102 1         8 my $struct = $self->get_struct_for_public_jwk();
103              
104 1         17 require MIME::Base64;
105              
106 1         3 $struct->{'d'} = MIME::Base64::encode_base64url($self->{'_private'});
107              
108 1         55 return $struct;
109             }
110              
111             sub get_private {
112 2     2 0 12 my ($self) = @_;
113              
114 2         34 return $self->{'_private'};
115             }
116              
117             sub get_public_key {
118 1     1 0 69 my ($self) = @_;
119              
120 1         611 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 851 my ($self, $msg) = @_;
127              
128 14         57 my @x = (0) x 64;
129              
130 14         44 my @p = map { [ Crypt::Perl::Ed25519::Math::gf0() ] } 1 .. 4;
  56         153  
131              
132 14         83 my $digest_ar = _digest32( $self->{'_private'} );
133              
134 14         45 my @sm = (0) x 32;
135 14         24 push @sm, @{$digest_ar}[32 .. 63];
  14         70  
136 14         88 push @sm, unpack( 'C*', $msg );
137              
138 14         236 my @r = unpack 'C*', Digest::SHA::sha512( pack 'C*', @sm[32 .. $#sm] );
139 14         89 Crypt::Perl::Ed25519::Math::reduce(\@r);
140 14         67 Crypt::Perl::Ed25519::Math::scalarbase( \@p, \@r );
141 14         61 @sm[ 0 .. 31 ] = @{ Crypt::Perl::Ed25519::Math::pack(\@p) };
  14         61  
142              
143 14         48 @sm[32 .. 63] = @{$self->{'_public_ar'}};
  14         90  
144              
145 14         305 my @h = unpack 'C*', Digest::SHA::sha512( pack 'C*', @sm );
146 14         84 Crypt::Perl::Ed25519::Math::reduce( \@h );
147              
148 14         89 @x[0 .. 31] = @r[0 .. 31];
149              
150 14         31 for my $i ( 0 .. 31) {
151 448         477 for my $j ( 0 .. 31 ) {
152 14336         15499 $x[ $i + $j ] += $h[$i] * $digest_ar->[$j];
153             }
154             }
155              
156 14         130 my @latter_sm = @sm[32 .. $#sm];
157              
158 14         79 Crypt::Perl::Ed25519::Math::modL( \@latter_sm, \@x );
159              
160 14         100 @sm[32 .. $#sm] = @latter_sm;
161              
162 14         429 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   32 my ($private) = @_;
180              
181 15         46 my $digest_ar = _digest32($private);
182              
183 15         44 my $p = [ map { [ Crypt::Perl::Ed25519::Math::gf0() ] } 0 .. 3 ];
  60         126  
184              
185             # private key is 32 bytes for private part
186             # plus 32 bytes for the public part
187              
188 15         66 Crypt::Perl::Ed25519::Math::scalarbase($p, $digest_ar);
189 15         98 my $pk = Crypt::Perl::Ed25519::Math::pack($p);
190              
191 15         133 return \@$pk;
192             }
193              
194             sub _digest32 {
195 29     29   75 my ($seed) = @_;
196              
197 29         391 my @digest = unpack 'C*', Digest::SHA::sha512($seed);
198 29         80 $digest[0] &= 0xf8; #248
199 29         54 $digest[31] &= 0x7f; #127
200 29         52 $digest[31] |= 0x40; # 64
201              
202 29         71 return \@digest;
203             }
204              
205             1;