]>
Commit | Line | Data |
---|---|---|
8b63877a KM |
1 | ## @file\r |
2 | # Generate a capsule.\r | |
3 | #\r | |
104a1aa1 | 4 | # This tool generates a UEFI Capsule around an FMP Capsule. The capsule payload\r |
d6f079b6 KM |
5 | # be signed using signtool or OpenSSL and if it is signed the signed content\r |
6 | # includes an FMP Payload Header.\r | |
7 | #\r | |
8 | # This tool is intended to be used to generate UEFI Capsules to update the\r | |
104a1aa1 | 9 | # system firmware or device firmware for integrated devices. In order to\r |
d6f079b6 | 10 | # keep the tool as simple as possible, it has the following limitations:\r |
d6f079b6 KM |
11 | # * Do not support vendor code bytes in a capsule.\r |
12 | #\r | |
104a1aa1 | 13 | # Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>\r |
2e351cbe | 14 | # SPDX-License-Identifier: BSD-2-Clause-Patent\r |
8b63877a KM |
15 | #\r |
16 | \r | |
17 | '''\r | |
18 | GenerateCapsule\r | |
19 | '''\r | |
20 | \r | |
21 | import sys\r | |
22 | import argparse\r | |
23 | import uuid\r | |
24 | import struct\r | |
25 | import subprocess\r | |
26 | import os\r | |
27 | import tempfile\r | |
28 | import shutil\r | |
29 | import platform\r | |
104a1aa1 | 30 | import json\r |
8b63877a KM |
31 | from Common.Uefi.Capsule.UefiCapsuleHeader import UefiCapsuleHeaderClass\r |
32 | from Common.Uefi.Capsule.FmpCapsuleHeader import FmpCapsuleHeaderClass\r | |
33 | from Common.Uefi.Capsule.FmpAuthHeader import FmpAuthHeaderClass\r | |
34 | from Common.Edk2.Capsule.FmpPayloadHeader import FmpPayloadHeaderClass\r | |
35 | \r | |
36 | #\r | |
37 | # Globals for help information\r | |
38 | #\r | |
39 | __prog__ = 'GenerateCapsule'\r | |
40 | __version__ = '0.9'\r | |
41 | __copyright__ = 'Copyright (c) 2018, Intel Corporation. All rights reserved.'\r | |
42 | __description__ = 'Generate a capsule.\n'\r | |
43 | \r | |
104a1aa1 | 44 | def SignPayloadSignTool (Payload, ToolPath, PfxFile, Verbose = False):\r |
8b63877a KM |
45 | #\r |
46 | # Create a temporary directory\r | |
47 | #\r | |
48 | TempDirectoryName = tempfile.mkdtemp()\r | |
49 | \r | |
50 | #\r | |
51 | # Generate temp file name for the payload contents\r | |
52 | #\r | |
53 | TempFileName = os.path.join (TempDirectoryName, 'Payload.bin')\r | |
54 | \r | |
55 | #\r | |
56 | # Create temporary payload file for signing\r | |
57 | #\r | |
58 | try:\r | |
104a1aa1 JE |
59 | with open (TempFileName, 'wb') as File:\r |
60 | File.write (Payload)\r | |
8b63877a KM |
61 | except:\r |
62 | shutil.rmtree (TempDirectoryName)\r | |
63 | raise ValueError ('GenerateCapsule: error: can not write temporary payload file.')\r | |
64 | \r | |
65 | #\r | |
66 | # Build signtool command\r | |
67 | #\r | |
68 | if ToolPath is None:\r | |
69 | ToolPath = ''\r | |
70 | Command = ''\r | |
71 | Command = Command + '"{Path}" '.format (Path = os.path.join (ToolPath, 'signtool.exe'))\r | |
72 | Command = Command + 'sign /fd sha256 /p7ce DetachedSignedData /p7co 1.2.840.113549.1.7.2 '\r | |
73 | Command = Command + '/p7 {TempDir} '.format (TempDir = TempDirectoryName)\r | |
74 | Command = Command + '/f {PfxFile} '.format (PfxFile = PfxFile)\r | |
75 | Command = Command + TempFileName\r | |
104a1aa1 JE |
76 | if Verbose:\r |
77 | print (Command)\r | |
8b63877a KM |
78 | \r |
79 | #\r | |
80 | # Sign the input file using the specified private key\r | |
81 | #\r | |
82 | try:\r | |
83 | Process = subprocess.Popen (Command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True)\r | |
84 | Result = Process.communicate('')\r | |
85 | except:\r | |
86 | shutil.rmtree (TempDirectoryName)\r | |
87 | raise ValueError ('GenerateCapsule: error: can not run signtool.')\r | |
88 | \r | |
89 | if Process.returncode != 0:\r | |
90 | shutil.rmtree (TempDirectoryName)\r | |
104a1aa1 | 91 | print (Result[1].decode())\r |
8b63877a KM |
92 | raise ValueError ('GenerateCapsule: error: signtool failed.')\r |
93 | \r | |
94 | #\r | |
95 | # Read the signature from the generated output file\r | |
96 | #\r | |
97 | try:\r | |
104a1aa1 JE |
98 | with open (TempFileName + '.p7', 'rb') as File:\r |
99 | Signature = File.read ()\r | |
8b63877a KM |
100 | except:\r |
101 | shutil.rmtree (TempDirectoryName)\r | |
102 | raise ValueError ('GenerateCapsule: error: can not read signature file.')\r | |
103 | \r | |
104 | shutil.rmtree (TempDirectoryName)\r | |
105 | return Signature\r | |
106 | \r | |
104a1aa1 | 107 | def VerifyPayloadSignTool (Payload, CertData, ToolPath, PfxFile, Verbose = False):\r |
8b63877a KM |
108 | print ('signtool verify is not supported.')\r |
109 | raise ValueError ('GenerateCapsule: error: signtool verify is not supported.')\r | |
110 | \r | |
104a1aa1 | 111 | def SignPayloadOpenSsl (Payload, ToolPath, SignerPrivateCertFile, OtherPublicCertFile, TrustedPublicCertFile, Verbose = False):\r |
8b63877a KM |
112 | #\r |
113 | # Build openssl command\r | |
114 | #\r | |
115 | if ToolPath is None:\r | |
116 | ToolPath = ''\r | |
117 | Command = ''\r | |
118 | Command = Command + '"{Path}" '.format (Path = os.path.join (ToolPath, 'openssl'))\r | |
119 | Command = Command + 'smime -sign -binary -outform DER -md sha256 '\r | |
120 | Command = Command + '-signer "{Private}" -certfile "{Public}"'.format (Private = SignerPrivateCertFile, Public = OtherPublicCertFile)\r | |
104a1aa1 JE |
121 | if Verbose:\r |
122 | print (Command)\r | |
8b63877a KM |
123 | \r |
124 | #\r | |
125 | # Sign the input file using the specified private key and capture signature from STDOUT\r | |
126 | #\r | |
127 | try:\r | |
128 | Process = subprocess.Popen (Command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True)\r | |
129 | Result = Process.communicate(input = Payload)\r | |
87bfb9bc | 130 | Signature = Result[0]\r |
8b63877a KM |
131 | except:\r |
132 | raise ValueError ('GenerateCapsule: error: can not run openssl.')\r | |
133 | \r | |
134 | if Process.returncode != 0:\r | |
104a1aa1 | 135 | print (Result[1].decode())\r |
8b63877a KM |
136 | raise ValueError ('GenerateCapsule: error: openssl failed.')\r |
137 | \r | |
138 | return Signature\r | |
139 | \r | |
104a1aa1 | 140 | def VerifyPayloadOpenSsl (Payload, CertData, ToolPath, SignerPrivateCertFile, OtherPublicCertFile, TrustedPublicCertFile, Verbose = False):\r |
8b63877a KM |
141 | #\r |
142 | # Create a temporary directory\r | |
143 | #\r | |
144 | TempDirectoryName = tempfile.mkdtemp()\r | |
145 | \r | |
146 | #\r | |
147 | # Generate temp file name for the payload contents\r | |
148 | #\r | |
149 | TempFileName = os.path.join (TempDirectoryName, 'Payload.bin')\r | |
150 | \r | |
151 | #\r | |
152 | # Create temporary payload file for verification\r | |
153 | #\r | |
154 | try:\r | |
104a1aa1 JE |
155 | with open (TempFileName, 'wb') as File:\r |
156 | File.write (Payload)\r | |
8b63877a KM |
157 | except:\r |
158 | shutil.rmtree (TempDirectoryName)\r | |
159 | raise ValueError ('GenerateCapsule: error: can not write temporary payload file.')\r | |
160 | \r | |
161 | #\r | |
162 | # Build openssl command\r | |
163 | #\r | |
164 | if ToolPath is None:\r | |
165 | ToolPath = ''\r | |
166 | Command = ''\r | |
167 | Command = Command + '"{Path}" '.format (Path = os.path.join (ToolPath, 'openssl'))\r | |
168 | Command = Command + 'smime -verify -inform DER '\r | |
169 | Command = Command + '-content {Content} -CAfile "{Public}"'.format (Content = TempFileName, Public = TrustedPublicCertFile)\r | |
104a1aa1 JE |
170 | if Verbose:\r |
171 | print (Command)\r | |
8b63877a KM |
172 | \r |
173 | #\r | |
174 | # Verify signature\r | |
175 | #\r | |
176 | try:\r | |
177 | Process = subprocess.Popen (Command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True)\r | |
178 | Result = Process.communicate(input = CertData)\r | |
179 | except:\r | |
180 | shutil.rmtree (TempDirectoryName)\r | |
181 | raise ValueError ('GenerateCapsule: error: can not run openssl.')\r | |
182 | \r | |
183 | if Process.returncode != 0:\r | |
184 | shutil.rmtree (TempDirectoryName)\r | |
104a1aa1 | 185 | print (Result[1].decode())\r |
8b63877a KM |
186 | raise ValueError ('GenerateCapsule: error: openssl failed.')\r |
187 | \r | |
188 | shutil.rmtree (TempDirectoryName)\r | |
189 | return Payload\r | |
190 | \r | |
191 | if __name__ == '__main__':\r | |
192 | def convert_arg_line_to_args(arg_line):\r | |
193 | for arg in arg_line.split():\r | |
194 | if not arg.strip():\r | |
195 | continue\r | |
196 | yield arg\r | |
197 | \r | |
198 | def ValidateUnsignedInteger (Argument):\r | |
199 | try:\r | |
200 | Value = int (Argument, 0)\r | |
201 | except:\r | |
202 | Message = '{Argument} is not a valid integer value.'.format (Argument = Argument)\r | |
203 | raise argparse.ArgumentTypeError (Message)\r | |
204 | if Value < 0:\r | |
205 | Message = '{Argument} is a negative value.'.format (Argument = Argument)\r | |
206 | raise argparse.ArgumentTypeError (Message)\r | |
207 | return Value\r | |
208 | \r | |
209 | def ValidateRegistryFormatGuid (Argument):\r | |
210 | try:\r | |
211 | Value = uuid.UUID (Argument)\r | |
212 | except:\r | |
213 | Message = '{Argument} is not a valid registry format GUID value.'.format (Argument = Argument)\r | |
214 | raise argparse.ArgumentTypeError (Message)\r | |
215 | return Value\r | |
216 | \r | |
104a1aa1 JE |
217 | def ConvertJsonValue (Config, FieldName, Convert, Required = True, Default = None, Open = False):\r |
218 | if FieldName not in Config:\r | |
219 | if Required:\r | |
220 | print ('GenerateCapsule: error: Payload descriptor invalid syntax. Could not find {Key} in payload descriptor.'.format(Key = FieldName))\r | |
221 | sys.exit (1)\r | |
222 | return Default\r | |
223 | try:\r | |
224 | Value = Convert (Config[FieldName])\r | |
225 | except:\r | |
226 | print ('GenerateCapsule: error: {Key} in payload descriptor has invalid syntax.'.format (Key = FieldName))\r | |
227 | sys.exit (1)\r | |
228 | if Open:\r | |
229 | try:\r | |
230 | Value = open (Value, "rb")\r | |
231 | except:\r | |
232 | print ('GenerateCapsule: error: can not open file {File}'.format (File = FieldName))\r | |
233 | sys.exit (1)\r | |
234 | return Value\r | |
235 | \r | |
236 | def DecodeJsonFileParse (Json):\r | |
237 | if 'Payloads' not in Json:\r | |
238 | print ('GenerateCapsule: error "Payloads" section not found in JSON file {File}'.format (File = args.JsonFile.name))\r | |
239 | sys.exit (1)\r | |
240 | for Config in Json['Payloads']:\r | |
241 | #\r | |
242 | # Parse fields from JSON\r | |
243 | #\r | |
244 | PayloadFile = ConvertJsonValue (Config, 'Payload', os.path.expandvars, Required = False)\r | |
245 | Guid = ConvertJsonValue (Config, 'Guid', ValidateRegistryFormatGuid, Required = False)\r | |
246 | FwVersion = ConvertJsonValue (Config, 'FwVersion', ValidateUnsignedInteger, Required = False)\r | |
247 | LowestSupportedVersion = ConvertJsonValue (Config, 'LowestSupportedVersion', ValidateUnsignedInteger, Required = False)\r | |
248 | HardwareInstance = ConvertJsonValue (Config, 'HardwareInstance', ValidateUnsignedInteger, Required = False, Default = 0)\r | |
249 | MonotonicCount = ConvertJsonValue (Config, 'MonotonicCount', ValidateUnsignedInteger, Required = False, Default = 0)\r | |
250 | SignToolPfxFile = ConvertJsonValue (Config, 'SignToolPfxFile', os.path.expandvars, Required = False, Default = None, Open = True)\r | |
251 | OpenSslSignerPrivateCertFile = ConvertJsonValue (Config, 'OpenSslSignerPrivateCertFile', os.path.expandvars, Required = False, Default = None, Open = True)\r | |
252 | OpenSslOtherPublicCertFile = ConvertJsonValue (Config, 'OpenSslOtherPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)\r | |
253 | OpenSslTrustedPublicCertFile = ConvertJsonValue (Config, 'OpenSslTrustedPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)\r | |
254 | SigningToolPath = ConvertJsonValue (Config, 'SigningToolPath', os.path.expandvars, Required = False, Default = None)\r | |
255 | UpdateImageIndex = ConvertJsonValue (Config, 'UpdateImageIndex', ValidateUnsignedInteger, Required = False, Default = 1)\r | |
256 | \r | |
257 | PayloadDescriptorList.append (PayloadDescriptor (\r | |
258 | PayloadFile,\r | |
259 | Guid,\r | |
260 | FwVersion,\r | |
261 | LowestSupportedVersion,\r | |
262 | MonotonicCount,\r | |
263 | HardwareInstance,\r | |
264 | UpdateImageIndex,\r | |
265 | SignToolPfxFile,\r | |
266 | OpenSslSignerPrivateCertFile,\r | |
267 | OpenSslOtherPublicCertFile,\r | |
268 | OpenSslTrustedPublicCertFile,\r | |
269 | SigningToolPath\r | |
270 | ))\r | |
271 | \r | |
272 | def EncodeJsonFileParse (Json):\r | |
273 | if 'EmbeddedDrivers' not in Json:\r | |
274 | print ('GenerateCapsule: warning "EmbeddedDrivers" section not found in JSON file {File}'.format (File = args.JsonFile.name))\r | |
275 | else:\r | |
276 | for Config in Json['EmbeddedDrivers']:\r | |
277 | EmbeddedDriverFile = ConvertJsonValue(Config, 'Driver', os.path.expandvars, Open = True)\r | |
278 | #\r | |
279 | #Read EmbeddedDriver file\r | |
280 | #\r | |
281 | try:\r | |
282 | if args.Verbose:\r | |
283 | print ('Read EmbeddedDriver file {File}'.format (File = EmbeddedDriverFile.name))\r | |
284 | Driver = EmbeddedDriverFile.read()\r | |
285 | except:\r | |
286 | print ('GenerateCapsule: error: can not read EmbeddedDriver file {File}'.format (File = EmbeddedDriverFile.name))\r | |
287 | sys.exit (1)\r | |
288 | EmbeddedDriverDescriptorList.append (Driver)\r | |
289 | \r | |
290 | if 'Payloads' not in Json:\r | |
291 | print ('GenerateCapsule: error: "Payloads" section not found in JSON file {File}'.format (File = args.JsonFile.name))\r | |
292 | sys.exit (1)\r | |
293 | for Config in Json['Payloads']:\r | |
294 | #\r | |
295 | # Parse fields from JSON\r | |
296 | #\r | |
297 | PayloadFile = ConvertJsonValue (Config, 'Payload', os.path.expandvars, Open = True)\r | |
298 | Guid = ConvertJsonValue (Config, 'Guid', ValidateRegistryFormatGuid)\r | |
299 | FwVersion = ConvertJsonValue (Config, 'FwVersion', ValidateUnsignedInteger)\r | |
300 | LowestSupportedVersion = ConvertJsonValue (Config, 'LowestSupportedVersion', ValidateUnsignedInteger)\r | |
301 | HardwareInstance = ConvertJsonValue (Config, 'HardwareInstance', ValidateUnsignedInteger, Required = False, Default = 0)\r | |
302 | UpdateImageIndex = ConvertJsonValue (Config, 'UpdateImageIndex', ValidateUnsignedInteger, Required = False, Default = 1)\r | |
303 | MonotonicCount = ConvertJsonValue (Config, 'MonotonicCount', ValidateUnsignedInteger, Required = False, Default = 0)\r | |
304 | SignToolPfxFile = ConvertJsonValue (Config, 'SignToolPfxFile', os.path.expandvars, Required = False, Default = None, Open = True)\r | |
305 | OpenSslSignerPrivateCertFile = ConvertJsonValue (Config, 'OpenSslSignerPrivateCertFile', os.path.expandvars, Required = False, Default = None, Open = True)\r | |
306 | OpenSslOtherPublicCertFile = ConvertJsonValue (Config, 'OpenSslOtherPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)\r | |
307 | OpenSslTrustedPublicCertFile = ConvertJsonValue (Config, 'OpenSslTrustedPublicCertFile', os.path.expandvars, Required = False, Default = None, Open = True)\r | |
308 | SigningToolPath = ConvertJsonValue (Config, 'SigningToolPath', os.path.expandvars, Required = False, Default = None)\r | |
309 | \r | |
310 | #\r | |
311 | # Read binary input file\r | |
312 | #\r | |
313 | try:\r | |
314 | if args.Verbose:\r | |
315 | print ('Read binary input file {File}'.format (File = PayloadFile.name))\r | |
316 | Payload = PayloadFile.read()\r | |
317 | PayloadFile.close ()\r | |
318 | except:\r | |
319 | print ('GenerateCapsule: error: can not read binary input file {File}'.format (File = PayloadFile.name))\r | |
320 | sys.exit (1)\r | |
321 | PayloadDescriptorList.append (PayloadDescriptor (\r | |
322 | Payload,\r | |
323 | Guid,\r | |
324 | FwVersion,\r | |
325 | LowestSupportedVersion,\r | |
326 | MonotonicCount,\r | |
327 | HardwareInstance,\r | |
328 | UpdateImageIndex,\r | |
329 | SignToolPfxFile,\r | |
330 | OpenSslSignerPrivateCertFile,\r | |
331 | OpenSslOtherPublicCertFile,\r | |
332 | OpenSslTrustedPublicCertFile,\r | |
333 | SigningToolPath\r | |
334 | ))\r | |
335 | \r | |
336 | def GenerateOutputJson (PayloadJsonDescriptorList):\r | |
337 | PayloadJson = {\r | |
338 | "Payloads" : [\r | |
339 | {\r | |
340 | "Guid": str(PayloadDescriptor.Guid).upper(),\r | |
341 | "FwVersion": str(PayloadDescriptor.FwVersion),\r | |
342 | "LowestSupportedVersion": str(PayloadDescriptor.LowestSupportedVersion),\r | |
343 | "MonotonicCount": str(PayloadDescriptor.MonotonicCount),\r | |
344 | "Payload": PayloadDescriptor.Payload,\r | |
345 | "HardwareInstance": str(PayloadDescriptor.HardwareInstance),\r | |
346 | "UpdateImageIndex": str(PayloadDescriptor.UpdateImageIndex),\r | |
347 | "SignToolPfxFile": str(PayloadDescriptor.SignToolPfxFile),\r | |
348 | "OpenSslSignerPrivateCertFile": str(PayloadDescriptor.OpenSslSignerPrivateCertFile),\r | |
349 | "OpenSslOtherPublicCertFile": str(PayloadDescriptor.OpenSslOtherPublicCertFile),\r | |
350 | "OpenSslTrustedPublicCertFile": str(PayloadDescriptor.OpenSslTrustedPublicCertFile),\r | |
351 | "SigningToolPath": str(PayloadDescriptor.SigningToolPath)\r | |
352 | }for PayloadDescriptor in PayloadJsonDescriptorList\r | |
353 | ]\r | |
354 | }\r | |
355 | OutputJsonFile = args.OutputFile.name + '.json'\r | |
356 | if 'Payloads' in PayloadJson:\r | |
357 | PayloadSection = PayloadJson ['Payloads']\r | |
358 | Index = 0\r | |
359 | for PayloadField in PayloadSection:\r | |
360 | if PayloadJsonDescriptorList[Index].SignToolPfxFile is None:\r | |
361 | del PayloadField ['SignToolPfxFile']\r | |
362 | if PayloadJsonDescriptorList[Index].OpenSslSignerPrivateCertFile is None:\r | |
363 | del PayloadField ['OpenSslSignerPrivateCertFile']\r | |
364 | if PayloadJsonDescriptorList[Index].OpenSslOtherPublicCertFile is None:\r | |
365 | del PayloadField ['OpenSslOtherPublicCertFile']\r | |
366 | if PayloadJsonDescriptorList[Index].OpenSslTrustedPublicCertFile is None:\r | |
367 | del PayloadField ['OpenSslTrustedPublicCertFile']\r | |
368 | if PayloadJsonDescriptorList[Index].SigningToolPath is None:\r | |
369 | del PayloadField ['SigningToolPath']\r | |
370 | Index = Index + 1\r | |
371 | Result = json.dumps (PayloadJson, indent=4, sort_keys=True, separators=(',', ': '))\r | |
372 | with open (OutputJsonFile, 'w') as OutputFile:\r | |
373 | OutputFile.write (Result)\r | |
374 | \r | |
375 | def CheckArgumentConflict (args):\r | |
376 | if args.Encode:\r | |
377 | if args.InputFile:\r | |
378 | print ('GenerateCapsule: error: Argument InputFile conflicts with Argument -j')\r | |
379 | sys.exit (1)\r | |
380 | if args.EmbeddedDriver:\r | |
381 | print ('GenerateCapsule: error: Argument --embedded-driver conflicts with Argument -j')\r | |
382 | sys.exit (1)\r | |
383 | if args.Guid:\r | |
384 | print ('GenerateCapsule: error: Argument --guid conflicts with Argument -j')\r | |
385 | sys.exit (1)\r | |
386 | if args.FwVersion:\r | |
387 | print ('GenerateCapsule: error: Argument --fw-version conflicts with Argument -j')\r | |
388 | sys.exit (1)\r | |
389 | if args.LowestSupportedVersion:\r | |
390 | print ('GenerateCapsule: error: Argument --lsv conflicts with Argument -j')\r | |
391 | sys.exit (1)\r | |
392 | if args.MonotonicCount:\r | |
393 | print ('GenerateCapsule: error: Argument --monotonic-count conflicts with Argument -j')\r | |
394 | sys.exit (1)\r | |
395 | if args.HardwareInstance:\r | |
396 | print ('GenerateCapsule: error: Argument --hardware-instance conflicts with Argument -j')\r | |
397 | sys.exit (1)\r | |
398 | if args.SignToolPfxFile:\r | |
399 | print ('GenerateCapsule: error: Argument --pfx-file conflicts with Argument -j')\r | |
400 | sys.exit (1)\r | |
401 | if args.OpenSslSignerPrivateCertFile:\r | |
402 | print ('GenerateCapsule: error: Argument --signer-private-cert conflicts with Argument -j')\r | |
403 | sys.exit (1)\r | |
404 | if args.OpenSslOtherPublicCertFile:\r | |
405 | print ('GenerateCapsule: error: Argument --other-public-cert conflicts with Argument -j')\r | |
406 | sys.exit (1)\r | |
407 | if args.OpenSslTrustedPublicCertFile:\r | |
408 | print ('GenerateCapsule: error: Argument --trusted-public-cert conflicts with Argument -j')\r | |
409 | sys.exit (1)\r | |
410 | if args.SigningToolPath:\r | |
411 | print ('GenerateCapsule: error: Argument --signing-tool-path conflicts with Argument -j')\r | |
412 | sys.exit (1)\r | |
413 | \r | |
414 | class PayloadDescriptor (object):\r | |
415 | def __init__(self,\r | |
416 | Payload,\r | |
417 | Guid,\r | |
418 | FwVersion,\r | |
419 | LowestSupportedVersion,\r | |
420 | MonotonicCount = 0,\r | |
421 | HardwareInstance = 0,\r | |
422 | UpdateImageIndex = 1,\r | |
423 | SignToolPfxFile = None,\r | |
424 | OpenSslSignerPrivateCertFile = None,\r | |
425 | OpenSslOtherPublicCertFile = None,\r | |
426 | OpenSslTrustedPublicCertFile = None,\r | |
427 | SigningToolPath = None\r | |
428 | ):\r | |
429 | self.Payload = Payload\r | |
430 | self.Guid = Guid\r | |
431 | self.FwVersion = FwVersion\r | |
432 | self.LowestSupportedVersion = LowestSupportedVersion\r | |
433 | self.MonotonicCount = MonotonicCount\r | |
434 | self.HardwareInstance = HardwareInstance\r | |
435 | self.UpdateImageIndex = UpdateImageIndex\r | |
436 | self.SignToolPfxFile = SignToolPfxFile\r | |
437 | self.OpenSslSignerPrivateCertFile = OpenSslSignerPrivateCertFile\r | |
438 | self.OpenSslOtherPublicCertFile = OpenSslOtherPublicCertFile\r | |
439 | self.OpenSslTrustedPublicCertFile = OpenSslTrustedPublicCertFile\r | |
440 | self.SigningToolPath = SigningToolPath\r | |
441 | \r | |
442 | self.UseSignTool = self.SignToolPfxFile is not None\r | |
443 | self.UseOpenSsl = (self.OpenSslSignerPrivateCertFile is not None and\r | |
444 | self.OpenSslOtherPublicCertFile is not None and\r | |
445 | self.OpenSslTrustedPublicCertFile is not None)\r | |
446 | self.AnyOpenSsl = (self.OpenSslSignerPrivateCertFile is not None or\r | |
447 | self.OpenSslOtherPublicCertFile is not None or\r | |
448 | self.OpenSslTrustedPublicCertFile is not None)\r | |
449 | \r | |
450 | def Validate(self, args):\r | |
451 | if self.UseSignTool and self.AnyOpenSsl:\r | |
452 | raise argparse.ArgumentTypeError ('Providing both signtool and OpenSSL options is not supported')\r | |
453 | if not self.UseSignTool and not self.UseOpenSsl and self.AnyOpenSsl:\r | |
454 | if args.JsonFile:\r | |
455 | raise argparse.ArgumentTypeError ('the following JSON fields are required for OpenSSL: OpenSslSignerPrivateCertFile, OpenSslOtherPublicCertFile, OpenSslTrustedPublicCertFile')\r | |
456 | else:\r | |
457 | raise argparse.ArgumentTypeError ('the following options are required for OpenSSL: --signer-private-cert, --other-public-cert, --trusted-public-cert')\r | |
458 | if self.UseSignTool and platform.system() != 'Windows':\r | |
459 | raise argparse.ArgumentTypeError ('Use of signtool is not supported on this operating system.')\r | |
460 | if args.Encode:\r | |
461 | if self.FwVersion is None or self.LowestSupportedVersion is None:\r | |
462 | if args.JsonFile:\r | |
463 | raise argparse.ArgumentTypeError ('the following JSON fields are required: FwVersion, LowestSupportedVersion')\r | |
464 | else:\r | |
465 | raise argparse.ArgumentTypeError ('the following options are required: --fw-version, --lsv')\r | |
466 | if self.FwVersion > 0xFFFFFFFF:\r | |
467 | if args.JsonFile:\r | |
468 | raise argparse.ArgumentTypeError ('JSON field FwVersion must be an integer in range 0x0..0xffffffff')\r | |
469 | else:\r | |
470 | raise argparse.ArgumentTypeError ('--fw-version must be an integer in range 0x0..0xffffffff')\r | |
471 | if self.LowestSupportedVersion > 0xFFFFFFFF:\r | |
472 | if args.JsonFile:\r | |
473 | raise argparse.ArgumentTypeError ('JSON field LowestSupportedVersion must be an integer in range 0x0..0xffffffff')\r | |
474 | else:\r | |
475 | raise argparse.ArgumentTypeError ('--lsv must be an integer in range 0x0..0xffffffff')\r | |
476 | \r | |
477 | if args.Encode:\r | |
478 | if self.Guid is None:\r | |
479 | if args.JsonFile:\r | |
480 | raise argparse.ArgumentTypeError ('the following JSON field is required: Guid')\r | |
481 | else:\r | |
482 | raise argparse.ArgumentTypeError ('the following option is required: --guid')\r | |
483 | if self.HardwareInstance > 0xFFFFFFFFFFFFFFFF:\r | |
484 | if args.JsonFile:\r | |
485 | raise argparse.ArgumentTypeError ('JSON field HardwareInstance must be an integer in range 0x0..0xffffffffffffffff')\r | |
486 | else:\r | |
487 | raise argparse.ArgumentTypeError ('--hardware-instance must be an integer in range 0x0..0xffffffffffffffff')\r | |
488 | if self.MonotonicCount > 0xFFFFFFFFFFFFFFFF:\r | |
489 | if args.JsonFile:\r | |
490 | raise argparse.ArgumentTypeError ('JSON field MonotonicCount must be an integer in range 0x0..0xffffffffffffffff')\r | |
491 | else:\r | |
492 | raise argparse.ArgumentTypeError ('--monotonic-count must be an integer in range 0x0..0xffffffffffffffff')\r | |
493 | if self.UpdateImageIndex >0xFF:\r | |
494 | if args.JsonFile:\r | |
495 | raise argparse.ArgumentTypeError ('JSON field UpdateImageIndex must be an integer in range 0x0..0xff')\r | |
496 | else:\r | |
497 | raise argparse.ArgumentTypeError ('--update-image-index must be an integer in range 0x0..0xff')\r | |
498 | \r | |
499 | if self.UseSignTool:\r | |
500 | self.SignToolPfxFile.close()\r | |
501 | self.SignToolPfxFile = self.SignToolPfxFile.name\r | |
502 | if self.UseOpenSsl:\r | |
503 | self.OpenSslSignerPrivateCertFile.close()\r | |
504 | self.OpenSslOtherPublicCertFile.close()\r | |
505 | self.OpenSslTrustedPublicCertFile.close()\r | |
506 | self.OpenSslSignerPrivateCertFile = self.OpenSslSignerPrivateCertFile.name\r | |
507 | self.OpenSslOtherPublicCertFile = self.OpenSslOtherPublicCertFile.name\r | |
508 | self.OpenSslTrustedPublicCertFile = self.OpenSslTrustedPublicCertFile.name\r | |
509 | \r | |
510 | #\r | |
511 | # Perform additional argument verification\r | |
512 | #\r | |
513 | if args.Encode:\r | |
514 | if 'PersistAcrossReset' not in args.CapsuleFlag:\r | |
515 | if 'InitiateReset' in args.CapsuleFlag:\r | |
516 | raise argparse.ArgumentTypeError ('--capflag InitiateReset also requires --capflag PersistAcrossReset')\r | |
517 | if args.CapsuleOemFlag > 0xFFFF:\r | |
518 | raise argparse.ArgumentTypeError ('--capoemflag must be an integer between 0x0000 and 0xffff')\r | |
519 | \r | |
520 | return True\r | |
521 | \r | |
522 | \r | |
523 | def Encode (PayloadDescriptorList, EmbeddedDriverDescriptorList, Buffer):\r | |
524 | if args.JsonFile:\r | |
525 | CheckArgumentConflict(args)\r | |
526 | try:\r | |
527 | Json = json.loads (args.JsonFile.read ())\r | |
528 | except:\r | |
529 | print ('GenerateCapsule: error: {JSONFile} loads failure. '.format (JSONFile = args.JsonFile))\r | |
530 | sys.exit (1)\r | |
531 | EncodeJsonFileParse(Json)\r | |
532 | else:\r | |
533 | for Driver in args.EmbeddedDriver:\r | |
534 | EmbeddedDriverDescriptorList.append (Driver.read())\r | |
535 | PayloadDescriptorList.append (PayloadDescriptor (\r | |
536 | Buffer,\r | |
537 | args.Guid,\r | |
538 | args.FwVersion,\r | |
539 | args.LowestSupportedVersion,\r | |
540 | args.MonotonicCount,\r | |
541 | args.HardwareInstance,\r | |
542 | args.UpdateImageIndex,\r | |
543 | args.SignToolPfxFile,\r | |
544 | args.OpenSslSignerPrivateCertFile,\r | |
545 | args.OpenSslOtherPublicCertFile,\r | |
546 | args.OpenSslTrustedPublicCertFile,\r | |
547 | args.SigningToolPath\r | |
548 | ))\r | |
549 | for SinglePayloadDescriptor in PayloadDescriptorList:\r | |
550 | try:\r | |
551 | SinglePayloadDescriptor.Validate (args)\r | |
552 | except Exception as Msg:\r | |
553 | print ('GenerateCapsule: error:' + str(Msg))\r | |
554 | sys.exit (1)\r | |
555 | for SinglePayloadDescriptor in PayloadDescriptorList:\r | |
556 | Result = SinglePayloadDescriptor.Payload\r | |
557 | try:\r | |
558 | FmpPayloadHeader.FwVersion = SinglePayloadDescriptor.FwVersion\r | |
559 | FmpPayloadHeader.LowestSupportedVersion = SinglePayloadDescriptor.LowestSupportedVersion\r | |
560 | FmpPayloadHeader.Payload = SinglePayloadDescriptor.Payload\r | |
561 | Result = FmpPayloadHeader.Encode ()\r | |
562 | if args.Verbose:\r | |
563 | FmpPayloadHeader.DumpInfo ()\r | |
564 | except:\r | |
565 | print ('GenerateCapsule: error: can not encode FMP Payload Header')\r | |
566 | sys.exit (1)\r | |
567 | if SinglePayloadDescriptor.UseOpenSsl or SinglePayloadDescriptor.UseSignTool:\r | |
568 | #\r | |
569 | # Sign image with 64-bit MonotonicCount appended to end of image\r | |
570 | #\r | |
571 | try:\r | |
572 | if SinglePayloadDescriptor.UseSignTool:\r | |
573 | CertData = SignPayloadSignTool (\r | |
574 | Result + struct.pack ('<Q', SinglePayloadDescriptor.MonotonicCount),\r | |
575 | SinglePayloadDescriptor.SigningToolPath,\r | |
576 | SinglePayloadDescriptor.SignToolPfxFile,\r | |
577 | Verbose = args.Verbose\r | |
578 | )\r | |
579 | else:\r | |
580 | CertData = SignPayloadOpenSsl (\r | |
581 | Result + struct.pack ('<Q', SinglePayloadDescriptor.MonotonicCount),\r | |
582 | SinglePayloadDescriptor.SigningToolPath,\r | |
583 | SinglePayloadDescriptor.OpenSslSignerPrivateCertFile,\r | |
584 | SinglePayloadDescriptor.OpenSslOtherPublicCertFile,\r | |
585 | SinglePayloadDescriptor.OpenSslTrustedPublicCertFile,\r | |
586 | Verbose = args.Verbose\r | |
587 | )\r | |
588 | except Exception as Msg:\r | |
589 | print ('GenerateCapsule: error: can not sign payload \n' + str(Msg))\r | |
590 | sys.exit (1)\r | |
591 | \r | |
592 | try:\r | |
593 | FmpAuthHeader.MonotonicCount = SinglePayloadDescriptor.MonotonicCount\r | |
594 | FmpAuthHeader.CertData = CertData\r | |
595 | FmpAuthHeader.Payload = Result\r | |
596 | Result = FmpAuthHeader.Encode ()\r | |
597 | if args.Verbose:\r | |
598 | FmpAuthHeader.DumpInfo ()\r | |
599 | except:\r | |
600 | print ('GenerateCapsule: error: can not encode FMP Auth Header')\r | |
601 | sys.exit (1)\r | |
602 | FmpCapsuleHeader.AddPayload (SinglePayloadDescriptor.Guid, Result, HardwareInstance = SinglePayloadDescriptor.HardwareInstance, UpdateImageIndex = SinglePayloadDescriptor.UpdateImageIndex)\r | |
603 | try:\r | |
604 | for EmbeddedDriver in EmbeddedDriverDescriptorList:\r | |
605 | FmpCapsuleHeader.AddEmbeddedDriver(EmbeddedDriver)\r | |
606 | \r | |
607 | Result = FmpCapsuleHeader.Encode ()\r | |
608 | if args.Verbose:\r | |
609 | FmpCapsuleHeader.DumpInfo ()\r | |
610 | except:\r | |
611 | print ('GenerateCapsule: error: can not encode FMP Capsule Header')\r | |
612 | sys.exit (1)\r | |
613 | \r | |
614 | try:\r | |
615 | UefiCapsuleHeader.OemFlags = args.CapsuleOemFlag\r | |
616 | UefiCapsuleHeader.PersistAcrossReset = 'PersistAcrossReset' in args.CapsuleFlag\r | |
617 | UefiCapsuleHeader.PopulateSystemTable = False\r | |
618 | UefiCapsuleHeader.InitiateReset = 'InitiateReset' in args.CapsuleFlag\r | |
619 | UefiCapsuleHeader.Payload = Result\r | |
620 | Result = UefiCapsuleHeader.Encode ()\r | |
621 | if args.Verbose:\r | |
622 | UefiCapsuleHeader.DumpInfo ()\r | |
623 | except:\r | |
624 | print ('GenerateCapsule: error: can not encode UEFI Capsule Header')\r | |
625 | sys.exit (1)\r | |
626 | try:\r | |
627 | if args.Verbose:\r | |
628 | print ('Write binary output file {File}'.format (File = args.OutputFile.name))\r | |
629 | args.OutputFile.write (Result)\r | |
630 | args.OutputFile.close ()\r | |
631 | except:\r | |
632 | print ('GenerateCapsule: error: can not write binary output file {File}'.format (File = args.OutputFile.name))\r | |
633 | sys.exit (1)\r | |
634 | \r | |
635 | def Decode (PayloadDescriptorList, PayloadJsonDescriptorList, Buffer):\r | |
636 | if args.JsonFile:\r | |
637 | CheckArgumentConflict(args)\r | |
638 | #\r | |
639 | # Parse payload descriptors from JSON\r | |
640 | #\r | |
641 | try:\r | |
642 | Json = json.loads (args.JsonFile.read())\r | |
643 | except:\r | |
644 | print ('GenerateCapsule: error: {JSONFile} loads failure. '.format (JSONFile = args.JsonFile))\r | |
645 | sys.exit (1)\r | |
646 | DecodeJsonFileParse (Json)\r | |
647 | else:\r | |
648 | PayloadDescriptorList.append (PayloadDescriptor (\r | |
649 | Buffer,\r | |
650 | args.Guid,\r | |
651 | args.FwVersion,\r | |
652 | args.LowestSupportedVersion,\r | |
653 | args.MonotonicCount,\r | |
654 | args.HardwareInstance,\r | |
655 | args.UpdateImageIndex,\r | |
656 | args.SignToolPfxFile,\r | |
657 | args.OpenSslSignerPrivateCertFile,\r | |
658 | args.OpenSslOtherPublicCertFile,\r | |
659 | args.OpenSslTrustedPublicCertFile,\r | |
660 | args.SigningToolPath\r | |
661 | ))\r | |
662 | #\r | |
663 | # Perform additional verification on payload descriptors\r | |
664 | #\r | |
665 | for SinglePayloadDescriptor in PayloadDescriptorList:\r | |
666 | try:\r | |
667 | SinglePayloadDescriptor.Validate (args)\r | |
668 | except Exception as Msg:\r | |
669 | print ('GenerateCapsule: error:' + str(Msg))\r | |
670 | sys.exit (1)\r | |
671 | try:\r | |
672 | Result = UefiCapsuleHeader.Decode (Buffer)\r | |
673 | if len (Result) > 0:\r | |
674 | Result = FmpCapsuleHeader.Decode (Result)\r | |
675 | if args.JsonFile:\r | |
676 | if FmpCapsuleHeader.PayloadItemCount != len (PayloadDescriptorList):\r | |
677 | CapsulePayloadNum = FmpCapsuleHeader.PayloadItemCount\r | |
678 | JsonPayloadNum = len (PayloadDescriptorList)\r | |
679 | print ('GenerateCapsule: Decode error: {JsonPayloadNumber} payloads in JSON file {File} and {CapsulePayloadNumber} payloads in Capsule {CapsuleName}'.format (JsonPayloadNumber = JsonPayloadNum, File = args.JsonFile.name, CapsulePayloadNumber = CapsulePayloadNum, CapsuleName = args.InputFile.name))\r | |
680 | sys.exit (1)\r | |
681 | for Index in range (0, FmpCapsuleHeader.PayloadItemCount):\r | |
682 | if Index < len (PayloadDescriptorList):\r | |
683 | GUID = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateImageTypeId\r | |
684 | HardwareInstance = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateHardwareInstance\r | |
685 | UpdateImageIndex = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateImageIndex\r | |
686 | if PayloadDescriptorList[Index].Guid != GUID or PayloadDescriptorList[Index].HardwareInstance != HardwareInstance:\r | |
687 | print ('GenerateCapsule: Decode error: Guid or HardwareInstance pair in input JSON file {File} does not match the payload {PayloadIndex} in Capsule {InputCapsule}'.format (File = args.JsonFile.name, PayloadIndex = Index + 1, InputCapsule = args.InputFile.name))\r | |
688 | sys.exit (1)\r | |
689 | PayloadDescriptorList[Index].Payload = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).Payload\r | |
690 | DecodeJsonOutput = args.OutputFile.name + '.Payload.{Index:d}.bin'.format (Index = Index + 1)\r | |
691 | PayloadJsonDescriptorList.append (PayloadDescriptor (\r | |
692 | DecodeJsonOutput,\r | |
693 | GUID,\r | |
694 | None,\r | |
695 | None,\r | |
696 | None,\r | |
697 | HardwareInstance,\r | |
698 | UpdateImageIndex,\r | |
699 | PayloadDescriptorList[Index].SignToolPfxFile,\r | |
700 | PayloadDescriptorList[Index].OpenSslSignerPrivateCertFile,\r | |
701 | PayloadDescriptorList[Index].OpenSslOtherPublicCertFile,\r | |
702 | PayloadDescriptorList[Index].OpenSslTrustedPublicCertFile,\r | |
703 | PayloadDescriptorList[Index].SigningToolPath\r | |
704 | ))\r | |
705 | else:\r | |
706 | PayloadDescriptorList[0].Payload = FmpCapsuleHeader.GetFmpCapsuleImageHeader (0).Payload\r | |
707 | for Index in range (0, FmpCapsuleHeader.PayloadItemCount):\r | |
708 | if Index > 0:\r | |
709 | PayloadDecodeFile = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).Payload\r | |
710 | PayloadDescriptorList.append (PayloadDescriptor (PayloadDecodeFile,\r | |
711 | None,\r | |
712 | None,\r | |
713 | None,\r | |
714 | None,\r | |
715 | None,\r | |
716 | None,\r | |
717 | None,\r | |
718 | None,\r | |
719 | None,\r | |
720 | None,\r | |
721 | None\r | |
722 | ))\r | |
723 | GUID = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateImageTypeId\r | |
724 | HardwareInstance = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateHardwareInstance\r | |
725 | UpdateImageIndex = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).UpdateImageIndex\r | |
726 | DecodeJsonOutput = args.OutputFile.name + '.Payload.{Index:d}.bin'.format (Index = Index + 1)\r | |
727 | PayloadJsonDescriptorList.append (PayloadDescriptor (\r | |
728 | DecodeJsonOutput,\r | |
729 | GUID,\r | |
730 | None,\r | |
731 | None,\r | |
732 | None,\r | |
733 | HardwareInstance,\r | |
734 | UpdateImageIndex,\r | |
735 | PayloadDescriptorList[Index].SignToolPfxFile,\r | |
736 | PayloadDescriptorList[Index].OpenSslSignerPrivateCertFile,\r | |
737 | PayloadDescriptorList[Index].OpenSslOtherPublicCertFile,\r | |
738 | PayloadDescriptorList[Index].OpenSslTrustedPublicCertFile,\r | |
739 | PayloadDescriptorList[Index].SigningToolPath\r | |
740 | ))\r | |
741 | JsonIndex = 0\r | |
742 | for SinglePayloadDescriptor in PayloadDescriptorList:\r | |
743 | if args.Verbose:\r | |
744 | print ('========')\r | |
745 | UefiCapsuleHeader.DumpInfo ()\r | |
746 | print ('--------')\r | |
747 | FmpCapsuleHeader.DumpInfo ()\r | |
748 | if FmpAuthHeader.IsSigned(SinglePayloadDescriptor.Payload):\r | |
749 | if not SinglePayloadDescriptor.UseOpenSsl and not SinglePayloadDescriptor.UseSignTool:\r | |
750 | print ('GenerateCapsule: decode warning: can not verify singed payload without cert or pfx file. Index = {Index}'.format (Index = JsonIndex + 1))\r | |
751 | SinglePayloadDescriptor.Payload = FmpAuthHeader.Decode (SinglePayloadDescriptor.Payload)\r | |
752 | PayloadJsonDescriptorList[JsonIndex].MonotonicCount = FmpAuthHeader.MonotonicCount\r | |
753 | if args.Verbose:\r | |
754 | print ('--------')\r | |
755 | FmpAuthHeader.DumpInfo ()\r | |
756 | \r | |
757 | #\r | |
758 | # Verify Image with 64-bit MonotonicCount appended to end of image\r | |
759 | #\r | |
760 | try:\r | |
761 | if SinglePayloadDescriptor.UseSignTool:\r | |
762 | CertData = VerifyPayloadSignTool (\r | |
763 | FmpAuthHeader.Payload + struct.pack ('<Q', FmpAuthHeader.MonotonicCount),\r | |
764 | FmpAuthHeader.CertData,\r | |
765 | SinglePayloadDescriptor.SigningToolPath,\r | |
766 | SinglePayloadDescriptor.SignToolPfxFile,\r | |
767 | Verbose = args.Verbose\r | |
768 | )\r | |
769 | else:\r | |
770 | CertData = VerifyPayloadOpenSsl (\r | |
771 | FmpAuthHeader.Payload + struct.pack ('<Q', FmpAuthHeader.MonotonicCount),\r | |
772 | FmpAuthHeader.CertData,\r | |
773 | SinglePayloadDescriptor.SigningToolPath,\r | |
774 | SinglePayloadDescriptor.OpenSslSignerPrivateCertFile,\r | |
775 | SinglePayloadDescriptor.OpenSslOtherPublicCertFile,\r | |
776 | SinglePayloadDescriptor.OpenSslTrustedPublicCertFile,\r | |
777 | Verbose = args.Verbose\r | |
778 | )\r | |
779 | except Exception as Msg:\r | |
780 | print ('GenerateCapsule: warning: payload verification failed Index = {Index} \n'.format (Index = JsonIndex + 1) + str(Msg))\r | |
781 | else:\r | |
782 | if args.Verbose:\r | |
783 | print ('--------')\r | |
784 | print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')\r | |
785 | try:\r | |
786 | SinglePayloadDescriptor.Payload = FmpPayloadHeader.Decode (SinglePayloadDescriptor.Payload)\r | |
787 | PayloadJsonDescriptorList[JsonIndex].FwVersion = FmpPayloadHeader.FwVersion\r | |
788 | PayloadJsonDescriptorList[JsonIndex].LowestSupportedVersion = FmpPayloadHeader.LowestSupportedVersion\r | |
789 | JsonIndex = JsonIndex + 1\r | |
790 | if args.Verbose:\r | |
791 | print ('--------')\r | |
792 | FmpPayloadHeader.DumpInfo ()\r | |
793 | print ('========')\r | |
794 | except:\r | |
795 | if args.Verbose:\r | |
796 | print ('--------')\r | |
797 | print ('No FMP_PAYLOAD_HEADER')\r | |
798 | print ('========')\r | |
799 | sys.exit (1)\r | |
800 | #\r | |
801 | # Write embedded driver file(s)\r | |
802 | #\r | |
803 | for Index in range (0, FmpCapsuleHeader.EmbeddedDriverCount):\r | |
804 | EmbeddedDriverBuffer = FmpCapsuleHeader.GetEmbeddedDriver (Index)\r | |
805 | EmbeddedDriverPath = args.OutputFile.name + '.EmbeddedDriver.{Index:d}.efi'.format (Index = Index + 1)\r | |
806 | try:\r | |
807 | if args.Verbose:\r | |
808 | print ('Write embedded driver file {File}'.format (File = EmbeddedDriverPath))\r | |
809 | with open (EmbeddedDriverPath, 'wb') as EmbeddedDriverFile:\r | |
810 | EmbeddedDriverFile.write (EmbeddedDriverBuffer)\r | |
811 | except:\r | |
812 | print ('GenerateCapsule: error: can not write embedded driver file {File}'.format (File = EmbeddedDriverPath))\r | |
813 | sys.exit (1)\r | |
814 | \r | |
815 | except:\r | |
816 | print ('GenerateCapsule: error: can not decode capsule')\r | |
817 | sys.exit (1)\r | |
818 | GenerateOutputJson(PayloadJsonDescriptorList)\r | |
819 | PayloadIndex = 0\r | |
820 | for SinglePayloadDescriptor in PayloadDescriptorList:\r | |
821 | if args.OutputFile is None:\r | |
822 | print ('GenerateCapsule: Decode error: OutputFile is needed for decode output')\r | |
823 | sys.exit (1)\r | |
824 | try:\r | |
825 | if args.Verbose:\r | |
826 | print ('Write binary output file {File}'.format (File = args.OutputFile.name))\r | |
827 | PayloadDecodePath = args.OutputFile.name + '.Payload.{Index:d}.bin'.format (Index = PayloadIndex + 1)\r | |
828 | with open (PayloadDecodePath, 'wb') as PayloadDecodeFile:\r | |
829 | PayloadDecodeFile.write (SinglePayloadDescriptor.Payload)\r | |
830 | PayloadIndex = PayloadIndex + 1\r | |
831 | except:\r | |
832 | print ('GenerateCapsule: error: can not write binary output file {File}'.format (File = SinglePayloadDescriptor.OutputFile.name))\r | |
833 | sys.exit (1)\r | |
834 | \r | |
835 | def DumpInfo (Buffer, args):\r | |
836 | if args.OutputFile is not None:\r | |
837 | raise argparse.ArgumentTypeError ('the following option is not supported for dumpinfo operations: --output')\r | |
838 | try:\r | |
839 | Result = UefiCapsuleHeader.Decode (Buffer)\r | |
840 | print ('========')\r | |
841 | UefiCapsuleHeader.DumpInfo ()\r | |
842 | if len (Result) > 0:\r | |
843 | FmpCapsuleHeader.Decode (Result)\r | |
844 | print ('--------')\r | |
845 | FmpCapsuleHeader.DumpInfo ()\r | |
846 | for Index in range (0, FmpCapsuleHeader.PayloadItemCount):\r | |
847 | Result = FmpCapsuleHeader.GetFmpCapsuleImageHeader (Index).Payload\r | |
848 | try:\r | |
849 | Result = FmpAuthHeader.Decode (Result)\r | |
850 | print ('--------')\r | |
851 | FmpAuthHeader.DumpInfo ()\r | |
852 | except:\r | |
853 | print ('--------')\r | |
854 | print ('No EFI_FIRMWARE_IMAGE_AUTHENTICATION')\r | |
855 | try:\r | |
856 | Result = FmpPayloadHeader.Decode (Result)\r | |
857 | print ('--------')\r | |
858 | FmpPayloadHeader.DumpInfo ()\r | |
859 | except:\r | |
860 | print ('--------')\r | |
861 | print ('No FMP_PAYLOAD_HEADER')\r | |
862 | print ('========')\r | |
863 | except:\r | |
864 | print ('GenerateCapsule: error: can not decode capsule')\r | |
865 | sys.exit (1)\r | |
8b63877a KM |
866 | #\r |
867 | # Create command line argument parser object\r | |
868 | #\r | |
869 | parser = argparse.ArgumentParser (\r | |
870 | prog = __prog__,\r | |
871 | description = __description__ + __copyright__,\r | |
872 | conflict_handler = 'resolve',\r | |
873 | fromfile_prefix_chars = '@'\r | |
874 | )\r | |
875 | parser.convert_arg_line_to_args = convert_arg_line_to_args\r | |
876 | \r | |
877 | #\r | |
878 | # Add input and output file arguments\r | |
879 | #\r | |
104a1aa1 | 880 | parser.add_argument("InputFile", type = argparse.FileType('rb'), nargs='?',\r |
8b63877a KM |
881 | help = "Input binary payload filename.")\r |
882 | parser.add_argument("-o", "--output", dest = 'OutputFile', type = argparse.FileType('wb'),\r | |
883 | help = "Output filename.")\r | |
884 | #\r | |
885 | # Add group for -e and -d flags that are mutually exclusive and required\r | |
886 | #\r | |
887 | group = parser.add_mutually_exclusive_group (required = True)\r | |
888 | group.add_argument ("-e", "--encode", dest = 'Encode', action = "store_true",\r | |
889 | help = "Encode file")\r | |
890 | group.add_argument ("-d", "--decode", dest = 'Decode', action = "store_true",\r | |
891 | help = "Decode file")\r | |
892 | group.add_argument ("--dump-info", dest = 'DumpInfo', action = "store_true",\r | |
893 | help = "Display FMP Payload Header information")\r | |
894 | #\r | |
895 | # Add optional arguments for this command\r | |
896 | #\r | |
104a1aa1 JE |
897 | parser.add_argument ("-j", "--json-file", dest = 'JsonFile', type=argparse.FileType('r'),\r |
898 | help = "JSON configuration file for multiple payloads and embedded drivers.")\r | |
8b63877a | 899 | parser.add_argument ("--capflag", dest = 'CapsuleFlag', action='append', default = [],\r |
2779c222 KM |
900 | choices=['PersistAcrossReset', 'InitiateReset'],\r |
901 | help = "Capsule flag can be PersistAcrossReset or InitiateReset or not set")\r | |
8b63877a KM |
902 | parser.add_argument ("--capoemflag", dest = 'CapsuleOemFlag', type = ValidateUnsignedInteger, default = 0x0000,\r |
903 | help = "Capsule OEM Flag is an integer between 0x0000 and 0xffff.")\r | |
904 | \r | |
905 | parser.add_argument ("--guid", dest = 'Guid', type = ValidateRegistryFormatGuid,\r | |
104a1aa1 | 906 | help = "The FMP/ESRT GUID in registry format. Required for single payload encode operations.")\r |
8b63877a KM |
907 | parser.add_argument ("--hardware-instance", dest = 'HardwareInstance', type = ValidateUnsignedInteger, default = 0x0000000000000000,\r |
908 | help = "The 64-bit hardware instance. The default is 0x0000000000000000")\r | |
909 | \r | |
910 | \r | |
911 | parser.add_argument ("--monotonic-count", dest = 'MonotonicCount', type = ValidateUnsignedInteger, default = 0x0000000000000000,\r | |
912 | help = "64-bit monotonic count value in header. Default is 0x0000000000000000.")\r | |
913 | \r | |
914 | parser.add_argument ("--fw-version", dest = 'FwVersion', type = ValidateUnsignedInteger,\r | |
104a1aa1 | 915 | help = "The 32-bit version of the binary payload (e.g. 0x11223344 or 5678). Required for encode operations.")\r |
8b63877a | 916 | parser.add_argument ("--lsv", dest = 'LowestSupportedVersion', type = ValidateUnsignedInteger,\r |
104a1aa1 | 917 | help = "The 32-bit lowest supported version of the binary payload (e.g. 0x11223344 or 5678). Required for encode operations.")\r |
8b63877a KM |
918 | \r |
919 | parser.add_argument ("--pfx-file", dest='SignToolPfxFile', type=argparse.FileType('rb'),\r | |
920 | help="signtool PFX certificate filename.")\r | |
921 | \r | |
922 | parser.add_argument ("--signer-private-cert", dest='OpenSslSignerPrivateCertFile', type=argparse.FileType('rb'),\r | |
923 | help="OpenSSL signer private certificate filename.")\r | |
924 | parser.add_argument ("--other-public-cert", dest='OpenSslOtherPublicCertFile', type=argparse.FileType('rb'),\r | |
925 | help="OpenSSL other public certificate filename.")\r | |
926 | parser.add_argument ("--trusted-public-cert", dest='OpenSslTrustedPublicCertFile', type=argparse.FileType('rb'),\r | |
927 | help="OpenSSL trusted public certificate filename.")\r | |
928 | \r | |
929 | parser.add_argument ("--signing-tool-path", dest = 'SigningToolPath',\r | |
930 | help = "Path to signtool or OpenSSL tool. Optional if path to tools are already in PATH.")\r | |
931 | \r | |
104a1aa1 JE |
932 | parser.add_argument ("--embedded-driver", dest = 'EmbeddedDriver', type = argparse.FileType('rb'), action='append', default = [],\r |
933 | help = "Path to embedded UEFI driver to add to capsule.")\r | |
934 | \r | |
8b63877a KM |
935 | #\r |
936 | # Add optional arguments common to all operations\r | |
937 | #\r | |
938 | parser.add_argument ('--version', action='version', version='%(prog)s ' + __version__)\r | |
939 | parser.add_argument ("-v", "--verbose", dest = 'Verbose', action = "store_true",\r | |
940 | help = "Turn on verbose output with informational messages printed, including capsule headers and warning messages.")\r | |
941 | parser.add_argument ("-q", "--quiet", dest = 'Quiet', action = "store_true",\r | |
942 | help = "Disable all messages except fatal errors.")\r | |
943 | parser.add_argument ("--debug", dest = 'Debug', type = int, metavar = '[0-9]', choices = range (0, 10), default = 0,\r | |
944 | help = "Set debug level")\r | |
104a1aa1 | 945 | parser.add_argument ("--update-image-index", dest = 'UpdateImageIndex', type = ValidateUnsignedInteger, default = 0x01, help = "unique number identifying the firmware image within the device ")\r |
8b63877a KM |
946 | \r |
947 | #\r | |
948 | # Parse command line arguments\r | |
949 | #\r | |
950 | args = parser.parse_args()\r | |
951 | \r | |
8b63877a KM |
952 | #\r |
953 | # Read binary input file\r | |
954 | #\r | |
104a1aa1 JE |
955 | Buffer = ''\r |
956 | if args.InputFile:\r | |
957 | if os.path.getsize (args.InputFile.name) == 0:\r | |
958 | print ('GenerateCapsule: error: InputFile {File} is empty'.format (File = args.InputFile.name))\r | |
959 | sys.exit (1)\r | |
960 | try:\r | |
961 | if args.Verbose:\r | |
962 | print ('Read binary input file {File}'.format (File = args.InputFile.name))\r | |
963 | Buffer = args.InputFile.read ()\r | |
964 | args.InputFile.close ()\r | |
965 | except:\r | |
966 | print ('GenerateCapsule: error: can not read binary input file {File}'.format (File = args.InputFile.name))\r | |
967 | sys.exit (1)\r | |
8b63877a KM |
968 | \r |
969 | #\r | |
970 | # Create objects\r | |
971 | #\r | |
972 | UefiCapsuleHeader = UefiCapsuleHeaderClass ()\r | |
973 | FmpCapsuleHeader = FmpCapsuleHeaderClass ()\r | |
974 | FmpAuthHeader = FmpAuthHeaderClass ()\r | |
975 | FmpPayloadHeader = FmpPayloadHeaderClass ()\r | |
976 | \r | |
104a1aa1 JE |
977 | EmbeddedDriverDescriptorList = []\r |
978 | PayloadDescriptorList = []\r | |
979 | PayloadJsonDescriptorList = []\r | |
8b63877a | 980 | \r |
104a1aa1 JE |
981 | #\r |
982 | #Encode Operation\r | |
983 | #\r | |
984 | if args.Encode:\r | |
985 | Encode (PayloadDescriptorList, EmbeddedDriverDescriptorList, Buffer)\r | |
8b63877a | 986 | \r |
104a1aa1 JE |
987 | #\r |
988 | #Decode Operation\r | |
989 | #\r | |
990 | if args.Decode:\r | |
991 | Decode (PayloadDescriptorList, PayloadJsonDescriptorList, Buffer)\r | |
8b63877a KM |
992 | \r |
993 | #\r | |
104a1aa1 | 994 | #Dump Info Operation\r |
8b63877a | 995 | #\r |
104a1aa1 JE |
996 | if args.DumpInfo:\r |
997 | DumpInfo (Buffer, args)\r | |
8b63877a KM |
998 | \r |
999 | if args.Verbose:\r | |
1000 | print('Success')\r |