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