1 package File
::KDBX
::KDF
;
2 # ABSTRACT: A key derivation function
7 use Crypt
::PRNG
qw(random_bytes);
8 use File
::KDBX
::Constants
qw(:version :kdf);
10 use File
::KDBX
::Util
qw(format_uuid);
12 use Scalar
::Util
qw(blessed);
15 our $VERSION = '999.999'; # VERSION
21 $kdf = File
::KDBX
::KDF-
>new(parameters
=> \
%params);
31 my $uuid = $args{+KDF_PARAM_UUID
} //= delete $args{uuid
} or throw
'Missing KDF UUID', args
=> \
%args;
32 my $formatted_uuid = format_uuid
($uuid);
34 my $kdf = $KDFS{$uuid} or throw
"Unsupported KDF ($formatted_uuid)", uuid
=> $uuid;
35 ($class, my %registration_args) = @$kdf;
38 my $self = bless {KDF_PARAM_UUID
() => $uuid}, $class;
39 return $self->init(%args, %registration_args);
46 @$self{keys %args} = values %args;
55 Get the UUID used to determine which function to
use.
59 sub uuid
{ $_[0]->{+KDF_PARAM_UUID
} }
65 Get the seed
(or salt
, depending on the function
).
69 sub seed
{ die "Not implemented" }
73 $transformed_key = $kdf->transform($key);
74 $transformed_key = $kdf->transform($key, $challenge);
76 Transform a key
. The input key can be either a L
<File
::KDBX
::Key
> or a raw binary key
, and the
77 transformed key will be a raw key
.
79 This can
take awhile
, depending on the KDF parameters
.
81 If a challenge
is provided
(and the KDF
is AES except
for the KeePassXC variant
), it will be passed to the key
82 so challenge-response
keys can produce raw
keys. See L
<File
::KDBX
::Key
/raw_key
>.
90 if (blessed
$key && $key->can('raw_key')) {
91 return $self->_transform($key->raw_key) if $self->uuid eq KDF_UUID_AES
;
92 return $self->_transform($key->raw_key($self->seed, @_));
95 return $self->_transform($key);
98 sub _transform
{ die "Not implemented" }
100 =method randomize_seed
102 $kdf->randomize_seed;
104 Generate a new random seed
/salt
.
110 $self->{+KDF_PARAM_AES_SEED
} = random_bytes
(length($self->seed));
115 File
::KDBX
::KDF-
>register($uuid => $package, %args);
117 Register a KDF
. Registered KDFs can be used to encrypt
and decrypt KDBX databases
. A KDF
's UUID B<must> be
118 unique and B<musn't change
>. A KDF UUID
is written into
each KDBX file
and the associated KDF must be
119 registered with the same UUID
in order to decrypt the KDBX file
.
121 C
<$package> should be a Perl
package relative to C
<File
::KDBX
::KDF
::> or prefixed with a C
<+> if it
is
122 a fully-qualified
package. C
<%args> are passed as-is to the KDF
's L</init> method.
132 my $formatted_id = format_uuid($id);
133 $package = "${class}::${package}" if $package !~ s/^\+// && $package !~ /^\Q${class}::\E/;
135 my %blacklist = map { File::KDBX::Util::uuid($_) => 1 } split(/,/, $ENV{FILE_KDBX_KDF_BLACKLIST} // '');
136 if ($blacklist{$id} || $blacklist{$package}) {
137 alert "Ignoring blacklisted KDF ($formatted_id)", id => $id, package => $package;
141 if (defined $KDFS{$id}) {
142 alert "Overriding already-registered KDF ($formatted_id) with package $package",
147 $KDFS{$id} = [$package, @args];
152 File::KDBX::KDF->unregister($uuid);
154 Unregister a KDF. Unregistered KDFs can no longer be used to encrypt and decrypt KDBX databases, until
155 reregistered (see L</register>).
160 delete $KDFS{$_} for @_;
164 __PACKAGE__->register(KDF_UUID_AES, 'AES
');
165 __PACKAGE__->register(KDF_UUID_AES_CHALLENGE_RESPONSE, 'AES
');
166 __PACKAGE__->register(KDF_UUID_ARGON2D, 'Argon2
');
167 __PACKAGE__->register(KDF_UUID_ARGON2ID, 'Argon2
');
175 A KDF (key derivation function) is used in the transformation of a master key (i.e. one or more component
176 keys) to produce the final encryption key protecting a KDBX database. The L<File::KDBX> distribution comes
177 with several pre-registered KDFs ready to go:
180 * C<C9D9F39A-628A-4460-BF74-0D08C18A4FEA> - AES
181 * C<7C02BB82-79A7-4AC0-927D-114A00648238> - AES (challenge-response variant)
182 * C<EF636DDF-8C29-444B-91F7-A9A403E30A0C> - Argon2d
183 * C<9E298B19-56DB-4773-B23D-FC3EC6F0A1E6> - Argon2id
185 B<NOTE:> If you want your KDBX file to be readable by other KeePass implementations, you must use a UUID and
186 algorithm that they support. From the list above, all are well-supported except the AES challenge-response
187 variant which is kind of a pseudo KDF and isn't usually written into files
. All of these are good
. AES
has
188 a longer track record
, but Argon2
has better ASIC resistance
.
190 You can also L
</register
> your own KDF
. Here
is a skeleton
:
192 package File
::KDBX
::KDF
::MyKDF
;
194 use parent
'File::KDBX::KDF';
196 File
::KDBX
::KDF-
>register(
197 # $uuid, $package, %args
198 "\x12\x34\x56\x78\x9a\xbc\xde\xfg\x12\x34\x56\x78\x9a\xbc\xde\xfg" => __PACKAGE__
,
201 sub init
{ ... } # optional
203 sub _transform
{ my ($key) = @_; ... }