File Coverage

blib/lib/Crypt/ECDH_ES.pm
Criterion Covered Total %
statement 50 54 92.5
branch 3 6 50.0
condition n/a
subroutine 10 11 90.9
pod 3 3 100.0
total 66 74 89.1


line stmt bran cond sub pod time code
1             package Crypt::ECDH_ES;
2             $Crypt::ECDH_ES::VERSION = '0.004';
3 1     1   68529 use strict;
  1         3  
  1         29  
4 1     1   5 use warnings;
  1         2  
  1         24  
5              
6 1     1   4 use Carp;
  1         3  
  1         57  
7 1     1   572 use Crypt::Curve25519;
  1         1026  
  1         68  
8 1     1   498 use Crypt::URandom qw/urandom/;
  1         5800  
  1         64  
9 1     1   461 use Crypt::Rijndael;
  1         407  
  1         37  
10 1     1   540 use Digest::SHA qw/sha256 hmac_sha256/;
  1         3152  
  1         88  
11              
12 1     1   8 use Exporter 5.57 'import';
  1         18  
  1         506  
13             our @EXPORT_OK = qw/ecdhes_encrypt ecdhes_decrypt ecdhes_generate_key/;
14             our %EXPORT_TAGS = (all => \@EXPORT_OK);
15              
16             my $format = 'C/a C/a n/a N/a';
17              
18             sub ecdhes_encrypt {
19 1     1 1 87 my ($public_key, $data) = @_;
20              
21 1         4 my $private = curve25519_secret_key(urandom(32));
22 1         10782 my $public = curve25519_public_key($private);
23 1         112 my $shared = curve25519_shared_secret($private, $public_key);
24              
25 1         19 my ($encrypt_key, $sign_key) = unpack 'a16 a16', sha256($shared);
26 1         8 my $iv = substr sha256($public), 0, 16;
27 1         14 my $cipher = Crypt::Rijndael->new($encrypt_key, Crypt::Rijndael::MODE_CBC);
28 1         6 $cipher->set_iv($iv);
29              
30 1         5 my $pad_length = 16 - length($data) % 16;
31 1         6 my $padding = chr($pad_length) x $pad_length;
32              
33 1         8 my $ciphertext = $cipher->encrypt($data . $padding);
34 1         11 my $mac = hmac_sha256($iv . $ciphertext, $sign_key);
35 1         18 return pack $format, '', $public, $mac, $ciphertext;
36             }
37              
38             sub ecdhes_decrypt {
39 1     1 1 818 my ($private_key, $packed_data) = @_;
40              
41 1         8 my ($options, $public, $mac, $ciphertext) = unpack $format, $packed_data;
42 1 50       6 croak 'Unknown options' if $options ne '';
43              
44 1         113 my $shared = curve25519_shared_secret($private_key, $public);
45 1         13 my ($encrypt_key, $sign_key) = unpack 'a16 a16', sha256($shared);
46 1         7 my $iv = substr sha256($public), 0, 16;
47 1 50       11 croak 'MAC is incorrect' if hmac_sha256($iv . $ciphertext, $sign_key) ne $mac;
48 1         9 my $cipher = Crypt::Rijndael->new($encrypt_key, Crypt::Rijndael::MODE_CBC);
49 1         4 $cipher->set_iv($iv);
50              
51 1         5 my $plaintext = $cipher->decrypt($ciphertext);
52 1         3 my $pad_length = ord substr $plaintext, -1;
53 1 50       6 substr($plaintext, -$pad_length, $pad_length, '') eq chr($pad_length) x $pad_length or croak 'Incorrectly padded';
54 1         5 return $plaintext;
55             }
56              
57             sub ecdhes_generate_key {
58 0     0 1   my $buf = urandom(32);
59 0           my $secret = curve25519_secret_key($buf);
60 0           my $public = curve25519_public_key($secret);
61 0           return ($public, $secret);
62             }
63              
64             1;
65              
66             #ABSTRACT: A fast and small hybrid crypto system
67              
68             __END__