| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2 |  |  |  |  |  |  | # File:         AES.pm | 
| 3 |  |  |  |  |  |  | # | 
| 4 |  |  |  |  |  |  | # Description:  AES encryption with cipher-block chaining | 
| 5 |  |  |  |  |  |  | # | 
| 6 |  |  |  |  |  |  | # Revisions:    2010/10/14 - P. Harvey Created | 
| 7 |  |  |  |  |  |  | # | 
| 8 |  |  |  |  |  |  | # References:   1) http://www.hoozi.com/Articles/AESEncryption.htm | 
| 9 |  |  |  |  |  |  | #               2) http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf | 
| 10 |  |  |  |  |  |  | #               3) http://www.faqs.org/rfcs/rfc3602.html | 
| 11 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | package Image::ExifTool::AES; | 
| 14 |  |  |  |  |  |  |  | 
| 15 | 1 |  |  | 1 |  | 733 | use strict; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 46 |  | 
| 16 | 1 |  |  | 1 |  | 5 | use vars qw($VERSION @ISA @EXPORT_OK); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 2487 |  | 
| 17 |  |  |  |  |  |  | require Exporter; | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | $VERSION = '1.01'; | 
| 20 |  |  |  |  |  |  | @ISA = qw(Exporter); | 
| 21 |  |  |  |  |  |  | @EXPORT_OK = qw(Crypt); | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | my $seeded; # flag set if we already seeded random number generator | 
| 24 |  |  |  |  |  |  | my $nr;     # number of rounds in AES cipher | 
| 25 |  |  |  |  |  |  | my @cbc;    # cipher-block chaining bytes | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | # arrays (all unsigned character) to hold intermediate results during encryption | 
| 28 |  |  |  |  |  |  | my @state = ([],[],[],[]);  # the 2-dimensional state array | 
| 29 |  |  |  |  |  |  | my @RoundKey;               # round keys | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | my @sbox = ( | 
| 32 |  |  |  |  |  |  | 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, | 
| 33 |  |  |  |  |  |  | 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, | 
| 34 |  |  |  |  |  |  | 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, | 
| 35 |  |  |  |  |  |  | 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, | 
| 36 |  |  |  |  |  |  | 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, | 
| 37 |  |  |  |  |  |  | 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, | 
| 38 |  |  |  |  |  |  | 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, | 
| 39 |  |  |  |  |  |  | 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, | 
| 40 |  |  |  |  |  |  | 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, | 
| 41 |  |  |  |  |  |  | 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, | 
| 42 |  |  |  |  |  |  | 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, | 
| 43 |  |  |  |  |  |  | 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, | 
| 44 |  |  |  |  |  |  | 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, | 
| 45 |  |  |  |  |  |  | 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, | 
| 46 |  |  |  |  |  |  | 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, | 
| 47 |  |  |  |  |  |  | 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, | 
| 48 |  |  |  |  |  |  | ); | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | # reverse sbox | 
| 51 |  |  |  |  |  |  | my @rsbox = ( | 
| 52 |  |  |  |  |  |  | 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, | 
| 53 |  |  |  |  |  |  | 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, | 
| 54 |  |  |  |  |  |  | 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, | 
| 55 |  |  |  |  |  |  | 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, | 
| 56 |  |  |  |  |  |  | 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, | 
| 57 |  |  |  |  |  |  | 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, | 
| 58 |  |  |  |  |  |  | 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, | 
| 59 |  |  |  |  |  |  | 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, | 
| 60 |  |  |  |  |  |  | 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, | 
| 61 |  |  |  |  |  |  | 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, | 
| 62 |  |  |  |  |  |  | 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, | 
| 63 |  |  |  |  |  |  | 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, | 
| 64 |  |  |  |  |  |  | 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, | 
| 65 |  |  |  |  |  |  | 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, | 
| 66 |  |  |  |  |  |  | 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, | 
| 67 |  |  |  |  |  |  | 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d, | 
| 68 |  |  |  |  |  |  | ); | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | # the round constant word array, $rcon[i], contains the values given by | 
| 71 |  |  |  |  |  |  | # x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) | 
| 72 |  |  |  |  |  |  | # Note that i starts at 1, not 0). | 
| 73 |  |  |  |  |  |  | my @rcon = ( | 
| 74 |  |  |  |  |  |  | 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, | 
| 75 |  |  |  |  |  |  | 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, | 
| 76 |  |  |  |  |  |  | 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, | 
| 77 |  |  |  |  |  |  | 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, | 
| 78 |  |  |  |  |  |  | 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, | 
| 79 |  |  |  |  |  |  | 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, | 
| 80 |  |  |  |  |  |  | 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, | 
| 81 |  |  |  |  |  |  | 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, | 
| 82 |  |  |  |  |  |  | 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, | 
| 83 |  |  |  |  |  |  | 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, | 
| 84 |  |  |  |  |  |  | 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, | 
| 85 |  |  |  |  |  |  | 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, | 
| 86 |  |  |  |  |  |  | 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, | 
| 87 |  |  |  |  |  |  | 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, | 
| 88 |  |  |  |  |  |  | 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, | 
| 89 |  |  |  |  |  |  | 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, | 
| 90 |  |  |  |  |  |  | ); | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 93 |  |  |  |  |  |  | # This function produces 4*($nr+1) round keys. | 
| 94 |  |  |  |  |  |  | # The round keys are used in each round to encrypt the states. | 
| 95 |  |  |  |  |  |  | # Inputs: 0) key string (must be 16, 24 or 32 bytes long) | 
| 96 |  |  |  |  |  |  | sub KeyExpansion($) | 
| 97 |  |  |  |  |  |  | { | 
| 98 | 6 |  |  | 6 | 0 | 11 | my $key = shift; | 
| 99 | 6 |  |  |  |  | 29 | my @key = unpack 'C*', $key;        # convert the key into a byte array | 
| 100 | 6 |  |  |  |  | 23 | my $nk = int(length($key) / 4);     # number of 32-bit words in the key | 
| 101 | 6 |  |  |  |  | 9 | $nr = $nk + 6;                      # number of rounds | 
| 102 |  |  |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | # temporary variables (all unsigned characters) | 
| 104 | 6 |  |  |  |  | 15 | my ($i,@temp); | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | # The first round key is the key itself. | 
| 107 | 6 |  |  |  |  | 21 | for ($i=0; $i<$nk; ++$i) { | 
| 108 | 44 |  |  |  |  | 168 | @RoundKey[$i*4..$i*4+3] = @key[$i*4..$i*4+3]; | 
| 109 |  |  |  |  |  |  | } | 
| 110 |  |  |  |  |  |  | # All other round keys are found from the previous round keys. | 
| 111 | 6 |  |  |  |  | 33 | while ($i < (4 * ($nr+1))) { | 
| 112 |  |  |  |  |  |  |  | 
| 113 | 300 |  |  |  |  | 557 | @temp[0..3] = @RoundKey[($i-1)*4..($i-1)*4+3]; | 
| 114 |  |  |  |  |  |  |  | 
| 115 | 300 | 100 | 100 |  |  | 868 | if ($i % $nk == 0) { | 
|  |  | 100 |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | # rotate the 4 bytes in a word to the left once | 
| 117 |  |  |  |  |  |  | # [a0,a1,a2,a3] becomes [a1,a2,a3,a0] | 
| 118 | 45 |  |  |  |  | 83 | @temp[0..3] = @temp[1,2,3,0]; | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | # take a four-byte input word and apply the S-box | 
| 121 |  |  |  |  |  |  | # to each of the four bytes to produce an output word. | 
| 122 | 45 |  |  |  |  | 95 | @temp[0..3] = @sbox[@temp[0..3]]; | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 45 |  |  |  |  | 86 | $temp[0] = $temp[0] ^ $rcon[$i/$nk]; | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | } elsif ($nk > 6 && $i % $nk == 4) { | 
| 127 |  |  |  |  |  |  |  | 
| 128 | 30 |  |  |  |  | 67 | @temp[0..3] = @sbox[@temp[0..3]]; | 
| 129 |  |  |  |  |  |  | } | 
| 130 | 300 |  |  |  |  | 486 | $RoundKey[$i*4+0] = $RoundKey[($i-$nk)*4+0] ^ $temp[0]; | 
| 131 | 300 |  |  |  |  | 487 | $RoundKey[$i*4+1] = $RoundKey[($i-$nk)*4+1] ^ $temp[1]; | 
| 132 | 300 |  |  |  |  | 487 | $RoundKey[$i*4+2] = $RoundKey[($i-$nk)*4+2] ^ $temp[2]; | 
| 133 | 300 |  |  |  |  | 441 | $RoundKey[$i*4+3] = $RoundKey[($i-$nk)*4+3] ^ $temp[3]; | 
| 134 | 300 |  |  |  |  | 540 | ++$i; | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 139 |  |  |  |  |  |  | # This function adds the round key to state. | 
| 140 |  |  |  |  |  |  | # The round key is added to the state by an XOR function. | 
| 141 |  |  |  |  |  |  | sub AddRoundKey($) | 
| 142 |  |  |  |  |  |  | { | 
| 143 | 131 |  |  | 131 | 0 | 239 | my $round = shift; | 
| 144 | 131 |  |  |  |  | 195 | my ($i,$j); | 
| 145 | 131 |  |  |  |  | 249 | for ($i=0; $i<4; ++$i) { | 
| 146 | 524 |  |  |  |  | 748 | my $k = $round*16 + $i*4; | 
| 147 | 524 |  |  |  |  | 873 | for ($j=0; $j<4; ++$j) { | 
| 148 | 2096 |  |  |  |  | 4008 | $state[$j][$i] ^= $RoundKey[$k + $j]; | 
| 149 |  |  |  |  |  |  | } | 
| 150 |  |  |  |  |  |  | } | 
| 151 |  |  |  |  |  |  | } | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 154 |  |  |  |  |  |  | # Substitute the values in the state matrix with values in an S-box | 
| 155 |  |  |  |  |  |  | sub SubBytes() | 
| 156 |  |  |  |  |  |  | { | 
| 157 | 0 |  |  | 0 | 0 | 0 | my $i; | 
| 158 | 0 |  |  |  |  | 0 | for ($i=0; $i<4; ++$i) { | 
| 159 | 0 |  |  |  |  | 0 | @{$state[$i]}[0..3] = @sbox[@{$state[$i]}[0..3]]; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 160 |  |  |  |  |  |  | } | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | sub InvSubBytes() | 
| 164 |  |  |  |  |  |  | { | 
| 165 | 122 |  |  | 122 | 0 | 163 | my $i; | 
| 166 | 122 |  |  |  |  | 250 | for ($i=0; $i<4; ++$i) { | 
| 167 | 488 |  |  |  |  | 644 | @{$state[$i]}[0..3] = @rsbox[@{$state[$i]}[0..3]]; | 
|  | 488 |  |  |  |  | 1057 |  | 
|  | 488 |  |  |  |  | 805 |  | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  | } | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 172 |  |  |  |  |  |  | # Shift the rows in the state to the left. | 
| 173 |  |  |  |  |  |  | # Each row is shifted with different offset. | 
| 174 |  |  |  |  |  |  | # Offset = Row number. So the first row is not shifted. | 
| 175 |  |  |  |  |  |  | sub ShiftRows() | 
| 176 |  |  |  |  |  |  | { | 
| 177 |  |  |  |  |  |  | # rotate first row 1 columns to left | 
| 178 | 0 |  |  | 0 | 0 | 0 | @{$state[1]}[0,1,2,3] = @{$state[1]}[1,2,3,0]; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | # rotate second row 2 columns to left | 
| 181 | 0 |  |  |  |  | 0 | @{$state[2]}[0,1,2,3] = @{$state[2]}[2,3,0,1]; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | # rotate third row 3 columns to left | 
| 184 | 0 |  |  |  |  | 0 | @{$state[3]}[0,1,2,3] = @{$state[3]}[3,0,1,2]; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 185 |  |  |  |  |  |  | } | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | sub InvShiftRows() | 
| 188 |  |  |  |  |  |  | { | 
| 189 |  |  |  |  |  |  | # rotate first row 1 columns to right | 
| 190 | 122 |  |  | 122 | 0 | 159 | @{$state[1]}[0,1,2,3] = @{$state[1]}[3,0,1,2]; | 
|  | 122 |  |  |  |  | 229 |  | 
|  | 122 |  |  |  |  | 232 |  | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | # rotate second row 2 columns to right | 
| 193 | 122 |  |  |  |  | 201 | @{$state[2]}[0,1,2,3] = @{$state[2]}[2,3,0,1]; | 
|  | 122 |  |  |  |  | 222 |  | 
|  | 122 |  |  |  |  | 211 |  | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | # rotate third row 3 columns to right | 
| 196 | 122 |  |  |  |  | 194 | @{$state[3]}[0,1,2,3] = @{$state[3]}[1,2,3,0]; | 
|  | 122 |  |  |  |  | 216 |  | 
|  | 122 |  |  |  |  | 193 |  | 
| 197 |  |  |  |  |  |  | } | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 200 |  |  |  |  |  |  | # Find the product of {02} and the argument to xtime modulo 0x1b | 
| 201 |  |  |  |  |  |  | # Note: returns an integer which may need to be trimmed to 8 bits | 
| 202 |  |  |  |  |  |  | sub xtime($) | 
| 203 |  |  |  |  |  |  | { | 
| 204 | 72320 |  |  | 72320 | 0 | 116122 | return ($_[0]<<1) ^ ((($_[0]>>7) & 1) * 0x1b); | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 208 |  |  |  |  |  |  | # Multiply numbers in the field GF(2^8) | 
| 209 |  |  |  |  |  |  | sub Mult($$) | 
| 210 |  |  |  |  |  |  | { | 
| 211 | 7232 |  |  | 7232 | 0 | 10330 | my ($x, $y) = @_; | 
| 212 | 7232 |  |  |  |  | 11345 | return (($y & 1) * $x) ^ | 
| 213 |  |  |  |  |  |  | (($y>>1 & 1) * xtime($x)) ^ | 
| 214 |  |  |  |  |  |  | (($y>>2 & 1) * xtime(xtime($x))) ^ | 
| 215 |  |  |  |  |  |  | (($y>>3 & 1) * xtime(xtime(xtime($x)))) ^ | 
| 216 |  |  |  |  |  |  | (($y>>4 & 1) * xtime(xtime(xtime(xtime($x))))); | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 220 |  |  |  |  |  |  | # Mix the columns of the state matrix | 
| 221 |  |  |  |  |  |  | sub MixColumns() | 
| 222 |  |  |  |  |  |  | { | 
| 223 | 0 |  |  | 0 | 0 | 0 | my ($i,$t0,$t1,$t2); | 
| 224 | 0 |  |  |  |  | 0 | for ($i=0; $i<4; ++$i) { | 
| 225 | 0 |  |  |  |  | 0 | $t0 = $state[0][$i]; | 
| 226 | 0 |  |  |  |  | 0 | $t2 = $state[0][$i] ^ $state[1][$i] ^ $state[2][$i] ^ $state[3][$i]; | 
| 227 | 0 |  |  |  |  | 0 | $t1 = $state[0][$i] ^ $state[1][$i] ; $t1 = xtime($t1) & 0xff; $state[0][$i] ^= $t1 ^ $t2 ; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 228 | 0 |  |  |  |  | 0 | $t1 = $state[1][$i] ^ $state[2][$i] ; $t1 = xtime($t1) & 0xff; $state[1][$i] ^= $t1 ^ $t2 ; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 229 | 0 |  |  |  |  | 0 | $t1 = $state[2][$i] ^ $state[3][$i] ; $t1 = xtime($t1) & 0xff; $state[2][$i] ^= $t1 ^ $t2 ; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 230 | 0 |  |  |  |  | 0 | $t1 = $state[3][$i] ^ $t0 ;           $t1 = xtime($t1) & 0xff; $state[3][$i] ^= $t1 ^ $t2 ; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 231 |  |  |  |  |  |  | } | 
| 232 |  |  |  |  |  |  | } | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | sub InvMixColumns() | 
| 235 |  |  |  |  |  |  | { | 
| 236 | 113 |  |  | 113 | 0 | 190 | my $i; | 
| 237 | 113 |  |  |  |  | 218 | for ($i=0; $i<4; ++$i) { | 
| 238 | 452 |  |  |  |  | 704 | my $a = $state[0][$i]; | 
| 239 | 452 |  |  |  |  | 666 | my $b = $state[1][$i]; | 
| 240 | 452 |  |  |  |  | 621 | my $c = $state[2][$i]; | 
| 241 | 452 |  |  |  |  | 642 | my $d = $state[3][$i]; | 
| 242 | 452 |  |  |  |  | 694 | $state[0][$i] = (Mult($a,0x0e) ^ Mult($b,0x0b) ^ Mult($c,0x0d) ^ Mult($d,0x09)) & 0xff; | 
| 243 | 452 |  |  |  |  | 938 | $state[1][$i] = (Mult($a,0x09) ^ Mult($b,0x0e) ^ Mult($c,0x0b) ^ Mult($d,0x0d)) & 0xff; | 
| 244 | 452 |  |  |  |  | 911 | $state[2][$i] = (Mult($a,0x0d) ^ Mult($b,0x09) ^ Mult($c,0x0e) ^ Mult($d,0x0b)) & 0xff; | 
| 245 | 452 |  |  |  |  | 915 | $state[3][$i] = (Mult($a,0x0b) ^ Mult($b,0x0d) ^ Mult($c,0x09) ^ Mult($d,0x0e)) & 0xff; | 
| 246 |  |  |  |  |  |  | } | 
| 247 |  |  |  |  |  |  | } | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 250 |  |  |  |  |  |  | # Encrypt (Cipher) or decrypt (InvCipher) a block of data with CBC | 
| 251 |  |  |  |  |  |  | # Inputs: 0) string to cipher (must be 16 bytes long) | 
| 252 |  |  |  |  |  |  | # Returns: cipher'd string | 
| 253 |  |  |  |  |  |  | sub Cipher($) | 
| 254 |  |  |  |  |  |  | { | 
| 255 | 0 |  |  | 0 | 0 | 0 | my @in = unpack 'C*', $_[0];    # unpack input plaintext | 
| 256 | 0 |  |  |  |  | 0 | my ($i, $j, $round); | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | # copy the input PlainText to state array and apply the CBC | 
| 259 | 0 |  |  |  |  | 0 | for ($i=0; $i<4; ++$i) { | 
| 260 | 0 |  |  |  |  | 0 | for ($j=0; $j<4; ++$j) { | 
| 261 | 0 |  |  |  |  | 0 | my $k = $i*4 + $j; | 
| 262 | 0 |  |  |  |  | 0 | $state[$j][$i] = $in[$k] ^ $cbc[$k]; | 
| 263 |  |  |  |  |  |  | } | 
| 264 |  |  |  |  |  |  | } | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | # add the First round key to the state before starting the rounds | 
| 267 | 0 |  |  |  |  | 0 | AddRoundKey(0); | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | # there will be $nr rounds; the first $nr-1 rounds are identical | 
| 270 | 0 |  |  |  |  | 0 | for ($round=1; ; ++$round) { | 
| 271 | 0 |  |  |  |  | 0 | SubBytes(); | 
| 272 | 0 |  |  |  |  | 0 | ShiftRows(); | 
| 273 | 0 | 0 |  |  |  | 0 | if ($round < $nr) { | 
| 274 | 0 |  |  |  |  | 0 | MixColumns(); | 
| 275 | 0 |  |  |  |  | 0 | AddRoundKey($round); | 
| 276 |  |  |  |  |  |  | } else { | 
| 277 |  |  |  |  |  |  | # MixColumns() is not used in the last round | 
| 278 | 0 |  |  |  |  | 0 | AddRoundKey($nr); | 
| 279 | 0 |  |  |  |  | 0 | last; | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  | } | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | # the encryption process is over | 
| 284 |  |  |  |  |  |  | # copy the state array to output array (and save for CBC) | 
| 285 | 0 |  |  |  |  | 0 | for ($i=0; $i<4; ++$i) { | 
| 286 | 0 |  |  |  |  | 0 | for ($j=0; $j<4; ++$j) { | 
| 287 | 0 |  |  |  |  | 0 | $cbc[$i*4+$j] = $state[$j][$i]; | 
| 288 |  |  |  |  |  |  | } | 
| 289 |  |  |  |  |  |  | } | 
| 290 | 0 |  |  |  |  | 0 | return pack 'C*', @cbc; # return packed ciphertext | 
| 291 |  |  |  |  |  |  | } | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | sub InvCipher($) | 
| 294 |  |  |  |  |  |  | { | 
| 295 | 9 |  |  | 9 | 0 | 49 | my @in = unpack 'C*', $_[0];    # unpack input ciphertext | 
| 296 | 9 |  |  |  |  | 19 | my (@out, $i, $j, $round); | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | # copy the input CipherText to state array | 
| 299 | 9 |  |  |  |  | 24 | for ($i=0; $i<4; ++$i) { | 
| 300 | 36 |  |  |  |  | 68 | for ($j=0; $j<4; ++$j) { | 
| 301 | 144 |  |  |  |  | 306 | $state[$j][$i] = $in[$i*4 + $j]; | 
| 302 |  |  |  |  |  |  | } | 
| 303 |  |  |  |  |  |  | } | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | # add the First round key to the state before starting the rounds | 
| 306 | 9 |  |  |  |  | 33 | AddRoundKey($nr); | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | # there will be $nr rounds; the first $nr-1 rounds are identical | 
| 309 | 9 |  |  |  |  | 16 | for ($round=$nr-1; ; --$round) { | 
| 310 | 122 |  |  |  |  | 292 | InvShiftRows(); | 
| 311 | 122 |  |  |  |  | 271 | InvSubBytes(); | 
| 312 | 122 |  |  |  |  | 285 | AddRoundKey($round); | 
| 313 |  |  |  |  |  |  | # InvMixColumns() is not used in the last round | 
| 314 | 122 | 100 |  |  |  | 250 | last if $round <= 0; | 
| 315 | 113 |  |  |  |  | 189 | InvMixColumns(); | 
| 316 |  |  |  |  |  |  | } | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | # copy the state array to output array and reverse the CBC | 
| 319 | 9 |  |  |  |  | 30 | for ($i=0; $i<4; ++$i) { | 
| 320 | 36 |  |  |  |  | 79 | for ($j=0; $j<4; ++$j) { | 
| 321 | 144 |  |  |  |  | 198 | my $k = $i*4 + $j; | 
| 322 | 144 |  |  |  |  | 328 | $out[$k] = $state[$j][$i] ^ $cbc[$k]; | 
| 323 |  |  |  |  |  |  | } | 
| 324 |  |  |  |  |  |  | } | 
| 325 | 9 |  |  |  |  | 31 | @cbc = @in;             # update CBC for next block | 
| 326 | 9 |  |  |  |  | 93 | return pack 'C*', @out; # return packed plaintext | 
| 327 |  |  |  |  |  |  | } | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 330 |  |  |  |  |  |  | # Encrypt/Decrypt using AES-CBC algorithm (with fixed 16-byte blocks) | 
| 331 |  |  |  |  |  |  | # Inputs: 0) data reference (with leading 16-byte initialization vector when decrypting) | 
| 332 |  |  |  |  |  |  | #         1) encryption key (16, 24 or 32 bytes for AES-128, AES-192 or AES-256) | 
| 333 |  |  |  |  |  |  | #         2) encrypt flag (false for decryption, true with length 16 bytes to | 
| 334 |  |  |  |  |  |  | #            encrypt using this as the CBC IV, or true with other length to | 
| 335 |  |  |  |  |  |  | #            encrypt with a randomly-generated IV) | 
| 336 |  |  |  |  |  |  | #         3) flag to disable padding | 
| 337 |  |  |  |  |  |  | # Returns: error string, or undef on success | 
| 338 |  |  |  |  |  |  | # Notes: encrypts/decrypts data in place (encrypted data returned with leading IV) | 
| 339 |  |  |  |  |  |  | sub Crypt($$;$$) | 
| 340 |  |  |  |  |  |  | { | 
| 341 | 6 |  |  | 6 | 1 | 34 | my ($dataPt, $key, $encrypt, $noPad) = @_; | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | # validate key length | 
| 344 | 6 |  |  |  |  | 16 | my $keyLen = length $key; | 
| 345 | 6 | 50 | 66 |  |  | 51 | unless ($keyLen == 16 or $keyLen == 24 or $keyLen == 32) { | 
|  |  |  | 66 |  |  |  |  | 
| 346 | 0 |  |  |  |  | 0 | return "Invalid AES key length ($keyLen)"; | 
| 347 |  |  |  |  |  |  | } | 
| 348 | 6 |  |  |  |  | 18 | my $partLen = length($$dataPt) % 16; | 
| 349 | 6 |  |  |  |  | 16 | my ($pos, $i); | 
| 350 | 6 | 50 |  |  |  | 43 | if ($encrypt) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 351 | 0 | 0 |  |  |  | 0 | if (length($encrypt) == 16) { | 
| 352 | 0 |  |  |  |  | 0 | @cbc = unpack 'C*', $encrypt; | 
| 353 |  |  |  |  |  |  | } else { | 
| 354 |  |  |  |  |  |  | # generate a random 16-byte CBC initialization vector | 
| 355 | 0 | 0 |  |  |  | 0 | unless ($seeded) { | 
| 356 | 0 |  |  |  |  | 0 | srand(time() & ($$ + ($$<<15))); | 
| 357 | 0 |  |  |  |  | 0 | $seeded = 1; | 
| 358 |  |  |  |  |  |  | } | 
| 359 | 0 |  |  |  |  | 0 | for ($i=0; $i<16; ++$i) { | 
| 360 | 0 |  |  |  |  | 0 | $cbc[$i] = int(rand(256)); | 
| 361 |  |  |  |  |  |  | } | 
| 362 | 0 |  |  |  |  | 0 | $encrypt = pack 'C*', @cbc; | 
| 363 |  |  |  |  |  |  | } | 
| 364 | 0 |  |  |  |  | 0 | $$dataPt = $encrypt . $$dataPt; # add IV to the start of the data | 
| 365 |  |  |  |  |  |  | # add required padding so we can recover the | 
| 366 |  |  |  |  |  |  | # original string length after decryption | 
| 367 |  |  |  |  |  |  | # (padding bytes have value set to padding length) | 
| 368 | 0 |  |  |  |  | 0 | my $padLen = 16 - $partLen; | 
| 369 | 0 | 0 | 0 |  |  | 0 | $$dataPt .= (chr($padLen)) x $padLen unless $padLen == 16 and $noPad; | 
| 370 | 0 |  |  |  |  | 0 | $pos = 16;      # start encrypting at byte 16 (after the IV) | 
| 371 |  |  |  |  |  |  | } elsif ($partLen) { | 
| 372 | 0 |  |  |  |  | 0 | return 'Invalid AES ciphertext length'; | 
| 373 |  |  |  |  |  |  | } elsif (length $$dataPt >= 32) { | 
| 374 |  |  |  |  |  |  | # take the CBC initialization vector from the start of the data | 
| 375 | 6 |  |  |  |  | 25 | @cbc = unpack 'C16', $$dataPt; | 
| 376 | 6 |  |  |  |  | 17 | $$dataPt = substr($$dataPt, 16); | 
| 377 | 6 |  |  |  |  | 11 | $pos = 0;       # start decrypting from byte 0 (now that IV is removed) | 
| 378 |  |  |  |  |  |  | } else { | 
| 379 | 0 |  |  |  |  | 0 | $$dataPt = '';  # empty text | 
| 380 | 0 |  |  |  |  | 0 | return undef; | 
| 381 |  |  |  |  |  |  | } | 
| 382 |  |  |  |  |  |  | # the KeyExpansion routine must be called before encryption | 
| 383 | 6 |  |  |  |  | 23 | KeyExpansion($key); | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | # loop through the data and convert in blocks | 
| 386 | 6 |  |  |  |  | 13 | my $dataLen = length $$dataPt; | 
| 387 | 6 |  |  |  |  | 18 | my $last = $dataLen - 16; | 
| 388 | 6 | 50 |  |  |  | 19 | my $func = $encrypt ? \&Cipher : \&InvCipher; | 
| 389 | 6 |  |  |  |  | 17 | while ($pos <= $last) { | 
| 390 |  |  |  |  |  |  | # cipher this block | 
| 391 | 9 |  |  |  |  | 34 | substr($$dataPt, $pos, 16) = &$func(substr($$dataPt, $pos, 16)); | 
| 392 | 9 |  |  |  |  | 53 | $pos += 16; | 
| 393 |  |  |  |  |  |  | } | 
| 394 | 6 | 100 | 66 |  |  | 57 | unless ($encrypt or $noPad) { | 
| 395 |  |  |  |  |  |  | # remove padding if necessary (padding byte value gives length of padding) | 
| 396 | 4 |  |  |  |  | 15 | my $padLen = ord(substr($$dataPt, -1, 1)); | 
| 397 | 4 | 50 |  |  |  | 13 | return 'AES decryption error (invalid pad byte)' if $padLen > 16; | 
| 398 | 4 |  |  |  |  | 15 | $$dataPt = substr($$dataPt, 0, $dataLen - $padLen); | 
| 399 |  |  |  |  |  |  | } | 
| 400 | 6 |  |  |  |  | 37 | return undef; | 
| 401 |  |  |  |  |  |  | } | 
| 402 |  |  |  |  |  |  |  | 
| 403 |  |  |  |  |  |  | 1; # end | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | __END__ |