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 5     5   72025 use strict;
  5         20  
  5         134  
4 5     5   23 use warnings;
  5         8  
  5         170  
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 5         25 use parent qw(
47             Crypt::Perl::Ed25519::KeyBase
48 5     5   364 );
  5         253  
49              
50 5     5   221 use Digest::SHA ();
  5         8  
  5         85  
51              
52 5     5   21 use Crypt::Perl::Ed25519::Math;
  5         10  
  5         126  
53              
54 5         294 use constant _ASN1 => q<
55             FG_Key ::= SEQUENCE {
56             version INTEGER,
57             algorithmIdentifier AlgorithmIdentifier,
58             privateKey PrivateKey
59             }
60              
61             PrivateKey ::= OCTET STRING
62 5     5   20 >;
  5         9  
63              
64 5     5   27 use constant _PEM_HEADER => 'PRIVATE KEY';
  5         6  
  5         4164  
65              
66             sub new {
67 16     16 0 7157 my ($class, $priv, $pub) = @_;
68              
69 16 100 66     84 if (defined($priv) && length($priv)) {
70 8         39 $class->_verify_binary_key_part($priv);
71             }
72             else {
73 8         16 $priv = do {
74 8         642 require Crypt::Perl::RNG;
75 8         36 Crypt::Perl::RNG::bytes(32);
76             };
77             }
78              
79 16         4730 my ($pub_ar);
80              
81 16 100 66     57 if (defined($pub) && length($pub)) {
82 1         4 $class->_verify_binary_key_part($pub);
83              
84 1         4 $pub_ar = unpack 'C*', $pub;
85             }
86             else {
87 15         47 $pub_ar = _deduce_public_from_private($priv);
88 15         121 $pub = pack 'C*', @$pub_ar;
89             }
90              
91 16         269 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         18 require MIME::Base64;
105              
106 1         3 $struct->{'d'} = MIME::Base64::encode_base64url($self->{'_private'});
107              
108 1         45 return $struct;
109             }
110              
111             sub get_private {
112 2     2 0 14 my ($self) = @_;
113              
114 2         42 return $self->{'_private'};
115             }
116              
117             sub get_public_key {
118 1     1 0 92 my ($self) = @_;
119              
120 1         774 require Crypt::Perl::Ed25519::PublicKey;
121              
122 1         80 return Crypt::Perl::Ed25519::PublicKey->new( $self->{'_public'} );
123             }
124              
125             sub sign {
126 14     14 0 833 my ($self, $msg) = @_;
127              
128 14         62 my @x = (0) x 64;
129              
130 14         49 my @p = map { [ Crypt::Perl::Ed25519::Math::gf0() ] } 1 .. 4;
  56         161  
131              
132 14         68 my $digest_ar = _digest32( $self->{'_private'} );
133              
134 14         56 my @sm = (0) x 32;
135 14         34 push @sm, @{$digest_ar}[32 .. 63];
  14         58  
136 14         94 push @sm, unpack( 'C*', $msg );
137              
138 14         278 my @r = unpack 'C*', Digest::SHA::sha512( pack 'C*', @sm[32 .. $#sm] );
139 14         108 Crypt::Perl::Ed25519::Math::reduce(\@r);
140 14         64 Crypt::Perl::Ed25519::Math::scalarbase( \@p, \@r );
141 14         55 @sm[ 0 .. 31 ] = @{ Crypt::Perl::Ed25519::Math::pack(\@p) };
  14         57  
142              
143 14         42 @sm[32 .. 63] = @{$self->{'_public_ar'}};
  14         79  
144              
145 14         292 my @h = unpack 'C*', Digest::SHA::sha512( pack 'C*', @sm );
146 14         91 Crypt::Perl::Ed25519::Math::reduce( \@h );
147              
148 14         107 @x[0 .. 31] = @r[0 .. 31];
149              
150 14         34 for my $i ( 0 .. 31) {
151 448         524 for my $j ( 0 .. 31 ) {
152 14336         17372 $x[ $i + $j ] += $h[$i] * $digest_ar->[$j];
153             }
154             }
155              
156 14         135 my @latter_sm = @sm[32 .. $#sm];
157              
158 14         72 Crypt::Perl::Ed25519::Math::modL( \@latter_sm, \@x );
159              
160 14         128 @sm[32 .. $#sm] = @latter_sm;
161              
162 14         432 return pack 'C*', @sm[ 0 .. ($self->SIGN_BYTE_LENGTH - 1) ];
163             }
164              
165             #----------------------------------------------------------------------
166              
167             sub _to_der_args {
168 1     1   4 my ($self) = @_;
169              
170             return (
171              
172             # The leading bytes are the encoding of the inner CurvePrivateKey
173             # (i.e., OCTET STRING).
174 1         11 privateKey => "\x04\x20" . $self->{'_private'},
175             );
176             }
177              
178             sub _deduce_public_from_private {
179 15     15   31 my ($private) = @_;
180              
181 15         42 my $digest_ar = _digest32($private);
182              
183 15         41 my $p = [ map { [ Crypt::Perl::Ed25519::Math::gf0() ] } 0 .. 3 ];
  60         138  
184              
185             # private key is 32 bytes for private part
186             # plus 32 bytes for the public part
187              
188 15         67 Crypt::Perl::Ed25519::Math::scalarbase($p, $digest_ar);
189 15         86 my $pk = Crypt::Perl::Ed25519::Math::pack($p);
190              
191 15         119 return \@$pk;
192             }
193              
194             sub _digest32 {
195 29     29   75 my ($seed) = @_;
196              
197 29         410 my @digest = unpack 'C*', Digest::SHA::sha512($seed);
198 29         72 $digest[0] &= 0xf8; #248
199 29         47 $digest[31] &= 0x7f; #127
200 29         50 $digest[31] |= 0x40; # 64
201              
202 29         61 return \@digest;
203             }
204              
205             1;