KDBX 4
从 KDBX 3.1 文件格式更改为 KDBX 4。
介绍
Keepass 2.35 引入 KDBX 文件格式版本 4。新格式既有改进又有新功能。以下各节概述了这些内容。
自 KDBX 3.1 以来,内部 XML 格式进行了一些更改(比如头部哈希 HeaderHash 元元素已经过时;用于条目和组的新的自定义数据 CustomData 元素;新的设置更改 SettingChanged 元素)。包装的二进制格式有很大的变化。此外,加密和数据身份验证的顺序也会更改。
迁移说明。 由于并非所有主要的 KeePass 实现都已完成对 KDBX 4 的支持,因此,仅在满足以下至少一项条件时,KeePass 2.35 才以这种新格式保存数据库。
- 未选择 AES-KDF 作为密钥生成方法(KDBX 3.1 仅支持 AES-KDF;任何其他的密钥生成方法,都需要 KDBX 4,如 Argon2)。
- 插件需要在 KDBX 文件中存储自定义头数据 custom header data。
- 插件需要在条目或组中存储自定义数据 custom data。
从 KeePass 2.44 开始,选择 ChaCha20 作为文件加密算法也将强制使用 KDBX 4 格式。
一旦所有主要的 KeePass 实现都支持 KDBX 4,KeePass 将始终以这种格式保存。
Argon2 算法
从 KDBX 4 开始,可以使用 Argon2 密钥生成方法来转换复合主密钥(以防止字典攻击)。
Argon2 是 “密码哈希竞赛” 的获胜者。在 KDBX 3.1 之前,KeePass 使用 AES-KDF,一种基于迭代高级加密标准(AES)的密钥生成方法。Argon2 相对于 AES-KDF 的主要优势在于具有更好的抵抗 GPU/ASIC 攻击的能力(由于具有硬内存功能)。
用户现在可以在 AES-KDF 和 Argon2 之间进行选择。
对于开发者
可以在 Argon2Kdf.Core.cs 中找到 Argon2 实现。文件 Argon2Kdf.cs 定义了参数的默认值,并实现了密钥生成基础结构的方法。仅支持 Argon2 的 Argon2d 变体(对 GPU/ASIC 破解攻击的强大防御最重要,而在这方面 Argon2d 优于 Argon2i;旁通道定时攻击基本上是无关紧要的,因为 KeePass 是本地应用程序,而不是远程服务器)。
KeePass 的 Argon2 实现支持官方规范中定义的所有参数,但是用户只能在数据库设置对话框中配置迭代次数,内存大小和并行度。对于其他参数,KeePass 选择合理的默认值:每次保存数据库时,CSPRNG 都会生成 256 位乱数盐,标签长度为 256 位,没有密钥或相关数据。支持 Argon2d 的所有版本(1.0 到 1.3)。 默认情况下,KeePass 使用最新版本 1.3。
可扩展的密钥生成
插件现在可以提供其他密钥生成方法(用于转换复合主密钥)。
对于开发者
直到 KDBX 3.1,AES-KDF 的回合数存储在 ID 为 6(TransformRounds)的头部字段中,而转换的种子存储在 ID 为 5(TransformSeed)的头部字段中。 这两个字段现在已过时。
从 KDBX 4 开始,密钥生成方法的参数存储在 ID 为 11(KdfParameters)的头部字段中。参数被序列化为 VariantDictionary(KDF UUID 存储在 $UUID 中);有关 AES-KDF 和 Argon2 使用的参数的详细信息,请参阅 AesKdf.cs 和 Argon2Kdf.cs。
VariantDictionary 是一个键值字典(键是一个字符串,值是一个对象),其序列化如下:
- [2 字节] 版本,为 UInt16,小字节序,当前为 0x0100(1.0版)。高字节重要(即高字节太高可拒绝加载数据),低字节是信息性的(即可以忽略)。
- [n 个项目] n 个序列化项目(请参见下文)。
- [1 字节] 空终止符字符。
n 个序列化项目中的每一个都具有以下格式:
- [1 字节] 值类型,可以是以下之一:
- 0x04:UInt32
- 0x05:UInt64
- 0x08:Bool
- 0x0C:Int32
- 0x0D:Int64
- 0x18:String(UTF-8,不带 BOM,不带空终止符)
- 0x42:Byte 数组
- [4 字节] 键名长度 k,以字节为单位,Int32,小字节序。
- [k 字节] 键名(字符串,UTF-8,不带 BOM,不带空终止符)。
- [4 字节] 值长度 v,以字节为单位,Int32,小字节序。
- [v 字节] 值,整数以小字节序存储,Bool 类型为一个字节(false = 0,true = 1)。
在 KDBX 3.1 之前,头部字段的长度为 2 个字节。从 KDBX 4 开始,长度为 4 个字节。此更改使其他密钥生成方法的实现成为可能,其参数可能需要超过 64KB 的空间。此外,插件提供的头部数据可以大于 64KB。
改进的头部验证
在 KDBX 4 中,头数据使用 HMAC-SHA-256 进行验证。
直到 KDBX 3.1 为止,头部数据都是使用存储在数据库文件加密部分中的 SHA-256 哈希进行验证。KDBX 4 中使用的 HMAC-SHA-256 方法具有各种优点。优点之一就是 KeePass 可以在尝试解密其余部分之前验证头部,从而可以防止对不正确数据解密的尝试。
对于开发者
在 KDBX 4 中,XML 部分中的 HeaderHash 元素现在已过时,不再存储。必须使用 HMAC-SHA-256 进行新的头部验证。
直接在头部之后,存储头部的(未加密的)SHA-256 哈希(这允许在不知道主密钥的情况下检测意外损坏)。在哈希之后,直接存储头部的 HMAC-SHA-256 值。
改进的数据验证
在KDBX 4中,通过加密文本的 HMAC-SHA-256 对数据块进行验证(先加密后 MAC 方案)。
在KDBX 3.1之前的版本中,通过解密该数据块的 SHA-256 哈希(类似先 MAC 后加密)来验证该数据块的方案。尽管通常认为先加密后 MAC 方案(KDBX 4)比先 MAC 后加密方案(KDBX 3.1)更安全,但我们不认为 KDBX 3.1 文件是不安全的(因为特定情况下的 KDBX 文件经过验证的数据块通常很大,并且使用了 CBC 模式下的块密码)。无论如何,我们认为最好切换到通常被认为是最安全的方案(即使实际上在 KDBX 没啥区别)。此外,先加密后 MAC 具有实际优势。一个优点就是 KeePass 可以在尝试解密数据块之前验证它的真实性。这样可以避免块密码填充异常(对于 KDBX 3.1,KeePass 会自动将其转换为文件损坏异常;尽管这样做也行,但在遇到此类异常之前先检测不正确的数据会更优雅。
对于开发者
对于使用 HMAC-SHA-256 认证的加载/保存的数据块的流,请参阅新文件 HmacBlockStream.cs。该流类似于 KDBX 3.1 所使用的 HashedBlockStream,但是使用 HMAC 而不只是哈希。此外,在 KDBX 4 中,HMAC 是在密文上计算的,而在 KDBX 3.1 中,是计算明文哈希(然后与明文一起加密)。
由 HmacBlockStream 生成的单个块如下所示:
- [32 字节] HMAC-SHA-256 值(请参见下文)。
- [4 字节] 块大小 n(以字节为单位,最小 0,最大 ,0 表示最后一个块,小字节序)。
- [n 字节] 块数据 C(密文)。
HMAC 通过 i || n || C 计算(其中 64 位的序列数 i 和 32 位的块长度 n 使用小字节序;i 是隐式的不需要存储)。HMAC 每块数据的密钥都不同;计算方式为 , 其中 是从用户复合主密钥和保存在 KDBX 头中的主种子生成的 512 位密钥。
在头部及其 HMAC 之后即是机密数据,按以上方式分为多块。KeePass 2.35 写入 KDBX 文件时使用 ,即每块 1 MB。
可扩展的头部
KDBX 4 文件的头部可由插件扩展。
对于开发者
插件提供的头部数据存储在 ID 为 12 的头部字段中。其值为序列化的 VariantDictionary。
对于插件开发者
注意附加的头部数据不加密,仅存在于 KDBX 文件中(无法用 XML 导出)。 除非确实需要未解密的附加头部数据,否则强烈建议插件将附加数据存储在 PwDatabase 的 CustomData 字段中。CustomData 序列化在 XML 中,加密,并支持 XML 导出。
ChaCha20 算法
添加了对 ChaCha20 加密算法的支持(如 RFC 7539 所述,具有 256 位密钥和 96 位随机数)。
ChaCha20 是 Salsa20 算法(包含在 eSTREAM 产品中)的衍生算法。
可以在数据库设置对话框中将其设为 KDBX 文件的加密算法。
此外,ChaCha20 取代 Salsa20 作为默认选项,用于生成 KDBX 4 文件内部的随机流。
对于开发者
在 KDBX 3.1 之前,存储在 KDBX 头部(ID 为 7 的字段 EncryptionIV)中的加密 IV 始终长 16 个字节(128 位)。从 KDBX 4 开始,从密码实现中检索加密 IV 长度,并且 KDBX 头部字段存储了恰好此长度的加密 IV。对于 ChaCha20,即 12 个字节(96 位)。
对于插件开发人员
加密算法插件开发人员,请注意以下内容。默认的 IV 长度继续为 16 个字节(128 位)。如果您的加密引擎实现了该 ICipherEngine2 接口,则可以通过 IVLength 属性指定 IV 长度 。
可扩展的条目和组
插件现在可以将自定义数据存储在条目和组中。
到目前,插件已将自定义数据存储在条目的字符串字段中,从设计的角度来看并不理想。从 KDBX 4 开始,每个条目和每个组都有一个键值字典,插件可以在其中存储自定义数据。
对于开发人员
PwEntry 和 PwGroup 现在有一个 CustomData 属性。此属性与 PwDatabase 的 CustomData 属性类似,序列化(相同的 XML 结构)。
内部头
KDBX 4 文件具有二进制内部头。该头文件位于 XML 之前;压缩(若打开压缩选项)并加密。内部头之后为 XML 部分(相同的压缩和加密流)。
内部头可以存储条目附件,这是引入内部头的主要原因。直到 KDBX 3.1,条目附件都是用 Base64 编码,并存储在 XML 中。与此相比,KDBX 4 内部头的使用可以减小数据库文件大小并提高加载/保存性能。
对于开发者
内部头的结构类似于外部头。由以下形式的任意多个项目组成:
- [1 字节] 项目类型。
- [4 字节] 数据长度 n(Int32,小字节序)。
- [n 字节] 数据 D。
支持的项目类型:
- 0x00:头部结束标识。
- 0x01:内部随机流 ID(取代 KDBX 3.1 文件外部头中的内部随机流 ID)。
- 0x02:内部随机流密钥(取代 KDBX 3.1 文件外部头中的内部随机流密钥)。
- 0x03:二进制文件(条目附件)。,F 单字节,存储附件数据标识,M 为条目附件实际二进制数据。F 支持的标识:
- 0x01:用户已为此二进制文件打开进程内存保护。
内部头必须以 0x00 类型的项目结束(且 n = 0)。