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   280438 use strict;
  7         25  
  7         164  
36 7     7   28 use warnings;
  7         10  
  7         174  
37              
38 7     7   32 use Try::Tiny;
  7         9  
  7         346  
39              
40 7     7   1084 use Crypt::Perl::BigInt ();
  7         14  
  7         136  
41 7     7   30 use Crypt::Perl::X ();
  7         13  
  7         209  
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   31 use constant CURVE_EQUIVALENCY => qw( p a b n gx gy );
  7         13  
  7         468  
57              
58 7     7   34 use constant GETTER_CURVE_ORDER => ( CURVE_EQUIVALENCY(), 'h', 'seed' );
  7         15  
  7         4614  
59              
60             sub get_oid_for_curve_name {
61 298     298 0 748 my ($name) = @_;
62              
63 298         633 my $name_alt = $name;
64 298         635 $name_alt =~ tr<-><_>;
65              
66 298         4027 require Crypt::Perl::ECDSA::EC::CurvesDB;
67              
68 298         2715 my $translator_cr = Crypt::Perl::ECDSA::EC::CurvesDB->can("OID_$name_alt");
69 298 50       1019 die Crypt::Perl::X::create('ECDSA::NoCurveForName', $name) if !$translator_cr;
70              
71 298         1775 return $translator_cr->();
72             }
73              
74             sub get_curve_name_by_data {
75 39     39 0 115 my ($data_hr) = @_;
76              
77 39         467 my %hex_data = map { $_ => substr( $data_hr->{$_}->as_hex(), 2 ) } keys %$data_hr;
  304         239673  
78              
79 39         37841 require Crypt::Perl::ECDSA::EC::CurvesDB;
80              
81 39         129 my $ns = \%Crypt::Perl::ECDSA::EC::CurvesDB::;
82              
83             NS_KEY:
84 39         7206 for my $key ( sort keys %$ns ) {
85 3560 100       5802 next if substr($key, 0, 4) ne 'OID_';
86              
87 1416         1527 my $oid;
88 1416 100       2812 if ('SCALAR' eq ref $ns->{$key}) {
    50          
89 1353         1515 $oid = ${ $ns->{$key} };
  1353         2679  
90             }
91 63         591 elsif ( *{$ns->{$key}}{'CODE'} ) {
92 63         205 $oid = $ns->{$key}->();
93             }
94             else {
95 0         0 next;
96             }
97              
98             #Avoid creating extra BigInt objects.
99 1416         1702 my $db_hex_data_hr;
100             try {
101 1416     1416   58813 $db_hex_data_hr = _get_curve_hex_data_by_oid($oid);
102             }
103             catch {
104 528 50   528   6639 if ( !try { $_->isa('Crypt::Perl::X::ECDSA::NoCurveForOID') } ) {
  528         13601  
105 0         0 local $@ = $_;
106 0         0 die;
107             }
108 1416         5664 };
109              
110 1416 100       16152 next if !$db_hex_data_hr; #i.e., if we have no params for the OID
111              
112 888         1765 for my $k ( CURVE_EQUIVALENCY() ) {
113 1083 100       3271 next NS_KEY if $hex_data{$k} ne $db_hex_data_hr->{$k};
114             }
115              
116             #We got a match!
117              
118 39         98 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         89 $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         91 for my $k ( qw( h seed ) ) {
129 78 50 66     364 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         510 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 116898 my ($name) = @_;
142              
143 215         696 my $oid = get_oid_for_curve_name($name);
144              
145 215         717 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 1538 my ($oid) = @_;
152              
153 530         1987 my $data_hr = _get_curve_hex_data_by_oid($oid);
154              
155 330         2376 $_ = Crypt::Perl::BigInt->from_hex($_) for values %$data_hr;
156              
157 330         1486114 return $data_hr;
158             }
159              
160             sub _get_curve_hex_data_by_oid {
161 1946     1946   3537 my ($oid) = @_;
162              
163 1946         3556 my $const = "CURVE_$oid";
164 1946         3093 $const =~ tr<.><_>;
165              
166 1946         8671 require Crypt::Perl::ECDSA::EC::CurvesDB;
167              
168 1946         10516 my $getter_cr = Crypt::Perl::ECDSA::EC::CurvesDB->can($const);
169 1946 100       5488 die Crypt::Perl::X::create('ECDSA::NoCurveForOID', $oid) if !$getter_cr;
170              
171 1218         1568 my %data;
172 1218         10323 @data{ GETTER_CURVE_ORDER() } = $getter_cr->();
173              
174 1218 100       3164 delete $data{'seed'} if !$data{'seed'};
175              
176 1218         2976 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;