blob: 17b8b388d21855ce989cae57752abbf55c32bb03 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright (c) 2022 Project CHIP Authors
# All rights reserved.
#
# 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 subprocess
import base64
import logging
import os
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_der_private_key
# Keys used to store KLV
KLV_KEYS = {
"Verifier": 1,
"Salt": 2,
"IC": 3,
"DacPKey": 4,
"DacCert": 5,
"PaiCert": 6,
"Disc": 7
}
def type_int(n):
return int(n, 0)
def read_der(der_file):
with open(der_file, "rb") as file:
return file.read()
def generate_spake2_params(spake2_path, passcode, it, salt):
cmd = [
spake2_path, "gen-verifier", "--iteration-count",
str(it), "--salt",
str(salt), "--pin-code",
str(passcode), "--out", "-"
]
out = subprocess.run(cmd, check=True, stdout=subprocess.PIPE).stdout
out = out.decode("utf-8").splitlines()
return dict(zip(out[0].split(','), out[1].split(',')))
def get_private_key_der(der_file, password):
data = read_der(der_file)
keys = load_der_private_key(data, password, backend=default_backend())
private_key = keys.private_numbers().private_value.to_bytes(
32, byteorder='big')
return private_key
def generate_klv(spake2_params, args):
klv = []
spake2p_verifier = str.encode(spake2_params["Verifier"])
spake2p_salt = str.encode(spake2_params["Salt"])
spake2p_it = int(spake2_params["Iteration Count"]).to_bytes(4, "little")
dac_private_key = get_private_key_der(args.dac_key, args.dac_key_password)
dac_cert = read_der(args.dac_cert)
pai_cert = read_der(args.pai_cert)
discriminator = int(args.discrimnator).to_bytes(4, "little")
klv.append((KLV_KEYS["Verifier"], len(spake2p_verifier), spake2p_verifier))
print("Verifier length: ", len(spake2p_verifier))
klv.append((KLV_KEYS["Salt"], len(spake2p_salt), spake2p_salt))
print("SALT length: ", len(spake2p_salt))
klv.append((KLV_KEYS["IC"], 4, spake2p_it))
klv.append((KLV_KEYS["DacPKey"], len(dac_private_key), dac_private_key))
print("DAC Private Key length: ", len(dac_private_key))
klv.append((KLV_KEYS["DacCert"], len(dac_cert), dac_cert))
print("DAC Certificate length: ", len(dac_cert))
klv.append((KLV_KEYS["PaiCert"], len(pai_cert), pai_cert))
print("PAI Certificate length: ", len(pai_cert))
klv.append((KLV_KEYS["Disc"], 4, discriminator))
return klv
def klv_to_bin(klv, out):
with open(out, "wb") as file:
for entry in klv:
file.write(entry[0].to_bytes(1, "little"))
file.write(entry[1].to_bytes(2, "little"))
file.write(entry[2])
size = file.seek(0, os.SEEK_END)
print("Size of generated binary is:", size, "bytes")
def main():
parser = argparse.ArgumentParser(description="NXP Factory Data Generator")
parser.add_argument("-i",
"--it",
required=True,
type=type_int,
help="[int | hex] Spake2 Iteration Counter")
parser.add_argument("-s",
"--salt",
type=str,
required=True,
help="[ascii] Spake2 Salt")
parser.add_argument("-p",
"--passcode",
type=type_int,
required=True,
help="[int | hex] PASE session passcode")
parser.add_argument("-d",
"--discrimnator",
type=type_int,
required=True,
help="[int | hex] BLE Pairing discrimantor")
parser.add_argument("--dac_cert",
type=str,
required=True,
help="[path] Path to DAC certificate in DER format")
parser.add_argument("--dac_key",
type=str,
required=True,
help="[path] Path to DAC key in DER format")
parser.add_argument("--dac_key_password",
type=str,
required=False,
help="[path] Password to decode DAC Key if available")
parser.add_argument("--pai_cert",
type=str,
required=True,
help="[path] Path to PAI certificate in DER format")
parser.add_argument("--spake2p_path",
type=str,
required=True,
help="[path] Path to Spake2P")
parser.add_argument("--out",
type=str,
required=True,
help="[path] Path to output binary")
args = parser.parse_args()
if args.dac_key_password is None:
logging.warning(
"DAC Key password not provided. It means DAC Key is not protected."
)
spake2_params = generate_spake2_params(args.spake2p_path, args.passcode,
args.it, args.salt)
klv = generate_klv(spake2_params, args)
klv_to_bin(klv, args.out)
if __name__ == "__main__":
main()
# Example usage:
# python3 generate_nxp_chip_factory_bin.py -i 10000 -s ABCDEFGHIJKLMNOPQRS -p 14014 -d 1000 --dac_cert /home/john/nxp/connectedhomeip_nxp/Chip-DAC-NXP-Cert.der --dac_key /home/john/nxp/connectedhomeip_nxp/Chip-DAC-NXP-Key.der --pai_cert /home/john/nxp/connectedhomeip_nxp/Chip-PAI-NXP-Cert.der --spake2p_path /home/john/nxp/connectedhomeip_nxp/out/host/spake2p --out out.bin