diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..337b9c9c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "crates/enclave-contract/lib/forge-std"] + path = crates/enclave-contract/lib/forge-std + url = https://github.com/foundry-rs/forge-std diff --git a/Cargo.lock b/Cargo.lock index 121ec8e6..377f893c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,27 +52,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "aes-kw" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fa2b352dcefb5f7f3a5fb840e02665d311d878955380515e4fd50095dd3d8c" -dependencies = [ - "aes", -] - -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy 0.8.26", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -116,9 +95,9 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4195a29a4b87137b2bb02105e746102873bc03561805cf45c0e510c961f160e6" dependencies = [ - "alloy-primitives", + "alloy-primitives 1.3.0", "num_enum", - "strum 0.27.2", + "strum", ] [[package]] @@ -128,7 +107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda689f7287f15bd3582daba6be8d1545bad3740fd1fb778f629a1fe866bb43b" dependencies = [ "alloy-eips", - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-rlp", "alloy-serde", "alloy-trie", @@ -154,7 +133,7 @@ checksum = "2b5659581e41e8fe350ecc3593cb5c9dcffddfd550896390f2b78a07af67b0fa" dependencies = [ "alloy-consensus", "alloy-eips", - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-rlp", "alloy-serde", "serde", @@ -168,13 +147,13 @@ checksum = "944085cf3ac8f32d96299aa26c03db7c8ca6cdaafdbc467910b889f0328e6b70" dependencies = [ "alloy-consensus", "alloy-dyn-abi", - "alloy-json-abi", + "alloy-json-abi 1.3.0", "alloy-network", "alloy-network-primitives", - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-provider", "alloy-rpc-types-eth", - "alloy-sol-types", + "alloy-sol-types 1.3.0", "alloy-transport", "futures", "futures-util", @@ -189,10 +168,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d47400608fc869727ad81dba058d55f97b29ad8b5c5256d9598523df8f356ab6" dependencies = [ "alloy-dyn-abi", - "alloy-json-abi", - "alloy-primitives", + "alloy-json-abi 1.3.0", + "alloy-primitives 1.3.0", "alloy-rlp", - "alloy-sol-types", + "alloy-sol-types 1.3.0", ] [[package]] @@ -201,10 +180,10 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9e8a436f0aad7df8bb47f144095fba61202265d9f5f09a70b0e3227881a668e" dependencies = [ - "alloy-json-abi", - "alloy-primitives", - "alloy-sol-type-parser", - "alloy-sol-types", + "alloy-json-abi 1.3.0", + "alloy-primitives 1.3.0", + "alloy-sol-type-parser 1.3.0", + "alloy-sol-types 1.3.0", "itoa", "serde", "serde_json", @@ -217,7 +196,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "741bdd7499908b3aa0b159bba11e71c8cddd009a2c2eb7a06e825f1ec87900a5" dependencies = [ - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-rlp", "crc", "serde", @@ -230,7 +209,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b82752a889170df67bbb36d42ca63c531eb16274f0d7299ae2a680facba17bd" dependencies = [ - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-rlp", "serde", ] @@ -241,7 +220,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d4769c6ffddca380b0070d71c8b7f30bed375543fe76bb2f74ec0acf4b7cd16" dependencies = [ - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-rlp", "serde", "thiserror 2.0.14", @@ -256,7 +235,7 @@ dependencies = [ "alloy-eip2124", "alloy-eip2930", "alloy-eip7702", - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-rlp", "alloy-serde", "auto_impl", @@ -274,21 +253,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d4009efea6f403b3a80531f9c6f70fc242399498ff71196a1688cc1c901f44" dependencies = [ "alloy-eips", - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-serde", "alloy-trie", "serde", "serde_with", ] +[[package]] +name = "alloy-json-abi" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4584e3641181ff073e9d5bec5b3b8f78f9749d9fb108a1cfbc4399a4a139c72a" +dependencies = [ + "alloy-primitives 0.8.26", + "alloy-sol-type-parser 0.8.26", + "serde", + "serde_json", +] + [[package]] name = "alloy-json-abi" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "459f98c6843f208856f338bfb25e65325467f7aff35dfeb0484d0a76e059134b" dependencies = [ - "alloy-primitives", - "alloy-sol-type-parser", + "alloy-primitives 1.3.0", + "alloy-sol-type-parser 1.3.0", "serde", "serde_json", ] @@ -299,9 +290,9 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "883dee3b4020fcb5667ee627b4f401e899dad82bf37b246620339dd980720ed9" dependencies = [ - "alloy-primitives", - "alloy-sol-types", - "http", + "alloy-primitives 1.3.0", + "alloy-sol-types 1.3.0", + "http 1.3.1", "serde", "serde_json", "thiserror 2.0.14", @@ -319,12 +310,12 @@ dependencies = [ "alloy-eips", "alloy-json-rpc", "alloy-network-primitives", - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-rpc-types-any", "alloy-rpc-types-eth", "alloy-serde", "alloy-signer", - "alloy-sol-types", + "alloy-sol-types 1.3.0", "async-trait", "auto_impl", "derive_more", @@ -342,11 +333,38 @@ checksum = "80d7980333dd9391719756ac28bc2afa9baa705fc70ffd11dc86ab078dd64477" dependencies = [ "alloy-consensus", "alloy-eips", - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-serde", "serde", ] +[[package]] +name = "alloy-primitives" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777d58b30eb9a4db0e5f59bc30e8c2caef877fee7dc8734cf242a51a60f22e05" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "foldhash", + "hashbrown 0.15.5", + "indexmap 2.10.0", + "itoa", + "k256", + "keccak-asm", + "paste", + "proptest", + "rand 0.8.5", + "ruint", + "rustc-hash", + "serde", + "sha3", + "tiny-keccak", +] + [[package]] name = "alloy-primitives" version = "1.3.0" @@ -368,7 +386,7 @@ dependencies = [ "proptest", "rand 0.9.2", "ruint", - "rustc-hash 2.1.1", + "rustc-hash", "serde", "sha3", "tiny-keccak", @@ -386,11 +404,11 @@ dependencies = [ "alloy-json-rpc", "alloy-network", "alloy-network-primitives", - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-rpc-client", "alloy-rpc-types-eth", "alloy-signer", - "alloy-sol-types", + "alloy-sol-types 1.3.0", "alloy-transport", "alloy-transport-http", "async-stream", @@ -401,9 +419,9 @@ dependencies = [ "futures", "futures-utils-wasm", "lru", - "parking_lot 0.12.4", + "parking_lot", "pin-project", - "reqwest", + "reqwest 0.12.23", "serde", "serde_json", "thiserror 2.0.14", @@ -442,17 +460,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a0c6d723fbdf4a87454e2e3a275e161be27edcfbf46e2e3255dd66c138634b6" dependencies = [ "alloy-json-rpc", - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-transport", "alloy-transport-http", "futures", "pin-project", - "reqwest", + "reqwest 0.12.23", "serde", "serde_json", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tracing", "url", "wasmtimer", @@ -464,7 +482,7 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41492dac39365b86a954de86c47ec23dcc7452cdb2fde591caadc194b3e34c6" dependencies = [ - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-rpc-types-eth", "alloy-serde", "serde", @@ -491,10 +509,10 @@ dependencies = [ "alloy-consensus-any", "alloy-eips", "alloy-network-primitives", - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-rlp", "alloy-serde", - "alloy-sol-types", + "alloy-sol-types 1.3.0", "itertools 0.14.0", "serde", "serde_json", @@ -508,7 +526,7 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee8d2c52adebf3e6494976c8542fbdf12f10123b26e11ad56f77274c16a2a039" dependencies = [ - "alloy-primitives", + "alloy-primitives 1.3.0", "serde", "serde_json", ] @@ -519,7 +537,7 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c0494d1e0f802716480aabbe25549c7f6bc2a25ff33b08fd332bbb4b7d06894" dependencies = [ - "alloy-primitives", + "alloy-primitives 1.3.0", "async-trait", "auto_impl", "either", @@ -536,7 +554,7 @@ checksum = "59c2435eb8979a020763ced3fb478932071c56e5f75ea86db41f320915d325ba" dependencies = [ "alloy-consensus", "alloy-network", - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-signer", "async-trait", "k256", @@ -544,18 +562,50 @@ dependencies = [ "thiserror 2.0.14", ] +[[package]] +name = "alloy-sol-macro" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e68b32b6fa0d09bb74b4cefe35ccc8269d711c26629bc7cd98a47eeb12fe353f" +dependencies = [ + "alloy-sol-macro-expander 0.8.26", + "alloy-sol-macro-input 0.8.26", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "alloy-sol-macro" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aedac07a10d4c2027817a43cc1f038313fc53c7ac866f7363239971fd01f9f18" dependencies = [ - "alloy-sol-macro-expander", - "alloy-sol-macro-input", + "alloy-sol-macro-expander 1.3.0", + "alloy-sol-macro-input 1.3.0", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2afe6879ac373e58fd53581636f2cce843998ae0b058ebe1e4f649195e2bd23c" +dependencies = [ + "alloy-sol-macro-input 0.8.26", + "const-hex", + "heck", + "indexmap 2.10.0", "proc-macro-error2", "proc-macro2", "quote", "syn 2.0.104", + "syn-solidity 0.8.26", + "tiny-keccak", ] [[package]] @@ -564,8 +614,8 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24f9a598f010f048d8b8226492b6401104f5a5c1273c2869b72af29b48bb4ba9" dependencies = [ - "alloy-json-abi", - "alloy-sol-macro-input", + "alloy-json-abi 1.3.0", + "alloy-sol-macro-input 1.3.0", "const-hex", "heck", "indexmap 2.10.0", @@ -573,17 +623,33 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.104", - "syn-solidity", + "syn-solidity 1.3.0", "tiny-keccak", ] +[[package]] +name = "alloy-sol-macro-input" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ba01aee235a8c699d07e5be97ba215607564e71be72f433665329bec307d28" +dependencies = [ + "const-hex", + "dunce", + "heck", + "macro-string", + "proc-macro2", + "quote", + "syn 2.0.104", + "syn-solidity 0.8.26", +] + [[package]] name = "alloy-sol-macro-input" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f494adf9d60e49aa6ce26dfd42c7417aa6d4343cf2ae621f20e4d92a5ad07d85" dependencies = [ - "alloy-json-abi", + "alloy-json-abi 1.3.0", "const-hex", "dunce", "heck", @@ -592,7 +658,17 @@ dependencies = [ "quote", "serde_json", "syn 2.0.104", - "syn-solidity", + "syn-solidity 1.3.0", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c13fc168b97411e04465f03e632f31ef94cad1c7c8951bf799237fd7870d535" +dependencies = [ + "serde", + "winnow", ] [[package]] @@ -605,15 +681,28 @@ dependencies = [ "winnow", ] +[[package]] +name = "alloy-sol-types" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e960c4b52508ef2ae1e37cae5058e905e9ae099b107900067a503f8c454036f" +dependencies = [ + "alloy-json-abi 0.8.26", + "alloy-primitives 0.8.26", + "alloy-sol-macro 0.8.26", + "const-hex", + "serde", +] + [[package]] name = "alloy-sol-types" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a285b46e3e0c177887028278f04cc8262b76fd3b8e0e20e93cea0a58c35f5ac5" dependencies = [ - "alloy-json-abi", - "alloy-primitives", - "alloy-sol-macro", + "alloy-json-abi 1.3.0", + "alloy-primitives 1.3.0", + "alloy-sol-macro 1.3.0", "serde", ] @@ -624,18 +713,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c0107675e10c7f248bf7273c1e7fdb02409a717269cc744012e6f3c39959bfb" dependencies = [ "alloy-json-rpc", - "alloy-primitives", + "alloy-primitives 1.3.0", "auto_impl", "base64 0.22.1", "derive_more", "futures", "futures-utils-wasm", - "parking_lot 0.12.4", + "parking_lot", "serde", "serde_json", "thiserror 2.0.14", "tokio", - "tower 0.5.2", + "tower", "tracing", "url", "wasmtimer", @@ -649,9 +738,9 @@ checksum = "78e3736701b5433afd06eecff08f0688a71a10e0e1352e0bbf0bed72f0dd4e35" dependencies = [ "alloy-json-rpc", "alloy-transport", - "reqwest", + "reqwest 0.12.23", "serde_json", - "tower 0.5.2", + "tower", "tracing", "url", ] @@ -662,7 +751,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bada1fc392a33665de0dc50d401a3701b62583c655e3522a323490a5da016962" dependencies = [ - "alloy-primitives", + "alloy-primitives 1.3.0", "alloy-rlp", "arrayvec", "derive_more", @@ -678,7 +767,7 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6acb36318dfa50817154064fea7932adf2eec3f51c86680e2b37d7e8906c66bb" dependencies = [ - "alloy-primitives", + "alloy-primitives 1.3.0", "darling", "proc-macro2", "quote", @@ -880,12 +969,6 @@ dependencies = [ "rand 0.8.5", ] -[[package]] -name = "arraydeque" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" - [[package]] name = "arrayref" version = "0.3.9" @@ -903,9 +986,9 @@ dependencies = [ [[package]] name = "asn1-rs" -version = "0.7.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", @@ -913,31 +996,31 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror 2.0.14", + "thiserror 1.0.69", "time", ] [[package]] name = "asn1-rs-derive" -version = "0.6.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", - "synstructure", + "syn 1.0.109", + "synstructure 0.12.6", ] [[package]] name = "asn1-rs-impl" -version = "0.2.0" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 1.0.109", ] [[package]] @@ -979,93 +1062,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "attestation-agent" -version = "0.1.0" -source = "git+https://github.com/confidential-containers/guest-components?rev=eb0d89751800a435659f97d6f55cdb822223dc86#eb0d89751800a435659f97d6f55cdb822223dc86" -dependencies = [ - "anyhow", - "async-trait", - "attester", - "base64 0.22.1", - "config", - "const_format", - "crypto", - "hex", - "kbs-types", - "kbs_protocol", - "log", - "serde", - "serde_json", - "sha2", - "strum 0.27.2", - "tempfile", - "thiserror 2.0.14", - "tokio", - "toml 0.8.23", -] - -[[package]] -name = "attestation-service" -version = "0.1.0" -source = "git+https://github.com/confidential-containers/trustee?rev=b526a2ee0e2e5c33d2f49b6215a6eaf1be93f1fd#b526a2ee0e2e5c33d2f49b6215a6eaf1be93f1fd" -dependencies = [ - "anyhow", - "async-trait", - "base64 0.22.1", - "canon-json", - "cfg-if", - "ear", - "futures", - "hex", - "jsonwebtoken", - "kbs-types", - "lazy_static", - "log", - "openssl", - "rand 0.8.5", - "reference-value-provider-service", - "regorus", - "rsa", - "serde", - "serde_json", - "serde_variant", - "sha2", - "shadow-rs", - "strum 0.27.2", - "tempfile", - "thiserror 2.0.14", - "time", - "tokio", - "toml 0.9.5", - "tonic-build", - "uuid", - "verifier", -] - -[[package]] -name = "attester" -version = "0.1.0" -source = "git+https://github.com/confidential-containers/guest-components?rev=eb0d89751800a435659f97d6f55cdb822223dc86#eb0d89751800a435659f97d6f55cdb822223dc86" -dependencies = [ - "anyhow", - "async-trait", - "az-snp-vtpm", - "az-tdx-vtpm", - "base64 0.22.1", - "cfg-if", - "crypto", - "hex", - "kbs-types", - "log", - "serde", - "serde_json", - "serde_with", - "sha2", - "strum 0.27.2", - "thiserror 2.0.14", -] - [[package]] name = "auto_impl" version = "1.3.0" @@ -1083,57 +1079,11 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "axum" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" -dependencies = [ - "async-trait", - "axum-core", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper", - "tower 0.5.2", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper", - "tower-layer", - "tower-service", -] - [[package]] name = "az-cvm-vtpm" -version = "0.7.1" -source = "git+https://github.com/kinvolk/azure-cvm-tooling?rev=2bef60b88db7554935177ec63dd169190a39eab7#2bef60b88db7554935177ec63dd169190a39eab7" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3d0900c6757c9674b05b0479236458297026e25fb505186dc8d7735091a21c" dependencies = [ "bincode", "jsonwebkey", @@ -1142,32 +1092,18 @@ dependencies = [ "serde", "serde-big-array", "serde_json", - "sev 4.0.0", + "sev", "sha2", "thiserror 2.0.14", "tss-esapi", - "zerocopy 0.7.35", -] - -[[package]] -name = "az-snp-vtpm" -version = "0.7.1" -source = "git+https://github.com/kinvolk/azure-cvm-tooling?rev=2bef60b88db7554935177ec63dd169190a39eab7#2bef60b88db7554935177ec63dd169190a39eab7" -dependencies = [ - "az-cvm-vtpm", - "bincode", - "clap", - "openssl", - "serde", - "sev 4.0.0", - "thiserror 2.0.14", - "ureq", + "zerocopy", ] [[package]] name = "az-tdx-vtpm" -version = "0.7.1" -source = "git+https://github.com/kinvolk/azure-cvm-tooling?rev=2bef60b88db7554935177ec63dd169190a39eab7#2bef60b88db7554935177ec63dd169190a39eab7" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04849677b3c0704d4593d89940cde0dc0caad2202bf9fb29352e153782b91ff8" dependencies = [ "az-cvm-vtpm", "base64-url", @@ -1176,7 +1112,7 @@ dependencies = [ "serde_json", "thiserror 2.0.14", "ureq", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -1242,35 +1178,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bindgen" -version = "0.65.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" -dependencies = [ - "bitflags 1.3.2", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.104", - "which", -] - -[[package]] -name = "binstring" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0669d5a35b64fdb5ab7fb19cae13148b6b5cbdf4b8247faf54ece47f699c8cef" - [[package]] name = "bit-set" version = "0.8.0" @@ -1308,12 +1215,6 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac" -[[package]] -name = "bitfield" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c821a6e124197eb56d907ccc2188eab1038fb919c914f47976e64dd8dbc855d1" - [[package]] name = "bitfield" version = "0.19.1" @@ -1345,9 +1246,6 @@ name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" -dependencies = [ - "serde", -] [[package]] name = "bitvec" @@ -1361,17 +1259,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "blake2b_simd" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e903a20b159e944f91ec8499fe1e55651480c541ea0a584f5d967c49ad9d99" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -1436,25 +1323,22 @@ dependencies = [ ] [[package]] -name = "canon-json" -version = "0.2.1" +name = "cbindgen" +version = "0.29.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5ae9f90437d2e2efba2a6c75b8279aa6b8f2f4017e0a4aeb64a76cd9d3a2bab" +checksum = "befbfd072a8e81c02f8c507aefce431fe5e7d051f83d48a23ffc9b9fe5a11799" dependencies = [ + "clap", + "heck", + "indexmap 2.10.0", + "log", + "proc-macro2", + "quote", "serde", - "serde_derive", "serde_json", - "thiserror 2.0.14", -] - -[[package]] -name = "cbor-codec" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e083a023562b37c52837e850131a51b1154cceb9d149f41ee3d386737b140f46" -dependencies = [ - "byteorder", - "libc", + "syn 2.0.104", + "tempfile", + "toml", ] [[package]] @@ -1463,8 +1347,6 @@ version = "1.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" dependencies = [ - "jobserver", - "libc", "shlex", ] @@ -1474,15 +1356,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.1" @@ -1504,43 +1377,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "chrono-tz" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" -dependencies = [ - "chrono", - "phf 0.12.1", -] - -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - [[package]] name = "cipher" version = "0.4.4" @@ -1551,22 +1387,11 @@ dependencies = [ "inout", ] -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" -version = "4.5.44" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1f056bae57e3e54c3375c41ff79619ddd13460a17d7438712bd0d83fda4ff8" +checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" dependencies = [ "clap_builder", "clap_derive", @@ -1574,9 +1399,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.44" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" +checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" dependencies = [ "anstream", "anstyle", @@ -1586,9 +1411,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.41" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", @@ -1603,14 +1428,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] -name = "coarsetime" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91849686042de1b41cd81490edc83afbcb0abe5a9b6f2c4114f23ce8cca1bcf4" +name = "coco-provider" +version = "0.3.0" +source = "git+https://github.com/automata-network/coco-provider-sdk#3a832b8cf5e88ef71649ab56e4efd67067b26b7c" dependencies = [ + "bitfield 0.19.1", + "cbindgen", + "iocuddle", "libc", - "wasix", - "wasm-bindgen", + "log", + "rand 0.8.5", + "serde", + "serde-big-array", + "sysinfo", + "uuid", ] [[package]] @@ -1635,34 +1466,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "concat-kdf" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d72c1252426a83be2092dd5884a5f6e3b8e7180f6891b6263d2c21b92ec8816" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "config" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68578f196d2a33ff61b27fae256c3164f65e36382648e30666dde05b8cc9dfdf" -dependencies = [ - "async-trait", - "convert_case", - "json5", - "nom", - "pathdiff", - "ron", - "rust-ini", - "serde", - "serde_json", - "toml 0.8.23", - "yaml-rust2", -] - [[package]] name = "const-hex" version = "1.14.1" @@ -1682,32 +1485,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom 0.2.16", - "once_cell", - "tiny-keccak", -] - -[[package]] -name = "const_fn" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f8a2ca5ac02d09563609681103aada9e1777d54fc57a5acd7a41404f9c93b6e" - [[package]] name = "const_format" version = "0.2.34" @@ -1728,21 +1505,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "constant_time_eq" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "core-foundation" version = "0.9.4" @@ -1769,17 +1531,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "cose-rust" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f221b4189b72ce93755b7fa1495d1741cc330bbd9698d3032562811698e3ab84" -dependencies = [ - "cbor-codec", - "openssl", - "rand 0.8.5", -] - [[package]] name = "cpufeatures" version = "0.2.17" @@ -1804,24 +1555,6 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -1834,29 +1567,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" -[[package]] -name = "crypto" -version = "0.1.0" -source = "git+https://github.com/confidential-containers/guest-components?rev=eb0d89751800a435659f97d6f55cdb822223dc86#eb0d89751800a435659f97d6f55cdb822223dc86" -dependencies = [ - "aes-gcm", - "aes-kw", - "anyhow", - "base64 0.22.1", - "concat-kdf", - "ctr", - "kbs-types", - "p256", - "rand 0.8.5", - "rand 0.9.2", - "rsa", - "serde", - "serde_json", - "sha2", - "strum 0.27.2", - "zeroize", -] - [[package]] name = "crypto-bigint" version = "0.5.5" @@ -1880,12 +1590,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ct-codecs" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b10589d1a5e400d61f9f38f12f884cfd080ff345de8f17efda36fe0e4a02aa8" - [[package]] name = "ctr" version = "0.9.2" @@ -1968,7 +1672,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.11", + "parking_lot_core", ] [[package]] @@ -1977,6 +1681,24 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +[[package]] +name = "dcap-rs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb87e05504da61258369005bf1b5640bac882157c1b9eb492422862a6969367" +dependencies = [ + "alloy-sol-types 0.8.26", + "chrono", + "hex", + "p256", + "serde", + "serde_json", + "sha2", + "sha3", + "time", + "x509-parser", +] + [[package]] name = "der" version = "0.7.10" @@ -1990,9 +1712,9 @@ dependencies = [ [[package]] name = "der-parser" -version = "10.0.0" +version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" dependencies = [ "asn1-rs", "displaydoc", @@ -2065,34 +1787,13 @@ dependencies = [ "subtle", ] -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys 0.4.1", -] - [[package]] name = "dirs" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "dirs-sys 0.5.0", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users 0.4.6", - "windows-sys 0.48.0", + "dirs-sys", ] [[package]] @@ -2103,7 +1804,7 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users 0.5.2", + "redox_users", "windows-sys 0.60.2", ] @@ -2118,15 +1819,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "dlv-list" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" -dependencies = [ - "const-random", -] - [[package]] name = "dunce" version = "1.0.5" @@ -2139,25 +1831,6 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" -[[package]] -name = "ear" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1134a8dbb5ad666d26d82da83d12b71703b16f2ed5433d5ba24d8cfea2b66d96" -dependencies = [ - "base64 0.22.1", - "ciborium", - "cose-rust", - "hex", - "jsonwebtoken", - "lazy_static", - "openssl", - "phf 0.11.3", - "serde", - "serde_json", - "thiserror 1.0.69", -] - [[package]] name = "ecdsa" version = "0.16.9" @@ -2173,16 +1846,6 @@ dependencies = [ "spki", ] -[[package]] -name = "ed25519-compact" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9b3460f44bea8cd47f45a0c70892f1eff856d97cd55358b2f73f663789f6190" -dependencies = [ - "ct-codecs", - "getrandom 0.2.16", -] - [[package]] name = "either" version = "1.15.0" @@ -2204,7 +1867,6 @@ dependencies = [ "ff", "generic-array", "group", - "hkdf", "pem-rfc7468", "pkcs8", "rand_core 0.6.4", @@ -2220,7 +1882,7 @@ version = "0.1.0" dependencies = [ "alloy", "anyhow", - "reqwest", + "reqwest 0.11.27", "tokio", ] @@ -2253,19 +1915,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "env_logger" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -2282,25 +1931,6 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "eventlog" -version = "0.1.0" -source = "git+https://github.com/confidential-containers/trustee?rev=b526a2ee0e2e5c33d2f49b6215a6eaf1be93f1fd#b526a2ee0e2e5c33d2f49b6215a6eaf1be93f1fd" -dependencies = [ - "anyhow", - "base64 0.22.1", - "byteorder", - "hex", - "log", - "scroll 0.13.0", - "serde", - "serde_json", - "sha2", - "shadow-rs", - "sm3", - "tonic-build", -] - [[package]] name = "fastrand" version = "2.3.0" @@ -2357,12 +1987,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - [[package]] name = "fnv" version = "1.0.7" @@ -2399,16 +2023,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "funty" version = "2.0.0" @@ -2521,19 +2135,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" [[package]] -name = "fxhash" -version = "0.2.1" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -2547,10 +2152,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] @@ -2591,19 +2194,6 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "git2" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1" -dependencies = [ - "bitflags 1.3.2", - "libc", - "libgit2-sys", - "log", - "url", -] - [[package]] name = "glob" version = "0.3.3" @@ -2620,7 +2210,7 @@ dependencies = [ "futures-core", "futures-sink", "gloo-utils", - "http", + "http 1.3.1", "js-sys", "pin-project", "serde", @@ -2669,16 +2259,16 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.12" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" dependencies = [ - "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http", + "futures-util", + "http 0.2.12", "indexmap 2.10.0", "slab", "tokio", @@ -2687,13 +2277,22 @@ dependencies = [ ] [[package]] -name = "half" -version = "2.6.0" +name = "h2" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ - "cfg-if", - "crunchy", + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.3.1", + "indexmap 2.10.0", + "slab", + "tokio", + "tokio-util", + "tracing", ] [[package]] @@ -2707,10 +2306,6 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] [[package]] name = "hashbrown" @@ -2724,15 +2319,6 @@ dependencies = [ "serde", ] -[[package]] -name = "hashlink" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" -dependencies = [ - "hashbrown 0.14.5", -] - [[package]] name = "heck" version = "0.5.0" @@ -2782,44 +2368,22 @@ dependencies = [ ] [[package]] -name = "hmac-sha1-compact" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18492c9f6f9a560e0d346369b665ad2bdbc89fa9bceca75796584e79042694c3" - -[[package]] -name = "hmac-sha256" -version = "1.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad6880c8d4a9ebf39c6e8b77007ce223f646a4d21ce29d99f70cb16420545425" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "hmac-sha512" -version = "1.1.7" +name = "hostname-validator" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89e8d20b3799fa526152a5301a771eaaad80857f83e01b23216ceaafb2d9280" -dependencies = [ - "digest 0.10.7", -] +checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" [[package]] -name = "home" -version = "0.5.11" +name = "http" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ - "windows-sys 0.59.0", + "bytes", + "fnv", + "itoa", ] -[[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" @@ -2831,6 +2395,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.1" @@ -2838,7 +2413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.3.1", ] [[package]] @@ -2849,8 +2424,8 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http", - "http-body", + "http 1.3.1", + "http-body 1.0.1", "pin-project-lite", ] @@ -2867,10 +2442,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] -name = "humantime" -version = "2.2.0" +name = "hyper" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] [[package]] name = "hyper" @@ -2881,9 +2474,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.12", + "http 1.3.1", + "http-body 1.0.1", "httparse", "httpdate", "itoa", @@ -2899,8 +2492,8 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http", - "hyper", + "http 1.3.1", + "hyper 1.6.0", "hyper-util", "log", "rustls", @@ -2911,16 +2504,16 @@ dependencies = [ ] [[package]] -name = "hyper-timeout" -version = "0.5.2" +name = "hyper-tls" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "hyper", - "hyper-util", - "pin-project-lite", + "bytes", + "hyper 0.14.32", + "native-tls", "tokio", - "tower-service", + "tokio-native-tls", ] [[package]] @@ -2931,7 +2524,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper", + "hyper 1.6.0", "hyper-util", "native-tls", "tokio", @@ -2950,9 +2543,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "http", - "http-body", - "hyper", + "http 1.3.1", + "http-body 1.0.1", + "hyper 1.6.0", "ipnet", "libc", "percent-encoding", @@ -3151,32 +2744,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "intel-tee-quote-verification-rs" -version = "0.3.0" -source = "git+https://github.com/intel/SGXDataCenterAttestationPrimitives?tag=DCAP_1.23#e880e54c8f35d44a4763e08dff32a046c8ef2230" -dependencies = [ - "intel-tee-quote-verification-sys", -] - -[[package]] -name = "intel-tee-quote-verification-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c8bc48d598fa48310e41f65a706e0beb2a74f5f9e5a26c5c2ca6cd83416fcc" -dependencies = [ - "bindgen", -] - [[package]] name = "io-uring" version = "0.7.9" @@ -3210,23 +2777,6 @@ dependencies = [ "serde", ] -[[package]] -name = "is-terminal" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "is_debug" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe266d2e243c931d8190177f20bf7f24eed45e96f39e87dc49a27b32d12d407" - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -3279,16 +2829,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" -[[package]] -name = "jobserver" -version = "0.1.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" -dependencies = [ - "getrandom 0.3.3", - "libc", -] - [[package]] name = "js-sys" version = "0.3.77" @@ -3299,22 +2839,11 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "json5" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" -dependencies = [ - "pest", - "pest_derive", - "serde", -] - [[package]] name = "jsonrpsee" -version = "0.24.9" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b26c20e2178756451cfeb0661fb74c47dd5988cb7e3939de7e9241fd604d42" +checksum = "3f3f48dc3e6b8bd21e15436c1ddd0bc22a6a54e8ec46fedd6adf3425f396ec6a" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", @@ -3330,22 +2859,22 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.24.9" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bacb85abf4117092455e1573625e21b8f8ef4dec8aff13361140b2dc266cdff2" +checksum = "cf36eb27f8e13fa93dcb50ccb44c417e25b818cfa1a481b5470cd07b19c60b98" dependencies = [ "base64 0.22.1", "futures-channel", "futures-util", "gloo-net", - "http", + "http 1.3.1", "jsonrpsee-core", "pin-project", "rustls", "rustls-pki-types", "rustls-platform-verifier", "soketto", - "thiserror 1.0.69", + "thiserror 2.0.14", "tokio", "tokio-rustls", "tokio-util", @@ -3355,41 +2884,41 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.24.9" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456196007ca3a14db478346f58c7238028d55ee15c1df15115596e411ff27925" +checksum = "316c96719901f05d1137f19ba598b5fe9c9bc39f4335f67f6be8613921946480" dependencies = [ "async-trait", "bytes", "futures-timer", "futures-util", - "http", - "http-body", + "http 1.3.1", + "http-body 1.0.1", "http-body-util", "jsonrpsee-types", - "parking_lot 0.12.4", + "parking_lot", "pin-project", - "rand 0.8.5", - "rustc-hash 2.1.1", + "rand 0.9.2", + "rustc-hash", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.14", "tokio", "tokio-stream", + "tower", "tracing", "wasm-bindgen-futures", ] [[package]] name = "jsonrpsee-http-client" -version = "0.24.9" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c872b6c9961a4ccc543e321bb5b89f6b2d2c7fe8b61906918273a3333c95400c" +checksum = "790bedefcec85321e007ff3af84b4e417540d5c87b3c9779b9e247d1bcc3dab8" dependencies = [ - "async-trait", "base64 0.22.1", - "http-body", - "hyper", + "http-body 1.0.1", + "hyper 1.6.0", "hyper-rustls", "hyper-util", "jsonrpsee-core", @@ -3398,18 +2927,17 @@ dependencies = [ "rustls-platform-verifier", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.14", "tokio", - "tower 0.4.13", - "tracing", + "tower", "url", ] [[package]] name = "jsonrpsee-proc-macros" -version = "0.24.9" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e65763c942dfc9358146571911b0cd1c361c2d63e2d2305622d40d36376ca80" +checksum = "2da3f8ab5ce1bb124b6d082e62dffe997578ceaf0aeb9f3174a214589dc00f07" dependencies = [ "heck", "proc-macro-crate", @@ -3420,15 +2948,15 @@ dependencies = [ [[package]] name = "jsonrpsee-server" -version = "0.24.9" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e363146da18e50ad2b51a0a7925fc423137a0b1371af8235b1c231a0647328" +checksum = "4c51b7c290bb68ce3af2d029648148403863b982f138484a73f02a9dd52dbd7f" dependencies = [ "futures-util", - "http", - "http-body", + "http 1.3.1", + "http-body 1.0.1", "http-body-util", - "hyper", + "hyper 1.6.0", "hyper-util", "jsonrpsee-core", "jsonrpsee-types", @@ -3437,47 +2965,49 @@ dependencies = [ "serde", "serde_json", "soketto", - "thiserror 1.0.69", + "thiserror 2.0.14", "tokio", "tokio-stream", "tokio-util", - "tower 0.4.13", + "tower", "tracing", ] [[package]] name = "jsonrpsee-types" -version = "0.24.9" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a8e70baf945b6b5752fc8eb38c918a48f1234daf11355e07106d963f860089" +checksum = "bc88ff4688e43cc3fa9883a8a95c6fa27aa2e76c96e610b737b6554d650d7fd5" dependencies = [ - "http", + "http 1.3.1", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.14", ] [[package]] name = "jsonrpsee-wasm-client" -version = "0.24.9" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6558a9586cad43019dafd0b6311d0938f46efc116b34b28c74778bc11a2edf6" +checksum = "7902885de4779f711a95d82c8da2d7e5f9f3a7c7cfa44d51c067fd1c29d72a3c" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-types", + "tower", ] [[package]] name = "jsonrpsee-ws-client" -version = "0.24.9" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b3323d890aa384f12148e8d2a1fd18eb66e9e7e825f9de4fa53bcc19b93eef" +checksum = "9b6fceceeb05301cc4c065ab3bd2fa990d41ff4eb44e4ca1b30fa99c057c3e79" dependencies = [ - "http", + "http 1.3.1", "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-types", + "tower", "url", ] @@ -3498,47 +3028,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "jsonwebtoken" -version = "9.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" -dependencies = [ - "base64 0.22.1", - "js-sys", - "pem", - "ring", - "serde", - "serde_json", - "simple_asn1", -] - -[[package]] -name = "jwt-simple" -version = "0.12.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "731011e9647a71ff4f8474176ff6ce6e0d2de87a0173f15613af3a84c3e3401a" -dependencies = [ - "anyhow", - "binstring", - "blake2b_simd", - "coarsetime", - "ct-codecs", - "ed25519-compact", - "hmac-sha1-compact", - "hmac-sha256", - "hmac-sha512", - "k256", - "p256", - "p384", - "rand 0.8.5", - "serde", - "serde_json", - "superboring", - "thiserror 2.0.14", - "zeroize", -] - [[package]] name = "k256" version = "0.13.4" @@ -3551,43 +3040,6 @@ dependencies = [ "once_cell", "serdect", "sha2", - "signature", -] - -[[package]] -name = "kbs-types" -version = "0.12.0" -source = "git+https://github.com/virtee/kbs-types.git?rev=e3cc706#e3cc706ec4c1a88565598c5ce224da06a03f2265" -dependencies = [ - "base64 0.22.1", - "serde", - "serde_json", - "sha2", - "strum 0.27.2", - "thiserror 2.0.14", -] - -[[package]] -name = "kbs_protocol" -version = "0.1.0" -source = "git+https://github.com/confidential-containers/guest-components?rev=eb0d89751800a435659f97d6f55cdb822223dc86#eb0d89751800a435659f97d6f55cdb822223dc86" -dependencies = [ - "anyhow", - "async-trait", - "attester", - "base64 0.22.1", - "crypto", - "jwt-simple", - "kbs-types", - "log", - "resource_uri", - "serde", - "serde_json", - "sha2", - "thiserror 2.0.14", - "tokio", - "url", - "zeroize", ] [[package]] @@ -3614,43 +3066,12 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.175" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" - -[[package]] -name = "libgit2-sys" -version = "0.14.2+1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" -dependencies = [ - "cc", - "libc", - "libz-sys", - "pkg-config", -] - -[[package]] -name = "libloading" -version = "0.8.8" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" -dependencies = [ - "cfg-if", - "windows-targets 0.53.3", -] +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libm" @@ -3668,24 +3089,6 @@ dependencies = [ "libc", ] -[[package]] -name = "libz-sys" -version = "1.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -3743,12 +3146,6 @@ dependencies = [ "regex-automata 0.1.10", ] -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - [[package]] name = "mbox" version = "0.7.1" @@ -3818,12 +3215,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "multimap" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" - [[package]] name = "native-tls" version = "0.2.14" @@ -3851,6 +3242,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3871,23 +3271,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - [[package]] name = "num-conv" version = "0.1.0" @@ -3914,17 +3297,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -3966,15 +3338,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - [[package]] name = "nybbles" version = "0.4.1" @@ -3989,6 +3352,25 @@ dependencies = [ "smallvec", ] +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "object" version = "0.36.7" @@ -4009,9 +3391,9 @@ dependencies = [ [[package]] name = "oid-registry" -version = "0.8.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" dependencies = [ "asn1-rs", ] @@ -4036,9 +3418,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.73" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ "bitflags 2.9.1", "cfg-if", @@ -4077,9 +3459,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.109" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -4094,16 +3476,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "ordered-multimap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" -dependencies = [ - "dlv-list", - "hashbrown 0.14.5", -] - [[package]] name = "overload" version = "0.1.1" @@ -4122,18 +3494,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "p384" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - [[package]] name = "parity-scale-codec" version = "3.7.5" @@ -4162,17 +3522,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.4" @@ -4180,21 +3529,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", - "parking_lot_core 0.9.11", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -4205,7 +3540,7 @@ checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.17", + "redox_syscall", "smallvec", "windows-targets 0.52.6", ] @@ -4216,28 +3551,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pathdiff" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - -[[package]] -name = "pem" -version = "3.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" -dependencies = [ - "base64 0.22.1", - "serde", -] - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -4264,110 +3577,6 @@ dependencies = [ "ucd-trie", ] -[[package]] -name = "pest_derive" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.104", -] - -[[package]] -name = "pest_meta" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" -dependencies = [ - "pest", - "sha2", -] - -[[package]] -name = "petgraph" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" -dependencies = [ - "fixedbitset", - "indexmap 2.10.0", -] - -[[package]] -name = "phf" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" -dependencies = [ - "phf_macros", - "phf_shared 0.11.3", - "serde", -] - -[[package]] -name = "phf" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" -dependencies = [ - "phf_shared 0.12.1", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared 0.11.3", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" -dependencies = [ - "phf_generator", - "phf_shared 0.11.3", - "proc-macro2", - "quote", - "syn 2.0.104", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" -dependencies = [ - "siphasher", -] - [[package]] name = "picky-asn1" version = "0.8.0" @@ -4435,17 +3644,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - [[package]] name = "pkcs8" version = "0.10.2" @@ -4495,17 +3693,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.26", -] - -[[package]] -name = "prettyplease" -version = "0.2.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" -dependencies = [ - "proc-macro2", - "syn 2.0.104", + "zerocopy", ] [[package]] @@ -4588,58 +3776,6 @@ dependencies = [ "unarray", ] -[[package]] -name = "prost" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-build" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" -dependencies = [ - "heck", - "itertools 0.14.0", - "log", - "multimap", - "once_cell", - "petgraph", - "prettyplease", - "prost", - "prost-types", - "regex", - "syn 2.0.104", - "tempfile", -] - -[[package]] -name = "prost-derive" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" -dependencies = [ - "anyhow", - "itertools 0.14.0", - "proc-macro2", - "quote", - "syn 2.0.104", -] - -[[package]] -name = "prost-types" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" -dependencies = [ - "prost", -] - [[package]] name = "quick-error" version = "1.2.3" @@ -4747,15 +3883,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.17" @@ -4765,17 +3892,6 @@ dependencies = [ "bitflags 2.9.1", ] -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom 0.2.16", - "libredox", - "thiserror 1.0.69", -] - [[package]] name = "redox_users" version = "0.5.2" @@ -4807,33 +3923,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "reference-value-provider-service" -version = "0.1.0" -source = "git+https://github.com/confidential-containers/trustee?rev=b526a2ee0e2e5c33d2f49b6215a6eaf1be93f1fd#b526a2ee0e2e5c33d2f49b6215a6eaf1be93f1fd" -dependencies = [ - "anyhow", - "async-trait", - "base64 0.22.1", - "cfg-if", - "chrono", - "clap", - "config", - "env_logger", - "log", - "prost", - "roxmltree", - "serde", - "serde_json", - "shadow-rs", - "sled", - "strum 0.27.2", - "tempfile", - "tokio", - "tonic", - "tonic-build", -] - [[package]] name = "regex" version = "1.11.1" @@ -4879,21 +3968,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] -name = "regorus" -version = "0.2.8" +name = "reqwest" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843c3d97f07e3b5ac0955d53ad0af4c91fe4a4f8525843ece5bf014f27829b73" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "anyhow", - "chrono", - "chrono-tz", - "data-encoding", - "lazy_static", - "rand 0.8.5", - "regex", - "scientific", + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-tls 0.5.0", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", "serde", "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", ] [[package]] @@ -4904,14 +4015,12 @@ checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "base64 0.22.1", "bytes", - "futures-channel", "futures-core", - "futures-util", - "http", - "http-body", + "http 1.3.1", + "http-body 1.0.1", "http-body-util", - "hyper", - "hyper-tls", + "hyper 1.6.0", + "hyper-tls 0.6.0", "hyper-util", "js-sys", "log", @@ -4922,10 +4031,10 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio", "tokio-native-tls", - "tower 0.5.2", + "tower", "tower-http", "tower-service", "url", @@ -4934,17 +4043,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "resource_uri" -version = "0.1.0" -source = "git+https://github.com/confidential-containers/guest-components?rev=eb0d89751800a435659f97d6f55cdb822223dc86#eb0d89751800a435659f97d6f55cdb822223dc86" -dependencies = [ - "anyhow", - "serde", - "serde_json", - "url", -] - [[package]] name = "rfc6979" version = "0.4.0" @@ -4970,60 +4068,21 @@ dependencies = [ ] [[package]] -name = "rlp" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" -dependencies = [ - "bytes", - "rustc-hex", -] - -[[package]] -name = "ron" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" -dependencies = [ - "base64 0.21.7", - "bitflags 2.9.1", - "serde", - "serde_derive", -] - -[[package]] -name = "route-recognizer" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" - -[[package]] -name = "roxmltree" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" - -[[package]] -name = "rsa" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" -dependencies = [ - "const-oid", - "digest 0.10.7", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "sha2", - "signature", - "spki", - "subtle", - "zeroize", +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", ] +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + [[package]] name = "ruint" version = "1.16.0" @@ -5057,28 +4116,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" -[[package]] -name = "rust-ini" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - [[package]] name = "rustc-demangle" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc-hash" version = "2.1.1" @@ -5118,19 +4161,6 @@ dependencies = [ "nom", ] -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.9.1", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - [[package]] name = "rustix" version = "1.0.8" @@ -5140,7 +4170,7 @@ dependencies = [ "bitflags 2.9.1", "errno", "libc", - "linux-raw-sys 0.9.4", + "linux-raw-sys", "windows-sys 0.60.2", ] @@ -5171,6 +4201,15 @@ dependencies = [ "security-framework 3.3.0", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + [[package]] name = "rustls-pki-types" version = "1.12.0" @@ -5253,9 +4292,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22b2d775fb28f245817589471dd49c5edf64237f4a19d10ce9a92ff4651a27f4" +checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" dependencies = [ "sdd", ] @@ -5314,72 +4353,12 @@ dependencies = [ "zeroize", ] -[[package]] -name = "scientific" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38a4b339a8de779ecb098a772ecbba2ace74e23ed959a5b4f30631d8bf1799a8" -dependencies = [ - "scientific-macro", -] - -[[package]] -name = "scientific-macro" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ee4885492bb655bfa05d039cd9163eb8fe9f79ddebf00ca23a1637510c2fd2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "scroll" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" -dependencies = [ - "scroll_derive 0.11.1", -] - -[[package]] -name = "scroll" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1257cd4248b4132760d6524d6dda4e053bc648c9070b960929bf50cfb1e7add" -dependencies = [ - "scroll_derive 0.13.0", -] - -[[package]] -name = "scroll_derive" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - -[[package]] -name = "scroll_derive" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fc4f90c27b57691bbaf11d8ecc7cfbfe98a4da6dbe60226115d322aa80c06e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "sdd" version = "3.0.10" @@ -5464,29 +4443,13 @@ version = "0.1.0" dependencies = [ "aes-gcm", "anyhow", - "base64 0.22.1", "hkdf", "jsonrpsee", - "kbs-types", "rand 0.9.2", - "reqwest", "schnorrkel", "secp256k1", - "seismic-enclave-derive", "serde", - "serde_json", "sha2", - "tokio", - "tracing", -] - -[[package]] -name = "seismic-enclave-derive" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", ] [[package]] @@ -5496,35 +4459,33 @@ dependencies = [ "aes-gcm", "alloy", "anyhow", - "attestation-agent", - "attestation-service", - "auto_impl", "az-tdx-vtpm", - "base64 0.22.1", + "base64-url", + "bincode", + "chrono", "clap", + "coco-provider", + "dcap-rs", "enclave-contract", "hex", "hkdf", "jsonrpsee", - "kbs-types", "libc", - "log", + "openssl", "rand 0.9.2", - "reqwest", + "reqwest 0.11.27", "schnorrkel", - "scroll 0.11.0", "secp256k1", "seismic-enclave", "serde", "serde_json", "serial_test", "sha2", - "strum 0.26.3", - "strum_macros 0.26.4", - "tempfile", "tokio", "tracing", "tracing-subscriber", + "urlencoding", + "x509-parser", "zeroize", ] @@ -5608,15 +4569,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - [[package]] name = "serde_spanned" version = "1.0.0" @@ -5638,15 +4590,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_variant" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a0068df419f9d9b6488fdded3f1c818522cdea328e02ce9d9f147380265a432" -dependencies = [ - "serde", -] - [[package]] name = "serde_with" version = "3.14.0" @@ -5698,7 +4641,7 @@ dependencies = [ "futures", "log", "once_cell", - "parking_lot 0.12.4", + "parking_lot", "scc", "serial_test_derive", ] @@ -5714,32 +4657,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "sev" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97bd0b2e2d937951add10c8512a2dacc6ad29b39e5c5f26565a3e443329857d" -dependencies = [ - "base64 0.22.1", - "bincode", - "bitfield 0.15.0", - "bitflags 1.3.2", - "byteorder", - "codicon", - "dirs 5.0.1", - "hex", - "iocuddle", - "lazy_static", - "libc", - "openssl", - "rdrand", - "serde", - "serde-big-array", - "serde_bytes", - "static_assertions", - "uuid", -] - [[package]] name = "sev" version = "6.2.1" @@ -5752,7 +4669,7 @@ dependencies = [ "bitflags 2.9.1", "byteorder", "codicon", - "dirs 6.0.0", + "dirs", "hex", "iocuddle", "lazy_static", @@ -5808,19 +4725,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "shadow-rs" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c401e795850edb4e9fdde5940f856364f0fbab573e8dea58f6ee5f85fcf471d" -dependencies = [ - "const_format", - "git2", - "is_debug", - "time", - "tzdb 0.5.10", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -5855,55 +4759,12 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "simple_asn1" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" -dependencies = [ - "num-bigint", - "num-traits", - "thiserror 2.0.14", - "time", -] - -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - [[package]] name = "slab" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" -[[package]] -name = "sled" -version = "0.34.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" -dependencies = [ - "crc32fast", - "crossbeam-epoch", - "crossbeam-utils", - "fs2", - "fxhash", - "libc", - "log", - "parking_lot 0.11.2", -] - -[[package]] -name = "sm3" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebb9a3b702d0a7e33bc4d85a14456633d2b165c2ad839c5fd9a8417c1ab15860" -dependencies = [ - "digest 0.10.7", -] - [[package]] name = "smallvec" version = "1.15.1" @@ -5942,19 +4803,13 @@ dependencies = [ "base64 0.22.1", "bytes", "futures", - "http", + "http 1.3.1", "httparse", "log", "rand 0.8.5", "sha1", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "spki" version = "0.7.3" @@ -5985,118 +4840,148 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.3" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros 0.26.4", + "strum_macros", ] [[package]] -name = "strum" +name = "strum_macros" version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "strum_macros 0.27.2", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "strum_macros" -version = "0.26.4" +name = "syn" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ - "heck", "proc-macro2", "quote", - "rustversion", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab4e6eed052a117409a1a744c8bda9c3ea6934597cf7419f791cb7d590871c4c" +dependencies = [ + "paste", + "proc-macro2", + "quote", "syn 2.0.104", ] [[package]] -name = "strum_macros" -version = "0.27.2" +name = "syn-solidity" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +checksum = "a7a985ff4ffd7373e10e0fb048110fb11a162e5a4c47f92ddb8787a6f766b769" dependencies = [ - "heck", + "paste", "proc-macro2", "quote", "syn 2.0.104", ] [[package]] -name = "subtle" -version = "2.6.1" +name = "sync_wrapper" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] -name = "superboring" -version = "0.1.4" +name = "sync_wrapper" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "515cce34a781d7250b8a65706e0f2a5b99236ea605cb235d4baed6685820478f" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ - "getrandom 0.2.16", - "hmac-sha256", - "hmac-sha512", - "rand 0.8.5", - "rsa", + "futures-core", ] [[package]] -name = "syn" -version = "1.0.109" +name = "synstructure" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn 1.0.109", + "unicode-xid", ] [[package]] -name = "syn" -version = "2.0.104" +name = "synstructure" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn 2.0.104", ] [[package]] -name = "syn-solidity" -version = "1.3.0" +name = "sysinfo" +version = "0.35.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a985ff4ffd7373e10e0fb048110fb11a162e5a4c47f92ddb8787a6f766b769" +checksum = "3c3ffa3e4ff2b324a57f7aeb3c349656c7b127c3c189520251a648102a92496e" dependencies = [ - "paste", - "proc-macro2", - "quote", - "syn 2.0.104", + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows", ] [[package]] -name = "sync_wrapper" -version = "1.0.2" +name = "system-configuration" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "futures-core", + "bitflags 1.3.2", + "core-foundation 0.9.4", + "system-configuration-sys", ] [[package]] -name = "synstructure" -version = "0.13.2" +name = "system-configuration-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", + "core-foundation-sys", + "libc", ] [[package]] @@ -6120,19 +5005,10 @@ dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.8", + "rustix", "windows-sys 0.59.0", ] -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - [[package]] name = "thiserror" version = "1.0.69" @@ -6199,9 +5075,7 @@ checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", - "libc", "num-conv", - "num_threads", "powerfmt", "serde", "time-core", @@ -6254,7 +5128,7 @@ dependencies = [ "io-uring", "libc", "mio", - "parking_lot 0.12.4", + "parking_lot", "pin-project-lite", "signal-hook-registry", "slab", @@ -6320,18 +5194,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.11", - "toml_edit", -] - [[package]] name = "toml" version = "0.9.5" @@ -6340,7 +5202,7 @@ checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" dependencies = [ "indexmap 2.10.0", "serde", - "serde_spanned 1.0.0", + "serde_spanned", "toml_datetime 0.7.0", "toml_parser", "toml_writer", @@ -6352,9 +5214,6 @@ name = "toml_datetime" version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] [[package]] name = "toml_datetime" @@ -6372,10 +5231,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap 2.10.0", - "serde", - "serde_spanned 0.6.9", "toml_datetime 0.6.11", - "toml_write", "winnow", ] @@ -6388,82 +5244,12 @@ dependencies = [ "winnow", ] -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - [[package]] name = "toml_writer" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" -[[package]] -name = "tonic" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" -dependencies = [ - "async-stream", - "async-trait", - "axum", - "base64 0.22.1", - "bytes", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-timeout", - "hyper-util", - "percent-encoding", - "pin-project", - "prost", - "socket2 0.5.10", - "tokio", - "tokio-stream", - "tower 0.4.13", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tonic-build" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" -dependencies = [ - "prettyplease", - "proc-macro2", - "prost-build", - "prost-types", - "quote", - "syn 2.0.104", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand 0.8.5", - "slab", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - [[package]] name = "tower" version = "0.5.2" @@ -6473,7 +5259,7 @@ dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio", "tower-layer", "tower-service", @@ -6488,11 +5274,11 @@ dependencies = [ "bitflags 2.9.1", "bytes", "futures-util", - "http", - "http-body", + "http 1.3.1", + "http-body 1.0.1", "iri-string", "pin-project-lite", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", ] @@ -6515,7 +5301,6 @@ version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -6616,46 +5401,6 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" -[[package]] -name = "tz-rs" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33851b15c848fad2cf4b105c6bb66eb9512b6f6c44a4b13f57c53c73c707e2b4" -dependencies = [ - "const_fn", -] - -[[package]] -name = "tzdb" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a18ee5bde3433d683d41859650804a5ad89cad17f153a53f1e6a96e0da2d969" -dependencies = [ - "iana-time-zone", - "tz-rs", - "tzdb 0.6.1", -] - -[[package]] -name = "tzdb" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b580f6b365fa89f5767cdb619a55d534d04a4e14c2d7e5b9a31e94598687fb1" -dependencies = [ - "iana-time-zone", - "tz-rs", - "tzdb_data", -] - -[[package]] -name = "tzdb_data" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d69ad05cd8412d9f6e7df6ac91e50ea557687cc1b734339cb6742e547704663" -dependencies = [ - "tz-rs", -] - [[package]] name = "ucd-trie" version = "0.1.7" @@ -6686,12 +5431,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - [[package]] name = "unicode-xid" version = "0.2.6" @@ -6723,12 +5462,9 @@ dependencies = [ "base64 0.22.1", "log", "once_cell", - "rustls", - "rustls-pki-types", "serde", "serde_json", "url", - "webpki-roots 0.26.11", ] [[package]] @@ -6742,6 +5478,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -6760,7 +5502,6 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" dependencies = [ - "getrandom 0.3.3", "js-sys", "serde", "wasm-bindgen", @@ -6778,41 +5519,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "verifier" -version = "0.1.0" -source = "git+https://github.com/confidential-containers/trustee?rev=b526a2ee0e2e5c33d2f49b6215a6eaf1be93f1fd#b526a2ee0e2e5c33d2f49b6215a6eaf1be93f1fd" -dependencies = [ - "anyhow", - "asn1-rs", - "async-trait", - "az-snp-vtpm", - "az-tdx-vtpm", - "base64 0.22.1", - "bincode", - "bitflags 2.9.1", - "byteorder", - "cfg-if", - "eventlog", - "hex", - "intel-tee-quote-verification-rs", - "jsonwebkey", - "kbs-types", - "log", - "openssl", - "reqwest", - "scroll 0.13.0", - "serde", - "serde_json", - "sev 6.2.1", - "sha2", - "shadow-rs", - "strum 0.27.2", - "thiserror 2.0.14", - "tonic-build", - "x509-parser", -] - [[package]] name = "version_check" version = "0.9.5" @@ -6862,15 +5568,6 @@ dependencies = [ "wit-bindgen-rt", ] -[[package]] -name = "wasix" -version = "0.12.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" -dependencies = [ - "wasi 0.11.1+wasi-snapshot-preview1", -] - [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -6950,7 +5647,7 @@ checksum = "d8d49b5d6c64e8558d9b1b065014426f35c18de636895d24893dbbd329743446" dependencies = [ "futures", "js-sys", - "parking_lot 0.12.4", + "parking_lot", "pin-utils", "slab", "wasm-bindgen", @@ -6984,36 +5681,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "webpki-roots" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" -dependencies = [ - "webpki-roots 1.0.2", -] - -[[package]] -name = "webpki-roots" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.44", -] - [[package]] name = "winapi" version = "0.3.9" @@ -7045,6 +5712,28 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core", +] + [[package]] name = "windows-core" version = "0.61.2" @@ -7058,6 +5747,17 @@ dependencies = [ "windows-strings", ] +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + [[package]] name = "windows-implement" version = "0.60.0" @@ -7086,6 +5786,16 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core", + "windows-link", +] + [[package]] name = "windows-result" version = "0.3.4" @@ -7212,6 +5922,15 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -7401,6 +6120,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wit-bindgen-rt" version = "0.39.0" @@ -7427,9 +6156,9 @@ dependencies = [ [[package]] name = "x509-parser" -version = "0.17.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" +checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" dependencies = [ "asn1-rs", "data-encoding", @@ -7438,21 +6167,10 @@ dependencies = [ "nom", "oid-registry", "rusticata-macros", - "thiserror 2.0.14", + "thiserror 1.0.69", "time", ] -[[package]] -name = "yaml-rust2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8902160c4e6f2fb145dbe9d6760a75e3c9522d8bf796ed7047c85919ac7115f8" -dependencies = [ - "arraydeque", - "encoding_rs", - "hashlink", -] - [[package]] name = "yasna" version = "0.4.0" @@ -7483,17 +6201,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.104", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive 0.7.35", + "synstructure 0.13.2", ] [[package]] @@ -7502,18 +6210,7 @@ version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ - "zerocopy-derive 0.8.26", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", + "zerocopy-derive", ] [[package]] @@ -7545,7 +6242,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.104", - "synstructure", + "synstructure 0.13.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 758f9df9..588eb7e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,108 +1,12 @@ [workspace] resolver = "2" members = [ - "crates/enclave", - "crates/enclave/derive", "crates/enclave-server", - "crates/enclave-contract", + "crates/enclave-contract", "crates/enclave", ] -[workspace.lints] -rust.missing_debug_implementations = "warn" -rust.missing_docs = "warn" -rust.rust_2018_idioms = { level = "deny", priority = -1 } -rust.unreachable_pub = "warn" -rust.unused_must_use = "deny" - -[workspace.lints.clippy] -# These are some of clippy's nursery (i.e., experimental) lints that we like. -# By default, nursery lints are allowed. Some of the lints below have made good -# suggestions which we fixed. The others didn't have any findings, so we can -# assume they don't have that many false positives. Let's enable them to -# prevent future problems. -borrow_as_ptr = "warn" -branches_sharing_code = "warn" -clear_with_drain = "warn" -cloned_instead_of_copied = "warn" -collection_is_never_read = "warn" -dbg_macro = "warn" -derive_partial_eq_without_eq = "warn" -doc_markdown = "warn" -empty_line_after_doc_comments = "warn" -empty_line_after_outer_attr = "warn" -enum_glob_use = "warn" -equatable_if_let = "warn" -explicit_into_iter_loop = "warn" -explicit_iter_loop = "warn" -flat_map_option = "warn" -from_iter_instead_of_collect = "warn" -if_not_else = "warn" -if_then_some_else_none = "warn" -implicit_clone = "warn" -imprecise_flops = "warn" -iter_on_empty_collections = "warn" -iter_on_single_items = "warn" -iter_with_drain = "warn" -iter_without_into_iter = "warn" -large_stack_frames = "warn" -manual_assert = "warn" -manual_clamp = "warn" -manual_is_variant_and = "warn" -manual_string_new = "warn" -match_same_arms = "warn" -missing_const_for_fn = "warn" -mutex_integer = "warn" -naive_bytecount = "warn" -needless_bitwise_bool = "warn" -needless_continue = "warn" -needless_for_each = "warn" -needless_pass_by_ref_mut = "warn" -nonstandard_macro_braces = "warn" -option_as_ref_cloned = "warn" -or_fun_call = "warn" -path_buf_push_overwrite = "warn" -read_zero_byte_vec = "warn" -redundant_clone = "warn" -redundant_else = "warn" -single_char_pattern = "warn" -string_lit_as_bytes = "warn" -string_lit_chars_any = "warn" -suboptimal_flops = "warn" -suspicious_operation_groupings = "warn" -trailing_empty_array = "warn" -trait_duplication_in_bounds = "warn" -transmute_undefined_repr = "warn" -trivial_regex = "warn" -tuple_array_conversions = "warn" -type_repetition_in_bounds = "warn" -uninhabited_references = "warn" -unnecessary_self_imports = "warn" -unnecessary_struct_initialization = "warn" -unnested_or_patterns = "warn" -unused_peekable = "warn" -unused_rounding = "warn" -use_self = "warn" -useless_let_if_seq = "warn" -while_float = "warn" -zero_sized_map_values = "warn" - -# These are nursery lints which have findings. Allow them for now. Some are not -# quite mature enough for use in our codebase and some we don't really want. -# Explicitly listing should make it easier to fix in the future. -as_ptr_cast_mut = "allow" -cognitive_complexity = "allow" -debug_assert_with_mut_call = "allow" -fallible_impl_from = "allow" -future_not_send = "allow" -needless_collect = "allow" -non_send_fields_in_send_ty = "allow" -redundant_pub_crate = "allow" -significant_drop_in_scrutinee = "allow" -significant_drop_tightening = "allow" -too_long_first_doc_paragraph = "allow" - [workspace.package] -edition = "2021" +edition = "2024" version = "0.1.0" repository = "https://github.com/SeismicSystems/enclave" homepage = "https://github.com/SeismicSystems/enclave" @@ -111,50 +15,44 @@ license = "MIT" readme = "README.md" [workspace.dependencies] -seismic-enclave-derive = { path = "crates/enclave/derive" } -seismic-enclave = { path = "crates/enclave" } enclave-contract = { path = "crates/enclave-contract" } - -# coco deps -# attestation-service depends on attestation-agent, ensure versions are compatible when updating -# also note the patches at the bottom -attestation-agent = { git = "https://github.com/confidential-containers/guest-components", rev="eb0d89751800a435659f97d6f55cdb822223dc86", default-features = false} -attestation-service = { git = "https://github.com/confidential-containers/trustee", rev="b526a2ee0e2e5c33d2f49b6215a6eaf1be93f1fd", default-features = false} -az-tdx-vtpm = "=0.7.1" -az-cvm-vtpm = "=0.7.1" -kbs-types = { "git" = "https://github.com/virtee/kbs-types.git", rev = "e3cc706" } +seismic-enclave = {path = "crates/enclave" } # Other deps alloy = "1.0" aes-gcm = "0.10" anyhow = "1.0" -auto_impl = "1" -base64 = "0.22" -thiserror = "2.0" +az-tdx-vtpm = "0.7.4" +bincode = "1.3.3" +clap = { version = "4.5.51", features = ["derive"] } +dcap-rs = "0.1.0" +#auto_impl = "1" +#base64 = "0.22" +base64-url = "3.0.0" +#thiserror = "2.0" hex = "0.4.3" hkdf = "0.12" -jsonrpsee = { version = "0.24", features = ["server", "client", "macros"] } -libc = "0.2.171" -log = "0.4" -rand = "0.9" -reqwest = { version = "0.12", default-features = false, features = ["blocking"] } +jsonrpsee = { version = "0.26.0", features = ["server", "client","macros"] } +libc = "0.2.177" +openssl = "0.10.74" +#log = "0.4" +rand = "0.9.2" +reqwest = { version = "0.11", features = ["json"] } schnorrkel = { version = "0.11.2", features = ["serde"] } -scroll = { version = "0.11.0", default-features = false, features = ["derive"] } +#scroll = { version = "0.11.0", default-features = false, features = ["derive"] } secp256k1 = { version = "0.30", features = ["rand", "recovery", "std", "serde"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -sha2 = "0.10" -strum = { version = "0.26", features = ["derive"] } -strum_macros = "0.26" +sha2 = "0.10.9" +#strum = { version = "0.26", features = ["derive"] } +#strum_macros = "0.26" tokio = { version = "1.44", features = ["full"] } -tracing = { version = "0.1"} +tracing = { version = "0.1.41"} tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt", "ansi", "json"] } -proc-macro2 = "1.0" -quote = "1.0" -syn = "2.0" +#proc-macro2 = "1.0" +#quote = "1.0" +#syn = "2.0" +urlencoding = "2.1" zeroize = "1.8.1" - -[patch.crates-io] -az-cvm-vtpm = { git = "https://github.com/kinvolk/azure-cvm-tooling", rev = "2bef60b88db7554935177ec63dd169190a39eab7" } -az-tdx-vtpm = { git = "https://github.com/kinvolk/azure-cvm-tooling", rev = "2bef60b88db7554935177ec63dd169190a39eab7" } -az-snp-vtpm = { git = "https://github.com/kinvolk/azure-cvm-tooling", rev = "2bef60b88db7554935177ec63dd169190a39eab7" } +coco-provider = { git = "https://github.com/automata-network/coco-provider-sdk", default-features = false} +x509-parser = "0.15.1" diff --git a/crates/enclave-contract/contracts/MultisigUpgradeOperator.sol b/crates/enclave-contract/contracts/MultisigUpgradeOperator.sol index dba1f0d3..cd0f36bf 100644 --- a/crates/enclave-contract/contracts/MultisigUpgradeOperator.sol +++ b/crates/enclave-contract/contracts/MultisigUpgradeOperator.sol @@ -9,181 +9,330 @@ import "./UpgradeOperator.sol"; * Uses the ANVIL test keys as the three signers */ contract MultisigUpgradeOperator { - // The three signers (ANVIL keys) - address public constant signer1 = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; // Alice (0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80) - address public constant signer2 = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8; // Bob (0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d) - address public constant signer3 = 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC; // Charlie (0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a) - + address public constant signer1 = + 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; // Alice (0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80) + address public constant signer2 = + 0x70997970C51812dc3A010C7d01b50e0d17dc79C8; // Bob (0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d) + address public constant signer3 = + 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC; // Charlie (0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a) + // The UpgradeOperator contract being controlled - UpgradeOperator constant public upgradeOperator = UpgradeOperator(0x1000000000000000000000000000000000000001); // Set in seismic-reth genesis - + UpgradeOperator public constant upgradeOperator = + UpgradeOperator(0x1000000000000000000000000000000000000001); // Set in seismic-reth genesis + // Nonce counter for proposal uniqueness uint256 public proposalNonce; - - // Mapping to track votes for each proposal - mapping(bytes32 => mapping(address => bool)) public votes; - - // Mapping to track proposal execution status - mapping(bytes32 => bool) public executed; - - // Event emitted when a proposal is created (version 1) - event ProposalCreatedV1(bytes32 indexed proposalId, uint256 nonce, bytes mrtd, bytes mrseam, bytes pcr4, bool status); - - // Event emitted when a vote is cast - event VoteCast(bytes32 indexed proposalId, address indexed voter, bool approved); - - // Event emitted when a proposal is executed - event ProposalExecuted(bytes32 indexed proposalId); - - // Event emitted when upgrade operator is set - event UpgradeOperatorSet(address indexed upgradeOperator); - + + // Enum for proposal types + enum ProposalType { + ADD_MEASUREMENTS, + DEPRECATE_MEASUREMENTS, + REINSTATE_MEASUREMENTS + } + + // Proposal struct + struct Proposal { + ProposalType proposalType; + bytes32 tagHash; + UpgradeOperator.Measurements measurements; + bool executed; + uint256 voteCount; + mapping(address => bool) hasVoted; + } + + // Mapping to track proposals + mapping(bytes32 => Proposal) public proposals; + + // Events + event ProposalCreated( + bytes32 indexed proposalId, + ProposalType indexed proposalType, + string tag, + uint256 nonce + ); + + event VoteCast(bytes32 indexed proposalId, address indexed voter); + + event ProposalExecuted( + bytes32 indexed proposalId, + ProposalType indexed proposalType + ); + + modifier onlySigner() { + require( + msg.sender == signer1 || + msg.sender == signer2 || + msg.sender == signer3, + "Not authorized" + ); + _; + } + + /** + * @dev Creates a proposal to add new measurements + * @param measurements The measurements to add + * @return proposalId The unique identifier for this proposal + */ + function proposeAddMeasurements( + UpgradeOperator.Measurements calldata measurements + ) external onlySigner returns (bytes32 proposalId) { + // Validate inputs + require(bytes(measurements.tag).length > 0, "Tag cannot be empty"); + require(measurements.mrtd.length > 0, "MRTD cannot be empty"); + require(measurements.mrseam.length > 0, "MRSEAM cannot be empty"); + require( + measurements.registrar_slots.length == + measurements.registrar_values.length, + "Registrar arrays length mismatch" + ); + + proposalNonce++; + proposalId = keccak256( + abi.encodePacked( + ProposalType.ADD_MEASUREMENTS, + measurements.tag, + measurements.mrtd, + measurements.mrseam, + proposalNonce + ) + ); + + Proposal storage proposal = proposals[proposalId]; + require(!proposal.executed, "Proposal already exists"); + + proposal.proposalType = ProposalType.ADD_MEASUREMENTS; + proposal.tagHash = keccak256(abi.encodePacked(measurements.tag)); + proposal.measurements = measurements; + + emit ProposalCreated( + proposalId, + ProposalType.ADD_MEASUREMENTS, + measurements.tag, + proposalNonce + ); + + // Auto-vote for proposer + _vote(proposalId); + + return proposalId; + } + + /** + * @dev Creates a proposal to deprecate measurements + * @param measurements The measurements to deprecate + * @return proposalId The unique identifier for this proposal + */ + function proposeDeprecateMeasurements( + UpgradeOperator.Measurements calldata measurements + ) external onlySigner returns (bytes32 proposalId) { + require(bytes(measurements.tag).length > 0, "Tag cannot be empty"); + + proposalNonce++; + proposalId = keccak256( + abi.encodePacked( + ProposalType.DEPRECATE_MEASUREMENTS, + measurements.tag, + proposalNonce + ) + ); + + Proposal storage proposal = proposals[proposalId]; + require(!proposal.executed, "Proposal already exists"); + + proposal.proposalType = ProposalType.DEPRECATE_MEASUREMENTS; + proposal.tagHash = keccak256(abi.encodePacked(measurements.tag)); + // Store tag in measurements.tag for execution + proposal.measurements = measurements; + + emit ProposalCreated( + proposalId, + ProposalType.DEPRECATE_MEASUREMENTS, + measurements.tag, + proposalNonce + ); + + // Auto-vote for proposer + _vote(proposalId); + + return proposalId; + } + /** - * @dev Creates a proposal to set defining attributes (version 1) in the UpgradeOperator - * @param mrtd The MRTD value (48 bytes) - * @param mrseam The MRSEAM value (48 bytes) - * @param pcr4 The PCR4 value (32 bytes) - * @param status The status to set + * @dev Creates a proposal to reinstate measurements + * @param measurements The measurements to reinstate * @return proposalId The unique identifier for this proposal */ - function createProposalV1( - bytes memory mrtd, - bytes memory mrseam, - bytes memory pcr4, - bool status - ) public returns (bytes32 proposalId) { - require(mrtd.length == 48, "Invalid mrtd length"); - require(mrseam.length == 48, "Invalid mrseam length"); - require(pcr4.length == 32, "Invalid pcr4 length"); - - // Increment nonce and use it in proposal ID calculation + function proposeReinstateMeasurements( + UpgradeOperator.Measurements calldata measurements + ) external onlySigner returns (bytes32 proposalId) { + require(bytes(measurements.tag).length > 0, "Tag cannot be empty"); + proposalNonce++; - proposalId = computeProposalIdV1(mrtd, mrseam, pcr4, status, proposalNonce); + proposalId = keccak256( + abi.encodePacked( + ProposalType.REINSTATE_MEASUREMENTS, + measurements.tag, + proposalNonce + ) + ); + + Proposal storage proposal = proposals[proposalId]; + require(!proposal.executed, "Proposal already exists"); + + proposal.proposalType = ProposalType.REINSTATE_MEASUREMENTS; + proposal.tagHash = keccak256(abi.encodePacked(measurements.tag)); + // Store tag in measurements.tag for execution + proposal.measurements = measurements; + + emit ProposalCreated( + proposalId, + ProposalType.REINSTATE_MEASUREMENTS, + measurements.tag, + proposalNonce + ); + + // Auto-vote for proposer + _vote(proposalId); - require(!executed[proposalId], "Proposal already executed"); - - emit ProposalCreatedV1(proposalId, proposalNonce, mrtd, mrseam, pcr4, status); - return proposalId; } - + /** - * @dev Casts a vote on a proposal + * @dev Vote on a proposal * @param proposalId The proposal to vote on - * @param approved Whether to approve the proposal */ - function vote(bytes32 proposalId, bool approved) public { - require(msg.sender == signer1 || msg.sender == signer2 || msg.sender == signer3, "Not authorized to vote"); - require(!executed[proposalId], "Proposal already executed"); - require(!votes[proposalId][msg.sender], "Already voted"); - - votes[proposalId][msg.sender] = approved; - - emit VoteCast(proposalId, msg.sender, approved); + function vote(bytes32 proposalId) external onlySigner { + _vote(proposalId); } - + /** - * @dev Executes a proposal if it has enough votes (version 1) - * @param mrtd The MRTD value (48 bytes) - * @param mrseam The MRSEAM value (48 bytes) - * @param pcr4 The PCR4 value (32 bytes) - * @param status The status to set - * @param nonce The nonce used when creating the proposal + * @dev Internal vote logic */ - function executeProposalV1( - bytes memory mrtd, - bytes memory mrseam, - bytes memory pcr4, - bool status, - uint256 nonce - ) public { - bytes32 proposalId = computeProposalIdV1(mrtd, mrseam, pcr4, status, nonce); - - require(!executed[proposalId], "Proposal already executed"); - - uint256 approvalCount = 0; - if (votes[proposalId][signer1]) approvalCount++; - if (votes[proposalId][signer2]) approvalCount++; - if (votes[proposalId][signer3]) approvalCount++; - - require(approvalCount >= 2, "Insufficient votes"); - - executed[proposalId] = true; - - // Execute the actual set_id_status_v1 call on the UpgradeOperator - upgradeOperator.set_id_status_v1(mrtd, mrseam, pcr4, status); - - emit ProposalExecuted(proposalId); + function _vote(bytes32 proposalId) internal { + Proposal storage proposal = proposals[proposalId]; + + require( + bytes(proposal.measurements.tag).length > 0, + "proposal does not exist" + ); + require(!proposal.executed, "Proposal already executed"); + require(!proposal.hasVoted[msg.sender], "Already voted"); + + proposal.hasVoted[msg.sender] = true; + proposal.voteCount++; + + emit VoteCast(proposalId, msg.sender); } - + /** - * @dev Gets the vote count for a proposal - * @param proposalId The proposal to check - * @return approvalCount Number of approvals - * @return totalVotes Total number of votes cast + * @dev Execute a proposal that has enough votes + * @param proposalId The proposal to execute */ - function getVoteCount(bytes32 proposalId) public view returns (uint256 approvalCount, uint256 totalVotes) { - if (votes[proposalId][signer1]) { - approvalCount++; - totalVotes++; - } - if (votes[proposalId][signer2]) { - approvalCount++; - totalVotes++; - } - if (votes[proposalId][signer3]) { - approvalCount++; - totalVotes++; + function executeProposal(bytes32 proposalId) external { + Proposal storage proposal = proposals[proposalId]; + + require(!proposal.executed, "Proposal already executed"); + require(proposal.voteCount >= 2, "Insufficient votes"); + _executeProposal(proposalId); + } + + /** + * @dev Internal execution logic + */ + function _executeProposal(bytes32 proposalId) internal { + Proposal storage proposal = proposals[proposalId]; + + proposal.executed = true; + + if (proposal.proposalType == ProposalType.ADD_MEASUREMENTS) { + upgradeOperator.addAcceptedMeasurements(proposal.measurements); + } else if ( + proposal.proposalType == ProposalType.DEPRECATE_MEASUREMENTS + ) { + upgradeOperator.deprecateMeasurements(proposal.measurements); + } else if ( + proposal.proposalType == ProposalType.REINSTATE_MEASUREMENTS + ) { + upgradeOperator.reinstateMeasurement(proposal.measurements); } - - return (approvalCount, totalVotes); + + emit ProposalExecuted(proposalId, proposal.proposalType); } - + /** - * @dev Checks if a proposal can be executed + * @dev Get vote status for a proposal * @param proposalId The proposal to check - * @return True if the proposal has enough votes to be executed + * @return voteCount Number of votes + * @return hasVoted1 Whether signer1 voted + * @return hasVoted2 Whether signer2 voted + * @return hasVoted3 Whether signer3 voted + * @return canExecute Whether proposal can be executed */ - function canExecute(bytes32 proposalId) public view returns (bool) { - if (executed[proposalId]) return false; - - uint256 approvalCount = 0; - if (votes[proposalId][signer1]) approvalCount++; - if (votes[proposalId][signer2]) approvalCount++; - if (votes[proposalId][signer3]) approvalCount++; - - return approvalCount >= 2; + function getVoteStatus( + bytes32 proposalId + ) + external + view + returns ( + uint256 voteCount, + bool hasVoted1, + bool hasVoted2, + bool hasVoted3, + bool canExecute + ) + { + Proposal storage proposal = proposals[proposalId]; + + voteCount = proposal.voteCount; + hasVoted1 = proposal.hasVoted[signer1]; + hasVoted2 = proposal.hasVoted[signer2]; + hasVoted3 = proposal.hasVoted[signer3]; + canExecute = !proposal.executed && proposal.voteCount >= 2; } - + /** - * @dev Computes the proposal ID for given parameters and nonce (version 1) - * Uses the UpgradeOperator's computeIdV1 method for the base ID calculation - * @param mrtd The MRTD value (48 bytes) - * @param mrseam The MRSEAM value (48 bytes) - * @param pcr4 The PCR4 value (32 bytes) - * @param status The status to set - * @param nonce The nonce to use - * @return The computed proposal ID + * @dev Get proposal details + * @param proposalId The proposal to query + * @return proposalType The type of proposal + * @return tag The measurement tag + * @return executed Whether the proposal has been executed + * @return voteCount Number of votes */ - function computeProposalIdV1( - bytes memory mrtd, - bytes memory mrseam, - bytes memory pcr4, - bool status, - uint256 nonce - ) public pure returns (bytes32) { - // Create the DefiningAttributesV1 struct and use the UpgradeOperator's computeIdV1 method - UpgradeOperator.DefiningAttributesV1 memory attrs = UpgradeOperator.DefiningAttributesV1(mrtd, mrseam, pcr4); - - bytes32 baseId; - try upgradeOperator.computeIdV1(attrs) returns (bytes32 result) { - baseId = result; - } catch { - revert("upgradeOperator.computeIdV1 failed"); - } + function getProposalInfo( + bytes32 proposalId + ) + external + view + returns ( + ProposalType proposalType, + string memory tag, + bool executed, + uint256 voteCount + ) + { + Proposal storage proposal = proposals[proposalId]; - // Combine with status and nonce for proposal uniqueness - return keccak256(abi.encodePacked(baseId, status, nonce)); + return ( + proposal.proposalType, + proposal.measurements.tag, + proposal.executed, + proposal.voteCount + ); + } + + /** + * @dev Get full measurement details for an add proposal + * @param proposalId The proposal to query + * @return measurements The full measurements struct + */ + function getProposalMeasurements( + bytes32 proposalId + ) external view returns (UpgradeOperator.Measurements memory) { + require( + proposals[proposalId].proposalType == ProposalType.ADD_MEASUREMENTS, + "Not an add measurements proposal" + ); + return proposals[proposalId].measurements; } -} \ No newline at end of file +} diff --git a/crates/enclave-contract/contracts/UpgradeOperator.sol b/crates/enclave-contract/contracts/UpgradeOperator.sol index 8ffcfa56..85593dd2 100644 --- a/crates/enclave-contract/contracts/UpgradeOperator.sol +++ b/crates/enclave-contract/contracts/UpgradeOperator.sol @@ -1,70 +1,199 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; -/* - The Upgrade Operator is responsible for defining the - configuration to upgrade. -*/ contract UpgradeOperator { - - struct DefiningAttributesV1 { + struct Measurements { + string tag; bytes mrtd; bytes mrseam; - bytes pcr4; + uint8[] registrar_slots; + bytes[] registrar_values; } - struct DefiningAttributesV2 { - bytes mrtd; - bytes mrseam; - bytes pcr4; - bytes pcr7; + mapping(bytes32 => Measurements) public acceptedMeasurements; + mapping(bytes32 => Measurements) public deprecatedMeasurements; + + // Keep track of all tags for enumeration if needed + bytes32[] public acceptedTags; + bytes32[] public deprecatedTags; + + // Track if a tag exists to prevent duplicates + mapping(bytes32 => bool) public tagExists; + + address public constant OWNER = 0x1000000000000000000000000000000000000002; + + event MeasurementAdded(string indexed tag, bytes32 indexed tagHash); + event MeasurementDeprecated(string indexed tag, bytes32 indexed tagHash); + + modifier onlyNetworkMultisig() virtual { + require(msg.sender == OWNER, "Ownable: caller is not the owner"); + _; } - address constant public owner = 0x1000000000000000000000000000000000000002; // Set in seismic-reth genesis - mapping(bytes32 => bool) public attributes; + /** + * @dev Add a set of measurements the network allows + * @param measurements The measurements to add + */ + function addAcceptedMeasurements( + Measurements calldata measurements + ) external onlyNetworkMultisig { + bytes32 tagHash = keccak256(abi.encodePacked(measurements.tag)); + + // Check uniqueness + require(!tagExists[tagHash], "Measurement tag already exists"); - event SetDefiningAttributesV1(bytes mrtd, bytes mrseam, bytes pcr4, bool status); - event SetDefiningAttributesV2(bytes mrtd, bytes mrseam, bytes pcr4, bytes pcr7, bool status); + // Validate inputs TODO: assert actual length these measurements should be + require(bytes(measurements.tag).length > 0, "Tag cannot be empty"); + require(measurements.mrtd.length > 0, "MRTD cannot be empty"); + require(measurements.mrseam.length > 0, "MRSEAM cannot be empty"); + require( + measurements.registrar_slots.length == + measurements.registrar_values.length, + "Registrar arrays length mismatch" + ); + bytes32 measurementHash = _getMeasurementHash(measurements); + + // Store the measurement + acceptedMeasurements[measurementHash] = measurements; + acceptedTags.push(measurementHash); + tagExists[tagHash] = true; + + emit MeasurementAdded(measurements.tag, measurementHash); + } /** - * @dev Sets the status for a set of defining attributes (version 1) + * @dev accept a currently deprecated set of measurements + * @param m the measurement to reinstate */ - function set_id_status_v1(bytes memory mrtd, bytes memory mrseam, bytes memory pcr4, bool status) public { - require(msg.sender == owner, "Only owner can set status"); - require(mrtd.length == 48, "Invalid mrtd length"); - require(mrseam.length == 48, "Invalid mrseam length"); - require(pcr4.length == 32, "Invalid pcr4 length"); - - DefiningAttributesV1 memory attrs = DefiningAttributesV1(mrtd, mrseam, pcr4); - bytes32 id = computeIdV1(attrs); - attributes[id] = status; - emit SetDefiningAttributesV1(mrtd, mrseam, pcr4, status); + function reinstateMeasurement( + Measurements calldata m + ) external onlyNetworkMultisig { + bytes32 tagHash = keccak256(abi.encodePacked(m.tag)); + + bytes32 measurementHash = _getMeasurementHash(m); + + // Check if measurement exists in deprecated + require( + keccak256( + abi.encodePacked(deprecatedMeasurements[measurementHash].tag) + ) == tagHash, + "No deprecated measurement with that tag or hash" + ); + + // Move from deprecated to accepted + Measurements memory measurement = deprecatedMeasurements[ + measurementHash + ]; + acceptedMeasurements[measurementHash] = measurement; + acceptedTags.push(measurementHash); + + // Remove from deprecated + delete deprecatedMeasurements[measurementHash]; + _removeFromArray(deprecatedTags, measurementHash); + + emit MeasurementAdded(m.tag, measurementHash); } /** - * @dev Gets the status of a set of defining attributes (version 1) + * @dev Deprecate a currently allowed set of measurements + * @param m the measurement to deprecate */ - function get_id_status_v1(bytes memory mrtd, bytes memory mrseam, bytes memory pcr4) public view returns (bool) { - require(mrtd.length == 48, "Invalid mrtd length"); - require(mrseam.length == 48, "Invalid mrseam length"); - require(pcr4.length == 32, "Invalid pcr4 length"); - - DefiningAttributesV1 memory attrs = DefiningAttributesV1(mrtd, mrseam, pcr4); - bytes32 id = computeIdV1(attrs); - return attributes[id]; + function deprecateMeasurements( + Measurements calldata m + ) external onlyNetworkMultisig { + bytes32 tagHash = keccak256(abi.encodePacked(m.tag)); + bytes32 measurementHash = _getMeasurementHash(m); + // Check if measurement exists in accepted + require( + keccak256( + abi.encodePacked(acceptedMeasurements[measurementHash].tag) + ) == tagHash, + "No deprecated measurement with that tag or hash" + ); + + // Move from accepted to deprecated + Measurements memory measurement = acceptedMeasurements[measurementHash]; + deprecatedMeasurements[measurementHash] = measurement; + deprecatedTags.push(measurementHash); + + // Remove from accepted + delete acceptedMeasurements[measurementHash]; + _removeFromArray(acceptedTags, measurementHash); + + emit MeasurementDeprecated(m.tag, measurementHash); + } + + /** + * @dev Check if measurements are accepted + * @param measurementHash Hash of the measurements to check + */ + function isAccepted(bytes32 measurementHash) external view returns (bool) { + return bytes(acceptedMeasurements[measurementHash].tag).length > 0; + } + + /** + * @dev Check if measurements are accepted + * @param measurementHash Hash of the measurements to check + */ + function isDeprecated( + bytes32 measurementHash + ) external view returns (bool) { + return bytes(deprecatedMeasurements[measurementHash].tag).length > 0; } /** - * @dev Computes the ID for a set of defining attributes (version 1) + * @dev Get accepted measurement by tag */ - function computeIdV1(DefiningAttributesV1 memory attrs) public pure returns (bytes32) { - return keccak256(abi.encode(attrs)); + function getAcceptedMeasurement( + bytes32 measurementHash + ) external view returns (Measurements memory) { + require( + bytes(acceptedMeasurements[measurementHash].tag).length > 0, + "Measurement not found" + ); + return acceptedMeasurements[measurementHash]; } /** - * @dev Computes the ID for a set of defining attributes (version 2) + * @dev Get count of accepted measurements */ - function computeIdV2(DefiningAttributesV2 memory attrs) public pure returns (bytes32) { - return keccak256(abi.encode(attrs)); + function getAcceptedCount() external view returns (uint256) { + return acceptedTags.length; + } + + function getMeasurementHash( + Measurements calldata measurements + ) external pure returns (bytes32) { + return _getMeasurementHash(measurements); + } + + /** + * @dev Helper to remove element from array + */ + function _removeFromArray( + bytes32[] storage array, + bytes32 element + ) private { + for (uint256 i = 0; i < array.length; i++) { + if (array[i] == element) { + array[i] = array[array.length - 1]; + array.pop(); + break; + } + } + } + + function _getMeasurementHash( + Measurements memory m + ) private pure returns (bytes32) { + return + keccak256( + abi.encode( + m.mrtd, + m.mrseam, + m.registrar_slots, + m.registrar_values + ) + ); } -} \ No newline at end of file +} diff --git a/crates/enclave-contract/contracts/foundry.toml b/crates/enclave-contract/contracts/foundry.toml deleted file mode 100644 index 82d497fc..00000000 --- a/crates/enclave-contract/contracts/foundry.toml +++ /dev/null @@ -1,4 +0,0 @@ -[profile.default] -src = "." -out = "out" -libs = [] \ No newline at end of file diff --git a/crates/enclave-contract/foundry.toml b/crates/enclave-contract/foundry.toml new file mode 100644 index 00000000..ecb7d231 --- /dev/null +++ b/crates/enclave-contract/foundry.toml @@ -0,0 +1,5 @@ +[profile.default] +src = "contracts" +out = "out" +libs = ["lib"] +test = "tests" \ No newline at end of file diff --git a/crates/enclave-contract/lib/forge-std b/crates/enclave-contract/lib/forge-std new file mode 160000 index 00000000..8e40513d --- /dev/null +++ b/crates/enclave-contract/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 8e40513d678f392f398620b3ef2b418648b33e89 diff --git a/crates/enclave-contract/src/contract_interface.rs b/crates/enclave-contract/src/contract_interface.rs index b57d3503..5eccd1bf 100644 --- a/crates/enclave-contract/src/contract_interface.rs +++ b/crates/enclave-contract/src/contract_interface.rs @@ -2,82 +2,56 @@ use alloy::{ network::EthereumWallet, - primitives::{bytes, Bytes, U256}, + primitives::{FixedBytes, Log}, providers::ProviderBuilder, signers::local::PrivateKeySigner, sol, }; -// Generate contract bindings for the factory -sol! { - #[sol(rpc)] - interface UpgradeOperatorFactory { - function deployUpgradeOperator(bytes32 salt) external returns (address); - function deployUpgradeOperatorWithOwner(bytes32 salt, address owner) external returns (address); - function deployMultisigUpgradeOperator(bytes32 salt, address upgradeOperator) external returns (address); - function deployUpgradeOperatorWithMultisig(bytes32 upgradeOperatorSalt, bytes32 multisigSalt) external returns (address, address); - function computeUpgradeOperatorAddress(bytes32 salt) external view returns (address); - function computeUpgradeOperatorAddressWithOwner(bytes32 salt, address owner) external view returns (address); - function computeMultisigUpgradeOperatorAddress(bytes32 salt, address upgradeOperator) external view returns (address); - function isDeployed(address contractAddress) external view returns (bool); - } -} +use crate::{Measurements, MultisigUpgradeOperator::ProposalCreated}; // Generate contract bindings for the upgrade operator sol! { #[sol(rpc)] interface UpgradeOperator { - function set_id_status_v1(bytes mrtd, bytes mrseam, bytes pcr4, bool status) external; - function get_id_status_v1(bytes mrtd, bytes mrseam, bytes pcr4) external view returns (bool); - function computeIdV1(bytes mrtd, bytes mrseam, bytes pcr4) external pure returns (bytes32); - function owner() external view returns (address); + struct Measurements { + string tag; + bytes mrtd; + bytes mrseam; + uint8[] registrar_slots; + bytes[] registrar_values; + } + function acceptedMeasurments(bytes32) public returns(Measurements); + function deprecatedMeasurments(bytes32) public returns(Measurements); + function acceptedTags() public returns(bytes32[]); + function deprecatedTags() public returns(bytes32[]); + + function addAcceptedMeasurements(Measurements measurements) external; + function reinstateMeasurement(Measurements measurements) external; + function deprecateMeasurements(Measurements measurements) external; + function isAccepted(bytes32 measurementHash) external view returns(bool); + function isDeprecated(bytes32 measurementHash) external view returns(bool); + function getAcceptedMeasurement(bytes32 measurementHash) external view returns(Measurements); + function getAcceptedCount() external view returns (uint256); + function OWNER() external view returns (address); + function getMeasurementHash(Measurements measurements) external pure returns(bytes32); } -} - // Generate contract bindings for the multisig contract -sol! { #[sol(rpc)] interface MultisigUpgradeOperator { - function createProposalV1(bytes mrtd, bytes mrseam, bytes pcr4, bool status) external returns (bytes32); - function vote(bytes32 proposalId, bool approved) external; - function executeProposalV1(bytes mrtd, bytes mrseam, bytes pcr4, bool status, uint256 nonce) external; - function getVoteCount(bytes32 proposalId) external view returns (uint256 approvalCount, uint256 totalVotes); - function canExecute(bytes32 proposalId) external view returns (bool); - function computeProposalIdV1(bytes mrtd, bytes mrseam, bytes pcr4, bool status, uint256 nonce) external view returns (bytes32); + event ProposalCreated(bytes32 indexed proposalId,uint8 indexed proposalType,string tag,uint256 nonce); + + function proposeAddMeasurements(UpgradeOperator.Measurements measurements) external returns(bytes32 proposalId); + function proposeDeprecateMeasurements(UpgradeOperator.Measurements measurements) external returns(bytes32 proposalId); + function proposeReinstateMeasurements(UpgradeOperator.Measurements measurements) external returns(bytes32 proposalId); + function vote(bytes32 proposalId) external; + function executeProposal(bytes32 proposalId) external; + function getVoteStatus(bytes32 proposalId) external view returns (uint256 voteCount, bool hasVoted1, bool hasVoted2, bool hasVoted3, bool canExecute); function proposalNonce() external view returns (uint256); function signer1() external view returns (address); function signer2() external view returns (address); function signer3() external view returns (address); function upgradeOperator() external view returns (address); - function setUpgradeOperator(address _upgradeOperator) external; - function factory() external view returns (address); - } -} - -/// Represents the proposal parameters for upgrade validation -/// This struct makes it easy to change the parameters in the future -/// To change parameters, just modify this struct and update the contract interfaces -#[derive(Debug, Clone)] -pub struct ProposalParamsV1 { - pub mrtd: Bytes, // 48 bytes - pub mrseam: Bytes, // 48 bytes - pub pcr4: Bytes, // 32 bytes -} - -impl ProposalParamsV1 { - /// Creates a new ProposalParams instance - pub fn new(mrtd: Bytes, mrseam: Bytes, pcr4: Bytes) -> Self { - Self { mrtd, mrseam, pcr4 } - } - - /// Creates test proposal parameters - /// Based off the devbox values - pub fn test_params() -> Self { - Self { - mrtd: bytes!("cbd40696f617d42254fc7037469cbcf1414fe173678798cfa1070b7d40e26fa8175b99d0cd245994278f980dec73146a"), - mrseam: bytes!("9790d89a10210ec6968a773cee2ca05b5aa97309f36727a968527be4606fc19e6f73acce350946c9d46a9bf7a63f8430"), - pcr4: bytes!("6f2f7d9a42b35a2f8f9d7bf366ca3e369a45d004f3ac49b0a93785fe817c82b5"), - } } } @@ -98,12 +72,11 @@ pub async fn create_multisig_proposal( multisig_address: alloy::primitives::Address, sk: &str, rpc: &str, - params: &ProposalParamsV1, - status: bool, -) -> Result<([u8; 32], u64), anyhow::Error> { + params: Measurements, +) -> Result, anyhow::Error> { // Set up signer with the provided sk let signer: PrivateKeySigner = sk.parse().unwrap(); - let wallet = EthereumWallet::from(signer); + let wallet = EthereumWallet::from(signer.clone()); let rpc_url = reqwest::Url::parse(rpc).unwrap(); let provider = ProviderBuilder::new().wallet(wallet).connect_http(rpc_url); @@ -111,20 +84,8 @@ pub async fn create_multisig_proposal( let multisig_contract = MultisigUpgradeOperator::new(multisig_address, std::sync::Arc::new(provider.clone())); - // Get current nonce before creating proposal (for debugging/logging if needed) - let _current_nonce = multisig_contract - .proposalNonce() - .call() - .await - .map_err(|e| anyhow::anyhow!("Failed to get current nonce: {:?}", e))?; - // Create proposal - let create_tx = multisig_contract.createProposalV1( - params.mrtd.clone(), - params.mrseam.clone(), - params.pcr4.clone(), - status, - ); + let create_tx = multisig_contract.proposeAddMeasurements(params); let create_pending = create_tx.send().await.map_err(|e| { anyhow::anyhow!( "create_multisig_proposal create proposal tx failed: {:?}", @@ -132,37 +93,17 @@ pub async fn create_multisig_proposal( ) })?; - let _create_receipt = create_pending - .watch() + // wait for it to be included + let receipt = create_pending + .get_receipt() .await .map_err(|e| anyhow::anyhow!("Failed to get proposal creation receipt: {:?}", e))?; - // Get the new nonce after proposal creation - let new_nonce = multisig_contract - .proposalNonce() - .call() - .await - .map_err(|e| anyhow::anyhow!("Failed to get new nonce: {:?}", e))?; - - // Compute the proposal ID using the new nonce - let proposal_id = multisig_contract - .computeProposalIdV1( - params.mrtd.clone(), - params.mrseam.clone(), - params.pcr4.clone(), - status, - new_nonce, - ) - .call() - .await - .map_err(|e| anyhow::anyhow!("Failed to compute proposal ID: {:?}", e))?; + let event: Log = receipt.decoded_log().unwrap(); - println!( - "Proposal created with ID: {:?}, nonce: {}", - proposal_id, new_nonce - ); + let proposal_id = event.proposalId; - Ok((proposal_id.into(), new_nonce.try_into().unwrap())) + Ok(proposal_id) } /// Votes on a proposal in the MultisigUpgradeOperator contract. @@ -182,8 +123,7 @@ pub async fn vote_on_multisig_proposal( multisig_address: alloy::primitives::Address, sk: &str, rpc: &str, - proposal_id: [u8; 32], - approved: bool, + proposal_id: FixedBytes<32>, ) -> Result<(), anyhow::Error> { // Set up signer with the provided sk let signer: PrivateKeySigner = sk.parse().unwrap(); @@ -196,7 +136,7 @@ pub async fn vote_on_multisig_proposal( MultisigUpgradeOperator::new(multisig_address, std::sync::Arc::new(provider)); // Vote on proposal - let vote_tx = multisig_contract.vote(proposal_id.into(), approved); + let vote_tx = multisig_contract.vote(proposal_id); let vote_pending = vote_tx .send() .await @@ -207,7 +147,7 @@ pub async fn vote_on_multisig_proposal( .await .map_err(|e| anyhow::anyhow!("Failed to get vote receipt: {:?}", e))?; - println!("Voted {} on proposal: {:?}", approved, proposal_id); + println!("Voted on proposal:{:?}", proposal_id); Ok(()) } @@ -230,9 +170,7 @@ pub async fn execute_multisig_proposal( multisig_address: alloy::primitives::Address, sk: &str, rpc: &str, - params: &ProposalParamsV1, - status: bool, - nonce: u64, + params: FixedBytes<32>, ) -> Result<(), anyhow::Error> { // Set up signer with the provided sk let signer: PrivateKeySigner = sk.parse().unwrap(); @@ -245,13 +183,7 @@ pub async fn execute_multisig_proposal( MultisigUpgradeOperator::new(multisig_address, std::sync::Arc::new(provider)); // Execute proposal - let execute_tx = multisig_contract.executeProposalV1( - params.mrtd.clone(), - params.mrseam.clone(), - params.pcr4.clone(), - status, - U256::from(nonce), - ); + let execute_tx = multisig_contract.executeProposal(params); let execute_pending = execute_tx .send() .await @@ -281,7 +213,7 @@ pub async fn execute_multisig_proposal( pub async fn can_execute_multisig_proposal( multisig_address: alloy::primitives::Address, rpc: &str, - proposal_id: [u8; 32], + proposal_id: FixedBytes<32>, ) -> Result { let rpc_url = reqwest::Url::parse(rpc).unwrap(); let provider = ProviderBuilder::new().connect_http(rpc_url); @@ -291,13 +223,13 @@ pub async fn can_execute_multisig_proposal( MultisigUpgradeOperator::new(multisig_address, std::sync::Arc::new(provider)); // Check if proposal can be executed - let can_execute = multisig_contract - .canExecute(proposal_id.into()) + let res = multisig_contract + .getVoteStatus(proposal_id) .call() .await .map_err(|e| anyhow::anyhow!("Failed to check if proposal can be executed: {:?}", e))?; - Ok(can_execute) + Ok(res.canExecute) } /// Gets the vote count for a proposal in the MultisigUpgradeOperator contract. @@ -314,8 +246,8 @@ pub async fn can_execute_multisig_proposal( pub async fn get_multisig_vote_count( multisig_address: alloy::primitives::Address, rpc: &str, - proposal_id: [u8; 32], -) -> Result<(u64, u64), anyhow::Error> { + proposal_id: FixedBytes<32>, +) -> Result { let rpc_url = reqwest::Url::parse(rpc).unwrap(); let provider = ProviderBuilder::new().connect_http(rpc_url); @@ -324,16 +256,13 @@ pub async fn get_multisig_vote_count( MultisigUpgradeOperator::new(multisig_address, std::sync::Arc::new(provider)); // Get vote count - let result = multisig_contract - .getVoteCount(proposal_id.into()) + let res = multisig_contract + .getVoteStatus(proposal_id) .call() .await .map_err(|e| anyhow::anyhow!("Failed to get vote count: {:?}", e))?; - Ok(( - result.approvalCount.try_into().unwrap(), - result.totalVotes.try_into().unwrap(), - )) + Ok(res.voteCount.try_into().unwrap()) } /// Checks if a proposal configuration is approved in the UpgradeOperator contract. @@ -347,10 +276,10 @@ pub async fn get_multisig_vote_count( /// # Returns /// /// * `Result` - Returns true if the proposal is approved, or an `anyhow::Error` if an error occurs. -pub async fn check_proposal_status_v1( +pub async fn check_proposal_status( upgrade_operator_address: alloy::primitives::Address, rpc: &str, - params: &ProposalParamsV1, + params: Measurements, ) -> Result { let rpc_url = reqwest::Url::parse(rpc).unwrap(); let provider = ProviderBuilder::new().connect_http(rpc_url); @@ -359,47 +288,18 @@ pub async fn check_proposal_status_v1( let upgrade_operator_contract = UpgradeOperator::new(upgrade_operator_address, std::sync::Arc::new(provider)); + let measurement_hash = upgrade_operator_contract + .getMeasurementHash(params) + .call() + .await + .map_err(|e| anyhow::anyhow!("get_measurement_hash failed: {:?}", e))?; + // Check proposal status let status = upgrade_operator_contract - .get_id_status_v1( - params.mrtd.clone(), - params.mrseam.clone(), - params.pcr4.clone(), - ) + .isAccepted(measurement_hash) .call() .await .map_err(|e| anyhow::anyhow!("check_proposal_status_v1 failed: {:?}", e))?; Ok(status) } - -/// Computes the CREATE2 address for a contract without deploying it. -/// -/// # Arguments -/// -/// * `factory_address` - The address of the factory contract. -/// * `rpc` - A string slice representing the RPC URL of the Ethereum node. -/// * `salt` - A 32-byte salt value for CREATE2 deployment. -/// -/// # Returns -/// -/// * `Result` - Returns the computed CREATE2 address if successful, or an `anyhow::Error` if an error occurs. -pub async fn compute_create2_address( - factory_address: alloy::primitives::Address, - rpc: &str, - salt: [u8; 32], -) -> Result { - let rpc_url = reqwest::Url::parse(rpc).unwrap(); - let provider = ProviderBuilder::new().connect_http(rpc_url); - - let factory_contract = - UpgradeOperatorFactory::new(factory_address, std::sync::Arc::new(provider)); - - let expected_address = factory_contract - .computeUpgradeOperatorAddress(salt.into()) - .call() - .await - .map_err(|e| anyhow::anyhow!("Failed to compute CREATE2 address: {:?}", e))?; - - Ok(expected_address) -} diff --git a/crates/enclave-contract/src/lib.rs b/crates/enclave-contract/src/lib.rs index 49fcb1cb..456295a1 100644 --- a/crates/enclave-contract/src/lib.rs +++ b/crates/enclave-contract/src/lib.rs @@ -7,6 +7,7 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] pub mod contract_interface; +pub use contract_interface::UpgradeOperator::Measurements; pub use contract_interface::*; /// Anvil's first secret key that they publically expose and fund for testing diff --git a/crates/enclave-contract/tests/MultisigUpgradeOperator.t.sol b/crates/enclave-contract/tests/MultisigUpgradeOperator.t.sol new file mode 100644 index 00000000..d46ab672 --- /dev/null +++ b/crates/enclave-contract/tests/MultisigUpgradeOperator.t.sol @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "../contracts/UpgradeOperator.sol"; +import "../contracts/MultisigUpgradeOperator.sol"; + +contract MultisigUpgradeOperatorTest is Test { + UpgradeOperatorMock public upgradeOperator; + MultisigUpgradeOperator public multisig; + + // ANVIL test accounts + address public signer1 = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; + address public signer2 = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8; + address public signer3 = 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC; + address public nonSigner = address(0x4); + + // Test data + UpgradeOperator.Measurements public testMeasurement1; + UpgradeOperator.Measurements public testMeasurement2; + bytes32 public measurement1Hash; + bytes32 public measurement2Hash; + + event ProposalCreated( + bytes32 indexed proposalId, + MultisigUpgradeOperator.ProposalType indexed proposalType, + string tag, + uint256 nonce + ); + + event VoteCast(bytes32 indexed proposalId, address indexed voter); + + event ProposalExecuted( + bytes32 indexed proposalId, + MultisigUpgradeOperator.ProposalType indexed proposalType + ); + + function setUp() public { + // Deploy MultisigUpgradeOperator + multisig = new MultisigUpgradeOperator(); + + // Since we can't actually transfer ownership in this test setup, + // we'll use vm.etch to deploy the UpgradeOperator at the expected address + // and modify it to accept the multisig as owner + + // For testing purposes, we'll deploy a modified UpgradeOperator + // that accepts our multisig as the owner + UpgradeOperatorMock mockUpgradeOperator = new UpgradeOperatorMock( + address(multisig) + ); + vm.etch( + address(0x1000000000000000000000000000000000000001), + address(mockUpgradeOperator).code + ); + + upgradeOperator = UpgradeOperatorMock( + 0x1000000000000000000000000000000000000001 + ); + + // Setup test measurements + testMeasurement1.tag = "AzureV1"; + testMeasurement1 + .mrtd = hex"cbd40696f617d42254fc7037469cbcf1414fe173678798cfa1070b7d40e26fa8175b99d0cd245994278f980dec73146a"; + testMeasurement1 + .mrseam = hex"9790d89a10210ec6968a773cee2ca05b5aa97309f36727a968527be4606fc19e6f73acce350946c9d46a9bf7a63f8430"; + testMeasurement1.registrar_slots = new uint8[](1); + testMeasurement1.registrar_slots[0] = 4; + testMeasurement1.registrar_values = new bytes[](1); + testMeasurement1.registrar_values[ + 0 + ] = hex"6f2f7d9a42b35a2f8f9d7bf366ca3e369a45d004f3ac49b0a93785fe817c82b5"; + + testMeasurement2.tag = "AWSV1"; + testMeasurement2 + .mrtd = hex"555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555"; + testMeasurement2 + .mrseam = hex"666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666"; + testMeasurement2.registrar_slots = new uint8[](1); + testMeasurement2.registrar_slots[0] = 3; + testMeasurement2.registrar_values = new bytes[](1); + testMeasurement2.registrar_values[ + 0 + ] = hex"77777777777777777777777777777777777777777777777777777777"; + + measurement1Hash = upgradeOperator.getMeasurementHash(testMeasurement1); + measurement2Hash = upgradeOperator.getMeasurementHash(testMeasurement2); + } + + // Test proposal creation for adding measurements + function testProposeAddMeasurements() public { + vm.prank(signer1); + + bytes32 proposalId = multisig.proposeAddMeasurements(testMeasurement1); + + // Check proposal was created + ( + MultisigUpgradeOperator.ProposalType proposalType, + string memory tag, + bool executed, + uint256 voteCount + ) = multisig.getProposalInfo(proposalId); + + assertEq( + uint(proposalType), + uint(MultisigUpgradeOperator.ProposalType.ADD_MEASUREMENTS) + ); + assertEq(tag, "AzureV1"); + assertFalse(executed); + assertEq(voteCount, 1); // Auto-voted by proposer + } + + // Test non-signer cannot create proposals + function testNonSignerCannotPropose() public { + vm.prank(nonSigner); + vm.expectRevert("Not authorized"); + multisig.proposeAddMeasurements(testMeasurement1); + } + + // Test voting on proposal + function testVoting() public { + vm.prank(signer1); + bytes32 proposalId = multisig.proposeAddMeasurements(testMeasurement1); + + // Check initial vote count + (uint256 voteCount, , , , ) = multisig.getVoteStatus(proposalId); + assertEq(voteCount, 1); + + // Second signer votes + vm.prank(signer2); + vm.expectEmit(true, true, false, true); + emit VoteCast(proposalId, signer2); + multisig.vote(proposalId); + + // Check vote count increased + (voteCount, , , , ) = multisig.getVoteStatus(proposalId); + assertEq(voteCount, 2); + } + + // Test double voting fails + function testDoubleVoteFails() public { + vm.prank(signer1); + bytes32 proposalId = multisig.proposeAddMeasurements(testMeasurement1); + + vm.prank(signer1); + vm.expectRevert("Already voted"); + multisig.vote(proposalId); + } + + // Test manual execution + function testManualExecution() public { + vm.prank(signer1); + bytes32 proposalId = multisig.proposeAddMeasurements(testMeasurement1); + + vm.prank(signer2); + multisig.vote(proposalId); + + // Anyone can execute once threshold is met + vm.prank(nonSigner); + multisig.executeProposal(proposalId); + + // Check proposal is executed + (, , bool executed, ) = multisig.getProposalInfo(proposalId); + assertTrue(executed); + } + + // Test cannot execute without enough votes + function testCannotExecuteWithoutVotes() public { + vm.prank(signer1); + bytes32 proposalId = multisig.proposeAddMeasurements(testMeasurement1); + + vm.expectRevert("Insufficient votes"); + multisig.executeProposal(proposalId); + } + + // Test deprecate measurements proposal + function testProposeDeprecateMeasurements() public { + // First add a measurement via multisig + vm.prank(signer1); + bytes32 addProposalId = multisig.proposeAddMeasurements( + testMeasurement1 + ); + vm.prank(signer2); + multisig.vote(addProposalId); + multisig.executeProposal(addProposalId); + + assertTrue(upgradeOperator.isAccepted(measurement1Hash)); + + // Now propose to deprecate it + vm.prank(signer1); + bytes32 deprecateProposalId = multisig.proposeDeprecateMeasurements( + testMeasurement1 + ); + + // Vote to execute + vm.prank(signer3); + multisig.vote(deprecateProposalId); + multisig.executeProposal(deprecateProposalId); + + // Check it's deprecated + assertFalse(upgradeOperator.isAccepted(measurement1Hash)); + assertTrue(upgradeOperator.isDeprecated(measurement1Hash)); + } + + // Test reinstate measurements proposal + function testProposeReinstateMeasurements() public { + // Add, then deprecate a measurement + vm.prank(signer1); + bytes32 addProposalId = multisig.proposeAddMeasurements( + testMeasurement1 + ); + vm.prank(signer2); + multisig.vote(addProposalId); + multisig.executeProposal(addProposalId); + + vm.prank(signer1); + bytes32 deprecateProposalId = multisig.proposeDeprecateMeasurements( + testMeasurement1 + ); + vm.prank(signer2); + multisig.vote(deprecateProposalId); + multisig.executeProposal(deprecateProposalId); + assertFalse(upgradeOperator.isAccepted(measurement1Hash)); + assertTrue(upgradeOperator.isDeprecated(measurement1Hash)); + + // Now propose to reinstate + vm.prank(signer1); + bytes32 reinstateProposalId = multisig.proposeReinstateMeasurements( + testMeasurement1 + ); + vm.prank(signer3); + multisig.vote(reinstateProposalId); + multisig.executeProposal(reinstateProposalId); + + // Check it's reinstated + assertTrue(upgradeOperator.isAccepted(measurement1Hash)); + assertFalse(upgradeOperator.isDeprecated(measurement1Hash)); + } + + // Test get vote status + function testGetVoteStatus() public { + vm.prank(signer1); + bytes32 proposalId = multisig.proposeAddMeasurements(testMeasurement1); + + ( + uint256 voteCount, + bool hasVoted1, + bool hasVoted2, + bool hasVoted3, + bool canExecute + ) = multisig.getVoteStatus(proposalId); + + assertEq(voteCount, 1); + assertTrue(hasVoted1); + assertFalse(hasVoted2); + assertFalse(hasVoted3); + assertFalse(canExecute); + + vm.prank(signer2); + multisig.vote(proposalId); + + (voteCount, hasVoted1, hasVoted2, hasVoted3, canExecute) = multisig + .getVoteStatus(proposalId); + + assertEq(voteCount, 2); + assertTrue(hasVoted1); + assertTrue(hasVoted2); + assertFalse(hasVoted3); + assertTrue(canExecute); + } + + // Test complete multisig workflow + function testCompleteMultisigWorkflow() public { + // Signer1 proposes to add Azure measurements + vm.prank(signer1); + bytes32 proposal1 = multisig.proposeAddMeasurements(testMeasurement1); + // Signer2 votes, triggering execution + vm.prank(signer2); + multisig.vote(proposal1); + multisig.executeProposal(proposal1); + + assertTrue(upgradeOperator.isAccepted(measurement1Hash)); + + // Signer2 proposes to add AWS measurements + vm.prank(signer2); + bytes32 proposal2 = multisig.proposeAddMeasurements(testMeasurement2); + + // Signer3 votes allowing enough votes for execution + vm.prank(signer3); + multisig.vote(proposal2); + + // execute + multisig.executeProposal(proposal2); + + assertTrue(upgradeOperator.isAccepted(measurement2Hash)); + + assertEq(upgradeOperator.getAcceptedCount(), 2); + + // Signer1 proposes to deprecate Azure + vm.prank(signer1); + bytes32 proposal3 = multisig.proposeDeprecateMeasurements( + testMeasurement1 + ); + + // Signer2 votes + vm.prank(signer2); + multisig.vote(proposal3); + multisig.executeProposal(proposal3); + + assertFalse(upgradeOperator.isAccepted(measurement1Hash)); + assertTrue(upgradeOperator.isDeprecated(measurement1Hash)); + assertEq(upgradeOperator.getAcceptedCount(), 1); + + // Signer3 proposes to reinstate Azure + vm.prank(signer3); + bytes32 proposal4 = multisig.proposeReinstateMeasurements( + testMeasurement1 + ); + + // Signer1 votes + vm.prank(signer1); + multisig.vote(proposal4); + multisig.executeProposal(proposal4); + + assertTrue(upgradeOperator.isAccepted(measurement1Hash)); + assertFalse(upgradeOperator.isDeprecated(measurement1Hash)); + assertEq(upgradeOperator.getAcceptedCount(), 2); + } + + // Test proposal with invalid measurements fails + function testInvalidMeasurementsFails() public { + UpgradeOperator.Measurements memory invalidMeasurement; + + // Empty tag + invalidMeasurement = testMeasurement1; + invalidMeasurement.tag = ""; + vm.prank(signer1); + vm.expectRevert("Tag cannot be empty"); + multisig.proposeAddMeasurements(invalidMeasurement); + + // Empty MRTD + invalidMeasurement = testMeasurement1; + invalidMeasurement.mrtd = ""; + vm.prank(signer1); + vm.expectRevert("MRTD cannot be empty"); + multisig.proposeAddMeasurements(invalidMeasurement); + + // Mismatched arrays + invalidMeasurement = testMeasurement1; + uint8[] memory slots = new uint8[](3); + slots[0] = 1; + slots[1] = 2; + slots[2] = 3; + invalidMeasurement.registrar_slots = slots; + vm.prank(signer1); + vm.expectRevert("Registrar arrays length mismatch"); + multisig.proposeAddMeasurements(invalidMeasurement); + } + + // Test get proposal measurements + function testGetProposalMeasurements() public { + vm.prank(signer1); + bytes32 proposalId = multisig.proposeAddMeasurements(testMeasurement1); + + UpgradeOperator.Measurements memory retrieved = multisig + .getProposalMeasurements(proposalId); + + assertEq(retrieved.tag, testMeasurement1.tag); + assertEq(retrieved.mrtd, testMeasurement1.mrtd); + assertEq(retrieved.mrseam, testMeasurement1.mrseam); + assertEq(retrieved.registrar_slots.length, 1); + assertEq(retrieved.registrar_values.length, 1); + } + + // Test cannot get measurements for non-add proposals + function testCannotGetMeasurementsForDeprecateProposal() public { + vm.prank(signer1); + bytes32 proposalId = multisig.proposeDeprecateMeasurements( + testMeasurement1 + ); + + vm.expectRevert("Not an add measurements proposal"); + multisig.getProposalMeasurements(proposalId); + } +} + +// Mock UpgradeOperator for testing that accepts a different owner +contract UpgradeOperatorMock is UpgradeOperator { + address public immutable testOwner; + + constructor(address _testOwner) { + testOwner = _testOwner; + } + + modifier onlyNetworkMultisig() override { + require(msg.sender == testOwner, "Ownable-- caller is not the owner"); + _; + } +} diff --git a/crates/enclave-contract/tests/multisig_test.rs b/crates/enclave-contract/tests/multisig_test.rs index b0b4d1fb..910be75d 100644 --- a/crates/enclave-contract/tests/multisig_test.rs +++ b/crates/enclave-contract/tests/multisig_test.rs @@ -1,13 +1,16 @@ +use enclave_contract::can_execute_multisig_proposal; +use enclave_contract::check_proposal_status; +use enclave_contract::create_multisig_proposal; +use enclave_contract::execute_multisig_proposal; +use enclave_contract::get_multisig_vote_count; +use enclave_contract::vote_on_multisig_proposal; +use enclave_contract::Measurements; use enclave_contract::UPGRADE_MULTISIG_ADDRESS; use enclave_contract::UPGRADE_OPERATOR_ADDRESS; -use enclave_contract::{ - can_execute_multisig_proposal, check_proposal_status_v1, create_multisig_proposal, - execute_multisig_proposal, get_multisig_vote_count, vote_on_multisig_proposal, - ProposalParamsV1, ANVIL_ALICE_SK, ANVIL_BOB_SK, -}; + +use enclave_contract::{ANVIL_ALICE_SK, ANVIL_BOB_SK}; use std::thread::sleep; use std::time::Duration; - /// Prints a string to standard output and immediately flushes the output buffer. /// Useful to see prints immediately during long-running Cargo tests. pub fn print_flush>(s: S) { @@ -36,37 +39,52 @@ pub async fn test_multisig_upgrade_operator_workflow() -> Result<(), anyhow::Err sleep(Duration::from_secs(2)); // Test data for proposal - let params = ProposalParamsV1::test_params(); - let status = true; + let params = Measurements { + tag: "AzureV1".to_string(), + mrtd: [ + 254, 39, 178, 170, 58, 5, 236, 86, 134, 76, 48, 138, 255, 3, 221, 19, 193, 137, 166, + 17, 45, 33, 228, 23, 236, 26, 254, 98, 106, 140, 185, 217, 20, 130, 209, 55, 158, 192, + 47, 230, 48, 137, 114, 149, 10, 147, 13, 10, + ] + .into(), + mrseam: [ + 151, 144, 216, 154, 16, 33, 14, 198, 150, 138, 119, 60, 238, 44, 160, 91, 90, 169, 115, + 9, 243, 103, 39, 169, 104, 82, 123, 228, 96, 111, 193, 158, 111, 115, 172, 206, 53, 9, + 70, 201, 212, 106, 155, 247, 166, 63, 132, 48, + ] + .into(), + registrar_slots: vec![0, 1, 2, 3], + registrar_values: vec![ + [0; 48].into(), + [0; 48].into(), + [0; 48].into(), + [0; 48].into(), + ], + }; print_flush("Creating multisig proposal...\n"); // Create a proposal to set MRTD - let (proposal_id, nonce) = - create_multisig_proposal(multisig_address, ANVIL_ALICE_SK, reth_rpc, ¶ms, status) + let proposal_id = + create_multisig_proposal(multisig_address, ANVIL_ALICE_SK, reth_rpc, params.clone()) .await .map_err(|e| anyhow::anyhow!("multisig workflow failed to create proposal: {:?}", e))?; - print_flush(format!( - "Proposal created with ID: {:?}, nonce: {}\n", - proposal_id, nonce - )); + print_flush(format!("Proposal created with ID: {:?}\n", proposal_id)); // Wait a bit for the transaction to be processed sleep(Duration::from_secs(2)); // Check initial vote count - let (approval_count, total_votes) = - get_multisig_vote_count(multisig_address, reth_rpc, proposal_id) - .await - .map_err(|e| anyhow::anyhow!("failed to get vote count: {:?}", e))?; + let total_votes = get_multisig_vote_count(multisig_address, reth_rpc, proposal_id) + .await + .map_err(|e| anyhow::anyhow!("failed to get vote count: {:?}", e))?; print_flush(format!( - "Initial vote count - Approvals: {}, Total votes: {}\n", - approval_count, total_votes + "Initial vote count - Total votes: {}\n", + total_votes )); - assert_eq!(approval_count, 0, "Initial approval count should be 0"); - assert_eq!(total_votes, 0, "Initial total votes should be 0"); + assert_eq!(total_votes, 1, "Initial total votes should be 1"); // Check if proposal can be executed (should be false initially) let can_execute = can_execute_multisig_proposal(multisig_address, reth_rpc, proposal_id) @@ -76,56 +94,10 @@ pub async fn test_multisig_upgrade_operator_workflow() -> Result<(), anyhow::Err print_flush(format!("Can execute proposal: {}\n", can_execute)); assert!(!can_execute, "Proposal should not be executable initially"); - print_flush("Alice voting yes on proposal...\n"); - - // Alice votes yes - vote_on_multisig_proposal( - multisig_address, - ANVIL_ALICE_SK, - reth_rpc, - proposal_id, - true, - ) - .await - .map_err(|e| anyhow::anyhow!("failed to vote with Alice: {:?}", e))?; - - // Wait a bit for the transaction to be processed - sleep(Duration::from_secs(2)); - - // Check vote count after Alice's vote - let (approval_count, total_votes) = - get_multisig_vote_count(multisig_address, reth_rpc, proposal_id) - .await - .map_err(|e| anyhow::anyhow!("failed to get vote count: {:?}", e))?; - - print_flush(format!( - "Vote count after Alice - Approvals: {}, Total votes: {}\n", - approval_count, total_votes - )); - assert_eq!( - approval_count, 1, - "Approval count should be 1 after Alice's vote" - ); - assert_eq!(total_votes, 1, "Total votes should be 1 after Alice's vote"); - - // Check if proposal can be executed (should still be false with only 1 vote) - let can_execute = can_execute_multisig_proposal(multisig_address, reth_rpc, proposal_id) - .await - .map_err(|e| anyhow::anyhow!("failed to check if proposal can be executed: {:?}", e))?; - - print_flush(format!( - "Can execute proposal after Alice: {}\n", - can_execute - )); - assert!( - !can_execute, - "Proposal should not be executable with only 1 vote" - ); - print_flush("Bob voting yes on proposal...\n"); // Bob votes yes - vote_on_multisig_proposal(multisig_address, ANVIL_BOB_SK, reth_rpc, proposal_id, true) + vote_on_multisig_proposal(multisig_address, ANVIL_BOB_SK, reth_rpc, proposal_id) .await .map_err(|e| anyhow::anyhow!("failed to vote with Bob: {:?}", e))?; @@ -133,19 +105,15 @@ pub async fn test_multisig_upgrade_operator_workflow() -> Result<(), anyhow::Err sleep(Duration::from_secs(2)); // Check vote count after Bob's vote - let (approval_count, total_votes) = - get_multisig_vote_count(multisig_address, reth_rpc, proposal_id) - .await - .map_err(|e| anyhow::anyhow!("failed to get vote count: {:?}", e))?; + let total_votes = get_multisig_vote_count(multisig_address, reth_rpc, proposal_id) + .await + .map_err(|e| anyhow::anyhow!("failed to get vote count: {:?}", e))?; print_flush(format!( - "Vote count after Bob - Approvals: {}, Total votes: {}\n", - approval_count, total_votes + "Vote count after Bob - Total votes: {}\n", + total_votes )); - assert_eq!( - approval_count, 2, - "Approval count should be 2 after Bob's vote" - ); + assert_eq!(total_votes, 2, "Total votes should be 2 after Bob's vote"); // Check if proposal can be executed (should be true with 2 votes) @@ -158,7 +126,7 @@ pub async fn test_multisig_upgrade_operator_workflow() -> Result<(), anyhow::Err // Check initial proposal status (should be false) let initial_proposal_status = - check_proposal_status_v1(upgrade_operator_address, reth_rpc, ¶ms) + check_proposal_status(upgrade_operator_address, reth_rpc, params.clone()) .await .map_err(|e| anyhow::anyhow!("failed to check initial proposal status: {:?}", e))?; @@ -174,16 +142,9 @@ pub async fn test_multisig_upgrade_operator_workflow() -> Result<(), anyhow::Err print_flush("Executing proposal...\n"); // Execute the proposal - execute_multisig_proposal( - multisig_address, - ANVIL_ALICE_SK, - reth_rpc, - ¶ms, - status, - nonce, - ) - .await - .map_err(|e| anyhow::anyhow!("failed to execute proposal: {:?}", e))?; + execute_multisig_proposal(multisig_address, ANVIL_ALICE_SK, reth_rpc, proposal_id) + .await + .map_err(|e| anyhow::anyhow!("failed to execute proposal: {:?}", e))?; // Wait a bit for the transaction to be processed sleep(Duration::from_secs(2)); @@ -191,10 +152,9 @@ pub async fn test_multisig_upgrade_operator_workflow() -> Result<(), anyhow::Err print_flush("Checking final proposal status...\n"); // Check final proposal status (should be true) - let final_proposal_status = - check_proposal_status_v1(upgrade_operator_address, reth_rpc, ¶ms) - .await - .map_err(|e| anyhow::anyhow!("failed to check final proposal status: {:?}", e))?; + let final_proposal_status = check_proposal_status(upgrade_operator_address, reth_rpc, params) + .await + .map_err(|e| anyhow::anyhow!("failed to check final proposal status: {:?}", e))?; print_flush(format!( "Final proposal status: {}\n", diff --git a/crates/enclave-server/Cargo.toml b/crates/enclave-server/Cargo.toml index d046cfb8..f15cf44d 100644 --- a/crates/enclave-server/Cargo.toml +++ b/crates/enclave-server/Cargo.toml @@ -17,66 +17,39 @@ path = "src/lib.rs" name = "seismic-enclave-server" path = "src/main.rs" -[[bin]] -name = "install_policies" -path = "src/bin/install_policies.rs" - - [dependencies] seismic-enclave.workspace = true enclave-contract.workspace = true -alloy.workspace = true -attestation-agent.workspace = true -attestation-service.workspace = true -az-tdx-vtpm = {workspace = true, optional = true} -kbs-types.workspace = true - -aes-gcm.workspace = true -auto_impl.workspace = true -zeroize.workspace = true -anyhow.workspace = true -base64.workspace = true +alloy.workspace = true +az-tdx-vtpm.workspace = true +openssl.workspace = true +coco-provider.workspace = true +chrono = "0.4.40" +hex.workspace = true hkdf.workspace = true jsonrpsee.workspace = true -libc.workspace = true -log.workspace = true -secp256k1.workspace = true -serde_json.workspace = true -sha2.workspace = true -strum.workspace = true -strum_macros.workspace = true tokio.workspace = true +serde.workspace = true +serde_json.workspace = true +anyhow.workspace = true +secp256k1.workspace = true schnorrkel.workspace = true +bincode.workspace = true +base64-url.workspace = true +dcap-rs.workspace = true +libc.workspace = true +x509-parser.workspace = true +reqwest.workspace = true +urlencoding.workspace = true +clap.workspace = true +aes-gcm.workspace = true tracing.workspace = true tracing-subscriber.workspace = true rand.workspace = true - -hex = {workspace = true} -scroll = { workspace = true, optional = true } -serde = {workspace = true, optional = true } - -clap = { version = "4.5", features = ["derive"] } +zeroize.workspace = true +sha2.workspace = true [dev-dependencies] -enclave-contract.workspace = true serial_test = "3.2.0" -tempfile = "3.17.1" -reqwest.workspace = true -alloy.workspace = true - - -[features] -default = ["az-tdx-vtpm-attester", "supervisorctl"] # remove supervisorctl before merging -az-tdx-vtpm-attester = [ - "attestation-agent/az-tdx-vtpm-attester", - "attestation-service/az-tdx-vtpm-verifier", - "attestation-service/az-snp-vtpm-verifier", - "az-tdx-vtpm", - "scroll", - "serde", -] -supervisorctl = [] -[package.metadata.cargo-udeps] -ignore = ["serial_test"] diff --git a/crates/enclave-server/src/attestation/mod.rs b/crates/enclave-server/src/attestation/mod.rs index de30b30a..fa3ad048 100644 --- a/crates/enclave-server/src/attestation/mod.rs +++ b/crates/enclave-server/src/attestation/mod.rs @@ -1,23 +1,79 @@ -mod seismic_agent; -pub use seismic_agent::SeismicAttestationAgent; +pub mod pccs; +pub mod upgrade_contract; +pub mod utils; -use attestation_service::token::simple; +use anyhow::{Result, anyhow}; +use az_tdx_vtpm::{hcl::HclReport, imds, tdx, vtpm}; +use coco_provider::{coco::CocoDeviceType, get_coco_provider}; +use dcap_rs::{types::quotes::version_4::QuoteV4, utils::quotes::version_4::verify_quote_dcapv4}; +use seismic_enclave::AttestationGetEvidenceResponse; -/// A reasonable default mock attestation agent for testing -pub(crate) async fn seismic_aa_mock() -> SeismicAttestationAgent { - let att_serv_config = simple_att_serv_config(); +use crate::attestation::{ + pccs::{IntelPccs, PccsProvider}, + upgrade_contract::verify_measurements_against_contract, +}; - let saa = SeismicAttestationAgent::new(None, att_serv_config).await; - saa +#[derive(Clone, Copy)] +#[repr(u8)] +pub enum CA { + PROCESSOR, + PLATFORM, + __Invalid, } -pub fn simple_att_serv_config() -> attestation_service::config::Config { - let token_broker_config = simple_token_broker_config(); - let mut att_serv_config: attestation_service::config::Config = Default::default(); - att_serv_config.attestation_token_broker = token_broker_config; - att_serv_config +pub struct AttestationAgent { + provider: CocoDeviceType, + pccs: IntelPccs, } -pub fn simple_token_broker_config() -> attestation_service::token::AttestationTokenConfig { - attestation_service::token::AttestationTokenConfig::Simple(simple::Configuration::default()) +impl AttestationAgent { + pub fn new() -> Result { + let provider = get_coco_provider()?; + Ok(Self { + provider: provider.device_type, + pccs: IntelPccs::new(), + }) + } + + pub fn get_attestation_evidence(&self) -> Result { + match self.provider { + // Azure and other TDX cloud providers use TPM + CocoDeviceType::Tpm => self.get_attestation_evidence_tpm(), + _ => self.get_attestation_evidence_tpm(), // todo: For some reason on Azure Ubuntu CVM it is going to Mock device type even when the tpm is available + } + } + + fn get_attestation_evidence_tpm(&self) -> Result { + // todo this will be included in the quote, placeholding this for now we probably want our public key or a nonce here instead + let report_data = [1u8; 32]; + + // Get Unsigned td report + let hcl_report_bytes = vtpm::get_report_with_report_data(&report_data)?; + let hcl_report = HclReport::new(hcl_report_bytes.clone())?; + + // get the unsigned td report + let unsigned_td_report: tdx::TdReport = hcl_report.try_into()?; + + // send td report to the imds to be signed + let signed_td_report_bytes = imds::get_td_quote(&unsigned_td_report).unwrap(); + + // let quote = QuoteV4::from_bytes(&signed_td_report_bytes); + Ok(AttestationGetEvidenceResponse { + hcl_report: hcl_report_bytes, + quote: signed_td_report_bytes, + }) + } + + pub async fn verify_attestation_report(&self, quote: QuoteV4) -> Result<()> { + let collaterals = self.pccs.get_collateral("e).await?; + let current_time = chrono::Utc::now().timestamp() as u64; + + match std::panic::catch_unwind(|| verify_quote_dcapv4("e, &collaterals, current_time)) { + Ok(_) => { + verify_measurements_against_contract("e).await?; + Ok(()) + } // todo actually respond with something + Err(e) => Err(anyhow!("DCAP Error: {:?}", e)), + } + } } diff --git a/crates/enclave-server/src/attestation/pccs.rs b/crates/enclave-server/src/attestation/pccs.rs new file mode 100644 index 00000000..8c3d2977 --- /dev/null +++ b/crates/enclave-server/src/attestation/pccs.rs @@ -0,0 +1,313 @@ +use anyhow::{Context, Result, anyhow}; +use dcap_rs::types::{collaterals::IntelCollateral, quotes::version_4::QuoteV4}; +use x509_parser::pem::parse_x509_pem; + +use crate::attestation::{CA, utils::get_pck_fmspc_and_issuer}; + +pub trait PccsProvider { + fn new() -> Self; + + async fn get_collateral(&self, quote: &QuoteV4) -> Result; + + async fn get_root_ca(&self) -> Result<(Vec, Vec)>; + async fn get_tcb_info( + &self, + tcb_type: u8, + fmspc: &str, + version: u32, + ) -> Result<(Vec, Vec)>; + + async fn get_enclave_identity(&self, version: u32) -> Result>; + + async fn get_certificate_by_id(&self, ca_id: CA) -> Result<(Vec, Vec)>; +} + +pub struct IntelPccs { + base_url: String, + client: reqwest::Client, +} + +impl PccsProvider for IntelPccs { + fn new() -> Self { + Self { + base_url: "https://pccs.scrtlabs.com".to_string(), + client: reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(30)) + .build() + .expect("Failed to create HTTP client"), + } + } + + async fn get_collateral(&self, quote: &QuoteV4) -> Result { + // 1. Get Root CA and root ca crl from PCCS + let (root_ca, root_ca_crl) = self.get_root_ca().await?; + + if root_ca.is_empty() || root_ca_crl.is_empty() { + return Err(anyhow!("Root CA or CRL is empty")); + } + + // 2. get fmspc and pck_type from the quote cert + let (fmspc, pck_type) = get_pck_fmspc_and_issuer(quote); + + // 3. get TCB info from PCCS + + // tcb_type: 0: SGX, 1: TDX + // version: TDX uses TcbInfoV3 + let (tcb_info, signing_ca) = self.get_tcb_info(1, &fmspc, 4).await?; + + if signing_ca.is_empty() { + return Err(anyhow!("Signing CA is empty".to_string())); + } + // 4. Get Enclave Identity from PCCS + let quote_version = quote.header.version; + let qe_identity = self.get_enclave_identity(quote_version as u32).await?; + + let (_, pck_crl) = self.get_certificate_by_id(pck_type).await?; + if pck_crl.is_empty() { + return Err(anyhow!("PCK CRL is empty".to_string())); + } + + let mut collaterals = IntelCollateral::new(); + + collaterals.set_tcbinfo_bytes(&tcb_info); + collaterals.set_qeidentity_bytes(&qe_identity); + collaterals.set_intel_root_ca_der(&root_ca); + collaterals.set_sgx_tcb_signing_der(&signing_ca); + collaterals.set_sgx_intel_root_ca_crl_der(&root_ca_crl); + + match pck_type { + CA::PLATFORM => { + collaterals.set_sgx_platform_crl_der(&pck_crl); + } + CA::PROCESSOR => { + collaterals.set_sgx_processor_crl_der(&pck_crl); + } + _ => { + return Err(anyhow!("Unknown PCK Type".to_string())); + } + } + Ok(collaterals) + } + + async fn get_root_ca(&self) -> Result<(Vec, Vec)> { + let cert_url = "https://certificates.trustedservices.intel.com/Intel_SGX_Provisioning_Certification_RootCA.cer"; + + let cert_response = self + .client + .get(cert_url) + .send() + .await + .context("Failed to fetch Root CA certificate")?; + + if !cert_response.status().is_success() { + return Err(anyhow!( + "Root CA certificate request failed with status {}: {}", + cert_response.status(), + cert_response.text().await.unwrap_or_default() + )); + } + + let cert = cert_response.bytes().await?.to_vec(); + + // Fetch Root CA CRL + let crl_url = format!("{}/sgx/certification/v4/rootcacrl", self.base_url); + + let crl_request = self.client.get(&crl_url); + + let crl_response = crl_request + .send() + .await + .context("Failed to fetch Root CA CRL")?; + + if !crl_response.status().is_success() { + return Err(anyhow!( + "Root CA CRL request failed with status {}: {}", + crl_response.status(), + crl_response.text().await.unwrap_or_default() + )); + } + let response_string = crl_response.text().await?; + let crl = hex::decode(&response_string)?; + + Ok((cert, crl)) + } + + /// Get TCB Info for SGX or TDX + /// + /// # Arguments + /// * `tcb_type` - 0 for SGX, 1 for TDX + /// * `fmspc` - Hex-encoded FMSPC value (12 hex chars representing 6 bytes) + /// * `version` - API version (should be 4 for v4 API) + async fn get_tcb_info( + &self, + tcb_type: u8, + fmspc: &str, + version: u32, + ) -> Result<(Vec, Vec)> { + let tech = match tcb_type { + 0 => "sgx", + 1 => "tdx", + _ => return Err(anyhow!("Invalid tcb_type: must be 0 (SGX) or 1 (TDX)")), + }; + + let url = format!( + "{}/{}/certification/v{}/tcb?fmspc={}", + self.base_url, tech, version, fmspc + ); + let request = self.client.get(&url); + + let response = request + .send() + .await + .context("Failed to send TCB Info request")?; + + if !response.status().is_success() { + return Err(anyhow!( + "TCB Info request failed with status {}: {}", + response.status(), + response.text().await.unwrap_or_default() + )); + } + + // Extract the certificate chain from headers + let cert_chain_header = response + .headers() + .get("tcb-info-issuer-chain") + .ok_or_else(|| anyhow!("Missing tcb-info-issuer-chain header"))? + .to_str() + .context("Invalid tcb-info-issuer-chain header encoding")?; + + // URL decode the certificate chain + let decoded_chain = urlencoding::decode(cert_chain_header) + .context("Failed to URL decode certificate chain")?; + + // Extract the first certificate (TCB Signing Certificate) + let signing_cert_pem = extract_first_certificate(decoded_chain.as_bytes())?; + + // Convert PEM to DER + let (_, pem) = parse_x509_pem(&signing_cert_pem)?; + + // Get the TCB Info JSON body + let tcb_info_json = response.bytes().await?.to_vec(); + + Ok((tcb_info_json, pem.contents)) + } + + /// Get Enclave Identity (QE Identity for SGX or TDX) + /// + /// # Arguments + /// * `version` - API version (should be 4 for v4 API) + async fn get_enclave_identity(&self, version: u32) -> Result> { + // For TDX Quote verification, we typically need the TDX QE Identity + let url = format!( + "{}/tdx/certification/v{}/qe/identity", + self.base_url, version + ); + + let request = self.client.get(&url); + + let response = request + .send() + .await + .context("Failed to send QE Identity request")?; + + if !response.status().is_success() { + return Err(anyhow!( + "QE Identity request failed with status {}: {}", + response.status(), + response.text().await.unwrap_or_default() + )); + } + + let qe_identity_json = response.bytes().await?.to_vec(); + + Ok(qe_identity_json) + } + + /// Get certificate by CA ID (Processor or Platform CA) + /// Returns tuple of (certificate_chain, crl) + /// + /// # Arguments + /// * `ca_id` - CA identifier (Processor or Platform) + /// Get certificate and CRL by CA ID (Root, Processor, or Platform CA) + /// Returns tuple of (certificate, crl) + /// + /// # Arguments + /// * `ca_id` - CA identifier (Root, Processor or Platform) + async fn get_certificate_by_id(&self, ca_id: CA) -> Result<(Vec, Vec)> { + let ca_str = match ca_id { + CA::PROCESSOR => "processor", + CA::PLATFORM => "platform", + _ => return Err(anyhow!("unssuported CA")), + }; + + // Get the CRL for this CA + let crl_url = format!( + "{}/sgx/certification/v4/pckcrl?ca={}", + self.base_url, ca_str + ); + + let crl_request = self.client.get(&crl_url); + + let crl_response = crl_request + .send() + .await + .context("Failed to send CRL request")?; + + if !crl_response.status().is_success() { + return Err(anyhow!( + "CRL request failed with status {}: {}", + crl_response.status(), + crl_response.text().await.unwrap_or_default() + )); + } + + // Extract the certificate chain from the response header + let cert_chain = crl_response + .headers() + .get("SGX-PCK-CRL-Issuer-Chain") + .ok_or_else(|| anyhow!("Missing SGX-PCK-CRL-Issuer-Chain header"))? + .to_str() + .context("Invalid certificate chain header")?; + + // URL decode the certificate chain + let cert_chain = urlencoding::decode(cert_chain) + .context("Failed to URL decode certificate chain")? + .into_owned() + .into_bytes(); + + let response_string = crl_response.text().await?; + let crl = hex::decode(response_string)?; + + // The cert chain contains both Root CA and the intermediate CA (Processor/Platform) + // We need to parse and extract just the requested CA certificate + // The chain format is: + let cert = extract_first_certificate(&cert_chain) + .context("Failed to extract CA certificate from chain")?; + + Ok((cert, crl)) + } +} + +/// Extract the first certificate from a PEM certificate chain +fn extract_first_certificate(pem_chain: &[u8]) -> Result> { + let chain_str = + std::str::from_utf8(pem_chain).context("Certificate chain is not valid UTF-8")?; + + // Find the first certificate in the chain + let begin_marker = "-----BEGIN CERTIFICATE-----"; + let end_marker = "-----END CERTIFICATE-----"; + + let start = chain_str + .find(begin_marker) + .ok_or_else(|| anyhow!("No certificate found in chain"))?; + + let end = chain_str[start..] + .find(end_marker) + .ok_or_else(|| anyhow!("Malformed certificate in chain"))?; + + // Extract the first certificate including the markers + let cert = &chain_str[start..start + end + end_marker.len()]; + + Ok(cert.as_bytes().to_vec()) +} diff --git a/crates/enclave-server/src/attestation/seismic_agent.rs b/crates/enclave-server/src/attestation/seismic_agent.rs deleted file mode 100644 index 598c5d67..00000000 --- a/crates/enclave-server/src/attestation/seismic_agent.rs +++ /dev/null @@ -1,139 +0,0 @@ -use anyhow::Result; -use jsonrpsee::core::async_trait; -use kbs_types::Tee; -use std::collections::HashMap; -use tokio::sync::Mutex; - -use attestation_agent::AttestationAPIs; -use attestation_agent::AttestationAgent; -use attestation_agent::InitDataResult; -use attestation_service::AttestationService; -use attestation_service::VerificationRequest; - -/// a centralized struct for making and verifying attestations -/// includes a mutex because the inner attestation agent is not thread safe -pub struct SeismicAttestationAgent { - quote_mutex: Mutex<()>, - attestation_agent: AttestationAgent, - verifier: AttestationService, -} - -impl SeismicAttestationAgent { - /// Create a new SeismicAttestationAgent wrapper - pub async fn new( - aa_config_path: Option<&str>, - as_config: attestation_service::config::Config, - ) -> Self { - Self { - quote_mutex: Mutex::new(()), - attestation_agent: AttestationAgent::new(aa_config_path) - .expect("Failed to create an AttestationAgent"), - verifier: AttestationService::new(as_config).await.unwrap(), - } - } - - #[allow(dead_code)] - pub(crate) async fn init(&mut self) -> Result<()> { - self.attestation_agent.init().await - } -} - -// delegate attestation_service fn to the inner verifier -impl SeismicAttestationAgent { - /// Set Attestation Verification Policy. - #[allow(dead_code)] - pub(crate) async fn set_policy(&mut self, policy_id: String, policy: String) -> Result<()> { - self.verifier.set_policy(policy_id, policy).await - } - - /// Get Attestation Verification Policy List. - /// The result is a `policy-id` -> `policy hash` map. - #[allow(dead_code)] - pub(crate) async fn list_policies(&self) -> Result> { - self.verifier.list_policies().await - } - - /// Get a single Policy content. - #[allow(dead_code)] - pub(crate) async fn get_policy(&self, policy_id: String) -> Result { - self.verifier.get_policy(policy_id).await - } - - /// Evaluate evidence against policies, verifying the attestation - #[allow(dead_code)] - pub(crate) async fn evaluate( - &self, - verification_requests: Vec, - policy_ids: Vec, - ) -> Result { - self.verifier - .evaluate(verification_requests, policy_ids) - .await - } - - #[allow(dead_code)] - pub(crate) async fn register_reference_value(&mut self, message: &str) -> Result<()> { - self.verifier.register_reference_value(message).await - } - - #[allow(dead_code)] - pub(crate) async fn query_reference_values( - &self, - ) -> Result> { - self.verifier.query_reference_values().await - } - - #[allow(dead_code)] - pub(crate) async fn generate_supplemental_challenge( - &self, - tee: Tee, - tee_parameters: String, - ) -> Result { - self.verifier - .generate_supplemental_challenge(tee, tee_parameters) - .await - } -} - -/// impl AttestationAPIs for SeismicAttestationAgent for easy use of inner methods -#[async_trait] -impl AttestationAPIs for SeismicAttestationAgent { - /// Get attestation Token (delegates to attestation_agent) - async fn get_token(&self, token_type: &str) -> Result> { - self.attestation_agent.get_token(token_type).await - } - - /// Get TEE hardware signed evidence with concurrency protection - async fn get_evidence(&self, runtime_data: &[u8]) -> Result> { - let _lock = self.quote_mutex.lock().await; - self.attestation_agent.get_evidence(runtime_data).await - } - - /// Get TEE hardware signed evidence (from all attesters) with concurrency protection - async fn get_additional_evidence(&self, runtime_data: &[u8]) -> Result> { - let _lock = self.quote_mutex.lock().await; - self.attestation_agent - .get_additional_evidence(runtime_data) - .await - } - - async fn extend_runtime_measurement( - &self, - domain: &str, - operation: &str, - content: &str, - register_index: Option, - ) -> Result<()> { - self.attestation_agent - .extend_runtime_measurement(domain, operation, content, register_index) - .await - } - - async fn bind_init_data(&self, init_data: &[u8]) -> Result { - self.attestation_agent.bind_init_data(init_data).await - } - - fn get_tee_type(&self) -> Tee { - self.attestation_agent.get_tee_type() - } -} diff --git a/crates/enclave-server/src/attestation/upgrade_contract.rs b/crates/enclave-server/src/attestation/upgrade_contract.rs new file mode 100644 index 00000000..0f89aba4 --- /dev/null +++ b/crates/enclave-server/src/attestation/upgrade_contract.rs @@ -0,0 +1,37 @@ +use anyhow::{Result, anyhow}; +use dcap_rs::types::quotes::{body::QuoteBody, version_4::QuoteV4}; +use enclave_contract::UpgradeOperator; + +pub async fn verify_measurements_against_contract(quote: &QuoteV4) -> Result<()> { + let QuoteBody::TD10QuoteBody(quote_body) = quote.quote_body else { + return Err(anyhow!("Not a tdx quote")); + }; + + let measurements = UpgradeOperator::Measurements { + tag: Default::default(), + mrtd: quote_body.mrtd.into(), + mrseam: quote_body.mrseam.into(), + registrar_slots: vec![0, 1, 2, 3], + registrar_values: vec![ + quote_body.rtmr0.into(), + quote_body.rtmr1.into(), + quote_body.rtmr2.into(), + quote_body.rtmr3.into(), + ], + }; + + // Get contract address and RPC URL from environment variables + let upgrade_operator_address = enclave_contract::UPGRADE_OPERATOR_ADDRESS + .parse::() + .unwrap(); + + let rpc_url = "http://localhost:8545"; // todo pass this down from cli args + + if enclave_contract::check_proposal_status(upgrade_operator_address, rpc_url, measurements) + .await? + { + Ok(()) + } else { + Err(anyhow!("Now approved")) + } +} diff --git a/crates/enclave-server/src/attestation/utils.rs b/crates/enclave-server/src/attestation/utils.rs new file mode 100644 index 00000000..41388f8c --- /dev/null +++ b/crates/enclave-server/src/attestation/utils.rs @@ -0,0 +1,63 @@ +use dcap_rs::types::quotes::QeReportCertData; +use dcap_rs::types::quotes::version_4::QuoteV4; +use dcap_rs::utils::cert::{get_x509_issuer_cn, parse_certchain, parse_pem}; +use x509_parser::der_parser::Oid; +use x509_parser::der_parser::asn1_rs::{OctetString, Sequence, oid}; +use x509_parser::prelude::*; + +use crate::attestation::CA; + +pub fn get_pck_fmspc_and_issuer(quote: &QuoteV4) -> (String, CA) { + let raw_cert_data = QeReportCertData::from_bytes("e.signature.qe_cert_data.cert_data); + + let pem = parse_pem(&raw_cert_data.qe_cert_data.cert_data).expect("Failed to parse cert data"); + // Cert Chain: + // [0]: pck -> + // [1]: pck ca -> + // [2]: root ca + let cert_chain = parse_certchain(&pem); + let pck = &cert_chain[0]; + + let pck_issuer = get_x509_issuer_cn(pck); + + let pck_ca = match pck_issuer.as_str() { + "Intel SGX PCK Platform CA" => CA::PLATFORM, + "Intel SGX PCK Processor CA" => CA::PROCESSOR, + _ => panic!("Unknown PCK Issuer"), + }; + + let fmspc_slice = extract_fmspc_from_extension(pck); + let fmspc = hex::encode(fmspc_slice); + (fmspc, pck_ca) +} + +pub fn extract_fmspc_from_extension<'a>(cert: &'a X509Certificate<'a>) -> [u8; 6] { + let sgx_extensions_bytes = cert + .get_extension_unique(&oid!(1.2.840.113741.1.13.1)) + .unwrap() + .unwrap() + .value; + + let (_, sgx_extensions) = Sequence::from_der(sgx_extensions_bytes).unwrap(); + + let mut fmspc = [0; 6]; + + let mut i = sgx_extensions.content.as_ref(); + + while !i.is_empty() { + let (j, current_sequence) = Sequence::from_der(i).unwrap(); + i = j; + let (j, current_oid) = Oid::from_der(current_sequence.content.as_ref()).unwrap(); + match current_oid.to_id_string().as_str() { + "1.2.840.113741.1.13.1.4" => { + let (k, fmspc_bytes) = OctetString::from_der(j).unwrap(); + assert_eq!(k.len(), 0); + fmspc.copy_from_slice(fmspc_bytes.as_ref()); + break; + } + _ => continue, + } + } + + fmspc +} diff --git a/crates/enclave-server/src/attestation/verifier.rs b/crates/enclave-server/src/attestation/verifier.rs deleted file mode 100644 index f5c6d349..00000000 --- a/crates/enclave-server/src/attestation/verifier.rs +++ /dev/null @@ -1,465 +0,0 @@ -use anyhow::{anyhow, Context, Result}; -use attestation_service::token::simple::{self}; -use attestation_service::token::{ear_broker, AttestationTokenBroker}; -use attestation_service::{Data, HashAlgorithm}; -use attestation_service::TeeEvidence; -use kbs_types::Tee; -use log::{debug, info}; -use serde_json::Value; -use sha2::{Digest, Sha256, Sha384, Sha512}; -use std::collections::HashMap; -use verifier::{InitDataHash, ReportData}; -use attestation_service::TeeClaims; - -/// A lightweight, concurrency-friendly DCAP attestation verifier -pub struct DcapAttVerifier { - token_broker: T, -} - -// Convenience methods for creating DcapAttVerifier with common token broker types -impl DcapAttVerifier { - /// Create a new DcapAttVerifier with SimpleAttestationTokenBroker - pub fn new_simple(config: simple::Configuration) -> Result { - let token_broker = simple::SimpleAttestationTokenBroker::new(config)?; - Ok(Self { token_broker }) - } - - /// Create a new DcapAttVerifier with default SimpleAttestationTokenBroker - pub fn default_simple() -> Result { - Self::new_simple(simple::Configuration::default()) - } -} - -impl DcapAttVerifier { - /// Create a new DcapAttVerifier with EarAttestationTokenBroker - pub fn new_ear(config: ear_broker::Configuration) -> Result { - let token_broker = ear_broker::EarAttestationTokenBroker::new(config)?; - Ok(Self { token_broker }) - } - - /// Create a new DcapAttVerifier with default EarAttestationTokenBroker - pub fn default_ear() -> Result { - Self::new_ear(ear_broker::Configuration::default()) - } -} - -impl DcapAttVerifier { - /// Create a new DcapAttVerifier instance - pub fn new(token_broker: T) -> Self { - Self { token_broker } - } - - /// Set Attestation Verification Policy. - pub async fn set_policy(&mut self, policy_id: String, policy: String) -> Result<()> { - self.token_broker.set_policy(policy_id, policy).await?; - Ok(()) - } - - /// Get Attestation Verification Policy List. - /// The result is a `policy-id` -> `policy hash` map. - pub async fn list_policies(&self) -> Result> { - self.token_broker - .list_policies() - .await - .context("Cannot List Policy") - } - - /// Get a single Policy content. - pub async fn get_policy(&self, policy_id: String) -> Result { - self.token_broker - .get_policy(policy_id) - .await - .context("Cannot Get Policy") - } - - /// Evaluate evidence against policies - pub async fn evaluate( - &self, - evidence: TeeEvidence, - tee: Tee, - runtime_data: Option, - runtime_data_hash_algorithm: HashAlgorithm, - init_data: Option, - init_data_hash_algorithm: HashAlgorithm, - policy_ids: Vec, - ) -> Result { - // Get the appropriate verifier for the TEE type - let verifier = verifier::to_verifier(&tee)?; - - // Parse and hash runtime data - let (report_data, runtime_data_claims) = self - .parse_data(runtime_data, runtime_data_hash_algorithm) - .context("parse runtime data")?; - - let report_data = match &report_data { - Some(data) => ReportData::Value(data), - None => ReportData::NotProvided, - }; - - // Parse and hash init data - let (init_data, init_data_claims) = self - .parse_data(init_data, init_data_hash_algorithm) - .context("parse init data")?; - - let init_data_hash = match &init_data { - Some(data) => InitDataHash::Value(data), - None => InitDataHash::NotProvided, - }; - - // Evaluate the evidence using the verifier - let (claim, tee_class) = verifier - .evaluate(evidence, &report_data, &init_data_hash) - .await - .map_err(|e| anyhow!("Verifier evaluate failed: {e:?}"))?; - info!("{:?} Verifier/endorsement check passed.", tee); - - let reference_data_map = self.get_reference_data().await?; - debug!("reference_data_map: {:#?}", reference_data_map); - - let tee_claims = TeeClaims { - tee, - tee_class, - claims: vec![claim], - init_data_claims, - runtime_data_claims, - }; - - let attestation_results_token = self - .token_broker - .issue( - claims_from_tee_evidence, - policy_ids, - init_data_claims, - runtime_data_claims, - reference_data_map, - tee, - ) - .await?; - - Ok(attestation_results_token) - } - - /// Parse and hash data using the specified algorithm - fn parse_data( - &self, - data: Option, - hash_algorithm: HashAlgorithm, - ) -> Result<(Option>, Value)> { - match data { - Some(value) => match value { - Data::Raw(raw) => Ok((Some(raw), Value::Null)), - Data::Structured(structured) => { - // Serialize the structured data (keys in alphabetical order) - let hash_materials = - serde_json::to_vec(&structured).context("parse JSON structured data")?; - let digest = self.accumulate_hash(hash_algorithm, hash_materials); - Ok((Some(digest), structured)) - } - }, - None => Ok((None, Value::Null)), - } - } - /// Get reference data for verification - async fn get_reference_data(&self) -> Result>> { - let reference_data: HashMap> = HashMap::new(); - Ok(reference_data) - } - - fn accumulate_hash(&self, hash_algorithm: HashAlgorithm, materials: Vec) -> Vec { - match hash_algorithm { - HashAlgorithm::Sha256 => { - let mut hasher = Sha256::new(); - hasher.update(materials); - hasher.finalize().to_vec() - } - HashAlgorithm::Sha384 => { - let mut hasher = Sha384::new(); - hasher.update(materials); - hasher.finalize().to_vec() - } - HashAlgorithm::Sha512 => { - let mut hasher = Sha512::new(); - hasher.update(materials); - hasher.finalize().to_vec() - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::utils::policy_fixture::{PolicyFixture, ALLOW_POLICY, DENY_POLICY}; - use crate::utils::test_utils::pub_key_eval_request; - use crate::utils::test_utils::read_vector_txt; - use attestation_service::token::simple::Configuration; - use attestation_service::token::simple::SimpleAttestationTokenBroker; - use seismic_enclave::get_unsecure_sample_secp256k1_pk; - use seismic_enclave::request_types::coco_as::ASCoreTokenClaims; - use std::env; - use tokio::test; - - #[test] - async fn test_parse_as_token() { - match env::current_dir() { - Ok(path) => println!("Current directory: {}", path.display()), - Err(e) => eprintln!("Error getting current directory: {}", e), - } - let ex_token_path = "../../examples/as_token.txt"; // assumes tests are run from enclaver-server dir - let ex_token = std::fs::read_to_string(ex_token_path).unwrap(); - - let claims = ASCoreTokenClaims::from_jwt(&ex_token).unwrap(); - - assert_eq!(claims.tee, "aztdxvtpm"); - let evaluation_reports = serde_json::to_string(&claims.evaluation_reports).unwrap(); - assert_eq!(evaluation_reports, "[{\"policy-hash\":\"b3b555df21b9e952384aec5e81e03e53ca82741da3c5d055ccdb6ba5a85dcc2e6fd1196819dc3c26d09471735275b30a\",\"policy-id\":\"yocto\"}]"); - let tcb_status_map: serde_json::Map = - serde_json::from_str(&claims.tcb_status).unwrap(); - assert_eq!( - tcb_status_map.get("aztdxvtpm.quote.body.mr_td"), - Some(&Value::String("bb379f8e734a755832509f61403f99db2258a70a01e1172a499d6d364101b0675455b4e372a35c1f006541f2de0d7154".to_string())) - ); - assert_eq!(claims.customized_claims.init_data, Value::Null); - assert_eq!(claims.customized_claims.runtime_data, Value::Null); - } - - #[test] - async fn verifier_test_policy_management() { - let mut verifier = - DcapAttVerifier::::new_simple(Configuration::default()) - .unwrap(); - - let fixture = PolicyFixture::testing_mock(); - fixture.configure_verifier(&mut verifier).await.unwrap(); - - let policy_id = "allow".to_string(); - let expected_content = fixture.get_policy_content(&policy_id).unwrap(); - let retrieved_policy = verifier.get_policy(policy_id.clone()).await.unwrap(); - assert_eq!( - &retrieved_policy, expected_content, - "allow policy not retrieved correctly" - ); - - // Add and update a policy - let policy_id = "test_management".to_string(); - verifier - .set_policy(policy_id.clone(), fixture.encode_policy(ALLOW_POLICY)) - .await - .unwrap(); - let retrieved_policy = verifier.get_policy(policy_id.clone()).await.unwrap(); - let encoded_policy = fixture.encode_policy(ALLOW_POLICY); - assert_eq!( - retrieved_policy, encoded_policy, - "test_management policy not added correctly" - ); - verifier - .set_policy(policy_id.clone(), fixture.encode_policy(DENY_POLICY)) - .await - .unwrap(); - let retrieved_policy = verifier.get_policy(policy_id.clone()).await.unwrap(); - let encoded_policy = fixture.encode_policy(DENY_POLICY); - assert_eq!( - retrieved_policy, encoded_policy, - "test_management policy not updated correctly" - ); - - // Try getting non-existent policy - let result = verifier.get_policy("non-existent".to_string()).await; - assert!(result.is_err()); - } - - #[test] - async fn verifier_test_eval_evidence_sample() { - // Create verifier with the policy fixture - let mut verifier = - DcapAttVerifier::::new_simple(Configuration::default()) - .unwrap(); - let fixture = PolicyFixture::testing_mock(); - fixture.configure_verifier(&mut verifier).await.unwrap(); - - // Sample evidence data - let evidence = vec![ - 123, 34, 115, 118, 110, 34, 58, 34, 49, 34, 44, 34, 114, 101, 112, 111, 114, 116, 95, - 100, 97, 116, 97, 34, 58, 34, 98, 109, 57, 117, 89, 50, 85, 61, 34, 125, - ]; - - // Runtime data - let runtime_data = Some(Data::Raw("nonce".as_bytes().to_vec())); - - // Evaluate the evidence - let raw_claims = verifier - .evaluate( - evidence, - Tee::Sample, - runtime_data, - HashAlgorithm::Sha256, - None, - HashAlgorithm::Sha256, - vec!["allow".to_string()], - ) - .await - .unwrap(); - - let claims = ASCoreTokenClaims::from_jwt(&raw_claims).unwrap(); - - // Verify results - assert_eq!(claims.tee, "sample"); - - // Check if policy evaluation was successful - let eval_reports = &claims.evaluation_reports; - assert!(!eval_reports.is_empty()); - - // For Sample TEE, we should have a report_data field in tcb_status - let tcb_status_map: serde_json::Map = - serde_json::from_str(&claims.tcb_status).unwrap(); - assert_eq!(tcb_status_map["report_data"], "bm9uY2U="); - } - - #[test] - async fn verifier_test_eval_policy_deny() { - // Create verifier with the policy fixture - let mut verifier = - DcapAttVerifier::::new_simple(Configuration::default()) - .unwrap(); - let fixture = PolicyFixture::testing_mock(); - fixture.configure_verifier(&mut verifier).await.unwrap(); - - // Sample evidence data - let evidence = vec![ - 123, 34, 115, 118, 110, 34, 58, 34, 49, 34, 44, 34, 114, 101, 112, 111, 114, 116, 95, - 100, 97, 116, 97, 34, 58, 34, 98, 109, 57, 117, 89, 50, 85, 61, 34, 125, - ]; - - // Runtime data - let runtime_data = Some(Data::Raw("nonce".as_bytes().to_vec())); - - // Evaluate with deny policy - should fail - let raw_claims_deny = verifier - .evaluate( - evidence, - Tee::Sample, - runtime_data, - HashAlgorithm::Sha256, - None, - HashAlgorithm::Sha256, - vec!["deny".to_string()], - ) - .await; - - println!("raw_claims_deny: {:?}", raw_claims_deny); - - assert!( - raw_claims_deny.is_err(), - "Deny policy should reject, but allowed" - ); - } - - #[test] - async fn verifier_test_eval_evidence_az_tdx() { - // Create verifier with the policy fixture - let mut verifier = - DcapAttVerifier::::new_simple(Configuration::default()) - .unwrap(); - let fixture = PolicyFixture::testing_mock(); - fixture.configure_verifier(&mut verifier).await.unwrap(); - - // Get the sample evidence - let eval_req: seismic_enclave::coco_as::AttestationEvalEvidenceRequest = - pub_key_eval_request(); - let runtime_data = get_unsecure_sample_secp256k1_pk().serialize().to_vec(); - - // Evaluate the evidence - let raw_claims = verifier - .evaluate( - eval_req.evidence, - Tee::AzTdxVtpm, - Some(Data::Raw(runtime_data)), - HashAlgorithm::Sha256, - None, - HashAlgorithm::Sha256, - vec!["allow".to_string()], - ) - .await - .unwrap(); - let claims = ASCoreTokenClaims::from_jwt(&raw_claims).unwrap(); - - // Verify results - assert_eq!(claims.tee, "aztdxvtpm"); - - // Check evaluation reports - assert!(!claims.evaluation_reports.is_empty()); - - // For AzTdxVtpm, we expect mr_td field in tcb_status - let tcb_status_map: serde_json::Map = - serde_json::from_str(&claims.tcb_status).unwrap(); - assert!(tcb_status_map.contains_key("aztdxvtpm.quote.body.mr_td")); - } - - #[test] - async fn verifier_test_eval_evidence_az_tdx_tpm_pcr04() { - // This test requires specific TDX evidence files - let evidence_path_pass = "../../examples/yocto_20241023223507.txt"; - let evidence_path_fail = "../../examples/yocto_20241025193121.txt"; - - if !std::path::Path::new(evidence_path_pass).exists() - || !std::path::Path::new(evidence_path_fail).exists() - { - println!("Skipping test_eval_evidence_az_tdx_tpm_pcr04: evidence files not found"); - return; - } - - // Create verifier with the policy fixture - let mut verifier = - DcapAttVerifier::::new_simple(Configuration::default()) - .unwrap(); - let fixture = PolicyFixture::testing_mock(); - fixture.configure_verifier(&mut verifier).await.unwrap(); - - // Read TDX evidence that should pass - let az_tdx_evidence_pass = read_vector_txt(evidence_path_pass.to_string()).unwrap(); - let runtime_data_bytes = vec![ - 240, 30, 194, 3, 67, 143, 162, 40, 249, 35, 238, 193, 59, 140, 203, 3, 98, 144, 105, - 221, 209, 34, 207, 229, 52, 61, 58, 14, 102, 234, 146, 8, - ]; - - // Evaluate the passing evidence - let raw_claims_pass = verifier - .evaluate( - az_tdx_evidence_pass, - Tee::AzTdxVtpm, - Some(Data::Raw(runtime_data_bytes.clone())), - HashAlgorithm::Sha256, - None, - HashAlgorithm::Sha256, - vec!["yocto".to_string()], - ) - .await - .map_err(|e| anyhow!("claim evaluation should pass but failed: {e:?}")) - .unwrap(); - let claims_pass = ASCoreTokenClaims::from_jwt(&raw_claims_pass).unwrap(); - - // Verify passing results - assert_eq!(claims_pass.tee, "aztdxvtpm"); - - // Read TDX evidence that should fail - let az_tdx_evidence_fail = read_vector_txt(evidence_path_fail.to_string()).unwrap(); - - // Evaluate the failing evidence - let raw_claims_fail = verifier - .evaluate( - az_tdx_evidence_fail, - Tee::AzTdxVtpm, - Some(Data::Raw(runtime_data_bytes)), - HashAlgorithm::Sha256, - None, - HashAlgorithm::Sha256, - vec!["yocto".to_string()], - ) - .await; - - assert!( - raw_claims_fail.is_err(), - "Expected rejection by policy 'yocto'" - ); - } -} diff --git a/crates/enclave-server/src/bin/install_policies.rs b/crates/enclave-server/src/bin/install_policies.rs deleted file mode 100644 index 68b45910..00000000 --- a/crates/enclave-server/src/bin/install_policies.rs +++ /dev/null @@ -1,13 +0,0 @@ -use seismic_enclave_server::utils::policy_fixture::install_all_policies; - -#[tokio::main] -async fn main() { - match install_all_policies().await { - Ok(_) => { - println!("Installed all policies"); - } - Err(e) => { - panic!("Failed to install policies: {}", e); - } - } -} diff --git a/crates/enclave-server/src/key_manager/builder.rs b/crates/enclave-server/src/key_manager/builder.rs deleted file mode 100644 index e6452d33..00000000 --- a/crates/enclave-server/src/key_manager/builder.rs +++ /dev/null @@ -1,54 +0,0 @@ -use super::manager::KeyManager; -use super::NetworkKeyProvider; - -use anyhow::Result; -use rand::rngs::OsRng; -use rand::TryRngCore; - -/// A builder for creating instances of [`KeyManager`]. -/// -/// Provides methods to create either a secure, randomly initialized key manager -/// or a deterministic mock version for testing purposes. -pub struct KeyManagerBuilder {} - -impl Default for KeyManagerBuilder { - fn default() -> Self { - Self::new() - } -} -impl KeyManagerBuilder { - /// Creates a new instance of the `KeyManagerBuilder`. - pub fn new() -> Self { - Self {} - } - - pub fn build_from_root_key(root_key: [u8; 32]) -> KeyManager { - let km = KeyManager::new(root_key); - km - } - - /// Builds a [`KeyManager`] using cryptographically secure random bytes - /// sourced from the operating system's RNG. - /// - /// # Errors - /// - /// Returns an error if the random number generator fails or if the - /// `KeyManager` fails to initialize. - pub fn build_from_os_rng() -> Result { - let mut rng = OsRng; - let mut rng_bytes = [0u8; 32]; - rng.try_fill_bytes(&mut rng_bytes)?; - - let km = KeyManager::new(rng_bytes); - Ok(km) - } - - /// Builds a mock [`KeyManager`] initialized with zeroed bytes. - /// - /// This method is intended for testing and non-production use. - pub fn build_mock() -> Result { - let km = KeyManager::new([0u8; 32]); - km.set_root_key([0u8; 32]); - Ok(km) - } -} diff --git a/crates/enclave-server/src/key_manager/manager.rs b/crates/enclave-server/src/key_manager/manager.rs deleted file mode 100644 index 814a55f7..00000000 --- a/crates/enclave-server/src/key_manager/manager.rs +++ /dev/null @@ -1,194 +0,0 @@ -use super::NetworkKeyProvider; - -use hkdf::Hkdf; -use sha2::Sha256; -use std::sync::Mutex; -use strum_macros::EnumIter; -use zeroize::{Zeroize, ZeroizeOnDrop}; - -/// Salt used during HKDF key derivation for purpose-specific keys. -const PURPOSE_DERIVE_SALT: &[u8] = b"seismic-purpose-derive-salt"; -/// Prefix used in domain separation when deriving purpose-specific keys. -const PREFIX: &str = "seismic-purpose"; - -/// Represents a derived key used for specific cryptographic purposes. -/// -/// Implements [`Zeroize`] and [`ZeroizeOnDrop`] to ensure the memory is cleared -/// when the value is dropped or explicitly zeroized. -#[derive(Debug, Clone, Zeroize, ZeroizeOnDrop)] -struct Key(pub Vec); -impl Key { - /// Creates a new `Key` from the given byte vector. - /// - /// This is primarily used internally by the key manager when deriving keys. - fn new(bytes: Vec) -> Self { - Self(bytes) - } -} -impl AsRef<[u8]> for Key { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -/// Enum representing the intended usage ("purpose") of a derived key. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter)] -pub(crate) enum KeyPurpose { - Snapshot, - RngPrecompile, - TxIo, -} -impl KeyPurpose { - /// Returns the short string label for the purpose. - fn label(&self) -> &'static str { - match self { - KeyPurpose::Snapshot => "snapshot", - KeyPurpose::RngPrecompile => "rng-precompile", - KeyPurpose::TxIo => "tx-io", - } - } - - /// Returns the domain separator for this purpose, used in HKDF expansion. - pub fn domain_separator(&self) -> Vec { - format!("{PREFIX}-{}", self.label()).into_bytes() - } -} - -/// Key manager for handling purpose-specific derived keys from a single root key. -/// -/// Keys are derived using HKDF-SHA256 with domain separation. -/// This struct supports retrieving keys. See KeyPurpose for the intended usages -pub struct KeyManager { - root_key: Mutex, -} -impl KeyManager { - /// Constructs a new `KeyManager` from a 32-byte root key. - pub fn new(root_key_bytes: [u8; 32]) -> Self { - let km = Self { - root_key: Mutex::new(Key(root_key_bytes.to_vec())), - }; - km - } - - /// Derives a key for a specific `KeyPurpose` - /// - /// # Errors - /// - /// Returns an error if HKDF expansion fails (though this is unlikely with correct parameters). - fn derive_purpose_key(&self, purpose: KeyPurpose, epoch: u64) -> Result { - let root_guard = self.root_key.lock().unwrap(); - let hk = Hkdf::::new(Some(PURPOSE_DERIVE_SALT), root_guard.as_ref()); - let mut info = purpose.domain_separator(); - info.extend_from_slice(&epoch.to_be_bytes()); - let mut derived_key = vec![0u8; 32]; - hk.expand(&info, &mut derived_key) - .expect("32 is a valid length for Sha256 to output"); - let key = Key::new(derived_key); - - Ok(key) - } - - /// Retrieves a purpose specific key derived from the root key - /// - /// Current implementation simply re-derives the key each time this function is called - /// Future implementations may cache the derived key, in which case this function will do more - fn get_purpose_key(&self, purpose: KeyPurpose, epoch: u64) -> Result { - let key = self.derive_purpose_key(purpose, epoch)?; - Ok(key) - } -} -impl NetworkKeyProvider for KeyManager { - /// Sets the root key for the key manager, replacing any existing key material. - fn set_root_key(&self, new_root_key: [u8; 32]) { - let mut root_guard = self.root_key.lock().unwrap(); - *root_guard = Key(new_root_key.to_vec()); - } - - /// Retrieves the secp256k1 secret key for transaction I/O signing. - fn get_tx_io_sk(&self, epoch: u64) -> secp256k1::SecretKey { - let key = self - .get_purpose_key(KeyPurpose::TxIo, epoch) - .expect("KeyManager should always have a snapshot key"); - secp256k1::SecretKey::from_slice(key.as_ref()) - .expect("retrieved secp256k1 secret key should be valid") - } - - /// Retrieves the secp256k1 public key corresponding to the TxIo secret key. - fn get_tx_io_pk(&self, epoch: u64) -> secp256k1::PublicKey { - let key = self - .get_purpose_key(KeyPurpose::TxIo, epoch) - .expect("KeyManager should always have a snapshot key"); - let sk = secp256k1::SecretKey::from_slice(key.as_ref()) - .expect("retrieved secp256k1 secret key should be valid"); - - secp256k1::PublicKey::from_secret_key(&secp256k1::Secp256k1::new(), &sk) - } - - /// Retrieves the Schnorrkel keypair used for randomness generation. - fn get_rng_keypair(&self, epoch: u64) -> schnorrkel::keys::Keypair { - let mini_key = self - .get_purpose_key(KeyPurpose::RngPrecompile, epoch) - .expect("KeyManager should always have a snapshot key"); - let mini_key_bytes = mini_key.as_ref(); - let mini_secret_key = schnorrkel::MiniSecretKey::from_bytes(mini_key_bytes) - .expect("mini_secret_key should be valid"); - mini_secret_key - .expand(schnorrkel::ExpansionMode::Uniform) - .into() - } - - /// Retrieves the AES-256-GCM encryption key used for snapshot operations. - fn get_snapshot_key(&self, epoch: u64) -> aes_gcm::Key { - let key = self - .get_purpose_key(KeyPurpose::Snapshot, epoch) - .expect("KeyManager should always have a snapshot key"); - let bytes: [u8; 32] = key.as_ref().try_into().expect("Key should be 32 bytes"); - bytes.into() - } - /// Retrieves a copy of the root secp256k1 secret key used for key management. - fn get_root_key(&self) -> [u8; 32] { - let root_guard = self.root_key.lock().unwrap(); - let bytes: [u8; 32] = root_guard.as_ref().try_into().unwrap(); - bytes - } -} - -#[cfg(test)] -mod tests { - use super::*; - use strum::IntoEnumIterator; - - #[test] - fn test_all_purpose_keys_are_initialized() { - let key_manager = KeyManager::new([0u8; 32]); - - for purpose in KeyPurpose::iter() { - let key = key_manager.get_purpose_key(purpose, 0).unwrap(); - assert!(!key.as_ref().is_empty()); - } - } - - #[test] - fn test_purpose_specific_keys_are_consistent() { - let key_manager = KeyManager::new([0u8; 32]); - let key_a = key_manager - .get_purpose_key(KeyPurpose::Snapshot, 0) - .unwrap(); - let key_b = key_manager - .get_purpose_key(KeyPurpose::Snapshot, 0) - .unwrap(); - assert_eq!(key_a.as_ref(), key_b.as_ref()); - } - - #[test] - fn test_epoch_key_rotation() { - let key_manager = KeyManager::new([0u8; 32]); - let key_a = key_manager - .get_purpose_key(KeyPurpose::Snapshot, 0) - .unwrap(); - let key_b = key_manager - .get_purpose_key(KeyPurpose::Snapshot, 1) - .unwrap(); - assert_ne!(key_a.as_ref(), key_b.as_ref()); - } -} diff --git a/crates/enclave-server/src/key_manager/mod.rs b/crates/enclave-server/src/key_manager/mod.rs index 0c79fe21..ecde7660 100644 --- a/crates/enclave-server/src/key_manager/mod.rs +++ b/crates/enclave-server/src/key_manager/mod.rs @@ -1,39 +1,128 @@ -mod builder; -mod manager; - -// re-export important types -pub use builder::KeyManagerBuilder; -pub use manager::KeyManager; - -use auto_impl::auto_impl; - -/// Trait for providing access to derived keys used in networking and other runtime logic. -/// -/// Used to abstract over how keys are retrieved (e.g., real or mocked key managers). -#[auto_impl(&, Arc)] -pub trait NetworkKeyProvider: Sync { - /// Sets the root key for the key manager, replacing any existing key material. - /// This update should propogate so that all derived keys are recalculated - /// based on the newly provided root key. +use anyhow::Result; +use hkdf::Hkdf; +use rand::{TryRngCore as _, rngs::OsRng}; +use sha2::Sha256; +use zeroize::{Zeroize, ZeroizeOnDrop}; + +/// Salt used during HKDF key derivation for purpose-specific keys. +const PURPOSE_DERIVE_SALT: &[u8] = b"seismic-purpose-derive-salt"; +/// Prefix used in domain separation when deriving purpose-specific keys. +const PREFIX: &str = "seismic-purpose"; + +#[derive(Zeroize, ZeroizeOnDrop)] +pub struct Key([u8; 32]); + +impl AsRef<[u8]> for Key { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +pub struct KeyManager { + root_key: Key, +} + +/// Enum representing the intended usage ("purpose") of a derived key. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum KeyPurpose { + Snapshot, + RngPrecompile, + TxIo, +} + +impl KeyPurpose { + /// Returns the short string label for the purpose. + fn label(&self) -> &'static str { + match self { + KeyPurpose::Snapshot => "snapshot", + KeyPurpose::RngPrecompile => "rng-precompile", + KeyPurpose::TxIo => "tx-io", + } + } + + /// Returns the domain separator for this purpose, used in HKDF expansion. + pub fn domain_separator(&self) -> Vec { + format!("{PREFIX}-{}", self.label()).into_bytes() + } +} + +impl KeyManager { + pub fn new(root_key: [u8; 32]) -> Self { + Self { + root_key: Key(root_key), + } + } + + pub fn new_as_genesis() -> Result { + let mut rng = OsRng; + let mut rng_bytes = [0u8; 32]; + rng.try_fill_bytes(&mut rng_bytes)?; + + let km = KeyManager::new(rng_bytes); + Ok(km) + } + + /// Derives a key for a specific `KeyPurpose` /// - /// This method should use interior mutability, e.g. Mutex, to allow - /// mutation without requiring a mutable reference to the key manager. - /// This keeps the higher-level API ergonomics clean by avoiding the need - /// for `&mut self` or external synchronization primitives. - fn set_root_key(&self, root_key: [u8; 32]); + /// # Errors + /// + /// Returns an error if HKDF expansion fails (though this is unlikely with correct parameters). + pub fn derive_purpose_key(&self, purpose: KeyPurpose, epoch: u64) -> Result { + let hk = Hkdf::::new(Some(PURPOSE_DERIVE_SALT), self.root_key.0.as_ref()); + let mut info = purpose.domain_separator(); + info.extend_from_slice(&epoch.to_be_bytes()); + let mut derived_key = vec![0u8; 32]; + hk.expand(&info, &mut derived_key) + .expect("32 is a valid length for Sha256 to output"); + let key = Key(derived_key.try_into().expect("unfallible")); + + Ok(key) + } - /// Retrieves the root secp256k1 secret key used for key management. - fn get_root_key(&self) -> [u8; 32]; + pub fn get_tx_io_sk(&self, epoch: u64) -> secp256k1::SecretKey { + let key = self + .derive_purpose_key(KeyPurpose::TxIo, epoch) + .expect("KeyManager should always have a snapshot key"); + secp256k1::SecretKey::from_slice(key.as_ref()) + .expect("retrieved secp256k1 secret key should be valid") + } - /// Retrieves the secp256k1 secret key used for transaction I/O. - fn get_tx_io_sk(&self, epoch: u64) -> secp256k1::SecretKey; + /// Retrieves the secp256k1 public key corresponding to the TxIo secret key. + pub fn get_tx_io_pk(&self, epoch: u64) -> secp256k1::PublicKey { + let key = self + .derive_purpose_key(KeyPurpose::TxIo, epoch) + .expect("KeyManager should always have a snapshot key"); + let sk = secp256k1::SecretKey::from_slice(key.as_ref()) + .expect("retrieved secp256k1 secret key should be valid"); - /// Retrieves the secp256k1 public key corresponding to the transaction I/O secret key. - fn get_tx_io_pk(&self, epoch: u64) -> secp256k1::PublicKey; + secp256k1::PublicKey::from_secret_key(&secp256k1::Secp256k1::new(), &sk) + } - /// Retrieves the Schnorrkel keypair used for generating randomness. - fn get_rng_keypair(&self, epoch: u64) -> schnorrkel::keys::Keypair; + /// Retrieves the Schnorrkel keypair used for randomness generation. + pub fn get_rng_keypair(&self, epoch: u64) -> schnorrkel::keys::Keypair { + let mini_key = self + .derive_purpose_key(KeyPurpose::RngPrecompile, epoch) + .expect("KeyManager should always have a snapshot key"); + let mini_key_bytes = mini_key.as_ref(); + let mini_secret_key = schnorrkel::MiniSecretKey::from_bytes(mini_key_bytes) + .expect("mini_secret_key should be valid"); + mini_secret_key + .expand(schnorrkel::ExpansionMode::Uniform) + .into() + } - /// Retrieves the AES-256-GCM encryption key used for snapshot encryption. - fn get_snapshot_key(&self, epoch: u64) -> aes_gcm::Key; + /// Retrieves the AES-256-GCM encryption key used for snapshot operations. + pub fn get_snapshot_key(&self, epoch: u64) -> aes_gcm::Key { + let key = self + .derive_purpose_key(KeyPurpose::Snapshot, epoch) + .expect("KeyManager should always have a snapshot key"); + let bytes: [u8; 32] = key.as_ref().try_into().expect("Key should be 32 bytes"); + bytes.into() + } + /// Retrieves a copy of the root secp256k1 secret key used for key management. + pub fn get_root_key(&self) -> [u8; 32] { + let root_guard = self.root_key.0; + let bytes: [u8; 32] = root_guard.as_ref().try_into().unwrap(); + bytes + } } diff --git a/crates/enclave-server/src/lib.rs b/crates/enclave-server/src/lib.rs index b6be9ef1..07e25218 100644 --- a/crates/enclave-server/src/lib.rs +++ b/crates/enclave-server/src/lib.rs @@ -1,10 +1,54 @@ -#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#![cfg_attr(not(test), warn(unused_crate_dependencies))] - -pub mod attestation; -pub mod key_manager; -pub mod server; -pub mod snapshot; +mod attestation; +mod key_manager; +mod server; pub mod utils; -use clap as _; // used by main.rs +const ENCLAVE_DEFAULT_ENDPOINT_IP: &str = "127.0.0.1"; +const DEFAULT_RETH_RPC: &str = "127.0.0.1:8545"; +pub const ENCLAVE_DEFAULT_ENDPOINT_PORT: u16 = 7878; + +use anyhow::Result; +use clap::Parser; +use seismic_enclave::mock::start_mock_server; +use std::net::SocketAddr; + +/// Command line arguments for the enclave server +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +pub struct Args { + /// The ip to bind the server to + #[arg(long, default_value_t = ENCLAVE_DEFAULT_ENDPOINT_IP.to_string())] + pub ip: String, + + /// The port to bind the server to + #[arg(long, default_value_t = ENCLAVE_DEFAULT_ENDPOINT_PORT)] + pub port: u16, + + /// Flag if this is the genesis node that needs to generate the keys + #[arg(long, default_value_t = false)] + pub genesis_node: bool, + + /// List of peer ips to fetch root key from. Must be {ip}:{port} + #[arg(long)] + pub peers: Vec, + + #[arg(long, default_value_t =DEFAULT_RETH_RPC.to_string())] + pub reth_rpc_url: String, + + #[arg(long, default_value_t = false)] + pub mock: bool, +} + +impl Args { + pub async fn start(self) -> Result<()> { + let addr: SocketAddr = format!("{}:{}", self.ip, self.port).parse()?; + + println!("Starting TDX Quote JSON-RPC Server on {addr}..."); + + if self.mock { + start_mock_server(addr).await + } else { + server::start_server(addr, self.genesis_node, self.peers).await + } + } +} diff --git a/crates/enclave-server/src/main.rs b/crates/enclave-server/src/main.rs index 09706d55..88d680f7 100644 --- a/crates/enclave-server/src/main.rs +++ b/crates/enclave-server/src/main.rs @@ -1,50 +1,8 @@ -use clap::arg; use clap::Parser; -use seismic_enclave::client::rpc::BuildableServer; -use seismic_enclave::{ENCLAVE_DEFAULT_ENDPOINT_IP, ENCLAVE_DEFAULT_ENDPOINT_PORT}; -use seismic_enclave_server::key_manager::KeyManager; -use seismic_enclave_server::key_manager::KeyManagerBuilder; -use seismic_enclave_server::server::{init_tracing, EnclaveServer, EnclaveServerBuilder}; -use std::net::IpAddr; -use tracing::info; +use seismic_enclave_server::{Args, utils::init_tracing}; -/// Command line arguments for the enclave server -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -struct Args { - /// The ip to bind the server to - #[arg(long, default_value_t = ENCLAVE_DEFAULT_ENDPOINT_IP)] - ip: IpAddr, - - /// The port to bind the server to - #[arg(long, default_value_t = ENCLAVE_DEFAULT_ENDPOINT_PORT)] - port: u16, -} - -/// Initializes a server with the given address and handlers #[tokio::main] -async fn main() { - if std::env::var_os("RUST_BACKTRACE").is_none() { - std::env::set_var("RUST_BACKTRACE", "1"); - } +async fn main() -> anyhow::Result<()> { init_tracing(); - - let args = Args::parse(); - info!("Enclave server starting on {}:{}", args.ip, args.port); - - // Using mock keys (hardcoded [0u8; 32]) so all nodes derive the same keys - // TODO: Replace with proper distributed key management for production - let key_manager = KeyManagerBuilder::build_mock().unwrap(); - info!("Using MOCK KEYS - all nodes will share the same temporary, insecure key"); - - // Use type parameter for the key provider (e.g., DefaultKeyProvider) - let builder: EnclaveServerBuilder = EnclaveServer::::builder() - .with_ip(args.ip) - .with_port(args.port) - .with_key_provider(key_manager); - - let server: EnclaveServer = builder.build().await.unwrap(); - let handle = server.start().await.unwrap(); - - handle.stopped().await; + Args::parse().start().await } diff --git a/crates/enclave-server/src/server.rs b/crates/enclave-server/src/server.rs new file mode 100644 index 00000000..c24f7040 --- /dev/null +++ b/crates/enclave-server/src/server.rs @@ -0,0 +1,153 @@ +use crate::{attestation::AttestationAgent, key_manager::KeyManager, utils::anyhow_to_rpc_error}; +use dcap_rs::types::quotes::version_4::QuoteV4; +use jsonrpsee::{ + core::{RpcResult, async_trait}, + http_client::HttpClientBuilder, + server::ServerBuilder, +}; +use seismic_enclave::{ + AttestationGetEvidenceResponse, GetPurposeKeysResponse, ShareRootKeyResponse, + TdxQuoteRpcClient as _, api::TdxQuoteRpcServer, +}; +use std::{net::SocketAddr, time::Duration}; +use tracing::{info, warn}; + +pub struct TdxQuoteServer { + attestation_agent: AttestationAgent, + key_manager: KeyManager, +} + +impl TdxQuoteServer { + pub fn new(attestation_agent: AttestationAgent, key_manager: KeyManager) -> Self { + Self { + attestation_agent, + key_manager, + } + } +} + +#[async_trait] +impl TdxQuoteRpcServer for TdxQuoteServer { + /// Health check endpoint that returns "OK" if service is running + async fn health_check(&self) -> RpcResult { + Ok("OK".to_string()) + } + + /// Get the secp256k1 public key + async fn get_purpose_keys(&self, epoch: u64) -> RpcResult { + Ok(GetPurposeKeysResponse { + tx_io_sk: self.key_manager.get_tx_io_sk(epoch), + tx_io_pk: self.key_manager.get_tx_io_pk(epoch), + snapshot_key_bytes: self.key_manager.get_snapshot_key(epoch).into(), + rng_keypair: self.key_manager.get_rng_keypair(epoch), + }) + } + + /// Generates attestation evidence from the attestation authority + async fn get_attestation_evidence(&self) -> RpcResult { + self.attestation_agent + .get_attestation_evidence() + .map_err(anyhow_to_rpc_error) + } + + /// Evaluates provided attestation evidence + async fn eval_attestation_evidence( + &self, + _hcl_report: Vec, + quote: Vec, + ) -> RpcResult<()> { + let quote = QuoteV4::from_bytes("e); // todo(dalton): This will panic if invalid quote bytes are sent find a way to catch or alternative + self.attestation_agent + .verify_attestation_report(quote) + .await + .map_err(anyhow_to_rpc_error) + } + + /// Shares the root key with an existing node + async fn boot_share_root_key(&self, quote: Vec) -> RpcResult { + let quote = QuoteV4::from_bytes("e); // todo(dalton): This will panic if invalid quote bytes are sent find a way to catch or alternative + + self.attestation_agent + .verify_attestation_report(quote) + .await + .map_err(anyhow_to_rpc_error)?; + + // quote is good send key + // Todo figure out encryption. We either force https or we handle encryption here + let root_key = self.key_manager.get_root_key(); + Ok(ShareRootKeyResponse { root_key }) + } + + /// Prepares an encrypted snapshot + async fn prepare_encrypted_snapshot(&self) -> RpcResult<()> { + todo!() + } + + /// Restores from an encrypted snapshot + async fn restore_from_encrypted_snapshot(&self) -> RpcResult<()> { + todo!() + } +} + +pub async fn start_server( + addr: SocketAddr, + genesis_node: bool, + peers: Vec, +) -> anyhow::Result<()> { + let attestation_agent = AttestationAgent::new().unwrap(); + + let key_manager = if genesis_node { + KeyManager::new_as_genesis()? + } else { + fetch_root_key_from_peers(peers, &attestation_agent).await + }; + + let server = ServerBuilder::default().build(addr).await?; + + let handle = server.start(TdxQuoteServer::new(attestation_agent, key_manager).into_rpc()); + + println!("TDX Quote JSON-RPC Server started at {}", addr); + + handle.stopped().await; + Ok(()) +} + +pub async fn fetch_root_key_from_peers( + peers: Vec, + attestation_agent: &AttestationAgent, +) -> KeyManager { + // let peers: Vec = peers.iter().filter_map(|p| p.parse().ok()).collect(); + + if peers.is_empty() { + panic!("Started in non-genesis with no valid peers"); + } + + info!("Starting root key fetching from peers"); + loop { + let evidence = attestation_agent + .get_attestation_evidence() + .expect("Unable to get our own quote data"); + + for peer in &peers { + let Ok(client) = HttpClientBuilder::default().build(peer) else { + warn!("Unable to make a connection with peer: {peer}. Trying next peer..."); + continue; + }; + + let Ok(res) = client.boot_share_root_key(evidence.quote.clone()).await else { + warn!("Peer({peer}) did not give us the key: \n Trying next peer..."); + continue; + }; + + // We got the key + info!("Key received. Starting Key manager"); + + return KeyManager::new(res.root_key); + } + + tracing::warn!( + "Cycled through all provided peers and did not receive root_key. Sleeping for 30 seconds and trying again" + ); + tokio::time::sleep(Duration::from_secs(30)).await; + } +} diff --git a/crates/enclave-server/src/server/boot.rs b/crates/enclave-server/src/server/boot.rs deleted file mode 100644 index e03522b7..00000000 --- a/crates/enclave-server/src/server/boot.rs +++ /dev/null @@ -1,291 +0,0 @@ -//! This module contains logic for allowing an operator -//! to configure the enclave server, e.g. to set the IP address of existing nodes - -use alloy; -use anyhow::anyhow; -use enclave_contract; -use hex; -use kbs_types::Tee; -use secp256k1::rand::rngs::OsRng as Secp256k1Rng; -use secp256k1::Secp256k1; -use seismic_enclave::request_types::{ShareRootKeyRequest, ShareRootKeyResponse}; -use seismic_enclave::rpc::SyncEnclaveApiClient; -use seismic_enclave::{crypto::Nonce, ecdh_decrypt, ecdh_encrypt}; -use seismic_enclave::{get_unsecure_sample_secp256k1_pk, get_unsecure_sample_secp256k1_sk}; -use std::sync::Mutex; -use tracing::info; - -pub struct Booter { - // pk and sk are the Booter's keys used to derive encryption keys for communication with other nodes - pk: secp256k1::PublicKey, - sk: secp256k1::SecretKey, - // a root key for the key manager - km_root_key: Mutex>, // mutex so that that functions can be called without &mut self in the engine - // tracks whether the booting process has been completed - completed: Mutex, -} -impl Booter { - pub fn new() -> Self { - let secp = Secp256k1::new(); - let (sk, pk) = secp.generate_keypair(&mut Secp256k1Rng); - Self { - pk, - sk, - km_root_key: None.into(), - completed: Mutex::new(false), - } - } - - /// a mock booter useful for testing - /// uses unsecure sample secp256k1 keys instead of a random ones - pub fn mock() -> Self { - Self { - pk: get_unsecure_sample_secp256k1_pk(), - sk: get_unsecure_sample_secp256k1_sk(), - km_root_key: None.into(), - completed: Mutex::new(false), - } - } - - /// Get the root key for the enclave server - pub fn get_root_key(&self) -> Option<[u8; 32]> { - let guard = self.km_root_key.lock().unwrap(); - guard.clone() - } - /// Get the Secp256k1 public key for communication with other nodes - pub fn pk(&self) -> secp256k1::PublicKey { - self.pk.clone() - } - /// Get the Secp256k1 secret key for communication with other nodes - pub fn sk(&self) -> secp256k1::SecretKey { - self.sk.clone() - } - /// Get the completion status of the booting process - /// Used to enable/disable certain engine endpoints - pub fn is_compelted(&self) -> bool { - let guard = self.completed.lock().unwrap(); - *guard - } - /// Mark the booting process as completed - pub fn mark_completed(&self) { - let mut completed_guard = self.completed.lock().unwrap(); - *completed_guard = true; - - // Zero the root key - let mut root_gurad = self.km_root_key.lock().unwrap(); - *root_gurad = None; - } - - /// Retrieves the network root key from an existing node and updates this node's root key. - /// - /// # Arguments - /// - /// * `tee` - The TEE (Trusted Execution Environment) type of the node retrieving the key. - /// * `attestation` - A byte vector containing the attestation from the existing node about the retriever's public key. - /// * `client` - A reference to an implementation of the `SyncEnclaveApiClient` trait, used to communicate with the existing node. - /// - /// # Returns - /// - /// * `Ok(())` if the key was successfully retrieved and stored. - /// * `Err(anyhow::Error)` if the retrieval, decryption, or storage failed. - pub fn retrieve_root_key( - &self, - tee: Tee, - attestation: &serde_json::Value, - client: &dyn SyncEnclaveApiClient, - ) -> Result<(), anyhow::Error> { - let req = ShareRootKeyRequest { - retriever_pk: self.pk(), - tee, - evidence: attestation.clone(), - }; - - info!("in boot_retrieve_root_key, beginning client boot_share_root_key call"); - let res = client.boot_share_root_key(req).map_err(|e| { - anyhow!( - "Error while requesting external service to share root key: {:?}", - e - ) - })?; - info!("in boot_retrieve_root_key, finished client boot_share_root_key call"); - - // decrypt ciphertext - let root_key = self.process_share_response(res)?; - let mut guard = self.km_root_key.lock().unwrap(); - *guard = Some(root_key); - Ok(()) - } - - /// Decrypts a shared root key from a `ShareRootKeyResponse`. - /// - /// # Returns - /// - /// * `Ok([u8; 32])` with the decrypted root key if successful. - /// * `Err(anyhow::Error)` if decryption fails or if the key is not the expected length. - pub fn process_share_response( - &self, - res: ShareRootKeyResponse, - ) -> Result<[u8; 32], anyhow::Error> { - let root_key_vec = ecdh_decrypt( - &res.sharer_pk, - &self.sk(), - &res.root_key_ciphertext, - res.nonce, - )?; - let root_key: [u8; 32] = root_key_vec - .try_into() - .map_err(|e| anyhow!("Error casting, root key had unexpected length: {:?}", e))?; - Ok(root_key) - } - - /// Encrypts an existing root key for a specified retriever using ECDH. - /// - /// # Arguments - /// - /// * `retriever_pk` - The public key of the node retrieving the root key. - /// * `existing_root_key` - A reference to the root key `[u8; 32]` to be encrypted. - /// - /// # Returns - /// - /// * `Ok((Nonce, Vec, secp256k1::PublicKey))` containing the nonce, ciphertext, and sharer's public key if successful. - /// * `Err(anyhow::Error)` if encryption fails. - pub fn encrypt_root_key( - &self, - retriever_pk: &secp256k1::PublicKey, - existing_root_key: &[u8; 32], - ) -> Result<(Nonce, Vec, secp256k1::PublicKey), anyhow::Error> { - let nonce = Nonce::new_rand(); - let root_key_ciphertext = - ecdh_encrypt(&retriever_pk, &self.sk(), existing_root_key, nonce.clone())?; - Ok((nonce, root_key_ciphertext, self.pk())) - } - - /// Generate a new genesis network root key - /// root key is generated using OsRng - pub fn genesis(&self) -> Result<(), anyhow::Error> { - // FUTURE WORK: consider using key shares instead of OsRng - // TEMPORARY: Using deterministic mock key for testing - let rng_bytes = [0u8; 32]; - - let mut guard = self.km_root_key.lock().unwrap(); - *guard = Some(rng_bytes); - Ok(()) - } - - pub async fn check_upgrade_contract(&self, tcb_status: &str) -> Result { - // Parse the tcb_status JSON string to access specific fields - let tcb_status_map: serde_json::Map = - serde_json::from_str(&tcb_status) - .map_err(|e| anyhow::anyhow!("Failed to parse tcb_status JSON: {e}"))?; - - let pcr4_hex = tcb_status_map - .get("aztdxvtpm.tpm.pcr04") - .and_then(|v| v.as_str()) - .map(|s| s.to_string()) - .ok_or(anyhow::anyhow!( - "Failed to parse tcb_status JSON field: pcr04" - ))?; - - let mr_td_hex = tcb_status_map - .get("aztdxvtpm.quote.body.mr_td") - .and_then(|v| v.as_str()) - .map(|s| s.to_string()) - .ok_or(anyhow::anyhow!( - "Failed to parse tcb_status JSON field: mrtd" - ))?; - - let mr_seam_hex = tcb_status_map - .get("aztdxvtpm.quote.body.mr_seam") - .and_then(|v| v.as_str()) - .map(|s| s.to_string()) - .ok_or(anyhow::anyhow!( - "Failed to parse tcb_status JSON field: mr_seam" - ))?; - - // Convert hex strings to bytes - let pcr4_bytes = hex::decode(pcr4_hex.strip_prefix("0x").unwrap_or(&pcr4_hex)) - .map_err(|e| anyhow::anyhow!("Failed to decode pcr4 hex: {e}"))?; - let mr_td_bytes = hex::decode(mr_td_hex.strip_prefix("0x").unwrap_or(&mr_td_hex)) - .map_err(|e| anyhow::anyhow!("Failed to decode mr_td hex: {e}"))?; - let mr_seam_bytes = hex::decode(mr_seam_hex.strip_prefix("0x").unwrap_or(&mr_seam_hex)) - .map_err(|e| anyhow::anyhow!("Failed to decode mr_seam hex: {e}"))?; - - // Ensure the bytes are the correct length as required by the contract - if pcr4_bytes.len() != 32 { - return Err(anyhow::anyhow!( - "pcr4 must be exactly 32 bytes, got {}", - pcr4_bytes.len() - )); - } - if mr_td_bytes.len() != 48 { - return Err(anyhow::anyhow!( - "mr_td must be exactly 48 bytes, got {}", - mr_td_bytes.len() - )); - } - if mr_seam_bytes.len() != 48 { - return Err(anyhow::anyhow!( - "mr_seam must be exactly 48 bytes, got {}", - mr_seam_bytes.len() - )); - } - - // Create ProposalParamsV1 struct - let params = enclave_contract::ProposalParamsV1::new( - alloy::primitives::Bytes::from(mr_td_bytes), - alloy::primitives::Bytes::from(mr_seam_bytes), - alloy::primitives::Bytes::from(pcr4_bytes), - ); - - // Get contract address and RPC URL from environment variables - let upgrade_operator_address = enclave_contract::UPGRADE_OPERATOR_ADDRESS - .parse::() - .unwrap(); - let rpc_url = "http://localhost:8545".to_string(); - - // Check the proposal status against the onchain contract - let status = - enclave_contract::check_proposal_status_v1(upgrade_operator_address, &rpc_url, ¶ms) - .await - .map_err(|e| anyhow::anyhow!("Booter failed to check proposal status: {e}"))?; - - Ok(status) - } -} - -#[cfg(test)] -mod tests { - use seismic_enclave::MockEnclaveClient; - - use super::*; - - #[test] - fn test_retrieve_root_key_mock() { - let booter = Booter::new(); - let client = MockEnclaveClient::default(); - let tee = kbs_types::Tee::AzTdxVtpm; - let res = booter.retrieve_root_key(tee, &serde_json::Value::Null, &client); - assert!(res.is_ok(), "failed to retrieve root key: {:?}", res); - assert!(booter.get_root_key().is_some(), "root key not set"); - assert!( - booter.get_root_key().unwrap() == [0u8; 32], - "root key does not match expected mock value" - ); - } - - #[test] - fn test_genesis() { - let booter = Booter::new(); - assert!(booter.get_root_key().is_none(), "root key should be empty"); - booter.genesis().unwrap(); - assert!( - booter.get_root_key().is_some(), - "root key should not be empty" - ); - let root_key = booter.get_root_key().unwrap(); - assert_eq!( - root_key, [0u8; 32], - "root key genesis should be deterministic mock key" - ); - } -} diff --git a/crates/enclave-server/src/server/engine.rs b/crates/enclave-server/src/server/engine.rs deleted file mode 100644 index b40b9215..00000000 --- a/crates/enclave-server/src/server/engine.rs +++ /dev/null @@ -1,551 +0,0 @@ -use attestation_agent::AttestationAPIs; -use attestation_service::VerificationRequest; -use jsonrpsee::core::{async_trait, RpcResult}; -use kbs_types::HashAlgorithm; -use log::error; -use serde_json; -use std::path::Path; -use std::sync::Arc; - -use super::boot::Booter; -use crate::attestation::seismic_aa_mock; -use crate::attestation::SeismicAttestationAgent; -use crate::key_manager::KeyManager; -use crate::key_manager::NetworkKeyProvider; -use crate::server::into_original::IntoOriginalData; -use crate::snapshot::{DATA_DISK_DIR, RETH_DATA_DIR, SNAPSHOT_DIR, SNAPSHOT_FILE}; -use seismic_enclave::request_types::*; -use seismic_enclave::rpc::EnclaveApiServer; -use seismic_enclave::rpc_missing_snapshot_error; -use seismic_enclave::EnclaveClient; -use seismic_enclave::{ - rpc_bad_argument_error, rpc_bad_evidence_error, rpc_conflict_error, rpc_internal_server_error, -}; - -/// The main execution engine for secure enclave logic -/// handles server api calls after http parsing and authentication -/// controls central resources, e.g. key manager, attestation agent -pub struct AttestationEngine { - key_provider: Arc, - attestation_agent: Arc, - booter: Arc, -} -impl AttestationEngine -where - K: NetworkKeyProvider + Send, -{ - pub(crate) fn new(key_provider: K, attestation_agent: SeismicAttestationAgent) -> Self { - Self { - key_provider: Arc::new(key_provider), - attestation_agent: Arc::new(attestation_agent), - booter: Arc::new(Booter::new()), - } - } - - /// helper function to get the key provider if it has been initialized - /// or return an RPC uninitialized resource error - pub fn key_provider(&self) -> Result, jsonrpsee::types::ErrorObjectOwned> { - if !self.booter.is_compelted() { - return Err(rpc_conflict_error(anyhow::anyhow!( - "Key provider not initialized" - ))); - } else { - return Ok(self.key_provider.clone()); - } - } -} - -#[async_trait] -impl EnclaveApiServer for AttestationEngine -where - K: NetworkKeyProvider + Send + Sync + 'static, -{ - async fn health_check(&self) -> RpcResult { - let boot_complete = self.booter.is_compelted(); - Ok(HealthCheckResponse { - status_ok: true, - boot_complete, - }) - } - - async fn get_purpose_keys( - &self, - req: GetPurposeKeysRequest, - ) -> RpcResult { - let key_provider = self.key_provider()?; - let epoch = req.epoch; - let resp = GetPurposeKeysResponse { - tx_io_sk: key_provider.get_tx_io_sk(epoch), - tx_io_pk: key_provider.get_tx_io_pk(epoch), - snapshot_key_bytes: key_provider.get_snapshot_key(epoch).into(), - rng_keypair: key_provider.get_rng_keypair(epoch), - }; - Ok(resp) - } - - // Attestation operations implementations - async fn get_attestation_evidence( - &self, - req: AttestationGetEvidenceRequest, - ) -> RpcResult { - // Use SeismicAttestationAgent's mutex-protected get_evidence method - // The mutex handling is already implemented in the agent - let evidence = match self - .attestation_agent - .get_evidence(req.runtime_data.as_slice()) - .await - { - Ok(evidence) => evidence, - Err(e) => { - error!("Failed to get attestation evidence: {}", e); - return Err(rpc_internal_server_error(anyhow::anyhow!( - "Issue in getting the evidence" - ))); - } - }; - - Ok(AttestationGetEvidenceResponse { evidence }) - } - - /// Evaluate the provided attestation evidence against a policy - /// and return the claims if the evidence is valid - /// Returns a rpc_bad_argument repsonse if the evidence is invalid or doens't match the provided policy - async fn eval_attestation_evidence( - &self, - request: AttestationEvalEvidenceRequest, - ) -> RpcResult { - // Convert the request's runtime data hash algorithm to the original enum - let runtime_data: Option = - request.runtime_data.map(|data| data.into_original()); - let runtime_data_hash_algorithm: HashAlgorithm = match request.runtime_data_hash_algorithm { - Some(alg) => alg, - None => HashAlgorithm::Sha256, - }; - - // Evaluate attestation evidence (no lock needed for evaluation) - let verification_request = VerificationRequest { - evidence: request.evidence, - tee: request.tee, - runtime_data, - runtime_data_hash_algorithm, - init_data: None, - }; - let eval_result = self - .attestation_agent - .evaluate(vec![verification_request], request.policy_ids) - .await; - - // Retrieve the claims from the AS token - let as_token: String = match eval_result { - Ok(as_token) => as_token, - Err(e) => { - error!("Failed to evaluate attestation evidence: {}", e); - return Err(rpc_bad_evidence_error(e)); - } - }; - - let claims: ASCoreTokenClaims = match ASCoreTokenClaims::from_jwt(&as_token) { - Ok(claims) => claims, - Err(e) => { - error!("Failed to parse AS token: {}", e); - return Err(rpc_internal_server_error(anyhow::anyhow!( - "Attestation evaluation passed, but encountered error while parsing AS token: {e}" - ))); - } - }; - - Ok(AttestationEvalEvidenceResponse { - claims: Some(claims), - }) - } - - /// Retrieves the network root key from an existing node - /// and updates this node's booter root key - /// Operator is expected to call complete_boot after this - async fn boot_retrieve_root_key( - &self, - req: RetrieveRootKeyRequest, - ) -> RpcResult { - if self.key_provider().is_ok() { - return Err(rpc_conflict_error(anyhow::anyhow!( - "Key provider already initialized" - ))); - } - let tee = self.attestation_agent.get_tee_type(); - let retriver_pk_bytes = self.booter.pk().serialize(); - - // make an attestation - let attestation_bytes: Vec = self - .attestation_agent - .get_evidence(&retriver_pk_bytes) - .await - .map_err(|e| rpc_internal_server_error(e))?; - let attestation_string = String::from_utf8(attestation_bytes).unwrap(); - let attestation: serde_json::Value = serde_json::from_str(&attestation_string).unwrap(); - - // Call the booter to retrieve the root key - // will be stored in the booter if successful - let client_builder = EnclaveClient::builder(); - let client = client_builder - .ip(req.addr.ip().to_string()) - .port(req.addr.port()) - .build() - .unwrap(); - self.booter - .retrieve_root_key(tee, &attestation, &client) - .map_err(|e| rpc_bad_argument_error(anyhow::anyhow!(e)))?; - - // respond to node operator - let resp = RetrieveRootKeyResponse {}; - - Ok(resp) - } - - /// Endpoint for requesting the network's root key from an existing node - /// Checks the the new node is authorized to retrieve the root key and - /// encrypts the existing root key for the new node - async fn boot_share_root_key( - &self, - req: ShareRootKeyRequest, - ) -> RpcResult { - // Verify new enclave's attestation is a valid attestation - let eval_response: AttestationEvalEvidenceResponse = - self.eval_attestation_evidence(req.clone().into()).await?; - - // Check the tcb_status against the upgrade contract - let claims = eval_response.claims.unwrap(); - let tcb_status = claims.tcb_status; - let valid_upgrade = self - .booter - .check_upgrade_contract(&tcb_status) - .await - .map_err(|e| rpc_internal_server_error(e))?; - if !valid_upgrade { - return Err(rpc_bad_evidence_error(anyhow::anyhow!( - "Attestation TCB is not approved in the upgrade contract" - ))); - } - - // Encrypt the existing root key - let key_provider = self.key_provider()?; - let existing_km_root_key = key_provider.get_root_key(); - let (nonce, root_key_ciphertext, sharer_pk) = self - .booter - .encrypt_root_key(&req.retriever_pk, &existing_km_root_key) - .map_err(|e| rpc_bad_argument_error(anyhow::anyhow!(e)))?; - - // return relevant response - Ok(ShareRootKeyResponse { - root_key_ciphertext, - nonce, - sharer_pk, - }) - } - - /// Endpoint for generating a new genesis network root key - /// User is expected to call complete_boot after this - async fn boot_genesis(&self) -> RpcResult<()> { - if self.key_provider().is_ok() { - return Err(rpc_conflict_error(anyhow::anyhow!( - "Key provider already initialized" - ))); - } - self.booter - .genesis() - .map_err(|e| rpc_internal_server_error(e))?; - Ok(()) - } - - /// Completes the booting process by setting the key manager - async fn complete_boot(&self) -> RpcResult<()> { - if self.key_provider().is_ok() { - return Err(rpc_conflict_error(anyhow::anyhow!( - "Key provider already initialized" - ))); - } - let root_key = match self.booter.get_root_key() { - Some(root_key) => root_key, - None => return Err(rpc_conflict_error(anyhow::anyhow!("Booter has not initialized a root key. Either call boot_retrieve_root_key or boot_genesis first"))), - }; - let existing_km = self.key_provider.clone(); - existing_km.set_root_key(root_key); - self.booter.mark_completed(); - - Ok(()) - } - - async fn prepare_encrypted_snapshot( - &self, - _req: PrepareEncryptedSnapshotRequest, - ) -> RpcResult { - let key_provider = self.key_provider()?; - let epoch = 0; // no key rotation yet - - let res = crate::snapshot::prepare_encrypted_snapshot( - &key_provider, - epoch, - RETH_DATA_DIR, - DATA_DISK_DIR, - SNAPSHOT_DIR, - SNAPSHOT_FILE, - ); - let resp = PrepareEncryptedSnapshotResponse { - success: res.is_ok(), - error: res.err().map(|e| e.to_string()).unwrap_or_default(), - }; - Ok(resp) - } - - async fn restore_from_encrypted_snapshot( - &self, - _req: RestoreFromEncryptedSnapshotRequest, - ) -> RpcResult { - let key_provider = self.key_provider()?; - let epoch = 0; // no key rotation yet - - let encrypted_snapshot_path = format!("{}/{}.enc", DATA_DISK_DIR, SNAPSHOT_FILE); - if !Path::new(&encrypted_snapshot_path).exists() { - return Err(rpc_missing_snapshot_error()); - } - let res = crate::snapshot::restore_from_encrypted_snapshot( - &key_provider, - epoch, - RETH_DATA_DIR, - DATA_DISK_DIR, - SNAPSHOT_DIR, - SNAPSHOT_FILE, - ); - let resp = RestoreFromEncryptedSnapshotResponse { - success: res.is_ok(), - error: res.err().map(|e| e.to_string()).unwrap_or_default(), - }; - Ok(resp) - } -} - -#[allow(dead_code)] -pub async fn engine_mock_booted() -> AttestationEngine { - let kp = KeyManager::new([0u8; 32]); - kp.set_root_key([0u8; 32]); - let saa = seismic_aa_mock().await; - let mut enclave_engine = AttestationEngine::new(kp, saa); - enclave_engine.booter = Booter::mock().into(); - enclave_engine.booter.mark_completed(); - enclave_engine -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::key_manager::KeyManagerBuilder; - use crate::utils::test_utils::get_random_port; - use crate::utils::test_utils::is_sudo; - use crate::utils::test_utils::pub_key_eval_request; - use seismic_enclave::get_unsecure_sample_secp256k1_pk; - use seismic_enclave::rpc::BuildableServer; - use seismic_enclave::MockEnclaveServer; - use seismic_enclave::ENCLAVE_DEFAULT_ENDPOINT_IP; - use serial_test::serial; - use std::net::SocketAddr; - use std::time::Duration; - use tokio::time::sleep; - - pub async fn default_unbooted_enclave_engine() -> AttestationEngine { - let kp = KeyManagerBuilder::build_mock().unwrap(); - let saa = seismic_aa_mock().await; - AttestationEngine::new(kp, saa) - } - - #[serial(attestation_agent)] - #[tokio::test] - pub async fn run_engine_tests() { - if !is_sudo() { - panic!("run_engine_tests: skipped (requires sudo privileges)"); - } - - let enclave_engine: AttestationEngine = engine_mock_booted().await; - - let t1 = test_attestation_evidence_handler_valid_request_sample(&enclave_engine); - let t2 = test_attestation_evidence_handler_aztdxvtpm_runtime_data(&enclave_engine); - let t3 = test_get_purpose_keys(&enclave_engine); - - // Run all concurrently and await them - let (_r1, _r2, _r3) = tokio::join!(t1, t2, t3); - } - - async fn test_attestation_evidence_handler_valid_request_sample( - enclave_engine: &AttestationEngine, - ) where - K: NetworkKeyProvider + Send + Sync + 'static, - { - // Mock a valid AttestationGetEvidenceRequest - let runtime_data = "nonce".as_bytes(); // Example runtime data - let evidence_request = AttestationGetEvidenceRequest { - runtime_data: runtime_data.to_vec(), - }; - - // Call the handler - let res = enclave_engine - .get_attestation_evidence(evidence_request) - .await - .unwrap(); - - // Ensure the response is not empty - assert!(!res.evidence.is_empty()); - } - - async fn test_attestation_evidence_handler_aztdxvtpm_runtime_data( - enclave_engine: &AttestationEngine, - ) where - K: NetworkKeyProvider + Send + Sync + 'static, - { - // handle set up permissions - if !is_sudo() { - panic!("test_eval_evidence_az_tdx: skipped (requires sudo privileges)"); - } - - // Make requests with different runtime data and see they are different - let runtime_data_1 = "nonce1".as_bytes(); - let evidence_request_1 = AttestationGetEvidenceRequest { - runtime_data: runtime_data_1.to_vec(), - }; - - let runtime_data_2 = "nonce2".as_bytes(); - let evidence_request_2 = AttestationGetEvidenceRequest { - runtime_data: runtime_data_2.to_vec(), - }; - - let res_1 = enclave_engine - .get_attestation_evidence(evidence_request_1) - .await - .unwrap(); - let res_2 = enclave_engine - .get_attestation_evidence(evidence_request_2) - .await - .unwrap(); - - assert_ne!(res_1.evidence, res_2.evidence); - } - - async fn test_get_purpose_keys(enclave_engine: &AttestationEngine) - where - K: NetworkKeyProvider + Send + Sync + 'static, - { - let epoch = 0; - let res = enclave_engine - .get_purpose_keys(GetPurposeKeysRequest { epoch: epoch }) - .await; - assert!(res.is_ok()); - let resp = res.unwrap(); - - let kp = enclave_engine.key_provider().unwrap(); - assert_eq!(resp.tx_io_pk, kp.get_tx_io_pk(epoch)); - assert_eq!(resp.tx_io_sk, kp.get_tx_io_sk(epoch)); - assert_eq!(resp.rng_keypair.secret, kp.get_rng_keypair(epoch).secret); - assert_eq!( - resp.snapshot_key_bytes, - Into::<[u8; 32]>::into(kp.get_snapshot_key(epoch)) - ); - } - - #[serial(attestation_agent)] - #[tokio::test] - async fn test_complete_boot() -> Result<(), anyhow::Error> { - if !is_sudo() { - panic!("test_complete_boot: skipped (requires sudo privileges)"); - } - let enclave_engine: AttestationEngine = default_unbooted_enclave_engine().await; - - let eval_context = pub_key_eval_request(); - - let mock_addr = SocketAddr::from((ENCLAVE_DEFAULT_ENDPOINT_IP, 0)); - let mock_share_req = ShareRootKeyRequest { - evidence: eval_context.evidence, - tee: eval_context.tee, - retriever_pk: get_unsecure_sample_secp256k1_pk(), - }; - - // test that key functions error before complete_boot - let res = enclave_engine - .get_purpose_keys(GetPurposeKeysRequest { epoch: 0 }) - .await; - assert!(res.is_err()); - let res = enclave_engine - .boot_share_root_key(mock_share_req.clone()) - .await; - assert!(res.is_err()); - - // test that complete_boot works - enclave_engine.boot_genesis().await.unwrap(); - enclave_engine.complete_boot().await.unwrap(); - assert!( - enclave_engine.booter.is_compelted(), - "booting should be marked complete" - ); - - // test that key functions work after complete_boot - let _ = enclave_engine - .get_purpose_keys(GetPurposeKeysRequest { epoch: 0 }) - .await?; - - // test that boot functions error after complete_boot - let res = enclave_engine.boot_genesis().await; - assert!( - res.is_err(), - "boot_genesis should return error after complete_boot" - ); - - let policy = "share_root".to_string(); - let res = enclave_engine - .boot_retrieve_root_key(RetrieveRootKeyRequest { - addr: mock_addr, - attestation_policy_id: policy, - }) - .await; - assert!( - res.is_err(), - "boot_retrieve_root_key should return error after complete_boot" - ); - let res = enclave_engine.complete_boot().await; - assert!( - res.is_err(), - "complete_boot should return error after complete_boot" - ); - - Ok(()) - } - - #[serial(attestation_agent)] - #[tokio::test(flavor = "multi_thread")] - async fn test_boot_retrieve_root_key() -> Result<(), anyhow::Error> { - if !is_sudo() { - panic!("test_boot_retrieve_root_key: skipped (requires sudo privileges)"); - } - let enclave_engine: AttestationEngine = default_unbooted_enclave_engine().await; - - // assert the booter root key begins uninitialized - assert!(enclave_engine.booter.get_root_key().is_none()); - - // start a mock server - let port = get_random_port(); - let addr = SocketAddr::from((ENCLAVE_DEFAULT_ENDPOINT_IP, port)); - let _server_handle = MockEnclaveServer::new(addr.clone()).start().await?; - let _ = sleep(Duration::from_secs(2)).await; - - // run the request - // Note: this is run against the mock, - // so bugs in boot_retrieve_root_key may slip through - // TODO: write an integration test with more realistic behavior - let _ = enclave_engine - .boot_retrieve_root_key(RetrieveRootKeyRequest { - addr, - attestation_policy_id: "share_root".to_string(), - }) - .await?; - - // check that the root key is now initialized to the mock value - assert_eq!(enclave_engine.booter.get_root_key().unwrap(), [0u8; 32]); - - Ok(()) - } -} diff --git a/crates/enclave-server/src/server/into_original.rs b/crates/enclave-server/src/server/into_original.rs deleted file mode 100644 index a0a9051a..00000000 --- a/crates/enclave-server/src/server/into_original.rs +++ /dev/null @@ -1,22 +0,0 @@ -//! Handles converting between the API and the original enums -//! These duplicate types are necessary because of Rust's orphan rule -//! and the conversion code cannot live in the seismic-enclave crate because -//! if you import the original enums directly in the API crate, -//! seismic-enclave builds the entire attestation service dependency, -//! which can break external projects like Reth - -use attestation_service::RuntimeData; -use seismic_enclave::request_types::Data as ApiData; - -pub trait IntoOriginalData { - fn into_original(self) -> RuntimeData; -} - -impl IntoOriginalData for ApiData { - fn into_original(self) -> RuntimeData { - match self { - ApiData::Raw(bytes) => RuntimeData::Raw(bytes), - ApiData::Structured(value) => RuntimeData::Structured(value), - } - } -} diff --git a/crates/enclave-server/src/server/mod.rs b/crates/enclave-server/src/server/mod.rs deleted file mode 100644 index ed66ff4e..00000000 --- a/crates/enclave-server/src/server/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod boot; -pub mod engine; -mod into_original; -pub mod server; - -// re-exports -pub use server::{init_tracing, EnclaveServer, EnclaveServerBuilder}; diff --git a/crates/enclave-server/src/server/server.rs b/crates/enclave-server/src/server/server.rs deleted file mode 100644 index 47994200..00000000 --- a/crates/enclave-server/src/server/server.rs +++ /dev/null @@ -1,328 +0,0 @@ -use crate::attestation::simple_token_broker_config; -use crate::attestation::SeismicAttestationAgent; -use crate::key_manager::NetworkKeyProvider; -use crate::server::engine::AttestationEngine; - -use seismic_enclave::request_types::*; -use seismic_enclave::rpc::{BuildableServer, EnclaveApiServer}; -use seismic_enclave::{ENCLAVE_DEFAULT_ENDPOINT_IP, ENCLAVE_DEFAULT_ENDPOINT_PORT}; - -use anyhow::{anyhow, Result}; -use attestation_service::token::simple; -use attestation_service::token::AttestationTokenConfig; -use jsonrpsee::core::{async_trait, RpcResult}; -use jsonrpsee::server::ServerHandle; -use jsonrpsee::Methods; -use std::net::{IpAddr, SocketAddr}; -use std::sync::Arc; -use tracing::{debug, info}; -use tracing_subscriber::{EnvFilter, FmtSubscriber}; - -/// The main server struct, with everything needed to run. -/// Can be constructed with the [`EnclaveServerBuilder`] -/// and started with the inherited [`start_rpc_server`] method -pub struct EnclaveServer -where - K: NetworkKeyProvider + Send + Sync + 'static, -{ - /// The address to listen on - addr: SocketAddr, - /// The main execution engine for secure enclave logic - /// controls central resources, e.g. key manager, attestation agent - inner: Arc>, -} - -/// A builder that lets us configure the server -pub struct EnclaveServerBuilder -where - K: NetworkKeyProvider + Send + Sync + 'static, -{ - addr: Option, - key_provider: Option, - attestation_config_path: Option, - token_broker_config: Option, -} -impl Default for EnclaveServerBuilder -where - K: NetworkKeyProvider + Send + Sync + 'static, -{ - fn default() -> Self { - Self { - addr: Some(SocketAddr::new( - ENCLAVE_DEFAULT_ENDPOINT_IP, - ENCLAVE_DEFAULT_ENDPOINT_PORT, - )), - key_provider: None, - attestation_config_path: None, - token_broker_config: Some(attestation_service::token::AttestationTokenConfig::Simple( - simple::Configuration::default(), - )), - } - } -} -impl EnclaveServerBuilder -where - K: NetworkKeyProvider + Send + Sync + 'static, -{ - pub fn with_ip(mut self, ip: IpAddr) -> Self { - if let Some(curr) = self.addr { - self.addr = Some(SocketAddr::new(ip, curr.port())); - } else { - self.addr = Some(SocketAddr::new(ip, ENCLAVE_DEFAULT_ENDPOINT_PORT)); - } - self - } - - pub fn with_port(mut self, port: u16) -> Self { - if let Some(curr) = self.addr { - self.addr = Some(SocketAddr::new(curr.ip(), port)); - } else { - self.addr = Some(SocketAddr::new(ENCLAVE_DEFAULT_ENDPOINT_IP, port)); - } - self - } - - pub fn with_key_provider(mut self, key_provider: K) -> Self { - self.key_provider = Some(key_provider); - self - } - - pub fn with_attestation_config(mut self, config_path: impl Into) -> Self { - self.attestation_config_path = Some(config_path.into()); - self - } - - pub fn with_token_broker_config(mut self, config: AttestationTokenConfig) -> Self { - self.token_broker_config = Some(config); - self - } - - /// Build the final `EnclaveServer` object. - /// Currently only support SimpleAttestationTokenBroker for the attestation verifier - /// Because getting the types to compile is a pain - pub async fn build(self) -> Result> { - let final_addr = self.addr.ok_or_else(|| { - anyhow!("No address found in builder (should not happen if default is set)") - })?; - let key_provider = self - .key_provider - .ok_or_else(|| anyhow!("No key provider supplied to builder"))?; - let token_broker_config = self - .token_broker_config - .unwrap_or_else(|| simple_token_broker_config()); - - // Initialize AttestationEngine with the key provider - let config_path = self.attestation_config_path.as_deref(); - let mut att_serv_config: attestation_service::config::Config = Default::default(); - att_serv_config.attestation_token_broker = token_broker_config; - let attestation_agent = SeismicAttestationAgent::new(config_path, att_serv_config).await; - - let inner = Arc::new(AttestationEngine::new(key_provider, attestation_agent)); - - Ok(EnclaveServer { - addr: final_addr, - inner, - }) - } -} - -impl EnclaveServer -where - K: NetworkKeyProvider + Send + Sync + 'static, -{ - /// Create a new builder with default address - pub fn builder() -> EnclaveServerBuilder { - EnclaveServerBuilder::default() - } - - /// Simplified constructor if you want to skip the builder - pub async fn new( - addr: impl Into, - key_provider: K, - token_broker: SeismicAttestationAgent, - ) -> Result { - let inner = Arc::new(AttestationEngine::new(key_provider, token_broker)); - - Ok(Self { - addr: addr.into(), - inner, - }) - } -} -impl BuildableServer for EnclaveServer -where - K: NetworkKeyProvider + Send + Sync + 'static, -{ - fn addr(&self) -> SocketAddr { - self.addr - } - - fn methods(self) -> Methods { - self.into_rpc().into() - } - - async fn start(self) -> Result { - // No need for separate attestation init as AttestationEngine handles this - let addr = self.addr.clone(); - let handle = BuildableServer::start_rpc_server(self).await; - info!(target: "rpc::enclave", "Server started at {}", addr); - handle - } -} - -/// Derive implementation of the async [`EnclaveApiServer`] trait -/// for [`EnclaveServer`] -/// Each implimentation logs using debug! and delegates to `self.inner` engine -macro_rules! impl_forwarding_async_server_trait { - ($(async fn $method_name:ident(&self $(, $param:ident: $param_ty:ty)*) - -> $ret:ty $(, log = $log_msg:literal)?),* $(,)?) => { - #[async_trait] - impl EnclaveApiServer for EnclaveServer - where - K: NetworkKeyProvider + Send + Sync + 'static, - { - $( - async fn $method_name(&self $(, $param: $param_ty)*) -> RpcResult<$ret> { - $(debug!(target: "rpc::enclave", "Serving {}", $log_msg);)? - self.inner.$method_name($($param),*).await - } - )* - } - }; -} -impl_forwarding_async_server_trait!( - async fn health_check(&self) -> HealthCheckResponse, - async fn get_purpose_keys(&self, req: GetPurposeKeysRequest) -> GetPurposeKeysResponse, log = "getPurposeKeys", - async fn get_attestation_evidence(&self, req: AttestationGetEvidenceRequest) -> AttestationGetEvidenceResponse, log = "getAttestationEvidence", - async fn eval_attestation_evidence(&self, req: AttestationEvalEvidenceRequest) -> AttestationEvalEvidenceResponse, log = "evalAttestationEvidence", - async fn boot_retrieve_root_key(&self, req: RetrieveRootKeyRequest) -> RetrieveRootKeyResponse, log = "boot_retrieve_root_key", - async fn boot_share_root_key(&self, req: ShareRootKeyRequest) -> ShareRootKeyResponse, log = "boot_share_root_key", - async fn boot_genesis(&self) -> (), log = "boot_genesis", - async fn complete_boot(&self) -> (), log = "complete_boot", - async fn prepare_encrypted_snapshot(&self, req: PrepareEncryptedSnapshotRequest) -> PrepareEncryptedSnapshotResponse, log = "prepare_encrypted_snapshot", - async fn restore_from_encrypted_snapshot(&self, req: RestoreFromEncryptedSnapshotRequest) -> RestoreFromEncryptedSnapshotResponse, log = "restore_from_encrypted_snapshot", -); - -pub fn init_tracing() { - // Read log level from RUST_LOG - let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("debug")); - - // Initialize the subscriber - let subscriber = FmtSubscriber::builder() - .with_env_filter(filter) // Use dynamic log level - .finish(); - - tracing::subscriber::set_global_default(subscriber).expect("Failed to set tracing subscriber"); - - info!("Enclave server tracing initialized"); -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::attestation::SeismicAttestationAgent; - use crate::key_manager::KeyManager; - use crate::server::{init_tracing, EnclaveServer}; - use crate::utils::test_utils::pub_key_eval_request; - use crate::utils::test_utils::{get_random_port, is_sudo}; - use seismic_enclave::client::boot_genesis_streamlined_async; - use seismic_enclave::client::rpc::BuildableServer; - use seismic_enclave::client::{ - EnclaveClient, EnclaveClientBuilder, ENCLAVE_DEFAULT_ENDPOINT_IP, - }; - use seismic_enclave::request_types::AttestationGetEvidenceRequest; - use seismic_enclave::rpc::EnclaveApiClient; - - use serial_test::serial; - use std::net::SocketAddr; - use std::thread::sleep; - use std::time::Duration; - - async fn test_health_check(client: &EnclaveClient) { - let response = client.health_check().await.unwrap(); - assert!(response.status_ok, "Status OK"); - } - - async fn test_attestation_get_evidence(client: &EnclaveClient) { - let runtime_data = "nonce".as_bytes(); // Example runtime data - let evidence_request = AttestationGetEvidenceRequest { - runtime_data: runtime_data.to_vec(), - }; - - // Call the handler - let res = client - .get_attestation_evidence(evidence_request) - .await - .unwrap(); - - // Ensure the response is not empty - assert!(!res.evidence.is_empty()); - } - - async fn test_attestation_eval_evidence(client: &EnclaveClient) { - // Mock a valid AttestationEvalEvidenceRequest - let eval_request = pub_key_eval_request(); - - client - .eval_attestation_evidence(eval_request) - .await - .unwrap(); - } - - async fn test_get_purpose_keys(client: &EnclaveClient) { - let response = client - .get_purpose_keys(GetPurposeKeysRequest { epoch: 0 }) - .await - .unwrap(); - assert!(response.snapshot_key_bytes.len() == 32); - assert_ne!( - response.snapshot_key_bytes, [0u8; 32], - "Snapshot key is not all zeros" - ); - } - - #[tokio::test] - #[serial(attestation_agent)] - async fn test_server_requests() { - init_tracing(); - // handle set up permissions - if !is_sudo() { - tracing::error!("test_server_requests: skipped (requires sudo privileges)"); - return; - } - - // spawn a seperate thread for the server, otherwise the test will hang - let port = get_random_port(); // rand port for test parallelization - let addr = SocketAddr::from((ENCLAVE_DEFAULT_ENDPOINT_IP, port)); - let kp = KeyManager::new([0u8; 32]); - - let token_broker_config = attestation_service::token::AttestationTokenConfig::Simple( - simple::Configuration::default(), - ); - let mut att_serv_config: attestation_service::config::Config = Default::default(); - att_serv_config.attestation_token_broker = token_broker_config; - - let seismic_attestation_agent = SeismicAttestationAgent::new(None, att_serv_config).await; - let _server_handle = EnclaveServer::::new(addr, kp, seismic_attestation_agent) - .await - .unwrap() - .start() - .await - .unwrap(); - sleep(Duration::from_secs(4)); - - let client = EnclaveClientBuilder::new() - .ip(ENCLAVE_DEFAULT_ENDPOINT_IP.to_string()) - .port(port) - .timeout(Duration::from_secs(5)) - .build() - .unwrap(); - - boot_genesis_streamlined_async(&client).await.unwrap(); - - test_health_check(&client).await; - test_attestation_get_evidence(&client).await; - test_attestation_eval_evidence(&client).await; - test_get_purpose_keys(&client).await; - } -} diff --git a/crates/enclave-server/src/utils.rs b/crates/enclave-server/src/utils.rs new file mode 100644 index 00000000..df700ce7 --- /dev/null +++ b/crates/enclave-server/src/utils.rs @@ -0,0 +1,53 @@ +use jsonrpsee::types::{ErrorCode, ErrorObjectOwned}; +use libc::uid_t; +use tracing::info; +use tracing_subscriber::{EnvFilter, FmtSubscriber}; + +pub fn get_current_uid() -> uid_t { + unsafe { libc::getuid() } +} + +pub fn anyhow_to_rpc_error(e: anyhow::Error) -> ErrorObjectOwned { + ErrorObjectOwned::owned(ErrorCode::InternalError.code(), e.to_string(), None::<()>) +} + +/// Checks if the current user has root (sudo) privileges. +/// +/// This function runs the `id -u` command, which returns the current user's ID. +/// In Unix-like systems, the user ID of the root user is `0`. The function checks +/// if the output of the `id -u` command is `"0"`, indicating that the user is running +/// as root (with sudo privileges). +/// +/// # Returns +/// +/// - `true`: If the user has root privileges (user ID is `0`). +/// - `false`: If the user does not have root privileges. +pub fn is_sudo() -> bool { + use std::process::Command; + + // Run the "id -u" command to check the user ID + let output = Command::new("id") + .arg("-u") + .output() + .expect("Failed to execute id command"); + + // Convert the output to a string and trim any whitespace + let user_id = String::from_utf8(output.stdout).unwrap().trim().to_string(); + + // Check if the user ID is 0 (which means the user is root) + user_id == "0" +} + +pub fn init_tracing() { + // Read log level from RUST_LOG + let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("debug")); + + // Initialize the subscriber + let subscriber = FmtSubscriber::builder() + .with_env_filter(filter) // Use dynamic log level + .finish(); + + tracing::subscriber::set_global_default(subscriber).expect("Failed to set tracing subscriber"); + + info!("Enclave server tracing initialized"); +} diff --git a/crates/enclave-server/src/utils/mod.rs b/crates/enclave-server/src/utils/mod.rs deleted file mode 100644 index ae6fc8c5..00000000 --- a/crates/enclave-server/src/utils/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -pub mod policy_fixture; - -/// Utils for testing -/// Not under a test flag so the integration tests can use them -pub mod test_utils; - -/// tdx_evidence_helpers contains helpers for dealing with Vec evidence -/// and converting it to a human readable format. It is mainly used for debugging -/// the logic is mostly copied and pasted from https://github.com/confidential-containers/trustee/tree/main/deps/verifier/src/tdx -#[allow(dead_code)] -#[cfg(feature = "az-tdx-vtpm-attester")] -pub mod tdx_evidence_helpers; - -/// runners has cargo tests so I can -/// one click run them and see the output -/// They are for dev convenience only -/// test runners are for dev convenience only -#[allow(unused_imports)] -pub mod runners; - -#[cfg(not(feature = "supervisorctl"))] -pub mod service; -#[cfg(feature = "supervisorctl")] -pub mod supervisorctl; diff --git a/crates/enclave-server/src/utils/policy_fixture.rs b/crates/enclave-server/src/utils/policy_fixture.rs deleted file mode 100644 index 8a0f5711..00000000 --- a/crates/enclave-server/src/utils/policy_fixture.rs +++ /dev/null @@ -1,144 +0,0 @@ -//! Attestation Verifier Policies and the PolicyFixture object -//! Useful for testing the attestation verifier - -use anyhow::Result; -use attestation_service::config::Config; -use attestation_service::token::simple::{Configuration, SimpleAttestationTokenBroker}; -use attestation_service::token::{AttestationTokenBroker, AttestationTokenConfig}; -use attestation_service::AttestationService; -use base64::Engine; -use std::collections::HashMap; - -pub(crate) const ALLOW_POLICY: &str = r#" -package policy - -default allow = true -"#; - -pub(crate) const DENY_POLICY: &str = r#" -package policy - -default allow = false -"#; - -// TODO: eval if we need 7 and 11. and/or anything else. -// See https://confidentialcontainers.org/blog/2024/03/01/building-trust-into-os-images-for-confidential-containers/ -// This article is written by Magnus Kulke, a CoCo maintainer who works at Microsoft -// You do not check rtmr because azure does not load the kernel until after they would be set -pub(crate) const YOCTO_POLICY: &str = r#" -package policy - -import rego.v1 - -default allow = false - -allow if { - input["aztdxvtpm.quote.body.mr_td"] == "bb379f8e734a755832509f61403f99db2258a70a01e1172a499d6d364101b0675455b4e372a35c1f006541f2de0d7154" - input["aztdxvtpm.quote.body.mr_seam"] == "9790d89a10210ec6968a773cee2ca05b5aa97309f36727a968527be4606fc19e6f73acce350946c9d46a9bf7a63f8430" - input["aztdxvtpm.tpm.pcr04"] == "fc846c8703feffa34e7c70cc62701f534abd3a59942a04a20081f0bff7cf182d" -} -"#; - -pub(crate) struct PolicyFixture { - pub policy_map: HashMap, -} - -impl Default for PolicyFixture { - fn default() -> Self { - Self::new() - } -} - -impl PolicyFixture { - /// Creates a blank PolicyFixture - /// Policies can then be added with `with_policy` - pub(crate) fn new() -> Self { - let policy_map = HashMap::new(); - Self { policy_map } - } - - /// Creates a PolicyFixture with several policies useful for testing - /// Not used in production, as we currently have no use case for blanket allow/deny policies - pub(crate) fn all_policies() -> Self { - let mut policy_map = HashMap::new(); - - policy_map.insert( - "allow".to_string(), - base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(ALLOW_POLICY), - ); - - policy_map.insert( - "deny".to_string(), - base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(DENY_POLICY), - ); - - policy_map.insert( - "yocto".to_string(), - base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(YOCTO_POLICY), - ); - - // for share_root, we allow all, - // and then the key fields are checked against an on-chain contract - policy_map.insert( - "share_root".to_string(), - base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(ALLOW_POLICY), - ); - - Self { policy_map } - } - - /// Add a custom policy to the fixture - #[allow(dead_code)] - pub(crate) fn with_policy(mut self, id: &str, content: &str) -> Self { - self.policy_map.insert(id.to_string(), content.to_string()); - self - } - - /// Configure the verifier with all policies in this fixture - pub(crate) async fn configure_verifier( - &self, - verifier: &mut AttestationService, - ) -> Result<()> - where - T: AttestationTokenBroker + Send + Sync + 'static, - { - for (policy_id, policy_content) in &self.policy_map { - verifier - .set_policy(policy_id.clone(), policy_content.clone()) - .await?; - } - Ok(()) - } - - /// Get the content of a specific policy - #[allow(dead_code)] - pub(crate) fn get_policy_content(&self, policy_id: &str) -> Option<&String> { - self.policy_map.get(policy_id) - } - - /// Get all policy IDs - #[allow(dead_code)] - pub(crate) fn get_policy_ids(&self) -> Vec { - self.policy_map.keys().cloned().collect() - } - - #[allow(dead_code)] - pub(crate) fn encode_policy(&self, policy: &str) -> String { - base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(policy) - } -} - -/// Install all of the policies we've defined -pub async fn install_all_policies() -> anyhow::Result<()> { - let as_config = Config { - attestation_token_broker: AttestationTokenConfig::Simple(Configuration::default()), - ..Default::default() - }; - let mut verifier = AttestationService::new(as_config).await.unwrap(); - let fixture = PolicyFixture::all_policies(); - fixture - .configure_verifier::(&mut verifier) - .await - .unwrap(); - Ok(()) -} diff --git a/crates/enclave-server/src/utils/runners.rs b/crates/enclave-server/src/utils/runners.rs deleted file mode 100644 index 2ebf66a1..00000000 --- a/crates/enclave-server/src/utils/runners.rs +++ /dev/null @@ -1,104 +0,0 @@ -//! This file has cargo tests so I can -//! one click run them and see the output -//! They are for dev convenience only -//! and should be ignored in automated testing workflows - -use crate::attestation::seismic_aa_mock; -use crate::attestation::SeismicAttestationAgent; -use crate::utils::test_utils::read_vector_txt; -use attestation_agent::AttestationAPIs; - -use anyhow::Ok; -use attestation_service::config::Config; -use base64::engine::general_purpose::URL_SAFE_NO_PAD; -use base64::Engine; -use seismic_enclave::get_unsecure_sample_secp256k1_pk; -use sha2::Digest; -use sha2::Sha256; -use std::str::FromStr; - -#[test] -#[ignore] -pub fn print_active_feature() { - #[cfg(feature = "az-tdx-vtpm-attester")] - { - println!("az-tdx-vtpm-attester enabled"); - } - - #[cfg(not(feature = "az-tdx-vtpm-attester"))] - { - println!("az-tdx-vtpm-attester not enabled"); - } -} - -#[test] -#[ignore] -fn see_as_token() -> Result<(), anyhow::Error> { - let as_token = std::fs::read_to_string("./src/coco_as/examples/as_token.txt").unwrap(); - let parts: Vec<&str> = as_token.splitn(3, '.').collect(); - let claims_b64 = parts[1]; - let claims_decoded_bytes = URL_SAFE_NO_PAD.decode(claims_b64)?; - let claims_decoded_string = String::from_utf8(claims_decoded_bytes)?; - let claims_pretty_str = serde_json::to_string_pretty(&claims_decoded_string)?; - println!("{claims_pretty_str}"); - Ok(()) -} - -#[tokio::test] -#[ignore] -async fn see_default_config() { - let config = Config::default(); - println!("{:?}", config); -} - -//#[tokio::test(flavor = "multi_thread")] -//async fn run_client_ping() { -// use seismic_enclave::rpc::SyncEnclaveApiClient; -// use seismic_enclave::snapshot::RestoreFromEncryptedSnapshotRequest; -// use seismic_enclave::EnclaveClient; -// -// let url = "http://yocto-1.seismicdev.net:7878"; -// // let url = "http://127.0.0.1:7878"; -// let client = EnclaveClient::new(url); -// -// // // health check -// // let resp = client.health_check().unwrap(); -// // println!("resp: {:?}", resp); -//} - -#[cfg(feature = "az-tdx-vtpm-attester")] -#[cfg(test)] -mod attester_tests { - use super::*; - use crate::utils::tdx_evidence_helpers::get_tdx_evidence_claims; - use crate::utils::test_utils::read_vector_txt; - - #[tokio::test] - #[ignore] - async fn run_create_tdx_evidence() -> Result<(), anyhow::Error> { - let unsecure_secp256k1_pk = get_unsecure_sample_secp256k1_pk(); - let runtime_data = unsecure_secp256k1_pk.serialize().to_vec(); - let saa = seismic_aa_mock().await; - let tdx_evidence = saa.get_evidence(&runtime_data.to_vec()).await?; - print_active_feature(); - println!("{:?}", saa.get_tee_type()); - println!("{:?}", tdx_evidence); - println!("runtime_data: {:?}", runtime_data); - println!("claims: {:?}", get_tdx_evidence_claims(tdx_evidence)?); - assert!(false); // so I can see the print statement - Ok(()) - } - - #[test] - #[ignore] - fn see_yocto_tdx_evidence() -> Result<(), anyhow::Error> { - let path = "../../examples/yocto_20241025193121.txt"; // Note this file has moved - let tdx_evidence: Vec = read_vector_txt(path.to_string())?; - - let claims = get_tdx_evidence_claims(tdx_evidence)?; - println!("{:?}", claims); - - assert!(false); // so I can see the print statement - Ok(()) - } -} diff --git a/crates/enclave-server/src/utils/service.rs b/crates/enclave-server/src/utils/service.rs deleted file mode 100644 index 69a6cbbf..00000000 --- a/crates/enclave-server/src/utils/service.rs +++ /dev/null @@ -1,46 +0,0 @@ -use std::process::{Command, Output}; - -const SEISMIC_RETH_SERVICE: &str = "reth"; - -/// Executes a `service` command for managing services. -/// -/// # Arguments -/// * `action` - The action to perform (e.g., "start", "stop", "status"). -/// * `service` - The name of the service to manage, e.g. "reth". -/// -/// # Returns -/// * `Result` - The command output or an error. -fn service_command(action: &str, service: &str) -> Result { - let output = Command::new("service").arg(service).arg(action).output()?; - - Ok(output) -} - -/// Stops the `reth` service using `service`. -pub fn stop_reth() -> Result<(), anyhow::Error> { - service_command("stop", SEISMIC_RETH_SERVICE) - .map_err(|e| anyhow::anyhow!("service stop reth failed: {}", e))?; - Ok(()) -} - -/// Starts the `reth` service using `service`. -pub fn start_reth() -> Result<(), anyhow::Error> { - service_command("start", SEISMIC_RETH_SERVICE) - .map_err(|e| anyhow::anyhow!("service start reth failed: {}", e))?; - Ok(()) -} - -/// Checks if the `reth` service is running using `service status`. -/// -/// # Returns -/// * `bool` - `true` if the service is running, otherwise `false`. -pub fn reth_is_running() -> bool { - let output = service_command("status", SEISMIC_RETH_SERVICE); - - if let Ok(output) = output { - let stdout = String::from_utf8_lossy(&output.stdout); - stdout.contains("running") || stdout.contains("is running") - } else { - false - } -} diff --git a/crates/enclave-server/src/utils/supervisorctl.rs b/crates/enclave-server/src/utils/supervisorctl.rs deleted file mode 100644 index 057b78bd..00000000 --- a/crates/enclave-server/src/utils/supervisorctl.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::process::{Command, Output}; - -const SEISMIC_RETH_SERVICE: &str = "reth"; - -/// Executes a `supervisorctl` command for managing services. -/// -/// # Arguments -/// * `action` - The action to perform (e.g., "start", "stop", "status"). -/// * `service` - The name of the service to manage, e.g. "reth". -/// -/// # Returns -/// * `Result` - The command output or an error for internal failures. -/// The output may contain additional information about the command's execution, ex stderr -/// if the command exited with an error. -fn supervisorctl_command(action: &str, service: &str) -> Result { - let output: Output = Command::new("sudo") - .arg("supervisorctl") - .arg(action) - .arg(service) - .output()?; - - Ok(output) -} - -/// Stops the `reth` service using `supervisorctl`. -pub fn stop_reth() -> Result<(), anyhow::Error> { - supervisorctl_command("stop", SEISMIC_RETH_SERVICE) - .map_err(|e| anyhow::anyhow!("supervisorctl stop reth failed: {}", e))?; - Ok(()) -} - -/// Starts the `reth` service using `supervisorctl`. -pub fn start_reth() -> Result<(), anyhow::Error> { - supervisorctl_command("start", SEISMIC_RETH_SERVICE) - .map_err(|e| anyhow::anyhow!("supervisorctl start reth failed: {}", e))?; - Ok(()) -} - -/// Checks if the `reth` service is running. -/// -/// # Returns -/// * `bool` - `true` if the service is running, otherwise `false`. -pub fn reth_is_running() -> bool { - let output = supervisorctl_command("status", SEISMIC_RETH_SERVICE) - .map_err(|e| anyhow::anyhow!("supervisorctl status reth failed: {}", e)) - .unwrap(); - let stdout = std::str::from_utf8(&output.stdout) - .map_err(|_| "Failed to parse command output".to_string()) - .unwrap(); - stdout.contains("RUNNING") -} diff --git a/crates/enclave-server/src/utils/tdx_evidence_helpers.rs b/crates/enclave-server/src/utils/tdx_evidence_helpers.rs deleted file mode 100644 index e91b15b1..00000000 --- a/crates/enclave-server/src/utils/tdx_evidence_helpers.rs +++ /dev/null @@ -1,571 +0,0 @@ -use anyhow::anyhow; -use anyhow::bail; -use anyhow::Context; -use anyhow::Result; -use core::fmt; -use log::debug; -use serde_json::{Map, Value}; -pub type TeeEvidenceParsedClaim = serde_json::Value; -use az_tdx_vtpm::vtpm::Quote as TpmQuote; -use az_tdx_vtpm::{imds, report}; -use scroll::Pread; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize)] -pub(crate) struct Evidence { - pub tpm_quote: TpmQuote, - pub hcl_report: Vec, - pub td_quote: Vec, -} - -/// parses an attestation, serialized as bytes, to the [`Evidence`] strruct -pub(crate) fn tdx_attestation_bytes_to_evidence_struct( - attestation_bytes: &[u8], -) -> anyhow::Result { - let evidence = serde_json::from_slice::(attestation_bytes) - .map_err(|_| anyhow::anyhow!("Failed to deserialize Azure vTPM TDX evidence"))?; - Ok(evidence) -} - -/// Takes in tdx_evidence as a vec, as it is returned by coco libs, -/// and prints out the claim as a string -/// Currrently this does not check the cc_eventlog or the aa_eventlog -/// because I don't think AxTdxVtpm uses them -pub fn get_tdx_evidence_claims(tdx_evidence: Vec) -> Result<(), anyhow::Error> { - let evidence = serde_json::from_slice::(tdx_evidence.as_slice()) - .context("Failed to deserialize Azure vTPM TDX evidence")?; - let td_quote = parse_tdx_quote(&evidence.td_quote)?; - let mut claim = generate_parsed_claim(td_quote)?; - extend_claim_with_tpm_quote(&mut claim, &evidence.tpm_quote)?; - let claim = serde_json::to_string_pretty(&claim)?; - println!("{claim}"); - Ok(()) -} - -pub(crate) fn get_tdx_quote() -> Result { - let td_report = report::get_report().map_err(|e| anyhow!("Failed to get TD report: {}", e))?; - - let td_quote = - imds::get_td_quote(&td_report).map_err(|e| anyhow!("Failed to get TD quote: {}", e))?; - - parse_tdx_quote(td_quote.as_slice()) -} - -macro_rules! parse_claim { - ($map_name: ident, $key_name: literal, $field: ident) => { - $map_name.insert($key_name.to_string(), serde_json::Value::Object($field)) - }; - ($map_name: ident, $key_name: literal, $field: expr) => { - $map_name.insert( - $key_name.to_string(), - serde_json::Value::String(hex::encode($field)), - ) - }; -} - -pub(crate) fn generate_parsed_claim(quote: Quote) -> Result { - let mut quote_map = Map::new(); - let mut quote_body = Map::new(); - let mut quote_header = Map::new(); - - match "e { - Quote::V4 { header, body } => { - parse_claim!(quote_header, "version", b"\x04\x00"); - parse_claim!(quote_header, "att_key_type", header.att_key_type); - parse_claim!(quote_header, "tee_type", header.tee_type); - parse_claim!(quote_header, "reserved", header.reserved); - parse_claim!(quote_header, "vendor_id", header.vendor_id); - parse_claim!(quote_header, "user_data", header.user_data); - parse_claim!(quote_body, "tcb_svn", body.tcb_svn); - parse_claim!(quote_body, "mr_seam", body.mr_seam); - parse_claim!(quote_body, "mrsigner_seam", body.mrsigner_seam); - parse_claim!(quote_body, "seam_attributes", body.seam_attributes); - parse_claim!(quote_body, "td_attributes", body.td_attributes); - parse_claim!(quote_body, "xfam", body.xfam); - parse_claim!(quote_body, "mr_td", body.mr_td); - parse_claim!(quote_body, "mr_config_id", body.mr_config_id); - parse_claim!(quote_body, "mr_owner", body.mr_owner); - parse_claim!(quote_body, "mr_owner_config", body.mr_owner_config); - parse_claim!(quote_body, "rtmr_0", body.rtmr_0); - parse_claim!(quote_body, "rtmr_1", body.rtmr_1); - parse_claim!(quote_body, "rtmr_2", body.rtmr_2); - parse_claim!(quote_body, "rtmr_3", body.rtmr_3); - parse_claim!(quote_body, "report_data", body.report_data); - - parse_claim!(quote_map, "header", quote_header); - parse_claim!(quote_map, "body", quote_body); - } - Quote::V5 { - header, - r#type, - size, - body, - } => { - parse_claim!(quote_header, "version", b"\x05\x00"); - parse_claim!(quote_header, "att_key_type", header.att_key_type); - parse_claim!(quote_header, "tee_type", header.tee_type); - parse_claim!(quote_header, "reserved", header.reserved); - parse_claim!(quote_header, "vendor_id", header.vendor_id); - parse_claim!(quote_header, "user_data", header.user_data); - parse_claim!(quote_map, "type", r#type.as_bytes()); - parse_claim!(quote_map, "size", &size[..]); - match body { - QuoteV5Body::Tdx10(body) => { - parse_claim!(quote_body, "tcb_svn", body.tcb_svn); - parse_claim!(quote_body, "mr_seam", body.mr_seam); - parse_claim!(quote_body, "mrsigner_seam", body.mrsigner_seam); - parse_claim!(quote_body, "seam_attributes", body.seam_attributes); - parse_claim!(quote_body, "td_attributes", body.td_attributes); - parse_claim!(quote_body, "xfam", body.xfam); - parse_claim!(quote_body, "mr_td", body.mr_td); - parse_claim!(quote_body, "mr_config_id", body.mr_config_id); - parse_claim!(quote_body, "mr_owner", body.mr_owner); - parse_claim!(quote_body, "mr_owner_config", body.mr_owner_config); - parse_claim!(quote_body, "rtmr_0", body.rtmr_0); - parse_claim!(quote_body, "rtmr_1", body.rtmr_1); - parse_claim!(quote_body, "rtmr_2", body.rtmr_2); - parse_claim!(quote_body, "rtmr_3", body.rtmr_3); - parse_claim!(quote_body, "report_data", body.report_data); - - parse_claim!(quote_map, "header", quote_header); - parse_claim!(quote_map, "body", quote_body); - } - QuoteV5Body::Tdx15(body) => { - parse_claim!(quote_body, "tcb_svn", body.tcb_svn); - parse_claim!(quote_body, "mr_seam", body.mr_seam); - parse_claim!(quote_body, "mrsigner_seam", body.mrsigner_seam); - parse_claim!(quote_body, "seam_attributes", body.seam_attributes); - parse_claim!(quote_body, "td_attributes", body.td_attributes); - parse_claim!(quote_body, "xfam", body.xfam); - parse_claim!(quote_body, "mr_td", body.mr_td); - parse_claim!(quote_body, "mr_config_id", body.mr_config_id); - parse_claim!(quote_body, "mr_owner", body.mr_owner); - parse_claim!(quote_body, "mr_owner_config", body.mr_owner_config); - parse_claim!(quote_body, "rtmr_0", body.rtmr_0); - parse_claim!(quote_body, "rtmr_1", body.rtmr_1); - parse_claim!(quote_body, "rtmr_2", body.rtmr_2); - parse_claim!(quote_body, "rtmr_3", body.rtmr_3); - parse_claim!(quote_body, "report_data", body.report_data); - - parse_claim!(quote_body, "tee_tcb_svn2", body.tee_tcb_svn2); - parse_claim!(quote_body, "mr_servicetd", body.mr_servicetd); - parse_claim!(quote_map, "header", quote_header); - parse_claim!(quote_map, "body", quote_body); - } - } - } - } - let mut claims = Map::new(); - - parse_claim!(claims, "quote", quote_map); - parse_claim!(claims, "report_data", quote.report_data()); - parse_claim!(claims, "init_data", quote.mr_config_id()); - - let claims_str = serde_json::to_string_pretty(&claims)?; - debug!("Parsed Evidence claims map: \n{claims_str}\n"); - - Ok(Value::Object(claims) as TeeEvidenceParsedClaim) -} - -/// The quote header. It is designed to compatible with earlier versions of the quote. -#[repr(C)] -#[derive(Debug, Pread)] -pub(crate) struct QuoteHeader { - ///< 0: The version this quote structure. - pub version: [u8; 2], - ///< 2: sgx_attestation_algorithm_id_t. Describes the type of signature in the signature_data[] field. - pub att_key_type: [u8; 2], - ///< 4: Type of Trusted Execution Environment for which the Quote has been generated. - /// Supported values: 0 (SGX), 0x81(TDX) - pub tee_type: [u8; 4], - ///< 8: Reserved field. - pub reserved: [u8; 4], - ///< 12: Unique identifier of QE Vendor. - pub vendor_id: [u8; 16], - ///< 28: Custom attestation key owner data. - pub user_data: [u8; 20], -} - -impl fmt::Display for QuoteHeader { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Quote Header: - \n\tVersion:\n\t{:X?} - \n\tAttestation Signature Key Type:\n\t{:X?} - \n\tTEE Type:\n\t{:X?} - \n\tReserved:\n\t{:X?} - \n\tVendor ID:\n\t{:X?} - \n\tUser Data:\n\t{:X?}\n", - hex::encode(self.version), - hex::encode(self.att_key_type), - hex::encode(self.tee_type), - hex::encode(self.reserved), - hex::encode(self.vendor_id), - hex::encode(self.user_data) - ) - } -} - -/// SGX Report2 body -#[repr(C)] -#[derive(Debug, Pread)] -pub(crate) struct ReportBody2 { - ///< 0: TEE_TCB_SVN Array - pub tcb_svn: [u8; 16], - ///< 16: Measurement of the SEAM module - pub mr_seam: [u8; 48], - ///< 64: Measurement of a 3rd party SEAM moduleโ€™s signer (SHA384 hash). - /// The value is 0โ€™ed for Intel SEAM module - pub mrsigner_seam: [u8; 48], - ///< 112: MBZ: TDX 1.0 - pub seam_attributes: [u8; 8], - ///< 120: TD's attributes - pub td_attributes: [u8; 8], - ///< 128: TD's XFAM - pub xfam: [u8; 8], - ///< 136: Measurement of the initial contents of the TD - pub mr_td: [u8; 48], - ///< 184: Software defined ID for non-owner-defined configuration on the guest TD. e.g., runtime or OS configuration - pub mr_config_id: [u8; 48], - ///< 232: Software defined ID for the guest TD's owner - pub mr_owner: [u8; 48], - ///< 280: Software defined ID for owner-defined configuration of the guest TD, e.g., specific to the workload rather than the runtime or OS - pub mr_owner_config: [u8; 48], - ///< 328: Array of 4(TDX1: NUM_RTMRS is 4) runtime extendable measurement registers - pub rtmr_0: [u8; 48], - pub rtmr_1: [u8; 48], - pub rtmr_2: [u8; 48], - pub rtmr_3: [u8; 48], - ///< 520: Additional report data - pub report_data: [u8; 64], -} - -impl fmt::Display for ReportBody2 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Report Body: - \n\tTCB SVN:\n\t{:X?} - \n\tMRSEAM:\n\t{:X?} - \n\tMRSIGNER_SEAM:\n\t{:X?} - \n\tSEAM Attributes:\n\t{:X?} - \n\tTD Attributes:\n\t{:X?} - \n\tTD XFAM:\n\t{:X?} - \n\tMRTD:\n\t{:X?} - \n\tMRCONFIG ID:\n\t{:X?} - \n\tMROWNER:\n\t{:X?} - \n\tMROWNER_CONFIG:\n\t{:X?} - \n\tRTMR[0]:\n\t{:X?} - \n\tRTMR[1]:\n\t{:X?} - \n\tRTMR[2]:\n\t{:X?} - \n\tRTMR[3]:\n\t{:X?} - \n\tReport Data:\n\t{:X?}", - hex::encode(self.tcb_svn), - hex::encode(self.mr_seam), - hex::encode(self.mrsigner_seam), - hex::encode(self.seam_attributes), - hex::encode(self.td_attributes), - hex::encode(self.xfam), - hex::encode(self.mr_td), - hex::encode(self.mr_config_id), - hex::encode(self.mr_owner), - hex::encode(self.mr_owner_config), - hex::encode(self.rtmr_0), - hex::encode(self.rtmr_1), - hex::encode(self.rtmr_2), - hex::encode(self.rtmr_3), - hex::encode(self.report_data) - ) - } -} - -/// SGX Report2 body for quote v5 -#[repr(C)] -#[derive(Debug, Pread)] -pub(crate) struct ReportBody2v15 { - ///< 0: TEE_TCB_SVN Array - pub tcb_svn: [u8; 16], - ///< 16: Measurement of the SEAM module - pub mr_seam: [u8; 48], - ///< 64: Measurement of a 3rd party SEAM moduleโ€™s signer (SHA384 hash). - /// The value is 0โ€™ed for Intel SEAM module - pub mrsigner_seam: [u8; 48], - ///< 112: MBZ: TDX 1.0 - pub seam_attributes: [u8; 8], - ///< 120: TD's attributes - pub td_attributes: [u8; 8], - ///< 128: TD's XFAM - pub xfam: [u8; 8], - ///< 136: Measurement of the initial contents of the TD - pub mr_td: [u8; 48], - ///< 184: Software defined ID for non-owner-defined configuration on - /// the guest TD. e.g., runtime or OS configuration - pub mr_config_id: [u8; 48], - ///< 232: Software defined ID for the guest TD's owner - pub mr_owner: [u8; 48], - ///< 280: Software defined ID for owner-defined configuration of the - /// guest TD, e.g., specific to the workload rather than the runtime or OS - pub mr_owner_config: [u8; 48], - ///< 328: Array of 4(TDX1: NUM_RTMRS is 4) runtime extendable - /// measurement registers - pub rtmr_0: [u8; 48], - pub rtmr_1: [u8; 48], - pub rtmr_2: [u8; 48], - pub rtmr_3: [u8; 48], - ///< 520: Additional report data - pub report_data: [u8; 64], - ///< 584: Array of TEE TCB SVNs (for TD preserving). - pub tee_tcb_svn2: [u8; 16], - ///< 600: If is one or more bound or pre-bound service TDs, SERVTD_HASH is - /// the SHA384 hash of the TDINFO_STRUCTs of those service TDs bound. - /// Else, SERVTD_HASH is 0. - pub mr_servicetd: [u8; 48], -} - -impl fmt::Display for ReportBody2v15 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Report Body: - \n\tTCB SVN:\n\t{:X?} - \n\tMRSEAM:\n\t{:X?} - \n\tMRSIGNER_SEAM:\n\t{:X?} - \n\tSEAM Attributes:\n\t{:X?} - \n\tTD Attributes:\n\t{:X?} - \n\tTD XFAM:\n\t{:X?} - \n\tMRTD:\n\t{:X?} - \n\tMRCONFIG ID:\n\t{:X?} - \n\tMROWNER:\n\t{:X?} - \n\tMROWNER_CONFIG:\n\t{:X?} - \n\tRTMR[0]:\n\t{:X?} - \n\tRTMR[1]:\n\t{:X?} - \n\tRTMR[2]:\n\t{:X?} - \n\tRTMR[3]:\n\t{:X?} - \n\tReport Data:\n\t{:X?} - \n\tTEE TCB SVN2:\n\t{:X?} - \n\tMR SERVICETD:\n\t{:X?}", - hex::encode(self.tcb_svn), - hex::encode(self.mr_seam), - hex::encode(self.mrsigner_seam), - hex::encode(self.seam_attributes), - hex::encode(self.td_attributes), - hex::encode(self.xfam), - hex::encode(self.mr_td), - hex::encode(self.mr_config_id), - hex::encode(self.mr_owner), - hex::encode(self.mr_owner_config), - hex::encode(self.rtmr_0), - hex::encode(self.rtmr_1), - hex::encode(self.rtmr_2), - hex::encode(self.rtmr_3), - hex::encode(self.report_data), - hex::encode(self.tee_tcb_svn2), - hex::encode(self.mr_servicetd) - ) - } -} - -#[repr(u16)] -#[derive(Debug)] -pub(crate) enum QuoteV5Type { - TDX10 = 2, - TDX15 = 3, -} - -impl fmt::Display for QuoteV5Type { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - QuoteV5Type::TDX10 => writeln!(f, "Quote v5 Type: TDX 1.0"), - QuoteV5Type::TDX15 => writeln!(f, "Quote v5 Type: TDX 1.5"), - } - } -} - -impl QuoteV5Type { - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() < 2 { - bail!("parse QuoteV5 Type failed. Bytes length < 2 bytes"); - } - let mut r#type: [u8; 2] = [0; 2]; - r#type.copy_from_slice(&bytes[0..2]); - let r#type = u16::from_le_bytes(r#type); - let r#type = match r#type { - 2 => QuoteV5Type::TDX10, - 3 => QuoteV5Type::TDX15, - others => bail!("parse QuoteV5 Type failed. {others} not defined."), - }; - - Ok(r#type) - } - - pub fn as_bytes(&self) -> [u8; 2] { - // The unsafe here is ok as it is marked as repr(u16) - unsafe { - let raw_value: u16 = *(self as *const QuoteV5Type as *const u16); - raw_value.to_ne_bytes() - } - } -} - -#[derive(Debug)] -pub(crate) enum QuoteV5Body { - Tdx10(ReportBody2), - Tdx15(ReportBody2v15), -} - -impl fmt::Display for QuoteV5Body { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - QuoteV5Body::Tdx10(body) => write!(f, "{}", body), - QuoteV5Body::Tdx15(body) => write!(f, "{}", body), - } - } -} - -#[derive(Debug)] -pub(crate) enum Quote { - /// TD Quote Payload(Version 4) - /// First 632 bytes of TD Quote - /// Excluding the signature data attached at the end of the Quote. - /// - /// Refer to: https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/master/QuoteGeneration/quote_wrapper/common/inc/sgx_quote_4.h#L141 - V4 { - header: QuoteHeader, - body: ReportBody2, - }, - - /// TD Quote Payload(Version 5) - /// First 638 bytes of TD Quote - /// Excluding the signature data attached at the end of the Quote. - /// - /// Refer to: https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/master/QuoteGeneration/quote_wrapper/common/inc/sgx_quote_5.h#L106 - V5 { - header: QuoteHeader, - r#type: QuoteV5Type, - size: [u8; 4], - body: QuoteV5Body, - }, -} - -macro_rules! body_field { - ($r: ident) => { - pub fn $r(&self) -> &[u8] { - match self { - Quote::V4 { body, .. } => &body.$r, - Quote::V5 { body, .. } => match body { - QuoteV5Body::Tdx10(body) => &body.$r, - QuoteV5Body::Tdx15(body) => &body.$r, - }, - } - } - }; -} - -impl Quote { - body_field!(report_data); - body_field!(mr_config_id); - body_field!(mr_td); - body_field!(rtmr_0); - body_field!(rtmr_1); - body_field!(rtmr_2); - body_field!(rtmr_3); -} - -impl fmt::Display for Quote { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Quote::V4 { header, body } => write!(f, "TD Quote (V4):\n{header}\n{body}\n"), - Quote::V5 { - header, - r#type, - size, - body, - } => write!( - f, - "TD Quote (V5):\n{header}\n{type}\n{}\n{body}\n", - hex::encode(size) - ), - } - } -} - -pub const QUOTE_HEADER_SIZE: usize = 48; - -pub(crate) fn parse_tdx_quote(quote_bin: &[u8]) -> Result { - let quote_header = "e_bin[..QUOTE_HEADER_SIZE]; - let header = quote_header - .pread::(0) - .map_err(|e| anyhow!("Parse TD quote header failed: {:?}", e))?; - - match header.version { - [4, 0] => { - let body: ReportBody2 = quote_bin - .pread::(QUOTE_HEADER_SIZE) - .map_err(|e| anyhow!("Parse TD quote v4 body failed: {:?}", e))?; - Ok(Quote::V4 { header, body }) - } - [5, 0] => { - let r#type = QuoteV5Type::from_bytes( - "e_bin - [QUOTE_HEADER_SIZE..QUOTE_HEADER_SIZE + std::mem::size_of::()], - )?; - let mut size: [u8; 4] = [0; 4]; - size.copy_from_slice( - "e_bin[QUOTE_HEADER_SIZE + std::mem::size_of::() - ..QUOTE_HEADER_SIZE - + std::mem::size_of::() - + std::mem::size_of::<[u8; 4]>()], - ); - match r#type { - QuoteV5Type::TDX10 => { - let offset = QUOTE_HEADER_SIZE - + std::mem::size_of::() - + std::mem::size_of::<[u8; 4]>(); - let body: ReportBody2 = quote_bin - .pread::(offset) - .map_err(|e| anyhow!("Parse TD quote v5 TDX1.0 body failed: {:?}", e))?; - Ok(Quote::V5 { - header, - r#type, - size, - body: QuoteV5Body::Tdx10(body), - }) - } - QuoteV5Type::TDX15 => { - let offset = QUOTE_HEADER_SIZE - + std::mem::size_of::() - + std::mem::size_of::<[u8; 4]>(); - let body: ReportBody2v15 = quote_bin - .pread::(offset) - .map_err(|e| anyhow!("Parse TD quote v5 TDX1.5 body failed: {:?}", e))?; - Ok(Quote::V5 { - header, - r#type, - size, - body: QuoteV5Body::Tdx15(body), - }) - } - } - } - _ => Err(anyhow!("Quote version not defined.")), - } -} - -pub fn extend_claim_with_tpm_quote( - claim: &mut TeeEvidenceParsedClaim, - quote: &TpmQuote, -) -> Result<()> { - let Value::Object(ref mut map) = claim else { - bail!("failed to extend the claim, not an object"); - }; - - let mut tpm_values = serde_json::Map::new(); - for (i, pcr) in quote.pcrs_sha256().enumerate() { - tpm_values.insert(format!("pcr{:02}", i), Value::String(hex::encode(pcr))); - } - debug!("extending claim with TPM quote: {:#?}", tpm_values); - map.insert("tpm".to_string(), Value::Object(tpm_values)); - - Ok(()) -} diff --git a/crates/enclave-server/src/utils/test_utils.rs b/crates/enclave-server/src/utils/test_utils.rs deleted file mode 100644 index b7605a50..00000000 --- a/crates/enclave-server/src/utils/test_utils.rs +++ /dev/null @@ -1,117 +0,0 @@ -use kbs_types::HashAlgorithm; -use seismic_enclave::get_unsecure_sample_secp256k1_pk; -use seismic_enclave::request_types::AttestationEvalEvidenceRequest; -use std::fs::{self, File}; -use std::io::Write; -use std::io::{self, Read}; -use std::net::TcpListener; -use std::os::unix::fs::PermissionsExt; -use std::path::Path; - -/// Checks if the current user has root (sudo) privileges. -/// -/// This function runs the `id -u` command, which returns the current user's ID. -/// In Unix-like systems, the user ID of the root user is `0`. The function checks -/// if the output of the `id -u` command is `"0"`, indicating that the user is running -/// as root (with sudo privileges). -/// -/// # Returns -/// -/// - `true`: If the user has root privileges (user ID is `0`). -/// - `false`: If the user does not have root privileges. -pub fn is_sudo() -> bool { - use std::process::Command; - - // Run the "id -u" command to check the user ID - let output = Command::new("id") - .arg("-u") - .output() - .expect("Failed to execute id command"); - - // Convert the output to a string and trim any whitespace - let user_id = String::from_utf8(output.stdout).unwrap().trim().to_string(); - - // Check if the user ID is 0 (which means the user is root) - user_id == "0" -} - -/// reads a vector of the form [1, 2, 3, 4] from a txt file -/// It takes the file path as a String and returns a Vec -pub fn read_vector_txt(path: String) -> io::Result> { - // Open the file - let mut file = File::open(path)?; - - // Read the file contents into a string - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - - // Remove the brackets and split the string into individual numbers - let trimmed = contents.trim_matches(|c| c == '[' || c == ']').trim(); - - // Convert the split numbers into a vector of u8 - let vec: Vec = trimmed - .split(',') - .map(|s| s.trim().parse().expect("Failed to parse number")) - .collect(); - - // Return the vector - Ok(vec) -} - -pub fn get_random_port() -> u16 { - TcpListener::bind("127.0.0.1:0") // 0 means OS assigns a free port - .expect("Failed to bind to a port") - .local_addr() - .unwrap() - .port() -} - -/// A mock attestation evaluation request for testing -/// Based on a saved sample attestation file -/// attests to a public secp256k1 key from an AzTdxVtpm machine -pub fn pub_key_eval_request() -> AttestationEvalEvidenceRequest { - use seismic_enclave::request_types::Data; - let evidence_bytes = read_vector_txt("../../examples/az_tdx_key_att.txt".to_string()).unwrap(); - let evidence_str = String::from_utf8(evidence_bytes).unwrap(); - let evidence = serde_json::from_str(&evidence_str).unwrap(); - let req = AttestationEvalEvidenceRequest { - evidence, - tee: kbs_types::Tee::AzTdxVtpm, - runtime_data: Some(Data::Raw( - get_unsecure_sample_secp256k1_pk().serialize().to_vec(), - )), - runtime_data_hash_algorithm: Some(HashAlgorithm::Sha256), - policy_ids: vec!["allow".to_string()], - }; - // println!("pub_key_eval_request: {:?}", req); - req -} - -//reads the first n bytes of a file -// useful for checking file equality -pub fn read_first_n_bytes(file_path: &str, n: usize) -> Result, anyhow::Error> { - let mut file = File::open(file_path)?; - let mut buffer = vec![0; n]; // Allocate a buffer of size `n` - let bytes_read = file.read(&mut buffer)?; - - buffer.truncate(bytes_read); // Truncate buffer in case file is smaller than `n` - Ok(buffer) -} - -// Function to generate a dummy database file -pub fn generate_dummy_file(path: &Path, size: usize) -> std::io::Result<()> { - let mut file = File::create(path)?; - file.write_all(&vec![0u8; size])?; // Fill with zero bytes - Ok(()) -} - -// simulates the db file being owned by root by settong permissions to 000 -pub fn restrict_file_permissions(path: &Path) -> std::io::Result<()> { - let perms = fs::Permissions::from_mode(0o000); // owner cannot access, sudo can still bypass permissions checks - fs::set_permissions(path, perms) -} - -pub fn unrestrict_file_permissions(path: &Path) -> std::io::Result<()> { - let perms = fs::Permissions::from_mode(0o644); - fs::set_permissions(path, perms) -} diff --git a/crates/enclave-server/tests/integration/booter.rs b/crates/enclave-server/tests/integration/booter.rs deleted file mode 100644 index d2e19a0e..00000000 --- a/crates/enclave-server/tests/integration/booter.rs +++ /dev/null @@ -1,50 +0,0 @@ -use seismic_enclave::rpc::EnclaveApiServer; -use seismic_enclave::AttestationEvalEvidenceRequest; -use seismic_enclave::ShareRootKeyRequest; -use seismic_enclave_server::key_manager::KeyManager; -use seismic_enclave_server::server::boot::Booter; -use seismic_enclave_server::server::engine::engine_mock_booted; -use seismic_enclave_server::server::engine::AttestationEngine; -use seismic_enclave_server::utils::test_utils::is_sudo; -use seismic_enclave_server::utils::test_utils::pub_key_eval_request; -use serial_test::serial; - -#[cfg(not(feature = "supervisorctl"))] -use seismic_enclave_server::utils::service::reth_is_running; -#[cfg(feature = "supervisorctl")] -use seismic_enclave_server::utils::supervisorctl::reth_is_running; - -// This test expects that the booter's attestation is already allowed by the upgrade operator -// This can be set up by running the test_multisig_upgrade_operator_workflow test in the enclave-contract crate -#[serial(attestation_agent)] -#[tokio::test] -async fn test_boot_share_root_key() { - // Check the starting conditions are as expected - if !is_sudo() { - panic!("test_boot_share_root_key: skipped (requires sudo privileges)"); - } - assert!(reth_is_running(), "Test startup error: Reth is not running"); - - // Test the booter with the canonical deployment - let enclave_engine: AttestationEngine = engine_mock_booted().await; - let new_node_booter = Booter::mock(); - let eval_context: AttestationEvalEvidenceRequest = pub_key_eval_request(); - assert_eq!( - seismic_enclave::request_types::Data::Raw(new_node_booter.pk().serialize().to_vec()), - eval_context.clone().runtime_data.unwrap(), - "test misconfigured, attestation should be of the new booter's public key" - ); - let resp = enclave_engine - .boot_share_root_key(ShareRootKeyRequest { - evidence: eval_context.evidence, - tee: eval_context.tee, - retriever_pk: new_node_booter.pk(), - }) - .await - .unwrap(); - let key_plaintext = new_node_booter.process_share_response(resp).unwrap(); // erroring due to mismatch - assert!( - key_plaintext == [0u8; 32], - "root key does not match expected mock value" - ); -} diff --git a/crates/enclave-server/tests/integration/integration.rs b/crates/enclave-server/tests/integration/integration.rs new file mode 100644 index 00000000..cb3cc366 --- /dev/null +++ b/crates/enclave-server/tests/integration/integration.rs @@ -0,0 +1,66 @@ +use std::time::Duration; + +use crate::utils::get_args; +use jsonrpsee::http_client::HttpClientBuilder; +use seismic_enclave::TdxQuoteRpcClient as _; +use seismic_enclave_server::utils::{init_tracing, is_sudo}; + +// This test expects that the booter's attestation is already allowed by the upgrade operator +// This can be set up by running the test_multisig_upgrade_operator_workflow test in the enclave-contract crate +#[serial_test::serial(attestation_agent)] +#[tokio::test] +async fn test_boot_share_root_key() { + init_tracing(); + // Check the starting conditions are as expected + if !is_sudo() { + panic!("test_boot_share_root_key: skipped (requires sudo privileges)"); + } + + // Start first enclave as genesis node + let args1 = get_args(0, true, Default::default()); + let enclave_one_url = format!("http://localhost:{}", args1.port); + let node1_handle = tokio::spawn(args1.start()); + + // sleep some time to allow him to start up + tokio::time::sleep(Duration::from_secs(1)).await; + + // start second enclave with node1 as his peer + let args2 = get_args(1, false, vec![enclave_one_url.clone()]); + let enclave_two_url = format!("http://localhost:{}", args2.port); + let node2_handle = tokio::spawn(args2.start()); + // sleep some time to allow them to share keys + tokio::time::sleep(Duration::from_secs(10)).await; + + // Get keys from both and make sure they match + let client1 = HttpClientBuilder::default() + .build(enclave_one_url) + .expect("Unable to connect to enclave 1"); + let client2 = HttpClientBuilder::default() + .build(enclave_two_url) + .expect("Unable to connect to enclave 2"); + + let keys1 = client1 + .get_purpose_keys(0) + .await + .expect("Unable to get purpose keys"); + let keys2 = client2 + .get_purpose_keys(0) + .await + .expect("Unable to get purpose keys"); + + // Ensure they are the same from both nodes + assert_eq!(keys1.rng_keypair.secret, keys2.rng_keypair.secret); + assert_eq!(keys1.snapshot_key_bytes, keys2.snapshot_key_bytes); + assert_eq!(keys1.tx_io_sk, keys2.tx_io_sk); + + // test the other endpoints + assert_eq!(client1.health_check().await.unwrap(), "OK".to_string()); + let evidence = client1.get_attestation_evidence().await.unwrap(); + client1 + .eval_attestation_evidence(evidence.hcl_report, evidence.quote) + .await + .unwrap(); + + node1_handle.abort(); + node2_handle.abort(); +} diff --git a/crates/enclave-server/tests/integration/main.rs b/crates/enclave-server/tests/integration/main.rs index 1dad45ac..6466e7be 100644 --- a/crates/enclave-server/tests/integration/main.rs +++ b/crates/enclave-server/tests/integration/main.rs @@ -1,10 +1,4 @@ #[cfg(test)] -mod server; - -#[cfg(test)] -mod snapshot; - -#[cfg(test)] -mod booter; +mod integration; mod utils; diff --git a/crates/enclave-server/tests/integration/server.rs b/crates/enclave-server/tests/integration/server.rs deleted file mode 100644 index 8e56645a..00000000 --- a/crates/enclave-server/tests/integration/server.rs +++ /dev/null @@ -1,151 +0,0 @@ -// use seismic_enclave::auth::JwtSecret; -use seismic_enclave::boot_genesis_streamlined_async; -use seismic_enclave::client::rpc::BuildableServer; -use seismic_enclave::client::{EnclaveClient, EnclaveClientBuilder, ENCLAVE_DEFAULT_ENDPOINT_IP}; -use seismic_enclave::request_types::AttestationGetEvidenceRequest; -use seismic_enclave::request_types::GetPurposeKeysRequest; -use seismic_enclave::rpc::EnclaveApiClient; -use seismic_enclave_server::attestation::simple_att_serv_config; -use seismic_enclave_server::attestation::SeismicAttestationAgent; -use seismic_enclave_server::key_manager::{KeyManager, KeyManagerBuilder}; -use seismic_enclave_server::server::{init_tracing, EnclaveServer}; - -use serial_test::serial; -use std::net::SocketAddr; -use std::net::TcpListener; -use std::thread::sleep; -use std::time::Duration; - -pub fn is_sudo() -> bool { - use std::process::Command; - - // Run the "id -u" command to check the user ID - let output = Command::new("id") - .arg("-u") - .output() - .expect("Failed to execute id command"); - - // Convert the output to a string and trim any whitespace - let user_id = String::from_utf8(output.stdout).unwrap().trim().to_string(); - - // Check if the user ID is 0 (which means the user is root) - user_id == "0" -} - -pub fn get_random_port() -> u16 { - TcpListener::bind("127.0.0.1:0") // 0 means OS assigns a free port - .expect("Failed to bind to a port") - .local_addr() - .unwrap() - .port() -} - -async fn test_get_purpose_keys(client: &EnclaveClient) { - client - .get_purpose_keys(GetPurposeKeysRequest { epoch: 0 }) - .await - .unwrap(); -} - -async fn test_health_check(client: &EnclaveClient) { - let response = client.health_check().await.unwrap(); - assert!(response.status_ok, "Status OK"); -} - -async fn test_attestation_get_evidence(client: &EnclaveClient) { - let runtime_data = "nonce".as_bytes(); // Example runtime data - let evidence_request = AttestationGetEvidenceRequest { - runtime_data: runtime_data.to_vec(), - }; - - // Call the handler - let res = client - .get_attestation_evidence(evidence_request) - .await - .unwrap(); - - // Ensure the response is not empty - assert!(!res.evidence.is_empty()); -} - -async fn test_attestation_eval_evidence(client: &EnclaveClient) { - // Mock a valid AttestationEvalEvidenceRequest - let eval_request = seismic_enclave_server::utils::test_utils::pub_key_eval_request(); - - let response = client - .eval_attestation_evidence(eval_request) - .await - .unwrap(); - - assert!(response.claims.is_some()); -} - -// async fn test_misconfigured_auth_secret(ip: IpAddr, port: u16) { -// let rand_auth_secret = JwtSecret::random(); -// let client = EnclaveClientBuilder::new() -// .auth_secret(rand_auth_secret) -// .ip(ip.to_string()) -// .port(port) -// .timeout(Duration::from_secs(5)) -// .build() -// .map_err(|e| { -// anyhow!( -// "test_misconfigured_auth_secret Failed to build client: {:?}", -// e -// ) -// }) -// .unwrap(); -// let response = client.health_check().await; -// assert!( -// response.is_err(), -// "client should not be able to connect to server with wrong auth secret" -// ); -// } - -#[tokio::test] -#[serial(attestation_agent, attestation_service)] -async fn test_server_requests() { - init_tracing(); - // handle set up permissions - if !is_sudo() { - tracing::error!("test_server_requests: skipped (requires sudo privileges)"); - return; - } - - // spawn a seperate thread for the server, otherwise the test will hang - let port = get_random_port(); // rand port for test parallelization - let addr = SocketAddr::from((ENCLAVE_DEFAULT_ENDPOINT_IP, port)); - let kp = KeyManagerBuilder::build_mock().unwrap(); - let att_serv_config = simple_att_serv_config(); - - let seismic_attestation_agent = SeismicAttestationAgent::new(None, att_serv_config).await; - // let auth_secret = JwtSecret::random(); - let _server_handle = EnclaveServer::::new( - addr, - kp, - seismic_attestation_agent, - // auth_secret, - ) - .await - .unwrap() - .start() - .await - .unwrap(); - sleep(Duration::from_secs(4)); - - let client = EnclaveClientBuilder::new() - // .auth_secret(auth_secret) - .ip(ENCLAVE_DEFAULT_ENDPOINT_IP.to_string()) - .port(port) - .timeout(Duration::from_secs(5)) - .build() - .unwrap(); - - boot_genesis_streamlined_async(&client).await.unwrap(); - - test_health_check(&client).await; - test_get_purpose_keys(&client).await; - test_attestation_get_evidence(&client).await; - test_attestation_eval_evidence(&client).await; - // test_misconfigured_auth_secret(addr.ip(), addr.port()).await; -} diff --git a/crates/enclave-server/tests/integration/snapshot.rs b/crates/enclave-server/tests/integration/snapshot.rs deleted file mode 100644 index 43ac9ed1..00000000 --- a/crates/enclave-server/tests/integration/snapshot.rs +++ /dev/null @@ -1,205 +0,0 @@ -use crate::utils::{get_balance, send_eth}; -use enclave_contract::{ANVIL_ALICE_SK, ANVIL_BOB_SK}; - -use seismic_enclave::boot_genesis_streamlined_async; -use seismic_enclave::request_types::{ - PrepareEncryptedSnapshotRequest, RestoreFromEncryptedSnapshotRequest, -}; -use seismic_enclave::rpc::SyncEnclaveApiClient; -use seismic_enclave::{ - EnclaveClientBuilder, ENCLAVE_DEFAULT_ENDPOINT_IP, ENCLAVE_DEFAULT_ENDPOINT_PORT, -}; -use seismic_enclave_server::utils::test_utils::is_sudo; - -#[cfg(not(feature = "supervisorctl"))] -use seismic_enclave_server::utils::service::reth_is_running; -#[cfg(feature = "supervisorctl")] -use seismic_enclave_server::utils::supervisorctl::reth_is_running; - -use seismic_enclave_server::snapshot::{DATA_DISK_DIR, RETH_DATA_DIR, SNAPSHOT_DIR, SNAPSHOT_FILE}; -use std::fs; -use std::net::SocketAddr; -use std::path::Path; -use std::thread::sleep; -use std::time::Duration; - -/// Prints a string to standard output and immediately flushes the output buffer. -/// Useful to see prints immediately during long-running Cargo tests. -pub fn print_flush>(s: S) { - use std::io::Write; - let stdout = std::io::stdout(); - let mut handle = stdout.lock(); // lock ensures safe writing - write!(handle, "{}", s.as_ref()).unwrap(); - handle.flush().unwrap(); -} - -// This test assumes the enclave-server and reth are both running -// with the relevant service manager -#[tokio::test(flavor = "multi_thread")] -pub async fn test_snapshot_integration_handlers() -> Result<(), anyhow::Error> { - print_flush("Running test_snapshot_integration_handlers. Expected runtime is ~90 sec\n"); - // Check the starting conditions are as expected - assert!(is_sudo(), "Must be run as sudo"); - assert!( - Path::new(format!("{}/db/mdbx.dat", RETH_DATA_DIR).as_str()).exists(), - "Test startup error: Reth mbdx.dat missing or misconfigured. Expected to find it at {}/db/mdbx.dat", RETH_DATA_DIR - ); - assert!( - !Path::new(format!("{}/{}.enc", SNAPSHOT_DIR, SNAPSHOT_FILE).as_str()).exists(), - "Test startup error: Encrypted snapshot already exists" - ); - assert!( - Path::new(DATA_DISK_DIR).is_dir(), - "Test startup error: DATA_DISK_DIR missing or misconfigured. Expected to find a directory at {}", DATA_DISK_DIR - ); - assert!(reth_is_running(), "Test startup error: Reth is not running"); - - // Set up clients - let enclave_addr = - SocketAddr::from((ENCLAVE_DEFAULT_ENDPOINT_IP, ENCLAVE_DEFAULT_ENDPOINT_PORT)); - let enclave_client = EnclaveClientBuilder::new() - .ip(enclave_addr.ip().to_string()) - .port(enclave_addr.port()) - .timeout(Duration::from_secs(120)) // snapshot takes a while - .build() - .unwrap(); - let reth_rpc = "http://localhost:8545"; - - // Boot genesis so we can interact with the enclaver-server - boot_genesis_streamlined_async(&enclave_client) - .await - .unwrap(); - - // Get Alice's address from her private key - let alice_signer: alloy::signers::local::PrivateKeySigner = ANVIL_ALICE_SK.parse().unwrap(); - let alice_address = alice_signer.address(); - - // Check initial balance - print_flush("Checking initial account balance...\n"); - let initial_balance = get_balance(alice_address, reth_rpc) - .await - .map_err(|e| anyhow::anyhow!("failed to get initial balance: {:?}", e))?; - print_flush(format!("Initial balance: {} wei\n", initial_balance)); - - // Send ETH transactions to trigger the reth persistence threshold - // and have the first block save to disk - // based on the assumption that reth is run with the --dev.block-max-transactions 1 flag - print_flush("Sending ETH transactions for persistence threshold...\n"); - // Send ETH from Alice to zero address (burning ETH) - burn a small amount - let burn_amount = 1_000_000_000u128; // 1 gwei - send_eth( - ANVIL_ALICE_SK, - alloy::primitives::Address::ZERO, - burn_amount, - reth_rpc, - ) - .await - .map_err(|e| anyhow::anyhow!("failed to send ETH to zero address: {:?}", e))?; - // make two more transactions to trigger the persistence threshold for Alice - send_eth( - ANVIL_BOB_SK, - alloy::primitives::Address::ZERO, - burn_amount, - reth_rpc, - ) - .await - .map_err(|e| anyhow::anyhow!("failed to send ETH to zero address: {:?}", e))?; - send_eth( - ANVIL_BOB_SK, - alloy::primitives::Address::ZERO, - burn_amount, - reth_rpc, - ) - .await - .map_err(|e| anyhow::anyhow!("failed to send ETH to zero address: {:?}", e))?; - print_flush("Sent ETH. Starting to prepare snapshot and restore\n"); - - sleep(Duration::from_secs(2)); - - // Create encrypted snapshot - let prepare_req = PrepareEncryptedSnapshotRequest {}; - let prepare_resp = enclave_client - .prepare_encrypted_snapshot(prepare_req) - .unwrap(); - assert!( - prepare_resp.success, - "prepare_encrypted_snapshot failed: {}", - prepare_resp.error - ); - assert!(Path::new(format!("{}/{}.enc", DATA_DISK_DIR, SNAPSHOT_FILE).as_str()).exists()); - assert!(reth_is_running()); - - // Delete files that will be recovered - fs::remove_dir_all(RETH_DATA_DIR).unwrap(); - - // Restore from encrypted snapshot - assert!(!Path::new(format!("{}/db/mdbx.dat", RETH_DATA_DIR).as_str()).exists()); - assert!(Path::new(format!("{}/{}.enc", DATA_DISK_DIR, SNAPSHOT_FILE).as_str()).exists()); - let restore_req = RestoreFromEncryptedSnapshotRequest {}; - let restore_resp = enclave_client - .restore_from_encrypted_snapshot(restore_req) - .unwrap(); - assert!( - restore_resp.success, - "restore_from_encrypted_snapshot failed: {}", - restore_resp.error - ); - assert!(Path::new(format!("{}/db/mdbx.dat", RETH_DATA_DIR).as_str()).exists()); - assert!(reth_is_running()); - - // Check that the chain data is recovered - // E.g. by checking that Alice's balance is lower than the initial balance - let sleep_sec = 45; // 30 sec is not enough sometimes - print_flush("Finished restoring. Checking account balance..."); - print_flush(format!("Sleeping for {} seconds... \n", sleep_sec)); - sleep(Duration::from_secs(sleep_sec)); // wait to avoid a connection refused error - - // Check final balance - let final_balance = get_balance(alice_address, reth_rpc) - .await - .map_err(|e| anyhow::anyhow!("failed to get final balance: {:?}", e))?; - print_flush(format!("Final balance: {} wei\n", final_balance)); - - // Verify that the balance decreased (due to burning ETH) - assert!( - final_balance < initial_balance, - "Balance should have decreased after burning ETH. Initial: {}, Final: {}", - initial_balance, - final_balance - ); - - Ok(()) -} - -///////////////////////////////////////////////////////////////////////////////// -/// Manual testing helpers -/// Useful for checking things work across machines -///////////////////////////////////////////////////////////////////////////////// - -#[tokio::test] -pub async fn run_restore() -> Result<(), anyhow::Error> { - assert!(is_sudo(), "Must be run as sudo"); - - let enclave_addr = - SocketAddr::from((ENCLAVE_DEFAULT_ENDPOINT_IP, ENCLAVE_DEFAULT_ENDPOINT_PORT)); - let enclave_client = EnclaveClientBuilder::new() - .ip(enclave_addr.ip().to_string()) - .port(enclave_addr.port()) - .build() - .unwrap(); - - assert!(reth_is_running()); - assert!(Path::new(format!("{}/{}.enc", DATA_DISK_DIR, SNAPSHOT_FILE).as_str()).exists()); - let restore_req = RestoreFromEncryptedSnapshotRequest {}; - let restore_resp = enclave_client - .restore_from_encrypted_snapshot(restore_req) - .unwrap(); - assert!( - restore_resp.success, - "Restore failed: {}", - restore_resp.error - ); - // restore_from_encrypted_snapshot(RETH_DATA_DIR, DATA_DISK_DIR, SNAPSHOT_DIR, SNAPSHOT_FILE)?; - assert!(reth_is_running()); - Ok(()) -} diff --git a/crates/enclave-server/tests/integration/utils.rs b/crates/enclave-server/tests/integration/utils.rs index 849220ce..5d79b557 100644 --- a/crates/enclave-server/tests/integration/utils.rs +++ b/crates/enclave-server/tests/integration/utils.rs @@ -1,91 +1,13 @@ -use alloy::{ - network::{EthereumWallet, TransactionBuilder}, - primitives::U256, - providers::{Provider, ProviderBuilder}, - rpc::types::TransactionRequest, - signers::local::PrivateKeySigner, -}; - -/// Sends ETH from one account to another to trigger transaction persistence. -/// -/// # Arguments -/// -/// * `from_sk` - The private key of the sender account. -/// * `to_address` - The address of the recipient. -/// * `amount_wei` - The amount to send in wei. -/// * `rpc` - A string slice representing the RPC URL of the Ethereum node. -/// -/// # Returns -/// -/// * `Result<(), anyhow::Error>` - Returns success or an `anyhow::Error` if an error occurs. -pub async fn send_eth( - from_sk: &str, - to_address: alloy::primitives::Address, - amount_wei: u128, - rpc: &str, -) -> Result<(), anyhow::Error> { - // Set up signer with the provided sk - let signer: PrivateKeySigner = from_sk.parse().unwrap(); - let wallet = EthereumWallet::from(signer.clone()); - let rpc_url = reqwest::Url::parse(rpc).unwrap(); - let provider = ProviderBuilder::new().wallet(wallet).connect_http(rpc_url); - - // Get the sender's address - let from_address = signer.address(); - - // Get current gas price and nonce - let gas_price = provider.get_gas_price().await?; - let nonce = provider.get_transaction_count(from_address).await?; - - // Create transaction request - let tx = TransactionRequest::default() - .with_to(to_address) - .with_value(U256::from(amount_wei)) - .with_gas_price(gas_price) - .with_nonce(nonce) - .with_gas_limit(21_000u64); // Standard ETH transfer gas limit - - // Send transaction - let pending_tx = provider - .send_transaction(tx) - .await - .map_err(|e| anyhow::anyhow!("Failed to send ETH: {:?}", e))?; - - // Wait for the transaction to be mined - let _receipt = pending_tx - .get_receipt() - .await - .map_err(|e| anyhow::anyhow!("Failed to get transaction receipt: {:?}", e))?; - - println!( - "ETH transfer completed: {} wei from {:?} to {:?}", - amount_wei, from_address, to_address - ); - - Ok(()) -} - -/// Gets the balance of an account in wei. -/// -/// # Arguments -/// -/// * `address` - The address of the account to check. -/// * `rpc` - A string slice representing the RPC URL of the Ethereum node. -/// -/// # Returns -/// -/// * `Result` - Returns the balance in wei if successful, or an `anyhow::Error` if an error occurs. -pub async fn get_balance( - address: alloy::primitives::Address, - rpc: &str, -) -> Result { - let rpc_url = reqwest::Url::parse(rpc).unwrap(); - let provider = ProviderBuilder::new().connect_http(rpc_url); - - let balance = provider - .get_balance(address) - .await - .map_err(|e| anyhow::anyhow!("Failed to get balance: {:?}", e))?; - - Ok(balance.try_into().unwrap()) +use seismic_enclave_server::Args; + +pub fn get_args(n: u16, genesis_node: bool, peers: Vec) -> Args { + let port = 7878 + n; + Args { + ip: "0.0.0.0".to_string(), + port, + genesis_node, + peers, + reth_rpc_url: "0.0.0.0:8545".to_string(), + mock: false, + } } diff --git a/crates/enclave/Cargo.toml b/crates/enclave/Cargo.toml index a533fc75..e7a02c69 100644 --- a/crates/enclave/Cargo.toml +++ b/crates/enclave/Cargo.toml @@ -1,36 +1,20 @@ - - [package] name = "seismic-enclave" -version.workspace = true edition.workspace = true +version.workspace = true repository.workspace = true homepage.workspace = true authors.workspace = true license.workspace = true readme.workspace = true -description = "Tools for building and interfacing with the Seismic enclave" - -[lib] -name = "seismic_enclave" -path = "src/lib.rs" [dependencies] -seismic-enclave-derive.workspace = true -aes-gcm.workspace = true anyhow.workspace = true -hkdf.workspace = true +aes-gcm.workspace = true jsonrpsee.workspace = true -kbs-types.workspace = true -rand.workspace = true +hkdf.workspace = true schnorrkel.workspace = true secp256k1.workspace = true -serde.workspace = true -serde_json.workspace = true sha2.workspace = true -tracing.workspace = true -tokio.workspace = true -base64.workspace = true - -[dev-dependencies] -reqwest.workspace = true \ No newline at end of file +rand.workspace = true +serde.workspace = true diff --git a/crates/enclave/derive/Cargo.toml b/crates/enclave/derive/Cargo.toml deleted file mode 100644 index ab643b26..00000000 --- a/crates/enclave/derive/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "seismic-enclave-derive" -version.workspace = true -edition.workspace = true -repository.workspace = true -homepage.workspace = true -authors.workspace = true -license.workspace = true -readme.workspace = true -description = "Tools for building and interfacing with the Seismic enclave" - -[lib] -proc-macro = true - -[dependencies] -proc-macro2.workspace = true -quote.workspace = true -syn = { version = "2.0", features = ["full"] } diff --git a/crates/enclave/derive/src/lib.rs b/crates/enclave/derive/src/lib.rs deleted file mode 100644 index 0bff69e6..00000000 --- a/crates/enclave/derive/src/lib.rs +++ /dev/null @@ -1,53 +0,0 @@ -use proc_macro::TokenStream; -use quote::quote; -use syn::{parse_macro_input, ItemTrait, ReturnType, TraitItem}; - -/// Derive a sync client from an async rpc trait. -#[proc_macro_attribute] -pub fn derive_sync_client_trait(_attr: TokenStream, item: TokenStream) -> TokenStream { - let input = parse_macro_input!(item as ItemTrait); - let trait_name = &input.ident; - let sync_trait_name = syn::Ident::new(&format!("Sync{}Client", trait_name), trait_name.span()); - - let methods = input.items.iter().filter_map(|item| { - if let TraitItem::Fn(m) = item { - let sig = &m.sig; - let method_name = &sig.ident; - let inputs = &sig.inputs; - match &sig.output { - - ReturnType::Type(_, ty) =>{ - if let syn::Type::Path(type_path) = &**ty { - if let Some(segment) = type_path.path.segments.last() { - if segment.ident == "RpcResult" { - if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { - if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() { - return Some(quote! { - fn #method_name(#inputs) -> Result<#inner_ty, jsonrpsee::core::client::Error>; - }) - } - } - } - } - } - panic!("Method {} has an invalid return type. Return type must be RpcResult", method_name); - } - _ => { - panic!("Method {} has an invalid return type. Return type must be RpcResult", method_name); - } - } - } else { - None - } - }); - - let expanded = quote! { - pub trait #sync_trait_name { - #(#methods)* - } - - #input // Keep the original async trait unchanged - }; - - TokenStream::from(expanded) -} diff --git a/crates/enclave/src/api.rs b/crates/enclave/src/api.rs new file mode 100644 index 00000000..e36cfef4 --- /dev/null +++ b/crates/enclave/src/api.rs @@ -0,0 +1,53 @@ +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; +use serde::{Deserialize, Serialize}; + +#[rpc(client, server)] +pub trait TdxQuoteRpc { + /// Health check endpoint that returns "OK" if service is running + #[method(name = "healthCheck")] + async fn health_check(&self) -> RpcResult; + + /// Get the secp256k1 public key + #[method(name = "getPurposeKeys")] + async fn get_purpose_keys(&self, epoch: u64) -> RpcResult; + + /// Generates attestation evidence from the attestation authority + #[method(name = "getAttestationEvidence")] + async fn get_attestation_evidence(&self) -> RpcResult; + + /// Evaluates provided attestation evidence + #[method(name = "evalAttestationEvidence")] + async fn eval_attestation_evidence(&self, hcl_report: Vec, quote: Vec) + -> RpcResult<()>; + + /// Shares the root key with an existing node + #[method(name = "boot.share_root_key")] + async fn boot_share_root_key(&self, quote: Vec) -> RpcResult; + + /// Prepares an encrypted snapshot + #[method(name = "snapshot.prepare_encrypted_snapshot")] + async fn prepare_encrypted_snapshot(&self) -> RpcResult<()>; + + /// Restores from an encrypted snapshot + #[method(name = "snapshot.restore_from_encrypted_snapshot")] + async fn restore_from_encrypted_snapshot(&self) -> RpcResult<()>; +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct GetPurposeKeysResponse { + pub tx_io_sk: secp256k1::SecretKey, + pub tx_io_pk: secp256k1::PublicKey, + pub snapshot_key_bytes: [u8; 32], + pub rng_keypair: schnorrkel::keys::Keypair, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct AttestationGetEvidenceResponse { + pub hcl_report: Vec, + pub quote: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct ShareRootKeyResponse { + pub root_key: [u8; 32], +} diff --git a/crates/enclave/src/client/booter.rs b/crates/enclave/src/client/booter.rs deleted file mode 100644 index 4842bfb8..00000000 --- a/crates/enclave/src/client/booter.rs +++ /dev/null @@ -1,20 +0,0 @@ -//! Convenience functions for booting the enclave in a streamlined way -use crate::rpc::EnclaveApiClient; -use crate::EnclaveClient; - -/// Command to boot the enclave's key manager -pub async fn boot_genesis_streamlined_async(client: &EnclaveClient) -> Result<(), anyhow::Error> { - let health_check = client.health_check().await?; - if health_check.boot_complete { - tracing::warn!("Enclave already booted. Skipping genesis boot"); - return Ok(()); - } - client.boot_genesis().await?; - client.complete_boot().await?; - Ok(()) -} - -pub fn boot_genesis_streamlined_sync(client: &EnclaveClient) -> Result<(), anyhow::Error> { - let rt = tokio::runtime::Runtime::new().unwrap(); - rt.block_on(boot_genesis_streamlined_async(client)) -} diff --git a/crates/enclave/src/client/client.rs b/crates/enclave/src/client/client.rs deleted file mode 100644 index c87ce8a5..00000000 --- a/crates/enclave/src/client/client.rs +++ /dev/null @@ -1,253 +0,0 @@ -//! A http client for interacting with a enclave server. -//! Comnstructed from a [`EnclaveClientBuilder`]. - -use anyhow::Result; -use jsonrpsee::http_client::transport::HttpBackend; -use jsonrpsee::{core::ClientError, http_client::HttpClient}; -use std::{ - future::Future, - net::{IpAddr, Ipv4Addr}, - ops::Deref, - sync::OnceLock, - time::Duration, -}; -use tokio::runtime::{Handle, Runtime}; - -use super::rpc::{EnclaveApiClient, SyncEnclaveApiClient, SyncEnclaveApiClientBuilder}; -use crate::request_types::*; - -pub const ENCLAVE_DEFAULT_ENDPOINT_IP: IpAddr = IpAddr::V4(Ipv4Addr::UNSPECIFIED); -pub const ENCLAVE_DEFAULT_ENDPOINT_PORT: u16 = 7878; -pub const ENCLAVE_DEFAULT_TIMEOUT_SECONDS: u64 = 5; -static ENCLAVE_CLIENT_RUNTIME: OnceLock = OnceLock::new(); - -/// The inner async HTTP client. -/// Useful to define here in case the HttpClient generic changes -type EnclaveHttpClient = HttpClient; - -/// Builder for [`EnclaveClient`]. -#[derive(Debug, Clone)] -pub struct EnclaveClientBuilder { - ip: Option, - port: Option, - timeout: Option, - url: Option, -} - -impl EnclaveClientBuilder { - pub fn new() -> Self { - Self { - ip: None, - port: None, - timeout: None, - url: None, - } - } - - pub fn ip(mut self, ip: impl Into) -> Self { - self.ip = Some(ip.into()); - self - } - - pub fn port(mut self, port: u16) -> Self { - self.port = Some(port); - self - } - - pub fn timeout(mut self, timeout: Duration) -> Self { - self.timeout = Some(timeout); - self - } - - pub fn url(mut self, url: impl Into) -> Self { - self.url = Some(url.into()); - self - } - - pub fn build(self) -> Result { - let url = self.url.unwrap_or_else(|| { - format!( - "http://{}:{}", - self.ip - .unwrap_or_else(|| ENCLAVE_DEFAULT_ENDPOINT_IP.to_string()), - self.port.unwrap_or(ENCLAVE_DEFAULT_ENDPOINT_PORT) - ) - }); - - let async_client: EnclaveHttpClient = jsonrpsee::http_client::HttpClientBuilder::default() - .request_timeout( - self.timeout - .unwrap_or(Duration::from_secs(ENCLAVE_DEFAULT_TIMEOUT_SECONDS)), - ) - .build(url) - .unwrap(); - - Ok(EnclaveClient::new_from_client(async_client)) - } -} - -impl Default for EnclaveClientBuilder { - fn default() -> Self { - let mut builder = EnclaveClientBuilder::new(); - - let url = format!( - "http://{}:{}", - ENCLAVE_DEFAULT_ENDPOINT_IP, ENCLAVE_DEFAULT_ENDPOINT_PORT - ); - builder = builder.url(url); - builder = builder.timeout(Duration::from_secs(5)); - builder - } -} - -impl SyncEnclaveApiClientBuilder for EnclaveClientBuilder { - type Client = EnclaveClient; - fn build(self) -> EnclaveClient { - EnclaveClientBuilder::build(self).unwrap() - } -} - -/// A client for the enclave API. -#[derive(Debug, Clone)] -pub struct EnclaveClient { - /// The inner HTTP client. - async_client: EnclaveHttpClient, - /// The runtime for the client. - handle: Handle, -} -impl Deref for EnclaveClient { - type Target = EnclaveHttpClient; - - fn deref(&self) -> &Self::Target { - &self.async_client - } -} -impl Default for EnclaveClient { - fn default() -> Self { - EnclaveClientBuilder::default().build().unwrap() - } -} - -impl EnclaveClient { - pub fn builder() -> EnclaveClientBuilder { - EnclaveClientBuilder::new() - } - - /// Create a new [`EnclaveClient`] from an [`EnclaveHttpClient`]. - pub fn new_from_client(async_client: EnclaveHttpClient) -> Self { - let handle = Handle::try_current().unwrap_or_else(|_| { - let runtime = ENCLAVE_CLIENT_RUNTIME.get_or_init(|| Runtime::new().unwrap()); - runtime.handle().clone() - }); - Self { - async_client, - handle, - } - } - - /// Block on a future with the runtime. - pub fn block_on_with_runtime(&self, future: F) -> T - where - F: Future, - { - tokio::task::block_in_place(|| self.handle.block_on(future)) - } - - /// A client enclave bade to work with the default mock server - /// Useful for testing - pub fn mock(ip: String, port: u16) -> Result { - let client = EnclaveClientBuilder::new() - .ip(ip) - .port(port) - .timeout(Duration::from_secs(ENCLAVE_DEFAULT_TIMEOUT_SECONDS)) - .build()?; - Ok(client) - } -} - -// impl the SyncEnclaveApiClient trait for EnclaveClient based on the [`EnclaveApi`] trait -macro_rules! impl_sync_client_trait { - ($(fn $method_name:ident(&self $(, $param:ident: $param_ty:ty)*) -> $return_ty:ty),* $(,)?) => { - impl SyncEnclaveApiClient for EnclaveClient { - $( - fn $method_name(&self, $($param: $param_ty),*) -> $return_ty { - self.block_on_with_runtime(self.async_client.$method_name($($param),*)) - } - )+ - } - }; -} -impl_sync_client_trait!( - fn health_check(&self) -> Result, - fn get_purpose_keys(&self, _req: GetPurposeKeysRequest) -> Result, - fn get_attestation_evidence(&self, _req: AttestationGetEvidenceRequest) -> Result, - fn eval_attestation_evidence(&self, _req: AttestationEvalEvidenceRequest) -> Result, - fn boot_retrieve_root_key(&self, _req: RetrieveRootKeyRequest) -> Result, - fn boot_share_root_key(&self, _req: ShareRootKeyRequest) -> Result, - fn boot_genesis(&self) -> Result<(), ClientError>, - fn complete_boot(&self) -> Result<(), ClientError>, - fn prepare_encrypted_snapshot(&self, _req: PrepareEncryptedSnapshotRequest) -> Result, - fn restore_from_encrypted_snapshot(&self, _req: RestoreFromEncryptedSnapshotRequest) -> Result, -); - -#[cfg(test)] -pub mod tests { - use crate::{rpc::BuildableServer, MockEnclaveServer}; - - use super::*; - use std::{ - net::{SocketAddr, TcpListener}, - time::Duration, - }; - use tokio::time::sleep; - - #[test] - fn test_client_sync_context() { - // testing if sync client can be created in a sync runtime - let port = get_random_port(); - let addr = SocketAddr::from((ENCLAVE_DEFAULT_ENDPOINT_IP, port)); - let _ = EnclaveClient::mock(addr.ip().to_string(), addr.port()); - } - - #[tokio::test(flavor = "multi_thread")] - async fn test_sync_client() -> Result<()> { - // spawn a seperate thread for the server, otherwise the test will hang - let port = get_random_port(); - let addr = SocketAddr::from((ENCLAVE_DEFAULT_ENDPOINT_IP, port)); - let _server_handle = MockEnclaveServer::new(addr).start().await?; - let _ = sleep(Duration::from_secs(2)).await; - - let client = EnclaveClient::mock(addr.ip().to_string(), addr.port())?; - sync_test_health_check(&client); - sync_test_get_purpose_keys(&client); - sync_test_prepare_encrypted_snapshot(&client); - Ok(()) - } - - pub fn get_random_port() -> u16 { - TcpListener::bind("127.0.0.1:0") // 0 means OS assigns a free port - .expect("Failed to bind to a port") - .local_addr() - .unwrap() - .port() - } - - pub fn sync_test_health_check(client: &C) { - let response = client.health_check().unwrap(); - assert!(response.status_ok, "Status OK"); - } - - pub fn sync_test_get_purpose_keys(client: &C) { - let response = client - .get_purpose_keys(GetPurposeKeysRequest { epoch: 0 }) - .unwrap(); - assert!(response.snapshot_key_bytes.len() > 0); - } - - pub fn sync_test_prepare_encrypted_snapshot(client: &C) { - let response = client - .prepare_encrypted_snapshot(PrepareEncryptedSnapshotRequest {}) - .unwrap(); - assert!(response.success); - } -} diff --git a/crates/enclave/src/client/mock.rs b/crates/enclave/src/client/mock.rs deleted file mode 100644 index 0b5810a4..00000000 --- a/crates/enclave/src/client/mock.rs +++ /dev/null @@ -1,260 +0,0 @@ -//! A mock enclave server for testing purposes. - -use anyhow::Result; -use jsonrpsee::{ - core::{async_trait, ClientError, RpcResult}, - server::ServerHandle, - Methods, -}; -use std::{ - net::{IpAddr, SocketAddr}, - str::FromStr, -}; - -use crate::{ - client::{ - rpc::{ - BuildableServer, EnclaveApiServer, SyncEnclaveApiClient, SyncEnclaveApiClientBuilder, - }, - ENCLAVE_DEFAULT_ENDPOINT_IP, ENCLAVE_DEFAULT_ENDPOINT_PORT, - }, - crypto::Nonce, - ecdh_encrypt, get_unsecure_sample_schnorrkel_keypair, get_unsecure_sample_secp256k1_pk, - get_unsecure_sample_secp256k1_sk, - request_types::*, -}; - -#[derive(Debug, Clone)] -pub struct MockEnclaveServer { - addr: SocketAddr, -} - -impl MockEnclaveServer { - pub fn new(addr: impl Into) -> Self { - Self { addr: addr.into() } - } - - pub fn new_from_ip_port(ip: String, port: u16) -> Self { - Self::new((IpAddr::from_str(&ip).unwrap(), port)) - } - - /// Mock implementation of the health check method. - pub fn health_check() -> HealthCheckResponse { - let boot_complete = true; // Mock enclave server starts booted as it uses hardcoded keys - HealthCheckResponse { - status_ok: true, - boot_complete, - } - } - - /// Mock implementation of the get_public_key method. - pub fn get_purpose_keys(_req: GetPurposeKeysRequest) -> GetPurposeKeysResponse { - GetPurposeKeysResponse { - tx_io_sk: get_unsecure_sample_secp256k1_sk(), - tx_io_pk: get_unsecure_sample_secp256k1_pk(), - snapshot_key_bytes: [0u8; 32], - rng_keypair: get_unsecure_sample_schnorrkel_keypair(), - } - } - - /// Mock implementation of the get_attestation_evidence method. - pub fn get_attestation_evidence( - _req: AttestationGetEvidenceRequest, - ) -> AttestationGetEvidenceResponse { - unimplemented!("get_attestation_evidence not implemented for mock server") - } - - /// Mock implementation of the eval_attestation_evidence method. - pub fn eval_attestation_evidence( - _req: AttestationEvalEvidenceRequest, - ) -> AttestationEvalEvidenceResponse { - unimplemented!("eval_attestation_evidence not implemented for mock server") - } - - fn boot_retrieve_root_key(_req: RetrieveRootKeyRequest) -> RetrieveRootKeyResponse { - // No-op, keys are hardcoded for mock server - RetrieveRootKeyResponse {} - } - - fn boot_share_root_key(req: ShareRootKeyRequest) -> ShareRootKeyResponse { - // skip checking the attestation since it's a mock - - // encrypt the root key - let sharer_sk = get_unsecure_sample_secp256k1_sk(); - let sharer_pk = get_unsecure_sample_secp256k1_pk(); - let mock_root_key = [0u8; 32]; - let nonce = Nonce::new_rand(); - - let root_key_ciphertext = - ecdh_encrypt(&req.retriever_pk, &sharer_sk, &mock_root_key, nonce.clone()).unwrap(); - - ShareRootKeyResponse { - nonce, - root_key_ciphertext, - sharer_pk, - } - } - - fn boot_genesis() { - // No-op, keys are hardcoded for mock server - } - - fn complete_boot() { - // No-op, keys are hardcoded for mock server - } - - fn prepare_encrypted_snapshot( - _req: PrepareEncryptedSnapshotRequest, - ) -> PrepareEncryptedSnapshotResponse { - PrepareEncryptedSnapshotResponse { - success: true, - error: "".to_string(), - } - } - - fn restore_from_encrypted_snapshot( - _req: RestoreFromEncryptedSnapshotRequest, - ) -> RestoreFromEncryptedSnapshotResponse { - RestoreFromEncryptedSnapshotResponse { - success: true, - error: "".to_string(), - } - } -} - -impl Default for MockEnclaveServer { - fn default() -> Self { - Self::new((ENCLAVE_DEFAULT_ENDPOINT_IP, ENCLAVE_DEFAULT_ENDPOINT_PORT)) - } -} - -impl BuildableServer for MockEnclaveServer { - fn addr(&self) -> SocketAddr { - self.addr - } - - fn methods(self) -> Methods { - self.into_rpc().into() - } - - async fn start(self) -> Result { - BuildableServer::start_rpc_server(self).await - } -} - -/// Derive implementation of the async [`EnclaveApiServer`] trait -/// for [`MockEnclaveServer`] -macro_rules! impl_mock_async_server_trait { - ($(async fn $method_name:ident(&self $(, $param:ident: $param_ty:ty)*) - -> $ret:ty),* $(,)?) => { - #[async_trait] - impl EnclaveApiServer for MockEnclaveServer { - $( - async fn $method_name(&self $(, $param: $param_ty)*) -> RpcResult<$ret> { - // For each method, call the corresponding method on the mock server - Ok(MockEnclaveServer::$method_name($($param),*)) - } - )* - } - }; -} -impl_mock_async_server_trait!( - async fn health_check(&self) -> HealthCheckResponse, - async fn get_purpose_keys(&self, req: GetPurposeKeysRequest) -> GetPurposeKeysResponse, - async fn get_attestation_evidence(&self, req: AttestationGetEvidenceRequest) -> AttestationGetEvidenceResponse, - async fn eval_attestation_evidence(&self, req: AttestationEvalEvidenceRequest) -> AttestationEvalEvidenceResponse, - async fn boot_retrieve_root_key(&self, req: RetrieveRootKeyRequest) -> RetrieveRootKeyResponse, - async fn boot_share_root_key(&self, req: ShareRootKeyRequest) -> ShareRootKeyResponse, - async fn boot_genesis(&self) -> (), - async fn complete_boot(&self) -> (), - async fn prepare_encrypted_snapshot(&self, req: PrepareEncryptedSnapshotRequest) -> PrepareEncryptedSnapshotResponse, - async fn restore_from_encrypted_snapshot(&self, req: RestoreFromEncryptedSnapshotRequest) -> RestoreFromEncryptedSnapshotResponse, -); - -/// Mock enclave client for testing purposes. -/// Useful for testing the against the mock server, -/// as it can be easily set up instead of going through the EnclaveClientBuilder -#[derive(Debug, Clone, Default)] -pub struct MockEnclaveClient; -impl MockEnclaveClient { - pub fn new() -> Self { - Self {} - } -} - -/// Derive implementation of the [`SyncEnclaveApiClient`] trait -/// for [`MockEnclaveClient`]. -/// based on functions implemented in the [`MockEnclaveServer`]. -macro_rules! impl_mock_sync_client_trait { - ($(fn $method_name:ident(&self $(, $param:ident: $param_ty:ty)*) -> $return_ty:ty),* $(,)?) => { - impl SyncEnclaveApiClient for MockEnclaveClient { - $( - fn $method_name(&self, $($param: $param_ty),*) -> $return_ty { - // for each method, call the corresponding method on the mock server - // simulates a client client rpc call where that code is executed - Ok(MockEnclaveServer::$method_name($($param),*)) - } - )+ - } - }; -} -impl_mock_sync_client_trait!( - fn health_check(&self) -> Result, - fn get_purpose_keys(&self, _req: GetPurposeKeysRequest) -> Result, - fn get_attestation_evidence(&self, _req: AttestationGetEvidenceRequest) -> Result, - fn eval_attestation_evidence(&self, _req: AttestationEvalEvidenceRequest) -> Result, - fn boot_retrieve_root_key(&self, _req: RetrieveRootKeyRequest) -> Result, - fn boot_share_root_key(&self, _req: ShareRootKeyRequest) -> Result, - fn boot_genesis(&self) -> Result<(), ClientError>, - fn complete_boot(&self) -> Result<(), ClientError>, - fn prepare_encrypted_snapshot(&self, _req: PrepareEncryptedSnapshotRequest) -> Result, - fn restore_from_encrypted_snapshot(&self, _req: RestoreFromEncryptedSnapshotRequest) -> Result, -); - -#[derive(Debug, Clone, Default)] -pub struct MockEnclaveClientBuilder {} -impl MockEnclaveClientBuilder { - pub fn new() -> Self { - Self {} - } -} -impl SyncEnclaveApiClientBuilder for MockEnclaveClientBuilder { - type Client = MockEnclaveClient; - fn build(self) -> MockEnclaveClient { - MockEnclaveClient::new() - } -} - -#[cfg(test)] -mod tests { - use std::{ops::Deref, time::Duration}; - use tokio::time::sleep; - - use super::*; - use crate::client::tests::*; - use crate::{rpc::EnclaveApiClient, EnclaveClient}; - - #[test] - fn test_mock_client() { - let client = MockEnclaveClient {}; - sync_test_health_check(&client); - } - - #[tokio::test(flavor = "multi_thread")] - async fn test_mock_server_and_sync_client() -> Result<()> { - // spawn a seperate thread for the server, otherwise the test will hang - let port = get_random_port(); - let addr = SocketAddr::from((ENCLAVE_DEFAULT_ENDPOINT_IP, port)); - let _server_handle = MockEnclaveServer::new(addr).start().await?; - let _ = sleep(Duration::from_secs(2)).await; - - let client = EnclaveClient::mock(addr.ip().to_string(), addr.port())?; - async_test_health_check(&client).await; - Ok(()) - } - - async fn async_test_health_check(client: &EnclaveClient) { - let response = client.deref().health_check().await.unwrap(); - assert!(response.status_ok, "Status OK"); - } -} diff --git a/crates/enclave/src/client/mod.rs b/crates/enclave/src/client/mod.rs deleted file mode 100644 index a24fa970..00000000 --- a/crates/enclave/src/client/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! This module provides a client for interacting with a TEE Service server. -//! -//! The TEE client makes HTTP requests to a TEE server to perform -//! operations, e.g. encryption and decryption operations. The main structures and -//! traits define the API and implementation for the TEE client. -#![allow(async_fn_in_trait)] -mod booter; -pub mod client; -pub mod mock; -pub mod rpc; - -pub use booter::{boot_genesis_streamlined_async, boot_genesis_streamlined_sync}; -pub use client::*; -pub use mock::*; diff --git a/crates/enclave/src/client/rpc.rs b/crates/enclave/src/client/rpc.rs deleted file mode 100644 index 6b88d1b4..00000000 --- a/crates/enclave/src/client/rpc.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! This module provides the JSON-RPC traits for the enclave server and client. -//! Defines how server's are expected to be built and the shared API - -use anyhow::Result; -use jsonrpsee::core::RpcResult; -use jsonrpsee::proc_macros::rpc; -use jsonrpsee::server::{ServerBuilder, ServerHandle}; -use jsonrpsee::Methods; -use seismic_enclave_derive::derive_sync_client_trait; -use std::fmt::Debug; -use std::net::SocketAddr; - -use crate::request_types::*; - -/// A trait for building a server. -pub trait BuildableServer { - fn addr(&self) -> SocketAddr; - fn methods(self) -> Methods; - async fn start(self) -> Result; - async fn start_rpc_server(self) -> Result - where - Self: Sized, - { - let addr = self.addr(); - let rpc_server = ServerBuilder::new().build(addr).await?; - let module = self.methods(); - - let server_handle = rpc_server.start(module); - Ok(server_handle) - } -} - -pub trait SyncEnclaveApiClientBuilder: Clone + Debug + Send + Sync + Unpin { - type Client: SyncEnclaveApiClient + Clone + Debug + Send + Sync + Unpin; - fn build(self) -> Self::Client; -} - -/// The JSON-RPC trait for the enclave server and client, defining the API. -#[derive_sync_client_trait] // derive the SyncEnclaveApi trait, which allows for sync calls, which seismic-reth requires -#[rpc(client, server)] // derive the EnclaveApiClient and EnclaveApiServer traits -pub trait EnclaveApi { - /// Health check endpoint that returns "OK" if service is running - #[method(name = "healthCheck")] - async fn health_check(&self) -> RpcResult; - - /// Get the secp256k1 public key - #[method(name = "getPurposeKeys")] - async fn get_purpose_keys( - &self, - req: GetPurposeKeysRequest, - ) -> RpcResult; - - /// Generates attestation evidence from the attestation authority - #[method(name = "getAttestationEvidence")] - async fn get_attestation_evidence( - &self, - _req: AttestationGetEvidenceRequest, - ) -> RpcResult; - - /// Evaluates provided attestation evidence - #[method(name = "evalAttestationEvidence")] - async fn eval_attestation_evidence( - &self, - _req: AttestationEvalEvidenceRequest, - ) -> RpcResult; - - /// Retrieves the root key from an existing node - #[method(name = "boot.retrieve_root_key")] - async fn boot_retrieve_root_key( - &self, - _req: RetrieveRootKeyRequest, - ) -> RpcResult; - - /// Shares the root key with an existing node - #[method(name = "boot.share_root_key")] - async fn boot_share_root_key( - &self, - _req: ShareRootKeyRequest, - ) -> RpcResult; - - /// Genesis boot - #[method(name = "boot.genesis_boot")] - async fn boot_genesis(&self) -> RpcResult<()>; - - /// Completes the genesis boot - #[method(name = "boot.complete_boot")] - async fn complete_boot(&self) -> RpcResult<()>; - - /// Prepares an encrypted snapshot - #[method(name = "snapshot.prepare_encrypted_snapshot")] - async fn prepare_encrypted_snapshot( - &self, - _req: PrepareEncryptedSnapshotRequest, - ) -> RpcResult; - - /// Restores from an encrypted snapshot - #[method(name = "snapshot.restore_from_encrypted_snapshot")] - async fn restore_from_encrypted_snapshot( - &self, - _req: RestoreFromEncryptedSnapshotRequest, - ) -> RpcResult; -} diff --git a/crates/enclave/src/crypto.rs b/crates/enclave/src/crypto.rs index 2c001ae3..171d6ab2 100644 --- a/crates/enclave/src/crypto.rs +++ b/crates/enclave/src/crypto.rs @@ -1,12 +1,12 @@ use aes_gcm::{ - aead::{Aead, KeyInit}, Aes256Gcm, Key, + aead::{Aead, KeyInit}, }; use anyhow::anyhow; use hkdf::Hkdf; pub use schnorrkel::keys::Keypair as SchnorrkelKeypair; use schnorrkel::{ExpansionMode, MiniSecretKey}; -use secp256k1::{ecdh::SharedSecret, ecdsa::Signature, Message, PublicKey, Secp256k1, SecretKey}; +use secp256k1::{Message, PublicKey, Secp256k1, SecretKey, ecdh::SharedSecret, ecdsa::Signature}; use sha2::{Digest, Sha256}; use std::str::FromStr; use std::{fs, io::Read, io::Write}; diff --git a/crates/enclave/src/errors.rs b/crates/enclave/src/errors.rs deleted file mode 100644 index 6f59e23b..00000000 --- a/crates/enclave/src/errors.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! Utility functions for converting errors into JSON-RPC error responses - -use anyhow::Error; - -/// JSON-RPC custom error code representing a conflict due to uninitialized resource -pub const CONFLICT_CODE: i32 = -32409; - -/// Convert a bad evidence error into a JSON-RPC error response -/// Occurs when verifying an attestation evidence fails -pub fn rpc_bad_evidence_error(e: Error) -> jsonrpsee::types::ErrorObjectOwned { - jsonrpsee::types::ErrorObject::owned( - jsonrpsee::types::error::INVALID_PARAMS_CODE, - format!("Error while evaluating evidence: {:?}", e), - None::<()>, - ) -} - -/// Convert a bad genesis error into a JSON-RPC error response -pub fn rpc_bad_genesis_error(e: Error) -> jsonrpsee::types::ErrorObjectOwned { - jsonrpsee::types::ErrorObject::owned( - jsonrpsee::types::error::INVALID_PARAMS_CODE, - format!("Error while generating a genesis attestation: {:?}", e), - None::<()>, - ) -} - -/// Convert a bad argument error into a JSON-RPC error response -pub fn rpc_bad_argument_error(e: Error) -> jsonrpsee::types::ErrorObjectOwned { - jsonrpsee::types::ErrorObject::owned( - jsonrpsee::types::error::INVALID_PARAMS_CODE, - format!("Invalid Argument: {:?}", e), - None::<()>, - ) -} - -// Convert a generic error into a JSON-RPC error response -pub fn rpc_internal_server_error(e: Error) -> jsonrpsee::types::ErrorObjectOwned { - jsonrpsee::types::ErrorObject::owned( - jsonrpsee::types::error::INTERNAL_ERROR_CODE, - format!("Internal server error: {}", e), - None::<()>, - ) -} - -/// Convert an invalid ciphertext error into a JSON-RPC error response -pub fn rpc_invalid_ciphertext_error(e: Error) -> jsonrpsee::types::ErrorObjectOwned { - jsonrpsee::types::ErrorObject::owned( - jsonrpsee::types::error::INVALID_PARAMS_CODE, - format!("Invalid ciphertext: {}", e), - None::<()>, - ) -} - -/// Convert an uninitialized resource error into a JSON-RPC error response -pub fn rpc_conflict_error(e: Error) -> jsonrpsee::types::ErrorObjectOwned { - jsonrpsee::types::ErrorObject::owned( - CONFLICT_CODE, - format!("Request conflicts with current state: {}", e), - None::<()>, - ) -} - -// convert a missing snapshot error into a JSON-RPC error response -pub fn rpc_missing_snapshot_error() -> jsonrpsee::types::ErrorObjectOwned { - jsonrpsee::types::ErrorObject::owned( - jsonrpsee::types::error::INVALID_PARAMS_CODE, - "Snapshot file not found. Snapshot must be prepared/uploaded before attempting this action.", - None::<()>, - ) -} diff --git a/crates/enclave/src/lib.rs b/crates/enclave/src/lib.rs index 742d242d..82b87e3c 100644 --- a/crates/enclave/src/lib.rs +++ b/crates/enclave/src/lib.rs @@ -1,14 +1,7 @@ -#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -#![cfg_attr(not(test), warn(unused_crate_dependencies))] +pub mod api; +mod crypto; +pub mod mock; -pub mod client; -pub mod crypto; -pub mod errors; -pub mod request_types; - -pub use client::*; +pub use api::*; pub use crypto::*; -pub use errors::*; -pub use request_types::*; - -pub use secp256k1::*; +pub use secp256k1; diff --git a/crates/enclave/src/mock.rs b/crates/enclave/src/mock.rs new file mode 100644 index 00000000..1df21bde --- /dev/null +++ b/crates/enclave/src/mock.rs @@ -0,0 +1,69 @@ +/* Mock enclave server for testing and running on non-tdx machines */ + +use std::net::SocketAddr; + +use jsonrpsee::core::{RpcResult, async_trait}; +use jsonrpsee::server::ServerBuilder; + +use crate::api::{ + AttestationGetEvidenceResponse, GetPurposeKeysResponse, ShareRootKeyResponse, TdxQuoteRpcServer, +}; +use crate::{ + get_unsecure_sample_schnorrkel_keypair, get_unsecure_sample_secp256k1_pk, + get_unsecure_sample_secp256k1_sk, +}; + +pub struct MockServer {} + +pub async fn start_mock_server(addr: SocketAddr) -> anyhow::Result<()> { + let server = ServerBuilder::default().build(addr).await?; + + let handle = server.start(MockServer {}.into_rpc()); + + println!("TDX Quote JSON-RPC Server started at {}", addr); + + handle.stopped().await; + Ok(()) +} + +#[async_trait] +impl TdxQuoteRpcServer for MockServer { + async fn health_check(&self) -> RpcResult { + Ok("OK".to_string()) + } + + async fn get_purpose_keys(&self, _epoch: u64) -> RpcResult { + Ok(GetPurposeKeysResponse { + tx_io_sk: get_unsecure_sample_secp256k1_sk(), + tx_io_pk: get_unsecure_sample_secp256k1_pk(), + snapshot_key_bytes: [0u8; 32], + rng_keypair: get_unsecure_sample_schnorrkel_keypair(), + }) + } + + async fn get_attestation_evidence(&self) -> RpcResult { + unimplemented!("get_attestation_evidence not implemented for mock server") + } + + async fn eval_attestation_evidence( + &self, + _hcl_report: Vec, + _quote: Vec, + ) -> RpcResult<()> { + unimplemented!("eval_attestation_evidence not implemented for mock server") + } + + async fn boot_share_root_key(&self, _quote: Vec) -> RpcResult { + Ok(ShareRootKeyResponse { + root_key: *get_unsecure_sample_secp256k1_sk().as_ref(), + }) + } + + async fn prepare_encrypted_snapshot(&self) -> RpcResult<()> { + unimplemented!("prepare_encrypted_snapshot is not implemented for mock server") + } + + async fn restore_from_encrypted_snapshot(&self) -> RpcResult<()> { + unimplemented!("restore_encrypted_snapshot is not implemented for mock server") + } +} diff --git a/crates/enclave/src/request_types/boot.rs b/crates/enclave/src/request_types/boot.rs deleted file mode 100644 index 2e1d297c..00000000 --- a/crates/enclave/src/request_types/boot.rs +++ /dev/null @@ -1,66 +0,0 @@ -use kbs_types::{HashAlgorithm, Tee}; -use std::net::SocketAddr; - -use crate::crypto::Nonce; -use crate::request_types::{AttestationEvalEvidenceRequest, Data}; -use serde::{Deserialize, Serialize}; - -/// CompleteBoot endpoint is used to signal all boot steps are complete -/// and node should launch the real enclave server and reth -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct CompleteBootRequest {} -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct CompleteBootResponse { - pub success: bool, -} - -/// RetieveRootKey endpoint triggers the enclave to retrieve the root key -/// via http from an existing node running the enclave server -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct RetrieveRootKeyRequest { - pub addr: SocketAddr, - pub attestation_policy_id: String, -} -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct RetrieveRootKeyResponse {} - -/// ShareRootKey endpoint triggers the enclave to share the root key with -/// an new enclave server that is booting -/// -/// It is expected that the attestation is created with the following parameters: -/// - runtime_data: Some(Data::Raw(req.retriever_pk.serialize().to_vec())), -/// - runtime_data_hash_algorithm: HashAlgorithm::Sha256, -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ShareRootKeyRequest { - pub evidence: serde_json::Value, - pub tee: Tee, - pub retriever_pk: secp256k1::PublicKey, -} -impl Into for ShareRootKeyRequest { - fn into(self) -> AttestationEvalEvidenceRequest { - AttestationEvalEvidenceRequest { - evidence: self.evidence, - tee: self.tee, - runtime_data: Some(Data::Raw(self.retriever_pk.serialize().to_vec())), - runtime_data_hash_algorithm: Some(HashAlgorithm::Sha256), - policy_ids: vec!["share_root".to_string()], - } - } -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ShareRootKeyResponse { - pub nonce: Nonce, - pub root_key_ciphertext: Vec, - pub sharer_pk: secp256k1::PublicKey, -} - -/// GenesisBoot endpoint triggers the server to boot in a configuration -/// for a new network genesis -/// For now this just means setting a new root_key from osrng -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct GenesisBootRequest {} -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct GenesisBootResponse { - pub attestation: Vec, -} diff --git a/crates/enclave/src/request_types/coco_aa.rs b/crates/enclave/src/request_types/coco_aa.rs deleted file mode 100644 index 1a52fe1f..00000000 --- a/crates/enclave/src/request_types/coco_aa.rs +++ /dev/null @@ -1,12 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct AttestationGetEvidenceRequest { - // For AzTdxVtpm, this affects the quotes's aztdxvtpm.quote.body.report_data - pub runtime_data: Vec, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct AttestationGetEvidenceResponse { - pub evidence: Vec, -} diff --git a/crates/enclave/src/request_types/coco_as.rs b/crates/enclave/src/request_types/coco_as.rs deleted file mode 100644 index 6ecbc1e9..00000000 --- a/crates/enclave/src/request_types/coco_as.rs +++ /dev/null @@ -1,246 +0,0 @@ -use kbs_types::{HashAlgorithm, Tee}; -use serde::{Deserialize, Serialize}; -use serde_json::Value; - -use anyhow::{anyhow, Result}; -use base64::engine::general_purpose::URL_SAFE_NO_PAD; -use base64::Engine; - -/// Runtime/Init Data used to check the binding relationship with report data -/// in Evidence -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub enum Data { - /// This will be used as the expected runtime/init data to check against - /// the one inside evidence. - Raw(Vec), - - /// Runtime/Init data in a JSON map. CoCoAS will rearrange each layer of the - /// data JSON object in dictionary order by key, then serialize and output - /// it into a compact string, and perform hash calculation on the whole - /// to check against the one inside evidence. - Structured(Value), -} - -/// Represents the request to evaluate attestation evidence. -/// -/// This struct contains the necessary information for evaluating attestation -/// evidence, including the raw evidence bytes, the TEE (Trusted Execution Environment) -/// type, and optional runtime data and its associated hash algorithm. -/// -/// # Fields -/// -/// - `evidence`: The raw bytes of the attestation evidence to be evaluated. -/// - `tee`: The TEE type of the attestation evidence, indicating which TEE generated the evidence. -/// - `runtime_data`: The expected runtime data that the evidence should match against. This is optional. -/// - `runtime_data_hash_algorithm`: The hash algorithm to use for the runtime data. This is optional. -/// -/// # Notes -/// -/// - For the `AzTdxVtpm` TEE, `runtime_data` and `runtime_data_hash_algorithm` must not be `None`. -/// - For empty data in `AzTdxVtpm`, set the following: -/// - `runtime_data = Some(Data::Raw("".into()))` -/// - `runtime_data_hash_algorithm = Some(HashAlgorithm::Sha256)` -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct AttestationEvalEvidenceRequest { - pub evidence: serde_json::Value, - pub tee: Tee, - pub runtime_data: Option, - pub runtime_data_hash_algorithm: Option, - pub policy_ids: Vec, -} - -/// Represents the response to an attestation evidence evaluation request. -/// -/// This struct contains the result of the attestation evaluation, including whether -/// the evidence was deemed valid and any claims extracted from the evidence. -/// -/// # Fields -/// -/// - `eval`: A boolean indicating whether the attestation service deemed the evidence valid (`true`) or invalid (`false`). -/// - `claims`: A summary of the claims included in the attestation evidence. This may be `None` if there are no claims. -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct AttestationEvalEvidenceResponse { - pub claims: Option, -} - -/// Struct representing the relevant fields of an Attestation Service (AS) token's claims. -/// -/// This struct contains information about the Trusted Execution Environment (TEE), -/// the evaluation of evidence, and various security properties attested by the AS. -/// -/// # Fields -/// -/// - `tee` - The TEE type of the attestation evidence. -/// - `evaluation_reports` - A list of policies that the evidence was evaluated against. -/// More information can be found in the [policy documentation](https://github.com/confidential-containers/trustee/blob/bd6b25add83ece4bb5204b8cf560e0727a7c3f8e/attestation-service/docs/policy.md). -/// - `tcb_status` - The Trusted Computing Base (TCB) status that was attested to. -/// This is verified against the hardware signature and then checked against a policy. -/// - `reference_data` - Reference values provided by the Reference Value Provider Service (RVPS) -/// to check against the attestation evidence. -/// - `customized_claims` - The initialization and runtime data that were enforced to match the evidence. -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ASCoreTokenClaims { - pub tee: String, - #[serde(rename = "evaluation-reports")] - pub evaluation_reports: Vec, - - #[serde(rename = "tcb-status")] - pub tcb_status: String, - - pub customized_claims: ASCustomizedClaims, -} -impl ASCoreTokenClaims { - /// Serializes the claims to JSON (without JWT encoding). - pub fn to_json(&self) -> Result { - Ok(serde_json::to_string(self)?) - } - - /// Parses a (base64-encoded) JWT string into an `ASCoreTokenClaims`. - /// - /// Expects the token to have three parts separated by '.', and - /// decodes the middle part as JSON claims. - pub fn from_jwt(token: &str) -> Result { - let parts: Vec<&str> = token.splitn(3, '.').collect(); - if parts.len() != 3 { - return Err(anyhow!( - "Invalid token format: expected 3 parts separated by '.'" - )); - } - let claims_b64 = parts[1]; - let claims_decoded_bytes = URL_SAFE_NO_PAD.decode(claims_b64)?; - let claims_decoded_string = String::from_utf8(claims_decoded_bytes)?; - let claims: ASCoreTokenClaims = serde_json::from_str(&claims_decoded_string)?; - Ok(claims) - } -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ASCustomizedClaims { - pub init_data: Value, - pub runtime_data: Value, -} - -#[cfg(test)] -mod tests { - use super::*; - use serde_json; - - #[test] - fn test_serialize_some_data() { - let evidence_bytes = vec![ - 123, 34, 115, 118, 110, 34, 58, 34, 49, 34, 44, 34, 114, 101, 112, 111, 114, 116, 95, - 100, 97, 116, 97, 34, 58, 34, 98, 109, 57, 117, 89, 50, 85, 61, 34, 125, - ]; - let evidence_str = String::from_utf8(evidence_bytes).unwrap(); - let evidence: serde_json::Value = serde_json::from_str(&evidence_str).unwrap(); - - let original_request = AttestationEvalEvidenceRequest { - evidence, - tee: Tee::Sample, - runtime_data: Some(Data::Raw("nonce".as_bytes().to_vec())), - runtime_data_hash_algorithm: Some(HashAlgorithm::Sha256), - policy_ids: vec!["allow".to_string()], - }; - - // Serialize the request to a JSON string - let serialized = serde_json::to_string(&original_request).expect("Failed to serialize"); - - // Deserialize the JSON string back to a `AttestationEvalEvidenceRequest` - let deserialized: AttestationEvalEvidenceRequest = - serde_json::from_str(&serialized).expect("Failed to deserialize"); - - // Check that the deserialized object is equal to the original - assert_eq!(original_request.evidence, deserialized.evidence); - assert_eq!(original_request.tee, deserialized.tee); - match ( - &original_request.runtime_data.unwrap(), - &deserialized.runtime_data.unwrap(), - ) { - (Data::Raw(bytes1), Data::Raw(bytes2)) => assert_eq!(bytes1, bytes2), - (Data::Structured(value1), Data::Structured(value2)) => assert_eq!(value1, value2), - _ => panic!("Mismatched runtime data types"), - } - assert_eq!( - original_request - .runtime_data_hash_algorithm - .unwrap() - .to_string(), - deserialized - .runtime_data_hash_algorithm - .unwrap() - .to_string() - ); - assert_eq!(original_request.policy_ids, deserialized.policy_ids); - } - - #[test] - fn test_serialize_none_hash_algorithm() { - let evidence_bytes = vec![ - 123, 34, 115, 118, 110, 34, 58, 34, 49, 34, 44, 34, 114, 101, 112, 111, 114, 116, 95, - 100, 97, 116, 97, 34, 58, 34, 98, 109, 57, 117, 89, 50, 85, 61, 34, 125, - ]; - let evidence_str = String::from_utf8(evidence_bytes).unwrap(); - let evidence: serde_json::Value = serde_json::from_str(&evidence_str).unwrap(); - - let original_request = AttestationEvalEvidenceRequest { - evidence, - tee: Tee::Sample, - runtime_data: Some(Data::Raw("nonce".as_bytes().to_vec())), - runtime_data_hash_algorithm: None, - policy_ids: vec!["allow".to_string()], - }; - - // Serialize the request to a JSON string - let serialized = serde_json::to_string(&original_request).expect("Failed to serialize"); - - // Deserialize the JSON string back to a `AttestationEvalEvidenceRequest` - let deserialized: AttestationEvalEvidenceRequest = - serde_json::from_str(&serialized).expect("Failed to deserialize"); - - // Check that the deserialized object is equal to the original - assert_eq!(original_request.evidence, deserialized.evidence); - assert_eq!(original_request.tee, deserialized.tee); - match ( - &original_request.runtime_data.unwrap(), - &deserialized.runtime_data.unwrap(), - ) { - (Data::Raw(bytes1), Data::Raw(bytes2)) => assert_eq!(bytes1, bytes2), - (Data::Structured(value1), Data::Structured(value2)) => assert_eq!(value1, value2), - _ => panic!("Mismatched runtime data types"), - } - assert!(deserialized.runtime_data_hash_algorithm.is_none()); - assert_eq!(original_request.policy_ids, deserialized.policy_ids); - } - - #[test] - fn test_serialize_none_data() { - let evidence_bytes = vec![ - 123, 34, 115, 118, 110, 34, 58, 34, 49, 34, 44, 34, 114, 101, 112, 111, 114, 116, 95, - 100, 97, 116, 97, 34, 58, 34, 98, 109, 57, 117, 89, 50, 85, 61, 34, 125, - ]; - let evidence_str = String::from_utf8(evidence_bytes).unwrap(); - let evidence: serde_json::Value = serde_json::from_str(&evidence_str).unwrap(); - - let original_request = AttestationEvalEvidenceRequest { - evidence, - tee: Tee::Sample, - runtime_data: None, - runtime_data_hash_algorithm: None, - policy_ids: vec!["allow".to_string()], - }; - - // Serialize the request to a JSON string - let serialized = serde_json::to_string(&original_request).expect("Failed to serialize"); - - // Deserialize the JSON string back to a `AttestationEvalEvidenceRequest` - let deserialized: AttestationEvalEvidenceRequest = - serde_json::from_str(&serialized).expect("Failed to deserialize"); - - // Check that the deserialized object is equal to the original - assert_eq!(original_request.evidence, deserialized.evidence); - assert_eq!(original_request.tee, deserialized.tee); - assert!(deserialized.runtime_data.is_none()); - assert!(deserialized.runtime_data_hash_algorithm.is_none()); - assert_eq!(original_request.policy_ids, deserialized.policy_ids); - } -} diff --git a/crates/enclave/src/request_types/health.rs b/crates/enclave/src/request_types/health.rs deleted file mode 100644 index d5d75571..00000000 --- a/crates/enclave/src/request_types/health.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct HealthCheckResponse { - pub status_ok: bool, - pub boot_complete: bool, -} diff --git a/crates/enclave/src/request_types/keys.rs b/crates/enclave/src/request_types/keys.rs deleted file mode 100644 index b5d4f8eb..00000000 --- a/crates/enclave/src/request_types/keys.rs +++ /dev/null @@ -1,14 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct GetPurposeKeysRequest { - pub epoch: u64, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct GetPurposeKeysResponse { - pub tx_io_sk: secp256k1::SecretKey, - pub tx_io_pk: secp256k1::PublicKey, - pub snapshot_key_bytes: [u8; 32], - pub rng_keypair: schnorrkel::keys::Keypair, -} diff --git a/crates/enclave/src/request_types/mod.rs b/crates/enclave/src/request_types/mod.rs deleted file mode 100644 index 142f69a9..00000000 --- a/crates/enclave/src/request_types/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Request types for the enclave server - -mod boot; -mod coco_aa; -mod coco_as; -mod health; -pub mod keys; -mod snapshot; - -pub use boot::*; -pub use coco_aa::*; -pub use coco_as::*; -pub use health::*; -pub use keys::*; -pub use snapshot::*; diff --git a/crates/enclave/src/request_types/snapshot.rs b/crates/enclave/src/request_types/snapshot.rs deleted file mode 100644 index bb19f7b7..00000000 --- a/crates/enclave/src/request_types/snapshot.rs +++ /dev/null @@ -1,23 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct PrepareEncryptedSnapshotRequest {} // require auth token eventually - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct PrepareEncryptedSnapshotResponse { - pub success: bool, - pub error: String, - // Potentially add fields if useful: - // file size - // block number at snapshot point - // block hash at snapshot point -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct RestoreFromEncryptedSnapshotRequest {} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct RestoreFromEncryptedSnapshotResponse { - pub success: bool, - pub error: String, -} diff --git a/scripts/run_integration_tests.sh b/scripts/run_integration_tests.sh index 1c21fa5f..04cc054e 100755 --- a/scripts/run_integration_tests.sh +++ b/scripts/run_integration_tests.sh @@ -17,6 +17,7 @@ echo "๐Ÿš€ Starting integration tests..." # Function to cleanup processes cleanup() { echo "๐Ÿงน Cleaning up processes..." + echo $(sudo supervisorctl status) sudo supervisorctl stop all || true # Logs are stored elsewhere: # ~/.reth-logs and /var/log/reth.{out,err}.log @@ -26,16 +27,19 @@ cleanup() { # # Set up trap to cleanup on exit trap cleanup EXIT - -# Make sure enclave is NOT running so we can access TPM in test -sudo supervisorctl stop seismic-enclave-server || true -sleep 2 +# Make sure enclave is running so we can start reth +sudo supervisorctl start enclave-server || true +sleep 10 # Start services via supervisor echo "๐Ÿ”ง Starting supervisor services..." sudo supervisorctl start reth || true sleep 10 +# Make sure enclave is NOT running so we can access TPM in test +sudo supervisorctl stop enclave-server || true +sleep 2 + # Check if reth is running echo "๐Ÿ” Checking if reth is running..." if ! sudo supervisorctl status reth | grep -q "RUNNING"; then @@ -44,6 +48,7 @@ if ! sudo supervisorctl status reth | grep -q "RUNNING"; then fi echo "โœ… reth service is running" + # Test 1: Run multisig upgrade operator workflow test echo "๐Ÿงช Running test_multisig_upgrade_operator_workflow..." cd crates/enclave-contract @@ -79,6 +84,7 @@ fi echo "Found binaries: ${binaries[*]}" # Run the first binary with the specific test sleep 2 + echo "๐Ÿš€ Executing: sudo ${binaries[0]} test_boot_share_root_key" if ! sudo "${binaries[0]}" test_boot_share_root_key; then echo "โŒ test_boot_share_root_key failed with exit code $?" @@ -88,14 +94,4 @@ if ! sudo "${binaries[0]}" test_boot_share_root_key; then fi echo "โœ… test_boot_share_root_key passed" -# Test 3: Run snapshot integration handlers test -echo "๐Ÿงช Running test_snapshot_integration_handlers..." -sudo supervisorctl start enclave-server -sleep 2 -if ! sudo "${binaries[0]}" test_snapshot_integration_handlers; then - echo "โŒ test_snapshot_integration_handlers failed" - exit 1 -fi -echo "โœ… test_snapshot_integration_handlers passed" - echo "๐ŸŽ‰ All integration tests passed!" \ No newline at end of file