|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
Name | Type | Description |
---|---|---|
Signature (1) | UInt32 | Must be 0x9AA2D903. |
Signature (2) | UInt32 | Must be 0xB54BFB67. |
Format version | UInt32 | The high word is the major version, and the low word is the minor version.
|
Fields | ID-value pairs | One or more header fields. See below. |
A header field consists of an ID t (byte) and a value V
(type depends on t). Let s be the size of V
in bytes, as Int32.
Each header field is stored as follows:
t ‖ s ‖ V.
The following header fields are supported:
Name | ID | Type | Description |
---|---|---|---|
End of header | 0 | Byte[4] | Indicates the end of the header. Must be present exactly once, as the last header field. The value should be the byte array (0x0D, 0x0A, 0x0D, 0x0A). |
Encryption algorithm | 2 | UUID | The following encryption algorithms are supported by KeePass
(built-in, without a plugin):
|
Compression algorithm | 3 | UInt32 | 0 = no compression, 1 = GZip. |
Master salt/seed (⟳) | 4 | Byte[32] | Salt/seed for computing the keys. |
Encryption IV/nonce (⟳) | 7 | Byte[] | Initialization vector or nonce for the encryption algorithm. 16 bytes for AES-256, 12 bytes for ChaCha20. |
KDF parameters | 11 | Variant dictionary | Parameters for the key derivation function (KDF). See below. |
Public custom data | 12 | Variant dictionary | Custom data of plugins/ports.
The name of an item should be unique, e.g. "PluginName_ItemName".
In this header field, only data that must be readable without decryption should
be stored (e.g. data by a key provider plugin required for decryption).
All other custom data should be stored in the encrypted
XML document (elements //Meta/CustomData ,
//Group/CustomData and //Entry/CustomData ). |
The IDs 1, 5, 6, 8, 9 and 10 were used in previous versions of the KDBX file format.
Key derivation function (KDF) parameters. The KDF parameters are stored in a variant dictionary. The following parameters are supported:
Name | Type | Description |
---|---|---|
All KDFs: | ||
$UUID |
UUID | UUID of the KDF. The following KDFs are supported by KeePass
(built-in, without a plugin):
|
AES-KDF: | ||
S (⟳) |
Byte[32] | Salt/seed. |
R |
UInt64 | Rounds. |
Argon2: | ||
V |
UInt32 | Version. 0x10 (version 1.0) or 0x13 (version 1.3), as defined in the Argon2 specification. 0x13 is recommended. |
S (⟳) |
Byte[] | Salt. Size: minimum 8, maximum 0x3FFFFFFF, recommended 32. |
I |
UInt64 | Iterations. Minimum 1, maximum 0xFFFFFFFF. |
M |
UInt64 | Memory, in bytes. Minimum 8192, maximum 0x7FFFFFFF. |
P |
UInt32 | Parallelism. Minimum 1, maximum 0x00FFFFFF. |
A variant dictionary is a name-value dictionary, where the name is a string and the type of the value depends on the item. It is stored as follows:
An item looks as follows:
Name | Type | Description |
---|---|---|
Value type t | Byte | Supported value types are:
|
Name size r | Int32 | Size of U in bytes. |
Name U | String (r bytes) | Name of the item. |
Value size s | Int32 | Size of V in bytes. |
Value V | t (s bytes) | Value of the item. |
The order of the items is arbitrary.
For some of the cryptographic primitives used in the KDBX file format, a key is required. The keys are computed as follows:
With T and the master salt/seed S (stored in the header), the final keys can be computed:
The inner encryption key is stored in the inner header and does not depend on the master key. For details, see the description of the inner encryption.
When loading the content of a KDBX file, KeePass first verifies the integrity and the authenticity of the data before decrypting, decompressing and parsing it. For this, HMACs are used. Computing only one HMAC for the whole data would be problematic when the data is large. KeePass would either need to read the whole data into the process memory (i.e. a lot of process memory would be required) or read the whole data twice (which would be a problem when I/O is slow, e.g. when loading the KDBX file over a slow network/Internet connection, and temporary files should of course be avoided for multiple reasons). Solution: splitting the data into blocks and protecting each block using a HMAC.
When saving a KDBX file, KeePass splits the input data for this stream into blocks.
Let M be the i-th input block (zero-based index,
type UInt64) and let s be the size of M (in bytes,
as Int32), then an output block (to be stored in the file) looks as follows:
HMAC-SHA-256(i ‖ s ‖ M) ‖
s ‖ M.
For computing the HMAC-SHA-256 hash, a key is required. See the section 'Computation of Keys' for details about the computation of the key.
A very small block size results in a large KDBX file (due to the additional HMACs and size values). A very large block size requires a lot of process memory. So, except in special cases (e.g. a small block at the end of the file), neither the minimum nor the maximum is a good choice for the block size; a good size is in the "middle". When saving a KDBX file, KeePass currently uses 1048576 (i.e. 1 MB) as size for every input block except the last one (which may be smaller).
The HMAC-protected block stream is terminated by an output block for an empty input block (i.e. M empty, s = 0).
The inner header (which is called "inner" because it is compressed and
encrypted) consists of one or more header fields.
A header field consists of an ID t (byte) and a value V
(type depends on t). Let s be the size of V
in bytes, as Int32.
Each header field is stored as follows:
t ‖ s ‖ V.
The following header fields are supported:
Name | ID | Type | Description |
---|---|---|---|
End of header | 0 | – | Indicates the end of the header. Must be present exactly once, as the last header field. The value should be empty. |
Inner encryption algorithm | 1 | Int32 | 2 = Salsa20, 3 = ChaCha20 (default, recommended). See 'Inner Encryption'. |
Inner encryption key (⟳) | 2 | Byte[] | See 'Inner Encryption'. |
Binary content | 3 | Byte[] | The value is f ‖ C, where f is a flags byte and C is the content of a binary (attachment). The flag 0x01 indicates that the binary content should be protected in the process memory. A binary content is referenced in the XML document by its index in the inner header (the first binary content has index 0, the second one has index 1, etc.). |
Most XML parsers work with regular strings, which may be difficult to erase from the process memory. So, if sensitive data would be stored unencryptedly in the XML document, a process memory protection could not be realized properly. Solution: all data that should be protected in the process memory is stored in encrypted form in the XML document. For this, the encryption algorithm and key stored in the inner header are used. The encryption algorithm is a stream cipher and its state is not reset for each data to be protected (thus the order in which the data to be protected appears is important). For example, if there is an entry A with a password consisting of 23 UTF-8 bytes and an entry B with a password consisting of 19 UTF-8 bytes (and A appears before B), then the password of A is encrypted using the first 23 output bytes of the stream cipher and the password of B is encrypted using the next (not first) 19 output bytes of the stream cipher. When loading a KDBX file, an application typically decrypts the protected data and immediately protects it using a method suitable for the current operating system (e.g. DPAPI on Windows).
We emphasize that the only purpose of the inner encryption is to support the process memory protection. The inner encryption has no effect on the cryptographic security of the KDBX file format.
Let K be the inner encryption key stored in the inner header. The parameters for the inner encryption algorithm are derived as follows:
The KDBX XML document contains most of the user data.
The format of the KDBX XML document is specified by the following
XML schema (including annotations):
KDBX_XML.xsd
(KDBX 4.1 XML Schema).