blob: 726734850c572c991eb58c56e52489c655e19a50 [file] [log] [blame]
Raef Coles59cf5d82024-12-09 15:41:13 +00001#!/usr/bin/env python3
2#-------------------------------------------------------------------------------
3# SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
4#
5# SPDX-License-Identifier: BSD-3-Clause
6#
7#-------------------------------------------------------------------------------
8
9from cryptography.hazmat.primitives.asymmetric import utils, ec
10from cryptography.hazmat.primitives import hashes
11from cryptography.hazmat.primitives.serialization import load_pem_private_key, Encoding, PublicFormat
12
13import pyhsslms
14
15from cryptography.hazmat.primitives.ciphers.aead import AESCCM
16import secrets
17
18import argparse
19import logging
Raef Colescfc31242025-04-04 09:38:47 +010020logger = logging.getLogger("TF-M.{}".format(__name__))
Raef Coles59cf5d82024-12-09 15:41:13 +000021from arg_utils import *
22
23def _asn1_sig_to_raw(sig : bytes , curve : ec.EllipticCurve) -> bytes:
24 point_size = curve.key_size // 8
25 r, s = utils.decode_dss_signature(sig)
26 return bytes(0).join([x.to_bytes(point_size, byteorder="big") for x in [r, s]])
27
28def _pubkey_ecdsa(key : str) -> bytes:
29 with open(key, "rb") as f:
30 key_data = f.read()
31
32 priv_key = load_pem_private_key(key_data, password=None)
33 return priv_key.public_key().public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
34
35def _pubkey_lms(key : str) -> bytes:
36 return pyhsslms.HssLmsPublicKey(key[:-4]).hss_pub.pub.serialize()
37
38pubkey_algs = {
39 "ECDSA": _pubkey_ecdsa,
40 "LMS": _pubkey_lms,
41}
42
43def _sign_ecdsa(data : bytes,
44 key : str,
45 curve : ec.EllipticCurve = None,
46 hash_alg : hashes.HashAlgorithm = None,
Raef Coles84ca7af2024-12-20 13:34:08 +000047 dsr_output_file = None,
Raef Coles59cf5d82024-12-09 15:41:13 +000048 **kwargs : {},
49 ) -> bytes:
50 with open(key, "rb") as f:
51 key_data = f.read()
52
53 priv_key = load_pem_private_key(key_data, password=None)
54
55 hash_alg_defaults = {
56 ec.SECP256R1: hashes.SHA256,
57 ec.SECP384R1: hashes.SHA384,
58 }
59
60 if curve:
61 assert curve.name == priv_key.curve.name, "Key curve {} does not match required curve {}".format(priv_key.curve.name, curve.name)
62
63 if not hash_alg:
64 hash_alg = hash_alg_defaults[type(priv_key.curve)]
65
66 logger.info("Signing with ECDSA key {} (curve {}) and hash {}".format(key, priv_key.curve.name, hash_alg.name))
67
68 digest = hashes.Hash(hash_alg())
69 digest.update(data)
Raef Coles84ca7af2024-12-20 13:34:08 +000070 data_hash = digest.finalize()
71 logger.info("Signing hash {}".format(data_hash.hex()))
Raef Coles59cf5d82024-12-09 15:41:13 +000072
Raef Coles84ca7af2024-12-20 13:34:08 +000073 if dsr_output_file:
74 dsr_output_file.write(data_hash)
75 exit(0)
76
77 asn1_sig = priv_key.sign(data_hash, ec.ECDSA(utils.Prehashed(hash_alg())))
Raef Coles59cf5d82024-12-09 15:41:13 +000078
79 return _asn1_sig_to_raw(asn1_sig, priv_key.curve)
80
81def _sign_lms(data : bytes,
82 key : str,
Raef Coles84ca7af2024-12-20 13:34:08 +000083 dsr_output_file = None,
Raef Coles59cf5d82024-12-09 15:41:13 +000084 **kwargs : {},
85 ) -> bytes:
Raef Coles84ca7af2024-12-20 13:34:08 +000086 if dsr_output_file:
87 dsr_output_file.write(data)
88 exit(0)
89
Raef Coles59cf5d82024-12-09 15:41:13 +000090 priv_key = pyhsslms.HssLmsPrivateKey(key[:-4])
91 logger.info("Signing with LMS key {}".format(key))
92 return priv_key.sign(data)[4:]
93
Raef Coles59cf5d82024-12-09 15:41:13 +000094def sign_data(data : bytes,
95 sign_key : str,
96 sign_alg : str,
97 sign_hash_alg : str = None,
Raef Coles84ca7af2024-12-20 13:34:08 +000098 signature : bytes = None,
Raef Coles59cf5d82024-12-09 15:41:13 +000099 **kwargs,
100 ) -> bytes:
Raef Coles84ca7af2024-12-20 13:34:08 +0000101 if not signature:
102 assert(sign_key)
103 assert(sign_alg)
Raef Coles59cf5d82024-12-09 15:41:13 +0000104
Raef Coles84ca7af2024-12-20 13:34:08 +0000105 signature = sign_algs[sign_alg](data = data,
106 key = sign_key,
107 hash_alg = sign_hash_alg,
108 **kwargs)
Raef Coles59cf5d82024-12-09 15:41:13 +0000109
Raef Coles84ca7af2024-12-20 13:34:08 +0000110 return signature
Raef Coles59cf5d82024-12-09 15:41:13 +0000111
112def get_pubkey(sign_key : str,
113 sign_alg : str,
114 **kwargs
115 ) -> bytes:
116 return pubkey_algs[sign_alg](sign_key)
117
118sign_algs = {
119 "ECDSA": _sign_ecdsa,
120 "LMS": _sign_lms,
121}
122
123def add_arguments(parser : argparse.ArgumentParser,
124 prefix : str = "",
125 required : bool = True,
126 ) -> None:
127 add_prefixed_argument(parser, "sign_key", prefix, help="signing key input file",
128 type=str, required=required)
129 add_prefixed_argument(parser, "sign_alg", prefix, help="signing algorithm",
130 choices=sign_algs.keys(), required=required)
131 add_prefixed_argument(parser, "sign_hash_alg", prefix, help="signing hash algorithm",
132 type=arg_type_hash, required=False)
Raef Coles84ca7af2024-12-20 13:34:08 +0000133 add_prefixed_argument(parser, "dsr_output_file", prefix, help="path to output Data Signing Request to",
134 type=arg_type_bytes_output_file, required=False)
135 add_prefixed_argument(parser, "signature", prefix, help="signature (if already signed)",
136 type=arg_type_bytes, required=False)
Raef Coles59cf5d82024-12-09 15:41:13 +0000137
138def parse_args(args : argparse.Namespace,
139 prefix : str = "",
140 ) -> dict:
141 out = parse_args_automatically(args, ["sign_key", "sign_alg", "sign_hash_alg"], prefix)
142 return out
143
144script_description = """
145Sign some data.
146"""
147if __name__ == "__main__":
148 import argparse
149
150 parser = argparse.ArgumentParser(allow_abbrev=False,
151 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
152 description=script_description)
153 parser.add_argument("--log_level", help="log level", required=False, default="ERROR", choices=logging._levelToName.values())
154 parser.add_argument("--data", help="data to sign", type=arg_type_bytes, required=True)
155
156 add_arguments(parser, required=True)
157
158 args = parser.parse_args()
Raef Colescfc31242025-04-04 09:38:47 +0100159 logging.getLogger("TF-M").setLevel(args.log_level)
160 logger.addHandler(logging.StreamHandler())
Raef Coles59cf5d82024-12-09 15:41:13 +0000161
162 config = parse_args(args)
163 config |= parse_args_automatically(args, ["data"])
164
165 print(sign_data(**config).hex())