CVE-2023-3674: Other (NVD-CWE-Other)
value: A flaw was found in the keylime attestation verifier, which fails to flag a device's submitted TPM quote as faulty when the quote's signature does not validate for some reason. Instead, it will only emit an error in the log without flagging the device as untrusted.
CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
CVSS v3 base score: 2.8
Published: 2023-07-19T19:15Z
Repository: https://github.com/keylime/keylime
Language: Python
Files changed: keylime/tpm/tpm_util.py, keylime/tpm/tpm_util_test.py
Commit date: 2023-07-12 10:58:57 -0400
Commit message: tpm_util: Replace a logger.error with an Exception in case of invalid signature

This fixes a possibly severe issue in 7.2.5 & 7.3.0.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

Diff: diff --git a/keylime/tpm/tpm_util.py b/keylime/tpm/tpm_util.py
index a0666deb..30ad7074 100644
--- a/keylime/tpm/tpm_util.py
+++ b/keylime/tpm/tpm_util.py
@@ -1,11 +1,10 @@
 import os
 import string
 import struct
 from typing import Any, Dict, List, Optional, Tuple, Union
 
-from cryptography.exceptions import InvalidSignature
 from cryptography.hazmat import backends
 from cryptography.hazmat.primitives import hashes, hmac, serialization
 from cryptography.hazmat.primitives.asymmetric import ec, padding
 from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey
 from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
@@ -153,14 +152,11 @@ def checkquote(
 
     digest = hashes.Hash(hashfunc, backend=backends.default_backend())
     digest.update(quoteblob)
     quote_digest = digest.finalize()
 
-    try:
-        verify(pubkey, signature, quote_digest, hashfunc)
-    except InvalidSignature:
-        logger.error("Invalid quote signature!")
+    verify(pubkey, signature, quote_digest, hashfunc)
 
     # Check that reported nonce is expected one
     retDict = tpm2_objects.unmarshal_tpms_attest(quoteblob)
     extradata = retDict["extraData"]
     if extradata.decode("utf-8") != nonce:
diff --git a/keylime/tpm/tpm_util_test.py b/keylime/tpm/tpm_util_test.py
index aaf16cdc..2c73997a 100644
--- a/keylime/tpm/tpm_util_test.py
+++ b/keylime/tpm/tpm_util_test.py
@@ -1,9 +1,10 @@
 import base64
 import unittest
 from unittest import mock
 
+from cryptography.exceptions import InvalidSignature
 from cryptography.hazmat.primitives.asymmetric.ec import (
     SECP256R1,
     EllipticCurve,
     EllipticCurvePrivateKey,
     EllipticCurvePrivateNumbers,
@@ -58,10 +59,30 @@ class TestTpmUtil(unittest.TestCase):
         try:
             checkquote(aikblob, nonce, sigblob, quoteblob, pcrblob, "sha256")
         except Exception as e:
             self.fail(f"checkquote failed with {e}")
 
+        # test bad input
+        bad_quoteblob = bytearray(quoteblob)
+        bad_quoteblob[5] ^= 0x1
+        with self.assertRaises(InvalidSignature):
+            checkquote(aikblob, nonce, sigblob, bad_quoteblob, pcrblob, "sha256")
+
+        l = list(nonce)
+        l[0] = "a"
+        bad_nonce = "".join(l)
+        with self.assertRaises(Exception):
+            checkquote(aikblob, bad_nonce, sigblob, quoteblob, pcrblob, "sha256")
+
+        bad_pcrblob = bytearray(pcrblob)
+        bad_pcrblob[5] ^= 0x1
+        with self.assertRaises(Exception):
+            checkquote(aikblob, nonce, sigblob, quoteblob, bad_pcrblob, "sha256")
+
+        with self.assertRaises(ValueError):
+            checkquote(aikblob, nonce, sigblob, quoteblob, pcrblob, "sha1")
+
     @staticmethod
     def not_random(numbytes: int) -> bytes:
         return b"\x12" * numbytes
 
     def test_makecredential(self) -> None:

Vulnerable code: from cryptography.exceptions import InvalidSignature
    try:
        verify(pubkey, signature, quote_digest, hashfunc)
    except InvalidSignature:
        logger.error("Invalid quote signature!")
Fixed code:     verify(pubkey, signature, quote_digest, hashfunc)
from cryptography.exceptions import InvalidSignature
        # test bad input
        bad_quoteblob = bytearray(quoteblob)
        bad_quoteblob[5] ^= 0x1
        with self.assertRaises(InvalidSignature):
            checkquote(aikblob, nonce, sigblob, bad_quoteblob, pcrblob, "sha256")

        l = list(nonce)
        l[0] = "a"
        bad_nonce = "".join(l)
        with self.assertRaises(Exception):
            checkquote(aikblob, bad_nonce, sigblob, quoteblob, pcrblob, "sha256")

        bad_pcrblob = bytearray(pcrblob)
        bad_pcrblob[5] ^= 0x1
        with self.assertRaises(Exception):
            checkquote(aikblob, nonce, sigblob, quoteblob, bad_pcrblob, "sha256")

        with self.assertRaises(ValueError):
            checkquote(aikblob, nonce, sigblob, quoteblob, pcrblob, "sha1")