TA dev kit: add support for TA encryption

Add CFG_ENCRYPT_TA as TA build time configuration option to enable
encryption of TA using encryption key provided via TA_ENC_KEY build
time option. The default value of TA_ENC_KEY is derived from 16 zero
bytes default hardware unique key.

Also rename scripts/sign.py to scripts/sign_encrypt.py to reflect
optional encryption support along with signing of TAs.

Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
diff --git a/scripts/sign.py b/scripts/sign_encrypt.py
similarity index 78%
rename from scripts/sign.py
rename to scripts/sign_encrypt.py
index 2939c59..0b3408d 100755
--- a/scripts/sign.py
+++ b/scripts/sign_encrypt.py
@@ -1,8 +1,8 @@
 #!/usr/bin/env python3
-#
-# Copyright (c) 2015, 2017, Linaro Limited
-#
 # SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2015, 2017, 2019, Linaro Limited
+#
 
 import sys
 
@@ -19,7 +19,7 @@
 def get_args(logger):
     from argparse import ArgumentParser, RawDescriptionHelpFormatter
     import textwrap
-    command_base = ['sign', 'digest', 'stitch']
+    command_base = ['sign-enc', 'digest', 'stitch']
     command_aliases_digest = ['generate-digest']
     command_aliases_stitch = ['stitch-ta']
     command_aliases = command_aliases_digest + command_aliases_stitch
@@ -29,26 +29,31 @@
     sat = '[' + ', '.join(command_aliases_stitch) + ']'
 
     parser = ArgumentParser(
-        description='Sign a Tusted Application for OP-TEE.',
+        description='Sign and encrypt (optional) a Tusted Application for' +
+        ' OP-TEE.',
         usage='\n   %(prog)s command [ arguments ]\n\n'
 
         '   command:\n' +
-        '     sign        Generate signed loadable TA image file.\n' +
-        '                 Takes arguments --uuid, --ta-version, --in, --out' +
-        ' and --key.\n' +
+        '     sign-enc    Generate signed and optionally encrypted loadable' +
+        ' TA image file.\n' +
+        '                 Takes arguments --uuid, --ta-version, --in, --out,' +
+        ' --key\n' +
+        '                 and --enc-key (optional).\n' +
         '     digest      Generate loadable TA binary image digest' +
         ' for offline\n' +
-        '                 signing. Takes arguments  --uuid, --ta-version,' +
-        ' --in, --key and --dig.\n' +
-        '     stitch      Generate loadable signed TA binary image' +
-        ' file from\n' +
+        '                 signing. Takes arguments --uuid, --ta-version,' +
+        ' --in, --key,\n'
+        '                 --enc-key (optional) and --dig.\n' +
+        '     stitch      Generate loadable signed and encrypted TA binary' +
+        ' image file from\n' +
         '                 TA raw image and its signature. Takes' +
         ' arguments\n' +
-        '                 --uuid, --in, --key, --out, and --sig.\n\n' +
+        '                 --uuid, --in, --key, --enc-key (optional), --out,' +
+        ' and --sig.\n\n' +
         '   %(prog)s --help  show available commands and arguments\n\n',
         formatter_class=RawDescriptionHelpFormatter,
         epilog=textwrap.dedent('''\
-            If no command is given, the script will default to "sign".
+            If no command is given, the script will default to "sign-enc".
 
             command aliases:
               The command \'digest\' can be aliased by ''' + dat + '''
@@ -63,12 +68,14 @@
 
     parser.add_argument(
         'command', choices=command_choices, nargs='?',
-        default='sign',
+        default='sign-enc',
         help='Command, one of [' + ', '.join(command_base) + ']')
     parser.add_argument('--uuid', required=True,
                         type=uuid_parse, help='String UUID of the TA')
     parser.add_argument('--key', required=True,
-                        help='Name of key file (PEM format)')
+                        help='Name of signing key file (PEM format)')
+    parser.add_argument('--enc-key', required=False,
+                        help='Encryption key string')
     parser.add_argument(
         '--ta-version', required=False, type=int_parse, default=0,
         help='TA version stored as a 32-bit unsigned integer and used for\n' +
@@ -156,7 +163,10 @@
     hdr_version = args.ta_version  # struct shdr_bootstrap_ta::ta_version
 
     magic = 0x4f545348   # SHDR_MAGIC
-    img_type = 1         # SHDR_BOOTSTRAP_TA
+    if args.enc_key:
+        img_type = 2         # SHDR_ENCRYPTED_TA
+    else:
+        img_type = 1         # SHDR_BOOTSTRAP_TA
     algo = 0x70004830    # TEE_ALG_RSASSA_PKCS1_V1_5_SHA256
 
     shdr = struct.pack('<IIIIHH',
@@ -164,9 +174,23 @@
     shdr_uuid = args.uuid.bytes
     shdr_version = struct.pack('<I', hdr_version)
 
+    if args.enc_key:
+        from Cryptodome.Cipher import AES
+        cipher = AES.new(bytearray.fromhex(args.enc_key), AES.MODE_GCM)
+        ciphertext, tag = cipher.encrypt_and_digest(img)
+
+        enc_algo = 0x40000810  # TEE_ALG_AES_GCM
+        flags = 0              # SHDR_ENC_KEY_DEV_SPECIFIC
+        ehdr = struct.pack('<IIHH',
+                           enc_algo, flags, len(cipher.nonce), len(tag))
+
     h.update(shdr)
     h.update(shdr_uuid)
     h.update(shdr_version)
+    if args.enc_key:
+        h.update(ehdr)
+        h.update(cipher.nonce)
+        h.update(tag)
     h.update(img)
     img_digest = h.digest()
 
@@ -177,9 +201,15 @@
             f.write(sig)
             f.write(shdr_uuid)
             f.write(shdr_version)
-            f.write(img)
+            if args.enc_key:
+                f.write(ehdr)
+                f.write(cipher.nonce)
+                f.write(tag)
+                f.write(ciphertext)
+            else:
+                f.write(img)
 
-    def sign_ta():
+    def sign_encrypt_ta():
         if not key.has_private():
             logger.error('Provided key cannot be used for signing, ' +
                          'please use offline-signing mode.')
@@ -222,12 +252,12 @@
 
     # dispatch command
     {
-        'sign': sign_ta,
+        'sign-enc': sign_encrypt_ta,
         'digest': generate_digest,
         'generate-digest': generate_digest,
         'stitch': stitch_ta,
         'stitch-ta': stitch_ta
-    }.get(args.command, 'sign_ta')()
+    }.get(args.command, 'sign_encrypt_ta')()
 
 
 if __name__ == "__main__":