-
Notifications
You must be signed in to change notification settings - Fork 98
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Opensource pcs and dcap_artifact_retrieval
- Loading branch information
1 parent
b6f0262
commit 6cf653f
Showing
33 changed files
with
4,657 additions
and
262 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
Oops, something went wrong.