File Coverage

blib/lib/Lemonldap/NG/Common/Crypto.pm
Criterion Covered Total %
statement 58 68 85.2
branch 7 12 58.3
condition 5 7 71.4
subroutine 12 12 100.0
pod 0 5 0.0
total 82 104 78.8


line stmt bran cond sub pod time code
1             ##@file
2             # Extend Crypt::Rijndael to get several keys from a single secret key,
3             # add base64 encoding of binary data, and cipher hexadecimal data.
4              
5             ##@class
6             # Extend Crypt::Rijndael to get several keys from a single secret key,
7             # add base64 encoding of binary data, and cipher hexadecimal data.
8             # $Lemonldap::NG::Common::Crypto::msg contains Crypt::Rijndael errors.
9             package Lemonldap::NG::Common::Crypto;
10              
11 7     7   58156 use strict;
  7         14  
  7         290  
12 7     7   7360 use Crypt::Rijndael;
  7         9902  
  7         266  
13 7     7   7226 use MIME::Base64;
  7         6345  
  7         636  
14 7     7   51 use Digest::MD5 qw(md5);
  7         15  
  7         393  
15 7     7   6660 use bytes;
  7         74  
  7         45  
16              
17             our $VERSION = '1.4.0';
18              
19             our $msg;
20              
21             ## @cmethod Lemonldap::NG::Common::Crypto new(string key, string mode)
22             # Constructor
23             # @param key key defined in LL::NG conf
24             # @param mode Crypt::Rijndael constant
25             # @return Lemonldap::NG::Common::Crypto object
26             sub new {
27 1     1 0 14 my ( $class, $key, $mode ) = @_;
28 1   50     5 $mode ||= Crypt::Rijndael::MODE_CBC();
29 1         6 my $self = {
30             key => $key,
31             mode => $mode,
32             ciphers => {}
33             };
34 1         9 return bless $self, $class;
35             }
36              
37             ## @method private Crypt::Rijndael _getCipher(string key)
38             # Returns a Crypt::Rijndael object whose key is mainKey ^ secondKey,
39             # where mainKey is defined in LL::NG conf,
40             # and secondKey is set in code so as to get different keys
41             # @param key that secondary key
42             # @return Crypt::Rijndael object
43             sub _getCipher {
44 37     37   52 my ( $self, $key ) = @_;
45 37   100     419 $key ||= "";
46 37   66     172 $self->{ciphers}->{$key} ||=
47             Crypt::Rijndael->new( md5( $self->{key}, $key ), $self->{mode} );
48 37         352 return $self->{ciphers}->{$key};
49             }
50              
51             ## @method string encrypt(string data)
52             # Encrypt $data and return it in Base64 format
53             # @param data datas to encrypt
54             # @return encrypted datas in Base64 format
55             sub encrypt {
56 17     17 0 14374 my ( $self, $data ) = @_;
57              
58             # pad $data so that its length be multiple of 16 bytes
59 17         65 my $l = bytes::length($data) % 16;
60 17 100       3345 $data .= "\0" x ( 16 - $l ) unless ( $l == 0 );
61              
62 17         23 eval { $data = encode_base64( $self->_getCipher->encrypt($data) ); };
  17         44  
63 17 50       45 if ($@) {
64 0         0 $msg = "Crypt::Rijndael error : $@";
65 0         0 return undef;
66             }
67             else {
68 17         22 $msg = '';
69 17         31 chomp $data;
70 17         52 return $data;
71             }
72             }
73              
74             ## @method string decrypt(string data)
75             # Decrypt $data and return it
76             # @param data datas to decrypt in Base64 format
77             # @return decrypted datas
78             sub decrypt {
79 18     18 0 25 my ( $self, $data ) = @_;
80 18         39 $data =~ s/%2B/\+/ig;
81 18         25 $data =~ s/%2F/\//ig;
82 18         25 $data =~ s/%3D/=/ig;
83 18         34 $data =~ s/%0A/\n/ig;
84 18         17 eval { $data = $self->_getCipher->decrypt( decode_base64($data) ); };
  18         36  
85 18 50       46 if ($@) {
86 0         0 $msg = "Crypt::Rijndael error : $@";
87 0         0 return undef;
88             }
89             else {
90 18         20 $msg = '';
91              
92             # Obscure Perl re bug...
93 18         24 $data .= "\0";
94 18         100 $data =~ s/\0*$//;
95 18         88 return $data;
96             }
97             }
98              
99             ## @method string encryptHex(string data, string key)
100             # Encrypt $data and return it in hexadecimal format
101             # Data must be hexadecimal and its length must be a multiple of 32
102             # the encrypted data have same length as the original data
103             # @param data datas to encrypt
104             # @param key optional secondary key
105             # @return encrypted datas in hexadecimal data
106             sub encryptHex {
107 1     1 0 1684 my ( $self, $data, $key ) = @_;
108 1         7 return _cryptHex( $self, $data, $key, "encrypt" );
109             }
110              
111             ## @method string decryptHex(string data, string key)
112             # Decrypt $data and return it in hexadecimal format
113             # Data must be hexadecimal and its length must be a multiple of 32
114             # the decrypted data have same length as the encrypted data
115             # @param data datas to decrypt
116             # @param key optional secondary key
117             # @return decrypted datas in hexadecimal data
118             sub decryptHex {
119 1     1 0 3 my ( $self, $data, $key ) = @_;
120 1         3 return _cryptHex( $self, $data, $key, "decrypt" );
121             }
122              
123             ## @method private string _cryptHex (string data, string key, string sub)
124             # Auxiliary method to share code between encrypt and decrypt
125             # @param data datas to decrypt
126             # @param key secondary key
127             # @param sub may be "encrypt" or "decrypt"
128             # @return decrypted datas in hexadecimal data
129             sub _cryptHex {
130 2     2   6 my ( $self, $data, $key, $sub ) = @_;
131 2 50       15 unless ( $data =~ /^([0-9a-fA-F]{2})*$/ ) {
132 0         0 $msg =
133             "Lemonldap::NG::Common::Crypto::${sub}Hex error : data is not hexadecimal";
134 0         0 return undef;
135             }
136              
137             # $data's length must be multiple of 32,
138             # since Rijndael requires data length multiple of 16
139 2 50       8 unless ( bytes::length($data) % 32 == 0 ) {
140 0         0 $msg =
141             "Lemonldap::NG::Common::Crypto::${sub}Hex error : data length must be multiple of 32";
142 0         0 return undef;
143             }
144 2         20 $data = pack "H*", $data;
145 2         36 eval { $data = $self->_getCipher($key)->$sub($data); };
  2         6  
146 2 50       8 if ($@) {
147 0         0 $msg = "Crypt::Rijndael error : $@";
148 0         0 return undef;
149             }
150 2         2 $msg = "";
151 2         8 $data = unpack "H*", $data;
152 2         10 return $data;
153             }
154              
155             1;