blob: ea152c762f63ee7c73d6b4cfb95e037036bc78c2 [file] [log] [blame]
Mate Toth-Pal51b61982022-03-17 14:19:30 +01001# -----------------------------------------------------------------------------
2# Copyright (c) 2019-2022, Arm Limited. All rights reserved.
Thomas Fossati833ca652024-04-26 08:47:09 +00003# Copyright (c) 2024, Linaro Limited. All rights reserved.
Mate Toth-Pal51b61982022-03-17 14:19:30 +01004#
5# SPDX-License-Identifier: BSD-3-Clause
6#
7# -----------------------------------------------------------------------------
8
Mate Toth-Palb9057ff2022-04-29 16:03:21 +02009"""Helper utilities for CLI tools and tests"""
10
Mate Toth-Pal51b61982022-03-17 14:19:30 +010011from collections.abc import Iterable
12from copy import deepcopy
13import logging
14
15import base64
Mate Toth-Pal51b61982022-03-17 14:19:30 +010016import yaml
Thomas Fossati833ca652024-04-26 08:47:09 +000017import yaml_include
Thomas Fossatif4e1ca32024-08-16 16:01:31 +000018from pycose.keys import CoseKey
19from pycose.keys.curves import P256, P384, P521
20from pycose.keys.keytype import KtyEC2
21from pycose.algorithms import Es256, Es384, Es512, HMAC256
Mate Toth-Palbb187d02022-04-26 16:01:51 +020022from iatverifier.attest_token_verifier import AttestationTokenVerifier
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020023from cbor2 import CBOREncoder
Thomas Fossatif4e1ca32024-08-16 16:01:31 +000024from pycose.keys import CoseKey
25from ecdsa.util import number_to_string
Mate Toth-Pal51b61982022-03-17 14:19:30 +010026
27_logger = logging.getLogger("util")
28
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020029_known_curves = {
Thomas Fossatif4e1ca32024-08-16 16:01:31 +000030 P256: Es256,
31 P384: Es384,
32 P521: Es512,
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020033}
34
Thomas Fossatif4e1ca32024-08-16 16:01:31 +000035
36def es384_cose_key_from_raw_ecdsa(raw_key):
37 d = {
38 'KTY': 'EC2',
39 'CURVE': 'P_384',
40 'ALG': 'ES384',
41 'X': number_to_string(raw_key.pubkey.point.x(), raw_key.curve.generator.order()),
42 'Y': number_to_string(raw_key.pubkey.point.y(), raw_key.curve.generator.order()),
43 }
44
45 return CoseKey.from_dict(d)
46
Mate Toth-Pale305e552022-10-07 14:04:53 +020047def convert_map_to_token(token_map, verifier, wfh, *, name_as_key, parse_raw_value):
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020048 """
49 Convert a map to token and write the result to a file.
50 """
51 encoder = CBOREncoder(wfh)
52 verifier.convert_map_to_token(
53 encoder,
54 token_map,
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020055 name_as_key=name_as_key,
56 parse_raw_value=parse_raw_value,
57 root=True)
Mate Toth-Pal51b61982022-03-17 14:19:30 +010058
59
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020060def read_token_map(file):
61 """
62 Read a yaml file and return a map
63 """
Thomas Fossati833ca652024-04-26 08:47:09 +000064 yaml.add_constructor("!inc", yaml_include.Constructor(base_dir='.'), Loader=yaml.SafeLoader)
65
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020066 if hasattr(file, 'read'):
Thomas Fossati833ca652024-04-26 08:47:09 +000067 raw = yaml.load(file, Loader=yaml.SafeLoader)
Mate Toth-Pal51b61982022-03-17 14:19:30 +010068 else:
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020069 with open(file, encoding="utf8") as file_obj:
Thomas Fossati833ca652024-04-26 08:47:09 +000070 raw = yaml.load(file_obj, Loader=yaml.SafeLoader)
Mate Toth-Pal51b61982022-03-17 14:19:30 +010071
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020072 return raw
Mate Toth-Pal51b61982022-03-17 14:19:30 +010073
74
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020075def recursive_bytes_to_strings(token, in_place=False):
76 """
77 Transform the map in 'token' by changing changing bytes to base64 encoded form.
Mate Toth-Pal51b61982022-03-17 14:19:30 +010078
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020079 if 'in_place' is True, 'token' is modified, a new map is returned otherwise.
80 """
Mate Toth-Pal51b61982022-03-17 14:19:30 +010081 if in_place:
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020082 result = token
Mate Toth-Pal51b61982022-03-17 14:19:30 +010083 else:
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020084 result = deepcopy(token)
Mate Toth-Pal51b61982022-03-17 14:19:30 +010085
86 if hasattr(result, 'items'):
Mate Toth-Palb9057ff2022-04-29 16:03:21 +020087 for key, value in result.items():
88 result[key] = recursive_bytes_to_strings(value, in_place=True)
Mate Toth-Pal51b61982022-03-17 14:19:30 +010089 elif (isinstance(result, Iterable) and
90 not isinstance(result, (str, bytes))):
91 result = [recursive_bytes_to_strings(r, in_place=True)
92 for r in result]
93 elif isinstance(result, bytes):
94 result = str(base64.b16encode(result))
95
96 return result
97
98
99def read_keyfile(keyfile, method=AttestationTokenVerifier.SIGN_METHOD_SIGN1):
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200100 """
101 Read a keyfile and return the key
102 """
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100103 if keyfile:
104 if method == AttestationTokenVerifier.SIGN_METHOD_SIGN1:
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200105 return _read_sign1_key(keyfile)
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100106 if method == AttestationTokenVerifier.SIGN_METHOD_MAC0:
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200107 return _read_hmac_key(keyfile)
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100108 err_msg = 'Unexpected method "{}"; must be one of: sign, mac'
109 raise ValueError(err_msg.format(method))
110
111 return None
112
Mate Toth-Pal5ebca512022-03-24 16:45:51 +0100113def get_cose_alg_from_key(key, default):
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200114 """Extract the algorithm from the key if possible
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100115
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200116 Returns the signature algorithm ID defined by COSE
Mate Toth-Pal5ebca512022-03-24 16:45:51 +0100117 If key is None, default is returned.
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200118 """
Mate Toth-Pal5ebca512022-03-24 16:45:51 +0100119 if key is None:
120 return default
Thomas Fossatif4e1ca32024-08-16 16:01:31 +0000121 if key.kty is not KtyEC2:
122 err_msg = 'Unexpected key type "{}"; must be KtyEC2'
123 raise ValueError(err_msg.format(key.kty))
124 if key.alg is not None:
125 return key.alg
126 return _known_curves[key.crv]
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200127
128def _read_sign1_key(keyfile):
129 with open(keyfile, 'rb') as file_obj:
Thomas Fossatif4e1ca32024-08-16 16:01:31 +0000130 s = file_obj.read()
131 raw_key = s.decode("utf-8") # assume PEM-encoded key
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100132 try:
Thomas Fossatif4e1ca32024-08-16 16:01:31 +0000133 key = CoseKey.from_pem_private_key(raw_key)
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200134 except Exception as exc:
135 signing_key_error = str(exc)
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100136
137 try:
Thomas Fossatif4e1ca32024-08-16 16:01:31 +0000138 key = CoseKey.from_pem_public_key(raw_key)
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200139 except Exception as vexc:
140 verifying_key_error = str(vexc)
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100141
142 msg = 'Bad key file "{}":\n\tpubkey error: {}\n\tprikey error: {}'
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200143 raise ValueError(msg.format(keyfile, verifying_key_error, signing_key_error)) from vexc
Mate Toth-Pal51b61982022-03-17 14:19:30 +0100144 return key
145
146
Mate Toth-Palb9057ff2022-04-29 16:03:21 +0200147def _read_hmac_key(keyfile):
Thomas Fossatif4e1ca32024-08-16 16:01:31 +0000148 k = open(keyfile, 'rb').read()
149 d = {
150 'KTY': 'SYMMETRIC',
151 'ALG': HMAC256,
152 'K': k,
153 }
154 return CoseKey.from_dict(d)