line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Crypt::ScryptKDF; |
2
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
24460
|
use strict; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
147
|
|
4
|
4
|
|
|
4
|
|
19
|
use warnings ; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
274
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = '0.009'; |
7
|
|
|
|
|
|
|
|
8
|
4
|
|
|
4
|
|
2248
|
use MIME::Base64 qw(decode_base64 encode_base64); |
|
4
|
|
|
|
|
2591
|
|
|
4
|
|
|
|
|
309
|
|
9
|
4
|
|
|
4
|
|
27
|
use Exporter 'import'; |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
4502
|
|
10
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( all => [qw(scrypt_raw scrypt_hex scrypt_b64 scrypt_hash scrypt_hash_verify random_bytes)] ); |
11
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); |
12
|
|
|
|
|
|
|
our @EXPORT = qw(); |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
require XSLoader; |
15
|
|
|
|
|
|
|
XSLoader::load('Crypt::ScryptKDF', $VERSION); |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
sub random_bytes { |
18
|
7
|
|
50
|
7
|
1
|
24
|
my $length = shift || 32; |
19
|
7
|
|
|
|
|
10
|
my $rv; |
20
|
|
|
|
|
|
|
|
21
|
7
|
50
|
|
|
|
10
|
if (eval {require Crypt::PRNG}) { |
|
7
|
50
|
|
|
|
1540
|
|
|
7
|
50
|
|
|
|
1045
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
22
|
0
|
|
|
|
|
0
|
$rv = Crypt::PRNG::random_bytes($length); |
23
|
|
|
|
|
|
|
} |
24
|
7
|
|
|
|
|
5550
|
elsif (eval {require Crypt::OpenSSL::Random}) { |
25
|
0
|
0
|
|
|
|
0
|
if (Crypt::OpenSSL::Random::random_status()) { |
26
|
0
|
|
|
|
|
0
|
$rv = Crypt::OpenSSL::Random::random_bytes($length); |
27
|
|
|
|
|
|
|
} |
28
|
|
|
|
|
|
|
} |
29
|
0
|
|
|
|
|
0
|
elsif (eval {require Net::SSLeay}) { |
30
|
7
|
50
|
|
|
|
15297
|
if (Net::SSLeay::RAND_status() == 1) { |
31
|
7
|
50
|
|
|
|
172
|
if (Net::SSLeay::RAND_bytes($rv, $length) != 1) { |
32
|
0
|
|
|
|
|
0
|
$rv = undef; |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
} |
36
|
0
|
|
|
|
|
0
|
elsif (eval {require Crypt::Random}) { |
37
|
0
|
|
|
|
|
0
|
$rv = Crypt::Random::makerandom_octet(Length=>$length); |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
elsif (eval {require Bytes::Random::Secure}) { |
40
|
0
|
|
|
|
|
0
|
$rv = Bytes::Random::Secure::random_bytes(32); |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
7
|
50
|
|
|
|
45
|
if (!defined $rv) { |
44
|
0
|
|
|
|
|
0
|
warn "WARNING: Generating random bytes via insecure rand()\n"; |
45
|
0
|
|
|
|
|
0
|
$rv = pack('C*', map(int(rand(256)), 1..$length)); |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
|
48
|
7
|
|
|
|
|
24
|
return $rv |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub scrypt_raw { |
52
|
2
|
50
|
0
|
2
|
1
|
15
|
warn "scrypt_raw: 2 or 6 parameters required\n" and return undef unless @_ == 2 || @_ == 6; |
|
|
|
66
|
|
|
|
|
53
|
2
|
|
|
|
|
7
|
my $key = _scrypt_extra(@_); |
54
|
2
|
|
|
|
|
18
|
return $key; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
sub scrypt_b64 { |
58
|
3
|
50
|
0
|
3
|
1
|
28
|
warn "scrypt_b64: 2 or 6 parameters required\n" and return undef unless @_ == 2 || @_ == 6; |
|
|
|
66
|
|
|
|
|
59
|
3
|
|
|
|
|
10
|
my $key = _scrypt_extra(@_); |
60
|
3
|
50
|
|
|
|
12
|
return undef unless defined $key; |
61
|
3
|
|
|
|
|
35
|
return encode_base64($key, ''); |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
sub scrypt_hex { |
65
|
5
|
50
|
0
|
5
|
1
|
46
|
warn "scrypt_hex: 2 or 6 parameters required\n" and return undef unless @_ == 2 || @_ == 6; |
|
|
|
66
|
|
|
|
|
66
|
5
|
|
|
|
|
18
|
my $key = _scrypt_extra(@_); |
67
|
5
|
50
|
|
|
|
24
|
return undef unless defined $key; |
68
|
5
|
|
|
|
|
60
|
return unpack("H*", $key); |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub scrypt_hash { |
72
|
9
|
|
|
9
|
1
|
34
|
my ($key, $salt, $N, $r, $p) = _scrypt_extra(@_); |
73
|
9
|
50
|
33
|
|
|
154
|
return undef unless defined $key && defined $salt && defined $N && defined $r && defined $p; |
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
74
|
9
|
|
|
|
|
185
|
return "SCRYPT:$N:$r:$p:" . MIME::Base64::encode($salt, "") . ":" . MIME::Base64::encode($key, ""); |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
sub scrypt_hash_verify { |
78
|
1
|
|
|
1
|
1
|
7
|
my ($passwd, $string) = @_; |
79
|
1
|
50
|
|
|
|
5
|
return 0 unless $string; |
80
|
1
|
50
|
|
|
|
4
|
return 0 unless defined $passwd; |
81
|
1
|
|
|
|
|
15
|
my ($alg, $N, $r, $p, $salt, $hash) = ($string =~ /^(SCRYPT):(\d+):(\d+):(\d+):([^\:]+):([^\:]+)$/); |
82
|
1
|
50
|
33
|
|
|
6
|
return 0 unless defined $salt && defined $hash; |
83
|
1
|
|
|
|
|
4
|
$salt = MIME::Base64::decode($salt); |
84
|
1
|
|
|
|
|
2
|
$hash = MIME::Base64::decode($hash); |
85
|
1
|
50
|
33
|
|
|
5
|
return 0 unless defined $salt && defined $hash; |
86
|
1
|
50
|
|
|
|
3
|
return 0 unless length($hash) > 0; |
87
|
1
|
50
|
33
|
|
|
10
|
return 0 unless $N > 0 && $r >= 0 && $p >= 0; |
|
|
|
33
|
|
|
|
|
88
|
|
|
|
|
|
|
#XXX-TODO utf8::encode($passwd) if utf8::is_utf8($passwd); |
89
|
1
|
|
|
|
|
119978
|
my $key = _scrypt($passwd, $salt, $N, $r, $p, length($hash)); |
90
|
1
|
50
|
|
|
|
9
|
return 0 unless defined $key; |
91
|
1
|
50
|
|
|
|
4
|
return 0 unless _slow_eq($key, $hash); |
92
|
1
|
|
|
|
|
7
|
return 1; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
sub _get_scrypt_defaults { |
96
|
|
|
|
|
|
|
# (N=2^14, r=8, p=1, len=32) |
97
|
10
|
|
|
10
|
|
39
|
return (16384, 8, 1, 32); |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub _scrypt_extra { |
101
|
19
|
|
|
19
|
|
66
|
my $salt; |
102
|
|
|
|
|
|
|
my @args; |
103
|
19
|
100
|
|
|
|
101
|
if (@_ == 1) { # ... ($passwd) |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
104
|
2
|
|
|
|
|
6
|
($salt, @args) = (random_bytes(32), _get_scrypt_defaults); |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
elsif (@_ == 2) { # ... ($passwd, $salt) |
107
|
8
|
|
|
|
|
23
|
($salt, @args) = ($_[1], _get_scrypt_defaults); |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
elsif (@_ == 5) { # ... ($passwd, $N, $r, $p, $dklen) |
110
|
1
|
|
|
|
|
494
|
($salt, @args) = (random_bytes(32), $_[1], $_[2], $_[3], $_[4]); |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
elsif (@_ == 6) { # ... ($passwd, $salt, $N, $r, $p, $dklen) |
113
|
8
|
|
|
|
|
28
|
(undef, $salt, @args) = @_; |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
else { |
116
|
0
|
|
|
|
|
0
|
warn "ERROR: scrypt() invalid number of arguments\n"; |
117
|
0
|
|
|
|
|
0
|
return; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
#check @args |
120
|
19
|
|
|
|
|
37
|
my $N = $args[0]; |
121
|
19
|
50
|
33
|
|
|
173
|
if ( ($N <= 0) || (($N&($N-1)) != 0) ) { warn "ERROR: invalid 'N'\n"; return } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
122
|
19
|
50
|
|
|
|
45
|
if ($args[1] < 1) { warn "ERROR: invalid 'r'\n"; return } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
123
|
19
|
50
|
|
|
|
49
|
if ($args[2] < 1) { warn "ERROR: invalid 'p'\n"; return } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
124
|
19
|
50
|
|
|
|
48
|
if ($args[3] < 1) { warn "ERROR: invalid 'len'\n"; return } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
125
|
|
|
|
|
|
|
#XXX-TODO utf8::encode($_[0]) if utf8::is_utf8($_[0]); |
126
|
19
|
100
|
66
|
|
|
119
|
$salt = random_bytes($$salt) if ref $salt eq 'SCALAR' && $$salt =~ /^\d+$/; |
127
|
19
|
|
|
|
|
1553108
|
my $key = _scrypt($_[0], $salt, @args); |
128
|
19
|
100
|
|
|
|
175
|
return wantarray ? ($key, $salt, $args[0], $args[1], $args[2]) : $key; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
sub _slow_eq { |
132
|
19
|
|
|
19
|
|
39
|
my ($a, $b) = @_; |
133
|
19
|
100
|
100
|
|
|
93
|
return unless defined $a && defined $b; |
134
|
14
|
|
|
|
|
20
|
my $diff = length $a ^ length $b; |
135
|
14
|
|
100
|
|
|
56
|
for(my $i = 0; $i < length $a && $i < length $b; $i++) { |
136
|
125
|
|
|
|
|
360
|
$diff |= ord(substr $a, $i) ^ ord(substr $b, $i); |
137
|
|
|
|
|
|
|
} |
138
|
14
|
|
|
|
|
48
|
return $diff == 0; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
1; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
__END__ |