File Coverage

blib/lib/Crypt/EECDH.pm
Criterion Covered Total %
statement 84 87 96.5
branch 19 28 67.8
condition 3 7 42.8
subroutine 15 16 93.7
pod 5 5 100.0
total 126 143 88.1


line stmt bran cond sub pod time code
1             # -*-cperl-*-
2             #
3             # Crypt::EECDH - Simple ephemeral ECDH + AES hybrid cryptosystem
4             # Copyright (c) Ashish Gulhati
5             #
6             # $Id: lib/Crypt/EECDH.pm v1.007 Tue Oct 16 23:45:54 PDT 2018 $
7              
8             package Crypt::EECDH;
9              
10 2     2   136813 use warnings;
  2         15  
  2         66  
11 2     2   20 use strict;
  2         3  
  2         40  
12              
13 2     2   1019 use Crypt::Curve25519;
  2         1840  
  2         119  
14 2     2   1045 use Crypt::Ed25519;
  2         632  
  2         60  
15 2     2   1012 use Crypt::EC_DSA;
  2         158808  
  2         81  
16 2     2   15 use Bytes::Random::Secure;
  2         5  
  2         102  
17 2     2   990 use Crypt::Rijndael;
  2         771  
  2         78  
18 2     2   13 use Digest::SHA qw/sha256 hmac_sha256/;
  2         5  
  2         100  
19 2     2   27 use vars qw( $VERSION $AUTOLOAD );
  2         4  
  2         2072  
20              
21             our ( $VERSION ) = '$Revision: 1.007 $' =~ /\s+([\d\.]+)/;
22              
23             sub new {
24 1     1 1 85 my ($class, %arg) = @_;
25             bless { debug => $arg{Debug} || 0,
26 1   50     13 sigscheme => $arg{SigScheme} || 'ECDSA'
      50        
27             }, $class;
28             }
29              
30             my $format = 'C/a C/a n/a N/a';
31              
32             sub encrypt {
33 2     2 1 1059 my ($self, %arg) = @_;
34              
35 2 50 33     21 if (defined $arg{SigningKey} && defined $arg{Signature}) {
36             # Check signature on $public_key
37 2 100       19 if ($self->sigscheme eq 'Ed25519') {
38 1 50       263 return unless Crypt::Ed25519::verify($arg{PublicKey}, $arg{SigningKey}, $arg{Signature});
39             }
40             else {
41 1         12 my $ecdsa = new Crypt::EC_DSA;
42             return unless $ecdsa->verify( Message => $arg{PublicKey}, Signature => $arg{Signature},
43 1 50       4535 Key => $arg{SigningKey} );
44             }
45             }
46              
47 2         24559625 my $random = Bytes::Random::Secure->new( Bits => 128 );
48 2         208 my $private = curve25519_secret_key($random->bytes(32));
49 2         623424266 my $public = curve25519_public_key($private);
50 2         225 my $shared = curve25519_shared_secret($private, $arg{PublicKey});
51              
52 2         36 my ($encrypt_key, $sign_key) = unpack 'a16 a16', sha256($shared);
53 2         15 my $iv = substr sha256($public), 0, 16;
54 2         33 my $cipher = Crypt::Rijndael->new($encrypt_key, Crypt::Rijndael::MODE_CBC);
55 2         12 $cipher->set_iv($iv);
56              
57 2         9 my $pad_length = 16 - length($arg{Message}) % 16;
58 2         12 my $padding = chr($pad_length) x $pad_length;
59              
60 2         17 my $ciphertext = $cipher->encrypt($arg{Message} . $padding);
61 2         24 my $mac = hmac_sha256($iv . $ciphertext, $sign_key);
62 2         67 return (pack ($format, '', $public, $mac, $ciphertext), $private);
63             }
64              
65             sub decrypt {
66 2     2 1 11 my ($self, %arg) = @_;
67              
68 2         16 my ($options, $public, $mac, $ciphertext) = unpack $format, $arg{Ciphertext};
69 2 50       12 die 'Unknown options' if $options ne '';
70              
71 2         225 my $shared = curve25519_shared_secret($arg{Key}, $public);
72 2         18 my ($encrypt_key, $sign_key) = unpack 'a16 a16', sha256($shared);
73 2         15 my $iv = substr sha256($public), 0, 16;
74 2 50       20 die 'MAC is incorrect' if hmac_sha256($iv . $ciphertext, $sign_key) ne $mac;
75 2         25 my $cipher = Crypt::Rijndael->new($encrypt_key, Crypt::Rijndael::MODE_CBC);
76 2         9 $cipher->set_iv($iv);
77              
78 2         9 my $plaintext = $cipher->decrypt($ciphertext);
79 2         7 my $pad_length = ord substr $plaintext, -1;
80 2 50       14 substr($plaintext, -$pad_length, $pad_length, '') eq chr($pad_length) x $pad_length or die 'Incorrectly padded';
81 2         19 return ($plaintext, $public);
82             }
83              
84             sub keygen {
85 4     4 1 956 my ($self, %arg) = @_;
86 4         35 my $random = Bytes::Random::Secure->new( Bits => 128 );
87 4         394 my $secret = curve25519_secret_key($random->bytes(32));
88 4         1176774186 my $public = curve25519_public_key($secret);
89 4 100       26 if ($arg{PrivateKey}) {
90             # Sign public key with the signing key
91 2         52 my $signature;
92 2 100       24 if ($self->sigscheme eq 'Ed25519') {
93 1         106 $signature = Crypt::Ed25519::sign( $public, $arg{PublicKey}, $arg{PrivateKey} );
94             }
95             else {
96 1         11 my $ecdsa = new Crypt::EC_DSA;
97 1         4596 $signature = $ecdsa->sign( Message => $public, Key => $arg{PrivateKey} );
98             }
99 2         225771068 return ($public, $secret, $signature);
100             }
101             else {
102 2         46 return ($public, $secret);
103             }
104             }
105              
106             sub signkeygen {
107 2     2 1 37 my $self = shift;
108 2 100       14 if ($self->sigscheme eq 'Ed25519') {
109 1         546 return Crypt::Ed25519::generate_keypair;
110             }
111             else {
112 1         7 my $ecdsa = new Crypt::EC_DSA;
113 1         4610 $ecdsa->keygen;
114             }
115             }
116              
117             sub AUTOLOAD {
118 11     11   87124571 my $self = shift; (my $auto = $AUTOLOAD) =~ s/.*:://;
  11         115  
119 11 100       184 return if $auto eq 'DESTROY';
120 10 50       82 if ($auto =~ /^(debug|sigscheme)$/x) {
121 10 100       35 $self->{$auto} = shift if (defined $_[0]);
122 10         96 return $self->{$auto};
123             }
124             else {
125 0           die "Could not AUTOLOAD method $auto.";
126             }
127             }
128              
129             sub _diag {
130 0     0     my $self = shift;
131 0 0         print STDERR @_ if $self->debug;
132             }
133              
134             1; # End of Crypt::EECDH
135              
136             __END__