1"""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 OR GPL-2.0-or-later 8# 9 10import enum 11import re 12from typing import FrozenSet, Iterable, List, Optional, Tuple, Dict 13 14from .asymmetric_key_data import ASYMMETRIC_KEY_DATA 15 16 17def short_expression(original: str, level: int = 0) -> str: 18 """Abbreviate the expression, keeping it human-readable. 19 20 If `level` is 0, just remove parts that are implicit from context, 21 such as a leading ``PSA_KEY_TYPE_``. 22 For larger values of `level`, also abbreviate some names in an 23 unambiguous, but ad hoc way. 24 """ 25 short = original 26 short = re.sub(r'\bPSA_(?:ALG|DH_FAMILY|ECC_FAMILY|KEY_[A-Z]+)_', r'', short) 27 short = re.sub(r' +', r'', short) 28 if level >= 1: 29 short = re.sub(r'PUBLIC_KEY\b', r'PUB', short) 30 short = re.sub(r'KEY_PAIR\b', r'PAIR', short) 31 short = re.sub(r'\bBRAINPOOL_P', r'BP', short) 32 short = re.sub(r'\bMONTGOMERY\b', r'MGM', short) 33 short = re.sub(r'AEAD_WITH_SHORTENED_TAG\b', r'AEAD_SHORT', short) 34 short = re.sub(r'\bDETERMINISTIC_', r'DET_', short) 35 short = re.sub(r'\bKEY_AGREEMENT\b', r'KA', short) 36 short = re.sub(r'_PSK_TO_MS\b', r'_PSK2MS', short) 37 return short 38 39 40BLOCK_CIPHERS = frozenset(['AES', 'ARIA', 'CAMELLIA', 'DES']) 41BLOCK_MAC_MODES = frozenset(['CBC_MAC', 'CMAC']) 42BLOCK_CIPHER_MODES = frozenset([ 43 'CTR', 'CFB', 'OFB', 'XTS', 'CCM_STAR_NO_TAG', 44 'ECB_NO_PADDING', 'CBC_NO_PADDING', 'CBC_PKCS7', 45]) 46BLOCK_AEAD_MODES = frozenset(['CCM', 'GCM']) 47 48class EllipticCurveCategory(enum.Enum): 49 """Categorization of elliptic curve families. 50 51 The category of a curve determines what algorithms are defined over it. 52 """ 53 54 SHORT_WEIERSTRASS = 0 55 MONTGOMERY = 1 56 TWISTED_EDWARDS = 2 57 58 @staticmethod 59 def from_family(family: str) -> 'EllipticCurveCategory': 60 if family == 'PSA_ECC_FAMILY_MONTGOMERY': 61 return EllipticCurveCategory.MONTGOMERY 62 if family == 'PSA_ECC_FAMILY_TWISTED_EDWARDS': 63 return EllipticCurveCategory.TWISTED_EDWARDS 64 # Default to SW, which most curves belong to. 65 return EllipticCurveCategory.SHORT_WEIERSTRASS 66 67 68class KeyType: 69 """Knowledge about a PSA key type.""" 70 71 def __init__(self, name: str, params: Optional[Iterable[str]] = None) -> None: 72 """Analyze a key type. 73 74 The key type must be specified in PSA syntax. In its simplest form, 75 `name` is a string 'PSA_KEY_TYPE_xxx' which is the name of a PSA key 76 type macro. For key types that take arguments, the arguments can 77 be passed either through the optional argument `params` or by 78 passing an expression of the form 'PSA_KEY_TYPE_xxx(param1, ...)' 79 in `name` as a string. 80 """ 81 82 self.name = name.strip() 83 """The key type macro name (``PSA_KEY_TYPE_xxx``). 84 85 For key types constructed from a macro with arguments, this is the 86 name of the macro, and the arguments are in `self.params`. 87 """ 88 if params is None: 89 if '(' in self.name: 90 m = re.match(r'(\w+)\s*\((.*)\)\Z', self.name) 91 assert m is not None 92 self.name = m.group(1) 93 params = m.group(2).split(',') 94 self.params = (None if params is None else 95 [param.strip() for param in params]) 96 """The parameters of the key type, if there are any. 97 98 None if the key type is a macro without arguments. 99 """ 100 assert re.match(r'PSA_KEY_TYPE_\w+\Z', self.name) 101 102 self.expression = self.name 103 """A C expression whose value is the key type encoding.""" 104 if self.params is not None: 105 self.expression += '(' + ', '.join(self.params) + ')' 106 107 m = re.match(r'PSA_KEY_TYPE_(\w+)', self.name) 108 assert m 109 self.head = re.sub(r'_(?:PUBLIC_KEY|KEY_PAIR)\Z', r'', m.group(1)) 110 """The key type macro name, with common prefixes and suffixes stripped.""" 111 112 self.private_type = re.sub(r'_PUBLIC_KEY\Z', r'_KEY_PAIR', self.name) 113 """The key type macro name for the corresponding key pair type. 114 115 For everything other than a public key type, this is the same as 116 `self.name`. 117 """ 118 119 def short_expression(self, level: int = 0) -> str: 120 """Abbreviate the expression, keeping it human-readable. 121 122 See `crypto_knowledge.short_expression`. 123 """ 124 return short_expression(self.expression, level=level) 125 126 def is_public(self) -> bool: 127 """Whether the key type is for public keys.""" 128 return self.name.endswith('_PUBLIC_KEY') 129 130 DH_KEY_SIZES = { 131 'PSA_DH_FAMILY_RFC7919': (2048, 3072, 4096, 6144, 8192), 132 } # type: Dict[str, Tuple[int, ...]] 133 ECC_KEY_SIZES = { 134 'PSA_ECC_FAMILY_SECP_K1': (192, 225, 256), 135 'PSA_ECC_FAMILY_SECP_R1': (224, 256, 384, 521), 136 'PSA_ECC_FAMILY_SECP_R2': (160,), 137 'PSA_ECC_FAMILY_SECT_K1': (163, 233, 239, 283, 409, 571), 138 'PSA_ECC_FAMILY_SECT_R1': (163, 233, 283, 409, 571), 139 'PSA_ECC_FAMILY_SECT_R2': (163,), 140 'PSA_ECC_FAMILY_BRAINPOOL_P_R1': (160, 192, 224, 256, 320, 384, 512), 141 'PSA_ECC_FAMILY_MONTGOMERY': (255, 448), 142 'PSA_ECC_FAMILY_TWISTED_EDWARDS': (255, 448), 143 } # type: Dict[str, Tuple[int, ...]] 144 KEY_TYPE_SIZES = { 145 'PSA_KEY_TYPE_AES': (128, 192, 256), # exhaustive 146 'PSA_KEY_TYPE_ARIA': (128, 192, 256), # exhaustive 147 'PSA_KEY_TYPE_CAMELLIA': (128, 192, 256), # exhaustive 148 'PSA_KEY_TYPE_CHACHA20': (256,), # exhaustive 149 'PSA_KEY_TYPE_DERIVE': (120, 128), # sample 150 'PSA_KEY_TYPE_DES': (64, 128, 192), # exhaustive 151 'PSA_KEY_TYPE_HMAC': (128, 160, 224, 256, 384, 512), # standard size for each supported hash 152 'PSA_KEY_TYPE_PASSWORD': (48, 168, 336), # sample 153 'PSA_KEY_TYPE_PASSWORD_HASH': (128, 256), # sample 154 'PSA_KEY_TYPE_PEPPER': (128, 256), # sample 155 'PSA_KEY_TYPE_RAW_DATA': (8, 40, 128), # sample 156 'PSA_KEY_TYPE_RSA_KEY_PAIR': (1024, 1536), # small sample 157 } # type: Dict[str, Tuple[int, ...]] 158 def sizes_to_test(self) -> Tuple[int, ...]: 159 """Return a tuple of key sizes to test. 160 161 For key types that only allow a single size, or only a small set of 162 sizes, these are all the possible sizes. For key types that allow a 163 wide range of sizes, these are a representative sample of sizes, 164 excluding large sizes for which a typical resource-constrained platform 165 may run out of memory. 166 """ 167 if self.private_type == 'PSA_KEY_TYPE_ECC_KEY_PAIR': 168 assert self.params is not None 169 return self.ECC_KEY_SIZES[self.params[0]] 170 if self.private_type == 'PSA_KEY_TYPE_DH_KEY_PAIR': 171 assert self.params is not None 172 return self.DH_KEY_SIZES[self.params[0]] 173 return self.KEY_TYPE_SIZES[self.private_type] 174 175 # "48657265006973206b6579a064617461" 176 DATA_BLOCK = b'Here\000is key\240data' 177 def key_material(self, bits: int) -> bytes: 178 """Return a byte string containing suitable key material with the given bit length. 179 180 Use the PSA export representation. The resulting byte string is one that 181 can be obtained with the following code: 182 ``` 183 psa_set_key_type(&attributes, `self.expression`); 184 psa_set_key_bits(&attributes, `bits`); 185 psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_EXPORT); 186 psa_generate_key(&attributes, &id); 187 psa_export_key(id, `material`, ...); 188 ``` 189 """ 190 if self.expression in ASYMMETRIC_KEY_DATA: 191 if bits not in ASYMMETRIC_KEY_DATA[self.expression]: 192 raise ValueError('No key data for {}-bit {}' 193 .format(bits, self.expression)) 194 return ASYMMETRIC_KEY_DATA[self.expression][bits] 195 if bits % 8 != 0: 196 raise ValueError('Non-integer number of bytes: {} bits for {}' 197 .format(bits, self.expression)) 198 length = bits // 8 199 if self.name == 'PSA_KEY_TYPE_DES': 200 # "644573206b457901644573206b457902644573206b457904" 201 des3 = b'dEs kEy\001dEs kEy\002dEs kEy\004' 202 return des3[:length] 203 return b''.join([self.DATA_BLOCK] * (length // len(self.DATA_BLOCK)) + 204 [self.DATA_BLOCK[:length % len(self.DATA_BLOCK)]]) 205 206 def can_do(self, alg: 'Algorithm') -> bool: 207 """Whether this key type can be used for operations with the given algorithm. 208 209 This function does not currently handle key derivation or PAKE. 210 """ 211 #pylint: disable=too-many-branches,too-many-return-statements 212 if not alg.is_valid_for_operation(): 213 return False 214 if self.head == 'HMAC' and alg.head == 'HMAC': 215 return True 216 if self.head == 'DES': 217 # 64-bit block ciphers only allow a reduced set of modes. 218 return alg.head in [ 219 'CBC_NO_PADDING', 'CBC_PKCS7', 220 'ECB_NO_PADDING', 221 ] 222 if self.head in BLOCK_CIPHERS and \ 223 alg.head in frozenset.union(BLOCK_MAC_MODES, 224 BLOCK_CIPHER_MODES, 225 BLOCK_AEAD_MODES): 226 if alg.head in ['CMAC', 'OFB'] and \ 227 self.head in ['ARIA', 'CAMELLIA']: 228 return False # not implemented in Mbed TLS 229 return True 230 if self.head == 'CHACHA20' and alg.head == 'CHACHA20_POLY1305': 231 return True 232 if self.head in {'ARC4', 'CHACHA20'} and \ 233 alg.head == 'STREAM_CIPHER': 234 return True 235 if self.head == 'RSA' and alg.head.startswith('RSA_'): 236 return True 237 if alg.category == AlgorithmCategory.KEY_AGREEMENT and \ 238 self.is_public(): 239 # The PSA API does not use public key objects in key agreement 240 # operations: it imports the public key as a formatted byte string. 241 # So a public key object with a key agreement algorithm is not 242 # a valid combination. 243 return False 244 if alg.is_invalid_key_agreement_with_derivation(): 245 return False 246 if self.head == 'ECC': 247 assert self.params is not None 248 eccc = EllipticCurveCategory.from_family(self.params[0]) 249 if alg.head == 'ECDH' and \ 250 eccc in {EllipticCurveCategory.SHORT_WEIERSTRASS, 251 EllipticCurveCategory.MONTGOMERY}: 252 return True 253 if alg.head == 'ECDSA' and \ 254 eccc == EllipticCurveCategory.SHORT_WEIERSTRASS: 255 return True 256 if alg.head in {'PURE_EDDSA', 'EDDSA_PREHASH'} and \ 257 eccc == EllipticCurveCategory.TWISTED_EDWARDS: 258 return True 259 if self.head == 'DH' and alg.head == 'FFDH': 260 return True 261 return False 262 263 264class AlgorithmCategory(enum.Enum): 265 """PSA algorithm categories.""" 266 # The numbers are aligned with the category bits in numerical values of 267 # algorithms. 268 HASH = 2 269 MAC = 3 270 CIPHER = 4 271 AEAD = 5 272 SIGN = 6 273 ASYMMETRIC_ENCRYPTION = 7 274 KEY_DERIVATION = 8 275 KEY_AGREEMENT = 9 276 PAKE = 10 277 278 def requires_key(self) -> bool: 279 """Whether operations in this category are set up with a key.""" 280 return self not in {self.HASH, self.KEY_DERIVATION} 281 282 def is_asymmetric(self) -> bool: 283 """Whether operations in this category involve asymmetric keys.""" 284 return self in { 285 self.SIGN, 286 self.ASYMMETRIC_ENCRYPTION, 287 self.KEY_AGREEMENT 288 } 289 290 291class AlgorithmNotRecognized(Exception): 292 def __init__(self, expr: str) -> None: 293 super().__init__('Algorithm not recognized: ' + expr) 294 self.expr = expr 295 296 297class Algorithm: 298 """Knowledge about a PSA algorithm.""" 299 300 @staticmethod 301 def determine_base(expr: str) -> str: 302 """Return an expression for the "base" of the algorithm. 303 304 This strips off variants of algorithms such as MAC truncation. 305 306 This function does not attempt to detect invalid inputs. 307 """ 308 m = re.match(r'PSA_ALG_(?:' 309 r'(?:TRUNCATED|AT_LEAST_THIS_LENGTH)_MAC|' 310 r'AEAD_WITH_(?:SHORTENED|AT_LEAST_THIS_LENGTH)_TAG' 311 r')\((.*),[^,]+\)\Z', expr) 312 if m: 313 expr = m.group(1) 314 return expr 315 316 @staticmethod 317 def determine_head(expr: str) -> str: 318 """Return the head of an algorithm expression. 319 320 The head is the first (outermost) constructor, without its PSA_ALG_ 321 prefix, and with some normalization of similar algorithms. 322 """ 323 m = re.match(r'PSA_ALG_(?:DETERMINISTIC_)?(\w+)', expr) 324 if not m: 325 raise AlgorithmNotRecognized(expr) 326 head = m.group(1) 327 if head == 'KEY_AGREEMENT': 328 m = re.match(r'PSA_ALG_KEY_AGREEMENT\s*\(\s*PSA_ALG_(\w+)', expr) 329 if not m: 330 raise AlgorithmNotRecognized(expr) 331 head = m.group(1) 332 head = re.sub(r'_ANY\Z', r'', head) 333 if re.match(r'ED[0-9]+PH\Z', head): 334 head = 'EDDSA_PREHASH' 335 return head 336 337 CATEGORY_FROM_HEAD = { 338 'SHA': AlgorithmCategory.HASH, 339 'SHAKE256_512': AlgorithmCategory.HASH, 340 'MD': AlgorithmCategory.HASH, 341 'RIPEMD': AlgorithmCategory.HASH, 342 'ANY_HASH': AlgorithmCategory.HASH, 343 'HMAC': AlgorithmCategory.MAC, 344 'STREAM_CIPHER': AlgorithmCategory.CIPHER, 345 'CHACHA20_POLY1305': AlgorithmCategory.AEAD, 346 'DSA': AlgorithmCategory.SIGN, 347 'ECDSA': AlgorithmCategory.SIGN, 348 'EDDSA': AlgorithmCategory.SIGN, 349 'PURE_EDDSA': AlgorithmCategory.SIGN, 350 'RSA_PSS': AlgorithmCategory.SIGN, 351 'RSA_PKCS1V15_SIGN': AlgorithmCategory.SIGN, 352 'RSA_PKCS1V15_CRYPT': AlgorithmCategory.ASYMMETRIC_ENCRYPTION, 353 'RSA_OAEP': AlgorithmCategory.ASYMMETRIC_ENCRYPTION, 354 'HKDF': AlgorithmCategory.KEY_DERIVATION, 355 'TLS12_PRF': AlgorithmCategory.KEY_DERIVATION, 356 'TLS12_PSK_TO_MS': AlgorithmCategory.KEY_DERIVATION, 357 'TLS12_ECJPAKE_TO_PMS': AlgorithmCategory.KEY_DERIVATION, 358 'PBKDF': AlgorithmCategory.KEY_DERIVATION, 359 'ECDH': AlgorithmCategory.KEY_AGREEMENT, 360 'FFDH': AlgorithmCategory.KEY_AGREEMENT, 361 # KEY_AGREEMENT(...) is a key derivation with a key agreement component 362 'KEY_AGREEMENT': AlgorithmCategory.KEY_DERIVATION, 363 'JPAKE': AlgorithmCategory.PAKE, 364 } 365 for x in BLOCK_MAC_MODES: 366 CATEGORY_FROM_HEAD[x] = AlgorithmCategory.MAC 367 for x in BLOCK_CIPHER_MODES: 368 CATEGORY_FROM_HEAD[x] = AlgorithmCategory.CIPHER 369 for x in BLOCK_AEAD_MODES: 370 CATEGORY_FROM_HEAD[x] = AlgorithmCategory.AEAD 371 372 def determine_category(self, expr: str, head: str) -> AlgorithmCategory: 373 """Return the category of the given algorithm expression. 374 375 This function does not attempt to detect invalid inputs. 376 """ 377 prefix = head 378 while prefix: 379 if prefix in self.CATEGORY_FROM_HEAD: 380 return self.CATEGORY_FROM_HEAD[prefix] 381 if re.match(r'.*[0-9]\Z', prefix): 382 prefix = re.sub(r'_*[0-9]+\Z', r'', prefix) 383 else: 384 prefix = re.sub(r'_*[^_]*\Z', r'', prefix) 385 raise AlgorithmNotRecognized(expr) 386 387 @staticmethod 388 def determine_wildcard(expr) -> bool: 389 """Whether the given algorithm expression is a wildcard. 390 391 This function does not attempt to detect invalid inputs. 392 """ 393 if re.search(r'\bPSA_ALG_ANY_HASH\b', expr): 394 return True 395 if re.search(r'_AT_LEAST_', expr): 396 return True 397 return False 398 399 def __init__(self, expr: str) -> None: 400 """Analyze an algorithm value. 401 402 The algorithm must be expressed as a C expression containing only 403 calls to PSA algorithm constructor macros and numeric literals. 404 405 This class is only programmed to handle valid expressions. Invalid 406 expressions may result in exceptions or in nonsensical results. 407 """ 408 self.expression = re.sub(r'\s+', r'', expr) 409 self.base_expression = self.determine_base(self.expression) 410 self.head = self.determine_head(self.base_expression) 411 self.category = self.determine_category(self.base_expression, self.head) 412 self.is_wildcard = self.determine_wildcard(self.expression) 413 414 def get_key_agreement_derivation(self) -> Optional[str]: 415 """For a combined key agreement and key derivation algorithm, get the derivation part. 416 417 For anything else, return None. 418 """ 419 if self.category != AlgorithmCategory.KEY_AGREEMENT: 420 return None 421 m = re.match(r'PSA_ALG_KEY_AGREEMENT\(\w+,\s*(.*)\)\Z', self.expression) 422 if not m: 423 return None 424 kdf_alg = m.group(1) 425 # Assume kdf_alg is either a valid KDF or 0. 426 if re.match(r'(?:0[Xx])?0+\s*\Z', kdf_alg): 427 return None 428 return kdf_alg 429 430 KEY_DERIVATIONS_INCOMPATIBLE_WITH_AGREEMENT = frozenset([ 431 'PSA_ALG_TLS12_ECJPAKE_TO_PMS', # secret input in specific format 432 ]) 433 def is_valid_key_agreement_with_derivation(self) -> bool: 434 """Whether this is a valid combined key agreement and key derivation algorithm.""" 435 kdf_alg = self.get_key_agreement_derivation() 436 if kdf_alg is None: 437 return False 438 return kdf_alg not in self.KEY_DERIVATIONS_INCOMPATIBLE_WITH_AGREEMENT 439 440 def is_invalid_key_agreement_with_derivation(self) -> bool: 441 """Whether this is an invalid combined key agreement and key derivation algorithm.""" 442 kdf_alg = self.get_key_agreement_derivation() 443 if kdf_alg is None: 444 return False 445 return kdf_alg in self.KEY_DERIVATIONS_INCOMPATIBLE_WITH_AGREEMENT 446 447 def short_expression(self, level: int = 0) -> str: 448 """Abbreviate the expression, keeping it human-readable. 449 450 See `crypto_knowledge.short_expression`. 451 """ 452 return short_expression(self.expression, level=level) 453 454 HASH_LENGTH = { 455 'PSA_ALG_MD5': 16, 456 'PSA_ALG_SHA_1': 20, 457 } 458 HASH_LENGTH_BITS_RE = re.compile(r'([0-9]+)\Z') 459 @classmethod 460 def hash_length(cls, alg: str) -> int: 461 """The length of the given hash algorithm, in bytes.""" 462 if alg in cls.HASH_LENGTH: 463 return cls.HASH_LENGTH[alg] 464 m = cls.HASH_LENGTH_BITS_RE.search(alg) 465 if m: 466 return int(m.group(1)) // 8 467 raise ValueError('Unknown hash length for ' + alg) 468 469 PERMITTED_TAG_LENGTHS = { 470 'PSA_ALG_CCM': frozenset([4, 6, 8, 10, 12, 14, 16]), 471 'PSA_ALG_CHACHA20_POLY1305': frozenset([16]), 472 'PSA_ALG_GCM': frozenset([4, 8, 12, 13, 14, 15, 16]), 473 } 474 MAC_LENGTH = { 475 'PSA_ALG_CBC_MAC': 16, # actually the block cipher length 476 'PSA_ALG_CMAC': 16, # actually the block cipher length 477 } 478 HMAC_RE = re.compile(r'PSA_ALG_HMAC\((.*)\)\Z') 479 @classmethod 480 def permitted_truncations(cls, base: str) -> FrozenSet[int]: 481 """Permitted output lengths for the given MAC or AEAD base algorithm. 482 483 For a MAC algorithm, this is the set of truncation lengths that 484 Mbed TLS supports. 485 For an AEAD algorithm, this is the set of truncation lengths that 486 are permitted by the algorithm specification. 487 """ 488 if base in cls.PERMITTED_TAG_LENGTHS: 489 return cls.PERMITTED_TAG_LENGTHS[base] 490 max_length = cls.MAC_LENGTH.get(base, None) 491 if max_length is None: 492 m = cls.HMAC_RE.match(base) 493 if m: 494 max_length = cls.hash_length(m.group(1)) 495 if max_length is None: 496 raise ValueError('Unknown permitted lengths for ' + base) 497 return frozenset(range(4, max_length + 1)) 498 499 TRUNCATED_ALG_RE = re.compile( 500 r'(?P<face>PSA_ALG_(?:AEAD_WITH_SHORTENED_TAG|TRUNCATED_MAC))' 501 r'\((?P<base>.*),' 502 r'(?P<length>0[Xx][0-9A-Fa-f]+|[1-9][0-9]*|0[0-7]*)[LUlu]*\)\Z') 503 def is_invalid_truncation(self) -> bool: 504 """False for a MAC or AEAD algorithm truncated to an invalid length. 505 506 True for a MAC or AEAD algorithm truncated to a valid length or to 507 a length that cannot be determined. True for anything other than 508 a truncated MAC or AEAD. 509 """ 510 m = self.TRUNCATED_ALG_RE.match(self.expression) 511 if m: 512 base = m.group('base') 513 to_length = int(m.group('length'), 0) 514 permitted_lengths = self.permitted_truncations(base) 515 if to_length not in permitted_lengths: 516 return True 517 return False 518 519 def is_valid_for_operation(self) -> bool: 520 """Whether this algorithm construction is valid for an operation. 521 522 This function assumes that the algorithm is constructed in a 523 "grammatically" correct way, and only rejects semantically invalid 524 combinations. 525 """ 526 if self.is_wildcard: 527 return False 528 if self.is_invalid_truncation(): 529 return False 530 return True 531 532 def can_do(self, category: AlgorithmCategory) -> bool: 533 """Whether this algorithm can perform operations in the given category. 534 """ 535 if category == self.category: 536 return True 537 if category == AlgorithmCategory.KEY_DERIVATION and \ 538 self.is_valid_key_agreement_with_derivation(): 539 return True 540 return False 541 542 def usage_flags(self, public: bool = False) -> List[str]: 543 """The list of usage flags describing operations that can perform this algorithm. 544 545 If public is true, only return public-key operations, not private-key operations. 546 """ 547 if self.category == AlgorithmCategory.HASH: 548 flags = [] 549 elif self.category == AlgorithmCategory.MAC: 550 flags = ['SIGN_HASH', 'SIGN_MESSAGE', 551 'VERIFY_HASH', 'VERIFY_MESSAGE'] 552 elif self.category == AlgorithmCategory.CIPHER or \ 553 self.category == AlgorithmCategory.AEAD: 554 flags = ['DECRYPT', 'ENCRYPT'] 555 elif self.category == AlgorithmCategory.SIGN: 556 flags = ['VERIFY_HASH', 'VERIFY_MESSAGE'] 557 if not public: 558 flags += ['SIGN_HASH', 'SIGN_MESSAGE'] 559 elif self.category == AlgorithmCategory.ASYMMETRIC_ENCRYPTION: 560 flags = ['ENCRYPT'] 561 if not public: 562 flags += ['DECRYPT'] 563 elif self.category == AlgorithmCategory.KEY_DERIVATION or \ 564 self.category == AlgorithmCategory.KEY_AGREEMENT: 565 flags = ['DERIVE'] 566 else: 567 raise AlgorithmNotRecognized(self.expression) 568 return ['PSA_KEY_USAGE_' + flag for flag in flags] 569