Files
2026-08-24 10:03:24 +08:00

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)