File Coverage

blib/lib/Crypt/Eksblowfish/Bcrypt.pm
Criterion Covered Total %
statement 45 45 100.0
branch 4 6 66.6
condition 3 3 100.0
subroutine 11 11 100.0
pod 4 4 100.0
total 67 69 97.1


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Crypt::Eksblowfish::Bcrypt - Blowfish-based Unix crypt() password hash
4              
5             =head1 SYNOPSIS
6              
7             use Crypt::Eksblowfish::Bcrypt qw(bcrypt_hash);
8              
9             $hash = bcrypt_hash({
10             key_nul => 1,
11             cost => 8,
12             salt => $salt,
13             }, $password);
14              
15             use Crypt::Eksblowfish::Bcrypt qw(en_base64 de_base64);
16              
17             $text = en_base64($octets);
18             $octets = de_base64($text);
19              
20             use Crypt::Eksblowfish::Bcrypt qw(bcrypt);
21              
22             $hashed_password = bcrypt($password, $settings);
23              
24             =head1 DESCRIPTION
25              
26             This module implements the Blowfish-based Unix crypt() password hashing
27             algorithm, known as "bcrypt". This hash uses a variant of Blowfish,
28             known as "Eksblowfish", modified to have particularly expensive key
29             scheduling. Eksblowfish and bcrypt were devised by Niels Provos and
30             David Mazieres for OpenBSD. The design is described in a paper at
31             L.
32              
33             =cut
34              
35             package Crypt::Eksblowfish::Bcrypt;
36              
37 3     3   90635 { use 5.006; }
  3         13  
  3         145  
38 3     3   19 use warnings;
  3         12  
  3         126  
39 3     3   42 use strict;
  3         13  
  3         113  
40              
41 3     3   19 use Carp qw(croak);
  3         5  
  3         348  
42 3     3   2751 use Crypt::Eksblowfish 0.005;
  3         68  
  3         113  
43 3     3   3495 use MIME::Base64 2.21 qw(encode_base64 decode_base64);
  3         3033  
  3         375  
44              
45             our $VERSION = "0.009";
46              
47 3     3   21 use parent "Exporter";
  3         5  
  3         20  
48             our @EXPORT_OK = qw(bcrypt_hash en_base64 de_base64 bcrypt);
49              
50             =head1 FUNCTIONS
51              
52             =over
53              
54             =item bcrypt_hash(SETTINGS, PASSWORD)
55              
56             Hashes PASSWORD according to the supplied SETTINGS, and returns the
57             23-octet hash. SETTINGS must be a reference to a hash, with these keys:
58              
59             =over
60              
61             =item B
62              
63             Truth value: whether to append a NUL to the password before using it as a key.
64             The algorithm as originally devised does not do this, but it was later
65             modified to do it. The version that does append NUL is to be preferred;
66             not doing so is supported only for backward compatibility.
67              
68             =item B
69              
70             Non-negative integer controlling the cost of the hash function.
71             The number of operations is proportional to 2^cost.
72              
73             =item B
74              
75             Exactly sixteen octets of salt.
76              
77             =back
78              
79             =cut
80              
81             sub bcrypt_hash($$) {
82 70     70 1 150 my($settings, $password) = @_;
83 70 100 100     461 $password .= "\0" if $settings->{key_nul} || $password eq "";
84 70         1080402 my $cipher = Crypt::Eksblowfish->new($settings->{cost},
85             $settings->{salt}, substr($password, 0, 72));
86 210         1176 my $hash = join("", map {
87 70         480 my $blk = $_;
88 210         578 for(my $i = 64; $i--; ) {
89 13440         49005 $blk = $cipher->encrypt($blk);
90             }
91 210         769 $blk;
92             } qw(OrpheanB eholderS cryDoubt));
93 70         214 chop $hash;
94 70         772 return $hash;
95             }
96              
97             =item en_base64(BYTES)
98              
99             Encodes the octet string textually using the form of base 64 that is
100             conventionally used with bcrypt.
101              
102             =cut
103              
104             sub en_base64($) {
105 92     92 1 11305 my($octets) = @_;
106 92         457 my $text = encode_base64($octets, "");
107 92         267 $text =~ tr#A-Za-z0-9+/=#./A-Za-z0-9#d;
108 92         690 return $text;
109             }
110              
111             =item de_base64(TEXT)
112              
113             Decodes an octet string that was textually encoded using the form of
114             base 64 that is conventionally used with bcrypt.
115              
116             =cut
117              
118             sub de_base64($) {
119 92     92 1 171 my($text) = @_;
120 92 50       546 croak "bad base64 encoding"
121             unless $text =~ m#\A(?>(?:[./A-Za-z0-9]{4})*)
122             (?:|[./A-Za-z0-9]{2}[.CGKOSWaeimquy26]|
123             [./A-Za-z0-9][.Oeu])\z#x;
124 92         204 $text =~ tr#./A-Za-z0-9#A-Za-z0-9+/#;
125 92         508 $text .= "=" x (3 - (length($text) + 3) % 4);
126 92         1188 return decode_base64($text);
127             }
128              
129             =item bcrypt(PASSWORD, SETTINGS)
130              
131             This is a version of C (see L) that implements the
132             bcrypt algorithm. It does not implement any other hashing algorithms,
133             so if others are desired then it necessary to examine the algorithm
134             prefix in SETTINGS and dispatch between more than one version of C.
135              
136             SETTINGS must be a string which encodes the algorithm parameters,
137             including salt. It must begin with "$2", optional "a", "$", two
138             digits, "$", and 22 base 64 digits. The rest of the string is ignored.
139             The presence of the optional "a" means that a NUL is to be appended
140             to the password before it is used as a key. The two digits set the
141             cost parameter. The 22 base 64 digits encode the salt. The function
142             will C if SETTINGS does not have this format.
143              
144             The PASSWORD is hashed according to the SETTINGS. The value returned
145             is a string which encodes the algorithm parameters and the hash: the
146             parameters are in the same format required in SETTINGS, and the hash is
147             appended in the form of 31 base 64 digits. This result is suitable to
148             be used as a SETTINGS string for input to this function: the hash part
149             of the string is ignored on input.
150              
151             =cut
152              
153             sub bcrypt($$) {
154 68     68 1 44868 my($password, $settings) = @_;
155 68 50       462 croak "bad bcrypt settings"
156             unless $settings =~ m#\A\$2(a?)\$([0-9]{2})\$
157             ([./A-Za-z0-9]{22})#x;
158 68         242 my($key_nul, $cost, $salt_base64) = ($1, $2, $3);
159 68         194 my $hash = bcrypt_hash({
160             key_nul => $key_nul,
161             cost => $cost,
162             salt => de_base64($salt_base64),
163             }, $password);
164 68         487 return "\$2${key_nul}\$${cost}\$${salt_base64}".en_base64($hash);
165             }
166              
167             =back
168              
169             =head1 SEE ALSO
170              
171             L,
172             L
173              
174             =head1 AUTHOR
175              
176             Andrew Main (Zefram)
177              
178             =head1 COPYRIGHT
179              
180             Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011
181             Andrew Main (Zefram)
182              
183             =head1 LICENSE
184              
185             This module is free software; you can redistribute it and/or modify it
186             under the same terms as Perl itself.
187              
188             =cut
189              
190             1;