Skip to content

Commit d63d5a5

Browse files
committed
runtime-sdk: Add provider-related endorsement constraints
1 parent 5e9837a commit d63d5a5

12 files changed

Lines changed: 502 additions & 93 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rofl-scheduler/src/manager.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use oasis_runtime_sdk::{
2323
};
2424
use oasis_runtime_sdk_rofl_market::{
2525
self as market,
26+
policy::{ProviderLabel, LABEL_PROVIDER},
2627
types::{Deployment, Instance, InstanceId, InstanceStatus},
2728
};
2829
use rand::Rng;
@@ -990,6 +991,13 @@ impl Manager {
990991
deployment_hash(deployment),
991992
);
992993

994+
let provider_label = ProviderLabel {
995+
provider: self.cfg.provider_address,
996+
instance: instance.id,
997+
};
998+
let provider_label = BASE64_STANDARD.encode(cbor::to_vec(provider_label));
999+
labels.insert(LABEL_PROVIDER.to_string(), provider_label);
1000+
9931001
let _ = self
9941002
.env
9951003
.host()

runtime-sdk/modules/rofl-market/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ oasis-runtime-sdk-evm = { path = "../evm" }
1313

1414
# Third party.
1515
anyhow = "1.0"
16+
base64 = "0.22.1"
1617
ethabi = { version = "18.0.0", default-features = false, features = ["std"] }
1718
once_cell = "1.8.0"
1819
rustc-hex = "2.0.1"

