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   249644 use strict;
  7         24  
  7         146  
36 7     7   351 use warnings;
  7         11  
  7         158  
37              
38 7     7   28 use Try::Tiny;
  7         10  
  7         288  
39              
40 7     7   1282 use Crypt::Perl::BigInt ();
  7         15  
  7         109  
41 7     7   32 use Crypt::Perl::X ();
  7         12  
  7         220  
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         13  
  7         473  
57              
58 7     7   38 use constant GETTER_CURVE_ORDER => ( CURVE_EQUIVALENCY(), 'h', 'seed' );
  7         13  
  7         5076  
59              
60             sub get_oid_for_curve_name {
61 298     298 0 602 my ($name) = @_;
62              
63 298         554 my $name_alt = $name;
64 298         611 $name_alt =~ tr<-><_>;
65              
66 298         3725 require Crypt::Perl::ECDSA::EC::CurvesDB;
67              
68 298         2651 my $translator_cr = Crypt::Perl::ECDSA::EC::CurvesDB->can("OID_$name_alt");
69 298 50       916 die Crypt::Perl::X::create('ECDSA::NoCurveForName', $name) if !$translator_cr;
70              
71 298         1602 return $translator_cr->();
72             }
73              
74             sub get_curve_name_by_data {
75 39     39 0 115 my ($data_hr) = @_;
76              
77 39         507 my %hex_data = map { $_ => substr( $data_hr->{$_}->as_hex(), 2 ) } keys %$data_hr;
  304         246901  
78              
79 39         29640 require Crypt::Perl::ECDSA::EC::CurvesDB;
80              
81 39         146 my $ns = \%Crypt::Perl::ECDSA::EC::CurvesDB::;
82              
83             NS_KEY:
84 39         7146 for my $key ( sort keys %$ns ) {
85 3560 100       5832 next if substr($key, 0, 4) ne 'OID_';
86              
87 1416         1477 my $oid;
88 1416 100       2810 if ('SCALAR' eq ref $ns->{$key}) {
    50          
89 1353         1474 $oid = ${ $ns->{$key} };
  1353         2384  
90             }
91 63         497 elsif ( *{$ns->{$key}}{'CODE'} ) {
92 63         211 $oid = $ns->{$key}->();
93             }
94             else {
95 0         0 next;
96             }
97              
98             #Avoid creating extra BigInt objects.
99 1416         1583 my $db_hex_data_hr;
100             try {
101 1416     1416   57242 $db_hex_data_hr = _get_curve_hex_data_by_oid($oid);
102             }
103             catch {
104 528 50   528   6446 if ( !try { $_->isa('Crypt::Perl::X::ECDSA::NoCurveForOID') } ) {
  528         13163  
105 0         0 local $@ = $_;
106 0         0 die;
107             }
108 1416         5544 };
109              
110 1416 100       15400 next if !$db_hex_data_hr; #i.e., if we have no params for the OID
111              
112 888         1752 for my $k ( CURVE_EQUIVALENCY() ) {
113 1083 100       3123 next NS_KEY if $hex_data{$k} ne $db_hex_data_hr->{$k};
114             }
115              
116             #We got a match!
117              
118 39         89 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 39         86 $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 39         88 for my $k ( qw( h seed ) ) {
129 78 50 66     463 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 39         545 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 110036 my ($name) = @_;
142              
143 215         552 my $oid = get_oid_for_curve_name($name);
144              
145 215         611 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 530     530 0 1630 my ($oid) = @_;
152              
153 530         2027 my $data_hr = _get_curve_hex_data_by_oid($oid);
154              
155 330         2453 $_ = Crypt::Perl::BigInt->from_hex($_) for values %$data_hr;
156              
157 330         1453783 return $data_hr;
158             }
159              
160             sub _get_curve_hex_data_by_oid {
161 1946     1946   3630 my ($oid) = @_;
162              
163 1946         3342 my $const = "CURVE_$oid";
164 1946         2923 $const =~ tr<.><_>;
165              
166 1946         8595 require Crypt::Perl::ECDSA::EC::CurvesDB;
167              
168 1946         10028 my $getter_cr = Crypt::Perl::ECDSA::EC::CurvesDB->can($const);
169 1946 100       5342 die Crypt::Perl::X::create('ECDSA::NoCurveForOID', $oid) if !$getter_cr;
170              
171 1218         1556 my %data;
172 1218         10322 @data{ GETTER_CURVE_ORDER() } = $getter_cr->();
173              
174 1218 100       3085 delete $data{'seed'} if !$data{'seed'};
175              
176 1218         2852 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;