Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 1 | # ----------------------------------------------------------------------------- |
| 2 | # Copyright (c) 2019-2022, Arm Limited. All rights reserved. |
Thomas Fossati | 833ca65 | 2024-04-26 08:47:09 +0000 | [diff] [blame] | 3 | # Copyright (c) 2024, Linaro Limited. All rights reserved. |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 4 | # |
| 5 | # SPDX-License-Identifier: BSD-3-Clause |
| 6 | # |
| 7 | # ----------------------------------------------------------------------------- |
| 8 | |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 9 | """Helper utilities for CLI tools and tests""" |
| 10 | |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 11 | from collections.abc import Iterable |
| 12 | from copy import deepcopy |
| 13 | import logging |
| 14 | |
| 15 | import base64 |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 16 | import yaml |
Thomas Fossati | 833ca65 | 2024-04-26 08:47:09 +0000 | [diff] [blame] | 17 | import yaml_include |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 18 | from ecdsa import SigningKey, VerifyingKey |
Mate Toth-Pal | bb187d0 | 2022-04-26 16:01:51 +0200 | [diff] [blame] | 19 | from iatverifier.attest_token_verifier import AttestationTokenVerifier |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 20 | from cbor2 import CBOREncoder |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 21 | |
| 22 | _logger = logging.getLogger("util") |
| 23 | |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 24 | _known_curves = { |
| 25 | "NIST256p": AttestationTokenVerifier.COSE_ALG_ES256, |
| 26 | "NIST384p": AttestationTokenVerifier.COSE_ALG_ES384, |
| 27 | "NIST521p": AttestationTokenVerifier.COSE_ALG_ES512, |
| 28 | } |
| 29 | |
Mate Toth-Pal | e305e55 | 2022-10-07 14:04:53 +0200 | [diff] [blame] | 30 | def convert_map_to_token(token_map, verifier, wfh, *, name_as_key, parse_raw_value): |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 31 | """ |
| 32 | Convert a map to token and write the result to a file. |
| 33 | """ |
| 34 | encoder = CBOREncoder(wfh) |
| 35 | verifier.convert_map_to_token( |
| 36 | encoder, |
| 37 | token_map, |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 38 | name_as_key=name_as_key, |
| 39 | parse_raw_value=parse_raw_value, |
| 40 | root=True) |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 41 | |
| 42 | |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 43 | def read_token_map(file): |
| 44 | """ |
| 45 | Read a yaml file and return a map |
| 46 | """ |
Thomas Fossati | 833ca65 | 2024-04-26 08:47:09 +0000 | [diff] [blame] | 47 | yaml.add_constructor("!inc", yaml_include.Constructor(base_dir='.'), Loader=yaml.SafeLoader) |
| 48 | |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 49 | if hasattr(file, 'read'): |
Thomas Fossati | 833ca65 | 2024-04-26 08:47:09 +0000 | [diff] [blame] | 50 | raw = yaml.load(file, Loader=yaml.SafeLoader) |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 51 | else: |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 52 | with open(file, encoding="utf8") as file_obj: |
Thomas Fossati | 833ca65 | 2024-04-26 08:47:09 +0000 | [diff] [blame] | 53 | raw = yaml.load(file_obj, Loader=yaml.SafeLoader) |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 54 | |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 55 | return raw |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 56 | |
| 57 | |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 58 | def recursive_bytes_to_strings(token, in_place=False): |
| 59 | """ |
| 60 | Transform the map in 'token' by changing changing bytes to base64 encoded form. |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 61 | |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 62 | if 'in_place' is True, 'token' is modified, a new map is returned otherwise. |
| 63 | """ |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 64 | if in_place: |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 65 | result = token |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 66 | else: |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 67 | result = deepcopy(token) |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 68 | |
| 69 | if hasattr(result, 'items'): |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 70 | for key, value in result.items(): |
| 71 | result[key] = recursive_bytes_to_strings(value, in_place=True) |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 72 | elif (isinstance(result, Iterable) and |
| 73 | not isinstance(result, (str, bytes))): |
| 74 | result = [recursive_bytes_to_strings(r, in_place=True) |
| 75 | for r in result] |
| 76 | elif isinstance(result, bytes): |
| 77 | result = str(base64.b16encode(result)) |
| 78 | |
| 79 | return result |
| 80 | |
| 81 | |
| 82 | def read_keyfile(keyfile, method=AttestationTokenVerifier.SIGN_METHOD_SIGN1): |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 83 | """ |
| 84 | Read a keyfile and return the key |
| 85 | """ |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 86 | if keyfile: |
| 87 | if method == AttestationTokenVerifier.SIGN_METHOD_SIGN1: |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 88 | return _read_sign1_key(keyfile) |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 89 | if method == AttestationTokenVerifier.SIGN_METHOD_MAC0: |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 90 | return _read_hmac_key(keyfile) |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 91 | err_msg = 'Unexpected method "{}"; must be one of: sign, mac' |
| 92 | raise ValueError(err_msg.format(method)) |
| 93 | |
| 94 | return None |
| 95 | |
Mate Toth-Pal | 5ebca51 | 2022-03-24 16:45:51 +0100 | [diff] [blame] | 96 | def get_cose_alg_from_key(key, default): |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 97 | """Extract the algorithm from the key if possible |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 98 | |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 99 | Returns the signature algorithm ID defined by COSE |
Mate Toth-Pal | 5ebca51 | 2022-03-24 16:45:51 +0100 | [diff] [blame] | 100 | If key is None, default is returned. |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 101 | """ |
Mate Toth-Pal | 5ebca51 | 2022-03-24 16:45:51 +0100 | [diff] [blame] | 102 | if key is None: |
| 103 | return default |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 104 | if not hasattr(key, "curve"): |
| 105 | raise ValueError("Key has no curve specified in it.") |
| 106 | return _known_curves[key.curve.name] |
| 107 | |
| 108 | def _read_sign1_key(keyfile): |
| 109 | with open(keyfile, 'rb') as file_obj: |
| 110 | raw_key = file_obj.read() |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 111 | try: |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 112 | key = SigningKey.from_pem(raw_key) |
| 113 | except Exception as exc: |
| 114 | signing_key_error = str(exc) |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 115 | |
| 116 | try: |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 117 | key = VerifyingKey.from_pem(raw_key) |
| 118 | except Exception as vexc: |
| 119 | verifying_key_error = str(vexc) |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 120 | |
| 121 | msg = 'Bad key file "{}":\n\tpubkey error: {}\n\tprikey error: {}' |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 122 | raise ValueError(msg.format(keyfile, verifying_key_error, signing_key_error)) from vexc |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 123 | return key |
| 124 | |
| 125 | |
Mate Toth-Pal | b9057ff | 2022-04-29 16:03:21 +0200 | [diff] [blame] | 126 | def _read_hmac_key(keyfile): |
Mate Toth-Pal | 51b6198 | 2022-03-17 14:19:30 +0100 | [diff] [blame] | 127 | return open(keyfile, 'rb').read() |