All of lore.kernel.org
 help / color / mirror / Atom feed
From: Paulo Alcantara <pc@cjr.nz>
To: u-boot@lists.denx.de
Subject: [PATCH] tools: add a simple script to generate EFI variables
Date: Mon, 30 Nov 2020 12:16:34 -0300	[thread overview]
Message-ID: <20201130151634.3927-1-pc@cjr.nz> (raw)

This script generates EFI variables for U-Boot variable store format.

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()
-- 
2.29.2

             reply	other threads:[~2020-11-30 15:16 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-11-30 15:16 Paulo Alcantara [this message]
2020-11-30 16:38 ` [PATCH] tools: add a simple script to generate EFI variables Heinrich Schuchardt
2020-11-30 18:26   ` Paulo Alcantara
2020-12-01 22:58 ` [PATCH v2] " Paulo Alcantara
2020-12-02 10:50   ` Heinrich Schuchardt
2020-12-02 16:46 ` [PATCH v3] " Paulo Alcantara
2020-12-08 23:10   ` [PATCH v4] " Paulo Alcantara
2020-12-20 10:52     ` Heinrich Schuchardt
2020-12-22 13:35       ` Paulo Alcantara

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201130151634.3927-1-pc@cjr.nz \
    --to=pc@cjr.nz \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.