efivar.py currently stores authenticated variables including the EFI_VARIABLE_AUTHENTICATION_2 descriptor (timestamp + WIN_CERTIFICATE) along with the payload.
When variables are set via U-Boot, SetVariable() validates and strips this authentication descriptor before persisting the variable data, resulting in only the payload being stored and returned by GetVariable(). This mismatch causes efivar.py-generated stores to differ from U-Boot runtime behavior and leads to incorrect GetVariable() results. Update efivar.py to strip the authentication descriptor and store only the payload for authenticated variables, ensuring consistency with U-Boot behavior and compliance with UEFI expectations. Signed-off-by: Aswin Murugan <[email protected]> --- Changes in v3: - Previously stripped the authentication descriptor at GetVariable(), now moved to strip it in efivar.py during variable pre-seeding/set. Link to v2: https://lore.kernel.org/u-boot/[email protected]/ Changes in v2: - Enhanced commit message with explicit UEFI spec reference Link to v1: https://lore.kernel.org/u-boot/[email protected]/ --- tools/efivar.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/tools/efivar.py b/tools/efivar.py index 67729fa8505..d248cd868ba 100755 --- a/tools/efivar.py +++ b/tools/efivar.py @@ -79,6 +79,50 @@ class EfiVariable: def calc_crc32(buf): return zlib.crc32(buf) & 0xffffffff +def strip_auth_descriptor(data, attrs): + """ + Strip the EFI auth descriptor from authenticated variable data. + + This is used during efivar.py-based pre-seeding of ubootefi.var to + match U-Boot SetVariable() behavior, where the authentication header + is consumed during validation and only the payload is stored. + + For variables with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS: + - Input format: [EFI_VARIABLE_AUTHENTICATION_2 | payload] + - Stored format: [payload only] + + Auth header size calculation (aligned with U-Boot efi_variable_authenticate()): + auth_size = sizeof(EFI_TIME) + WIN_CERTIFICATE.dwLength + + Only the payload portion is retained after stripping the header. + """ + if not (attrs & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS): + return data + if not data: + return data + + efi = EfiStruct() + if len(data) < efi.var_time_size: + return data + + offset = efi.var_time_size + if len(data) < offset + efi.var_win_cert_size: + return data + + try: + cert_hdr = struct.unpack_from(efi.var_win_cert_fmt, data, offset) + dwLength = cert_hdr[0] + except struct.error: + return data + + auth_size = efi.var_time_size + dwLength + if auth_size <= 0 or auth_size > len(data): + return data + + if len(data) <= auth_size: + return b'' + return data[auth_size:] + class EfiVariableStore: def __init__(self, infile): self.infile = infile @@ -172,8 +216,12 @@ class EfiVariableStore: break offs = loffs + if data and (attrs & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS): + data = strip_auth_descriptor(data, attrs) + size = len(data) if data else 0 + tsec = int(time.time()) if attrs & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS else 0 - nd = name.encode('utf_16_le') + b"\x00\x00" + data + nd = name.encode('utf_16_le') + b"\x00\x00" + (data if data else b'') # U-Boot variable format requires the name + data blob to be 8-byte aligned pad = ((len(nd) + 7) & ~7) - len(nd) nd += bytes([0] * pad) -- 2.34.1

