From 72a65a4c3e4569dd20ff2d38feb1ba51a75ed68b Mon Sep 17 00:00:00 2001 From: jsteube Date: Tue, 19 Feb 2019 16:05:42 +0100 Subject: [PATCH] Add -m 13400 unit test --- tools/test_modules/m13400.pm | 406 +++++++++++++++++++++++++++++++++++ 1 file changed, 406 insertions(+) create mode 100644 tools/test_modules/m13400.pm diff --git a/tools/test_modules/m13400.pm b/tools/test_modules/m13400.pm new file mode 100644 index 000000000..2854b7ed2 --- /dev/null +++ b/tools/test_modules/m13400.pm @@ -0,0 +1,406 @@ +#!/usr/bin/env perl + +## +## Author......: See docs/credits.txt +## License.....: MIT +## + +use strict; +use warnings; + +use Crypt::CBC; +use Crypt::Mode::ECB; +use Digest::SHA qw (sha256); + +sub module_constraints { [[0, 255], [-1, -1], [-1, -1], [-1, -1], [-1, -1]] } + +sub get_random_keepass_salt +{ + my $version = random_number (1, 2); + + my $algorithm; + + my $iteration; + + my $final_random_seed; + + if ($version == 1) + { + $algorithm = random_number (0, 1); + + $iteration = random_number (50000, 99999); + + $final_random_seed = random_bytes (16); + $final_random_seed = unpack ("H*", $final_random_seed); + } + elsif ($version == 2) + { + $algorithm = 0; + + $iteration = random_number (6000, 99999); + + $final_random_seed = random_bytes (32); + $final_random_seed = unpack ("H*", $final_random_seed); + } + + my $transf_random_seed = random_bytes (32); + $transf_random_seed = unpack ("H*", $transf_random_seed); + + my $enc_iv = random_bytes (16); + $enc_iv = unpack ("H*", $enc_iv); + + my $contents_hash = random_bytes (32); + $contents_hash = unpack ("H*", $contents_hash); + + my $inline_flag = 1; + + my $contents_len = random_number (128, 499); + + my $contents = random_bytes ($contents_len); + + $contents_len += 16 - $contents_len % 16; + + $contents = unpack ("H*", $contents); + + my $salt_buf; + + my $is_keyfile = random_number (0, 1); + + my $keyfile_attributes = ""; + + if ($is_keyfile == 1) + { + $keyfile_attributes = $keyfile_attributes + . "1*64*" + . unpack ("H*", random_bytes (32)); + } + + if ($version == 1) + { + $salt_buf = $version . '*' . + $iteration . '*' . + $algorithm . '*' . + $final_random_seed . '*' . + $transf_random_seed . '*' . + $enc_iv . '*' . + $contents_hash . '*' . + $inline_flag . '*' . + $contents_len . '*' . + $contents . '*' . + $keyfile_attributes; + } + elsif ($version == 2) + { + $contents = random_bytes (32); + $contents = unpack ("H*", $contents); + + $salt_buf = $version . '*' . + $iteration . '*' . + $algorithm . '*' . + $final_random_seed . '*' . + $transf_random_seed . '*' . + $enc_iv . '*' . + $contents_hash . '*' . + $contents . '*' . + $keyfile_attributes; + } + + return $salt_buf; +} + +sub module_generate_hash +{ + my $word = shift; + my $salt = shift; + my $param = shift; + + if (length $salt == 0) + { + $salt = get_random_keepass_salt (); + } + + my @salt_arr = split ('\*', $salt); + + my $version = $salt_arr[0]; + + my $iteration = $salt_arr[1]; + + my $algorithm = $salt_arr[2]; + + my $final_random_seed = $salt_arr[3]; + + my $transf_random_seed = $salt_arr[4]; + + my $enc_iv = $salt_arr[5]; + + my $contents_hash; + + # specific to version 1 + my $inline_flag; + my $contents_len; + my $contents; + + # specific to version 2 + my $expected_bytes; + + # specific to keyfile handling + my $inline_keyfile_flag; + my $keyfile_len; + my $keyfile_content; + my $keyfile_attributes = ""; + + $final_random_seed = pack ("H*", $final_random_seed); + + $transf_random_seed = pack ("H*", $transf_random_seed); + + $enc_iv = pack ("H*", $enc_iv); + + my $intermediate_hash = sha256 ($word); + + if ($version == 1) + { + $contents_hash = $salt_arr[6]; + + $contents_hash = pack ("H*", $contents_hash); + + $inline_flag = $salt_arr[7]; + + + $contents_len = $salt_arr[8]; + + + $contents = $salt_arr[9]; + + $contents = pack ("H*", $contents); + + # keyfile handling + if (scalar @salt_arr == 13) + { + $inline_keyfile_flag = $salt_arr[10]; + + $keyfile_len = $salt_arr[11]; + + $keyfile_content = $salt_arr[12]; + + $keyfile_attributes = $keyfile_attributes + . "*" . $inline_keyfile_flag + . "*" . $keyfile_len + . "*" . $keyfile_content; + + $intermediate_hash = $intermediate_hash . pack ("H*", $keyfile_content); + + $intermediate_hash = sha256 ($intermediate_hash); + } + } + elsif ($version == 2) + { + # keyfile handling + if (scalar @salt_arr == 11) + { + $inline_keyfile_flag = $salt_arr[8]; + + $keyfile_len = $salt_arr[9]; + + $keyfile_content = $salt_arr[10]; + + $intermediate_hash = $intermediate_hash . pack ("H*", $keyfile_content); + + $keyfile_attributes = $keyfile_attributes + . "*" . $inline_keyfile_flag + . "*" . $keyfile_len + . "*" . $keyfile_content; + + } + + $intermediate_hash = sha256 ($intermediate_hash); + } + + my $aes = Crypt::Mode::ECB->new ('AES', 1); + + for (my $j = 0; $j < $iteration; $j++) + { + $intermediate_hash = $aes->encrypt ($intermediate_hash, $transf_random_seed); + + $intermediate_hash = substr ($intermediate_hash, 0, 32); + } + + $intermediate_hash = sha256 ($intermediate_hash); + + my $final_key = sha256 ($final_random_seed . $intermediate_hash); + + my $final_algorithm; + + if ($version == 1 && $algorithm == 1) + { + $final_algorithm = "Crypt::Twofish"; + } + else + { + $final_algorithm = "Crypt::Rijndael"; + } + + my $cipher = Crypt::CBC->new ({ + key => $final_key, + cipher => $final_algorithm, + iv => $enc_iv, + literal_key => 1, + header => "none", + keysize => 32 + }); + + my $hash; + + if ($version == 1) + { + if (defined $param) + { + # if we try to verify the crack, we need to decrypt the contents instead of only encrypting it: + + $contents = $cipher->decrypt ($contents); + + # and check the output + + my $contents_hash_old = $contents_hash; + + $contents_hash = sha256 ($contents); + + if ($contents_hash_old ne $contents_hash) + { + # fake content + $contents = "\x00" x length ($contents); + } + } + else + { + $contents_hash = sha256 ($contents); + } + + $contents = $cipher->encrypt ($contents); + + $hash = sprintf ('$keepass$*%d*%d*%d*%s*%s*%s*%s*%d*%d*%s%s', + $version, + $iteration, + $algorithm, + unpack ("H*", $final_random_seed), + unpack ("H*", $transf_random_seed), + unpack ("H*", $enc_iv), + unpack ("H*", $contents_hash), + $inline_flag, + $contents_len, + unpack ("H*", $contents), + $keyfile_attributes); + } + if ($version == 2) + { + $expected_bytes = $salt_arr[6]; + + $contents_hash = $salt_arr[7]; + $contents_hash = pack ("H*", $contents_hash); + + $expected_bytes = $cipher->decrypt ($contents_hash); + + $hash = sprintf ('$keepass$*%d*%d*%d*%s*%s*%s*%s*%s%s', + $version, + $iteration, + $algorithm, + unpack ("H*", $final_random_seed), + unpack ("H*", $transf_random_seed), + unpack ("H*", $enc_iv), + unpack ("H*", $expected_bytes), + unpack ("H*", $contents_hash), + $keyfile_attributes); + } + + return $hash; +} + +sub module_verify_hash +{ + my $line = shift; + + my ($hash_in, $word) = split ":", $line; + + return unless defined $hash_in; + return unless defined $word; + + my @data = split ('\*', $hash_in); + + return unless (scalar @data == 9 + || scalar @data == 11 + || scalar @data == 12 + || scalar @data == 14); + + my $signature = shift @data; + return unless ($signature eq '$keepass$'); + + my $version = shift @data; + return unless ($version == 1 || $version == 2); + + my $iteration = shift @data; + + my $algorithm = shift @data; + + my $final_random_seed = shift @data; + + if ($version == 1) + { + return unless (length ($final_random_seed) == 32); + } + elsif ($version == 2) + { + return unless (length ($final_random_seed) == 64); + } + + my $transf_random_seed = shift @data; + return unless (length ($transf_random_seed) == 64); + + my $enc_iv = shift @data; + return unless (length ($enc_iv) == 32); + + if ($version == 1) + { + my $contents_hash = shift @data; + return unless (length ($contents_hash) == 64); + + my $inline_flags = shift @data; + return unless ($inline_flags == 1); + + my $contents_len = shift @data; + + my $contents = shift @data; + return unless (length ($contents) == $contents_len * 2); + } + elsif ($version == 2) + { + my $expected_bytes = shift @data; + return unless (length ($expected_bytes) == 64); + + my $contents_hash = shift @data; + return unless (length ($contents_hash) == 64); + } + + if (scalar @data == 12 || scalar @data == 14) + { + my $inline_flags = shift @data; + return unless ($inline_flags == 1); + + my $keyfile_len = shift @data; + return unless ($keyfile_len == 64); + + my $keyfile = shift @data; + return unless (length ($keyfile) == $keyfile_len); + } + + my $salt = substr ($hash_in, length ("*keepass*") + 1); + my $param = 1; # distinguish between encrypting vs decrypting + + return unless defined $salt; + + $word = pack_if_HEX_notation ($word); + + my $new_hash = module_generate_hash ($word, $salt, $param); + + return ($new_hash, $word); +} + +1;