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) 2017 Ashish Gulhati
5             #
6             # $Id: lib/Crypt/EECDH.pm v1.004 Fri Jun 9 03:52:54 PDT 2017 $
7              
8             package Crypt::EECDH;
9              
10 2     2   33807 use warnings;
  2         4  
  2         66  
11 2     2   12 use strict;
  2         6  
  2         40  
12              
13 2     2   963 use Crypt::Curve25519;
  2         1607  
  2         128  
14 2     2   890 use Crypt::Ed25519;
  2         540  
  2         57  
15 2     2   845 use Crypt::EC_DSA;
  2         116858  
  2         58  
16 2     2   17 use Bytes::Random::Secure;
  2         4  
  2         78  
17 2     2   765 use Crypt::Rijndael;
  2         764  
  2         71  
18 2     2   15 use Digest::SHA qw/sha256 hmac_sha256/;
  2         5  
  2         106  
19 2     2   20 use vars qw( $VERSION $AUTOLOAD );
  2         5  
  2         1632  
20              
21             our ( $VERSION ) = '$Revision: 1.004 $' =~ /\s+([\d\.]+)/;
22              
23             sub new {
24 1     1 1 12 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 1858 my ($self, %arg) = @_;
34              
35 2 50 33     28 if (defined $arg{SigningKey} && defined $arg{Signature}) {
36             # Check signature on $public_key
37 2 100       25 if ($self->sigscheme eq 'Ed25519') {
38 1 50       257 return unless Crypt::Ed25519::verify($arg{PublicKey}, $arg{SigningKey}, $arg{Signature});
39             }
40             else {
41 1         14 my $ecdsa = new Crypt::EC_DSA;
42             return unless $ecdsa->verify( Message => $arg{PublicKey}, Signature => $arg{Signature},
43 1 50       7370 Key => $arg{SigningKey} );
44             }
45             }
46              
47 2         25862323 my $random = Bytes::Random::Secure->new( Bits => 128 );
48 2         236 my $private = curve25519_secret_key($random->bytes(32));
49 2         442546235 my $public = curve25519_public_key($private);
50 2         267 my $shared = curve25519_shared_secret($private, $arg{PublicKey});
51              
52 2         64 my ($encrypt_key, $sign_key) = unpack 'a16 a16', sha256($shared);
53 2         21 my $iv = substr sha256($public), 0, 16;
54 2         37 my $cipher = Crypt::Rijndael->new($encrypt_key, Crypt::Rijndael::MODE_CBC);
55 2         14 $cipher->set_iv($iv);
56              
57 2         10 my $pad_length = 16 - length($arg{Message}) % 16;
58 2         11 my $padding = chr($pad_length) x $pad_length;
59              
60 2         16 my $ciphertext = $cipher->encrypt($arg{Message} . $padding);
61 2         28 my $mac = hmac_sha256($iv . $ciphertext, $sign_key);
62 2         64 return (pack ($format, '', $public, $mac, $ciphertext), $private);
63             }
64              
65             sub decrypt {
66 2     2 1 13 my ($self, %arg) = @_;
67              
68 2         18 my ($options, $public, $mac, $ciphertext) = unpack $format, $arg{Ciphertext};
69 2 50       13 die 'Unknown options' if $options ne '';
70              
71 2         244 my $shared = curve25519_shared_secret($arg{Key}, $public);
72 2         20 my ($encrypt_key, $sign_key) = unpack 'a16 a16', sha256($shared);
73 2         17 my $iv = substr sha256($public), 0, 16;
74 2 50       31 die 'MAC is incorrect' if hmac_sha256($iv . $ciphertext, $sign_key) ne $mac;
75 2         24 my $cipher = Crypt::Rijndael->new($encrypt_key, Crypt::Rijndael::MODE_CBC);
76 2         10 $cipher->set_iv($iv);
77              
78 2         12 my $plaintext = $cipher->decrypt($ciphertext);
79 2         8 my $pad_length = ord substr $plaintext, -1;
80 2 50       16 substr($plaintext, -$pad_length, $pad_length, '') eq chr($pad_length) x $pad_length or die 'Incorrectly padded';
81 2         13 return ($plaintext, $public);
82             }
83              
84             sub keygen {
85 4     4 1 12702199 my ($self, %arg) = @_;
86 4         39 my $random = Bytes::Random::Secure->new( Bits => 128 );
87 4         443 my $secret = curve25519_secret_key($random->bytes(32));
88 4         605238315 my $public = curve25519_public_key($secret);
89 4 100       33 if ($arg{PrivateKey}) {
90             # Sign public key with the signing key
91 2         66 my $signature;
92 2 100       43 if ($self->sigscheme eq 'Ed25519') {
93 1         90 $signature = Crypt::Ed25519::sign( $public, $arg{PublicKey}, $arg{PrivateKey} );
94             }
95             else {
96 1         14 my $ecdsa = new Crypt::EC_DSA;
97 1         6587 $signature = $ecdsa->sign( Message => $public, Key => $arg{PrivateKey} );
98             }
99 2         54098564 return ($public, $secret, $signature);
100             }
101             else {
102 2         54 return ($public, $secret);
103             }
104             }
105              
106             sub signkeygen {
107 2     2 1 18 my $self = shift;
108 2 100       16 if ($self->sigscheme eq 'Ed25519') {
109 1         806 return Crypt::Ed25519::generate_keypair;
110             }
111             else {
112 1         7 my $ecdsa = new Crypt::EC_DSA;
113 1         4246 $ecdsa->keygen;
114             }
115             }
116              
117             sub AUTOLOAD {
118 9     9   51 my $self = shift; (my $auto = $AUTOLOAD) =~ s/.*:://;
  9         76  
119 9 100       74 return if $auto eq 'DESTROY';
120 8 50       60 if ($auto =~ /^(debug|sigscheme)$/x) {
121 8 100       35 $self->{$auto} = shift if (defined $_[0]);
122 8         84 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__