File Coverage

blib/lib/Crypt/Passphrase/Scrypt.pm
Criterion Covered Total %
statement 34 35 97.1
branch 4 8 50.0
condition 11 19 57.8
subroutine 10 11 90.9
pod 5 5 100.0
total 64 78 82.0


line stmt bran cond sub pod time code
1             package Crypt::Passphrase::Scrypt;
2             $Crypt::Passphrase::Scrypt::VERSION = '0.003';
3 1     1   68629 use strict;
  1         10  
  1         31  
4 1     1   5 use warnings;
  1         2  
  1         26  
5              
6 1     1   413 use parent 'Crypt::Passphrase::Encoder';
  1         274  
  1         5  
7              
8 1     1   7438 use Carp 'croak';
  1         2  
  1         59  
9 1     1   7 use Crypt::ScryptKDF qw/scrypt_b64 scrypt_raw/;
  1         2  
  1         52  
10 1     1   5 use MIME::Base64 qw/encode_base64 decode_base64/;
  1         3  
  1         446  
11              
12             sub new {
13 2     2 1 92 my ($class, %args) = @_;
14             return bless {
15             cost => $args{cost} || 16,
16             block_size => $args{block_size} || 8,
17             parallel => $args{parallel} || 1,
18             salt_size => $args{salt_size} || 16,
19 2   100     44 output_size => $args{output_size} || 16,
      50        
      50        
      50        
      50        
20             }, $class;
21             }
22              
23             sub hash_password {
24 2     2 1 14 my ($self, $password) = @_;
25 2         23 my $salt = $self->random_bytes($self->{salt_size});
26 2         10265 my $hash = scrypt_b64($password, $salt, 1 << $self->{cost}, $self->{block_size}, $self->{parallel}, $self->{output_size});
27 2         657883 return sprintf '$scrypt$ln=%d,r=%d,p=%d$%s$%s', $self->{cost}, $self->{block_size}, $self->{parallel}, encode_base64($salt), $hash;
28             }
29              
30             my $decode_regex = qr/ \A \$ scrypt \$ ln=(\d+),r=(\d+),p=(\d+) \$ ([^\$]+) \$ ([^\$]*) \z /x;
31              
32             sub needs_rehash {
33 3     3 1 1684 my ($self, $hash) = @_;
34 3 50       60 my ($cost, $block_size, $parallel, $salt64, $hash64) = $hash =~ $decode_regex or return 1;
35 3 50 66     53 return 1 if $cost < $self->{cost} or $block_size < $self->{block_size} or $parallel < $self->{parallel};
      66        
36 2 50 33     28 return 1 if length decode_base64($salt64) < $self->{salt_size} or length decode_base64($hash64) < $self->{output_size};
37 2         12 return 0;
38             }
39              
40             sub crypt_subtypes {
41 0     0 1 0 return 'scrypt';
42             }
43              
44             sub verify_password {
45 3     3 1 40 my ($class, $password, $hash) = @_;
46 3 50       56 my ($cost, $block_size, $parallel, $salt64, $hash64) = $hash =~ $decode_regex or return 0;
47 3         16 my $old_hash = decode_base64($hash64);
48 3         29 my $new_hash = scrypt_raw($password, decode_base64($salt64), 1 << $cost, $block_size, $parallel, length $old_hash);
49 3         1098030 return $class->secure_compare($new_hash, $old_hash);
50             }
51              
52             1;
53              
54             #ABSTRACT: A scrypt encoder for Crypt::Passphrase
55              
56             __END__