diff --git a/go.mod b/go.mod index c3ef054f..2fcb3edb 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ module github.com/google/go-tpm-tools go 1.16 require ( + github.com/google/certificate-transparency-go v1.1.1 github.com/google/go-attestation v0.3.2 github.com/google/go-tpm v0.3.2 github.com/spf13/cobra v1.1.3 diff --git a/proto/attest.proto b/proto/attest.proto index 95fee876..ff383f45 100644 --- a/proto/attest.proto +++ b/proto/attest.proto @@ -65,11 +65,54 @@ message Event { bool digest_verified = 5; } + // Common, publicly-listed certificates by different vendors. + enum WellKnownCertificate { + UNKNOWN = 0; + + // Microsoft certs: + // https://go.microsoft.com/fwlink/p/?linkid=321192 + MS_WINDOWS_PROD_PCA_2011 = 1; + // https://go.microsoft.com/fwlink/p/?linkid=321194 + MS_THIRD_PARTY_UEFI_CA_2011 = 2; + } + +message Certificate { + + // The representation of the certificate. If the certificate matches a + // well-known certificate above, representation should contain the value in + // the enum. Otherwise, it will contain the raw DER. + oneof representation { + // DER representation of the certificate. + bytes der = 1; + WellKnownCertificate well_known = 2; + } +} + +// A Secure Boot database containing lists of hashes and certificates, +// as defined by section 32.4.1 Signature Database in the UEFI spec. +message Database { + repeated Certificate certs = 1; + repeated bytes hashes = 2; +} + +// The Secure Boot state for this instance. +message SecureBootState { + // Whether Secure Boot is enabled. + bool enabled = 1; + // The Secure Boot signature (allowed) database. + Database db = 2; + // The Secure Boot revoked signature (forbidden) database. + Database dbx = 3; + // Authority events post-separator. Pre-separator authorities + // are currently not supported. + Database authority = 4; +} + // The verified state of a booted machine, obtained from an Attestation message MachineState { PlatformState platform = 1; - // SecureBootState secure_boot = 2; + SecureBootState secure_boot = 2; // The complete parsed TCG Event Log, including those events used to // create the PlatformState. diff --git a/proto/attest/attest.pb.go b/proto/attest/attest.pb.go index 82a1501e..07aa5713 100644 --- a/proto/attest/attest.pb.go +++ b/proto/attest/attest.pb.go @@ -71,6 +71,59 @@ func (GCEConfidentialTechnology) EnumDescriptor() ([]byte, []int) { return file_attest_proto_rawDescGZIP(), []int{0} } +// Common, publicly-listed certificates by different vendors. +type WellKnownCertificate int32 + +const ( + WellKnownCertificate_UNKNOWN WellKnownCertificate = 0 + // Microsoft certs: + // https://go.microsoft.com/fwlink/p/?linkid=321192 + WellKnownCertificate_MS_WINDOWS_PROD_PCA_2011 WellKnownCertificate = 1 + // https://go.microsoft.com/fwlink/p/?linkid=321194 + WellKnownCertificate_MS_THIRD_PARTY_UEFI_CA_2011 WellKnownCertificate = 2 +) + +// Enum value maps for WellKnownCertificate. +var ( + WellKnownCertificate_name = map[int32]string{ + 0: "UNKNOWN", + 1: "MS_WINDOWS_PROD_PCA_2011", + 2: "MS_THIRD_PARTY_UEFI_CA_2011", + } + WellKnownCertificate_value = map[string]int32{ + "UNKNOWN": 0, + "MS_WINDOWS_PROD_PCA_2011": 1, + "MS_THIRD_PARTY_UEFI_CA_2011": 2, + } +) + +func (x WellKnownCertificate) Enum() *WellKnownCertificate { + p := new(WellKnownCertificate) + *p = x + return p +} + +func (x WellKnownCertificate) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (WellKnownCertificate) Descriptor() protoreflect.EnumDescriptor { + return file_attest_proto_enumTypes[1].Descriptor() +} + +func (WellKnownCertificate) Type() protoreflect.EnumType { + return &file_attest_proto_enumTypes[1] +} + +func (x WellKnownCertificate) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use WellKnownCertificate.Descriptor instead. +func (WellKnownCertificate) EnumDescriptor() ([]byte, []int) { + return file_attest_proto_rawDescGZIP(), []int{1} +} + // Information uniquely identifying a GCE instance. Can be used to create an // instance URL, which can then be used with GCE APIs. Formatted like: // https://www.googleapis.com/compute/v1/projects/{project_id}/zones/{zone}/instances/{instance_name} @@ -417,13 +470,233 @@ func (x *Event) GetDigestVerified() bool { return false } +type Certificate struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The representation of the certificate. If the certificate matches a + // well-known certificate above, representation should contain the value in + // the enum. Otherwise, it will contain the raw DER. + // + // Types that are assignable to Representation: + // *Certificate_Der + // *Certificate_WellKnown + Representation isCertificate_Representation `protobuf_oneof:"representation"` +} + +func (x *Certificate) Reset() { + *x = Certificate{} + if protoimpl.UnsafeEnabled { + mi := &file_attest_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Certificate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Certificate) ProtoMessage() {} + +func (x *Certificate) ProtoReflect() protoreflect.Message { + mi := &file_attest_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Certificate.ProtoReflect.Descriptor instead. +func (*Certificate) Descriptor() ([]byte, []int) { + return file_attest_proto_rawDescGZIP(), []int{4} +} + +func (m *Certificate) GetRepresentation() isCertificate_Representation { + if m != nil { + return m.Representation + } + return nil +} + +func (x *Certificate) GetDer() []byte { + if x, ok := x.GetRepresentation().(*Certificate_Der); ok { + return x.Der + } + return nil +} + +func (x *Certificate) GetWellKnown() WellKnownCertificate { + if x, ok := x.GetRepresentation().(*Certificate_WellKnown); ok { + return x.WellKnown + } + return WellKnownCertificate_UNKNOWN +} + +type isCertificate_Representation interface { + isCertificate_Representation() +} + +type Certificate_Der struct { + // DER representation of the certificate. + Der []byte `protobuf:"bytes,1,opt,name=der,proto3,oneof"` +} + +type Certificate_WellKnown struct { + WellKnown WellKnownCertificate `protobuf:"varint,2,opt,name=well_known,json=wellKnown,proto3,enum=attest.WellKnownCertificate,oneof"` +} + +func (*Certificate_Der) isCertificate_Representation() {} + +func (*Certificate_WellKnown) isCertificate_Representation() {} + +// A Secure Boot database containing lists of hashes and certificates, +// as defined by section 32.4.1 Signature Database in the UEFI spec. +type Database struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Certs []*Certificate `protobuf:"bytes,1,rep,name=certs,proto3" json:"certs,omitempty"` + Hashes [][]byte `protobuf:"bytes,2,rep,name=hashes,proto3" json:"hashes,omitempty"` +} + +func (x *Database) Reset() { + *x = Database{} + if protoimpl.UnsafeEnabled { + mi := &file_attest_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Database) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Database) ProtoMessage() {} + +func (x *Database) ProtoReflect() protoreflect.Message { + mi := &file_attest_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Database.ProtoReflect.Descriptor instead. +func (*Database) Descriptor() ([]byte, []int) { + return file_attest_proto_rawDescGZIP(), []int{5} +} + +func (x *Database) GetCerts() []*Certificate { + if x != nil { + return x.Certs + } + return nil +} + +func (x *Database) GetHashes() [][]byte { + if x != nil { + return x.Hashes + } + return nil +} + +// The Secure Boot state for this instance. +type SecureBootState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Whether Secure Boot is enabled. + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + // The Secure Boot signature (allowed) database. + Db *Database `protobuf:"bytes,2,opt,name=db,proto3" json:"db,omitempty"` + // The Secure Boot revoked signature (forbidden) database. + Dbx *Database `protobuf:"bytes,3,opt,name=dbx,proto3" json:"dbx,omitempty"` + // Authority events post-separator. Pre-separator authorities + // are currently not supported. + Authority *Database `protobuf:"bytes,4,opt,name=authority,proto3" json:"authority,omitempty"` +} + +func (x *SecureBootState) Reset() { + *x = SecureBootState{} + if protoimpl.UnsafeEnabled { + mi := &file_attest_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SecureBootState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SecureBootState) ProtoMessage() {} + +func (x *SecureBootState) ProtoReflect() protoreflect.Message { + mi := &file_attest_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SecureBootState.ProtoReflect.Descriptor instead. +func (*SecureBootState) Descriptor() ([]byte, []int) { + return file_attest_proto_rawDescGZIP(), []int{6} +} + +func (x *SecureBootState) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *SecureBootState) GetDb() *Database { + if x != nil { + return x.Db + } + return nil +} + +func (x *SecureBootState) GetDbx() *Database { + if x != nil { + return x.Dbx + } + return nil +} + +func (x *SecureBootState) GetAuthority() *Database { + if x != nil { + return x.Authority + } + return nil +} + // The verified state of a booted machine, obtained from an Attestation type MachineState struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Platform *PlatformState `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform,omitempty"` + Platform *PlatformState `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform,omitempty"` + SecureBoot *SecureBootState `protobuf:"bytes,2,opt,name=secure_boot,json=secureBoot,proto3" json:"secure_boot,omitempty"` // The complete parsed TCG Event Log, including those events used to // create the PlatformState. RawEvents []*Event `protobuf:"bytes,3,rep,name=raw_events,json=rawEvents,proto3" json:"raw_events,omitempty"` @@ -436,7 +709,7 @@ type MachineState struct { func (x *MachineState) Reset() { *x = MachineState{} if protoimpl.UnsafeEnabled { - mi := &file_attest_proto_msgTypes[4] + mi := &file_attest_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -449,7 +722,7 @@ func (x *MachineState) String() string { func (*MachineState) ProtoMessage() {} func (x *MachineState) ProtoReflect() protoreflect.Message { - mi := &file_attest_proto_msgTypes[4] + mi := &file_attest_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -462,7 +735,7 @@ func (x *MachineState) ProtoReflect() protoreflect.Message { // Deprecated: Use MachineState.ProtoReflect.Descriptor instead. func (*MachineState) Descriptor() ([]byte, []int) { - return file_attest_proto_rawDescGZIP(), []int{4} + return file_attest_proto_rawDescGZIP(), []int{7} } func (x *MachineState) GetPlatform() *PlatformState { @@ -472,6 +745,13 @@ func (x *MachineState) GetPlatform() *PlatformState { return nil } +func (x *MachineState) GetSecureBoot() *SecureBootState { + if x != nil { + return x.SecureBoot + } + return nil +} + func (x *MachineState) GetRawEvents() []*Event { if x != nil { return x.RawEvents @@ -507,7 +787,7 @@ type PlatformPolicy struct { func (x *PlatformPolicy) Reset() { *x = PlatformPolicy{} if protoimpl.UnsafeEnabled { - mi := &file_attest_proto_msgTypes[5] + mi := &file_attest_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -520,7 +800,7 @@ func (x *PlatformPolicy) String() string { func (*PlatformPolicy) ProtoMessage() {} func (x *PlatformPolicy) ProtoReflect() protoreflect.Message { - mi := &file_attest_proto_msgTypes[5] + mi := &file_attest_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -533,7 +813,7 @@ func (x *PlatformPolicy) ProtoReflect() protoreflect.Message { // Deprecated: Use PlatformPolicy.ProtoReflect.Descriptor instead. func (*PlatformPolicy) Descriptor() ([]byte, []int) { - return file_attest_proto_rawDescGZIP(), []int{5} + return file_attest_proto_rawDescGZIP(), []int{8} } func (x *PlatformPolicy) GetAllowedScrtmVersionIds() [][]byte { @@ -569,7 +849,7 @@ type Policy struct { func (x *Policy) Reset() { *x = Policy{} if protoimpl.UnsafeEnabled { - mi := &file_attest_proto_msgTypes[6] + mi := &file_attest_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -582,7 +862,7 @@ func (x *Policy) String() string { func (*Policy) ProtoMessage() {} func (x *Policy) ProtoReflect() protoreflect.Message { - mi := &file_attest_proto_msgTypes[6] + mi := &file_attest_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -595,7 +875,7 @@ func (x *Policy) ProtoReflect() protoreflect.Message { // Deprecated: Use Policy.ProtoReflect.Descriptor instead. func (*Policy) Descriptor() ([]byte, []int) { - return file_attest_proto_rawDescGZIP(), []int{6} + return file_attest_proto_rawDescGZIP(), []int{9} } func (x *Policy) GetPlatform() *PlatformPolicy { @@ -656,42 +936,74 @@ var file_attest_proto_rawDesc = []byte{ 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x64, 0x69, - 0x67, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x22, 0x92, 0x01, 0x0a, - 0x0c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x31, 0x0a, + 0x67, 0x65, 0x73, 0x74, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x22, 0x72, 0x0a, 0x0b, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x64, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x03, 0x64, 0x65, 0x72, 0x12, + 0x3d, 0x0a, 0x0a, 0x77, 0x65, 0x6c, 0x6c, 0x5f, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x57, 0x65, 0x6c, + 0x6c, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x48, 0x00, 0x52, 0x09, 0x77, 0x65, 0x6c, 0x6c, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x42, 0x10, + 0x0a, 0x0e, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0x4d, 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x05, + 0x63, 0x65, 0x72, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x52, 0x05, 0x63, 0x65, 0x72, 0x74, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x22, + 0xa1, 0x01, 0x0a, 0x0f, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x42, 0x6f, 0x6f, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, + 0x02, 0x64, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x02, 0x64, 0x62, 0x12, + 0x22, 0x0a, 0x03, 0x64, 0x62, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x03, + 0x64, 0x62, 0x78, 0x12, 0x2e, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, + 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x22, 0xcc, 0x01, 0x0a, 0x0c, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, + 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x08, 0x70, + 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x38, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x75, 0x72, + 0x65, 0x5f, 0x62, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x42, 0x6f, 0x6f, 0x74, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x42, 0x6f, 0x6f, + 0x74, 0x12, 0x2c, 0x0a, 0x0a, 0x72, 0x61, 0x77, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x52, 0x09, 0x72, 0x61, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, + 0x21, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, + 0x74, 0x70, 0x6d, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x41, 0x6c, 0x67, 0x6f, 0x52, 0x04, 0x68, 0x61, + 0x73, 0x68, 0x22, 0xde, 0x01, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x39, 0x0a, 0x19, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, + 0x5f, 0x73, 0x63, 0x72, 0x74, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x16, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, + 0x64, 0x53, 0x63, 0x72, 0x74, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, + 0x12, 0x3f, 0x0a, 0x1c, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x67, 0x63, 0x65, 0x5f, + 0x66, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x19, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x47, + 0x63, 0x65, 0x46, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x50, 0x0a, 0x12, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x74, 0x65, 0x63, + 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, + 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x47, 0x43, 0x45, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x79, + 0x52, 0x11, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, + 0x6f, 0x67, 0x79, 0x22, 0x3c, 0x0a, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x32, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, - 0x6d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, - 0x12, 0x2c, 0x0a, 0x0a, 0x72, 0x61, 0x77, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x52, 0x09, 0x72, 0x61, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x21, - 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0d, 0x2e, 0x74, - 0x70, 0x6d, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x41, 0x6c, 0x67, 0x6f, 0x52, 0x04, 0x68, 0x61, 0x73, - 0x68, 0x22, 0xde, 0x01, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x12, 0x39, 0x0a, 0x19, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, - 0x73, 0x63, 0x72, 0x74, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x16, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, - 0x53, 0x63, 0x72, 0x74, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, - 0x3f, 0x0a, 0x1c, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x67, 0x63, 0x65, 0x5f, 0x66, - 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x19, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x47, 0x63, - 0x65, 0x46, 0x69, 0x72, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x50, 0x0a, 0x12, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x74, 0x65, 0x63, 0x68, - 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x61, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x47, 0x43, 0x45, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x52, - 0x11, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, - 0x67, 0x79, 0x22, 0x3c, 0x0a, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x32, 0x0a, 0x08, - 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, - 0x2a, 0x42, 0x0a, 0x19, 0x47, 0x43, 0x45, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x61, 0x6c, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x12, 0x08, 0x0a, - 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x44, 0x5f, 0x53, - 0x45, 0x56, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x4d, 0x44, 0x5f, 0x53, 0x45, 0x56, 0x5f, - 0x45, 0x53, 0x10, 0x02, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x67, 0x6f, 0x2d, 0x74, 0x70, 0x6d, - 0x2d, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x74, 0x74, - 0x65, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x16, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, + 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, + 0x6d, 0x2a, 0x42, 0x0a, 0x19, 0x47, 0x43, 0x45, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x79, 0x12, 0x08, + 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x4d, 0x44, 0x5f, + 0x53, 0x45, 0x56, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x4d, 0x44, 0x5f, 0x53, 0x45, 0x56, + 0x5f, 0x45, 0x53, 0x10, 0x02, 0x2a, 0x62, 0x0a, 0x14, 0x57, 0x65, 0x6c, 0x6c, 0x4b, 0x6e, 0x6f, + 0x77, 0x6e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, + 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x4d, 0x53, + 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x5f, 0x50, 0x52, 0x4f, 0x44, 0x5f, 0x50, 0x43, + 0x41, 0x5f, 0x32, 0x30, 0x31, 0x31, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x4d, 0x53, 0x5f, 0x54, + 0x48, 0x49, 0x52, 0x44, 0x5f, 0x50, 0x41, 0x52, 0x54, 0x59, 0x5f, 0x55, 0x45, 0x46, 0x49, 0x5f, + 0x43, 0x41, 0x5f, 0x32, 0x30, 0x31, 0x31, 0x10, 0x02, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x67, + 0x6f, 0x2d, 0x74, 0x70, 0x6d, 0x2d, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -706,35 +1018,45 @@ func file_attest_proto_rawDescGZIP() []byte { return file_attest_proto_rawDescData } -var file_attest_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_attest_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_attest_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_attest_proto_msgTypes = make([]protoimpl.MessageInfo, 10) var file_attest_proto_goTypes = []interface{}{ (GCEConfidentialTechnology)(0), // 0: attest.GCEConfidentialTechnology - (*GCEInstanceInfo)(nil), // 1: attest.GCEInstanceInfo - (*Attestation)(nil), // 2: attest.Attestation - (*PlatformState)(nil), // 3: attest.PlatformState - (*Event)(nil), // 4: attest.Event - (*MachineState)(nil), // 5: attest.MachineState - (*PlatformPolicy)(nil), // 6: attest.PlatformPolicy - (*Policy)(nil), // 7: attest.Policy - (*tpm.Quote)(nil), // 8: tpm.Quote - (tpm.HashAlgo)(0), // 9: tpm.HashAlgo + (WellKnownCertificate)(0), // 1: attest.WellKnownCertificate + (*GCEInstanceInfo)(nil), // 2: attest.GCEInstanceInfo + (*Attestation)(nil), // 3: attest.Attestation + (*PlatformState)(nil), // 4: attest.PlatformState + (*Event)(nil), // 5: attest.Event + (*Certificate)(nil), // 6: attest.Certificate + (*Database)(nil), // 7: attest.Database + (*SecureBootState)(nil), // 8: attest.SecureBootState + (*MachineState)(nil), // 9: attest.MachineState + (*PlatformPolicy)(nil), // 10: attest.PlatformPolicy + (*Policy)(nil), // 11: attest.Policy + (*tpm.Quote)(nil), // 12: tpm.Quote + (tpm.HashAlgo)(0), // 13: tpm.HashAlgo } var file_attest_proto_depIdxs = []int32{ - 8, // 0: attest.Attestation.quotes:type_name -> tpm.Quote - 1, // 1: attest.Attestation.instance_info:type_name -> attest.GCEInstanceInfo - 0, // 2: attest.PlatformState.technology:type_name -> attest.GCEConfidentialTechnology - 1, // 3: attest.PlatformState.instance_info:type_name -> attest.GCEInstanceInfo - 3, // 4: attest.MachineState.platform:type_name -> attest.PlatformState - 4, // 5: attest.MachineState.raw_events:type_name -> attest.Event - 9, // 6: attest.MachineState.hash:type_name -> tpm.HashAlgo - 0, // 7: attest.PlatformPolicy.minimum_technology:type_name -> attest.GCEConfidentialTechnology - 6, // 8: attest.Policy.platform:type_name -> attest.PlatformPolicy - 9, // [9:9] is the sub-list for method output_type - 9, // [9:9] is the sub-list for method input_type - 9, // [9:9] is the sub-list for extension type_name - 9, // [9:9] is the sub-list for extension extendee - 0, // [0:9] is the sub-list for field type_name + 12, // 0: attest.Attestation.quotes:type_name -> tpm.Quote + 2, // 1: attest.Attestation.instance_info:type_name -> attest.GCEInstanceInfo + 0, // 2: attest.PlatformState.technology:type_name -> attest.GCEConfidentialTechnology + 2, // 3: attest.PlatformState.instance_info:type_name -> attest.GCEInstanceInfo + 1, // 4: attest.Certificate.well_known:type_name -> attest.WellKnownCertificate + 6, // 5: attest.Database.certs:type_name -> attest.Certificate + 7, // 6: attest.SecureBootState.db:type_name -> attest.Database + 7, // 7: attest.SecureBootState.dbx:type_name -> attest.Database + 7, // 8: attest.SecureBootState.authority:type_name -> attest.Database + 4, // 9: attest.MachineState.platform:type_name -> attest.PlatformState + 8, // 10: attest.MachineState.secure_boot:type_name -> attest.SecureBootState + 5, // 11: attest.MachineState.raw_events:type_name -> attest.Event + 13, // 12: attest.MachineState.hash:type_name -> tpm.HashAlgo + 0, // 13: attest.PlatformPolicy.minimum_technology:type_name -> attest.GCEConfidentialTechnology + 10, // 14: attest.Policy.platform:type_name -> attest.PlatformPolicy + 15, // [15:15] is the sub-list for method output_type + 15, // [15:15] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name } func init() { file_attest_proto_init() } @@ -792,7 +1114,7 @@ func file_attest_proto_init() { } } file_attest_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MachineState); i { + switch v := v.(*Certificate); i { case 0: return &v.state case 1: @@ -804,7 +1126,7 @@ func file_attest_proto_init() { } } file_attest_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PlatformPolicy); i { + switch v := v.(*Database); i { case 0: return &v.state case 1: @@ -816,6 +1138,42 @@ func file_attest_proto_init() { } } file_attest_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SecureBootState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_attest_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MachineState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_attest_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PlatformPolicy); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_attest_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Policy); i { case 0: return &v.state @@ -832,13 +1190,17 @@ func file_attest_proto_init() { (*PlatformState_ScrtmVersionId)(nil), (*PlatformState_GceVersion)(nil), } + file_attest_proto_msgTypes[4].OneofWrappers = []interface{}{ + (*Certificate_Der)(nil), + (*Certificate_WellKnown)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_attest_proto_rawDesc, - NumEnums: 1, - NumMessages: 7, + NumEnums: 2, + NumMessages: 10, NumExtensions: 0, NumServices: 0, }, diff --git a/server/eventlog.go b/server/eventlog.go index bb01ac58..4b13d9d0 100644 --- a/server/eventlog.go +++ b/server/eventlog.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" + "github.com/google/certificate-transparency-go/x509" "github.com/google/go-attestation/attest" pb "github.com/google/go-tpm-tools/proto/attest" tpmpb "github.com/google/go-tpm-tools/proto/tpm" @@ -14,16 +15,22 @@ import ( // ParseMachineState parses a raw event log and replays the parsed event // log against the given PCR values. It returns the corresponding MachineState -// containing the events verified by particular PCR indexes/digests. An error is -// returned if the replay for any PCR index does not match the provided value. +// containing the events verified by particular PCR indexes/digests. It returns +// an error if the replay for any PCR index does not match the provided value. +// +// The returned MachineState may be a partial MachineState where fields can be +// the zero value. In this case, an error of type MachineStateError will be +// returned. Callers can inspect individual parsing errors by examining +// `MachineStateError.Errors`. // // It is the caller's responsibility to ensure that the passed PCR values can be // trusted. Users can establish trust in PCR values by either calling // client.ReadPCRs() themselves or by verifying the values via a PCR quote. func ParseMachineState(rawEventLog []byte, pcrs *tpmpb.PCRs) (*pb.MachineState, error) { + var errors []error events, err := parseReplayHelper(rawEventLog, pcrs) if err != nil { - return nil, err + return nil, createGroupedError("", []error{err}) } // error is already checked in convertToAttestPcrs cryptoHash, _ := tpm2.Algorithm(pcrs.GetHash()).Hash() @@ -31,21 +38,19 @@ func ParseMachineState(rawEventLog []byte, pcrs *tpmpb.PCRs) (*pb.MachineState, rawEvents := convertToPbEvents(cryptoHash, events) platform, err := getPlatformState(cryptoHash, rawEvents) if err != nil { - // Eventually, we want to support a partial failure model. - // The MachineState can contain empty {Platform,SecureBoot}States when - // those individually fail parsing. The error will contain suberrors - // for the fields in MachineState that failed parsing. - // - // For now, since the MachineState only comprises PlatformState, we - // return an empty MachineState with empty platform state and the error. - return &pb.MachineState{}, err + errors = append(errors, err) + } + sbState, err := getSecureBootState(events) + if err != nil { + errors = append(errors, err) } return &pb.MachineState{ - Platform: platform, - RawEvents: rawEvents, - Hash: pcrs.GetHash(), - }, nil + Platform: platform, + SecureBoot: sbState, + RawEvents: rawEvents, + Hash: pcrs.GetHash(), + }, createGroupedError("failed to fully parse MachineState:", errors) } func contains(set [][]byte, value []byte) bool { @@ -182,3 +187,47 @@ func convertToPbEvents(hash crypto.Hash, events []attest.Event) []*pb.Event { } return pbEvents } + +func convertToPbDatabase(certs []x509.Certificate, hashes [][]byte) *pb.Database { + protoCerts := make([]*pb.Certificate, 0, len(certs)) + for _, cert := range certs { + wkEnum, err := matchWellKnown(cert) + var pbCert pb.Certificate + if err == nil { + pbCert.Representation = &pb.Certificate_WellKnown{WellKnown: wkEnum} + } else { + pbCert.Representation = &pb.Certificate_Der{Der: cert.Raw} + } + protoCerts = append(protoCerts, &pbCert) + } + return &pb.Database{ + Certs: protoCerts, + Hashes: hashes, + } +} + +func matchWellKnown(cert x509.Certificate) (pb.WellKnownCertificate, error) { + if bytes.Equal(WindowsProductionPCA2011Cert, cert.Raw) { + return pb.WellKnownCertificate_MS_WINDOWS_PROD_PCA_2011, nil + } + if bytes.Equal(MicrosoftUEFICA2011Cert, cert.Raw) { + return pb.WellKnownCertificate_MS_THIRD_PARTY_UEFI_CA_2011, nil + } + return pb.WellKnownCertificate_UNKNOWN, errors.New("failed to find matching well known certificate") +} + +func getSecureBootState(attestEvents []attest.Event) (*pb.SecureBootState, error) { + attestSbState, err := attest.ParseSecurebootState(attestEvents) + if err != nil { + return nil, fmt.Errorf("failed to parse SecureBootState: %v", err) + } + if len(attestSbState.PreSeparatorAuthority) != 0 { + return nil, fmt.Errorf("event log contained %v pre-separator authorities, which are not expected or supported", len(attestSbState.PreSeparatorAuthority)) + } + return &pb.SecureBootState{ + Enabled: attestSbState.Enabled, + Db: convertToPbDatabase(attestSbState.PermittedKeys, attestSbState.PermittedHashes), + Dbx: convertToPbDatabase(attestSbState.ForbiddenKeys, attestSbState.ForbiddenHashes), + Authority: convertToPbDatabase(attestSbState.PostSeparatorAuthority, nil), + }, nil +} diff --git a/server/eventlog_test.go b/server/eventlog_test.go index b39f0cac..47e5d88e 100644 --- a/server/eventlog_test.go +++ b/server/eventlog_test.go @@ -7,6 +7,7 @@ import ( "github.com/google/go-tpm-tools/client" "github.com/google/go-tpm-tools/internal/test" + attestpb "github.com/google/go-tpm-tools/proto/attest" pb "github.com/google/go-tpm-tools/proto/tpm" "github.com/google/go-tpm/tpm2" ) @@ -16,6 +17,8 @@ type eventLog struct { Banks []*pb.PCRs } +var archLinuxBadSecureBoot = "SecureBoot data len is 0, expected 1" + // Agile Event Log from a RHEL 8 GCE instance with Secure Boot enabled var Rhel8GCE = eventLog{ RawLog: test.Rhel8EventLog, @@ -240,16 +243,24 @@ var Debian10GCE = eventLog{ } func TestParseEventLogs(t *testing.T) { + sbatErrorStr := "asn1: structure error: tags don't match (16 vs {class:0 tag:24 length:10 isCompound:true})" logs := []struct { eventLog name string + // This field handles known issues with event log parsing or bad event + // logs. + // An empty string will not attempt to pattern match the error result. + errorSubstr string }{ - {Debian10GCE, "Debian10GCE"}, - {Rhel8GCE, "Rhel8GCE"}, - {UbuntuAmdSevGCE, "UbuntuAmdSevGCE"}, - {Ubuntu2104NoDbxGCE, "Ubuntu2104NoDbxGCE"}, - {Ubuntu2104NoSecureBootGCE, "Ubuntu2104NoSecureBootGCE"}, - {ArchLinuxWorkstation, "ArchLinuxWorkstation"}, + {Debian10GCE, "Debian10GCE", ""}, + {Rhel8GCE, "Rhel8GCE", ""}, + {UbuntuAmdSevGCE, "UbuntuAmdSevGCE", ""}, + // TODO: remove once the fix is pulled in + // https://github.com/google/go-attestation/pull/222 + {Ubuntu2104NoDbxGCE, "Ubuntu2104NoDbxGCE", sbatErrorStr}, + {Ubuntu2104NoSecureBootGCE, "Ubuntu2104NoSecureBootGCE", sbatErrorStr}, + // This event log has a SecureBoot variable length of 0. + {ArchLinuxWorkstation, "ArchLinuxWorkstation", archLinuxBadSecureBoot}, } for _, log := range logs { @@ -259,13 +270,35 @@ func TestParseEventLogs(t *testing.T) { subtestName := fmt.Sprintf("%s-%s", log.name, hashName) t.Run(subtestName, func(t *testing.T) { if _, err := ParseMachineState(rawLog, bank); err != nil { - t.Errorf("failed to parse and replay log: %v", err) + gErr, ok := err.(*GroupedError) + if !ok { + t.Errorf("ParseMachineState should return a GroupedError") + } + if log.errorSubstr != "" && !gErr.containsOnlySubstring(log.errorSubstr) { + t.Errorf("failed to parse and replay log: %v", err) + } } }) } } } +func TestParseMachineStateReplayFail(t *testing.T) { + badPcrs := pb.PCRs{Hash: pb.HashAlgo_SHA1} + pcrMap := make(map[uint32][]byte) + pcrMap[0] = []byte{0, 0, 0, 0} + badPcrs.Pcrs = pcrMap + + _, err := ParseMachineState(Debian10GCE.RawLog, &badPcrs) + if err == nil { + t.Errorf("ParseMachineState should fail to replay the event log") + } + _, ok := err.(*GroupedError) + if !ok { + t.Errorf("ParseMachineState should return a GroupedError") + } +} + func TestSystemParseEventLog(t *testing.T) { rwc := test.GetTPM(t) defer client.CheckedClose(t, rwc) @@ -282,8 +315,39 @@ func TestSystemParseEventLog(t *testing.T) { } if _, err = ParseMachineState(evtLog, pcrs); err != nil { - t.Errorf("failed to parse and replay log: %v", err) + t.Errorf("failed to parse MachineState: %v", err) + } +} + +func TestParseSecureBootState(t *testing.T) { + for _, bank := range UbuntuAmdSevGCE.Banks { + msState, err := ParseMachineState(UbuntuAmdSevGCE.RawLog, bank) + if err != nil { + t.Errorf("failed to parse and replay log: %v", err) + } + containsWinProdPCA := false + contains3PUEFI := false + if len(msState.GetSecureBoot().GetDb().GetHashes()) != 0 { + t.Error("found hashes in db") + } + for _, cert := range msState.GetSecureBoot().GetDb().GetCerts() { + switch c := cert.GetRepresentation().(type) { + case *attestpb.Certificate_WellKnown: + if c.WellKnown == attestpb.WellKnownCertificate_UNKNOWN { + t.Error(("found WellKnownCertificate_UNKNOWN in db")) + } + if c.WellKnown == attestpb.WellKnownCertificate_MS_THIRD_PARTY_UEFI_CA_2011 { + contains3PUEFI = true + } else if c.WellKnown == attestpb.WellKnownCertificate_MS_WINDOWS_PROD_PCA_2011 { + containsWinProdPCA = true + } + } + } + if !contains3PUEFI || !containsWinProdPCA { + t.Error("expected to see both WinProdPCA and ThirdPartyUEFI certs") + } } + } func decodeHex(hexStr string) []byte { diff --git a/server/grouped_error.go b/server/grouped_error.go new file mode 100644 index 00000000..e6ca0c7a --- /dev/null +++ b/server/grouped_error.go @@ -0,0 +1,48 @@ +package server + +import "strings" + +var fatalError = "fatal: invalid GroupedError" + +// GroupedError collects related errors and exposes them as a single error. +// Users can inspect the `Errors` field for details on the suberrors. +type GroupedError struct { + // The prefix string returned by `Error()`, followed by the grouped errors. + Prefix string + Errors []error +} + +func (gErr *GroupedError) Error() string { + if len(gErr.Errors) == 0 { + return fatalError + } + var sb strings.Builder + for _, err := range gErr.Errors { + sb.WriteString("\n") + sb.WriteString(err.Error()) + } + return gErr.Prefix + sb.String() +} + +func createGroupedError(prefix string, errors []error) error { + if len(errors) == 0 { + return nil + } + return &GroupedError{Prefix: prefix, Errors: errors} +} + +func (gErr *GroupedError) containsSubstring(substr string) bool { + for _, err := range gErr.Errors { + if strings.Contains(err.Error(), substr) { + return true + } + } + return false +} + +func (gErr *GroupedError) containsOnlySubstring(substr string) bool { + if len(gErr.Errors) != 1 { + return false + } + return gErr.containsSubstring(substr) +} diff --git a/server/grouped_error_test.go b/server/grouped_error_test.go new file mode 100644 index 00000000..50733f48 --- /dev/null +++ b/server/grouped_error_test.go @@ -0,0 +1,42 @@ +package server + +import ( + "errors" + "fmt" + "testing" +) + +func TestGroupedError(t *testing.T) { + var gErr GroupedError + gErr.Errors = append(gErr.Errors, errors.New("error1")) + gErr.Errors = append(gErr.Errors, errors.New("error2")) + gErr.Errors = append(gErr.Errors, fmt.Errorf("fmted error")) + gErr.Errors = append(gErr.Errors, fmt.Errorf("wrapped: %w", errors.New("error3"))) + gErr.Prefix = "failed action:" + + expected := `failed action: +error1 +error2 +fmted error +wrapped: error3` + + if gErr.Error() != expected { + t.Errorf("error string output (%s) did not match expected (%s)", + gErr.Error(), expected) + } +} + +func TestEmptyGroupedError(t *testing.T) { + outErr := GroupedError{Prefix: "foo:", Errors: []error{}} + if outErr.Error() != fatalError { + t.Errorf("error string output (%s) did not match fatal error (%s)", + outErr.Error(), fatalError) + } +} + +func TestCreateGroupedErrorFail(t *testing.T) { + outErr := createGroupedError("foo:", []error{}) + if outErr != nil { + t.Errorf("expected nil error!") + } +} diff --git a/server/policy_constants_test.go b/server/policy_constants_test.go new file mode 100644 index 00000000..37dca188 --- /dev/null +++ b/server/policy_constants_test.go @@ -0,0 +1,56 @@ +package server + +import ( + "testing" + + pb "github.com/google/go-tpm-tools/proto/attest" +) + +func getGceMemoryEncryptionNonhostEvent(memoryEncrypted bool) []byte { + event := make([]byte, 32) + copy(event[:], []byte(GCENonHostInfoSignature)) + // event[15] is a null byte. + if memoryEncrypted { + event[16] = 0x01 + } + // Last 15 bytes are reserved. + return event +} + +func TestParseGCENonHostInfo(t *testing.T) { + nonconfidentialEvent := getGceMemoryEncryptionNonhostEvent( /*memoryEncrypted=*/ false) + + // Empty events should return NONCONFIDENTIAL. + confTech, err := ParseGCENonHostInfo([]byte{}) + if err == nil { + t.Error("expected error on incorrect size!") + } + if confTech != pb.GCEConfidentialTechnology_NONE { + t.Errorf("expected ConfidentialTechnology %v, received %v", pb.GCEConfidentialTechnology_NONE, confTech) + } + + confTech, err = ParseGCENonHostInfo(nonconfidentialEvent) + if err != nil { + t.Errorf("failed to parse GCE confidential tech: %v", err) + } + if confTech != pb.GCEConfidentialTechnology_NONE { + t.Errorf("expected ConfidentialTechnology %v, received %v", pb.GCEConfidentialTechnology_NONE, confTech) + } + + sevEvent := getGceMemoryEncryptionNonhostEvent( /*memoryEncrypted=*/ true) + confTech, err = ParseGCENonHostInfo(sevEvent) + if err != nil { + t.Errorf("failed to parse GCE confidential tech: %v", err) + } + if confTech != pb.GCEConfidentialTechnology_AMD_SEV { + t.Errorf("expected ConfidentialTechnology %v, received %v", pb.GCEConfidentialTechnology_AMD_SEV, confTech) + } +} + +func TestParseGCENonHostInfoUnknownType(t *testing.T) { + nonconfidentialEvent := getGceMemoryEncryptionNonhostEvent( /*memoryEncrypted=*/ false) + nonconfidentialEvent[16] = 0x99 + if _, err := ParseGCENonHostInfo(nonconfidentialEvent); err == nil { + t.Errorf("expected error parsing GCE confidential nonhost event") + } +} diff --git a/server/policy_test.go b/server/policy_test.go index 59e07557..b845f575 100644 --- a/server/policy_test.go +++ b/server/policy_test.go @@ -6,19 +6,12 @@ import ( pb "github.com/google/go-tpm-tools/proto/attest" ) -var ( - defaultGcePolicy = pb.Policy{ - Platform: &pb.PlatformPolicy{ - MinimumGceFirmwareVersion: 1, - MinimumTechnology: pb.GCEConfidentialTechnology_NONE, - }, - } - defaultPhysicalPolicy = pb.Policy{ - Platform: &pb.PlatformPolicy{ - AllowedScrtmVersionIds: [][]byte{}, - }, - } -) +var defaultGcePolicy = pb.Policy{ + Platform: &pb.PlatformPolicy{ + MinimumGceFirmwareVersion: 1, + MinimumTechnology: pb.GCEConfidentialTechnology_NONE, + }, +} func TestNilPolicyAlwaysPasses(t *testing.T) { subtests := []struct { @@ -64,9 +57,10 @@ func TestEvaluatePolicy(t *testing.T) { {"Debian10-SHA1", Debian10GCE, &defaultGcePolicy}, {"RHEL8-CryptoAgile", Rhel8GCE, &defaultGcePolicy}, {"Ubuntu1804AmdSev-CryptoAgile", UbuntuAmdSevGCE, &defaultGcePolicy}, - {"Ubuntu2104NoDbx-CryptoAgile", Ubuntu2104NoDbxGCE, &defaultGcePolicy}, - {"Ubuntu2104NoSecureBoot-CryptoAgile", Ubuntu2104NoSecureBootGCE, &defaultGcePolicy}, - {"ArchLinuxWorkstation-CryptoAgile", ArchLinuxWorkstation, &defaultPhysicalPolicy}, + // TODO: add the tests below back once go-attestation has releases: + // https://github.com/google/go-attestation/pull/222/ + // {"Ubuntu2104NoDbx-CryptoAgile", Ubuntu2104NoDbxGCE, &defaultGcePolicy}, + // {"Ubuntu2104NoSecureBoot-CryptoAgile", Ubuntu2104NoSecureBootGCE, &defaultGcePolicy}, } for _, test := range tests { @@ -91,7 +85,10 @@ func TestEvaluatePolicySCRTM(t *testing.T) { } machineState, err := ParseMachineState(ArchLinuxWorkstation.RawLog, ArchLinuxWorkstation.Banks[0]) if err != nil { - t.Fatalf("failed to get machine state: %v", err) + gErr := err.(*GroupedError) + if !gErr.containsOnlySubstring(archLinuxBadSecureBoot) { + t.Fatalf("failed to get machine state: %v", err) + } } if err := EvaluatePolicy(machineState, &archLinuxWorkstationSCRTMPolicy); err != nil { t.Errorf("failed to apply policy: %v", err) @@ -126,18 +123,27 @@ func TestEvaluatePolicyFailure(t *testing.T) { name string log eventLog policy *pb.Policy + // This field handles known issues with event log parsing or bad event + // logs. + // An empty string will not attempt to pattern match the error result. + errorSubstr string }{ - {"Debian10-SHA1", Debian10GCE, &badGcePolicyVersion}, - {"Debian10-SHA1", Debian10GCE, &badGcePolicySEV}, - {"Ubuntu1804AmdSev-CryptoAgile", UbuntuAmdSevGCE, &badGcePolicySEVES}, - {"ArchLinuxWorkstation-CryptoAgile", ArchLinuxWorkstation, &badPhysicalPolicy}, + {"Debian10-SHA1", Debian10GCE, &badGcePolicyVersion, ""}, + {"Debian10-SHA1", Debian10GCE, &badGcePolicySEV, ""}, + {"Ubuntu1804AmdSev-CryptoAgile", UbuntuAmdSevGCE, &badGcePolicySEVES, + ""}, + {"ArchLinuxWorkstation-CryptoAgile", ArchLinuxWorkstation, + &badPhysicalPolicy, archLinuxBadSecureBoot}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { machineState, err := ParseMachineState(test.log.RawLog, test.log.Banks[0]) if err != nil { - t.Fatalf("failed to get machine state: %v", err) + gErr := err.(*GroupedError) + if test.errorSubstr != "" && !gErr.containsOnlySubstring(test.errorSubstr) { + t.Fatalf("failed to get machine state: %v", err) + } } if err := EvaluatePolicy(machineState, test.policy); err == nil { t.Errorf("expected policy failure; got success") diff --git a/server/secure-boot/dbxupdate-2014-08-11.bin b/server/secure-boot/dbxupdate-2014-08-11.bin new file mode 100644 index 00000000..e79929f7 Binary files /dev/null and b/server/secure-boot/dbxupdate-2014-08-11.bin differ diff --git a/server/secure-boot/dbxupdate_x64-2020-10-12.bin b/server/secure-boot/dbxupdate_x64-2020-10-12.bin new file mode 100644 index 00000000..aa7b7162 Binary files /dev/null and b/server/secure-boot/dbxupdate_x64-2020-10-12.bin differ diff --git a/server/secure-boot/dbxupdate_x64-2021-04-29.bin b/server/secure-boot/dbxupdate_x64-2021-04-29.bin new file mode 100644 index 00000000..7c282d1c Binary files /dev/null and b/server/secure-boot/dbxupdate_x64-2021-04-29.bin differ