File Coverage

lib/Crypt/Perl/ECDSA/EC/DB.pm
Criterion Covered Total %
statement 71 80 88.7
branch 16 20 80.0
condition 2 3 66.6
subroutine 14 15 93.3
pod 0 4 0.0
total 103 122 84.4


line stmt bran cond sub pod time code
1             package Crypt::Perl::ECDSA::EC::DB;
2              
3             =encoding utf-8
4              
5             =head1 NAME
6              
7             Crypt::Perl::ECDSA::EC::DB - Interface to this module’s CurvesDB datastore
8              
9             =head1 SYNOPSIS
10              
11             my $oid = Crypt::Perl::ECDSA::EC::DB::get_oid_for_curve_name('prime256v1');
12              
13             my $data_hr = Crypt::Perl::ECDSA::EC::DB::get_curve_data_by_oid('1.2.840.10045.3.1.7');
14              
15             my $name = Crypt::Perl::ECDSA::EC::DB::get_curve_name_by_data(
16             p => ..., #isa Crypt::Perl::BigInt
17             a => ..., #isa Crypt::Perl::BigInt
18             b => ..., #isa Crypt::Perl::BigInt
19             n => ..., #isa Crypt::Perl::BigInt
20             h => ..., #isa Crypt::Perl::BigInt
21             gx => ..., #isa Crypt::Perl::BigInt
22             gy => ..., #isa Crypt::Perl::BigInt
23             seed => ..., #isa Crypt::Perl::BigInt, optional
24             );
25              
26             #The opposite query from the preceding.
27             my $data_hr = Crypt::Perl::ECDSA::EC::DB::get_curve_data_by_name('prime256v1');
28              
29             =head1 DISCUSSION
30              
31             This interface is undocumented for now.
32              
33             =cut
34              
35 10     10   9255 use strict;
  10         26  
  10         314  
36 10     10   59 use warnings;
  10         33  
  10         242  
37              
38 10     10   1131 use Try::Tiny;
  10         4279  
  10         637  
39              
40 10     10   512 use Crypt::Perl::BigInt ();
  10         23  
  10         184  
41 10     10   70 use Crypt::Perl::X ();
  10         36  
  10         357  
42              
43             #----------------------------------------------------------------------
44             # p = prime
45             # generator (uncompressed) = \x04 . gx . gy
46             # n = order
47             # h = cofactor
48             #
49             # a and b fit into the general form for an elliptic curve:
50             #
51             # y^2 = x^3 + ax + b
52             #----------------------------------------------------------------------
53              
54             #“h” is determinable from the other curve parameters
55             #and should not be considered necessary to match.
56 10     10   65 use constant CURVE_EQUIVALENCY => qw( p a b n gx gy );
  10         49  
  10         889  
57              
58 10     10   100 use constant GETTER_CURVE_ORDER => ( CURVE_EQUIVALENCY(), 'h', 'seed' );
  10         27  
  10         9730  
