UEFI Variable with Signature List: Nim parsing library

Parse UEFI variables db and dbx that contain signatures, certificates and hashes. On a Linux system using UEFI, these variables are readable from /sys/firmware/efi/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f, /sys/firmware/efi/efivars/dbDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c, /sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f and /sys/firmware/efi/efivars/dbxDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c. ("d719b2cb-3d3a-4596-a3bc-dad00e67656f" is defined as EFI_IMAGE_SECURITY_DATABASE_GUID and "8be4df61-93ca-11d2-aa0d-00e098032b8c" as EFI_GLOBAL_VARIABLE). Each file contains an EFI attribute (32-bit integer) followed by a list of EFI_SIGNATURE_LIST structures.

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of UEFI Variable with Signature List using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Nim source code to parse UEFI Variable with Signature List

efivar_signature_list.nim

import kaitai_struct_nim_runtime
import options

type
  EfivarSignatureList* = ref object of KaitaiStruct
    `varAttributes`*: EfivarSignatureList_EfiVarAttr
    `signatures`*: seq[EfivarSignatureList_SignatureList]
    `parent`*: KaitaiStruct
  EfivarSignatureList_SignatureList* = ref object of KaitaiStruct
    `signatureType`*: seq[byte]
    `lenSignatureList`*: uint32
    `lenSignatureHeader`*: uint32
    `lenSignature`*: uint32
    `header`*: seq[byte]
    `signatures`*: seq[EfivarSignatureList_SignatureData]
    `parent`*: EfivarSignatureList
    `rawSignatures`*: seq[seq[byte]]
    `isCertSha512X509Inst`: bool
    `isCertSha512X509InstFlag`: bool
    `isCertSha224Inst`: bool
    `isCertSha224InstFlag`: bool
    `isCertX509Inst`: bool
    `isCertX509InstFlag`: bool
    `isCertSha256X509Inst`: bool
    `isCertSha256X509InstFlag`: bool
    `isCertRsa2048KeyInst`: bool
    `isCertRsa2048KeyInstFlag`: bool
    `isCertSha512Inst`: bool
    `isCertSha512InstFlag`: bool
    `isCertSha384Inst`: bool
    `isCertSha384InstFlag`: bool
    `isCertSha1Inst`: bool
    `isCertSha1InstFlag`: bool
    `isCertRsa2048Sha1Inst`: bool
    `isCertRsa2048Sha1InstFlag`: bool
    `isCertSha256Inst`: bool
    `isCertSha256InstFlag`: bool
    `isCertSha384X509Inst`: bool
    `isCertSha384X509InstFlag`: bool
    `isCertRsa2048Sha256Inst`: bool
    `isCertRsa2048Sha256InstFlag`: bool
    `isCertDerPkcs7Inst`: bool
    `isCertDerPkcs7InstFlag`: bool
  EfivarSignatureList_SignatureData* = ref object of KaitaiStruct
    `owner`*: seq[byte]
    `data`*: seq[byte]
    `parent`*: EfivarSignatureList_SignatureList
  EfivarSignatureList_EfiVarAttr* = ref object of KaitaiStruct
    `enhancedAuthenticatedAccess`*: bool
    `appendWrite`*: bool
    `timeBasedAuthenticatedWriteAccess`*: bool
    `authenticatedWriteAccess`*: bool
    `hardwareErrorRecord`*: bool
    `runtimeAccess`*: bool
    `bootserviceAccess`*: bool
    `nonVolatile`*: bool
    `reserved1`*: uint64
    `parent`*: EfivarSignatureList

proc read*(_: typedesc[EfivarSignatureList], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): EfivarSignatureList
proc read*(_: typedesc[EfivarSignatureList_SignatureList], io: KaitaiStream, root: KaitaiStruct, parent: EfivarSignatureList): EfivarSignatureList_SignatureList
proc read*(_: typedesc[EfivarSignatureList_SignatureData], io: KaitaiStream, root: KaitaiStruct, parent: EfivarSignatureList_SignatureList): EfivarSignatureList_SignatureData
proc read*(_: typedesc[EfivarSignatureList_EfiVarAttr], io: KaitaiStream, root: KaitaiStruct, parent: EfivarSignatureList): EfivarSignatureList_EfiVarAttr

