File Coverage

blib/lib/Authen/Passphrase/Scrypt.pm
Criterion Covered Total %
statement 52 54 96.3
branch 11 16 68.7
condition n/a
subroutine 15 17 88.2
pod 6 9 66.6
total 84 96 87.5


line stmt bran cond sub pod time code
1             package Authen::Passphrase::Scrypt;
2              
3 1     1   15760 use 5.014000;
  1         4  
4 1     1   7 use strict;
  1         1  
  1         20  
5 1     1   3 use warnings;
  1         12  
  1         43  
6 1     1   7 use Carp;
  1         3  
  1         108  
7              
8 1     1   399 use parent qw/Exporter Authen::Passphrase Class::Accessor::Fast/;
  1         249  
  1         5  
9              
10             our @EXPORT = qw/crypto_scrypt/;
11             our @EXPORT_OK = @EXPORT;
12             our $VERSION = '0.001001';
13              
14 1     1   7809 use Data::Entropy::Algorithms qw/rand_bits/;
  1         12243  
  1         77  
15 1     1   513 use Digest::SHA qw/sha256 hmac_sha256/;
  1         3492  
  1         98  
16 1     1   12 use MIME::Base64;
  1         3  
  1         520  
17              
18             require XSLoader;
19             XSLoader::load('Authen::Passphrase::Scrypt', $VERSION);
20              
21             __PACKAGE__->mk_accessors(qw/data logN r p salt hmac passphrase/);
22              
23             sub compute_hash {
24 6     6 0 56 my ($self, $passphrase) = @_;
25 6         31 crypto_scrypt ($passphrase, $self->salt, (1 << $self->logN), $self->r, $self->p, 64);
26             }
27              
28             sub truncated_sha256 {
29 3     3 0 41 my $sha = sha256 shift;
30 3         15 substr $sha, 0, 16
31             }
32              
33             sub truncate_hash {
34 6     6 0 2460429 substr shift, 32
35             }
36              
37             sub new {
38 2     2 1 751581 my ($class, @args) = @_;
39 2         33 my $self = $class->SUPER::new(@args);
40              
41 2 100       37 $self->logN(14) unless defined $self->logN;
42 2 100       37 $self->r(16) unless defined $self->r;
43 2 50       24 $self->p(1) unless defined $self->p;
44 2 50       32 croak "passphrase not set" unless defined $self->passphrase;
45 2 100       16 $self->salt(rand_bits 256) unless $self->salt;
46              
47 2         6146 my $data = "scrypt\x00" . pack 'CNNa32',
48             $self->logN, $self->r, $self->p, $self->salt;
49 2         47 $data .= truncated_sha256 $data;
50 2         10 $self->data($data);
51 2         17 $self->hmac(hmac_sha256 $self->data, truncate_hash $self->compute_hash($self->passphrase));
52 2         41 $self
53             }
54              
55             sub from_rfc2307 {
56 1     1 1 863 my ($class, $rfc2307) = @_;
57 1 50       10 croak "Invalid Scrypt RFC2307" unless $rfc2307 =~ m,^{SCRYPT}([A-Za-z0-9+/]{128})$,;
58 1         17 my $data = decode_base64 $1;
59 1         16 my ($scrypt, $logN, $r, $p, $salt, $cksum, $hmac) =
60             unpack 'Z7CNNa32a16a32', $data;
61 1 50       6 croak 'Invalid Scrypt hash: should start with "scrypt"' unless $scrypt eq 'scrypt';
62 1 50       9 croak 'Invalid Scrypt hash: bad checksum', unless $cksum eq truncated_sha256 (substr $data, 0, 48);
63 1         16 $class->SUPER::new({data => (substr $data, 0, 64), logN => $logN, r => $r, p => $p, salt => $salt, hmac => $hmac});
64             }
65              
66             sub match {
67 4     4 1 1902 my ($self, $passphrase) = @_;
68 4         21 my $correct_hmac = hmac_sha256 $self->data, truncate_hash $self->compute_hash($passphrase);
69 4         54 $self->hmac eq $correct_hmac
70             }
71              
72             sub as_rfc2307 {
73 1     1 1 15 my ($self) = @_;
74 1         6 '{SCRYPT}' . encode_base64 ($self->data . $self->hmac, '')
75             }
76              
77             sub from_crypt {
78 0     0 1   croak __PACKAGE__ ." does not support crypt strings, use from_rfc2307 instead";
79             }
80              
81             sub as_crypt {
82 0     0 1   croak __PACKAGE__ ." does not support crypt strings, use as_rfc2307 instead";
83             }
84              
85             1;
86             __END__