filesystem_uefi

package module
v0.0.0-...-8bad894 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 22, 2026 License: BSD-3-Clause Imports: 16 Imported by: 0

README

go-filesystems/uefi

uefi

Go Reference CI

Pure-Go read/write access to UEFI variable stores in the OVMF/EDK2 NvVar binary format — no root privileges, no external tools, no CGO.

Targets the non-authenticated NvVar store (OVMF_VARS.fd, QEMU_VARS.fd). Typical use case: enrolling Secure Boot keys offline before starting a virtual machine.

Support summary

Feature Status Notes
Open / Close Parses NvVar variable store header
List Returns all valid (non-deleted) variables
Get Lookup by name + GUID
Set Create or replace; rewrites store atomically
Delete Removes a variable; rewrites store atomically
Boot variables BootOrder / Boot#### / BootNext / Timeout semantic layer with EFI_LOAD_OPTION + device-path parse/marshal
Secure Boot enrolment EnrollSecureBootKeys (PK/KEK/db) over EFI_SIGNATURE_LIST
Authenticated writes ⚠️ No Time-based authenticated variables require a signature chain

Module

github.com/go-filesystems/uefi

API

Open returns a Store interface — callers never hold a concrete struct pointer.

Open
func Open(path string) (Store, error)

Store combines VariableStore (UEFI operations) and filesystem.Filesystem (generic adapter).

VariableStore interface
type VariableStore interface {
    Close() error
    List() []Variable
    Get(name string, guid GUID) (Variable, error)
    Set(v Variable) error
    Delete(name string, guid GUID) error
}
Types
type Variable struct {
    Name       string
    GUID       GUID
    Attributes Attributes
    Data       []byte
}

type GUID [16]byte
type Attributes uint32

const (
    AttrNonVolatile                       Attributes = 0x00000001
    AttrBootServiceAccess                 Attributes = 0x00000002
    AttrRuntimeAccess                     Attributes = 0x00000004
    AttrHardwareErrorRecord               Attributes = 0x00000008
    AttrTimeBasedAuthenticatedWriteAccess Attributes = 0x00000020
    AttrAppendWrite                       Attributes = 0x00000040
)

Implements filesystem.Filesystem

Store (the interface returned by Open) satisfies filesystem.Filesystem defined in github.com/go-filesystems/interface. Variable names are mapped to paths; DefaultNamespaceGUID (EFI global variable GUID) is used as namespace.

filesystem.Filesystem method Behaviour
ReadFile(name) Returns Variable.Data for the variable named name
WriteFile(name, data, _) Creates or replaces the variable; perm is ignored
DeleteFile(name) Deletes the variable
ListDir("/") Returns all variables as dir entries
Stat(name) Returns Size = len(Variable.Data), mode 0600
Rename(old, new) Copies variable data to new, then deletes old
MkDir, DeleteDir, ReadLink Return "not supported" error
import (
    filesystem "github.com/go-filesystems/interface"
    fsuefi     "github.com/go-filesystems/uefi"
)

s, _ := fsuefi.Open("OVMF_VARS.fd")
defer s.Close()

var fs filesystem.Filesystem = s
data, _ := fs.ReadFile("BootOrder")

Secure Boot use case

Variables and GUIDs

Secure Boot variables live in two namespaces. GUIDs are exported directly by the package:

Variable Exported GUID constant Content
PK EFIGlobalVariableGUID Platform Key — activates User Mode
KEK EFIGlobalVariableGUID Key Exchange Keys
db EFIImageSecurityDatabaseGUID Allowed certificates / hashes
dbx EFIImageSecurityDatabaseGUID Revoked certificates / hashes
Certificate format: EFI_SIGNATURE_LIST

Each Secure Boot variable's data is an EFI_SIGNATURE_LIST: an EDK2 binary structure that wraps one or more DER-encoded X.509 certificates. The package exposes BuildEFISignatureList to encode one:

Offset  Size  Field
     0    16  SignatureType GUID  ← EFICertX509GUID
    16     4  SignatureListSize
    20     4  SignatureHeaderSize ← always 0 for X.509
    24     4  SignatureSize       ← 16 (owner GUID) + len(certDER)
    28    16  SignatureOwner GUID
    44     n  DER certificate bytes
sl := fsuefi.BuildEFISignatureList(fsuefi.EFIGlobalVariableGUID, certDER)
Enrolling keys into OVMF_VARS.fd

EnrollSecureBootKeys enforces the required order (db → KEK → PK) and handles EFI_SIGNATURE_LIST encoding internally:

store, err := fsuefi.Open("my_vm_vars.fd")
if err != nil { log.Fatal(err) }
defer store.Close()