proc isCertSha512X509*(this: EfivarSignatureList_SignatureList): bool
proc isCertSha224*(this: EfivarSignatureList_SignatureList): bool
proc isCertX509*(this: EfivarSignatureList_SignatureList): bool
proc isCertSha256X509*(this: EfivarSignatureList_SignatureList): bool
proc isCertRsa2048Key*(this: EfivarSignatureList_SignatureList): bool
proc isCertSha512*(this: EfivarSignatureList_SignatureList): bool
proc isCertSha384*(this: EfivarSignatureList_SignatureList): bool
proc isCertSha1*(this: EfivarSignatureList_SignatureList): bool
proc isCertRsa2048Sha1*(this: EfivarSignatureList_SignatureList): bool
proc isCertSha256*(this: EfivarSignatureList_SignatureList): bool
proc isCertSha384X509*(this: EfivarSignatureList_SignatureList): bool
proc isCertRsa2048Sha256*(this: EfivarSignatureList_SignatureList): bool
proc isCertDerPkcs7*(this: EfivarSignatureList_SignatureList): bool


##[
Parse UEFI variables db and dbx that contain signatures, certificates and
hashes. On a Linux system using UEFI, these variables are readable from
/sys/firmware/efi/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f,
/sys/firmware/efi/efivars/dbDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c,
/sys/firmware/efi/efivars/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f and
/sys/firmware/efi/efivars/dbxDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c.
("d719b2cb-3d3a-4596-a3bc-dad00e67656f" is defined as
EFI_IMAGE_SECURITY_DATABASE_GUID and "8be4df61-93ca-11d2-aa0d-00e098032b8c"
as EFI_GLOBAL_VARIABLE).
Each file contains an EFI attribute (32-bit integer) followed by a list of
EFI_SIGNATURE_LIST structures.

@see <a href="https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf">Source</a>
]##
proc read*(_: typedesc[EfivarSignatureList], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): EfivarSignatureList =
  template this: untyped = result
  this = new(EfivarSignatureList)
  let root = if root == nil: cast[EfivarSignatureList](this) else: cast[EfivarSignatureList](root)
  this.io = io
  this.root = root
  this.parent = parent


  ##[
  Attributes of the UEFI variable
  ]##
  let varAttributesExpr = EfivarSignatureList_EfiVarAttr.read(this.io, this.root, this)
  this.varAttributes = varAttributesExpr
  block:
    var i: int
    while not this.io.isEof:
      let it = EfivarSignatureList_SignatureList.read(this.io, this.root, this)
      this.signatures.add(it)
      inc i

proc fromFile*(_: typedesc[EfivarSignatureList], filename: string): EfivarSignatureList =
  EfivarSignatureList.read(newKaitaiFileStream(filename), nil, nil)


##[
@see "EFI_SIGNATURE_LIST"
]##
proc read*(_: typedesc[EfivarSignatureList_SignatureList], io: KaitaiStream, root: KaitaiStruct, parent: EfivarSignatureList): EfivarSignatureList_SignatureList =
  template this: untyped = result
  this = new(EfivarSignatureList_SignatureList)
  let root = if root == nil: cast[EfivarSignatureList](this) else: cast[EfivarSignatureList](root)
  this.io = io
  this.root = root
  this.parent = parent


  ##[
  Type of the signature as a GUID
  ]##
  let signatureTypeExpr = this.io.readBytes(int(16))
  this.signatureType = signatureTypeExpr

  ##[
  Total size of the signature list, including this header
  ]##
  let lenSignatureListExpr = this.io.readU4le()
  this.lenSignatureList = lenSignatureListExpr

  ##[
  Size of the signature header which precedes the array of signatures
  ]##
  let lenSignatureHeaderExpr = this.io.readU4le()
  this.lenSignatureHeader = lenSignatureHeaderExpr

  ##[
  Size of each signature
  ]##
  let lenSignatureExpr = this.io.readU4le()
  this.lenSignature = lenSignatureExpr

  ##[
  Header before the array of signatures
  ]##
  let headerExpr = this.io.readBytes(int(this.lenSignatureHeader))
  this.header = headerExpr

  ##[
  An array of signatures
  ]##
  if this.lenSignature > 0:
    for i in 0 ..< int((((this.lenSignatureList - this.lenSignatureHeader) - 28) div this.lenSignature)):
      let buf = this.io.readBytes(int(this.lenSignature))
      this.rawSignatures.add(buf)
      let rawSignaturesIo = newKaitaiStream(buf)
      let it = EfivarSignatureList_SignatureData.read(rawSignaturesIo, this.root, this)
      this.signatures.add(it)

