| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package File::KDBX::KDF::AES; |
|
2
|
|
|
|
|
|
|
# ABSTRACT: Using the AES cipher as a key derivation function |
|
3
|
|
|
|
|
|
|
|
|
4
|
11
|
|
|
11
|
|
5345
|
use warnings; |
|
|
11
|
|
|
|
|
38
|
|
|
|
11
|
|
|
|
|
336
|
|
|
5
|
11
|
|
|
11
|
|
47
|
use strict; |
|
|
11
|
|
|
|
|
20
|
|
|
|
11
|
|
|
|
|
223
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
11
|
|
|
11
|
|
2890
|
use Crypt::Cipher; |
|
|
11
|
|
|
|
|
1751
|
|
|
|
11
|
|
|
|
|
298
|
|
|
8
|
11
|
|
|
11
|
|
741
|
use Crypt::Digest qw(digest_data); |
|
|
11
|
|
|
|
|
1210
|
|
|
|
11
|
|
|
|
|
522
|
|
|
9
|
11
|
|
|
11
|
|
60
|
use File::KDBX::Constants qw(:bool :kdf); |
|
|
11
|
|
|
|
|
17
|
|
|
|
11
|
|
|
|
|
1923
|
|
|
10
|
11
|
|
|
11
|
|
67
|
use File::KDBX::Error; |
|
|
11
|
|
|
|
|
18
|
|
|
|
11
|
|
|
|
|
462
|
|
|
11
|
11
|
|
|
11
|
|
69
|
use File::KDBX::Util qw(:class :load can_fork); |
|
|
11
|
|
|
|
|
17
|
|
|
|
11
|
|
|
|
|
1296
|
|
|
12
|
11
|
|
|
11
|
|
68
|
use namespace::clean; |
|
|
11
|
|
|
|
|
20
|
|
|
|
11
|
|
|
|
|
72
|
|
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
extends 'File::KDBX::KDF'; |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
our $VERSION = '0.906'; # VERSION |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# Rounds higher than this are eligible for forking: |
|
19
|
|
|
|
|
|
|
my $FORK_OPTIMIZATION_THRESHOLD = 100_000; |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
BEGIN { |
|
22
|
11
|
|
33
|
11
|
|
5567
|
my $use_fork = $ENV{NO_FORK} || !can_fork; |
|
23
|
11
|
50
|
|
|
|
6155
|
*_USE_FORK = $use_fork ? \&TRUE : \&FALSE; |
|
24
|
|
|
|
|
|
|
} |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
|
27
|
42
|
50
|
|
42
|
1
|
119
|
sub rounds { $_[0]->{+KDF_PARAM_AES_ROUNDS} || KDF_DEFAULT_AES_ROUNDS } |
|
28
|
56
|
|
|
56
|
1
|
125
|
sub seed { $_[0]->{+KDF_PARAM_AES_SEED} } |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
sub init { |
|
31
|
161
|
|
|
161
|
1
|
259
|
my $self = shift; |
|
32
|
161
|
|
|
|
|
425
|
my %args = @_; |
|
33
|
|
|
|
|
|
|
return $self->SUPER::init( |
|
34
|
|
|
|
|
|
|
KDF_PARAM_AES_ROUNDS() => $args{+KDF_PARAM_AES_ROUNDS} // $args{rounds}, |
|
35
|
|
|
|
|
|
|
KDF_PARAM_AES_SEED() => $args{+KDF_PARAM_AES_SEED} // $args{seed}, |
|
36
|
161
|
|
66
|
|
|
833
|
); |
|
|
|
|
66
|
|
|
|
|
|
37
|
|
|
|
|
|
|
} |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
sub _transform { |
|
40
|
42
|
|
|
42
|
|
87
|
my $self = shift; |
|
41
|
42
|
|
|
|
|
66
|
my $key = shift; |
|
42
|
|
|
|
|
|
|
|
|
43
|
42
|
|
|
|
|
111
|
my $seed = $self->seed; |
|
44
|
42
|
|
|
|
|
111
|
my $rounds = $self->rounds; |
|
45
|
|
|
|
|
|
|
|
|
46
|
42
|
100
|
|
|
|
110
|
length($key) == 32 or throw 'Raw key must be 32 bytes', size => length($key); |
|
47
|
40
|
50
|
|
|
|
96
|
length($seed) == 32 or throw 'Invalid seed length', size => length($seed); |
|
48
|
|
|
|
|
|
|
|
|
49
|
40
|
|
|
|
|
182
|
my ($key_l, $key_r) = unpack('(a16)2', $key); |
|
50
|
|
|
|
|
|
|
|
|
51
|
40
|
|
|
|
|
111
|
goto NO_FORK if !_USE_FORK || $rounds < $FORK_OPTIMIZATION_THRESHOLD; |
|
52
|
|
|
|
|
|
|
{ |
|
53
|
0
|
|
0
|
|
|
0
|
my $pid = open(my $read, '-|') // do { alert "fork failed: $!"; goto NO_FORK }; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
54
|
0
|
0
|
|
|
|
0
|
if ($pid == 0) { # child |
|
55
|
0
|
|
|
|
|
0
|
my $l = _transform_half($seed, $key_l, $rounds); |
|
56
|
0
|
|
|
|
|
0
|
require POSIX; |
|
57
|
0
|
0
|
|
|
|
0
|
print $l or POSIX::_exit(1); |
|
58
|
0
|
|
|
|
|
0
|
POSIX::_exit(0); |
|
59
|
|
|
|
|
|
|
} |
|
60
|
0
|
|
|
|
|
0
|
my $r = _transform_half($seed, $key_r, $rounds); |
|
61
|
0
|
0
|
|
|
|
0
|
read($read, my $l, length($key_l)) == length($key_l) or do { alert "read failed: $!", goto NO_FORK }; |
|
|
0
|
|
|
|
|
0
|
|
|
62
|
0
|
0
|
|
|
|
0
|
close($read) or do { alert "worker thread exited abnormally", status => $?; goto NO_FORK }; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
63
|
0
|
|
|
|
|
0
|
return digest_data('SHA256', $l, $r); |
|
64
|
|
|
|
|
|
|
} |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
# FIXME: This used to work but now it crashes frequently. Threads are now discouraged anyway, but it might |
|
67
|
|
|
|
|
|
|
# be nice if this was available for no-fork platforms. |
|
68
|
|
|
|
|
|
|
# if ($ENV{THREADS} && eval 'use threads; 1') { |
|
69
|
|
|
|
|
|
|
# my $l = threads->create(\&_transform_half, $key_l, $seed, $rounds); |
|
70
|
|
|
|
|
|
|
# my $r = _transform_half($key_r, $seed, $rounds); |
|
71
|
|
|
|
|
|
|
# return digest_data('SHA256', $l->join, $r); |
|
72
|
|
|
|
|
|
|
# } |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
NO_FORK: |
|
75
|
40
|
|
|
|
|
89685
|
my $l = _transform_half($seed, $key_l, $rounds); |
|
76
|
40
|
|
|
|
|
89623
|
my $r = _transform_half($seed, $key_r, $rounds); |
|
77
|
40
|
|
|
|
|
386
|
return digest_data('SHA256', $l, $r); |
|
78
|
|
|
|
|
|
|
} |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
sub _transform_half_pp { |
|
81
|
2
|
|
|
2
|
|
2
|
my $seed = shift; |
|
82
|
2
|
|
|
|
|
3
|
my $key = shift; |
|
83
|
2
|
|
|
|
|
2
|
my $rounds = shift; |
|
84
|
|
|
|
|
|
|
|
|
85
|
2
|
|
|
|
|
15
|
my $c = Crypt::Cipher->new('AES', $seed); |
|
86
|
|
|
|
|
|
|
|
|
87
|
2
|
|
|
|
|
3
|
my $result = $key; |
|
88
|
2
|
|
|
|
|
4
|
for (my $i = 0; $i < $rounds; ++$i) { |
|
89
|
20
|
|
|
|
|
58
|
$result = $c->encrypt($result); |
|
90
|
|
|
|
|
|
|
} |
|
91
|
|
|
|
|
|
|
|
|
92
|
2
|
|
|
|
|
7
|
return $result; |
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
BEGIN { |
|
96
|
11
|
|
|
11
|
|
66
|
my $use_xs = load_xs; |
|
97
|
11
|
100
|
|
|
|
327
|
*_transform_half = $use_xs ? \&File::KDBX::XS::kdf_aes_transform_half : \&_transform_half_pp; |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
1; |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
__END__ |