Browse Source

Modify cryptotools.py

- Append signature in the end of the signee
- Add parameters for output file and rd certificate
- Add a default test key

Signed-off-by: Jukka Laitinen <jukkax@ssrc.tii.ae>
release/1.12
Jukka Laitinen 4 years ago committed by Lorenz Meier
parent
commit
0f80296340
  1. 151
      Tools/cryptotools.py
  2. 1
      Tools/test_keys.json

151
Tools/cryptotools.py

@ -2,7 +2,6 @@
import nacl.encoding import nacl.encoding
import nacl.signing import nacl.signing
import nacl.encoding
import nacl.hash import nacl.hash
import struct import struct
import binascii import binascii
@ -12,81 +11,54 @@ import argparse
from pathlib import Path from pathlib import Path
import sys import sys
def make_public_key_h_file(signing_key,key_name):
# Definitins for the meta data heder of the signed application
meta_data ={'appp_size':0,
'app_hash':0,
}
META_DATA_STRUCT = "<I64s60x" # [4 length][64 sha512][60 padding]
META_DATA_LEN = 128
def pack_meta_data(meta_data):
"""
Packs the given meta data into a binary struct, to be written
on top of the binary application file.
"""
return struct.pack(META_DATA_STRUCT, meta_data['appp_size'],
meta_data['app_hash'])
def make_public_key_h_file(signing_key):
""" """
This file generate the public key header file This file generate the public key header file
to be included into the bootloader build. to be included into the bootloader build.
""" """
public_key_c='' public_key_c='\n'
public_key_c="const uint8_t public_key={\n"
for i,c in enumerate(signing_key.verify_key.encode(encoder=nacl.encoding.RawEncoder)): for i,c in enumerate(signing_key.verify_key.encode(encoder=nacl.encoding.RawEncoder)):
public_key_c+= hex(c) public_key_c+= hex(c)
public_key_c+= ', ' public_key_c+= ', '
if((i+1)%8==0): if((i+1)%8==0):
public_key_c+= '\n' public_key_c+= '\n'
public_key_c+= "};" with open(key_name+'.pub' ,mode='w') as f:
with open("public_key.h" ,mode='w') as f:
f.write("//Public key to verify signed binaries") f.write("//Public key to verify signed binaries")
f.write(public_key_c) f.write(public_key_c)
def make_key_file(signing_key): def make_key_file(signing_key, key_name):
""" """
Writes the key.json file. Writes the key.json file.
Attention do not override your existing key files. Attention do not override your existing key files.
Do not publish your private key!! Do not publish your private key!!
""" """
key_file = Path("keys.json") key_file = Path(key_name+'.json')
if key_file.is_file(): if key_file.is_file():
print("ATTENTION: key.json already exists, are you sure you wnat to overwrite it?") print("ATTENTION: key.json already exists, are you sure you want to overwrite it?")
print("Remove file and run script again.") print("Remove file and run script again.")
print("Script aborted!") print("Script aborted!")
sys.exit(-1) sys.exit(1)
keys={} keys={}
keys["date"] = time.asctime() keys["date"] = time.asctime()
keys["public"] = (signing_key.verify_key.encode(encoder=nacl.encoding.HexEncoder)).decode() keys["public"] = (signing_key.verify_key.encode(encoder=nacl.encoding.HexEncoder)).decode()
keys["private"] = binascii.hexlify(signing_key._seed).decode() keys["private"] = binascii.hexlify(signing_key._seed).decode()
print (keys) #print (keys)
with open("keys.json", "w") as write_file: with open(key_name+'.json', "w") as write_file:
json.dump(keys, write_file) json.dump(keys, write_file)
return keys
def ed25519_sign(private_key, signee_bin): def ed25519_sign(private_key, signee_bin):
""" """
This functino does the magic. It tkaes the pricate key and the binary file This function creates the signature. It takes the private key and the binary file
and generates the signed binary. it adds the meta data to the beginning of the file and returns the tuple (signature, public key)
Ouput: "SignedBin.bin"
""" """
# metadata is already included into the source bin,
# but filled with dummy data
meta_data['appp_size'] = len(signee_bin) - META_DATA_LEN
signing_key = nacl.signing.SigningKey(private_key, encoder=nacl.encoding.HexEncoder) signing_key = nacl.signing.SigningKey(private_key, encoder=nacl.encoding.HexEncoder)
# Sign a message with the signing key # Sign a message with the signing key
signed = signing_key.sign(signee_bin[META_DATA_LEN:],encoder=nacl.encoding.RawEncoder) signed = signing_key.sign(signee_bin,encoder=nacl.encoding.RawEncoder)
meta_data['app_hash'] = signed.signature
# Obtain the verify key for a given signing key # Obtain the verify key for a given signing key
verify_key = signing_key.verify_key verify_key = signing_key.verify_key
@ -94,17 +66,10 @@ def ed25519_sign(private_key, signee_bin):
# Serialize the verify key to send it to a third party # Serialize the verify key to send it to a third party
verify_key_hex = verify_key.encode(encoder=nacl.encoding.HexEncoder) verify_key_hex = verify_key.encode(encoder=nacl.encoding.HexEncoder)
print(len(signed.signature), "signature:" ,binascii.hexlify(signed.signature)) return signed.signature, verify_key_hex
print('Public key: ', verify_key_hex)
with open("SignedBin.bin" ,mode='wb') as f:
data = pack_meta_data(meta_data)
print("meta data header: ", binascii.hexlify(data),len(data))
f.write(data)
f.write(signee_bin[META_DATA_LEN:])
def sign(bin_file_path, key_file_path=None): def sign(bin_file_path, key_file_path=None, generated_key_file=None):
""" """
reads the binary file and the key file. reads the binary file and the key file.
If the key file does not exist, it generates a If the key file does not exist, it generates a
@ -113,28 +78,38 @@ def sign(bin_file_path, key_file_path=None):
with open(bin_file_path,mode='rb') as f: with open(bin_file_path,mode='rb') as f:
signee_bin = f.read() signee_bin = f.read()
if key_file_path == None: # Align to 4 bytes. Signature always starts at
print('generating new key') # 4 byte aligned address, but the signee size
generate_key() # might not be aligned
key_file_path = 'keys.json' signee_bin += bytearray(b'\xff')*(4-len(signee_bin)%4)
with open(key_file_path,mode='r') as f: try:
keys = json.load(f) with open(key_file_path,mode='r') as f:
print(keys) keys = json.load(f)
#print(keys)
except:
print('ERROR: Key file',key_file_path,'not found')
sys.exit(1)
ed25519_sign(keys["private"], signee_bin) signature, public_key = ed25519_sign(keys["private"], signee_bin)
# Do a sanity check. This type of signature is always 64 bytes long
assert len(signature) == 64
# Print out the signing information
print("Binary \"%s\" signed."%bin_file_path)
print("Signature:",binascii.hexlify(signature))
print("Public key:",binascii.hexlify(public_key))
def generate_key(): return signee_bin + signature, public_key
def generate_key(key_file):
""" """
Call it and it generate two files, Generate two files:
one file is made to be include in the bootloader build "key_file.pub" containing the public key in C-format to be included in the bootloader build
so its the "public_key.h" containg the verfication key. "key_file.json, containt both private and public key.
The other file key.json, containt both private and public key.
Do not leak or loose the key file. This is mandatory for signing Do not leak or loose the key file. This is mandatory for signing
all future binaries you want to deploy! all future binaries you want to deploy!
""" """
# Generate a new random signing key # Generate a new random signing key
@ -146,18 +121,52 @@ def generate_key():
private_key_hex=binascii.hexlify(signing_key._seed) private_key_hex=binascii.hexlify(signing_key._seed)
print("private key :",private_key_hex) print("private key :",private_key_hex)
make_key_file(signing_key) keys = make_key_file(signing_key,key_file)
make_public_key_h_file(signing_key) make_public_key_h_file(signing_key,key_file)
return keys
if(__name__ == "__main__"): if(__name__ == "__main__"):
parser = argparse.ArgumentParser(description="""CLI tool to calculate and add signature to px4. bin files\n parser = argparse.ArgumentParser(description="""CLI tool to calculate and add signature to px4. bin files\n
if given it takes an existing key file, else it generate new keys""", if given it takes an existing key file, else it generate new keys""",
epilog="Output: SignedBin.bin and a key.json file") epilog="Output: SignedBin.bin and a key.json file")
parser.add_argument("signee", help=".bin file to add signature") parser.add_argument("signee", help=".bin file to add signature", nargs='?', default=None)
parser.add_argument("--key", help="key.json file", default=None) parser.add_argument("signed", help="signed output .bin", nargs='?', default=None)
parser.add_argument("--key", help="key.json file", default="Tools/test_keys.json")
parser.add_argument("--rdct", help="binary R&D certificate file", default=None)
parser.add_argument("--genkey", help="new generated key", default=None)
args = parser.parse_args() args = parser.parse_args()
sign(args.signee, args.key) # Only generate a key pair, don't sign
if args.genkey:
# Only create a key file, don't sign
generate_key(args.genkey)
print('New key file generated:',args.genkey)
sys.exit(0);
# Check that both signee and signed exist
if not args.signee or not args.signed:
print("ERROR: Must either provide file names for both signee and signed")
print(" or --genkey [key] to generate a new key pair")
sys.exit(1)
# Issue a warning when signing with testing key
if args.key=='Tools/test_keys.json':
print("WARNING: Signing with PX4 test key")
# Sign the binary
signed, public_key = sign(args.signee, args.key, args.genkey)
with open(args.signed, mode='wb') as fs:
# Write signed binary
fs.write(signed)
# Append rdcert if given
try:
with open(args.rdct ,mode='rb') as f:
with open(args.signed, mode='ab') as fs:
fs.write(f.read())
except:
pass

1
Tools/test_keys.json

@ -0,0 +1 @@
{"date": "Tue Nov 3 13:02:09 2020", "public": "4db0c20105552a3cd7fbaf5cba7ab0811b3663db28525edb1436f2578d02b7fd", "private": "734d597e7d8ab0a1d0d64b95083aa6bee34b46b9e6e76dac1e363af114f12d15"}
Loading…
Cancel
Save