pcs/
pckcrl.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
8use std::path::PathBuf;
9
10use pkix::pem::PEM_CRL;
11use serde::{Deserialize, Deserializer, Serialize};
12use std::marker::PhantomData;
13#[cfg(feature = "verify")]
14use {
15    mbedtls::alloc::List as MbedtlsList,
16    mbedtls::x509::{Certificate, Crl},
17    std::convert::TryFrom,
18    std::ffi::CString,
19    std::ops::Deref,
20};
21
22use crate::io::{self};
23use crate::{DcapArtifactIssuer, Error, Unverified, VerificationType, Verified};
24
25#[derive(Clone, Serialize, Debug, Eq, PartialEq)]
26pub struct PckCrl<V: VerificationType = Verified> {
27    crl: String,
28    ca_chain: Vec<String>,
29    #[serde(skip)]
30    type_: PhantomData<V>,
31}
32
33impl<'de> Deserialize<'de> for PckCrl<Unverified> {
34    fn deserialize<D>(deserializer: D) -> Result<PckCrl<Unverified>, D::Error>
35    where
36        D: Deserializer<'de>,
37    {
38        #[derive(Deserialize)]
39        struct Dummy {
40            crl: String,
41            ca_chain: Vec<String>,
42        }
43
44        let Dummy { crl, ca_chain } = Dummy::deserialize(deserializer)?;
45        Ok(PckCrl::<Unverified> {
46            crl,
47            ca_chain,
48            type_: PhantomData,
49        })
50    }
51}
52
53impl PckCrl<Unverified> {
54    pub fn new(crl: String, ca_chain: Vec<String>) -> Result<PckCrl<Unverified>, Error> {
55        let crl = PckCrl { crl, ca_chain, type_: PhantomData };
56
57        Ok(crl)
58    }
59
60    #[cfg(feature = "verify")]
61    pub fn verify<B: Deref<Target = [u8]>>(self, trusted_root_certs: &[B]) -> Result<PckCrl<Verified>, Error> {
62        // Check if ca_chain is a valid chain
63        let (chain, root) = crate::create_cert_chain(&self.ca_chain)?;
64        let chain: MbedtlsList<Certificate> = chain.into_iter().collect();
65        let root = std::iter::once(root).collect();
66        let mut err = String::default();
67        Certificate::verify(&chain, &root, None, Some(&mut err))
68            .map_err(|e| Error::InvalidCrl(e))?;
69
70        // Check if the root in self.ca_chain is in `trusted_root_certs`
71        crate::check_root_ca(trusted_root_certs, &root)?;
72
73        // Check if first entry in CA list signed CRL
74        let crl = self.as_mbedtls_crl()?;
75        let crl_signature = crl.signature()
76            .map_err(|e| Error::InvalidCrl(e))?;
77        let crl_tbs = crl.tbs_raw()
78            .map_err(|e| Error::InvalidCrl(e))?;
79        let mut hash = [0u8; 32];
80        mbedtls::hash::Md::hash(mbedtls::hash::Type::Sha256, &crl_tbs, &mut hash).unwrap();
81
82        let pck_ca = self.ca_chain.first()
83            .ok_or(Error::InvalidPck("Pck CRL doesn't have a CA".into()))?;
84        let pck_ca = CString::new(pck_ca.as_bytes()).map_err(|_| Error::InvalidCaFormat)?;
85        let mut pck_ca = Certificate::from_pem(pck_ca.as_bytes_with_nul()).map_err(|_| Error::InvalidCaFormat)?;
86        let pck_ca = pck_ca.public_key_mut();
87        pck_ca
88            .verify(mbedtls::hash::Type::Sha256, &hash, &crl_signature)
89            .map_err(|e| Error::InvalidCrl(e))?;
90
91        // Sanity check on Pck CRL
92        self.ca()?;
93
94        let PckCrl { crl, ca_chain, .. } = self;
95        Ok(PckCrl::<Verified>{ crl, ca_chain, type_: PhantomData})
96    }
97
98    pub fn read_from_file(input_dir: &str, ca: DcapArtifactIssuer) -> Result<Self, Error> {
99        let filename = Self::filename_from_ca(ca);
100        let crl: Self = io::read_from_file(input_dir, &filename)?;
101        Ok(crl)
102    }
103}
104
105impl<V: VerificationType> PckCrl<V> {
106    fn filename_from_ca(ca: DcapArtifactIssuer) -> String {
107        match ca {
108            DcapArtifactIssuer::PCKProcessorCA => String::from("processor.crl"),
109            DcapArtifactIssuer::PCKPlatformCA => String::from("platform.crl"),
110            DcapArtifactIssuer::SGXRootCA => String::from("root.crl"),
111        }
112    }
113
114    #[cfg(feature = "verify")]
115    pub fn filename(&self) -> Result<String, Error> {
116        Ok(Self::filename_from_ca(self.ca()?))
117    }
118
119    #[cfg(feature = "verify")]
120    pub fn write_to_file(&self, output_dir: &str) -> Result<String, Error> {
121        let filename = self.filename()?;
122        io::write_to_file(&self, output_dir, &filename)?;
123        Ok(filename)
124    }
125
126    pub fn write_to_file_as(&self, output_dir: &str, ca: DcapArtifactIssuer) -> Result<String, Error> {
127        let filename = Self::filename_from_ca(ca);
128        io::write_to_file(&self, output_dir, &filename)?;
129        Ok(filename)
130    }
131
132    #[cfg(feature = "verify")]
133    pub fn write_to_file_if_not_exist(&self, output_dir: &str) -> Result<Option<PathBuf>, Error> {
134        let filename = self.filename()?;
135        io::write_to_file_if_not_exist(&self, output_dir, &filename)
136    }
137
138    pub fn write_to_file_if_not_exist_as(&self, output_dir: &str, ca: DcapArtifactIssuer) -> Result<Option<PathBuf>, Error> {
139        let filename = Self::filename_from_ca(ca);
140        io::write_to_file_if_not_exist(&self, output_dir, &filename)
141    }
142
143    pub fn crl_as_pem(&self) -> &String {
144        &self.crl
145    }
146
147    pub fn crl_as_der(&self) -> Result<Vec<u8>, Error> {
148        pkix::pem::pem_to_der(&self.crl, Some(PEM_CRL)).ok_or(Error::InvalidCrlFormat)
149    }
150
151    pub fn certificate_chain(&self) -> &Vec<String> {
152        &self.ca_chain
153    }
154
155    #[cfg(feature = "verify")]
156    pub(crate) fn as_mbedtls_crl(&self) -> Result<Crl, Error> {
157        let c = CString::new(self.crl.as_bytes()).map_err(|_| Error::InvalidCrlFormat)?;
158        let mut crl = Crl::new();
159        crl.push_from_pem(c.as_bytes_with_nul()).map_err(|_| Error::InvalidCrlFormat)?;
160        Ok(crl)
161    }
162
163    #[cfg(feature = "verify")]
164    fn ca(&self) -> Result<DcapArtifactIssuer, Error> {
165        let issuer = self
166            .as_mbedtls_crl()
167            .and_then(|crl| crl.issuer().map_err(|_| Error::InvalidCrlFormat))?;
168        let issuer = DcapArtifactIssuer::try_from(issuer.as_str())?;
169
170        if let DcapArtifactIssuer::SGXRootCA = issuer {
171            // PCK Crls should be signed by the PCKPlatformCA or PCKProcessorCA
172            return Err(Error::InvalidCrlFormat);
173        }
174        Ok(issuer)
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    #[cfg(all(not(target_env = "sgx"), feature = "verify"))]
181    use {
182        super::PckCrl,
183        crate::DcapArtifactIssuer,
184    };
185
186    #[cfg(all(not(target_env = "sgx"), feature = "verify"))]
187    #[test]
188    fn read_pck_crl() {
189        let crl = PckCrl::read_from_file("./tests/data/", DcapArtifactIssuer::PCKProcessorCA).unwrap();
190        assert_eq!(crl.ca().unwrap(), DcapArtifactIssuer::PCKProcessorCA);
191        let root_ca = include_bytes!("../tests/data/root_SGX_CA_der.cert");
192        let root_cas = [&root_ca[..]];
193        crl.verify(&root_cas).unwrap();
194    }
195
196    #[cfg(all(not(target_env = "sgx"), feature = "verify"))]
197    #[test]
198    fn read_platform_pck_crl() {
199        let pckcrl = PckCrl::read_from_file("./tests/data/", DcapArtifactIssuer::PCKPlatformCA).unwrap();
200        let root_ca = include_bytes!("../tests/data/root_SGX_CA_der.cert");
201        let root_cas = [&root_ca[..]];
202        pckcrl.clone().verify(&root_cas).unwrap();
203        assert_eq!(pckcrl.ca().unwrap(), DcapArtifactIssuer::PCKPlatformCA);
204    }
205}