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]

Reply via email to