def Decode(self, Buffer): if len(Buffer) < self._StructSize: raise ValueError (CapsuleGuid, HeaderSize, Flags, CapsuleImageSize, Reserved) = struct.unpack(self._StructFormat, Buffer[0:self._StructSize]) if HeaderSize < self._StructSize: raise ValueError if CapsuleImageSize != len(Buffer): raise ValueError self.CapsuleGuid = uuid.UUID(bytes_le=CapsuleGuid) self.HeaderSize = HeaderSize self.OemFlags = Flags & 0xffff self.PersistAcrossReset = ( Flags & self._CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0 self.PopulateSystemTable = ( Flags & self._CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0 self.InitiateReset = (Flags & self._CAPSULE_FLAGS_INITIATE_RESET) != 0 self.CapsuleImageSize = CapsuleImageSize self.Payload = Buffer[self.HeaderSize:] if len( self.Payload ) > 0 and self.CapsuleGuid == self.EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID: self.FmpCapsuleHeader = FmpCapsuleHeaderClass() self.FmpCapsuleHeader.Decode(self.Payload) return self.Payload
def test_payload_item_count_should_track_additions(self): test_header = FmpCapsuleHeaderClass() self.assertEqual(test_header.PayloadItemCount, 0) test_header.AddFmpCapsuleImageHeader(b'dummyheader') self.assertEqual(test_header.PayloadItemCount, 1) test_header.AddFmpCapsuleImageHeader(b'dummyheader2') self.assertEqual(test_header.PayloadItemCount, 2)
def test_embedded_driver_count_should_track_additions(self): test_header = FmpCapsuleHeaderClass() self.assertEqual(test_header.EmbeddedDriverCount, 0) test_header.AddEmbeddedDriver(b'dummydriver') self.assertEqual(test_header.EmbeddedDriverCount, 1) test_header.AddEmbeddedDriver(b'dummydriver2') self.assertEqual(test_header.EmbeddedDriverCount, 2)
def build_capsule(capsule_data, capsule_options, signer_module, signer_options): ''' goes through all of the steps of capsule generation for a single-payload FMP capsule takes in capsule_data as a byte string, a signer module, and capsule and signer options, and produces all of the headers necessary. Will use the signer module to produce the cert data for the FMP Auth header. NOTE: Uses a fixed MonotonicCount of 1. capsule_data - a byte string for the innermost payload capsule_options - a dictionary that will be used for all the capsule payload fields. Must include 'fw_version', 'lsv_version', and 'esrt_guid' at a minimum. These should all be strings and the two versions should be strings of hex number (e.g. 0x12345) signer_module - a capsule signer module that implements the sign() function (see pyopenssl_signer or signtool_signer built-in modules for examples) signer_options - a dictionary of options that will be passed to the signer_module. The required values depend on the expectations of the signer_module provided returns a UefiCapsuleHeaderClass object containing all of the provided data ''' # Start building the capsule as we go. # Create the FMP Payload and set all the necessary options. fmp_payload_header = FmpPayloadHeaderClass() fmp_payload_header.FwVersion = int(capsule_options['fw_version'], 16) fmp_payload_header.LowestSupportedVersion = int(capsule_options['lsv_version'], 16) fmp_payload_header.Payload = capsule_data # Create the auth header and get ready to sign the data. fmp_auth_header = FmpAuthHeaderClass() fmp_auth_header.MonotonicCount = 1 fmp_auth_header.FmpPayloadHeader = fmp_payload_header data_to_sign = fmp_payload_header.Encode() data_to_sign = data_to_sign + struct.pack("<Q", fmp_auth_header.MonotonicCount) # Sign the data and assign it to the cert data. signature_options = { 'sign_alg': 'pkcs12', 'hash_alg': 'sha256' } # Set or override OID. signer_options['oid'] = PKCS7_SIGNED_DATA_OID fmp_auth_header.AuthInfo.CertData = signer_module.sign(data_to_sign, signature_options, signer_options) fmp_capsule_image_header = FmpCapsuleImageHeaderClass() fmp_capsule_image_header.UpdateImageTypeId = uuid.UUID(capsule_options['esrt_guid']) fmp_capsule_image_header.UpdateImageIndex = 1 fmp_capsule_image_header.FmpAuthHeader = fmp_auth_header fmp_capsule_header = FmpCapsuleHeaderClass() fmp_capsule_header.AddFmpCapsuleImageHeader(fmp_capsule_image_header) uefi_capsule_header = UefiCapsuleHeaderClass() uefi_capsule_header.FmpCapsuleHeader = fmp_capsule_header uefi_capsule_header.PersistAcrossReset = True uefi_capsule_header.InitiateReset = True return uefi_capsule_header
def test_should_be_able_to_save_a_capsule(self): fmp_capsule_image_header = FmpCapsuleImageHeaderClass() fmp_capsule_image_header.UpdateImageTypeId = uuid.UUID( DUMMY_OPTIONS['capsule']['esrt_guid']) fmp_capsule_image_header.UpdateImageIndex = 1 fmp_capsule_header = FmpCapsuleHeaderClass() fmp_capsule_header.AddFmpCapsuleImageHeader(fmp_capsule_image_header) uefi_capsule_header = UefiCapsuleHeaderClass() uefi_capsule_header.FmpCapsuleHeader = fmp_capsule_header uefi_capsule_header.PersistAcrossReset = True uefi_capsule_header.InitiateReset = True capsule_file_path = capsule_helper.save_capsule( uefi_capsule_header, DUMMY_OPTIONS['capsule'], self.temp_dir) # Now read the data and check for the GUID. with open(capsule_file_path, 'rb') as capsule_file: capsule_bytes = capsule_file.read() self.assertTrue( uuid.UUID(DUMMY_OPTIONS['capsule']['esrt_guid']).bytes_le in capsule_bytes)
class UefiCapsuleHeaderClass(object): # typedef struct { # /// # /// A GUID that defines the contents of a capsule. # /// # EFI_GUID CapsuleGuid; # /// # /// The size of the capsule header. This may be larger than the size of # /// the EFI_CAPSULE_HEADER since CapsuleGuid may imply # /// extended header entries # /// # UINT32 HeaderSize; # /// # /// Bit-mapped list describing the capsule attributes. The Flag values # /// of 0x0000 - 0xFFFF are defined by CapsuleGuid. Flag values # /// of 0x10000 - 0xFFFFFFFF are defined by this specification # /// # UINT32 Flags; # /// # /// Size in bytes of the capsule. # /// # UINT32 CapsuleImageSize; # } EFI_CAPSULE_HEADER; # # #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET 0x00010000 # #define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 # #define CAPSULE_FLAGS_INITIATE_RESET 0x00040000 # _StructFormat = '<16sIIII' _StructSize = struct.calcsize(_StructFormat) EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID = uuid.UUID( '6DCBD5ED-E82D-4C44-BDA1-7194199AD92A') _CAPSULE_FLAGS_PERSIST_ACROSS_RESET = 0x00010000 _CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE = 0x00020000 _CAPSULE_FLAGS_INITIATE_RESET = 0x00040000 def __init__(self): self.CapsuleGuid = self.EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID self.HeaderSize = self._StructSize self.OemFlags = 0x0000 self.PersistAcrossReset = False self.PopulateSystemTable = False self.InitiateReset = False self.CapsuleImageSize = self.HeaderSize self.Payload = b'' self.FmpCapsuleHeader = None def Encode(self): Flags = self.OemFlags if self.PersistAcrossReset: Flags = Flags | self._CAPSULE_FLAGS_PERSIST_ACROSS_RESET if self.PopulateSystemTable: Flags = Flags | self._CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE if self.InitiateReset: Flags = Flags | self._CAPSULE_FLAGS_INITIATE_RESET # If we have an FmpCapsuleHeader, let's collapse that now. if self.FmpCapsuleHeader is not None: self.Payload = self.FmpCapsuleHeader.Encode() self.CapsuleImageSize = self.HeaderSize + len(self.Payload) UefiCapsuleHeader = struct.pack(self._StructFormat, self.CapsuleGuid.bytes_le, self.HeaderSize, Flags, self.CapsuleImageSize, 0) return UefiCapsuleHeader + self.Payload def Decode(self, Buffer): if len(Buffer) < self._StructSize: raise ValueError (CapsuleGuid, HeaderSize, Flags, CapsuleImageSize, Reserved) = struct.unpack(self._StructFormat, Buffer[0:self._StructSize]) if HeaderSize < self._StructSize: raise ValueError if CapsuleImageSize != len(Buffer): raise ValueError self.CapsuleGuid = uuid.UUID(bytes_le=CapsuleGuid) self.HeaderSize = HeaderSize self.OemFlags = Flags & 0xffff self.PersistAcrossReset = ( Flags & self._CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0 self.PopulateSystemTable = ( Flags & self._CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0 self.InitiateReset = (Flags & self._CAPSULE_FLAGS_INITIATE_RESET) != 0 self.CapsuleImageSize = CapsuleImageSize self.Payload = Buffer[self.HeaderSize:] if len( self.Payload ) > 0 and self.CapsuleGuid == self.EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID: self.FmpCapsuleHeader = FmpCapsuleHeaderClass() self.FmpCapsuleHeader.Decode(self.Payload) return self.Payload def DumpInfo(self): Flags = self.OemFlags if self.PersistAcrossReset: Flags = Flags | self._CAPSULE_FLAGS_PERSIST_ACROSS_RESET if self.PopulateSystemTable: Flags = Flags | self._CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE if self.InitiateReset: Flags = Flags | self._CAPSULE_FLAGS_INITIATE_RESET print('EFI_CAPSULE_HEADER.CapsuleGuid = {Guid}'.format( Guid=str(self.CapsuleGuid).upper())) print('EFI_CAPSULE_HEADER.HeaderSize = {Size:08X}'.format( Size=self.HeaderSize)) print('EFI_CAPSULE_HEADER.Flags = {Flags:08X}'.format( Flags=Flags)) print(' OEM Flags = {Flags:04X}'.format( Flags=self.OemFlags)) if self.PersistAcrossReset: print(' CAPSULE_FLAGS_PERSIST_ACROSS_RESET') if self.PopulateSystemTable: print(' CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE') if self.InitiateReset: print(' CAPSULE_FLAGS_INITIATE_RESET') print('EFI_CAPSULE_HEADER.CapsuleImageSize = {Size:08X}'.format( Size=self.CapsuleImageSize)) print('sizeof (Payload) = {Size:08X}'.format( Size=len(self.Payload))) if self.FmpCapsuleHeader is not None: self.FmpCapsuleHeader.DumpInfo()
def test_encoding_twice_should_yield_identical_results(self): test_header = FmpCapsuleHeaderClass() test_header.AddEmbeddedDriver(b'dummydriver') encode_1 = test_header.Encode() encode_2 = test_header.Encode() self.assertEqual(encode_1, encode_2)