blob: fb99b8af3b7d16293777541fbf09d88483df5f61 [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"]
21chipVariants = ["general", "nocs-jts-ap", "nocs-prod"]
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,
188 "casProvider": casProvider,
189 "keyType": keyType,
Hangyu Lia38f9f42024-06-06 17:36:18 +0800190 "csSigScheme": csSigScheme,
191 "dvSigScheme": dvSigScheme,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800192 "testService": testService,
Bo Lv9edb6992021-09-06 11:31:00 +0800193 }
194
195 elif type == "bl2":
196 chipAcsfileName = os.path.basename(chipAcsFilePath)
197 uploadFile = {
198 fileParameter: (fileName, open(inputFilePath, "rb")),
199 "chipAcsFile": (chipAcsfileName, open(chipAcsFilePath, "rb")),
200 }
201 data = {
202 "chipPartNumber": chipType,
203 "chipVariant": chipVariant,
204 "ddrType": ddrType,
205 "keyType": keyType,
Hangyu Lia38f9f42024-06-06 17:36:18 +0800206 "csSigScheme": csSigScheme,
207 "dvSigScheme": dvSigScheme,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800208 "testService": testService,
Bo Lv9edb6992021-09-06 11:31:00 +0800209 }
Hangyu Li22720d52022-03-29 11:07:20 +0800210 else: # bl2e, bl2x, bl31, bl40, aucpufw, vdecfw
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800211 data = {
212 "chipPartNumber": chipType,
213 "keyType": keyType,
Tao Zeng4c3f74e2021-09-26 14:09:13 +0800214 "extraArgs": extraArgs,
Hangyu Lia38f9f42024-06-06 17:36:18 +0800215 "csSigScheme": csSigScheme,
216 "dvSigScheme": dvSigScheme,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800217 "testService": testService,
218 }
Bo Lv9edb6992021-09-06 11:31:00 +0800219
220 response = requests.post(url, auth=auth, data=data, files=uploadFile)
221
222 if response.status_code == 201:
223 print("Sumbit signing job successfully, please wait...")
224
225 else:
226 print(
227 "Fail to start signing job due to the error: " + str(response.status_code)
228 )
229 exit(1)
230
231
232def queryBuildStatus(rootJobUrl, buildNumber):
233 url = rootJobUrl + str(buildNumber) + "/api/json?tree=building"
234
235 response = requests.get(url, auth=auth)
236
237 if response.status_code == 200:
238 result = json.loads(response.text)
239 return str(result["building"])
240 else:
241 return "NotStart"
242
Hangyu Li4fac3cd2023-07-03 17:39:34 +0800243def queryBuildUser(rootJobUrl, buildNumber):
244 url = rootJobUrl + str(buildNumber) + "/api/json?tree=actions[causes[userId]]"
245
246 response = requests.get(url, auth=auth)
247
248 if response.status_code == 200:
249 result = json.loads(response.text)
250 return str(result["actions"][1]["causes"][0]["userId"])
251 else:
252 return "Error"
Bo Lv9edb6992021-09-06 11:31:00 +0800253
Hangyu Li56e85b62023-09-28 16:08:09 +0800254def checkBuildParameter(rootJobUrl, buildNumber, argsChip, argsCasProvider, argsKeyType):
255 url = rootJobUrl + str(buildNumber) + "/api/json"
256 job = rootJobUrl + str(buildNumber)
257
258 response = requests.get(url, auth=auth)
259 data = response.json()
260
261 # parse JSON data to get chipPartNumber, casProvider, keyType
262 actions = data.get("actions", [])
263 for action in actions:
264 parameters = action.get("parameters", [])
265 for parameter in parameters:
266 if parameter.get("_class") == "hudson.model.StringParameterValue":
267 if parameter.get("name") == "chipPartNumber":
268 chipPartNumber = parameter.get("value")
269 if chipPartNumber != argsChip:
270 print("The jenkins Build job %s chip value doesn't match with yours." % (job))
271 return False
272
273 if parameter.get("name") == "casProvider":
274 casProvider = parameter.get("value")
275 if casProvider != argsCasProvider:
276 print("The jenkins Build job %s casProvider value doesn't match with yours." % (job))
277 return False
278
279 if parameter.get("name") == "keyType":
280 keyType = parameter.get("value")
281 if keyType != argsKeyType:
282 print("The jenkins Build job %s keyType value doesn't match with yours." % (job))
283 return False
284
285 return True
286
Bo Lv9edb6992021-09-06 11:31:00 +0800287def downloadSignedFile(rootJobUrl, buildNumber, inFileDir="", specifiedOutFilePath=""):
288
289 url = rootJobUrl + str(buildNumber) + "/api/json?tree=artifacts[relativePath]"
290
291 response = requests.get(url, auth=auth)
292
293 if response.status_code == 200:
294 result = json.loads(response.text)
295 if len(result["artifacts"]) == 0:
296 print("Fail to build, please check jenkins log for detailed error")
297 exit(1)
298 relativePath = result["artifacts"][0]["relativePath"]
299 # http://127.0.0.1:8080/job/Sign_Bl31/46/artifact/46/output/bl31-payload.bin.signed
300 downloadUrl = rootJobUrl + str(buildNumber) + "/artifact/" + "/" + relativePath
301 if specifiedOutFilePath == "":
302 outFilePath = os.path.join(inFileDir, os.path.basename(relativePath))
303 else:
304 outFilePath = specifiedOutFilePath
305 r = requests.get(downloadUrl, auth=auth)
306 with open(outFilePath, "wb") as f:
307 f.write(r.content)
308 print("Download the signed file at " + outFilePath)
309 return 0
310 else:
311 print("Fail to download the signed file")
312 exit(1)
313 return 1
314
315
316def waitForSubmit(type):
317 jobName = getJobName(type)
318
319 while True:
320 queues = server.get_queue_info()
321 inQueue = False
322 if queues:
323 for queue_job_info in queues:
324 if queue_job_info["task"].get("name", "") == jobName:
325 inQueue = True
326 break
327 if inQueue:
328 time.sleep(1)
329 print(
330 "Otherone is signing same firmware as you request. Please wait them to complete."
331 )
332 else:
333 print("It is your turn to submit your signing job now.")
334 break
335
336
337def main():
338 print(sys.argv)
339 init()
340 args = get_args()
341
342 rootJobUrl = getJobRootUrl(args.type)
343
344 waitForSubmit(args.type)
345 lastBuildNumber = getLastBuildNumber(rootJobUrl)
346 submitSignJob(
347 type=args.type,
348 chipType=args.chip,
349 inputFilePath=args.inputFilePath,
350 chipAcsFilePath=args.chipAcsFilePath,
351 taVersion=args.taVersion,
Hangyu Li4d44bba2022-05-11 14:33:08 +0800352 marketId=args.marketId,
Bo Lv9edb6992021-09-06 11:31:00 +0800353 casProvider=args.casProvider,
354 chipVariant=args.chipVariant,
355 ddrType=args.ddrType,
356 keyType=args.keyType,
Tao Zeng4c3f74e2021-09-26 14:09:13 +0800357 extraArgs=args.extraArgs,
Hangyu Lia38f9f42024-06-06 17:36:18 +0800358 csSigScheme=args.csSigScheme,
359 dvSigScheme=args.dvSigScheme,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800360 testService=args.testService,
Bo Lv9edb6992021-09-06 11:31:00 +0800361 )
362
363 buildNumber = int(lastBuildNumber) + 1
364 print("The jenkins build number: " + str(buildNumber))
365 while True:
366 time.sleep(1)
367 building = queryBuildStatus(rootJobUrl, buildNumber)
368 print("Building Status= " + str(building))
369 if building == "False":
Hangyu Li4fac3cd2023-07-03 17:39:34 +0800370 userId = queryBuildUser(rootJobUrl, buildNumber)
371 if userId.lower() != user.lower():
372 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.")
373 print("Please wait a moment, and try again.")
374 exit(1)
375
Hangyu Li56e85b62023-09-28 16:08:09 +0800376 status = checkBuildParameter(rootJobUrl, buildNumber, args.chip, args.casProvider, args.keyType)
377 if status == False:
378 print("It may be caused by too many signing requests submit to the same job.")
379 print("Please wait a moment, and try again.")
380 exit(1)
381
Bo Lv9edb6992021-09-06 11:31:00 +0800382 print("Build is done. Will start to download the signed file")
383 break
Hangyu Li4fac3cd2023-07-03 17:39:34 +0800384
Bo Lv9edb6992021-09-06 11:31:00 +0800385 inputFileDir = os.path.dirname(args.inputFilePath)
386 downloadSignedFile(rootJobUrl, buildNumber, inputFileDir, args.outFilePath)
387
388
389if __name__ == "__main__":
390 main()