File Coverage

blib/lib/Crypt/Rijndael/PP.pm
Criterion Covered Total %
statement 178 353 50.4
branch 5 32 15.6
condition 1 6 16.6
subroutine 26 52 50.0
pod 9 14 64.2
total 219 457 47.9


line stmt bran cond sub pod time code
1             package Crypt::Rijndael::PP;
2              
3 8     8   212988 use strict;
  8         18  
  8         297  
4 8     8   33 use warnings;
  8         11  
  8         184  
5              
6 8     8   1056 use bytes;
  8         22  
  8         42  
7              
8 8     8   4628 use Smart::Comments -ENV;
  8         189048  
  8         59  
9 8     8   32403 use Data::Dumper;
  8         15  
  8         376  
10 8     8   34 use Carp;
  8         12  
  8         373  
11              
12 8     8   3559 use Crypt::Rijndael::PP::GF qw( gf_multiply );
  8         21  
  8         508  
13 8     8   3002 use Crypt::Rijndael::PP::Debug qw( generate_printable_state );
  8         15  
  8         440  
14              
15 8     8   3611 use Crypt::Random::Source qw(get_weak);
  8         472397  
  8         55  
16              
17             our $VERSION = '0.2.1'; # VERSION
18             # ABSTRACT: Pure Perl Implementation of the Rijndael Cipher
19              
20 8     8   2494 use Readonly;
  8         13  
  8         25121  
