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")