File Coverage

blib/lib/File/KDBX/KDF.pm
Criterion Covered Total %
statement 61 70 87.1
branch 9 14 64.2
condition 7 14 50.0
subroutine 15 19 78.9
pod 8 8 100.0
total 100 125 80.0


line stmt bran cond sub pod time code
1             package File::KDBX::KDF;
2             # ABSTRACT: A key derivation function
3              
4 11     11   178442 use warnings;
  11         20  
  11         340  
5 11     11   58 use strict;
  11         18  
  11         229  
6              
7 11     11   43 use Crypt::PRNG qw(random_bytes);
  11         39  
  11         519  
8 11     11   65 use File::KDBX::Constants qw(:version :kdf);
  11         17  
  11         2676  
9 11     11   70 use File::KDBX::Error;
  11         23  
  11         557  
10 11     11   79 use File::KDBX::Util qw(format_uuid);
  11         16  
  11         432  
11 11     11   87 use Module::Load;
  11         41  
  11         98  
12 11     11   636 use Scalar::Util qw(blessed);
  11         22  
  11         482  
13 11     11   58 use namespace::clean;
  11         17  
  11         76  
14              
15             our $VERSION = '0.906'; # VERSION
16              
17             my %KDFS;
18              
19             our %ROUNDS_INFO = (
20             KDF_UUID_ARGON2D() => {p => KDF_PARAM_ARGON2_ITERATIONS, d => KDF_DEFAULT_ARGON2_ITERATIONS},
21             KDF_UUID_ARGON2ID() => {p => KDF_PARAM_ARGON2_ITERATIONS, d => KDF_DEFAULT_ARGON2_ITERATIONS},
22             );
23             our $DEFAULT_ROUNDS_INFO = {
24             p => KDF_PARAM_AES_ROUNDS,
25             d => KDF_DEFAULT_AES_ROUNDS,
26             };
27              
28              
29             sub new {
30 180     180 1 6078 my $class = shift;
31 180         530 my %args = @_;
32              
33 180 50 66     513 my $uuid = $args{+KDF_PARAM_UUID} //= delete $args{uuid} or throw 'Missing KDF UUID', args => \%args;
34 180         603 my $formatted_uuid = format_uuid($uuid);
35              
36 180 50       473 my $kdf = $KDFS{$uuid} or throw "Unsupported KDF ($formatted_uuid)", uuid => $uuid;
37 180         316 ($class, my %registration_args) = @$kdf;
38              
39 180         529 load $class;
40 180         9628 my $self = bless {KDF_PARAM_UUID() => $uuid}, $class;
41 180         716 return $self->init(%args, %registration_args);
42             }
43              
44              
45             sub init {
46 180     180 1 270 my $self = shift;
47 180         379 my %args = @_;
48              
49 180         614 @$self{keys %args} = values %args;
50              
51 180         1112 return $self;
52             }
53              
54              
55 185     185 1 717 sub uuid { $_[0]->{+KDF_PARAM_UUID} }
56              
57              
58 0     0 1 0 sub seed { die 'Not implemented' }
59              
60              
61             sub transform {
62 51     51 1 2616 my $self = shift;
63 51         79 my $key = shift;
64              
65 51 100 66     348 if (blessed $key && $key->can('raw_key')) {
66 45 100       116 return $self->_transform($key->raw_key) if $self->uuid eq KDF_UUID_AES;
67 21         60 return $self->_transform($key->raw_key($self->seed, @_));
68             }
69              
70 6         15 return $self->_transform($key);
71             }
72              
73 0     0   0 sub _transform { die 'Not implemented' }
74              
75              
76             sub randomize_seed {
77 0     0 1 0 my $self = shift;
78 0         0 $self->{+KDF_PARAM_AES_SEED} = random_bytes(length($self->seed));
79             }
80              
81              
82             sub register {
83 44     44 1 70 my $class = shift;
84 44         54 my $id = shift;
85 44         50 my $package = shift;
86 44         76 my @args = @_;
87              
88 44         106 my $formatted_id = format_uuid($id);
89 44 50 33     384 $package = "${class}::${package}" if $package !~ s/^\+// && $package !~ /^\Q${class}::\E/;
90              
91 44   50     193 my %blacklist = map { File::KDBX::Util::uuid($_) => 1 } split(/,/, $ENV{FILE_KDBX_KDF_BLACKLIST} // '');
  0         0  
92 44 50 33     150 if ($blacklist{$id} || $blacklist{$package}) {
93 0         0 alert "Ignoring blacklisted KDF ($formatted_id)", id => $id, package => $package;
94 0         0 return;
95             }
96              
97 44 50       86 if (defined $KDFS{$id}) {
98 0         0 alert "Overriding already-registered KDF ($formatted_id) with package $package",
99             id => $id,
100             package => $package;
101             }
102              
103 44         449 $KDFS{$id} = [$package, @args];
104             }
105              
106              
107             sub unregister {
108 0     0 1   delete $KDFS{$_} for @_;
109             }
110              
111             BEGIN {
112 11     11   13808 __PACKAGE__->register(KDF_UUID_AES, 'AES');
113 11         48 __PACKAGE__->register(KDF_UUID_AES_CHALLENGE_RESPONSE, 'AES');
114 11         32 __PACKAGE__->register(KDF_UUID_ARGON2D, 'Argon2');
115 11         38 __PACKAGE__->register(KDF_UUID_ARGON2ID, 'Argon2');
116             }
117              
118             1;
119              
120             __END__