21             #<<< Don't Tidy S Boxes
22             Readonly my @SBOX => (
23             0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
24             0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
25             0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
26             0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
27             0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
28             0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
29             0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
30             0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
31             0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
32             0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
33             0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
34             0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
35             0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
36             0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
37             0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
38             0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
39             );
40              
41             Readonly my @INVSBOX => (
42             0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
43             0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
44             0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
45             0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
46             0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
47             0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
48             0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
49             0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
50             0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
51             0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
52             0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
53             0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
54             0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
55             0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
56             0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
57             0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
58             );
59             #>>>
60              
61             #<<< Don't Tidy the Round Constansts
62             Readonly my @RCONST => (
63             0x8d000000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000,
64             0x80000000, 0x1b000000, 0x36000000, 0x6c000000, 0xd8000000, 0xab000000, 0x4d000000, 0x9a000000,
65             0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
66             0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
67             0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
68             0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
69             0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
70             0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
71             0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
72             0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
73             0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
74             0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
75             0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
76             0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
77             0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
78             0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
79             0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d
80             );
81             #>>>
82              
83             Readonly my $NUM_ROUNDS => {
84             128 => 10,
85             192 => 12,
86             256 => 14,
87             };
88              
89             sub new {
90 0     0 1 0 my $class = shift;
91 0         0 my $key = shift;
92 0   0     0 my $mode = shift // MODE_ECB();
93              
94 0         0 my $self = {
95             key => $key,
96             mode => $mode,
97             iv => get_weak(16),
98             };
99              
100 0         0 bless $self, $class;
101              
102 0         0 return $self;
103             }
104              
105             sub MODE_ECB {
106 0     0 1 0 return 1;
107             }
108              
109             sub MODE_CBC {
110 0     0 1 0 return 2;
111             }
112              
113             sub MODE_CTR {
114 0     0 1 0 return 3;
115             }
116              
117             sub MODE_CFB {
118 0     0 1 0 return 4;
119             }
120              
121             sub MODE_OFB {
122 0     0 1 0 return 5;
123             }
124              
125             sub blocksize {
126 0     0 0 0 return 16;
127             }
128              
129             sub keysize {
130 0     0 0 0 return 32;
131             }
132              
133             sub set_iv {
134 0     0 1 0 my $self = shift;
135 0         0 my $iv = shift;
136              
137 0 0       0 if( length( $iv ) != 16 ) {
138 0         0 croak 'set_iv: initial value must be the blocksize (16 bytes), but was '
139             . length( $iv ) . ' bytes';
140             }
141              
142 0         0 $self->{iv} = $iv;
143              
144 0         0 return $self;
145             }
146              
147             sub get_iv {
148 0     0 1 0 my $self = shift;
149              
150 0         0 return $self->{iv};
151             }
152              
153             sub _increment_nonce {
154 0     0   0 my $self = shift;
155 0         0 my $packed_nonce = shift;
156              
157 0         0 my @nonce_parts = unpack( 'N4', $packed_nonce );
158              
159             ### Nonce Part 0 : unpack( 'A4', $nonce_parts[0] ) . ' - ' . unpack( 'B32', $nonce_parts[0] )
160             ### Nonce Part 1 : unpack( 'A4', $nonce_parts[1] ) . ' - ' . unpack( 'B32', $nonce_parts[1] )
161             ### Nonce Part 2 : unpack( 'A4', $nonce_parts[2] ) . ' - ' . unpack( 'B32', $nonce_parts[2] )
162             ### Nonce Part 3 : unpack( 'A4', $nonce_parts[3] ) . ' - ' . unpack( 'B32', $nonce_parts[3] )
163              
164 0         0 for my $nonce_part_index ( 0 .. 3 ) {
165 0         0 my $original_nonce_part = $nonce_parts[ 3 - $nonce_part_index ];
166 0         0 $nonce_parts[ 3 - $nonce_part_index ]++;
167              
168             ### Nonce Part Index : ( $nonce_part_index )
169             ### Original Nonce Part : unpack( 'A4', $original_nonce_part ) . ' - ' . unpack( 'B32', $original_nonce_part )
170             ### Incremented Nonce Part : unpack( 'A4', $nonce_parts[ 3 - $nonce_part_index ] ) . ' - ' . unpack( 'B32', $nonce_parts[ 3 - $nonce_part_index ] )
171              
172 0 0       0 if( $original_nonce_part < $nonce_parts[ 3 - $nonce_part_index ] ) {
173 0         0 last;
174             }
175             }
176              
177 0         0 return pack( 'N4', @nonce_parts );
178             }
179              
180             sub encrypt {
181 0     0 0 0 my $self = shift;
182 0         0 my $input = shift;
183              
184             ## no critic (ControlStructures::ProhibitCascadingIfElse)
185 0 0       0 if( $self->{mode} == MODE_ECB() ) {
    0          
    0          
    0          
    0          
186 0         0 return $self->_encrypt_mode_ecb( $input );
187             }
188             elsif( $self->{mode} == MODE_CBC() ) {
189 0         0 return $self->_encrypt_mode_cbc( $input );
190             }
191             elsif( $self->{mode} == MODE_CTR() ) {
192 0         0 return $self->_encrypt_mode_ctr( $input );
193             }
194             elsif( $self->{mode} == MODE_CFB() ) {
195 0         0 return $self->_encrypt_mode_cfb( $input );
196             }
197             elsif( $self->{mode} == MODE_OFB() ) {
198 0         0 return $self->_encrypt_mode_ofb( $input );
199             }
200             else {
201 0         0 croak "Invalid Mode specified";
202             }
203             ## use critic
204             }
205              
206             sub _encrypt_mode_ecb {
207 0     0   0 my $self = shift;
208 0         0 my $input = shift;
209              
210 0         0 my $cipher_text = '';
211 0         0 for( my $block_index = 0; $block_index < ( length($input) / 16 ); $block_index++ ) {
212 0         0 my $block = substr( $input, $block_index * 16, 16 );
213              
214 0         0 $cipher_text .= $self->encrypt_block( $block, $self->{key} );
215             }
216              
217 0         0 return $cipher_text;
218             }
219              
220             sub _encrypt_mode_cbc {
221 0     0   0 my $self = shift;
222 0         0 my $input = shift;
223              
224 0         0 my $last_block = $self->{iv};
225              
226 0         0 my $cipher_text = '';
227 0         0 for( my $block_index = 0; $block_index < ( length($input) / 16 ); $block_index++ ) {
228 0         0 my $block = substr( $input, $block_index * 16, 16 );
229              
230 0         0 $block = $block ^ $last_block;
231              
232 0         0 $last_block = $self->encrypt_block( $block, $self->{key} );
233              
234 0         0 $cipher_text .= $last_block;
235             }
236              
237 0         0 return $cipher_text;
238             }
239              
240             sub _encrypt_mode_ctr {
241 0     0   0 my $self = shift;
242 0         0 my $input = shift;
243              
244 0         0 my $nonce = $self->{iv};
245              
246 0         0 my $cipher_text = '';
247 0         0 for( my $block_index = 0; $block_index < ( length($input) / 16 ); $block_index++ ) {
248 0         0 my $block = substr( $input, $block_index * 16, 16 );
249              
250 0         0 my $packed_nonce = pack( 'A16', $nonce );
251              
252             ### Nonce : ( $nonce )
253             ### Nonce Bit String : ( unpack( 'B128', $packed_nonce ) )
254              
255 0         0 my $ctr_block = $self->encrypt_block( unpack( 'A16', $packed_nonce ), $self->{key} );
256              
257 0         0 $cipher_text .= $ctr_block ^ $block;
258 0         0 $packed_nonce = $self->_increment_nonce( $packed_nonce );
259              
260             ### Nonce + 1 Bit String: ( unpack( 'B128', $packed_nonce ) )
261 0         0 $nonce = unpack('A16', $packed_nonce );
262             }
263              
264 0         0 return $cipher_text;
265             }
266              
267             sub _encrypt_mode_cfb {
268 0     0   0 my $self = shift;
269 0         0 my $input = shift;
270              
271 0         0 my $last_block = $self->{iv};
272              
273 0         0 my $cipher_text = '';
274 0         0 for( my $block_index = 0; $block_index < ( length($input) / 16 ); $block_index++ ) {
275 0         0 my $block = substr( $input, $block_index * 16, 16 );
276              
277 0         0 my $cfb_block = $self->encrypt_block( $last_block, $self->{key} );
278              
279 0         0 $last_block = $block ^ $cfb_block;
280 0         0 $cipher_text .= $last_block;
281             }
282              
283 0         0 return $cipher_text;
284             }
285              
286             sub _encrypt_mode_ofb {
287 0     0   0 my $self = shift;
288 0         0 my $input = shift;
289              
290 0         0 my $last_block = $self->{iv};
291              
292 0         0 my $cipher_text = '';
293 0         0 for( my $block_index = 0; $block_index < ( length($input) / 16 ); $block_index++ ) {
294 0         0 my $block = substr( $input, $block_index * 16, 16 );
295              
296 0         0 my $ofb_block = $self->encrypt_block( $last_block, $self->{key} );
297              
298 0         0 $cipher_text .= $block ^ $ofb_block;
299 0         0 $last_block = $ofb_block;
300             }
301              
302 0         0 return $cipher_text;
303             }
304              
305             sub decrypt {
306 0     0 1 0 my $self = shift;
307 0         0 my $input = shift;
308              
309             ## no critic (ControlStructures::ProhibitCascadingIfElse)
310 0 0       0 if( $self->{mode} == MODE_ECB() ) {
    0          
    0          
    0          
    0          
311 0         0 return $self->_decrypt_mode_ecb( $input );
312             }
313             elsif( $self->{mode} == MODE_CBC() ) {
314 0         0 return $self->_decrypt_mode_cbc( $input );
315             }
316             elsif( $self->{mode} == MODE_CTR() ) {
317 0         0 return $self->_decrypt_mode_ctr( $input );
318             }
319             elsif( $self->{mode} == MODE_CFB() ) {
320 0         0 return $self->_decrypt_mode_cfb( $input );
321             }
322             elsif( $self->{mode} == MODE_OFB() ) {
323 0         0 return $self->_decrypt_mode_ofb( $input );
324             }
325             else {
326 0         0 croak "Invalid Mode specified";
327             }
328             ## use critic
329             }
330              
331             sub _decrypt_mode_ecb {
332 0     0   0 my $self = shift;
333 0         0 my $input = shift;
334              
335 0         0 my $plain_text = '';
336 0         0 for( my $block_index = 0; $block_index < ( length($input) / 16 ); $block_index++ ) {
337 0         0 my $cipher_block = substr( $input, $block_index * 16, 16 );
338              
339 0         0 $plain_text .= $self->decrypt_block( $cipher_block, $self->{key} );
340             }
341              
342 0         0 return $plain_text;
343             }
344              
345             sub _decrypt_mode_cbc {
346 0     0   0 my $self = shift;
347 0         0 my $input = shift;
348              
349 0         0 my $last_cipher_block;
350              
351 0         0 my $plain_text = '';
352 0         0 for( my $block_index = 0; $block_index < ( length($input) / 16 ); $block_index++ ) {
353 0         0 my $cipher_block = substr( $input, $block_index * 16, 16 );
354              
355 0         0 my $plain_block = $self->decrypt_block( $cipher_block, $self->{key} );
356              
357 0 0       0 if( $block_index == 0 ) {
358 0         0 $plain_block = $plain_block ^ $self->{iv};
359             }
360             else {
361 0         0 $plain_block = $plain_block ^ $last_cipher_block;
362             }
363              
364 0         0 $last_cipher_block = $cipher_block;
365 0         0 $plain_text .= $plain_block;
366             }
367              
368 0         0 return $plain_text;
369             }
370              
371             sub _decrypt_mode_ctr {
372 0     0   0 my $self = shift;
373 0         0 my $input = shift;
374              
375 0         0 return $self->_encrypt_mode_ctr( $input );
376             }
377              
378             sub _decrypt_mode_cfb {
379 0     0   0 my $self = shift;
380 0         0 my $input = shift;
381              
382 0         0 my $last_block = $self->{iv};
383              
384 0         0 my $plain_text = '';
385 0         0 for( my $block_index = 0; $block_index < ( length($input) / 16 ); $block_index++ ) {
386 0         0 my $cipher_block = substr( $input, $block_index * 16, 16 );
387              
388 0         0 my $cfb_block = $self->encrypt_block( $last_block, $self->{key} );
389              
390 0         0 $last_block = $cipher_block;
391 0         0 $plain_text .= $cfb_block ^ $cipher_block;
392             }
393              
394 0         0 return $plain_text;
395             }
396              
397             sub _decrypt_mode_ofb {
398 0     0   0 my $self = shift;
399 0         0 my $input = shift;
400              
401 0         0 return $self->_encrypt_mode_ofb( $input );
402             }
403              
404             sub encrypt_block {
405 0     0 0 0 my $self = shift;
406 0         0 my $input = shift;
407 0         0 my $key = shift;
408              
409 0         0 my $bits_in_initial_key = length( unpack("H*", $key ) ) * 4;
410 0         0 my $number_of_rounds = $NUM_ROUNDS->{ $bits_in_initial_key };
411             ##### Number of Bits in Initial Key : ( $bits_in_initial_key )
412             ##### Words In Initial Key : ( $bits_in_initial_key / ( 8 * 4 ) )
413             ##### Number of Rounds : ( $number_of_rounds )
414              
415 0         0 my $state = $self->_input_to_state( $input );
416             #### Inital State: ( generate_printable_state( $state ) )
417              
418 0         0 my $key_schedule = $self->_ExpandKey( $key );
419             #### Key Schedule: ( unpack("H*", $key_schedule ) )
420              
421 0         0 $self->_AddRoundKey($state, $key_schedule, 0);
422             #### State After Round 0 AddRoundKey: ( generate_printable_state( $state ) )
423              
424 0         0 for( my $round = 1; $round < $number_of_rounds; $round++ ) {
425             #### Processing Round Number: ( $round )
426              
427 0         0 $self->_SubBytes( $state );
428             #### State after SubBytes: ( generate_printable_state( $state ) )
429              
430 0         0 $self->_ShiftRows( $state );
431             #### State after ShiftRows: ( generate_printable_state( $state ) )
432              
433 0         0 $self->_MixColumns( $state );
434             #### State after MixColumns: ( generate_printable_state( $state ) )
435              
436 0         0 $self->_AddRoundKey( $state, $key_schedule, $round );
437             #### State after AddRoundKey: ( generate_printable_state( $state ) )
438             }
439              
440             #### Performing final transforms...
441              
442 0         0 $self->_SubBytes( $state );
443             #### State after SubBytes: ( generate_printable_state( $state ) )
444              
445 0         0 $self->_ShiftRows( $state );
446             #### State after ShiftRows: ( generate_printable_state( $state ) )
447              
448 0         0 $self->_AddRoundKey( $state, $key_schedule, $number_of_rounds );
449             #### State after AddRoundKey: ( generate_printable_state( $state ) )
450              
451 0         0 return $self->_state_to_output( $state );
452             }
453              
454             sub decrypt_block {
455 0     0 0 0 my $self = shift;
456 0         0 my $input = shift;
457 0         0 my $key = shift;
458              
459 0         0 my $bits_in_initial_key = length( unpack("H*", $key ) ) * 4;
460 0         0 my $number_of_rounds = $NUM_ROUNDS->{ $bits_in_initial_key };
461             ##### Number of Bits in Initial Key : ( $bits_in_initial_key )
462             ##### Words In Initial Key : ( $bits_in_initial_key / ( 8 * 4 ) )
463             ##### Number of Rounds : ( $number_of_rounds )
464              
465 0         0 my $state = $self->_input_to_state( $input );
466             #### Inital State: ( generate_printable_state( $state ) )
467              
468 0         0 my $key_schedule = $self->_ExpandKey( $key );
469             #### Key Schedule: ( unpack("H*", $key_schedule ) )
470              
471 0         0 $self->_AddRoundKey($state, $key_schedule, $number_of_rounds);
472             #### State After Round 0 AddRoundKey: ( generate_printable_state( $state ) )
473              
474 0         0 for( my $round = $number_of_rounds - 1; $round > 0; $round-- ) {
475             #### Processing Round Number: ( $round )
476              
477 0         0 $self->_InvShiftRows( $state );
478             #### State After InvShiftRows: ( generate_printable_state( $state ) )
479              
480 0         0 $self->_InvSubBytes( $state );
481             #### State After InvSubBytes: ( generate_printable_state( $state ) )
482              
483 0         0 $self->_AddRoundKey( $state, $key_schedule, $round );
484             #### State After AddRoundKey: ( generate_printable_state( $state ) )
485              
486 0         0 $self->_InvMixColumns( $state );
487             #### State After InvMixColumns: ( generate_printable_state( $state ) )
488             }
489              
490             #### Performing final transforms...
491              
492 0         0 $self->_InvShiftRows( $state );
493             #### State After InvShiftRows: ( generate_printable_state( $state ) )
494              
495 0         0 $self->_InvSubBytes( $state );
496             #### State After InvSubBytes: ( generate_printable_state( $state ) )
497              
498 0         0 $self->_AddRoundKey( $state, $key_schedule, 0 );
499             #### State After AddRoundKey: ( generate_printable_state( $state ) )
500              
501 0         0 return $self->_state_to_output( $state );
502             }
503              
504             sub _SubBytes {
505 1     1   50 my $self = shift;
506 1         2 my $state = shift;
507              
508 1         3 return $self->_sub_bytes( $state, \@SBOX );
509             }
510              
511             sub _InvSubBytes {
512 1     1   40 my $self = shift;
513 1         1 my $state = shift;
514              
515 1         3 return $self->_sub_bytes( $state, \@INVSBOX );
516             }
517              
518             sub _sub_bytes {
519 2     2   4 my $self = shift;
520 2         3 my $state = shift;
521 2         2 my $sbox = shift;
522              
523 2         8 for( my $column_index = 0; $column_index < 4; $column_index++ ) {
524 8         16 for( my $row_index = 0; $row_index < 4; $row_index++ ) {
525 32         30 my $original_byte = $state->[$row_index][$column_index];
526              
527 32         38 my $xy = unpack( "H2", $original_byte );
528 32         31 my $x = substr( $xy, 0, 1 );
529 32         25 my $y = substr( $xy, 1, 1 );
530              
531 32         65 my $substituted_byte = pack( "C", $sbox->[
532             ( hex($x) * 16 ) + hex($y)
533             ]);
534              
535             ##### Row Index : ( $row_index )
536             ##### Column Index : ( $column_index )
537             ##### X Coordinate : ( $x )
538             ##### Y Coordinate : ( $y )
539             ##### Original Byte : ( unpack "H2", $original_byte )
540             ##### Substituted Byte : ( unpack "H2", $substituted_byte )
541              
542 32         184 $state->[$row_index][$column_index] = $substituted_byte;
543             }
544             }
545              
546 2         9 return $state;
547             }
548              
549             sub _ShiftRows {
550 1     1   26 my $self = shift;
551 1         2 my $state = shift;
552              
553             # Row 0 does not shift
554 1         4 for( my $row_index = 1; $row_index < 4; $row_index++ ) {
555 3         4 $self->_shift_row( $state->[$row_index], $row_index );
556             }
557              
558 1         3 return $state;
559             }
560              
561             sub _shift_row {
562 7     7   6129 my $self = shift;
563 7         8 my $row = shift;
564 7         7 my $num_bytes = shift;
565              
566 7         21 for( my $shift_round = 0; $shift_round < $num_bytes; $shift_round++ ) {
567 12         10 push ( @{ $row }, shift @{ $row });
  12         13  
  12         23  
568             }
569              
570 7         20 return $row;
571             }
572              
573             sub _MixColumns {
574 1     1   24 my $self = shift;
575 1         1 my $state = shift;
576              
577 1         4 for( my $column = 0; $column < 4; $column++ ) {
578 4         16 my $mixed_column = $self->_mix_column([
579             unpack( "C", $state->[0][$column] ),
580             unpack( "C", $state->[1][$column] ),
581             unpack( "C", $state->[2][$column] ),
582             unpack( "C", $state->[3][$column] ),
583             ]);
584              
585 4         8 $state->[0][$column] = $mixed_column->[0];
586 4         5 $state->[1][$column] = $mixed_column->[1];
587 4         4 $state->[2][$column] = $mixed_column->[2];
588 4         9 $state->[3][$column] = $mixed_column->[3];
589             }
590              
591 1         5 return $state;
592             }
593              
594             sub _mix_column {
595 5     5   852 my $self = shift;
596 5         6 my $column = shift;
597              
598 5         7 my $s0 = $column->[0];
599 5         3 my $s1 = $column->[1];
600 5         5 my $s2 = $column->[2];
601 5         6 my $s3 = $column->[3];
602              
603 5         11 my $s0_prime =
604             pack( "C", gf_multiply( 0x02, $s0 ) )
605             ^ pack( "C", gf_multiply( 0x03, $s1 ) )
606             ^ pack( "C", $s2 )
607             ^ pack( "C", $s3 );
608              
609             ##### S0 => S0_Prime : ( unpack( "H2", $s0 ) . " => " . unpack( "H2", $s0_prime ) )
610              
611 5         40 my $s1_prime =
612             pack( "C", $s0 )
613             ^ pack( "C", gf_multiply( 0x02, $s1 ) )
614             ^ pack( "C", gf_multiply( 0x03, $s2 ) )
615             ^ pack( "C", $s3 );
616              
617             ##### S1 => S1_Prime : ( unpack( "H2", $s1 ) . " => " . unpack( "H2", $s1_prime ) )
618              
619 5         29 my $s2_prime =
620             pack( "C", $s0 )
621             ^ pack( "C", $s1 )
622             ^ pack( "C", gf_multiply( 0x02, $s2 ) )
623             ^ pack( "C", gf_multiply( 0x03, $s3 ) );
624              
625             ##### S2 => S2_Prime : ( unpack( "H2", $s2 ) . " => " . unpack( "H2", $s2_prime ) )
626              
627 5         23 my $s3_prime =
628             pack( "C", gf_multiply( 0x03, $s0 ) )
629             ^ pack( "C", $s1 )
630             ^ pack( "C", $s2 )
631             ^ pack( "C", gf_multiply( 0x02, $s3 ) );
632              
633             ##### S3 => S3_Prime : ( unpack( "H2", $s3 ) . " => " . unpack( "H2", $s3_prime ) )
634              
635 5         27 return [ $s0_prime, $s1_prime, $s2_prime, $s3_prime ];
636             }
637              
638             sub _InvMixColumns {
639 1     1   33 my $self = shift;
640 1         3 my $state = shift;
641              
642 1         4 for( my $column = 0; $column < 4; $column++ ) {
643 4         19 my $mixed_column = $self->_inv_mix_column([
644             unpack( "C", $state->[0][$column] ),
645             unpack( "C", $state->[1][$column] ),
646             unpack( "C", $state->[2][$column] ),
647             unpack( "C", $state->[3][$column] ),
648             ]);
649              
650 4         9 $state->[0][$column] = $mixed_column->[0];
651 4         3 $state->[1][$column] = $mixed_column->[1];
652 4         8 $state->[2][$column] = $mixed_column->[2];
653 4         11 $state->[3][$column] = $mixed_column->[3];
654             }
655              
656 1         5 return $state;
657             }
658              
659             sub _inv_mix_column {
660 5     5   1208 my $self = shift;
661 5         6 my $column = shift;
662              
663 5         6 my $s0 = $column->[0];
664 5         7 my $s1 = $column->[1];
665 5         5 my $s2 = $column->[2];
666 5         6 my $s3 = $column->[3];
667              
668 5         11 my $s0_prime =
669             pack( "C", gf_multiply( 0x0e, $s0 ) )
670             ^ pack( "C", gf_multiply( 0x0b, $s1 ) )
671             ^ pack( "C", gf_multiply( 0x0d, $s2 ) )
672             ^ pack( "C", gf_multiply( 0x09, $s3 ) );
673              
674             ##### S0 => S0_Prime : ( unpack( "H2", $s0 ) . " => " . unpack( "H2", $s0_prime ) )
675              
676 5         25 my $s1_prime =
677             pack( "C", gf_multiply( 0x09, $s0 ) )
678             ^ pack( "C", gf_multiply( 0x0e, $s1 ) )
679             ^ pack( "C", gf_multiply( 0x0b, $s2 ) )
680             ^ pack( "C", gf_multiply( 0x0d, $s3 ) );
681              
682             ##### S1 => S1_Prime : ( unpack( "H2", $s1 ) . " => " . unpack( "H2", $s1_prime ) )
683              
684 5         24 my $s2_prime =
685             pack( "C", gf_multiply( 0x0d, $s0 ) )
686             ^ pack( "C", gf_multiply( 0x09, $s1 ) )
687             ^ pack( "C", gf_multiply( 0x0e, $s2 ) )
688             ^ pack( "C", gf_multiply( 0x0b, $s3 ) );
689              
690             ##### S2 => S2_Prime : ( unpack( "H2", $s2 ) . " => " . unpack( "H2", $s2_prime ) )
691              
692 5         23 my $s3_prime =
693             pack( "C", gf_multiply( 0x0b, $s0 ) )
694             ^ pack( "C", gf_multiply( 0x0d, $s1 ) )
695             ^ pack( "C", gf_multiply( 0x09, $s2 ) )
696             ^ pack( "C", gf_multiply( 0x0e, $s3 ) );
697              
698             ##### S3 => S3_Prime : ( unpack( "H2", $s3 ) . " => " . unpack( "H2", $s3_prime ) )
699              
700 5         32 return [ $s0_prime, $s1_prime, $s2_prime, $s3_prime ];
701             }
702              
703             sub _AddRoundKey {
704 2     2   66 my $self = shift;
705 2         3 my $state = shift;
706 2         2 my $key_schedule = shift;
707 2         5 my $round = shift;
708              
709 2         5 my $relevant_key_schedule = substr( $key_schedule, ($round * 16), 16 );
710             ##### Full Key Schedule : ( unpack("H*", $key_schedule ) )
711             ##### Relevant Portion of Key Schedule : ( unpack("H*", $relevant_key_schedule ) )
712              
713 2         5 for( my $column = 0; $column < 4; $column++ ) {
714             ##### Processing Column : ( $column )
715              
716 8         10 my $key_word = substr( $relevant_key_schedule, ($column * 4 ), 4 );
717 8         27 my $state_column = pack( "C4", (
718             unpack( "C", $state->[0][$column] ),
719             unpack( "C", $state->[1][$column] ),
720             unpack( "C", $state->[2][$column] ),
721             unpack( "C", $state->[3][$column] ),
722             ) );
723             ##### Key Word : ( unpack("B*", $key_word ) . " - " . unpack("H*", $key_word ) )
724             ##### State Column : ( unpack("B*", $state_column ) . " - " . unpack("H*", $state_column ) )
725              
726 8         11 my $int_key_word = unpack( "N1", $key_word );
727 8         7 my $int_state_column = unpack( "N1", $state_column );
728 8         7 my $xored_column = $int_key_word ^ $int_state_column;
729             ##### Int Key Word : ( unpack("B*", pack( "N", $int_key_word ) ) . " - " . unpack("H*", pack( "N", $int_key_word ) ) )
730             ##### Int State Column : ( unpack("B*", pack( "N", $int_state_column ) ) . " - " . unpack("H*", pack( "N", $int_state_column ) ) )
731             ##### XOR'ed Column : ( unpack("B*", pack( "N", $xored_column ) ) . " - " . unpack("H*", pack( "N", $xored_column ) ) )
732              
733 8         18 $state->[0][$column] = pack("C", unpack( "x0C", pack( "N1", $xored_column ) ) );
734 8         12 $state->[1][$column] = pack("C", unpack( "x1C", pack( "N1", $xored_column ) ) );
735 8         13 $state->[2][$column] = pack("C", unpack( "x2C", pack( "N1", $xored_column ) ) );
736 8         21 $state->[3][$column] = pack("C", unpack( "x3C", pack( "N1", $xored_column ) ) );
737             ##### Value of State Row 0 : ( unpack("H*", $state->[0][$column] ) )
738             ##### Value of State Row 1 : ( unpack("H*", $state->[1][$column] ) )
739             ##### Value of State Row 2 : ( unpack("H*", $state->[2][$column] ) )
740             ##### Value of State Row 3 : ( unpack("H*", $state->[3][$column] ) )
741             }
742              
743 2         8 return $state;
744             }
745              
746             sub _ExpandKey {
747 1     1   483 my $self = shift;
748 1         2 my $key = shift;
749              
750             ##### Initial Key: ( unpack("H*", $key ) )
751              
752 1         2 my $expanded_key = $key;
753              
754             # Nb * (Nr + 1)
755 1         5 my $bits_in_initial_key = length( unpack("H*", $key ) ) * 4;
756 1         4 my $words_in_key = $bits_in_initial_key / ( 8 * 4 );
757 1         5 my $number_of_rounds = 4 * ( $NUM_ROUNDS->{ $bits_in_initial_key } + 1);
758             ##### Number of Bits in Initial Key : ( $bits_in_initial_key )
759             ##### Words In Initial Key : ( $words_in_key )
760             ##### Number of Rounds : ( $number_of_rounds )
761              
762 1         16 for( my $expansion_round = $words_in_key; $expansion_round < $number_of_rounds; $expansion_round++ ) {
763             ##### Expansion Round: ( $expansion_round )
764              
765 40         39 my $temp = substr( $expanded_key, ($expansion_round * 4) - 4, 4 );
766             ##### Temp : ( unpack("B*", $temp ) . " - " . unpack("H*", $temp ) )
767              
768 40 100 33     98 if( $expansion_round % $words_in_key == 0 ) {
    50          
769             ##### Performing Transformation...
770              
771 10         18 my $rotted_word = $self->_RotWord( $temp );
772             ##### Rotted Word : ( unpack("B*", $rotted_word ) . " - " . unpack("H*", $rotted_word ) )
773              
774 10         14 my $subbed_word = $self->_SubWord( $rotted_word );
775             ##### Subbed Word : ( unpack("B*", $subbed_word ) . " - " . unpack("H*", $subbed_word ) )
776              
777 10         12 my $int_subbed_word = unpack( "N1", $subbed_word );
778 10         22 $temp = $int_subbed_word ^ $RCONST[ $expansion_round / $words_in_key ];
779             ##### Int Subbed Word : ( unpack("B*", pack( "N", $int_subbed_word ) ) . " - " . unpack("H*", pack( "N", $int_subbed_word ) ) )
780             ##### Index into RCON : ( $expansion_round / $words_in_key )
781             ##### RCON : ( unpack("B*", pack( "N", $RCONST[$expansion_round / $words_in_key] ) ) . " - " . unpack("H*", pack( "N", $RCONST[$expansion_round / $words_in_key] ) ) )
782             ##### Xored Result : ( unpack("B*", pack( "N", $temp ) ) . " - " . unpack("H*", pack("N", $temp ) ) )
783              
784 10         39 $temp = pack("N1", $temp );
785             ##### Temp : ( unpack("B*", $temp ) . " - " . unpack("H*", $temp ) )
786             }
787             elsif( $words_in_key > 6 && $expansion_round % $words_in_key == 4 ) {
788             ##### Performing 256 Bit Transform...
789              
790 0         0 $temp = $self->_SubWord( $temp );
791             ##### Subbed Word : ( unpack("B*", $temp ) . " - " . unpack("H*", $temp ) )
792             }
793              
794 40         44 my $previous_word = substr( $expanded_key, ($expansion_round * 4) - ( $words_in_key * 4 ), 4 );
795 40         34 my $int_previous_word = unpack( "N1", $previous_word );
796 40         35 my $new_word = $int_previous_word ^ unpack("N1", $temp);
797             ##### Previous Word : ( unpack("B*", $previous_word) . " - " . unpack("H*", $previous_word ) )
798             ##### Int Previous Word : ( unpack("B*", pack("N", $int_previous_word)) . " - " . unpack("H*", pack("N", $int_previous_word ) ) )
799             ##### New Word : ( unpack("B*", pack("N", $new_word ) ) . " - " . unpack("H*", pack("N", $new_word ) ) )
800              
801 40         130 $expanded_key .= pack("N1", $new_word);
802             ##### Expanded Key : ( unpack("H*", $expanded_key ) )
803             }
804              
805 1         3 return $expanded_key;
806             }
807              
808             sub _SubWord {
809 10     10   10 my $self = shift;
810 10         9 my $word = shift;
811              
812 10         6 my $subbed_word = "";
813 10         22 for( my $byte_index = 0; $byte_index < 4; $byte_index++ ) {
814 40         33 my $original_byte = substr( $word, $byte_index, 1 );
815              
816 40         54 my $xy = unpack( "H2", $original_byte );
817 40         35 my $x = substr( $xy, 0, 1 );
818 40         30 my $y = substr( $xy, 1, 1 );
819              
820 40         90 my $substituted_byte = pack( "C", $SBOX[
821             ( 16 * hex($x) ) + hex($y)
822             ]);
823              
824             ##### Byte Index : ( $byte_index )
825             ##### X Coordinate : ( $x )
826             ##### Y Coordinate : ( $y )
827             ##### Original Byte : ( unpack "H2", $original_byte )
828             ##### Substituted Byte : ( unpack "H2", $substituted_byte )
829              
830 40         177 $subbed_word .= $substituted_byte;
831             }
832              
833 10         13 return $subbed_word;
834             }
835              
836             sub _RotWord {
837 10     10   14 my $self = shift;
838 10         9 my $word = shift;
839              
840 10         10 my @byte_array;
841 10         15 for( my $byte_index = 0; $byte_index < 4; $byte_index++ ) {
842 40         71 push @byte_array, substr( $word, $byte_index, 1 );
843             }
844              
845 10         8 push (@byte_array, shift @byte_array);
846              
847 10         27 return join('', @byte_array );
848             }
849              
850             sub _input_to_state {
851 19     19   23729 my $self = shift;
852 19         37 my $input = shift;
853              
854             ##### Input : ( unpack( "H*", $input ) )
855             ##### Length of Input : ( length $input )
856              
857 19 100       76 if( length $input != 16 ) {
858 2         30 croak "Invalid Input Length, Must be 128 Bits";
859             }
860              
861 17         17 my $state;
862              
863 17         25 my $byte_index = 0;
864 17         55 for( my $column_index = 0; $column_index < 4; $column_index++ ) {
865 68         114 for( my $row_index = 0; $row_index < 4; $row_index++ ) {
866 272         417 my $byte = unpack("x" . ( $byte_index++ ) . "a", $input );
867              
868             ##### Row Index : ( $row_index )
869             ##### Column Index : ( $column_index )
870             ##### Byte Index : ( $byte_index )
871             ##### Raw Byte : ( $byte )
872             ##### Byte : ( unpack "H2", $byte )
873              
874 272         571 $state->[$row_index][$column_index] = $byte;
875             }
876             }
877              
878 17         36 return $state;
879             }
880              
881             sub _state_to_output {
882 0     0   0 my $self = shift;
883 0         0 my $state = shift;
884              
885 0         0 my $output = "";
886              
887 0         0 for( my $column_index = 0; $column_index < 4; $column_index++ ) {
888 0         0 for( my $row_index = 0; $row_index < 4; $row_index++ ) {
889 0         0 $output .= $state->[$row_index][$column_index];
890             }
891             }
892              
893 0         0 return $output;
894             }
895              
896             sub _InvShiftRows {
897 1     1   25 my $self = shift;
898 1         1 my $state = shift;
899              
900             # Row 0 does not shift
901 1         4 for( my $row_index = 1; $row_index < 4; $row_index++ ) {
902 3         4 $self->_inv_shift_row( $state->[$row_index], $row_index );
903             }
904              
905 1         3 return $state;
906             }
907             sub _inv_shift_row {
908 7     7   5773 my $self = shift;
909 7         6 my $row = shift;
910 7         6 my $num_bytes = shift;
911              
912 7         15 for( my $shift_round = 0; $shift_round < $num_bytes; $shift_round++ ) {
913 12         10 unshift ( @{ $row }, pop @{ $row });
  12         11  
  12         26  
914             }
915              
916 7         18 return $row;
917             }
918              
919             1;