diff --git a/Cargo.lock b/Cargo.lock index e885662..287908a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3295,16 +3295,17 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if", "getrandom", "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3757,7 +3758,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", - "ring 0.17.7", + "ring 0.17.8", "rustls-webpki", "sct", ] @@ -3777,7 +3778,7 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -3882,7 +3883,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] diff --git a/methods/guest/Cargo.lock b/methods/guest/Cargo.lock index fb74b5d..84d9df6 100644 --- a/methods/guest/Cargo.lock +++ b/methods/guest/Cargo.lock @@ -322,6 +322,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "asn1_der" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" + [[package]] name = "auto_impl" version = "1.1.2" @@ -460,11 +466,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "5fb8dd288a69fc53a1996d7ecfbf4a20d59065bff137ce7e56bbd620de191189" dependencies = [ - "libc", + "shlex", ] [[package]] @@ -552,9 +558,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", + "der_derive", + "flagset", "zeroize", ] +[[package]] +name = "der_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + [[package]] name = "derivative" version = "2.2.0" @@ -712,6 +731,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "flagset" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec" + [[package]] name = "fnv" version = "1.0.7" @@ -764,19 +789,26 @@ dependencies = [ "alloy-primitives", "alloy-sol-types", "anyhow", + "asn1_der", "base64", "bincode", "byteorder", "chrono", + "const-oid", + "der", "hex", "parity-scale-codec", + "pem", "pink-json", "primitive-io", + "ring", "risc0-zkvm", + "rustls-webpki", "scale-info", "serde", "serde_bytes", "thiserror", + "x509-cert", ] [[package]] @@ -901,9 +933,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libm" @@ -1008,6 +1040,16 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64", + "serde", +] + [[package]] name = "pest" version = "2.7.7" @@ -1218,6 +1260,20 @@ dependencies = [ "subtle", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "git+https://github.com/tolak/ring.git?branch=riscv64-support-0.17.8-compat#e638863691bfb150a92adf056523733f6db8251b" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys", +] + [[package]] name = "risc0-binfmt" version = "0.20.1" @@ -1417,6 +1473,23 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rustls-pki-types" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + +[[package]] +name = "rustls-webpki" +version = "0.102.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rusty-fork" version = "0.3.0" @@ -1547,6 +1620,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signature" version = "2.2.0" @@ -1557,6 +1636,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" version = "0.7.3" @@ -1766,6 +1851,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "valuable" version = "0.1.0" @@ -1877,6 +1968,17 @@ dependencies = [ "tap", ] +[[package]] +name = "x509-cert" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" +dependencies = [ + "const-oid", + "der", + "spki", +] + [[package]] name = "zerocopy" version = "0.7.32" diff --git a/methods/guest/Cargo.toml b/methods/guest/Cargo.toml index 8c2540f..7a7ea89 100644 --- a/methods/guest/Cargo.toml +++ b/methods/guest/Cargo.toml @@ -31,12 +31,21 @@ chrono = { version = "0.4.31", default-features = false, features = ["alloc", "s anyhow = { version = "1.0.79", default-features = false } thiserror = "1.0.50" -#ring = { version = "0.17.5", default-features = false, features = ["alloc"] } -#webpki = { version = "0.102.0-alpha.6", package = "rustls-webpki", default-features = false, features = ["alloc", "ring"] } -#const-oid = { version = "0.9.5", default-features = false } -#der = { version = "0.7.8", default-features = false } -#pem = { version = "3", default-features = false } -#x509-cert = { version = "0.2.4", default-features = false } +asn1_der = { version = "0.7", default-features = false, features = [ + "native_types", + "std", +] } +ring = { version = "0.17.5", default-features = false, features = ["std"] } +const-oid = { version = "0.9.5", default-features = false, features = ["std"] } +der = { version = "0.7.8", default-features = false, features = ["std"] } +pem = { version = "3", default-features = false, features = ["std"] } +x509-cert = { version = "0.2.4", default-features = false } +# Release version no-std has bug +webpki = { package = "rustls-webpki", version = "=0.102.6", default-features = false, features = ["alloc", "ring", "std"] } [profile.release] lto = "thin" + + +[patch.crates-io] +ring = { git = "https://github.com/tolak/ring.git", package = "ring", branch = "riscv64-support-0.17.8-compat" } diff --git a/methods/guest/src/dcap.rs b/methods/guest/src/dcap.rs index 4cfd995..8dc7c33 100644 --- a/methods/guest/src/dcap.rs +++ b/methods/guest/src/dcap.rs @@ -1,8 +1,7 @@ mod quote; mod tcb_info; mod constants; -// TODO: need port ring -// mod utils; +mod utils; use alloc::borrow::ToOwned; use alloc::string::{String, ToString}; @@ -13,8 +12,7 @@ use scale_info::TypeInfo; use crate::dcap::quote::{AuthData, EnclaveReport, Quote}; use crate::dcap::tcb_info::TcbInfo; use crate::dcap::constants::*; -// TODO: need port ring -// use crate::dcap::utils::*; +use crate::dcap::utils::*; use crate::Error; #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Eq, Debug)] @@ -50,7 +48,7 @@ pub fn verify( return Err(Error::TCBInfoExpired); } - let _now_in_milli = now * 1000; + let now_in_milli = now * 1000; // Verify enclave @@ -60,27 +58,26 @@ pub fn verify( // Verify integrity - // TODO: need port ring // Check TCB info cert chain and signature - // let leaf_certs = extract_certs(quote_collateral.tcb_info_issuer_chain.as_bytes())?; - // if leaf_certs.len() < 2 { - // return Err(Error::CertificateChainIsTooShort); - // } - // let leaf_cert: webpki::EndEntityCert = webpki::EndEntityCert::try_from(&leaf_certs[0]) - // .map_err(|_| Error::LeafCertificateParsingError)?; - // let intermediate_certs = &leaf_certs[1..]; - // verify_certificate_chain(&leaf_cert, intermediate_certs, now_in_milli)?; - // let asn1_signature = encode_as_der("e_collateral.tcb_info_signature)?; - // if leaf_cert - // .verify_signature( - // webpki::ECDSA_P256_SHA256, - // quote_collateral.tcb_info.as_bytes(), - // &asn1_signature, - // ) - // .is_err() - // { - // return Err(Error::RsaSignatureIsInvalid); - // } + let leaf_certs = extract_certs(quote_collateral.tcb_info_issuer_chain.as_bytes())?; + if leaf_certs.len() < 2 { + return Err(Error::CertificateChainIsTooShort); + } + let leaf_cert: webpki::EndEntityCert = webpki::EndEntityCert::try_from(&leaf_certs[0]) + .map_err(|_| Error::LeafCertificateParsingError)?; + let intermediate_certs = &leaf_certs[1..]; + verify_certificate_chain(&leaf_cert, intermediate_certs, now_in_milli)?; + let asn1_signature = encode_as_der("e_collateral.tcb_info_signature)?; + if leaf_cert + .verify_signature( + webpki::ring::ECDSA_P256_SHA256, + quote_collateral.tcb_info.as_bytes(), + &asn1_signature, + ) + .is_err() + { + return Err(Error::RsaSignatureIsInvalid); + } // Check quote fields if quote.header.version != QUOTE_VERSION_V3 { @@ -102,27 +99,25 @@ pub fn verify( return Err(Error::UnsupportedDCAPPckCertFormat); } - // TODO: need port ring - // let certification_certs = extract_certs(&certification_data.body.data)?; - // if certification_certs.len() < 2 { - // return Err(Error::CertificateChainIsTooShort); - // } - // // Check certification_data - // let leaf_cert: webpki::EndEntityCert = - // webpki::EndEntityCert::try_from(&certification_certs[0]) - // .map_err(|_| Error::LeafCertificateParsingError)?; - // let intermediate_certs = &certification_certs[1..]; - // verify_certificate_chain(&leaf_cert, intermediate_certs, now_in_milli)?; + let certification_certs = extract_certs(&certification_data.body.data)?; + if certification_certs.len() < 2 { + return Err(Error::CertificateChainIsTooShort); + } + // Check certification_data + let leaf_cert: webpki::EndEntityCert = + webpki::EndEntityCert::try_from(&certification_certs[0]) + .map_err(|_| Error::LeafCertificateParsingError)?; + let intermediate_certs = &certification_certs[1..]; + verify_certificate_chain(&leaf_cert, intermediate_certs, now_in_milli)?; // Check QE signature - // TODO: need port ring - // let asn1_signature = encode_as_der(&auth_data.qe_report_signature)?; - // if leaf_cert - // .verify_signature(webpki::ECDSA_P256_SHA256, &auth_data.qe_report, &asn1_signature) - // .is_err() - // { - // return Err(Error::RsaSignatureIsInvalid); - // } + let asn1_signature = encode_as_der(&auth_data.qe_report_signature)?; + if leaf_cert + .verify_signature(webpki::ring::ECDSA_P256_SHA256, &auth_data.qe_report, &asn1_signature) + .is_err() + { + return Err(Error::RsaSignatureIsInvalid); + } // Extract QE report from quote let mut qe_report = auth_data.qe_report.as_slice(); @@ -133,41 +128,34 @@ pub fn verify( let mut qe_hash_data = [0u8; QE_HASH_DATA_BYTE_LEN]; qe_hash_data[0..ATTESTATION_KEY_LEN].copy_from_slice(&auth_data.ecdsa_attestation_key); qe_hash_data[ATTESTATION_KEY_LEN..].copy_from_slice(&auth_data.qe_auth_data.data); - // TODO: need port ring - // let qe_hash = ring::digest::digest(&ring::digest::SHA256, &qe_hash_data); - // if qe_hash.as_ref() != &qe_report.report_data[0..32] { - // return Err(Error::QEReportHashMismatch); - // } + let qe_hash = ring::digest::digest(&ring::digest::SHA256, &qe_hash_data); + if qe_hash.as_ref() != &qe_report.report_data[0..32] { + return Err(Error::QEReportHashMismatch); + } // Check signature from auth data - // TODO: need port ring - // let mut pub_key = [0x04u8; 65]; //Prepend 0x04 to specify uncompressed format - // pub_key[1..].copy_from_slice(&auth_data.ecdsa_attestation_key); - // let peer_public_key = - // ring::signature::UnparsedPublicKey::new(&ring::signature::ECDSA_P256_SHA256_FIXED, pub_key); - // peer_public_key - // .verify( - // &raw_quote[..(HEADER_BYTE_LEN + ENCLAVE_REPORT_BYTE_LEN)], - // &auth_data.ecdsa_signature, - // ) - // .map_err(|_| Error::IsvEnclaveReportSignatureIsInvalid)?; + let mut pub_key = [0x04u8; 65]; //Prepend 0x04 to specify uncompressed format + pub_key[1..].copy_from_slice(&auth_data.ecdsa_attestation_key); + let peer_public_key = + ring::signature::UnparsedPublicKey::new(&ring::signature::ECDSA_P256_SHA256_FIXED, pub_key); + peer_public_key + .verify( + &raw_quote[..(HEADER_BYTE_LEN + ENCLAVE_REPORT_BYTE_LEN)], + &auth_data.ecdsa_signature, + ) + .map_err(|_| Error::IsvEnclaveReportSignatureIsInvalid)?; // Extract information from the quote - // TODO: need port ring - // let extension_section = get_intel_extension(&certification_certs[0])?; - // let cpu_svn = get_cpu_svn(&extension_section)?; - // let pce_svn = get_pce_svn(&extension_section)?; - // let fmspc = get_fmspc(&extension_section)?; - - let cpu_svn = qe_report.cpu_svn; - let pce_svn = quote.header.pce_svn; + let extension_section = get_intel_extension(&certification_certs[0])?; + let cpu_svn = get_cpu_svn(&extension_section)?; + let pce_svn = get_pce_svn(&extension_section)?; + let fmspc = get_fmspc(&extension_section)?; - // TODO: need port ring - // let tcb_fmspc = hex::decode(&tcb_info.fmspc).map_err(|_| Error::CodecError)?; - // if fmspc != tcb_fmspc[..] { - // return Err(Error::FmspcMismatch); - // } + let tcb_fmspc = hex::decode(&tcb_info.fmspc).map_err(|_| Error::CodecError)?; + if fmspc != tcb_fmspc[..] { + return Err(Error::FmspcMismatch); + } // TCB status and advisory ids let mut tcb_status = "Unknown".to_owned(); diff --git a/methods/guest/src/dcap/constants.rs b/methods/guest/src/dcap/constants.rs index d0f3b2b..9b6f0f7 100644 --- a/methods/guest/src/dcap/constants.rs +++ b/methods/guest/src/dcap/constants.rs @@ -74,45 +74,44 @@ pub const ATTESTATION_KEY_LEN: usize = 64; pub const AUTHENTICATION_DATA_LEN: usize = 32; pub const QE_HASH_DATA_BYTE_LEN: usize = ATTESTATION_KEY_LEN + AUTHENTICATION_DATA_LEN; -// TODO: need port ring -// /// The needed code for a trust anchor can be extracted using `webpki` with something like this: -// /// println!("{:?}", webpki::TrustAnchor::try_from_cert_der(&root_cert)); -// #[allow(clippy::zero_prefixed_literal)] -// pub static DCAP_SERVER_ROOTS: &[webpki::types::TrustAnchor<'static>; 1] = -// &[webpki::types::TrustAnchor { -// subject: webpki::types::Der::from_slice(&[ -// 49, 26, 48, 24, 06, 03, 85, 04, 03, 12, 17, 73, 110, 116, 101, 108, 32, 83, 71, 88, 32, -// 82, 111, 111, 116, 32, 67, 65, 49, 26, 48, 24, 06, 03, 85, 04, 10, 12, 17, 73, 110, -// 116, 101, 108, 32, 67, 111, 114, 112, 111, 114, 97, 116, 105, 111, 110, 49, 20, 48, 18, -// 06, 03, 85, 04, 07, 12, 11, 83, 97, 110, 116, 97, 32, 67, 108, 97, 114, 97, 49, 11, 48, -// 09, 06, 03, 85, 04, 08, 12, 02, 67, 65, 49, 11, 48, 09, 06, 03, 85, 04, 06, 19, 02, 85, -// 83, -// ]), -// subject_public_key_info: webpki::types::Der::from_slice(&[ -// 48, 19, 06, 07, 42, 134, 72, 206, 61, 02, 01, 06, 08, 42, 134, 72, 206, 61, 03, 01, 07, -// 03, 66, 00, 04, 11, 169, 196, 192, 192, 200, 97, 147, 163, 254, 35, 214, 176, 44, 218, -// 16, 168, 187, 212, 232, 142, 72, 180, 69, 133, 97, 163, 110, 112, 85, 37, 245, 103, -// 145, 142, 46, 220, 136, 228, 13, 134, 11, 208, 204, 78, 226, 106, 172, 201, 136, 229, -// 05, 169, 83, 85, 140, 69, 63, 107, 09, 04, 174, 115, 148, -// ]), -// name_constraints: None, -// }]; -// -// pub mod oids { -// use const_oid::ObjectIdentifier as OID; -// -// const fn oid(s: &str) -> OID { -// OID::new_unwrap(s) -// } -// -// pub const SGX_EXTENSION: OID = oid("1.2.840.113741.1.13.1"); -// pub const PPID: OID = oid("1.2.840.113741.1.13.1.1"); -// pub const TCB: OID = oid("1.2.840.113741.1.13.1.2"); -// pub const PCEID: OID = oid("1.2.840.113741.1.13.1.3"); -// pub const FMSPC: OID = oid("1.2.840.113741.1.13.1.4"); -// pub const SGX_TYPE: OID = oid("1.2.840.113741.1.13.1.5"); // ASN1 Enumerated -// pub const PLATFORM_INSTANCE_ID: OID = oid("1.2.840.113741.1.13.1.6"); -// pub const CONFIGURATION: OID = oid("1.2.840.113741.1.13.1.7"); -// pub const PCESVN: OID = oid("1.2.840.113741.1.13.1.2.17"); -// pub const CPUSVN: OID = oid("1.2.840.113741.1.13.1.2.18"); -// } +/// The needed code for a trust anchor can be extracted using `webpki` with something like this: +/// println!("{:?}", webpki::TrustAnchor::try_from_cert_der(&root_cert)); +#[allow(clippy::zero_prefixed_literal)] +pub static DCAP_SERVER_ROOTS: &[webpki::types::TrustAnchor<'static>; 1] = + &[webpki::types::TrustAnchor { + subject: webpki::types::Der::from_slice(&[ + 49, 26, 48, 24, 06, 03, 85, 04, 03, 12, 17, 73, 110, 116, 101, 108, 32, 83, 71, 88, 32, + 82, 111, 111, 116, 32, 67, 65, 49, 26, 48, 24, 06, 03, 85, 04, 10, 12, 17, 73, 110, + 116, 101, 108, 32, 67, 111, 114, 112, 111, 114, 97, 116, 105, 111, 110, 49, 20, 48, 18, + 06, 03, 85, 04, 07, 12, 11, 83, 97, 110, 116, 97, 32, 67, 108, 97, 114, 97, 49, 11, 48, + 09, 06, 03, 85, 04, 08, 12, 02, 67, 65, 49, 11, 48, 09, 06, 03, 85, 04, 06, 19, 02, 85, + 83, + ]), + subject_public_key_info: webpki::types::Der::from_slice(&[ + 48, 19, 06, 07, 42, 134, 72, 206, 61, 02, 01, 06, 08, 42, 134, 72, 206, 61, 03, 01, 07, + 03, 66, 00, 04, 11, 169, 196, 192, 192, 200, 97, 147, 163, 254, 35, 214, 176, 44, 218, + 16, 168, 187, 212, 232, 142, 72, 180, 69, 133, 97, 163, 110, 112, 85, 37, 245, 103, + 145, 142, 46, 220, 136, 228, 13, 134, 11, 208, 204, 78, 226, 106, 172, 201, 136, 229, + 05, 169, 83, 85, 140, 69, 63, 107, 09, 04, 174, 115, 148, + ]), + name_constraints: None, + }]; + +pub mod oids { + use const_oid::ObjectIdentifier as OID; + + const fn oid(s: &str) -> OID { + OID::new_unwrap(s) + } + + pub const SGX_EXTENSION: OID = oid("1.2.840.113741.1.13.1"); + pub const PPID: OID = oid("1.2.840.113741.1.13.1.1"); + pub const TCB: OID = oid("1.2.840.113741.1.13.1.2"); + pub const PCEID: OID = oid("1.2.840.113741.1.13.1.3"); + pub const FMSPC: OID = oid("1.2.840.113741.1.13.1.4"); + pub const SGX_TYPE: OID = oid("1.2.840.113741.1.13.1.5"); // ASN1 Enumerated + pub const PLATFORM_INSTANCE_ID: OID = oid("1.2.840.113741.1.13.1.6"); + pub const CONFIGURATION: OID = oid("1.2.840.113741.1.13.1.7"); + pub const PCESVN: OID = oid("1.2.840.113741.1.13.1.2.17"); + pub const CPUSVN: OID = oid("1.2.840.113741.1.13.1.2.18"); +} diff --git a/methods/guest/src/dcap/utils.rs b/methods/guest/src/dcap/utils.rs index d36e4c9..165db38 100644 --- a/methods/guest/src/dcap/utils.rs +++ b/methods/guest/src/dcap/utils.rs @@ -1,160 +1,160 @@ -// TODO: need port ring -// use alloc::vec; -// use alloc::vec::Vec; -// use core::time::Duration; -// use webpki::types::CertificateDer; -// use x509_cert::Certificate; -// use asn1_der::{ -// typed::{DerDecodable, Sequence}, -// DerObject, -// }; -// -// use crate::dcap::constants::*; -// use crate::Error; - -// pub fn get_intel_extension(der_encoded: &[u8]) -> Result, Error> { -// let cert: Certificate = der::Decode::from_der(der_encoded) -// .map_err(|_| Error::IntelExtensionCertificateDecodingError)?; -// let mut extension_iter = cert -// .tbs_certificate -// .extensions -// .as_deref() -// .unwrap_or(&[]) -// .iter() -// .filter(|e| e.extn_id == oids::SGX_EXTENSION) -// .map(|e| e.extn_value.clone()); -// -// let extension = extension_iter.next().ok_or(Error::IntelExtensionAmbiguity)?; -// if extension_iter.next().is_some() { -// //"There should only be one section containing Intel extensions" -// return Err(Error::IntelExtensionAmbiguity); -// } -// Ok(extension.into_bytes()) -// } -// -// pub fn find_extension(path: &[&[u8]], raw: &[u8]) -> Result, Error> { -// let obj = DerObject::decode(raw).map_err(|_| Error::DerDecodingError)?; -// let subobj = get_obj(path, obj)?; -// Ok(subobj.value().to_vec()) -// } -// -// fn get_obj<'a>(path: &[&[u8]], mut obj: DerObject<'a>) -> Result, Error> { -// for oid in path { -// let seq = Sequence::load(obj).map_err(|_| Error::DerDecodingError )?; -// obj = sub_obj(oid, seq)?; -// } -// Ok(obj) -// } -// -// fn sub_obj<'a>(oid: &[u8], seq: Sequence<'a>) -> Result, Error> { -// for i in 0..seq.len() { -// let entry = seq.get(i).map_err(|_| Error::OidIsMissing)?; -// let entry = Sequence::load(entry).map_err(|_| Error::DerDecodingError )?; -// let name = entry.get(0).map_err(|_| Error::OidIsMissing)?; -// let value = entry.get(1).map_err(|_| Error::OidIsMissing)?; -// if name.value() == oid { -// return Ok(value); -// } -// } -// Err(Error::OidIsMissing) -// } -// -// pub fn get_fmspc(extension_section: &[u8]) -> Result { -// let data = find_extension(&[oids::FMSPC.as_bytes()], extension_section)?; -// if data.len() != 6 { -// return Err(Error::FmspcLengthMismatch) -// } -// -// data.try_into().map_err(|_| Error::FmspcDecodingError) -// } -// -// pub fn get_cpu_svn(extension_section: &[u8]) -> Result { -// let data = find_extension(&[oids::TCB.as_bytes(), oids::CPUSVN.as_bytes()], extension_section)?; -// if data.len() != 16 { -// return Err(Error::CpuSvnLengthMismatch) -// } -// -// data.try_into().map_err(|_| Error::CpuSvnDecodingError) -// } -// -// pub fn get_pce_svn(extension_section: &[u8]) -> Result { -// let data = find_extension(&[oids::TCB.as_bytes(), oids::PCESVN.as_bytes()], extension_section)?; -// -// match data.len() { -// 1 => Ok(u16::from(data[0])), -// 2 => Ok(u16::from_be_bytes(data.try_into().map_err(|_| Error::PceSvnDecodingError)?)), -// _ => Err(Error::PceSvnLengthMismatch), -// } -// } -// -// pub fn extract_raw_certs(cert_chain: &[u8]) -> Result>, Error> { -// Ok( -// pem::parse_many(cert_chain) -// .map_err(|_| Error::CodecError)? -// .iter() -// .map(|i| i.contents().to_vec() ) -// .collect() -// ) -// } -// -// pub fn extract_certs<'a>(cert_chain: &'a [u8]) -> Result>, Error> { -// let mut certs = Vec::>::new(); -// -// let raw_certs = extract_raw_certs(cert_chain)?; -// for raw_cert in raw_certs.iter() { -// let cert = webpki::types::CertificateDer::<'a>::from(raw_cert.to_vec()); -// certs.push(cert); -// } -// -// Ok(certs) -// } -// -// /// Encode two 32-byte values in DER format -// /// This is meant for 256 bit ECC signatures or public keys -// /// TODO: We may could use `asn1_der` crate reimplement this, so we can remove `der` which overlap with `asn1_der` -// pub fn encode_as_der(data: &[u8]) -> Result, Error> { -// if data.len() != 64 { -// return Err(Error::KeyLengthIsInvalid); -// } -// let mut sequence = der::asn1::SequenceOf::::new(); -// sequence -// .add(der::asn1::UintRef::new(&data[0..32]).map_err(|_| Error::PublicKeyIsInvalid)?) -// .map_err(|_| Error::PublicKeyIsInvalid)?; -// sequence -// .add(der::asn1::UintRef::new(&data[32..]).map_err(|_| Error::PublicKeyIsInvalid)?) -// .map_err(|_| Error::PublicKeyIsInvalid)?; -// // 72 should be enough in all cases. 2 + 2 x (32 + 3) -// let mut asn1 = vec![0u8; 72]; -// let mut writer = der::SliceWriter::new(&mut asn1); -// writer -// .encode(&sequence) -// .map_err(|_| Error::DerEncodingError)?; -// Ok(writer -// .finish() -// .map_err(|_| Error::DerEncodingError)? -// .to_vec()) -// } -// -// /// Verifies that the `leaf_cert` in combination with the `intermediate_certs` establishes -// /// a valid certificate chain that is rooted in one of the trust anchors that was compiled into to the pallet -// pub fn verify_certificate_chain( -// leaf_cert: &webpki::EndEntityCert, -// intermediate_certs: &[CertificateDer], -// verification_time: u64, -// ) -> Result<(), Error> { -// let time = -// webpki::types::UnixTime::since_unix_epoch(Duration::from_secs(verification_time / 1000)); -// let sig_algs = &[webpki::ECDSA_P256_SHA256]; -// leaf_cert -// .verify_for_usage( -// sig_algs, -// DCAP_SERVER_ROOTS, -// intermediate_certs, -// time, -// webpki::KeyUsage::server_auth(), -// None, -// ) -// .map_err(|_e| Error::CertificateChainIsInvalid)?; -// -// Ok(()) -// } +use alloc::vec; +use alloc::vec::Vec; +use core::time::Duration; +use webpki::types::CertificateDer; +use x509_cert::Certificate; +use asn1_der::{ + typed::{DerDecodable, Sequence}, + DerObject, +}; + +use crate::dcap::constants::*; +use crate::Error; + +pub fn get_intel_extension(der_encoded: &[u8]) -> Result, Error> { + let cert: Certificate = der::Decode::from_der(der_encoded) + .map_err(|_| Error::IntelExtensionCertificateDecodingError)?; + let mut extension_iter = cert + .tbs_certificate + .extensions + .as_deref() + .unwrap_or(&[]) + .iter() + .filter(|e| e.extn_id == oids::SGX_EXTENSION) + .map(|e| e.extn_value.clone()); + + let extension = extension_iter.next().ok_or(Error::IntelExtensionAmbiguity)?; + if extension_iter.next().is_some() { + //"There should only be one section containing Intel extensions" + return Err(Error::IntelExtensionAmbiguity); + } + Ok(extension.into_bytes()) +} + +pub fn find_extension(path: &[&[u8]], raw: &[u8]) -> Result, Error> { + let obj = DerObject::decode(raw).map_err(|_| Error::DerDecodingError)?; + let subobj = get_obj(path, obj)?; + Ok(subobj.value().to_vec()) +} + +fn get_obj<'a>(path: &[&[u8]], mut obj: DerObject<'a>) -> Result, Error> { + for oid in path { + let seq = Sequence::load(obj).map_err(|_| Error::DerDecodingError )?; + obj = sub_obj(oid, seq)?; + } + Ok(obj) +} + +fn sub_obj<'a>(oid: &[u8], seq: Sequence<'a>) -> Result, Error> { + for i in 0..seq.len() { + let entry = seq.get(i).map_err(|_| Error::OidIsMissing)?; + let entry = Sequence::load(entry).map_err(|_| Error::DerDecodingError )?; + let name = entry.get(0).map_err(|_| Error::OidIsMissing)?; + let value = entry.get(1).map_err(|_| Error::OidIsMissing)?; + if name.value() == oid { + return Ok(value); + } + } + Err(Error::OidIsMissing) +} + +pub fn get_fmspc(extension_section: &[u8]) -> Result { + let data = find_extension(&[oids::FMSPC.as_bytes()], extension_section)?; + if data.len() != 6 { + return Err(Error::FmspcLengthMismatch) + } + + data.try_into().map_err(|_| Error::FmspcDecodingError) +} + +pub fn get_cpu_svn(extension_section: &[u8]) -> Result { + let data = find_extension(&[oids::TCB.as_bytes(), oids::CPUSVN.as_bytes()], extension_section)?; + if data.len() != 16 { + return Err(Error::CpuSvnLengthMismatch) + } + + data.try_into().map_err(|_| Error::CpuSvnDecodingError) +} + +pub fn get_pce_svn(extension_section: &[u8]) -> Result { + let data = find_extension(&[oids::TCB.as_bytes(), oids::PCESVN.as_bytes()], extension_section)?; + + match data.len() { + 1 => Ok(u16::from(data[0])), + 2 => Ok(u16::from_be_bytes(data.try_into().map_err(|_| Error::PceSvnDecodingError)?)), + _ => Err(Error::PceSvnLengthMismatch), + } +} + +pub fn extract_raw_certs(cert_chain: &[u8]) -> Result>, Error> { + Ok( + pem::parse_many(cert_chain) + .map_err(|_| Error::CodecError)? + .iter() + .map(|i| i.contents().to_vec() ) + .collect() + ) +} + +pub fn extract_certs<'a>(cert_chain: &'a [u8]) -> Result>, Error> { + let mut certs = Vec::>::new(); + + let raw_certs = extract_raw_certs(cert_chain)?; + for raw_cert in raw_certs.iter() { + let cert = webpki::types::CertificateDer::<'a>::from(raw_cert.to_vec()); + certs.push(cert); + } + + Ok(certs) +} + +/// Encode two 32-byte values in DER format +/// This is meant for 256 bit ECC signatures or public keys +/// TODO: We may could use `asn1_der` crate reimplement this, so we can remove `der` which overlap with `asn1_der` +pub fn encode_as_der(data: &[u8]) -> Result, Error> { + if data.len() != 64 { + return Err(Error::KeyLengthIsInvalid); + } + let mut sequence = der::asn1::SequenceOf::::new(); + sequence + .add(der::asn1::UintRef::new(&data[0..32]).map_err(|_| Error::PublicKeyIsInvalid)?) + .map_err(|_| Error::PublicKeyIsInvalid)?; + sequence + .add(der::asn1::UintRef::new(&data[32..]).map_err(|_| Error::PublicKeyIsInvalid)?) + .map_err(|_| Error::PublicKeyIsInvalid)?; + // 72 should be enough in all cases. 2 + 2 x (32 + 3) + let mut asn1 = vec![0u8; 72]; + let mut writer = der::SliceWriter::new(&mut asn1); + writer + .encode(&sequence) + .map_err(|_| Error::DerEncodingError)?; + Ok(writer + .finish() + .map_err(|_| Error::DerEncodingError)? + .to_vec()) +} + +/// Verifies that the `leaf_cert` in combination with the `intermediate_certs` establishes +/// a valid certificate chain that is rooted in one of the trust anchors that was compiled into to the pallet +pub fn verify_certificate_chain( + leaf_cert: &webpki::EndEntityCert, + intermediate_certs: &[CertificateDer], + verification_time: u64, +) -> Result<(), Error> { + let time = + webpki::types::UnixTime::since_unix_epoch(Duration::from_secs(verification_time / 1000)); + let sig_algs = &[webpki::ring::ECDSA_P256_SHA256]; + leaf_cert + .verify_for_usage( + sig_algs, + DCAP_SERVER_ROOTS, + intermediate_certs, + time, + webpki::KeyUsage::server_auth(), + None, + None, + ) + .map_err(|_e| Error::CertificateChainIsInvalid)?; + + Ok(()) +}