Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an initial implementation of parsing event types #108

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

mjg59
Copy link
Collaborator

@mjg59 mjg59 commented Sep 20, 2019

Raw events aren't enormously helpful. Add an initial implementation of
some of the basic event types - this is largely a scratch proposal so we
can figure out API design.

@ericchiang
Copy link
Member

Can you please put this in a different package for a bit? This would double the amount of exported API and we've worked hard to get the godoc to be digestible (#63): https://godoc.org/github.com/google/go-attestation/attest

With that in mind, I think we can avoid making the user type cast every event back to the type we want. Tom and I were discussing having Parse methods that parsed the event body. e.g.

type MicrocodeEvent struct {
    // ...
}
func ParseMicrocodeEvent(eventData []byte) (*MicrocodeEvent, error) {
    // ...
}

So the user can iterate through the events and use the corresponding Parse method

for _, e := range events {
    switch e.Type {
    case eventlog.TypeCPUMicrocode:
        mcEvent, err := eventlog.ParseMicrocodeEvent(e.Data)
    // ... 
    }
}

@mjg59
Copy link
Collaborator Author

mjg59 commented Sep 20, 2019

Can you please put this in a different package for a bit? This would double the amount of exported API and we've worked hard to get the godoc to be digestible (#63): https://godoc.org/github.com/google/go-attestation/attest

Sure, though that'll mean exporting rawEvent?

With that in mind, I think we can avoid making the user type cast every event back to the type we want. Tom and I were discussing having Parse methods that parsed the event body. e.g.

for _, e := range events {
switch e.Type {
case eventlog.TypeCPUMicrocode:
mcEvent, err := eventlog.ParseMicrocodeEvent(e.Data)

Is this really easier than

switch e.Type {
  case eventlog.TypeCPUMicrocode:
     mcEvent, ok := e.(CPUMicrocodeEvent)

? Splitting out the individual parse methods means a lot of basically identical functions, which feels a little less clean than basically doing the same in a switch statement (although for more complex types, we should probably also be exporting information about whether we succeeded in parsing the event)

@ericchiang
Copy link
Member

You can use the exported Event type :) https://godoc.org/github.com/google/go-attestation/attest#Event

I think both strategies are similar amounts of code for the caller. For example to get a single event:

events, err := eventlog.ParseEvents(verifiedEvents)
if err != nil {
    // ...
}
for _, e := range events {
    if pbce, ok := e.(eventlog.PrebootCertEvent); ok {
        // use parsed event
    }
}

vs.

for _, e := range events {
    if e.Type == eventlog.TypePrebootCertEvent {
        pbce, err := eventlog.ParsePrebootCertEvent(e.Data)
        if err != nil {
            // ...
        }
        // use parsed event
    }
}

I suggested the latter because there's a clearer place for users to parse event types that we don't provide a parser for.

Either way, I don't think there's a huge difference. Feel free to go with either API

@mjg59
Copy link
Collaborator Author

mjg59 commented Sep 20, 2019

You can use the exported Event type :) https://godoc.org/github.com/google/go-attestation/attest#Event

But EventLog contains an array of rawEvent rather than Event?

@ericchiang
Copy link
Member

Part of the API design is to require replaying the event log against PCRs before moving forward https://godoc.org/github.com/google/go-attestation/attest#EventLog.Verify

We want users of the API to be safe by default, and not mistakenly use something that's not been verified.

@mjg59
Copy link
Collaborator Author

mjg59 commented Sep 20, 2019

Oh, yes, I see what you mean now.

events/events.go Outdated Show resolved Hide resolved
events/events.go Outdated Show resolved Hide resolved
events/events.go Outdated Show resolved Hide resolved
@mjg59 mjg59 force-pushed the master branch 5 times, most recently from 4b93759 to 6f41e62 Compare October 11, 2019 05:01
@mjg59
Copy link
Collaborator Author

mjg59 commented Oct 11, 2019

Ok, added support for Windows event types and tests against a bunch of sample event logs.

Copy link
Member

@ericchiang ericchiang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some comments, but this PR got a lot bigger before addressing the previous review. To avoid a moving target, can we get the original code merged first? :)

Reviewed the existing files, and will start looking at the events/windows.go later. Sorry I know the timezone difference isn't helping here.

events/events.go Outdated
return ""
}
uuid := uuid.UUID{}
if err := uuid.UnmarshalBinary(buf.Bytes()); err != nil {
Copy link
Member

@ericchiang ericchiang Oct 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of importing a uuid package, please do:

func (d efiGUID) String() string {
    data1 := [4]byte{}
    data2 := [2]byte{}
    data3 := [2]byte{}
    binary.BigEndian.PutUint32(data1[:], d.Data1)
    binary.BigEndian.PutUint16(data2[:], d.Data2)
    binary.BigEndian.PutUint16(data3[:], d.Data3)
    return fmt.Sprintf("%x-%x-%x-%x", data1, data2, data3, d.Data4)
}

events/events.go Outdated
}

if !bootOption.MatchString(parsedEvent.VariableName) {
return nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not an error here?

events/events.go Outdated
return fmt.Errorf("malformed boot variable")
}

parsedEvent.DevicePathRaw = make([]byte, dplength)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, this could be a dos vector like #126 and #128. At the point where we're parsing the event, we don't actually trust the device.

events/events.go Outdated
// Check whether there's any optional data
optionaldatalen := len(parsedEvent.VariableData) - dpoffset - int(dplength)
if optionaldatalen > 0 {
parsedEvent.OptionalData = make([]byte, optionaldatalen)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same dos vector as above

events/events.go Outdated
if err != nil {
return err
}
parsedEvent.DevicePathRaw = make([]byte, devicePathLength)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

events/events.go Outdated
if err != nil {
return err
}
parsedEvent.Tables = make([]efiConfigurationTable, numTables)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same dos

events/events.go Outdated
if err != nil {
return err
}
unicodeName := make([]uint16, header.UnicodeNameLength)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same dos

return fmt.Sprintf("%s(%d,%02x)", prefix, dp.SubType, data[:])
}

// efiDevicePath translates an EFI Device Path into the canonical string representation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got a link for where the canonical representation is specified?

@mjg59 mjg59 force-pushed the master branch 3 times, most recently from d4551cc to fbfc4a7 Compare May 23, 2023 19:09
mjg59 and others added 4 commits May 24, 2023 12:16
There's currently no support for creating application keys on Windows systems. This patch transitions the Windows key type to specifically refer to attestation keys, and reuses the existing wrapped key support for application keys. This allows the creation of keys in the platform store, while still allowing said keys to be manipulated with existing TPM functionality rather than duplicating it.
When generating a new key using a Windows TPM, a `wrappedKey20` was
returned, which couldn't be used for signing on Windows, as it's
backed by a `windowsTPM`. The `wrappedKey20` seems to be a type
specifically aimed at usage with a `wrappedTPM20`, which in turn
seems to be used on Linux and for testing, but not when instantiating
a TPM on Windows.

This commit adds the `newWindowsKey20` function, which returns
a key backed by a `windowsTPM`. The key is a `windowsAK20`,
now also conforming to the `key` interface, so that it can be used
for signing purposes.
My system is returning RCScheme if TPM_ALG_NULL is passed here. This should
be causing the key's default scheme to be used, but for some reason it
seems unhappy. Just explicitly set the scheme for now to avoid that.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants