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 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 21import enum 22import re 23from typing import FrozenSet, Iterable, List, Optional, Tuple, Dict 24 25from .asymmetric_key_data import ASYMMETRIC_KEY_DATA 26 27 28def short_expression(original: str, level: int = 0) -> str: 29 """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) 39 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) 48 return short 49 50 51BLOCK_CIPHERS = frozenset(['AES', 'ARIA', 'CAMELLIA', 'DES']) 52BLOCK_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 59class 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 78 79class KeyType: 80 """Knowledge about a PSA key type.""" 81 82 def __init__(self, name: str, params: Optional[Iterable[str]] = None) -> None: 83 """Analyze a key type. 84 85 The key type must be specified in PSA syntax. In its simplest form, 86 `name` is a string 'PSA_KEY_TYPE_xxx' which is the name of a PSA key 87 type macro. For key types that take arguments, the arguments can 88 be passed either through the optional argument `params` or by 89 passing an expression of the form 'PSA_KEY_TYPE_xxx(param1, ...)' 90 in `name` as a string. 91 """ 92 93 self.name = name.strip() 94 """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 """ 99 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) 104 params = m.group(2).split(',') 105 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 """ 111 assert re.match(r'PSA_KEY_TYPE_\w+\Z', self.name) 112 113 self.expression = self.name 114 """A C expression whose value is the key type encoding.""" 115 if self.params is not None: 116 self.expression += '(' + ', '.join(self.params) + ')' 117 118 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 123 self.private_type = re.sub(r'_PUBLIC_KEY\Z', r'_KEY_PAIR', self.name) 124 """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 """ 129 130 def short_expression(self, level: int = 0) -> str: 131 """Abbreviate the expression, keeping it human-readable. 132 133 See `crypto_knowledge.short_expression`. 134 """ 135 return short_expression(self.expression, level=level) 136 137 def is_public(self) -> bool: 138 """Whether the key type is for public keys.""" 139 return self.name.endswith('_PUBLIC_KEY') 140 141 ECC_KEY_SIZES = { 142 'PSA_ECC_FAMILY_SECP_K1': (192, 224, 256), 143 'PSA_ECC_FAMILY_SECP_R1': (225, 256, 384, 521), 144 '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), 150 'PSA_ECC_FAMILY_TWISTED_EDWARDS': (255, 448), 151 } # type: Dict[str, Tuple[int, ...]] 152 KEY_TYPE_SIZES = { 153 'PSA_KEY_TYPE_AES': (128, 192, 256), # exhaustive 154 '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 160 '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 163 'PSA_KEY_TYPE_RAW_DATA': (8, 40, 128), # sample 164 'PSA_KEY_TYPE_RSA_KEY_PAIR': (1024, 1536), # small sample 165 } # type: Dict[str, Tuple[int, ...]] 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] 179 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 """ 195 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] 200 if bits % 8 != 0: 201 raise ValueError('Non-integer number of bytes: {} bits for {}' 202 .format(bits, self.expression)) 203 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] 208 return b''.join([self.DATA_BLOCK] * (length // len(self.DATA_BLOCK)) + 209 [self.DATA_BLOCK[:length % len(self.DATA_BLOCK)]]) 210 211 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-branches,too-many-return-statements 217 if not alg.is_valid_for_operation(): 218 return False 219 if self.head == 'HMAC' and alg.head == 'HMAC': 220 return True 221 if self.head == 'DES': 222 # 64-bit block ciphers only allow a reduced set of modes. 223 return alg.head in [ 224 'CBC_NO_PADDING', 'CBC_PKCS7', 225 'ECB_NO_PADDING', 226 ] 227 if self.head in BLOCK_CIPHERS and \ 228 alg.head in frozenset.union(BLOCK_MAC_MODES, 229 BLOCK_CIPHER_MODES, 230 BLOCK_AEAD_MODES): 231 if alg.head in ['CMAC', 'OFB'] and \ 232 self.head in ['ARIA', 'CAMELLIA']: 233 return False # not implemented in Mbed TLS 234 return True 235 if self.head == 'CHACHA20' and alg.head == 'CHACHA20_POLY1305': 236 return True 237 if self.head in {'ARC4', 'CHACHA20'} and \ 238 alg.head == 'STREAM_CIPHER': 239 return True 240 if self.head == 'RSA' and alg.head.startswith('RSA_'): 241 return True 242 if alg.category == AlgorithmCategory.KEY_AGREEMENT and \ 243 self.is_public(): 244 # The PSA API does not use public key objects in key agreement 245 # operations: it imports the public key as a formatted byte string. 246 # So a public key object with a key agreement algorithm is not 247 # a valid combination. 248 return False 249 if alg.is_invalid_key_agreement_with_derivation(): 250 return False 251 if self.head == 'ECC': 252 assert self.params is not None 253 eccc = EllipticCurveCategory.from_family(self.params[0]) 254 if alg.head == 'ECDH' and \ 255 eccc in {EllipticCurveCategory.SHORT_WEIERSTRASS, 256 EllipticCurveCategory.MONTGOMERY}: 257 return True 258 if alg.head == 'ECDSA' and \ 259 eccc == EllipticCurveCategory.SHORT_WEIERSTRASS: 260 return True 261 if alg.head in {'PURE_EDDSA', 'EDDSA_PREHASH'} and \ 262 eccc == EllipticCurveCategory.TWISTED_EDWARDS: 263 return True 264 return False 265 266 267class AlgorithmCategory(enum.Enum): 268 """PSA algorithm categories.""" 269 # The numbers are aligned with the category bits in numerical values of 270 # algorithms. 271 HASH = 2 272 MAC = 3 273 CIPHER = 4 274 AEAD = 5 275 SIGN = 6 276 ASYMMETRIC_ENCRYPTION = 7 277 KEY_DERIVATION = 8 278 KEY_AGREEMENT = 9 279 PAKE = 10 280 281 def requires_key(self) -> bool: 282 """Whether operations in this category are set up with a key.""" 283 return self not in {self.HASH, self.KEY_DERIVATION} 284 285 def is_asymmetric(self) -> bool: 286 """Whether operations in this category involve asymmetric keys.""" 287 return self in { 288 self.SIGN, 289 self.ASYMMETRIC_ENCRYPTION, 290 self.KEY_AGREEMENT 291 } 292 293 294class AlgorithmNotRecognized(Exception): 295 def __init__(self, expr: str) -> None: 296 super().__init__('Algorithm not recognized: ' + expr) 297 self.expr = expr 298 299 300class Algorithm: 301 """Knowledge about a PSA algorithm.""" 302 303 @staticmethod 304 def determine_base(expr: str) -> str: 305 """Return an expression for the "base" of the algorithm. 306 307 This strips off variants of algorithms such as MAC truncation. 308 309 This function does not attempt to detect invalid inputs. 310 """ 311 m = re.match(r'PSA_ALG_(?:' 312 r'(?:TRUNCATED|AT_LEAST_THIS_LENGTH)_MAC|' 313 r'AEAD_WITH_(?:SHORTENED|AT_LEAST_THIS_LENGTH)_TAG' 314 r')\((.*),[^,]+\)\Z', expr) 315 if m: 316 expr = m.group(1) 317 return expr 318 319 @staticmethod 320 def determine_head(expr: str) -> str: 321 """Return the head of an algorithm expression. 322 323 The head is the first (outermost) constructor, without its PSA_ALG_ 324 prefix, and with some normalization of similar algorithms. 325 """ 326 m = re.match(r'PSA_ALG_(?:DETERMINISTIC_)?(\w+)', expr) 327 if not m: 328 raise AlgorithmNotRecognized(expr) 329 head = m.group(1) 330 if head == 'KEY_AGREEMENT': 331 m = re.match(r'PSA_ALG_KEY_AGREEMENT\s*\(\s*PSA_ALG_(\w+)', expr) 332 if not m: 333 raise AlgorithmNotRecognized(expr) 334 head = m.group(1) 335 head = re.sub(r'_ANY\Z', r'', head) 336 if re.match(r'ED[0-9]+PH\Z', head): 337 head = 'EDDSA_PREHASH' 338 return head 339 340 CATEGORY_FROM_HEAD = { 341 'SHA': AlgorithmCategory.HASH, 342 'SHAKE256_512': AlgorithmCategory.HASH, 343 'MD': AlgorithmCategory.HASH, 344 'RIPEMD': AlgorithmCategory.HASH, 345 'ANY_HASH': AlgorithmCategory.HASH, 346 'HMAC': AlgorithmCategory.MAC, 347 'STREAM_CIPHER': AlgorithmCategory.CIPHER, 348 'CHACHA20_POLY1305': AlgorithmCategory.AEAD, 349 'DSA': AlgorithmCategory.SIGN, 350 'ECDSA': AlgorithmCategory.SIGN, 351 'EDDSA': AlgorithmCategory.SIGN, 352 'PURE_EDDSA': AlgorithmCategory.SIGN, 353 'RSA_PSS': AlgorithmCategory.SIGN, 354 'RSA_PKCS1V15_SIGN': AlgorithmCategory.SIGN, 355 'RSA_PKCS1V15_CRYPT': AlgorithmCategory.ASYMMETRIC_ENCRYPTION, 356 'RSA_OAEP': AlgorithmCategory.ASYMMETRIC_ENCRYPTION, 357 'HKDF': AlgorithmCategory.KEY_DERIVATION, 358 'TLS12_PRF': AlgorithmCategory.KEY_DERIVATION, 359 'TLS12_PSK_TO_MS': AlgorithmCategory.KEY_DERIVATION, 360 'TLS12_ECJPAKE_TO_PMS': AlgorithmCategory.KEY_DERIVATION, 361 'PBKDF': AlgorithmCategory.KEY_DERIVATION, 362 'ECDH': AlgorithmCategory.KEY_AGREEMENT, 363 'FFDH': AlgorithmCategory.KEY_AGREEMENT, 364 # KEY_AGREEMENT(...) is a key derivation with a key agreement component 365 'KEY_AGREEMENT': AlgorithmCategory.KEY_DERIVATION, 366 'JPAKE': AlgorithmCategory.PAKE, 367 } 368 for x in BLOCK_MAC_MODES: 369 CATEGORY_FROM_HEAD[x] = AlgorithmCategory.MAC 370 for x in BLOCK_CIPHER_MODES: 371 CATEGORY_FROM_HEAD[x] = AlgorithmCategory.CIPHER 372 for x in BLOCK_AEAD_MODES: 373 CATEGORY_FROM_HEAD[x] = AlgorithmCategory.AEAD 374 375 def determine_category(self, expr: str, head: str) -> AlgorithmCategory: 376 """Return the category of the given algorithm expression. 377 378 This function does not attempt to detect invalid inputs. 379 """ 380 prefix = head 381 while prefix: 382 if prefix in self.CATEGORY_FROM_HEAD: 383 return self.CATEGORY_FROM_HEAD[prefix] 384 if re.match(r'.*[0-9]\Z', prefix): 385 prefix = re.sub(r'_*[0-9]+\Z', r'', prefix) 386 else: 387 prefix = re.sub(r'_*[^_]*\Z', r'', prefix) 388 raise AlgorithmNotRecognized(expr) 389 390 @staticmethod 391 def determine_wildcard(expr) -> bool: 392 """Whether the given algorithm expression is a wildcard. 393 394 This function does not attempt to detect invalid inputs. 395 """ 396 if re.search(r'\bPSA_ALG_ANY_HASH\b', expr): 397 return True 398 if re.search(r'_AT_LEAST_', expr): 399 return True 400 return False 401 402 def __init__(self, expr: str) -> None: 403 """Analyze an algorithm value. 404 405 The algorithm must be expressed as a C expression containing only 406 calls to PSA algorithm constructor macros and numeric literals. 407 408 This class is only programmed to handle valid expressions. Invalid 409 expressions may result in exceptions or in nonsensical results. 410 """ 411 self.expression = re.sub(r'\s+', r'', expr) 412 self.base_expression = self.determine_base(self.expression) 413 self.head = self.determine_head(self.base_expression) 414 self.category = self.determine_category(self.base_expression, self.head) 415 self.is_wildcard = self.determine_wildcard(self.expression) 416 417 def get_key_agreement_derivation(self) -> Optional[str]: 418 """For a combined key agreement and key derivation algorithm, get the derivation part. 419 420 For anything else, return None. 421 """ 422 if self.category != AlgorithmCategory.KEY_AGREEMENT: 423 return None 424 m = re.match(r'PSA_ALG_KEY_AGREEMENT\(\w+,\s*(.*)\)\Z', self.expression) 425 if not m: 426 return None 427 kdf_alg = m.group(1) 428 # Assume kdf_alg is either a valid KDF or 0. 429 if re.match(r'(?:0[Xx])?0+\s*\Z', kdf_alg): 430 return None 431 return kdf_alg 432 433 KEY_DERIVATIONS_INCOMPATIBLE_WITH_AGREEMENT = frozenset([ 434 'PSA_ALG_TLS12_ECJPAKE_TO_PMS', # secret input in specific format 435 ]) 436 def is_valid_key_agreement_with_derivation(self) -> bool: 437 """Whether this is a valid combined key agreement and key derivation algorithm.""" 438 kdf_alg = self.get_key_agreement_derivation() 439 if kdf_alg is None: 440 return False 441 return kdf_alg not in self.KEY_DERIVATIONS_INCOMPATIBLE_WITH_AGREEMENT 442 443 def is_invalid_key_agreement_with_derivation(self) -> bool: 444 """Whether this is an invalid combined key agreement and key derivation algorithm.""" 445 kdf_alg = self.get_key_agreement_derivation() 446 if kdf_alg is None: 447 return False 448 return kdf_alg in self.KEY_DERIVATIONS_INCOMPATIBLE_WITH_AGREEMENT 449 450 def short_expression(self, level: int = 0) -> str: 451 """Abbreviate the expression, keeping it human-readable. 452 453 See `crypto_knowledge.short_expression`. 454 """ 455 return short_expression(self.expression, level=level) 456 457 HASH_LENGTH = { 458 'PSA_ALG_MD5': 16, 459 'PSA_ALG_SHA_1': 20, 460 } 461 HASH_LENGTH_BITS_RE = re.compile(r'([0-9]+)\Z') 462 @classmethod 463 def hash_length(cls, alg: str) -> int: 464 """The length of the given hash algorithm, in bytes.""" 465 if alg in cls.HASH_LENGTH: 466 return cls.HASH_LENGTH[alg] 467 m = cls.HASH_LENGTH_BITS_RE.search(alg) 468 if m: 469 return int(m.group(1)) // 8 470 raise ValueError('Unknown hash length for ' + alg) 471 472 PERMITTED_TAG_LENGTHS = { 473 'PSA_ALG_CCM': frozenset([4, 6, 8, 10, 12, 14, 16]), 474 'PSA_ALG_CHACHA20_POLY1305': frozenset([16]), 475 'PSA_ALG_GCM': frozenset([4, 8, 12, 13, 14, 15, 16]), 476 } 477 MAC_LENGTH = { 478 'PSA_ALG_CBC_MAC': 16, # actually the block cipher length 479 'PSA_ALG_CMAC': 16, # actually the block cipher length 480 } 481 HMAC_RE = re.compile(r'PSA_ALG_HMAC\((.*)\)\Z') 482 @classmethod 483 def permitted_truncations(cls, base: str) -> FrozenSet[int]: 484 """Permitted output lengths for the given MAC or AEAD base algorithm. 485 486 For a MAC algorithm, this is the set of truncation lengths that 487 Mbed TLS supports. 488 For an AEAD algorithm, this is the set of truncation lengths that 489 are permitted by the algorithm specification. 490 """ 491 if base in cls.PERMITTED_TAG_LENGTHS: 492 return cls.PERMITTED_TAG_LENGTHS[base] 493 max_length = cls.MAC_LENGTH.get(base, None) 494 if max_length is None: 495 m = cls.HMAC_RE.match(base) 496 if m: 497 max_length = cls.hash_length(m.group(1)) 498 if max_length is None: 499 raise ValueError('Unknown permitted lengths for ' + base) 500 return frozenset(range(4, max_length + 1)) 501 502 TRUNCATED_ALG_RE = re.compile( 503 r'(?P<face>PSA_ALG_(?:AEAD_WITH_SHORTENED_TAG|TRUNCATED_MAC))' 504 r'\((?P<base>.*),' 505 r'(?P<length>0[Xx][0-9A-Fa-f]+|[1-9][0-9]*|0[0-7]*)[LUlu]*\)\Z') 506 def is_invalid_truncation(self) -> bool: 507 """False for a MAC or AEAD algorithm truncated to an invalid length. 508 509 True for a MAC or AEAD algorithm truncated to a valid length or to 510 a length that cannot be determined. True for anything other than 511 a truncated MAC or AEAD. 512 """ 513 m = self.TRUNCATED_ALG_RE.match(self.expression) 514 if m: 515 base = m.group('base') 516 to_length = int(m.group('length'), 0) 517 permitted_lengths = self.permitted_truncations(base) 518 if to_length not in permitted_lengths: 519 return True 520 return False 521 522 def is_valid_for_operation(self) -> bool: 523 """Whether this algorithm construction is valid for an operation. 524 525 This function assumes that the algorithm is constructed in a 526 "grammatically" correct way, and only rejects semantically invalid 527 combinations. 528 """ 529 if self.is_wildcard: 530 return False 531 if self.is_invalid_truncation(): 532 return False 533 return True 534 535 def can_do(self, category: AlgorithmCategory) -> bool: 536 """Whether this algorithm can perform operations in the given category. 537 """ 538 if category == self.category: 539 return True 540 if category == AlgorithmCategory.KEY_DERIVATION and \ 541 self.is_valid_key_agreement_with_derivation(): 542 return True 543 return False 544 545 def usage_flags(self, public: bool = False) -> List[str]: 546 """The list of usage flags describing operations that can perform this algorithm. 547 548 If public is true, only return public-key operations, not private-key operations. 549 """ 550 if self.category == AlgorithmCategory.HASH: 551 flags = [] 552 elif self.category == AlgorithmCategory.MAC: 553 flags = ['SIGN_HASH', 'SIGN_MESSAGE', 554 'VERIFY_HASH', 'VERIFY_MESSAGE'] 555 elif self.category == AlgorithmCategory.CIPHER or \ 556 self.category == AlgorithmCategory.AEAD: 557 flags = ['DECRYPT', 'ENCRYPT'] 558 elif self.category == AlgorithmCategory.SIGN: 559 flags = ['VERIFY_HASH', 'VERIFY_MESSAGE'] 560 if not public: 561 flags += ['SIGN_HASH', 'SIGN_MESSAGE'] 562 elif self.category == AlgorithmCategory.ASYMMETRIC_ENCRYPTION: 563 flags = ['ENCRYPT'] 564 if not public: 565 flags += ['DECRYPT'] 566 elif self.category == AlgorithmCategory.KEY_DERIVATION or \ 567 self.category == AlgorithmCategory.KEY_AGREEMENT: 568 flags = ['DERIVE'] 569 else: 570 raise AlgorithmNotRecognized(self.expression) 571 return ['PSA_KEY_USAGE_' + flag for flag in flags] 572