xref: /aosp_15_r20/system/chre/tools/tinysys_nanoapp_signer.py (revision 84e339476a462649f82315436d70fd732297a399)
1*84e33947SAndroid Build Coastguard Worker#!/usr/bin/python
2*84e33947SAndroid Build Coastguard Worker
3*84e33947SAndroid Build Coastguard Worker#
4*84e33947SAndroid Build Coastguard Worker# Copyright 2024, The Android Open Source Project
5*84e33947SAndroid Build Coastguard Worker#
6*84e33947SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
7*84e33947SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
8*84e33947SAndroid Build Coastguard Worker# You may obtain a copy of the License at
9*84e33947SAndroid Build Coastguard Worker#
10*84e33947SAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
11*84e33947SAndroid Build Coastguard Worker#
12*84e33947SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
13*84e33947SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
14*84e33947SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15*84e33947SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
16*84e33947SAndroid Build Coastguard Worker# limitations under the License.
17*84e33947SAndroid Build Coastguard Worker#
18*84e33947SAndroid Build Coastguard Worker
19*84e33947SAndroid Build Coastguard Worker"""A script to sign nanoapps for testing purpose on tinysys platforms."""
20*84e33947SAndroid Build Coastguard Worker
21*84e33947SAndroid Build Coastguard Workerimport argparse
22*84e33947SAndroid Build Coastguard Workerimport ctypes
23*84e33947SAndroid Build Coastguard Workerimport hashlib
24*84e33947SAndroid Build Coastguard Workerfrom cryptography.hazmat.primitives import hashes
25*84e33947SAndroid Build Coastguard Workerfrom cryptography.hazmat.primitives import serialization
26*84e33947SAndroid Build Coastguard Workerfrom cryptography.hazmat.primitives.asymmetric import ec
27*84e33947SAndroid Build Coastguard Workerfrom cryptography.hazmat.primitives.asymmetric import utils
28*84e33947SAndroid Build Coastguard Worker
29*84e33947SAndroid Build Coastguard Worker
30*84e33947SAndroid Build Coastguard Workerclass HeaderInfo(ctypes.LittleEndianStructure):
31*84e33947SAndroid Build Coastguard Worker  _fields_ = [
32*84e33947SAndroid Build Coastguard Worker      ("magic_number", ctypes.c_uint32),
33*84e33947SAndroid Build Coastguard Worker      ("header_version", ctypes.c_uint32),
34*84e33947SAndroid Build Coastguard Worker      ("rollback_info", ctypes.c_uint32),
35*84e33947SAndroid Build Coastguard Worker      ("binary_length", ctypes.c_uint32),
36*84e33947SAndroid Build Coastguard Worker      ("flags", ctypes.c_uint64 * 2),
37*84e33947SAndroid Build Coastguard Worker      ("binary_sha256", ctypes.c_uint8 * 32),
38*84e33947SAndroid Build Coastguard Worker      ("reserved_chip_id", ctypes.c_uint8 * 32),
39*84e33947SAndroid Build Coastguard Worker      ("reserved_auth_config", ctypes.c_uint8 * 256),
40*84e33947SAndroid Build Coastguard Worker      ("reserved_image_config", ctypes.c_uint8 * 256),
41*84e33947SAndroid Build Coastguard Worker  ]
42*84e33947SAndroid Build Coastguard Worker
43*84e33947SAndroid Build Coastguard Worker
44*84e33947SAndroid Build Coastguard Workerdef read_private_key(key_file, password):
45*84e33947SAndroid Build Coastguard Worker  with open(key_file, "rb") as file:
46*84e33947SAndroid Build Coastguard Worker    key_bytes = file.read()
47*84e33947SAndroid Build Coastguard Worker    try:
48*84e33947SAndroid Build Coastguard Worker      return serialization.load_der_private_key(key_bytes, password=password)
49*84e33947SAndroid Build Coastguard Worker    except ValueError as e:
50*84e33947SAndroid Build Coastguard Worker      pass  # Not a DER private key
51*84e33947SAndroid Build Coastguard Worker
52*84e33947SAndroid Build Coastguard Worker    try:
53*84e33947SAndroid Build Coastguard Worker      return serialization.load_pem_private_key(key_bytes, password=password)
54*84e33947SAndroid Build Coastguard Worker    except ValueError:
55*84e33947SAndroid Build Coastguard Worker      pass  # Not a PEM private key
56*84e33947SAndroid Build Coastguard Worker    raise ValueError("Unable to parse the key file as DER or PEM")
57*84e33947SAndroid Build Coastguard Worker
58*84e33947SAndroid Build Coastguard Worker
59*84e33947SAndroid Build Coastguard Workerdef main():
60*84e33947SAndroid Build Coastguard Worker
61*84e33947SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(
62*84e33947SAndroid Build Coastguard Worker      description="Sign a binary to be authenticated on tinysys platforms"
63*84e33947SAndroid Build Coastguard Worker  )
64*84e33947SAndroid Build Coastguard Worker  parser.add_argument(
65*84e33947SAndroid Build Coastguard Worker      "private_key_file",
66*84e33947SAndroid Build Coastguard Worker      help="The private key (DER or PEM format) used to sign the binary",
67*84e33947SAndroid Build Coastguard Worker  )
68*84e33947SAndroid Build Coastguard Worker  parser.add_argument(
69*84e33947SAndroid Build Coastguard Worker      "-p",
70*84e33947SAndroid Build Coastguard Worker      "--password",
71*84e33947SAndroid Build Coastguard Worker      type=str,
72*84e33947SAndroid Build Coastguard Worker      help="Optional password encrypting the private key",
73*84e33947SAndroid Build Coastguard Worker  )
74*84e33947SAndroid Build Coastguard Worker  parser.add_argument(
75*84e33947SAndroid Build Coastguard Worker      "nanoapp", help="The name of the nanoapp binary file to be signed"
76*84e33947SAndroid Build Coastguard Worker  )
77*84e33947SAndroid Build Coastguard Worker  parser.add_argument(
78*84e33947SAndroid Build Coastguard Worker      "output_path", help="The path where the signed binary should be stored"
79*84e33947SAndroid Build Coastguard Worker  )
80*84e33947SAndroid Build Coastguard Worker  args = parser.parse_args()
81*84e33947SAndroid Build Coastguard Worker
82*84e33947SAndroid Build Coastguard Worker  # Load the binary file.
83*84e33947SAndroid Build Coastguard Worker  binary_data = None
84*84e33947SAndroid Build Coastguard Worker  with open(args.nanoapp, "rb") as binary_file:
85*84e33947SAndroid Build Coastguard Worker    binary_data = binary_file.read()
86*84e33947SAndroid Build Coastguard Worker
87*84e33947SAndroid Build Coastguard Worker  # Load ECDSA private key.
88*84e33947SAndroid Build Coastguard Worker  password = args.password.encode() if args.password else None
89*84e33947SAndroid Build Coastguard Worker  private_key = read_private_key(args.private_key_file, password)
90*84e33947SAndroid Build Coastguard Worker
91*84e33947SAndroid Build Coastguard Worker  # Generate a zero-filled header.
92*84e33947SAndroid Build Coastguard Worker  header = bytearray(0x1000)
93*84e33947SAndroid Build Coastguard Worker
94*84e33947SAndroid Build Coastguard Worker  # Fill the public key.
95*84e33947SAndroid Build Coastguard Worker  public_key_numbers = private_key.public_key().public_numbers()
96*84e33947SAndroid Build Coastguard Worker  header[0x200:0x220] = public_key_numbers.x.to_bytes(32, "big")
97*84e33947SAndroid Build Coastguard Worker  header[0x220:0x240] = public_key_numbers.y.to_bytes(32, "big")
98*84e33947SAndroid Build Coastguard Worker
99*84e33947SAndroid Build Coastguard Worker  # Fill header_info.
100*84e33947SAndroid Build Coastguard Worker  sha256_hasher = hashlib.sha256()
101*84e33947SAndroid Build Coastguard Worker  sha256_hasher.update(binary_data)
102*84e33947SAndroid Build Coastguard Worker  header_info = HeaderInfo(
103*84e33947SAndroid Build Coastguard Worker      magic_number=0x45524843,
104*84e33947SAndroid Build Coastguard Worker      header_version=1,
105*84e33947SAndroid Build Coastguard Worker      binary_length=len(binary_data),
106*84e33947SAndroid Build Coastguard Worker      binary_sha256=(ctypes.c_uint8 * 32)(*sha256_hasher.digest()),
107*84e33947SAndroid Build Coastguard Worker  )
108*84e33947SAndroid Build Coastguard Worker  header_info_bytes = bytes(header_info)
109*84e33947SAndroid Build Coastguard Worker  header[0x400 : 0x400 + len(header_info_bytes)] = header_info_bytes
110*84e33947SAndroid Build Coastguard Worker
111*84e33947SAndroid Build Coastguard Worker  # Generate the signature.
112*84e33947SAndroid Build Coastguard Worker  signature = private_key.sign(header[0x200:], ec.ECDSA(hashes.SHA256()))
113*84e33947SAndroid Build Coastguard Worker  r, s = utils.decode_dss_signature(signature)
114*84e33947SAndroid Build Coastguard Worker  r_bytes = r.to_bytes(32, "big")
115*84e33947SAndroid Build Coastguard Worker  s_bytes = s.to_bytes(32, "big")
116*84e33947SAndroid Build Coastguard Worker  header[:32] = r_bytes
117*84e33947SAndroid Build Coastguard Worker  header[32:64] = s_bytes
118*84e33947SAndroid Build Coastguard Worker
119*84e33947SAndroid Build Coastguard Worker  with open(f"{args.output_path}/{args.nanoapp}", "wb") as output:
120*84e33947SAndroid Build Coastguard Worker    output.write(header)
121*84e33947SAndroid Build Coastguard Worker    output.write(binary_data)
122*84e33947SAndroid Build Coastguard Worker
123*84e33947SAndroid Build Coastguard Worker
124*84e33947SAndroid Build Coastguard Workerif __name__ == "__main__":
125*84e33947SAndroid Build Coastguard Worker  main()
126