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