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