dcap_artifact_retrieval/provisioning_client/
azure.rs1use pcs::{CpuSvn, EncPpid, PceId, PceIsvsvn, PckCert, QeId, Unverified};
9use rustc_serialize::hex::ToHex;
10use serde::Deserialize;
11use std::time::Duration;
12
13use super::common::PckCertsApiNotSupported;
14use super::intel::{INTEL_BASE_URL, TcbEvaluationDataNumbersApi, PckCrlApi, QeIdApi, TcbInfoApi};
15use super::{
16 Client, ClientBuilder, Fetcher, PckCertIn, PckCertService, PcsVersion, ProvisioningServiceApi,
17 StatusCode,
18};
19use crate::Error;
20
21pub struct AzureProvisioningClientBuilder {
27 api_version: PcsVersion,
28 client_builder: ClientBuilder,
29}
30
31impl AzureProvisioningClientBuilder {
32 pub fn new(api_version: PcsVersion) -> Self {
33 Self {
34 api_version,
35 client_builder: ClientBuilder::new(),
36 }
37 }
38
39 pub fn set_retry_timeout(mut self, retry_timeout: Duration) -> Self {
40 self.client_builder = self.client_builder.set_retry_timeout(retry_timeout);
41 self
42 }
43
44 pub fn build<F: for<'a> Fetcher<'a>>(self, fetcher: F) -> Client<F> {
45 let pck_certs = PckCertsApiNotSupported;
46 let pck_cert = PckCertApi::new(self.api_version.clone());
47 let pck_crl = PckCrlApi::new(self.api_version.clone());
48 let qeid = QeIdApi::new(self.api_version.clone());
49 let tcbinfo = TcbInfoApi::new(self.api_version.clone());
50 let evaluation_data_numbers = TcbEvaluationDataNumbersApi::new(INTEL_BASE_URL.into());
51 self.client_builder
52 .build(pck_certs, pck_cert, pck_crl, qeid, tcbinfo, evaluation_data_numbers, fetcher)
53 }
54}
55
56pub struct PckCertApi {
57 api_version: PcsVersion,
58}
59
60impl PckCertApi {
61 const SECONDARY_CERT_URL: &'static str = "https://global.acccache.azure.net/sgx/certification";
64 const DEFAULT_CLIENT_ID: &'static str = "production_client";
65 const API_VERSION_07_2021: &'static str = "2021-07-22-preview";
66}
67
68impl PckCertApi {
69 pub(crate) fn new(api_version: PcsVersion) -> PckCertApi {
70 PckCertApi { api_version }
71 }
72}
73
74impl<'inp> PckCertService<'inp> for PckCertApi {
75 fn build_input(
76 &'inp self,
77 encrypted_ppid: Option<&'inp EncPpid>,
78 pce_id: &'inp PceId,
79 cpu_svn: &'inp CpuSvn,
80 pce_isvsvn: PceIsvsvn,
81 qe_id: Option<&'inp QeId>,
82 ) -> <Self as ProvisioningServiceApi<'inp>>::Input {
83 PckCertIn {
84 encrypted_ppid,
85 pce_id,
86 cpu_svn,
87 pce_isvsvn,
88 qe_id,
89 api_version: self.api_version,
90 api_key: &None,
91 }
92 }
93}
94
95impl<'inp> ProvisioningServiceApi<'inp> for PckCertApi {
96 type Input = PckCertIn<'inp>;
97 type Output = PckCert<Unverified>;
98
99 fn build_request(&self, input: &Self::Input) -> Result<(String, Vec<(String, String)>), Error> {
100 fn build_pck_cert_url(
103 pce_id: &PceId,
104 cpu_svn: &CpuSvn,
105 pce_isvsvn: PceIsvsvn,
106 qe_id: &QeId,
107 api_version: PcsVersion,
108 ) -> String {
109 let base_url = PckCertApi::SECONDARY_CERT_URL;
112 let version = api_version as u8;
113 let qeid = qe_id.to_hex();
114 let cpusvn = cpu_svn.to_hex();
115 let pcesvn = pce_isvsvn.to_le_bytes().to_hex();
116 let pceid = pce_id.to_le_bytes().to_hex();
117 let clientid = PckCertApi::DEFAULT_CLIENT_ID;
118 let api_version = PckCertApi::API_VERSION_07_2021;
119 format!("{base_url}/v{version}/pckcert?qeid={qeid}&cpusvn={cpusvn}&pcesvn={pcesvn}&pceid={pceid}&clientid={clientid}&api-version={api_version}")
120 }
121
122 let qe_id = input.qe_id.ok_or(Error::NoQeID)?;
123 let url = build_pck_cert_url(
124 input.pce_id,
125 input.cpu_svn,
126 input.pce_isvsvn,
127 qe_id,
128 input.api_version,
129 );
130 Ok((url, Vec::new()))
131 }
132
133 fn validate_response(&self, status_code: StatusCode) -> Result<(), Error> {
134 match &status_code {
135 StatusCode::Ok => Ok(()),
136 StatusCode::BadRequest => Err(Error::PCSError(status_code, "Invalid parameter")),
137 StatusCode::Unauthorized => Err(Error::PCSError(
138 status_code,
139 "Failed to authenticate or authorize the request (check your PCS key)",
140 )),
141 StatusCode::NotFound => Err(Error::PCSError(
142 status_code,
143 "Cannot find the requested certificate",
144 )),
145 StatusCode::InternalServerError => Err(Error::PCSError(
146 status_code,
147 "PCS suffered from an internal server error",
148 )),
149 StatusCode::ServiceUnavailable => Err(Error::PCSError(
150 status_code,
151 "PCS is temporarily unavailable",
152 )),
153 _ => Err(Error::PCSError(
154 status_code.clone(),
155 "Unexpected response from PCS server",
156 )),
157 }
158 }
159
160 fn parse_response(
161 &self,
162 response_body: String,
163 _response_headers: Vec<(String, String)>,
164 _api_version: PcsVersion,
165 ) -> Result<PckCert<Unverified>, Error> {
166 #[derive(Deserialize)]
167 struct AzurePckCertResp {
168 #[serde(rename = "pckCert")]
169 pck_cert: String,
170 #[serde(rename = "sgx-Pck-Certificate-Issuer-Chain")]
173 cert_chain: String,
174 }
175
176 let AzurePckCertResp {
177 pck_cert,
178 cert_chain,
179 } = serde_json::from_str(&response_body)
180 .map_err(|e| Error::PCSDecodeError(format!("{}", e).into()))?;
181
182 let cert_chain: Vec<String> = percent_encoding::percent_decode_str(&cert_chain)
183 .decode_utf8()
184 .map_err(|e| Error::PCSDecodeError(format!("{}", e).into()))?
185 .split_inclusive("-----END CERTIFICATE-----\n")
186 .map(|c| c.trim().to_string())
187 .collect();
188 Ok(PckCert::new(pck_cert, cert_chain))
189 }
190}
191
192#[cfg(all(test, feature = "reqwest"))]
193mod tests {
194 use std::path::PathBuf;
195 use std::time::Duration;
196
197 use pcs::PckID;
198
199 use crate::provisioning_client::{
200 test_helpers, AzureProvisioningClientBuilder, DcapArtifactIssuer, PcsVersion, ProvisioningClient,
201 };
202 use crate::reqwest_client;
203
204 const PCKID_TEST_FILE: &str = "./tests/data/azure_icelake_pckid.csv";
205
206 const TIME_RETRY_TIMEOUT: Duration = Duration::from_secs(180);
207
208 #[test]
209 pub fn pcks_azure() {
210 for api_version in [PcsVersion::V3, PcsVersion::V4] {
211 let client = AzureProvisioningClientBuilder::new(api_version)
212 .set_retry_timeout(TIME_RETRY_TIMEOUT)
213 .build(reqwest_client());
214 let root_ca = include_bytes!("../../tests/data/root_SGX_CA_der.cert");
215 let root_cas = [&root_ca[..]];
216
217 for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
218 .unwrap()
219 .iter()
220 {
221 let pck = client
222 .pckcert(
223 None,
224 &pckid.pce_id,
225 &pckid.cpu_svn,
226 pckid.pce_isvsvn,
227 Some(&pckid.qe_id),
228 )
229 .unwrap();
230
231 let pck = pck.verify(&root_cas, None).unwrap();
232 assert_eq!(
233 test_helpers::get_cert_subject(&pck.ca_chain().last().unwrap()),
234 "Intel SGX Root CA"
235 );
236 assert_eq!(
237 test_helpers::get_cert_subject(&pck.pck_pem()),
238 "Intel SGX PCK Certificate"
239 );
240
241 let fmspc = pck.fmspc().unwrap();
242 assert!(client.tcbinfo(&fmspc, None).is_ok());
243 }
244 }
245 }
246
247 #[test]
248 pub fn pck_crl() {
249 for api_version in [PcsVersion::V3, PcsVersion::V4] {
250 let client = AzureProvisioningClientBuilder::new(api_version)
251 .set_retry_timeout(TIME_RETRY_TIMEOUT)
252 .build(reqwest_client());
253 assert!(client.pckcrl(DcapArtifactIssuer::PCKProcessorCA).is_ok());
254 assert!(client.pckcrl(DcapArtifactIssuer::PCKPlatformCA).is_ok());
255 }
256 }
257
258 #[test]
259 pub fn qe_identity() {
260 for api_version in [PcsVersion::V3, PcsVersion::V4] {
261 let client = AzureProvisioningClientBuilder::new(api_version)
262 .set_retry_timeout(TIME_RETRY_TIMEOUT)
263 .build(reqwest_client());
264 assert!(client.qe_identity(None).is_ok());
265 }
266 }
267
268 #[test]
269 pub fn test_pckcerts_with_fallback() {
270 for api_version in [PcsVersion::V3, PcsVersion::V4] {
271 let client = AzureProvisioningClientBuilder::new(api_version)
272 .set_retry_timeout(TIME_RETRY_TIMEOUT)
273 .build(reqwest_client());
274
275 for pckid in PckID::parse_file(&PathBuf::from(PCKID_TEST_FILE).as_path())
276 .unwrap()
277 .iter()
278 {
279 let pckcerts = client.pckcerts_with_fallback(&pckid).unwrap();
280 println!("Found {} PCK certs.", pckcerts.as_pck_certs().len());
281
282 let tcb_info = client.tcbinfo(&pckcerts.fmspc().unwrap(), None).unwrap();
283 let tcb_data = tcb_info.data().unwrap();
284
285 let selected = pckcerts.select_pck(
286 &tcb_data,
287 &pckid.cpu_svn,
288 pckid.pce_isvsvn,
289 pckid.pce_id,
290 ).unwrap();
291
292 let pck = client.pckcert(
293 Some(&pckid.enc_ppid),
294 &pckid.pce_id,
295 &pckid.cpu_svn,
296 pckid.pce_isvsvn,
297 Some(&pckid.qe_id),
298 ).unwrap();
299
300 assert_eq!(format!("{:?}", selected.sgx_extension().unwrap()),
301 format!("{:?}", pck.sgx_extension().unwrap()));
302 }
303 }
304 }
305
306 #[test]
307 pub fn tcb_evaluation_data_numbers() {
308 for api_version in [PcsVersion::V3, PcsVersion::V4] {
309 let client = AzureProvisioningClientBuilder::new(api_version)
310 .set_retry_timeout(TIME_RETRY_TIMEOUT)
311 .build(reqwest_client());
312 assert!(client.tcb_evaluation_data_numbers().is_ok());
313 }
314 }
315}