1#!/usr/bin/env perl 2 3## 4## Author......: See docs/credits.txt 5## License.....: MIT 6## 7 8use strict; 9use warnings; 10 11use Crypt::CBC; 12use Digest::CRC qw (crc32); 13use Digest::SHA qw (sha256); 14use Encode; 15 16sub module_constraints { [[0, 256], [0, 16], [0, 20], [0, 16], [-1, -1]] } 17 18sub module_generate_hash 19{ 20 my $word_buf = shift; 21 my $salt_buf = shift; 22 my $iter = shift; 23 my $additional_param = shift; 24 my $additional_param2 = shift; 25 my $additional_param3 = shift; 26 my $additional_param4 = shift; 27 my $additional_param5 = shift; 28 my $additional_param6 = shift; 29 30 my ($p, $num_cycle_power, $seven_zip_salt_len, $seven_zip_salt_buf, $salt_len, $data_len, $unpack_size, $data_buf); 31 32 $p = 0; # is fixed 33 34 my $validation_only = 0; 35 36 $validation_only = 1 if (defined ($additional_param)); 37 38 if ($validation_only == 1) 39 { 40 $num_cycle_power = int ($iter); 41 $seven_zip_salt_len = $additional_param; 42 $seven_zip_salt_buf = $additional_param2; 43 $salt_len = $additional_param3; 44 # $salt_buf set in parser 45 # $hash_buf (resulting crc) 46 $data_len = $additional_param4; 47 $unpack_size = $additional_param5; 48 $data_buf = $additional_param6; 49 } 50 else 51 { 52 $num_cycle_power = 14; # by default it is 19 53 $seven_zip_salt_len = 0; 54 $seven_zip_salt_buf = ""; 55 $salt_len = length ($salt_buf); 56 # $salt_buf set automatically 57 # $hash_buf (resulting crc) 58 # $data_len will be set when encrypting 59 $unpack_size = random_number (1, 32 + 1); 60 $data_buf = random_string ($unpack_size); 61 } 62 63 # 64 # 2 ^ NumCyclesPower "iterations" of SHA256 (only one final SHA256) 65 # 66 67 $word_buf = encode ("UTF-16LE", $word_buf); 68 69 my $rounds = 1 << $num_cycle_power; 70 71 my $pass_buf = ""; 72 73 for (my $i = 0; $i < $rounds; $i++) 74 { 75 my $num_buf = ""; 76 77 $num_buf .= pack ("V", $i); 78 $num_buf .= "\x00" x 4; 79 80 # this would be better but only works on 64-bit systems: 81 # $num_buf = pack ("q", $i); 82 83 $pass_buf .= sprintf ("%s%s", $word_buf, $num_buf); 84 } 85 86 my $key = sha256 ($pass_buf); 87 88 # the salt_buf is our IV for AES CBC 89 # pad the salt_buf 90 91 my $salt_buf_len = length ($salt_buf); 92 my $salt_padding_len = 0; 93 94 if ($salt_buf_len < 16) 95 { 96 $salt_padding_len = 16 - $salt_buf_len; 97 } 98 99 $salt_buf .= "\x00" x $salt_padding_len; 100 101 my $aes = Crypt::CBC->new ({ 102 cipher => "Crypt::Rijndael", 103 key => $key, 104 keysize => 32, 105 literal_key => 1, 106 iv => $salt_buf, 107 header => "none", 108 }); 109 110 my $hash_buf; 111 112 if ($validation_only == 1) 113 { 114 # decrypt 115 116 my $decrypted_data = $aes->decrypt ($data_buf); 117 118 $decrypted_data = substr ($decrypted_data, 0, $unpack_size); 119 120 $hash_buf = crc32 ($decrypted_data); 121 } 122 else 123 { 124 # encrypt 125 126 $hash_buf = crc32 ($data_buf); 127 128 $data_buf = $aes->encrypt ($data_buf); 129 130 $data_len = length ($data_buf); 131 } 132 133 my $tmp_hash = sprintf ("\$7z\$%i\$%i\$%i\$%s\$%i\$%08s\$%u\$%u\$%u\$%s", $p, $num_cycle_power, $seven_zip_salt_len, $seven_zip_salt_buf, $salt_len, unpack ("H*", $salt_buf), $hash_buf, $data_len, $unpack_size, unpack ("H*", $data_buf)); 134 135 return $tmp_hash; 136} 137 138sub module_verify_hash 139{ 140 my $line = shift; 141 142 return unless (substr ($line, 0, 4) eq '$7z$'); 143 144 # p 145 146 my $index1 = index ($line, '$', 4); 147 148 return if $index1 < 0; 149 150 my $p = substr ($line, 4, $index1 - 4); 151 152 return unless ($p eq "0"); 153 154 # num cycle power 155 156 my $index2 = index ($line, '$', $index1 + 1); 157 158 return if $index2 < 0; 159 160 my $iter = substr ($line, $index1 + 1, $index2 - $index1 - 1); 161 162 # seven zip salt length 163 164 $index1 = index ($line, '$', $index2 + 1); 165 166 return if $index1 < 0; 167 168 my $param = substr ($line, $index2 + 1, $index1 - $index2 - 1); 169 170 # seven zip salt 171 172 $index2 = index ($line, '$', $index1 + 1); 173 174 return if $index2 < 0; 175 176 my $param2 = substr ($line, $index1 + 1, $index2 - $index1 - 1); 177 178 # salt len 179 180 $index1 = index ($line, '$', $index2 + 1); 181 182 return if $index1 < 0; 183 184 my $param3 = substr ($line, $index2 + 1, $index1 - $index2 - 1); 185 186 # salt 187 188 $index2 = index ($line, '$', $index1 + 1); 189 190 return if $index2 < 0; 191 192 my $salt = substr ($line, $index1 + 1, $index2 - $index1 - 1); 193 194 $salt = pack ("H*", $salt); 195 196 # crc / hash 197 198 $index1 = index ($line, '$', $index2 + 1); 199 200 return if $index1 < 0; 201 202 my $crc = substr ($line, $index2 + 1, $index1 - $index2 - 1); 203 204 # ignore this crc, we don't need to pass it to gen_hash () 205 206 # data len 207 208 $index2 = index ($line, '$', $index1 + 1); 209 210 return if $index2 < 0; 211 212 my $param4 = substr ($line, $index1 + 1, $index2 - $index1 - 1); 213 214 # unpack size 215 216 $index1 = index ($line, '$', $index2 + 1); 217 218 return if $index1 < 0; 219 220 my $param5 = substr ($line, $index2 + 1, $index1 - $index2 - 1); 221 222 # data 223 224 $index2 = index ($line, ':', $index1 + 1); 225 226 return if $index2 < 0; 227 228 my $param6 = substr ($line, $index1 + 1, $index2 - $index1 - 1); 229 $param6 = pack ("H*", $param6); 230 231 my $word = substr ($line, $index2 + 1); 232 233 my $word_packed = pack_if_HEX_notation ($word); 234 235 my $new_hash = module_generate_hash ($word_packed, $salt, $iter, $param, $param2, $param3, $param4, $param5, $param6); 236 237 return ($new_hash, $word); 238} 239 2401; 241