proc isCertSha512X509(this: EfivarSignatureList_SignatureList): bool = 

  ##[
  SHA512 hash of an X.509 certificate's To-Be-Signed contents, and a time of revocation
  @see "EFI_CERT_X509_SHA512_GUID"
  ]##
  if this.isCertSha512X509InstFlag:
    return this.isCertSha512X509Inst
  let isCertSha512X509InstExpr = bool(this.signatureType == @[99'u8, 191'u8, 109'u8, 68'u8, 2'u8, 37'u8, 218'u8, 76'u8, 188'u8, 250'u8, 36'u8, 101'u8, 210'u8, 176'u8, 254'u8, 157'u8])
  this.isCertSha512X509Inst = isCertSha512X509InstExpr
  this.isCertSha512X509InstFlag = true
  return this.isCertSha512X509Inst

proc isCertSha224(this: EfivarSignatureList_SignatureList): bool = 

  ##[
  SHA-224 hash
  @see "EFI_CERT_SHA224_GUID"
  ]##
  if this.isCertSha224InstFlag:
    return this.isCertSha224Inst
  let isCertSha224InstExpr = bool(this.signatureType == @[51'u8, 82'u8, 110'u8, 11'u8, 92'u8, 166'u8, 201'u8, 68'u8, 148'u8, 7'u8, 217'u8, 171'u8, 131'u8, 191'u8, 200'u8, 189'u8])
  this.isCertSha224Inst = isCertSha224InstExpr
  this.isCertSha224InstFlag = true
  return this.isCertSha224Inst

proc isCertX509(this: EfivarSignatureList_SignatureList): bool = 

  ##[
  X.509 certificate
  @see "EFI_CERT_X509_GUID"
  ]##
  if this.isCertX509InstFlag:
    return this.isCertX509Inst
  let isCertX509InstExpr = bool(this.signatureType == @[161'u8, 89'u8, 192'u8, 165'u8, 228'u8, 148'u8, 167'u8, 74'u8, 135'u8, 181'u8, 171'u8, 21'u8, 92'u8, 43'u8, 240'u8, 114'u8])
  this.isCertX509Inst = isCertX509InstExpr
  this.isCertX509InstFlag = true
  return this.isCertX509Inst

proc isCertSha256X509(this: EfivarSignatureList_SignatureList): bool = 

  ##[
  SHA256 hash of an X.509 certificate's To-Be-Signed contents, and a time of revocation
  @see "EFI_CERT_X509_SHA256_GUID"
  ]##
  if this.isCertSha256X509InstFlag:
    return this.isCertSha256X509Inst
  let isCertSha256X509InstExpr = bool(this.signatureType == @[146'u8, 164'u8, 210'u8, 59'u8, 192'u8, 150'u8, 121'u8, 64'u8, 180'u8, 32'u8, 252'u8, 249'u8, 142'u8, 241'u8, 3'u8, 237'u8])
  this.isCertSha256X509Inst = isCertSha256X509InstExpr
  this.isCertSha256X509InstFlag = true
  return this.isCertSha256X509Inst

proc isCertRsa2048Key(this: EfivarSignatureList_SignatureList): bool = 

  ##[
  RSA-2048 key (only the modulus since the public key exponent is known to be 0x10001)
  @see "EFI_CERT_RSA2048_GUID"
  ]##
  if this.isCertRsa2048KeyInstFlag:
    return this.isCertRsa2048KeyInst
  let isCertRsa2048KeyInstExpr = bool(this.signatureType == @[232'u8, 102'u8, 87'u8, 60'u8, 156'u8, 38'u8, 52'u8, 78'u8, 170'u8, 20'u8, 237'u8, 119'u8, 110'u8, 133'u8, 179'u8, 182'u8])
  this.isCertRsa2048KeyInst = isCertRsa2048KeyInstExpr
  this.isCertRsa2048KeyInstFlag = true
  return this.isCertRsa2048KeyInst

proc isCertSha512(this: EfivarSignatureList_SignatureList): bool = 

  ##[
  SHA-512 hash
  @see "EFI_CERT_SHA512_GUID"
  ]##
  if this.isCertSha512InstFlag:
    return this.isCertSha512Inst
  let isCertSha512InstExpr = bool(this.signatureType == @[174'u8, 15'u8, 62'u8, 9'u8, 196'u8, 166'u8, 80'u8, 79'u8, 159'u8, 27'u8, 212'u8, 30'u8, 43'u8, 137'u8, 193'u8, 154'u8])
  this.isCertSha512Inst = isCertSha512InstExpr
  this.isCertSha512InstFlag = true
  return this.isCertSha512Inst

