#!/usr/bin/env python # Copyright 2020 Espressif Systems (Shanghai) Co., Ltd. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import argparse import os import sys import hashlib import hmac import struct from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.utils import int_to_bytes try: import esptool import espefuse except ImportError: idf_path = os.getenv("IDF_PATH") if not idf_path or not os.path.exists(idf_path): raise Exception("IDF_PATH not found") sys.path.insert(0, os.path.join(idf_path, "components", "esptool_py", "esptool")) import esptool import espefuse try: import nvs_partition_gen as nvs_gen except ImportError: idf_path = os.getenv("IDF_PATH") if not idf_path or not os.path.exists(idf_path): raise Exception("IDF_PATH not found") sys.path.insert(0, os.path.join(idf_path, "components", "nvs_flash", "nvs_partition_generator")) import nvs_partition_gen as nvs_gen esp_ds_data_dir = 'esp_ds_data' # hmac_key_file is generated when HMAC_KEY is calculated, it is used when burning HMAC_KEY to efuse hmac_key_file = esp_ds_data_dir + '/hmac_key.bin' # csv and bin filenames are default filenames for nvs partition files created with this script csv_filename = esp_ds_data_dir + '/pre_prov.csv' bin_filename = esp_ds_data_dir + '/pre_prov.bin' def load_privatekey(key_file_path, password=None): key_file = open(key_file_path, 'rb') key = key_file.read() key_file.close() return serialization.load_pem_private_key(key, password=password, backend=default_backend()) def number_as_bytes(number, pad_bits=None): """ Given a number, format as a little endian array of bytes """ result = int_to_bytes(number)[::-1] while pad_bits is not None and len(result) < (pad_bits // 8): result += b'\x00' return result def calculate_ds_parameters(privkey, priv_key_pass): private_key = load_privatekey(privkey, priv_key_pass) if not isinstance(private_key, rsa.RSAPrivateKey): print("Only RSA private keys are supported") sys.exit(-1) priv_numbers = private_key.private_numbers() pub_numbers = private_key.public_key().public_numbers() Y = priv_numbers.d M = pub_numbers.n key_size = private_key.key_size supported_key_size = [1024, 2048, 3072, 4096] if key_size not in supported_key_size: print("Key size not supported, supported sizes are" + str(supported_key_size)) sys.exit(-1) hmac_key = os.urandom(32) with open(hmac_key_file, 'wb') as key_file: key_file.write(hmac_key) iv = os.urandom(16) rr = 1 << (key_size * 2) rinv = rr % pub_numbers.n mprime = - rsa._modinv(M, 1 << 32) mprime &= 0xFFFFFFFF length = key_size // 32 - 1 aes_key = hmac.HMAC(hmac_key, b"\xFF" * 32, hashlib.sha256).digest() md_in = number_as_bytes(Y, 4096) + \ number_as_bytes(M, 4096) + \ number_as_bytes(rinv, 4096) + \ struct.pack("