Introduction
Welcome to the KDBX compendium! This book is intended as an overview of the KeePass KDBX file format for developers implementing support for KeePass databases in their applications.
Notable KDBX format implementations
This book aims to compare different implementations of the KDBX file format, with direct references to the source code of each implementation. This is toidentify gotchas that developers might encounter.
Example: For Argon2, some implementationsKP, KP2A, STBX default to 2 lanes of parallelism, while some default to 4KPXC, KPDX, KPIU, . Libraries generally don’t have a default, and require the caller to specify the number of lanes.
GUI applications
| Application | Shortcode | Platforms | Language | Notes |
|---|---|---|---|---|
| KeePass | KP | Windows, (Linux, MacOS) | C# | No public git repo, will use unofficial mirror |
| KeePassXC | KPXC | Windows, Linux, MacOS | C++ | |
| Keepass2Android | KP2A | Android | Java | |
| KeePassDX | KPDX | Android | Kotlin | |
| KeePassium | KPIU | iOS | Swift | |
| Strongbox | STBX | iOS, macOS | Objective-C, Swift |
Libraries
| Library | Shortcode | Language | Read support | Write support | Notes |
|---|---|---|---|---|---|
| pykeepass | PYKP | Python | KDBX3, KDBX4 | ? | |
| keepass-rs | KPRS | Rust | KDB, KDBX3, KDBX4, KDBX4.1 | experimental, KDBX4.1 | maintained by the author of this book |
| kdbxweb | KDBW | TypeScript | KDBX3, KDBX4, KDBX4.1 | KDBX3, KDBX4 | seems unmaintained, last commit Dec 2024 |
Other resources
- KeePass KDBX file format specification
- KeePass KDBX XML Schema (partial)
- Almost Secure: “Documenting KeePass KDBX4 file format”
Conventions in this Document
- All byte offsets are zero-based, so the first byte of a structure is at offset
0. - All byte ranges are non-inclusive, so the byte range
0-4includes bytes at offsets0,1,2, and3, but not the byte at offset4. - All lengths are in bytes, so a length of
4means that the value occupies 4 bytes. - All hexadecimal numbers are prefixed with
0x, so the number255will be represented as0xFF.
KDBX file format
The KDBX format can be conceptually divided into two layers: the outer database with its header, and the compressed and encrypted HMAC block stream containing the inner database with its header.
Encoding
- All numbers are stored in little-endian format, so the 32-bit hex number
0x01234567will be stored as the byte sequence67 45 23 01 - All boolean values are stored as a single byte, with
0x00representingfalseand0x01representingtrue. - All UUIDs are stored as 16 bytes in big-endian format, so the UUID
123e4567-e89b-12d3-a456-426614174000will be stored as the byte sequence12 3E 45 67 E8 9B 12 D3 A4 56 42 66 14 17 40 00. - All strings are UTF-8 encoded, so the string
Hello 😃will be stored as the byte sequence48 65 6C 6C 6F 20 F0 9F 98 83.
Outer database
The outer database consists of an identifier followed by the database version, then the outer header, and finally hashes and HMACs for integrity and password verification. After the outer database, the HMAC block stream begins, which contains the compressed and encrypted inner database.
KDBX Identifier (4 bytes)
A KDBX file always starts with the bytes 03 D9 A2 9A corresponding to the little-endian encoding of the 32-bit hex number 0x9aa2d903. In the remainder of this chapter, numbers will be listed in their hex form, keep in mind that they are stored in little-endian format in the file.
Version (8 bytes)
The next 8 bytes are the version number, with the following structure:
| Byte(s) | Description |
|---|---|
| 0-4 | Main version KDB: 0xb54bfb65KDB2: 0xb54bfb66KDBX3 / KDBX4: 0xb54bfb67 |
| 4-6 | Minor version KDBX4: 0x0000KDBX4.1: 0x0001 |
| 6-8 | Major version KDBX3: 0x0003KDBX4: 0x0004 |
Outer header
The outer header consists of an arbitrary number of entries, each with the following structure:
| Byte(s) | Description |
|---|---|
| 0 | Entry type ID |
| 1-4 | Entry length |
| 4-… | Entry value |
The header must be terminated by an entry with type 0x00 and length 0x00000000.
Valid entry types are:
| Name | Type ID | Type | Length | Description |
|---|---|---|---|---|
| End of header | 0 | - | 0 | Marks the end of the header |
| Outer encryption | 2 | UUID | 16 | The UUID of the encryption algorithm used for the HMAC block stream |
| Compression | 3 | UInt32 | 4 | The ID of the compression algorithm used for the inner database |
| Master seed | 4 | Bytes | 32 | The master seed used for key derivation |
| Encryption IV / nonce | 7 | Bytes | 16 or 12 | The initialization vector (IV) or nonce used for encryption. AES-256 uses 16 bytes, ChaCha20 uses 12 bytes. |
| KDF parameters | 11 | VariantDictionary | Variable | The parameters for the key derivation function. |
| Public custom data | 12 | VariantDictionary | Variable | Unencrypted custom data that may be used by applications. |
Outer encryption
The outer encryption algorithm is used to encrypt the HMAC block stream. The outer encryption algorithm is specified by its UUID, which is stored in the outer header. The following outer encryption algorithms exist:
| Name | UUID | Notes |
|---|---|---|
| AES-128 | 61ab05a1-9464-41c3-8d74-3a563df8dd35 | AES-128 in CBC mode with PKCS7 padding. This algorithm is deprecated. |
| AES-256 | 31c1f2e6-bf71-4350-be58-05216afc5aff | AES-256 in CBC mode with PKCS7 padding. |
| Twofish | ad68f29f-576f-4bb9-a36a-d47af965346c | Twofish in CBC mode with PKCS7 padding. This algorithm is deprecated. |
| ChaCha20 | d6038a2b-8b6f-4cb5-a524-339a31dbb59a | ChaCha20 |
Compression
The compression algorithm is used to compress the inner database before encryption. The compression algorithm is specified by its ID, which is stored in the outer header. The following compression algorithms exist:
| Name | ID | Notes |
|---|---|---|
| None | 0 | No compression. |
| GZip | 1 | GZip compression. |
Master seed and encryption IV / nonce
Every time a KDBX file is saved, a new random master seed and encryption IV / nonce should be generated.
KDF parameters
The parameters for the key derivation function (KDF) are stored in the outer header as a VariantDictionary.
Key derivation functions are identified by a UUID and are configured using a set of parameters. The following key derivation functions are defined:
| Name | UUID | Notes |
|---|---|---|
| AES-KDFKP,KPXC | c9d9f39a-628a-4460-bf74-0d08c18a4fea | [1] |
| Argon2dKP,KPXC | ef636ddf-8c29-444b-91f7-a9a403e30a0c | |
| Argon2idKP,KPXC | 9e298b19-56db-4773-b23d-fc3ec6f0a1e6 |
[1]: There is an alternate AES-KDF UUID 7c02bb82-79a7-4ac0-927d-114a00648238 that should be recognized as AES-KDF, but databases should never be saved with this UUID.
The following parameters are defined:
| Key | Type | KDFs | Description |
|---|---|---|---|
$UUID | UUID | All | The UUID of the key derivation function. |
S | Byte[32] | AES-KDF | The salt used for key derivation. This should be re-generated on every database save. |
R | UInt32 | AES-KDF | The number of rounds. |
V | UInt32 | Argon2 | The version number. This should be set to 0x13 for version 1.3 (recommended) or 0x10 for version 1.0. |
S | Byte[] | Argon2 | The salt used for key derivation. Minimum 8 bytes, maximum 0x3FFFFFFF bytes, 32 bytes recommended. This should be re-generated on every database save. |
I | UInt32 | Argon2 | The number of iterations. Minimum 1, maximum 0xFFFFFFFF |
M | UInt32 | Argon2 | The amount of memory in bytes. Minimum 8192, maximum 0x7FFFFFFF |
P | UInt32 | Argon2 | The degree of parallelism. Minimum 1, maximum 0x00FFFFFF |
Public custom data
Public, unencrypted custom data stored as a VariantDictionary. The keys should be unique and prefixed with the name of the application, e.g. MyApplication_MyItem
VariantDictionary
A variant dictionary is a typed key-value store used to store the parameters for the key derivation function and the public custom data in the outer header. It consists of an arbitrary number of entries, each with the following structure:
| Byte(s) | Type | Description |
|---|---|---|
0 | UInt8 | Value type ID |
1-5 | UInt32 | Key length K |
5-(5+K) | String | Key |
(5+K)-(5+K+4) | UInt32 | Value length V |
(5+K+4)-(5+K+4+V) | Bytes | Value (type depends on value type ID) |
Valid value type IDs are:
| Type ID | Length | Description |
|---|---|---|
0x04 | 4 | UInt32 |
0x05 | 8 | UInt64 |
0x08 | 1 | Boolean |
0x0c | 4 | Int32 |
0x0d | 8 | Int64 |
0x18 | Variable | String (UTF-8 encoded) |
0x42 | Variable | Byte array |
VariantDictionaries must be terminated by a single null byte (0x00).
Outer header verification
All data from the start of the KDBX file up to and including the end of the outer header is used to verify the integrity of the outer header by computing a SHA-256 hash of this data and comparing it to the hash stored after the outer header. A mismatch indicates that the file is corrupted.
Key derivation and verification
The outer header contains the parameters for the key derivation function (KDF) used to derive the master key from the user-provided secrets.
CompositeKey = SHA-256(concatenate-if-present(
SHA256(masterPassword),
keyFromKeyfile,
keyFromKeyProvider,
keyFromWindowsUserAccount
))
TransformedKey = KDF(CompositeKey)
MasterKey = SHA-256(concatenate(MasterSeed, TransformedKey))
The AES-KDF is simply a repeated encryption of the composite key with itself as the key. The number of rounds is determined by the R parameter in the KDF parameters. The result of the final round is hashed with SHA-256 to produce the transformed key.
The Argon2 KDFs are used with the parameters specified in the KDF parameters and a 32 byte output length to produce the transformed key.
The correctness of the master key is verified by computing the HMAC of the header data as follows:
HMACKey = SHA-512(concatenate(MasterSeed, TransfomedKey, 0x01))
HMACBlockKey = SHA-512(concatenate(0xFFFFFFFFFFFFFFFF, HMACKey))
CheckHMAC = HMAC(HMACBlockKey, headerData)
The data in CheckHMAC is compared to the data stored after the outer header hash. A mismatch indicates that the master key is incorrect.
HMAC block stream
The HMAC block stream is a stream of blocks (typically 1 MiB in sizeKP, KPXC) where each block is authenticated with an HMAC.
For the i-th block, the HMAC key is verified as follows:
HMACBlockKey = SHA-512(concatenate(i as UInt64, HMACKey))
CheckHMac = HMAC(HMACBlockKey, blockData)
From HMAC block stream to inner database
The HMAC block stream is decrypted using the outer encryption algorithm specified in the outer header, resulting in the compressed inner database. The compressed inner database is then decompressed using the compression algorithm specified in the outer header, resulting in the inner database.
Inner database
The inner database is the result of decrypting and decompressing the HMAC block stream. It consists of the inner header and the XML database.
Inner Header
The inner header consists of an arbitrary number of entries, each with the following structure:
| Byte(s) | Description |
|---|---|
| 0 | Entry type ID |
| 1-4 | Entry length |
| 4-… | Entry value |
The header must be terminated by an entry with type 0x00 and length 0x00000000.
Valid entry types are:
| Name | Type ID | Type | Length | Description |
|---|---|---|---|---|
| End of header | 0 | - | 0 | Marks the end of the header |
| Inner encryption | 1 | UInt32 | 4 | The ID of the encryption algorithm used for the inner database |
| Inner encryption key | 2 | Bytes | Variable | The key used for encrypting the inner database. This should be re-generated on each database save. |
| Binary attachment | 3 | Bytes | Variable | A binary file attachment |
Inner encryption
Inner encryption is used to encrypt protected fields within the XML database. The inner encryption key is specified as a field of the inner header, and independent of the outer encryption key.
Valid inner encryption algorithm IDs are:
| Name | ID | Description |
|---|---|---|
| AES | 1 | AES encryption in CBC mode with PKCS7 padding. |
| Twofish | 2 | Twofish encryption in CBC mode with PKCS7 padding. |
| ChaCha20 | 3 | ChaCha20 encryption. |
Binary attachments
Binary attachments are used to store binary files within the database. They are referenced in the XML database by their ID, which is the index of the binary attachment entry in the inner header.
Binary attachments have the following structure:
| Byte(s) | Description |
|---|---|
| 0 | Flags. 0x01 indicates the binary content should be protected in process memory, but does not indicate that the content is encrypted in the file. |
| 1-… | Binary content |
XML Database
The XML database is a UTF-8 encoded XML document that contains most actual data of the database, including groups, entries, and their fields. The structure of the XML database is defined by the KDBX XML Schema. This section fills in some details about the XML database that are important for implementors.
Value types
Some value types have special encoding rules within the XML database, which are described here.
(Nullable) Booleans
There are two boolean types in the XML database - TBool and TNullableBoolEx. TBool values are stored as the string True or False, while TNullableBoolEx values are stored as True, False, or the string Null.
Most boolean fields in the XML database are of type TBool, which means that an unspecified value will default to a field-specific default value.
KP,
KPXC
For TNullableBoolEx fields, an unspecified or Null value will be treated as inheriting the parent group’s value. The two fields of type TNullableBoolEx are EnableAutoType and EnableSearching.
KP,
KPXC
Colors
Colors are stored as RGB values in the format “#RRGGBB”. Empty strings should be treated as default values, chosen by the application. KP, KPXC
Example: an orangeish color could be expressed as #FF6600.
UUID values
UUID values are stored as 128-bit unsigned integers that are base64 encoded. KP, KPXC
Example: the UUID 00010203-0405-0607-0809-0a0b0c0d0e0f would be represented as the byte sequence 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f, which is base64 encoded as AAECAwQFBgcICQoLDA0ODw==.
Timestamps
Timestamps should be stored as seconds since the special epoch of 2000-01-01T00:00:00Z in UTC, as a 64-bit signed integer that is base64 encoded.
KP,
KPXC
Some older versions of the KDBX format used ISO 8601 datetime strings, so parsing from this format should be supported for backward compatibility.
Example: the timestamp 2000-01-01T00:00:01Z, one second after the epoch, would be stored as the byte sequence 01 00 00 00 00 00 00 00, which is base64 encoded as AQAAAAAAAA==.
DataTransferObfuscation aka AutoTypeObfuscation
DataTransferObfuscation is an integer-valued field for entries in the XML database. It corresponds to an enumeration of obfuscation methods used for AutoType:
| Value | Method |
|---|---|
| 0 | No obfuscation |
| 1 | Use clipboard |
Protected values
Values in the XML database can be marked as protected with an attribute Protected="True". Protected values are stored as Base64-encoded encrypted data, using the encryption method and key specified in the inner header. There is also a ProtectInMemory="True" attribute, but it is only used for plaintext XML exports.
While some implementations allow protections on any field KPXC , the official KeePass implementation only allows the string and binary fields of entries to be protected KP
Attachment references
In KDBX4, attachments are stored as binary entries in the inner header, and referenced in the XML database by their ID, which is the index of the binary attachment entry in the inner header. In KDBX3, attachments are stored as Base64-encoded data with an optional Compressed="True" and Protected="True" attributes, and are not referenced by ID
KP,
KPXC
.
KDBX4 example:
<Binary>
<Key>myfile.txt</Key>
<Value Ref="1"/>
</Binary>
KDBX3 example:
<Binary>
<Key>myfile.txt</Key>
<Value Compressed="True">H4sIAAAAAAAAA/NIzcnJVyjJyCxWAKLk/NyCotTi4tQUhZTEkkQuAFCFZIEeAAAA</Value>
</Binary>
Tags
Tags are stored as a single string with individual tags delimited by semicolons KP, KP2A, KPRS , though some implementations use commas instead KPXC, KPDX .
When parsing, the string should be split by commas, semicolons, and tab characters to ensure compatibility with different implementations KP, KPXC, KP2A, KPDX, KPRS , and whitespace around individual tags should be trimmed. Duplicates can be removed and the tags can be sorted alphabetically KP, KPXC, KP2A , though some implementations preserve the original order and don’t remove duplicates KPRS .
Suggestions for improvement
As implementors of the KDBX file format, we have identified several areas where the format could be improved in a future version. This chapter outlines some of these suggestions.
Many of the topics are drawn from the KDBX Standard Discussion (#6229) on the KeePassXC GitHub repository, where maintainers and developers of various KeePass-compatible clients discussed desired improvements to the KDBX format.
Serialize tags as XML elements
Instead of the heterogeneous way tags are processed from strings, the list of tags could be stored using XML elements, with one XML element per tag.
Identify attachments by UUIDs
Currently, attachments in the inner header are referenced in the XML database by their order of appearance. This is the only case where elements in the XML database are not identified by UUIDs, and having attachment UUIDs would simplify unambiguous identification of attachments, e.g. during merge operations.
Argon2id as the Standard KDF
The discussion proposed standardizing on Argon2id instead of Argon2d as the key derivation function. Argon2id is a hybrid variant that provides better resistance to both side-channel and GPU-based attacks compared to Argon2d alone, making it a stronger default choice for password databases.
Source: #6229 (opening post)
Password Modification Datetime
A dedicated timestamp recording when the password field of an entry was last changed was proposed as a “must have.” The use case is that the generic “last modified” timestamp of an entry changes whenever any field is edited — not just the password — so it cannot reliably track password age for rotation policies.
KeePass developer DReichl argued against adding this field, noting that: a user may create an entry for an account that predates the entry; a failed password-change attempt could result in an incorrect automatic timestamp; and KeePass already approximates this via a “Last Password Modification Time (Based on History)” column. KeePassXC maintainer droidmonkey disagreed, arguing that edge cases should not block a broadly useful feature and that the application can detect password field changes through text-change events.
Source: #6229 — DReichl, May 2021; droidmonkey reply
Built-in Entry Layout Specification
The format currently provides no standard way to describe how an entry’s fields should be presented in a UI. A built-in entry layout specification would allow clients to declare structured display templates for different entry types (e.g., credit cards, identity documents, SSH keys), ensuring consistent rendering across all KDBX-compatible applications.
Source: #6229 (opening post); related issue #863
Built-in TOTP Specification
Multiple incompatible TOTP implementations exist across the KeePass ecosystem: KeePass’s own multi-field approach, KeePassXC’s system, and various historical plugin formats. The proposal is to standardize on a single otp attribute using the widely-adopted otpauth:// URL format as the canonical representation.
KeePassXC maintainers confirmed they already prefer otpauth URLs and maintain legacy formats only for backwards compatibility. KeePass’s multi-field approach was criticized for being the only client to implement that particular scheme.
Source: #6229 — sirtao, Apr 2021; phoerious reply; droidmonkey reply
Built-in Hardware Key Specification
The proposal asks for a standardized way to specify hardware key (e.g., YubiKey) parameters as part of the KDBX credential set, including support for multiple hardware keys to allow backup keys. Currently each client implements hardware key support differently, preventing interoperability.
Related: Discussion #9506
Source: #6229 (opening post)
Built-in Quick Unlock Specification
The proposal asks for the KDBX format to define a standard quick unlock mechanism — allowing users to re-open a locked database with a shorter credential (e.g., a PIN or biometric) without repeating the expensive key derivation step. The implementation model would store the fully-derived database key encrypted in memory, with the quick unlock credential as the encryption key for that in-memory store.
KeePass developer DReichl argued the feature should remain application-level rather than database-level, because: different devices need different unlock methods; device capabilities vary; multiple open databases would create ambiguous unlock semantics; and the feature is inherently tied to the operating environment. KeePassDX developer J-Jamet provided a taxonomy of two technical approaches (in-memory re-encryption vs. OS credential store), noting both have different security trade-offs.
Source: #6229 — DReichl, Mar 2021; droidmonkey, Apr 2021; J-Jamet, Apr 2021
Built-in Favorites / Pinned Items
A standard mechanism to mark entries as “favorites” or “pinned” was proposed, allowing clients to surface important entries without requiring workarounds like special group names or tags. Some participants noted that tags can already serve this purpose and that a vendor-specific predefined tag (as done in Enpass) achieves the same effect, but a first-class standard field would ensure cross-client portability.
Source: #6229 (opening post); user858753257, Feb 2020
Key File Format Simplification
The proposal is to support only two key file formats going forward: plain binary key files, and the structured .key/.keyx XML format. Eliminating other ad-hoc formats would simplify implementation across clients and reduce the risk of weak key files being accepted.
Source: #6229 (opening post)
Multi-Valued Fields
Both core fields (e.g., URL) and custom fields should support multiple values natively within the KDBX schema, rather than requiring clients to invent their own conventions (e.g., URL2, KP2A_URL_1, WebDomain[1]). The lack of a standard for multiple values has led to divergent, incompatible implementations across clients.
A related problem raised in the discussion is that KeePassDX uses WebDomain / WebDomain[counter] while Keepass2Android uses KP2A_URL / KP2A_URL_<counter> for storing additional URLs, causing browser autofill failures when switching clients. Standardizing a single prefix — or natively supporting multiple URL values — would resolve this.
Source: #6229 (opening post); ghost comment, Nov 2020; related issues #398, #3558, KeePassDX #524
Passkey / WebAuthn Credential Storage
Strongbox developer strongbox-mark raised the need to standardize passkey (WebAuthn) credential storage across KeePass clients before incompatible implementations emerge. A de facto convention was already present in a KeePassXC work-in-progress PR: the private key is stored as a PEM file attachment named webauthn.pem, the WebAuthn-generated username is stored in the password field, and the user-chosen username/email is stored in the username field. Entries are identified as passkey entries by scanning for the webauthn.pem attachment name.
Open issues noted in the discussion include: how to export WebAuthn credentials portably for import into other password managers; and uncertainty about whether major browsers would expose APIs allowing third-party passkey providers.
Source: #6229 — strongbox-mark, Jan 2023; varjolintu reply; related PR #8825
Database-Level Transactional History
Currently, history is tracked at the entry level only. The proposal asks for transactional history at the database level, enabling undo of destructive operations such as entry deletions and group moves, and ensuring overall database consistency across operations. This would require the format to store a global change log rather than — or in addition to — per-entry history snapshots.
Source: #6229 (opening post)