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   15768 use strict;
  7         8  
  7         246  
12 7     7   3067 use Crypt::Rijndael;
  7         5126  
  7         175  
13 7     7   3326 use MIME::Base64;
  7         3709  
  7         470  
14 7     7   39 use Digest::MD5 qw(md5);
  7         9  
  7         317  
15 7     7   3287 use bytes;
  7         56  
  7         31  
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     4 $mode ||= Crypt::Rijndael::MODE_CBC();
29 1         5 my $self = {
30             key => $key,
31             mode => $mode,
32             ciphers => {}
33             };
34 1         7 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   38 my ( $self, $key ) = @_;
45 37   100     111 $key ||= "";
46 37   66     119 $self->{ciphers}->{$key} ||=
47             Crypt::Rijndael->new( md5( $self->{key}, $key ), $self->{mode} );
48 37         250 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 7641 my ( $self, $data ) = @_;
57              
58             # pad $data so that its length be multiple of 16 bytes
59 17         47 my $l = bytes::length($data) % 16;
60 17 100       814 $data .= "\0" x ( 16 - $l ) unless ( $l == 0 );
61              
62 17         24 eval { $data = encode_base64( $self->_getCipher->encrypt($data) ); };
  17         30  
63 17 50       39 if ($@) {
64 0         0 $msg = "Crypt::Rijndael error : $@";
65 0         0 return undef;
66             }
67             else {
68 17         20 $msg = '';
69 17         25 chomp $data;
70 17         44 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 19 my ( $self, $data ) = @_;
80 18         41 $data =~ s/%2B/\+/ig;
81 18         25 $data =~ s/%2F/\//ig;
82 18         16 $data =~ s/%3D/=/ig;
83 18         22 $data =~ s/%0A/\n/ig;
84 18         18 eval { $data = $self->_getCipher->decrypt( decode_base64($data) ); };
  18         25  
85 18 50       31 if ($@) {
86 0         0 $msg = "Crypt::Rijndael error : $@";
87 0         0 return undef;
88             }
89             else {
90 18         27 $msg = '';
91              
92             # Obscure Perl re bug...
93 18         19 $data .= "\0";
94 18         85 $data =~ s/\0*$//;
95 18         84 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 340 my ( $self, $data, $key ) = @_;
108 1         8 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 2 my ( $self, $data, $key ) = @_;
120 1         2 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   4 my ( $self, $data, $key, $sub ) = @_;
131 2 50       13 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       5 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         16 $data = pack "H*", $data;
145 2         25 eval { $data = $self->_getCipher($key)->$sub($data); };
  2         6  
146 2 50       6 if ($@) {
147 0         0 $msg = "Crypt::Rijndael error : $@";
148 0         0 return undef;
149             }
150 2         1 $msg = "";
151 2         6 $data = unpack "H*", $data;
152 2         7 return $data;
153             }
154              
155             1;