#!/usr/bin/env perl ## ## Author......: See docs/credits.txt ## License.....: MIT ## use strict; use warnings; use Crypt::PBKDF2; use MIME::Base64 qw (encode_base64 decode_base64); sub module_constraints { [[0, 256], [1, 15], [-1, -1], [-1, -1], [-1, -1]] } sub module_generate_hash { my $word = shift; my $salt = shift; my $iterations = shift // 1000; my $out_len = shift // 32; # # call PHP here - WTF # # sanitize $word_buf and $salt_buf: my $word_buf_base64 = encode_base64 ($word, ""); my $salt_buf_base64 = encode_base64 ($salt, ""); # sanitize lenghs $out_len = int ($out_len); # output is in hex encoding, otherwise it could be screwed (but shouldn't) my $php_code = <<'END_CODE'; function pbkdf2 ($algorithm, $password, $salt, $count, $key_length, $raw_output = false) { $algorithm = strtolower ($algorithm); if (! in_array ($algorithm, hash_algos (), true)) { trigger_error ("PBKDF2 ERROR: Invalid hash algorithm.", E_USER_ERROR); } if ($count <= 0 || $key_length <= 0) { trigger_error ("PBKDF2 ERROR: Invalid parameters.", E_USER_ERROR); } if (function_exists ("hash_pbkdf2")) { if (!$raw_output) { $key_length = $key_length * 2; } return hash_pbkdf2 ($algorithm, $password, $salt, $count, $key_length, $raw_output); } $hash_length = strlen (hash ($algorithm, "", true)); $block_count = ceil ($key_length / $hash_length); $output = ""; for ($i = 1; $i <= $block_count; $i++) { $last = $salt . pack ("N", $i); $last = $xorsum = hash_hmac ($algorithm, $last, $password, true); for ($j = 1; $j < $count; $j++) { $xorsum ^= ($last = hash_hmac ($algorithm, $last, $password, true)); } $output .= $xorsum; } if ($raw_output) { return substr ($output, 0, $key_length); } else { return bin2hex (substr ($output, 0, $key_length)); } } print pbkdf2 ("md5", base64_decode ("$word_buf_base64"), base64_decode ("$salt_buf_base64"), $iterations, $out_len, False); END_CODE # replace with these command line arguments $php_code =~ s/\$word_buf_base64/$word_buf_base64/; $php_code =~ s/\$salt_buf_base64/$salt_buf_base64/; $php_code =~ s/\$iterations/$iterations/; $php_code =~ s/\$out_len/$out_len/; my $php_output = `php -r '$php_code'`; my $hash_buf = pack ("H*", $php_output); $hash_buf = encode_base64 ($hash_buf, ""); my $base64_salt_buf = encode_base64 ($salt, ""); my $hash = sprintf ("md5:%i:%s:%s", $iterations, $base64_salt_buf, $hash_buf); return $hash; } sub module_verify_hash { my $line = shift; my ($digest, $word) = split (/:([^:]+)$/, $line); return unless defined $digest; return unless defined $word; my @data = split (':', $digest); return unless scalar (@data) == 4; my $signature = shift @data; return unless ($signature eq 'md5'); my $iterations = int (shift @data); my $salt = decode_base64 (shift @data); my $hash = decode_base64 (shift @data); my $out_len = length ($hash); my $word_packed = pack_if_HEX_notation ($word); my $new_hash = module_generate_hash ($word_packed, $salt, $iterations, $out_len); return ($new_hash, $word); } 1;