Tools: Add python modules
Add generic python modules to TF-M to support build and image packaging
tools.
* arg_utils provides helpers to deal with argparse arguments
* c_include gets the list of include paths for a file from the
`compile_commands.json` build database
* c_macro includes a python implementation of (most of) the C
preprocessor.
* c_struct is a libclang-based evaluator of C datastructures (Including
enums) which can be used to generate python representations of nested
C datastructures which rely on complex macro configuration.
* crypto_conversion_utils provides helpers to convert various types of
crypto keys to different and convert string representations of
algorithms and hash functions to their python objects
* encrypt_data provides functions to encrypt bytes() objects
* file_loader provides automatic handler functions for various filetypes
based on their extensions, primarily useful for loading crypto keys
* key_derivation provides a python implementation of HKDF and a
SP200-108 CMAC KDF, both matching the TF-M/MbedTLS/CC3XX implementation
* sign_data provides functions to perform symmetric and asymmetric
signatures of bytes() objects
* sign_then_encrypt_data provides combined signing and encryption,
either via symmetric AEAD modes or a combination of the sign_data and
encrypt_data modules
* struct_pack provides helper functions for packing bytes objects
together.
Change-Id: I858dd8ef69c9069ec0a44e4ad3f9a1d70cc5d4da
Signed-off-by: Raef Coles <raef.coles@arm.com>
diff --git a/tools/modules/sign_then_encrypt_data.py b/tools/modules/sign_then_encrypt_data.py
new file mode 100644
index 0000000..7af18bb
--- /dev/null
+++ b/tools/modules/sign_then_encrypt_data.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+#-------------------------------------------------------------------------------
+# SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+import argparse
+
+import logging
+logger = logging.getLogger("TF-M")
+
+import arg_utils
+import sign_data
+import encrypt_data
+
+from cryptography.hazmat.primitives.ciphers.aead import AESCCM
+import secrets
+
+def _sign_then_encrypt_aes_ccm(data : bytes,
+ sign_and_encrypt_key : bytes,
+ aad : bytes = bytes(0),
+ iv : bytes = None,
+ tag_length : int = 16,
+ **kwargs,
+ ):
+ if not iv:
+ iv = secrets.token_bytes(12)
+
+ output = AESCCM(sign_and_encrypt_key, tag_length=tag_length).encrypt(nonce=iv,
+ data=data,
+ associated_data=aad)
+ signature = output[-tag_length:]
+ ciphertext = output[:-tag_length]
+ logger.info("Encrypting with IV {}".format(iv.hex()))
+ logger.info("Encrypting with key {}".format(sign_and_encrypt_key.hex()))
+ return iv, ciphertext, signature
+
+combined_algs = {
+ "AES_CCM": _sign_then_encrypt_aes_ccm,
+}
+
+def add_arguments(parser : argparse.ArgumentParser,
+ prefix : str = "",
+ required : bool = True,
+ ) -> None:
+ arg_utils.add_prefixed_argument(parser, "sign_and_encrypt_key", prefix,
+ help="combined sign/encrypt key input file",
+ type=arg_utils.arg_type_bytes, required=False)
+ arg_utils.add_prefixed_argument(parser, "sign_and_encrypt_alg", prefix,
+ help="combined sign/encrypt algorithm",
+ choices=combined_algs.keys(), required=False)
+ encrypt_data.add_arguments(parser, prefix, False)
+ sign_data.add_arguments(parser, prefix, False)
+
+def parse_args(args : argparse.Namespace,
+ prefix : str = "",
+ ) -> dict:
+ out = arg_utils.parse_args_automatically(args, ["sign_and_encrypt_alg",
+ "sign_and_encrypt_key"], prefix)
+
+ out |= sign_data.parse_args(args, prefix)
+ out |= encrypt_data.parse_args(args, prefix)
+
+ arg_utils.incompatible_arg(args, "sign_and_encrypt_alg", "sign_alg")
+ arg_utils.incompatible_arg(args, "sign_and_encrypt_alg", "encrypt_alg")
+
+ return out
+
+def sign_then_encrypt_data(data : bytes,
+ sign_and_encrypt_alg : str = None,
+ aad : bytes = bytes(0),
+ **kwargs,
+ ) -> bytes:
+ if sign_and_encrypt_alg:
+ return combined_algs[sign_and_encrypt_alg](data=data, aad=aad, **kwargs)
+ else:
+ iv, ciphertext = encrypt_data.encrypt_data(data=data, **kwargs)
+ return iv, ciphertext, sign_data.sign_data(data=aad + data, **kwargs)
+
+script_description = """
+"""
+if __name__ == "__main__":
+ import argparse
+
+ parser = argparse.ArgumentParser(allow_abbrev=False,
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ description=script_description)
+ parser.add_argument("--log_level", help="log level", required=False, default="ERROR", choices=logging._levelToName.values())
+
+ args = parser.parse_args()
+ logger.setLevel(args.log_level)