From e3a0890e4f5b1122ee68063035c827e340d715e2 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sat, 19 Mar 2022 10:37:33 +0100 Subject: [PATCH] Reject invalid MAC and AEAD truncations Reject algorithms of the form PSA_ALG_TRUNCATED_MAC(...) or PSA_ALG_AEAD_WITH_SHORTENED_TAG(...) when the truncation length is invalid or not accepted by policy in Mbed TLS. This is done in KeyType.can_do, so in generate_psa_tests.py, keys will be tested for operation failure with this algorithm if the algorithm is rejected, and for storage if the algorithm is accepted. Signed-off-by: Gilles Peskine --- scripts/mbedtls_dev/crypto_knowledge.py | 53 ++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/scripts/mbedtls_dev/crypto_knowledge.py b/scripts/mbedtls_dev/crypto_knowledge.py index bd4d94ee82..7d2e755f63 100644 --- a/scripts/mbedtls_dev/crypto_knowledge.py +++ b/scripts/mbedtls_dev/crypto_knowledge.py @@ -20,7 +20,7 @@ This module is entirely based on the PSA API. import enum import re -from typing import Iterable, List, Optional, Tuple +from typing import FrozenSet, Iterable, List, Optional, Tuple from mbedtls_dev.asymmetric_key_data import ASYMMETRIC_KEY_DATA @@ -216,6 +216,8 @@ class KeyType: #pylint: disable=too-many-return-statements if alg.is_wildcard: return False + if alg.is_invalid_truncation(): + return False if self.head == 'HMAC' and alg.head == 'HMAC': return True if self.head == 'DES': @@ -420,8 +422,55 @@ class Algorithm: """ return short_expression(self.expression, level=level) + PERMITTED_TAG_LENGTHS = { + 'PSA_ALG_CCM': frozenset([4, 6, 8, 10, 12, 14, 16]), + 'PSA_ALG_CHACHA20_POLY1305': frozenset([16]), + 'PSA_ALG_GCM': frozenset([4, 8, 12, 13, 14, 15, 16]), + } + MAC_LENGTH = { + 'PSA_ALG_CBC_MAC': 16, + 'PSA_ALG_CMAC': 16, + 'PSA_ALG_HMAC(PSA_ALG_MD5)': 16, + 'PSA_ALG_HMAC(PSA_ALG_SHA_1)': 20, + } + HMAC_WITH_NOMINAL_LENGTH_RE = re.compile(r'PSA_ALG_HMAC\(\w+([0-9])+\)\Z') + @classmethod + def mac_or_tag_length(cls, base: str) -> FrozenSet[int]: + """Return the set of permitted lengths for the given MAC or AEAD tag.""" + if base in cls.PERMITTED_TAG_LENGTHS: + return cls.PERMITTED_TAG_LENGTHS[base] + max_length = cls.MAC_LENGTH.get(base, None) + if max_length is None: + m = cls.HMAC_WITH_NOMINAL_LENGTH_RE.match(base) + if m: + max_length = int(m.group(1)) // 8 + if max_length is None: + raise ValueError('Unknown permitted lengths for ' + base) + return frozenset(range(4, max_length + 1)) + + TRUNCATED_ALG_RE = re.compile( + r'(?PPSA_ALG_(?:AEAD_WITH_SHORTENED_TAG|TRUNCATED_MAC))' + r'\((?P.*),' + r'(?P0[Xx][0-9A-Fa-f]+|[1-9][0-9]*|0[0-9]*)[LUlu]*\)\Z') + def is_invalid_truncation(self) -> bool: + """False for a MAC or AEAD algorithm truncated to an invalid length. + + True for a MAC or AEAD algorithm truncated to a valid length or to + a length that cannot be determined. True for anything other than + a truncated MAC or AEAD. + """ + m = self.TRUNCATED_ALG_RE.match(self.expression) + if m: + base = m.group('base') + to_length = int(m.group('length'), 0) + permitted_lengths = self.mac_or_tag_length(base) + if to_length not in permitted_lengths: + return True + return False + def can_do(self, category: AlgorithmCategory) -> bool: - """Whether this algorithm fits the specified operation category.""" + """Whether this algorithm can perform operations in the given category. + """ if category == self.category: return True if category == AlgorithmCategory.KEY_DERIVATION and \