err = fsuefi.EnrollSecureBootKeys(store, fsuefi.SecureBootKeys{
    PK:  pkDER,  // DER-encoded X.509
    KEK: kekDER,
    DB:  dbDER,
})

Order: db and KEK may be written in any order as long as PK is written last. Once PK is present, OVMF leaves Setup Mode and activates User Mode (Secure Boot enforced). Any subsequent change to PK or KEK must be signed with the previous key.

For the complete guide (key generation, disk image creation, QEMU command lines for x86-64 and arm64): docs/uefi-secure-boot-qemu.md.

Boot variable management

A semantic layer over the raw variable store models the UEFI boot manager: BootOrder, Boot####, BootNext, BootCurrent and Timeout. Boot entries are EFI_LOAD_OPTION records, each carrying an EFI_DEVICE_PATH_PROTOCOL node list that is parsed and marshalled with exact byte round-trip. All parsing is bounds-checked against malicious input via go-volumes/safeio.

store, _ := fsuefi.Open("OVMF_VARS.fd")
defer store.Close()

// Build a Linux/Windows-style \EFI\BOOT\BOOTX64.EFI entry on GPT partition 1.
hd := fsuefi.HardDriveNode{
    PartitionNumber: 1, PartitionStart: 0x800, PartitionSize: 0x100000,
    MBRType: fsuefi.HDMBRTypeGPT, SignatureType: fsuefi.HDSigTypeGUID,
}
lo := &fsuefi.LoadOption{
    Attributes:  fsuefi.LoadOptionActive,
    Description: "UEFI OS",
    DevicePath: []fsuefi.DevicePathNode{
        hd.Node(),
        fsuefi.FilePathNode(`\EFI\BOOT\BOOTX64.EFI`),
    },
}

n, _ := fsuefi.AddBootEntry(store, lo)   // writes Boot#### + appends to BootOrder
fsuefi.SetBootNext(store, n)             // one-shot: boot this entry next time
fsuefi.SetTimeout(store, 5)              // menu timeout, seconds

println(lo.Text()) // HD(1,GPT,...,0x800,0x100000)/File(\EFI\BOOT\BOOTX64.EFI)
Boot-manager API
// Boot order (packed UINT16 LE array).
func BootOrder(store VariableStore) ([]uint16, error)
func SetBootOrder(store VariableStore, order []uint16) error

// Boot#### entries (EFI_LOAD_OPTION).
func BootEntry(store VariableStore, n uint16) (*LoadOption, error)
func SetBootEntry(store VariableStore, n uint16, lo *LoadOption) error
func DeleteBootEntry(store VariableStore, n uint16) error
func AddBootEntry(store VariableStore, lo *LoadOption) (uint16, error)
func ListBootEntries(store VariableStore) (map[uint16]*LoadOption, []uint16, error)

// One-shot, current, and timeout controls.
func BootNext(store VariableStore) (uint16, bool, error)
func SetBootNext(store VariableStore, n uint16) error
func ClearBootNext(store VariableStore) error
func BootCurrent(store VariableStore) (uint16, error)
func Timeout(store VariableStore) (uint16, bool, error)
func SetTimeout(store VariableStore, seconds uint16) error

// EFI_LOAD_OPTION and EFI_DEVICE_PATH_PROTOCOL codecs (exact byte round-trip).
func ParseLoadOption(b []byte) (*LoadOption, error)
func (lo *LoadOption) Marshal() ([]byte, error)
func ParseDevicePath(b []byte) ([]DevicePathNode, error)
func MarshalDevicePath(nodes []DevicePathNode) ([]byte, error)
func DevicePathText(nodes []DevicePathNode) string

Documentation

Overview

Package filesystem_uefi provides read/write access to UEFI variable stores in the OVMF/EDK2 NvVar binary format (non-authenticated variant).

Overview

UEFI firmware stores runtime configuration variables (boot order, Secure Boot keys, timeout, …) in a binary NvVar store on a dedicated pflash chip or in a file (e.g. OVMF_VARS.fd, QEMU_VARS.fd). This package lets you open such a file, enumerate, read, write, and delete variables without requiring root privileges, external binaries, or CGO.

Getting started

import fsuefi "github.com/go-filesystems/uefi/src"

s, err := fsuefi.Open("OVMF_VARS.fd")
if err != nil { log.Fatal(err) }
defer s.Close()

// List all variables
for _, v := range s.List() {
    fmt.Printf("%s  attrs=%#x  size=%d\n", v.Name, v.Attributes, len(v.Data))
}

// Write a variable
err = s.Set(fsuefi.Variable{
    Name:       "MyVar",
    GUID:       fsuefi.DefaultNamespaceGUID,
    Attributes: fsuefi.AttrNonVolatile | fsuefi.AttrBootServiceAccess | fsuefi.AttrRuntimeAccess,
    Data:       []byte{0x01, 0x02},
})

