
On 11/30/20 4:16 PM, Paulo Alcantara wrote:
This script generates EFI variables for U-Boot variable store format.
Hello Paulo,
thanks for you valuable contribution.
Wouldn't it make sense to allow overwriting and deleting variables too?
Best regards
Heinrich
An example of generating secure boot variables
$ openssl x509 -in foo.crt -outform DER -out foo.der $ efisiglist -a -c foo.der -o foo.esl $ efivar.py -i ubootefi.var add -n db -d foo.esl -t file $ efivar.py -i ubootefi.var add -n kek -d foo.esl -t file $ efivar.py -i ubootefi.var add -n pk -d foo.esl -t file
Signed-off-by: Paulo Alcantara (SUSE) pc@cjr.nz
tools/efivar.py | 141 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100755 tools/efivar.py
diff --git a/tools/efivar.py b/tools/efivar.py new file mode 100755 index 000000000000..31e5508f08fd --- /dev/null +++ b/tools/efivar.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +## SPDX-License-Identifier: GPL-2.0-only +# +# Generate UEFI variables for U-Boot. +# +# (c) 2020 Paulo Alcantara palcantara@suse.de +#
+import os +import struct +import uuid +import time +import zlib +import argparse +import subprocess as sp
+# U-Boot variable store format (version 1) +UBOOT_EFI_VAR_FILE_MAGIC = 0x0161566966456255
+# UEFI variable attributes +EFI_VARIABLE_NON_VOLATILE = 0x1 +EFI_VARIABLE_BOOTSERVICE_ACCESS = 0x2 +EFI_VARIABLE_RUNTIME_ACCESS = 0x4 +EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS = 0x20 +NV_BS = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS +NV_BS_RT = NV_BS | EFI_VARIABLE_RUNTIME_ACCESS +NV_BS_RT_AT = NV_BS_RT | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
+# UEFI variable GUIDs +EFI_GLOBAL_VARIABLE_GUID = '{8be4df61-93ca-11d2-aa0d-00e098032b8c}' +EFI_IMAGE_SECURITY_DATABASE_GUID = '{d719b2cb-3d3a-4596-a3bc-dad00e67656f}'
+class EfiStruct:
# struct efi_var_file
var_file_fmt = '<QQLL'
var_file_size = struct.calcsize(var_file_fmt)
# struct efi_var_entry
var_entry_fmt = '<LLQ16s'
var_entry_size = struct.calcsize(var_entry_fmt)
+class EfiVariableStore:
- def __init__(self, infile):
self.infile = infile
self.efi = EfiStruct()
if os.path.exists(self.infile) and os.stat(self.infile).st_size > self.efi.var_file_size:
with open(self.infile, 'rb') as f:
# skip header since it will be recreated by save()
self.buf = f.read()[self.efi.var_file_size:]
else:
self.buf = bytearray()
- def _set_var(self, guid, name_data, size, attr, tsec):
ent = struct.pack(self.efi.var_entry_fmt,
size,
attr,
tsec,
uuid.UUID(guid).bytes_le)
ent += name_data
self.buf += ent
- def set_var(self, guid, name, data, size, attr):
tsec = int(time.time()) if attr & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS else 0
nd = name.encode('utf_16_le') + b"\x00\x00" + data
# 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)
return self._set_var(guid, nd, size, attr, tsec)
- def save(self):
hdr = struct.pack(self.efi.var_file_fmt,
0,
UBOOT_EFI_VAR_FILE_MAGIC,
len(self.buf) + self.efi.var_file_size,
zlib.crc32(self.buf) & 0xffffffff)
with open(self.infile, 'wb') as f:
f.write(hdr)
f.write(self.buf)
+def parse_attrs(attr):
- attrs = {
'nv': EFI_VARIABLE_NON_VOLATILE,
'bs': EFI_VARIABLE_BOOTSERVICE_ACCESS,
'rt': EFI_VARIABLE_RUNTIME_ACCESS,
'at': EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS,
- }
- v = 0
- for i in attr.split(','):
v |= attrs[i.lower()]
- return v
+def parse_data(val, vtype):
- fmt = { 'u8': '<B', 'u16': '<H', 'u32': '<L', 'u64': '<Q' }
- if vtype.lower() == 'file':
with open(val, 'rb') as f:
data = f.read()
return data, len(data)
- if vtype.lower() == 'str':
data = val.encode('utf-8') + b'\x00'
return data, len(data)
- i = fmt[vtype.lower()]
- return struct.pack(i, int(val)), struct.calcsize(i)
+def cmd_add(args):
- env = EfiVariableStore(args.infile)
- data, size = parse_data(args.data, args.type)
- if args.name.lower() == 'pk':
env.set_var(guid=EFI_GLOBAL_VARIABLE_GUID, name='PK', data=data, size=size, attr=NV_BS_RT_AT)
- elif args.name.lower() == 'kek':
env.set_var(guid=EFI_GLOBAL_VARIABLE_GUID, name='KEK', data=data, size=size, attr=NV_BS_RT_AT)
- elif args.name.lower() == 'db':
env.set_var(guid=EFI_IMAGE_SECURITY_DATABASE_GUID, name='db', data=data, size=size, attr=NV_BS_RT_AT)
- elif args.name.lower() == 'dbx':
env.set_var(guid=EFI_IMAGE_SECURITY_DATABASE_GUID, name='dbx', data=data, size=size, attr=NV_BS_RT_AT)
- else:
guid = args.guid if args.guid else EFI_GLOBAL_VARIABLE_GUID
attr = parse_attrs(args.attr) if args.attr else NV_BS
env.set_var(guid=guid, name=args.name, data=data, size=size, attr=attr)
- env.save()
+def main():
- ap = argparse.ArgumentParser(description='Generate U-Boot variable store')
- ap.add_argument('--infile', '-i', required=True, help='file to save the UEFI variables')
- subp = ap.add_subparsers(help="sub-command help")
- addp = subp.add_parser('add', help='add UEFI variable')
- addp.add_argument('--name', '-n', required=True, help='variable name')
- addp.add_argument('--attr', '-a', help='variable attributes (default: nv,bs)')
- addp.add_argument('--guid', '-g', help="variable guid (default: %s)"%EFI_GLOBAL_VARIABLE_GUID)
- addp.add_argument('--type', '-t', required=True, help='variable type (values: file|u8|u16|u32|u64|str)')
- addp.add_argument('--data', '-d', required=True, help='variable data')
- addp.set_defaults(func=cmd_add)
- args = ap.parse_args()
- args.func(args)
+if __name__ == '__main__':
- main()