From e42dd0fef36562e09013823d30e97524f5783e30 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Fri, 11 Jun 2021 11:37:47 +0000 Subject: [PATCH] runtime: Stop using webpki to validate the IAS cert chain --- .changelog/4021.internal.md | 1 + Cargo.lock | 350 ++++++++++++++++++++++------------ Cargo.toml | 4 - runtime/Cargo.toml | 9 +- runtime/src/common/sgx/avr.rs | 303 +++++++++++++++++++---------- runtime/src/lib.rs | 5 +- 6 files changed, 435 insertions(+), 237 deletions(-) create mode 100644 .changelog/4021.internal.md diff --git a/.changelog/4021.internal.md b/.changelog/4021.internal.md new file mode 100644 index 00000000000..3769d93a3fe --- /dev/null +++ b/.changelog/4021.internal.md @@ -0,0 +1 @@ +runtime: Stop using webpki to validate the IAS cert chain diff --git a/Cargo.lock b/Cargo.lock index 61d8fdc22ce..fba3f2c938e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,6 +125,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e906254e445520903e7fc9da4f709886c84ae4bc4ddaf0e093188d66df4dc820" +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "async-trait" version = "0.1.50" @@ -147,6 +153,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + [[package]] name = "autocfg" version = "1.0.1" @@ -214,6 +226,18 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bitvec" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake2" version = "0.9.1" @@ -243,12 +267,6 @@ dependencies = [ "cmake", ] -[[package]] -name = "bumpalo" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" - [[package]] name = "byteorder" version = "1.4.3" @@ -289,7 +307,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" dependencies = [ - "nom", + "nom 5.1.2", ] [[package]] @@ -472,7 +490,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ - "autocfg", + "autocfg 1.0.1", "cfg-if 0.1.10", "crossbeam-utils 0.7.2", "lazy_static", @@ -521,7 +539,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg", + "autocfg 1.0.1", "cfg-if 0.1.10", "lazy_static", ] @@ -584,6 +602,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "data-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" + [[package]] name = "deoxysii" version = "0.2.2" @@ -595,6 +619,31 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-oid-macro" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4cccf60bb98c0fca115a581f894aed0e43fa55bf289fdac5599bec440bb4fd6" +dependencies = [ + "nom 6.1.2", + "num-bigint", + "num-traits", + "syn", +] + +[[package]] +name = "der-parser" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120842c2385dea19347e2f6e31caa5dced5ba8afdfacaac16c59465fdd1168f2" +dependencies = [ + "der-oid-macro", + "nom 6.1.2", + "num-bigint", + "num-traits", + "rusticata-macros", +] + [[package]] name = "derive_arbitrary" version = "1.0.1" @@ -809,7 +858,7 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121" dependencies = [ - "autocfg", + "autocfg 1.0.1", "proc-macro-hack", "proc-macro2", "quote", @@ -834,7 +883,7 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" dependencies = [ - "autocfg", + "autocfg 1.0.1", "futures 0.1.31", "futures-channel", "futures-core", @@ -1050,15 +1099,6 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" -[[package]] -name = "js-sys" -version = "0.3.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "kernel32-sys" version = "0.2.2" @@ -1074,6 +1114,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] [[package]] name = "lazycell" @@ -1081,6 +1124,19 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 1.0.0", + "ryu", + "static_assertions", +] + [[package]] name = "libc" version = "0.2.95" @@ -1107,6 +1163,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "libm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" + [[package]] name = "libz-sys" version = "1.1.3" @@ -1180,7 +1242,7 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" dependencies = [ - "autocfg", + "autocfg 1.0.1", ] [[package]] @@ -1189,7 +1251,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" dependencies = [ - "autocfg", + "autocfg 1.0.1", ] [[package]] @@ -1199,7 +1261,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", - "autocfg", + "autocfg 1.0.1", ] [[package]] @@ -1312,18 +1374,49 @@ dependencies = [ "version_check", ] +[[package]] +name = "nom" +version = "6.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" +dependencies = [ + "bitvec", + "funty", + "lexical-core", + "memchr", + "version_check", +] + [[package]] name = "num-bigint" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512" dependencies = [ - "autocfg", + "autocfg 1.0.1", "num-integer", "num-traits", "serde", ] +[[package]] +name = "num-bigint-dig" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4547ee5541c18742396ae2c895d0717d0f886d8823b8399cdaf7b07d63ad0480" +dependencies = [ + "autocfg 0.1.7", + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.3", + "smallvec", + "zeroize", +] + [[package]] name = "num-derive" version = "0.3.3" @@ -1341,7 +1434,18 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ - "autocfg", + "autocfg 1.0.1", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg 1.0.1", + "num-integer", "num-traits", ] @@ -1351,7 +1455,8 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ - "autocfg", + "autocfg 1.0.1", + "libm", ] [[package]] @@ -1456,10 +1561,10 @@ dependencies = [ "log", "num-bigint", "num-traits", - "pem", + "oid-registry", "percent-encoding", "rand 0.7.3", - "ring", + "rsa", "rustc-hex", "serde", "serde_bytes", @@ -1480,9 +1585,8 @@ dependencies = [ "thiserror", "tiny-keccak 2.0.2", "tokio 1.6.1", - "untrusted", - "webpki", "x25519-dalek", + "x509-parser", "zeroize", ] @@ -1517,6 +1621,15 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170" +[[package]] +name = "oid-registry" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b26c042283b4ecc71edde70de2d6d0ab7e8743b8e7a7cb8467e4d153dfc7ad43" +dependencies = [ + "der-parser", +] + [[package]] name = "once_cell" version = "1.7.2" @@ -1549,7 +1662,7 @@ version = "0.9.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6b0d6fb7d80f877617dfcb014e605e2b5ab2fb0afdf27935219bb6bd984cb98" dependencies = [ - "autocfg", + "autocfg 1.0.1", "cc", "libc", "pkg-config", @@ -1766,6 +1879,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" + [[package]] name = "rand" version = "0.7.3" @@ -1881,17 +2000,23 @@ dependencies = [ ] [[package]] -name = "ring" -version = "0.16.11" -source = "git+https://github.com/oasislabs/ring-sgx?branch=sgx-target#765fab6352395852091bc0605de16fcfc73712ec" +name = "rsa" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ef841a26fc5d040ced0417c6c6a64ee851f42489df11cdf0218e545b6f8d28" dependencies = [ - "cc", + "byteorder", + "digest", "lazy_static", - "libc", - "spin", - "untrusted", - "web-sys", - "winapi 0.3.9", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pem", + "rand 0.8.3", + "simple_asn1", + "subtle", + "zeroize", ] [[package]] @@ -1921,6 +2046,21 @@ dependencies = [ "semver", ] +[[package]] +name = "rusticata-macros" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7390af60e66c44130b4c5ea85f2555b7ace835d73b4b889c704dc3cb4c0468c8" +dependencies = [ + "nom 6.1.2", +] + +[[package]] +name = "rustversion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" + [[package]] name = "ryu" version = "1.0.5" @@ -2175,6 +2315,18 @@ dependencies = [ "tokio 1.6.1", ] +[[package]] +name = "simple_asn1" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc31e6cf34ad4321d3a2b8f934949b429e314519f753a77962f16c664dca8e13" +dependencies = [ + "chrono", + "num-bigint", + "num-traits", + "thiserror", +] + [[package]] name = "slab" version = "0.4.3" @@ -2260,6 +2412,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.8.0" @@ -2304,6 +2462,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.2.0" @@ -2486,7 +2650,7 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a38d31d7831c6ed7aad00aa4c12d9375fd225a6dd77da1d25b707346319a975" dependencies = [ - "autocfg", + "autocfg 1.0.1", "num_cpus", "pin-project-lite 0.2.6", ] @@ -2609,12 +2773,6 @@ dependencies = [ "libc", ] -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "url" version = "2.2.2" @@ -2674,80 +2832,6 @@ version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" -[[package]] -name = "wasm-bindgen" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" -dependencies = [ - "cfg-if 1.0.0", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" - -[[package]] -name = "web-sys" -version = "0.3.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f50e1972865d6b1adb54167d1c8ed48606004c2c9d0ea5f1eeb34d95e863ef" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "which" version = "4.1.0" @@ -2811,6 +2895,12 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + [[package]] name = "x25519-dalek" version = "1.1.1" @@ -2822,6 +2912,24 @@ dependencies = [ "zeroize", ] +[[package]] +name = "x509-parser" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64abca276c58f8341ddc13fd4bd6ae75993cc669043f5b34813c90f7dff04771" +dependencies = [ + "base64", + "chrono", + "data-encoding", + "der-parser", + "lazy_static", + "nom 6.1.2", + "oid-registry", + "rusticata-macros", + "rustversion", + "thiserror", +] + [[package]] name = "zeroize" version = "1.3.0" diff --git a/Cargo.toml b/Cargo.toml index e56b472657a..5dbdc9963e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,9 +19,5 @@ members = [ "tests/clients/test-long-term", ] -[patch.crates-io] -# TODO: Remove when merged upstream (briansmith/ring#738). -ring = { git = "https://github.com/oasislabs/ring-sgx", branch = "sgx-target" } - [profile.release] panic = "abort" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 642dffe4f04..bf64b5b1370 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -21,15 +21,9 @@ byteorder = "1.4.3" anyhow = "1.0" thiserror = "1.0" sgx-isa = { version = "0.3.3", features = ["sgxstd"] } -webpki = "0.21.2" -# We are using a ring-sgx patch, so a specific version is required. -# https://github.com/oasislabs/ring-sgx -ring = "=0.16.11" -untrusted = "0.7.0" bincode = "1.3.3" snow = "0.8.0" percent-encoding = "2.1.0" -pem = "0.8.3" chrono = "0.4.19" base64 = "0.13.0" rustc-hex = "2.0.1" @@ -55,6 +49,9 @@ num-bigint = { version = "0.4", features = ["serde"] } num-traits = "0.2.14" bech32 = "0.8.0" impl-trait-for-tuples = "0.2.1" +x509-parser = "0.9.2" +oid-registry = "0.1.1" +rsa = "0.4.0" [dev-dependencies] # For storage interoperability tests only. diff --git a/runtime/src/common/sgx/avr.rs b/runtime/src/common/sgx/avr.rs index 871248b199f..24a0be03010 100644 --- a/runtime/src/common/sgx/avr.rs +++ b/runtime/src/common/sgx/avr.rs @@ -5,13 +5,16 @@ use anyhow::{anyhow, Result}; use base64; use byteorder::{LittleEndian, ReadBytesExt}; use chrono::prelude::*; -use pem::parse_many; +use lazy_static::lazy_static; +use oid_registry::{OID_PKCS1_RSAENCRYPTION, OID_PKCS1_SHA256WITHRSA}; use percent_encoding; +use rsa::{padding::PaddingScheme, Hash, PublicKey, RSAPublicKey}; use serde::{Deserialize, Serialize}; use serde_json; use sgx_isa::{AttributesFlags, Report}; +use sha2::{Digest, Sha256}; use thiserror::Error; -use webpki; +use x509_parser::prelude::*; use crate::common::time::{insecure_posix_time, update_insecure_posix_time}; @@ -40,10 +43,16 @@ enum AVRError { MissingNonce, #[error("failed to parse quote")] MalformedQuote, - #[error("unable to find any certificates")] - NoCertificates, + #[error("unable to find exactly 2 certificates")] + ChainNotTwoCertificates, #[error("malformed certificate PEM")] MalformedCertificatePEM, + #[error("malformed certificate DER")] + MalformedCertificateDER, + #[error("expired certificate")] + ExpiredCertificate, + #[error("invalid signature")] + InvalidSignature, } pub const QUOTE_CONTEXT_LEN: usize = 8; @@ -55,80 +64,57 @@ impl_bytes!(MrEnclave, 32, "Enclave hash (MRENCLAVE)."); impl_bytes!(MrSigner, 32, "Enclave signer hash (MRSIGNER)."); // AVR signature validation constants. -static IAS_ANCHORS: [webpki::TrustAnchor<'static>; 1] = [ - // Derived via webpki::trust_anchor_util::generate_code_for_trust_anchors. - // - // -----BEGIN CERTIFICATE----- - // MIIFSzCCA7OgAwIBAgIJANEHdl0yo7CUMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNV - // BAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNV - // BAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQDDCdJbnRlbCBTR1ggQXR0ZXN0 - // YXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwIBcNMTYxMTE0MTUzNzMxWhgPMjA0OTEy - // MzEyMzU5NTlaMH4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwL - // U2FudGEgQ2xhcmExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQD - // DCdJbnRlbCBTR1ggQXR0ZXN0YXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwggGiMA0G - // CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCfPGR+tXc8u1EtJzLA10Feu1Wg+p7e - // LmSRmeaCHbkQ1TF3Nwl3RmpqXkeGzNLd69QUnWovYyVSndEMyYc3sHecGgfinEeh - // rgBJSEdsSJ9FpaFdesjsxqzGRa20PYdnnfWcCTvFoulpbFR4VBuXnnVLVzkUvlXT - // L/TAnd8nIZk0zZkFJ7P5LtePvykkar7LcSQO85wtcQe0R1Raf/sQ6wYKaKmFgCGe - // NpEJUmg4ktal4qgIAxk+QHUxQE42sxViN5mqglB0QJdUot/o9a/V/mMeH8KvOAiQ - // byinkNndn+Bgk5sSV5DFgF0DffVqmVMblt5p3jPtImzBIH0QQrXJq39AT8cRwP5H - // afuVeLHcDsRp6hol4P+ZFIhu8mmbI1u0hH3W/0C2BuYXB5PC+5izFFh/nP0lc2Lf - // 6rELO9LZdnOhpL1ExFOq9H/B8tPQ84T3Sgb4nAifDabNt/zu6MmCGo5U8lwEFtGM - // RoOaX4AS+909x00lYnmtwsDVWv9vBiJCXRsCAwEAAaOByTCBxjBgBgNVHR8EWTBX - // MFWgU6BRhk9odHRwOi8vdHJ1c3RlZHNlcnZpY2VzLmludGVsLmNvbS9jb250ZW50 - // L0NSTC9TR1gvQXR0ZXN0YXRpb25SZXBvcnRTaWduaW5nQ0EuY3JsMB0GA1UdDgQW - // BBR4Q3t2pn680K9+QjfrNXw7hwFRPDAfBgNVHSMEGDAWgBR4Q3t2pn680K9+Qjfr - // NXw7hwFRPDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkq - // hkiG9w0BAQsFAAOCAYEAeF8tYMXICvQqeXYQITkV2oLJsp6J4JAqJabHWxYJHGir - // IEqucRiJSSx+HjIJEUVaj8E0QjEud6Y5lNmXlcjqRXaCPOqK0eGRz6hi+ripMtPZ - // sFNaBwLQVV905SDjAzDzNIDnrcnXyB4gcDFCvwDFKKgLRjOB/WAqgscDUoGq5ZVi - // zLUzTqiQPmULAQaB9c6Oti6snEFJiCQ67JLyW/E83/frzCmO5Ru6WjU4tmsmy8Ra - // Ud4APK0wZTGtfPXU7w+IBdG5Ez0kE1qzxGQaL4gINJ1zMyleDnbuS8UicjJijvqA - // 152Sq049ESDz+1rRGc2NVEqh1KaGXmtXvqxXcTB+Ljy5Bw2ke0v8iGngFBPqCTVB - // 3op5KBG3RjbF6RRSzwzuWfL7QErNC8WEy5yDVARzTA5+xmBc388v9Dm21HGfcC8O - // DD+gT9sSpssq0ascmvH49MOgjt1yoysLtdCtJW/9FZpoOypaHx0R+mJTLwPXVMrv - // DaVzWh5aiEx+idkSGMnX - // -----END CERTIFICATE----- - webpki::TrustAnchor { - subject: &[ - 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 11, 48, 9, 6, 3, 85, 4, 8, 12, 2, 67, - 65, 49, 20, 48, 18, 6, 3, 85, 4, 7, 12, 11, 83, 97, 110, 116, 97, 32, 67, 108, 97, 114, - 97, 49, 26, 48, 24, 6, 3, 85, 4, 10, 12, 17, 73, 110, 116, 101, 108, 32, 67, 111, 114, - 112, 111, 114, 97, 116, 105, 111, 110, 49, 48, 48, 46, 6, 3, 85, 4, 3, 12, 39, 73, 110, - 116, 101, 108, 32, 83, 71, 88, 32, 65, 116, 116, 101, 115, 116, 97, 116, 105, 111, 110, - 32, 82, 101, 112, 111, 114, 116, 32, 83, 105, 103, 110, 105, 110, 103, 32, 67, 65, - ], - spki: &[ - 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 143, 0, 48, 130, 1, - 138, 2, 130, 1, 129, 0, 159, 60, 100, 126, 181, 119, 60, 187, 81, 45, 39, 50, 192, 215, - 65, 94, 187, 85, 160, 250, 158, 222, 46, 100, 145, 153, 230, 130, 29, 185, 16, 213, 49, - 119, 55, 9, 119, 70, 106, 106, 94, 71, 134, 204, 210, 221, 235, 212, 20, 157, 106, 47, - 99, 37, 82, 157, 209, 12, 201, 135, 55, 176, 119, 156, 26, 7, 226, 156, 71, 161, 174, - 0, 73, 72, 71, 108, 72, 159, 69, 165, 161, 93, 122, 200, 236, 198, 172, 198, 69, 173, - 180, 61, 135, 103, 157, 245, 156, 9, 59, 197, 162, 233, 105, 108, 84, 120, 84, 27, 151, - 158, 117, 75, 87, 57, 20, 190, 85, 211, 47, 244, 192, 157, 223, 39, 33, 153, 52, 205, - 153, 5, 39, 179, 249, 46, 215, 143, 191, 41, 36, 106, 190, 203, 113, 36, 14, 243, 156, - 45, 113, 7, 180, 71, 84, 90, 127, 251, 16, 235, 6, 10, 104, 169, 133, 128, 33, 158, 54, - 145, 9, 82, 104, 56, 146, 214, 165, 226, 168, 8, 3, 25, 62, 64, 117, 49, 64, 78, 54, - 179, 21, 98, 55, 153, 170, 130, 80, 116, 64, 151, 84, 162, 223, 232, 245, 175, 213, - 254, 99, 30, 31, 194, 175, 56, 8, 144, 111, 40, 167, 144, 217, 221, 159, 224, 96, 147, - 155, 18, 87, 144, 197, 128, 93, 3, 125, 245, 106, 153, 83, 27, 150, 222, 105, 222, 51, - 237, 34, 108, 193, 32, 125, 16, 66, 181, 201, 171, 127, 64, 79, 199, 17, 192, 254, 71, - 105, 251, 149, 120, 177, 220, 14, 196, 105, 234, 26, 37, 224, 255, 153, 20, 136, 110, - 242, 105, 155, 35, 91, 180, 132, 125, 214, 255, 64, 182, 6, 230, 23, 7, 147, 194, 251, - 152, 179, 20, 88, 127, 156, 253, 37, 115, 98, 223, 234, 177, 11, 59, 210, 217, 118, - 115, 161, 164, 189, 68, 196, 83, 170, 244, 127, 193, 242, 211, 208, 243, 132, 247, 74, - 6, 248, 156, 8, 159, 13, 166, 205, 183, 252, 238, 232, 201, 130, 26, 142, 84, 242, 92, - 4, 22, 209, 140, 70, 131, 154, 95, 128, 18, 251, 221, 61, 199, 77, 37, 98, 121, 173, - 194, 192, 213, 90, 255, 111, 6, 34, 66, 93, 27, 2, 3, 1, 0, 1, - ], - name_constraints: None, - }, -]; -static IAS_SIG_ALGS: &'static [&'static webpki::SignatureAlgorithm] = - &[&webpki::RSA_PKCS1_2048_8192_SHA256]; +const IAS_TRUST_ANCHOR_PEM: &str = r#"-----BEGIN CERTIFICATE----- +MIIFSzCCA7OgAwIBAgIJANEHdl0yo7CUMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNV +BAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQDDCdJbnRlbCBTR1ggQXR0ZXN0 +YXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwIBcNMTYxMTE0MTUzNzMxWhgPMjA0OTEy +MzEyMzU5NTlaMH4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwL +U2FudGEgQ2xhcmExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQD +DCdJbnRlbCBTR1ggQXR0ZXN0YXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwggGiMA0G +CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCfPGR+tXc8u1EtJzLA10Feu1Wg+p7e +LmSRmeaCHbkQ1TF3Nwl3RmpqXkeGzNLd69QUnWovYyVSndEMyYc3sHecGgfinEeh +rgBJSEdsSJ9FpaFdesjsxqzGRa20PYdnnfWcCTvFoulpbFR4VBuXnnVLVzkUvlXT +L/TAnd8nIZk0zZkFJ7P5LtePvykkar7LcSQO85wtcQe0R1Raf/sQ6wYKaKmFgCGe +NpEJUmg4ktal4qgIAxk+QHUxQE42sxViN5mqglB0QJdUot/o9a/V/mMeH8KvOAiQ +byinkNndn+Bgk5sSV5DFgF0DffVqmVMblt5p3jPtImzBIH0QQrXJq39AT8cRwP5H +afuVeLHcDsRp6hol4P+ZFIhu8mmbI1u0hH3W/0C2BuYXB5PC+5izFFh/nP0lc2Lf +6rELO9LZdnOhpL1ExFOq9H/B8tPQ84T3Sgb4nAifDabNt/zu6MmCGo5U8lwEFtGM +RoOaX4AS+909x00lYnmtwsDVWv9vBiJCXRsCAwEAAaOByTCBxjBgBgNVHR8EWTBX +MFWgU6BRhk9odHRwOi8vdHJ1c3RlZHNlcnZpY2VzLmludGVsLmNvbS9jb250ZW50 +L0NSTC9TR1gvQXR0ZXN0YXRpb25SZXBvcnRTaWduaW5nQ0EuY3JsMB0GA1UdDgQW +BBR4Q3t2pn680K9+QjfrNXw7hwFRPDAfBgNVHSMEGDAWgBR4Q3t2pn680K9+Qjfr +NXw7hwFRPDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkq +hkiG9w0BAQsFAAOCAYEAeF8tYMXICvQqeXYQITkV2oLJsp6J4JAqJabHWxYJHGir +IEqucRiJSSx+HjIJEUVaj8E0QjEud6Y5lNmXlcjqRXaCPOqK0eGRz6hi+ripMtPZ +sFNaBwLQVV905SDjAzDzNIDnrcnXyB4gcDFCvwDFKKgLRjOB/WAqgscDUoGq5ZVi +zLUzTqiQPmULAQaB9c6Oti6snEFJiCQ67JLyW/E83/frzCmO5Ru6WjU4tmsmy8Ra +Ud4APK0wZTGtfPXU7w+IBdG5Ez0kE1qzxGQaL4gINJ1zMyleDnbuS8UicjJijvqA +152Sq049ESDz+1rRGc2NVEqh1KaGXmtXvqxXcTB+Ljy5Bw2ke0v8iGngFBPqCTVB +3op5KBG3RjbF6RRSzwzuWfL7QErNC8WEy5yDVARzTA5+xmBc388v9Dm21HGfcC8O +DD+gT9sSpssq0ascmvH49MOgjt1yoysLtdCtJW/9FZpoOypaHx0R+mJTLwPXVMrv +DaVzWh5aiEx+idkSGMnX +-----END CERTIFICATE-----"#; const PEM_CERTIFICATE_LABEL: &str = "CERTIFICATE"; const IAS_TS_FMT: &str = "%FT%T%.6f"; +lazy_static! { + static ref IAS_TRUST_ANCHOR: Vec = { + let pem = match parse_x509_pem(IAS_TRUST_ANCHOR_PEM.as_bytes()) { + Ok((rem, pem)) => { + if !rem.is_empty() { + panic!("anchor PEM has trailing garbage"); + } + if pem.label != PEM_CERTIFICATE_LABEL { + panic!("PEM does not contain a certificate: '{:?}'", pem.label); + } + pem + } + err => panic!("failed to decode anchor PEM: {:?}", err), + }; + + pem.contents.to_vec() + }; +} /// Decoded quote body. #[derive(Default, Debug)] @@ -342,47 +328,156 @@ fn validate_avr_signature( signature: &[u8], unix_time: u64, ) -> Result<()> { - // Load the Intel SGX Attestation Report Signing CA certificate. - let anchors = webpki::TLSServerTrustAnchors(&IAS_ANCHORS); + // WARNING: This is the entirely wrong way to validate a certificate + // chain as it does not come close to implementing anything resembling + // what is specified in RFC 5280 6.1. There probably should be a CRL + // check here as well, now that I think about it. + // + // The main assumptions made about how exactly the signing key is + // certified/distributed, and the AVR is signed are based on the + // following documentation: + // + // * 4.2.2 Report Signature: + // * "The Attestation Verification Report is cryptographically + // signed by Report Signing Key (owned by the Attestation + // Service) using the RSA-SHA256 algorithm." + // * 4.2.3 Report Signing Certificate Chain: + // * "The public part of Report Key is distributed in the form + // of an x.509 digital certificate called Attestation Report + // Signing Certificate. It is a leaf certificate issued by + // the Attestation Report Signing CA Certificate" + // * "A PEM-encoded certificate chain consisting of Attestation + // Report Signing Certificate and Attestation Report Signing + // CA Certificate is returned..." + // + // See: "Attestation Service for Intel(R) Software Guard Extensions + // (Intel(R) SGX): API Documentation" (Revision: 6.0) - // Decode the certificate chain. + // Decode the certificate chain from percent encoded PEM to DER. let raw_pem = percent_encoding::percent_decode(cert_chain).decode_utf8()?; - let mut cert_chain = Vec::new(); - for pem in parse_many(&raw_pem.as_bytes()) { - if pem.tag != PEM_CERTIFICATE_LABEL { + let mut cert_ders = Vec::new(); + for pem in pem::Pem::iter_from_buffer(&raw_pem.as_bytes()) { + let pem = match pem { + Ok(p) => p, + Err(_) => return Err(AVRError::MalformedCertificatePEM.into()), + }; + if pem.label != PEM_CERTIFICATE_LABEL { return Err(AVRError::MalformedCertificatePEM.into()); } - cert_chain.push(pem.contents); + cert_ders.push(pem.contents); } - if cert_chain.len() == 0 { - return Err(AVRError::NoCertificates.into()); + + // IAS per the API will only ever send two certificates. + if cert_ders.len() != 2 { + return Err(AVRError::ChainNotTwoCertificates.into()); } - // Decode the signature. - let signature = base64::decode(signature)?; + // Convert our timestamp to something that can be used to check + // certificate expiration. + let time = ASN1Time::from_timestamp(unix_time as i64); - let time = webpki::Time::from_seconds_since_unix_epoch(unix_time); + // Attestation Report Signing CA Certificate: + // + // Ensure that it matches the hard-coded copy, and decode it, so + // that the expiration can be validated and the public key can + // be used to verify the leaf certificate's signature. + // + // This could be more paranoid and check that the cert doesn't + // have trailing garbage, the usage is correct, etc, but we can + // take it as a matter of faith that it is well-formed since + // it is the same as the hard-coded one. + // + // TODO/perf: In theory this can be done once and only once, but + // the borrow checker thwarted my attempts to initialize a tuple + // containing a X509Certificate and Pem via lazy_static. + if cert_ders[1] != *IAS_TRUST_ANCHOR { + return Err(anyhow!("AVR certificate chain trust anchor mismatch")); + } + let anchor = match parse_x509_certificate(&cert_ders[1]) { + Ok((_, cert)) => cert, + Err(_) => return Err(AVRError::MalformedCertificateDER.into()), + }; + if !anchor.validity().is_valid_at(time) { + return Err(AVRError::ExpiredCertificate.into()); + } + let anchor_pk = extract_certificate_rsa_public_key(&anchor)?; + if !check_certificate_rsa_signature(&anchor, &anchor_pk) { + // The hard-coded cert is self-signed. This will need to be + // changed if it ever isn't. + return Err(anyhow!( + "AVR certificate chain trust anchor has invalid signature" + )); + } + if !anchor.tbs_certificate.is_ca() { + return Err(anyhow!("AVR certificate trust anchor is not a CA")); + } - // Do all the actual validation. - match validate_decoded_avr_signature(&anchors, &cert_chain, message, signature, time) { + // Attestation Report Signing Certificate (leaf): + // + // Decode the certificate, ensure that it appears to be sensible, + // and then pull out the public key that presumably signs the AVR. + let leaf = match parse_x509_certificate(&cert_ders[0]) { + Ok((rem, cert)) => { + if !rem.is_empty() { + return Err(AVRError::MalformedCertificateDER.into()); + } + cert + } + Err(_) => return Err(AVRError::MalformedCertificateDER.into()), + }; + if !check_certificate_rsa_signature(&leaf, &anchor_pk) { + return Err(anyhow!("invalid leaf certificate signature")); + } + + if !leaf.validity().is_valid_at(time) { + return Err(AVRError::ExpiredCertificate.into()); + } + match leaf.tbs_certificate.key_usage() { + Some(ku) => { + if !ku.1.digital_signature() { + return Err(anyhow!("leaf certificate can't sign")); + } + } + None => { + return Err(anyhow!("leaf cert missing key usage")); + } + } + + // Validate the actual signature. + let leaf_pk = extract_certificate_rsa_public_key(&leaf)?; + let padding = PaddingScheme::new_pkcs1v15_sign(Some(Hash::SHA2_256)); + let digest = Sha256::new().chain(message).finalize(); + let signature = base64::decode(signature)?; + match leaf_pk.verify(padding, &digest, &signature) { Ok(_) => Ok(()), - Err(err) => Err(anyhow!("Failed to validate AVR signature: {:?}", err)), + Err(_) => return Err(AVRError::InvalidSignature.into()), } } -fn validate_decoded_avr_signature( - anchors: &webpki::TLSServerTrustAnchors, - cert_ders: &Vec>, - message: &[u8], - signature: Vec, - time: webpki::Time, -) -> Result<()> { - assert!(cert_ders.len() >= 1); - let (cert_der, inter_ders) = cert_ders.split_at(1); - let inter_ders: Vec<_> = inter_ders.iter().map(|der| &der[..]).collect(); - let cert = webpki::EndEntityCert::from(&cert_der[0])?; - cert.verify_is_valid_tls_server_cert(IAS_SIG_ALGS, &anchors, &inter_ders, time)?; - Ok(cert.verify_signature(IAS_SIG_ALGS[0], message, &signature)?) +fn extract_certificate_rsa_public_key(cert: &X509Certificate) -> Result { + let cert_spki = &cert.tbs_certificate.subject_pki; + if cert_spki.algorithm.algorithm != OID_PKCS1_RSAENCRYPTION { + return Err(anyhow!("invalid certificate public key algorithm")); + } + + match RSAPublicKey::from_pkcs1(cert_spki.subject_public_key.data) { + Ok(pk) => Ok(pk), + Err(err) => return Err(anyhow!("invalid certificate public key: {:?}", err)), + } +} + +fn check_certificate_rsa_signature(cert: &X509Certificate, public_key: &RSAPublicKey) -> bool { + if cert.signature_algorithm.algorithm != OID_PKCS1_SHA256WITHRSA { + return false; + } + let padding = PaddingScheme::new_pkcs1v15_sign(Some(Hash::SHA2_256)); + let digest = Sha256::new() + .chain(cert.tbs_certificate.as_ref()) + .finalize(); + + public_key + .verify(padding, &digest, &cert.signature_value.data) + .is_ok() } /// Return true iff the (POXIX) timestamp is considered "fresh" for the purposes diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 745b888ed5a..241e5fead8b 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -36,14 +36,15 @@ extern crate slog_stdlog; #[macro_use] extern crate intrusive_collections; extern crate io_context; -extern crate pem; +extern crate oid_registry; extern crate percent_encoding; extern crate rand; +extern crate rsa; extern crate rustc_hex; extern crate snow; #[cfg(test)] extern crate tempfile; -extern crate webpki; +extern crate x509_parser; use lazy_static::lazy_static; #[cfg(target_env = "sgx")]