Interfaces

Open returns a Store interface — callers never hold a concrete struct pointer. Store combines two sub-interfaces:

  • VariableStore: UEFI-specific operations (List, Get, Set, Delete).
  • filesystem.Filesystem (from github.com/go-filesystems/interface): generic filesystem operations (ReadFile, WriteFile, ListDir, Stat, …). Variable names are used as paths; DefaultNamespaceGUID is the namespace.

Binary format

The store starts with a VARIABLE_STORE_HEADER (28 bytes) followed by zero or more VARIABLE_HEADERs (32 bytes each). Variable names are encoded as null-terminated UTF-16LE; data payloads follow the name with 4-byte alignment. Deleted variables are skipped on read; writes reconstruct the full file from the surviving variables.

Supported store signatures: EFIVariableGUID (non-authenticated) and EFIAuthenticatedVariableGUID (authenticated). Authenticated write validation (time-based signatures) is not performed.

Secure Boot use case

See docs/uefi-secure-boot-qemu.md in the parent repository for a step-by-step guide to enrolling PK / KEK / db keys into OVMF_VARS.fd and launching a Secure Boot VM under QEMU on x86-64 and arm64.

Package filesystem_uefi provides read/write access to UEFI variable stores in the OVMF/EDK2 NvVar binary format (non-authenticated variant).

Index

Constants

View Source
const (
	LoadOptionActive         uint32 = 0x00000001 // LOAD_OPTION_ACTIVE
	LoadOptionForceReconnect uint32 = 0x00000002 // LOAD_OPTION_FORCE_RECONNECT
	LoadOptionHidden         uint32 = 0x00000008 // LOAD_OPTION_HIDDEN
	LoadOptionCategoryMask   uint32 = 0x00001F00 // LOAD_OPTION_CATEGORY
	LoadOptionCategoryBoot   uint32 = 0x00000000 // LOAD_OPTION_CATEGORY_BOOT
	LoadOptionCategoryApp    uint32 = 0x00000100 // LOAD_OPTION_CATEGORY_APP
)

Load-option attribute flags (EFI_LOAD_OPTION.Attributes, UEFI spec §3.1.3).

View Source
const (
	DevPathTypeHardware  uint8 = 0x01
	DevPathTypeACPI      uint8 = 0x02
	DevPathTypeMessaging uint8 = 0x03
	DevPathTypeMedia     uint8 = 0x04
	DevPathTypeBIOS      uint8 = 0x05
	DevPathTypeEnd       uint8 = 0x7F
)

EFI_DEVICE_PATH_PROTOCOL node Type values (UEFI spec §10.3).

View Source
const (
	DevPathEndInstance uint8 = 0x01 // end of one path instance, more follow
	DevPathEndEntire   uint8 = 0xFF // end of the entire device path
)

SubType values for the End-of-path node (Type 0x7F).

View Source
const (
	HDMBRTypePCAT uint8 = 0x01 // legacy MBR partition table
	HDMBRTypeGPT  uint8 = 0x02 // GUID partition table
)

MBRType values for a Hard Drive (HD) media node.

View Source
const (
	HDSigTypeNone uint8 = 0x00 // no disk signature
	HDSigTypeMBR  uint8 = 0x01 // 4-byte MBR (NT) disk signature
	HDSigTypeGUID uint8 = 0x02 // 16-byte GPT disk GUID
)

SignatureType values for a Hard Drive (HD) media node.

View Source
const (
	// VarsSizeX86_64 is the standard OVMF_VARS.fd size for x86-64 (512 KiB).
	VarsSizeX86_64 = int64(512 * 1024)
	// VarsSizeARM64 is the standard QEMU_VARS.fd size for arm64 (64 MiB).
	VarsSizeARM64 = int64(64 * 1024 * 1024)
)

Standard UEFI variable store sizes used by QEMU firmware images.

View Source
const (
	VariableData   = 0x55AA // VARIABLE_DATA start marker
	StoreFormatted = 0x5A   // VARIABLE_STORE_FORMATTED
	StoreHealthy   = 0xFE   // VARIABLE_STORE_HEALTHY

	VarAdded   = 0x3F // complete, valid variable
	VarDeleted = 0xFD // obsolete/deleted

	StoreHeaderSize   = 28 // sizeof(VARIABLE_STORE_HEADER)
	VarHeaderSize     = 32 // sizeof(VARIABLE_HEADER) — non-auth variant
	AuthVarHeaderSize = 60 // sizeof(AUTHENTICATED_VARIABLE_HEADER) — adds

	// FvHeaderSize is the on-disk size of an EFI_FIRMWARE_VOLUME_HEADER
	// with exactly one block-map entry plus the (0, 0) terminator. Both
	// QEMU's edk2-i386-vars.fd and the OVMF aarch64 NvVar region use this
	// 72-byte layout (HeaderLength field = 0x48).
	FvHeaderSize = 72
)

