1
0
mirror of https://github.com/hashcat/hashcat.git synced 2024-11-22 16:18:09 +00:00

Merge pull request #1918 from 0xbsec/modes_unit_tests_3

Add unit tests for multiple modules:
This commit is contained in:
Jens Steube 2019-02-15 20:52:32 +01:00 committed by GitHub
commit 0a77d089b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1162 additions and 0 deletions

View File

@ -0,0 +1,100 @@
#!/usr/bin/env perl
##
## Author......: See docs/credits.txt
## License.....: MIT
##
use strict;
use warnings;
use Digest::SHA qw (sha1);
use Encode;
sub module_constraints { [[0, 16], [-1, -1], [0, 16], [-1, -1], [-1, -1]] }
sub module_generate_hash
{
my $word = shift;
my $iv = shift || random_hex_string (40);
my $enc_key = shift || random_hex_string (random_number (1, 1500));
my $alias = shift || "test";
if (length $iv)
{
$iv = pack ("H*", $iv);
}
if (length $enc_key)
{
$enc_key = pack ("H*", $enc_key);
}
my $word_utf16be = encode ("UTF-16BE", $word);
my $digest = sha1 ($word_utf16be . $iv);
my $DER1 = substr ($digest, 0, 1);
my $DER2 = substr ($digest, 6, 14);
my @enc_key_data = split "", $enc_key;
my $enc_key_data_length = scalar @enc_key_data;
my @key_data = ();
for (my $i = 0; $i < scalar $enc_key_data_length; $i += 20)
{
my @digest_data = split "", $digest;
for (my $j = 0; $j < 20; $j++)
{
last if (($i + $j) >= $enc_key_data_length);
$key_data[$i + $j] = $enc_key_data[$i + $j] ^ $digest_data[$j];
}
$digest = sha1 ($word_utf16be . $digest);
}
my $key = join "", @key_data;
$digest = sha1 ($word_utf16be . $key);
my $hash = sprintf ("\$jksprivk\$*%s*%s*%s*%s*%s*%s", uc unpack ("H*", $digest), uc unpack ("H*", $iv), uc unpack ("H*", $enc_key), uc unpack ("H*", $DER1), uc unpack ("H*", $DER2), $alias);
return $hash;
}
sub module_verify_hash
{
my $line = shift;
my ($hash, $word) = split (':', $line);
return unless defined $hash;
return unless defined $word;
my @data = split ('\*', $hash);
return unless scalar @data == 7;
my $signature = shift @data;
return unless ($signature eq '$jksprivk$');
my $checksum = shift @data;
my $iv = shift @data;
my $enc_key = shift @data;
my $DER1 = shift @data;
my $DER2 = shift @data;
my $alias = shift @data;
my $word_packed = pack_if_HEX_notation ($word);
my $new_hash = module_generate_hash ($word_packed, $iv, $enc_key, $alias);
return ($new_hash, $word);
}
1;

View File

@ -0,0 +1,71 @@
#!/usr/bin/env perl
##
## Author......: See docs/credits.txt
## License.....: MIT
##
use strict;
use warnings;
use Crypt::PBKDF2;
use Digest::Keccak qw (keccak_256_hex);
sub module_constraints { [[0, 255], [32, 32], [0, 55], [32, 32], [-1, -1]] }
sub module_generate_hash
{
my $word = shift;
my $salt = shift;
my $iterations = shift || 1024; # 262144 originally
my $ciphertext = shift || random_bytes (32);
my $pbkdf2 = Crypt::PBKDF2->new
(
hasher => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2', 256),
iterations => $iterations,
out_len => 32
);
my $derived_key = $pbkdf2->PBKDF2 ($salt, $word);
my $derived_key_cropped = substr ($derived_key, 16, 16);
my $digest = keccak_256_hex ($derived_key_cropped . $ciphertext);
my $hash = sprintf ("\$ethereum\$p*%i*%s*%s*%s", $iterations, unpack ("H*", $salt), unpack ("H*", $ciphertext), $digest);
return $hash;
}
sub module_verify_hash
{
my $line = shift;
my ($hash, $word) = split (':', $line);
return unless defined $hash;
return unless defined $word;
my $signature = substr ($hash, 0, 12);
return unless ($signature eq "\$ethereum\$p\*");
my @data = split ('\*', $hash);
return unless scalar (@data) == 5;
shift @data;
my $iterations = shift @data;
my $salt = pack ("H*", shift @data);
my $ciphertext = pack ("H*", shift @data);
my $word_packed = pack_if_HEX_notation ($word);
my $new_hash = module_generate_hash ($word_packed, $salt, $iterations, $ciphertext);
return ($new_hash, $word);
}
1;

