Skip to content

Commit 1a11878

Browse files
authored
Merge pull request #669 from fortanix/mz/use-v4-apis-for-dcap-artifact-retrieval
dcap-artifact-retrieval: add CLI option for specifying PCS API version and format code
2 parents f3b05c8 + 36a0134 commit 1a11878

File tree

8 files changed

+838
-370
lines changed

8 files changed

+838
-370
lines changed

Cargo.lock

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

intel-sgx/dcap-artifact-retrieval/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "dcap-artifact-retrieval"
3-
version = "0.2.0"
3+
version = "0.3.0"
44
authors = ["Fortanix, Inc."]
55
license = "MPL-2.0"
66
edition = "2018"
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
/* Copyright (c) Fortanix, Inc.
2+
*
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
use std::convert::TryInto;
9+
use std::path::{Path, PathBuf};
10+
11+
use clap::clap_app;
12+
use pcs::PckID;
13+
use rustc_serialize::hex::ToHex;
14+
use serde::de::{value, IntoDeserializer};
15+
use serde::Deserialize;
16+
17+
use crate::{
18+
AzureProvisioningClientBuilder, Error, IntelProvisioningClientBuilder, PcsVersion,
19+
ProvisioningClient, StatusCode,
20+
};
21+
22+
#[derive(Debug, Deserialize, Copy, Clone, Eq, PartialEq, Hash)]
23+
#[serde(rename_all = "kebab-case")]
24+
enum Origin {
25+
Intel,
26+
Azure,
27+
}
28+
29+
fn str_deserialize(s: &str) -> value::StrDeserializer<value::Error> {
30+
s.into_deserializer()
31+
}
32+
33+
fn parse_origin(p: &str) -> Result<Origin, String> {
34+
Origin::deserialize(str_deserialize(p)).map_err(|e| e.to_string())
35+
}
36+
37+
fn download_dcap_artifacts(
38+
prov_client: &dyn ProvisioningClient,
39+
pckid_file: &str,
40+
output_dir: &str,
41+
verbose: bool,
42+
) -> Result<(), Error> {
43+
for (idx, pckid) in PckID::parse_file(&PathBuf::from(&pckid_file).as_path())?
44+
.iter()
45+
.enumerate()
46+
{
47+
let enc_ppid = &pckid.enc_ppid.as_slice();
48+
if verbose {
49+
println!("==[ entry {} ]==", idx);
50+
println!(" Info:");
51+
println!(
52+
" Encr. PPID: {}..{}",
53+
enc_ppid[..12].to_hex(),
54+
enc_ppid[enc_ppid.len() - 3..].to_hex()
55+
);
56+
println!(" pce_id: {}", &&pckid.pce_id.to_le_bytes().to_hex());
57+
println!(" cpu svn: {}", pckid.cpu_svn.as_slice().to_hex());
58+
println!(
59+
" pce isvsvn: {}",
60+
pckid.pce_isvsvn.to_le_bytes().to_hex()
61+
);
62+
println!(" qe_id: {}", pckid.qe_id.as_slice().to_hex());
63+
println!(" Storing artifacts:");
64+
}
65+
66+
// Fetch pckcerts, note that Azure does not support this API, instead we mimic it
67+
let pckcerts = match prov_client.pckcerts(&pckid.enc_ppid, pckid.pce_id) {
68+
Ok(pckcerts) => pckcerts,
69+
Err(Error::RequestNotSupported) => prov_client
70+
.pckcert(
71+
None,
72+
&pckid.pce_id,
73+
&pckid.cpu_svn,
74+
pckid.pce_isvsvn,
75+
Some(&pckid.qe_id),
76+
)?
77+
.try_into()
78+
.map_err(|e| Error::PCSDecodeError(format!("{}", e).into()))?,
79+
Err(e) => return Err(e),
80+
};
81+
let pckcerts_file = pckcerts.store(output_dir, pckid.qe_id.as_slice())?;
82+
83+
if verbose {
84+
println!(" pckcerts: {}", pckcerts_file);
85+
}
86+
87+
let fmspc = pckcerts.fmspc()?;
88+
let tcbinfo = prov_client.tcbinfo(&fmspc)?;
89+
let tcbinfo_file = tcbinfo
90+
.store(output_dir)
91+
.map_err(|e| Error::OfflineAttestationError(e))?;
92+
93+
if verbose {
94+
println!(" tcb info: {}\n", tcbinfo_file);
95+
}
96+
}
97+
let pckcrl = prov_client
98+
.pckcrl()
99+
.and_then(|crl| crl.write_to_file(output_dir).map_err(|e| e.into()))?;
100+
let qe_identity = prov_client
101+
.qe_identity()
102+
.and_then(|qe_id| qe_id.write_to_file(output_dir).map_err(|e| e.into()))?;
103+
if verbose {
104+
println!("==[ generic ]==");
105+
println!(" pckcrl: {}", pckcrl);
106+
println!(" QE identity: {}", qe_identity);
107+
}
108+
Ok(())
109+
}
110+
111+
pub fn main() {
112+
fn is_directory(directory_path: String) -> Result<(), String> {
113+
let path = Path::new(&directory_path);
114+
115+
match (path.exists(), path.is_dir()) {
116+
(true, true) => return Ok(()),
117+
(true, false) => {
118+
return Err(format!(
119+
"Path {} exists, but is not a directory",
120+
directory_path
121+
))
122+
}
123+
(false, _) => return Err(format!("Directory {} does not exists", directory_path)),
124+
};
125+
}
126+
127+
fn is_file(filename: String) -> Result<(), String> {
128+
if Path::new(&filename).exists() {
129+
Ok(())
130+
} else {
131+
Err(format!("Cannot open {}", filename))
132+
}
133+
}
134+
135+
fn parse_pcs_version(value: &str) -> Result<PcsVersion, String> {
136+
match value {
137+
"3" => Ok(PcsVersion::V3),
138+
"4" => Ok(PcsVersion::V4),
139+
_ => Err(format!(
140+
"Expected 3 or 4, found `{}`",
141+
value
142+
)),
143+
}
144+
}
145+
146+
let matches = clap::clap_app!(("DCAP Artifact Retrieval Tool") =>
147+
(author: "Fortanix")
148+
(about: "Fortanix ecdsa artifact retrieval tool for DCAP attestation")
149+
(
150+
@arg ORIGIN: --("origin") +takes_value
151+
validator(|s| parse_origin(s.as_str()).map(|_| ()))
152+
"Location from where artifacts need to be fetched. Options are: \"intel\" and \"azure\".\
153+
Note that Azure does not provide access to all artifacts. Intel will be contacted as a fallback (default: \"intel\")"
154+
)
155+
(
156+
@arg PCKID_FILE: --("pckid-file") +takes_value +required requires("PCKID_FILE")
157+
validator(is_file)
158+
"File describing the PCK identity (outputted by PCKIDRetrievalTool)"
159+
)
160+
(
161+
@arg OUTPUT_DIR: --("output-dir") +takes_value +required requires("OUTPUT_DIR")
162+
validator(is_directory)
163+
"Destination folder for data retrieved from Intel certification services"
164+
)
165+
(
166+
@arg API_VERSION: --("api-version") +takes_value
167+
validator(|s| parse_pcs_version(s.as_str()).map(|_| ()))
168+
"API version for provisioning service, supported values are 3 and 4 (default: 3)"
169+
)
170+
(
171+
@arg API_KEY: --("api-key") +takes_value
172+
"API key for authenticating with Intel provisioning service"
173+
)
174+
(
175+
@arg VERBOSE: -v --verbose
176+
"Print information of which files are fetched"
177+
)
178+
)
179+
.get_matches();
180+
181+
let result = match (
182+
matches.value_of("PCKID_FILE"),
183+
matches.value_of("OUTPUT_DIR"),
184+
) {
185+
(Some(pckid_file), Some(output_dir)) => {
186+
let verboseness = matches.occurrences_of("VERBOSE");
187+
let api_version = parse_pcs_version(matches.value_of("API_VERSION").unwrap_or("3"))
188+
.expect("validated");
189+
190+
let origin =
191+
parse_origin(matches.value_of("ORIGIN").unwrap_or("intel")).expect("validated");
192+
193+
let fetcher = crate::reqwest_client();
194+
let client: Box<dyn ProvisioningClient> = match origin {
195+
Origin::Intel => {
196+
let mut client_builder = IntelProvisioningClientBuilder::new(api_version);
197+
if let Some(api_key) = matches.value_of("API_KEY") {
198+
client_builder.set_api_key(api_key.into());
199+
}
200+
Box::new(client_builder.build(fetcher))
201+
}
202+
Origin::Azure => {
203+
let client_builder = AzureProvisioningClientBuilder::new(api_version);
204+
Box::new(client_builder.build(fetcher))
205+
}
206+
};
207+
download_dcap_artifacts(&*client, pckid_file, output_dir, 0 < verboseness)
208+
}
209+
_ => unreachable!("validated"),
210+
};
211+
212+
match result {
213+
Ok(()) => {}
214+
Err(Error::PCSError(StatusCode::NotFound, _)) => {
215+
eprintln!("Error: Artifact not found. Perhaps specify a different origin?");
216+
std::process::exit(1);
217+
}
218+
Err(err) => {
219+
eprintln!("Error downloading artifact: {}", err);
220+
std::process::exit(1);
221+
}
222+
}
223+
}

intel-sgx/dcap-artifact-retrieval/src/lib.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,21 @@
88
//! DCAP attestations require access to Intel-signed artifacts. This library provides clients to
99
//! access these artifacts both from Intel directly, and from Microsoft Azure.
1010
11-
mod provisioning_client;
12-
pub use provisioning_client::*;
11+
#[cfg(not(target_env = "sgx"))]
12+
pub mod cli;
13+
pub mod provisioning_client;
14+
15+
pub use self::provisioning_client::*;
1316

1417
use std::borrow::Cow;
1518
use std::io::Error as IoError;
1619
use std::str::Utf8Error;
1720

18-
#[cfg(feature = "reqwest")]
19-
pub use reqwest::blocking::{Client as ReqwestClient};
2021
use pcs::Error as OAError;
2122
use pkix::ASN1Error;
2223
use quick_error::quick_error;
24+
#[cfg(feature = "reqwest")]
25+
pub use reqwest::blocking::Client as ReqwestClient;
2326

2427
quick_error! {
2528
#[derive(Debug)]

0 commit comments

Comments
 (0)