Initial commit
[authen-passphrase-scrypt.git] / lib / Authen / Passphrase / Scrypt.pm
CommitLineData
0c1f3509
MG
1package Authen::Passphrase::Scrypt;
2
3use 5.014000;
4use strict;
5use warnings;
6use Carp;
7
8use parent qw/Exporter Authen::Passphrase Class::Accessor::Fast/;
9
10our @EXPORT = qw/crypto_scrypt/;
11our @EXPORT_OK = @EXPORT;
12our $VERSION = '0.001';
13
14use Data::Entropy::Algorithms qw/rand_bits/;
15use Digest::SHA qw/sha256 hmac_sha256/;
16use MIME::Base64;
17
18require XSLoader;
19XSLoader::load('Authen::Passphrase::Scrypt', $VERSION);
20
21__PACKAGE__->mk_accessors(qw/data logN r p salt hmac passphrase/);
22
23sub compute_hash {
24 my ($self, $passphrase) = @_;
25 crypto_scrypt ($passphrase, $self->salt, (1 << $self->logN), $self->r, $self->p, 64);
26}
27
28sub truncated_sha256 {
29 my $sha = sha256 shift;
30 substr $sha, 0, 16
31}
32
33sub truncate_hash {
34 substr shift, 32
35}
36
37sub 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
55sub 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
66sub 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
72sub as_rfc2307 {
73 my ($self) = @_;
74 '{SCRYPT}' . encode_base64 ($self->data . $self->hmac, '')
75}
76
77sub from_crypt {
78 croak __PACKAGE__ ." does not support crypt strings, use from_rfc2307 instead";
79}
80
81sub as_crypt {
82 croak __PACKAGE__ ." does not support crypt strings, use as_rfc2307 instead";
83}
84
851;
86__END__
87
88=encoding utf-8
89
90=head1 NAME
91
92Authen::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
123B<This is experimental code, DO NOT USE in security-critical software>.
124
125Scrypt is a key derivation function that was originally developed for
126use in the Tarsnap online backup system and is designed to be far more
127secure against hardware brute-force attacks than alternative functions
128such as PBKDF2 or bcrypt.
129
130Authen::Passphrase::Scrypt is a module for hashing and verifying
131passphrases using scrypt. It offers the same interface as
132L<Authen::Passphrase>. It is not however possible to use this module
133from within L<Authen::Passphrase>. The methods are:
134
135=over
136
137=item Authen::Passphrase::Scrypt->B<new>(I<\%args>)
138
139Creates a new L<Authen::Passphrase::Scrypt> from a given passphrase
140and parameters. Use this to hash a passphrase. The arguments are:
141
142=over
143
144=item B<passphrase>
145
146The passphrase. Mandatory.
147
148=item B<logN>
149
150The general work factor (affects both CPU and memory cost). Defaults to 14
151
152=item B<r>
153
154The blocksize (affects memory cost). Defaults to 16.
155
156=item B<p>
157
158The parallelization factor (affects CPU cost). Defaults to 1.
159
160=item B<salt>
161
162A 32-byte string used as a salt. By default it is randomly generated
163using L<Data::Entropy>.
164
165=back
166
167All of the parameters change the result of the hash. They are all
168stored in the hash, so there is no need to store them separately (or
169provide them to the hash verification methods).
170
171It is normally sufficient to only use the B<logN> parameter to control
172the speed of scrypt. B<r> and B<p> are intended to be used only for
173fine-tuning: if scrypt uses too much memory but not enough CPU,
174decrease logN and increase p; if scrypt uses too much CPU but not
175enough memory, decrease logN and increase r.
176
177=item $sc->B<as_rfc2307>
178
179Returns 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
184Creates a new L<Authen::Passphrase::Scrypt> from a hash in RFC2307
185format. Use this to verify if a passphrase matches a hash.
186
187=item $sc->B<match>(I<$passphrase>)
188
189Returns true if the given passphrase matches the hash, false
190otherwise.
191
192=item Authen::Passphrase::Scrypt->from_crypt
193=item $sc->as_crypt
194
195These functions both croak. They are provided for compatibility with
196the Authen::Passphrase interface.
197
198=back
199
200=head1 SEE ALSO
201
202L<Authen::Passphrase>,
203L<https://www.tarsnap.com/scrypt.html>,
204L<https://www.npmjs.com/package/scrypt>
205
206=head1 AUTHOR
207
208Marius Gavrilescu, E<lt>marius@ieval.roE<gt>
209
210=head1 COPYRIGHT AND LICENSE
211
212Copyright (C) 2017 by Marius Gavrilescu
213
214This library is free software; you can redistribute it and/or modify
215it under the same terms as Perl itself, either Perl version 5.24.1 or,
216at your option, any later version of Perl 5 you may have available.
217
218
219=cut
This page took 0.046862 seconds and 4 git commands to generate.