跳到主要内容

5.2 Encryption and Decryption (加密和解密)

5.2 Encryption and Decryption (加密和解密)

HPKE 允许基于给定的建立事务执行多个加密操作.由于建立中涉及的公钥操作通常比对称加密或解密更昂贵, 这允许应用程序分摊公钥操作的成本, 从而降低整体开销.

然而, 为了避免 nonce 重用, 此加密必须是有状态的.上述每个建立过程都会生成一个特定于角色的上下文对象, 该对象存储 AEAD 和秘密导出参数.AEAD 参数包括:

  • 正在使用的 AEAD 算法

  • 秘密密钥

  • 基础 nonce base_nonce

  • 序列号 (最初为 0)

秘密导出参数包括:

  • 正在使用的 HPKE 密码套件, 以及

  • 用于秘密导出接口的 exporter_secret (请参见 5.3 节)

除 AEAD 序列号外, 所有这些参数都是恒定的.序列号提供 nonce 唯一性: 用于每个加密或解密操作的 nonce 是将 base_nonce 与当前序列号进行异或的结果, 序列号编码为与 base_nonce 长度相同的大端整数.实现可以使用短于 nonce 长度的序列号 (在左侧用零填充), 但如果序列号溢出, 则必须引发错误.AEAD 算法生成的密文比明文长 Nt 字节.对于本文档中定义的 AEAD 算法, Nt = 16.

加密是从发送者到接收者的单向.发送者的上下文可以使用关联数据 aad 加密明文 pt, 如下所示:

def ContextS.Seal(aad, pt):
ct = Seal(self.key, self.ComputeNonce(self.seq), aad, pt)
self.IncrementSeq()
return ct

接收者的上下文可以使用关联数据 aad 解密密文 ct, 如下所示:

def ContextR.Open(aad, ct):
pt = Open(self.key, self.ComputeNonce(self.seq), aad, ct)
if pt == OpenError:
raise OpenError
self.IncrementSeq()
return pt

每个加密或解密操作都会递增正在使用的上下文的序列号.每条消息的 nonce 和序列号递增详细信息如下:

def Context<ROLE>.ComputeNonce(seq):
seq_bytes = I2OSP(seq, Nn)
return xor(self.base_nonce, seq_bytes)

def Context<ROLE>.IncrementSeq():
if self.seq >= (1 << (8*Nn)) - 1:
raise MessageLimitReachedError
self.seq += 1

发送者的上下文绝对不能用于解密.同样, 接收者的上下文绝对不能用于加密.重用 HPKE 密钥交换用于更一般目的的更高级别协议可以根据需要使用秘密导出接口派生单独的密钥材料; 有关更多详细信息, 请参见 5.3 节和 9.8 节.

应用程序需要确保以正确的顺序进行加密和解密, 以便加密和解密 nonce 对齐.如果 ContextS.Seal() 或 ContextR.Open() 会导致 seq 字段溢出, 则实现必须失败并返回错误.(在下面的伪代码中, 当 seq 溢出时, Context<ROLE>.IncrementSeq() 会失败并返回错误, 这会导致 ContextS.Seal() 和 ContextR.Open() 相应地失败.) 请注意, 内部的 Seal() 和 Open() 调用对应于上下文的 AEAD 算法.