cryptography
cryptography copied to clipboard
AES_GCM_SIV does not accept empty plaintexts.
A test I have started failing recently when using AES-GCM-SIV with empty plaintexts.
The failing test is
from cryptography.hazmat.primitives.ciphers import aead
import cryptography
import sys
def test_empty_pt():
key = bytes(range(16))
nonce = bytes(range(12))
pt = b""
aad = b"associated data"
crypter = aead.AESGCMSIV(key)
ct = crypter.encrypt(nonce, pt, aad)
if __name__ == "__main__":
print(f"{sys.version=}")
print(f"{cryptography.__version__=}")
test_empty_pt()
This gives the following output
sys.version='3.12.3 (tags/v3.12.3:f6650f9, Apr 9 2024, 14:05:25) [MSC v.1938 64 bit (AMD64)]'
cryptography.version='42.0.5'
Traceback (most recent call last):
File "...pyca/aesgcmsiv.py", line 16, in
The exception is thrown for all supported key sizes of AES-GCM-SIV. No other AEAD mode seems to be affected. Other parameter sizes give the expected results. The test passed before updating to the Python version 3.12.3 and cryptography version 42.0.5.
We only added AES-GCM-SIV support in cryptography 42.0.0, so you were seeing empty PT succeed in 42.0.x but failing in 42.0.5?
You are probably right. I can't find a test log where AES-GCM-SIV did run and all tests passed.
I don't see a reason why we shouldn't support empty PT so I'll put in a PR to improve this 😄
It looks like OpenSSL doesn't support 0 length PT (see reproducer below). I'll file an upstream issue soon.
#include <assert.h>
#include <stdio.h>
#include <openssl/evp.h>
int main() {
EVP_CIPHER_CTX *ctx;
unsigned char key[16] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned char nonce[12] = {3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned char pt[12] = {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int pt_len = 12;
unsigned char aad[1] = {1};
int aad_len = 1;
unsigned char tag[16];
unsigned char outbuf[16];
int outlen;
ctx = EVP_CIPHER_CTX_new();
EVP_CIPHER *ciph = EVP_CIPHER_fetch(NULL, "aes-128-gcm-siv", NULL);
assert(ciph != NULL);
assert(EVP_EncryptInit_ex(ctx, ciph, NULL, key, nonce) == 1);
assert(EVP_EncryptUpdate(ctx, NULL, &outlen, aad, aad_len) == 1);
assert(EVP_EncryptUpdate(ctx, outbuf, &outlen, pt, pt_len) == 1);
// assert(EVP_EncryptUpdate(ctx, outbuf, &outlen, NULL, 0) == 1); // uncomment this and it will not fail
assert(EVP_EncryptFinal_ex(ctx, outbuf, &outlen) == 1);
assert(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, tag) == 1);
EVP_CIPHER_CTX_free(ctx);
printf("Tag: ");
for (int i = 0; i < 16; i++) {
printf("%02x", tag[i]);
}
printf("\n");
return 0;
}
Note: Someone should file an upstream bug with OpenSSL for this.