本文介绍了DAEAD(具有关联数据的确定性认证加密)中的SIV模式,它通过合成初始化向量来克服nonce重用/误用的问题。SIV模式结合确定性特征与AEAD,使用AES-SIV模式进行密钥包装,增强了加密的安全性,并提供了使用Go语言的Tink库实现DAEAD的示例代码。
密码学中有一个核心原则,即输出可以是确定性的,也可以是非确定性的。对于确定性输出,对于相同的输入,我们总是得到相同的输出。例如,我们可以从明文创建一个密码,其输出将始终是相同的密文。对于非确定性输出,我们无法预测输出将是什么,因为在该过程中使用了一些形式的随机化。对于签名,ECDSA 是非确定性的,而 EdDSA 是确定性的。
当涉及到对称密钥方法时,确定性性质在搜索数据时可能很有用,但从安全角度来看,它会比较弱,因为攻击者可以将输入映射到输出。在这种情况下,我们将确定性性质与带有相关数据的认证加密(Deterministic AEAD)相结合,并使用 AES-SIV 模式。
我们泄露了太多的秘密,并且常常未能保护我们最重要的秘密之一……我们的加密密钥。为了克服这个问题,我们可以使用一种称为密钥包装的方法来保护密钥。这在通过不受信任的通道传输密钥或将其存储在没有严格访问控制的地方时尤其重要。对于密钥包装,Rogaway 等人提出了 SIV(合成初始化向量)方法 [ 这里],它对密钥进行认证和加密,并对与密钥相关的任何附加数据进行认证:
该方法现在已通过 RFC 5297 标准化 [ 这里]。通过增强的加密方法,我们可以同时对密码进行认证并证明其完整性。这被称为带有相关数据的认证加密(AEAD)。为此,我们提供额外的数据来认证加密过程,并且我们可以识别密文已被修改的位置,因此无法对其进行解密。对于大多数传统的 AEAD 方法,我们创建一个 nonce 值并添加额外的数据 (AD),这些数据经过认证但未加密。对于无 nonce 的方法,我们可以使用密钥包装方法,该方法通常用于保护加密密钥。附加数据可以包括 [ 这里]:
地址、端口、序列号、协议版本号以及指示如何处理、转发或处理明文或密文的其他字段。
通过这种方式,我们可以将网络数据包绑定到加密数据并提供完整性,以便入侵者无法从其他传输中复制和粘贴有效的密文。例如,如果我们绑定到数据包序列号和端口,则对于另一个序列号或另一个端口,认证将失败。
在以下代码中,我们将使用“101112131415161718191a1b1c1d1e1f2021222324252627”的附加信息,但在实践中,它可以是任何长度。在 SIV 中,我们也可以有多个此附加数据的来源(称为字符串向量)。此信息可能分布在多个来源,并且入侵者可能难以捕获和复制。这是 SIV 的特殊功能之一,它允许使用多个字符串进行认证,而不必将它们合并为用于附加信息的单个字符串:
byte[] plaintext = pl.getBytes();
String additional="101112131415161718191a1b1c1d1e1f2021222324252627";
byte[] ciphertext = daead.encryptDeterministically(plaintext, Hex.decode(additional));
传统的 AEAD 方法在 nonce 重用以及 nonce 滥用方面可能很弱。在许多系统中,我们使用生成 nonce 的非确定性方法,因此无法知道以前生成的值是否已被使用过。另一个弱点是可以通过回滚虚拟机并发现使用的 nonce 值来“回放”nonce。因此,大多数 AEAD 方法在使用唯一 nonce 时是安全的,但如果 nonce 不是唯一的,则无法保证安全性。由于 nonce 值的尺寸有限,因此始终有可能使用相同的密钥重用它,因此我们可能需要定期更改密钥。SIV 为 nonce 重用/滥用提供了更多保护,攻击者只能确定给定的明文值和给定的关联数据集是使用定义的密钥和 nonce 值保护的。
在以下代码中,我们创建了一个 SIV 密钥,然后创建了密文值,这两个值应该是相同的 [ 这里]:
package main
import (
"fmt"
"os"
"github.com/tink-crypto/tink-go/v2/daead"
"github.com/tink-crypto/tink-go/v2/keyset"
)
func main() {
msg:="Hello"
associated:="Hello"
argCount := len(os.Args[1:])
if (argCount>0) {msg = os.Args[1]}
if (argCount>1) {associated =os.Args[2]}
handle, _ := keyset.NewHandle(daead.AESSIVKeyTemplate())
primitive, _ := daead.New(handle)
plaintext := []byte(msg)
associatedData := []byte(associated)
ciphertext1, _:= primitive.EncryptDeterministically(plaintext, associatedData)
ciphertext2, _:= primitive.EncryptDeterministically(plaintext, associatedData)
res, _ := primitive.DecryptDeterministically(ciphertext1, associatedData)
fmt.Printf("Plaintext is %s\n",msg)
fmt.Printf("Associated data is %s\n\n",associated)
fmt.Printf("Key is %s\n\n",handle)
fmt.Printf("Cipher1 is %x\n\n",ciphertext1)
fmt.Printf("Cipher2 is %x\n\n",ciphertext2)
fmt.Printf("Decrypted is %s",res)
}
一个示例运行表明,当我们以确定方式加密时,我们具有相同的密码 [ 这里]:
Plaintext is Testing 123
Associated data is qwerty123
Key is primary_key_id:1537855825 key_info:{type_url:"type.googleapis.com/google.crypto.tink.AesSivKey" status:ENABLED key_id:1537855825 output_prefix_type:TINK}
Cipher1 is 015ba9d1518f3a5d213ce9630294f87af76ce86011c8631842aa96571d0dee03
Cipher2 is 015ba9d1518f3a5d213ce9630294f87af76ce86011c8631842aa96571d0dee03
Decrypted is Testing 123
- 原文链接: billatnapier.medium.com/...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!