File Coverage

blib/lib/Crypt/Perl/Ed25519/KeyBase.pm
Criterion Covered Total %
statement 64 70 91.4
branch 5 8 62.5
condition n/a
subroutine 14 15 93.3
pod 0 5 0.0
total 83 98 84.6


line stmt bran cond sub pod time code
1             package Crypt::Perl::Ed25519::KeyBase;
2              
3 4     4   1344 use strict;
  4         6  
  4         81  
4 4     4   15 use warnings;
  4         7  
  4         71  
5              
6 4     4   1359 use Crypt::Perl::Ed25519::Math;
  4         10  
  4         103  
7 4     4   346 use Crypt::Perl::X;
  4         6  
  4         70  
8              
9 4     4   1226 use Digest::SHA ();
  4         5717  
  4         78  
10              
11 4     4   19 use parent qw( Crypt::Perl::KeyBase );
  4         24  
  4         25  
12              
13             use constant {
14 4         240 SIGN_BYTE_LENGTH => 64,
15             OID_Ed25519 => '1.3.101.112',
16 4     4   178 };
  4         5  
17              
18 4         146 use constant _ASN1_BASE => q<
19             -- cf. RFC 3280 4.1.1.2
20             -- XXX COPIED FROM RSA TEMPLATE MODULE
21             AlgorithmIdentifier ::= SEQUENCE {
22             algorithm OBJECT IDENTIFIER,
23             parameters ANY DEFINED BY algorithm OPTIONAL
24             }
25 4     4   19 >;
  4         6  
26              
27 4     4   17 use constant _JWK_THUMBPRINT_JSON_ORDER => qw( crv kty x );
  4         7  
  4         2070  
28              
29             sub to_der {
30 3     3 0 7 my ($self) = @_;
31              
32 3         19 require Crypt::Perl::ASN1;
33 3         14 my $asn1 = Crypt::Perl::ASN1->new()->prepare(
34             _ASN1_BASE() . $self->_ASN1()
35             )->find('FG_Key');
36              
37 3         49 return $asn1->encode( {
38             version => 0,
39             algorithmIdentifier => {
40             algorithm => OID_Ed25519(),
41             },
42             $self->_to_der_args(),
43             } );
44             }
45              
46             # TODO: refactor; duplicated w/ RSA
47             sub to_pem {
48 0     0 0 0 my ($self) = @_;
49              
50 0         0 require Crypt::Format;
51 0         0 return Crypt::Format::der2pem( $self->to_der(), $self->_PEM_HEADER() );
52             }
53              
54             sub get_public {
55 6     6 0 62 my ($self) = @_;
56              
57 6         66 return $self->{'_public'};
58             }
59              
60             sub get_struct_for_public_jwk {
61 4     4 0 383 my ($self) = @_;
62              
63 4         608 require MIME::Base64;
64              
65             return {
66             kty => 'OKP',
67             crv => 'Ed25519',
68 4         703 x => MIME::Base64::encode_base64url($self->{'_public'}),
69             }
70             }
71              
72             sub verify {
73 21     21 0 12003 my ($self, $msg, $sig) = @_;
74              
75 21 50       82 if (SIGN_BYTE_LENGTH() != length $sig) {
76 0         0 die Crypt::Perl::X::create('Generic', sprintf('Invalid length (%d) of Ed25519 signature: %v.02x', length($sig), $sig));
77             }
78              
79 21         56 my $public_ar = $self->{'_public_ar'};
80              
81 21         148 my $sig_ar = [ unpack 'C*', $sig ];
82              
83 21         133 my @sm = ( @$sig_ar, unpack( 'C*', $msg ) );
84 21         99 my @m = (0) x @sm;
85              
86 21         85 @m = @sm;
87              
88 21         36 @m[ 32 .. 63 ] = @{$public_ar};
  21         82  
89              
90 21         60 my @p = map { [ Crypt::Perl::Ed25519::Math::gf0() ] } 1 .. 4;
  84         203  
91 21         61 my @q = map { [ Crypt::Perl::Ed25519::Math::gf0() ] } 1 .. 4;
  84         139  
92              
93 21 50       93 if ( Crypt::Perl::Ed25519::Math::unpackneg( \@q, $public_ar ) ) {
94 0         0 return !1;
95             }
96              
97 21         479 my @h = unpack 'C*', Digest::SHA::sha512( pack 'C*', @m );
98 21         116 Crypt::Perl::Ed25519::Math::reduce(\@h);
99              
100 21         96 Crypt::Perl::Ed25519::Math::scalarmult(\@p, \@q, \@h);
101              
102 21         284 my @latter_sm = @sm[32 .. $#sm];
103 21         131 Crypt::Perl::Ed25519::Math::scalarbase( \@q, \@latter_sm );
104 21         209 @sm[32 .. $#sm] = @latter_sm;
105              
106 21         115 Crypt::Perl::Ed25519::Math::add( \@p, \@q );
107 21         82 my $t_ar = Crypt::Perl::Ed25519::Math::pack(\@p);
108              
109 21 100       110 if( Crypt::Perl::Ed25519::Math::crypto_verify_32(\@sm, 0, $t_ar, 0)) {
110 9         261 return !1;
111             }
112              
113 12         24 my $n = @sm - SIGN_BYTE_LENGTH;
114              
115 12         319 return $n >= 0;
116             }
117              
118             sub _verify_binary_key_part {
119 12 50   12   34 if (32 != length $_[1]) {
120 0         0 die Crypt::Perl::X::create('Generic', sprintf('Invalid length (%d) of Ed25519 key piece: %v.02x', length($_[1]), $_[1]));
121             }
122              
123 12         22 return;
124             }
125              
126             1;