Boot: Migrate from pycrypto to cryptography

This commit migrates imgtool -- specifically
imgtool_lib/keys.py -- from Pycrypto, which hasn't been
updated in many years, to the actively maintained
Cryptography module.

Aside from active development, generally meaning a
lower risk of unaddressed security issues, this migration
has a number of secondary benefits:

- It moves imgtool closer to the upstream mcuboot,
  which recently removed Pycrypto usage as well.
- It allows the possibility of migrating to a more
  secure key format, due to support for PKCS8, etc.
- It allows TF-M binaries to be signed with the
  Zephyr SDK (tested on 0.10.1) with no additional
  dependencies.

Author: Kevin Townsend <kevin.townsend@linaro.org>
Signed-off-by: Kevin Townsend <kevin.townsend@linaro.org>
Change-Id: I4f520fb3491831d9f83fe6b163b230b591bf4a8a
diff --git a/bl2/ext/mcuboot/scripts/imgtool_lib/keys.py b/bl2/ext/mcuboot/scripts/imgtool_lib/keys.py
index b652d90..7ee9671 100644
--- a/bl2/ext/mcuboot/scripts/imgtool_lib/keys.py
+++ b/bl2/ext/mcuboot/scripts/imgtool_lib/keys.py
@@ -1,4 +1,4 @@
-# Copyright 2017 Linaro Limited
+# Copyright (c) 2017,2019 Linaro Limited.
 # Copyright (c) 2017-2019, Arm Limited.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,9 +18,12 @@
 """
 
 from __future__ import print_function
-from Crypto.Hash import SHA256
-from Crypto.PublicKey import RSA
-from Crypto.Signature import PKCS1_v1_5, PKCS1_PSS
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.hashes import SHA256
+from cryptography.hazmat.primitives.asymmetric import rsa
+from cryptography.hazmat.primitives.asymmetric.padding import PSS, PKCS1v15
+from cryptography.hazmat.primitives.asymmetric.padding import MGF1
 import hashlib
 from pyasn1.type import namedtype, univ
 from pyasn1.codec.der.encoder import encode
@@ -28,6 +31,9 @@
 # Sizes that bootutil will recognize
 RSA_KEY_SIZES = [2048, 3072]
 
+# Public exponent
+PUBLIC_EXPONENT = 65537
+
 # By default, we use RSA-PSS (PKCS 2.1).  That can be overridden on
 # the command line to support the older (less secure) PKCS1.5
 sign_rsa_pss = True
@@ -37,11 +43,6 @@
 class RSAUsageError(Exception):
     pass
 
-class RSAPublicKey(univ.Sequence):
-    componentType = namedtype.NamedTypes(
-            namedtype.NamedType('modulus', univ.Integer()),
-            namedtype.NamedType('publicExponent', univ.Integer()))
-
 class RSAutil():
     def __init__(self, key, public_key_format='hash'):
         """Construct an RSA key with the given key data"""
@@ -49,7 +50,7 @@
         self.public_key_format = public_key_format
 
     def key_size(self):
-        return self.key.n.bit_length()
+        return self.key.key_size
 
     def get_public_key_format(self):
         return self.public_key_format
@@ -59,17 +60,22 @@
         if key_size not in RSA_KEY_SIZES:
             raise RSAUsageError("Key size {} is not supported by MCUboot"
                                 .format(key_size))
-        return RSAutil(RSA.generate(key_size))
+        return RSAutil(rsa.generate_private_key(
+                public_exponent=PUBLIC_EXPONENT,
+                key_size=key_size,
+                backend=default_backend()))
 
     def export_private(self, path):
         with open(path, 'wb') as f:
-            f.write(self.key.exportKey('PEM'))
+            f.write(self.key.private_bytes(
+                    encoding=serialization.Encoding.PEM,
+                    format=serialization.PrivateFormat.TraditionalOpenSSL,
+                    encryption_algorithm=serialization.NoEncryption()))
 
     def get_public_bytes(self):
-        node = RSAPublicKey()
-        node['modulus'] = self.key.n
-        node['publicExponent'] = self.key.e
-        return bytearray(encode(node))
+        return self.key.public_key().public_bytes(
+                encoding=serialization.Encoding.DER,
+                format=serialization.PublicFormat.PKCS1)
 
     def emit_c(self):
         print(AUTOGEN_MESSAGE)
@@ -98,13 +104,21 @@
         return "RSA2048" if self.key_size() == 2048 else "RSA3072"
 
     def sign(self, payload):
-        converted_payload = bytes(payload)
-        sha = SHA256.new(converted_payload)
         if sign_rsa_pss:
-            signer = PKCS1_PSS.new(self.key)
+            signature = self.key.sign(
+                data=payload,
+                padding=PSS(
+                    mgf=MGF1(SHA256()),
+                    salt_length=32
+                ),
+                algorithm=SHA256()
+            )
         else:
-            signer = PKCS1_v1_5.new(self.key)
-        signature = signer.sign(sha)
+            signature = self.key.sign(
+                data=payload,
+                padding=PKCS1v15(),
+                algorithm=SHA256()
+            )
         assert len(signature) == self.sig_len()
         return signature
 
@@ -112,7 +126,11 @@
     with open(path, 'rb') as f:
         pem = f.read()
     try:
-        key = RSA.importKey(pem)
+        key = serialization.load_pem_private_key(
+            pem,
+            password=None,
+            backend=default_backend()
+        )
         return RSAutil(key, public_key_format)
     except ValueError:
         raise Exception("Unsupported RSA key file")