Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/snp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,16 @@ jobs:
BRANCH=${GITHUB_REF_NAME}
fi
./scripts/accli_wrapper.sh dev cvm run -- \
"git fetch origin $BRANCH && git checkout $BRANCH && git reset --hard origin/$BRANCH"
"git fetch origin $BRANCH && git checkout $BRANCH && git reset --hard origin/$BRANCH && ./scripts/apt.sh"

# Build SNP applications and embed the attestation service's certificate.
- name: "Build SNP applications"
run: ./scripts/accli_wrapper.sh applications build --clean --as-cert-dir ./certs/cert.pem --in-cvm
run: ./scripts/accli_wrapper.sh applications build --clean --as-cert-dir ./certs --in-cvm

- name: "Run supported SNP applications"
run: |
# First get the external IP so that we can reach the attestation-service from the cVM.
AS_URL=$(./scripts/accli_wrapper.sh attestation-service health --url "https://0.0.0.0:8443" --cert-path ./certs/cert.pem 2>&1 \
AS_URL=$(./scripts/accli_wrapper.sh attestation-service health --url "https://0.0.0.0:8443" --cert-dir ./certs 2>&1 \
| grep "attestation service is healthy and reachable on:" | awk '{print $NF}')
echo "Got AS URL: ${AS_URL}"
./scripts/accli_wrapper.sh applications run function hello-snp --backend cvm -- --as-url ${AS_URL} --as-cert-path ./certs/cert.pem
Expand Down
13 changes: 12 additions & 1 deletion accli/src/tasks/applications.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use clap::ValueEnum;
use log::error;
use std::{
fmt::{Display, Formatter, Result as FmtResult},
fs,
path::{Path, PathBuf},
str::FromStr,
};
Expand Down Expand Up @@ -163,7 +164,17 @@ impl Applications {
if let Some(host_cert_dir) = as_cert_dir {
let guest_cert_dir =
host_cert_dir_to_target_path(&host_cert_dir, &ApplicationBackend::Cvm)?;
scp_files.push((host_cert_dir, guest_cert_dir.clone()));
for entry in fs::read_dir(&host_cert_dir)? {
let entry = entry?;
let file_type = entry.file_type()?;
if !file_type.is_file() {
anyhow::bail!(
"certificate directory may only contain files (found: {})",
entry.path().display()
);
}
scp_files.push((entry.path(), guest_cert_dir.join(entry.file_name())));
}

cmd.push("--as-cert-dir".to_string());
cmd.push(guest_cert_dir.display().to_string());
Expand Down
12 changes: 10 additions & 2 deletions accli/src/tasks/attestation_service.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::env::Env;
use anyhow::{Context, Result};
use log::info;
use log::{info, warn};
use nix::{
sys::signal::{Signal, kill},
unistd::Pid,
Expand Down Expand Up @@ -154,7 +154,15 @@ impl AttestationService {
let path = entry.path();
if path.is_file() && path.extension().is_some_and(|s| s == "pem") {
let cert = fs::read(&path)?;
let cert = reqwest::Certificate::from_pem(&cert)?;
let cert = match reqwest::Certificate::from_pem(&cert) {
Ok(cert) => cert,
Err(e) => {
warn!(
"health(): error parsing certificate PEM file (path={path:?}, error={e:?})"
);
continue;
}
};
client_builder = client_builder.add_root_certificate(cert);
}
}
Expand Down
53 changes: 35 additions & 18 deletions accli/src/tasks/experiments/plot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
use anyhow::Result;
use csv::ReaderBuilder;
use log::{debug, error, info};
use plotters::prelude::*;
use plotters::{element::DashedPathElement, prelude::*};
use serde::Deserialize;
use std::{
collections::BTreeMap,
Expand Down Expand Up @@ -1378,7 +1378,7 @@ fn plot_escrow_cost() {
))
.unwrap();
root.draw(&Text::new(
"# of users ",
"# of tenants",
(120, 280),
("sans-serif", FONT_SIZE).into_font().color(&BLACK),
))
Expand Down Expand Up @@ -1564,6 +1564,7 @@ fn plot_policy_decryption(data_files: &Vec<PathBuf>) -> Result<()> {
enum PolicyShape {
Conjunction,
Disjunction,
DisjunctionSingleAuthority,
}

impl PolicyShape {
Expand All @@ -1572,6 +1573,7 @@ fn plot_policy_decryption(data_files: &Vec<PathBuf>) -> Result<()> {
match stem {
"conjunction" => Some(Self::Conjunction),
"disjunction" => Some(Self::Disjunction),
"disjunction-single-authority" => Some(Self::DisjunctionSingleAuthority),
_ => None,
}
}
Expand All @@ -1580,15 +1582,22 @@ fn plot_policy_decryption(data_files: &Vec<PathBuf>) -> Result<()> {
match self {
Self::Conjunction => "all conjunction policy",
Self::Disjunction => "all disjunction policy",
Self::DisjunctionSingleAuthority => "all disjunction policy (single authority)",
}
}

fn color(&self) -> Result<RGBColor> {
match self {
Self::Conjunction => get_color_from_label("dark-red"),
Self::Disjunction => get_color_from_label("dark-blue"),
Self::Disjunction | Self::DisjunctionSingleAuthority => {
get_color_from_label("dark-blue")
}
}
}

fn is_dashed(&self) -> bool {
matches!(self, Self::DisjunctionSingleAuthority)
}
}

let mut agg: BTreeMap<PolicyShape, BTreeMap<usize, (f64, f64, u32)>> = BTreeMap::new();
Expand Down Expand Up @@ -1695,23 +1704,31 @@ fn plot_policy_decryption(data_files: &Vec<PathBuf>) -> Result<()> {
))?;

for (shape, rows) in data {
let series = rows.iter().map(|(x, vals)| (*x as i32, selector(vals)));
let series: Vec<(i32, f64)> = rows
.iter()
.map(|(x, vals)| (*x as i32, selector(vals)))
.collect();

// Draw line.
chart.draw_series(LineSeries::new(
series,
shape.color()?.stroke_width(STROKE_WIDTH),
))?;
if shape.is_dashed() {
chart.draw_series(std::iter::once(DashedPathElement::new(
series.clone().into_iter(),
5,
5,
shape.color()?.stroke_width(STROKE_WIDTH),
)))?;
} else {
chart.draw_series(LineSeries::new(
series.clone(),
shape.color()?.stroke_width(STROKE_WIDTH),
))?;
}

// Draw points on line.
chart
.draw_series(rows.iter().map(|(x, vals)| {
Circle::new(
(*x as i32, selector(vals)),
5,
shape.color().unwrap().filled(),
)
}))
.draw_series(
series
.iter()
.map(|(x, y)| Circle::new((*x, *y), 5, shape.color().unwrap().filled())),
)
.unwrap();
}

Expand All @@ -1731,7 +1748,7 @@ fn plot_policy_decryption(data_files: &Vec<PathBuf>) -> Result<()> {
))
.unwrap();

// legend as color box + text.
// legend as colored box + text
let x_pos = 100;
let y_pos = 5;
let square_side = 20;
Expand Down
77 changes: 69 additions & 8 deletions accli/src/tasks/experiments/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use clap::Args;
use csv::Writer;
use indicatif::{ProgressBar, ProgressStyle};
use log::{error, info};
use rand::{seq::SliceRandom, thread_rng};
use std::{
fs::{self, File},
path::PathBuf,
Expand Down Expand Up @@ -48,6 +49,21 @@ impl PolicyShape {
}
}

#[derive(Clone, Copy)]
enum AttributeFlavor {
AllAuthorities,
SingleAuthorityOnly,
}

impl AttributeFlavor {
fn file_suffix(&self) -> &'static str {
match self {
AttributeFlavor::AllAuthorities => "",
AttributeFlavor::SingleAuthorityOnly => "-single-authority",
}
}
}

#[derive(serde::Serialize)]
struct Record {
#[serde(rename = "NumAuthorities")]
Expand All @@ -64,12 +80,29 @@ fn build_authorities(num: usize) -> Vec<String> {
(0..num).map(|idx| format!("as{idx:02}")).collect()
}

fn build_user_attributes(authorities: &[String]) -> Result<Vec<UserAttribute>> {
fn build_user_attributes(
authorities: &[String],
flavor: AttributeFlavor,
) -> Result<Vec<UserAttribute>> {
let mut user_attrs = Vec::new();
for auth in authorities {
user_attrs.push(UserAttribute::parse(&format!("{auth}.wf:{WORKFLOW_ID}"))?);
user_attrs.push(UserAttribute::parse(&format!("{auth}.node:{NODE_ID}"))?);
}
match flavor {
AttributeFlavor::AllAuthorities => {
for auth in authorities {
user_attrs.push(UserAttribute::parse(&format!("{auth}.wf:{WORKFLOW_ID}"))?);
user_attrs.push(UserAttribute::parse(&format!("{auth}.node:{NODE_ID}"))?);
}
}
AttributeFlavor::SingleAuthorityOnly => {
// Pick an attribute at random.
let mut rng = thread_rng();
if let Some(auth) = authorities.choose(&mut rng) {
user_attrs.push(UserAttribute::parse(&format!("{auth}.wf:{WORKFLOW_ID}"))?);
user_attrs.push(UserAttribute::parse(&format!("{auth}.node:{NODE_ID}"))?);
} else {
anyhow::bail!("cannot build user attributes without authorities");
};
}
};
Ok(user_attrs)
}

Expand Down Expand Up @@ -119,12 +152,40 @@ fn measure_single_run(
}

fn run_shape(shape: PolicyShape, args: &ProfileRunArgs) -> Result<()> {
// Skip unsupported combinations.
if matches!(shape, PolicyShape::Conjunction) {
return run_shape_with_flavor(shape, AttributeFlavor::AllAuthorities, args);
}
run_shape_with_flavor(shape, AttributeFlavor::AllAuthorities, args)?;
run_shape_with_flavor(shape, AttributeFlavor::SingleAuthorityOnly, args)
}

fn run_shape_with_flavor(
shape: PolicyShape,
flavor: AttributeFlavor,
args: &ProfileRunArgs,
) -> Result<()> {
if matches!(
(shape, flavor),
(
PolicyShape::Conjunction,
AttributeFlavor::SingleAuthorityOnly
)
) {
info!("Skipping single-authority flavour for conjunction policy");
return Ok(());
}

let data_dir = ensure_data_dir()?;
let mut csv_path = data_dir;
csv_path.push(format!("{}.csv", shape.file_stem()));
csv_path.push(format!("{}{}.csv", shape.file_stem(), flavor.file_suffix()));
let mut writer = Writer::from_writer(File::create(csv_path)?);
let total_iters = (POLICY_SIZES.len() as u64) * (args.num_warmup_runs + args.num_runs) as u64;
let pb = ProgressBar::new(total_iters).with_message(shape.file_stem());
let pb = ProgressBar::new(total_iters).with_message(format!(
"{}{}",
shape.file_stem(),
flavor.file_suffix()
));
pb.set_style(
ProgressStyle::with_template("{msg} [{bar:40.cyan/blue}] {pos}/{len}")
.unwrap()
Expand All @@ -133,7 +194,7 @@ fn run_shape(shape: PolicyShape, args: &ProfileRunArgs) -> Result<()> {

for &num_auth in POLICY_SIZES {
let authorities = build_authorities(num_auth);
let user_attrs = build_user_attributes(&authorities)?;
let user_attrs = build_user_attributes(&authorities, flavor)?;
let auths_ref: Vec<&str> = authorities.iter().map(|s| s.as_str()).collect();

let mut rng = StdRng::seed_from_u64(1337 + num_auth as u64);
Expand Down
48 changes: 48 additions & 0 deletions experiments/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Accless Experiments

Figure 5:
- 5.a: [Escrow Throughput-Latency](./escrow-xput/README.md) - FINISH ME
- 5.a: [Escrow Cost](./escrow-cost/README.md) - FINISH ME
Table 4: [Attribute Minting Latency Breakdown](./secret-release-breakdown/README.md)

List of experiment claims -> Experiment that validates them:
C1. Accless is cheaper than centralized escrow -> E1
C2. Accless has better throughput-latency than centralized escrow -> E1
C3. Accless has better throughput-latency than single-authority CP-ABE -> E1
FIXME: this is not true. what if we increase the size of the VM running the single-auth system?
C4. The size of the policy does not affect decryption time.
C5. The attribute minting protocol introduces negligible overhead compared to ??
C6. The attribute minting protocol introduces negligible overhead compared to cold-start time.
C7. Accless introduces negligible overhead in the end-to-end execution of workflows.

List of experiments:
E1. Throughput-latency of different access control mechanisms.
E2. Micro-benchmark of decryption time as we increase the number of attestation services. (also include encryption time and the corresponding CP-ABE operations like keygen) (needs I6)
E3. Access control breakdown (time to "decrypt" payload)
E4. Cold-start breakdown

Big implementation tasks:
I1. Verify azure cVM reports -> Done
I2. Actually implement the user-side encryption of data from template graph
I3. Run SGX functions from accli
I4. Embed accless checks in S3 library
I5. Support running with multiple replicated attestation-services -> Done
I6. Implement CP-ABE hybrid scheme.

Order:
- Escrow-xput:
- Use as opportunity to fix SNP HW mode
- Use as opportunity to try to run SNP bare-metal stuff from `accli`.
- Use as an opportunity to fix SNP in a para-virtualized VM
- If we could get rid of the annoyint azguestattestaion crate that would be perfect, and vendor in the code.
- Breakdown table:
- Compile to native and cross-compile to WASM
- Use as opportunity to fix SGX HW mode
- Use as opportunity to try to run WASM functions through Faasm in the `accli`.
- SNP breakdown could be run either para-virtualized or bare metal.
- Cold start CDF:
- Use as opportunity to close the loop in generating template graph and uploading encrypted dta
- Chaining ubench
- Use as opportunity to close the loop on the chainig protocol w/ CP-ABE
- Workflows
- Move workflows to `./applications`
2 changes: 1 addition & 1 deletion experiments/escrow-cost/plots/escrow-cost.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading