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