Skip to main content

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