File Coverage

blib/lib/DTOne/Crypt.pm
Criterion Covered Total %
statement 50 53 94.3
branch 6 12 50.0
condition n/a
subroutine 13 13 100.0
pod 2 2 100.0
total 71 80 88.7


line stmt bran cond sub pod time code
1             package DTOne::Crypt;
2              
3 1     1   71035 use strict;
  1         2  
  1         31  
4 1     1   23 use 5.008_005;
  1         4  
5             our $VERSION = '0.04';
6              
7             require Exporter;
8             our @ISA = qw(Exporter);
9             our @EXPORT_OK = qw(encrypt_aes256gcm decrypt_aes256gcm);
10              
11 1     1   534 use Crypt::AuthEnc::GCM qw(gcm_encrypt_authenticate gcm_decrypt_verify);
  1         10718  
  1         63  
12 1     1   480 use Crypt::ScryptKDF qw(scrypt_raw);
  1         2959  
  1         60  
13 1     1   571 use Bytes::Random::Secure qw(random_bytes);
  1         9141  
  1         61  
14 1     1   7 use MIME::Base64;
  1         2  
  1         39  
15 1     1   6 use Carp;
  1         1  
  1         50  
16              
17 1     1   5 use constant SCRYPT_ITERATIONS => 32768; # 2**15
  1         1  
  1         51  
18 1     1   18 use constant SCRYPT_BLOCK_SIZE => 8;
  1         3  
  1         49  
19 1     1   18 use constant SCRYPT_PARALLELISM => 1;
  1         1  
  1         42  
20 1     1   5 use constant SCRYPT_DERIVED_KEY_LEN => 32;
  1         2  
  1         374  
21              
22             sub encrypt_aes256gcm {
23 1     1 1 86 my $plaintext = shift;
24 1 50       5 my $master_key = shift or croak "master key required";
25              
26 1 50       3 unless (defined $plaintext) {
27 0         0 croak "plaintext data required";
28             }
29              
30 1         14 $master_key = decode_base64($master_key);
31 1 50       4 unless (length($master_key) == 32) {
32 0         0 croak "invalid master key length";
33             }
34              
35 1         4 my $iv = random_bytes(12);
36 1         164299093 my $salt = random_bytes(16);
37 1         85 my $key = scrypt_raw(
38             $master_key,
39             $salt,
40             SCRYPT_ITERATIONS,
41             SCRYPT_BLOCK_SIZE,
42             SCRYPT_PARALLELISM,
43             SCRYPT_DERIVED_KEY_LEN
44             );
45              
46 1         224646 my ($ciphertext, $tag) = gcm_encrypt_authenticate(
47             'AES',
48             $key,
49             $iv,
50             undef,
51             $plaintext
52             );
53              
54 1         19 return encode_base64(join('', $salt, $iv, $tag, $ciphertext), '');
55             }
56              
57             sub decrypt_aes256gcm {
58 1 50   1 1 14 my $encrypted = shift or croak "encrypted data required";
59 1 50       5 my $master_key = shift or croak "master key required";
60              
61 1         5 $master_key = decode_base64($master_key);
62 1 50       6 unless (length($master_key) == 32) {
63 0         0 croak "invalid master key length";
64             }
65              
66 1         5 $encrypted = decode_base64($encrypted);
67 1         11 my ($salt, $iv, $tag, $ciphertext) = unpack('a16 a12 a16 a*', $encrypted);
68 1         6 my $key = scrypt_raw(
69             $master_key,
70             $salt,
71             SCRYPT_ITERATIONS,
72             SCRYPT_BLOCK_SIZE,
73             SCRYPT_PARALLELISM,
74             SCRYPT_DERIVED_KEY_LEN
75             );
76              
77 1         223673 return gcm_decrypt_verify(
78             'AES',
79             $key,
80             $iv,
81             undef,
82             $ciphertext,
83             $tag
84             );
85             }
86              
87             1;
88             __END__