blob: a1bbe9a83bd804403a09d719b9803de55f615b48 [file] [log] [blame]
Bo Lv9edb6992021-09-06 11:31:00 +08001#!/usr/bin/env python3
2
3import sys
4import os
5import re
6import requests
7from requests.auth import HTTPBasicAuth
8from urllib.parse import urljoin
9import urllib.request
10import json
11import time
12from os.path import expanduser
13import jenkins
14
15
Hangyu Lib97619c2022-08-01 18:07:35 +080016serverRootUrl = "https://jenkins-sh.amlogic.com/job/Security/job/"
Bo Lv9edb6992021-09-06 11:31:00 +080017homeConfigFilePath = "~/.sign.cfg"
Hangyu Li505e5eb2023-03-28 15:54:19 +080018types = ["ta", "vmxta", "irdetota", "bl32", "bl31", "bl2", "bl2e", "bl2x", "bl40", "aucpufw", "vdecfw"]
Hangyu Li0d2e7052022-11-02 15:26:11 +080019casProviders = ["", "VMX", "nagra", "nagra-dev", "vo-dev", "vo", "gs-dev", "gs", "irdeto"]
Bo Lv9edb6992021-09-06 11:31:00 +080020ddrTypes = ["ddr4", "lpddr4", "ddr3", "lpddr3", "lpddr4_lpddr5"]
Hangyu Lidf046bc2024-06-20 18:38:11 +080021chipVariants = ["", "general", "nocs-jts-ap", "nocs-prod", "onboot"]
Hangyu Lia38f9f42024-06-06 17:36:18 +080022csSigSchemes = ["", "rsa", "rsa-mldsa"]
23dvSigSchemes = ["", "rsa", "rsa-mldsa"]
Bo Lv9edb6992021-09-06 11:31:00 +080024
25user = ""
26password = ""
27auth = None
28server = None
29
30
31def init():
32 global user
33 global password
34 global auth
35 global server
36 expandedHomeConfigFilePath = expanduser(homeConfigFilePath)
37 configPyPath = os.path.join(sys.path[0], "config.py")
38 if os.path.exists(expandedHomeConfigFilePath):
39 configFilePath = expandedHomeConfigFilePath
40 elif os.path.exists(configPyPath):
41 configFilePath = configPyPath
42 else:
43 print(
44 "Can't find configuration file. Please create configuration file .sign.cfg at your home directory."
45 )
46 exit(1)
47
48 with open(configFilePath, "r") as configFile:
49 while True:
50 line = configFile.readline()
51 if not line:
52 break
53 words = line.split("=")
54 if words[0].strip() == "user":
55 user = words[1].strip().replace("'", "")
56 elif words[0].strip() == "password":
57 password = words[1].strip().replace("'", "")
58 auth = HTTPBasicAuth(user, password)
59 server = jenkins.Jenkins(
60 "https://jenkins-sh.amlogic.com", username=user, password=password
61 )
62
63
64def get_args():
65 from argparse import ArgumentParser
66
67 parser = ArgumentParser()
68
69 parser.add_argument("--in", dest="inputFilePath", required=True, help="input file")
70 parser.add_argument(
71 "--chipAcsFile", dest="chipAcsFilePath", default="null", help="chip acs file"
72 )
73 parser.add_argument(
74 "--out", dest="outFilePath", type=str, default="", help="output signed file"
75 )
76 parser.add_argument(
77 "-v", "--version", action="version", version="%(prog)s 1.0", help="version"
78 )
79 parser.add_argument("--type", choices=types, default=types[0], required=True)
80 parser.add_argument("--chip", type=str)
81 parser.add_argument("--taVersion", type=int, default=0)
Hangyu Li4d44bba2022-05-11 14:33:08 +080082 parser.add_argument("--marketId", type=str, default="null")
Bo Lv9edb6992021-09-06 11:31:00 +080083 parser.add_argument("--casProvider", choices=casProviders, default=casProviders[0])
84 parser.add_argument("--ddrType", type=str, default=ddrTypes[0])
85 parser.add_argument("--chipVariant", choices=chipVariants, default=chipVariants[0])
86 parser.add_argument("--keyType", type=str, dest="keyType", default="dev-keys")
Tao Zeng4c3f74e2021-09-26 14:09:13 +080087 parser.add_argument("--extraArgs", type=str, default="")
Hangyu Lia38f9f42024-06-06 17:36:18 +080088 parser.add_argument("--csSigScheme", choices=csSigSchemes, default=csSigSchemes[0])
89 parser.add_argument("--dvSigScheme", choices=dvSigSchemes, default=dvSigSchemes[0])
Pengguang Zhu899a8de2021-09-17 15:48:28 +080090 parser.add_argument("--testService", type=int, default=0)
Bo Lv9edb6992021-09-06 11:31:00 +080091
92 return parser.parse_args()
93
94
95def getLastBuildNumber(rootJobUrl):
96 url = urljoin(rootJobUrl, "lastBuild/buildNumber")
97
98 response = requests.get(url, auth=auth)
99
100 if response.status_code == 200:
101 return response.text
102 else:
103 print(
104 "Fail to get last build number due to the error "
105 + str(response.status_code)
106 )
107 return 0
108
109
110def getJobRootUrl(type):
111 if type == "ta":
Hangyu Lib97619c2022-08-01 18:07:35 +0800112 return urljoin(serverRootUrl, "Signing/job/Sign_TA/")
113 elif type == "vmxta":
114 return urljoin(serverRootUrl, "CAS/job/VMX/job/VMX_TA_Sign/")
Hangyu Li505e5eb2023-03-28 15:54:19 +0800115 elif type == "irdetota":
116 return urljoin(serverRootUrl, "Signing/job/Sign_Irdeto_TA_for_s905c1/")
Bo Lv9edb6992021-09-06 11:31:00 +0800117 elif type == "bl31":
Hangyu Lib97619c2022-08-01 18:07:35 +0800118 return urljoin(serverRootUrl, "Signing/job/Sign_Bl31/")
Bo Lv9edb6992021-09-06 11:31:00 +0800119 elif type == "bl2":
Hangyu Lib97619c2022-08-01 18:07:35 +0800120 return urljoin(serverRootUrl, "Signing/job/Sign_Bl2/")
Bo Lv9edb6992021-09-06 11:31:00 +0800121 elif type == "bl32":
Hangyu Lib97619c2022-08-01 18:07:35 +0800122 return urljoin(serverRootUrl, "Signing/job/Sign_Bl32/")
Hangyu Li229ddee2022-03-24 13:57:51 +0800123 elif type == "aucpufw":
Hangyu Lib97619c2022-08-01 18:07:35 +0800124 return urljoin(serverRootUrl, "Signing/job/Sign_AUCPU_FW/")
Hangyu Li22720d52022-03-29 11:07:20 +0800125 elif type == "vdecfw":
Hangyu Lib97619c2022-08-01 18:07:35 +0800126 return urljoin(serverRootUrl, "Signing/job/Sign_VDEC_FW/")
Bo Lv9edb6992021-09-06 11:31:00 +0800127 else: # bl2e, bl2x, bl40
Hangyu Lib97619c2022-08-01 18:07:35 +0800128 return urljoin(serverRootUrl, "Signing/job/Sign_Bl2e_Bl2x_Bl40/")
Bo Lv9edb6992021-09-06 11:31:00 +0800129
130
131def getJobName(type):
132 if type == "ta":
133 return "Sign_TA"
Hangyu Li505e5eb2023-03-28 15:54:19 +0800134 elif type == "vmxta":
135 return "Sign_VMX_TA"
136 elif type == "irdetota":
137 return "Sign_Irdeto_TA_for_s905c1"
Bo Lv9edb6992021-09-06 11:31:00 +0800138 elif type == "bl31":
139 return "Sign_Bl31"
140 elif type == "bl2":
141 return "Sign_Bl2"
142 elif type == "bl32":
143 return "Sign_Bl32"
Hangyu Li229ddee2022-03-24 13:57:51 +0800144 elif type == "aucpufw":
145 return "Sign_AUCPU_FW"
Hangyu Li22720d52022-03-29 11:07:20 +0800146 elif type == "vdecfw":
147 return "Sign_VDEC_FW"
Bo Lv9edb6992021-09-06 11:31:00 +0800148 else: # bl2e, bl2x, bl40
149 return "Sign_Bl2e_Bl2x_Bl40"
150
151
152def submitSignJob(
153 type,
154 chipType,
155 inputFilePath,
156 chipAcsFilePath,
157 taVersion="0",
Hangyu Li4d44bba2022-05-11 14:33:08 +0800158 marketId="",
Bo Lv9edb6992021-09-06 11:31:00 +0800159 casProvider="",
160 chipVariant="",
161 ddrType="",
162 keyType="dev-keys",
Tao Zeng4c3f74e2021-09-26 14:09:13 +0800163 extraArgs="",
Hangyu Lia38f9f42024-06-06 17:36:18 +0800164 csSigScheme="",
165 dvSigScheme="",
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800166 testService=0,
Bo Lv9edb6992021-09-06 11:31:00 +0800167):
168
169 fileName = os.path.basename(inputFilePath)
170 fileParameter = "file"
171 uploadFile = {
172 fileParameter: (fileName, open(inputFilePath, "rb")),
173 }
174 url = getJobRootUrl(type) + "buildWithParameters"
Hangyu Li505e5eb2023-03-28 15:54:19 +0800175 if type == "ta" or type == "vmxta" or type == "irdetota":
Bo Lv9edb6992021-09-06 11:31:00 +0800176 data = {
177 "chip_part_number": chipType,
178 "ta_version": taVersion,
Hangyu Li4d44bba2022-05-11 14:33:08 +0800179 "market_id": marketId,
Hangyu Lia38f9f42024-06-06 17:36:18 +0800180 "csSigScheme": csSigScheme,
181 "dvSigScheme": dvSigScheme,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800182 "testService": testService,
Bo Lv9edb6992021-09-06 11:31:00 +0800183 }
184 elif type == "bl32":
185
186 data = {
187 "chipPartNumber": chipType,
Hangyu Lidf046bc2024-06-20 18:38:11 +0800188 "chipVariant": chipVariant,
Bo Lv9edb6992021-09-06 11:31:00 +0800189 "casProvider": casProvider,
190 "keyType": keyType,
Hangyu Lia38f9f42024-06-06 17:36:18 +0800191 "csSigScheme": csSigScheme,
192 "dvSigScheme": dvSigScheme,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800193 "testService": testService,
Bo Lv9edb6992021-09-06 11:31:00 +0800194 }
195
196 elif type == "bl2":
197 chipAcsfileName = os.path.basename(chipAcsFilePath)
198 uploadFile = {
199 fileParameter: (fileName, open(inputFilePath, "rb")),
200 "chipAcsFile": (chipAcsfileName, open(chipAcsFilePath, "rb")),
201 }
202 data = {
203 "chipPartNumber": chipType,
204 "chipVariant": chipVariant,
205 "ddrType": ddrType,
206 "keyType": keyType,
Hangyu Lia38f9f42024-06-06 17:36:18 +0800207 "csSigScheme": csSigScheme,
208 "dvSigScheme": dvSigScheme,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800209 "testService": testService,
Bo Lv9edb6992021-09-06 11:31:00 +0800210 }
Hangyu Li22720d52022-03-29 11:07:20 +0800211 else: # bl2e, bl2x, bl31, bl40, aucpufw, vdecfw
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800212 data = {
213 "chipPartNumber": chipType,
Hangyu Lidf046bc2024-06-20 18:38:11 +0800214 "chipVariant": chipVariant,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800215 "keyType": keyType,
Tao Zeng4c3f74e2021-09-26 14:09:13 +0800216 "extraArgs": extraArgs,
Hangyu Lia38f9f42024-06-06 17:36:18 +0800217 "csSigScheme": csSigScheme,
218 "dvSigScheme": dvSigScheme,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800219 "testService": testService,
220 }
Bo Lv9edb6992021-09-06 11:31:00 +0800221
222 response = requests.post(url, auth=auth, data=data, files=uploadFile)
223
224 if response.status_code == 201:
225 print("Sumbit signing job successfully, please wait...")
226
227 else:
228 print(
229 "Fail to start signing job due to the error: " + str(response.status_code)
230 )
231 exit(1)
232
233
234def queryBuildStatus(rootJobUrl, buildNumber):
235 url = rootJobUrl + str(buildNumber) + "/api/json?tree=building"
236
237 response = requests.get(url, auth=auth)
238
239 if response.status_code == 200:
240 result = json.loads(response.text)
241 return str(result["building"])
242 else:
243 return "NotStart"
244
Hangyu Li4fac3cd2023-07-03 17:39:34 +0800245def queryBuildUser(rootJobUrl, buildNumber):
246 url = rootJobUrl + str(buildNumber) + "/api/json?tree=actions[causes[userId]]"
247
248 response = requests.get(url, auth=auth)
249
250 if response.status_code == 200:
251 result = json.loads(response.text)
252 return str(result["actions"][1]["causes"][0]["userId"])
253 else:
254 return "Error"
Bo Lv9edb6992021-09-06 11:31:00 +0800255
Hangyu Li56e85b62023-09-28 16:08:09 +0800256def checkBuildParameter(rootJobUrl, buildNumber, argsChip, argsCasProvider, argsKeyType):
257 url = rootJobUrl + str(buildNumber) + "/api/json"
258 job = rootJobUrl + str(buildNumber)
259
260 response = requests.get(url, auth=auth)
261 data = response.json()
262
263 # parse JSON data to get chipPartNumber, casProvider, keyType
264 actions = data.get("actions", [])
265 for action in actions:
266 parameters = action.get("parameters", [])
267 for parameter in parameters:
268 if parameter.get("_class") == "hudson.model.StringParameterValue":
269 if parameter.get("name") == "chipPartNumber":
270 chipPartNumber = parameter.get("value")
271 if chipPartNumber != argsChip:
272 print("The jenkins Build job %s chip value doesn't match with yours." % (job))
273 return False
274
275 if parameter.get("name") == "casProvider":
276 casProvider = parameter.get("value")
277 if casProvider != argsCasProvider:
278 print("The jenkins Build job %s casProvider value doesn't match with yours." % (job))
279 return False
280
281 if parameter.get("name") == "keyType":
282 keyType = parameter.get("value")
283 if keyType != argsKeyType:
284 print("The jenkins Build job %s keyType value doesn't match with yours." % (job))
285 return False
286
287 return True
288
Bo Lv9edb6992021-09-06 11:31:00 +0800289def downloadSignedFile(rootJobUrl, buildNumber, inFileDir="", specifiedOutFilePath=""):
290
291 url = rootJobUrl + str(buildNumber) + "/api/json?tree=artifacts[relativePath]"
292
293 response = requests.get(url, auth=auth)
294
295 if response.status_code == 200:
296 result = json.loads(response.text)
297 if len(result["artifacts"]) == 0:
298 print("Fail to build, please check jenkins log for detailed error")
299 exit(1)
300 relativePath = result["artifacts"][0]["relativePath"]
301 # http://127.0.0.1:8080/job/Sign_Bl31/46/artifact/46/output/bl31-payload.bin.signed
302 downloadUrl = rootJobUrl + str(buildNumber) + "/artifact/" + "/" + relativePath
303 if specifiedOutFilePath == "":
304 outFilePath = os.path.join(inFileDir, os.path.basename(relativePath))
305 else:
306 outFilePath = specifiedOutFilePath
307 r = requests.get(downloadUrl, auth=auth)
308 with open(outFilePath, "wb") as f:
309 f.write(r.content)
310 print("Download the signed file at " + outFilePath)
311 return 0
312 else:
313 print("Fail to download the signed file")
314 exit(1)
315 return 1
316
317
318def waitForSubmit(type):
319 jobName = getJobName(type)
320
321 while True:
322 queues = server.get_queue_info()
323 inQueue = False
324 if queues:
325 for queue_job_info in queues:
326 if queue_job_info["task"].get("name", "") == jobName:
327 inQueue = True
328 break
329 if inQueue:
330 time.sleep(1)
331 print(
332 "Otherone is signing same firmware as you request. Please wait them to complete."
333 )
334 else:
335 print("It is your turn to submit your signing job now.")
336 break
337
338
339def main():
340 print(sys.argv)
341 init()
342 args = get_args()
343
344 rootJobUrl = getJobRootUrl(args.type)
345
346 waitForSubmit(args.type)
347 lastBuildNumber = getLastBuildNumber(rootJobUrl)
348 submitSignJob(
349 type=args.type,
350 chipType=args.chip,
351 inputFilePath=args.inputFilePath,
352 chipAcsFilePath=args.chipAcsFilePath,
353 taVersion=args.taVersion,
Hangyu Li4d44bba2022-05-11 14:33:08 +0800354 marketId=args.marketId,
Bo Lv9edb6992021-09-06 11:31:00 +0800355 casProvider=args.casProvider,
356 chipVariant=args.chipVariant,
357 ddrType=args.ddrType,
358 keyType=args.keyType,
Tao Zeng4c3f74e2021-09-26 14:09:13 +0800359 extraArgs=args.extraArgs,
Hangyu Lia38f9f42024-06-06 17:36:18 +0800360 csSigScheme=args.csSigScheme,
361 dvSigScheme=args.dvSigScheme,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800362 testService=args.testService,
Bo Lv9edb6992021-09-06 11:31:00 +0800363 )
364
365 buildNumber = int(lastBuildNumber) + 1
366 print("The jenkins build number: " + str(buildNumber))
367 while True:
368 time.sleep(1)
369 building = queryBuildStatus(rootJobUrl, buildNumber)
370 print("Building Status= " + str(building))
371 if building == "False":
Hangyu Li4fac3cd2023-07-03 17:39:34 +0800372 userId = queryBuildUser(rootJobUrl, buildNumber)
373 if userId.lower() != user.lower():
374 print("The jenkins Build number user name doesn't match yours.It may be caused by too many signing requests submit to the same job.")
375 print("Please wait a moment, and try again.")
376 exit(1)
377
Hangyu Li56e85b62023-09-28 16:08:09 +0800378 status = checkBuildParameter(rootJobUrl, buildNumber, args.chip, args.casProvider, args.keyType)
379 if status == False:
380 print("It may be caused by too many signing requests submit to the same job.")
381 print("Please wait a moment, and try again.")
382 exit(1)
383
Bo Lv9edb6992021-09-06 11:31:00 +0800384 print("Build is done. Will start to download the signed file")
385 break
Hangyu Li4fac3cd2023-07-03 17:39:34 +0800386
Bo Lv9edb6992021-09-06 11:31:00 +0800387 inputFileDir = os.path.dirname(args.inputFilePath)
388 downloadSignedFile(rootJobUrl, buildNumber, inputFileDir, args.outFilePath)
389
390
391if __name__ == "__main__":
392 main()