diff --git a/.changelog/4055.internal.2.md b/.changelog/4055.internal.2.md new file mode 100644 index 00000000000..2171a03cf12 --- /dev/null +++ b/.changelog/4055.internal.2.md @@ -0,0 +1,8 @@ +common/node: Verify AVR quote status + +When verifying a TEECapabilities structure, additionally verify the +quote status against the new SGXConstraints.AllowedQuoteStatuses vector +so that nodes that have an invalid quote status can be omitted from +scheduling entirely. + +Note: QuoteOK is ALWAYS allowed, as disallowing it is nonsensical. diff --git a/go/common/node/node.go b/go/common/node/node.go index 4900910a8b8..1e459e41ca2 100644 --- a/go/common/node/node.go +++ b/go/common/node/node.go @@ -40,6 +40,10 @@ var ( // identity doesn't match the required values. ErrBadEnclaveIdentity = errors.New("node: bad TEE enclave identity") + // ErrConstraintViolation the error returned when the TEE attestation + // fails to conform to the optional additional constraints. + ErrConstraintViolation = errors.New("node: TEE constraint violation") + teeHashContext = []byte("oasis-core/node: TEE RAK binding") _ prettyprint.PrettyPrinter = (*MultiSignedNode)(nil) @@ -436,6 +440,37 @@ type CapabilityTEE struct { Attestation []byte `json:"attestation"` } +// SGXConstraints are the Intel SGX TEE constraints. +type SGXConstraints struct { + // Enclaves is the allowed MRENCLAVE/MRSIGNER pairs. + Enclaves []sgx.EnclaveIdentity `json:"enclaves,omitempty"` + + // AllowedQuoteStatuses are the allowed quote statuses for the node + // to be scheduled as a compute worker. + // + // Note: QuoteOK is ALWAYS allowed, and does not need to be specified. + AllowedQuoteStatuses []ias.ISVEnclaveQuoteStatus `json:"allowed_quote_statuses,omitempty"` +} + +func (constraints *SGXConstraints) quoteStatusAllowed(avr *ias.AttestationVerificationReport) bool { + status := avr.ISVEnclaveQuoteStatus + + // Always allow "OK". + if status == ias.QuoteOK { + return true + } + + // Search through the constraints to see if the AVR quote status is + // explicitly allowed. + for _, v := range constraints.AllowedQuoteStatuses { + if v == status { + return true + } + } + + return false +} + // RAKHash computes the expected AVR report hash bound to a given public RAK. func RAKHash(rak signature.PublicKey) hash.Hash { hData := make([]byte, 0, len(teeHashContext)+signature.PublicKeySize) @@ -468,7 +503,7 @@ func (c *CapabilityTEE) Verify(ts time.Time, constraints []byte) error { // Ensure that the MRENCLAVE/MRSIGNER match what is specified // in the TEE-specific constraints field. - var cs sgx.Constraints + var cs SGXConstraints if err := cbor.Unmarshal(constraints, &cs); err != nil { return fmt.Errorf("node: malformed SGX constraints: %w", err) } @@ -493,6 +528,11 @@ func (c *CapabilityTEE) Verify(ts time.Time, constraints []byte) error { return ErrRAKHashMismatch } + // Ensure that the quote status is acceptable. + if !cs.quoteStatusAllowed(avr) { + return ErrConstraintViolation + } + // The last 32 bytes of the quote ReportData are deliberately // ignored. diff --git a/go/common/sgx/common.go b/go/common/sgx/common.go index c7becb6d96b..4c47f01b845 100644 --- a/go/common/sgx/common.go +++ b/go/common/sgx/common.go @@ -259,8 +259,10 @@ func (id EnclaveIdentity) String() string { return hex.EncodeToString(id.MrEnclave[:]) + hex.EncodeToString(id.MrSigner[:]) } +/* // Constraints are the Intel SGX TEE constraints. type Constraints struct { // Enclaves is the allowed MRENCLAVE/MRSIGNER pairs. Enclaves []EnclaveIdentity `json:"enclaves"` } +*/ diff --git a/go/genesis/genesis_test.go b/go/genesis/genesis_test.go index 11d1624a064..103b0fc4f78 100644 --- a/go/genesis/genesis_test.go +++ b/go/genesis/genesis_test.go @@ -184,7 +184,7 @@ func TestGenesisSanityCheck(t *testing.T) { Kind: registry.KindKeyManager, TEEHardware: node.TEEHardwareIntelSGX, Version: registry.VersionInfo{ - TEE: cbor.Marshal(sgx.Constraints{ + TEE: cbor.Marshal(node.SGXConstraints{ Enclaves: []sgx.EnclaveIdentity{{}}, }), }, @@ -243,7 +243,7 @@ func TestGenesisSanityCheck(t *testing.T) { }, TEEHardware: node.TEEHardwareIntelSGX, Version: registry.VersionInfo{ - TEE: cbor.Marshal(sgx.Constraints{ + TEE: cbor.Marshal(node.SGXConstraints{ Enclaves: []sgx.EnclaveIdentity{{}}, }), }, diff --git a/go/oasis-node/cmd/debug/byzantine/steps_test.go b/go/oasis-node/cmd/debug/byzantine/steps_test.go index 1a6c96bcc23..b71255aedc7 100644 --- a/go/oasis-node/cmd/debug/byzantine/steps_test.go +++ b/go/oasis-node/cmd/debug/byzantine/steps_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/node" "github.com/oasisprotocol/oasis-core/go/common/sgx" "github.com/oasisprotocol/oasis-core/go/common/sgx/ias" ) @@ -15,7 +16,7 @@ func TestFakeCapabilitySGX(t *testing.T) { _, fakeCapabilitiesSGX, err := initFakeCapabilitiesSGX() require.NoError(t, err, "initFakeCapabilitiesSGX failed") - cs := cbor.Marshal(sgx.Constraints{ + cs := cbor.Marshal(node.SGXConstraints{ Enclaves: []sgx.EnclaveIdentity{{}}, }) diff --git a/go/oasis-node/cmd/ias/auth.go b/go/oasis-node/cmd/ias/auth.go index ab5f8a7b11b..0332d864eb2 100644 --- a/go/oasis-node/cmd/ias/auth.go +++ b/go/oasis-node/cmd/ias/auth.go @@ -55,7 +55,7 @@ func (st *enclaveStore) addRuntime(runtime *registry.Runtime) (int, error) { return len(st.enclaves), nil } - var cs sgx.Constraints + var cs node.SGXConstraints if err := cbor.Unmarshal(runtime.Version.TEE, &cs); err != nil { return len(st.enclaves), err } diff --git a/go/oasis-test-runner/oasis/runtime.go b/go/oasis-test-runner/oasis/runtime.go index 2ed316384a2..13f7a2779ed 100644 --- a/go/oasis-test-runner/oasis/runtime.go +++ b/go/oasis-test-runner/oasis/runtime.go @@ -121,7 +121,7 @@ func (rt *Runtime) RefreshEnclaveIdentity() error { enclaveIdentities = append(enclaveIdentities, sgx.EnclaveIdentity{MrEnclave: *mrEnclave, MrSigner: *rt.mrSigner}) mrEnclaves = append(mrEnclaves, mrEnclave) } - rt.descriptor.Version.TEE = cbor.Marshal(sgx.Constraints{ + rt.descriptor.Version.TEE = cbor.Marshal(node.SGXConstraints{ Enclaves: enclaveIdentities, }) rt.mrEnclaves = mrEnclaves diff --git a/go/registry/api/api.go b/go/registry/api/api.go index 5f6b7e40f10..bd611cc388f 100644 --- a/go/registry/api/api.go +++ b/go/registry/api/api.go @@ -19,7 +19,6 @@ import ( "github.com/oasisprotocol/oasis-core/go/common/logging" "github.com/oasisprotocol/oasis-core/go/common/node" "github.com/oasisprotocol/oasis-core/go/common/pubsub" - "github.com/oasisprotocol/oasis-core/go/common/sgx" "github.com/oasisprotocol/oasis-core/go/consensus/api/transaction" staking "github.com/oasisprotocol/oasis-core/go/staking/api" ) @@ -996,7 +995,7 @@ func VerifyRuntime( // nolint: gocyclo if rt.TEEHardware != node.TEEHardwareInvalid { switch rt.TEEHardware { case node.TEEHardwareIntelSGX: - var cs sgx.Constraints + var cs node.SGXConstraints if err := cbor.Unmarshal(rt.Version.TEE, &cs); err != nil { logger.Error("RegisterRuntime: invalid SGX TEE constraints", "err", err, diff --git a/go/registry/tests/tester.go b/go/registry/tests/tester.go index 2cc7e567c21..a20dc814299 100644 --- a/go/registry/tests/tester.go +++ b/go/registry/tests/tester.go @@ -775,7 +775,7 @@ func testRegistryRuntime(t *testing.T, backend api.Backend, consensus consensusA rt.Kind = api.KindKeyManager rt.TEEHardware = node.TEEHardwareIntelSGX - cs := sgx.Constraints{ + cs := node.SGXConstraints{ Enclaves: []sgx.EnclaveIdentity{{}}, } rt.Version.TEE = cbor.Marshal(cs) @@ -812,7 +812,7 @@ func testRegistryRuntime(t *testing.T, backend api.Backend, consensus consensusA rt.KeyManager = &rtMapByName["KeyManager"].ID rt.TEEHardware = node.TEEHardwareIntelSGX - cs := sgx.Constraints{ + cs := node.SGXConstraints{ Enclaves: []sgx.EnclaveIdentity{{}}, } rt.Version.TEE = cbor.Marshal(cs)