]> Dogcows Code - chaz/p5-File-KDBX/blob - lib/File/KDBX/KDF.pm
Version 0.900
[chaz/p5-File-KDBX] / lib / File / KDBX / KDF.pm
1 package File::KDBX::KDF;
2 # ABSTRACT: A key derivation function
3
4 use warnings;
5 use strict;
6
7 use Crypt::PRNG qw(random_bytes);
8 use File::KDBX::Constants qw(:version :kdf);
9 use File::KDBX::Error;
10 use File::KDBX::Util qw(format_uuid);
11 use Module::Load;
12 use Scalar::Util qw(blessed);
13 use namespace::clean;
14
15 our $VERSION = '0.900'; # VERSION
16
17 my %KDFS;
18
19
20 sub new {
21 my $class = shift;
22 my %args = @_;
23
24 my $uuid = $args{+KDF_PARAM_UUID} //= delete $args{uuid} or throw 'Missing KDF UUID', args => \%args;
25 my $formatted_uuid = format_uuid($uuid);
26
27 my $kdf = $KDFS{$uuid} or throw "Unsupported KDF ($formatted_uuid)", uuid => $uuid;
28 ($class, my %registration_args) = @$kdf;
29
30 load $class;
31 my $self = bless {KDF_PARAM_UUID() => $uuid}, $class;
32 return $self->init(%args, %registration_args);
33 }
34
35
36 sub init {
37 my $self = shift;
38 my %args = @_;
39
40 @$self{keys %args} = values %args;
41
42 return $self;
43 }
44
45
46 sub uuid { $_[0]->{+KDF_PARAM_UUID} }
47
48
49 sub seed { die 'Not implemented' }
50
51
52 sub transform {
53 my $self = shift;
54 my $key = shift;
55
56 if (blessed $key && $key->can('raw_key')) {
57 return $self->_transform($key->raw_key) if $self->uuid eq KDF_UUID_AES;
58 return $self->_transform($key->raw_key($self->seed, @_));
59 }
60
61 return $self->_transform($key);
62 }
63
64 sub _transform { die 'Not implemented' }
65
66
67 sub randomize_seed {
68 my $self = shift;
69 $self->{+KDF_PARAM_AES_SEED} = random_bytes(length($self->seed));
70 }
71
72
73 sub register {
74 my $class = shift;
75 my $id = shift;
76 my $package = shift;
77 my @args = @_;
78
79 my $formatted_id = format_uuid($id);
80 $package = "${class}::${package}" if $package !~ s/^\+// && $package !~ /^\Q${class}::\E/;
81
82 my %blacklist = map { File::KDBX::Util::uuid($_) => 1 } split(/,/, $ENV{FILE_KDBX_KDF_BLACKLIST} // '');
83 if ($blacklist{$id} || $blacklist{$package}) {
84 alert "Ignoring blacklisted KDF ($formatted_id)", id => $id, package => $package;
85 return;
86 }
87
88 if (defined $KDFS{$id}) {
89 alert "Overriding already-registered KDF ($formatted_id) with package $package",
90 id => $id,
91 package => $package;
92 }
93
94 $KDFS{$id} = [$package, @args];
95 }
96
97
98 sub unregister {
99 delete $KDFS{$_} for @_;
100 }
101
102 BEGIN {
103 __PACKAGE__->register(KDF_UUID_AES, 'AES');
104 __PACKAGE__->register(KDF_UUID_AES_CHALLENGE_RESPONSE, 'AES');
105 __PACKAGE__->register(KDF_UUID_ARGON2D, 'Argon2');
106 __PACKAGE__->register(KDF_UUID_ARGON2ID, 'Argon2');
107 }
108
109 1;
110
111 __END__
112
113 =pod
114
115 =encoding UTF-8
116
117 =head1 NAME
118
119 File::KDBX::KDF - A key derivation function
120
121 =head1 VERSION
122
123 version 0.900
124
125 =head1 DESCRIPTION
126
127 A KDF (key derivation function) is used in the transformation of a master key (i.e. one or more component
128 keys) to produce the final encryption key protecting a KDBX database. The L<File::KDBX> distribution comes
129 with several pre-registered KDFs ready to go:
130
131 =over 4
132
133 =item *
134
135 C<C9D9F39A-628A-4460-BF74-0D08C18A4FEA> - AES
136
137 =item *
138
139 C<7C02BB82-79A7-4AC0-927D-114A00648238> - AES (challenge-response variant)
140
141 =item *
142
143 C<EF636DDF-8C29-444B-91F7-A9A403E30A0C> - Argon2d
144
145 =item *
146
147 C<9E298B19-56DB-4773-B23D-FC3EC6F0A1E6> - Argon2id
148
149 =back
150
151 B<NOTE:> If you want your KDBX file to be readable by other KeePass implementations, you must use a UUID and
152 algorithm that they support. From the list above, all are well-supported except the AES challenge-response
153 variant which is kind of a pseudo KDF and isn't usually written into files. All of these are good. AES has
154 a longer track record, but Argon2 has better ASIC resistance.
155
156 You can also L</register> your own KDF. Here is a skeleton:
157
158 package File::KDBX::KDF::MyKDF;
159
160 use parent 'File::KDBX::KDF';
161
162 File::KDBX::KDF->register(
163 # $uuid, $package, %args
164 "\x12\x34\x56\x78\x9a\xbc\xde\xfg\x12\x34\x56\x78\x9a\xbc\xde\xfg" => __PACKAGE__,
165 );
166
167 sub init { ... } # optional
168
169 sub _transform { my ($key) = @_; ... }
170
171 =head1 ATTRIBUTES
172
173 =head2 uuid
174
175 $uuid => $kdf->uuid;
176
177 Get the UUID used to determine which function to use.
178
179 =head2 seed
180
181 $seed = $kdf->seed;
182
183 Get the seed (or salt, depending on the function).
184
185 =head1 METHODS
186
187 =head2 new
188
189 $kdf = File::KDBX::KDF->new(parameters => \%params);
190
191 Construct a new KDF.
192
193 =head2 init
194
195 $kdf = $kdf->init(%attributes);
196
197 Called by method to set attributes. You normally shouldn't call this.
198
199 =head2 transform
200
201 $transformed_key = $kdf->transform($key);
202 $transformed_key = $kdf->transform($key, $challenge);
203
204 Transform a key. The input key can be either a L<File::KDBX::Key> or a raw binary key, and the
205 transformed key will be a raw key.
206
207 This can take awhile, depending on the KDF parameters.
208
209 If a challenge is provided (and the KDF is AES except for the KeePassXC variant), it will be passed to the key
210 so challenge-response keys can produce raw keys. See L<File::KDBX::Key/raw_key>.
211
212 =head2 randomize_seed
213
214 $kdf->randomize_seed;
215
216 Generate a new random seed/salt.
217
218 =head2 register
219
220 File::KDBX::KDF->register($uuid => $package, %args);
221
222 Register a KDF. Registered KDFs can be used to encrypt and decrypt KDBX databases. A KDF's UUID B<must> be
223 unique and B<musn't change>. A KDF UUID is written into each KDBX file and the associated KDF must be
224 registered with the same UUID in order to decrypt the KDBX file.
225
226 C<$package> should be a Perl package relative to C<File::KDBX::KDF::> or prefixed with a C<+> if it is
227 a fully-qualified package. C<%args> are passed as-is to the KDF's L</init> method.
228
229 =head2 unregister
230
231 File::KDBX::KDF->unregister($uuid);
232
233 Unregister a KDF. Unregistered KDFs can no longer be used to encrypt and decrypt KDBX databases, until
234 reregistered (see L</register>).
235
236 =head1 BUGS
237
238 Please report any bugs or feature requests on the bugtracker website
239 L<https://github.com/chazmcgarvey/File-KDBX/issues>
240
241 When submitting a bug or request, please include a test-file or a
242 patch to an existing test-file that illustrates the bug or desired
243 feature.
244
245 =head1 AUTHOR
246
247 Charles McGarvey <ccm@cpan.org>
248
249 =head1 COPYRIGHT AND LICENSE
250
251 This software is copyright (c) 2022 by Charles McGarvey.
252
253 This is free software; you can redistribute it and/or modify it under
254 the same terms as the Perl 5 programming language system itself.
255
256 =cut
This page took 0.053538 seconds and 4 git commands to generate.