package Crypt::Perl::ECDSA::EC::DB;
=encoding utf-8
=head1 NAME
Crypt::Perl::ECDSA::EC::DB - Interface to this module’s CurvesDB datastore
=head1 SYNOPSIS
my $oid = Crypt::Perl::ECDSA::EC::DB::get_oid_for_curve_name('prime256v1');
my $data_hr = Crypt::Perl::ECDSA::EC::DB::get_curve_data_by_oid('1.2.840.10045.3.1.7');
my $name = Crypt::Perl::ECDSA::EC::DB::get_curve_name_by_data(
p => ..., #isa Crypt::Perl::BigInt
a => ..., #isa Crypt::Perl::BigInt
b => ..., #isa Crypt::Perl::BigInt
n => ..., #isa Crypt::Perl::BigInt
h => ..., #isa Crypt::Perl::BigInt
gx => ..., #isa Crypt::Perl::BigInt
gy => ..., #isa Crypt::Perl::BigInt
seed => ..., #isa Crypt::Perl::BigInt, optional
);
#The opposite query from the preceding.
my $data_hr = Crypt::Perl::ECDSA::EC::DB::get_curve_data_by_name('prime256v1');
=head1 DISCUSSION
This interface is undocumented for now.
=cut
use strict;
use warnings;
use Try::Tiny;
use Crypt::Perl::BigInt ();
use Crypt::Perl::X ();
#----------------------------------------------------------------------
# p = prime
# generator (uncompressed) = \x04 . gx . gy
# n = order
# h = cofactor
#
# a and b fit into the general form for an elliptic curve:
#
# y^2 = x^3 + ax + b
#----------------------------------------------------------------------
#“h” is determinable from the other curve parameters
#and should not be considered necessary to match.
use constant CURVE_EQUIVALENCY => qw( p a b n gx gy );
use constant GETTER_CURVE_ORDER => ( CURVE_EQUIVALENCY(), 'h', 'seed' );
sub get_oid_for_curve_name {
my ($name) = @_;
my $name_alt = $name;
$name_alt =~ tr<-><_>;
require Crypt::Perl::ECDSA::EC::CurvesDB;
my $translator_cr = Crypt::Perl::ECDSA::EC::CurvesDB->can("OID_$name_alt");
die Crypt::Perl::X::create('ECDSA::NoCurveForName', $name) if !$translator_cr;
return $translator_cr->();
}
sub get_curve_name_by_data {
my ($data_hr) = @_;
my %hex_data = map { $_ => substr( $data_hr->{$_}->as_hex(), 2 ) } keys %$data_hr;
require Crypt::Perl::ECDSA::EC::CurvesDB;
my $ns = \%Crypt::Perl::ECDSA::EC::CurvesDB::;
NS_KEY:
for my $key ( sort keys %$ns ) {
next if substr($key, 0, 4) ne 'OID_';
my $oid;
if ('SCALAR' eq ref $ns->{$key}) {
$oid = ${ $ns->{$key} };
}
elsif ( *{$ns->{$key}}{'CODE'} ) {
$oid = $ns->{$key}->();
}
else {
next;
}
#Avoid creating extra BigInt objects.
my $db_hex_data_hr;
try {
$db_hex_data_hr = _get_curve_hex_data_by_oid($oid);
}
catch {
if ( !try { $_->isa('Crypt::Perl::X::ECDSA::NoCurveForOID') } ) {
local $@ = $_;
die;
}
};
next if !$db_hex_data_hr; #i.e., if we have no params for the OID
for my $k ( CURVE_EQUIVALENCY() ) {
next NS_KEY if $hex_data{$k} ne $db_hex_data_hr->{$k};
}
#We got a match!
my $name = substr($key, 4); # strip leading “OID_”
#We store dashes as underscores so we can use the namespace.
#Hopefully no curve OID name will ever contain an underscore!!
$name =~ tr<_><->;
#… but let’s make sure the extras (cofactor and seed) are correct,
#if given. Note that all curves have cofactor == 1 except secp112r2 and
#secp128r2, both of which have cofactor == 4.
#
for my $k ( qw( h seed ) ) {
if ( defined $hex_data{$k} && $hex_data{$k} ne $db_hex_data_hr->{$k} ) {
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})!");
}
}
return $name;
}
die Crypt::Perl::X::create('ECDSA::NoCurveForParameters', %hex_data);
}
sub get_curve_data_by_name {
my ($name) = @_;
my $oid = get_oid_for_curve_name($name);
return get_curve_data_by_oid( $oid );
}
#This returns the same information as
#Crypt::Perl::ECDSA::ECParameters::normalize().
sub get_curve_data_by_oid {
my ($oid) = @_;
my $data_hr = _get_curve_hex_data_by_oid($oid);
$_ = Crypt::Perl::BigInt->from_hex($_) for values %$data_hr;
return $data_hr;
}
sub _get_curve_hex_data_by_oid {
my ($oid) = @_;
my $const = "CURVE_$oid";
$const =~ tr<.><_>;
require Crypt::Perl::ECDSA::EC::CurvesDB;
my $getter_cr = Crypt::Perl::ECDSA::EC::CurvesDB->can($const);
die Crypt::Perl::X::create('ECDSA::NoCurveForOID', $oid) if !$getter_cr;
my %data;
@data{ GETTER_CURVE_ORDER() } = $getter_cr->();
delete $data{'seed'} if !$data{'seed'};
return \%data;
}
sub _upgrade_hex_to_bigint {
my ($data_hr) = @_;
$_ = Crypt::Perl::BigInt->from_hex($_) for @{$data_hr}{ GETTER_CURVE_ORDER() };
return;
}
1;