Binary format constants derived from EDK2 MdeModulePkg/Include/Guid/VariableFormat.h.

Variables

View Source
var (
	// EFIGlobalVariableGUID is the namespace for PK and KEK variables.
	// {8be4df61-93ca-11d2-aa0d-00e098032b8c}
	EFIGlobalVariableGUID = GUID{
		0x61, 0xdf, 0xe4, 0x8b,
		0xca, 0x93,
		0xd2, 0x11,
		0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c,
	}

	// EFIImageSecurityDatabaseGUID is the namespace for db and dbx variables.
	// {d719b2cb-3d3a-4596-a3bc-dad00e67656f}
	EFIImageSecurityDatabaseGUID = GUID{
		0xcb, 0xb2, 0x19, 0xd7,
		0x3a, 0x3d,
		0x96, 0x45,
		0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f,
	}

	// EFICertX509GUID is the SignatureType GUID for X.509 certificates in an
	// EFI_SIGNATURE_LIST. {a5c059a1-94e4-4aa7-87b5-ab155c2bf072}
	EFICertX509GUID = GUID{
		0xa1, 0x59, 0xc0, 0xa5,
		0xe4, 0x94,
		0xa7, 0x4a,
		0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72,
	}
)

Well-known GUIDs for Secure Boot variable management.

View Source
var DefaultNamespaceGUID = GUID{
	0x61, 0xdf, 0xe4, 0x8b,
	0xca, 0x93,
	0xd2, 0x11,
	0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c,
}

DefaultNamespaceGUID is used by the filesystem.Filesystem adapter when no GUID is embedded in the path. It equals EFI_GLOBAL_VARIABLE_GUID.

{8be4df61-93ca-11d2-aa0d-00e098032b8c}
View Source
var EFIAuthenticatedVariableGUID = GUID{
	0x78, 0x2c, 0xf3, 0xaa,
	0x7b, 0x94,
	0x9a, 0x43,
	0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92,
}

EFIAuthenticatedVariableGUID is the store signature GUID for authenticated NvVar stores. {0xaaf32c78, 0x947b, 0x439a, {0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92}}

View Source
var EFICertTypePKCS7GUID = GUID{
	0x9d, 0xd2, 0xaf, 0x4a,
	0xdf, 0x68,
	0xee, 0x49,
	0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7,
}

EFICertTypePKCS7GUID is EFI_CERT_TYPE_PKCS7_GUID, the WIN_CERTIFICATE_UEFI_GUID CertType for a PKCS#7 SignedData payload. {4aafd29d-68df-49ee-8aa9-347d375665a7}

View Source
var EFISystemNvDataFvGUID = GUID{
	0x8d, 0x2b, 0xf1, 0xff,
	0x96, 0x76,
	0x8b, 0x4c,
	0xa9, 0x85, 0x27, 0x47, 0x07, 0x5b, 0x4f, 0x50,
}

EFISystemNvDataFvGUID is the FvHeader.FileSystemGuid used for the NvVar firmware volume in both QEMU x86_64 (edk2-i386-vars.fd) and QEMU aarch64 (the varstore region of edk2-aarch64-code.fd / QEMU_VARS.fd) prebuilt OVMF images. EDK2 calls it `gEfiSystemNvDataFvGuid`.

{FFF12B8D-7696-4C8B-A985-2747075B4F50}

Wire encoding follows the standard EFI mixed-endian rule:

Data1 (uint32, LE)   = 0xFFF12B8D  →  bytes 8d 2b f1 ff
Data2 (uint16, LE)   = 0x7696      →  bytes 96 76
Data3 (uint16, LE)   = 0x4C8B      →  bytes 8b 4c
Data4 (8 raw bytes)                 →  a9 85 27 47 07 5b 4f 50
View Source
var EFIVariableGUID = GUID{
	0x16, 0x36, 0xcf, 0xdd,
	0x75, 0x32,
	0x64, 0x41,
	0x98, 0xb6, 0xfe, 0x85, 0x70, 0x7f, 0xfe, 0x7d,
}

EFIVariableGUID is the store signature GUID for non-authenticated NvVar stores. {0xddcf3616, 0x3275, 0x4164, {0x98, 0xb6, 0xfe, 0x85, 0x70, 0x7f, 0xfe, 0x7d}}

Functions

func AddBootEntry

func AddBootEntry(store VariableStore, lo *LoadOption) (uint16, error)

