diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 93e8fc1..82d0005 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,6 +10,11 @@ jobs: - name: Checkout code uses: actions/checkout@v3 + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y pkg-config libtss2-dev + - name: Set up Rust uses: dtolnay/rust-toolchain@stable with: diff --git a/Cargo.lock b/Cargo.lock index 2af1c6b..d9f2237 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aho-corasick" version = "1.1.4" @@ -85,7 +91,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror", + "thiserror 2.0.17", "time", ] @@ -141,6 +147,8 @@ version = "0.1.0" dependencies = [ "anyhow", "axum", + "az-tdx-vtpm", + "base64 0.22.1", "bytes", "clap", "configfs-tsm", @@ -150,6 +158,8 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", + "josekit", + "openssl", "parity-scale-codec", "pem-rfc7468", "rand_core 0.6.4", @@ -160,11 +170,12 @@ dependencies = [ "serde_json", "sha2", "tdx-quote", - "thiserror", + "thiserror 2.0.17", "tokio", "tokio-rustls", "tracing", "tracing-subscriber", + "tss-esapi", "webpki-roots", "x509-parser", ] @@ -227,24 +238,142 @@ dependencies = [ "tracing", ] +[[package]] +name = "az-cvm-vtpm" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3d0900c6757c9674b05b0479236458297026e25fb505186dc8d7735091a21c" +dependencies = [ + "bincode 1.3.3", + "jsonwebkey", + "memoffset", + "openssl", + "serde", + "serde-big-array", + "serde_json", + "sev", + "sha2", + "thiserror 2.0.17", + "tss-esapi", + "zerocopy", +] + +[[package]] +name = "az-tdx-vtpm" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04849677b3c0704d4593d89940cde0dc0caad2202bf9fb29352e153782b91ff8" +dependencies = [ + "az-cvm-vtpm", + "base64-url", + "bincode 1.3.3", + "serde", + "serde_json", + "thiserror 2.0.17", + "ureq", + "zerocopy", +] + [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-url" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5b428e9fb429c6fda7316e9b006f993e6b4c33005e4659339fb5214479dddec" +dependencies = [ + "base64 0.22.1", +] + [[package]] name = "base64ct" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bincode" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "bincode_derive", + "serde", + "unty", +] + +[[package]] +name = "bincode_derive" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" +dependencies = [ + "virtue", +] + +[[package]] +name = "bitfield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" + +[[package]] +name = "bitfield" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21ba6517c6b0f2bf08be60e187ab64b038438f22dd755614d8fe4d4098c46419" +dependencies = [ + "bitfield-macros", +] + +[[package]] +name = "bitfield-macros" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f48d6ace212fdf1b45fd6b566bb40808415344642b76c3224c07c8df9da81e97" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.10.0" @@ -391,6 +520,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +[[package]] +name = "codicon" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12170080f3533d6f09a19f81596f836854d0fa4867dc32c8172b8474b4e9de61" + [[package]] name = "colorchoice" version = "1.0.4" @@ -438,6 +573,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "critical-section" version = "1.2.0" @@ -530,7 +674,7 @@ checksum = "435989ce7ba46ba3f837f9df3c8139469e72ae810e707893b19f8b6b370d14ef" dependencies = [ "anyhow", "asn1_der", - "base64", + "base64 0.22.1", "borsh", "byteorder", "chrono", @@ -644,6 +788,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -655,6 +820,30 @@ dependencies = [ "syn", ] +[[package]] +name = "dummy-attestation-server" +version = "0.1.0" +dependencies = [ + "anyhow", + "attested-tls-proxy", + "axum", + "clap", + "configfs-tsm", + "hex", + "parity-scale-codec", + "rcgen", + "reqwest", + "rustls-pemfile", + "serde", + "serde_json", + "thiserror 2.0.17", + "tokio", + "tokio-rustls", + "tracing", + "tracing-subscriber", + "webpki-roots", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -721,6 +910,26 @@ dependencies = [ "syn", ] +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -755,12 +964,37 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" +[[package]] +name = "flate2" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -969,7 +1203,7 @@ dependencies = [ "once_cell", "rand 0.9.2", "ring", - "thiserror", + "thiserror 2.0.17", "tinyvec", "tokio", "tracing", @@ -992,7 +1226,7 @@ dependencies = [ "rand 0.9.2", "resolv-conf", "smallvec", - "thiserror", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -1006,6 +1240,12 @@ dependencies = [ "digest", ] +[[package]] +name = "hostname-validator" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" + [[package]] name = "http" version = "1.3.1" @@ -1098,7 +1338,7 @@ version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-core", @@ -1239,6 +1479,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "iocuddle" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8972d5be69940353d5347a1344cb375d9b457d6809b428b05bb1ca2fb9ce007" + [[package]] name = "ipconfig" version = "0.3.2" @@ -1279,6 +1525,23 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "josekit" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a808e078330e6af222eb0044b71d4b1ff981bfef43e7bc8133a88234e0c86a0c" +dependencies = [ + "anyhow", + "base64 0.22.1", + "flate2", + "openssl", + "regex", + "serde", + "serde_json", + "thiserror 2.0.17", + "time", +] + [[package]] name = "js-sys" version = "0.3.82" @@ -1289,6 +1552,23 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebkey" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c57c852b14147e2bd58c14fde40398864453403ef632b1101db130282ee6e2cc" +dependencies = [ + "base64 0.13.1", + "bitflags 1.3.2", + "generic-array", + "num-bigint", + "serde", + "serde_json", + "thiserror 1.0.69", + "yasna 0.4.0", + "zeroize", +] + [[package]] name = "k256" version = "0.13.4" @@ -1322,6 +1602,16 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +[[package]] +name = "libredox" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +dependencies = [ + "bitflags 2.10.0", + "libc", +] + [[package]] name = "litemap" version = "0.8.1" @@ -1364,12 +1654,31 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" +[[package]] +name = "mbox" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d142aeadbc4e8c679fc6d93fbe7efe1c021fa7d80629e615915b519e3bc6de" +dependencies = [ + "libc", + "stable_deref_trait", +] + [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.17" @@ -1382,6 +1691,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.1.0" @@ -1462,6 +1781,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -1492,6 +1822,15 @@ dependencies = [ "libm", ] +[[package]] +name = "oid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c19903c598813dba001b53beeae59bb77ad4892c5c1b9b3500ce4293a0d06c2" +dependencies = [ + "serde", +] + [[package]] name = "oid-registry" version = "0.8.1" @@ -1517,6 +1856,60 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags 2.10.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-src" +version = "300.5.4+3.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "p256" version = "0.13.2" @@ -1611,7 +2004,7 @@ version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" dependencies = [ - "base64", + "base64 0.22.1", "serde_core", ] @@ -1630,6 +2023,41 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "picky-asn1" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "295eea0f33c16be21e2a98b908fdd4d73c04dd48c8480991b76dbcf0cb58b212" +dependencies = [ + "oid", + "serde", + "serde_bytes", +] + +[[package]] +name = "picky-asn1-der" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5df7873a9e36d42dadb393bea5e211fe83d793c172afad5fb4ec846ec582793f" +dependencies = [ + "picky-asn1", + "serde", + "serde_bytes", +] + +[[package]] +name = "picky-asn1-x509" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c5f20f71a68499ff32310f418a6fad8816eac1a2859ed3f0c5c741389dd6208" +dependencies = [ + "base64 0.21.7", + "oid", + "picky-asn1", + "picky-asn1-der", + "serde", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1663,6 +2091,12 @@ dependencies = [ "spki", ] +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "portable-atomic" version = "1.11.1" @@ -1734,7 +2168,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2 0.6.1", - "thiserror", + "thiserror 2.0.17", "tokio", "tracing", "web-time", @@ -1755,7 +2189,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -1864,7 +2298,16 @@ dependencies = [ "ring", "rustls-pki-types", "time", - "yasna", + "yasna 0.5.2", +] + +[[package]] +name = "rdrand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92195228612ac8eed47adbc2ed0f04e513a4ccb98175b6f2bd04d963b533655" +dependencies = [ + "rand_core 0.6.4", ] [[package]] @@ -1873,7 +2316,30 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.10.0", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 2.0.17", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", ] [[package]] @@ -1899,7 +2365,7 @@ version = "0.12.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ - "base64", + "base64 0.22.1", "bytes", "futures-channel", "futures-core", @@ -2137,6 +2603,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-big-array" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +dependencies = [ + "serde", +] + [[package]] name = "serde-human-bytes" version = "0.1.1" @@ -2147,6 +2622,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -2204,6 +2689,32 @@ dependencies = [ "serde", ] +[[package]] +name = "sev" +version = "6.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420c6c161b5d6883d8195584a802b114af6c884ed56d937d994e30f7f81d54ec" +dependencies = [ + "base64 0.22.1", + "bincode 2.0.1", + "bitfield 0.19.4", + "bitflags 2.10.0", + "byteorder", + "codicon", + "dirs", + "hex", + "iocuddle", + "lazy_static", + "libc", + "openssl", + "rdrand", + "serde", + "serde-big-array", + "serde_bytes", + "static_assertions", + "uuid", +] + [[package]] name = "sha2" version = "0.10.9" @@ -2249,6 +2760,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "slab" version = "0.4.11" @@ -2303,6 +2820,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[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.11.1" @@ -2358,6 +2881,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "tdx-quote" version = "0.0.4" @@ -2372,13 +2901,33 @@ dependencies = [ "x509-verify", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2560,7 +3109,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags", + "bitflags 2.10.0", "bytes", "futures-util", "http", @@ -2665,6 +3214,39 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tss-esapi" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ea9ccde878b029392ac97b5be1f470173d06ea41d18ad0bb3c92794c16a0f2" +dependencies = [ + "bitfield 0.14.0", + "enumflags2", + "getrandom 0.2.16", + "hostname-validator", + "log", + "mbox", + "num-derive", + "num-traits", + "oid", + "picky-asn1", + "picky-asn1-x509", + "regex", + "serde", + "tss-esapi-sys", + "zeroize", +] + +[[package]] +name = "tss-esapi-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "535cd192581c2ec4d5f82e670b1d3fbba6a23ccce8c85de387642051d7cad5b5" +dependencies = [ + "pkg-config", + "target-lexicon", +] + [[package]] name = "typenum" version = "1.19.0" @@ -2689,6 +3271,26 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64 0.22.1", + "log", + "once_cell", + "serde", + "serde_json", + "url", +] + [[package]] name = "url" version = "2.5.7" @@ -2727,6 +3329,7 @@ checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ "getrandom 0.3.4", "js-sys", + "serde", "wasm-bindgen", ] @@ -2736,12 +3339,24 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "virtue" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" + [[package]] name = "want" version = "0.3.1" @@ -3163,7 +3778,7 @@ dependencies = [ "nom", "oid-registry", "rusticata-macros", - "thiserror", + "thiserror 2.0.17", "time", ] @@ -3189,6 +3804,15 @@ dependencies = [ "x509-ocsp", ] +[[package]] +name = "yasna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e262a29d0e61ccf2b6190d7050d4b237535fc76ce4c1210d9caa316f71dffa75" +dependencies = [ + "num-bigint", +] + [[package]] name = "yasna" version = "0.5.2" @@ -3267,6 +3891,20 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "zerotrie" diff --git a/Cargo.toml b/Cargo.toml index 4c62518..28b8486 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ +[workspace] +members = [".", "dummy-attestation-server"] + [package] name = "attested-tls-proxy" version = "0.1.0" @@ -26,12 +29,17 @@ http-body-util = "0.1.3" bytes = "1.10.1" http = "1.3.1" serde_json = "1.0.145" +az-tdx-vtpm = "0.7.4" serde = "1.0.228" +base64 = "0.22.1" +reqwest = { version = "0.12.23", default-features = false, features = ["rustls-tls-webpki-roots-no-provider"] } +josekit = "0.10.3" tracing = "0.1.41" tracing-subscriber = { version = "0.3.20", features = ["env-filter", "json"] } parity-scale-codec = "3.7.5" +openssl = "0.10.75" +tss-esapi = "7.6.0" [dev-dependencies] rcgen = "0.14.5" axum = "0.8.6" -reqwest = { version = "0.12.23", default-features = false, features = ["rustls-tls-webpki-roots-no-provider"] } diff --git a/dummy-attestation-server/Cargo.toml b/dummy-attestation-server/Cargo.toml new file mode 100644 index 0000000..10c5379 --- /dev/null +++ b/dummy-attestation-server/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "dummy-attestation-server" +version = "0.1.0" +edition = "2024" +license = "MIT" +publish = false + +[dependencies] +attested-tls-proxy = { path = ".." } +tokio = { version = "1.48.0", features = ["full"] } +axum = "0.8.6" +tokio-rustls = { version = "0.26.4", default-features = false, features = ["ring"] } +thiserror = "2.0.17" +clap = { version = "4.5.51", features = ["derive", "env"] } +webpki-roots = "1.0.4" +rustls-pemfile = "2.2.0" +anyhow = "1.0.100" +configfs-tsm = "0.0.2" +hex = "0.4.3" +serde_json = "1.0.145" +serde = "1.0.228" +tracing = "0.1.41" +tracing-subscriber = { version = "0.3.20", features = ["env-filter", "json"] } +rcgen = "0.14.5" +parity-scale-codec = "3.7.5" +reqwest = { version = "0.12.23", default-features = false } + +[dev-dependencies] diff --git a/dummy-attestation-server/src/lib.rs b/dummy-attestation-server/src/lib.rs new file mode 100644 index 0000000..9181b44 --- /dev/null +++ b/dummy-attestation-server/src/lib.rs @@ -0,0 +1,131 @@ +use std::{ + net::{IpAddr, SocketAddr}, + sync::Arc, +}; + +use attested_tls_proxy::{attestation::AttestationExchangeMessage, QuoteGenerator}; +use axum::{ + extract::{Path, State}, + http::StatusCode, + response::{IntoResponse, Response}, +}; +use parity_scale_codec::{Decode, Encode}; +use tokio::net::TcpListener; +use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer}; + +#[derive(Clone)] +struct SharedState { + attestation_generator: Arc, +} + +pub async fn dummy_attestation_server( + listener: TcpListener, + attestation_generator: Arc, +) -> anyhow::Result { + let addr = listener.local_addr()?; + + let app = axum::Router::new() + .route("/attest/{input_data}", axum::routing::get(get_attest)) + .with_state(SharedState { + attestation_generator, + }); + + tokio::spawn(async move { + axum::serve(listener, app).await.unwrap(); + }); + + Ok(addr) +} + +async fn get_attest( + State(shared_state): State, + Path(input_data): Path, +) -> Result<(StatusCode, Vec), ServerError> { + let (cert_chain, _) = generate_certificate_chain("0.0.0.0".parse().unwrap()); + let input_data: [u8; 64] = hex::decode(input_data).unwrap().try_into().unwrap(); + + let attestation = AttestationExchangeMessage::from_attestation_generator( + &cert_chain, + input_data[..32].try_into().unwrap(), + shared_state.attestation_generator, + )? + .encode(); + + Ok((StatusCode::OK, attestation)) +} + +pub async fn dummy_attestation_client(server_addr: SocketAddr) -> anyhow::Result<()> { + let input_data = [0; 64]; + let response = reqwest::get(format!( + "http://{server_addr}/attest/{}", + hex::encode(input_data) + )) + .await + .unwrap() + .bytes() + .await + .unwrap(); + + let remote_attestation_message = AttestationExchangeMessage::decode(&mut &response[..])?; + let remote_attestation_type = remote_attestation_message.attestation_type; + println!("{remote_attestation_type}"); + + // TODO validate the attestation + Ok(()) +} + +struct ServerError(pub anyhow::Error); + +impl From for ServerError +where + E: Into, +{ + fn from(err: E) -> Self { + ServerError(err.into()) + } +} + +impl IntoResponse for ServerError { + fn into_response(self) -> Response { + eprintln!("{:?}", self.0); + (StatusCode::INTERNAL_SERVER_ERROR, format!("{:?}", self.0)).into_response() + } +} + +/// Helper to generate a self-signed certificate for testing +fn generate_certificate_chain( + ip: IpAddr, +) -> (Vec>, PrivateKeyDer<'static>) { + let mut params = rcgen::CertificateParams::new(vec![]).unwrap(); + params.subject_alt_names.push(rcgen::SanType::IpAddress(ip)); + params + .distinguished_name + .push(rcgen::DnType::CommonName, ip.to_string()); + + let keypair = rcgen::KeyPair::generate().unwrap(); + let cert = params.self_signed(&keypair).unwrap(); + + let certs = vec![CertificateDer::from(cert)]; + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(keypair.serialize_der())); + (certs, key) +} + +#[cfg(test)] +mod tests { + + use attested_tls_proxy::attestation::AttestationType; + + use super::*; + + #[tokio::test] + async fn test_dummy_server() { + let attestation_generator = AttestationType::None.get_quote_generator().unwrap(); + + let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); + let server_addr = listener.local_addr().unwrap(); + dummy_attestation_server(listener, attestation_generator) + .await + .unwrap(); + dummy_attestation_client(server_addr).await.unwrap(); + } +} diff --git a/dummy-attestation-server/src/main.rs b/dummy-attestation-server/src/main.rs new file mode 100644 index 0000000..fe1946d --- /dev/null +++ b/dummy-attestation-server/src/main.rs @@ -0,0 +1,76 @@ +use attested_tls_proxy::attestation::AttestationType; +use clap::{Parser, Subcommand}; +use dummy_attestation_server::{dummy_attestation_client, dummy_attestation_server}; +use std::net::SocketAddr; +use tokio::net::TcpListener; +use tracing::level_filters::LevelFilter; + +#[derive(Parser, Debug, Clone)] +#[clap(version, about, long_about = None)] +struct Cli { + #[clap(subcommand)] + command: CliCommand, + /// Log debug messages + #[arg(long, global = true)] + log_debug: bool, + /// Log in JSON format + #[arg(long, global = true)] + log_json: bool, +} +#[derive(Subcommand, Debug, Clone)] +enum CliCommand { + Server { + /// Socket address to listen on + #[arg(short, long, default_value = "0.0.0.0:0", env = "LISTEN_ADDR")] + listen_addr: SocketAddr, + /// Type of attestation to present (defaults to none) + #[arg(long)] + server_attestation_type: Option, + }, + Client { + /// Socket address of a dummy attestation server + server_addr: SocketAddr, + }, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let cli = Cli::parse(); + + let level_filter = if cli.log_debug { + LevelFilter::DEBUG + } else { + LevelFilter::WARN + }; + + let env_filter = tracing_subscriber::EnvFilter::builder() + .with_default_directive(level_filter.into()) + .from_env_lossy(); + + let subscriber = tracing_subscriber::fmt::Subscriber::builder().with_env_filter(env_filter); + + if cli.log_json { + subscriber.json().init(); + } else { + subscriber.pretty().init(); + } + + match cli.command { + CliCommand::Server { + listen_addr, + server_attestation_type, + } => { + let server_attestation_type: AttestationType = serde_json::from_value( + serde_json::Value::String(server_attestation_type.unwrap_or("none".to_string())), + )?; + + let attestation_generator = server_attestation_type.get_quote_generator()?; + + let listener = TcpListener::bind(listen_addr).await?; + dummy_attestation_server(listener, attestation_generator).await?; + } + CliCommand::Client { server_addr } => dummy_attestation_client(server_addr).await?, + } + + Ok(()) +} diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..f852fe5 --- /dev/null +++ b/shell.nix @@ -0,0 +1,11 @@ +{ pkgs ? import {} }: + +pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + pkg-config + ]; + + buildInputs = with pkgs;[ + tpm2-tss + ]; +} diff --git a/src/attestation/azure.rs b/src/attestation/azure.rs new file mode 100644 index 0000000..a355cff --- /dev/null +++ b/src/attestation/azure.rs @@ -0,0 +1,197 @@ +//! Microsoft Azure Attestation (MAA) evidence generation and verification +use std::string::FromUtf8Error; + +use az_tdx_vtpm::{hcl, imds, report, vtpm}; +use base64::{engine::general_purpose::URL_SAFE as BASE64_URL_SAFE, Engine as _}; +use openssl::pkey::PKey; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +use crate::attestation::{ + self, + measurements::{CvmImageMeasurements, Measurements, PlatformMeasurements}, + nv_index, +}; + +const TPM_AK_CERT_IDX: u32 = 0x1C101D0; + +pub async fn create_azure_attestation(input_data: [u8; 64]) -> Result, MaaError> { + let td_report = report::get_report()?; + + // This makes a request to Azure Instance metadata service and gives us a binary response + let td_quote_bytes = imds::get_td_quote(&td_report)?; + + let hcl_report_bytes = vtpm::get_report_with_report_data(&input_data)?; + + let ak_certificate_der = read_ak_certificate_from_tpm()?; + + let tpm_attestation = TpmAttest { + ak_certificate_pem: pem_rfc7468::encode_string( + "CERTIFICATE", + pem_rfc7468::LineEnding::default(), + &ak_certificate_der, + )?, + quote: vtpm::get_quote(&input_data)?, + event_log: Vec::new(), + instance_info: None, + }; + + let attestation_document = AttestationDocument { + tdx_quote_base64: BASE64_URL_SAFE.encode(&td_quote_bytes), + hcl_report_base64: BASE64_URL_SAFE.encode(&hcl_report_bytes), + tpm_attestation, + }; + + Ok(serde_json::to_vec(&attestation_document)?) +} + +pub async fn verify_azure_attestation( + input: Vec, + expected_input_data: [u8; 64], + pccs_url: Option, +) -> Result { + let attestation_document: AttestationDocument = serde_json::from_slice(&input)?; + + // Verify TDX quote (same as with DCAP) - TODO deduplicate this code + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(); + + let tdx_quote_bytes = BASE64_URL_SAFE + .decode(attestation_document.tdx_quote_base64) + .unwrap(); + + let quote = dcap_qvl::quote::Quote::parse(&tdx_quote_bytes).unwrap(); + + let ca = quote.ca().unwrap(); + let fmspc = hex::encode_upper(quote.fmspc().unwrap()); + let collateral = dcap_qvl::collateral::get_collateral_for_fmspc( + &pccs_url + .clone() + .unwrap_or(attestation::dcap::PCS_URL.to_string()), + fmspc, + ca, + false, // Indicates not SGX + ) + .await + .unwrap(); + + let _verified_report = dcap_qvl::verify::verify(&input, &collateral, now).unwrap(); + + // Check that hcl_report_bytes (hashed?) matches TDX quote report data + // if get_quote_input_data(quote.report) != quote_input { + // return Err(AttestationError::InputMismatch); + // } + + let hcl_report_bytes = BASE64_URL_SAFE + .decode(attestation_document.hcl_report_base64) + .unwrap(); + + let hcl_report = hcl::HclReport::new(hcl_report_bytes)?; + let var_data_hash = hcl_report.var_data_sha256(); + let hcl_ak_pub = hcl_report.ak_pub()?; + let td_report: az_tdx_vtpm::tdx::TdReport = hcl_report.try_into()?; + assert!(var_data_hash == td_report.report_mac.reportdata[..32]); + + let vtpm_quote = attestation_document.tpm_attestation.quote; + let hcl_ak_pub_der = hcl_ak_pub.key.try_to_der().unwrap(); + let pub_key = PKey::public_key_from_der(&hcl_ak_pub_der).unwrap(); + vtpm_quote.verify(&pub_key, &expected_input_data)?; + let _pcrs = vtpm_quote.pcrs_sha256(); + + // TODO parse AK certificate + // Check that AK public key matches that from TPM quote + // Verify AK certificate against microsoft root cert + + Ok(Measurements { + platform: PlatformMeasurements::from_dcap_qvl_quote("e).unwrap(), + cvm_image: CvmImageMeasurements::from_dcap_qvl_quote("e).unwrap(), + }) +} + +/// The attestation evidence payload that gets sent over the channel +#[derive(Debug, Serialize, Deserialize)] +struct AttestationDocument { + /// TDX quote from the IMDS + tdx_quote_base64: String, + /// Serialized HCL report + hcl_report_base64: String, + /// vTPM related evidence + tpm_attestation: TpmAttest, +} + +#[derive(Debug, Serialize, Deserialize)] +struct TpmAttest { + /// Attestation Key certificate from vTPM + ak_certificate_pem: String, + /// vTPM quotes over the selected PCR bank(s). + quote: vtpm::Quote, + /// Raw TCG event log bytes (UEFI + IMA) + /// + /// `/sys/kernel/security/ima/ascii_runtime_measurements`, + /// `/sys/kernel/security/tpm0/binary_bios_measurements`, + event_log: Vec, + /// Optional platform / instance metadata used to bind or verify the AK + instance_info: Option>, +} + +fn read_ak_certificate_from_tpm() -> Result, tss_esapi::Error> { + let mut context = nv_index::get_session_context()?; + nv_index::read_nv_index(&mut context, TPM_AK_CERT_IDX) +} + +#[derive(Error, Debug)] +pub enum MaaError { + #[error("Failed to build input data: {0}")] + InputData(String), + #[error("Report: {0}")] + Report(#[from] az_tdx_vtpm::report::ReportError), + #[error("IMDS: {0}")] + Imds(#[from] imds::ImdsError), + #[error("vTPM report: {0}")] + VtpmReport(#[from] az_tdx_vtpm::vtpm::ReportError), + #[error("HCL: {0}")] + Hcl(#[from] hcl::HclError), + #[error("JSON: {0}")] + Json(#[from] serde_json::Error), + #[error("HTTP Client: {0}")] + HttpClient(#[from] reqwest::Error), + #[error("MAA provider response: {0} - {1}")] + MaaProvider(http::StatusCode, String), + #[error("Token is bad UTF8: {0}")] + BadUtf8(#[from] FromUtf8Error), + #[error("vTPM quote: {0}")] + VtpmQuote(#[from] vtpm::QuoteError), + #[error("AK public key: {0}")] + AkPub(#[from] vtpm::AKPubError), + #[error("vTPM quote could not be verified: {0}")] + TpmQuoteVerify(#[from] vtpm::VerifyError), + #[error("vTPM read: {0}")] + TssEsapi(#[from] tss_esapi::Error), + #[error("PEM encode: {0}")] + Pem(#[from] pem_rfc7468::Error), +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_decode_hcl() { + // from cvm-reverse-proxy/internal/attestation/azure/tdx/testdata/hclreport.bin + let hcl_bytes: &'static [u8] = include_bytes!("../../test-assets/hclreport.bin"); + + let hcl_report = hcl::HclReport::new(hcl_bytes.to_vec()).unwrap(); + let hcl_var_data = hcl_report.var_data(); + let var_data_values: serde_json::Value = serde_json::from_slice(&hcl_var_data).unwrap(); + + // Check that it contains 64 byte user data + assert_eq!( + hex::decode(var_data_values["user-data"].as_str().unwrap()) + .unwrap() + .len(), + 64 + ); + } +} diff --git a/src/attestation/dcap.rs b/src/attestation/dcap.rs new file mode 100644 index 0000000..6ed406e --- /dev/null +++ b/src/attestation/dcap.rs @@ -0,0 +1,99 @@ +//! Data Center Attestation Primitives (DCAP) evidence generation and verification +use crate::attestation::{ + measurements::{CvmImageMeasurements, Measurements, PlatformMeasurements}, + AttestationError, +}; + +use configfs_tsm::QuoteGenerationError; +use dcap_qvl::{ + collateral::get_collateral_for_fmspc, + quote::{Quote, Report}, +}; + +/// For fetching collateral directly from Intel, if no PCCS is specified +pub const PCS_URL: &str = "https://api.trustedservices.intel.com"; + +/// Quote generation using configfs_tsm +pub async fn create_dcap_attestation(input_data: [u8; 64]) -> Result, AttestationError> { + Ok(generate_quote(input_data)?) +} + +/// Verify a DCAP TDX quote, and return the measurement values +pub async fn verify_dcap_attestation( + input: Vec, + expected_input_data: [u8; 64], + pccs_url: Option, +) -> Result { + let (platform_measurements, image_measurements) = if cfg!(not(test)) { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH)? + .as_secs(); + let quote = Quote::parse(&input)?; + + let ca = quote.ca()?; + let fmspc = hex::encode_upper(quote.fmspc()?); + let collateral = get_collateral_for_fmspc( + &pccs_url.clone().unwrap_or(PCS_URL.to_string()), + fmspc, + ca, + false, // Indicates not SGX + ) + .await?; + + let _verified_report = dcap_qvl::verify::verify(&input, &collateral, now)?; + + let measurements = ( + PlatformMeasurements::from_dcap_qvl_quote("e)?, + CvmImageMeasurements::from_dcap_qvl_quote("e)?, + ); + if get_quote_input_data(quote.report) != expected_input_data { + return Err(AttestationError::InputMismatch); + } + measurements + } else { + // In tests we use mock quotes which will fail to verify + let quote = tdx_quote::Quote::from_bytes(&input)?; + if quote.report_input_data() != expected_input_data { + return Err(AttestationError::InputMismatch); + } + + ( + PlatformMeasurements::from_tdx_quote("e), + CvmImageMeasurements::from_tdx_quote("e), + ) + }; + + Ok(Measurements { + platform: platform_measurements, + cvm_image: image_measurements, + }) +} + +/// Create a mock quote for testing on non-confidential hardware +#[cfg(test)] +fn generate_quote(input: [u8; 64]) -> Result, QuoteGenerationError> { + let attestation_key = tdx_quote::SigningKey::random(&mut rand_core::OsRng); + let provisioning_certification_key = tdx_quote::SigningKey::random(&mut rand_core::OsRng); + Ok(tdx_quote::Quote::mock( + attestation_key.clone(), + provisioning_certification_key.clone(), + input, + b"Mock cert chain".to_vec(), + ) + .as_bytes()) +} + +/// Create a quote +#[cfg(not(test))] +fn generate_quote(input: [u8; 64]) -> Result, QuoteGenerationError> { + configfs_tsm::create_quote(input) +} + +/// Given a [Report] get the input data regardless of report type +fn get_quote_input_data(report: Report) -> [u8; 64] { + match report { + Report::TD10(r) => r.report_data, + Report::TD15(r) => r.base.report_data, + Report::SgxEnclave(r) => r.report_data, + } +} diff --git a/src/attestation/mod.rs b/src/attestation/mod.rs index 87b7c02..727dead 100644 --- a/src/attestation/mod.rs +++ b/src/attestation/mod.rs @@ -1,27 +1,18 @@ +pub mod azure; +pub mod dcap; pub mod measurements; +pub mod nv_index; -use measurements::{CvmImageMeasurements, MeasurementRecord, Measurements, PlatformMeasurements}; +use measurements::{MeasurementRecord, Measurements}; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; use std::{ fmt::{self, Display, Formatter}, - sync::Arc, time::SystemTimeError, }; -use configfs_tsm::QuoteGenerationError; -use dcap_qvl::{ - collateral::get_collateral_for_fmspc, - quote::{Quote, Report}, -}; -use sha2::{Digest, Sha256}; use tdx_quote::QuoteParseError; use thiserror::Error; -use tokio_rustls::rustls::pki_types::CertificateDer; -use x509_parser::prelude::*; - -/// For fetching collateral directly from intel, if no PCCS is specified -const PCS_URL: &str = "https://api.trustedservices.intel.com"; /// This is the type sent over the channel to provide an attestation #[derive(Debug, Serialize, Deserialize, Encode, Decode)] @@ -33,20 +24,6 @@ pub struct AttestationExchangeMessage { } impl AttestationExchangeMessage { - /// Given an attestation generator (quote generation function for a specific platform) - /// return an attestation - /// This also takes the certificate chain and exporter as they are given as input to the attestation - pub fn from_attestation_generator( - cert_chain: &[CertificateDer<'_>], - exporter: [u8; 32], - attesation_generator: Arc, - ) -> Result { - Ok(Self { - attestation_type: attesation_generator.attestation_type(), - attestation: attesation_generator.create_attestation(cert_chain, exporter)?, - }) - } - /// Create an empty attestation payload for the case that we are running in a non-confidential /// environment pub fn without_attestation() -> Self { @@ -88,18 +65,6 @@ impl AttestationType { AttestationType::DcapTdx => "dcap-tdx", } } - - /// Get a quote generator for this type of platform - pub fn get_quote_generator(&self) -> Result, AttestationError> { - match self { - AttestationType::None => Ok(Arc::new(NoQuoteGenerator)), - AttestationType::AzureTdx => Err(AttestationError::AttestationTypeNotSupported), - AttestationType::Dummy => Err(AttestationError::AttestationTypeNotSupported), - _ => Ok(Arc::new(DcapTdxQuoteGenerator { - attestation_type: *self, - })), - } - } } /// SCALE encode (used over the wire) @@ -125,17 +90,36 @@ impl Display for AttestationType { } } -/// Defines how to generate a quote -pub trait QuoteGenerator: Send + Sync + 'static { - /// Type of attestation used - fn attestation_type(&self) -> AttestationType; +/// Can generate a local attestation based on attestation type +#[derive(Clone)] +pub struct AttestationGenerator { + pub attestation_type: AttestationType, +} + +impl AttestationGenerator { + /// Generate an attestation exchange message + pub async fn generate_attestation( + &self, + input_data: [u8; 64], + ) -> Result { + Ok(AttestationExchangeMessage { + attestation_type: self.attestation_type, + attestation: self.generate_attestation_bytes(input_data).await?, + }) + } - /// Generate an attestation - fn create_attestation( + /// Generate attestation evidence bytes based on attestation type + async fn generate_attestation_bytes( &self, - cert_chain: &[CertificateDer<'_>], - exporter: [u8; 32], - ) -> Result, AttestationError>; + input_data: [u8; 64], + ) -> Result, AttestationError> { + match self.attestation_type { + AttestationType::None => Ok(Vec::new()), + AttestationType::AzureTdx => Ok(azure::create_azure_attestation(input_data).await?), + AttestationType::Dummy => Err(AttestationError::AttestationTypeNotSupported), + _ => dcap::create_dcap_attestation(input_data).await, + } + } } /// Allows remote attestations to be verified @@ -167,11 +151,11 @@ impl AttestationVerifier { attestation_type: AttestationType::DcapTdx, measurement_id: "test".to_string(), measurements: Measurements { - platform: PlatformMeasurements { + platform: measurements::PlatformMeasurements { mrtd: [0; 48], rtmr0: [0; 48], }, - cvm_image: CvmImageMeasurements { + cvm_image: measurements::CvmImageMeasurements { rtmr1: [0; 48], rtmr2: [0; 48], rtmr3: [0; 48], @@ -186,21 +170,11 @@ impl AttestationVerifier { pub async fn verify_attestation( &self, attestation_exchange_message: AttestationExchangeMessage, - cert_chain: &[CertificateDer<'_>], - exporter: [u8; 32], + expected_input_data: [u8; 64], ) -> Result, AttestationError> { let attestation_type = attestation_exchange_message.attestation_type; let measurements = match attestation_type { - AttestationType::DcapTdx => { - verify_dcap_attestation( - attestation_exchange_message.attestation, - cert_chain, - exporter, - self.pccs_url.clone(), - ) - .await? - } AttestationType::None => { if self.has_remote_attestion() { return Err(AttestationError::AttestationTypeNotAccepted); @@ -211,9 +185,25 @@ impl AttestationVerifier { return Err(AttestationError::AttestationGivenWhenNoneExpected); } } - _ => { + AttestationType::AzureTdx => { + azure::verify_azure_attestation( + attestation_exchange_message.attestation, + expected_input_data, + self.pccs_url.clone(), + ) + .await? + } + AttestationType::Dummy => { return Err(AttestationError::AttestationTypeNotSupported); } + _ => { + dcap::verify_dcap_attestation( + attestation_exchange_message.attestation, + expected_input_data, + self.pccs_url.clone(), + ) + .await? + } }; // look through all our accepted measurements @@ -231,159 +221,6 @@ impl AttestationVerifier { } } -/// Quote generation using configfs_tsm -#[derive(Clone)] -pub struct DcapTdxQuoteGenerator { - pub attestation_type: AttestationType, -} - -impl QuoteGenerator for DcapTdxQuoteGenerator { - /// Type of attestation used - fn attestation_type(&self) -> AttestationType { - self.attestation_type - } - - fn create_attestation( - &self, - cert_chain: &[CertificateDer<'_>], - exporter: [u8; 32], - ) -> Result, AttestationError> { - let quote_input = compute_report_input(cert_chain, exporter)?; - - Ok(generate_quote(quote_input)?) - } -} - -/// Verify DCAP TDX quotes, allowing them if they have one of a given set of platform-specific and -/// OS image specific measurements -async fn verify_dcap_attestation( - input: Vec, - cert_chain: &[CertificateDer<'_>], - exporter: [u8; 32], - pccs_url: Option, -) -> Result { - let quote_input = compute_report_input(cert_chain, exporter)?; - let (platform_measurements, image_measurements) = if cfg!(not(test)) { - let now = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH)? - .as_secs(); - let quote = Quote::parse(&input)?; - - let ca = quote.ca()?; - let fmspc = hex::encode_upper(quote.fmspc()?); - let collateral = get_collateral_for_fmspc( - &pccs_url.clone().unwrap_or(PCS_URL.to_string()), - fmspc, - ca, - false, // Indicates not SGX - ) - .await?; - - let _verified_report = dcap_qvl::verify::verify(&input, &collateral, now)?; - - let measurements = ( - PlatformMeasurements::from_dcap_qvl_quote("e)?, - CvmImageMeasurements::from_dcap_qvl_quote("e)?, - ); - if get_quote_input_data(quote.report) != quote_input { - return Err(AttestationError::InputMismatch); - } - measurements - } else { - // In tests we use mock quotes which will fail to verify - let quote = tdx_quote::Quote::from_bytes(&input)?; - if quote.report_input_data() != quote_input { - return Err(AttestationError::InputMismatch); - } - - ( - PlatformMeasurements::from_tdx_quote("e), - CvmImageMeasurements::from_tdx_quote("e), - ) - }; - - Ok(Measurements { - platform: platform_measurements, - cvm_image: image_measurements, - }) -} - -/// Given a [Report] get the input data regardless of report type -fn get_quote_input_data(report: Report) -> [u8; 64] { - match report { - Report::TD10(r) => r.report_data, - Report::TD15(r) => r.base.report_data, - Report::SgxEnclave(r) => r.report_data, - } -} - -/// Given a certificate chain and an exporter (session key material), build the quote input value -/// SHA256(pki) || exporter -pub fn compute_report_input( - cert_chain: &[CertificateDer<'_>], - exporter: [u8; 32], -) -> Result<[u8; 64], AttestationError> { - let mut quote_input = [0u8; 64]; - let pki_hash = get_pki_hash_from_certificate_chain(cert_chain)?; - quote_input[..32].copy_from_slice(&pki_hash); - quote_input[32..].copy_from_slice(&exporter); - Ok(quote_input) -} - -/// For no CVM platform (eg: for one-sided remote-attested TLS) -#[derive(Clone)] -pub struct NoQuoteGenerator; - -impl QuoteGenerator for NoQuoteGenerator { - /// Type of attestation used - fn attestation_type(&self) -> AttestationType { - AttestationType::None - } - - /// Create an empty attestation - fn create_attestation( - &self, - _cert_chain: &[CertificateDer<'_>], - _exporter: [u8; 32], - ) -> Result, AttestationError> { - Ok(Vec::new()) - } -} - -/// Create a mock quote for testing on non-confidential hardware -#[cfg(test)] -fn generate_quote(input: [u8; 64]) -> Result, QuoteGenerationError> { - let attestation_key = tdx_quote::SigningKey::random(&mut rand_core::OsRng); - let provisioning_certification_key = tdx_quote::SigningKey::random(&mut rand_core::OsRng); - Ok(tdx_quote::Quote::mock( - attestation_key.clone(), - provisioning_certification_key.clone(), - input, - b"Mock cert chain".to_vec(), - ) - .as_bytes()) -} - -/// Create a quote -#[cfg(not(test))] -fn generate_quote(input: [u8; 64]) -> Result, QuoteGenerationError> { - configfs_tsm::create_quote(input) -} - -/// Given a certificate chain, get the [Sha256] hash of the public key of the leaf certificate -fn get_pki_hash_from_certificate_chain( - cert_chain: &[CertificateDer<'_>], -) -> Result<[u8; 32], AttestationError> { - let leaf_certificate = cert_chain.first().ok_or(AttestationError::NoCertificate)?; - let (_, cert) = parse_x509_certificate(leaf_certificate.as_ref())?; - let public_key = &cert.tbs_certificate.subject_pki; - let key_bytes = public_key.subject_public_key.as_ref(); - - let mut hasher = Sha256::new(); - hasher.update(key_bytes); - Ok(hasher.finalize().into()) -} - /// An error when generating or verifying an attestation #[derive(Error, Debug)] pub enum AttestationError { @@ -417,4 +254,6 @@ pub enum AttestationError { AttestationTypeNotAccepted, #[error("Measurements not accepted")] MeasurementsNotAccepted, + #[error("MAA: {0}")] + Maa(#[from] azure::MaaError), } diff --git a/src/attestation/nv_index.rs b/src/attestation/nv_index.rs new file mode 100644 index 0000000..40b325a --- /dev/null +++ b/src/attestation/nv_index.rs @@ -0,0 +1,28 @@ +use tss_esapi::{ + handles::NvIndexHandle, + interface_types::{resource_handles::NvAuth, session_handles::AuthSession}, + structures::MaxNvBuffer, + tcti_ldr::{DeviceConfig, TctiNameConf}, + Context, +}; + +pub fn get_session_context() -> Result { + let conf: TctiNameConf = TctiNameConf::Device(DeviceConfig::default()); + let mut context = Context::new(conf)?; + let auth_session = AuthSession::Password; + context.set_sessions((Some(auth_session), None, None)); + Ok(context) +} + +pub fn read_nv_index(ctx: &mut Context, index: u32) -> Result, tss_esapi::Error> { + let handle = NvIndexHandle::from(index); + let size = ctx + .nv_read_public(handle)? + .0 + .data_size() + .try_into() + .unwrap_or(0u16); + + let data: MaxNvBuffer = ctx.nv_read(NvAuth::Owner, handle, size, 0)?; + Ok(data.to_vec()) +} diff --git a/src/lib.rs b/src/lib.rs index 546fe1c..b748526 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,17 +1,19 @@ pub mod attestation; +pub use attestation::AttestationGenerator; use attestation::{measurements::Measurements, AttestationError, AttestationType}; -pub use attestation::{DcapTdxQuoteGenerator, NoQuoteGenerator, QuoteGenerator}; use bytes::Bytes; use http::HeaderValue; use http_body_util::{combinators::BoxBody, BodyExt}; use hyper::{service::service_fn, Response}; use hyper_util::rt::TokioIo; use parity_scale_codec::{Decode, Encode}; +use sha2::{Digest, Sha256}; use thiserror::Error; use tokio::sync::{mpsc, oneshot}; use tokio_rustls::rustls::server::{VerifierBuilderError, WebPkiClientVerifier}; use tracing::{error, warn}; +use x509_parser::parse_x509_certificate; #[cfg(test)] mod test_helpers; @@ -66,7 +68,7 @@ pub struct ProxyServer { /// The underlying TCP listener listener: TcpListener, /// Quote generation type to use (including none) - local_quote_generator: Arc, + attestation_generator: AttestationGenerator, /// Verifier for remote attestation (including none) attestation_verifier: AttestationVerifier, /// The certificate chain @@ -82,13 +84,15 @@ impl ProxyServer { cert_and_key: TlsCertAndKey, local: impl ToSocketAddrs, target: SocketAddr, - local_quote_generator: Arc, + attestation_generator: AttestationGenerator, attestation_verifier: AttestationVerifier, client_auth: bool, ) -> Result { + println!("here"); if attestation_verifier.has_remote_attestion() && !client_auth { return Err(ProxyError::NoClientAuth); } + println!("here2"); let mut server_config = if client_auth { let root_store = @@ -114,7 +118,7 @@ impl ProxyServer { server_config.into(), local, target, - local_quote_generator, + attestation_generator, attestation_verifier, ) .await @@ -128,7 +132,7 @@ impl ProxyServer { server_config: Arc, local: impl ToSocketAddrs, target: SocketAddr, - local_quote_generator: Arc, + attestation_generator: AttestationGenerator, attestation_verifier: AttestationVerifier, ) -> Result { let acceptor = tokio_rustls::TlsAcceptor::from(server_config); @@ -136,7 +140,7 @@ impl ProxyServer { Ok(Self { listener, - local_quote_generator, + attestation_generator, attestation_verifier, acceptor, target, @@ -151,7 +155,7 @@ impl ProxyServer { let acceptor = self.acceptor.clone(); let target = self.target; let cert_chain = self.cert_chain.clone(); - let local_quote_generator = self.local_quote_generator.clone(); + let attestation_generator = self.attestation_generator.clone(); let attestation_verifier = self.attestation_verifier.clone(); tokio::spawn(async move { if let Err(err) = Self::handle_connection( @@ -159,7 +163,7 @@ impl ProxyServer { acceptor, target, cert_chain, - local_quote_generator, + attestation_generator, attestation_verifier, ) .await @@ -182,7 +186,7 @@ impl ProxyServer { acceptor: TlsAcceptor, target: SocketAddr, cert_chain: Vec>, - local_quote_generator: Arc, + attestation_generator: AttestationGenerator, attestation_verifier: AttestationVerifier, ) -> Result<(), ProxyError> { // Do TLS handshake @@ -200,16 +204,16 @@ impl ProxyServer { None, // context )?; + let input_data = compute_report_input(&cert_chain, exporter)?; + // Get the TLS certficate chain of the client, if there is one let remote_cert_chain = connection.peer_certificates().map(|c| c.to_owned()); // If we are in a CVM, generate an attestation - let attestation = AttestationExchangeMessage::from_attestation_generator( - &cert_chain, - exporter, - local_quote_generator, - )? - .encode(); + let attestation = attestation_generator + .generate_attestation(input_data) + .await? + .encode(); // Write our attestation to the channel, with length prefix let attestation_length_prefix = length_prefix(&attestation); @@ -230,12 +234,13 @@ impl ProxyServer { // If we expect an attestaion from the client, verify it and get measurements let measurements = if attestation_verifier.has_remote_attestion() { + let remote_input_data = compute_report_input( + &remote_cert_chain.ok_or(ProxyError::NoClientAuth)?, + exporter, + )?; + attestation_verifier - .verify_attestation( - remote_attestation_message, - &remote_cert_chain.ok_or(ProxyError::NoClientAuth)?, - exporter, - ) + .verify_attestation(remote_attestation_message, remote_input_data) .await? } else { None @@ -343,13 +348,12 @@ impl ProxyClient { cert_and_key: Option, address: impl ToSocketAddrs, server_name: String, - local_quote_generator: Arc, + attestation_generator: AttestationGenerator, attestation_verifier: AttestationVerifier, remote_certificate: Option>, ) -> Result { // If we will provide attestation, we must also use client auth - if local_quote_generator.attestation_type() != AttestationType::None - && cert_and_key.is_none() + if attestation_generator.attestation_type != AttestationType::None && cert_and_key.is_none() { return Err(ProxyError::NoClientAuth); } @@ -387,7 +391,7 @@ impl ProxyClient { client_config.into(), address, server_name, - local_quote_generator, + attestation_generator, attestation_verifier, cert_and_key.map(|c| c.cert_chain), ) @@ -401,7 +405,7 @@ impl ProxyClient { client_config: Arc, local: impl ToSocketAddrs, target_name: String, - local_quote_generator: Arc, + attestation_generator: AttestationGenerator, attestation_verifier: AttestationVerifier, cert_chain: Option>>, ) -> Result { @@ -425,7 +429,7 @@ impl ProxyClient { connector.clone(), target.clone(), cert_chain.clone(), - local_quote_generator.clone(), + attestation_generator.clone(), attestation_verifier.clone(), ) .await?; @@ -481,7 +485,7 @@ impl ProxyClient { connector.clone(), target.clone(), cert_chain.clone(), - local_quote_generator.clone(), + attestation_generator.clone(), attestation_verifier.clone(), ) .await; @@ -551,7 +555,7 @@ impl ProxyClient { connector: TlsConnector, target: String, cert_chain: Option>>, - local_quote_generator: Arc, + attestation_generator: AttestationGenerator, attestation_verifier: AttestationVerifier, ) -> (Http2Sender, Option, AttestationType) { let mut delay = Duration::from_secs(1); @@ -562,7 +566,7 @@ impl ProxyClient { connector.clone(), target.clone(), cert_chain.clone(), - local_quote_generator.clone(), + attestation_generator.clone(), attestation_verifier.clone(), ) .await @@ -586,7 +590,7 @@ impl ProxyClient { connector: TlsConnector, target: String, cert_chain: Option>>, - local_quote_generator: Arc, + attestation_generator: AttestationGenerator, attestation_verifier: AttestationVerifier, ) -> Result<(Http2Sender, Option, AttestationType), ProxyError> { // Make a TCP client connection and TLS handshake @@ -616,6 +620,8 @@ impl ProxyClient { .ok_or(ProxyError::NoCertificate)? .to_owned(); + let remote_input_data = compute_report_input(&remote_cert_chain, exporter)?; + // Read a length prefixed attestation from the proxy-server let mut length_bytes = [0; 4]; tls_stream.read_exact(&mut length_bytes).await?; @@ -629,17 +635,18 @@ impl ProxyClient { // Verify the remote attestation against our accepted measurements let measurements = attestation_verifier - .verify_attestation(remote_attestation_message, &remote_cert_chain, exporter) + .verify_attestation(remote_attestation_message, remote_input_data) .await?; // If we are in a CVM, provide an attestation - let attestation = if local_quote_generator.attestation_type() != AttestationType::None { - AttestationExchangeMessage::from_attestation_generator( - &cert_chain.ok_or(ProxyError::NoClientAuth)?, - exporter, - local_quote_generator, - )? - .encode() + let attestation = if attestation_generator.attestation_type != AttestationType::None { + println!("fff"); + let local_input_data = + compute_report_input(&cert_chain.ok_or(ProxyError::NoClientAuth)?, exporter)?; + attestation_generator + .generate_attestation(local_input_data) + .await? + .encode() } else { AttestationExchangeMessage::without_attestation().encode() }; @@ -725,13 +732,42 @@ async fn get_tls_cert_with_config( let remote_attestation_message = AttestationExchangeMessage::decode(&mut &buf[..])?; + let remote_input_data = compute_report_input(&remote_cert_chain, exporter)?; + let _measurements = attestation_verifier - .verify_attestation(remote_attestation_message, &remote_cert_chain, exporter) + .verify_attestation(remote_attestation_message, remote_input_data) .await?; Ok(remote_cert_chain) } +/// Given a certificate chain and an exporter (session key material), build the quote input value +/// SHA256(pki) || exporter +pub fn compute_report_input( + cert_chain: &[CertificateDer<'_>], + exporter: [u8; 32], +) -> Result<[u8; 64], AttestationError> { + let mut quote_input = [0u8; 64]; + let pki_hash = get_pki_hash_from_certificate_chain(cert_chain)?; + quote_input[..32].copy_from_slice(&pki_hash); + quote_input[32..].copy_from_slice(&exporter); + Ok(quote_input) +} + +/// Given a certificate chain, get the [Sha256] hash of the public key of the leaf certificate +fn get_pki_hash_from_certificate_chain( + cert_chain: &[CertificateDer<'_>], +) -> Result<[u8; 32], AttestationError> { + let leaf_certificate = cert_chain.first().ok_or(AttestationError::NoCertificate)?; + let (_, cert) = parse_x509_certificate(leaf_certificate.as_ref())?; + let public_key = &cert.tbs_certificate.subject_pki; + let key_bytes = public_key.subject_public_key.as_ref(); + + let mut hasher = Sha256::new(); + hasher.update(key_bytes); + Ok(hasher.finalize().into()) +} + /// An error when running a proxy client or server #[derive(Error, Debug)] pub enum ProxyError { @@ -841,9 +877,9 @@ mod tests { server_config, "127.0.0.1:0", target_addr, - Arc::new(DcapTdxQuoteGenerator { + AttestationGenerator { attestation_type: AttestationType::DcapTdx, - }), + }, AttestationVerifier::do_not_verify(), ) .await @@ -859,7 +895,9 @@ mod tests { client_config, "127.0.0.1:0".to_string(), proxy_addr.to_string(), - Arc::new(NoQuoteGenerator), + AttestationGenerator { + attestation_type: AttestationType::None, + }, AttestationVerifier::mock(), None, ) @@ -917,7 +955,9 @@ mod tests { server_tls_server_config, "127.0.0.1:0", target_addr, - Arc::new(NoQuoteGenerator), + AttestationGenerator { + attestation_type: AttestationType::None, + }, AttestationVerifier::mock(), ) .await @@ -934,9 +974,9 @@ mod tests { client_tls_client_config, "127.0.0.1:0", proxy_addr.to_string(), - Arc::new(DcapTdxQuoteGenerator { + AttestationGenerator { attestation_type: AttestationType::DcapTdx, - }), + }, AttestationVerifier::do_not_verify(), Some(client_cert_chain), ) @@ -999,9 +1039,9 @@ mod tests { server_tls_server_config, "127.0.0.1:0", target_addr, - Arc::new(DcapTdxQuoteGenerator { + AttestationGenerator { attestation_type: AttestationType::DcapTdx, - }), + }, AttestationVerifier::mock(), ) .await @@ -1018,9 +1058,9 @@ mod tests { client_tls_client_config, "127.0.0.1:0", proxy_addr.to_string(), - Arc::new(DcapTdxQuoteGenerator { + AttestationGenerator { attestation_type: AttestationType::DcapTdx, - }), + }, AttestationVerifier::mock(), Some(client_cert_chain), ) @@ -1096,9 +1136,9 @@ mod tests { server_config, "127.0.0.1:0", target_addr, - Arc::new(DcapTdxQuoteGenerator { + AttestationGenerator { attestation_type: AttestationType::DcapTdx, - }), + }, AttestationVerifier::do_not_verify(), ) .await @@ -1135,7 +1175,9 @@ mod tests { server_config, "127.0.0.1:0", target_addr, - Arc::new(NoQuoteGenerator), + AttestationGenerator { + attestation_type: AttestationType::None, + }, AttestationVerifier::do_not_verify(), ) .await @@ -1151,7 +1193,9 @@ mod tests { client_config, "127.0.0.1:0".to_string(), proxy_addr.to_string(), - Arc::new(NoQuoteGenerator), + AttestationGenerator { + attestation_type: AttestationType::None, + }, AttestationVerifier::mock(), None, ) @@ -1177,9 +1221,9 @@ mod tests { server_config, "127.0.0.1:0", target_addr, - Arc::new(DcapTdxQuoteGenerator { + AttestationGenerator { attestation_type: AttestationType::DcapTdx, - }), + }, AttestationVerifier::do_not_verify(), ) .await @@ -1214,7 +1258,9 @@ mod tests { client_config, "127.0.0.1:0".to_string(), proxy_addr.to_string(), - Arc::new(NoQuoteGenerator), + AttestationGenerator { + attestation_type: AttestationType::None, + }, attestation_verifier, None, ) diff --git a/src/main.rs b/src/main.rs index 6ba01b5..ae494b3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ use tracing::level_filters::LevelFilter; use attested_tls_proxy::{ attestation::{measurements::get_measurements_from_file, AttestationType, AttestationVerifier}, - get_tls_cert, ProxyClient, ProxyServer, TlsCertAndKey, + get_tls_cert, AttestationGenerator, ProxyClient, ProxyServer, TlsCertAndKey, }; #[derive(Parser, Debug, Clone)] @@ -184,7 +184,9 @@ async fn main() -> anyhow::Result<()> { None => None, }; - let client_attestation_generator = client_attestation_type.get_quote_generator()?; + let client_attestation_generator = AttestationGenerator { + attestation_type: client_attestation_type, + }; let client = ProxyClient::new( tls_cert_and_chain, @@ -219,7 +221,9 @@ async fn main() -> anyhow::Result<()> { serde_json::Value::String(server_attestation_type.unwrap_or("none".to_string())), )?; - let local_attestation_generator = server_attestation_type.get_quote_generator()?; + let local_attestation_generator = AttestationGenerator { + attestation_type: server_attestation_type, + }; let attestation_verifier = match client_measurements { Some(client_measurements) => AttestationVerifier { diff --git a/test-assets/hclreport.bin b/test-assets/hclreport.bin new file mode 100644 index 0000000..ff9494a Binary files /dev/null and b/test-assets/hclreport.bin differ