6. 私钥格式 (Private Key Format)
[FIPS203] 为 ML-KEM private key 规定了两种格式: 64-octet seed 和一个 (展开的) private key, 后者称为 decapsulation key.展开的 private key (以及 public key) 使用 ML-KEM.KeyGen_internal(d,z) (算法 16) 从 seed 计算得到, 其中前 32 个 octets 用作 d, 其余 32 个 octets 用作 z.如果生成展开的 private key 时不导出 seed, 则使用 ML-KEM.KeyGen() (算法 19); 它将 seed 生成与 ML-KEM.KeyGen_internal(d,z) 结合起来.
密钥对通过从密码学安全伪随机数生成器 (cryptographically secure pseudorandom number generator, CSPRNG) 中均匀随机采样 64 个 octets 作为 seed (private key) 生成.随后可以按前述方式使用 ML-KEM.KeyGen_internal(d,z) 计算 public key.
"Asymmetric Key Packages" [RFC5958] 描述了如何在一种结构中编码 private key, 该结构既标识 private key 所属的算法, 也允许同时包含 public key 以及关于该密钥的其他属性.为便于说明, 下面复制 ASN.1 结构 OneAsymmetricKey.
OneAsymmetricKey ::= SEQUENCE {
version Version,
privateKeyAlgorithm SEQUENCE {
algorithm PUBLIC-KEY.&id({PublicKeySet}),
parameters PUBLIC-KEY.&Params({PublicKeySet}
{@privateKeyAlgorithm.algorithm})
OPTIONAL}
privateKey OCTET STRING (CONTAINING
PUBLIC-KEY.&PrivateKey({PublicKeySet}
{@privateKeyAlgorithm.algorithm})),
attributes [0] Attributes OPTIONAL,
...,
[[2: publicKey [1] BIT STRING (CONTAINING
PUBLIC-KEY.&Params({PublicKeySet}
{@privateKeyAlgorithm.algorithm})
OPTIONAL ]],
...
}
对于 ML-KEM private key, OneAsymmetricKey 中的 privateKey 字段包含下列 DER 编码 CHOICE 结构之一.对于所有安全级别, seed 格式都是固定的 64-byte OCTET STRING (加上 0x8040 tag 和 length 后总计 66 bytes), 而 expandedKey 和 both 格式的大小随安全级别变化:
ML-KEM-512-PrivateKey ::= CHOICE {
seed [0] OCTET STRING (SIZE (64)),
expandedKey OCTET STRING (SIZE (1632)),
both SEQUENCE {
seed OCTET STRING (SIZE (64)),
expandedKey OCTET STRING (SIZE (1632))
}
}
ML-KEM-768-PrivateKey ::= CHOICE {
seed [0] OCTET STRING (SIZE (64)),
expandedKey OCTET STRING (SIZE (2400)),
both SEQUENCE {
seed OCTET STRING (SIZE (64)),
expandedKey OCTET STRING (SIZE (2400))
}
}
ML-KEM-1024-PrivateKey ::= CHOICE {
seed [0] OCTET STRING (SIZE (64)),
expandedKey OCTET STRING (SIZE (3168)),
both SEQUENCE {
seed OCTET STRING (SIZE (64)),
expandedKey OCTET STRING (SIZE (3168))
}
}
该 CHOICE 允许 private key 使用三种表示形式:
-
seed 格式 (tag [0]) 仅包含 64-byte seed 值.使用 ML-KEM.KeyGen_internal(d,z) (算法 16), 并将前 32 个 octets 作为 d, 其余 32 个 octets 作为 z, 即可从该值派生展开的 private key 和 public key.
-
expandedKey 格式包含从 seed 派生得到的展开 private key.如果 seed 未导出, 可以使用 ML-KEM.KeyGen() (算法 16) 派生展开的 private key 和 public key.
-
both 格式同时包含 seed 和展开的 private key, 从而支持互操作; 有些实现可能希望使用并保留 seed, 另一些实现可能只支持展开的 private key.
privateKeyAlgorithm 字段使用第 3 节定义的 AlgorithmIdentifier 结构和相应 OID.
publicKey 字段包含 public key 的字节流.如果存在, publicKey 字段将保存第 4 节所定义的编码 public key.
注意, 虽然 private key 可以以多种格式存储, 但推荐使用 seed-only 格式, 因为它是最紧凑的表示形式.使用 ML-KEM.KeyGen_internal(d,z) (算法 16), 并将前 32 个 octets 作为 d, 其余 32 个 octets 作为 z, 可以从 seed 确定性地派生展开的 private key 和 public key.或者, public key 也可以从展开的 private key 中提取.使用 seed-only 格式时, publicKey 字段和 expandedKey 格式在技术上是冗余的, 但可以包含它们, 以便在导入操作期间执行密钥对一致性检查.
解析 private key 时, ASN.1 tag 会明确指出 CHOICE 中存在的变体.实现应该使用上下文特定 tag IMPLICIT [0] (原始值 0x80) 解析 seed, 使用 OCTET STRING (0x04) 解析 expandedKey, 使用 SEQUENCE (0x30) 解析 both, 而不是使用封装 OCTET STRING 长度等任何其他启发式方法.
附录 C.1 包含使用 [RFC7468] 所定义文本编码方式编码的 ML-KEM private key 示例.