File Coverage

lib/Crypt/Perl/ECDSA/Utils.pm
Criterion Covered Total %
statement 42 46 91.3
branch 11 16 68.7
condition n/a
subroutine 8 8 100.0
pod 0 4 0.0
total 61 74 82.4


line stmt bran cond sub pod time code
1             package Crypt::Perl::ECDSA::Utils;
2              
3             =encoding utf-8
4              
5             =head1 NAME
6              
7             Crypt::Perl::ECDSA::Utils
8              
9             =head1 DISCUSSION
10              
11             This interface is undocumented for now.
12              
13             =cut
14              
15 7     7   40 use strict;
  7         13  
  7         166  
16 7     7   28 use warnings;
  7         11  
  7         130  
17              
18 7     7   2388 use Crypt::Perl::ECDSA::Math ();
  7         18  
  7         108  
19 7     7   55 use Crypt::Perl::X ();
  7         10  
  7         5863  
20              
21             #Splits the combined (uncompressed) generator or the public key
22             #into its two component halves (octet strings).
23             sub split_G_or_public {
24 401     401 0 1364 my ($bytes_str) = @_;
25              
26 401 50       1340 die Crypt::Perl::X::create('Generic', "Only bytes, not “$bytes_str”!") if ref $bytes_str;
27              
28 401         1347 my $gen_prefix = ord( substr $bytes_str, 0, 1);
29              
30 401 50       1339 if ( $gen_prefix ne 0x04 ) {
31 0         0 die Crypt::Perl::X::create('Generic', "Unrecognized generator or public key prefix/type ($gen_prefix)!");
32             }
33              
34             #Should never happen, but.
35 401 50       1474 if ( !(length($bytes_str) % 2) ) {
36 0         0 die Crypt::Perl::X::create('Generic', "Invalid generator or public key: length must be uneven" );
37             }
38              
39 401         1161 my $len = (length($bytes_str) - 1) / 2;
40              
41 401         7895 return unpack( "x a$len a$len", $bytes_str );
42             }
43              
44             sub compress_point {
45 7     7 0 16 my ($pub_bin) = @_;
46              
47 7 50       36 if (substr($pub_bin, 0, 1) ne "\x04") {
48 0         0 die( sprintf "Invalid point to compress: %v.02x", $pub_bin );
49             }
50              
51 7 100       32 my $first_octet = (ord( substr $pub_bin, -1 ) % 2) ? "\x03" : "\x02";
52              
53 7         20 my ($xb) = split_G_or_public( $pub_bin );
54              
55 7         33 return( $first_octet . $xb );
56             }
57              
58             #$pub_bin is a string; $p/$a/$b are BigInt
59             #returns a string
60             sub decompress_point {
61 112     112 0 798 my ($cpub_bin, $p, $a, $b) = @_;
62              
63 112         288 my $y_is_even = 0;
64 112         875 my $octet1 = substr($cpub_bin, 0, 1);
65 112 100       1118 if ($octet1 eq "\x02") {
    50          
66 53         179 $y_is_even = 1;
67             }
68             elsif ($octet1 ne "\x03") {
69 0         0 die( sprintf "Invalid point to decompress: %v.02x", $cpub_bin );
70             }
71              
72             #http://stackoverflow.com/questions/17171542/algorithm-for-elliptic-curve-point-compression
73 112         667 my $a_p = $a->copy()->bsub($p);
74              
75 112         20809 my $x = Crypt::Perl::BigInt->from_bytes(substr $cpub_bin, 1);
76 112         93571 my $y = $x->copy()->bmodpow(3, $p);
77              
78 112         292730 my $t2 = $x->copy()->bmul($a)->bmod($p);
79 112         97696 $y->badd($t2)->badd($b);
80 112         14832 $y = Crypt::Perl::ECDSA::Math::tonelli_shanks( $y, $p );
81              
82 112 100       36800119 if (!!$y_is_even eq !!$y->is_odd()) {
83 55         2151 $y->bsub($p)->bneg();
84             }
85              
86 112         13146 return join(
87             q<>,
88             "\x04",
89             pad_bytes_for_asn1(substr($cpub_bin, 1), $p),
90             pad_bytes_for_asn1($y->as_bytes(), $p),
91             );
92             }
93              
94             sub pad_bytes_for_asn1 {
95 1138     1138 0 4527 my ($bytes, $p) = @_;
96              
97 1138         4705 my $nbytes = length $p->as_bytes();
98              
99 1138         5681 substr( $bytes, 0, 0 ) = ("\0" x ($nbytes - length $bytes));
100              
101 1138         7812 return $bytes;
102             }
103              
104             1;