File Coverage

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


line stmt bran cond sub pod time code
1             package DTOne::Crypt;
2              
3 1     1   75048 use strict;
  1         3  
  1         30  
4 1     1   32 use 5.008_005;
  1         3  
5             our $VERSION = '0.05';
6              
7             require Exporter;
8             our @ISA = qw(Exporter);
9             our @EXPORT_OK = qw(encrypt_aes256gcm decrypt_aes256gcm);
10              
11 1     1   578 use Crypt::AuthEnc::GCM qw(gcm_encrypt_authenticate gcm_decrypt_verify);
  1         10355  
  1         73  
12 1     1   487 use Crypt::ScryptKDF qw(scrypt_raw);
  1         3004  
  1         123  
13 1     1   614 use Bytes::Random::Secure qw(random_bytes);
  1         9208  
  1         59  
14 1     1   8 use MIME::Base64;
  1         2  
  1         39  
15 1     1   6 use Carp;
  1         2  
  1         48  
16              
17 1     1   5 use constant SCRYPT_ITERATIONS => 32768; # 2**15
  1         2  
  1         68  
18 1     1   7 use constant SCRYPT_BLOCK_SIZE => 8;
  1         2  
  1         49  
19 1     1   14 use constant SCRYPT_PARALLELISM => 1;
  1         1  
  1         43  
20 1     1   6 use constant SCRYPT_DERIVED_KEY_LEN => 32;
  1         1  
  1         354  
21              
22             sub encrypt_aes256gcm {
23 8     8 1 5615 my $plaintext = shift;
24 8 100       62 my $master_key = shift or croak "master key required";
25              
26 6 100       19 unless (defined $plaintext) {
27 1         65 croak "plaintext data required";
28             }
29              
30 5         33 $master_key = decode_base64($master_key);
31 5 100       21 unless (length($master_key) == 32) {
32 2         18 croak "invalid master key length";
33             }
34              
35 3         30 my $iv = random_bytes(12);
36 3         271290526 my $salt = random_bytes(16);
37 3         172 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 3         684276 my ($ciphertext, $tag) = gcm_encrypt_authenticate(
47             'AES',
48             $key,
49             $iv,
50             undef,
51             $plaintext
52             );
53              
54 3         74 return encode_base64(join('', $salt, $iv, $tag, $ciphertext), '');
55             }
56              
57             sub decrypt_aes256gcm {
58 9 100   9 1 5665 my $encrypted = shift or croak "encrypted data required";
59 6 100       48 my $master_key = shift or croak "master key required";
60              
61 4         20 $master_key = decode_base64($master_key);
62 4 100       16 unless (length($master_key) == 32) {
63 2         19 croak "invalid master key length";
64             }
65              
66 2         7 $encrypted = decode_base64($encrypted);
67 2         20 my ($salt, $iv, $tag, $ciphertext) = unpack('a16 a12 a16 a*', $encrypted);
68 2         12 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 2         450949 return gcm_decrypt_verify(
78             'AES',
79             $key,
80             $iv,
81             undef,
82             $ciphertext,
83             $tag
84             );
85             }
86              
87             1;
88             __END__