mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
attestation: validate GCP machine state
This commit is contained in:
parent
2535073df8
commit
0a344e4cf6
@ -7,13 +7,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
package gcp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@ -23,11 +21,12 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||
"github.com/google/go-tpm-tools/proto/attest"
|
||||
"github.com/google/go-tpm-tools/server"
|
||||
"github.com/googleapis/gax-go/v2"
|
||||
"google.golang.org/api/option"
|
||||
)
|
||||
|
||||
const minimumGceVersion = 1
|
||||
|
||||
// Validator for GCP confidential VM attestation.
|
||||
type Validator struct {
|
||||
oid.GCPSEVES
|
||||
@ -40,7 +39,7 @@ func NewValidator(pcrs measurements.M, log vtpm.AttestationLogger) *Validator {
|
||||
Validator: vtpm.NewValidator(
|
||||
pcrs,
|
||||
trustedKeyFromGCEAPI(newInstanceClient),
|
||||
gceNonHostInfoEvent,
|
||||
validateCVM,
|
||||
log,
|
||||
),
|
||||
}
|
||||
@ -102,22 +101,18 @@ func trustedKeyFromGCEAPI(getClient func(ctx context.Context, opts ...option.Cli
|
||||
}
|
||||
}
|
||||
|
||||
// gceNonHostInfoEvent looks for the GCE Non-Host info event in an event log.
|
||||
// Returns an error if the event is not found, or if the event is missing the required flag to mark the VM confidential.
|
||||
func gceNonHostInfoEvent(attDoc vtpm.AttestationDocument, _ *attest.MachineState) error {
|
||||
if attDoc.Attestation == nil {
|
||||
return errors.New("missing attestation in attestation document")
|
||||
// validateCVM checks that the machine state represents a GCE AMD-SEV VM.
|
||||
func validateCVM(_ vtpm.AttestationDocument, state *attest.MachineState) error {
|
||||
gceVersion := state.Platform.GetGceVersion()
|
||||
if gceVersion < minimumGceVersion {
|
||||
return fmt.Errorf("outdated GCE version: %v (require >= %v)", gceVersion, minimumGceVersion)
|
||||
}
|
||||
// The event log of a GCE VM contains the GCE Non-Host info event
|
||||
// This event is 32-bytes, followed by one byte 0x01 if it is confidential, 0x00 otherwise,
|
||||
// followed by 15 reserved bytes.
|
||||
// See https://pkg.go.dev/github.com/google/go-tpm-tools@v0.3.1/server#pkg-variables
|
||||
idx := bytes.Index(attDoc.Attestation.EventLog, server.GCENonHostInfoSignature)
|
||||
if idx <= 0 {
|
||||
return fmt.Errorf("event log is missing GCE Non-Host info event")
|
||||
}
|
||||
if attDoc.Attestation.EventLog[idx+len(server.GCENonHostInfoSignature)] != 0x01 {
|
||||
return fmt.Errorf("GCE Non-Host info is missing confidential bit")
|
||||
|
||||
tech := state.Platform.Technology
|
||||
wantTech := attest.GCEConfidentialTechnology_AMD_SEV
|
||||
if tech != wantTech {
|
||||
return fmt.Errorf("unexpected confidential technology: %v (expected: %v)", tech, wantTech)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -23,38 +23,35 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestGceNonHostInfoEvent(t *testing.T) {
|
||||
func TestValidateCVM(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
attDoc vtpm.AttestationDocument
|
||||
state *attest.MachineState
|
||||
wantErr bool
|
||||
}{
|
||||
"is cvm": {
|
||||
attDoc: vtpm.AttestationDocument{
|
||||
Attestation: &attest.Attestation{
|
||||
EventLog: []byte("\x00\x00\x00GCE NonHostInfo\x00\x01\x00\x00"),
|
||||
},
|
||||
},
|
||||
"is current cvm": {
|
||||
state: &attest.MachineState{Platform: &attest.PlatformState{
|
||||
Firmware: &attest.PlatformState_GceVersion{GceVersion: minimumGceVersion},
|
||||
Technology: attest.GCEConfidentialTechnology_AMD_SEV,
|
||||
}},
|
||||
},
|
||||
"attestation is nil": {
|
||||
attDoc: vtpm.AttestationDocument{
|
||||
Attestation: nil,
|
||||
},
|
||||
wantErr: true,
|
||||
"is newer cvm": {
|
||||
state: &attest.MachineState{Platform: &attest.PlatformState{
|
||||
Firmware: &attest.PlatformState_GceVersion{GceVersion: minimumGceVersion + 1},
|
||||
Technology: attest.GCEConfidentialTechnology_AMD_SEV,
|
||||
}},
|
||||
},
|
||||
"missing GCE Non-Host info event": {
|
||||
attDoc: vtpm.AttestationDocument{
|
||||
Attestation: &attest.Attestation{
|
||||
EventLog: []byte("No GCE Event"),
|
||||
},
|
||||
},
|
||||
"is older cvm": {
|
||||
state: &attest.MachineState{Platform: &attest.PlatformState{
|
||||
Firmware: &attest.PlatformState_GceVersion{GceVersion: minimumGceVersion - 1},
|
||||
Technology: attest.GCEConfidentialTechnology_AMD_SEV,
|
||||
}},
|
||||
wantErr: true,
|
||||
},
|
||||
"not a cvm": {
|
||||
attDoc: vtpm.AttestationDocument{
|
||||
Attestation: &attest.Attestation{
|
||||
EventLog: []byte("\x00\x00\x00GCE NonHostInfo\x00\x00\x00\x00"),
|
||||
},
|
||||
},
|
||||
state: &attest.MachineState{Platform: &attest.PlatformState{
|
||||
Firmware: &attest.PlatformState_GceVersion{GceVersion: minimumGceVersion},
|
||||
Technology: attest.GCEConfidentialTechnology_NONE,
|
||||
}},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
@ -62,7 +59,7 @@ func TestGceNonHostInfoEvent(t *testing.T) {
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
err := gceNonHostInfoEvent(tc.attDoc, nil)
|
||||
err := validateCVM(vtpm.AttestationDocument{}, tc.state)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
|
@ -198,23 +198,24 @@ func (v *Validator) Validate(attDocRaw []byte, nonce []byte) (userData []byte, e
|
||||
return nil, fmt.Errorf("validating attestation public key: %w", err)
|
||||
}
|
||||
|
||||
// Validate confidential computing capabilities of the VM
|
||||
if err := v.validateCVM(attDoc, nil); err != nil {
|
||||
return nil, fmt.Errorf("verifying VM confidential computing capabilities: %w", err)
|
||||
}
|
||||
|
||||
// Verify the TPM attestation
|
||||
if _, err := tpmServer.VerifyAttestation(
|
||||
state, err := tpmServer.VerifyAttestation(
|
||||
attDoc.Attestation,
|
||||
tpmServer.VerifyOpts{
|
||||
Nonce: makeExtraData(attDoc.UserData, nonce),
|
||||
TrustedAKs: []crypto.PublicKey{aKP},
|
||||
AllowSHA1: false,
|
||||
},
|
||||
); err != nil {
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("verifying attestation document: %w", err)
|
||||
}
|
||||
|
||||
// Validate confidential computing capabilities of the VM
|
||||
if err := v.validateCVM(attDoc, state); err != nil {
|
||||
return nil, fmt.Errorf("verifying VM confidential computing capabilities: %w", err)
|
||||
}
|
||||
|
||||
// Verify PCRs
|
||||
quoteIdx, err := GetSHA256QuoteIndex(attDoc.Attestation.Quotes)
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user