blob: 7d2e755f631ef00ea0cc81926edd94c30b2917c3 [file] [log] [blame]
Gilles Peskine0156a152021-01-26 21:23:56 +01001"""Knowledge about cryptographic mechanisms implemented in Mbed TLS.
2
3This module is entirely based on the PSA API.
4"""
5
6# Copyright The Mbed TLS Contributors
7# SPDX-License-Identifier: Apache-2.0
8#
9# Licensed under the Apache License, Version 2.0 (the "License"); you may
10# not use this file except in compliance with the License.
11# You may obtain a copy of the License at
12#
13# http://www.apache.org/licenses/LICENSE-2.0
14#
15# Unless required by applicable law or agreed to in writing, software
16# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18# See the License for the specific language governing permissions and
19# limitations under the License.
20
Gilles Peskineee7554e2021-04-29 20:38:01 +020021import enum
Gilles Peskine0156a152021-01-26 21:23:56 +010022import re
Gilles Peskinee3a08902022-03-19 10:37:33 +010023from typing import FrozenSet, Iterable, List, Optional, Tuple
Gilles Peskine0156a152021-01-26 21:23:56 +010024
Gilles Peskine6f6483f2021-01-27 12:43:24 +010025from mbedtls_dev.asymmetric_key_data import ASYMMETRIC_KEY_DATA
26
Gilles Peskineee7554e2021-04-29 20:38:01 +020027
Gilles Peskine16b25062022-03-18 00:02:15 +010028def short_expression(original: str, level: int = 0) -> str:
Gilles Peskinee8e058c2022-03-17 23:42:25 +010029 """Abbreviate the expression, keeping it human-readable.
30
31 If `level` is 0, just remove parts that are implicit from context,
32 such as a leading ``PSA_KEY_TYPE_``.
33 For larger values of `level`, also abbreviate some names in an
34 unambiguous, but ad hoc way.
35 """
36 short = original
37 short = re.sub(r'\bPSA_(?:ALG|ECC_FAMILY|KEY_[A-Z]+)_', r'', short)
38 short = re.sub(r' +', r'', short)
Gilles Peskine16b25062022-03-18 00:02:15 +010039 if level >= 1:
40 short = re.sub(r'PUBLIC_KEY\b', r'PUB', short)
41 short = re.sub(r'KEY_PAIR\b', r'PAIR', short)
42 short = re.sub(r'\bBRAINPOOL_P', r'BP', short)
43 short = re.sub(r'\bMONTGOMERY\b', r'MGM', short)
44 short = re.sub(r'AEAD_WITH_SHORTENED_TAG\b', r'AEAD_SHORT', short)
45 short = re.sub(r'\bDETERMINISTIC_', r'DET_', short)
46 short = re.sub(r'\bKEY_AGREEMENT\b', r'KA', short)
47 short = re.sub(r'_PSK_TO_MS\b', r'_PSK2MS', short)
Gilles Peskinee8e058c2022-03-17 23:42:25 +010048 return short
49
50
Gilles Peskine8345d632021-04-29 20:38:47 +020051BLOCK_CIPHERS = frozenset(['AES', 'ARIA', 'CAMELLIA', 'DES'])
Gilles Peskineee7554e2021-04-29 20:38:01 +020052BLOCK_MAC_MODES = frozenset(['CBC_MAC', 'CMAC'])
53BLOCK_CIPHER_MODES = frozenset([
54 'CTR', 'CFB', 'OFB', 'XTS', 'CCM_STAR_NO_TAG',
55 'ECB_NO_PADDING', 'CBC_NO_PADDING', 'CBC_PKCS7',
56])
57BLOCK_AEAD_MODES = frozenset(['CCM', 'GCM'])
58
Gilles Peskine8345d632021-04-29 20:38:47 +020059class EllipticCurveCategory(enum.Enum):
60 """Categorization of elliptic curve families.
61
62 The category of a curve determines what algorithms are defined over it.
63 """
64
65 SHORT_WEIERSTRASS = 0
66 MONTGOMERY = 1
67 TWISTED_EDWARDS = 2
68
69 @staticmethod
70 def from_family(family: str) -> 'EllipticCurveCategory':
71 if family == 'PSA_ECC_FAMILY_MONTGOMERY':
72 return EllipticCurveCategory.MONTGOMERY
73 if family == 'PSA_ECC_FAMILY_TWISTED_EDWARDS':
74 return EllipticCurveCategory.TWISTED_EDWARDS
75 # Default to SW, which most curves belong to.
76 return EllipticCurveCategory.SHORT_WEIERSTRASS
77
Gilles Peskineee7554e2021-04-29 20:38:01 +020078
Gilles Peskine0156a152021-01-26 21:23:56 +010079class KeyType:
80 """Knowledge about a PSA key type."""
81
Gilles Peskineb9dbb7f2021-04-29 20:19:57 +020082 def __init__(self, name: str, params: Optional[Iterable[str]] = None) -> None:
Gilles Peskine0156a152021-01-26 21:23:56 +010083 """Analyze a key type.
84
85 The key type must be specified in PSA syntax. In its simplest form,
Gilles Peskinefa3c69a2021-02-16 14:29:22 +010086 `name` is a string 'PSA_KEY_TYPE_xxx' which is the name of a PSA key
Gilles Peskine0156a152021-01-26 21:23:56 +010087 type macro. For key types that take arguments, the arguments can
88 be passed either through the optional argument `params` or by
Gilles Peskine4d0b0892021-04-12 13:41:52 +020089 passing an expression of the form 'PSA_KEY_TYPE_xxx(param1, ...)'
Gilles Peskinefa3c69a2021-02-16 14:29:22 +010090 in `name` as a string.
Gilles Peskine0156a152021-01-26 21:23:56 +010091 """
Gilles Peskined75adfc2021-02-17 18:04:28 +010092
Gilles Peskine0156a152021-01-26 21:23:56 +010093 self.name = name.strip()
Gilles Peskinefa3c69a2021-02-16 14:29:22 +010094 """The key type macro name (``PSA_KEY_TYPE_xxx``).
95
96 For key types constructed from a macro with arguments, this is the
97 name of the macro, and the arguments are in `self.params`.
98 """
Gilles Peskine0156a152021-01-26 21:23:56 +010099 if params is None:
100 if '(' in self.name:
101 m = re.match(r'(\w+)\s*\((.*)\)\Z', self.name)
102 assert m is not None
103 self.name = m.group(1)
Gilles Peskine4d0b0892021-04-12 13:41:52 +0200104 params = m.group(2).split(',')
Gilles Peskinefa3c69a2021-02-16 14:29:22 +0100105 self.params = (None if params is None else
106 [param.strip() for param in params])
107 """The parameters of the key type, if there are any.
108
109 None if the key type is a macro without arguments.
110 """
Gilles Peskined75adfc2021-02-17 18:04:28 +0100111 assert re.match(r'PSA_KEY_TYPE_\w+\Z', self.name)
112
Gilles Peskine0156a152021-01-26 21:23:56 +0100113 self.expression = self.name
Gilles Peskinefa3c69a2021-02-16 14:29:22 +0100114 """A C expression whose value is the key type encoding."""
Gilles Peskine0156a152021-01-26 21:23:56 +0100115 if self.params is not None:
116 self.expression += '(' + ', '.join(self.params) + ')'
Gilles Peskined75adfc2021-02-17 18:04:28 +0100117
Gilles Peskine8345d632021-04-29 20:38:47 +0200118 m = re.match(r'PSA_KEY_TYPE_(\w+)', self.name)
119 assert m
120 self.head = re.sub(r'_(?:PUBLIC_KEY|KEY_PAIR)\Z', r'', m.group(1))
121 """The key type macro name, with common prefixes and suffixes stripped."""
122
Gilles Peskine0156a152021-01-26 21:23:56 +0100123 self.private_type = re.sub(r'_PUBLIC_KEY\Z', r'_KEY_PAIR', self.name)
Gilles Peskinefa3c69a2021-02-16 14:29:22 +0100124 """The key type macro name for the corresponding key pair type.
125
126 For everything other than a public key type, this is the same as
127 `self.name`.
128 """
Gilles Peskinedf639682021-01-26 21:25:34 +0100129
Gilles Peskine16b25062022-03-18 00:02:15 +0100130 def short_expression(self, level: int = 0) -> str:
Gilles Peskinee8e058c2022-03-17 23:42:25 +0100131 """Abbreviate the expression, keeping it human-readable.
132
133 See `crypto_knowledge.short_expression`.
134 """
Gilles Peskine16b25062022-03-18 00:02:15 +0100135 return short_expression(self.expression, level=level)
Gilles Peskinee8e058c2022-03-17 23:42:25 +0100136
Gilles Peskinee6300952021-04-29 21:56:59 +0200137 def is_public(self) -> bool:
138 """Whether the key type is for public keys."""
139 return self.name.endswith('_PUBLIC_KEY')
140
Gilles Peskinedf639682021-01-26 21:25:34 +0100141 ECC_KEY_SIZES = {
142 'PSA_ECC_FAMILY_SECP_K1': (192, 224, 256),
Gilles Peskine0ac258e2021-01-27 13:11:59 +0100143 'PSA_ECC_FAMILY_SECP_R1': (225, 256, 384, 521),
Gilles Peskinedf639682021-01-26 21:25:34 +0100144 'PSA_ECC_FAMILY_SECP_R2': (160,),
145 'PSA_ECC_FAMILY_SECT_K1': (163, 233, 239, 283, 409, 571),
146 'PSA_ECC_FAMILY_SECT_R1': (163, 233, 283, 409, 571),
147 'PSA_ECC_FAMILY_SECT_R2': (163,),
148 'PSA_ECC_FAMILY_BRAINPOOL_P_R1': (160, 192, 224, 256, 320, 384, 512),
149 'PSA_ECC_FAMILY_MONTGOMERY': (255, 448),
Gilles Peskinea00abc62021-03-16 18:25:14 +0100150 'PSA_ECC_FAMILY_TWISTED_EDWARDS': (255, 448),
Gilles Peskinedf639682021-01-26 21:25:34 +0100151 }
152 KEY_TYPE_SIZES = {
153 'PSA_KEY_TYPE_AES': (128, 192, 256), # exhaustive
Gilles Peskinedf639682021-01-26 21:25:34 +0100154 'PSA_KEY_TYPE_ARIA': (128, 192, 256), # exhaustive
155 'PSA_KEY_TYPE_CAMELLIA': (128, 192, 256), # exhaustive
156 'PSA_KEY_TYPE_CHACHA20': (256,), # exhaustive
157 'PSA_KEY_TYPE_DERIVE': (120, 128), # sample
158 'PSA_KEY_TYPE_DES': (64, 128, 192), # exhaustive
159 'PSA_KEY_TYPE_HMAC': (128, 160, 224, 256, 384, 512), # standard size for each supported hash
Manuel Pégourié-Gonnardb12de9f2021-05-03 11:02:56 +0200160 'PSA_KEY_TYPE_PASSWORD': (48, 168, 336), # sample
161 'PSA_KEY_TYPE_PASSWORD_HASH': (128, 256), # sample
162 'PSA_KEY_TYPE_PEPPER': (128, 256), # sample
Gilles Peskinedf639682021-01-26 21:25:34 +0100163 'PSA_KEY_TYPE_RAW_DATA': (8, 40, 128), # sample
164 'PSA_KEY_TYPE_RSA_KEY_PAIR': (1024, 1536), # small sample
165 }
166 def sizes_to_test(self) -> Tuple[int, ...]:
167 """Return a tuple of key sizes to test.
168
169 For key types that only allow a single size, or only a small set of
170 sizes, these are all the possible sizes. For key types that allow a
171 wide range of sizes, these are a representative sample of sizes,
172 excluding large sizes for which a typical resource-constrained platform
173 may run out of memory.
174 """
175 if self.private_type == 'PSA_KEY_TYPE_ECC_KEY_PAIR':
176 assert self.params is not None
177 return self.ECC_KEY_SIZES[self.params[0]]
178 return self.KEY_TYPE_SIZES[self.private_type]
Gilles Peskine397b0282021-01-26 21:26:26 +0100179
180 # "48657265006973206b6579a064617461"
181 DATA_BLOCK = b'Here\000is key\240data'
182 def key_material(self, bits: int) -> bytes:
183 """Return a byte string containing suitable key material with the given bit length.
184
185 Use the PSA export representation. The resulting byte string is one that
186 can be obtained with the following code:
187 ```
188 psa_set_key_type(&attributes, `self.expression`);
189 psa_set_key_bits(&attributes, `bits`);
190 psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_EXPORT);
191 psa_generate_key(&attributes, &id);
192 psa_export_key(id, `material`, ...);
193 ```
194 """
Gilles Peskine6f6483f2021-01-27 12:43:24 +0100195 if self.expression in ASYMMETRIC_KEY_DATA:
196 if bits not in ASYMMETRIC_KEY_DATA[self.expression]:
197 raise ValueError('No key data for {}-bit {}'
198 .format(bits, self.expression))
199 return ASYMMETRIC_KEY_DATA[self.expression][bits]
Gilles Peskine397b0282021-01-26 21:26:26 +0100200 if bits % 8 != 0:
Gilles Peskine6f6483f2021-01-27 12:43:24 +0100201 raise ValueError('Non-integer number of bytes: {} bits for {}'
202 .format(bits, self.expression))
Gilles Peskine397b0282021-01-26 21:26:26 +0100203 length = bits // 8
204 if self.name == 'PSA_KEY_TYPE_DES':
205 # "644573206b457901644573206b457902644573206b457904"
206 des3 = b'dEs kEy\001dEs kEy\002dEs kEy\004'
207 return des3[:length]
Gilles Peskine397b0282021-01-26 21:26:26 +0100208 return b''.join([self.DATA_BLOCK] * (length // len(self.DATA_BLOCK)) +
209 [self.DATA_BLOCK[:length % len(self.DATA_BLOCK)]])
gabor-mezei-arm2784bfe2021-06-28 20:02:11 +0200210
Gilles Peskine8345d632021-04-29 20:38:47 +0200211 def can_do(self, alg: 'Algorithm') -> bool:
212 """Whether this key type can be used for operations with the given algorithm.
213
214 This function does not currently handle key derivation or PAKE.
215 """
216 #pylint: disable=too-many-return-statements
217 if alg.is_wildcard:
218 return False
Gilles Peskinee3a08902022-03-19 10:37:33 +0100219 if alg.is_invalid_truncation():
220 return False
Gilles Peskine8345d632021-04-29 20:38:47 +0200221 if self.head == 'HMAC' and alg.head == 'HMAC':
222 return True
Gilles Peskinec47d3a42022-03-18 10:18:58 +0100223 if self.head == 'DES':
224 # 64-bit block ciphers only allow a reduced set of modes.
225 return alg.head in [
226 'CBC_NO_PADDING', 'CBC_PKCS7',
227 'ECB_NO_PADDING',
228 ]
Gilles Peskine8345d632021-04-29 20:38:47 +0200229 if self.head in BLOCK_CIPHERS and \
230 alg.head in frozenset.union(BLOCK_MAC_MODES,
231 BLOCK_CIPHER_MODES,
232 BLOCK_AEAD_MODES):
233 return True
234 if self.head == 'CHACHA20' and alg.head == 'CHACHA20_POLY1305':
235 return True
236 if self.head in {'ARC4', 'CHACHA20'} and \
237 alg.head == 'STREAM_CIPHER':
238 return True
239 if self.head == 'RSA' and alg.head.startswith('RSA_'):
240 return True
241 if self.head == 'ECC':
242 assert self.params is not None
243 eccc = EllipticCurveCategory.from_family(self.params[0])
244 if alg.head == 'ECDH' and \
245 eccc in {EllipticCurveCategory.SHORT_WEIERSTRASS,
246 EllipticCurveCategory.MONTGOMERY}:
247 return True
248 if alg.head == 'ECDSA' and \
249 eccc == EllipticCurveCategory.SHORT_WEIERSTRASS:
250 return True
251 if alg.head in {'PURE_EDDSA', 'EDDSA_PREHASH'} and \
252 eccc == EllipticCurveCategory.TWISTED_EDWARDS:
253 return True
254 return False
255
Gilles Peskineee7554e2021-04-29 20:38:01 +0200256
257class AlgorithmCategory(enum.Enum):
258 """PSA algorithm categories."""
259 # The numbers are aligned with the category bits in numerical values of
260 # algorithms.
261 HASH = 2
262 MAC = 3
263 CIPHER = 4
264 AEAD = 5
265 SIGN = 6
266 ASYMMETRIC_ENCRYPTION = 7
267 KEY_DERIVATION = 8
268 KEY_AGREEMENT = 9
269 PAKE = 10
270
271 def requires_key(self) -> bool:
Gilles Peskinee6300952021-04-29 21:56:59 +0200272 """Whether operations in this category are set up with a key."""
Gilles Peskineee7554e2021-04-29 20:38:01 +0200273 return self not in {self.HASH, self.KEY_DERIVATION}
274
Gilles Peskinee6300952021-04-29 21:56:59 +0200275 def is_asymmetric(self) -> bool:
276 """Whether operations in this category involve asymmetric keys."""
277 return self in {
278 self.SIGN,
279 self.ASYMMETRIC_ENCRYPTION,
280 self.KEY_AGREEMENT
281 }
282
Gilles Peskineee7554e2021-04-29 20:38:01 +0200283
284class AlgorithmNotRecognized(Exception):
285 def __init__(self, expr: str) -> None:
286 super().__init__('Algorithm not recognized: ' + expr)
287 self.expr = expr
288
289
290class Algorithm:
291 """Knowledge about a PSA algorithm."""
292
293 @staticmethod
294 def determine_base(expr: str) -> str:
295 """Return an expression for the "base" of the algorithm.
296
297 This strips off variants of algorithms such as MAC truncation.
298
299 This function does not attempt to detect invalid inputs.
300 """
301 m = re.match(r'PSA_ALG_(?:'
302 r'(?:TRUNCATED|AT_LEAST_THIS_LENGTH)_MAC|'
303 r'AEAD_WITH_(?:SHORTENED|AT_LEAST_THIS_LENGTH)_TAG'
304 r')\((.*),[^,]+\)\Z', expr)
305 if m:
306 expr = m.group(1)
307 return expr
308
309 @staticmethod
310 def determine_head(expr: str) -> str:
311 """Return the head of an algorithm expression.
312
313 The head is the first (outermost) constructor, without its PSA_ALG_
314 prefix, and with some normalization of similar algorithms.
315 """
316 m = re.match(r'PSA_ALG_(?:DETERMINISTIC_)?(\w+)', expr)
317 if not m:
318 raise AlgorithmNotRecognized(expr)
319 head = m.group(1)
320 if head == 'KEY_AGREEMENT':
321 m = re.match(r'PSA_ALG_KEY_AGREEMENT\s*\(\s*PSA_ALG_(\w+)', expr)
322 if not m:
323 raise AlgorithmNotRecognized(expr)
324 head = m.group(1)
325 head = re.sub(r'_ANY\Z', r'', head)
326 if re.match(r'ED[0-9]+PH\Z', head):
327 head = 'EDDSA_PREHASH'
328 return head
329
330 CATEGORY_FROM_HEAD = {
331 'SHA': AlgorithmCategory.HASH,
332 'SHAKE256_512': AlgorithmCategory.HASH,
333 'MD': AlgorithmCategory.HASH,
334 'RIPEMD': AlgorithmCategory.HASH,
335 'ANY_HASH': AlgorithmCategory.HASH,
336 'HMAC': AlgorithmCategory.MAC,
337 'STREAM_CIPHER': AlgorithmCategory.CIPHER,
338 'CHACHA20_POLY1305': AlgorithmCategory.AEAD,
339 'DSA': AlgorithmCategory.SIGN,
340 'ECDSA': AlgorithmCategory.SIGN,
341 'EDDSA': AlgorithmCategory.SIGN,
342 'PURE_EDDSA': AlgorithmCategory.SIGN,
343 'RSA_PSS': AlgorithmCategory.SIGN,
344 'RSA_PKCS1V15_SIGN': AlgorithmCategory.SIGN,
345 'RSA_PKCS1V15_CRYPT': AlgorithmCategory.ASYMMETRIC_ENCRYPTION,
346 'RSA_OAEP': AlgorithmCategory.ASYMMETRIC_ENCRYPTION,
347 'HKDF': AlgorithmCategory.KEY_DERIVATION,
348 'TLS12_PRF': AlgorithmCategory.KEY_DERIVATION,
349 'TLS12_PSK_TO_MS': AlgorithmCategory.KEY_DERIVATION,
350 'PBKDF': AlgorithmCategory.KEY_DERIVATION,
351 'ECDH': AlgorithmCategory.KEY_AGREEMENT,
352 'FFDH': AlgorithmCategory.KEY_AGREEMENT,
353 # KEY_AGREEMENT(...) is a key derivation with a key agreement component
354 'KEY_AGREEMENT': AlgorithmCategory.KEY_DERIVATION,
355 'JPAKE': AlgorithmCategory.PAKE,
356 }
357 for x in BLOCK_MAC_MODES:
358 CATEGORY_FROM_HEAD[x] = AlgorithmCategory.MAC
359 for x in BLOCK_CIPHER_MODES:
360 CATEGORY_FROM_HEAD[x] = AlgorithmCategory.CIPHER
361 for x in BLOCK_AEAD_MODES:
362 CATEGORY_FROM_HEAD[x] = AlgorithmCategory.AEAD
363
364 def determine_category(self, expr: str, head: str) -> AlgorithmCategory:
365 """Return the category of the given algorithm expression.
366
367 This function does not attempt to detect invalid inputs.
368 """
369 prefix = head
370 while prefix:
371 if prefix in self.CATEGORY_FROM_HEAD:
372 return self.CATEGORY_FROM_HEAD[prefix]
373 if re.match(r'.*[0-9]\Z', prefix):
374 prefix = re.sub(r'_*[0-9]+\Z', r'', prefix)
375 else:
376 prefix = re.sub(r'_*[^_]*\Z', r'', prefix)
377 raise AlgorithmNotRecognized(expr)
378
379 @staticmethod
380 def determine_wildcard(expr) -> bool:
381 """Whether the given algorithm expression is a wildcard.
382
383 This function does not attempt to detect invalid inputs.
384 """
385 if re.search(r'\bPSA_ALG_ANY_HASH\b', expr):
386 return True
387 if re.search(r'_AT_LEAST_', expr):
388 return True
389 return False
390
391 def __init__(self, expr: str) -> None:
392 """Analyze an algorithm value.
393
394 The algorithm must be expressed as a C expression containing only
395 calls to PSA algorithm constructor macros and numeric literals.
396
397 This class is only programmed to handle valid expressions. Invalid
398 expressions may result in exceptions or in nonsensical results.
399 """
400 self.expression = re.sub(r'\s+', r'', expr)
401 self.base_expression = self.determine_base(self.expression)
402 self.head = self.determine_head(self.base_expression)
403 self.category = self.determine_category(self.base_expression, self.head)
404 self.is_wildcard = self.determine_wildcard(self.expression)
Gilles Peskinea4013862021-04-29 20:54:40 +0200405
406 def is_key_agreement_with_derivation(self) -> bool:
407 """Whether this is a combined key agreement and key derivation algorithm."""
408 if self.category != AlgorithmCategory.KEY_AGREEMENT:
409 return False
410 m = re.match(r'PSA_ALG_KEY_AGREEMENT\(\w+,\s*(.*)\)\Z', self.expression)
411 if not m:
412 return False
413 kdf_alg = m.group(1)
414 # Assume kdf_alg is either a valid KDF or 0.
415 return not re.match(r'(?:0[Xx])?0+\s*\Z', kdf_alg)
416
Gilles Peskinee8e058c2022-03-17 23:42:25 +0100417
Gilles Peskine16b25062022-03-18 00:02:15 +0100418 def short_expression(self, level: int = 0) -> str:
Gilles Peskinee8e058c2022-03-17 23:42:25 +0100419 """Abbreviate the expression, keeping it human-readable.
420
421 See `crypto_knowledge.short_expression`.
422 """
Gilles Peskine16b25062022-03-18 00:02:15 +0100423 return short_expression(self.expression, level=level)
Gilles Peskinee8e058c2022-03-17 23:42:25 +0100424
Gilles Peskinee3a08902022-03-19 10:37:33 +0100425 PERMITTED_TAG_LENGTHS = {
426 'PSA_ALG_CCM': frozenset([4, 6, 8, 10, 12, 14, 16]),
427 'PSA_ALG_CHACHA20_POLY1305': frozenset([16]),
428 'PSA_ALG_GCM': frozenset([4, 8, 12, 13, 14, 15, 16]),
429 }
430 MAC_LENGTH = {
431 'PSA_ALG_CBC_MAC': 16,
432 'PSA_ALG_CMAC': 16,
433 'PSA_ALG_HMAC(PSA_ALG_MD5)': 16,
434 'PSA_ALG_HMAC(PSA_ALG_SHA_1)': 20,
435 }
436 HMAC_WITH_NOMINAL_LENGTH_RE = re.compile(r'PSA_ALG_HMAC\(\w+([0-9])+\)\Z')
437 @classmethod
438 def mac_or_tag_length(cls, base: str) -> FrozenSet[int]:
439 """Return the set of permitted lengths for the given MAC or AEAD tag."""
440 if base in cls.PERMITTED_TAG_LENGTHS:
441 return cls.PERMITTED_TAG_LENGTHS[base]
442 max_length = cls.MAC_LENGTH.get(base, None)
443 if max_length is None:
444 m = cls.HMAC_WITH_NOMINAL_LENGTH_RE.match(base)
445 if m:
446 max_length = int(m.group(1)) // 8
447 if max_length is None:
448 raise ValueError('Unknown permitted lengths for ' + base)
449 return frozenset(range(4, max_length + 1))
450
451 TRUNCATED_ALG_RE = re.compile(
452 r'(?P<face>PSA_ALG_(?:AEAD_WITH_SHORTENED_TAG|TRUNCATED_MAC))'
453 r'\((?P<base>.*),'
454 r'(?P<length>0[Xx][0-9A-Fa-f]+|[1-9][0-9]*|0[0-9]*)[LUlu]*\)\Z')
455 def is_invalid_truncation(self) -> bool:
456 """False for a MAC or AEAD algorithm truncated to an invalid length.
457
458 True for a MAC or AEAD algorithm truncated to a valid length or to
459 a length that cannot be determined. True for anything other than
460 a truncated MAC or AEAD.
461 """
462 m = self.TRUNCATED_ALG_RE.match(self.expression)
463 if m:
464 base = m.group('base')
465 to_length = int(m.group('length'), 0)
466 permitted_lengths = self.mac_or_tag_length(base)
467 if to_length not in permitted_lengths:
468 return True
469 return False
470
Gilles Peskinea4013862021-04-29 20:54:40 +0200471 def can_do(self, category: AlgorithmCategory) -> bool:
Gilles Peskinee3a08902022-03-19 10:37:33 +0100472 """Whether this algorithm can perform operations in the given category.
473 """
Gilles Peskinea4013862021-04-29 20:54:40 +0200474 if category == self.category:
475 return True
476 if category == AlgorithmCategory.KEY_DERIVATION and \
477 self.is_key_agreement_with_derivation():
478 return True
479 return False
Gilles Peskinee6b85b42022-03-18 09:58:09 +0100480
481 def usage_flags(self, public: bool = False) -> List[str]:
482 """The list of usage flags describing operations that can perform this algorithm.
483
484 If public is true, only return public-key operations, not private-key operations.
485 """
486 if self.category == AlgorithmCategory.HASH:
487 flags = []
488 elif self.category == AlgorithmCategory.MAC:
489 flags = ['SIGN_HASH', 'SIGN_MESSAGE',
490 'VERIFY_HASH', 'VERIFY_MESSAGE']
491 elif self.category == AlgorithmCategory.CIPHER or \
492 self.category == AlgorithmCategory.AEAD:
493 flags = ['DECRYPT', 'ENCRYPT']
494 elif self.category == AlgorithmCategory.SIGN:
495 flags = ['VERIFY_HASH', 'VERIFY_MESSAGE']
496 if not public:
497 flags += ['SIGN_HASH', 'SIGN_MESSAGE']
498 elif self.category == AlgorithmCategory.ASYMMETRIC_ENCRYPTION:
499 flags = ['ENCRYPT']
500 if not public:
501 flags += ['DECRYPT']
502 elif self.category == AlgorithmCategory.KEY_DERIVATION or \
503 self.category == AlgorithmCategory.KEY_AGREEMENT:
504 flags = ['DERIVE']
505 else:
506 raise AlgorithmNotRecognized(self.expression)
507 return ['PSA_KEY_USAGE_' + flag for flag in flags]