File Coverage

lib/Crypt/Mode/CBC/Easy.pm
Criterion Covered Total %
statement 47 49 95.9
branch 5 10 50.0
condition 1 3 33.3
subroutine 11 11 100.0
pod 2 2 100.0
total 66 75 88.0


line stmt bran cond sub pod time code
1 1     1   1124 use strict;
  1         1  
  1         39  
2             package Crypt::Mode::CBC::Easy;
3             #ABSTRACT: Encrypts/decrypts text and verifies decrypted text with a checksum and a random initialization vector.
4             $Crypt::Mode::CBC::Easy::VERSION = '0.001';
5 1     1   380 use Mouse;
  1         18658  
  1         4  
6 1     1   715 use Crypt::CBC;
  1         3171  
  1         23  
7 1     1   415 use Digest::SHA;
  1         2222  
  1         39  
8 1     1   383 use MIME::Base64;
  1         529  
  1         46  
9 1     1   500 use Bytes::Random::Secure qw//;
  1         6821  
  1         18  
10 1     1   436 use Crypt::Mode::CBC;
  1         8032  
  1         20  
11 1     1   4 use Carp;
  1         1  
  1         404  
12              
13              
14              
15              
16             has key => (
17             isa => 'Str',
18             is => 'ro',
19             required => 1,
20             );
21              
22              
23             has crypt_mode_cbc => (
24             isa => 'Crypt::Mode::CBC',
25             is => 'ro',
26             required => 1,
27             default => sub { Crypt::Mode::CBC->new('Twofish') },
28             );
29              
30              
31             has block_size => (
32             isa => 'Int',
33             is => 'ro',
34             required => 1,
35             default => 16,
36             );
37              
38              
39             has checksum_digest_hex => (
40             isa => 'CodeRef',
41             is => 'ro',
42             required => 1,
43             default => sub { \&Digest::SHA::sha512_hex },
44             );
45              
46              
47             has bytes_random_secure => (
48             isa => 'Bytes::Random::Secure',
49             is => 'ro',
50             required => 1,
51             default => sub { Bytes::Random::Secure->new(NonBlocking => 1) },
52             );
53              
54              
55             has separator => (
56             isa => 'Str',
57             is => 'ro',
58             required => 1,
59             default => '::~;~;~::',
60             );
61              
62              
63             sub encrypt {
64 1     1 1 69 my ($self, @plain_texts) = @_;
65              
66 1 50       4 croak "must pass in text to be encrypted" unless @plain_texts;
67              
68 1         7 my $iv = $self->bytes_random_secure->bytes($self->block_size);
69              
70 1         320 my $digest = $self->_get_digest($iv, \@plain_texts);
71 1         2 push @plain_texts, $digest;
72            
73 1         3 my $plain_texts_str = join($self->separator, @plain_texts);
74 1         12 my $encrypted = $iv . $self->separator . $self->crypt_mode_cbc->encrypt($plain_texts_str, $self->key, $iv);
75 1         61 my $cipher_text = MIME::Base64::encode($encrypted);
76              
77 1         2 return $cipher_text;
78             }
79              
80              
81             sub decrypt {
82 1     1 1 4 my ($self, $cipher_text) = @_;
83              
84 1 50       6 croak "must pass in text to be decrypted" unless $cipher_text;
85              
86 1         2 my $separator = $self->separator;
87 1         17 my ($iv, $to_decrypt) = split qr/$separator/, MIME::Base64::decode($cipher_text);
88              
89 1 50 33     7 croak "invalid cipher text" unless $iv and $to_decrypt;
90              
91 1         7 my $plain_text_with_checksum = $self->crypt_mode_cbc->decrypt($to_decrypt, $self->key, $iv);
92            
93 1         43 my @decrypted_values = split($self->separator, $plain_text_with_checksum);
94 1         2 my $digest = pop @decrypted_values;
95 1         2 my $confirm_digest = $self->_get_digest($iv, \@decrypted_values);
96              
97 1 50       4 if ($confirm_digest eq $digest) {
98 1 50       2 if (wantarray) {
99 1         5 return @decrypted_values;
100             }
101             else {
102 0         0 return join($separator , @decrypted_values);
103             }
104             }
105             else {
106 0         0 croak "invalid encrypted text";
107             }
108             }
109              
110             sub _get_digest {
111 2     2   4 my ($self, $iv, $texts) = @_;
112 2         26 return $self->checksum_digest_hex->($iv . join('', @$texts));
113             }
114              
115             1;
116              
117             __END__