1#!/usr/bin/env perl 2 3## 4## Author......: See docs/credits.txt 5## License.....: MIT 6## 7 8use strict; 9use warnings; 10 11use MIME::Base64 qw (decode_base64 encode_base64); 12use Digest::SHA qw (sha256); 13use Digest::HMAC qw (hmac); 14use Crypt::PBKDF2; 15 16sub module_constraints { [[0, 256], [28, 28], [-1, -1], [-1, -1], [-1, -1]] } 17 18my $ITERATIONS = 15000; 19my $HMAC_SALT = "Server Key"; 20 21sub module_generate_hash 22{ 23 my $word = shift; 24 my $salt = shift; 25 my $iter = shift // $ITERATIONS; 26 my $user = shift // random_string (random_number (0, 64)); 27 28 my $pbkdf = Crypt::PBKDF2->new 29 ( 30 hasher => Crypt::PBKDF2->hasher_from_algorithm ('HMACSHA2', 256), 31 iterations => $iter, 32 output_len => 32 33 ); 34 35 my $pbkdf2_dgst = $pbkdf->PBKDF2 ($salt, $word); 36 37 my $hash_buf = hmac ($HMAC_SALT, $pbkdf2_dgst, \&sha256); 38 39 my $hash = sprintf ('$mongodb-scram$*1*%s*%i*%s*%s', encode_base64 ($user, ""), $iter, encode_base64 ($salt, ""), encode_base64 ($hash_buf, "")); 40 41 return $hash; 42} 43 44sub module_verify_hash 45{ 46 my $line = shift; 47 48 my $idx = index ($line, ':'); 49 50 return unless $idx >= 0; 51 52 my $hash = substr ($line, 0, $idx); 53 my $word = substr ($line, $idx + 1); 54 55 return unless substr ($hash, 0, 17) eq '$mongodb-scram$*1'; 56 57 my (undef, undef, $user, $iter, $salt) = split ('\*', $hash); 58 59 return unless defined ($user); 60 return unless defined ($iter); 61 return unless defined ($salt); 62 63 return unless ($user =~ m/^[A-Za-z0-9+\/=]{0,88}$/); 64 65 $user = decode_base64 ($user); 66 67 return unless (length ($user) <= 64); 68 69 return unless ($iter =~ m/^[1-9][0-9]{0,7}$/); 70 71 $iter = int ($iter); 72 73 return unless ($salt =~ m/^[A-Za-z0-9+\/=]{40}$/); 74 75 $salt = decode_base64 ($salt); 76 77 my $word_packed = pack_if_HEX_notation ($word); 78 79 my $new_hash = module_generate_hash ($word_packed, $salt, $iter, $user); 80 81 return ($new_hash, $word); 82} 83 841; 85