runtime-sdk/modules/rofl-market/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ mod config;
1616
mod error;
1717
mod event;
1818
mod payment;
19+
pub mod policy;
1920
pub mod state;
2021
#[cfg(test)]
2122
mod test;
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
use std::collections::BTreeMap;
2+
3+
use base64::prelude::*;
4+
5+
use oasis_runtime_sdk::{
6+
core::{
7+
common::crypto::signature::{PublicKey, Signature},
8+
consensus::registry::{EndorsedCapabilityTEE, Node},
9+
host::attestation::{LabelAttestation, ATTEST_LABELS_SIGNATURE_CONTEXT},
10+
},
11+
modules::rofl::{
12+
policy::{AllowedEndorsement, EndorsementPolicyEvaluator},
13+
Error,
14+
},
15+
types::address::Address,
16+
Context,
17+
};
18+
19+
use crate::{state, types::InstanceId};
20+
21+
/// Name of the ROFL app instance metadata key used to store the provider attestation.
22+
const METADATA_KEY_POLICY_PROVIDER_ATTESTATION: &str = "net.oasis.policy.provider";
23+
/// Name of the provider label set by the scheduler.
24+
pub const LABEL_PROVIDER: &str = "net.oasis.provider";
25+
26+
/// Value of the `LABEL_PROVIDER` label as set by the scheduler.
27+
#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
28+
pub struct ProviderLabel {
29+
/// Address of the provider.
30+
pub provider: Address,
31+
/// Instance identifier.
32+
pub instance: InstanceId,
33+
}
34+
35+
/// Provider attestation metadata stored in `METADATA_KEY_POLICY_PROVIDER_ATTESTATION` label.
36+
#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
37+
pub struct ProviderAttestation {
38+
/// A CBOR-serialized `LabelAttestation`.
39+
pub label_attestation: Vec<u8>,
40+
/// Signature from endorsing node.
41+
pub signature: Signature,
42+
}
43+
44+
/// An endorsement policy evaluator that supports provider-related constraints via lookups
45+
/// in ROFL market module state.
46+
pub struct ProviderEndorsementPolicyEvaluator;
47+
48+
impl EndorsementPolicyEvaluator for ProviderEndorsementPolicyEvaluator {
49+
fn verify_atom<C: Context>(
50+
_ctx: &C,
51+
policy: &AllowedEndorsement,
52+
ect: &EndorsedCapabilityTEE,
53+
endorsing_node_id: PublicKey,
54+
_endorsing_node: &Option<Node>,
55+
metadata: &BTreeMap<String, String>,
56+
) -> Result<(), Error> {
57+
match policy {
58+
AllowedEndorsement::Provider(address) => {
59+
// Check if the endorsing node is endorsed by the given provider.
60+
let provider = state::get_provider(*address).ok_or(Error::NodeNotAllowed)?;
61+
if !provider.nodes.contains(&endorsing_node_id) {
62+
return Err(Error::NodeNotAllowed);
63+
}
64+
Ok(())
65+
}
66+
AllowedEndorsement::ProviderMachineAdmin(expected_admin) => {
67+
let pa: ProviderAttestation = cbor::from_slice(
68+
&BASE64_STANDARD
69+
.decode(
70+
metadata
71+
.get(METADATA_KEY_POLICY_PROVIDER_ATTESTATION)
72+
.ok_or(Error::NodeNotAllowed)?,
73+
)
74+
.map_err(|_| Error::NodeNotAllowed)?,
75+
)
76+
.map_err(|_| Error::NodeNotAllowed)?;
77+
78+
// Verify node label attestation.
79+
pa.signature
80+
.verify(
81+
&endorsing_node_id,
82+
ATTEST_LABELS_SIGNATURE_CONTEXT,
83+
&pa.label_attestation,
84+
)
85+
.map_err(|_| Error::NodeNotAllowed)?;
86+
let label_attestation: LabelAttestation =
87+
cbor::from_slice(&pa.label_attestation).map_err(|_| Error::NodeNotAllowed)?;
88+
if label_attestation.rak != ect.capability_tee.rak {
89+
return Err(Error::NodeNotAllowed);
90+
}
91+
92+
// Extract provider label (set by the provider's scheduler).
93+
let provider_label: ProviderLabel = cbor::from_slice(
94+
&BASE64_STANDARD
95+
.decode(
96+
label_attestation
97+
.labels
98+
.get(LABEL_PROVIDER)
99+
.ok_or(Error::NodeNotAllowed)?,
100+
)
101+
.map_err(|_| Error::NodeNotAllowed)?,
102+
)
103+
.map_err(|_| Error::NodeNotAllowed)?;
104+
105+
let provider =
106+
state::get_provider(provider_label.provider).ok_or(Error::NodeNotAllowed)?;
107+
if !provider.nodes.contains(&endorsing_node_id) {
108+
return Err(Error::NodeNotAllowed);
109+
}
110+
111+
let instance =
112+
state::get_instance(provider_label.provider, provider_label.instance)
113+
.ok_or(Error::NodeNotAllowed)?;
114+
if &instance.admin != expected_admin {
115+
return Err(Error::NodeNotAllowed);
116+
}
117+
118+
Ok(())
119+
}
120+
_ => Err(Error::NodeNotAllowed),
121+
}
122+
}
123+
}

runtime-sdk/modules/rofl-market/src/test.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ struct Config;
2323

2424
impl core::Config for Config {}
2525

26-
impl rofl::Config for Config {}
26+
impl rofl::Config for Config {
27+
type EndorsementPolicyEvaluator = rofl::policy::BasicEndorsementPolicyEvaluator;
28+
}
2729

2830
impl super::Config for Config {
2931
type Rofl = rofl::Module<Config>;

runtime-sdk/src/modules/rofl/app/registration.rs

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
use std::{sync::Arc, time::Duration};
1+
use std::{collections::BTreeMap, sync::Arc, time::Duration};
22

33
use anyhow::{anyhow, Result};
4+
use base64::{prelude::BASE64_STANDARD, Engine};
45
use tokio::sync::mpsc;
56

67
use crate::{
78
core::{
8-
common::logger::get_logger,
9+
common::{crypto::signature::Signature, logger::get_logger},
910
consensus::{
1011
beacon::EpochTime, state::beacon::ImmutableState as BeaconState, verifier::Verifier,
1112
},
13+
host::attestation::{AttestLabelsRequest, LabelAttestation},
1214
},
1315
modules::rofl::types::{AppInstanceQuery, Register, Registration},
1416
};
@@ -134,7 +136,7 @@ where
134136
"epoch" => epoch,
135137
);
136138

