Skip to content

Commit

Permalink
runtime/src/enclave_rpc: Verify RPC quotes with key manager quote policy
Browse files Browse the repository at this point in the history
  • Loading branch information
peternose committed Dec 9, 2022
1 parent 1ae2da7 commit 28b322f
Show file tree
Hide file tree
Showing 18 changed files with 321 additions and 75 deletions.
1 change: 1 addition & 0 deletions .changelog/5092.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
runtime/src/enclave_rpc: Verify RPC quotes with key manager quote policy
6 changes: 6 additions & 0 deletions go/registry/api/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,12 @@ type VersionInfo struct {

// Equal compares vs another VersionInfo for equality.
func (vi *VersionInfo) Equal(cmp *VersionInfo) bool {
if vi == cmp {
return true
}
if vi == nil || cmp == nil {
return false
}
if vi.Version.ToU64() != cmp.Version.ToU64() {
return false
}
Expand Down
66 changes: 37 additions & 29 deletions go/runtime/host/protocol/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,34 +67,36 @@ type Body struct {
Error *Error `json:",omitempty"`

// Runtime interface.
RuntimeInfoRequest *RuntimeInfoRequest `json:",omitempty"`
RuntimeInfoResponse *RuntimeInfoResponse `json:",omitempty"`
RuntimePingRequest *Empty `json:",omitempty"`
RuntimeShutdownRequest *Empty `json:",omitempty"`
RuntimeCapabilityTEERakInitRequest *RuntimeCapabilityTEERakInitRequest `json:",omitempty"`
RuntimeCapabilityTEERakInitResponse *Empty `json:",omitempty"`
RuntimeCapabilityTEERakReportRequest *Empty `json:",omitempty"`
RuntimeCapabilityTEERakReportResponse *RuntimeCapabilityTEERakReportResponse `json:",omitempty"`
RuntimeCapabilityTEERakAvrRequest *RuntimeCapabilityTEERakAvrRequest `json:",omitempty"`
RuntimeCapabilityTEERakAvrResponse *Empty `json:",omitempty"`
RuntimeCapabilityTEERakQuoteRequest *RuntimeCapabilityTEERakQuoteRequest `json:",omitempty"`
RuntimeCapabilityTEERakQuoteResponse *RuntimeCapabilityTEERakQuoteResponse `json:",omitempty"`
RuntimeRPCCallRequest *RuntimeRPCCallRequest `json:",omitempty"`
RuntimeRPCCallResponse *RuntimeRPCCallResponse `json:",omitempty"`
RuntimeLocalRPCCallRequest *RuntimeLocalRPCCallRequest `json:",omitempty"`
RuntimeLocalRPCCallResponse *RuntimeLocalRPCCallResponse `json:",omitempty"`
RuntimeCheckTxBatchRequest *RuntimeCheckTxBatchRequest `json:",omitempty"`
RuntimeCheckTxBatchResponse *RuntimeCheckTxBatchResponse `json:",omitempty"`
RuntimeExecuteTxBatchRequest *RuntimeExecuteTxBatchRequest `json:",omitempty"`
RuntimeExecuteTxBatchResponse *RuntimeExecuteTxBatchResponse `json:",omitempty"`
RuntimeAbortRequest *Empty `json:",omitempty"`
RuntimeAbortResponse *Empty `json:",omitempty"`
RuntimeKeyManagerPolicyUpdateRequest *RuntimeKeyManagerPolicyUpdateRequest `json:",omitempty"`
RuntimeKeyManagerPolicyUpdateResponse *Empty `json:",omitempty"`
RuntimeQueryRequest *RuntimeQueryRequest `json:",omitempty"`
RuntimeQueryResponse *RuntimeQueryResponse `json:",omitempty"`
RuntimeConsensusSyncRequest *RuntimeConsensusSyncRequest `json:",omitempty"`
RuntimeConsensusSyncResponse *Empty `json:",omitempty"`
RuntimeInfoRequest *RuntimeInfoRequest `json:",omitempty"`
RuntimeInfoResponse *RuntimeInfoResponse `json:",omitempty"`
RuntimePingRequest *Empty `json:",omitempty"`
RuntimeShutdownRequest *Empty `json:",omitempty"`
RuntimeCapabilityTEERakInitRequest *RuntimeCapabilityTEERakInitRequest `json:",omitempty"`
RuntimeCapabilityTEERakInitResponse *Empty `json:",omitempty"`
RuntimeCapabilityTEERakReportRequest *Empty `json:",omitempty"`
RuntimeCapabilityTEERakReportResponse *RuntimeCapabilityTEERakReportResponse `json:",omitempty"`
RuntimeCapabilityTEERakAvrRequest *RuntimeCapabilityTEERakAvrRequest `json:",omitempty"`
RuntimeCapabilityTEERakAvrResponse *Empty `json:",omitempty"`
RuntimeCapabilityTEERakQuoteRequest *RuntimeCapabilityTEERakQuoteRequest `json:",omitempty"`
RuntimeCapabilityTEERakQuoteResponse *RuntimeCapabilityTEERakQuoteResponse `json:",omitempty"`
RuntimeRPCCallRequest *RuntimeRPCCallRequest `json:",omitempty"`
RuntimeRPCCallResponse *RuntimeRPCCallResponse `json:",omitempty"`
RuntimeLocalRPCCallRequest *RuntimeLocalRPCCallRequest `json:",omitempty"`
RuntimeLocalRPCCallResponse *RuntimeLocalRPCCallResponse `json:",omitempty"`
RuntimeCheckTxBatchRequest *RuntimeCheckTxBatchRequest `json:",omitempty"`
RuntimeCheckTxBatchResponse *RuntimeCheckTxBatchResponse `json:",omitempty"`
RuntimeExecuteTxBatchRequest *RuntimeExecuteTxBatchRequest `json:",omitempty"`
RuntimeExecuteTxBatchResponse *RuntimeExecuteTxBatchResponse `json:",omitempty"`
RuntimeAbortRequest *Empty `json:",omitempty"`
RuntimeAbortResponse *Empty `json:",omitempty"`
RuntimeKeyManagerPolicyUpdateRequest *RuntimeKeyManagerPolicyUpdateRequest `json:",omitempty"`
RuntimeKeyManagerPolicyUpdateResponse *Empty `json:",omitempty"`
RuntimeKeyManagerQuotePolicyUpdateRequest *RuntimeKeyManagerQuotePolicyUpdateRequest `json:",omitempty"`
RuntimeKeyManagerQuotePolicyUpdateResponse *Empty `json:",omitempty"`
RuntimeQueryRequest *RuntimeQueryRequest `json:",omitempty"`
RuntimeQueryResponse *RuntimeQueryResponse `json:",omitempty"`
RuntimeConsensusSyncRequest *RuntimeConsensusSyncRequest `json:",omitempty"`
RuntimeConsensusSyncResponse *Empty `json:",omitempty"`

// Host interface.
HostRPCCallRequest *HostRPCCallRequest `json:",omitempty"`
Expand Down Expand Up @@ -395,12 +397,18 @@ type RuntimeExecuteTxBatchResponse struct {
Deprecated1 cbor.RawMessage `json:"batch_weight_limits,omitempty"`
}

// RuntimeKeyManagerPolicyUpdateRequest is a runtime key manager policy request
// RuntimeKeyManagerPolicyUpdateRequest is a runtime key manager SGX policy request
// message body.
type RuntimeKeyManagerPolicyUpdateRequest struct {
SignedPolicyRaw []byte `json:"signed_policy_raw"`
}

// RuntimeKeyManagerQuotePolicyUpdateRequest is a runtime key manager quote policy request
// message body.
type RuntimeKeyManagerQuotePolicyUpdateRequest struct {
PolicyRaw []byte `json:"policy_raw"`
}

// RuntimeQueryRequest is a runtime query request message body.
type RuntimeQueryRequest struct {
// ConsensusBlock is the consensus light block at the last finalized round
Expand Down
86 changes: 81 additions & 5 deletions go/runtime/registry/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"github.com/oasisprotocol/oasis-core/go/common/cbor"
"github.com/oasisprotocol/oasis-core/go/common/identity"
"github.com/oasisprotocol/oasis-core/go/common/logging"
"github.com/oasisprotocol/oasis-core/go/common/node"
"github.com/oasisprotocol/oasis-core/go/common/sgx/quote"
"github.com/oasisprotocol/oasis-core/go/common/version"
consensus "github.com/oasisprotocol/oasis-core/go/consensus/api"
consensusResults "github.com/oasisprotocol/oasis-core/go/consensus/api/transaction/results"
Expand Down Expand Up @@ -502,11 +504,21 @@ func (n *runtimeHostNotifier) watchKmPolicyUpdates(ctx context.Context, kmRtID *

n.logger.Debug("watching key manager policy updates", "keymanager", kmRtID)

// Subscribe to key manager status updates.
// Subscribe to key manager status updates (SGX policy might change).
stCh, stSub := n.consensus.KeyManager().WatchStatuses()
defer stSub.Close()

// Subscribe to runtime host events.
// Subscribe to epoch transitions (quote policy might change).
epoCh, sub, err := n.consensus.Beacon().WatchEpochs(ctx)
if err != nil {
n.logger.Error("failed to watch epochs",
"err", err,
)
return
}
defer sub.Close()

// Subscribe to runtime host events (policies will be lost on restarts).
evCh, evSub, err := n.host.WatchEvents(n.ctx)
if err != nil {
n.logger.Error("failed to subscribe to runtime host events",
Expand All @@ -516,7 +528,11 @@ func (n *runtimeHostNotifier) watchKmPolicyUpdates(ctx context.Context, kmRtID *
}
defer evSub.Close()

var st *keymanager.Status
var (
st *keymanager.Status
sc *node.SGXConstraints
vi *registry.VersionInfo
)

for {
select {
Expand All @@ -529,15 +545,55 @@ func (n *runtimeHostNotifier) watchKmPolicyUpdates(ctx context.Context, kmRtID *
}
st = newSt
n.updateSGXPolicy(ctx, st.Policy)
case epoch := <-epoCh:
// Check if the key manager was redeployed, as that is when a new quote policy might
// take effect.
dsc, err := n.consensus.Registry().GetRuntime(ctx, &registry.GetRuntimeQuery{
Height: consensus.HeightLatest,
ID: *kmRtID,
})
if err != nil {
n.logger.Error("failed to query key manager runtime descriptor",
"err", err,
)
continue
}

// Quote polices can only be set on SGX hardwares.
if dsc.TEEHardware != node.TEEHardwareIntelSGX {
continue
}

// No need to update the policy if the key manager is sill running the same version.
newVi := dsc.ActiveDeployment(epoch)
if newVi.Equal(vi) {
continue
}
vi = newVi

// Parse SGX constraints.
var newSc node.SGXConstraints
if err := cbor.Unmarshal(vi.TEE, &newSc); err != nil {
n.logger.Error("malformed SGX constraints",
"err", err,
)
continue
}
sc = &newSc

n.updateQuotePolicy(ctx, sc.Policy)
case ev := <-evCh:
// Runtime host changes, make sure to update the policy if runtime is restarted.
// Runtime host changes, make sure to update the policies if runtime is restarted.
if ev.Started == nil && ev.Updated == nil {
continue
}
// Make sure that we actually have a policy.
// Make sure that we actually have policies.
if st != nil {
n.updateSGXPolicy(ctx, st.Policy)
}
if sc != nil {
n.updateQuotePolicy(ctx, sc.Policy)
}
}
}
}
Expand All @@ -563,6 +619,26 @@ func (n *runtimeHostNotifier) updateSGXPolicy(ctx context.Context, policy *keyma
n.logger.Debug("key manager SGX policy update dispatched")
}

func (n *runtimeHostNotifier) updateQuotePolicy(ctx context.Context, policy *quote.Policy) {
n.logger.Debug("got quote policy update", "policy", policy)

raw := cbor.Marshal(policy)
req := &protocol.Body{RuntimeKeyManagerQuotePolicyUpdateRequest: &protocol.RuntimeKeyManagerQuotePolicyUpdateRequest{
PolicyRaw: raw,
}}

ctx, cancel := context.WithTimeout(ctx, notifyTimeout)
defer cancel()

if _, err := n.host.Call(ctx, req); err != nil {
n.logger.Error("failed dispatching key manager quote policy update to runtime",
"err", err,
)
return
}
n.logger.Debug("key manager quote policy update dispatched")
}

func (n *runtimeHostNotifier) watchConsensusLightBlocks() {
rawCh, sub, err := n.consensus.WatchBlocks(n.ctx)
if err != nil {
Expand Down
30 changes: 23 additions & 7 deletions keymanager/src/client/remote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use io_context::Context;
use lru::LruCache;

use oasis_core_runtime::{
common::{namespace::Namespace, sgx::EnclaveIdentity},
common::{
namespace::Namespace,
sgx::{EnclaveIdentity, QuotePolicy},
},
consensus::{beacon::EpochTime, keymanager::SignedPolicySGX, verifier::Verifier},
enclave_rpc::{client::RpcClient, session},
protocol::Protocol,
Expand Down Expand Up @@ -76,10 +79,11 @@ impl RemoteClient {
}

/// Create a new key manager client with runtime-internal transport and explicit key manager
/// enclave identities.
pub fn new_runtime_with_enclave_identities(
/// enclave identities and quote policy.
pub fn new_runtime_with_enclaves_and_policy(
runtime_id: Namespace,
enclaves: Option<HashSet<EnclaveIdentity>>,
policy: Option<Arc<QuotePolicy>>,
protocol: Arc<Protocol>,
consensus_verifier: Arc<dyn Verifier>,
rak: Arc<RAK>,
Expand All @@ -90,6 +94,7 @@ impl RemoteClient {
RpcClient::new_runtime(
session::Builder::default()
.remote_enclaves(enclaves)
.quote_policy(policy)
.local_rak(rak),
protocol,
KEY_MANAGER_ENDPOINT,
Expand All @@ -101,9 +106,10 @@ impl RemoteClient {

/// Create a new key manager client with runtime-internal transport.
///
/// Using this method valid enclave identities won't be preset and should be obtained via the
/// worker-host protocol and updated with the set_policy method. In case the signer set is
/// non-empty, session establishment will fail until the initial policies will be updated.
/// Using this method valid enclave identities and quote policy won't be preset and should
/// be obtained via the runtime-host protocol and updated with the `set_sgx_policy` and
/// `set_quote_policy` methods. In case the signer set is non-empty, session establishment
/// will fail until the initial policies will be updated.
pub fn new_runtime(
runtime_id: Namespace,
protocol: Arc<Protocol>,
Expand All @@ -128,12 +134,17 @@ impl RemoteClient {
None
};

// Key manager's quote policy should be obtained via the runtime-host protocol. Until then,
// all quote verifications will fail with a missing quote policy error.
let policy = None;

// Configure trusted policy signers.
set_trusted_policy_signers(signers);

Self::new_runtime_with_enclave_identities(
Self::new_runtime_with_enclaves_and_policy(
runtime_id,
enclaves,
policy,
protocol,
consensus_verifier,
rak,
Expand All @@ -151,6 +162,11 @@ impl RemoteClient {

Ok(())
}

/// Set key manager's quote policy.
pub fn set_quote_policy(&self, policy: QuotePolicy) {
self.inner.rpc_client.update_quote_policy(policy);
}
}

impl KeyManagerClient for RemoteClient {
Expand Down
3 changes: 2 additions & 1 deletion keymanager/src/crypto/kdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,10 @@ impl Kdf {

let rctx = runtime_context!(ctx, KmContext);

let km_client = RemoteClient::new_runtime_with_enclave_identities(
let km_client = RemoteClient::new_runtime_with_enclaves_and_policy(
rctx.runtime_id,
Policy::global().may_replicate_from(),
ctx.rak.quote_policy(),
rctx.protocol.clone(),
ctx.consensus_verifier.clone(),
ctx.rak.clone(),
Expand Down
2 changes: 1 addition & 1 deletion runtime/src/common/sgx/ias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ lazy_static! {
}

/// Quote validity policy.
#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
pub struct QuotePolicy {
/// Whether IAS quotes are disabled and will always be rejected.
#[cbor(optional)]
Expand Down
2 changes: 1 addition & 1 deletion runtime/src/common/sgx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ impl Quote {
}

/// Quote validity policy.
#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
pub struct QuotePolicy {
#[cbor(rename = "ias")]
pub ias: Option<ias::QuotePolicy>,
Expand Down
2 changes: 1 addition & 1 deletion runtime/src/common/sgx/pcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ pub enum Error {
}

/// Quote validity policy.
#[derive(Clone, Debug, cbor::Encode, cbor::Decode)]
#[derive(Clone, Debug, PartialEq, Eq, cbor::Encode, cbor::Decode)]
pub struct QuotePolicy {
/// Whether PCS quotes are disabled and will always be rejected.
#[cbor(optional)]
Expand Down
38 changes: 38 additions & 0 deletions runtime/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,10 @@ impl Dispatcher {
// Key manager SGX policy update local RPC call.
self.handle_km_sgx_policy_update(ctx, state, signed_policy_raw)
}
Body::RuntimeKeyManagerQuotePolicyUpdateRequest { policy_raw } => {
// Key manager quote policy update local RPC call.
self.handle_km_quote_policy_update(ctx, state, policy_raw)
}
Body::RuntimeConsensusSyncRequest { height } => state
.consensus_verifier
.sync(height)
Expand Down Expand Up @@ -1004,4 +1008,38 @@ impl Dispatcher {

Ok(Body::RuntimeKeyManagerPolicyUpdateResponse {})
}

fn handle_km_quote_policy_update(
&self,
ctx: Context,
state: State,
policy_raw: Vec<u8>,
) -> Result<Body, Error> {
// Make sure to abort the process on panic during quote policy processing as that indicates
// a serious problem and should make sure to clean up the process.
let _guard = AbortOnPanic;

debug!(self.logger, "Received km quote policy update request");

// Verify and decode the policy.
let ctx = ctx.freeze();
let runtime_id = state.protocol.get_host_info().runtime_id;
let key_manager = state
.policy_verifier
.key_manager(ctx.clone(), &runtime_id, true)?;
let policy = state.policy_verifier.verify_quote_policy(
ctx,
&policy_raw,
&key_manager,
None,
false,
)?;

// Dispatch the local RPC call.
state.rpc_dispatcher.handle_km_quote_policy_update(policy);

debug!(self.logger, "KM quote policy update request complete");

Ok(Body::RuntimeKeyManagerQuotePolicyUpdateResponse {})
}
}
Loading

0 comments on commit 28b322f

Please sign in to comment.