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