AddBootEntry writes lo to the lowest free Boot#### number, appends that number to the end of BootOrder, and returns the assigned number.

func BootCurrent

func BootCurrent(store VariableStore) (uint16, error)

BootCurrent returns the entry number the firmware booted from this session (the read-only BootCurrent variable). Returns an error if it is absent.

func BootNext

func BootNext(store VariableStore) (uint16, bool, error)

BootNext returns the one-shot next-boot entry number and ok=true if the BootNext variable is set.

func BootOrder

func BootOrder(store VariableStore) ([]uint16, error)

BootOrder returns the platform boot order: the packed UINT16 LE array stored in the BootOrder variable. A missing BootOrder yields an empty slice.

func BuildAuthentication2

func BuildAuthentication2(name string, guid GUID, attrs Attributes, data []byte, ts EFITime, signer AuthSigner) ([]byte, error)

BuildAuthentication2 constructs the serialized EFI_VARIABLE_AUTHENTICATION_2 descriptor for an authenticated write of variable name/guid carrying attrs and data, signed by signer at timestamp ts.

The returned bytes are the descriptor only (EFI_TIME + WIN_CERTIFICATE_UEFI_GUID); to perform the write, prepend them to data — see WriteAuthenticatedVariable.

attrs must include AttrTimeBasedAuthenticatedWriteAccess; the signature is bound to the exact attribute value, so the caller must pass the same attrs it will write.

func BuildEFISignatureList

func BuildEFISignatureList(ownerGUID GUID, certDER []byte) []byte

BuildEFISignatureList encodes a single DER-encoded X.509 certificate into an EFI_SIGNATURE_LIST binary blob suitable for writing into PK, KEK, db or dbx.

ownerGUID identifies the entity that owns the certificate (e.g. EFIGlobalVariableGUID for vendor keys). certDER must be a valid DER-encoded X.509 certificate; no structural validation is performed.

Layout (all little-endian):

 0-15  SignatureType GUID (EFICertX509GUID)
16-19  SignatureListSize
20-23  SignatureHeaderSize (always 0 for X.509)
24-27  SignatureSize = 16 + len(certDER)
28-43  SignatureOwner GUID
44-…   certDER bytes

func ClearBootNext

func ClearBootNext(store VariableStore) error

ClearBootNext deletes the BootNext variable. A missing BootNext is not an error (the one-shot is simply already clear).

func DecodeUTF16LE

func DecodeUTF16LE(b []byte) (string, error)

DecodeUTF16LE converts a UTF-16LE byte slice (may include null terminator) to a Go string.

func DeleteBootEntry

func DeleteBootEntry(store VariableStore, n uint16) error

DeleteBootEntry removes the Boot#### variable. It does NOT touch BootOrder; use SetBootOrder to drop the number from the order if desired.

func DevicePathText

func DevicePathText(nodes []DevicePathNode) string

DevicePathText renders a node list in the canonical UEFI text form, e.g.:

PciRoot(0x0)/Pci(0x1,0x0)/HD(1,GPT,<guid>,0x800,0x100000)/File(\EFI\BOOT\BOOTX64.EFI)

Typed renderers are emitted for the common production nodes (Hardware/PCI, ACPI, Messaging MAC/IPv4/IPv6, Media HD/File/CD-ROM); any other node is rendered generically as Path(<type>,<subtype>,<hexdata>) so the text form is always total and never loses information about node identity.

func EncodeUTF16LE

func EncodeUTF16LE(s string) []byte

EncodeUTF16LE converts a Go string to UTF-16LE bytes with null terminator.

func EnrollSecureBootKeys

func EnrollSecureBootKeys(store VariableStore, keys SecureBootKeys) error

EnrollSecureBootKeys writes PK, KEK and db into the variable store at store. Variables are enrolled in the order db → KEK → PK: writing PK last causes OVMF to leave Setup Mode and activate Secure Boot (User Mode). After that, any PK or KEK change must be signed with the previous key.

func ListBootEntries

func ListBootEntries(store VariableStore) (map[uint16]*LoadOption, []uint16, error)

ListBootEntries returns every parseable Boot#### entry, keyed by number, and a slice of numbers in BootOrder sequence (BootOrder entries first, in order; any Boot#### present in the store but absent from BootOrder is appended in ascending numeric order). Entries whose load option fails to parse are skipped rather than aborting the whole listing.

func MarshalDevicePath

func MarshalDevicePath(nodes []DevicePathNode) ([]byte, error)

MarshalDevicePath encodes a node list back to its on-disk byte form, appending the entire-path End node (0x7F/0xFF, length 4). It is the exact inverse of ParseDevicePath: MarshalDevicePath(ParseDevicePath(b)) == b for any well-formed b that ends in a single entire-path terminator.

func SetBootCurrent

