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, 224, 256),
135        'PSA_ECC_FAMILY_SECP_R1': (225, 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