213d4136f9242c14ed6e2e3f9f39b99aa625d452
[authen-passphrase-scrypt.git] / lib / Authen / Passphrase / Scrypt.pm
1 package Authen::Passphrase::Scrypt;
2
3 use 5.014000;
4 use strict;
5 use warnings;
6 use Carp;
7
8 use parent qw/Exporter Authen::Passphrase Class::Accessor::Fast/;
9
10 our @EXPORT = qw/crypto_scrypt/;
11 our @EXPORT_OK = @EXPORT;
12 our $VERSION = '0.001001';
13
14 use Data::Entropy::Algorithms qw/rand_bits/;
15 use Digest::SHA qw/sha256 hmac_sha256/;
16 use MIME::Base64;
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 my ($self, $passphrase) = @_;
25 crypto_scrypt ($passphrase, $self->salt, (1 << $self->logN), $self->r, $self->p, 64);
26 }
27
28 sub truncated_sha256 {
29 my $sha = sha256 shift;
30 substr $sha, 0, 16
31 }
32
33 sub truncate_hash {
34 substr shift, 32
35 }
36
37 sub new {
38 my ($class, @args) = @_;
39 my $self = $class->SUPER::new(@args);
40
41 $self->logN(14) unless defined $self->logN;
42 $self->r(16) unless defined $self->r;
43 $self->p(1) unless defined $self->p;
44 croak "passphrase not set" unless defined $self->passphrase;
45 $self->salt(rand_bits 256) unless $self->salt;
46
47 my $data = "scrypt\x00" . pack 'CNNa32',
48 $self->logN, $self->r, $self->p, $self->salt;
49 $data .= truncated_sha256 $data;
50 $self->data($data);
51 $self->hmac(hmac_sha256 $self->data, truncate_hash $self->compute_hash($self->passphrase));
52 $self
53 }
54
55 sub from_rfc2307 {
56 my ($class, $rfc2307) = @_;
57 croak "Invalid Scrypt RFC2307" unless $rfc2307 =~ m,^{SCRYPT}([A-Za-z0-9+/]{128})$,;
58 my $data = decode_base64 $1;
59 my ($scrypt, $logN, $r, $p, $salt, $cksum, $hmac) =
60 unpack 'Z7CNNa32a16a32', $data;
61 croak 'Invalid Scrypt hash: should start with "scrypt"' unless $scrypt eq 'scrypt';
62 croak 'Invalid Scrypt hash: bad checksum', unless $cksum eq truncated_sha256 (substr $data, 0, 48);
63 $class->SUPER::new({data => (substr $data, 0, 64), logN => $logN, r => $r, p => $p, salt => $salt, hmac => $hmac});
64 }
65
66 sub match {
67 my ($self, $passphrase) = @_;
68 my $correct_hmac = hmac_sha256 $self->data, truncate_hash $self->compute_hash($passphrase);
69 $self->hmac eq $correct_hmac
70 }
71
72 sub as_rfc2307 {
73 my ($self) = @_;
74 '{SCRYPT}' . encode_base64 ($self->data . $self->hmac, '')
75 }
76
77 sub from_crypt {
78 croak __PACKAGE__ ." does not support crypt strings, use from_rfc2307 instead";
79 }
80
81 sub as_crypt {
82 croak __PACKAGE__ ." does not support crypt strings, use as_rfc2307 instead";
83 }
84
85 1;
86 __END__
87
88 =encoding utf-8
89
90 =head1 NAME
91
92 Authen::Passphrase::Scrypt - passphrases using Tarsnap's scrypt algorithm
93
94 =head1 SYNOPSIS
95
96 use Authen::Passphrase::Scrypt;
97
98 # Hash a password
99 my $sc = Authen::Passphrase::Scrypt->new({
100 passphrase => 'correcthorsebatterystaple'
101 });
102 my $hash = $sc->as_rfc2307;
103 say "The given password hashes to $hash";
104
105 # Verify a password
106 $sc = Authen::Passphrase::Scrypt->from_rfc2307($hash);
107 say 'The password was "correcthorsebatterystaple"'
108 if $sc->match('correcthorsebatterystaple');
109 say 'The password was "xkcd"' if $sc->match('xkcd');
110
111 # Advanced hashing
112 my $sc = Authen::Passphrase::Scrypt->new({
113 passphrase => 'xkcd',
114 logN => 14, # General work factor
115 r => 16, # Memory work factor
116 p => 1, # CPU (parallellism) work factor
117 salt => 'SodiumChloride && sODIUMcHLORIDE', # Must be 32 bytes
118 });
119 say 'The given password now hashes to ', $sc->as_rfc2307;
120
121 =head1 DESCRIPTION
122
123 B<This is experimental code, DO NOT USE in security-critical software>.
124
125 Scrypt is a key derivation function that was originally developed for
126 use in the Tarsnap online backup system and is designed to be far more
127 secure against hardware brute-force attacks than alternative functions
128 such as PBKDF2 or bcrypt.
129
130 Authen::Passphrase::Scrypt is a module for hashing and verifying
131 passphrases using scrypt. It offers the same interface as
132 L<Authen::Passphrase>. It is not however possible to use this module
133 from within L<Authen::Passphrase>. The methods are:
134
135 =over
136
137 =item Authen::Passphrase::Scrypt->B<new>(I<\%args>)
138
139 Creates a new L<Authen::Passphrase::Scrypt> from a given passphrase
140 and parameters. Use this to hash a passphrase. The arguments are:
141
142 =over
143
144 =item B<passphrase>
145
146 The passphrase. Mandatory.
147
148 =item B<logN>
149
150 The general work factor (affects both CPU and memory cost). Defaults to 14
151
152 =item B<r>
153
154 The blocksize (affects memory cost). Defaults to 16.
155
156 =item B<p>
157
158 The parallelization factor (affects CPU cost). Defaults to 1.
159
160 =item B<salt>
161
162 A 32-byte string used as a salt. By default it is randomly generated
163 using L<Data::Entropy>.
164
165 =back
166
167 All of the parameters change the result of the hash. They are all
168 stored in the hash, so there is no need to store them separately (or
169 provide them to the hash verification methods).
170
171 It is normally sufficient to only use the B<logN> parameter to control
172 the speed of scrypt. B<r> and B<p> are intended to be used only for
173 fine-tuning: if scrypt uses too much memory but not enough CPU,
174 decrease logN and increase p; if scrypt uses too much CPU but not
175 enough memory, decrease logN and increase r.
176
177 =item $sc->B<as_rfc2307>
178
179 Returns the hash of the passphrase, in RFC2307 format. This is
180 "{SCRYPT}" followed by the base64-encoded 96-byte result described here: L<https://security.stackexchange.com/questions/88678/why-does-node-js-scrypt-function-use-hmac-this-way/91050>
181
182 =item Authen::Passphrase::Scrypt->B<from_rfc2307>(I<$rfc2307>)
183
184 Creates a new L<Authen::Passphrase::Scrypt> from a hash in RFC2307
185 format. Use this to verify if a passphrase matches a hash.
186
187 =item $sc->B<match>(I<$passphrase>)
188
189 Returns true if the given passphrase matches the hash, false
190 otherwise.
191
192 =item Authen::Passphrase::Scrypt->from_crypt
193 =item $sc->as_crypt
194
195 These functions both croak. They are provided for compatibility with
196 the Authen::Passphrase interface.
197
198 =back
199
200 =head1 SEE ALSO
201
202 L<Authen::Passphrase>,
203 L<https://www.tarsnap.com/scrypt.html>,
204 L<https://www.npmjs.com/package/scrypt>
205
206 =head1 AUTHOR
207
208 Marius Gavrilescu, E<lt>marius@ieval.roE<gt>
209
210 =head1 COPYRIGHT AND LICENSE
211
212 Copyright (C) 2017 by Marius Gavrilescu
213
214 This library is free software; you can redistribute it and/or modify
215 it under the same terms as Perl itself, either Perl version 5.24.1 or,
216 at your option, any later version of Perl 5 you may have available.
217
218
219 =cut
This page took 0.033615 seconds and 3 git commands to generate.