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) <p...@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 <palcant...@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()


Reply via email to