2019-02-19 14:31:36 +00:00
|
|
|
#!/usr/bin/env perl
|
|
|
|
|
|
|
|
##
|
|
|
|
## Author......: See docs/credits.txt
|
|
|
|
## License.....: MIT
|
|
|
|
##
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
|
|
|
|
use Crypt::Mode::ECB;
|
|
|
|
use Digest::SHA qw (sha1);
|
|
|
|
|
2019-02-26 20:20:07 +00:00
|
|
|
sub module_constraints { [[0, 256], [-1, -1], [-1, -1], [-1, -1], [-1, -1]] }
|
2019-02-19 14:31:36 +00:00
|
|
|
|
|
|
|
sub get_random_axcrypt_salt
|
|
|
|
{
|
|
|
|
my $mysalt = random_bytes (16);
|
|
|
|
|
|
|
|
$mysalt = unpack ("H*", $mysalt);
|
|
|
|
|
|
|
|
my $iteration = random_number (6, 99999);
|
|
|
|
|
|
|
|
my $salt_buf = $iteration . '*' . $mysalt;
|
|
|
|
|
|
|
|
return $salt_buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub module_generate_hash
|
|
|
|
{
|
|
|
|
my $word = shift;
|
|
|
|
my $salt = shift;
|
|
|
|
my $param = shift;
|
|
|
|
|
|
|
|
if (length $salt == 0)
|
|
|
|
{
|
|
|
|
$salt = get_random_axcrypt_salt ();
|
|
|
|
}
|
|
|
|
|
|
|
|
my @salt_arr = split ('\*', $salt);
|
|
|
|
|
|
|
|
my $iteration = $salt_arr[0];
|
|
|
|
|
|
|
|
my $mysalt = $salt_arr[1];
|
|
|
|
|
|
|
|
$mysalt = pack ("H*", $mysalt);
|
|
|
|
|
|
|
|
my $iv = "a6a6a6a6a6a6a6a6";
|
|
|
|
|
|
|
|
my $KEK = sha1 ($word);
|
|
|
|
|
|
|
|
$KEK = substr ($KEK ^ $mysalt, 0, 16);
|
|
|
|
|
2020-07-21 09:28:59 +00:00
|
|
|
my $aes = Crypt::Mode::ECB->new ('AES', 0);
|
2019-02-19 14:31:36 +00:00
|
|
|
|
|
|
|
my $B;
|
|
|
|
|
|
|
|
my $A;
|
|
|
|
|
|
|
|
my @R = ();
|
|
|
|
|
|
|
|
if (defined $param)
|
|
|
|
{
|
|
|
|
$param = pack ("H*", $param);
|
|
|
|
|
|
|
|
$A = substr ($param, 0, 8);
|
|
|
|
$B = 0x00 x 8;
|
|
|
|
|
|
|
|
$R[1] = substr ($param, 8, 8);
|
|
|
|
$R[2] = substr ($param, 16, 8);
|
|
|
|
|
|
|
|
for (my $j = $iteration - 1; $j >= 0; $j--)
|
|
|
|
{
|
|
|
|
$A = substr ($A, 0, 8) ^ pack ("l", (2 * $j + 2));
|
|
|
|
|
|
|
|
$B = $R[2];
|
|
|
|
|
2020-07-21 09:28:59 +00:00
|
|
|
$A = $aes->decrypt (substr ($A . $B . "\x00" x 16, 0, 16), $KEK);
|
2019-02-19 14:31:36 +00:00
|
|
|
|
|
|
|
$R[2] = substr ($A, 8, 16);
|
|
|
|
|
|
|
|
$A = substr ($A, 0, 8) ^ pack ("l", (2 * $j + 1));
|
|
|
|
|
|
|
|
$B = $R[1];
|
|
|
|
|
2020-07-21 09:28:59 +00:00
|
|
|
$A = $aes->decrypt (substr ($A . $B . "\x00" x 16, 0, 16), $KEK);
|
2019-02-19 14:31:36 +00:00
|
|
|
|
|
|
|
$R[1] = substr ($A, 8, 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
# check if valid
|
|
|
|
if (index ($A, "\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6") != 0)
|
|
|
|
{
|
|
|
|
# fake wrong @R and $A values
|
|
|
|
|
|
|
|
@R = ('', "\x00" x 8, "\x00" x 8);
|
|
|
|
|
|
|
|
$A = "\x00" x 16;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
my $DEK = random_hex_string (32);
|
|
|
|
|
|
|
|
@R = ('', substr (pack ("H*", $DEK), 0, 8), substr (pack ("H*", $DEK), 8, 16));
|
|
|
|
|
|
|
|
$A = pack ("H*", $iv);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (my $j = 0; $j < $iteration; $j++)
|
|
|
|
{
|
2020-07-21 09:28:59 +00:00
|
|
|
$B = $aes->encrypt (substr ($A . $R[1] . "\x00" x 16, 0, 16), $KEK);
|
2019-02-19 14:31:36 +00:00
|
|
|
|
|
|
|
$A = substr ($B, 0, 8) ^ pack ("q", (2 * $j + 1));
|
|
|
|
|
|
|
|
$R[1] = substr ($B, 8, 16);
|
|
|
|
|
2020-07-21 09:28:59 +00:00
|
|
|
$B = $aes->encrypt (substr ($A . $R[2] . "\x00" x 16, 0, 16), $KEK);
|
2019-02-19 14:31:36 +00:00
|
|
|
|
|
|
|
$A = substr ($B, 0, 8) ^ pack ("q", (2 * $j + 2));
|
|
|
|
|
|
|
|
$R[2] = substr ($B, 8, 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
my $wrapped_key = unpack ("H*", $A . substr ($R[1], 0 ,8) . substr ($R[2], 0 ,8));
|
|
|
|
|
|
|
|
$mysalt = unpack ("H*", $mysalt);
|
|
|
|
|
|
|
|
my $hash = sprintf ('$axcrypt$*1*%s*%s*%s', $iteration, $mysalt, $wrapped_key);
|
|
|
|
|
|
|
|
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 == 5;
|
|
|
|
|
|
|
|
my $signature = shift @data;
|
|
|
|
my $version = shift @data;
|
|
|
|
my $iteration = shift @data;
|
|
|
|
my $mysalt = shift @data;
|
|
|
|
my $digest = shift @data;
|
|
|
|
|
|
|
|
return unless ($signature eq '$axcrypt$');
|
|
|
|
return unless (length ($mysalt) == 32);
|
|
|
|
return unless (length ($digest) == 48);
|
|
|
|
|
|
|
|
my $salt = $iteration . '*' . $mysalt;
|
|
|
|
my $param = $digest;
|
|
|
|
|
|
|
|
return unless defined $salt;
|
|
|
|
return unless defined $param;
|
|
|
|
|
|
|
|
$word = pack_if_HEX_notation ($word);
|
|
|
|
|
|
|
|
my $new_hash = module_generate_hash ($word, $salt, $param);
|
|
|
|
|
|
|
|
return ($new_hash, $word);
|
|
|
|
}
|
|
|
|
|
|
|
|
1;
|