View File

@ -0,0 +1,68 @@
#!/usr/bin/env perl
##
## Author......: See docs/credits.txt
## License.....: MIT
##
use strict;
use warnings;
use Crypt::ScryptKDF qw (scrypt_raw);
use Digest::Keccak qw (keccak_256_hex);
sub module_constraints { [[0, 255], [32, 32], [0, 55], [32, 32], [-1, -1]] }
sub module_generate_hash
{
my $word = shift;
my $salt = shift;
my $scrypt_N = shift || 1024 ; # 262144 originally
my $scrypt_r = shift || 1; # 8 originally
my $scrypt_p = shift || 1;
my $ciphertext = shift || random_bytes (32);
my $derived_key = scrypt_raw ($word, $salt, $scrypt_N, $scrypt_r, $scrypt_p, 32);
my $derived_key_cropped = substr ($derived_key, 16, 16);
my $digest = keccak_256_hex ($derived_key_cropped . $ciphertext);
my $hash = sprintf ("\$ethereum\$s*%i*%i*%i*%s*%s*%s", $scrypt_N, $scrypt_r, $scrypt_p, unpack ("H*", $salt), unpack ("H*", $ciphertext), $digest);
return $hash;
}
sub module_verify_hash
{
my $line = shift;
my ($hash, $word) = split (':', $line);
return unless defined $hash;
return unless defined $word;
my $signature = substr ($hash, 0, 12);
return unless ($signature eq "\$ethereum\$s\*");
my @data = split ('\*', $hash);
return unless scalar (@data) == 7;
shift @data;
my $scrypt_N = shift @data;
my $scrypt_r = shift @data;
my $scrypt_p = shift @data;
my $salt = pack ("H*", shift @data);
my $ciphertext = pack ("H*", shift @data);
my $word_packed = pack_if_HEX_notation ($word);
my $new_hash = module_generate_hash ($word_packed, $salt, $scrypt_N, $scrypt_r, $scrypt_p, $ciphertext);
return ($new_hash, $word);
}
1;

View File