func SetBootCurrent(store VariableStore, n uint16) error

SetBootCurrent writes the BootCurrent variable. Firmware sets this itself in practice; the setter exists for crafting test fixtures and mock stores.

func SetBootEntry

func SetBootEntry(store VariableStore, n uint16, lo *LoadOption) error

SetBootEntry writes a load option into Boot####.

func SetBootNext

func SetBootNext(store VariableStore, n uint16) error

SetBootNext sets the one-shot BootNext entry number.

func SetBootOrder

func SetBootOrder(store VariableStore, order []uint16) error

SetBootOrder writes the BootOrder variable from a UINT16 slice.

func SetTimeout

func SetTimeout(store VariableStore, seconds uint16) error

SetTimeout writes the boot-manager menu timeout in seconds.

func Timeout

func Timeout(store VariableStore) (uint16, bool, error)

Timeout returns the boot-manager menu timeout in seconds (the Timeout variable). Returns ok=false if unset.

func WriteAuthenticatedVariable

func WriteAuthenticatedVariable(s VariableStore, name string, guid GUID, attrs Attributes, data []byte, ts EFITime, signer AuthSigner) error

WriteAuthenticatedVariable performs a time-based authenticated write of a Secure Boot variable into store. It builds the EFI_VARIABLE_AUTHENTICATION_2 descriptor, signs the time-to-be-signed payload with signer, prepends the descriptor to data and writes the result with the AttrTimeBasedAuthenticatedWriteAccess attribute set.

The resulting on-disk variable Data is exactly what edk2's VariableServicesSmm expects from a SetVariable() call with an EFI_VARIABLE_AUTHENTICATION_2 prefix: [descriptor || data].

This constructs and signs the payload only; the store backend does not itself verify the signature (firmware does that at runtime). attrs, if zero, defaults to the standard Secure Boot attribute set plus the time-based-auth flag.

Types

type Attributes

type Attributes uint32

Attributes holds EFI variable attribute flags.

const (
	AttrNonVolatile                       Attributes = 0x00000001
	AttrBootServiceAccess                 Attributes = 0x00000002
	AttrRuntimeAccess                     Attributes = 0x00000004
	AttrHardwareErrorRecord               Attributes = 0x00000008
	AttrTimeBasedAuthenticatedWriteAccess Attributes = 0x00000020
	AttrAppendWrite                       Attributes = 0x00000040
)

type AuthSigner

type AuthSigner struct {
	// Key is the signing private key. Only *rsa.PrivateKey is supported,
	// which is what every shipping Secure Boot tool (sbsign, efitools) and
	// edk2's authenticated SetVariable verifier accept.
	Key *rsa.PrivateKey
	// Cert is the signer certificate, included in the PKCS#7 SignedData.
	Cert *x509.Certificate
}

AuthSigner holds the private key and certificate used to sign an authenticated variable write. The certificate's public key must match the private key, and the certificate (or its issuer chain) must be enrolled in the relevant key database (PK signs PK/KEK, KEK signs db/dbx, …).

type Authentication2

type Authentication2 struct {
	// TimeStamp is the EFI_TIME the signature was bound to.
	TimeStamp EFITime
	// CertType is the WIN_CERTIFICATE_UEFI_GUID CertType (PKCS#7 for Secure Boot).
	CertType GUID
	// PKCS7 is the raw DER PKCS#7 SignedData blob (CertData).
	PKCS7 []byte
}

Authentication2 is a parsed EFI_VARIABLE_AUTHENTICATION_2 descriptor.

func ParseAuthentication2

func ParseAuthentication2(b []byte) (Authentication2, error)

ParseAuthentication2 decodes a serialized EFI_VARIABLE_AUTHENTICATION_2 descriptor produced by BuildAuthentication2. It validates the WIN_CERTIFICATE header fields and returns the timestamp, cert type and the embedded PKCS#7 blob. It does not verify the signature.

type DevicePathNode

type DevicePathNode struct {
	Type    uint8
	SubType uint8
	Data    []byte
}

DevicePathNode is a single EFI_DEVICE_PATH_PROTOCOL node. Data holds the node-specific bytes that follow the 4-byte header (i.e. everything after Type/SubType/Length). Unknown node types are preserved losslessly via this generic representation.

func FilePathNode

func FilePathNode(path string) DevicePathNode

FilePathNode builds a Media/File Path node (Type 0x04, SubType 0x04) whose Data is the UCS-2 (UTF-16LE) NUL-terminated path, e.g. \EFI\BOOT\BOOTX64.EFI.

func ParseDevicePath

func ParseDevicePath(b []byte) ([]DevicePathNode, error)

