File Coverage

blib/lib/Crypt/Fernet.pm
Criterion Covered Total %
statement 86 91 94.5
branch 8 10 80.0
condition 2 2 100.0
subroutine 19 23 82.6
pod 0 8 0.0
total 115 134 85.8


line stmt bran cond sub pod time code
1             package Crypt::Fernet;
2             our $VERSION = '0.0306'; # VERSION: generated by DZP::OurPkgVersion
3              
4 1     1   189307 use 5.018002;
  1         5  
5 1     1   6 use strict;
  1         9  
  1         32  
6 1     1   6 use warnings;
  1         16  
  1         105  
7              
8 1     1   8 use Exporter 5.57 qw( import );
  1         2335  
  1         238  
9              
10             # Items to export into callers namespace by default. Note: do not export
11             # names by default without a very good reason. Use EXPORT_OK instead.
12             # Do not simply export all your public functions/methods/constants.
13              
14             # This allows declaration use Crypt::Fernet ':all';
15             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
16             # will save memory.
17             our %EXPORT_TAGS = ( 'all' => [ qw(
18             fernet_genkey fernet_encrypt fernet_verify fernet_decrypt
19             ) ] );
20              
21             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
22              
23             our @EXPORT = qw(
24            
25             );
26              
27             our $FERNET_TOKEN_VERSION = pack("H*", '80');
28              
29              
30             # Preloaded methods go here.
31              
32 0     0 0 0 sub fernet_genkey { Crypt::Fernet::generate_key() }
33 0     0 0 0 sub fernet_encrypt { Crypt::Fernet::encrypt(@_) }
34 0     0 0 0 sub fernet_verify { Crypt::Fernet::verify(@_) }
35 0     0 0 0 sub fernet_decrypt { Crypt::Fernet::decrypt(@_) }
36              
37              
38 1     1   9 use Crypt::CBC;
  1         2  
  1         27  
39 1     1   612 use Crypt::Rijndael;
  1         561  
  1         45  
40 1     1   10 use Crypt::URandom qw( urandom );
  1         2  
  1         76  
41 1     1   8 use Digest::SHA qw(hmac_sha256);
  1         2  
  1         129  
42 1     1   7 use MIME::Base64::URLSafe;
  1         1  
  1         766  
43              
44             sub generate_key {
45 1     1 0 235276 return _urlsafe_pading_base64_encode(urandom(32));
46             }
47              
48             sub encrypt {
49 1     1 0 9 my ($key, $data) = @_;
50 1         5 my $b64decode_key = urlsafe_b64decode($key);
51 1         14 my $signkey = substr $b64decode_key, 0, 16;
52 1         3 my $encryptkey = substr $b64decode_key, 16, 16;
53 1         4 my $iv = urandom(16);
54 1         39 my $cipher = Crypt::CBC->new(-literal_key => 1,
55             -key => $encryptkey,
56             -iv => $iv,
57             -keysize => 16,
58             -padding => 'standard',
59             -cipher => 'Rijndael',
60             -header => 'none',
61             );
62 1         427 my $ciphertext = $cipher->encrypt($data);
63 1         340 my $pre_token = $FERNET_TOKEN_VERSION . _timestamp() . $iv . $ciphertext;
64 1         18 my $digest=hmac_sha256($pre_token, $signkey);
65 1         4 my $token = $pre_token . $digest;
66 1         4 return _urlsafe_pading_base64_encode($token);
67             }
68              
69             sub decrypt {
70 4     4 0 19 my ($key, $token, $ttl) = @_;
71 4 100       9 verify($key, $token, $ttl) or return;
72 3         10 my $b64decode_key = urlsafe_b64decode($key);
73 3         30 my $token_data = urlsafe_b64decode($token);
74              
75 3         45 my $encryptkey = substr $b64decode_key, 16, 16;
76 3         9 my $iv = substr $token_data, 9, 16;
77              
78 3         7 my $ciphertextlen = (length $token_data) - 25 - 32;
79 3         4 my $ciphertext = substr $token_data, 25, $ciphertextlen;
80            
81 3         20 my $cipher = Crypt::CBC->new(-literal_key => 1,
82             -key => $encryptkey,
83             -iv => $iv,
84             -keysize => 16,
85             -padding => 'standard',
86             -cipher => 'Rijndael',
87             -header => 'none',
88             );
89 3         1007 my $plaintext = $cipher->decrypt($ciphertext);
90 3         968 return $plaintext;
91             }
92              
93             sub verify {
94 8     8 0 34 my ($key, $token, $ttl) = @_;
95 8   100     33 $ttl ||= 0;
96 8         25 my $b64decode_key = urlsafe_b64decode($key);
97 8         86 my $msg = urlsafe_b64decode($token);
98 8         78 my $token_version = substr $msg, 0, 1;
99 8 50       21 ($token_version eq $FERNET_TOKEN_VERSION) or return 0;
100              
101 8 100       24 if ($ttl > 0) {
102 4         11 my $timestamp_bytes = substr $msg, 1, 8;
103 4         10 my $timestamp = _byte_to_time($timestamp_bytes);
104 4 100       19 return 0 if (time - $timestamp > $ttl);
105             }
106              
107 6         12 my $token_sign = substr $msg, (length $msg) - 32, 32;
108 6         13 my $signkey = substr $b64decode_key, 0, 16;
109 6         12 my $pre_token = substr $msg, 0, (length $msg) - 32;
110 6         60 my $verify_digest = hmac_sha256($pre_token , $signkey);
111 6 50       28 ($token_sign eq $verify_digest) and return 1;
112 0         0 return 0;
113             }
114              
115             sub _timestamp {
116 1     1   9 use bytes;
  1         2  
  1         8  
117 1     1   3 my $time = time;
118 1         2 my $time64bit;
119 1         4 for my $index (0..7) {
120 8         23 $time64bit .= substr pack("I", ($time >> $index * 8) & 0xFF), 0, 1;
121             }
122 1         4 my $result = reverse $time64bit;
123 1     1   163 no bytes;
  1         2  
  1         6  
124 1         3 return $result;
125             }
126              
127             sub _urlsafe_pading_base64_encode {
128 2     2   43 my ($msg) = @_;
129 2         10 my $s = urlsafe_b64encode($msg);
130 2         66 return $s.("=" x (4 - length($s) % 4));
131             }
132              
133             sub _byte_to_time {
134 4     4   9 my ($bytes) = @_;
135 1     1   177 use bytes;
  1         3  
  1         7  
136 4         8 my $rb = reverse $bytes;
137 4         11 my $time = unpack 'V', $rb;
138 4         8 return $time;
139             }
140              
141             1;
142             __END__