This is an automated email from the ASF dual-hosted git repository. mssun pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-teaclave.git
commit f53bc8f818ecb2711ee0cda2124449c3a47161e6 Author: Mingshen Sun <[email protected]> AuthorDate: Thu Nov 5 21:40:49 2020 -0800 [cli] Add the attest subcommand to display attestation report from the remote Teaclave service --- attestation/src/report.rs | 68 ++++++++++++++++++++++++++++++++++++- cli/Cargo.toml | 6 ++++ cli/README.md | 27 +++++++++++++++ cli/src/main.rs | 85 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 185 insertions(+), 1 deletion(-) diff --git a/attestation/src/report.rs b/attestation/src/report.rs index 5b8d758..095a59e 100644 --- a/attestation/src/report.rs +++ b/attestation/src/report.rs @@ -26,6 +26,7 @@ use crate::AttestationError; use crate::EndorsedAttestationReport; use std::convert::TryFrom; +use std::fmt; use std::time::*; #[cfg(feature = "mesalock_sgx")] use std::untrusted::time::SystemTimeEx; @@ -97,6 +98,35 @@ impl std::fmt::Debug for SgxEnclaveReport { } } +impl fmt::Display for SgxEnclaveReport { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "CPU version (hex): {}", hex::encode(self.cpu_svn))?; + writeln!(f, "SSA Frame extended feature set: {}", self.misc_select)?; + writeln!( + f, + "Attributes of the enclave (hex): {}", + hex::encode(self.attributes) + )?; + writeln!( + f, + "Enclave measurement (hex): {}", + hex::encode(self.mr_enclave) + )?; + writeln!( + f, + "Hash of the enclave singing key (hex): {}", + hex::encode(self.mr_signer) + )?; + writeln!(f, "Enclave product ID: {}", self.isv_prod_id)?; + writeln!(f, "Security version of the enclave: {}", self.isv_svn)?; + writeln!( + f, + "The value of REPORT (hex): {}", + hex::encode(&self.report_data.to_vec()) + ) + } +} + impl SgxEnclaveReport { /// Parse bytes of report into `SgxEnclaveReport`. pub fn parse_from<'a>(bytes: &'a [u8]) -> Result<Self> { @@ -196,6 +226,18 @@ pub enum SgxEcdsaQuoteAkType { P384_384, } +impl std::fmt::Display for SgxQuoteVersion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SgxQuoteVersion::V1(key_type) => write!(f, "Version 1, EPID {:?} signature", key_type), + SgxQuoteVersion::V2(key_type) => write!(f, "Version 2, EPID {:?} signature", key_type), + SgxQuoteVersion::V3(key_type) => { + write!(f, "Version 3, ECDSA {:?} attestation key", key_type) + } + } + } +} + /// SGX Quote status #[derive(PartialEq, Debug)] pub enum SgxQuoteStatus { @@ -336,7 +378,23 @@ impl std::fmt::Debug for SgxQuote { writeln!(f, "isv_svn_pce: {}", self.isv_svn_pce)?; writeln!(f, "qe_vendor_id: {}", self.qe_vendor_id)?; writeln!(f, "user_data: {:?}", &self.user_data)?; - writeln!(f, "isv_enclave_report: \n{:?}", self.isv_enclave_report) + write!(f, "isv_enclave_report: \n{:?}", self.isv_enclave_report) + } +} + +impl fmt::Display for SgxQuote { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "Version and signature/key type: {}", self.version)?; + writeln!(f, "GID or reserved: {}", self.gid)?; + writeln!(f, "Security version of the QE: {}", self.isv_svn_qe)?; + writeln!(f, "Security version of the PCE: {}", self.isv_svn_pce)?; + writeln!(f, "ID of the QE vendor: {}", self.qe_vendor_id)?; + writeln!( + f, + "Custom user-defined data (hex): {}", + hex::encode(&self.user_data) + )?; + write!(f, "{}", self.isv_enclave_report) } } @@ -432,6 +490,14 @@ pub struct AttestationReport { pub sgx_quote_body: SgxQuote, } +impl fmt::Display for AttestationReport { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "Report Freshness: {:?}", self.freshness)?; + writeln!(f, "SGX Quote status: {:?}", self.sgx_quote_status)?; + write!(f, "{}", self.sgx_quote_body) + } +} + impl AttestationReport { /// Construct a AttestationReport from a X509 certificate and verify /// attestation report with the report_ca_cert which is from the attestation diff --git a/cli/Cargo.toml b/cli/Cargo.toml index afd8757..24d2d5f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -12,4 +12,10 @@ structopt = "0.3" teaclave_crypto = { path = "../crypto" } hex = { version = "0.4.0" } teaclave_types = { path = "../types" } +teaclave_attestation = { path = "../attestation" } +env_logger = { version = "0.7.1" } +webpki-roots = { version = "0.19.0" } +webpki = { version = "0.21.0" } +rustls = { version = "0.16.0", features = ["dangerous_configuration"] } +http = { version = "0.2" } pem = "0.7.0" diff --git a/cli/README.md b/cli/README.md index 90e9d76..c5b835b 100644 --- a/cli/README.md +++ b/cli/README.md @@ -14,6 +14,9 @@ interactive with the platform. The command line tool has several sub-commands: and `MRENCLAVE`) signed by auditors with their public keys. The enclave info is used for remote attestation, Please verify it before connecting the platform with the client SDK. +- `attest`: Establish an attested TLS with one of the Teaclave services and get + an attestation report, validate it with attestation service's cert and display + the report details. ## Encrypt/Decrypt @@ -46,3 +49,27 @@ $ ./teaclave_cli verify \ --signatures $(find ../examples -name "*.sign.sha256") Verify successfully. ``` + +## Attest + +Here is an example to display the attestation report from a Teaclave service. + +``` +$ ./teaclave_cli attest --address accvm-dev:7776 --as-ca-cert ../../keys/ias_root_ca_cert.pem +Report Freshness: 1854s +SGX Quote status: SwHardeningNeeded +Version and signature/key type: Version 2, EPID Linkable signature +GID or reserved: 3014 +Security version of the QE: 11 +Security version of the PCE: 10 +ID of the QE vendor: 00000000-XXXX-XXXX-XXXX-XXXXXXXXXXXX +Custom user-defined data (hex): 75b6024c00000000000000000000000000000000 +CPU version (hex): 0f0f0305ff8006000000000000000000 +SSA Frame extended feature set: 0 +Attributes of the enclave (hex): 07000000000000000700000000000000 +Enclave measurement (hex): eadeb5537962d2451a8619fb6a4b10b72f56479e0b7db0bb9c3f5edc143ca6eb +Hash of the enclave singing key (hex): 83d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e +Enclave product ID: 0 +Security version of the enclave: 0 +The value of REPORT (hex): 317cb5c0d9a26747a08833e51bac8ca2ce814aa362c8cd0e2672fdcb6bfee77b9ba32ed7d605778aa52b9f2d2ce698f83ec49e6beecb89c684d861bb078d7dc2 +``` diff --git a/cli/src/main.rs b/cli/src/main.rs index 6d12190..de9a71e 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -15,11 +15,16 @@ // specific language governing permissions and limitations // under the License. +use anyhow::anyhow; use anyhow::bail; use anyhow::Result; +use http::Uri; use std::fs; +use std::io::Write; use std::path::PathBuf; +use std::sync::Arc; use structopt::StructOpt; +use teaclave_attestation::report::AttestationReport; use teaclave_crypto::{AesGcm128Key, AesGcm256Key, TeaclaveFile128Key}; @@ -75,6 +80,17 @@ struct VerifyOpt { } #[derive(Debug, StructOpt)] +struct AttestOpt { + /// Address of the remote service + #[structopt(short, long)] + address: String, + + /// CA cert of attestation service for verifying the attestation report + #[structopt(short = "c", long)] + as_ca_cert: PathBuf, +} + +#[derive(Debug, StructOpt)] enum Command { /// Encrypt file #[structopt(name = "encrypt")] @@ -87,6 +103,9 @@ enum Command { /// Verify signatures of enclave info with auditors' public keys #[structopt(name = "verify")] Verify(VerifyOpt), + + #[structopt(name = "attest")] + Attest(AttestOpt), } #[derive(Debug, StructOpt)] @@ -181,7 +200,72 @@ fn verify(opt: VerifyOpt) -> Result<bool> { )) } +struct TeaclaveServerCertVerifier { + pub root_ca: Vec<u8>, +} + +impl TeaclaveServerCertVerifier { + pub fn new(root_ca: &[u8]) -> Self { + Self { + root_ca: root_ca.to_vec(), + } + } + + fn display_attestation_report(&self, cert_der: &[u8]) -> bool { + match AttestationReport::from_cert(&cert_der, &self.root_ca) { + Ok(report) => println!("{}", report), + Err(e) => println!("{:?}", e), + } + true + } +} + +impl rustls::ServerCertVerifier for TeaclaveServerCertVerifier { + 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 + if certs.len() != 1 { + return Err(rustls::TLSError::NoCertificatesPresented); + } + if self.display_attestation_report(&certs[0].0) { + Ok(rustls::ServerCertVerified::assertion()) + } else { + Err(rustls::TLSError::WebPKIError( + webpki::Error::ExtensionValueInvalid, + )) + } + } +} + +fn attest(opt: AttestOpt) -> Result<()> { + let uri = opt.address.parse::<Uri>()?; + let hostname = uri.host().ok_or_else(|| anyhow!("Invalid hostname."))?; + let mut stream = std::net::TcpStream::connect(opt.address)?; + let hostname = webpki::DNSNameRef::try_from_ascii_str(hostname)?; + let content = fs::read(opt.as_ca_cert)?; + let pem = pem::parse(content)?; + let verifier = Arc::new(TeaclaveServerCertVerifier::new(&pem.contents)); + let mut config = rustls::ClientConfig::new(); + config.dangerous().set_certificate_verifier(verifier); + config.versions.clear(); + config.enable_sni = false; + config.versions.push(rustls::ProtocolVersion::TLSv1_2); + let rc_config = Arc::new(config); + + let mut session = rustls::ClientSession::new(&rc_config, hostname); + let mut tls_stream = rustls::Stream::new(&mut session, &mut stream); + tls_stream.write(&[0]).unwrap(); + + Ok(()) +} + fn main() -> Result<()> { + env_logger::init(); let args = Opt::from_args(); match args.command { Command::Decrypt(opt) => { @@ -207,6 +291,7 @@ fn main() -> Result<()> { return Ok(()); } }, + Command::Attest(opt) => attest(opt)?, }; Ok(()) --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
