File Coverage

blib/lib/Dancer/Plugin/Bcrypt.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Dancer::Plugin::Bcrypt;
2              
3             # ABSTRACT: DEPRECATED Bcrypt interface for Dancer
4              
5 2     2   30751 use strict;
  2         4  
  2         74  
6              
7 2     2   2042 use Dancer::Plugin;
  0            
  0            
8             use Dancer::Config;
9              
10             use Crypt::Eksblowfish::Bcrypt qw/en_base64/;
11             use Crypt::Random::Source;
12              
13             our $VERSION = '0.4.1';
14              
15              
16             register bcrypt => \&bcrypt;
17             register bcrypt_validate_password => \&bcrypt_validate_password;
18              
19              
20             sub bcrypt {
21             my ($plaintext, $bcrypted) = @_;
22              
23             return if !$plaintext;
24              
25             # Sanity checks, and provide some good defaults.
26             my $config = sanity_check();
27              
28             # On to the actual work...
29              
30             # If you pass a plaintext password and an bcrypted one (from a DB f.ex)
31             # we hash the plaintext password using the same method, salt and
32             # work factor as the stored version. If the plaintext password matches
33             # the stored version then the resulting hashes should be identical.
34              
35             if ($bcrypted && $bcrypted =~ /^\$2a\$/) {
36             return Crypt::Eksblowfish::Bcrypt::bcrypt($plaintext, $bcrypted);
37             }
38              
39             # If we have been passed only the plaintext, then we
40             # generate the bcrypted version with all new settings
41              
42             # Use bcrypt and append with a NULL - The accepted way to do it
43             my $method = '$2a';
44              
45             # Has to be 2 digits exactly
46             my $work_factor = sprintf("%02d", $config->{work_factor});
47              
48             # Salt must be exactly 16 octets, base64 encoded.
49             my $salt = en_base64( generate_salt( $config->{random_factor} ) );
50              
51             # Create the settings string that we will use to bcrypt the plaintext
52             # Read the docs of the Crypt:: modules for an explanation of this string
53             my $new_settings = join('$', $method, $work_factor, $salt);
54              
55              
56             return Crypt::Eksblowfish::Bcrypt::bcrypt($plaintext, $new_settings);
57             };
58              
59              
60             sub bcrypt_validate_password {
61             my ($plaintext, $bcrypted) = @_;
62              
63             if ($plaintext && $bcrypted) {
64             return bcrypt($plaintext, $bcrypted) eq $bcrypted;
65             } else {
66             return;
67             }
68             }
69              
70              
71             sub sanity_check {
72             my $config = plugin_setting;
73              
74             # Takes ~0.007 seconds on 2011 hardware
75             $config->{work_factor} ||= 4;
76              
77             # Uses /dev/urandom - which is pretty good
78             $config->{random_factor} ||= 'weak';
79              
80             # Work factors higher than 31 aren't supported.
81             if ($config->{work_factor} > 31) {
82             $config->{work_factor} = 31;
83             };
84              
85             # Can only specify weak or strong as random_factor
86             unless ( grep { $_ eq $config->{random_factor} } ('strong', 'weak') ) {
87             $config->{random_factor} = 'weak';
88             }
89              
90             return {
91             work_factor => $config->{work_factor},
92             random_factor => $config->{random_factor},
93             };
94             }
95              
96              
97             sub generate_salt {
98             my ($type) = @_;
99              
100             if ($type eq 'strong') {
101             return Crypt::Random::Source::get_strong(16);
102             } else {
103             return Crypt::Random::Source::get_weak(16);
104             }
105             }
106              
107              
108             register_plugin;
109              
110             1;
111              
112              
113             =pod
114              
115             =head1 NAME
116              
117             Dancer::Plugin::Bcrypt - DEPRECATED Bcrypt interface for Dancer
118              
119              
120             =head1 VERSION
121              
122             version 0.4.1
123              
124              
125             =head1 DESCRIPTION
126              
127             PLEASE NOTE THAT WHILE THIS MODULE WORKS, IT IS DEPRECATED, AND NO LONGER MAINTAINED.
128              
129             I suggest you use the more flexible replacement L -
130             It has all the same functionality as the module, and also allows you to match
131             against other hashing algorithms as well as brcypt.
132              
133             Original documentation continues below...
134              
135             This plugin is a simple interface to the bcrypt algorithm allowing web apps
136             created by dancer to easily store passwords in a secure way.
137              
138             It generates a crypographically strong salt for each password, uses the
139             very strong bcrypts algorithm to hash the password - and does these in a
140             configurable and portable manner.
141              
142              
143             =head1 BACKGROUND
144              
145             See L
146              
147             To safely store passwords in the modern era, you should use bcrypt.
148             It's that simple
149              
150             MD5, SHA and their ilk are general purpose hash functions, designed for speed.
151              
152             An average server can calculate the MD5 hash of every 6 character, alphanumeric
153             password in about 40 seconds. The beefiest boxen can do the same in ONE second
154              
155             Bcrypt is an adaptive password hashing algorithm. It uses a work factor
156             to determine how SLOWLY it hashes a password. This work factor
157             can be increased to keep up with the ever increasing power of computers.
158              
159              
160             =head1 KEYWORDS
161              
162             =head2 bcrypt
163              
164             Pass it a plaintext password, and it will return a string suitable for
165             storage, using the settings specified in the app config.
166              
167             This string contains the bcrypted hash, work factor used, and the salt used
168             to generate the hash, delimited by a $.
169              
170             my $hash = bcrypt($plaintext);
171              
172             Pass a plaintext password and a stored bcrypted string, it will return a hash
173             of the plaintext password using the work factor and salt from the stored hash.
174              
175             You would use this to verify that a password provided by a user matches the
176             hash you have stored in the database.
177              
178             my $hash = bcrypt($plaintext, $stored_hash);
179              
180             =head2 bcrypt_validate_password
181              
182             Pass it a plaintext password and the crypted password you have stored, and it
183             will return a boolean to indicate whether the plaintext password entered is
184             correct (it hashes to the same has the stored hash).
185              
186             if (bcrypt_validate_password($entered_password, $stored_hash)) {
187             ...
188             }
189              
190              
191             =head1 USAGE
192              
193             package MyWebService;
194             use Dancer;
195             use Dancer::Plugin::Bcrypt;
196              
197             get '/' sub => {
198              
199             # Generate a new hashed password - suitable for storing in a DB.
200             my $hash = bcrypt( param('password') );
201              
202             # [...]
203              
204             # Validate password provided by user against stored hash.
205             my $stored_hash = ''; # [...] retreive password from the DB.
206              
207             if (bcrypt_validate_password(param('password'), $stored_hash)) {
208             # Entered password matches
209             }
210              
211             };
212              
213              
214             =head1 CONFIGURATION
215              
216             You can set the work factor and the random-ness of the salt in your config.yml
217              
218             plugins:
219             bcrypt:
220             work_factor: 8
221             random_factor: strong
222              
223              
224             =head1 SEE ALSO
225              
226             L, L, L,
227             L
228              
229              
230             =head1 AUTHOR
231              
232             James Aitken
233              
234              
235             =head1 COPYRIGHT AND LICENSE
236              
237             This software is copyright (c) 2011 by James Aitken.
238              
239             This is free software; you can redistribute it and/or modify it under
240             the same terms as the Perl 5 programming language system itself.
241              
242             =cut