1 package File
::KDBX
::Cipher
;
2 # ABSTRACT: A block cipher mode or cipher stream
7 use Devel
::GlobalDestruction
;
8 use File
::KDBX
::Constants
qw(:cipher :random_stream);
10 use File
::KDBX
::Util
qw(:class erase format_uuid);
12 use Scalar
::Util
qw(looks_like_number);
15 our $VERSION = '999.999'; # VERSION
21 $uuid = $cipher->uuid;
23 Get the UUID
if the cipher was constructed with one
.
27 $stream_id = $cipher->stream_id;
29 Get the stream ID
if the cipher was constructed with one
.
35 Get the raw encryption key
.
41 Get the initialization vector
.
45 $size = $cipher->iv_size;
47 Get the expected size of the initialization vector
, in bytes
.
51 $size = $cipher->key_size;
53 Get the size the mode
or stream expects the key to be
, in bytes
.
57 $size = $cipher->block_size;
59 Get the block size
, in bytes
.
63 Get the symmetric cipher algorithm
.
67 has 'uuid', is => 'ro';
68 has 'stream_id', is => 'ro';
69 has 'key', is => 'ro';
74 sub algorithm
{ $_[0]->{algorithm
} or throw
'Block cipher algorithm is not set' }
80 =method new_from_stream_id
82 $cipher = File
::KDBX
::Cipher-
>new(uuid
=> $uuid, key
=> $key, iv
=> $iv);
84 $cipher = File
::KDBX
::Cipher-
>new_from_uuid($uuid, key
=> $key, iv
=> $iv);
86 $cipher = File
::KDBX
::Cipher-
>new(stream_id
=> $id, key
=> $key);
88 $cipher = File
::KDBX
::Cipher-
>new_from_stream_id($id, key
=> $key);
90 Construct a new L
<File
::KDBX
::Cipher
>.
92 This
is a factory
method which returns a subclass
.
100 return $class->new_from_uuid(delete $args{uuid
}, %args) if defined $args{uuid
};
101 return $class->new_from_stream_id(delete $args{stream_id
}, %args) if defined $args{stream_id
};
103 throw
'Must pass uuid or stream_id';
111 $args{key
} or throw
'Missing encryption key';
112 $args{iv
} or throw
'Missing encryption IV';
114 my $formatted_uuid = format_uuid
($uuid);
116 my $cipher = $CIPHERS{$uuid} or throw
"Unsupported cipher ($formatted_uuid)", uuid
=> $uuid;
117 ($class, my %registration_args) = @$cipher;
119 my @args = (%args, %registration_args, uuid
=> $uuid);
121 my $self = bless {@args}, $class;
122 return $self->init(@args);
125 sub new_from_stream_id
{
130 $args{key
} or throw
'Missing encryption key';
132 my $cipher = $CIPHERS{$id} or throw
"Unsupported stream cipher ($id)", id
=> $id;
133 ($class, my %registration_args) = @$cipher;
135 my @args = (%args, %registration_args, stream_id
=> $id);
137 my $self = bless {@args}, $class;
138 return $self->init(@args);
145 Initialize the cipher
. Called by
</new
>.
151 sub DESTROY
{ !in_global_destruction
and erase \
$_[0]->{key
} }
155 $ciphertext = $cipher->encrypt($plaintext, ...);
161 sub encrypt
{ die 'Not implemented' }
165 $plaintext = $cipher->decrypt($ciphertext, ...);
171 sub decrypt
{ die 'Not implemented' }
175 $ciphertext .= $cipher->finish; # if encrypting
176 $plaintext .= $cipher->finish; # if decrypting
184 =method encrypt_finish
186 $ciphertext = $cipher->encrypt_finish($plaintext, ...);
188 Encrypt
and finish a stream
in one call
.
194 my $out = $self->encrypt(@_);
195 $out .= $self->finish;
199 =method decrypt_finish
201 $plaintext = $cipher->decrypt_finish($ciphertext, ...);
203 Decrypt
and finish a stream
in one call
.
209 my $out = $self->decrypt(@_);
210 $out .= $self->finish;
216 File
::KDBX
::Cipher-
>register($uuid => $package, %args);
218 Register a cipher
. Registered ciphers can be used to encrypt
and decrypt KDBX databases
. A cipher
's UUID
219 B<must> be unique and B<musn't change
>. A cipher UUID
is written into
each KDBX file
and the associated cipher
220 must be registered with the same UUID
in order to decrypt the KDBX file
.
222 C
<$package> should be a Perl
package relative to C
<File
::KDBX
::Cipher
::> or prefixed with a C
<+> if it
is
223 a fully-qualified
package. C
<%args> are passed as-is to the cipher
's L</init> method.
233 my $formatted_id = looks_like_number($id) ? $id : format_uuid($id);
234 $package = "${class}::${package}" if $package !~ s/^\+// && $package !~ /^\Q${class}::\E/;
236 my %blacklist = map { (looks_like_number($_) ? $_ : File::KDBX::Util::uuid($_)) => 1 }
237 split(/,/, $ENV{FILE_KDBX_CIPHER_BLACKLIST} // '');
238 if ($blacklist{$id} || $blacklist{$package}) {
239 alert "Ignoring blacklisted cipher ($formatted_id)", id => $id, package => $package;
243 if (defined $CIPHERS{$id}) {
244 alert "Overriding already-registered cipher ($formatted_id) with package $package",
249 $CIPHERS{$id} = [$package, @args];
254 File::KDBX::Cipher->unregister($uuid);
256 Unregister a cipher. Unregistered ciphers can no longer be used to encrypt and decrypt KDBX databases, until
257 reregistered (see L</register>).
262 delete $CIPHERS{$_} for @_;
266 __PACKAGE__->register(CIPHER_UUID_AES128, 'CBC
', algorithm => 'AES
', key_size => 16);
267 __PACKAGE__->register(CIPHER_UUID_AES256, 'CBC
', algorithm => 'AES
', key_size => 32);
268 __PACKAGE__->register(CIPHER_UUID_SERPENT, 'CBC
', algorithm => 'Serpent
', key_size => 32);
269 __PACKAGE__->register(CIPHER_UUID_TWOFISH, 'CBC
', algorithm => 'Twofish
', key_size => 32);
270 __PACKAGE__->register(CIPHER_UUID_CHACHA20, 'Stream
', algorithm => 'ChaCha
');
271 __PACKAGE__->register(CIPHER_UUID_SALSA20, 'Stream
', algorithm => 'Salsa20
');
272 __PACKAGE__->register(STREAM_ID_CHACHA20, 'Stream
', algorithm => 'ChaCha
');
273 __PACKAGE__->register(STREAM_ID_SALSA20, 'Stream
', algorithm => 'Salsa20
');
281 use File::KDBX::Cipher;
283 my $cipher = File::KDBX::Cipher->new(uuid => $uuid, key => $key, iv => $iv);
285 my $ciphertext = $cipher->encrypt('plaintext
');
286 $ciphertext .= $cipher->encrypt('more plaintext
');
287 $ciphertext .= $cipher->finish;
289 my $plaintext = $cipher->decrypt('ciphertext
');
290 $plaintext .= $cipher->decrypt('more ciphertext
');
291 $plaintext .= $cipher->finish;
295 A cipher is used to encrypt and decrypt KDBX files. The L<File::KDBX> distribution comes with several
296 pre-registered ciphers ready to go:
299 * C<61AB05A1-9464-41C3-8D74-3A563DF8DD35> - AES128 (legacy)
300 * C<31C1F2E6-BF71-4350-BE58-05216AFC5AFF> - AES256
301 * C<D6038A2B-8B6F-4CB5-A524-339A31DBB59A> - ChaCha20
302 * C<716E1C8A-EE17-4BDC-93AE-A977B882833A> - Salsa20
303 * C<098563FF-DDF7-4F98-8619-8079F6DB897A> - Serpent
304 * C<AD68F29F-576F-4BB9-A36A-D47AF965346C> - Twofish
306 B<NOTE:> If you want your KDBX file to be readable by other KeePass implementations, you must use a UUID and
307 algorithm that they support. From the list above, AES256 and ChaCha20 are well-supported. You should avoid
308 AES128 for new databases.
310 You can also L</register> your own cipher. Here is a skeleton:
312 package File::KDBX::Cipher::MyCipher;
314 use parent 'File
::KDBX
::Cipher
';
316 File::KDBX::Cipher->register(
317 # $uuid, $package, %args
318 "\x12\x34\x56\x78\x9a\xbc\xde\xfg\x12\x34\x56\x78\x9a\xbc\xde\xfg" => __PACKAGE__,
321 sub init { ... } # optional
329 sub block_size { ... }