proc isCertSha384(this: EfivarSignatureList_SignatureList): bool = 

  ##[
  SHA-384 hash
  @see "EFI_CERT_SHA384_GUID"
  ]##
  if this.isCertSha384InstFlag:
    return this.isCertSha384Inst
  let isCertSha384InstExpr = bool(this.signatureType == @[7'u8, 83'u8, 62'u8, 255'u8, 208'u8, 159'u8, 201'u8, 72'u8, 133'u8, 241'u8, 138'u8, 213'u8, 108'u8, 112'u8, 30'u8, 1'u8])
  this.isCertSha384Inst = isCertSha384InstExpr
  this.isCertSha384InstFlag = true
  return this.isCertSha384Inst

proc isCertSha1(this: EfivarSignatureList_SignatureList): bool = 

  ##[
  SHA-1 hash
  @see "EFI_CERT_SHA1_GUID"
  ]##
  if this.isCertSha1InstFlag:
    return this.isCertSha1Inst
  let isCertSha1InstExpr = bool(this.signatureType == @[18'u8, 165'u8, 108'u8, 130'u8, 16'u8, 207'u8, 201'u8, 74'u8, 177'u8, 135'u8, 190'u8, 1'u8, 73'u8, 102'u8, 49'u8, 189'u8])
  this.isCertSha1Inst = isCertSha1InstExpr
  this.isCertSha1InstFlag = true
  return this.isCertSha1Inst

proc isCertRsa2048Sha1(this: EfivarSignatureList_SignatureList): bool = 

  ##[
  RSA-2048 signature of a SHA-1 hash
  @see "EFI_CERT_RSA2048_SHA1_GUID"
  ]##
  if this.isCertRsa2048Sha1InstFlag:
    return this.isCertRsa2048Sha1Inst
  let isCertRsa2048Sha1InstExpr = bool(this.signatureType == @[79'u8, 68'u8, 248'u8, 103'u8, 67'u8, 135'u8, 241'u8, 72'u8, 163'u8, 40'u8, 30'u8, 170'u8, 184'u8, 115'u8, 96'u8, 128'u8])
  this.isCertRsa2048Sha1Inst = isCertRsa2048Sha1InstExpr
  this.isCertRsa2048Sha1InstFlag = true
  return this.isCertRsa2048Sha1Inst

proc isCertSha256(this: EfivarSignatureList_SignatureList): bool = 

  ##[
  SHA-256 hash
  @see "EFI_CERT_SHA256_GUID"
  ]##
  if this.isCertSha256InstFlag:
    return this.isCertSha256Inst
  let isCertSha256InstExpr = bool(this.signatureType == @[38'u8, 22'u8, 196'u8, 193'u8, 76'u8, 80'u8, 146'u8, 64'u8, 172'u8, 169'u8, 65'u8, 249'u8, 54'u8, 147'u8, 67'u8, 40'u8])
  this.isCertSha256Inst = isCertSha256InstExpr
  this.isCertSha256InstFlag = true
  return this.isCertSha256Inst

proc isCertSha384X509(this: EfivarSignatureList_SignatureList): bool = 

  ##[
  SHA384 hash of an X.509 certificate's To-Be-Signed contents, and a time of revocation
  @see "EFI_CERT_X509_SHA384_GUID"
  ]##
  if this.isCertSha384X509InstFlag:
    return this.isCertSha384X509Inst
  let isCertSha384X509InstExpr = bool(this.signatureType == @[110'u8, 135'u8, 118'u8, 112'u8, 194'u8, 128'u8, 230'u8, 78'u8, 170'u8, 210'u8, 40'u8, 179'u8, 73'u8, 166'u8, 134'u8, 91'u8])
  this.isCertSha384X509Inst = isCertSha384X509InstExpr
  this.isCertSha384X509InstFlag = true
  return this.isCertSha384X509Inst

proc isCertRsa2048Sha256(this: EfivarSignatureList_SignatureList): bool = 

  ##[
  RSA-2048 signature of a SHA-256 hash
  @see "EFI_CERT_RSA2048_SHA256_GUID"
  ]##
  if this.isCertRsa2048Sha256InstFlag:
    return this.isCertRsa2048Sha256Inst
  let isCertRsa2048Sha256InstExpr = bool(this.signatureType == @[144'u8, 97'u8, 179'u8, 226'u8, 155'u8, 135'u8, 61'u8, 74'u8, 173'u8, 141'u8, 242'u8, 231'u8, 187'u8, 163'u8, 39'u8, 132'u8])
  this.isCertRsa2048Sha256Inst = isCertRsa2048Sha256InstExpr
  this.isCertRsa2048Sha256InstFlag = true
  return this.isCertRsa2048Sha256Inst

