| 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__ |