| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| //! This module provides types used to verify attestation reports. |
| |
| use crate::report::AttestationReport; |
| |
| use std::vec::Vec; |
| |
| use log::{debug, error}; |
| use teaclave_types::EnclaveAttr; |
| |
| /// User defined verification function to further verify the attestation report. |
| pub type AttestationReportVerificationFn = fn(&AttestationReport) -> bool; |
| |
| /// Type used to verify attestation reports (this can be set as a certificate |
| /// verifier in `rustls::ClientConfig`). |
| #[derive(Clone)] |
| pub struct AttestationReportVerifier { |
| /// Valid enclave attributes (only enclaves with attributes in this vector |
| /// will be accepted). |
| pub accepted_enclave_attrs: Vec<EnclaveAttr>, |
| /// Root certificate of the attestation service provider (e.g., IAS). |
| pub root_ca: Vec<u8>, |
| /// User defined function to verify the attestation report. |
| pub verifier: AttestationReportVerificationFn, |
| } |
| |
| /// Checks if he quote's status is not `UnknownBadStatus` |
| pub fn universal_quote_verifier(report: &AttestationReport) -> bool { |
| debug!("report.sgx_quote_status: {:?}", report.sgx_quote_status); |
| report.sgx_quote_status != crate::report::SgxQuoteStatus::UnknownBadStatus |
| } |
| |
| impl AttestationReportVerifier { |
| pub fn new( |
| accepted_enclave_attrs: Vec<EnclaveAttr>, |
| root_ca: &[u8], |
| verifier: AttestationReportVerificationFn, |
| ) -> Self { |
| Self { |
| accepted_enclave_attrs, |
| root_ca: root_ca.to_vec(), |
| verifier, |
| } |
| } |
| |
| /// Verify whether the `MR_SIGNER` and `MR_ENCLAVE` in the attestation report is |
| /// accepted by us, which are defined in `accepted_enclave_attrs`. |
| fn verify_measures(&self, attestation_report: &AttestationReport) -> bool { |
| debug!("verify measures"); |
| let this_mr_signer = attestation_report |
| .sgx_quote_body |
| .isv_enclave_report |
| .mr_signer; |
| let this_mr_enclave = attestation_report |
| .sgx_quote_body |
| .isv_enclave_report |
| .mr_enclave; |
| |
| self.accepted_enclave_attrs.iter().any(|a| { |
| a.measurement.mr_signer == this_mr_signer && a.measurement.mr_enclave == this_mr_enclave |
| }) |
| } |
| |
| /// Verify TLS certificate. |
| fn verify_cert(&self, cert_der: &[u8]) -> bool { |
| debug!("verify cert"); |
| if cfg!(sgx_sim) { |
| return true; |
| } |
| |
| let report = match AttestationReport::from_cert(&cert_der, &self.root_ca) { |
| Ok(report) => report, |
| Err(e) => { |
| error!("cert verification error {:?}", e); |
| return false; |
| } |
| }; |
| |
| // Enclave measures are not tested in test mode since we have |
| // a dedicated test enclave not known to production enclaves |
| if cfg!(test_mode) { |
| return (self.verifier)(&report); |
| } |
| |
| self.verify_measures(&report) && (self.verifier)(&report) |
| } |
| } |
| |
| impl rustls::ServerCertVerifier for AttestationReportVerifier { |
| fn verify_server_cert( |
| &self, |
| _roots: &rustls::RootCertStore, |
| certs: &[rustls::Certificate], |
| _hostname: webpki::DNSNameRef, |
| _ocsp: &[u8], |
| ) -> std::result::Result<rustls::ServerCertVerified, rustls::TLSError> { |
| // This call automatically verifies certificate signature |
| debug!("verify server cert"); |
| if certs.len() != 1 { |
| return Err(rustls::TLSError::NoCertificatesPresented); |
| } |
| if self.verify_cert(&certs[0].0) { |
| Ok(rustls::ServerCertVerified::assertion()) |
| } else { |
| Err(rustls::TLSError::WebPKIError( |
| webpki::Error::ExtensionValueInvalid, |
| )) |
| } |
| } |
| } |
| |
| impl rustls::ClientCertVerifier for AttestationReportVerifier { |
| fn offer_client_auth(&self) -> bool { |
| // If test_mode is on, then disable TLS client authentication. |
| !cfg!(test_mode) |
| } |
| |
| fn client_auth_root_subjects(&self) -> rustls::DistinguishedNames { |
| rustls::DistinguishedNames::new() |
| } |
| |
| fn verify_client_cert( |
| &self, |
| certs: &[rustls::Certificate], |
| ) -> std::result::Result<rustls::ClientCertVerified, rustls::TLSError> { |
| // This call automatically verifies certificate signature |
| debug!("verify client cert"); |
| if certs.len() != 1 { |
| return Err(rustls::TLSError::NoCertificatesPresented); |
| } |
| if self.verify_cert(&certs[0].0) { |
| Ok(rustls::ClientCertVerified::assertion()) |
| } else { |
| Err(rustls::TLSError::WebPKIError( |
| webpki::Error::ExtensionValueInvalid, |
| )) |
| } |
| } |
| } |