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::PBKDF2;
12use MIME::Base64 qw (encode_base64 decode_base64);
13use Encode;
14
15sub module_constraints { [[0, 256], [64, 64], [-1, -1], [-1, -1], [-1, -1]] }
16
17sub aes_decrypt
18{
19  my $key_main  = shift;
20  my $key_tweak = shift;
21  my $data      = shift;
22
23  my $python_code = <<'END_CODE';
24
25from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
26from cryptography.hazmat.primitives import hashes
27from cryptography.hazmat.backends import default_backend
28import base64
29
30key = base64.b64decode (key_main)
31tweak = base64.b64decode (key_tweak)
32
33cipher = Cipher(algorithms.AES(key), modes.XTS(tweak), backend=default_backend())
34decryptor = cipher.decryptor()
35
36decrypted = decryptor.update(base64.b64decode (data)[0:64])
37print (decrypted.hex ())
38
39END_CODE
40
41  # replace code with these values
42
43  $python_code =~ s/key_main/"$key_main"/;
44  $python_code =~ s/key_tweak/"$key_tweak"/;
45  $python_code =~ s/data/"$data"/;
46
47  my $output_buf = `python3 -c '$python_code'`;
48
49  $output_buf =~ s/[\r\n]//g;
50
51  $output_buf = pack ("H*", $output_buf);
52
53  return $output_buf;
54}
55
56sub module_generate_hash
57{
58  my $word = shift;
59  my $salt1 = shift;
60  my $iter1 = shift // 160000;
61  my $enc_pass = shift // random_hex_string (128);
62  my $salt2 = shift // random_hex_string (64);
63  my $iter2 = shift // 20000;
64
65  my $pbkdf2 = Crypt::PBKDF2->new
66  (
67    hasher     => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2', 256),
68    iterations => $iter1,
69    output_len => 64
70  );
71
72  my $salt1_bin = pack ("H*", $salt1);
73
74  my $key = $pbkdf2->PBKDF2 ($salt1_bin, $word);
75
76  my $tweak = "\x00" x 16;
77
78  my $key_b64 = encode_base64 ($key, "");
79  my $tweak_b64 = encode_base64 ($tweak, "");
80  my $enc_pass_b64 = encode_base64 (pack ("H*", $enc_pass), "");
81
82  my $dec_pass = aes_decrypt ($key_b64, $tweak_b64, $enc_pass_b64);
83
84  $pbkdf2 = Crypt::PBKDF2->new
85  (
86    hasher     => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2', 256),
87    iterations => $iter2,
88    output_len => 32
89  );
90
91  my $salt2_bin = pack ("H*", $salt2);
92
93  my $hash_buf = $pbkdf2->PBKDF2 ($salt2_bin, $dec_pass);
94
95  my $hash = sprintf ("\$vbox\$0\$%s\$%s\$16\$%s\$%s\$%s\$%s", $iter1, $salt1, $enc_pass, $iter2, $salt2, unpack ("H*", $hash_buf));
96
97  return $hash;
98}
99
100sub module_verify_hash
101{
102  my $line = shift;
103
104  my $idx = index ($line, ':');
105
106  return unless $idx >= 0;
107
108  my $hash = substr ($line, 0, $idx);
109  my $word = substr ($line, $idx + 1);
110
111  return unless substr ($hash, 0, 6) eq '$vbox$';
112
113  my (undef, $signature, $version, $iter1, $salt1, $klen, $enc_pass, $iter2, $salt2) = split '\$', $hash;
114
115  return unless defined $signature;
116  return unless defined $version;
117  return unless defined $iter1;
118  return unless defined $salt1;
119  return unless defined $klen;
120  return unless defined $enc_pass;
121  return unless defined $iter2;
122  return unless defined $salt2;
123
124  return unless ($version eq "0");
125  return unless (length $salt1 eq 64);
126  return unless ($klen eq "16");
127  return unless (length $enc_pass eq 128);
128  return unless (length $salt2 eq 64);
129
130  my $word_packed = pack_if_HEX_notation ($word);
131
132  my $new_hash = module_generate_hash ($word, $salt1, $iter1, $enc_pass, $salt2, $iter2);
133
134  return ($new_hash, $word);
135}
136
1371;
138