#!/usr/bin/env perl ## ## Author......: See docs/credits.txt ## License.....: MIT ## use strict; use warnings; use Crypt::Mode::ECB; use Digest::SHA qw (sha1); sub module_constraints { [[0, 256], [-1, -1], [-1, -1], [-1, -1], [-1, -1]] } 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); my $aes = Crypt::Mode::ECB->new ('AES', 0); 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]; $A = $aes->decrypt (substr ($A . $B . "\x00" x 16, 0, 16), $KEK); $R[2] = substr ($A, 8, 16); $A = substr ($A, 0, 8) ^ pack ("l", (2 * $j + 1)); $B = $R[1]; $A = $aes->decrypt (substr ($A . $B . "\x00" x 16, 0, 16), $KEK); $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++) { $B = $aes->encrypt (substr ($A . $R[1] . "\x00" x 16, 0, 16), $KEK); $A = substr ($B, 0, 8) ^ pack ("q", (2 * $j + 1)); $R[1] = substr ($B, 8, 16); $B = $aes->encrypt (substr ($A . $R[2] . "\x00" x 16, 0, 16), $KEK); $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;