File Coverage

blib/lib/DBIx/Class/EncodedColumn/Digest.pm
Criterion Covered Total %
statement 40 40 100.0
branch 7 12 58.3
condition 12 17 70.5
subroutine 9 9 100.0
pod 2 2 100.0
total 70 80 87.5


line stmt bran cond sub pod time code
1             package DBIx::Class::EncodedColumn::Digest;
2              
3 2     2   7 use strict;
  2         3  
  2         48  
4 2     2   5 use warnings;
  2         3  
  2         35  
5 2     2   7 use Digest;
  2         3  
  2         887  
6              
7             our $VERSION = '0.00001';
8              
9             my %digest_lengths =
10             (
11             'MD2' => { base64 => 22, binary => 16, hex => 32 },
12             'MD4' => { base64 => 22, binary => 16, hex => 32 },
13             'MD5' => { base64 => 22, binary => 16, hex => 32 },
14              
15             'SHA-1' => { base64 => 27, binary => 20, hex => 40 },
16             'SHA-256' => { base64 => 43, binary => 32, hex => 64 },
17             'SHA-384' => { base64 => 64, binary => 48, hex => 96 },
18             'SHA-512' => { base64 => 86, binary => 64, hex => 128 },
19              
20             'CRC-CCITT' => { base64 => 2, binary => 3, hex => 3 },
21             'CRC-16' => { base64 => 6, binary => 5, hex => 4 },
22             'CRC-32' => { base64 => 14, binary => 10, hex => 8 },
23              
24             'Adler-32' => { base64 => 6, binary => 4, hex => 8 },
25             'Whirlpool' => { base64 => 86, binary => 64, hex => 128 },
26             'Haval-256' => { base64 => 44, binary => 32, hex => 64 },
27             );
28             my @salt_pool = ('A' .. 'Z', 'a' .. 'z', 0 .. 9, '+','/','=');
29              
30             sub make_encode_sub {
31 7     7 1 10 my($class, $col, $args) = @_;
32 7   100     22 my $for = $args->{format} ||= 'base64';
33 7   100     17 my $alg = $args->{algorithm} ||= 'SHA-256';
34 7   100     19 my $slen = $args->{salt_length} ||= 0;
35              
36 7 50       46 die("Valid Digest formats are 'binary', 'hex' or 'base64'. You used '$for'.")
37             unless $for =~ /^(?:hex|base64|binary)$/;
38 7 50       6 defined(my $object = eval{ Digest->new($alg) }) ||
  7         23  
39             die("Can't use Digest algorithm ${alg}: $@");
40              
41 7 100       198 my $format_method = $for eq 'binary' ? 'digest' :
    50          
42             ($for eq 'hex' ? 'hexdigest' : 'b64digest');
43             #thanks Haval for breaking the standard. thanks!
44 7 50 33     20 $format_method = 'base64digest 'if ($alg eq 'Haval-256' && $for eq 'base64');
45              
46             my $encoder = sub {
47 18     18   89 my ($plain_text, $salt) = @_;
48 18   100     83 $salt ||= join('', map { $salt_pool[int(rand(65))] } 1 .. $slen);
  28         54  
49 18         49 $object->reset()->add($plain_text.$salt);
50 18         287 my $digest = $object->$format_method;
51             #print "${plain_text}\t ${salt}:\t${digest}${salt}\n" if $salt;
52 18         69 return $digest.$salt;
53 7         24 };
54              
55             #in case i didn't prepopulate it
56 7   33     14 $digest_lengths{$alg}{$for} ||= length($encoder->('test1'));
57 7         20 return $encoder;
58             }
59              
60             sub make_check_sub {
61 5     5 1 6 my($class, $col, $args) = @_;
62              
63             #this is the digest length
64 5         9 my $len = $digest_lengths{$args->{algorithm}}{$args->{format}};
65 5 50       12 die("Unable to find digest length") unless defined $len;
66              
67             #fast fast fast
68 5   50 1   443 return eval qq^ sub {
  1     1   6  
  1     1   40  
  1         25  
  1         21257  
  1         27  
  1         18  
  1         5  
  1         23  
  1         15  
69             my \$col_v = \$_[0]->get_column('${col}');
70             my \$salt = substr(\$col_v, ${len});
71             \$_[0]->_column_encoders->{${col}}->(\$_[1], \$salt) eq \$col_v;
72             } ^ || die($@);
73             }
74              
75             1;
76              
77             __END__;
78              
79             =head1 NAME
80              
81             DBIx::Class::EncodedColumn::Digest - Digest backend
82              
83             =head1 SYNOPSYS
84              
85             #SHA-1 / hex encoding / generate check method
86             __PACKAGE__->add_columns(
87             'password' => {
88             data_type => 'CHAR',
89             size => 40 + 10,
90             encode_column => 1,
91             encode_class => 'Digest',
92             encode_args => {algorithm => 'SHA-1', format => 'hex', salt_length => 10},
93             encode_check_method => 'check_password',
94             }
95              
96             #SHA-256 / base64 encoding / generate check method
97             __PACKAGE__->add_columns(
98             'password' => {
99             data_type => 'CHAR',
100             size => 40,
101             encode_column => 1,
102             encode_class => 'Digest',
103             encode_check_method => 'check_password',
104             #no encode_args necessary because these are the defaults ...
105             }
106              
107              
108             =head1 DESCRIPTION
109              
110             =head1 ACCEPTED ARGUMENTS
111              
112             =head2 format
113              
114             The encoding to use for the digest. Valid values are 'binary', 'hex', and
115             'base64'. Will default to 'base64' if not specified.
116              
117             =head2 algorithm
118              
119             The digest algorithm to use for the digest. You may specify any valid L<Digest>
120             algorithm. Examples are L<MD5|Digest::MD5>, L<SHA-1|Digest::SHA>,
121             L<Whirlpool|Digest::Whirlpool> etc. Will default to 'SHA-256' if not specified.
122              
123             See L<Digest> for supported digest algorithms.
124              
125             =head2 salt_length
126              
127             If you would like to use randomly generated salts to encode values make sure
128             this option is set to > 0. Salts will be automatically generated at encode time
129             and will be appended to the end of the digest. Please make sure that you
130             remember to make sure that to expand the size of your db column to have enough
131             space to store both the digest AND the salt. Please see list below for common
132             digest lengths.
133              
134             =head1 METHODS
135              
136             =head2 make_encode_sub $column_name, \%encode_args
137              
138             Returns a coderef that takes two arguments, a plaintext value and an optional
139             salt and returns the encoded value with the salt appended to the end of the
140             digest. If a salt is not provided and the salt_length option was greater than
141             zero it will be randomly generated.
142              
143             =head2 make_check_sub $column_name, \%encode_args
144              
145             Returns a coderef that takes the row object and a plaintext value and will
146             return a boolean if the plaintext matches the encoded value. This is typically
147             used for password authentication.
148              
149             =head1 COMMON DIGEST LENGTHS
150              
151             CIPHER | Binary | Base64 | Hex
152             ---------------------------------------
153             | MD2 | 16 | 22 | 32 |
154             | MD4 | 16 | 22 | 32 |
155             | MD5 | 16 | 22 | 32 |
156             | SHA-1 | 20 | 27 | 40 |
157             | SHA-256 | 32 | 43 | 64 |
158             | SHA-384 | 48 | 64 | 96 |
159             | SHA-512 | 64 | 86 | 128 |
160             | CRC-CCITT | 3 | 2 | 3 |
161             | CRC-16 | 5 | 6 | 4 |
162             | CRC-32 | 10 | 14 | 8 |
163             | Adler-32 | 4 | 6 | 8 |
164             | Whirlpool | 64 | 86 | 128 |
165             | Haval-256 | 32 | 44 | 64 |
166             ---------------------------------------
167              
168             =head1 SEE ALSO
169              
170             L<DBIx::Class::EncodedColumn::Crypt::Eksblowfish::Bcrypt>,
171             L<DBIx::Class::EncodedColumn>, L<Digest>
172              
173             =head1 AUTHOR
174              
175             Guillermo Roditi (groditi) <groditi@cpan.org>
176              
177             Based on the Vienna WoC ToDo manager code by Matt S trout (mst)
178              
179             =head1 CONTRIBUTORS
180              
181             See L<DBIx::Class::EncodedColumn>
182              
183             =head1 LICENSE
184              
185             This module is free software; you can redistribute it and/or modify it under
186             the same terms as Perl itself.
187              
188             =cut