pcs/
lib.rs

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#![deny(warnings)]
9
10extern crate failure;
11extern crate percent_encoding;
12extern crate yasna;
13#[macro_use]
14extern crate quick_error;
15
16use std::convert::TryFrom;
17use std::fmt::{self, Display, Formatter};
18
19use serde::de::{self};
20use serde::{Deserialize, Deserializer, Serialize};
21pub use yasna::ASN1Error;
22#[cfg(feature = "verify")]
23use {
24    mbedtls::Error as MbedError,
25    mbedtls::alloc::{Box as MbedtlsBox, List as MbedtlsList},
26    mbedtls::x509::certificate::Certificate,
27    std::ffi::CString,
28    std::ops::Deref,
29};
30
31pub use crate::pckcrl::PckCrl;
32pub use crate::pckcrt::{PckCert, PckCerts, SGXPCKCertificateExtension, SGXType, TcbComponent};
33pub use crate::qe_identity::{EnclaveIdentity, QeIdentity, QeIdentitySigned};
34pub use crate::tcb_info::{AdvisoryID, Fmspc, TcbInfo, TcbData, TcbLevel};
35pub use crate::tcb_evaluation_data_numbers::{RawTcbEvaluationDataNumbers, TcbEvalNumber, TcbEvaluationDataNumbers, TcbPolicy};
36
37mod io;
38mod iso8601;
39mod pckcrl;
40mod pckcrt;
41mod pckid;
42mod qe_identity;
43mod tcb_info;
44mod tcb_evaluation_data_numbers;
45
46pub type CpuSvn = [u8; 16];
47pub type EncPpid = Vec<u8>;
48pub type PceId = u16;
49pub type PceIsvsvn = u16;
50pub type QeId = [u8; 16];
51pub use crate::pckid::PckID;
52
53#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
54pub enum Platform {
55    SGX,
56    TDX,
57}
58
59impl Display for Platform {
60    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
61        match self {
62            Platform::SGX => write!(f, "SGX"),
63            Platform::TDX => write!(f, "TDX"),
64        }
65    }
66}
67
68fn sgx_platform() -> Platform {
69    Platform::SGX
70}
71
72
73quick_error! {
74    #[derive(Debug)]
75    pub enum Error {
76        MissingCaChain{
77            display("CA chain was unexpectedly empty")
78        }
79        IncorrectCA {
80            display("Invalid CA")
81        }
82        InvalidCaFormat {
83            display("CA certificate could not be parsed")
84        }
85        InvalidPckFormat(err: ASN1Error){
86            display("Invalid formatted PckCert: {}", err)
87        }
88        InvalidPck(err: String){
89            display("Invalid PCK: {}", err)
90        }
91        InvalidPcks(err: String){
92            display("Invalid PCKs: {}", err)
93        }
94        InvalidFormatQe3Quote{
95            display("Qe3 Quote could not be parsed")
96        }
97        NoPckForTcbFound{
98            display("No PCK matching the TCB was found")
99        }
100        #[cfg(feature = "verify")]
101        InvalidCrl(err: MbedError){
102            display("Invalid CRL: {}", err)
103        }
104        InvalidCrlFormat{
105            display("Invalid CRL format")
106        }
107        InvalidTcbInfo(err: String){
108            display("Invalid TCB info: {}", err)
109        }
110        InvalidTcbEvaluationDataNumbers(err: String){
111            display("Invalid TCB Evaluation Data Numbers: {}", err)
112        }
113        #[cfg(feature = "verify")]
114        UntrustworthyTcbEvaluationDataNumber(err: MbedError) {
115            display("TCB Evaluation Data Number not trustworthy: {}", err)
116        }
117        UnknownTcbType(tcb_type: u16){
118            display("Unknown TCB type: {}", tcb_type)
119        }
120        #[cfg(feature = "verify")]
121        InvalidQe3Id(err: MbedError){
122            display("Invalid QE3 ID: {}", err)
123        }
124        Qe3NotValid(err: String){
125            display("Invalid QE3: {}", err)
126        }
127        InvalidFormatQe3Identity{
128            display("Invalid QE3 Identity format")
129        }
130        IoError(err: std::io::Error){
131            display("I/O error: {}", err)
132            from()
133        }
134        ParseError(err: serde_json::error::Error){
135            from()
136            display("json error: {}", err)
137        }
138        NoPckCertData{
139            display("Empty PckCerts")
140        }
141        EncodingError(err: serde_json::error::Error){
142            display("json error: {}", err)
143        }
144        UnknownTcbInfoVersion(version: u16){
145            display("The TCB Info structure has unexpected version: {}", version)
146        }
147        UntrustedTcbInfoVersion(curr_version: u16, min_version: u16) {
148            display("The TCB Info structure has version {curr_version}, while at least {min_version} is required")
149        }
150        EnclaveTcbLevelNotFound {
151            display("TCB level not found for enclave")
152        }
153        UnknownQeIdentityVersion(version: u16){
154            display("The QEIdentity structure has unexpected version: {}", version)
155        }
156        InvalidDcapAttestationFormat{
157            display("The DCAP Attestation certificate has an unexpected format")
158        }
159    }
160}
161
162#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
163pub enum DcapArtifactIssuer {
164    PCKPlatformCA,
165    PCKProcessorCA,
166    SGXRootCA,
167}
168
169impl TryFrom<&str> for DcapArtifactIssuer {
170    type Error = Error;
171
172    fn try_from(value: &str) -> Result<Self, Self::Error> {
173        if value.contains("Intel SGX PCK Platform CA") {
174            return Ok(DcapArtifactIssuer::PCKPlatformCA);
175        }
176
177        if value.contains("Intel SGX PCK Processor CA") {
178            return Ok(DcapArtifactIssuer::PCKProcessorCA);
179        }
180
181        if value.contains("Intel SGX Root CA") {
182            return Ok(DcapArtifactIssuer::SGXRootCA);
183        }
184
185        Err(Error::InvalidCaFormat)
186    }
187}
188
189pub trait VerificationType {}
190
191#[derive(Clone, Debug, Eq, PartialEq)]
192pub struct Verified;
193
194impl VerificationType for Verified {}
195
196#[derive(Clone, Debug, Eq, PartialEq)]
197pub struct Unverified;
198
199impl VerificationType for Unverified {}
200
201/// Intel specifies raw ECDSA signatures in a different format than mbedtls. Convert ECDSA
202/// signature to RFC5480 ASN.1 representation.
203fn get_ecdsa_sig_der(sig: &[u8]) -> Result<Vec<u8>, ()> {
204    if sig.len() % 2 != 0 {
205        return Err(());
206    }
207
208    let (r_bytes, s_bytes) = sig.split_at(sig.len() / 2);
209    let r = num::BigUint::from_bytes_be(r_bytes);
210    let s = num::BigUint::from_bytes_be(s_bytes);
211
212    let der = yasna::construct_der(|writer| {
213        writer.write_sequence(|writer| {
214            writer.next().write_biguint(&r);
215            writer.next().write_biguint(&s);
216        })
217    });
218
219    Ok(der)
220}
221
222fn intel_signature_deserializer<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
223    let signature = String::deserialize(deserializer)?;
224    let signature = &base16::decode(signature.as_bytes()).map_err(de::Error::custom)?;
225    crate::get_ecdsa_sig_der(signature).map_err(|_| de::Error::custom("Failed ECDSA signature conversion"))
226}
227
228#[cfg(feature = "verify")]
229fn create_cert_chain(certs: &Vec<String>) -> Result<(Vec<MbedtlsBox<Certificate>>, MbedtlsBox<Certificate>), Error> {
230    fn str_to_cert_box(ca: &String) -> Result<MbedtlsBox<Certificate>, Error> {
231        let ca = CString::new(ca.as_bytes()).map_err(|_| Error::InvalidCaFormat)?;
232        Certificate::from_pem(ca.as_bytes_with_nul()).map_err(|_| Error::InvalidCaFormat)
233    }
234    if let Some((last_cert, certs)) = certs.split_last() {
235        let chain = certs.iter().map(str_to_cert_box).collect::<Result<Vec<_>, _>>()?;
236        let last_cert = str_to_cert_box(last_cert)?;
237        Ok((chain, last_cert))
238    } else {
239        Err(Error::MissingCaChain)
240    }
241}
242
243// Typically, certificates are verified directly against a pool of trusted root
244// certificates. The DCAP attestation verification logic works differently.
245// It first verifies against a root certificate included in the attestation,
246// and then checks that the root certificate included in the attestation is
247// a trusted root certificate.
248//
249// There are two different versions of the SGX root CA in circulation (both
250// available in tests/data/ of this crate). They share the same key, but
251// have a different expiration date and a different CRL reference (PEM vs. DER
252// format). Because we have existing DCAP verifiers configured with only one
253// of the certificates, we perform a certificate verification of the root
254// in the attestation against the trusted root, rather than look for a
255// byte-for-byte match between the attestation root and the trusted root.
256#[cfg(feature = "verify")]
257fn check_root_ca<B: Deref<Target = [u8]>>(trusted_root_certs: &[B], candidate: &MbedtlsList<Certificate>) -> Result<(), Error> {
258    if trusted_root_certs
259        .iter()
260        .filter_map(|trusted_der| Certificate::from_der(&**trusted_der).ok())
261        .any(|trusted| Certificate::verify(candidate, &std::iter::once(trusted).collect(), None, None).is_ok())
262    {
263        return Ok(());
264    } else {
265        return Err(Error::IncorrectCA);
266    }
267}
268
269#[cfg(test)]
270#[cfg(not(target_env = "sgx"))]
271fn get_cert_subject(cert: &str) -> String {
272    let der = &pkix::pem::pem_to_der(cert.trim(), Some(pkix::pem::PEM_CERTIFICATE))
273        .ok_or(ASN1Error::new(yasna::ASN1ErrorKind::Invalid))
274        .unwrap();
275    get_cert_subject_from_der(der)
276}
277
278#[cfg(test)]
279#[cfg(not(target_env = "sgx"))]
280fn get_cert_subject_from_der(cert: &Vec<u8>) -> String {
281    use pkix::FromBer;
282    let cert = pkix::x509::GenericCertificate::from_ber(&cert).unwrap();
283    let name = cert.tbscert.subject.get(&*pkix::oid::commonName).unwrap();
284    String::from_utf8_lossy(&name.value()).to_string()
285}
286
287#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Copy)]
288pub enum TcbStatus {
289    UpToDate,
290    SWHardeningNeeded,
291    ConfigurationNeeded,
292    ConfigurationAndSWHardeningNeeded,
293    OutOfDate,
294    OutOfDateConfigurationNeeded,
295    Revoked,
296}
297
298impl TcbStatus {
299    pub(crate) fn drop_sw_hardening_needed(self) -> Self {
300        match self {
301            Self::SWHardeningNeeded => Self::UpToDate,
302            Self::ConfigurationAndSWHardeningNeeded => Self::ConfigurationNeeded,
303            v => v,
304        }
305    }
306}
307
308impl fmt::Display for TcbStatus {
309    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310        match self {
311            TcbStatus::UpToDate => write!(f, "Up to Date"),
312            TcbStatus::SWHardeningNeeded => write!(f, "Software Hardening Needed"),
313            TcbStatus::ConfigurationNeeded => write!(f, "Configuration Needed"),
314            TcbStatus::ConfigurationAndSWHardeningNeeded => write!(f, "Configuration And Software Hardening Needed"),
315            TcbStatus::OutOfDate => write!(f, "Out of Date"),
316            TcbStatus::OutOfDateConfigurationNeeded => write!(f, "Out of Date, Configuration Needed"),
317            TcbStatus::Revoked => write!(f, "Revoked"),
318        }
319    }
320}