go-attestation/attest/win_events.go
hansinator 17f9c05652
fix returning wrong error in ParseWinEvents (#291)
Co-authored-by: Hans-Gert Dahmen <hans-gert.dahmen@immu.ne>
2022-10-11 09:22:10 -07:00

807 lines
27 KiB
Go

// Copyright 2020 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
package attest
import (
"bytes"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"io"
"strings"
"unicode/utf16"
"github.com/google/go-attestation/attest/internal"
)
type windowsEvent uint32
// SIPA event types
const (
sipaTypeMask windowsEvent = 0x000f0000
sipaContainer windowsEvent = 0x00010000
sipaInformation windowsEvent = 0x00020000
sipaError windowsEvent = 0x00030000
sipaPreOsParameter windowsEvent = 0x00040000
sipaOSParameter windowsEvent = 0x00050000
sipaAuthority windowsEvent = 0x00060000
sipaLoadedModule windowsEvent = 0x00070000
sipaTrustPoint windowsEvent = 0x00080000
sipaELAM windowsEvent = 0x00090000
sipaVBS windowsEvent = 0x000a0000
trustBoundary windowsEvent = 0x40010001
elamAggregation windowsEvent = 0x40010002
loadedModuleAggregation windowsEvent = 0x40010003
trustpointAggregation windowsEvent = 0xC0010004
ksrAggregation windowsEvent = 0x40010005
ksrSignedMeasurementAggregation windowsEvent = 0x40010006
information windowsEvent = 0x00020001
bootCounter windowsEvent = 0x00020002
transferControl windowsEvent = 0x00020003
applicationReturn windowsEvent = 0x00020004
bitlockerUnlock windowsEvent = 0x00020005
eventCounter windowsEvent = 0x00020006
counterID windowsEvent = 0x00020007
morBitNotCancelable windowsEvent = 0x00020008
applicationSVN windowsEvent = 0x00020009
svnChainStatus windowsEvent = 0x0002000A
morBitAPIStatus windowsEvent = 0x0002000B
bootDebugging windowsEvent = 0x00040001
bootRevocationList windowsEvent = 0x00040002
osKernelDebug windowsEvent = 0x00050001
codeIntegrity windowsEvent = 0x00050002
testSigning windowsEvent = 0x00050003
dataExecutionPrevention windowsEvent = 0x00050004
safeMode windowsEvent = 0x00050005
winPE windowsEvent = 0x00050006
physicalAddressExtension windowsEvent = 0x00050007
osDevice windowsEvent = 0x00050008
systemRoot windowsEvent = 0x00050009
hypervisorLaunchType windowsEvent = 0x0005000A
hypervisorPath windowsEvent = 0x0005000B
hypervisorIOMMUPolicy windowsEvent = 0x0005000C
hypervisorDebug windowsEvent = 0x0005000D
driverLoadPolicy windowsEvent = 0x0005000E
siPolicy windowsEvent = 0x0005000F
hypervisorMMIONXPolicy windowsEvent = 0x00050010
hypervisorMSRFilterPolicy windowsEvent = 0x00050011
vsmLaunchType windowsEvent = 0x00050012
osRevocationList windowsEvent = 0x00050013
vsmIDKInfo windowsEvent = 0x00050020
flightSigning windowsEvent = 0x00050021
pagefileEncryptionEnabled windowsEvent = 0x00050022
vsmIDKSInfo windowsEvent = 0x00050023
hibernationDisabled windowsEvent = 0x00050024
dumpsDisabled windowsEvent = 0x00050025
dumpEncryptionEnabled windowsEvent = 0x00050026
dumpEncryptionKeyDigest windowsEvent = 0x00050027
lsaISOConfig windowsEvent = 0x00050028
noAuthority windowsEvent = 0x00060001
authorityPubKey windowsEvent = 0x00060002
filePath windowsEvent = 0x00070001
imageSize windowsEvent = 0x00070002
hashAlgorithmID windowsEvent = 0x00070003
authenticodeHash windowsEvent = 0x00070004
authorityIssuer windowsEvent = 0x00070005
authoritySerial windowsEvent = 0x00070006
imageBase windowsEvent = 0x00070007
authorityPublisher windowsEvent = 0x00070008
authoritySHA1Thumbprint windowsEvent = 0x00070009
imageValidated windowsEvent = 0x0007000A
moduleSVN windowsEvent = 0x0007000B
quote windowsEvent = 0x80080001
quoteSignature windowsEvent = 0x80080002
aikID windowsEvent = 0x80080003
aikPubDigest windowsEvent = 0x80080004
elamKeyname windowsEvent = 0x00090001
elamConfiguration windowsEvent = 0x00090002
elamPolicy windowsEvent = 0x00090003
elamMeasured windowsEvent = 0x00090004
vbsVSMRequired windowsEvent = 0x000A0001
vbsSecurebootRequired windowsEvent = 0x000A0002
vbsIOMMURequired windowsEvent = 0x000A0003
vbsNXRequired windowsEvent = 0x000A0004
vbsMSRFilteringRequired windowsEvent = 0x000A0005
vbsMandatoryEnforcement windowsEvent = 0x000A0006
vbsHVCIPolicy windowsEvent = 0x000A0007
vbsMicrosoftBootChainRequired windowsEvent = 0x000A0008
ksrSignature windowsEvent = 0x000B0001
)
type WinCSPAlg uint32
// Valid CSP Algorithm IDs.
const (
WinAlgMD4 WinCSPAlg = 0x02
WinAlgMD5 WinCSPAlg = 0x03
WinAlgSHA1 WinCSPAlg = 0x04
WinAlgSHA256 WinCSPAlg = 0x0c
WinAlgSHA384 WinCSPAlg = 0x0d
WinAlgSHA512 WinCSPAlg = 0x0e
)
// BitlockerStatus describes the status of BitLocker on a Windows system.
type BitlockerStatus uint8
// Valid BitlockerStatus values.
const (
BitlockerStatusCached = 0x01
BitlockerStatusMedia = 0x02
BitlockerStatusTPM = 0x04
BitlockerStatusPin = 0x10
BitlockerStatusExternal = 0x20
BitlockerStatusRecovery = 0x40
)
// Ternary describes a boolean value that can additionally be unknown.
type Ternary uint8
// Valid Ternary values.
const (
TernaryUnknown Ternary = iota
TernaryTrue
TernaryFalse
)
// WinEvents describes information from the event log recorded during
// bootup of Microsoft Windows.
type WinEvents struct {
// ColdBoot is set to true if the system was not resuming from hibernation.
ColdBoot bool
// BootCount contains the value of the monotonic boot counter. This
// value is not set for TPM 1.2 devices and some TPMs with buggy
// implementations of monotonic counters.
BootCount uint64
// LoadedModules contains authenticode hashes for binaries which
// were loaded during boot.
LoadedModules map[string]WinModuleLoad
// ELAM describes the configuration of each Early Launch AntiMalware driver,
// for each AV Vendor key.
ELAM map[string]WinELAM
// BootDebuggingEnabled is true if boot debugging was ever reported
// as enabled.
BootDebuggingEnabled bool
// KernelDebugEnabled is true if kernel debugging was recorded as
// enabled at any point during boot.
KernelDebugEnabled bool
// DEPEnabled is true if NX (Data Execution Prevention) was consistently
// reported as enabled.
DEPEnabled Ternary
// CodeIntegrityEnabled is true if code integrity was consistently
// reported as enabled.
CodeIntegrityEnabled Ternary
// TestSigningEnabled is true if test-mode signature verification was
// ever reported as enabled.
TestSigningEnabled bool
// BitlockerUnlocks reports the bitlocker status for every instance of
// a disk unlock, where bitlocker was used to secure the disk.
BitlockerUnlocks []BitlockerStatus
}
// WinModuleLoad describes a module which was loaded while
// Windows booted.
type WinModuleLoad struct {
// FilePath represents the path from which the module was loaded. This
// information is not always present.
FilePath string
// AuthenticodeHash contains the authenticode hash of the binary
// blob which was loaded.
AuthenticodeHash []byte
// ImageBase describes all the addresses to which the the blob was loaded.
ImageBase []uint64
// ImageSize describes the size of the image in bytes. This information
// is not always present.
ImageSize uint64
// HashAlgorithm describes the hash algorithm used.
HashAlgorithm WinCSPAlg
// ImageValidated is set if the post-boot loader validated the image.
ImageValidated bool
// AuthorityIssuer identifies the issuer of the certificate which certifies
// the signature on this module.
AuthorityIssuer string
// AuthorityPublisher identifies the publisher of the certificate which
// certifies the signature on this module.
AuthorityPublisher string
// AuthoritySerial contains the serial of the certificate certifying this
// module.
AuthoritySerial []byte
// AuthoritySHA1 is the SHA1 hash of the certificate thumbprint.
AuthoritySHA1 []byte
}
// WinELAM describes the configuration of an Early Launch AntiMalware driver.
// These values represent the 3 measured registry values stored in the ELAM
// hive for the driver.
type WinELAM struct {
Measured []byte
Config []byte
Policy []byte
}
// ParseWinEvents parses a series of events to extract information about
// the bringup of Microsoft Windows. This information is not trustworthy
// unless the integrity of platform & bootloader events has already been
// established.
func ParseWinEvents(events []Event) (*WinEvents, error) {
var (
out = WinEvents{
LoadedModules: map[string]WinModuleLoad{},
ELAM: map[string]WinELAM{},
}
seenSeparator struct {
PCR12 bool
PCR13 bool
}
)
for _, e := range events {
if e.Index != 12 && e.Index != 13 {
continue
}
et, err := internal.UntrustedParseEventType(uint32(e.Type))
if err != nil {
return nil, fmt.Errorf("unrecognised event type: %v", err)
}
digestVerify := e.digestEquals(e.Data)
switch e.Index {
case 12: // 'early boot' events
switch et {
case internal.EventTag:
if seenSeparator.PCR12 {
continue
}
s, err := internal.ParseTaggedEventData(e.Data)
if err != nil {
return nil, fmt.Errorf("invalid tagged event structure at event %d: %w", e.sequence, err)
}
if digestVerify != nil {
return nil, fmt.Errorf("invalid digest for tagged event %d: %w", e.sequence, digestVerify)
}
if err := out.readWinEventBlock(s, e.Index); err != nil {
return nil, fmt.Errorf("invalid SIPA events in event %d: %w", e.sequence, err)
}
case internal.Separator:
if seenSeparator.PCR12 {
return nil, fmt.Errorf("duplicate WBCL separator at event %d", e.sequence)
}
seenSeparator.PCR12 = true
if !bytes.Equal(e.Data, []byte("WBCL")) {
return nil, fmt.Errorf("invalid WBCL separator data at event %d: %v", e.sequence, e.Data)
}
if digestVerify != nil {
return nil, fmt.Errorf("invalid separator digest at event %d: %v", e.sequence, digestVerify)
}
default:
return nil, fmt.Errorf("unexpected (PCR12) event type: %v", et)
}
case 13: // Post 'early boot' events
switch et {
case internal.EventTag:
if seenSeparator.PCR13 {
continue
}
s, err := internal.ParseTaggedEventData(e.Data)
if err != nil {
return nil, fmt.Errorf("invalid tagged event structure at event %d: %w", e.sequence, err)
}
if digestVerify != nil {
return nil, fmt.Errorf("invalid digest for tagged event %d: %w", e.sequence, digestVerify)
}
if err := out.readWinEventBlock(s, e.Index); err != nil {
return nil, fmt.Errorf("invalid SIPA events in event %d: %w", e.sequence, err)
}
case internal.Separator:
if seenSeparator.PCR13 {
return nil, fmt.Errorf("duplicate WBCL separator at event %d", e.sequence)
}
seenSeparator.PCR13 = true
if !bytes.Equal(e.Data, []byte("WBCL")) {
return nil, fmt.Errorf("invalid WBCL separator data at event %d: %v", e.sequence, e.Data)
}
if digestVerify != nil {
return nil, fmt.Errorf("invalid separator digest at event %d: %v", e.sequence, digestVerify)
}
default:
return nil, fmt.Errorf("unexpected (PCR13) event type: %v", et)
}
}
}
return &out, nil
}
type microsoftEventHeader struct {
Type windowsEvent
Size uint32
}
// unknownSIPAEvent is returned by parseSIPAEvent if the event type is
// not handled. Unlike other events in the TCG log, it is safe to skip
// unhandled SIPA events, as they are embedded within EventTag structures,
// and these structures should match the event digest.
var unknownSIPAEvent = errors.New("unknown event")
func (w *WinEvents) readBooleanInt64Event(header microsoftEventHeader, r *bytes.Reader) error {
if header.Size != 8 {
return fmt.Errorf("payload was %d bytes, want 8", header.Size)
}
var num uint64
if err := binary.Read(r, binary.LittleEndian, &num); err != nil {
return fmt.Errorf("reading u64: %w", err)
}
isSet := num != 0
switch header.Type {
// Boolean signals that latch off if the are ever false (ie: attributes
// that represent a stronger security state when set).
case dataExecutionPrevention:
if isSet && w.DEPEnabled == TernaryUnknown {
w.DEPEnabled = TernaryTrue
} else if !isSet {
w.DEPEnabled = TernaryFalse
}
}
return nil
}
func (w *WinEvents) readBooleanByteEvent(header microsoftEventHeader, r *bytes.Reader) error {
if header.Size != 1 {
return fmt.Errorf("payload was %d bytes, want 1", header.Size)
}
var b byte
if err := binary.Read(r, binary.LittleEndian, &b); err != nil {
return fmt.Errorf("reading byte: %w", err)
}
isSet := b != 0
switch header.Type {
// Boolean signals that latch on if they are ever true (ie: attributes
// that represent a weaker security state when set).
case osKernelDebug:
w.KernelDebugEnabled = w.KernelDebugEnabled || isSet
case bootDebugging:
w.BootDebuggingEnabled = w.BootDebuggingEnabled || isSet
case testSigning:
w.TestSigningEnabled = w.TestSigningEnabled || isSet
// Boolean signals that latch off if the are ever false (ie: attributes
// that represent a stronger security state when set).
case codeIntegrity:
if isSet && w.CodeIntegrityEnabled == TernaryUnknown {
w.CodeIntegrityEnabled = TernaryTrue
} else if !isSet {
w.CodeIntegrityEnabled = TernaryFalse
}
}
return nil
}
func (w *WinEvents) readUint32(header microsoftEventHeader, r io.Reader) (uint32, error) {
if header.Size != 4 {
return 0, fmt.Errorf("integer size not uint32 (%d bytes)", header.Size)
}
data := make([]uint8, header.Size)
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
return 0, fmt.Errorf("reading u32: %w", err)
}
i := binary.LittleEndian.Uint32(data)
return i, nil
}
func (w *WinEvents) readUint64(header microsoftEventHeader, r io.Reader) (uint64, error) {
if header.Size != 8 {
return 0, fmt.Errorf("integer size not uint64 (%d bytes)", header.Size)
}
data := make([]uint8, header.Size)
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
return 0, fmt.Errorf("reading u64: %w", err)
}
i := binary.LittleEndian.Uint64(data)
return i, nil
}
func (w *WinEvents) readBootCounter(header microsoftEventHeader, r *bytes.Reader) error {
i, err := w.readUint64(header, r)
if err != nil {
return fmt.Errorf("boot counter: %v", err)
}
if w.BootCount > 0 && w.BootCount != i {
return fmt.Errorf("conflicting values for boot counter: %d != %d", i, w.BootCount)
}
w.BootCount = i
return nil
}
func (w *WinEvents) readTransferControl(header microsoftEventHeader, r *bytes.Reader) error {
i, err := w.readUint32(header, r)
if err != nil {
return fmt.Errorf("transfer control: %v", err)
}
// A transferControl event with a value of 1 indicates that bootmngr
// launched WinLoad. A different (unknown) value is set if WinResume
// is launched.
w.ColdBoot = i == 0x1
return nil
}
func (w *WinEvents) readBitlockerUnlock(header microsoftEventHeader, r *bytes.Reader, pcr int) error {
if header.Size > 8 {
return fmt.Errorf("bitlocker data too large (%d bytes)", header.Size)
}
data := make([]uint8, header.Size)
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
return fmt.Errorf("reading u%d: %w", header.Size<<8, err)
}
i, n := binary.Uvarint(data)
if n <= 0 {
return fmt.Errorf("reading u%d: invalid varint", header.Size<<8)
}
if pcr == 13 {
// The bitlocker status is duplicated across both PCRs. As such,
// we prefer the earlier one, and bail here to prevent duplicate
// records.
return nil
}
w.BitlockerUnlocks = append(w.BitlockerUnlocks, BitlockerStatus(i))
return nil
}
func (w *WinEvents) parseImageValidated(header microsoftEventHeader, r io.Reader) (bool, error) {
if header.Size != 1 {
return false, fmt.Errorf("payload was %d bytes, want 1", header.Size)
}
var num byte
if err := binary.Read(r, binary.LittleEndian, &num); err != nil {
return false, fmt.Errorf("reading u8: %w", err)
}
return num == 1, nil
}
func (w *WinEvents) parseHashAlgID(header microsoftEventHeader, r io.Reader) (WinCSPAlg, error) {
i, err := w.readUint32(header, r)
if err != nil {
return 0, fmt.Errorf("hash algorithm ID: %v", err)
}
switch alg := WinCSPAlg(i & 0xff); alg {
case WinAlgMD4, WinAlgMD5, WinAlgSHA1, WinAlgSHA256, WinAlgSHA384, WinAlgSHA512:
return alg, nil
default:
return 0, fmt.Errorf("unknown algorithm ID: %x", i)
}
}
func (w *WinEvents) parseAuthoritySerial(header microsoftEventHeader, r io.Reader) ([]byte, error) {
if header.Size > 128 {
return nil, fmt.Errorf("authority serial is too long (%d bytes)", header.Size)
}
data := make([]byte, header.Size)
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
return nil, fmt.Errorf("reading bytes: %w", err)
}
return data, nil
}
func (w *WinEvents) parseAuthoritySHA1(header microsoftEventHeader, r io.Reader) ([]byte, error) {
if header.Size > 20 {
return nil, fmt.Errorf("authority thumbprint is too long (%d bytes)", header.Size)
}
data := make([]byte, header.Size)
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
return nil, fmt.Errorf("reading bytes: %w", err)
}
return data, nil
}
func (w *WinEvents) parseImageBase(header microsoftEventHeader, r io.Reader) (uint64, error) {
if header.Size != 8 {
return 0, fmt.Errorf("payload was %d bytes, want 8", header.Size)
}
var num uint64
if err := binary.Read(r, binary.LittleEndian, &num); err != nil {
return 0, fmt.Errorf("reading u64: %w", err)
}
return num, nil
}
func (w *WinEvents) parseAuthenticodeHash(header microsoftEventHeader, r io.Reader) ([]byte, error) {
if header.Size > 32 {
return nil, fmt.Errorf("authenticode hash data exceeds the size of any valid hash (%d bytes)", header.Size)
}
data := make([]byte, header.Size)
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
return nil, fmt.Errorf("reading bytes: %w", err)
}
return data, nil
}
func (w *WinEvents) readLoadedModuleAggregation(rdr *bytes.Reader, header microsoftEventHeader) error {
var (
r = &io.LimitedReader{R: rdr, N: int64(header.Size)}
codeHash []byte
imgBase, imgSize uint64
fPath string
algID WinCSPAlg
imgValidated bool
aIssuer, aPublisher string
aSerial, aSHA1 []byte
)
for r.N > 0 {
var h microsoftEventHeader
if err := binary.Read(r, binary.LittleEndian, &h); err != nil {
return fmt.Errorf("parsing LMA sub-event: %v", err)
}
if int64(h.Size) > r.N {
return fmt.Errorf("LMA sub-event is larger than available data: %d > %d", h.Size, r.N)
}
var err error
switch h.Type {
case imageBase:
if imgBase != 0 {
return errors.New("duplicate image base data in LMA event")
}
if imgBase, err = w.parseImageBase(h, r); err != nil {
return err
}
case authenticodeHash:
if codeHash != nil {
return errors.New("duplicate authenticode hash structure in LMA event")
}
if codeHash, err = w.parseAuthenticodeHash(h, r); err != nil {
return err
}
case filePath:
if fPath != "" {
return errors.New("duplicate file path in LMA event")
}
if fPath, err = w.parseUTF16(h, r); err != nil {
return err
}
case imageSize:
if imgSize != 0 {
return errors.New("duplicate image size in LMA event")
}
if imgSize, err = w.readUint64(h, r); err != nil {
return err
}
case hashAlgorithmID:
if algID != 0 {
return errors.New("duplicate hash algorithm ID in LMA event")
}
if algID, err = w.parseHashAlgID(h, r); err != nil {
return err
}
case imageValidated:
if imgValidated == true {
return errors.New("duplicate image validated field in LMA event")
}
if imgValidated, err = w.parseImageValidated(h, r); err != nil {
return err
}
case authorityIssuer:
if aIssuer != "" {
return errors.New("duplicate authority issuer in LMA event")
}
if aIssuer, err = w.parseUTF16(h, r); err != nil {
return err
}
case authorityPublisher:
if aPublisher != "" {
return errors.New("duplicate authority publisher in LMA event")
}
if aPublisher, err = w.parseUTF16(h, r); err != nil {
return err
}
case authoritySerial:
if aSerial != nil {
return errors.New("duplicate authority serial in LMA event")
}
if aSerial, err = w.parseAuthoritySerial(h, r); err != nil {
return err
}
case authoritySHA1Thumbprint:
if aSHA1 != nil {
return errors.New("duplicate authority SHA1 thumbprint in LMA event")
}
if aSHA1, err = w.parseAuthoritySHA1(h, r); err != nil {
return err
}
case moduleSVN:
// Ignore - consume value.
b := make([]byte, h.Size)
if err := binary.Read(r, binary.LittleEndian, &b); err != nil {
return err
}
default:
return fmt.Errorf("unknown event in LMA aggregation: %v", h.Type)
}
}
var iBase []uint64
if imgBase != 0 {
iBase = []uint64{imgBase}
}
l := WinModuleLoad{
FilePath: fPath,
AuthenticodeHash: codeHash,
ImageBase: iBase,
ImageSize: imgSize,
ImageValidated: imgValidated,
HashAlgorithm: algID,
AuthorityIssuer: aIssuer,
AuthorityPublisher: aPublisher,
AuthoritySerial: aSerial,
AuthoritySHA1: aSHA1,
}
hashHex := hex.EncodeToString(l.AuthenticodeHash)
l.ImageBase = append(l.ImageBase, w.LoadedModules[hashHex].ImageBase...)
w.LoadedModules[hashHex] = l
return nil
}
// parseUTF16 decodes data representing a UTF16 string. It is assumed the
// caller has validated that the data size is within allowable bounds.
func (w *WinEvents) parseUTF16(header microsoftEventHeader, r io.Reader) (string, error) {
data := make([]uint16, header.Size/2)
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
return "", err
}
return strings.TrimSuffix(string(utf16.Decode(data)), "\x00"), nil
}
func (w *WinEvents) readELAMAggregation(rdr *bytes.Reader, header microsoftEventHeader) error {
var (
r = &io.LimitedReader{R: rdr, N: int64(header.Size)}
driverName string
measured []byte
policy []byte
config []byte
)
for r.N > 0 {
var h microsoftEventHeader
if err := binary.Read(r, binary.LittleEndian, &h); err != nil {
return fmt.Errorf("parsing ELAM aggregation sub-event: %v", err)
}
if int64(h.Size) > r.N {
return fmt.Errorf("ELAM aggregation sub-event is larger than available data: %d > %d", h.Size, r.N)
}
var err error
switch h.Type {
case elamKeyname:
if driverName != "" {
return errors.New("duplicate driver name in ELAM aggregation event")
}
if driverName, err = w.parseUTF16(h, r); err != nil {
return fmt.Errorf("parsing ELAM driver name: %v", err)
}
case elamMeasured:
if measured != nil {
return errors.New("duplicate measured data in ELAM aggregation event")
}
measured = make([]byte, h.Size)
if err := binary.Read(r, binary.LittleEndian, &measured); err != nil {
return fmt.Errorf("reading ELAM measured value: %v", err)
}
case elamPolicy:
if policy != nil {
return errors.New("duplicate policy data in ELAM aggregation event")
}
policy = make([]byte, h.Size)
if err := binary.Read(r, binary.LittleEndian, &policy); err != nil {
return fmt.Errorf("reading ELAM policy value: %v", err)
}
case elamConfiguration:
if config != nil {
return errors.New("duplicate config data in ELAM aggregation event")
}
config = make([]byte, h.Size)
if err := binary.Read(r, binary.LittleEndian, &config); err != nil {
return fmt.Errorf("reading ELAM config value: %v", err)
}
default:
return fmt.Errorf("unknown event in LMA aggregation: %v", h.Type)
}
}
if driverName == "" {
return errors.New("ELAM driver name not specified")
}
w.ELAM[driverName] = WinELAM{
Measured: measured,
Config: config,
Policy: policy,
}
return nil
}
func (w *WinEvents) readSIPAEvent(r *bytes.Reader, pcr int) error {
var header microsoftEventHeader
if err := binary.Read(r, binary.LittleEndian, &header); err != nil {
return err
}
switch header.Type {
case elamAggregation:
return w.readELAMAggregation(r, header)
case loadedModuleAggregation:
return w.readLoadedModuleAggregation(r, header)
case bootCounter:
return w.readBootCounter(header, r)
case bitlockerUnlock:
return w.readBitlockerUnlock(header, r, pcr)
case transferControl:
return w.readTransferControl(header, r)
case osKernelDebug, codeIntegrity, bootDebugging, testSigning: // Parse boolean values.
return w.readBooleanByteEvent(header, r)
case dataExecutionPrevention: // Parse booleans represented as uint64's.
return w.readBooleanInt64Event(header, r)
default:
// Event type was not handled, consume the data.
if int(header.Size) > r.Len() {
return fmt.Errorf("event data len (%d bytes) larger than event length (%d bytes)", header.Size, r.Len())
}
tmp := make([]byte, header.Size)
if err := binary.Read(r, binary.LittleEndian, &tmp); err != nil {
return fmt.Errorf("reading unknown data section of length %d: %w", header.Size, err)
}
return unknownSIPAEvent
}
}
// readWinEventBlock extracts boot configuration from SIPA events contained in
// the given tagged event.
func (w *WinEvents) readWinEventBlock(evt *internal.TaggedEventData, pcr int) error {
r := bytes.NewReader(evt.Data)
// All windows information should be sub events in an enclosing SIPA
// container event.
if (windowsEvent(evt.ID) & sipaTypeMask) != sipaContainer {
return fmt.Errorf("expected container event, got %v", windowsEvent(evt.ID))
}
for r.Len() > 0 {
if err := w.readSIPAEvent(r, pcr); err != nil {
if errors.Is(err, unknownSIPAEvent) {
// Unknown SIPA events are okay as all TCG events are verifiable.
continue
}
return err
}
}
return nil
}