From 9ab9e08c21c3569da8aeb7a3ef1b0e1ddd5070e7 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Thu, 18 Sep 2025 17:12:40 +0200 Subject: [PATCH 1/2] go/common/sgx/pcs/policy: Add FMSP whitelist to quote policy --- .changelog/6331.breaking.md | 1 + go/common/sgx/pcs/policy.go | 4 +++ go/common/sgx/pcs/quote.go | 3 ++- go/common/sgx/pcs/quote_test.go | 25 +++++++++++++++++- go/common/sgx/pcs/tcb.go | 9 +++++-- go/common/sgx/quote/quote_test.go | 12 ++++++--- go/upgrade/migrations/consensus_242.go | 4 ++- runtime/src/common/sgx/pcs/mod.rs | 35 ++++++++++++++++++++++++-- runtime/src/common/sgx/pcs/policy.rs | 6 +++++ runtime/src/common/sgx/pcs/tcb.rs | 13 +++++----- 10 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 .changelog/6331.breaking.md diff --git a/.changelog/6331.breaking.md b/.changelog/6331.breaking.md new file mode 100644 index 00000000000..598b92541e4 --- /dev/null +++ b/.changelog/6331.breaking.md @@ -0,0 +1 @@ +go/common/sgx/pcs/policy: Add FMSPC whitelist to quote policy diff --git a/go/common/sgx/pcs/policy.go b/go/common/sgx/pcs/policy.go index de8394db4b0..ba846fadacc 100644 --- a/go/common/sgx/pcs/policy.go +++ b/go/common/sgx/pcs/policy.go @@ -16,6 +16,10 @@ type QuotePolicy struct { // valid. TCB bundles containing smaller values will be invalid. MinTCBEvaluationDataNumber uint32 `json:"min_tcb_evaluation_data_number" yaml:"min_tcb_evaluation_data_number"` + // FMSPCWhitelist is a list of hexadecimal encoded FMSPCs specifying which processor + // packages and platform instances are allowed. + FMSPCWhitelist []string `json:"fmspc_whitelist,omitempty" yaml:"fmspc_whitelist,omitempty"` + // FMSPCBlacklist is a list of hexadecimal encoded FMSPCs specifying which processor // packages and platform instances are blocked. FMSPCBlacklist []string `json:"fmspc_blacklist,omitempty" yaml:"fmspc_blacklist,omitempty"` diff --git a/go/common/sgx/pcs/quote.go b/go/common/sgx/pcs/quote.go index 99d13d924af..16e18cad98f 100644 --- a/go/common/sgx/pcs/quote.go +++ b/go/common/sgx/pcs/quote.go @@ -144,7 +144,8 @@ func (q *Quote) Verify(policy *QuotePolicy, ts time.Time, tcb *TCBBundle) (*sgx. policy = &QuotePolicy{ TCBValidityPeriod: 30, MinTCBEvaluationDataNumber: DefaultMinTCBEvaluationDataNumber, - FMSPCBlacklist: []string{}, + FMSPCWhitelist: make([]string, 0), + FMSPCBlacklist: make([]string, 0), } } diff --git a/go/common/sgx/pcs/quote_test.go b/go/common/sgx/pcs/quote_test.go index 97ee170c158..21b4efeee24 100644 --- a/go/common/sgx/pcs/quote_test.go +++ b/go/common/sgx/pcs/quote_test.go @@ -121,6 +121,29 @@ func TestQuoteV3_ECDSA_P256_PCK_CertificateChain(t *testing.T) { require.Error(err, "Quote verification should fail for invalid TCB evaluation data number") require.ErrorContains(err, "pcs/quote: failed to verify TCB bundle: pcs/tcb: failed to verify QE identity: pcs/tcb: invalid QE identity: pcs/tcb: invalid QE evaluation data number") + // Test whitelisted FMSPC. + quotePolicy = &QuotePolicy{ + TCBValidityPeriod: 90, + FMSPCWhitelist: []string{}, + } + _, err = quote.Verify(quotePolicy, now, &tcbBundle) + require.NoError(err, "Quote verification should succeed for whitelisted FMSPCs") + + quotePolicy = &QuotePolicy{ + TCBValidityPeriod: 90, + FMSPCWhitelist: []string{"00606A000000"}, + } + _, err = quote.Verify(quotePolicy, now, &tcbBundle) + require.NoError(err, "Quote verification should succeed for whitelisted FMSPCs") + + quotePolicy = &QuotePolicy{ + TCBValidityPeriod: 90, + FMSPCWhitelist: []string{"00606A000001"}, + } + _, err = quote.Verify(quotePolicy, now, &tcbBundle) + require.Error(err, "Quote verification should fail for non-whitelisted FMSPCs") + require.ErrorContains(err, "pcs/quote: failed to verify TCB bundle: pcs/tcb: failed to verify TCB info: pcs/tcb: invalid TCB info: pcs/tcb: FMSPC is not whitelisted") + // Test blacklisted FMSPC. quotePolicy = &QuotePolicy{ TCBValidityPeriod: 90, @@ -128,7 +151,7 @@ func TestQuoteV3_ECDSA_P256_PCK_CertificateChain(t *testing.T) { } _, err = quote.Verify(quotePolicy, now, &tcbBundle) require.Error(err, "Quote verification should fail for blacklisted FMSPCs") - require.ErrorContains(err, "pcs/quote: failed to verify TCB bundle: pcs/tcb: failed to verify TCB info: pcs/tcb: invalid TCB info: pcs/tcb: blacklisted FMSPC") + require.ErrorContains(err, "pcs/quote: failed to verify TCB bundle: pcs/tcb: failed to verify TCB info: pcs/tcb: invalid TCB info: pcs/tcb: FMSPC is blacklisted") // Test TCB info certificates missing. tcbBundle2 := TCBBundle{ diff --git a/go/common/sgx/pcs/tcb.go b/go/common/sgx/pcs/tcb.go index 8c729a11bd2..40df25631fe 100644 --- a/go/common/sgx/pcs/tcb.go +++ b/go/common/sgx/pcs/tcb.go @@ -270,9 +270,14 @@ func (ti *TCBInfo) validate(teeType TeeType, ts time.Time, policy *QuotePolicy) return fmt.Errorf("pcs/tcb: invalid TCB evaluation data number") } - // Validate FMSPC not blacklisted. + // Validate FMSPC is whitelisted. + if len(policy.FMSPCWhitelist) > 0 && !slices.Contains(policy.FMSPCWhitelist, ti.FMSPC) { + return fmt.Errorf("pcs/tcb: FMSPC is not whitelisted") + } + + // Validate FMSPC is not blacklisted. if slices.Contains(policy.FMSPCBlacklist, ti.FMSPC) { - return fmt.Errorf("pcs/tcb: blacklisted FMSPC") + return fmt.Errorf("pcs/tcb: FMSPC is blacklisted") } return nil diff --git a/go/common/sgx/quote/quote_test.go b/go/common/sgx/quote/quote_test.go index 2f655a70c0a..3bfa885ca7c 100644 --- a/go/common/sgx/quote/quote_test.go +++ b/go/common/sgx/quote/quote_test.go @@ -15,9 +15,12 @@ pcs: disabled: false tcb_validity_period: 30 min_tcb_evaluation_data_number: 17 - fmspc_blacklist: + fmspc_whitelist: - "000000000000" - "00606A000000" + fmspc_blacklist: + - "000000000001" + - "00606A000001" ` var dec Policy err := yaml.Unmarshal([]byte(testCase1), &dec) @@ -27,7 +30,10 @@ pcs: require.EqualValues(false, dec.PCS.Disabled) require.EqualValues(30, dec.PCS.TCBValidityPeriod) require.EqualValues(17, dec.PCS.MinTCBEvaluationDataNumber) + require.Len(dec.PCS.FMSPCWhitelist, 2) + require.EqualValues("000000000000", dec.PCS.FMSPCWhitelist[0]) + require.EqualValues("00606A000000", dec.PCS.FMSPCWhitelist[1]) require.Len(dec.PCS.FMSPCBlacklist, 2) - require.EqualValues("000000000000", dec.PCS.FMSPCBlacklist[0]) - require.EqualValues("00606A000000", dec.PCS.FMSPCBlacklist[1]) + require.EqualValues("000000000001", dec.PCS.FMSPCBlacklist[0]) + require.EqualValues("00606A000001", dec.PCS.FMSPCBlacklist[1]) } diff --git a/go/upgrade/migrations/consensus_242.go b/go/upgrade/migrations/consensus_242.go index 1f88f1b0152..194776b8f0f 100644 --- a/go/upgrade/migrations/consensus_242.go +++ b/go/upgrade/migrations/consensus_242.go @@ -11,8 +11,10 @@ import ( // Consensus242 is the name of the upgrade that enables features introduced in Oasis Core 24.2. // // This upgrade includes: -// - The `MayQuery“ field in the CHURP SGX policy, which defines which enclave identities +// - The `MayQuery` field in the CHURP SGX policy, which defines which enclave identities // are allowed to query runtime key shares. +// - The `FMSPCWhitelist` field in the quote policy, which defines which processor packages +// and platform instances are allowed. // - An updated key manager policy update transaction that applies a new policy at the epoch // boundary. const Consensus242 = "consensus242" diff --git a/runtime/src/common/sgx/pcs/mod.rs b/runtime/src/common/sgx/pcs/mod.rs index ed03000464d..0c0ef5da0b6 100644 --- a/runtime/src/common/sgx/pcs/mod.rs +++ b/runtime/src/common/sgx/pcs/mod.rs @@ -41,6 +41,8 @@ pub enum Error { TCBMismatch, #[error("TCB evaluation data number is invalid")] TCBEvaluationDataNumberInvalid, + #[error("FMSPC is not whitelisted")] + NotWhitelistedFMSPC, #[error("FMSPC is blacklisted")] BlacklistedFMSPC, #[error("QE report is malformed")] @@ -217,19 +219,48 @@ mod tests { } #[test] - fn test_quote_blacklisted_fmscp() { + fn test_quote_whitelisted_fmscp() { // From Go implementation. const RAW_QUOTE_BUNDLE: &[u8] = include_bytes!("../../../../testdata/pcs_quote_bundle.cbor"); let qb: QuoteBundle = cbor::from_slice(RAW_QUOTE_BUNDLE).unwrap(); + let now = Utc.timestamp_opt(1671497404, 0).unwrap(); + + let policy = &QuotePolicy { + ..Default::default() + }; + qb.verify(policy, now) + .expect("quote verification should succeed for whitelisted FMSPCs"); + + let policy = &QuotePolicy { + fmspc_whitelist: vec!["00606A000000".to_string()], + ..Default::default() + }; + qb.verify(policy, now) + .expect("quote verification should succeed for whitelisted FMSPCs"); + + let policy: &QuotePolicy = &QuotePolicy { + fmspc_whitelist: vec!["00606A000001".to_string()], + ..Default::default() + }; + qb.verify(policy, now) + .expect_err("quote verification should fail for non-whitelisted FMSPCs"); + } + #[test] + fn test_quote_blacklisted_fmscp() { + // From Go implementation. + const RAW_QUOTE_BUNDLE: &[u8] = + include_bytes!("../../../../testdata/pcs_quote_bundle.cbor"); + + let qb: QuoteBundle = cbor::from_slice(RAW_QUOTE_BUNDLE).unwrap(); let now = Utc.timestamp_opt(1671497404, 0).unwrap(); + let policy = &QuotePolicy { fmspc_blacklist: vec!["00606A000000".to_string()], ..Default::default() }; - qb.verify(policy, now) .expect_err("quote verification should fail for blacklisted FMSPCs"); } diff --git a/runtime/src/common/sgx/pcs/policy.rs b/runtime/src/common/sgx/pcs/policy.rs index 849f44fdf6e..cd855a24995 100644 --- a/runtime/src/common/sgx/pcs/policy.rs +++ b/runtime/src/common/sgx/pcs/policy.rs @@ -15,6 +15,11 @@ pub struct QuotePolicy { /// smaller values will be invalid. pub min_tcb_evaluation_data_number: u32, + /// A list of hexadecimal encoded FMSPCs specifying which processor packages and platform + /// instances are allowed. + #[cbor(optional)] + pub fmspc_whitelist: Vec, + /// A list of hexadecimal encoded FMSPCs specifying which processor packages and platform /// instances are blocked. #[cbor(optional)] @@ -31,6 +36,7 @@ impl Default for QuotePolicy { disabled: false, tcb_validity_period: 30, min_tcb_evaluation_data_number: DEFAULT_MIN_TCB_EVALUATION_DATA_NUMBER, + fmspc_whitelist: Vec::new(), fmspc_blacklist: Vec::new(), tdx: None, } diff --git a/runtime/src/common/sgx/pcs/tcb.rs b/runtime/src/common/sgx/pcs/tcb.rs index 1b25988b5d9..7b3ea9296fd 100644 --- a/runtime/src/common/sgx/pcs/tcb.rs +++ b/runtime/src/common/sgx/pcs/tcb.rs @@ -229,12 +229,13 @@ impl TCBInfo { return Err(Error::TCBEvaluationDataNumberInvalid); } - // Validate FMSPC not blacklisted. - let blocked = policy - .fmspc_blacklist - .iter() - .any(|blocked| blocked == &self.fmspc); - if blocked { + // Validate FMSPC is whitelisted. + if !policy.fmspc_whitelist.is_empty() && !policy.fmspc_whitelist.contains(&self.fmspc) { + return Err(Error::NotWhitelistedFMSPC); + } + + // Validate FMSPC is not blacklisted. + if policy.fmspc_blacklist.contains(&self.fmspc) { return Err(Error::BlacklistedFMSPC); } From 8ca2ad2ff8161caf3a474ee2abedc7f0a5ea59ae Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Mon, 22 Sep 2025 00:40:58 +0200 Subject: [PATCH 2/2] go/common/sgx/pcs/policy: Ensure FMSPC whitelist is empty Ensure the FMSPC whitelist in the quote policy remains empty until the next feature version is enabled. Once that feature is enabled, these changes can be removed. --- go/common/node/node.go | 4 +-- go/common/node/sgx.go | 9 ++++++- go/common/node/sgx_test.go | 8 +++--- go/common/sgx/quote/quote.go | 17 ++++++++++++ .../apps/keymanager/common/genesis.go | 2 +- .../cometbft/apps/keymanager/secrets/epoch.go | 9 ++++++- .../apps/keymanager/secrets/status.go | 6 +++-- .../apps/keymanager/secrets/status_test.go | 18 ++++++------- .../cometbft/apps/keymanager/secrets/txs.go | 6 ++--- .../cometbft/apps/registry/genesis.go | 2 +- .../cometbft/apps/registry/messages.go | 9 ++++++- .../cometbft/apps/registry/messages_test.go | 9 +++++++ .../cometbft/apps/registry/transactions.go | 23 ++++++++++++++-- .../apps/registry/transactions_test.go | 16 ++++++++++++ .../cometbft/apps/scheduler/scheduler.go | 13 ++++++++++ .../cometbft/apps/scheduler/scheduler_test.go | 1 + .../cometbft/apps/scheduler/shuffle.go | 7 +++-- .../apps/supplementarysanity/checks.go | 11 ++++++-- .../cmd/debug/byzantine/steps_test.go | 2 +- go/registry/api/api.go | 14 ++++++---- go/registry/api/api_test.go | 2 +- go/registry/api/runtime.go | 4 +-- go/registry/api/runtime_test.go | 2 +- go/registry/api/sanity_check.go | 26 ++++++++++++++----- 24 files changed, 173 insertions(+), 47 deletions(-) diff --git a/go/common/node/node.go b/go/common/node/node.go index 680646f5aea..27bd22bbd8b 100644 --- a/go/common/node/node.go +++ b/go/common/node/node.go @@ -574,7 +574,7 @@ func HashRAK(rak signature.PublicKey) hash.Hash { } // Verify verifies the node's TEE capabilities, at the provided timestamp and height. -func (c *CapabilityTEE) Verify(teeCfg *TEEFeatures, ts time.Time, height uint64, constraints []byte, nodeID signature.PublicKey) error { +func (c *CapabilityTEE) Verify(teeCfg *TEEFeatures, ts time.Time, height uint64, constraints []byte, nodeID signature.PublicKey, isFeatureVersion242 bool) error { switch c.Hardware { case TEEHardwareIntelSGX: // Parse SGX remote attestation. @@ -591,7 +591,7 @@ func (c *CapabilityTEE) Verify(teeCfg *TEEFeatures, ts time.Time, height uint64, if err := cbor.Unmarshal(constraints, &sc); err != nil { return fmt.Errorf("node: malformed SGX constraints: %w", err) } - if err := sc.ValidateBasic(teeCfg); err != nil { + if err := sc.ValidateBasic(teeCfg, isFeatureVersion242); err != nil { return fmt.Errorf("node: malformed SGX constraints: %w", err) } diff --git a/go/common/node/sgx.go b/go/common/node/sgx.go index 3af87193331..9b188cbe5d7 100644 --- a/go/common/node/sgx.go +++ b/go/common/node/sgx.go @@ -97,7 +97,7 @@ func (sc *SGXConstraints) MarshalCBOR() ([]byte, error) { } // ValidateBasic performs basic structure validity checks. -func (sc *SGXConstraints) ValidateBasic(cfg *TEEFeatures) error { +func (sc *SGXConstraints) ValidateBasic(cfg *TEEFeatures, isFeatureVersion242 bool) error { if cfg == nil { cfg = &emptyFeatures } @@ -116,6 +116,13 @@ func (sc *SGXConstraints) ValidateBasic(cfg *TEEFeatures) error { return fmt.Errorf("TDX policy not supported") } + // Check that policy is compliant with the current feature version. + if sc.Policy != nil { + if err := sc.Policy.Validate(isFeatureVersion242); err != nil { + return fmt.Errorf("invalid policy: %w", err) + } + } + return nil } diff --git a/go/common/node/sgx_test.go b/go/common/node/sgx_test.go index 7be73f08615..dd01867345e 100644 --- a/go/common/node/sgx_test.go +++ b/go/common/node/sgx_test.go @@ -27,7 +27,7 @@ func TestSGXConstraintsV0(t *testing.T) { err = cbor.Unmarshal(raw, &sc) require.NoError(err, "Decode V0 SGX constraints") - err = sc.ValidateBasic(nil) + err = sc.ValidateBasic(nil, true) require.NoError(err, "ValidateBasic V0 SGX constraints") enc := cbor.Marshal(sc) @@ -60,11 +60,11 @@ func TestSGXConstraintsV1(t *testing.T) { }, }, } - err = sc.ValidateBasic(nil) + err = sc.ValidateBasic(nil, true) require.Error(err, "ValidateBasic V1 SGX constraints without PCS support") - err = sc.ValidateBasic(&TEEFeatures{}) + err = sc.ValidateBasic(&TEEFeatures{}, true) require.Error(err, "ValidateBasic V1 SGX constraints without PCS support") - err = sc.ValidateBasic(&TEEFeatures{SGX: TEEFeaturesSGX{PCS: true}}) + err = sc.ValidateBasic(&TEEFeatures{SGX: TEEFeaturesSGX{PCS: true}}, true) require.NoError(err, "ValidateBasic V1 SGX constraints") } diff --git a/go/common/sgx/quote/quote.go b/go/common/sgx/quote/quote.go index cd2a41c5232..0441987e51d 100644 --- a/go/common/sgx/quote/quote.go +++ b/go/common/sgx/quote/quote.go @@ -64,3 +64,20 @@ type Policy struct { IAS *ias.QuotePolicy `json:"ias,omitempty" yaml:"ias,omitempty"` PCS *pcs.QuotePolicy `json:"pcs,omitempty" yaml:"pcs,omitempty"` } + +// Validate validates the policy. +func (p *Policy) Validate(isFeatureVersion242 bool) error { + if isFeatureVersion242 { + return nil + } + + if p.PCS == nil { + return nil + } + + if len(p.PCS.FMSPCWhitelist) == 0 { + return nil + } + + return fmt.Errorf("fmspc whitelist should be empty") +} diff --git a/go/consensus/cometbft/apps/keymanager/common/genesis.go b/go/consensus/cometbft/apps/keymanager/common/genesis.go index 78406aa818a..9e4a5b86341 100644 --- a/go/consensus/cometbft/apps/keymanager/common/genesis.go +++ b/go/consensus/cometbft/apps/keymanager/common/genesis.go @@ -16,7 +16,7 @@ func RegistryRuntimes(ctx *tmapi.Context, doc *genesis.Document, epoch beacon.Ep regSt := doc.Registry rtMap := make(map[common.Namespace]*registry.Runtime) for _, rt := range regSt.Runtimes { - err := registry.VerifyRuntime(®St.Parameters, ctx.Logger(), rt, true, false, epoch) + err := registry.VerifyRuntime(®St.Parameters, ctx.Logger(), rt, true, false, epoch, true) if err != nil { ctx.Logger().Error("InitChain: Invalid runtime", "err", err, diff --git a/go/consensus/cometbft/apps/keymanager/secrets/epoch.go b/go/consensus/cometbft/apps/keymanager/secrets/epoch.go index 0f73e035f63..41e52ca5ad1 100644 --- a/go/consensus/cometbft/apps/keymanager/secrets/epoch.go +++ b/go/consensus/cometbft/apps/keymanager/secrets/epoch.go @@ -10,8 +10,10 @@ import ( tmapi "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/api" secretsState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/keymanager/secrets/state" registryState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/registry/state" + "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/features" "github.com/oasisprotocol/oasis-core/go/keymanager/secrets" registry "github.com/oasisprotocol/oasis-core/go/registry/api" + "github.com/oasisprotocol/oasis-core/go/upgrade/migrations" ) func (ext *secretsExt) onEpochChange(ctx *tmapi.Context, epoch beacon.EpochTime) error { @@ -26,6 +28,11 @@ func (ext *secretsExt) onEpochChange(ctx *tmapi.Context, epoch beacon.EpochTime) return fmt.Errorf("failed to get consensus parameters: %w", err) } + isFeatureVersion242, err := features.IsFeatureVersion(ctx, migrations.Version242) + if err != nil { + return err + } + // Recalculate all the key manager statuses. // // Note: This assumes that once a runtime is registered, it never expires. @@ -64,7 +71,7 @@ func (ext *secretsExt) onEpochChange(ctx *tmapi.Context, epoch beacon.EpochTime) return fmt.Errorf("failed to query key manager master secret: %w", err) } - newStatus := generateStatus(ctx, rt, oldStatus, secret, nodes, params, epoch) + newStatus := generateStatus(ctx, rt, oldStatus, secret, nodes, params, epoch, isFeatureVersion242) if forceEmit || !bytes.Equal(cbor.Marshal(oldStatus), cbor.Marshal(newStatus)) { ctx.Logger().Debug("status updated", "id", newStatus.ID, diff --git a/go/consensus/cometbft/apps/keymanager/secrets/status.go b/go/consensus/cometbft/apps/keymanager/secrets/status.go index ebb8bb5d5da..fc978655dcd 100644 --- a/go/consensus/cometbft/apps/keymanager/secrets/status.go +++ b/go/consensus/cometbft/apps/keymanager/secrets/status.go @@ -32,6 +32,7 @@ func generateStatus( // nolint: gocyclo nodes []*node.Node, params *registry.ConsensusParameters, epoch beacon.EpochTime, + isFeatureVersion242 bool, ) *secrets.Status { status := &secrets.Status{ ID: kmrt.ID, @@ -111,7 +112,7 @@ nextNode: continue nextNode } - initResponse, err := VerifyExtraInfo(ctx.Logger(), n.ID, kmrt, nodeRt, ts, height, params) + initResponse, err := VerifyExtraInfo(ctx.Logger(), n.ID, kmrt, nodeRt, ts, height, params, isFeatureVersion242) if err != nil { ctx.Logger().Error("failed to validate ExtraInfo", append(vars, "err", err)...) continue nextNode @@ -231,8 +232,9 @@ func VerifyExtraInfo( ts time.Time, height uint64, params *registry.ConsensusParameters, + isFeatureVersion242 bool, ) (*secrets.InitResponse, error) { - if err := registry.VerifyNodeRuntimeEnclaveIDs(logger, nodeID, nodeRt, rt, params.TEEFeatures, ts, height); err != nil { + if err := registry.VerifyNodeRuntimeEnclaveIDs(logger, nodeID, nodeRt, rt, params.TEEFeatures, ts, height, isFeatureVersion242); err != nil { return nil, err } if nodeRt.ExtraInfo == nil { diff --git a/go/consensus/cometbft/apps/keymanager/secrets/status_test.go b/go/consensus/cometbft/apps/keymanager/secrets/status_test.go index 414bb57ecaf..563b6aa1617 100644 --- a/go/consensus/cometbft/apps/keymanager/secrets/status_test.go +++ b/go/consensus/cometbft/apps/keymanager/secrets/status_test.go @@ -207,10 +207,10 @@ func TestGenerateStatus(t *testing.T) { t.Run("No nodes", func(t *testing.T) { require := require.New(t) - newStatus := generateStatus(ctx, runtimes[0], uninitializedStatus, nil, nodes[0:6], params, epoch) + newStatus := generateStatus(ctx, runtimes[0], uninitializedStatus, nil, nodes[0:6], params, epoch, true) require.Equal(uninitializedStatus, newStatus, "key manager committee should be empty") - newStatus = generateStatus(ctx, runtimes[0], initializedStatus, nil, nodes[0:6], params, epoch) + newStatus = generateStatus(ctx, runtimes[0], initializedStatus, nil, nodes[0:6], params, epoch, true) require.Equal(initializedStatus, newStatus, "key manager committee should be empty") }) @@ -225,16 +225,16 @@ func TestGenerateStatus(t *testing.T) { Policy: &policy, Nodes: []signature.PublicKey{nodes[6].ID}, } - newStatus := generateStatus(ctx, runtimes[0], uninitializedStatus, nil, nodes[6:7], params, epoch) + newStatus := generateStatus(ctx, runtimes[0], uninitializedStatus, nil, nodes[6:7], params, epoch, true) require.Equal(expStatus, newStatus, "node 6 should form the committee if key manager not initialized") - newStatus = generateStatus(ctx, runtimes[0], expStatus, nil, nodes[6:7], params, epoch) + newStatus = generateStatus(ctx, runtimes[0], expStatus, nil, nodes[6:7], params, epoch, true) require.Equal(expStatus, newStatus, "node 6 should form the committee if key manager is not secure") expStatus.IsSecure = true expStatus.Checksum = checksum expStatus.Nodes = nil - newStatus = generateStatus(ctx, runtimes[0], initializedStatus, nil, nodes[6:7], params, epoch) + newStatus = generateStatus(ctx, runtimes[0], initializedStatus, nil, nodes[6:7], params, epoch, true) require.Equal(expStatus, newStatus, "node 6 should not be added to the committee if key manager is secure or checksum differs") }) @@ -251,20 +251,20 @@ func TestGenerateStatus(t *testing.T) { Policy: &policy, Nodes: []signature.PublicKey{nodes[6].ID}, } - newStatus := generateStatus(ctx, runtimes[0], uninitializedStatus, nil, nodes, params, epoch) + newStatus := generateStatus(ctx, runtimes[0], uninitializedStatus, nil, nodes, params, epoch, true) require.Equal(expStatus, newStatus, "node 6 should be the source of truth and form the committee") // If the order is reversed, it should be the other way around. expStatus.IsSecure = true expStatus.Nodes = []signature.PublicKey{nodes[7].ID} - newStatus = generateStatus(ctx, runtimes[0], uninitializedStatus, nil, reverse(nodes), params, epoch) + newStatus = generateStatus(ctx, runtimes[0], uninitializedStatus, nil, reverse(nodes), params, epoch, true) require.Equal(expStatus, newStatus, "node 7 should be the source of truth and form the committee") // If the key manager is already initialized as secure with a checksum, then all nodes // except 8 and 9 are ignored. expStatus.Checksum = checksum expStatus.Nodes = []signature.PublicKey{nodes[8].ID, nodes[9].ID} - newStatus = generateStatus(ctx, runtimes[0], initializedStatus, nil, nodes, params, epoch) + newStatus = generateStatus(ctx, runtimes[0], initializedStatus, nil, nodes, params, epoch, true) require.Equal(expStatus, newStatus, "node 7 and 8 should form the committee if key manager is initialized as secure") // The second key manager. @@ -277,7 +277,7 @@ func TestGenerateStatus(t *testing.T) { Nodes: []signature.PublicKey{nodes[4].ID, nodes[9].ID}, } initializedStatus.ID = runtimeIDs[1] - newStatus = generateStatus(ctx, runtimes[1], initializedStatus, nil, nodes, params, epoch) + newStatus = generateStatus(ctx, runtimes[1], initializedStatus, nil, nodes, params, epoch, true) require.Equal(expStatus, newStatus, "node 4 and 9 should form the committee") }) } diff --git a/go/consensus/cometbft/apps/keymanager/secrets/txs.go b/go/consensus/cometbft/apps/keymanager/secrets/txs.go index 67caa714a91..284e9bcdcc2 100644 --- a/go/consensus/cometbft/apps/keymanager/secrets/txs.go +++ b/go/consensus/cometbft/apps/keymanager/secrets/txs.go @@ -76,11 +76,11 @@ func (ext *secretsExt) updatePolicy( status.NextPolicy = sigPol // Support legacy behavior where the policy was applied immediately. - ok, err := features.IsFeatureVersion(ctx, migrations.Version242) + isFeatureVersion242, err := features.IsFeatureVersion(ctx, migrations.Version242) if err != nil { return err } - if !ok { + if !isFeatureVersion242 { // Ok, as far as we can tell the new policy is valid, apply it. // // Note: The key manager cohort responsible for servicing this ID @@ -104,7 +104,7 @@ func (ext *secretsExt) updatePolicy( nodes, _ := regState.Nodes(ctx) registry.SortNodeList(nodes) - status = generateStatus(ctx, kmRt, status, nil, nodes, regParams, epoch) + status = generateStatus(ctx, kmRt, status, nil, nodes, regParams, epoch, isFeatureVersion242) } if err := state.SetStatus(ctx, status); err != nil { diff --git a/go/consensus/cometbft/apps/registry/genesis.go b/go/consensus/cometbft/apps/registry/genesis.go index ee48328e71b..120ef50e8c3 100644 --- a/go/consensus/cometbft/apps/registry/genesis.go +++ b/go/consensus/cometbft/apps/registry/genesis.go @@ -55,7 +55,7 @@ func (app *Application) InitChain(ctx *abciAPI.Context, _ types.RequestInitChain if rt == nil { return fmt.Errorf("registry: genesis runtime index %d is nil", i) } - err := registry.VerifyRuntime(&st.Parameters, ctx.Logger(), rt, ctx.IsInitChain(), false, epoch) + err := registry.VerifyRuntime(&st.Parameters, ctx.Logger(), rt, ctx.IsInitChain(), false, epoch, true) if err != nil { return err } diff --git a/go/consensus/cometbft/apps/registry/messages.go b/go/consensus/cometbft/apps/registry/messages.go index cadf3318bf3..e7dfbabb152 100644 --- a/go/consensus/cometbft/apps/registry/messages.go +++ b/go/consensus/cometbft/apps/registry/messages.go @@ -6,8 +6,10 @@ import ( "github.com/oasisprotocol/oasis-core/go/common/cbor" "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/api" registryState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/registry/state" + "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/features" governance "github.com/oasisprotocol/oasis-core/go/governance/api" registry "github.com/oasisprotocol/oasis-core/go/registry/api" + "github.com/oasisprotocol/oasis-core/go/upgrade/migrations" ) func (app *Application) changeParameters(ctx *api.Context, msg any, apply bool) (any, error) { @@ -26,6 +28,11 @@ func (app *Application) changeParameters(ctx *api.Context, msg any, apply bool) return nil, fmt.Errorf("registry: failed to unmarshal consensus parameter changes: %w", err) } + isFeatureVersion242, err := features.IsFeatureVersion(ctx, migrations.Version242) + if err != nil { + return nil, err + } + // Validate changes against current parameters. state := registryState.NewMutableState(ctx.State()) params, err := state.ConsensusParameters(ctx) @@ -38,7 +45,7 @@ func (app *Application) changeParameters(ctx *api.Context, msg any, apply bool) if err = changes.Apply(params); err != nil { return nil, fmt.Errorf("registry: failed to apply consensus parameter changes: %w", err) } - if err = params.SanityCheck(); err != nil { + if err = params.SanityCheck(isFeatureVersion242); err != nil { return nil, fmt.Errorf("registry: failed to validate consensus parameters: %w", err) } diff --git a/go/consensus/cometbft/apps/registry/messages_test.go b/go/consensus/cometbft/apps/registry/messages_test.go index 649770802d8..3bdb2153c59 100644 --- a/go/consensus/cometbft/apps/registry/messages_test.go +++ b/go/consensus/cometbft/apps/registry/messages_test.go @@ -7,8 +7,11 @@ import ( beacon "github.com/oasisprotocol/oasis-core/go/beacon/api" "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/version" abciAPI "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/api" + consensusState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/consensus/state" registryState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/registry/state" + "github.com/oasisprotocol/oasis-core/go/consensus/genesis" governance "github.com/oasisprotocol/oasis-core/go/governance/api" registry "github.com/oasisprotocol/oasis-core/go/registry/api" ) @@ -30,6 +33,12 @@ func TestChangeParameters(t *testing.T) { err := state.SetConsensusParameters(ctx, params) require.NoError(t, err, "setting consensus parameters should succeed") + consensusState := consensusState.NewMutableState(ctx.State()) + err = consensusState.SetConsensusParameters(ctx, &genesis.Parameters{ + FeatureVersion: &version.Version{Major: 100}, + }) + require.NoError(t, err, "setting consensus parameters should succeed") + // Prepare proposal. maxNodeExpiration := beacon.EpochTime(20) changes := registry.ConsensusParameterChanges{ diff --git a/go/consensus/cometbft/apps/registry/transactions.go b/go/consensus/cometbft/apps/registry/transactions.go index b2463ce0277..7e841b38683 100644 --- a/go/consensus/cometbft/apps/registry/transactions.go +++ b/go/consensus/cometbft/apps/registry/transactions.go @@ -12,8 +12,10 @@ import ( registryApi "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/registry/api" registryState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/registry/state" stakingState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/staking/state" + "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/features" registry "github.com/oasisprotocol/oasis-core/go/registry/api" staking "github.com/oasisprotocol/oasis-core/go/staking/api" + "github.com/oasisprotocol/oasis-core/go/upgrade/migrations" ) func (app *Application) registerEntity( @@ -226,6 +228,14 @@ func (app *Application) registerNode( // nolint: gocyclo return err } + isFeatureVersion242 := true + if !ctx.IsInitChain() { + isFeatureVersion242, err = features.IsFeatureVersion(ctx, migrations.Version242) + if err != nil { + return err + } + } + newNode, paidRuntimes, err := registry.VerifyRegisterNodeArgs( ctx, params, @@ -239,6 +249,7 @@ func (app *Application) registerNode( // nolint: gocyclo epoch, state, state, + isFeatureVersion242, ) if err != nil { return err @@ -579,7 +590,15 @@ func (app *Application) registerRuntime( // nolint: gocyclo return nil, err } - if err = registry.VerifyRuntime(params, ctx.Logger(), rt, ctx.IsInitChain(), false, epoch); err != nil { + isFeatureVersion242 := true + if !ctx.IsInitChain() { + isFeatureVersion242, err = features.IsFeatureVersion(ctx, migrations.Version242) + if err != nil { + return nil, err + } + } + + if err = registry.VerifyRuntime(params, ctx.Logger(), rt, ctx.IsInitChain(), false, epoch, isFeatureVersion242); err != nil { return nil, err } @@ -629,7 +648,7 @@ func (app *Application) registerRuntime( // nolint: gocyclo switch { case existingRt != nil: // Existing runtime, verify update. - err = registry.VerifyRuntimeUpdate(ctx.Logger(), existingRt, rt, epoch, params) + err = registry.VerifyRuntimeUpdate(ctx.Logger(), existingRt, rt, epoch, params, isFeatureVersion242) default: // New runtime, verify new descriptor. err = registry.VerifyRuntimeNew(ctx.Logger(), rt, epoch, params, ctx.IsInitChain()) diff --git a/go/consensus/cometbft/apps/registry/transactions_test.go b/go/consensus/cometbft/apps/registry/transactions_test.go index 5861fcaf4a1..92428bb29a2 100644 --- a/go/consensus/cometbft/apps/registry/transactions_test.go +++ b/go/consensus/cometbft/apps/registry/transactions_test.go @@ -17,8 +17,10 @@ import ( "github.com/oasisprotocol/oasis-core/go/common/version" abciAPI "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/api" beaconState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/beacon/state" + consensusState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/consensus/state" registryState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/registry/state" stakingState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/staking/state" + "github.com/oasisprotocol/oasis-core/go/consensus/genesis" registry "github.com/oasisprotocol/oasis-core/go/registry/api" staking "github.com/oasisprotocol/oasis-core/go/staking/api" ) @@ -36,6 +38,7 @@ func TestRegisterNode(t *testing.T) { state := registryState.NewMutableState(ctx.State()) stakeState := stakingState.NewMutableState(ctx.State()) beaconState := beaconState.NewMutableState(ctx.State()) + consensusState := consensusState.NewMutableState(ctx.State()) // Set up default staking consensus parameters. defaultStakeParameters := staking.ConsensusParameters{ @@ -62,6 +65,12 @@ func TestRegisterNode(t *testing.T) { }) require.NoError(err, "beacon.SetConsensusParameters") + // Set consensus parameters. + err = consensusState.SetConsensusParameters(ctx, &genesis.Parameters{ + FeatureVersion: &version.Version{Major: 100}, + }) + require.NoError(err, "consensus.SetConsensusParameters") + // Store all successful registrations in a map for easier reference in later test cases. type testCaseData struct { // Signers. @@ -682,6 +691,7 @@ func TestRegisterRuntime(t *testing.T) { state := registryState.NewMutableState(ctx.State()) stakeState := stakingState.NewMutableState(ctx.State()) beaconState := beaconState.NewMutableState(ctx.State()) + consensusState := consensusState.NewMutableState(ctx.State()) // Set up default staking consensus parameters. defaultStakeParameters := staking.ConsensusParameters{ @@ -708,6 +718,12 @@ func TestRegisterRuntime(t *testing.T) { }) require.NoError(err, "beacon.SetConsensusParameters") + // Set consensus parameters. + err = consensusState.SetConsensusParameters(ctx, &genesis.Parameters{ + FeatureVersion: &version.Version{Major: 100}, + }) + require.NoError(err, "consensus.SetConsensusParameters") + // Store all successful registrations in a map for easier reference in later test cases. type testCaseData struct { // Signers. diff --git a/go/consensus/cometbft/apps/scheduler/scheduler.go b/go/consensus/cometbft/apps/scheduler/scheduler.go index 47b5440038d..7f354e152ad 100644 --- a/go/consensus/cometbft/apps/scheduler/scheduler.go +++ b/go/consensus/cometbft/apps/scheduler/scheduler.go @@ -29,9 +29,11 @@ import ( schedulerState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/scheduler/state" stakingapp "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/staking" stakingState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/staking/state" + "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/features" registry "github.com/oasisprotocol/oasis-core/go/registry/api" scheduler "github.com/oasisprotocol/oasis-core/go/scheduler/api" staking "github.com/oasisprotocol/oasis-core/go/staking/api" + "github.com/oasisprotocol/oasis-core/go/upgrade/migrations" ) var ( @@ -243,6 +245,12 @@ func (app *Application) elect(ctx *api.Context, epoch beacon.EpochTime, reward b return fmt.Errorf("cometbft/scheduler: couldn't elect validators: %w", err) } + // Check if the current feature version is at least 24.2. + isFeatureVersion242, err := features.IsFeatureVersion(ctx, migrations.Version242) + if err != nil { + return err + } + if err = app.electCommittees( ctx, epoch, @@ -254,6 +262,7 @@ func (app *Application) elect(ctx *api.Context, epoch beacon.EpochTime, reward b rewardableEntities, validatorEntities, committeeNodes, + isFeatureVersion242, ); err != nil { return fmt.Errorf("cometbft/scheduler: couldn't elect committees: %w", err) } @@ -363,6 +372,7 @@ func isSuitableExecutorWorker( rt *registry.Runtime, epoch beacon.EpochTime, registryParams *registry.ConsensusParameters, + isFeatureVersion242 bool, ) bool { if !n.node.HasRoles(node.RoleComputeWorker) { return false @@ -403,6 +413,7 @@ func isSuitableExecutorWorker( uint64(ctx.LastHeight()), activeDeployment.TEE, n.node.ID, + isFeatureVersion242, ); err != nil { ctx.Logger().Warn("failed to verify node TEE attestation", "err", err, @@ -429,6 +440,7 @@ func (app *Application) electCommittees( rewardableEntities map[staking.Address]struct{}, validatorEntities map[staking.Address]struct{}, nodes []*nodeWithStatus, + isFeatureVersion242 bool, ) error { runtimes, err := fetchRuntimes(ctx) if err != nil { @@ -454,6 +466,7 @@ func (app *Application) electCommittees( runtime, nodes, kind, + isFeatureVersion242, ); err != nil { return err } diff --git a/go/consensus/cometbft/apps/scheduler/scheduler_test.go b/go/consensus/cometbft/apps/scheduler/scheduler_test.go index 7ba70a22bc0..39f93733d6d 100644 --- a/go/consensus/cometbft/apps/scheduler/scheduler_test.go +++ b/go/consensus/cometbft/apps/scheduler/scheduler_test.go @@ -817,6 +817,7 @@ func TestElectCommittee(t *testing.T) { &tc.rt, nodes, tc.kind, + true, ) require.NoError(err, "committee election should not fail") diff --git a/go/consensus/cometbft/apps/scheduler/shuffle.go b/go/consensus/cometbft/apps/scheduler/shuffle.go index 57cc8a651b4..a51cae01947 100644 --- a/go/consensus/cometbft/apps/scheduler/shuffle.go +++ b/go/consensus/cometbft/apps/scheduler/shuffle.go @@ -148,6 +148,7 @@ func electCommittee( rt *registry.Runtime, nodes []*nodeWithStatus, kind scheduler.CommitteeKind, + isFeatureVersion242 bool, ) error { ctx.Logger().Debug("electing committee", "epoch", epoch, @@ -173,6 +174,7 @@ func electCommittee( rt, nodes, kind, + isFeatureVersion242, ) if err != nil { return err @@ -217,6 +219,7 @@ func electCommitteeMembers( //nolint: gocyclo rt *registry.Runtime, nodes []*nodeWithStatus, kind scheduler.CommitteeKind, + isFeatureVersion242 bool, ) ([]*scheduler.CommitteeNode, error) { // Workers must be listed before backup workers, as other parts of the code depend on this // order for better performance. @@ -256,7 +259,7 @@ func electCommitteeMembers( //nolint: gocyclo // Determine the committee size, and pre-filter the node-list based // on eligibility, entity stake and other criteria. - var isSuitableFn func(*api.Context, *nodeWithStatus, *registry.Runtime, beacon.EpochTime, *registry.ConsensusParameters) bool + var isSuitableFn func(*api.Context, *nodeWithStatus, *registry.Runtime, beacon.EpochTime, *registry.ConsensusParameters, bool) bool groupSizes := make(map[scheduler.Role]int) switch kind { case scheduler.KindComputeExecutor: @@ -291,7 +294,7 @@ func electCommitteeMembers( //nolint: gocyclo } // Check general node compatibility. - if !isSuitableFn(ctx, n, rt, epoch, registryParameters) { + if !isSuitableFn(ctx, n, rt, epoch, registryParameters, isFeatureVersion242) { continue } diff --git a/go/consensus/cometbft/apps/supplementarysanity/checks.go b/go/consensus/cometbft/apps/supplementarysanity/checks.go index 13f512c1d5e..d16c69e2504 100644 --- a/go/consensus/cometbft/apps/supplementarysanity/checks.go +++ b/go/consensus/cometbft/apps/supplementarysanity/checks.go @@ -13,6 +13,7 @@ import ( registryState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/registry/state" roothashState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/roothash/state" stakingState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/staking/state" + "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/features" governance "github.com/oasisprotocol/oasis-core/go/governance/api" "github.com/oasisprotocol/oasis-core/go/keymanager/churp" "github.com/oasisprotocol/oasis-core/go/keymanager/secrets" @@ -20,6 +21,7 @@ import ( roothash "github.com/oasisprotocol/oasis-core/go/roothash/api" "github.com/oasisprotocol/oasis-core/go/roothash/api/block" staking "github.com/oasisprotocol/oasis-core/go/staking/api" + "github.com/oasisprotocol/oasis-core/go/upgrade/migrations" ) func checkEpochTime(_ *abciAPI.Context, now beacon.EpochTime) error { @@ -59,7 +61,12 @@ func checkRegistry(ctx *abciAPI.Context, now beacon.EpochTime) error { return fmt.Errorf("SuspendedRuntimes: %w", err) } - runtimeLookup, err := registry.SanityCheckRuntimes(logger, params, runtimes, suspendedRuntimes, false, now) + isFeatureVersion242, err := features.IsFeatureVersion(ctx, migrations.Version242) + if err != nil { + return err + } + + runtimeLookup, err := registry.SanityCheckRuntimes(logger, params, runtimes, suspendedRuntimes, false, now, isFeatureVersion242) if err != nil { return fmt.Errorf("SanityCheckRuntimes: %w", err) } @@ -69,7 +76,7 @@ func checkRegistry(ctx *abciAPI.Context, now beacon.EpochTime) error { if err != nil { return fmt.Errorf("SignedNodes: %w", err) } - _, err = registry.SanityCheckNodes(logger, params, signedNodes, seenEntities, runtimeLookup, false, now, ctx.Now(), uint64(ctx.LastHeight())) + _, err = registry.SanityCheckNodes(logger, params, signedNodes, seenEntities, runtimeLookup, false, now, ctx.Now(), uint64(ctx.LastHeight()), isFeatureVersion242) if err != nil { return fmt.Errorf("SanityCheckNodes: %w", err) } diff --git a/go/oasis-node/cmd/debug/byzantine/steps_test.go b/go/oasis-node/cmd/debug/byzantine/steps_test.go index bde13be10df..38e1afb7645 100644 --- a/go/oasis-node/cmd/debug/byzantine/steps_test.go +++ b/go/oasis-node/cmd/debug/byzantine/steps_test.go @@ -31,5 +31,5 @@ func TestFakeCapabilitySGX(t *testing.T) { ias.SetSkipVerify() ias.SetAllowDebugEnclaves() - require.NoError(t, fakeCapabilitiesSGX.TEE.Verify(&teeCfg, time.Now(), 1, cs, nodeID), "fakeCapabilitiesSGX not valid") + require.NoError(t, fakeCapabilitiesSGX.TEE.Verify(&teeCfg, time.Now(), 1, cs, nodeID, true), "fakeCapabilitiesSGX not valid") } diff --git a/go/registry/api/api.go b/go/registry/api/api.go index f6ff1b6651c..2811a120fac 100644 --- a/go/registry/api/api.go +++ b/go/registry/api/api.go @@ -497,6 +497,7 @@ func VerifyRegisterNodeArgs( // nolint: gocyclo epoch beacon.EpochTime, runtimeLookup RuntimeLookup, nodeLookup NodeLookup, + isFeatureVersion242 bool, ) (*node.Node, []*Runtime, error) { var n node.Node if sigNode == nil { @@ -618,7 +619,7 @@ func VerifyRegisterNodeArgs( // nolint: gocyclo // both validators and compute nodes and have out of date attestation evidence. Removing // such nodes could lead to consensus not having the proper majority. This is safe as // attestation evidence is independently verified before scheduling committees. - if err := VerifyNodeRuntimeEnclaveIDs(logger, n.ID, rt, regRt, params.TEEFeatures, now, height); err != nil && !isSanityCheck && !isGenesis { + if err := VerifyNodeRuntimeEnclaveIDs(logger, n.ID, rt, regRt, params.TEEFeatures, now, height, isFeatureVersion242); err != nil && !isSanityCheck && !isGenesis { return nil, nil, err } @@ -800,6 +801,7 @@ func VerifyNodeRuntimeEnclaveIDs( teeCfg *node.TEEFeatures, ts time.Time, height uint64, + isFeatureVersion242 bool, ) error { // Verify that the node is running on the same hardware as the runtime. hw := node.TEEHardwareInvalid @@ -828,7 +830,7 @@ func VerifyNodeRuntimeEnclaveIDs( continue } - if err := rt.Capabilities.TEE.Verify(teeCfg, ts, height, rtVersionInfo.TEE, nodeID); err != nil { + if err := rt.Capabilities.TEE.Verify(teeCfg, ts, height, rtVersionInfo.TEE, nodeID, isFeatureVersion242); err != nil { logger.Error("VerifyNodeRuntimeEnclaveIDs: failed to validate attestation", "node_id", nodeID, "runtime_id", rt.ID, @@ -1097,6 +1099,7 @@ func VerifyRuntime( // nolint: gocyclo isGenesis bool, isSanityCheck bool, now beacon.EpochTime, + isFeatureVersion242 bool, ) error { if rt == nil { return fmt.Errorf("%w: no runtime given", ErrInvalidArgument) @@ -1141,7 +1144,7 @@ func VerifyRuntime( // nolint: gocyclo // Validate the deployments. This also handles validating that the // appropriate TEE configuration is present in each deployment. - if err := rt.ValidateDeployments(now, params); err != nil { + if err := rt.ValidateDeployments(now, params, isFeatureVersion242); err != nil { logger.Error("RegisterRuntime: invalid deployments", "runtime_id", rt.ID, "err", err, @@ -1215,6 +1218,7 @@ func VerifyRuntimeUpdate( currentRt, newRt *Runtime, now beacon.EpochTime, params *ConsensusParameters, + isFeatureVersion242 bool, ) error { if !currentRt.ID.Equal(&newRt.ID) { logger.Error("RegisterRuntime: trying to update runtime ID", @@ -1270,7 +1274,7 @@ func VerifyRuntimeUpdate( // Validate the deployments. activeDeployment := currentRt.ActiveDeployment(now) - if err := currentRt.ValidateDeployments(now, params); err != nil { + if err := currentRt.ValidateDeployments(now, params, isFeatureVersion242); err != nil { // Invariant violation, this should NEVER happen. logger.Error("RegisterRuntime: malformed deployments present in state", "runtime_id", currentRt.ID, @@ -1284,7 +1288,7 @@ func VerifyRuntimeUpdate( } newActiveDeployment := newRt.ActiveDeployment(now) - if err := newRt.ValidateDeployments(now, params); err != nil { + if err := newRt.ValidateDeployments(now, params, isFeatureVersion242); err != nil { logger.Error("RegisterRuntime: malformed deployments", "runtime_id", currentRt.ID, "err", err, diff --git a/go/registry/api/api_test.go b/go/registry/api/api_test.go index bbc5be40b70..3fb56c76ddd 100644 --- a/go/registry/api/api_test.go +++ b/go/registry/api/api_test.go @@ -211,7 +211,7 @@ func TestVerifyRegisterNodeArgs(t *testing.T) { &tc.n, ) require.NoError(err, "singing node") - _, _, err = VerifyRegisterNodeArgs(context.Background(), params, logger, signedNode, entity, time.Now(), 1, false, false, beacon.EpochTime(10), rtLookup, ndLookup) + _, _, err = VerifyRegisterNodeArgs(context.Background(), params, logger, signedNode, entity, time.Now(), 1, false, false, beacon.EpochTime(10), rtLookup, ndLookup, true) switch { case tc.err == nil: require.NoError(err, tc.msg) diff --git a/go/registry/api/runtime.go b/go/registry/api/runtime.go index 3ebe883864c..722ed53febf 100644 --- a/go/registry/api/runtime.go +++ b/go/registry/api/runtime.go @@ -526,7 +526,7 @@ func (r *Runtime) DeploymentForVersion(v version.Version) *VersionInfo { // ValidateDeployments validates a runtime descriptor's Deployments field // at the specified epoch. -func (r *Runtime) ValidateDeployments(now beacon.EpochTime, params *ConsensusParameters) error { +func (r *Runtime) ValidateDeployments(now beacon.EpochTime, params *ConsensusParameters, isFeatureVersion242 bool) error { // The runtime descriptor's deployments field is considered valid // if: // * There is at least one entry present. @@ -594,7 +594,7 @@ func (r *Runtime) ValidateDeployments(now beacon.EpochTime, params *ConsensusPar if err := cbor.Unmarshal(deployment.TEE, &cs); err != nil { return fmt.Errorf("%w: invalid SGX TEE constraints", ErrInvalidArgument) } - if err := cs.ValidateBasic(params.TEEFeatures); err != nil { + if err := cs.ValidateBasic(params.TEEFeatures, isFeatureVersion242); err != nil { return fmt.Errorf("%w: invalid SGX TEE constraints", ErrInvalidArgument) } if len(cs.Enclaves) == 0 { diff --git a/go/registry/api/runtime_test.go b/go/registry/api/runtime_test.go index adaab49b87b..562f65a3278 100644 --- a/go/registry/api/runtime_test.go +++ b/go/registry/api/runtime_test.go @@ -656,7 +656,7 @@ func TestVerifyRuntime(t *testing.T) { tc.cpFn(&cp) } - err := VerifyRuntime(&cp, logging.GetLogger("runtime/tests"), &tc.rr, false, true, beacon.EpochTime(10)) + err := VerifyRuntime(&cp, logging.GetLogger("runtime/tests"), &tc.rr, false, true, beacon.EpochTime(10), true) switch { case tc.err == nil: require.NoError(err, tc.msg) diff --git a/go/registry/api/sanity_check.go b/go/registry/api/sanity_check.go index 6ba14896227..8a7e23cd685 100644 --- a/go/registry/api/sanity_check.go +++ b/go/registry/api/sanity_check.go @@ -16,7 +16,7 @@ import ( ) // SanityCheck performs a sanity check on the consensus parameters. -func (p *ConsensusParameters) SanityCheck() error { +func (p *ConsensusParameters) SanityCheck(isFeatureVersion242 bool) error { if !flags.DebugDontBlameOasis() { if p.DebugAllowUnroutableAddresses || p.DebugDeployImmediately { return fmt.Errorf("one or more unsafe debug flags set") @@ -25,6 +25,17 @@ func (p *ConsensusParameters) SanityCheck() error { return fmt.Errorf("maximum node expiration not specified") } } + + if p.TEEFeatures != nil { + defaultPolicy := p.TEEFeatures.SGX.DefaultPolicy + if defaultPolicy != nil { + if err := defaultPolicy.Validate(isFeatureVersion242); err != nil { + return err + } + } + + } + return nil } @@ -51,7 +62,7 @@ func (g *Genesis) SanityCheck( ) error { logger := logging.NewNopLogger() - if err := g.Parameters.SanityCheck(); err != nil { + if err := g.Parameters.SanityCheck(true); err != nil { return fmt.Errorf("registry: sanity check failed: %w", err) } @@ -62,13 +73,13 @@ func (g *Genesis) SanityCheck( } // Check runtimes. - runtimesLookup, err := SanityCheckRuntimes(logger, &g.Parameters, g.Runtimes, g.SuspendedRuntimes, true, baseEpoch) + runtimesLookup, err := SanityCheckRuntimes(logger, &g.Parameters, g.Runtimes, g.SuspendedRuntimes, true, baseEpoch, true) if err != nil { return err } // Check nodes. - nodeLookup, err := SanityCheckNodes(logger, &g.Parameters, g.Nodes, seenEntities, runtimesLookup, true, baseEpoch, now, height) + nodeLookup, err := SanityCheckNodes(logger, &g.Parameters, g.Nodes, seenEntities, runtimesLookup, true, baseEpoch, now, height, true) if err != nil { return err } @@ -134,11 +145,12 @@ func SanityCheckRuntimes( suspendedRuntimes []*Runtime, isGenesis bool, now beacon.EpochTime, + isFeatureVersion242 bool, ) (RuntimeLookup, error) { // First go through all runtimes and perform general sanity checks. seenRuntimes := []*Runtime{} for _, rt := range runtimes { - if err := VerifyRuntime(params, logger, rt, isGenesis, true, now); err != nil { + if err := VerifyRuntime(params, logger, rt, isGenesis, true, now, isFeatureVersion242); err != nil { return nil, fmt.Errorf("runtime sanity check failed: %w", err) } seenRuntimes = append(seenRuntimes, rt) @@ -146,7 +158,7 @@ func SanityCheckRuntimes( seenSuspendedRuntimes := []*Runtime{} for _, rt := range suspendedRuntimes { - if err := VerifyRuntime(params, logger, rt, isGenesis, true, now); err != nil { + if err := VerifyRuntime(params, logger, rt, isGenesis, true, now, isFeatureVersion242); err != nil { return nil, fmt.Errorf("runtime sanity check failed: %w", err) } seenSuspendedRuntimes = append(seenSuspendedRuntimes, rt) @@ -184,6 +196,7 @@ func SanityCheckNodes( epoch beacon.EpochTime, now time.Time, height uint64, + isFeatureVersion242 bool, ) (NodeLookup, error) { // nolint: gocyclo nodeLookup := &sanityCheckNodeLookup{ @@ -218,6 +231,7 @@ func SanityCheckNodes( epoch, runtimesLookup, nodeLookup, + isFeatureVersion242, ) if err != nil { return nil, fmt.Errorf("registry: node sanity check failed: ID: %s, error: %w", n.ID.String(), err)