File Coverage

blib/lib/Authen/Passphrase/Scrypt.pm
Criterion Covered Total %
statement 59 59 100.0
branch 12 12 100.0
condition n/a
subroutine 18 18 100.0
pod 6 9 66.6
total 95 98 96.9


line stmt bran cond sub pod time code
1             package Authen::Passphrase::Scrypt;
2              
3 1     1   57355 use 5.014000;
  1         4  
4 1     1   5 use strict;
  1         1  
  1         17  
5 1     1   3 use warnings;
  1         2  
  1         36  
6 1     1   6 use Carp;
  1         1  
  1         79  
7              
8 1     1   392 use parent qw/Exporter Authen::Passphrase/;
  1         250  
  1         4  
9              
10             our @EXPORT = qw/crypto_scrypt/;
11             our @EXPORT_OK = @EXPORT;
12             our $VERSION = '0.002';
13              
14 1     1   4704 use Data::Entropy::Algorithms qw/rand_bits/;
  1         11598  
  1         57  
15 1     1   432 use Digest::SHA qw/sha256 hmac_sha256/;
  1         2035  
  1         73  
16 1     1   6 use MIME::Base64;
  1         2  
  1         57  
17              
18             require XSLoader;
19             XSLoader::load('Authen::Passphrase::Scrypt', $VERSION);
20              
21 1     1   447 use Object::Tiny qw/data logN r p salt hmac passphrase/;
  1         289  
  1         5  
22              
23             sub compute_hash {
24 7     7 0 49 my ($self, $passphrase) = @_;
25 7         99 crypto_scrypt ($passphrase, $self->salt, (1 << $self->logN), $self->r, $self->p, 64);
26             }
27              
28             sub truncated_sha256 {
29 5     5 0 37 my $sha = sha256 shift;
30 5         26 substr $sha, 0, 16
31             }
32              
33             sub truncate_hash {
34 6     6 0 2414547 substr shift, 32
35             }
36              
37             sub new {
38 4     4 1 715950 my ($class, @args) = @_;
39 4 100       16 if ('HASH' eq ref $args[0]) { # we were given a hash
40 1         2 @args = %{$args[0]}
  1         4  
41             }
42 4         17 unshift @args, logN => 14, r => 16, p => 1; # default values
43 4         18 my %args = @args;
44 4 100       23 $args{salt} = rand_bits 256 unless exists $args{salt};
45 4         6589 my $self = bless \%args, $class;
46              
47 4 100       84 croak "passphrase not set" unless defined $self->passphrase;
48              
49 3         60 my $data = "scrypt\x00" . pack 'CNNa32',
50             $self->logN, $self->r, $self->p, $self->salt;
51 3         66 $data .= truncated_sha256 $data;
52 3         13 $self->{data} = $data;
53 3         47 $self->{hmac} = hmac_sha256 $self->data, truncate_hash $self->compute_hash($self->passphrase);
54 2         39 $self
55             }
56              
57             sub from_rfc2307 {
58 4     4 1 2446 my ($class, $rfc2307) = @_;
59 4 100       56 croak "Invalid Scrypt RFC2307" unless $rfc2307 =~ m,^{SCRYPT}([A-Za-z0-9+/]{128})$,;
60 3         16 my $data = decode_base64 $1;
61 3         22 my ($scrypt, $logN, $r, $p, $salt, $cksum, $hmac) =
62             unpack 'Z7CNNa32a16a32', $data;
63 3 100       16 croak 'Invalid Scrypt hash: should start with "scrypt"' unless $scrypt eq 'scrypt';
64 2 100       9 croak 'Invalid Scrypt hash: bad checksum', unless $cksum eq truncated_sha256 (substr $data, 0, 48);
65 1         21 bless { data => (substr $data, 0, 64), logN => $logN, r => $r, p => $p, salt => $salt, hmac => $hmac }, $class;
66             }
67              
68             sub match {
69 4     4 1 898 my ($self, $passphrase) = @_;
70 4         110 my $correct_hmac = hmac_sha256 $self->data, truncate_hash $self->compute_hash($passphrase);
71 4         158 $self->hmac eq $correct_hmac
72             }
73              
74             sub as_rfc2307 {
75 1     1 1 8 my ($self) = @_;
76 1         35 '{SCRYPT}' . encode_base64 ($self->data . $self->hmac, '')
77             }
78              
79             sub from_crypt {
80 1     1 1 608 croak __PACKAGE__ ." does not support crypt strings, use from_rfc2307 instead";
81             }
82              
83             sub as_crypt {
84 1     1 1 593 croak __PACKAGE__ ." does not support crypt strings, use as_rfc2307 instead";
85             }
86              
87             1;
88             __END__