oasis_core_runtime/
policy.rs

1//! Consensus SGX and quote policy handling.
2
3use std::sync::Arc;
4
5use anyhow::{bail, Result};
6use slog::{debug, Logger};
7use thiserror::Error;
8
9use crate::{
10    common::{logger::get_logger, namespace::Namespace, sgx::QuotePolicy, version::Version},
11    consensus::{
12        keymanager::SignedPolicySGX,
13        registry::{SGXConstraints, TEEHardware},
14        state::{
15            beacon::ImmutableState as BeaconState,
16            keymanager::{ImmutableState as KeyManagerState, Status},
17            registry::ImmutableState as RegistryState,
18        },
19        verifier::Verifier,
20    },
21    future::block_on,
22};
23
24/// Policy verifier error.
25#[derive(Error, Debug)]
26pub enum PolicyVerifierError {
27    #[error("missing runtime descriptor")]
28    MissingRuntimeDescriptor,
29    #[error("no corresponding runtime deployment")]
30    NoDeployment,
31    #[error("bad TEE constraints")]
32    BadTEEConstraints,
33    #[error("policy mismatch")]
34    PolicyMismatch,
35    #[error("policy hasn't been published")]
36    PolicyNotPublished,
37    #[error("status mismatch")]
38    StatusMismatch,
39    #[error("status hasn't been published")]
40    StatusNotPublished,
41    #[error("configured runtime hardware mismatch")]
42    HardwareMismatch,
43    #[error("runtime doesn't use key manager")]
44    NoKeyManager,
45}
46
47/// Consensus policy verifier.
48pub struct PolicyVerifier {
49    consensus_verifier: Arc<dyn Verifier>,
50    logger: Logger,
51}
52
53impl PolicyVerifier {
54    /// Create a new consensus policy verifier.
55    pub fn new(consensus_verifier: Arc<dyn Verifier>) -> Self {
56        let logger = get_logger("runtime/policy_verifier");
57        Self {
58            consensus_verifier,
59            logger,
60        }
61    }
62
63    /// Fetch runtime's quote policy from the latest verified consensus layer state.
64    ///
65    /// If the runtime version is not provided, the policy for the active deployment is returned.
66    pub fn quote_policy(
67        &self,
68        runtime_id: &Namespace,
69        version: Option<Version>,
70    ) -> Result<QuotePolicy> {
71        // Fetch quote policy from the consensus layer using the given or the active version.
72        // TODO: Make this async.
73        let consensus_state = block_on(self.consensus_verifier.latest_state())?;
74        let registry_state = RegistryState::new(&consensus_state);
75        let runtime = registry_state
76            .runtime(runtime_id)?
77            .ok_or(PolicyVerifierError::MissingRuntimeDescriptor)?;
78
79        let ad = match version {
80            Some(version) => runtime
81                .deployment_for_version(version)
82                .ok_or(PolicyVerifierError::NoDeployment)?,
83            None => {
84                let beacon_state = BeaconState::new(&consensus_state);
85                let epoch = beacon_state.epoch()?;
86
87                runtime
88                    .active_deployment(epoch)
89                    .ok_or(PolicyVerifierError::NoDeployment)?
90            }
91        };
92
93        let policy = match runtime.tee_hardware {
94            TEEHardware::TEEHardwareIntelSGX => {
95                let sc: SGXConstraints = ad
96                    .try_decode_tee()
97                    .map_err(|_| PolicyVerifierError::BadTEEConstraints)?;
98                sc.policy()
99            }
100            _ => bail!(PolicyVerifierError::HardwareMismatch),
101        };
102
103        Ok(policy)
104    }
105
106    /// Verify that runtime's quote policy has been published in the consensus layer.
107    pub fn verify_quote_policy(
108        &self,
109        policy: QuotePolicy,
110        runtime_id: &Namespace,
111        version: Option<Version>,
112    ) -> Result<QuotePolicy> {
113        let published_policy = self.quote_policy(runtime_id, version)?;
114
115        if policy != published_policy {
116            debug!(
117                self.logger,
118                "quote policy mismatch";
119                "untrusted" => ?policy,
120                "published" => ?published_policy,
121            );
122            return Err(PolicyVerifierError::PolicyMismatch.into());
123        }
124
125        Ok(published_policy)
126    }
127
128    /// Fetch key manager's status from the latest verified consensus layer state.
129    pub fn key_manager_status(&self, key_manager: Namespace) -> Result<Status> {
130        // TODO: Make this async.
131        let consensus_state = block_on(self.consensus_verifier.latest_state())?;
132        let km_state = KeyManagerState::new(&consensus_state);
133        km_state
134            .status(key_manager)?
135            .ok_or_else(|| PolicyVerifierError::StatusNotPublished.into())
136    }
137
138    /// Verify that key manager's status has been published in the consensus layer.
139    pub fn verify_key_manager_status(
140        &self,
141        status: Status,
142        key_manager: Namespace,
143    ) -> Result<Status> {
144        let published_status = self.key_manager_status(key_manager)?;
145
146        if status != published_status {
147            debug!(
148                self.logger,
149                "key manager status mismatch";
150                "untrusted" => ?status,
151                "published" => ?published_status,
152            );
153            return Err(PolicyVerifierError::StatusMismatch.into());
154        }
155
156        Ok(published_status)
157    }
158
159    /// Fetch key manager's policy from the latest verified consensus layer state.
160    pub fn key_manager_policy(&self, key_manager: Namespace) -> Result<SignedPolicySGX> {
161        self.key_manager_status(key_manager)?
162            .policy
163            .ok_or_else(|| PolicyVerifierError::PolicyNotPublished.into())
164    }
165
166    /// Verify that key manager's policy has been published in the consensus layer.
167    pub fn verify_key_manager_policy(
168        &self,
169        policy: SignedPolicySGX,
170        key_manager: Namespace,
171    ) -> Result<SignedPolicySGX> {
172        let published_policy = self.key_manager_policy(key_manager)?;
173
174        if policy != published_policy {
175            debug!(
176                self.logger,
177                "key manager policy mismatch";
178                "untrusted" => ?policy,
179                "published" => ?published_policy,
180            );
181            return Err(PolicyVerifierError::PolicyMismatch.into());
182        }
183
184        Ok(published_policy)
185    }
186
187    /// Fetch runtime's key manager.
188    pub fn key_manager(&self, runtime_id: &Namespace) -> Result<Namespace> {
189        // TODO: Make this async.
190        let consensus_state = block_on(self.consensus_verifier.latest_state())?;
191        let registry_state = RegistryState::new(&consensus_state);
192        let runtime = registry_state
193            .runtime(runtime_id)?
194            .ok_or(PolicyVerifierError::MissingRuntimeDescriptor)?;
195        let key_manager = runtime
196            .key_manager
197            .ok_or(PolicyVerifierError::NoKeyManager)?;
198
199        Ok(key_manager)
200    }
201}