File Coverage

blib/lib/Crypt/Bcrypt.pm
Criterion Covered Total %
statement 46 51 90.2
branch 8 18 44.4
condition 4 9 44.4
subroutine 12 13 92.3
pod 5 5 100.0
total 75 96 78.1


line stmt bran cond sub pod time code
1             package Crypt::Bcrypt;
2             $Crypt::Bcrypt::VERSION = '0.011';
3 2     2   134339 use strict;
  2         16  
  2         59  
4 2     2   10 use warnings;
  2         17  
  2         52  
5              
6 2     2   10 use XSLoader;
  2         4  
  2         64  
7             XSLoader::load('Crypt::Bcrypt');
8              
9 2     2   9 use Exporter 5.57 'import';
  2         45  
  2         125  
10             our @EXPORT_OK = qw(bcrypt bcrypt_check bcrypt_prehashed bcrypt_check_prehashed bcrypt_hashed bcrypt_check_hashed bcrypt_needs_rehash bcrypt_supported_prehashes);
11              
12 2     2   16 use Carp 'croak';
  2         4  
  2         105  
13 2     2   1086 use Digest::SHA;
  2         6207  
  2         127  
14 2     2   936 use MIME::Base64 2.21 qw(encode_base64);
  2         1281  
  2         1644  
15              
16             sub bcrypt {
17 24     24 1 8007 my ($password, $subtype, $cost, $salt) = @_;
18 24 50       140 croak "Unknown subtype $subtype" if $subtype !~ /^2[abxy]$/;
19 24 50 33     137 croak "Invalid cost factor $cost" if $cost < 4 || $cost > 31;
20 24 50       59 croak "Salt must be 16 bytes" if length $salt != 16;
21 24         86 my $encoded_salt = encode_base64($salt, "");
22 24         66 $encoded_salt =~ tr{A-Za-z0-9+/=}{./A-Za-z0-9}d;
23 24         11799708 return _bcrypt_hashpw($password, sprintf '$%s$%02d$%s', $subtype, $cost, $encoded_salt);
24             }
25              
26             my $subtype_qr = qr/2[abxy]/;
27             my $cost_qr = qr/\d{2}/;
28             my $salt_qr = qr{ [./A-Za-z0-9]{22} }x;
29             my $algo_qr = qr{ sha[0-9]+ }x;
30              
31             my %hash_for = (
32             sha256 => \&Digest::SHA::hmac_sha256,
33             sha384 => \&Digest::SHA::hmac_sha384,
34             sha512 => \&Digest::SHA::hmac_sha512,
35             );
36              
37             sub bcrypt_prehashed {
38 2     2 1 1643975 my ($password, $subtype, $cost, $salt, $algorithm) = @_;
39 2 50       12 if (length $algorithm) {
40 2         16 (my $encoded_salt = encode_base64($salt, "")) =~ tr{A-Za-z0-9+/=}{./A-Za-z0-9}d;
41 2   33     13 my $hasher = $hash_for{$algorithm} || croak "No such hash $algorithm";
42 2         33 my $hashed_password = encode_base64($hasher->($password, $encoded_salt), "");
43 2         8 my $hash = bcrypt($hashed_password, $subtype, $cost, $salt);
44 2 50       160 $hash =~ s{ ^ \$ ($subtype_qr) \$ ($cost_qr) \$ ($salt_qr) }{\$bcrypt-$algorithm\$v=2,t=$1,r=$2\$$3\$}x or croak $hash;
45 2         56 return $hash;
46             }
47             else {
48 0         0 bcrypt($password, $subtype, $cost, $salt);
49             }
50             }
51              
52             sub bcrypt_check_prehashed {
53 4     4 1 945 my ($password, $hash) = @_;
54 4 50       141 if ($hash =~ s/ ^ \$ bcrypt-(\w+) \$ v=2,t=($subtype_qr),r=($cost_qr) \$ ($salt_qr) \$ /\$$2\$$3\$$4/x) {
55 4 50       27 my $hasher = $hash_for{$1} or return 0;
56 4         14785972 return bcrypt_check(encode_base64($hasher->($password, $4), ""), $hash);
57             }
58             else {
59 0         0 return bcrypt_check($password, $hash);
60             }
61             }
62              
63             #legacy names
64             *bcrypt_hashed = \&bcrypt_prehashed;
65             *bcrypt_check_hashed = \&bcrypt_check_prehashed;
66              
67             sub _get_parameters {
68 3     3   6 my ($hash) = @_;
69 3 50       74 if ($hash =~ / \A \$ ($subtype_qr) \$ ($cost_qr) \$ /x) {
    0          
70 3         17 return ($1, $2, '');
71             }
72             elsif ($hash =~ / ^ \$ bcrypt-($algo_qr) \$ v=2,t=($subtype_qr),r=($cost_qr) \$ /x) {
73 0         0 return ($2, $3, $1);
74             }
75 0         0 return ('', 0, '');
76             }
77              
78             sub bcrypt_needs_rehash {
79 3     3 1 12 my ($hash, $wanted_subtype, $wanted_cost, $wanted_hash) = @_;
80 3         8 my ($my_subtype, $my_cost, $my_hash) = _get_parameters($hash);
81 3   66     37 return $my_subtype ne $wanted_subtype || $my_cost != $wanted_cost || $my_hash ne ($wanted_hash || '');
82             }
83              
84             sub bcrypt_supported_prehashes {
85 0     0 1   return sort keys %hash_for;
86             }
87              
88             1;
89              
90             # ABSTRACT: A modern bcrypt implementation
91              
92             __END__