[在WebCrypto中使用AES-CBC时,为什么错误的密钥会导致OperationError,但是密文或IV不会导致错误?

问题描述 投票:0回答:1

据我了解,使用错误的密钥(具有正确的大小)使用AES-CBC解密内容应该只会输出一些垃圾。 CBC没有任何类型的MAC,因此您实际上只能查看解密的结果,并自行决定是否是您想要的纯文本。

但是,用SubtleCrypto解密时,错误的密钥将导致OperationError,但错误的密文不会,并且IV也不错误。我本来希望这三种情况都有类似的行为。

实现如何可能知道密钥是错误的,而不是其他任何输入?除大小外,键是否必须具有特定的结构?在这种情况下,密钥空间将小于密钥的广告位长度,不是吗?

async function simpleCryptoTest() {
    // all zeroes plaintext, key and IV
    const iv = new ArrayBuffer(16)
    const key = new ArrayBuffer(32)
    const plaintext = new ArrayBuffer(64)

    const algorithm = {name: 'AES-CBC'};
    const correctCryptoKey = await crypto.subtle.importKey('raw', key, algorithm, false, ['encrypt', 'decrypt'])
    const ciphertext = await crypto.subtle.encrypt({...algorithm, iv: iv}, correctCryptoKey, plaintext)
    console.log("ciphertext", ciphertext)

    const decryptedCorrect = crypto.subtle.decrypt({...algorithm, iv: iv}, correctCryptoKey, ciphertext)

    const wrongCiphertext = new Uint8Array(ciphertext)
    wrongCiphertext[0] = ~ciphertext[0] // flipping the first byte should be enough
    const decryptedWrongCiphertext = crypto.subtle.decrypt({...algorithm, iv: iv}, correctCryptoKey, wrongCiphertext)

    const wrongIv = new Uint8Array(iv)
    wrongIv[0] = 1 // we know the correct IV is all zeroes
    const decryptedWrongIv = crypto.subtle.decrypt({...algorithm, iv: wrongIv}, correctCryptoKey, ciphertext)

    const wrongKey = new Uint8Array(key)
    wrongKey[0] = ~key[0]
    const decryptedWrongKey = crypto.subtle.importKey('raw', wrongKey, algorithm, false, ['decrypt']).then((wrongCryptoKey) => {
        return crypto.subtle.decrypt({...algorithm, iv: iv}, wrongCryptoKey, ciphertext)
    })

    const results = await Promise.allSettled([decryptedCorrect, decryptedWrongCiphertext, decryptedWrongIv, decryptedWrongKey])
    console.log("decrypted with the correct key", results[0])
    console.log("decrypted with corrupted ciphertext", results[1])
    console.log("decrypted with corrupted IV", results[2])
    console.log('decrypted with the wrong key', results[3])
}

simpleCryptoTest()

/*
    decrypted with the correct key → {status: "fulfilled", value: ArrayBuffer(64)}
    decrypted with corrupted ciphertext → {status: "fulfilled", value: ArrayBuffer(64)}
    decrypted with corrupted IV → {status: "fulfilled", value: ArrayBuffer(64)}
    decrypted with the wrong key → {status: "rejected", reason: DOMException} // e.name == 'OperationError'
*/

[请注意,我知道CBC没有身份验证,并且我知道GCM存在。我需要CBC,因为我正在实现Signal Protocol的变体,如果没有适当的加密审查,我很肯定不打算在生产中推出它。谢谢:-)

而且,我也在Linux的Firefox 77.0.1和Chromium 83.0.4103.97上进行了测试。

javascript cryptography aes webcrypto-api
1个回答
0
投票

没有MAC,但是有填充。我对WebCrypto不太熟悉,但是您可能会在加密算法规范中使用PKCS7填充,无论是显式的还是默认的。添加到明文末尾的填充字节的值是kk ... k,其中k是所需填充字节的数量,1 <= k <=16。解密后,检查最后一个字节k是否在指定的范围,以及最后k个字节是否等于k。如果该检查失败,则说明出现了问题,并返回了OperationError。

现在,对于损坏的IV和损坏的密文,它起作用的原因是CBC模式的“功能”。如果仔细查看diagram of the decrypt directionCBC mode,您会注意到以下事实(请记住,这是关于解密的):

  1. 损坏的IV仅影响第一段明文。所有其余的都正确解密
  2. 损坏的密文仅影响当前的明文块和下一个块。正确解密前后的所有块。

因此,请尝试在最后一个块之前更改密文块,您应该会看到OperationError。但是,填充检查不能替代真实的MAC,并且即使密钥已损坏或最后一个或倒数第二个密文块,填充检查仍会成功的可能性很大。如果最终解密块的最后一个字节等于1,则填充检查成功。对于列出的损坏项目,此可能性为1/256。 (实际上要高一点,因为如果最后两个字节等于2,或者最后3个字节等于3,...,依此类推,则填充检查也会成功)。因此,作为实验,请尝试更改密钥的两个字节大约500次左右,并且您应该在1或2个实例中成功解密,而不会出现错误。

© www.soinside.com 2019 - 2024. All rights reserved.