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>
This commit is contained in:
Jukka Laitinen 2020-10-21 11:52:44 +03:00 committed by Lorenz Meier
parent 5c9e7c2581
commit 0f80296340
2 changed files with 81 additions and 71 deletions

View File

@ -2,7 +2,6 @@
import nacl.encoding
import nacl.signing
import nacl.encoding
import nacl.hash
import struct
import binascii
@ -12,81 +11,54 @@ import argparse
from pathlib import Path
import sys
# 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):
def make_public_key_h_file(signing_key,key_name):
"""
This file generate the public key header file
to be included into the bootloader build.
"""
public_key_c=''
public_key_c="const uint8_t public_key={\n"
public_key_c='\n'
for i,c in enumerate(signing_key.verify_key.encode(encoder=nacl.encoding.RawEncoder)):
public_key_c+= hex(c)
public_key_c+= ', '
if((i+1)%8==0):
public_key_c+= '\n'
public_key_c+= "};"
with open("public_key.h" ,mode='w') as f:
with open(key_name+'.pub' ,mode='w') as f:
f.write("//Public key to verify signed binaries")
f.write(public_key_c)
def make_key_file(signing_key):
def make_key_file(signing_key, key_name):
"""
Writes the key.json file.
Attention do not override your existing key files.
Do not publish your private key!!
"""
key_file = Path("keys.json")
key_file = Path(key_name+'.json')
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("Script aborted!")
sys.exit(-1)
sys.exit(1)
keys={}
keys["date"] = time.asctime()
keys["public"] = (signing_key.verify_key.encode(encoder=nacl.encoding.HexEncoder)).decode()
keys["private"] = binascii.hexlify(signing_key._seed).decode()
print (keys)
with open("keys.json", "w") as write_file:
#print (keys)
with open(key_name+'.json', "w") as write_file:
json.dump(keys, write_file)
return keys
def ed25519_sign(private_key, signee_bin):
"""
This functino does the magic. It tkaes the pricate key and the binary file
and generates the signed binary. it adds the meta data to the beginning of the file
Ouput: "SignedBin.bin"
This function creates the signature. It takes the private key and the binary file
and returns the tuple (signature, public key)
"""
# 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)
# Sign a message with the signing key
signed = signing_key.sign(signee_bin[META_DATA_LEN:],encoder=nacl.encoding.RawEncoder)
meta_data['app_hash'] = signed.signature
signed = signing_key.sign(signee_bin,encoder=nacl.encoding.RawEncoder)
# Obtain the verify key for a given signing 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
verify_key_hex = verify_key.encode(encoder=nacl.encoding.HexEncoder)
print(len(signed.signature), "signature:" ,binascii.hexlify(signed.signature))
print('Public key: ', verify_key_hex)
return signed.signature, 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.
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:
signee_bin = f.read()
if key_file_path == None:
print('generating new key')
generate_key()
key_file_path = 'keys.json'
# Align to 4 bytes. Signature always starts at
# 4 byte aligned address, but the signee size
# might not be aligned
signee_bin += bytearray(b'\xff')*(4-len(signee_bin)%4)
with open(key_file_path,mode='r') as f:
keys = json.load(f)
print(keys)
try:
with open(key_file_path,mode='r') as f:
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,
one file is made to be include in the bootloader build
so its the "public_key.h" containg the verfication key.
The other file key.json, containt both private and public key.
Generate two files:
"key_file.pub" containing the public key in C-format to be included in the bootloader build
"key_file.json, containt both private and public key.
Do not leak or loose the key file. This is mandatory for signing
all future binaries you want to deploy!
"""
# Generate a new random signing key
@ -146,18 +121,52 @@ def generate_key():
private_key_hex=binascii.hexlify(signing_key._seed)
print("private key :",private_key_hex)
make_key_file(signing_key)
make_public_key_h_file(signing_key)
keys = make_key_file(signing_key,key_file)
make_public_key_h_file(signing_key,key_file)
return keys
if(__name__ == "__main__"):
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""",
epilog="Output: SignedBin.bin and a key.json file")
parser.add_argument("signee", help=".bin file to add signature")
parser.add_argument("--key", help="key.json file", default=None)
parser.add_argument("signee", help=".bin file to add signature", nargs='?', 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()
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 Normal file
View File

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