squash(pcr0tool): Add packages "bytes", "check", "errors", "mathtools", "ostools" and "tpmeventlog"
This commit is contained in:
parent
fa79b4903a
commit
e5b1ee4c0a
|
@ -0,0 +1,16 @@
|
|||
```
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
BenchmarkIsZeroFilled/size_0/default-8 1000000000 3.03 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIsZeroFilled/size_0/simple-8 1000000000 2.93 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIsZeroFilled/size_1/default-8 1000000000 4.74 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIsZeroFilled/size_1/simple-8 1000000000 3.31 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIsZeroFilled/size_256/default-8 212255557 28.4 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIsZeroFilled/size_256/simple-8 71001369 86.8 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIsZeroFilled/size_65536/default-8 1466428 3961 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIsZeroFilled/size_65536/simple-8 308932 19780 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIsZeroFilled/size_1048576/default-8 106068 65791 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIsZeroFilled/size_1048576/simple-8 17924 335547 ns/op 0 B/op 0 allocs/op
|
||||
PASS
|
||||
ok _/home/xaionaro/go/src/github.com/9elements/converged-security-suite/v2/cmd/pcr0tool/pkg/bytes 63.711s
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
package bytes
|
||||
|
||||
//go:nosplit
|
||||
func isZeroFilledSimple(b []byte) bool {
|
||||
for _, v := range b {
|
||||
if v != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// +build amd64
|
||||
|
||||
package bytes
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// IsZeroFilled returns true if b consists of zeros only.
|
||||
func IsZeroFilled(b []byte) bool {
|
||||
hdr := (*reflect.SliceHeader)((unsafe.Pointer)(&b))
|
||||
data := hdr.Data
|
||||
length := hdr.Len
|
||||
if data&0x07 != 0 {
|
||||
// the data is not aligned, fallback to a simple way
|
||||
return isZeroFilledSimple(b)
|
||||
}
|
||||
dataEnd := hdr.Data + uintptr(length)
|
||||
dataWordsEnd := dataEnd & ^uintptr(0x07)
|
||||
for ; data < dataWordsEnd; data += 8 {
|
||||
if *(*uint64)(unsafe.Pointer(data)) != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for ; data < dataEnd; data++ {
|
||||
if *(*uint8)(unsafe.Pointer(data)) != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
// +build !amd64
|
||||
|
||||
package bytes
|
||||
|
||||
// IsZeroFilled returns true if b consists of zeros only.
|
||||
//go:nosplit
|
||||
func IsZeroFilled(b []byte) bool {
|
||||
return isZeroFilledSimple(b)
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package bytes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkIsZeroFilled(b *testing.B) {
|
||||
for _, size := range []uint64{0, 1, 256, 65536, 1 << 20} {
|
||||
d := make([]byte, size)
|
||||
b.Run(fmt.Sprintf("size_%d", size), func(b *testing.B) {
|
||||
b.Run("default", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
IsZeroFilled(d)
|
||||
}
|
||||
})
|
||||
b.Run("simple", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
isZeroFilledSimple(d)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package bytes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Range defines is a generic bytes range headers.
|
||||
type Range struct {
|
||||
Offset uint64
|
||||
Length uint64
|
||||
}
|
||||
|
||||
func (r Range) String() string {
|
||||
return fmt.Sprintf(`{"Offset":"0x%x", "Length":"0x%x"}`, r.Offset, r.Length)
|
||||
}
|
||||
|
||||
// Intersect returns True if ranges "r" and "cmp" has at least
|
||||
// one byte with the same offset.
|
||||
func (r Range) Intersect(cmp Range) bool {
|
||||
if r.Length == 0 || cmp.Length == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
startIdx0 := r.Offset
|
||||
startIdx1 := cmp.Offset
|
||||
endIdx0 := startIdx0 + r.Length
|
||||
endIdx1 := startIdx1 + cmp.Length
|
||||
|
||||
if endIdx0 <= startIdx1 {
|
||||
return false
|
||||
}
|
||||
if startIdx0 >= endIdx1 {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Ranges is a helper to manipulate multiple `Range`-s at once
|
||||
type Ranges []Range
|
||||
|
||||
func (s Ranges) String() string {
|
||||
r := make([]string, 0, len(s))
|
||||
for _, oneRange := range s {
|
||||
r = append(r, oneRange.String())
|
||||
}
|
||||
return `[` + strings.Join(r, `, `) + `]`
|
||||
}
|
||||
|
||||
// Sort sorts the slice by field Offset
|
||||
func (s Ranges) Sort() {
|
||||
sort.Slice(s, func(i, j int) bool {
|
||||
return s[i].Offset < s[j].Offset
|
||||
})
|
||||
}
|
||||
|
||||
// MergeRanges just merges ranges which has distance less or equal to
|
||||
// mergeDistance.
|
||||
//
|
||||
// Warning: should be called only on sorted ranges!
|
||||
func MergeRanges(in Ranges, mergeDistance uint64) Ranges {
|
||||
if len(in) < 2 {
|
||||
return in
|
||||
}
|
||||
|
||||
var result Ranges
|
||||
entry := in[0]
|
||||
for _, nextEntry := range in[1:] {
|
||||
// merge "nextEntry" to "entry" if the distance is lower or equal to
|
||||
// mergeDistance.
|
||||
|
||||
if entry.Offset+entry.Length+mergeDistance >= nextEntry.Offset {
|
||||
entry.Length = (nextEntry.Offset - entry.Offset) + nextEntry.Length
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, entry)
|
||||
entry = nextEntry
|
||||
}
|
||||
result = append(result, entry)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// SortAndMerge sorts the slice (by field Offset) and the merges ranges
|
||||
// which could be merged.
|
||||
func (s *Ranges) SortAndMerge() {
|
||||
// See also TestDiffEntriesSortAndMerge
|
||||
|
||||
if len(*s) < 2 {
|
||||
return
|
||||
}
|
||||
s.Sort()
|
||||
|
||||
*s = MergeRanges(*s, 0)
|
||||
}
|
||||
|
||||
// Compile returns the bytes from `b` which are referenced by `Range`-s `s`.
|
||||
func (s Ranges) Compile(b []byte) []byte {
|
||||
var result []byte
|
||||
for _, r := range s {
|
||||
result = append(result, b[r.Offset:r.Offset+r.Length]...)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// IsIn returns if the index is covered by this ranges
|
||||
func (s Ranges) IsIn(index uint64) bool {
|
||||
for _, r := range s {
|
||||
startIdx := r.Offset
|
||||
endIdx := r.Offset + r.Length
|
||||
// `startIdx` is inclusive, while `endIdx` is exclusive.
|
||||
// The same as usual slice indices works:
|
||||
//
|
||||
// slice[startIdx:endIdx]
|
||||
|
||||
if startIdx <= index && index < endIdx {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package bytes
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRangesSortAndMerge(t *testing.T) {
|
||||
t.Run("nothing_to_merge", func(t *testing.T) {
|
||||
entries := Ranges{{
|
||||
Offset: 2,
|
||||
Length: 1,
|
||||
}, {
|
||||
Offset: 0,
|
||||
Length: 1,
|
||||
}}
|
||||
entries.SortAndMerge()
|
||||
require.Equal(t, Ranges{{
|
||||
Offset: 0,
|
||||
Length: 1,
|
||||
}, {
|
||||
Offset: 2,
|
||||
Length: 1,
|
||||
}}, entries)
|
||||
})
|
||||
t.Run("merge_overlapping", func(t *testing.T) {
|
||||
entries := Ranges{{
|
||||
Offset: 2,
|
||||
Length: 3,
|
||||
}, {
|
||||
Offset: 0,
|
||||
Length: 3,
|
||||
}}
|
||||
entries.SortAndMerge()
|
||||
require.Equal(t, Ranges{{
|
||||
Offset: 0,
|
||||
Length: 5,
|
||||
}}, entries)
|
||||
})
|
||||
t.Run("merge_no_distance", func(t *testing.T) {
|
||||
entries := Ranges{{
|
||||
Offset: 2,
|
||||
Length: 2,
|
||||
}, {
|
||||
Offset: 0,
|
||||
Length: 2,
|
||||
}}
|
||||
entries.SortAndMerge()
|
||||
require.Equal(t, Ranges{{
|
||||
Offset: 0,
|
||||
Length: 4,
|
||||
}}, entries)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"github.com/9elements/converged-security-suite/v2/pkg/errors"
|
||||
)
|
||||
|
||||
func bounds(length uint, startIdx, endIdx int) error {
|
||||
result := &errors.MultiError{}
|
||||
if startIdx < 0 {
|
||||
result.Add(&errors.ErrStartLessThanZero{StartIdx: startIdx})
|
||||
}
|
||||
if endIdx < startIdx {
|
||||
result.Add(&errors.ErrEndLessThanStart{StartIdx: startIdx, EndIdx: endIdx})
|
||||
}
|
||||
if endIdx >= 0 && uint(endIdx) > length {
|
||||
result.Add(&errors.ErrEndGreaterThanLength{Length: length, EndIdx: endIdx})
|
||||
}
|
||||
|
||||
return result.ReturnValue()
|
||||
}
|
||||
|
||||
// BytesRange checks if starting index `startIdx`, ending index `endIdx` and
|
||||
// len(b) passes sanity checks:
|
||||
// * 0 <= startIdx
|
||||
// * startIdx <= endIdx
|
||||
// * endIdx < len(b)
|
||||
func BytesRange(b []byte, startIdx, endIdx int) error {
|
||||
return bounds(uint(len(b)), startIdx, endIdx)
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ErrStartLessThanZero means `startIdx` has negative value
|
||||
type ErrStartLessThanZero struct {
|
||||
StartIdx int
|
||||
}
|
||||
|
||||
func (err *ErrStartLessThanZero) Error() string {
|
||||
return fmt.Sprintf("start index is less than zero: %d", err.StartIdx)
|
||||
}
|
||||
|
||||
// ErrEndLessThanStart means `endIdx` value is less than `startIdx` value
|
||||
type ErrEndLessThanStart struct {
|
||||
StartIdx int
|
||||
EndIdx int
|
||||
}
|
||||
|
||||
func (err *ErrEndLessThanStart) Error() string {
|
||||
return fmt.Sprintf("end index is less than start index: %d < %d",
|
||||
err.EndIdx, err.StartIdx)
|
||||
}
|
||||
|
||||
// ErrEndGreaterThanLength means `endIdx` is greater or equal to the length.
|
||||
type ErrEndGreaterThanLength struct {
|
||||
Length uint
|
||||
EndIdx int
|
||||
}
|
||||
|
||||
func (err *ErrEndGreaterThanLength) Error() string {
|
||||
return fmt.Sprintf("end index is outside of the bounds: %d >= %d",
|
||||
err.EndIdx, err.Length)
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MultiError is a type which aggregates multiple `error`-s into one `error`.
|
||||
//
|
||||
// Some functions may have multiple errors at one call, and sometimes
|
||||
// we don't want to lose them (so we collect them all).
|
||||
type MultiError []error
|
||||
|
||||
func (mErr MultiError) Error() string {
|
||||
if mErr.Count() == 0 {
|
||||
return "no errors"
|
||||
}
|
||||
var errStrings []string
|
||||
for _, err := range mErr {
|
||||
errStrings = append(errStrings, err.Error())
|
||||
}
|
||||
return fmt.Sprintf("errors: %v", strings.Join(errStrings, "; "))
|
||||
}
|
||||
|
||||
// MarshalJSON just implements encoding/json.Marshaler
|
||||
func (mErr MultiError) MarshalJSON() ([]byte, error) {
|
||||
if len(mErr) == 0 {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
|
||||
return json.Marshal(mErr.Error())
|
||||
}
|
||||
|
||||
// Add adds errors to the collection.
|
||||
//
|
||||
// If some of passed errors are nil, then they won't be added
|
||||
// (a `nil` error is not considered to be an error).
|
||||
func (mErr *MultiError) Add(errs ...error) *MultiError {
|
||||
for _, err := range errs {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
switch err := err.(type) {
|
||||
case *MultiError:
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
*mErr = append(*mErr, *err...)
|
||||
case MultiError:
|
||||
*mErr = append(*mErr, err...)
|
||||
default:
|
||||
*mErr = append(*mErr, err)
|
||||
}
|
||||
}
|
||||
|
||||
return mErr
|
||||
}
|
||||
|
||||
// Count returns the amount of collected errors.
|
||||
func (mErr MultiError) Count() uint {
|
||||
return uint(len(mErr))
|
||||
}
|
||||
|
||||
// ReturnValue is a helper which returns `nil` if there was
|
||||
// no errors collected, and the collection if there're any.
|
||||
//
|
||||
// It supposed to be used in `return`-s:
|
||||
// return mErr.ReturnValue()
|
||||
func (mErr MultiError) ReturnValue() error {
|
||||
if mErr.Count() > 0 {
|
||||
return mErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Filter returns a copy of mErr but only with entries on which function "fn"
|
||||
// returns "true".
|
||||
func (mErr MultiError) Filter(fn func(err error) bool) MultiError {
|
||||
var result MultiError
|
||||
for _, err := range mErr {
|
||||
if fn(err) {
|
||||
result = append(result, err)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Unwrap is used by functions like `errors.As()`. Since it is not allowed
|
||||
// to return multiple errors, it unwraps an error if there's only one.
|
||||
func (mErr MultiError) Unwrap() error {
|
||||
if mErr.Count() == 1 {
|
||||
return mErr[0]
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMultiErrorMarshalJSON(t *testing.T) {
|
||||
mErr := MultiError{errors.New("simple error message"), errors.New(`I am a citizen of "Earth"!`)}
|
||||
b, err := mErr.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
var errDescr string
|
||||
err = json.Unmarshal(b, &errDescr)
|
||||
require.NoError(t, err, string(b))
|
||||
require.Equal(t, `"errors: simple error message; I am a citizen of \"Earth\"!"`, string(b))
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package mathtools
|
||||
|
||||
// Max returns a maximal value (from the `int`-s passed as the arguments)
|
||||
func Max(arg0 int, args ...int) int {
|
||||
result := arg0
|
||||
for _, arg := range args {
|
||||
if arg > result {
|
||||
result = arg
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// UMax returns a maximal value (from the `uint`-s passed as the arguments)
|
||||
func UMax(arg0 uint, args ...uint) uint {
|
||||
result := arg0
|
||||
for _, arg := range args {
|
||||
if arg > result {
|
||||
result = arg
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Min returns a minimal value (from the `int`-s passed as the arguments)
|
||||
func Min(arg0 int, args ...int) int {
|
||||
result := arg0
|
||||
for _, arg := range args {
|
||||
if arg < result {
|
||||
result = arg
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package ostools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/edsrzf/mmap-go"
|
||||
)
|
||||
|
||||
// FileToBytes returns the contents of the file by path `filePath`.
|
||||
func FileToBytes(filePath string) ([]byte, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`unable to open the image-file "%v": %w`,
|
||||
filePath, err)
|
||||
}
|
||||
defer file.Close() // it was a read-only Open(), so we don't check the Close()
|
||||
|
||||
// To consume less memory we use mmap() instead of reading the image
|
||||
// into the memory. However these bytes are also parsed by
|
||||
// linuxboot/fiano/pkg/uefi which consumes a lot of memory anyway :(
|
||||
//
|
||||
// See "man 2 mmap".
|
||||
contents, err := mmap.Map(file, mmap.RDONLY, 0)
|
||||
if err == nil {
|
||||
return contents, nil
|
||||
}
|
||||
|
||||
// An error? OK, let's try the usual way to read data:
|
||||
contents, err = ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`unable to access data of the image-file "%v": %w`,
|
||||
filePath, err)
|
||||
}
|
||||
return contents, nil
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package tpmeventlog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ErrRead means unable to read from the io.Reader
|
||||
type ErrRead struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// Error implements interface `error`.
|
||||
func (err ErrRead) Error() string {
|
||||
return fmt.Sprintf("unable to read the reader: %v", err.Err)
|
||||
}
|
||||
|
||||
// Unwrap implements `xerrors.Wrapper`.
|
||||
func (err ErrRead) Unwrap() error {
|
||||
return err.Err
|
||||
}
|
||||
|
||||
// ErrParse means unable to read from the io.Reader
|
||||
type ErrParse struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// Error implements interface `error`.
|
||||
func (err ErrParse) Error() string {
|
||||
return fmt.Sprintf("unable to parse the EventLog: %v", err.Err)
|
||||
}
|
||||
|
||||
// Unwrap implements `xerrors.Wrapper`.
|
||||
func (err ErrParse) Unwrap() error {
|
||||
return err.Err
|
||||
}
|
||||
|
||||
// ErrLocality means it was unable to detect the locality to initialize
|
||||
// the PCR0 value.
|
||||
type ErrLocality struct {
|
||||
EventData []byte
|
||||
}
|
||||
|
||||
// Error implements interface `error`.
|
||||
func (err ErrLocality) Error() string {
|
||||
return fmt.Sprintf("unable to detect locality by event data '%x'", err.EventData)
|
||||
}
|
||||
|
||||
// ErrInvalidDigestLength means an event has a digest of a size not appropriate
|
||||
// for a selected hash algorithm.
|
||||
type ErrInvalidDigestLength struct {
|
||||
Expected int
|
||||
Received int
|
||||
}
|
||||
|
||||
// Error implements interface `error`.
|
||||
func (err ErrInvalidDigestLength) Error() string {
|
||||
return fmt.Sprintf("invalid digest length, expected:%d, received:%d", err.Expected, err.Received)
|
||||
}
|
||||
|
||||
// ErrNotSupportedHashAlgo means selected hash algorithm is not supported (yet?)
|
||||
type ErrNotSupportedHashAlgo struct {
|
||||
TPMAlgo TPMAlgorithm
|
||||
}
|
||||
|
||||
// Error implements interface `error`.
|
||||
func (err ErrNotSupportedHashAlgo) Error() string {
|
||||
return fmt.Sprintf("not supported hash algorithm: 0x%x", err.TPMAlgo)
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package tpmeventlog
|
||||
|
||||
// EventType defines the kind of data reported by an Event.
|
||||
//
|
||||
// See also: https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientSpecPlat_TPM_2p0_1p04_pub.pdf#page=103
|
||||
type EventType uint32
|
||||
|
||||
const (
|
||||
EV_PREBOOT_CERT = EventType(0x00000000)
|
||||
EV_POST_CODE = EventType(0x00000001)
|
||||
EV_UNUSED = EventType(0x00000002)
|
||||
EV_NO_ACTION = EventType(0x00000003)
|
||||
EV_SEPARATOR = EventType(0x00000004)
|
||||
EV_ACTION = EventType(0x00000005)
|
||||
EV_EVENT_TAG = EventType(0x00000006)
|
||||
EV_S_CRTM_CONTENTS = EventType(0x00000007)
|
||||
EV_S_CRTM_VERSION = EventType(0x00000008)
|
||||
EV_CPU_MICROCODE = EventType(0x00000009)
|
||||
EV_PLATFORM_CONFIG_FLAGS = EventType(0x0000000A)
|
||||
EV_TABLE_OF_DEVICES = EventType(0x0000000B)
|
||||
EV_COMPACT_HASH = EventType(0x0000000C)
|
||||
EV_IPL = EventType(0x0000000D)
|
||||
EV_IPL_PARTITION_DATA = EventType(0x0000000E)
|
||||
EV_NONHOST_CODE = EventType(0x0000000F)
|
||||
EV_NONHOST_CONFIG = EventType(0x00000010)
|
||||
EV_NONHOST_INFO = EventType(0x00000011)
|
||||
EV_OMIT_BOOT_DEVICE_EVENTS = EventType(0x00000012)
|
||||
EV_EFI_EVENT_BASE = EventType(0x80000000)
|
||||
EV_EFI_VARIABLE_DRIVER_CONFIG = EventType(0x80000001)
|
||||
EV_EFI_VARIABLE_BOOT = EventType(0x80000002)
|
||||
EV_EFI_BOOT_SERVICES_APPLICATION = EventType(0x80000003)
|
||||
EV_EFI_BOOT_SERVICES_DRIVER = EventType(0x80000004)
|
||||
EV_EFI_RUNTIME_SERVICES_DRIVER = EventType(0x80000005)
|
||||
EV_EFI_GPT_EVENT = EventType(0x80000006)
|
||||
EV_EFI_ACTION = EventType(0x80000007)
|
||||
EV_EFI_PLATFORM_FIRMWARE_BLOB = EventType(0x80000008)
|
||||
EV_EFI_HANDOFF_TABLES = EventType(0x80000009)
|
||||
EV_EFI_HCRTM_EVENT = EventType(0x80000010)
|
||||
EV_EFI_VARIABLE_AUTHORITY = EventType(0x800000E0)
|
||||
)
|
|
@ -0,0 +1,52 @@
|
|||
package tpmeventlog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
pcr "github.com/9elements/converged-security-suite/v2/pkg/pcr/types"
|
||||
)
|
||||
|
||||
// ParseLocality parses TPM locality from EV_NO_ACTION event corresponding
|
||||
// to the TPM initialization.
|
||||
func ParseLocality(eventData []byte) (uint8, error) {
|
||||
// There no known way to reliably detect the locality using the event log,
|
||||
// but here we will add working recipes for specific cases.
|
||||
|
||||
// event.Data example: "StartupLocality\x00\x03"
|
||||
descrWords := bytes.Split(eventData, []byte{0})
|
||||
switch {
|
||||
case bytes.Compare(descrWords[0], []byte("StartupLocality")) == 0:
|
||||
if len(descrWords) > 0 && len(descrWords[1]) == 1 {
|
||||
return descrWords[1][0], nil
|
||||
}
|
||||
}
|
||||
return 0, ErrLocality{EventData: eventData}
|
||||
}
|
||||
|
||||
// FilterEvents returns only the events which has a specified PCR index and
|
||||
// a digest of a specified hash algorithm.
|
||||
func (eventLog *TPMEventLog) FilterEvents(pcrIndex pcr.ID, hashAlgo TPMAlgorithm) ([]*Event, error) {
|
||||
hash, err := hashAlgo.Hash()
|
||||
if err != nil {
|
||||
return nil, ErrNotSupportedHashAlgo{TPMAlgo: hashAlgo}
|
||||
}
|
||||
hasher := hash.HashFunc()
|
||||
|
||||
var result []*Event
|
||||
for _, event := range eventLog.Events {
|
||||
if event.PCRIndex != pcrIndex {
|
||||
continue
|
||||
}
|
||||
if event.Digest == nil || event.Digest.HashAlgo != hashAlgo {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(event.Digest.Digest) != hasher.Size() {
|
||||
return nil, ErrInvalidDigestLength{Expected: hasher.Size(), Received: len(event.Digest.Digest)}
|
||||
}
|
||||
|
||||
result = append(result, event)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package tpmeventlog
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/google/go-attestation/attest"
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
|
||||
pcr "github.com/9elements/converged-security-suite/v2/pkg/pcr/types"
|
||||
)
|
||||
|
||||
// TPMEventLog is a parsed EventLog.
|
||||
type TPMEventLog struct {
|
||||
Events []*Event
|
||||
}
|
||||
|
||||
// TPMAlgorithm is an identified of a TPM-supported hash algorithm.
|
||||
//
|
||||
// See also: https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf#page=42
|
||||
type TPMAlgorithm = tpm2.Algorithm
|
||||
|
||||
// Event is a single entry of a parsed EventLog.
|
||||
type Event struct {
|
||||
PCRIndex pcr.ID
|
||||
Type EventType
|
||||
Data []byte
|
||||
Digest *Digest
|
||||
}
|
||||
|
||||
// Digest is the digest reported by an Event.
|
||||
type Digest struct {
|
||||
HashAlgo TPMAlgorithm
|
||||
Digest []byte
|
||||
}
|
||||
|
||||
const (
|
||||
// TPMAlgorithmSHA1 is the identified of SHA1 algorithm.
|
||||
TPMAlgorithmSHA1 = tpm2.AlgSHA1
|
||||
|
||||
// TPMAlgorithmSHA256 is the identified of SHA256 algorithm.
|
||||
TPMAlgorithmSHA256 = tpm2.AlgSHA256
|
||||
)
|
||||
|
||||
// Parse parses a binary EventLog.
|
||||
func Parse(input io.Reader) (*TPMEventLog, error) {
|
||||
b, err := ioutil.ReadAll(input)
|
||||
if err != nil {
|
||||
return nil, ErrRead{Err: err}
|
||||
}
|
||||
eventLog, err := attest.ParseEventLog(b)
|
||||
if err != nil {
|
||||
return nil, ErrParse{Err: err}
|
||||
}
|
||||
|
||||
result := &TPMEventLog{}
|
||||
for _, alg := range eventLog.Algs {
|
||||
for _, entry := range eventLog.Events(alg) {
|
||||
result.Events = append(result.Events, &Event{
|
||||
PCRIndex: pcr.ID(entry.Index),
|
||||
Type: EventType(entry.Type),
|
||||
Data: entry.Data,
|
||||
Digest: &Digest{
|
||||
HashAlgo: TPMAlgorithm(alg),
|
||||
Digest: entry.Digest,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
Loading…
Reference in New Issue