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   182147 use warnings;
  11         23  
  11         377  
5 11     11   51 use strict;
  11         19  
  11         1106  
6              
7 11     11   57 use Crypt::PRNG qw(random_bytes);
  11         46  
  11         540  
8 11     11   57 use File::KDBX::Constants qw(:version :kdf);
  11         25  
  11         2449  
9 11     11   72 use File::KDBX::Error;
  11         19  
  11         528  
10 11     11   78 use File::KDBX::Util qw(format_uuid);
  11         27  
  11         481  
11 11     11   61 use Module::Load;
  11         34  
  11         82  
12 11     11   611 use Scalar::Util qw(blessed);
  11         21  
  11         422  
13 11     11   54 use namespace::clean;
  11         23  
  11         81  
14              
15             our $VERSION = '0.904'; # VERSION
16              
17             my %KDFS;
18              
19              
20             sub new {
21 177     177 1 6830 my $class = shift;
22 177         575 my %args = @_;
23              
24 177 50 66     916 my $uuid = $args{+KDF_PARAM_UUID} //= delete $args{uuid} or throw 'Missing KDF UUID', args => \%args;
25 177         520 my $formatted_uuid = format_uuid($uuid);
26              
27 177 50       625 my $kdf = $KDFS{$uuid} or throw "Unsupported KDF ($formatted_uuid)", uuid => $uuid;
28 177         410 ($class, my %registration_args) = @$kdf;
29              
30 177         736 load $class;
31 177         11159 my $self = bless {KDF_PARAM_UUID() => $uuid}, $class;
32 177         826 return $self->init(%args, %registration_args);
33             }
34              
35              
36             sub init {
37 177     177 1 393 my $self = shift;
38 177         474 my %args = @_;
39              
40 177         611 @$self{keys %args} = values %args;
41              
42 177         1180 return $self;
43             }
44              
45              
46 182     182 1 844 sub uuid { $_[0]->{+KDF_PARAM_UUID} }
47              
48              
49 0     0 1 0 sub seed { die 'Not implemented' }
50              
51              
52             sub transform {
53 51     51 1 2992 my $self = shift;
54 51         97 my $key = shift;
55              
56 51 100 66     471 if (blessed $key && $key->can('raw_key')) {
57 45 100       135 return $self->_transform($key->raw_key) if $self->uuid eq KDF_UUID_AES;
58 21         97 return $self->_transform($key->raw_key($self->seed, @_));
59             }
60              
61 6         19 return $self->_transform($key);
62             }
63              
64 0     0   0 sub _transform { die 'Not implemented' }
65              
66              
67             sub randomize_seed {
68 0     0 1 0 my $self = shift;
69 0         0 $self->{+KDF_PARAM_AES_SEED} = random_bytes(length($self->seed));
70             }
71              
72              
73             sub register {
74 44     44 1 81 my $class = shift;
75 44         59 my $id = shift;
76 44         51 my $package = shift;
77 44         63 my @args = @_;
78              
79 44         118 my $formatted_id = format_uuid($id);
80 44 50 33     409 $package = "${class}::${package}" if $package !~ s/^\+// && $package !~ /^\Q${class}::\E/;
81              
82 44   50     230 my %blacklist = map { File::KDBX::Util::uuid($_) => 1 } split(/,/, $ENV{FILE_KDBX_KDF_BLACKLIST} // '');
  0         0  
83 44 50 33     154 if ($blacklist{$id} || $blacklist{$package}) {
84 0         0 alert "Ignoring blacklisted KDF ($formatted_id)", id => $id, package => $package;
85 0         0 return;
86             }
87              
88 44 50       95 if (defined $KDFS{$id}) {
89 0         0 alert "Overriding already-registered KDF ($formatted_id) with package $package",
90             id => $id,
91             package => $package;
92             }
93              
94 44         425 $KDFS{$id} = [$package, @args];
95             }
96              
97              
98             sub unregister {
99 0     0 1   delete $KDFS{$_} for @_;
100             }
101              
102             BEGIN {
103 11     11   12596 __PACKAGE__->register(KDF_UUID_AES, 'AES');
104 11         45 __PACKAGE__->register(KDF_UUID_AES_CHALLENGE_RESPONSE, 'AES');
105 11         42 __PACKAGE__->register(KDF_UUID_ARGON2D, 'Argon2');
106 11         33 __PACKAGE__->register(KDF_UUID_ARGON2ID, 'Argon2');
107             }
108              
109             1;
110              
111             __END__