59              
60             sub get_oid_for_curve_name {
61 300     300 0 867 my ($name) = @_;
62              
63 300         866 my $name_alt = $name;
64 300         875 $name_alt =~ tr<-><_>;
65              
66 300         5780 require Crypt::Perl::ECDSA::EC::CurvesDB;
67              
68 300         3207 my $translator_cr = Crypt::Perl::ECDSA::EC::CurvesDB->can("OID_$name_alt");
69 300 50       1177 die Crypt::Perl::X::create('ECDSA::NoCurveForName', $name) if !$translator_cr;
70              
71 300         2312 return $translator_cr->();
72             }
73              
74             sub get_curve_name_by_data {
75 43     43 0 135 my ($data_hr) = @_;
76              
77 43         435 my %hex_data = map { $_ => substr( $data_hr->{$_}->as_hex(), 2 ) } keys %$data_hr;
  336         8941  
78              
79 43         3117 require Crypt::Perl::ECDSA::EC::CurvesDB;
80              
81 43         221 my $ns = \%Crypt::Perl::ECDSA::EC::CurvesDB::;
82              
83             NS_KEY:
84 43         10750 for my $key ( sort keys %$ns ) {
85 3957 100       8331 next if substr($key, 0, 4) ne 'OID_';
86              
87 1589         2292 my $oid;
88 1589 100       4965 if ('SCALAR' eq ref $ns->{$key}) {
    50          
89 1519         2166 $oid = ${ $ns->{$key} };
  1519         4593  
90             }
91 70         986 elsif ( *{$ns->{$key}}{'CODE'} ) {
92 70         308 $oid = $ns->{$key}->();
93             }
94             else {
95 0         0 next;
96             }
97              
98             #Avoid creating extra BigInt objects.
99 1589         2383 my $db_hex_data_hr;
100             try {
101 1589     1589   82376 $db_hex_data_hr = _get_curve_hex_data_by_oid($oid);
102             }
103             catch {
104 592 50   592   9940 if ( !try { $_->isa('Crypt::Perl::X::ECDSA::NoCurveForOID') } ) {
  592         19473  
105 0         0 local $@ = $_;
106 0         0 die;
107             }
108 1589         8118 };
109              
110 1589 100       23002 next if !$db_hex_data_hr; #i.e., if we have no params for the OID
111              
112 997         2481 for my $k ( CURVE_EQUIVALENCY() ) {
113 1212 100       4788 next NS_KEY if $hex_data{$k} ne $db_hex_data_hr->{$k};
114             }
115              
116             #We got a match!
117              
118 43         187 my $name = substr($key, 4); # strip leading “OID_”
119              
120             #We store dashes as underscores so we can use the namespace.
121             #Hopefully no curve OID name will ever contain an underscore!!
122 43         136 $name =~ tr<_><->;
123              
124             #… but let’s make sure the extras (cofactor and seed) are correct,
125             #if given. Note that all curves have cofactor == 1 except secp112r2 and
126             #secp128r2, both of which have cofactor == 4.
127             #
128 43         149 for my $k ( qw( h seed ) ) {
129 86 50 66     600 if ( defined $hex_data{$k} && $hex_data{$k} ne $db_hex_data_hr->{$k} ) {
130 0         0 die Crypt::Perl::X::create('Generic', "Curve parameters match “$name”, but “$k” ($hex_data{$k}) does not match expected value ($db_hex_data_hr->{$k})!");
131             }
132             }
133              
134 43         692 return $name;
135             }
136              
137 0         0 die Crypt::Perl::X::create('ECDSA::NoCurveForParameters', %hex_data);
138             }
139              
140             sub get_curve_data_by_name {
141 216     216 0 155783 my ($name) = @_;
142              
143 216         1695 my $oid = get_oid_for_curve_name($name);
144              
145 216         600 return get_curve_data_by_oid( $oid );
146             }
147              
148             #This returns the same information as
149             #Crypt::Perl::ECDSA::ECParameters::normalize().
150             sub get_curve_data_by_oid {
151 545     545 0 1976 my ($oid) = @_;
152              
153 545         2476 my $data_hr = _get_curve_hex_data_by_oid($oid);
154              
155 345         3166 $_ = Crypt::Perl::BigInt->from_hex($_) for values %$data_hr;
156              
157 345         509774 return $data_hr;
158             }
159              
160             sub _get_curve_hex_data_by_oid {
161 2134     2134   4868 my ($oid) = @_;
162              
163 2134         4893 my $const = "CURVE_$oid";
164 2134         4080 $const =~ tr<.><_>;
165              
166 2134         11203 require Crypt::Perl::ECDSA::EC::CurvesDB;
167              
168 2134         14368 my $getter_cr = Crypt::Perl::ECDSA::EC::CurvesDB->can($const);
169 2134 100       7050 die Crypt::Perl::X::create('ECDSA::NoCurveForOID', $oid) if !$getter_cr;
170              
171 1342         2257 my %data;
172 1342         16008 @data{ GETTER_CURVE_ORDER() } = $getter_cr->();
173              
174 1342 100       4535 delete $data{'seed'} if !$data{'seed'};
175              
176 1342         3956 return \%data;
177             }
178              
179             sub _upgrade_hex_to_bigint {
180 0     0     my ($data_hr) = @_;
181              
182 0           $_ = Crypt::Perl::BigInt->from_hex($_) for @{$data_hr}{ GETTER_CURVE_ORDER() };
  0            
183              
184 0           return;
185             }
186              
187             1;