@ -0,0 +1,54 @@
#!/usr/bin/env perl
##
## Author......: See docs/credits.txt
## License.....: MIT
##
use strict;
use warnings;
use Text::Iconv;
sub module_constraints { [[0, 8], [-1, -1], [0, 8], [-1, -1], [-1, -1]] }
sub module_generate_hash
{
my $word = shift;
my $converter = Text::Iconv->new ("utf-8", "shift-jis");
$word = $converter->convert ($word);
my $salt = substr ($word . '..', 1, 2);
$salt =~ s/[^\.-z]/\./go;
$salt =~ tr/:;<=>?@[\\]^_`/A-Ga-f/;
my $digest = crypt ($word, $salt);
$digest = substr ($digest, -10);
my $hash = sprintf ("%s", $digest);
return $hash;
}
sub module_verify_hash
{
my $line = shift;
my ($hash, $word) = split (':', $line);
return unless defined $hash;
return unless defined $word;
my $word_packed = pack_if_HEX_notation ($word);
my $new_hash = module_generate_hash ($word_packed);
return ($new_hash, $word);
}
1;

View File

@ -0,0 +1,104 @@
#!/usr/bin/env perl
##
## Author......: See docs/credits.txt
## License.....: MIT
##
use strict;
use warnings;
use Digest::MD5 qw (md5);
sub module_constraints { [[0, 243], [-1, -1], [0, 43], [-1, -1], [-1, -1]] }
sub module_generate_hash
{
my $word = shift;
my $session_id = shift || random_bytes (8);
my $encrypted_data = shift;
my $sequence = shift || "c006";
$session_id = pack ("H*", $session_id);
if (defined $encrypted_data)
{
$encrypted_data = pack ("H*", $encrypted_data);
}
$sequence = pack ("H*", $sequence);
my $key = md5 ($session_id . $word . $sequence);
if (defined $encrypted_data)
{
## verify case
my $encrypted_data_len = length $encrypted_data;
my $plain_data = substr ($encrypted_data, 0, 6) ^ substr ($key, 0, 6);
my ($status, $flags, $server_msg_len, $data_len) = unpack ("CCnn", $plain_data);
if ((($status >= 0x01 && $status <= 0x07) || $status == 0x21)
&& ($flags == 0x01 || $flags == 0x00)
&& (6 + $server_msg_len + $data_len == $encrypted_data_len))
{
## ok
}
else
{
$encrypted_data = ""; # some invalid data
}
}
else
{
my $plain_data = "\x01\x00\x00\x00\x00\x00";
my $plain_data_len = length $plain_data;
my $shortest = ($plain_data_len > 16) ? 16 : $plain_data_len;
$encrypted_data = substr ($plain_data, 0, $shortest) ^ substr ($key, 0, $shortest);
}
my $hash = sprintf ('$tacacs-plus$0$%s$%s$%s', unpack ("H*", $session_id), unpack ("H*", $encrypted_data), unpack ("H*", $sequence));
return $hash;
}
sub module_verify_hash
{
my $line = shift;
my ($hash, $word) = split (':', $line);
return unless defined $hash;
return unless defined $word;
my @data = split ('\$', $hash);
return unless scalar @data == 6;
shift @data;
my $signature = shift @data;
return unless ($signature eq "tacacs-plus");
my $auth_version = shift @data;
return unless ($auth_version eq "0");
my $session_id = shift @data;
my $encrypted_data = shift @data;
my $sequence = shift @data;
my $word_packed = pack_if_HEX_notation ($word);
my $new_hash = module_generate_hash ($word_packed, $session_id, $encrypted_data, $sequence);
return ($new_hash, $word);
}
1;

View File

@ -0,0 +1,164 @@
#!/usr/bin/env perl
##
## Author......: See docs/credits.txt
## License.....: MIT
##
use strict;
use warnings;
use Crypt::PBKDF2;
use Crypt::Mode::ECB;
sub module_constraints { [[0, 255], [32, 32], [0, 55], [32, 32], [-1, -1]] }
sub module_generate_hash
{
my $word = shift;
my $salt = shift;
my $iterations = shift || 20000;
my $Z_PK = shift || 1;
my $ZCRYPTOWRAPPEDKEY = shift;
my $salt_bin = pack ("H*", $salt);
my $pbkdf2 = Crypt::PBKDF2->new
(
hasher => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2', 256),
iterations => $iterations,
output_len => 16,
);
my $KEK = $pbkdf2->PBKDF2 ($salt_bin, $word);
my $aes = Crypt::Mode::ECB->new ('AES', 0);
my $blob_bin;
my $A;
my $B;
my $P1;
my $P2;
if (defined $ZCRYPTOWRAPPEDKEY)
{
$blob_bin = pack ("H*", $ZCRYPTOWRAPPEDKEY);
$A = substr ($blob_bin, 0, 8);
$P1 = substr ($blob_bin, 8, 8);
$P2 = substr ($blob_bin, 16, 8);
for (my $j = 5; $j >= 0; $j--)
{
# N = 2
$B = $A;
$B ^= pack ("Q>", (2 * $j + 2));
$B .= $P2;
$B = $aes->decrypt ($B, $KEK);
$A = substr ($B, 0, 8);
$P2 = substr ($B, 8, 8);
# N = 1
$B = $A;
$B ^= pack ("Q>", (2 * $j + 1));
$B .= $P1;
$B = $aes->decrypt ($B, $KEK);
$A = substr ($B, 0, 8);
$P1 = substr ($B, 8, 8);
}
if ($A eq "\xa6" x 8)
{
for (my $j = 0; $j <= 5; $j++)
{
# N = 1
$B = $A;
$B .= $P1;
$B = $aes->encrypt ($B, $KEK);
$A = substr ($B, 0, 8);
$A ^= pack ("Q>", (2 * $j + 1));
$P1 = substr ($B, 8, 8);
# N = 2
$B = $A;
$B .= $P2;
$B = $aes->encrypt ($B, $KEK);
$A = substr ($B, 0, 8);
$A ^= pack ("Q>", (2 * $j + 2));
$P2 = substr ($B, 8, 8);
}
$blob_bin = $A . $P1 . $P2;
}
else
{
$blob_bin = "\xff" x 24;
}
}
else
{
$A = "\xa6" x 8;
$P1 = "\xff" x 8;
$P2 = "\xff" x 8;
for (my $j = 0; $j <= 5; $j++)
{
# N = 1
$B = $A;
$B .= $P1;
$B = $aes->encrypt ($B, $KEK);
$A = substr ($B, 0, 8);
$A ^= pack ("Q>", (2 * $j + 1));
$P1 = substr ($B, 8, 8);
# N = 2
$B = $A;
$B .= $P2;
$B = $aes->encrypt ($B, $KEK);
$A = substr ($B, 0, 8);
$A ^= pack ("Q>", (2 * $j + 2));
$P2 = substr ($B, 8, 8);
}
$blob_bin = $A . $P1 . $P2;
}
my $hash = sprintf ('$ASN$*%d*%d*%s*%s', $Z_PK, $iterations, unpack ("H*", $salt_bin), unpack ("H*", $blob_bin));
return $hash;
}
sub module_verify_hash
{
my $line = shift;
my ($hash, $word) = split (':', $line);
return unless defined $hash;
return unless defined $word;
my @data = split ('\*', $hash);
return unless scalar @data == 5;
my $signature = shift @data;
return unless ($signature eq '$ASN$');
my ($Z_PK, $ZCRYPTOITERATIONCOUNT, $ZCRYPTOSALT, $ZCRYPTOWRAPPEDKEY) = @data;
my $word_packed = pack_if_HEX_notation ($word);
my $new_hash = module_generate_hash ($word_packed, $ZCRYPTOSALT, $ZCRYPTOITERATIONCOUNT, $Z_PK, $ZCRYPTOWRAPPEDKEY);
return ($new_hash, $word);
}
1;

View File

@ -0,0 +1,111 @@
#!/usr/bin/env perl
##
## Author......: See docs/credits.txt
## License.....: MIT
##
use strict;
use warnings;
use Crypt::CBC;
use Crypt::PBKDF2;
use Digest::Keccak qw (keccak_256_hex);
sub module_constraints { [[0, 255], [40, 40], [0, 55], [40, 40], [-1, -1]] }
sub module_generate_hash
{
my $word = shift;
my $ethaddr = shift;
my $encseed = shift;
my $iv = "";
my $seed = "";
# setup pbkdf2 params:
my $pbkdf2 = Crypt::PBKDF2->new
(
hasher => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2', 256),
iterations => 2000,
output_len => 16
);
my $key = $pbkdf2->PBKDF2 ($word, $word);
if (defined $encseed)
{
$iv = substr ($encseed, 0, 16);
$encseed = substr ($encseed, 16);
# AES-128-CBC decrypt:
my $aes_cbc = Crypt::CBC->new ({
key => $key,
cipher => "Crypt::Rijndael",
iv => $iv,
literal_key => 1,
header => "none",
keysize => 16
});
$seed = $aes_cbc->decrypt ($encseed);
}
else
{
$iv = random_bytes (16);
$seed = random_bytes (592);
# AES-128-CBC encrypt:
my $aes_cbc = Crypt::CBC->new ({
key => $key,
cipher => "Crypt::Rijndael",
iv => $iv,
literal_key => 1,
header => "none",
keysize => 16
});
$encseed = $aes_cbc->encrypt ($seed);
}
my $digest = keccak_256_hex ($seed . "\x02");
my $hash = sprintf ("\$ethereum\$w*%s*%s*%s", unpack ("H*", $iv . $encseed), $ethaddr, substr ($digest, 0, 32));
return $hash;
}
sub module_verify_hash
{
my $line = shift;
my ($hash, $word) = split (':', $line);
return unless defined $hash;
return unless defined $word;
my $signature = substr ($hash, 0, 12);
return unless ($signature eq "\$ethereum\$w\*");
my @data = split ('\*', $hash);
return unless scalar (@data) == 4;
shift @data;
my $encseed = pack ("H*", shift @data);
my $ethaddr = shift @data;
my $bpk = pack ("H*", shift @data);
my $word_packed = pack_if_HEX_notation ($word);
my $new_hash = module_generate_hash ($word_packed, $ethaddr, $encseed, $bpk);
return ($new_hash, $word);
}
1;

View File

@ -0,0 +1,55 @@
#!/usr/bin/env perl
##
## Author......: See docs/credits.txt
## License.....: MIT
##
use strict;
use warnings;
use Digest::Perl::MD5;
sub module_constraints { [[0, 64], [-1, -1], [0, 55], [-1, -1], [-1, -1]] }
sub module_generate_hash
{
my $word = shift;
my $md5 = Digest::Perl::MD5->new;
my $length = length ($word);
$md5->{_data} = $word ^ ("\x5c" x $length);
$md5->{_data} .= "\x5c" x (64 - $length);
$md5->add();
my $digest = unpack ("H*", pack ('V4', @{$md5->{_state}}));
my $hash = sprintf ("{CRAM-MD5}%s00000000000000000000000000000000", $digest);
return $hash;
}
sub module_verify_hash
{
my $line = shift;
my ($digest, $word) = split (':', $line);
return unless defined $digest;
return unless defined $word;
my $signature = substr ($digest, 0, 10);
return unless ($signature eq "{CRAM-MD5}");
my $hash = substr ($digest, 10);
my $word_packed = pack_if_HEX_notation ($word);
my $new_hash = module_generate_hash ($word_packed);
return ($new_hash, $word);
}
1;

View File

@ -0,0 +1,123 @@
#!/usr/bin/env perl
##
## Author......: See docs/credits.txt
## License.....: MIT
##
use strict;
use warnings;
use Digest::SHA qw (sha256 sha384 sha512);
use Digest::HMAC qw (hmac);
use MIME::Base64 qw (encode_base64url decode_base64url);
use JSON qw (encode_json decode_json);
sub module_constraints { [[0, 64], [-1, -1], [0, 55], [-1, -1], [-1, -1]] }
sub module_generate_hash
{
my $word = shift;
my $salt = shift || get_random_jwt_salt ();
my ($header_base64) = split (/\./, $salt);
my $header_jwt = decode_base64url ($header_base64);
my $header = decode_json ($header_jwt);
my $alg = $header->{"alg"};
my $digest;
if ($alg eq "HS256")
{
$digest = hmac ($salt, $word, \&sha256, 64);
}
elsif ($alg eq "HS384")
{
$digest = hmac ($salt, $word, \&sha384, 128);
}
elsif ($alg eq "HS512")
{
$digest = hmac ($salt, $word, \&sha512, 128);
}
else
{
die "not supported hash\n";
}
my $hash = sprintf ("%s.%s", $salt, encode_base64url ($digest, ""));
return $hash;
}
sub module_verify_hash
{
my $line = shift;
my ($hash, $word) = split (':', $line);
return unless defined $hash;
return unless defined $word;
my @data = split (/\./, $hash);
return unless scalar @data == 3;
my ($header, $payload, $signature) = @data;
my $salt = $header . "." . $payload;
my $word_packed = pack_if_HEX_notation ($word);
my $new_hash = module_generate_hash ($word_packed, $salt);
return ($new_hash, $word);
}
sub get_random_jwt_salt
{
my @hashes =
(
"HS256",
#"HS384", #this is support in hashcat, but commented out here to prevent mixed hash output files in single mode
#"HS512", #this is support in hashcat, but commented out here to prevent mixed hash output files in single mode
#"RS256", #not supported by hashcat
#"RS384",
#"RS512",
#"PS256",
#"PS384",
#"PS512",
#"ES256",
#"ES384",
#"ES512",
);
my $rnd = random_number (0, scalar @hashes - 1);
my $hash = $hashes[$rnd];
my $header =
{
"alg" => $hash
};
my $random_key = random_number (1, 100000000);
my $random_val = random_number (1, 100000000);
my $payload =
{
$random_key => $random_val
};
my $header_json = encode_json ($header);
my $payload_json = encode_json ($payload);
my $header_base64 = encode_base64url ($header_json, "");
my $payload_base64 = encode_base64url ($payload_json, "");
return $header_base64 . "." . $payload_base64;
}
1;

View File

@ -0,0 +1,140 @@
#!/usr/bin/env perl
##
## Author......: See docs/credits.txt
## License.....: MIT
##
use strict;
use warnings;
use Digest::SHA qw (sha256);
use Crypt::CBC;
sub module_constraints { [[0, 255], [-1, -1], [0, 55], [-1, -1], [-1, -1]] }
sub module_generate_hash
{
my $word = shift;
my $iv = shift || random_hex_string (32);
my $salt_type = shift || 1;
my $plain_bin = shift;
if ($salt_type ne "1") { die "currently only salt_type 1 supported\n"; }
my $key_bin = sha256 (sha256 ($word));
my $iv_bin = pack ("H*", $iv);
my $cipher = Crypt::CBC->new ({
key => $key_bin,
cipher => "Crypt::Rijndael",
iv => $iv_bin,
literal_key => 1,
header => "none",
keysize => 32,
padding => "null",
});
if (defined $plain_bin)
{
my $encrypted_bin = pack ("H*", $plain_bin);
my $test = $cipher->decrypt ($encrypted_bin);
if ($test =~ /^[0-9a-f]+$/)
{
$plain_bin = $test;
}
else
{
$plain_bin = "\xff" x 16;
}
}
else
{
my $plain = "30313233343536373839616263646566";
$plain_bin = pack ("H*", $plain);
}
my $encrypted_bin = $cipher->encrypt ($plain_bin);
my $encrypted = unpack ("H*", $encrypted_bin);
my $hash = sprintf ("\$electrum\$%d*%s*%s", $salt_type, $iv, $encrypted);
return $hash;
}
sub module_verify_hash
{
my $line = shift;
my ($hash, $word) = split (':', $line);
return unless defined $hash;
return unless defined $word;
my @data = split (/\*/, $hash);
return unless scalar @data == 3;
my ($mode, $iv, $encrypted) = @data;
my (undef, $signature, $salt_type) = split ('\$', $mode);
return unless ($signature eq "electrum");
my $word_packed = pack_if_HEX_notation ($word);
my $new_hash = module_generate_hash ($word_packed, $iv, $salt_type, $encrypted);
return ($new_hash, $word);
}
sub get_random_jwt_salt
{
my @hashes =
(
"HS256",
#"HS384", #this is support in hashcat, but commented out here to prevent mixed hash output files in single mode
#"HS512", #this is support in hashcat, but commented out here to prevent mixed hash output files in single mode
#"RS256", #not supported by hashcat
#"RS384",
#"RS512",
#"PS256",
#"PS384",
#"PS512",
#"ES256",
#"ES384",
#"ES512",
);
my $rnd = random_number (0, scalar @hashes - 1);
my $hash = $hashes[$rnd];
my $header =
{
"alg" => $hash
};
my $random_key = random_number (1, 100000000);
my $random_val = random_number (1, 100000000);
my $payload =
{
$random_key => $random_val
};
my $header_json = encode_json ($header);
my $payload_json = encode_json ($payload);
my $header_base64 = encode_base64url ($header_json, "");
my $payload_base64 = encode_base64url ($payload_json, "");
return $header_base64 . "." . $payload_base64;
}
1;

View File

@ -0,0 +1,172 @@
#!/usr/bin/env perl
##
## Author......: See docs/credits.txt
## License.....: MIT
##
use strict;
use warnings;
use Crypt::PBKDF2;
use Crypt::Mode::ECB;
sub module_constraints { [[0, 255], [32, 32], [0, 55], [32, 32], [-1, -1]] }
sub module_generate_hash
{
my $word = shift;
my $salt = shift;
my $iterations = shift || 20000;
my $Z_PK = shift || 1;
my $blob_bin = shift;
my $salt_bin = pack ("H*", $salt);
my $pbkdf2 = Crypt::PBKDF2->new
(
hasher => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2', 256),
iterations => $iterations,
output_len => 16,
);
my $KEK = $pbkdf2->PBKDF2 ($salt_bin, $word);
my $aes = Crypt::Mode::ECB->new ('AES', 0);
my $A;
my $B;
my $P1;
my $P2;
if (defined $blob_bin)
{
$blob_bin = pack ("H*", $blob_bin);
$A = substr ($blob_bin, 0, 8);
$P1 = substr ($blob_bin, 8, 8);
$P2 = substr ($blob_bin, 16, 8);
for (my $j = 5; $j >= 0; $j--)
{
# N = 2
$B = $A;
$B ^= pack ("Q>", (2 * $j + 2));
$B .= $P2;
$B = $aes->decrypt ($B, $KEK);
$A = substr ($B, 0, 8);
$P2 = substr ($B, 8, 8);
# N = 1
$B = $A;
$B ^= pack ("Q>", (2 * $j + 1));
$B .= $P1;
$B = $aes->decrypt ($B, $KEK);
$A = substr ($B, 0, 8);
$P1 = substr ($B, 8, 8);
}
if ($A eq "\xa6" x 8)
{
for (my $j = 0; $j <= 5; $j++)
{
# N = 1
$B = $A;
$B .= $P1;
$B = $aes->encrypt ($B, $KEK);
$A = substr ($B, 0, 8);
$A ^= pack ("Q>", (2 * $j + 1));
$P1 = substr ($B, 8, 8);
# N = 2
$B = $A;
$B .= $P2;
$B = $aes->encrypt ($B, $KEK);
$A = substr ($B, 0, 8);
$A ^= pack ("Q>", (2 * $j + 2));
$P2 = substr ($B, 8, 8);
}
$blob_bin = $A . $P1 . $P2;
}
else
{
$blob_bin = "\xff" x 24;
}
}
else
{
$A = "\xa6" x 8;
$P1 = "\xff" x 8;
$P2 = "\xff" x 8;
for (my $j = 0; $j <= 5; $j++)
{
# N = 1
$B = $A;
$B .= $P1;
$B = $aes->encrypt ($B, $KEK);
$A = substr ($B, 0, 8);
$A ^= pack ("Q>", (2 * $j + 1));
$P1 = substr ($B, 8, 8);
# N = 2
$B = $A;
$B .= $P2;
$B = $aes->encrypt ($B, $KEK);
$A = substr ($B, 0, 8);
$A ^= pack ("Q>", (2 * $j + 2));
$P2 = substr ($B, 8, 8);
}
$blob_bin = $A . $P1 . $P2;
}
my $hash = sprintf ('$fvde$%d$%d$%s$%d$%s', $Z_PK, length ($salt_bin), unpack ("H*", $salt_bin), $iterations, unpack ("H*", $blob_bin));
return $hash;
}
sub module_verify_hash
{
my $line = shift;
my ($hash, $word) = split (':', $line);
return unless defined $hash;
return unless defined $word;
my @data = split ('\$', $hash);
return unless scalar @data == 7;
shift @data;
my $signature = shift @data;
return unless ($signature eq 'fvde');
my $Z_PK = shift @data;
return unless ($Z_PK eq '1');
my $salt_length = shift @data;
return unless ($salt_length eq '16');
my ($ZCRYPTOSALT, $ZCRYPTOITERATIONCOUNT, $ZCRYPTOWRAPPEDKEY) = @data;
my $word_packed = pack_if_HEX_notation ($word);
my $new_hash = module_generate_hash ($word_packed, $ZCRYPTOSALT, $ZCRYPTOITERATIONCOUNT, $Z_PK, $ZCRYPTOWRAPPEDKEY);
return ($new_hash, $word);
}
1;