137-
let metadata = match self.state.app.clone().get_metadata(self.env.clone()).await {
139+
let mut metadata = match self.state.app.clone().get_metadata(self.env.clone()).await {
138140
Ok(metadata) => metadata,
139141
Err(err) => {
140142
slog::error!(self.logger, "failed to get instance metadata"; "err" => ?err);
@@ -143,6 +145,11 @@ where
143145
}
144146
};
145147

148+
// Include provider-specific metadata if available.
149+
if let Err(err) = self.collect_provider_metadata(&mut metadata).await {
150+
slog::error!(self.logger, "failed to collect provider metadata"; "err" => ?err);
151+
}
152+
146153
// Refresh registration.
147154
let ect = self
148155
.state
@@ -191,4 +198,49 @@ where
191198

192199
Ok(())
193200
}
201+
202+
async fn collect_provider_metadata(
203+
&self,
204+
metadata: &mut BTreeMap<String, String>,
205+
) -> Result<()> {
206+
let rsp = self
207+
.env
208+
.host()
209+
.attestation()
210+
.attest_labels(AttestLabelsRequest {
211+
labels: vec![LABEL_PROVIDER.to_string()],
212+
})
213+
.await?;
214+
215+
// Decode the attestation to check if the provider label is set and skip setting
216+
// metadata in case it is not.
217+
let la: LabelAttestation = cbor::from_slice(&rsp.attestation)?;
218+
if la.labels.get(LABEL_PROVIDER) == Some(&String::new()) {
219+
return Ok(());
220+
}
221+
222+
let pa = ProviderAttestation {
223+
label_attestation: rsp.attestation,
224+
signature: rsp.signature,
225+
};
226+
227+
let pa = BASE64_STANDARD.encode(cbor::to_vec(pa));
228+
metadata.insert(METADATA_KEY_POLICY_PROVIDER_ATTESTATION.to_string(), pa);
229+
230+
Ok(())
231+
}
232+
}
233+
234+
/// Name of the ROFL app instance metadata key used to store the provider attestation.
235+
const METADATA_KEY_POLICY_PROVIDER_ATTESTATION: &str = "net.oasis.policy.provider";
236+
/// Name of the provider label set by the scheduler.
237+
const LABEL_PROVIDER: &str = "net.oasis.provider";
238+
239+
/// Provider attestation metadata stored in `METADATA_KEY_POLICY_PROVIDER_ATTESTATION` label.
240+
#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
241+
struct ProviderAttestation {
242+
/// A CBOR-serialized `LabelAttestation`.
243+
pub label_attestation: Vec<u8>,
244+
/// Signature from endorsing node.
245+
pub signature: Signature,
194246
}

runtime-sdk/src/modules/rofl/config.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use crate::types::token;
22

3+
use super::policy::EndorsementPolicyEvaluator;
4+
35
/// Module configuration.
46
pub trait Config: 'static {
57
/// Gas cost of rofl.Create call.
@@ -37,4 +39,10 @@ pub trait Config: 'static {
3739

3840
/// Maximum key identifier length for rofl.DeriveKey call.
3941
const DERIVE_KEY_MAX_KEY_ID_LENGTH: usize = 128;
42+
43+
/// Maximum number of endorsment policy atoms.
44+
const MAX_ENDORSEMENT_POLICY_ATOMS: usize = 32;
45+
46+
/// Endorsement policy evaluator.
47+
type EndorsementPolicyEvaluator: EndorsementPolicyEvaluator;
4048
}

0 commit comments

Comments
 (0)