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 Called by L
</new
> to set attributes
. You normally shouldn
't call this. Returns itself to allow method
152 sub DESTROY { !in_global_destruction and erase \$_[0]->{key} }
156 $ciphertext = $cipher->encrypt($plaintext, ...);
162 sub encrypt { die 'Not implemented
' }
166 $plaintext = $cipher->decrypt($ciphertext, ...);
172 sub decrypt { die 'Not implemented
' }
176 $ciphertext .= $cipher->finish; # if encrypting
177 $plaintext .= $cipher->finish; # if decrypting
185 =method encrypt_finish
187 $ciphertext = $cipher->encrypt_finish($plaintext, ...);
189 Encrypt and finish a stream in one call.
195 my $out = $self->encrypt(@_);
196 $out .= $self->finish;
200 =method decrypt_finish
202 $plaintext = $cipher->decrypt_finish($ciphertext, ...);
204 Decrypt and finish a stream in one call.
210 my $out = $self->decrypt(@_);
211 $out .= $self->finish;
217 File::KDBX::Cipher->register($uuid => $package, %args);
219 Register a cipher. Registered ciphers can be used to encrypt and decrypt KDBX databases. A cipher's UUID
220 B
<must
> be unique
and B
<musn
't change>. A cipher UUID is written into each KDBX file and the associated cipher
221 must be registered with the same UUID in order to decrypt the KDBX file.
223 C<$package> should be a Perl package relative to C<File::KDBX::Cipher::> or prefixed with a C<+> if it is
224 a fully-qualified package. C<%args> are passed as-is to the cipher's L
</init
> method.
234 my $formatted_id = looks_like_number
($id) ? $id : format_uuid
($id);
235 $package = "${class}::${package}" if $package !~ s/^\+// && $package !~ /^\Q${class}::\E/;
237 my %blacklist = map { (looks_like_number
($_) ? $_ : File
::KDBX
::Util
::uuid
($_)) => 1 }
238 split(/,/, $ENV{FILE_KDBX_CIPHER_BLACKLIST
} // '');
239 if ($blacklist{$id} || $blacklist{$package}) {
240 alert
"Ignoring blacklisted cipher ($formatted_id)", id
=> $id, package => $package;
244 if (defined $CIPHERS{$id}) {
245 alert
"Overriding already-registered cipher ($formatted_id) with package $package",
250 $CIPHERS{$id} = [$package, @args];
255 File
::KDBX
::Cipher-
>unregister($uuid);
257 Unregister a cipher
. Unregistered ciphers can
no longer be used to encrypt
and decrypt KDBX databases
, until
258 reregistered
(see L
</register
>).
263 delete $CIPHERS{$_} for @_;
267 __PACKAGE__-
>register(CIPHER_UUID_AES128
, 'CBC', algorithm
=> 'AES', key_size
=> 16);
268 __PACKAGE__-
>register(CIPHER_UUID_AES256
, 'CBC', algorithm
=> 'AES', key_size
=> 32);
269 __PACKAGE__-
>register(CIPHER_UUID_SERPENT
, 'CBC', algorithm
=> 'Serpent', key_size
=> 32);
270 __PACKAGE__-
>register(CIPHER_UUID_TWOFISH
, 'CBC', algorithm
=> 'Twofish', key_size
=> 32);
271 __PACKAGE__-
>register(CIPHER_UUID_CHACHA20
, 'Stream', algorithm
=> 'ChaCha');
272 __PACKAGE__-
>register(CIPHER_UUID_SALSA20
, 'Stream', algorithm
=> 'Salsa20');
273 __PACKAGE__-
>register(STREAM_ID_CHACHA20
, 'Stream', algorithm
=> 'ChaCha');
274 __PACKAGE__-
>register(STREAM_ID_SALSA20
, 'Stream', algorithm
=> 'Salsa20');
282 use File::KDBX::Cipher;
284 my $cipher = File::KDBX::Cipher->new(uuid => $uuid, key => $key, iv => $iv);
286 my $ciphertext = $cipher->encrypt('plaintext');
287 $ciphertext .= $cipher->encrypt('more plaintext');
288 $ciphertext .= $cipher->finish;
290 my $plaintext = $cipher->decrypt('ciphertext');
291 $plaintext .= $cipher->decrypt('more ciphertext');
292 $plaintext .= $cipher->finish;
296 A cipher is used to encrypt and decrypt KDBX files. The L<File::KDBX> distribution comes with several
297 pre-registered ciphers ready to go:
300 * C<61AB05A1-9464-41C3-8D74-3A563DF8DD35> - AES128 (legacy)
301 * C<31C1F2E6-BF71-4350-BE58-05216AFC5AFF> - AES256
302 * C<D6038A2B-8B6F-4CB5-A524-339A31DBB59A> - ChaCha20
303 * C<716E1C8A-EE17-4BDC-93AE-A977B882833A> - Salsa20
304 * C<098563FF-DDF7-4F98-8619-8079F6DB897A> - Serpent
305 * C<AD68F29F-576F-4BB9-A36A-D47AF965346C> - Twofish
307 B<NOTE:> If you want your KDBX file to be readable by other KeePass implementations, you must use a UUID and
308 algorithm that they support. From the list above, AES256 and ChaCha20 are well-supported. You should avoid
309 AES128 for new databases.
311 You can also L</register> your own cipher. Here is a skeleton:
313 package File::KDBX::Cipher::MyCipher;
315 use parent 'File::KDBX::Cipher';
317 File::KDBX::Cipher->register(
318 # $uuid, $package, %args
319 "\x12\x34\x56\x78\x9a\xbc\xde\xfg\x12\x34\x56\x78\x9a\xbc\xde\xfg" => __PACKAGE__,
322 sub init { ... } # optional
330 sub block_size { ... }