File Coverage

blib/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 7     7   307953 use strict;
  7         27  
  7         243  
36 7     7   35 use warnings;
  7         13  
  7         183  
37              
38 7     7   31 use Try::Tiny;
  7         11  
  7         327  
39              
40 7     7   1117 use Crypt::Perl::BigInt ();
  7         17  
  7         108  
41 7     7   33 use Crypt::Perl::X ();
  7         13  
  7         226  
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 7     7   37 use constant CURVE_EQUIVALENCY => qw( p a b n gx gy );
  7         12  
  7         522  
57              
58 7     7   58 use constant GETTER_CURVE_ORDER => ( CURVE_EQUIVALENCY(), 'h', 'seed' );
  7         15  
  7         5338  
59              
60             sub get_oid_for_curve_name {
61 328     328 0 971 my ($name) = @_;
62              
63 328         931 my $name_alt = $name;
64 328         738 $name_alt =~ tr<-><_>;
65              
66 328         4385 require Crypt::Perl::ECDSA::EC::CurvesDB;
67              
68 328         3384 my $translator_cr = Crypt::Perl::ECDSA::EC::CurvesDB->can("OID_$name_alt");
69 328 50       1181 die Crypt::Perl::X::create('ECDSA::NoCurveForName', $name) if !$translator_cr;
70              
71 328         2457 return $translator_cr->();
72             }
73              
74             sub get_curve_name_by_data {
75 69     69 0 218 my ($data_hr) = @_;
76              
77 69         899 my %hex_data = map { $_ => substr( $data_hr->{$_}->as_hex(), 2 ) } keys %$data_hr;
  526         425972  
78              
79 69         70261 require Crypt::Perl::ECDSA::EC::CurvesDB;
80              
81 69         410 my $ns = \%Crypt::Perl::ECDSA::EC::CurvesDB::;
82              
83             NS_KEY:
84 69         18750 for my $key ( sort keys %$ns ) {
85 6717 100       12444 next if substr($key, 0, 4) ne 'OID_';
86              
87 2173         2646 my $oid;
88 2173 100       5126 if ('SCALAR' eq ref $ns->{$key}) {
    50          
89 1353         1803 $oid = ${ $ns->{$key} };
  1353         2770  
90             }
91 820         5211 elsif ( *{$ns->{$key}}{'CODE'} ) {
92 820         2500 $oid = $ns->{$key}->();
93             }
94             else {
95 0         0 next;
96             }
97              
98             #Avoid creating extra BigInt objects.
99 2173         3164 my $db_hex_data_hr;
100             try {
101 2173     2173   90664 $db_hex_data_hr = _get_curve_hex_data_by_oid($oid);
102             }
103             catch {
104 784 50   784   11145 if ( !try { $_->isa('Crypt::Perl::X::ECDSA::NoCurveForOID') } ) {
  784         21243  
105 0         0 local $@ = $_;
106 0         0 die;
107             }
108 2173         10062 };
109              
110 2173 100       27515 next if !$db_hex_data_hr; #i.e., if we have no params for the OID
111              
112 1389         2968 for my $k ( CURVE_EQUIVALENCY() ) {
113 1754 100       5916 next NS_KEY if $hex_data{$k} ne $db_hex_data_hr->{$k};
114             }
115              
116             #We got a match!
117              
118 69         262 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 69         187 $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 69         161 for my $k ( qw( h seed ) ) {
129 138 50 66     688 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 69         996 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 215     215 0 135486 my ($name) = @_;
142              
143 215         946 my $oid = get_oid_for_curve_name($name);
144              
145 215         687 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 531     531 0 1699 my ($oid) = @_;
152              
153 531         2260 my $data_hr = _get_curve_hex_data_by_oid($oid);
154              
155 331         3051 $_ = Crypt::Perl::BigInt->from_hex($_) for values %$data_hr;
156              
157 331         1759906 return $data_hr;
158             }
159              
160             sub _get_curve_hex_data_by_oid {
161 2704     2704   4831 my ($oid) = @_;
162              
163 2704         5161 my $const = "CURVE_$oid";
164 2704         4463 $const =~ tr<.><_>;
165              
166 2704         12007 require Crypt::Perl::ECDSA::EC::CurvesDB;
167              
168 2704         13943 my $getter_cr = Crypt::Perl::ECDSA::EC::CurvesDB->can($const);
169 2704 100       7739 die Crypt::Perl::X::create('ECDSA::NoCurveForOID', $oid) if !$getter_cr;
170              
171 1720         2340 my %data;
172 1720         16342 @data{ GETTER_CURVE_ORDER() } = $getter_cr->();
173              
174 1720 100       4999 delete $data{'seed'} if !$data{'seed'};
175              
176 1720         4776 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;