blob: 0a1f7b2f4ec1c4cb86cd72b53e6b23a9cca8acbe [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,
wenbo.wangc7934642024-07-24 11:16:47 +0800207 "extraArgs": extraArgs,
Hangyu Lia38f9f42024-06-06 17:36:18 +0800208 "csSigScheme": csSigScheme,
209 "dvSigScheme": dvSigScheme,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800210 "testService": testService,
Bo Lv9edb6992021-09-06 11:31:00 +0800211 }
Hangyu Li22720d52022-03-29 11:07:20 +0800212 else: # bl2e, bl2x, bl31, bl40, aucpufw, vdecfw
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800213 data = {
214 "chipPartNumber": chipType,
Hangyu Lidf046bc2024-06-20 18:38:11 +0800215 "chipVariant": chipVariant,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800216 "keyType": keyType,
Tao Zeng4c3f74e2021-09-26 14:09:13 +0800217 "extraArgs": extraArgs,
Hangyu Lia38f9f42024-06-06 17:36:18 +0800218 "csSigScheme": csSigScheme,
219 "dvSigScheme": dvSigScheme,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800220 "testService": testService,
221 }
Bo Lv9edb6992021-09-06 11:31:00 +0800222
223 response = requests.post(url, auth=auth, data=data, files=uploadFile)
224
225 if response.status_code == 201:
226 print("Sumbit signing job successfully, please wait...")
227
228 else:
229 print(
230 "Fail to start signing job due to the error: " + str(response.status_code)
231 )
232 exit(1)
233
234
235def queryBuildStatus(rootJobUrl, buildNumber):
236 url = rootJobUrl + str(buildNumber) + "/api/json?tree=building"
237
238 response = requests.get(url, auth=auth)
239
240 if response.status_code == 200:
241 result = json.loads(response.text)
242 return str(result["building"])
243 else:
244 return "NotStart"
245
Hangyu Li4fac3cd2023-07-03 17:39:34 +0800246def queryBuildUser(rootJobUrl, buildNumber):
247 url = rootJobUrl + str(buildNumber) + "/api/json?tree=actions[causes[userId]]"
248
249 response = requests.get(url, auth=auth)
250
251 if response.status_code == 200:
252 result = json.loads(response.text)
253 return str(result["actions"][1]["causes"][0]["userId"])
254 else:
255 return "Error"
Bo Lv9edb6992021-09-06 11:31:00 +0800256
Hangyu Li56e85b62023-09-28 16:08:09 +0800257def checkBuildParameter(rootJobUrl, buildNumber, argsChip, argsCasProvider, argsKeyType):
258 url = rootJobUrl + str(buildNumber) + "/api/json"
259 job = rootJobUrl + str(buildNumber)
260
261 response = requests.get(url, auth=auth)
262 data = response.json()
263
264 # parse JSON data to get chipPartNumber, casProvider, keyType
265 actions = data.get("actions", [])
266 for action in actions:
267 parameters = action.get("parameters", [])
268 for parameter in parameters:
269 if parameter.get("_class") == "hudson.model.StringParameterValue":
270 if parameter.get("name") == "chipPartNumber":
271 chipPartNumber = parameter.get("value")
272 if chipPartNumber != argsChip:
273 print("The jenkins Build job %s chip value doesn't match with yours." % (job))
274 return False
275
276 if parameter.get("name") == "casProvider":
277 casProvider = parameter.get("value")
278 if casProvider != argsCasProvider:
279 print("The jenkins Build job %s casProvider value doesn't match with yours." % (job))
280 return False
281
282 if parameter.get("name") == "keyType":
283 keyType = parameter.get("value")
284 if keyType != argsKeyType:
285 print("The jenkins Build job %s keyType value doesn't match with yours." % (job))
286 return False
287
288 return True
289
Bo Lv9edb6992021-09-06 11:31:00 +0800290def downloadSignedFile(rootJobUrl, buildNumber, inFileDir="", specifiedOutFilePath=""):
291
292 url = rootJobUrl + str(buildNumber) + "/api/json?tree=artifacts[relativePath]"
293
294 response = requests.get(url, auth=auth)
295
296 if response.status_code == 200:
297 result = json.loads(response.text)
298 if len(result["artifacts"]) == 0:
299 print("Fail to build, please check jenkins log for detailed error")
300 exit(1)
301 relativePath = result["artifacts"][0]["relativePath"]
302 # http://127.0.0.1:8080/job/Sign_Bl31/46/artifact/46/output/bl31-payload.bin.signed
303 downloadUrl = rootJobUrl + str(buildNumber) + "/artifact/" + "/" + relativePath
304 if specifiedOutFilePath == "":
305 outFilePath = os.path.join(inFileDir, os.path.basename(relativePath))
306 else:
307 outFilePath = specifiedOutFilePath
308 r = requests.get(downloadUrl, auth=auth)
309 with open(outFilePath, "wb") as f:
310 f.write(r.content)
311 print("Download the signed file at " + outFilePath)
312 return 0
313 else:
314 print("Fail to download the signed file")
315 exit(1)
316 return 1
317
318
319def waitForSubmit(type):
320 jobName = getJobName(type)
321
322 while True:
323 queues = server.get_queue_info()
324 inQueue = False
325 if queues:
326 for queue_job_info in queues:
327 if queue_job_info["task"].get("name", "") == jobName:
328 inQueue = True
329 break
330 if inQueue:
331 time.sleep(1)
332 print(
333 "Otherone is signing same firmware as you request. Please wait them to complete."
334 )
335 else:
336 print("It is your turn to submit your signing job now.")
337 break
338
339
340def main():
341 print(sys.argv)
342 init()
343 args = get_args()
344
345 rootJobUrl = getJobRootUrl(args.type)
346
347 waitForSubmit(args.type)
348 lastBuildNumber = getLastBuildNumber(rootJobUrl)
349 submitSignJob(
350 type=args.type,
351 chipType=args.chip,
352 inputFilePath=args.inputFilePath,
353 chipAcsFilePath=args.chipAcsFilePath,
354 taVersion=args.taVersion,
Hangyu Li4d44bba2022-05-11 14:33:08 +0800355 marketId=args.marketId,
Bo Lv9edb6992021-09-06 11:31:00 +0800356 casProvider=args.casProvider,
357 chipVariant=args.chipVariant,
358 ddrType=args.ddrType,
359 keyType=args.keyType,
Tao Zeng4c3f74e2021-09-26 14:09:13 +0800360 extraArgs=args.extraArgs,
Hangyu Lia38f9f42024-06-06 17:36:18 +0800361 csSigScheme=args.csSigScheme,
362 dvSigScheme=args.dvSigScheme,
Pengguang Zhu899a8de2021-09-17 15:48:28 +0800363 testService=args.testService,
Bo Lv9edb6992021-09-06 11:31:00 +0800364 )
365
366 buildNumber = int(lastBuildNumber) + 1
367 print("The jenkins build number: " + str(buildNumber))
368 while True:
369 time.sleep(1)
370 building = queryBuildStatus(rootJobUrl, buildNumber)
371 print("Building Status= " + str(building))
372 if building == "False":
Hangyu Li4fac3cd2023-07-03 17:39:34 +0800373 userId = queryBuildUser(rootJobUrl, buildNumber)
374 if userId.lower() != user.lower():
375 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.")
376 print("Please wait a moment, and try again.")
377 exit(1)
378
Hangyu Li56e85b62023-09-28 16:08:09 +0800379 status = checkBuildParameter(rootJobUrl, buildNumber, args.chip, args.casProvider, args.keyType)
380 if status == False:
381 print("It may be caused by too many signing requests submit to the same job.")
382 print("Please wait a moment, and try again.")
383 exit(1)
384
Bo Lv9edb6992021-09-06 11:31:00 +0800385 print("Build is done. Will start to download the signed file")
386 break
Hangyu Li4fac3cd2023-07-03 17:39:34 +0800387
Bo Lv9edb6992021-09-06 11:31:00 +0800388 inputFileDir = os.path.dirname(args.inputFilePath)
389 downloadSignedFile(rootJobUrl, buildNumber, inputFileDir, args.outFilePath)
390
391
392if __name__ == "__main__":
393 main()