proc isCertDerPkcs7(this: EfivarSignatureList_SignatureList): bool = 

  ##[
  DER-encoded PKCS #7 version 1.5 [RFC2315]
  @see "EFI_CERT_TYPE_PKCS7_GUID"
  ]##
  if this.isCertDerPkcs7InstFlag:
    return this.isCertDerPkcs7Inst
  let isCertDerPkcs7InstExpr = bool(this.signatureType == @[157'u8, 210'u8, 175'u8, 74'u8, 223'u8, 104'u8, 238'u8, 73'u8, 138'u8, 169'u8, 52'u8, 125'u8, 55'u8, 86'u8, 101'u8, 167'u8])
  this.isCertDerPkcs7Inst = isCertDerPkcs7InstExpr
  this.isCertDerPkcs7InstFlag = true
  return this.isCertDerPkcs7Inst

proc fromFile*(_: typedesc[EfivarSignatureList_SignatureList], filename: string): EfivarSignatureList_SignatureList =
  EfivarSignatureList_SignatureList.read(newKaitaiFileStream(filename), nil, nil)


##[
@see "EFI_SIGNATURE_DATA"
]##
proc read*(_: typedesc[EfivarSignatureList_SignatureData], io: KaitaiStream, root: KaitaiStruct, parent: EfivarSignatureList_SignatureList): EfivarSignatureList_SignatureData =
  template this: untyped = result
  this = new(EfivarSignatureList_SignatureData)
  let root = if root == nil: cast[EfivarSignatureList](this) else: cast[EfivarSignatureList](root)
  this.io = io
  this.root = root
  this.parent = parent


  ##[
  An identifier which identifies the agent which added the signature to the list
  ]##
  let ownerExpr = this.io.readBytes(int(16))
  this.owner = ownerExpr

  ##[
  The format of the signature is defined by the SignatureType.
  ]##
  let dataExpr = this.io.readBytesFull()
  this.data = dataExpr

proc fromFile*(_: typedesc[EfivarSignatureList_SignatureData], filename: string): EfivarSignatureList_SignatureData =
  EfivarSignatureList_SignatureData.read(newKaitaiFileStream(filename), nil, nil)


##[
Attributes of a UEFI variable
]##
proc read*(_: typedesc[EfivarSignatureList_EfiVarAttr], io: KaitaiStream, root: KaitaiStruct, parent: EfivarSignatureList): EfivarSignatureList_EfiVarAttr =
  template this: untyped = result
  this = new(EfivarSignatureList_EfiVarAttr)
  let root = if root == nil: cast[EfivarSignatureList](this) else: cast[EfivarSignatureList](root)
  this.io = io
  this.root = root
  this.parent = parent

  let enhancedAuthenticatedAccessExpr = this.io.readBitsIntBe(1) != 0
  this.enhancedAuthenticatedAccess = enhancedAuthenticatedAccessExpr
  let appendWriteExpr = this.io.readBitsIntBe(1) != 0
  this.appendWrite = appendWriteExpr
  let timeBasedAuthenticatedWriteAccessExpr = this.io.readBitsIntBe(1) != 0
  this.timeBasedAuthenticatedWriteAccess = timeBasedAuthenticatedWriteAccessExpr
  let authenticatedWriteAccessExpr = this.io.readBitsIntBe(1) != 0
  this.authenticatedWriteAccess = authenticatedWriteAccessExpr
  let hardwareErrorRecordExpr = this.io.readBitsIntBe(1) != 0
  this.hardwareErrorRecord = hardwareErrorRecordExpr
  let runtimeAccessExpr = this.io.readBitsIntBe(1) != 0
  this.runtimeAccess = runtimeAccessExpr
  let bootserviceAccessExpr = this.io.readBitsIntBe(1) != 0
  this.bootserviceAccess = bootserviceAccessExpr
  let nonVolatileExpr = this.io.readBitsIntBe(1) != 0
  this.nonVolatile = nonVolatileExpr

  ##[
  Reserved (unused) bits
  ]##
  let reserved1Expr = this.io.readBitsIntBe(24)
  this.reserved1 = reserved1Expr

proc fromFile*(_: typedesc[EfivarSignatureList_EfiVarAttr], filename: string): EfivarSignatureList_EfiVarAttr =
  EfivarSignatureList_EfiVarAttr.read(newKaitaiFileStream(filename), nil, nil)