Files
BPI-CM9-Linux/aml-comp/vendor/amlogic/provision/tool/attest_cut_key.py
T
2026-08-24 10:03:24 +08:00

311 lines
8.0 KiB
Python
Executable File

#!/usr/bin/env python3
#
# Copyright (C) 2018 Amlogic, Inc. All rights reserved.
#
# All information contained herein is Amlogic confidential.
#
# This software is provided to you pursuant to Software License
# Agreement (SLA) with Amlogic Inc ("Amlogic"). This software may be
# used only in accordance with the terms of this agreement.
#
# Redistribution and use in source and binary forms, with or without
# modification is strictly prohibited without prior written permission
# from Amlogic.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
def get_args():
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('--in', required=True, dest='inf', help='Name of input file')
parser.add_argument('--out', type=str, default='null', help='key output dir')
parser.add_argument('--skip', type=int, default=1, help='key cutting interval')
parser.add_argument('--start', type=int, default=0, help='first key index')
parser.add_argument('--count', type=int, default=-1, help='to cut key count')
return parser.parse_args()
def file_write(head, start_p, length, fd, name):
import gc
ret = 0
size_h = len(head)
fd1 = open(name, 'w+')
fd1.write(head)
fd.seek(start_p, 0)
buf = fd.read(length).decode()
fd1.write(buf)
del buf
gc.collect()
fd1.close()
return ret
def file_get_pos(start, end, fd, cursor):
import re
import gc
ret = 0
fd.seek(cursor, 0)
# 16KB buf > a attestation key size
keybuf = fd.read(0x4000).decode()
start_m = re.search(start, keybuf)
if start_m == None:
ret = -1
print ("not found the start position of key\n")
return ret, 0, 0
end_m = re.search(end, keybuf)
if end_m == None:
ret = -1
return ret, 0, 0
start_p = cursor + start_m.end()
end_p = cursor + end_m.end()
del keybuf
gc.collect()
return ret, start_p, end_p
def key_pack(dirname, name_prefix):
import sys
import os
import struct
keyname = dirname + '/' + name_prefix + ".keybox"
keyoffset = [0] * 8
keysize = [0] * 8
keynum = 0
# version,keynum,keyoffset[],keysize[] = 18int
keyheadlen = struct.calcsize('<18I')
keyoffset[0] = keyheadlen
file_name = ("AttestKey.ec", "AttestCert.ec0", "AttestCert.ec1", "AttestCert.ec2", \
"AttestKey.rsa", "AttestCert.rsa0", "AttestCert.rsa1", "AttestCert.rsa2", )
for i in range(0, 8):
if os.path.exists(dirname + "/" + file_name[i]):
fd = open(dirname + "/" + file_name[i], 'rb')
fd.seek(0, 2)
keysize[i] = fd.tell()
keynum = keynum + 1
fd.close()
else:
keysize[i] = 0
if i != 0:
keyoffset[i] = keyoffset[i-1] + keysize[i-1]
keyhead = struct.pack('<18I', \
0x2, keynum, keyoffset[0], \
keysize[0], keyoffset[1], keysize[1], keyoffset[2], keysize[2], \
keyoffset[3], keysize[3], keyoffset[4], keysize[4], \
keyoffset[5], keysize[5], keyoffset[6], keysize[6], \
keyoffset[7], keysize[7])
keybox_data = keyhead
for i in range(0, 8):
if keysize[i] != 0:
fd = open(dirname + "/" + file_name[i], 'rb')
keybox_data = keybox_data + fd.read()
fd.close()
os.remove(dirname + "/" + file_name[i])
fdw = open(keyname, 'w+b')
fdw.write(keybox_data)
fdw.close()
print (keyname + ' generated!')
def key_partial_cut(data_file, suffix):
import sys
import os
import ssl
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
cert_s = "-----BEGIN CERTIFICATE-----"
cert_e = "-----END CERTIFICATE-----"
if suffix == "ec":
key_s = "-----BEGIN EC PRIVATE KEY-----"
key_e = "-----END EC PRIVATE KEY-----"
else:
key_s = "-----BEGIN RSA PRIVATE KEY-----"
key_e = "-----END RSA PRIVATE KEY-----"
dirname = os.path.dirname(data_file) + "/"
fd = open(data_file, 'rb')
dername = dirname + "AttestKey." + suffix
pemname = dirname + "AttestKey." + suffix + ".pem"
(ret, start_p, end_p) = file_get_pos(key_s, key_e, fd, 0)
if ret != 0:
print ('cannot find the specified string %s or %s after the seek of the file' %(key_s, key_e))
fd.close()
sys.exit(0)
ret = file_write(key_s, start_p, end_p - start_p, fd, pemname)
if ret != 0:
print ('write file %s failed' %pemname)
fd.close()
sys.exit(0)
with open(pemname, "rb") as keyfile:
# Load the PEM format key
pemkey = serialization.load_pem_private_key(
keyfile.read(), None, default_backend())
keyfile.close()
# Serialize it to DER format
derkey = pemkey.private_bytes(
serialization.Encoding.DER,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption()
)
# And write the DER format to a file
with open(dername, "wb") as outfile:
outfile.write(derkey)
outfile.close()
os.remove(pemname)
for i in range(0, 3):
(ret, start_p, end_p) = file_get_pos(cert_s, cert_e, fd, end_p)
if ret != 0:
print ('cannot find the specified string %s or %s after the seek of the file' %(cert_s, cert_e))
sys.exit(0)
dername = dirname + "AttestCert." + suffix + str(i)
pemname = dirname + "AttestCert." + suffix + str(i) + ".pem"
ret = file_write(cert_s, start_p, end_p - start_p, fd, pemname)
pem_file = open(pemname, 'rb')
pem_data = pem_file.read().decode()
pem_file.close()
der_data = ssl.PEM_cert_to_DER_cert(pem_data)
der_file = open(dername, 'wb')
der_file.write(der_data)
der_file.close()
os.remove(pemname)
if ret != 0:
print ('write file %s failed' %pemname)
sys.exit(0)
fd.close()
return ret
def key_file_cut(data_file, dirname):
import sys
import os
import gc
key_s = "<Keybox DeviceID"
key_e = "</Key></Keybox>"
args = get_args()
skip_num = args.skip
fd = open(data_file, 'rb')
# find the end of the file and redundant 16 Bytes
fd.seek(0,2)
file_end = fd.tell() - len("</AndroidAttestation>") - 16;
end_p = 0;
start_idx = args.start
i = 0
while i < start_idx and end_p < file_end:
(ret, start_p, end_p) = file_get_pos(key_s, key_e, fd, end_p)
if ret != 0:
print ('cannot find the specified string %s or %s after the seek of the file' %(key_s, key_e))
fd.close()
sys.exit(0)
i = i + 1
valid_key_cnt = 0
key_num = 0;
while end_p < file_end:
(ret, start_p, end_p) = file_get_pos(key_s, key_e, fd, end_p)
if ret != 0:
print ('cannot find the specified string %s or %s after the seek of the file' %(key_s, key_e))
fd.close()
sys.exit(0)
if key_num % skip_num != 0:
# Do not skip the last key
if end_p + 4096 < file_end:
key_num += 1;
continue
key_num += 1;
(ret, name_sp, name_ep) = file_get_pos('="', '">', fd, start_p)
if ret != 0:
print ('cannot find the specified string = or > after the seek of the file')
fd.close()
sys.exit(0)
fd.seek(name_sp, 0)
name_prefix = fd.read(name_ep - name_sp - 2).decode()
file_name = dirname + "/" + name_prefix + ".origin";
file_write(key_s, start_p, end_p - start_p, fd, file_name)
key_partial_cut(file_name, "ec")
key_partial_cut(file_name, "rsa")
os.remove(file_name)
key_pack(dirname, name_prefix)
gc.collect()
valid_key_cnt = valid_key_cnt + 1
if args.count > 0:
if valid_key_cnt >= args.count:
break
fd.close()
return ret
def main():
import os
import sys
import time
start_time = time.perf_counter()
# parse arguments
args = get_args()
if args.out != 'null':
dirname = args.out
if os.path.exists(dirname) == False:
os.mkdir(dirname)
else:
dirname = "./"
ret = key_file_cut(args.inf, dirname);
print ('spent time: %f seconds' %(time.perf_counter() - start_time))
if __name__ == "__main__":
main()