fabc7aeadb41418fa90872b1b8cbe83bc4979e5c
[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/;
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 use Object::Tiny 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 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;
46
47 croak "passphrase not set" unless defined $self->passphrase;
48
49 my $data = "scrypt\x00" . pack 'CNNa32',
50 $self->logN, $self->r, $self->p, $self->salt;
51 $data .= truncated_sha256 $data;
52 $self->{data} = $data;
53 $self->{hmac} = hmac_sha256 $self->data, truncate_hash $self->compute_hash($self->passphrase);
54 $self
55 }
56
57 sub 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);
65 bless { data => (substr $data, 0, 64), logN => $logN, r => $r, p => $p, salt => $salt, hmac => $hmac }, $class;
66 }
67
68 sub 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
74 sub as_rfc2307 {
75 my ($self) = @_;
76 '{SCRYPT}' . encode_base64 ($self->data . $self->hmac, '')
77 }
78
79 sub from_crypt {
80 croak __PACKAGE__ ." does not support crypt strings, use from_rfc2307 instead";
81 }
82
83 sub as_crypt {
84 croak __PACKAGE__ ." does not support crypt strings, use as_rfc2307 instead";
85 }
86
87 1;
88 __END__
89
90 =encoding utf-8
91
92 =head1 NAME
93
94 Authen::Passphrase::Scrypt - passphrases using Tarsnap's scrypt algorithm
95
96 =head1 SYNOPSIS
97
98 use Authen::Passphrase::Scrypt;
99
100 # Hash a password
101 my $sc = Authen::Passphrase::Scrypt->new({
102 passphrase => 'correcthorsebatterystaple'
103 });
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
114 my $sc = Authen::Passphrase::Scrypt->new({
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
120 });
121 say 'The given password now hashes to ', $sc->as_rfc2307;
122
123 =head1 DESCRIPTION
124
125 B<This is experimental code, DO NOT USE in security-critical software>.
126
127 Scrypt is a key derivation function that was originally developed for
128 use in the Tarsnap online backup system and is designed to be far more
129 secure against hardware brute-force attacks than alternative functions
130 such as PBKDF2 or bcrypt.
131
132 Authen::Passphrase::Scrypt is a module for hashing and verifying
133 passphrases using scrypt. It offers the same interface as
134 L<Authen::Passphrase>. It is not however possible to use this module
135 from within L<Authen::Passphrase>. The methods are:
136
137 =over
138
139 =item Authen::Passphrase::Scrypt->B<new>(I<\%args>)
140
141 Creates a new L<Authen::Passphrase::Scrypt> from a given passphrase
142 and parameters. Use this to hash a passphrase. The arguments are:
143
144 =over
145
146 =item B<passphrase>
147
148 The passphrase. Mandatory.
149
150 =item B<logN>
151
152 The general work factor (affects both CPU and memory cost). Defaults to 14
153
154 =item B<r>
155
156 The blocksize (affects memory cost). Defaults to 16.
157
158 =item B<p>
159
160 The parallelization factor (affects CPU cost). Defaults to 1.
161
162 =item B<salt>
163
164 A 32-byte string used as a salt. By default it is randomly generated
165 using L<Data::Entropy>.
166
167 =back
168
169 All of the parameters change the result of the hash. They are all
170 stored in the hash, so there is no need to store them separately (or
171 provide them to the hash verification methods).
172
173 It is normally sufficient to only use the B<logN> parameter to control
174 the speed of scrypt. B<r> and B<p> are intended to be used only for
175 fine-tuning: if scrypt uses too much memory but not enough CPU,
176 decrease logN and increase p; if scrypt uses too much CPU but not
177 enough memory, decrease logN and increase r.
178
179 =item $sc->B<as_rfc2307>
180
181 Returns the hash of the passphrase, in RFC2307 format. This is
182 "{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>
183
184 =item Authen::Passphrase::Scrypt->B<from_rfc2307>(I<$rfc2307>)
185
186 Creates a new L<Authen::Passphrase::Scrypt> from a hash in RFC2307
187 format. Use this to verify if a passphrase matches a hash.
188
189 =item $sc->B<match>(I<$passphrase>)
190
191 Returns true if the given passphrase matches the hash, false
192 otherwise.
193
194 =item Authen::Passphrase::Scrypt->from_crypt
195 =item $sc->as_crypt
196
197 These functions both croak. They are provided for compatibility with
198 the Authen::Passphrase interface.
199
200 =back
201
202 =head1 SEE ALSO
203
204 L<Authen::Passphrase>,
205 L<https://www.tarsnap.com/scrypt.html>,
206 L<https://www.npmjs.com/package/scrypt>
207
208 =head1 AUTHOR
209
210 Marius Gavrilescu, E<lt>marius@ieval.roE<gt>
211
212 =head1 COPYRIGHT AND LICENSE
213
214 Copyright (C) 2017 by Marius Gavrilescu
215
216 This library is free software; you can redistribute it and/or modify
217 it under the same terms as Perl itself, either Perl version 5.24.1 or,
218 at your option, any later version of Perl 5 you may have available.
219
220
221 =cut
This page took 0.031441 seconds and 4 git commands to generate.