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   1164 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.003';
5 1     1   404 use Mouse;
  1         18992  
  1         3  
6 1     1   724 use Crypt::CBC;
  1         3194  
  1         22  
7 1     1   421 use Digest::SHA;
  1         2273  
  1         42  
8 1     1   412 use MIME::Base64;
  1         466  
  1         45  
9 1     1   520 use Bytes::Random::Secure qw//;
  1         6932  
  1         17  
10 1     1   415 use Crypt::Mode::CBC;
  1         8244  
  1         19  
11 1     1   5 use Carp;
  1         1  
  1         411  
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 51 my ($self, @plain_texts) = @_;
65              
66 1 50       3 croak "must pass in text to be encrypted" unless @plain_texts;
67              
68 1         4 my $iv = $self->bytes_random_secure->bytes($self->block_size);
69              
70 1         257 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         9 my $encrypted = $iv . $self->separator . $self->crypt_mode_cbc->encrypt($plain_texts_str, $self->key, $iv);
75 1         52 my $cipher_text = MIME::Base64::encode($encrypted);
76              
77 1         2 return $cipher_text;
78             }
79              
80              
81             sub decrypt {
82 1     1 1 3 my ($self, $cipher_text) = @_;
83              
84 1 50       2 croak "must pass in text to be decrypted" unless $cipher_text;
85              
86 1         2 my $separator = $self->separator;
87 1         14 my ($iv, $to_decrypt) = split qr/$separator/, MIME::Base64::decode($cipher_text);
88              
89 1 50 33     6 croak "invalid cipher text" unless $iv and $to_decrypt;
90              
91 1         5 my $plain_text_with_checksum = $self->crypt_mode_cbc->decrypt($to_decrypt, $self->key, $iv);
92            
93 1         46 my @decrypted_values = split($self->separator, $plain_text_with_checksum);
94 1         2 my $digest = pop @decrypted_values;
95 1         1 my $confirm_digest = $self->_get_digest($iv, \@decrypted_values);
96              
97 1 50       3 if ($confirm_digest eq $digest) {
98 1 50       2 if (wantarray) {
99 1         4 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   3 my ($self, $iv, $texts) = @_;
112 2         23 return $self->checksum_digest_hex->($iv . join('', @$texts));
113             }
114              
115             1;
116              
117             __END__