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