File Coverage

blib/lib/Crypt/Bcrypt.pm
Criterion Covered Total %
statement 41 45 91.1
branch 7 16 43.7
condition 3 6 50.0
subroutine 11 11 100.0
pod 4 4 100.0
total 66 82 80.4


line stmt bran cond sub pod time code
1             package Crypt::Bcrypt;
2             $Crypt::Bcrypt::VERSION = '0.010';
3 2     2   138059 use strict;
  2         14  
  2         92  
4 2     2   13 use warnings;
  2         2  
  2         94  
5              
6 2     2   13 use XSLoader;
  2         4  
  2         67  
7             XSLoader::load('Crypt::Bcrypt');
8              
9 2     2   8 use Exporter 5.57 'import';
  2         51  
  2         121  
10             our @EXPORT_OK = qw(bcrypt bcrypt_check bcrypt_prehashed bcrypt_check_prehashed bcrypt_hashed bcrypt_check_hashed bcrypt_needs_rehash);
11              
12 2     2   1127 use Digest::SHA 'hmac_sha256';
  2         6298  
  2         197  
13 2     2   1057 use MIME::Base64 2.21 qw(encode_base64);
  2         1360  
  2         1400  
14              
15             sub bcrypt {
16 24     24 1 8398 my ($password, $subtype, $cost, $salt) = @_;
17 24 50       151 die "Unknown subtype $subtype" if $subtype !~ /^2[abxy]$/;
18 24 50 33     141 die "Invalid cost factor $cost" if $cost < 4 || $cost > 31;
19 24 50       65 die "Salt must be 16 bytes" if length $salt != 16;
20 24         90 my $encoded_salt = encode_base64($salt, "");
21 24         58 $encoded_salt =~ tr{A-Za-z0-9+/=}{./A-Za-z0-9}d;
22 24         11807279 return _bcrypt_hashpw($password, sprintf '$%s$%02d$%s', $subtype, $cost, $encoded_salt);
23             }
24              
25             my $subtype_qr = qr/2[abxy]/;
26             my $cost_qr = qr/\d{2}/;
27             my $salt_qr = qr{ [./A-Za-z0-9]{22} }x;
28              
29             sub bcrypt_prehashed {
30 2     2 1 1651373 my ($password, $subtype, $cost, $salt, $hash_algorithm) = @_;
31 2 50       10 if ($hash_algorithm) {
32 2         15 (my $encoded_salt = encode_base64($salt, "")) =~ tr{A-Za-z0-9+/=}{./A-Za-z0-9}d;
33 2         59 my $hashed_password = encode_base64(hmac_sha256($password, $encoded_salt), "");
34 2         9 my $hash = bcrypt($hashed_password, $subtype, $cost, $salt);
35 2 50       216 $hash =~ s{ ^ \$ ($subtype_qr) \$ ($cost_qr) \$ ($salt_qr) }{\$bcrypt-sha256\$v=2,t=$1,r=$2\$$3\$}x or die $hash;
36 2         34 return $hash;
37             }
38             else {
39 0         0 bcrypt($password, $subtype, $cost, $salt);
40             }
41             }
42              
43             sub bcrypt_check_prehashed {
44 4     4 1 1044 my ($password, $hash) = @_;
45 4 50       141 if ($hash =~ s/ ^ \$ bcrypt-sha256 \$ v=2,t=($subtype_qr),r=($cost_qr) \$ ($salt_qr) \$ /\$$1\$$2\$$3/x) {
46 4         14798999 return bcrypt_check(encode_base64(hmac_sha256($password, $3), ""), $hash);
47             }
48             else {
49 0         0 bcrypt_check($password, $hash);
50             }
51             }
52              
53             #legacy names
54             *bcrypt_hashed = \&bcrypt_prehashed;
55             *bcrypt_check_hashed = \&bcrypt_check_prehashed;
56              
57             sub _get_parameters {
58 3     3   6 my ($hash) = @_;
59 3 50       82 if ($hash =~ / \A \$ ($subtype_qr) \$ ($cost_qr) \$ /x) {
    0          
60 3         18 return ($1, $2, '');
61             }
62             elsif ($hash =~ / ^ \$ bcrypt-sha256 \$ v=2,t=($subtype_qr),r=($cost_qr) \$ /x) {
63 0         0 return ($1, $2, 'sha256');
64             }
65 0         0 return ('', 0, '');
66             }
67              
68             sub bcrypt_needs_rehash {
69 3     3 1 10 my ($hash, $wanted_subtype, $wanted_cost, $wanted_hash) = @_;
70 3         32 my ($my_subtype, $my_cost, $my_hash) = _get_parameters($hash);
71 3   66     61 return $my_subtype ne $wanted_subtype || $my_cost != $wanted_cost || $my_hash ne ($wanted_hash || '');
72             }
73              
74             1;
75              
76             # ABSTRACT: A modern bcrypt implementation
77              
78             __END__