ParseDevicePath decodes a complete EFI_DEVICE_PATH_PROTOCOL node list from b. Parsing stops after the entire-path End node (Type 0x7F, SubType 0xFF); the returned slice does NOT include that terminator (MarshalDevicePath re-appends it). The input is treated as untrusted: every length field is bounds-checked via safeio so malformed or truncated data yields an error rather than a panic or out-of-bounds read.

func (DevicePathNode) FilePath

func (n DevicePathNode) FilePath() (string, bool)

FilePath returns the UCS-2 path of a Media/File Path node, or false if n is not a file-path node (or its data is malformed).

func (DevicePathNode) HardDrive

func (n DevicePathNode) HardDrive() (HardDriveNode, bool)

HardDrive returns the typed HD view of n, or false if n is not a well-formed Media/Hard Drive node.

func (DevicePathNode) Length

func (n DevicePathNode) Length() int

Length returns the on-disk node length (header + Data), as written into the 2-byte Length field.

type EFITime

type EFITime struct {
	Year       uint16
	Month      uint8
	Day        uint8
	Hour       uint8
	Minute     uint8
	Second     uint8
	Nanosecond uint32
	TimeZone   int16
	Daylight   uint8
}

EFITime mirrors EFI_TIME. Only the fields UEFI authenticated writes care about are populated; Nanosecond/TimeZone/Daylight are written as zero, which is what edk2 SetVariable produces for an authenticated payload.

func EFITimeFromGoTime

func EFITimeFromGoTime(t time.Time) EFITime

EFITimeFromGoTime converts a Go time (interpreted in UTC) into the EFI_TIME fields UEFI authenticated writes use. Sub-second precision and time-zone offsets are dropped to zero, matching edk2's authenticated SetVariable.

func (EFITime) Marshal

func (e EFITime) Marshal() []byte

Marshal encodes EFI_TIME into its 16-byte on-disk layout (little-endian):

0..2   Year
2      Month
3      Day
4      Hour
5      Minute
6      Second
7      Pad1
8..12  Nanosecond
12..14 TimeZone (int16)
14     Daylight
15     Pad2

type GUID

type GUID [16]byte

GUID represents a 16-byte EFI GUID stored in mixed-endian on-disk format. Bytes 0-3: Data1 LE, 4-5: Data2 LE, 6-7: Data3 LE, 8-15: Data4 big-endian.

type HardDriveNode

type HardDriveNode struct {
	PartitionNumber uint32
	PartitionStart  uint64 // starting LBA
	PartitionSize   uint64 // size in LBAs
	Signature       [16]byte
	MBRType         uint8 // HDMBRTypePCAT or HDMBRTypeGPT
	SignatureType   uint8 // HDSigTypeNone / HDSigTypeMBR / HDSigTypeGUID
}

HardDriveNode describes a Media/Hard Drive (HD) device-path node — the typed view of the partition that a load option boots from.

func (HardDriveNode) Node

func (hd HardDriveNode) Node() DevicePathNode

HardDriveNodeFrom builds the on-disk Media/HD node for hd. The fixed 42-byte layout (UEFI spec Table 10.x) is:

 0..4   PartitionNumber (u32 LE)
 4..12  PartitionStart  (u64 LE)
12..20  PartitionSize   (u64 LE)
20..36  Signature       (16 raw bytes)
36      MBRType
37      SignatureType

type LoadOption

type LoadOption struct {
	Attributes   uint32
	Description  string
	DevicePath   []DevicePathNode
	OptionalData []byte
}

LoadOption is a parsed EFI_LOAD_OPTION — the value of a Boot#### (or Driver####/SysPrep####) variable. On-disk layout (UEFI spec §3.1.3, all multi-byte fields little-endian):

0..4              Attributes        (u32 LE)
4..6              FilePathListLength (u16 LE) — byte length of DevicePath
6..               Description       (UCS-2, NUL-terminated)
(after desc)      DevicePath        (FilePathListLength bytes)
(remaining)       OptionalData      (everything left over)

func BootEntry

func BootEntry(store VariableStore, n uint16) (*LoadOption, error)

BootEntry returns the parsed EFI_LOAD_OPTION stored in Boot####.

func ParseLoadOption

func ParseLoadOption(b []byte) (*LoadOption, error)

ParseLoadOption decodes an EFI_LOAD_OPTION from untrusted bytes. Every length field is validated with safeio so malformed/truncated input yields a graceful error rather than a panic or out-of-bounds read.

func (*LoadOption) Marshal

func (lo *LoadOption) Marshal() ([]byte, error)

Marshal encodes the load option to its exact on-disk byte form. It is the inverse of ParseLoadOption: Marshal(ParseLoadOption(b)) == b for any well-formed b.

func (*LoadOption) Text

func (lo *LoadOption) Text() string

