From e085177e587fca4778eb88f72240cc5184588036 Mon Sep 17 00:00:00 2001 From: mhasbini Date: Fri, 15 Feb 2019 21:45:49 +0200 Subject: [PATCH] Add unit tests for multiple modules: 15500, 15600, 15700, 16000, 16100, 16200, 16300, 16400, 16500, 16600 & 16700 --- tools/test_modules/m15500.pm | 100 ++++++++++++++++++++ tools/test_modules/m15600.pm | 71 +++++++++++++++ tools/test_modules/m15700.pm | 68 ++++++++++++++ tools/test_modules/m16000.pm | 54 +++++++++++ tools/test_modules/m16100.pm | 104 +++++++++++++++++++++ tools/test_modules/m16200.pm | 164 +++++++++++++++++++++++++++++++++ tools/test_modules/m16300.pm | 111 ++++++++++++++++++++++ tools/test_modules/m16400.pm | 55 +++++++++++ tools/test_modules/m16500.pm | 123 +++++++++++++++++++++++++ tools/test_modules/m16600.pm | 140 ++++++++++++++++++++++++++++ tools/test_modules/m16700.pm | 172 +++++++++++++++++++++++++++++++++++ 11 files changed, 1162 insertions(+) create mode 100644 tools/test_modules/m15500.pm create mode 100644 tools/test_modules/m15600.pm create mode 100644 tools/test_modules/m15700.pm create mode 100644 tools/test_modules/m16000.pm create mode 100644 tools/test_modules/m16100.pm create mode 100644 tools/test_modules/m16200.pm create mode 100644 tools/test_modules/m16300.pm create mode 100644 tools/test_modules/m16400.pm create mode 100644 tools/test_modules/m16500.pm create mode 100644 tools/test_modules/m16600.pm create mode 100644 tools/test_modules/m16700.pm diff --git a/tools/test_modules/m15500.pm b/tools/test_modules/m15500.pm new file mode 100644 index 000000000..63d7a75d5 --- /dev/null +++ b/tools/test_modules/m15500.pm @@ -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; diff --git a/tools/test_modules/m15600.pm b/tools/test_modules/m15600.pm new file mode 100644 index 000000000..9b4ec1043 --- /dev/null +++ b/tools/test_modules/m15600.pm @@ -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; diff --git a/tools/test_modules/m15700.pm b/tools/test_modules/m15700.pm new file mode 100644 index 000000000..3214359dd --- /dev/null +++ b/tools/test_modules/m15700.pm @@ -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; diff --git a/tools/test_modules/m16000.pm b/tools/test_modules/m16000.pm new file mode 100644 index 000000000..9cb68ecaa --- /dev/null +++ b/tools/test_modules/m16000.pm @@ -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; diff --git a/tools/test_modules/m16100.pm b/tools/test_modules/m16100.pm new file mode 100644 index 000000000..50d21a0e1 --- /dev/null +++ b/tools/test_modules/m16100.pm @@ -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; diff --git a/tools/test_modules/m16200.pm b/tools/test_modules/m16200.pm new file mode 100644 index 000000000..36bd76d68 --- /dev/null +++ b/tools/test_modules/m16200.pm @@ -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; diff --git a/tools/test_modules/m16300.pm b/tools/test_modules/m16300.pm new file mode 100644 index 000000000..ed40d40df --- /dev/null +++ b/tools/test_modules/m16300.pm @@ -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; diff --git a/tools/test_modules/m16400.pm b/tools/test_modules/m16400.pm new file mode 100644 index 000000000..88549dcc0 --- /dev/null +++ b/tools/test_modules/m16400.pm @@ -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; diff --git a/tools/test_modules/m16500.pm b/tools/test_modules/m16500.pm new file mode 100644 index 000000000..52372d449 --- /dev/null +++ b/tools/test_modules/m16500.pm @@ -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; \ No newline at end of file diff --git a/tools/test_modules/m16600.pm b/tools/test_modules/m16600.pm new file mode 100644 index 000000000..d6a3134d2 --- /dev/null +++ b/tools/test_modules/m16600.pm @@ -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; \ No newline at end of file diff --git a/tools/test_modules/m16700.pm b/tools/test_modules/m16700.pm new file mode 100644 index 000000000..9cc2059fd --- /dev/null +++ b/tools/test_modules/m16700.pm @@ -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; \ No newline at end of file