Skip to content

Commit

Permalink
Opensource pcs and dcap_artifact_retrieval
Browse files Browse the repository at this point in the history
  • Loading branch information
raoulstrackx committed Jun 28, 2024
1 parent b6f0262 commit 6cf653f
Show file tree
Hide file tree
Showing 33 changed files with 4,657 additions and 262 deletions.
1,077 changes: 815 additions & 262 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ members = [
"fortanix-vme/vme-pkix",
"intel-sgx/aesm-client",
"intel-sgx/async-usercalls",
"intel-sgx/dcap_artifact_retrieval",
"intel-sgx/dcap-provider",
"intel-sgx/dcap-ql-sys",
"intel-sgx/dcap-ql",
Expand All @@ -25,6 +26,7 @@ members = [
"intel-sgx/fortanix-sgx-abi",
"intel-sgx/fortanix-sgx-tools",
"intel-sgx/ias",
"intel-sgx/pcs",
"intel-sgx/report-test",
"intel-sgx/sgx_pkix",
"intel-sgx/sgx-isa",
Expand Down
38 changes: 38 additions & 0 deletions intel-sgx/dcap_artifact_retrieval/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[package]
name = "dcap_artifact_retrieval"
version = "0.1.0"
authors = ["Raoul Strackx <raoul.strackx@fortanix.com>"]
edition = "2018"

[dependencies]
backoff = "0.4.0"
clap = { version = "2.23.3", optional = true }
lazy_static = "1"
lru-cache = "0.1.2"
mbedtls = { version = "0.12.3", features = [
"x509",
"ssl",
"std",
], default-features = false }
num_enum = { version = "0.7", features = ["complex-expressions"] }
pcs = { path = "../pcs" }
percent-encoding = "2.1.0"
pkix = "0.2.0"
quick-error = "1.1.0"
rustc-serialize = "0.3"
reqwest = { version = "0.12", features = ["blocking", "native-tls"], optional = true }
serde_cbor = "0.11"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

[features]
default = ["clap", "reqwest"]

[dev-dependencies]
yasna = { version = "0.3", features = ["num-bigint", "bit-vec"] }
pcs = { path = "../pcs", features = ["verify"] }

[build-dependencies]
mbedtls = { version = "0.12.3", features = ["ssl", "x509"] }
pkix = "0.2.0"
serde_cbor = "0.11"
111 changes: 111 additions & 0 deletions intel-sgx/dcap_artifact_retrieval/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//! DCAP attestations require access to Intel-signed artifacts. This library provides clients to
//! access these artifacts both from Intel directly, and from Microsoft Azure.

mod provisioning_client;
pub use provisioning_client::*;

use std::borrow::Cow;
use std::io::Error as IoError;
use std::str::Utf8Error;

#[cfg(feature = "reqwest")]
use reqwest::blocking::{Client as ReqwestClient};
use pcs::Error as OAError;
use pkix::ASN1Error;
use quick_error::quick_error;

quick_error! {
#[derive(Debug)]
pub enum Error {
PckIdParseError(msg: &'static str) {
description("Error during parsing PCKID file")
display("Error parsing PCKID file: {}", msg)
}
ReadResponseError(msg: Cow<'static, str>) {
from()
display("{}", msg)
}
FetcherFailure(err: String) {
from()
display("{}", err)
}
IoError(err: IoError) {
from()
}
PCSError(status_code: StatusCode, msg : &'static str ) {
description("Certification services returned an unexpected response")
display("{}", msg)
}
PCSParseError(err: serde_json::error::Error) {
description("Intel PCS response failed to parse correctly")
display("json parse error: {}", err)
}
PCSDecodeError(error: Cow<'static, str>) {
description("Intel PCS response could not be decoded")
display("percent decoding failed: {}", error)
}
HeaderMissing(msg : &'static str) {
description("Expected header was not present")
display("Expected header \"{}\" missing", msg)
}
HeaderDecodeError(err : Utf8Error) {
description("Intel certification services returned a header that could not be decoded")
display("Failed to decode header")
}
HeaderParseError(msg : &'static str) {
description("Header could not be parsed")
display("Failed to parse header {}", msg)
}
CertificateParseError(msg: &'static str) {
description("Certificate could not be parsed")
display("Failed to parse certificate {}", msg)
}
CertificateEncodingError(err: ASN1Error) {
from()
}
NoEncPPID {
description("Enc_ppid is required, but not provided")
display("No enc_ppid was provided")
}
NoCPUSVN {
description("CPU_svn is required, but not provided")
display("No cpu_svn was provided")
}
NoPCEISVSVN {
description("PCE ISVSVN is required, but not provided")
display("No pce_isvsvn was provided")
}
NoPCEID {
description("PCEID is required, but not provided")
display("No pce_id was provided")
}
NoQeID {
description("QEID is required, but not provided")
display("No QE ID was provided")
}
NoAPIKey {
description("PCS key is required, but not provided")
display("No api_key was provided")
}
OfflineAttestationError(err: OAError) {
from()
}
BadRequest(err: &'static str) {
description("Bad Request")
display("{}", err)
}
RequestNotSupported {
description("Client does not support this request")
display("Client does not support this request")
}
}
}

pub type Result<T> = std::result::Result<T, Error>;

pub fn reqwest_client() -> ReqwestClient {
ReqwestClient::builder()
.use_native_tls()
.build()
.expect("Failed to build reqwest client")
}
152 changes: 152 additions & 0 deletions intel-sgx/dcap_artifact_retrieval/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
use std::path::{Path, PathBuf};

use clap::clap_app;
use dcap_artifact_retrieval::{
AzureProvisioningClientBuilder, IntelProvisioningClientBuilder, ProvisioningClient, PcsVersion,
};
use dcap_artifact_retrieval::{Error, StatusCode};
use pcs::PckID;
use rustc_serialize::hex::ToHex;
use serde::de::{value, IntoDeserializer};
use serde::Deserialize;
use std::convert::TryInto;

#[derive(Debug, Deserialize, Copy, Clone, Eq, PartialEq, Hash)]
#[serde(rename_all = "kebab-case")]
enum Origin {
Intel,
Azure,
}

fn str_deserialize(s: &str) -> value::StrDeserializer<value::Error> {
s.into_deserializer()
}

fn parse_origin(p: &str) -> std::result::Result<Origin, String> {
Origin::deserialize(str_deserialize(p)).map_err(|e| e.to_string())
}

pub fn download_dcap_artifacts(
prov_client: &dyn ProvisioningClient,
pckid_file: &str,
output_dir: &str,
verbose: bool,
) -> Result<(), Error> {
for (idx, pckid) in PckID::parse_file(&PathBuf::from(&pckid_file).as_path())?.iter().enumerate() {
let enc_ppid = &pckid.enc_ppid.as_slice();
if verbose {
println!("==[ entry {} ]==", idx);
println!(" Info:");
println!(
" Encr. PPID: {}..{}",
enc_ppid[..12].to_hex(),
enc_ppid[enc_ppid.len() - 3..].to_hex()
);
println!(" pce_id: {}", &&pckid.pce_id.to_le_bytes().to_hex());
println!(" cpu svn: {}", pckid.cpu_svn.as_slice().to_hex());
println!(" pce isvsvn: {}", pckid.pce_isvsvn.to_le_bytes().to_hex());
println!(" qe_id: {}", pckid.qe_id.as_slice().to_hex());
println!(" Storing artifacts:");
}

// Fetch pckcerts, note that Azure does not support this API, instead we mimic it
let pckcerts = match prov_client.pckcerts(&pckid.enc_ppid, pckid.pce_id) {
Ok(pckcerts) => pckcerts,
Err(Error::RequestNotSupported) => prov_client.pckcert(None, &pckid.pce_id, &pckid.cpu_svn, pckid.pce_isvsvn, Some(&pckid.qe_id))?
.try_into()
.map_err(|e| Error::PCSDecodeError(format!("{}", e).into()))?,
Err(e) => return Err(e),
};
let pckcerts_file = pckcerts.store(output_dir, pckid.qe_id.as_slice())?;

if verbose {
println!(" pckcerts: {}", pckcerts_file);
}

let fmspc = pckcerts.fmspc()?;
let tcbinfo = prov_client.tcbinfo(&fmspc)?;
let tcbinfo_file = tcbinfo.store(output_dir).map_err(|e| Error::OfflineAttestationError(e))?;

if verbose {
println!(" tcb info: {}\n", tcbinfo_file);
}
}
let pckcrl = prov_client
.pckcrl()
.and_then(|crl| crl.write_to_file(output_dir).map_err(|e| e.into()))?;
let qe_identity = prov_client
.qe_identity()
.and_then(|qe_id| qe_id.write_to_file(output_dir).map_err(|e| e.into()))?;
if verbose {
println!("==[ generic ]==");
println!(" pckcrl: {}", pckcrl);
println!(" QE identity: {}", qe_identity);
}
Ok(())
}

fn main() {
fn is_directory(directory_path: String) -> std::result::Result<(), String> {
let path = Path::new(&directory_path);

match (path.exists(), path.is_dir()) {
(true, true) => return Ok(()),
(true, false) => return Err(format!("Path {} exists, but is not a directory", directory_path)),
(false, _) => return Err(format!("Directory {} does not exists", directory_path)),
};
}

fn is_pckid_file(filename: String) -> std::result::Result<(), String> {
if Path::new(&filename).exists() {
Ok(())
} else {
Err(format!("Cannot open {}", filename))
}
}

let matches = clap_app!(tool =>
(author: "Fortanix")
(about: "Fortanix ecdsa artifact retrieval tool for DCAP attestation")
(@arg ORIGIN: --("origin") +takes_value validator(|s| parse_origin(s.as_str()).map(|_| ())) "Location from where artifacts need to be fetched. Options are: \"intel\" and \"azure\". Note that Azure does not provide access to all artifacts. Intel will be contacted as a fallback (default: \"intel\")")
(@arg PCKID_FILE: --("pckid-file") +takes_value +required requires("PCKID_FILE") validator(is_pckid_file) "File describing the PCK identity (outputed by PCKIDRetrievalTool)")
(@arg OUTPUT_DIR: --("output-dir") +takes_value +required requires("OUTPUT_DIR") validator(is_directory) "Destination folder for data retrieved from Intel certification services")
(@arg API_KEY: --("api-key") +takes_value "API key for authenticating with Intel provisioning service")
(@arg VERBOSE: -v --verbose "Print information of which files are fetched")
)
.get_matches();

let result = match (matches.value_of("PCKID_FILE"), matches.value_of("OUTPUT_DIR")) {
(Some(pckid_file), Some(output_dir)) => {
let verboseness = matches.occurrences_of("VERBOSE");
let origin = parse_origin(matches.value_of("ORIGIN").unwrap_or("intel")).expect("validated");
let fetcher = dcap_artifact_retrieval::reqwest_client();
let client: Box<dyn ProvisioningClient> = match origin {
Origin::Intel => {
let mut client_builder = IntelProvisioningClientBuilder::new(PcsVersion::V3);
if let Some(api_key) = matches.value_of("API_KEY") {
client_builder.set_api_key(api_key.into());
}
Box::new(client_builder.build(fetcher))
}
Origin::Azure => {
let client_builder = AzureProvisioningClientBuilder::new(PcsVersion::V3);
Box::new(client_builder.build(fetcher))
}
};
download_dcap_artifacts(&*client, pckid_file, output_dir, 0 < verboseness)
}
_ => unreachable!("validated"),
};

match result {
Ok(()) => {}
Err(Error::PCSError(StatusCode::NotFound, _)) => {
eprintln!("Error: Artifact not found. Perhaps specify a different origin?");
std::process::exit(1);
}
Err(err) => {
eprintln!("Error downloading artifact: {}", err);
std::process::exit(1);
}
}
}
Loading

0 comments on commit 6cf653f

Please sign in to comment.