Skip to content

Commit cce6686

Browse files
committed
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.
1 parent 6e6f3ac commit cce6686

File tree

8 files changed

+123
-10
lines changed

8 files changed

+123
-10
lines changed

go/common/sgx/quote/quote.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,16 @@ type Policy struct {
6464
IAS *ias.QuotePolicy `json:"ias,omitempty" yaml:"ias,omitempty"`
6565
PCS *pcs.QuotePolicy `json:"pcs,omitempty" yaml:"pcs,omitempty"`
6666
}
67+
68+
// VerifyFMSPCWhitelist verifies that the FMSPC whitelist is empty.
69+
func (p *Policy) VerifyFMSPCWhitelist() error {
70+
if p.PCS == nil {
71+
return nil
72+
}
73+
74+
if len(p.PCS.FMSPCWhitelist) == 0 {
75+
return nil
76+
}
77+
78+
return fmt.Errorf("fmspc whitelist should be empty")
79+
}

go/consensus/cometbft/apps/registry/messages.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import (
66
"github.com/oasisprotocol/oasis-core/go/common/cbor"
77
"github.com/oasisprotocol/oasis-core/go/consensus/cometbft/api"
88
registryState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/registry/state"
9+
"github.com/oasisprotocol/oasis-core/go/consensus/cometbft/features"
910
governance "github.com/oasisprotocol/oasis-core/go/governance/api"
1011
registry "github.com/oasisprotocol/oasis-core/go/registry/api"
12+
"github.com/oasisprotocol/oasis-core/go/upgrade/migrations"
1113
)
1214