Text renders the load option's device path in canonical UEFI text form.

type OVMFFlavor

type OVMFFlavor int

OVMFFlavor selects between the two observed OVMF varstore layouts.

const (
	// OVMFX86_64 — the layout used by QEMU's edk2-i386-vars.fd. FvLength
	// equals the file size; BlockMap is a single span covering the same
	// range; the NvVar store fills the FV minus the 72-byte header.
	// Typical file size: 512 KiB.
	OVMFX86_64 OVMFFlavor = iota

	// OVMFAArch64 — the layout used by QEMU's ArmVirt prebuilt. The
	// pflash slot is large (64 MiB on Homebrew QEMU) but the firmware
	// only uses 768 KiB at offset 0 as the NvVar FV, split as
	// 256 KiB (Variable region) + 256 KiB (FTW working) + 256 KiB (FTW
	// spare). FvLength is fixed at 0xC0000, BlockMap covers the entire
	// pflash slot, and the NvVar VARIABLE_STORE_HEADER.Size is
	// 0x3FFB8 (256 KiB − the 72-byte FV header).
	OVMFAArch64
)

type SecureBootKeys

type SecureBootKeys struct {
	// PK is the Platform Key certificate. Writing PK last activates User Mode.
	PK []byte
	// KEK is the Key Exchange Key certificate.
	KEK []byte
	// DB is the allowed-signatures database certificate.
	DB []byte
}

SecureBootKeys holds the three certificates required to activate Secure Boot. All certificates must be DER-encoded X.509.

type Store

type Store interface {
	VariableStore
	filesystem.Filesystem
}

Store is the full interface returned by Open. It combines UEFI variable operations (VariableStore) with the generic filesystem.Filesystem surface, allowing the store to be used by tooling that operates on any filesystem driver in this repository.

func Format

func Format(path string, sizeBytes int64) (Store, error)

Format creates a new empty UEFI NvVar variable store file at path with the given total size, in the legacy *raw non-auth* format: signature GUID at offset 0, no FV wrapper, non-authenticated variable records.

Use FormatOVMF instead when targeting a real QEMU OVMF varstore on either x86_64 or aarch64 — both prebuilts (edk2-i386-vars.fd and the NvVar region of edk2-aarch64-code.fd) use the FV-wrapped + auth layout, and OVMF will reformat any other layout on first boot.

The returned Store is open and ready for use; call Close when done. sizeBytes must be at least StoreHeaderSize + 4 bytes.

func FormatOVMF

func FormatOVMF(path string, sizeBytes int64, flavor OVMFFlavor) (Store, error)

FormatOVMF creates a new empty UEFI NvVar variable store file at path in the format real QEMU OVMF prebuilts use: a 72-byte EFI_FIRMWARE_VOLUME header at offset 0, a VARIABLE_STORE_HEADER with the EFIAuthenticatedVariableGUID signature at offset 72, and 0xFF padding (erased flash) for the rest of the FV.

flavor picks the FV layout — see OVMFFlavor constants. Use OVMFX86_64 for OVMF_VARS.fd and OVMFAArch64 for QEMU_VARS.fd (ArmVirt).

sizeBytes is the full file size to produce — for x86_64 this equals the FV size; for aarch64 it's typically 64 MiB (the pflash slot) and only the first 768 KiB is the FV.

Variables created via Set on the returned Store are written with the 60-byte AUTHENTICATED_VARIABLE_HEADER (MonotonicCount + TimeStamp + PubKeyIndex zeros), matching how OVMF itself stores non-authenticated SetVariable calls inside an auth-format store.

func Open

func Open(path string) (Store, error)

Open opens a UEFI variable store file and parses its contents. It returns a Store, which combines VariableStore and filesystem.Filesystem.

Auto-detects the on-disk format: raw NvVar vs FV-wrapped, and authenticated vs non-authenticated variable records. The detection is sticky — flush() preserves whatever flavor was found at Open time so the file stays compatible with the firmware that owns it.

type Variable

type Variable struct {
	Name       string
	GUID       GUID
	Attributes Attributes
	Data       []byte
}

Variable is a single parsed UEFI variable.

type VariableStore

type VariableStore interface {
	// Close releases any resources held by the store.
	Close() error
	// List returns a copy of all valid (non-deleted) variables in the store.
	List() []Variable
	// Get retrieves a variable by name and GUID namespace.
	Get(name string, guid GUID) (Variable, error)
	// Set creates or replaces a variable; the backing file is rewritten.
	Set(v Variable) error
	// Delete removes a variable by name and GUID; returns an error if not found.
	Delete(name string, guid GUID) error
}

VariableStore provides read/write access to a UEFI NvVar variable store. All write operations (Set, Delete) serialize through the backing file.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL