blob: 05db887a0995e56bd292f105ef069885f3112ea1 [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 Peskine0dacd4d2021-04-29 20:38:01 +020021import enum
Gilles Peskine0156a152021-01-26 21:23:56 +010022import re
gabor-mezei-arm5071a2e2021-06-29 11:17:54 +020023from typing import Dict, Iterable, Optional, Pattern, 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 Peskine0dacd4d2021-04-29 20:38:01 +020027
28BLOCK_MAC_MODES = frozenset(['CBC_MAC', 'CMAC'])
29BLOCK_CIPHER_MODES = frozenset([
30 'CTR', 'CFB', 'OFB', 'XTS', 'CCM_STAR_NO_TAG',
31 'ECB_NO_PADDING', 'CBC_NO_PADDING', 'CBC_PKCS7',
32])
33BLOCK_AEAD_MODES = frozenset(['CCM', 'GCM'])
34
35
Gilles Peskine0156a152021-01-26 21:23:56 +010036class KeyType:
37 """Knowledge about a PSA key type."""
38
Gilles Peskine2a71b722021-04-29 20:19:57 +020039 def __init__(self, name: str, params: Optional[Iterable[str]] = None) -> None:
Gilles Peskine0156a152021-01-26 21:23:56 +010040 """Analyze a key type.
41
42 The key type must be specified in PSA syntax. In its simplest form,
Gilles Peskinefa3c69a2021-02-16 14:29:22 +010043 `name` is a string 'PSA_KEY_TYPE_xxx' which is the name of a PSA key
Gilles Peskine0156a152021-01-26 21:23:56 +010044 type macro. For key types that take arguments, the arguments can
45 be passed either through the optional argument `params` or by
Gilles Peskine0ba69a42021-04-12 13:41:52 +020046 passing an expression of the form 'PSA_KEY_TYPE_xxx(param1, ...)'
Gilles Peskinefa3c69a2021-02-16 14:29:22 +010047 in `name` as a string.
Gilles Peskine0156a152021-01-26 21:23:56 +010048 """
Gilles Peskined75adfc2021-02-17 18:04:28 +010049
Gilles Peskine0156a152021-01-26 21:23:56 +010050 self.name = name.strip()
Gilles Peskinefa3c69a2021-02-16 14:29:22 +010051 """The key type macro name (``PSA_KEY_TYPE_xxx``).
52
53 For key types constructed from a macro with arguments, this is the
54 name of the macro, and the arguments are in `self.params`.
55 """
Gilles Peskine0156a152021-01-26 21:23:56 +010056 if params is None:
57 if '(' in self.name:
58 m = re.match(r'(\w+)\s*\((.*)\)\Z', self.name)
59 assert m is not None
60 self.name = m.group(1)
Gilles Peskine0ba69a42021-04-12 13:41:52 +020061 params = m.group(2).split(',')
Gilles Peskinefa3c69a2021-02-16 14:29:22 +010062 self.params = (None if params is None else
63 [param.strip() for param in params])
64 """The parameters of the key type, if there are any.
65
66 None if the key type is a macro without arguments.
67 """
Gilles Peskined75adfc2021-02-17 18:04:28 +010068 assert re.match(r'PSA_KEY_TYPE_\w+\Z', self.name)
69
Gilles Peskine0156a152021-01-26 21:23:56 +010070 self.expression = self.name
Gilles Peskinefa3c69a2021-02-16 14:29:22 +010071 """A C expression whose value is the key type encoding."""
Gilles Peskine0156a152021-01-26 21:23:56 +010072 if self.params is not None:
73 self.expression += '(' + ', '.join(self.params) + ')'
Gilles Peskined75adfc2021-02-17 18:04:28 +010074
Gilles Peskine0156a152021-01-26 21:23:56 +010075 self.private_type = re.sub(r'_PUBLIC_KEY\Z', r'_KEY_PAIR', self.name)
Gilles Peskinefa3c69a2021-02-16 14:29:22 +010076 """The key type macro name for the corresponding key pair type.
77
78 For everything other than a public key type, this is the same as
79 `self.name`.
80 """
Gilles Peskinedf639682021-01-26 21:25:34 +010081
82 ECC_KEY_SIZES = {
83 'PSA_ECC_FAMILY_SECP_K1': (192, 224, 256),
Gilles Peskine0ac258e2021-01-27 13:11:59 +010084 'PSA_ECC_FAMILY_SECP_R1': (225, 256, 384, 521),
Gilles Peskinedf639682021-01-26 21:25:34 +010085 'PSA_ECC_FAMILY_SECP_R2': (160,),
86 'PSA_ECC_FAMILY_SECT_K1': (163, 233, 239, 283, 409, 571),
87 'PSA_ECC_FAMILY_SECT_R1': (163, 233, 283, 409, 571),
88 'PSA_ECC_FAMILY_SECT_R2': (163,),
89 'PSA_ECC_FAMILY_BRAINPOOL_P_R1': (160, 192, 224, 256, 320, 384, 512),
90 'PSA_ECC_FAMILY_MONTGOMERY': (255, 448),
Gilles Peskinea00abc62021-03-16 18:25:14 +010091 'PSA_ECC_FAMILY_TWISTED_EDWARDS': (255, 448),
Gilles Peskinedf639682021-01-26 21:25:34 +010092 }
93 KEY_TYPE_SIZES = {
94 'PSA_KEY_TYPE_AES': (128, 192, 256), # exhaustive
95 'PSA_KEY_TYPE_ARC4': (8, 128, 2048), # extremes + sensible
96 'PSA_KEY_TYPE_ARIA': (128, 192, 256), # exhaustive
97 'PSA_KEY_TYPE_CAMELLIA': (128, 192, 256), # exhaustive
98 'PSA_KEY_TYPE_CHACHA20': (256,), # exhaustive
99 'PSA_KEY_TYPE_DERIVE': (120, 128), # sample
100 'PSA_KEY_TYPE_DES': (64, 128, 192), # exhaustive
101 'PSA_KEY_TYPE_HMAC': (128, 160, 224, 256, 384, 512), # standard size for each supported hash
102 'PSA_KEY_TYPE_RAW_DATA': (8, 40, 128), # sample
103 'PSA_KEY_TYPE_RSA_KEY_PAIR': (1024, 1536), # small sample
104 }
105 def sizes_to_test(self) -> Tuple[int, ...]:
106 """Return a tuple of key sizes to test.
107
108 For key types that only allow a single size, or only a small set of
109 sizes, these are all the possible sizes. For key types that allow a
110 wide range of sizes, these are a representative sample of sizes,
111 excluding large sizes for which a typical resource-constrained platform
112 may run out of memory.
113 """
114 if self.private_type == 'PSA_KEY_TYPE_ECC_KEY_PAIR':
115 assert self.params is not None
116 return self.ECC_KEY_SIZES[self.params[0]]
117 return self.KEY_TYPE_SIZES[self.private_type]
Gilles Peskine397b0282021-01-26 21:26:26 +0100118
119 # "48657265006973206b6579a064617461"
120 DATA_BLOCK = b'Here\000is key\240data'
121 def key_material(self, bits: int) -> bytes:
122 """Return a byte string containing suitable key material with the given bit length.
123
124 Use the PSA export representation. The resulting byte string is one that
125 can be obtained with the following code:
126 ```
127 psa_set_key_type(&attributes, `self.expression`);
128 psa_set_key_bits(&attributes, `bits`);
129 psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_EXPORT);
130 psa_generate_key(&attributes, &id);
131 psa_export_key(id, `material`, ...);
132 ```
133 """
Gilles Peskine6f6483f2021-01-27 12:43:24 +0100134 if self.expression in ASYMMETRIC_KEY_DATA:
135 if bits not in ASYMMETRIC_KEY_DATA[self.expression]:
136 raise ValueError('No key data for {}-bit {}'
137 .format(bits, self.expression))
138 return ASYMMETRIC_KEY_DATA[self.expression][bits]
Gilles Peskine397b0282021-01-26 21:26:26 +0100139 if bits % 8 != 0:
Gilles Peskine6f6483f2021-01-27 12:43:24 +0100140 raise ValueError('Non-integer number of bytes: {} bits for {}'
141 .format(bits, self.expression))
Gilles Peskine397b0282021-01-26 21:26:26 +0100142 length = bits // 8
143 if self.name == 'PSA_KEY_TYPE_DES':
144 # "644573206b457901644573206b457902644573206b457904"
145 des3 = b'dEs kEy\001dEs kEy\002dEs kEy\004'
146 return des3[:length]
Gilles Peskine397b0282021-01-26 21:26:26 +0100147 return b''.join([self.DATA_BLOCK] * (length // len(self.DATA_BLOCK)) +
148 [self.DATA_BLOCK[:length % len(self.DATA_BLOCK)]])
gabor-mezei-arm805c7352021-06-28 20:02:11 +0200149
150 KEY_TYPE_FOR_SIGNATURE = {
gabor-mezei-arm5071a2e2021-06-29 11:17:54 +0200151 'PSA_KEY_USAGE_SIGN_HASH': re.compile('.*KEY_PAIR'),
152 'PSA_KEY_USAGE_VERIFY_HASH': re.compile('.*KEY.*')
153 } #type: Dict[str, Pattern]
gabor-mezei-arm805c7352021-06-28 20:02:11 +0200154 """Use a regexp to determine key types for which signature is possible
155 when using the actual usage flag.
156 """
157 def is_valid_for_signature(self, usage: str) -> bool:
158 """Determine if the key type is compatible with the specified
159 signitute type.
160
161 """
162 # This is just temporaly solution for the implicit usage flags.
163 return re.match(self.KEY_TYPE_FOR_SIGNATURE[usage], self.name) is not None
Gilles Peskine0dacd4d2021-04-29 20:38:01 +0200164
165
166class AlgorithmCategory(enum.Enum):
167 """PSA algorithm categories."""
168 # The numbers are aligned with the category bits in numerical values of
169 # algorithms.
170 HASH = 2
171 MAC = 3
172 CIPHER = 4
173 AEAD = 5
174 SIGN = 6
175 ASYMMETRIC_ENCRYPTION = 7
176 KEY_DERIVATION = 8
177 KEY_AGREEMENT = 9
178 PAKE = 10
179
180 def requires_key(self) -> bool:
181 return self not in {self.HASH, self.KEY_DERIVATION}
182
183
184class AlgorithmNotRecognized(Exception):
185 def __init__(self, expr: str) -> None:
186 super().__init__('Algorithm not recognized: ' + expr)
187 self.expr = expr
188
189
190class Algorithm:
191 """Knowledge about a PSA algorithm."""
192
193 @staticmethod
194 def determine_base(expr: str) -> str:
195 """Return an expression for the "base" of the algorithm.
196
197 This strips off variants of algorithms such as MAC truncation.
198
199 This function does not attempt to detect invalid inputs.
200 """
201 m = re.match(r'PSA_ALG_(?:'
202 r'(?:TRUNCATED|AT_LEAST_THIS_LENGTH)_MAC|'
203 r'AEAD_WITH_(?:SHORTENED|AT_LEAST_THIS_LENGTH)_TAG'
204 r')\((.*),[^,]+\)\Z', expr)
205 if m:
206 expr = m.group(1)
207 return expr
208
209 @staticmethod
210 def determine_head(expr: str) -> str:
211 """Return the head of an algorithm expression.
212
213 The head is the first (outermost) constructor, without its PSA_ALG_
214 prefix, and with some normalization of similar algorithms.
215 """
216 m = re.match(r'PSA_ALG_(?:DETERMINISTIC_)?(\w+)', expr)
217 if not m:
218 raise AlgorithmNotRecognized(expr)
219 head = m.group(1)
220 if head == 'KEY_AGREEMENT':
221 m = re.match(r'PSA_ALG_KEY_AGREEMENT\s*\(\s*PSA_ALG_(\w+)', expr)
222 if not m:
223 raise AlgorithmNotRecognized(expr)
224 head = m.group(1)
225 head = re.sub(r'_ANY\Z', r'', head)
226 if re.match(r'ED[0-9]+PH\Z', head):
227 head = 'EDDSA_PREHASH'
228 return head
229
230 CATEGORY_FROM_HEAD = {
231 'SHA': AlgorithmCategory.HASH,
232 'SHAKE256_512': AlgorithmCategory.HASH,
233 'MD': AlgorithmCategory.HASH,
234 'RIPEMD': AlgorithmCategory.HASH,
235 'ANY_HASH': AlgorithmCategory.HASH,
236 'HMAC': AlgorithmCategory.MAC,
237 'STREAM_CIPHER': AlgorithmCategory.CIPHER,
238 'CHACHA20_POLY1305': AlgorithmCategory.AEAD,
239 'DSA': AlgorithmCategory.SIGN,
240 'ECDSA': AlgorithmCategory.SIGN,
241 'EDDSA': AlgorithmCategory.SIGN,
242 'PURE_EDDSA': AlgorithmCategory.SIGN,
243 'RSA_PSS': AlgorithmCategory.SIGN,
244 'RSA_PKCS1V15_SIGN': AlgorithmCategory.SIGN,
245 'RSA_PKCS1V15_CRYPT': AlgorithmCategory.ASYMMETRIC_ENCRYPTION,
246 'RSA_OAEP': AlgorithmCategory.ASYMMETRIC_ENCRYPTION,
247 'HKDF': AlgorithmCategory.KEY_DERIVATION,
248 'TLS12_PRF': AlgorithmCategory.KEY_DERIVATION,
249 'TLS12_PSK_TO_MS': AlgorithmCategory.KEY_DERIVATION,
250 'PBKDF': AlgorithmCategory.KEY_DERIVATION,
251 'ECDH': AlgorithmCategory.KEY_AGREEMENT,
252 'FFDH': AlgorithmCategory.KEY_AGREEMENT,
253 # KEY_AGREEMENT(...) is a key derivation with a key agreement component
254 'KEY_AGREEMENT': AlgorithmCategory.KEY_DERIVATION,
255 'JPAKE': AlgorithmCategory.PAKE,
256 }
257 for x in BLOCK_MAC_MODES:
258 CATEGORY_FROM_HEAD[x] = AlgorithmCategory.MAC
259 for x in BLOCK_CIPHER_MODES:
260 CATEGORY_FROM_HEAD[x] = AlgorithmCategory.CIPHER
261 for x in BLOCK_AEAD_MODES:
262 CATEGORY_FROM_HEAD[x] = AlgorithmCategory.AEAD
263
264 def determine_category(self, expr: str, head: str) -> AlgorithmCategory:
265 """Return the category of the given algorithm expression.
266
267 This function does not attempt to detect invalid inputs.
268 """
269 prefix = head
270 while prefix:
271 if prefix in self.CATEGORY_FROM_HEAD:
272 return self.CATEGORY_FROM_HEAD[prefix]
273 if re.match(r'.*[0-9]\Z', prefix):
274 prefix = re.sub(r'_*[0-9]+\Z', r'', prefix)
275 else:
276 prefix = re.sub(r'_*[^_]*\Z', r'', prefix)
277 raise AlgorithmNotRecognized(expr)
278
279 @staticmethod
280 def determine_wildcard(expr) -> bool:
281 """Whether the given algorithm expression is a wildcard.
282
283 This function does not attempt to detect invalid inputs.
284 """
285 if re.search(r'\bPSA_ALG_ANY_HASH\b', expr):
286 return True
287 if re.search(r'_AT_LEAST_', expr):
288 return True
289 return False
290
291 def __init__(self, expr: str) -> None:
292 """Analyze an algorithm value.
293
294 The algorithm must be expressed as a C expression containing only
295 calls to PSA algorithm constructor macros and numeric literals.
296
297 This class is only programmed to handle valid expressions. Invalid
298 expressions may result in exceptions or in nonsensical results.
299 """
300 self.expression = re.sub(r'\s+', r'', expr)
301 self.base_expression = self.determine_base(self.expression)
302 self.head = self.determine_head(self.base_expression)
303 self.category = self.determine_category(self.base_expression, self.head)
304 self.is_wildcard = self.determine_wildcard(self.expression)