1315
func (app *Application) changeParameters(ctx *api.Context, msg any, apply bool) (any, error) {
@@ -41,6 +43,9 @@ func (app *Application) changeParameters(ctx *api.Context, msg any, apply bool)
4143
if err = params.SanityCheck(); err != nil {
4244
return nil, fmt.Errorf("registry: failed to validate consensus parameters: %w", err)
4345
}
46+
if err = sanityCheckFMSPCWhitelist(ctx, &changes); err != nil {
47+
return nil, fmt.Errorf("registry: failed to validate quote policy changes: %w", err)
48+
}
4449

4550
// Apply changes.
4651
if apply {
@@ -52,3 +57,30 @@ func (app *Application) changeParameters(ctx *api.Context, msg any, apply bool)
5257
// Non-nil response signals that changes are valid and were successfully applied (if required).
5358
return struct{}{}, nil
5459
}
60+
61+
func sanityCheckFMSPCWhitelist(ctx *api.Context, changes *registry.ConsensusParameterChanges) error {
62+
if changes.TEEFeatures == nil {
63+
return nil
64+
}
65+
66+
teeFeatures := *changes.TEEFeatures
67+
if teeFeatures == nil {
68+
return nil
69+
}
70+
71+
defaultPolicy := teeFeatures.SGX.DefaultPolicy
72+
if defaultPolicy == nil {
73+
return nil
74+
}
75+
76+
// Allow non-empty `fmpcs_whitelist` when this feature is available.
77+
isFeatureVersion242, err := features.IsFeatureVersion(ctx, migrations.Version242)
78+
if err != nil {
79+
return err
80+
}
81+
if isFeatureVersion242 {
82+
return nil
83+
}
84+
85+
return defaultPolicy.VerifyFMSPCWhitelist()
86+
}

go/consensus/cometbft/apps/registry/transactions.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import (
1212
registryApi "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/registry/api"
1313
registryState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/registry/state"
1414
stakingState "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/apps/staking/state"
15+
"github.com/oasisprotocol/oasis-core/go/consensus/cometbft/features"
1516
registry "github.com/oasisprotocol/oasis-core/go/registry/api"
1617
staking "github.com/oasisprotocol/oasis-core/go/staking/api"
18+
"github.com/oasisprotocol/oasis-core/go/upgrade/migrations"
1719
)
1820

1921
func (app *Application) registerEntity(
@@ -226,6 +228,11 @@ func (app *Application) registerNode( // nolint: gocyclo
226228
return err
227229
}
228230

231+
isFeatureVersion242, err := features.IsFeatureVersion(ctx, migrations.Version242)
232+
if err != nil {
233+
return err
234+
}
235+
229236
newNode, paidRuntimes, err := registry.VerifyRegisterNodeArgs(
230237
ctx,
231238
params,
@@ -239,6 +246,7 @@ func (app *Application) registerNode( // nolint: gocyclo
239246
epoch,
240247
state,
241248
state,
249+
isFeatureVersion242,
242250
)
243251
if err != nil {
244252
return err
@@ -579,7 +587,12 @@ func (app *Application) registerRuntime( // nolint: gocyclo
579587
return nil, err
580588
}
581589

582-
if err = registry.VerifyRuntime(params, ctx.Logger(), rt, ctx.IsInitChain(), false, epoch); err != nil {
590+
isFeatureVersion242, err := features.IsFeatureVersion(ctx, migrations.Version242)
591+
if err != nil {
592+
return nil, err
593+
}
594+
595+
if err = registry.VerifyRuntime(params, ctx.Logger(), rt, ctx.IsInitChain(), false, epoch, isFeatureVersion242); err != nil {
583596
return nil, err
584597
}
585598

@@ -629,7 +642,7 @@ func (app *Application) registerRuntime( // nolint: gocyclo
629642
switch {
630643
case existingRt != nil:
631644
// Existing runtime, verify update.
632-
err = registry.VerifyRuntimeUpdate(ctx.Logger(), existingRt, rt, epoch, params)
645+
err = registry.VerifyRuntimeUpdate(ctx.Logger(), existingRt, rt, epoch, params, isFeatureVersion242)
633646
default:
634647
// New runtime, verify new descriptor.
635648
err = registry.VerifyRuntimeNew(ctx.Logger(), rt, epoch, params, ctx.IsInitChain())

go/registry/api/api.go

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
beacon "github.com/oasisprotocol/oasis-core/go/beacon/api"
1313
"github.com/oasisprotocol/oasis-core/go/common"
14+
"github.com/oasisprotocol/oasis-core/go/common/cbor"
1415
"github.com/oasisprotocol/oasis-core/go/common/crypto/hash"
1516
"github.com/oasisprotocol/oasis-core/go/common/crypto/signature"
1617
"github.com/oasisprotocol/oasis-core/go/common/entity"
@@ -497,6 +498,7 @@ func VerifyRegisterNodeArgs( // nolint: gocyclo
497498
epoch beacon.EpochTime,
498499
runtimeLookup RuntimeLookup,
499500
nodeLookup NodeLookup,
501+
isFeatureVersion242 bool,
500502
) (*node.Node, []*Runtime, error) {
501503
var n node.Node
502504
if sigNode == nil {
@@ -621,6 +623,11 @@ func VerifyRegisterNodeArgs( // nolint: gocyclo
621623
if err := VerifyNodeRuntimeEnclaveIDs(logger, n.ID, rt, regRt, params.TEEFeatures, now, height); err != nil && !isSanityCheck && !isGenesis {
622624
return nil, nil, err
623625
}
626+
if !isFeatureVersion242 {
627+
if err := VerifyNodeRuntimeCapabilities(rt, regRt); err != nil {
628+
return nil, nil, err
629+
}
630+
}
624631

625632
// Enforce what kinds of runtimes are allowed.
626633
if regRt.Kind == KindKeyManager && !n.HasRoles(KeyManagerRuntimeAllowedRoles) {
@@ -849,6 +856,44 @@ func VerifyNodeRuntimeEnclaveIDs(
849856
return fmt.Errorf("%w: node running unknown runtime enclave version", ErrInvalidArgument)
850857
}
851858

859+
// VerifyNodeRuntimeCapabilities verifies that the node's runtimes' capabilities
860+
// have valid FMSPC whitelists.
861+
func VerifyNodeRuntimeCapabilities(rt *node.Runtime, regRt *Runtime) error {
862+
if rt.Capabilities.TEE == nil {
863+
return nil
864+
}
865+
866+
for _, rtVersionInfo := range regRt.Deployments {
867+
if rtVersionInfo.Version != rt.Version {
868+
continue
869+
}
870+
if err := VerifyNodeRuntimeCapability(rt.Capabilities.TEE, rtVersionInfo.TEE); err != nil {
871+
return err
872+
}
873+
}
874+
return nil
875+
}
876+
877+
// VerifyNodeRuntimeCapability verifies that the node's runtime capabilities
878+
// have valid FMSPC whitelist.
879+
func VerifyNodeRuntimeCapability(c *node.CapabilityTEE, constraints []byte) error {
880+
switch c.Hardware {
881+
case node.TEEHardwareIntelSGX:
882+
var sc node.SGXConstraints
883+
if err := cbor.Unmarshal(constraints, &sc); err != nil {
884+
return fmt.Errorf("node: malformed SGX constraints: %w", err)
885+
}
886+
if sc.Policy == nil {
887+
return nil
888+
}
889+
if err := sc.Policy.VerifyFMSPCWhitelist(); err != nil {
890+
return fmt.Errorf("node: malformed SGX constraints: %w", err)
891+
}
892+
default:
893+
return nil
894+
}
895+
}
896+
852897
// VerifyAddress verifies a node address.
853898
func VerifyAddress(addr node.Address, allowUnroutable bool) error {
854899
if !allowUnroutable {
@@ -1097,6 +1142,7 @@ func VerifyRuntime( // nolint: gocyclo
10971142
isGenesis bool,
10981143
isSanityCheck bool,
10991144
now beacon.EpochTime,
1145+
isFeatureVersion242 bool,
11001146
) error {
11011147
if rt == nil {
11021148
return fmt.Errorf("%w: no runtime given", ErrInvalidArgument)
@@ -1141,7 +1187,7 @@ func VerifyRuntime( // nolint: gocyclo
11411187

11421188
// Validate the deployments. This also handles validating that the
11431189
// appropriate TEE configuration is present in each deployment.
1144-
if err := rt.ValidateDeployments(now, params); err != nil {
1190+
if err := rt.ValidateDeployments(now, params, isFeatureVersion242); err != nil {
11451191
logger.Error("RegisterRuntime: invalid deployments",
11461192
"runtime_id", rt.ID,
11471193
"err", err,
@@ -1215,6 +1261,7 @@ func VerifyRuntimeUpdate(
12151261
currentRt, newRt *Runtime,
12161262
now beacon.EpochTime,
12171263
params *ConsensusParameters,
1264+
isFeatureVersion242 bool,
12181265
) error {
12191266
if !currentRt.ID.Equal(&newRt.ID) {
12201267
logger.Error("RegisterRuntime: trying to update runtime ID",
@@ -1270,7 +1317,7 @@ func VerifyRuntimeUpdate(
12701317

12711318
// Validate the deployments.
12721319
activeDeployment := currentRt.ActiveDeployment(now)
1273-
if err := currentRt.ValidateDeployments(now, params); err != nil {
1320+
if err := currentRt.ValidateDeployments(now, params, isFeatureVersion242); err != nil {
12741321
// Invariant violation, this should NEVER happen.
12751322
logger.Error("RegisterRuntime: malformed deployments present in state",
12761323
"runtime_id", currentRt.ID,
@@ -1284,7 +1331,7 @@ func VerifyRuntimeUpdate(
12841331
}
12851332

12861333
newActiveDeployment := newRt.ActiveDeployment(now)
1287-
if err := newRt.ValidateDeployments(now, params); err != nil {
1334+
if err := newRt.ValidateDeployments(now, params, isFeatureVersion242); err != nil {
12881335
logger.Error("RegisterRuntime: malformed deployments",
12891336
"runtime_id", currentRt.ID,
12901337
"err", err,

go/registry/api/api_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ func TestVerifyRegisterNodeArgs(t *testing.T) {
211211
&tc.n,
212212
)
213213
require.NoError(err, "singing node")
214-
_, _, err = VerifyRegisterNodeArgs(context.Background(), params, logger, signedNode, entity, time.Now(), 1, false, false, beacon.EpochTime(10), rtLookup, ndLookup)
214+
_, _, err = VerifyRegisterNodeArgs(context.Background(), params, logger, signedNode, entity, time.Now(), 1, false, false, beacon.EpochTime(10), rtLookup, ndLookup, true)
215215
switch {
216216
case tc.err == nil:
217217
require.NoError(err, tc.msg)

go/registry/api/runtime.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ func (r *Runtime) DeploymentForVersion(v version.Version) *VersionInfo {
526526

527527
// ValidateDeployments validates a runtime descriptor's Deployments field
528528
// at the specified epoch.
529-
func (r *Runtime) ValidateDeployments(now beacon.EpochTime, params *ConsensusParameters) error {
529+
func (r *Runtime) ValidateDeployments(now beacon.EpochTime, params *ConsensusParameters, isFeatureVersion242 bool) error {
530530
// The runtime descriptor's deployments field is considered valid
531531
// if:
532532
// * There is at least one entry present.
@@ -600,6 +600,13 @@ func (r *Runtime) ValidateDeployments(now beacon.EpochTime, params *ConsensusPar
600600
if len(cs.Enclaves) == 0 {
601601
return fmt.Errorf("%w: invalid SGX TEE constraints", ErrNoEnclaveForRuntime)
602602
}
603+
if !isFeatureVersion242 {
604+
if cs.Policy != nil {
605+
if err := cs.Policy.VerifyFMSPCWhitelist(); err != nil {
606+
return fmt.Errorf("%w: invalid SGX TEE constraints: %v", ErrInvalidArgument, err)
607+
}
608+
}
609+
}
603610
default:
604611
return fmt.Errorf("%w: invalid TEE hardware", ErrInvalidArgument)
605612
}

go/registry/api/runtime_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ func TestVerifyRuntime(t *testing.T) {
656656
tc.cpFn(&cp)
657657
}
658658

659-
err := VerifyRuntime(&cp, logging.GetLogger("runtime/tests"), &tc.rr, false, true, beacon.EpochTime(10))
659+
err := VerifyRuntime(&cp, logging.GetLogger("runtime/tests"), &tc.rr, false, true, beacon.EpochTime(10), true)
660660
switch {
661661
case tc.err == nil:
662662
require.NoError(err, tc.msg)

go/registry/api/sanity_check.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,15 @@ func SanityCheckRuntimes(
138138
// First go through all runtimes and perform general sanity checks.
139139
seenRuntimes := []*Runtime{}
140140
for _, rt := range runtimes {
141-
if err := VerifyRuntime(params, logger, rt, isGenesis, true, now); err != nil {
141+
if err := VerifyRuntime(params, logger, rt, isGenesis, true, now, true); err != nil {
142142
return nil, fmt.Errorf("runtime sanity check failed: %w", err)
143143
}
144144
seenRuntimes = append(seenRuntimes, rt)
145145
}
146146

147147
seenSuspendedRuntimes := []*Runtime{}
148148
for _, rt := range suspendedRuntimes {
149-
if err := VerifyRuntime(params, logger, rt, isGenesis, true, now); err != nil {
149+
if err := VerifyRuntime(params, logger, rt, isGenesis, true, now, true); err != nil {
150150
return nil, fmt.Errorf("runtime sanity check failed: %w", err)
151151
}
152152
seenSuspendedRuntimes = append(seenSuspendedRuntimes, rt)
@@ -218,6 +218,7 @@ func SanityCheckNodes(
218218
epoch,
219219
runtimesLookup,
220220
nodeLookup,
221+
true,
221222
)
222223
if err != nil {
223224
return nil, fmt.Errorf("registry: node sanity check failed: ID: %s, error: %w", n.ID.String(), err)

0 commit comments

Comments
 (0)