1#!/usr/bin/env perl
2
3##
4## Author......: See docs/credits.txt
5## License.....: MIT
6##
7
8use strict;
9use warnings;
10
11use Crypt::PBKDF2;
12use MIME::Base64 qw (encode_base64 decode_base64);
13
14sub module_constraints { [[0, 256], [-1, -1], [-1, -1], [-1, -1], [-1, -1]] }
15
16sub module_generate_hash
17{
18  my $word = shift;
19  my $salt = shift;
20  my $iter = shift // 1024;
21
22  if (length $salt == 0)
23  {
24    $salt = random_bytes (16);
25  }
26
27  my $pbkdf2 = Crypt::PBKDF2->new
28  (
29    hasher     => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2', 256),
30    iterations => $iter
31  );
32
33  my $hash_buf = encode_base64 ($pbkdf2->PBKDF2 ($salt, $word), '');
34  my $salt_buf = encode_base64 ($salt, '');
35
36  # replace + with .
37  $hash_buf =~ s/\+/\./g;
38  $salt_buf =~ s/\+/\./g;
39
40  # remove padding =
41  $hash_buf =~ s/\=+$//;
42  $salt_buf =~ s/\=+$//;
43
44  my $hash = sprintf ("\$pbkdf2-sha256\$%i\$%s\$%s", $iter, $salt_buf, $hash_buf);
45
46  return $hash;
47}
48
49sub module_verify_hash
50{
51  my $line = shift;
52
53  # check signature
54  return unless (substr ($line, 0, 15) eq '$pbkdf2-sha256$');
55
56  # get hash
57  my $index1 = index ($line, '$', 15);
58
59  return if $index1 < 1;
60
61  my $index2 = index ($line, '$', $index1 + 1);
62
63  my $iter = substr ($line, 15,  $index1 - 15);
64
65  my $salt = substr ($line, $index1 + 1,  $index2 - $index1 - 1);
66
67  $index1 = index ($line, ':', $index2 + 1);
68
69  return if $index1 < 1;
70
71  my $word = substr ($line, $index1 + 1);
72
73  return unless defined $salt;
74  return unless defined $iter;
75  return unless defined $word;
76
77  $word = pack_if_HEX_notation ($word);
78
79  # fix salt from 'alternate' to 'ordinary' base64 encoding before
80  $salt =~ s/\./\+/g;
81  $salt .= '==';
82
83  my $new_hash = module_generate_hash ($word, decode_base64 ($salt), $iter);
84
85  return ($new_hash, $word);
86}
87
881;
89