test(core): adjust aes-gcm python unit tests
What changed, and why it matters
This commit only changes Python unit tests for the AES-GCM cryptography code in the Trezor firmware. It updates existing tests to pass an expected authentication tag to the finish() method and adds new tests that check the code correctly rejects missing, wrong-length, or invalid tags. There is no change to the actual cryptographic implementation or firmware behavior, so this commit does not introduce or fix a live security vulnerability by itself.
No immediate action is required for this test-only commit. If reviewing a related series, verify that the corresponding production AES-GCM implementation change that made expected_tag mandatory has been audited and that all decryption call sites in firmware supply and validate the tag correctly.
Security signals we found
AES-GCM authentication tag verification is being tested
New tests assert rejection of missing, wrong-length, and invalid tags
No production code or cryptographic implementation changes are present in the diff
Evidence from the diff
The diff modifies core/tests/test_trezor.crypto.aesgcm.py. All prior calls to ctx.finish() during decryption are changed to ctx.finish(tag), matching an API that now requires the expected tag for decryption verification. Three new test cases are added: one verifying that finish() without a tag raises RuntimeError when decrypting, one verifying that tags of invalid length raise ValueError, and one verifying that a 16-byte incorrect tag raises RuntimeError (‘Authentication failed.’). These are defensive test additions; the underlying aesgcm implementation is not shown changing here.
Changed components
core/tests/test_trezor.crypto.aesgcm.pyInspect captured patch +64 / −6
diff --git a/core/tests/test_trezor.crypto.aesgcm.py b/core/tests/test_trezor.crypto.aesgcm.py
index 9f2bc816..44775e79 100644
--- a/core/tests/test_trezor.crypto.aesgcm.py
+++ b/core/tests/test_trezor.crypto.aesgcm.py
@@ -60,7 +60,7 @@ class TestCryptoAes(unittest.TestCase):
if aad:
ctx.auth(aad)
self.assertEqual(ctx.decrypt(ct), pt)
- self.assertEqual(ctx.finish(), tag)
+ self.assertEqual(ctx.finish(tag), tag)
def test_gcm_in_place(self):
for vector in self.vectors:
@@ -83,7 +83,7 @@ class TestCryptoAes(unittest.TestCase):
returned = ctx.decrypt_in_place(buffer)
self.assertEqual(buffer, pt)
self.assertEqual(returned, len(buffer))
- self.assertEqual(ctx.finish(), tag)
+ self.assertEqual(ctx.finish(tag), tag)
def test_gcm_chunks(self):
for vector in self.vectors:
@@ -97,7 +97,7 @@ class TestCryptoAes(unittest.TestCase):
ctx.auth(aad[:17])
self.assertEqual(ctx.decrypt(ct[chunk1:]), pt[chunk1:])
ctx.auth(aad[17:])
- self.assertEqual(ctx.finish(), tag)
+ self.assertEqual(ctx.finish(tag), tag)
# Encrypt by chunks and add authenticated data by chunks.
ctx.reset(iv)
@@ -105,7 +105,7 @@ class TestCryptoAes(unittest.TestCase):
self.assertEqual(ctx.encrypt(pt[:chunk1]), ct[:chunk1])
ctx.auth(aad[7:])
self.assertEqual(ctx.encrypt(pt[chunk1:]), ct[chunk1:])
- self.assertEqual(ctx.finish(), tag)
+ self.assertEqual(ctx.finish(tag), tag)
def test_gcm_chunks_in_place(self):
for vector in self.vectors:
@@ -123,7 +123,7 @@ class TestCryptoAes(unittest.TestCase):
ctx.auth(aad[17:])
self.assertEqual(returned, chunk2_length)
self.assertEqual(buffer, pt)
- self.assertEqual(ctx.finish(), tag)
+ self.assertEqual(ctx.finish(tag), tag)
# Encrypt by chunks and add authenticated data by chunks.
ctx.reset(iv)
@@ -134,7 +134,65 @@ class TestCryptoAes(unittest.TestCase):
returned = ctx.encrypt_in_place(memoryview(buffer)[chunk1_length:])
self.assertEqual(returned, chunk2_length)
self.assertEqual(buffer, ct)
- self.assertEqual(ctx.finish(), tag)
+ self.assertEqual(ctx.finish(tag), tag)
+
+ def test_gcm_missing_expected_tag(self):
+ for vector in self.vectors:
+ key, iv, pt, aad, ct, _ = map(unhexlify, vector)
+
+ ctx = aesgcm(key, iv)
+ if aad:
+ ctx.auth(aad)
+ self.assertEqual(ctx.decrypt(ct), pt)
+
+ # Try finishing the decryption with expected_tag missing
+ with self.assertRaises(RuntimeError) as e:
+ ctx.finish()
+ self.assertEqual(
+ e.value.value, "Argument `expected_tag` is required when decrypting."
+ )
+
+ def test_gcm_invalid_tag_len(self):
+ for vector in self.vectors:
+ key, iv, pt, aad, ct, _tag = map(unhexlify, vector)
+ invalid_tags = [
+ b"",
+ b"\x00",
+ _tag[:15],
+ b"\x00" + _tag,
+ _tag + _tag,
+ ]
+
+ for tag in invalid_tags:
+ ctx = aesgcm(key, iv)
+ if aad:
+ ctx.auth(aad)
+ self.assertEqual(ctx.decrypt(ct), pt)
+
+ # Try finishing the decryption with invalid-length tag
+ with self.assertRaises(ValueError) as e:
+ ctx.finish(tag)
+ self.assertEqual(
+ e.value.value,
+ "Invalid length of the tag. It has to be 16 bytes.",
+ )
+
+ def test_gcm_invalid_tag(self):
+ invalid_tag = b"\xab" * 16
+ for vector in self.vectors:
+ key, iv, pt, aad, ct, _ = map(unhexlify, vector)
+ ctx = aesgcm(key, iv)
+ if aad:
+ ctx.auth(aad)
+ self.assertEqual(ctx.decrypt(ct), pt)
+
+ # Try finishing the decryption with invalid tag
+ with self.assertRaises(RuntimeError) as e:
+ ctx.finish(invalid_tag)
+ self.assertEqual(
+ e.value.value,
+ "Authentication failed.",
+ )
if __name__ == "__main__":
Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.