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/encrypt_data.py b/tools/modules/encrypt_data.py
new file mode 100644
index 0000000..e43c9fa
--- /dev/null
+++ b/tools/modules/encrypt_data.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+#-------------------------------------------------------------------------------
+# SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+
+import key_derivation
+
+import argparse
+import logging
+logger = logging.getLogger("TF-M")
+from arg_utils import *
+import secrets
+
+def _encrypt_aes_ctr(data : bytes,
+ iv : bytes,
+ key : bytes
+ ) -> bytes:
+ assert len(iv) >= 8
+
+ if len(iv) < 16:
+ iv += bytes(16 - len(iv))
+
+ c = Cipher(algorithms.AES(key), modes.CTR(iv))
+ return c.encryptor().update(data)
+
+encrypt_algs = {
+ "AES_CTR": _encrypt_aes_ctr,
+}
+
+def add_arguments(parser : argparse.ArgumentParser,
+ prefix : str = "",
+ required : bool = True,
+ ) -> None:
+ add_prefixed_argument(parser, "encrypt_key", prefix, help="encryption key input file",
+ required=required, type=arg_type_bytes)
+ add_prefixed_argument(parser, "encrypt_alg", prefix, help="encryption algorithm",
+ choices=encrypt_algs.keys(), required=required)
+ add_prefixed_argument(parser, "iv", prefix, help="encryption intial value",
+ type=arg_type_bytes, required=False)
+
+ key_derivation.add_arguments(parser, prefix, required=False)
+
+def parse_args(args : argparse.Namespace,
+ prefix : str = "",
+ ) -> dict:
+ out = parse_args_automatically(args, ["encrypt_alg", "encrypt_key", "iv"], prefix)
+ out["kdf_args"] = key_derivation.parse_args(args)
+
+ return out
+
+def encrypt_data(data : bytes,
+ encrypt_key : bytes,
+ encrypt_alg : str,
+ kdf_args : dict,
+ iv : bytes = None,
+ **kwargs,
+ ) -> (bytes, bytes):
+ if not data:
+ return iv, data
+
+ assert(encrypt_key)
+ assert(encrypt_alg)
+
+ if kdf_args:
+ # FixMe: If RSE_SYMMETRIC_PROVISIONING is OFF, should we use directly
+ # the encrypt_key without deriving another one?
+ encrypt_key = key_derivation.derive_symmetric_key(**kdf_args,
+ input_key = encrypt_key,
+ length = 32
+ )
+
+ if not iv:
+ iv = secrets.token_bytes(12) + int(0).to_bytes(4, 'little')
+
+ encrypted_data = encrypt_algs[encrypt_alg](
+ data = data,
+ key = encrypt_key,
+ iv = iv)
+ return iv, encrypted_data
+
+script_description = """
+Encrypt some data
+"""
+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())
+ parser.add_argument("--data", help="data to sign", type=arg_type_bytes, required=True)
+
+ add_arguments(parser, required=True)
+
+ args = parser.parse_args()
+ logger.setLevel(args.log_level)
+ config = parse_args(args)
+ config |= parse_args_automatically(args, ["data"])
+
+ print(encrypt_data(**config)[1].hex())