139 lines
4.4 KiB
Python
Executable File
139 lines
4.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# Copyright Amlogic
|
|
|
|
import argparse
|
|
import logging
|
|
import os
|
|
import sys
|
|
import random
|
|
|
|
from Cryptodome.PublicKey import ECC
|
|
from Cryptodome.Cipher import AES
|
|
|
|
def parse_args():
|
|
parser = argparse.ArgumentParser(
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
description='\n\
|
|
Encrypts OTP secret for programming.\n\
|
|
encrypt_secret.py -k vendor_priv.pem -d device_pub.pem -i dgpk1.bin -o edgpk1.bin -x edpgk1.txt')
|
|
|
|
parser.add_argument('-k', '--private-key',
|
|
metavar='vendor_priv.pem',
|
|
required=True,
|
|
help='Input vendor private key file',
|
|
)
|
|
parser.add_argument('-d', '--device-public-key',
|
|
metavar='device_public_key',
|
|
required=True,
|
|
help='Input device public key in hex string',
|
|
)
|
|
parser.add_argument('-i', '--input',
|
|
metavar='input',
|
|
required=True,
|
|
help='Input secret file',
|
|
)
|
|
parser.add_argument('-o', '--output',
|
|
metavar='output',
|
|
required=False,
|
|
help='Output encrypted secret file',
|
|
)
|
|
parser.add_argument('-x', '--output-hex',
|
|
metavar='output',
|
|
required=False,
|
|
help='Output encrypted secret file in hex',
|
|
)
|
|
|
|
parser.add_argument('-v', '--verbose',
|
|
action='store_true',
|
|
help='Enable verbose log')
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.verbose:
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
else:
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
logging.info('Running {}'.format(sys.argv))
|
|
|
|
return args
|
|
|
|
def key_import(key):
|
|
""" Check keys are valid, on the curve.
|
|
returns status, EccKey
|
|
"""
|
|
if not key:
|
|
logging.error('key arg missing')
|
|
return False, None
|
|
if len(key) < 128:
|
|
logging.error('key arg should be 64 bytes in hex')
|
|
logging.error(' {}'.format(len(key)))
|
|
return False, None
|
|
kb = bytes.fromhex(key)
|
|
if len(kb) != 64:
|
|
logging.error('key arg should be 64 bytes in hex')
|
|
logging.error(' {}'.format(len(kb)))
|
|
return False, None
|
|
xb = kb[:32]
|
|
yb = kb[32:]
|
|
x = int.from_bytes(xb, byteorder='big', signed=False)
|
|
y = int.from_bytes(yb, byteorder='big', signed=False)
|
|
ecc_key = ECC.construct(curve='p256', point_x=x, point_y=y)
|
|
|
|
return True, ecc_key
|
|
|
|
if __name__ == '__main__':
|
|
args = parse_args()
|
|
|
|
if not os.path.exists(args.private_key):
|
|
logging.error('Private key {} does not exist'.format(args.private_key))
|
|
sys.exit(1)
|
|
device_key_r, device_key = key_import(args.device_public_key)
|
|
if not device_key_r:
|
|
logging.error('Device public key is invalid')
|
|
sys.exit(1)
|
|
if not os.path.exists(args.input):
|
|
logging.error('Input {} does not exist'.format(args.input))
|
|
sys.exit(1)
|
|
if not args.output and not args.output_hex:
|
|
logging.error('Missing output arg')
|
|
sys.exit(1)
|
|
if os.path.exists(args.output):
|
|
logging.error('Output {} already exists'.format(args.output))
|
|
sys.exit(1)
|
|
if os.path.exists(args.output_hex):
|
|
logging.error('Output {} already exists'.format(args.output_hex))
|
|
sys.exit(1)
|
|
|
|
# Load keys
|
|
with open(args.private_key, 'rt') as f:
|
|
vendor_key = ECC.import_key(f.read())
|
|
|
|
# Derive shared secret with ECDH
|
|
pointZ = device_key.pointQ * vendor_key.d
|
|
shared_secret = int.to_bytes(int(pointZ.x), length=32, byteorder='big')
|
|
|
|
# AES-GCM encrypt
|
|
# randbytes only in 3.9+
|
|
iv = bytes([random.randint(0, 255) for _ in range(12)])
|
|
|
|
cipher = AES.new(shared_secret, AES.MODE_GCM, nonce=iv, mac_len=16)
|
|
with open(args.input, 'rb') as f:
|
|
pt = f.read()
|
|
ct, tag = cipher.encrypt_and_digest(pt)
|
|
|
|
with open(args.output, 'wb') as f:
|
|
f.write(ct)
|
|
f.write(iv)
|
|
f.write(tag)
|
|
with open(args.output_hex, 'wt') as f:
|
|
f.write(ct.hex())
|
|
f.write(iv.hex())
|
|
f.write(tag.hex())
|
|
f.write("\n")
|
|
|
|
#logging.debug(f'out(enc_efuseinfo)= {ct.hex()}{iv.hex()}{tag.hex()}')
|
|
|
|
sys.exit(0)
|