mirror of
https://github.com/matter-labs/teepot.git
synced 2025-07-21 23:23:57 +02:00
feat: add TDX support
Signed-off-by: Harald Hoyer <harald@matterlabs.dev>
This commit is contained in:
parent
f4fba51e3e
commit
4610475fae
18 changed files with 2316 additions and 369 deletions
184
Cargo.lock
generated
184
Cargo.lock
generated
|
@ -1,6 +1,6 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-codec"
|
name = "actix-codec"
|
||||||
|
@ -311,6 +311,15 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansi_term"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
|
@ -358,6 +367,17 @@ version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.1.19",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
|
@ -535,6 +555,29 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bindgen"
|
||||||
|
version = "0.59.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"cexpr",
|
||||||
|
"clang-sys",
|
||||||
|
"clap 2.34.0",
|
||||||
|
"env_logger",
|
||||||
|
"lazy_static",
|
||||||
|
"lazycell",
|
||||||
|
"log",
|
||||||
|
"peeking_take_while",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"rustc-hash",
|
||||||
|
"shlex",
|
||||||
|
"which",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bindgen"
|
name = "bindgen"
|
||||||
version = "0.65.1"
|
version = "0.65.1"
|
||||||
|
@ -849,6 +892,21 @@ dependencies = [
|
||||||
"libloading",
|
"libloading",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "2.34.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||||
|
dependencies = [
|
||||||
|
"ansi_term",
|
||||||
|
"atty",
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"strsim 0.8.0",
|
||||||
|
"textwrap",
|
||||||
|
"unicode-width",
|
||||||
|
"vec_map",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.17"
|
version = "4.5.17"
|
||||||
|
@ -1486,6 +1544,19 @@ dependencies = [
|
||||||
"syn 2.0.77",
|
"syn 2.0.77",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"humantime",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
"termcolor",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "envy"
|
name = "envy"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
|
@ -1948,6 +2019,15 @@ version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@ -2066,6 +2146,12 @@ version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.30"
|
version = "0.14.30"
|
||||||
|
@ -2794,7 +2880,7 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi 0.3.9",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"wasi",
|
"wasi",
|
||||||
|
@ -4702,6 +4788,12 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -4827,12 +4919,28 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tdx-attest-rs"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "git+https://github.com/intel/SGXDataCenterAttestationPrimitives.git?rev=aa239d25a437a28f3f4de92c38f5b6809faac842#aa239d25a437a28f3f4de92c38f5b6809faac842"
|
||||||
|
dependencies = [
|
||||||
|
"tdx-attest-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tdx-attest-sys"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/intel/SGXDataCenterAttestationPrimitives.git?rev=aa239d25a437a28f3f4de92c38f5b6809faac842#aa239d25a437a28f3f4de92c38f5b6809faac842"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen 0.59.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tee-key-preexec"
|
name = "tee-key-preexec"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap 4.5.17",
|
||||||
"rand",
|
"rand",
|
||||||
"secp256k1 0.29.1",
|
"secp256k1 0.29.1",
|
||||||
"teepot",
|
"teepot",
|
||||||
|
@ -4846,7 +4954,7 @@ name = "tee-ratls-preexec"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap 4.5.17",
|
||||||
"rsa",
|
"rsa",
|
||||||
"teepot",
|
"teepot",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
@ -4874,7 +4982,7 @@ version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap 4.5.17",
|
||||||
"serde",
|
"serde",
|
||||||
"teepot",
|
"teepot",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
@ -4890,7 +4998,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"awc",
|
"awc",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"clap",
|
"clap 4.5.17",
|
||||||
"hex",
|
"hex",
|
||||||
"rustls 0.22.4",
|
"rustls 0.22.4",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -4909,7 +5017,7 @@ dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"awc",
|
"awc",
|
||||||
"clap",
|
"clap 4.5.17",
|
||||||
"rustls 0.22.4",
|
"rustls 0.22.4",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"teepot",
|
"teepot",
|
||||||
|
@ -4929,7 +5037,7 @@ dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"bytes",
|
"bytes",
|
||||||
"clap",
|
"clap 4.5.17",
|
||||||
"const-oid",
|
"const-oid",
|
||||||
"enumset",
|
"enumset",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
@ -4948,12 +5056,15 @@ dependencies = [
|
||||||
"serde_with 3.9.0",
|
"serde_with 3.9.0",
|
||||||
"sha2",
|
"sha2",
|
||||||
"signature 2.2.0",
|
"signature 2.2.0",
|
||||||
|
"tdx-attest-rs",
|
||||||
"teepot-tee-quote-verification-rs",
|
"teepot-tee-quote-verification-rs",
|
||||||
"testaso",
|
"testaso",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-log 0.2.0",
|
"tracing-log 0.2.0",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"tracing-test",
|
||||||
"webpki-roots",
|
"webpki-roots",
|
||||||
"x509-cert",
|
"x509-cert",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
|
@ -4966,7 +5077,7 @@ dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"awc",
|
"awc",
|
||||||
"clap",
|
"clap 4.5.17",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"teepot",
|
"teepot",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
@ -4989,7 +5100,7 @@ dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"awc",
|
"awc",
|
||||||
"clap",
|
"clap 4.5.17",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"teepot",
|
"teepot",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
@ -5010,6 +5121,15 @@ dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"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]]
|
[[package]]
|
||||||
name = "terminal_size"
|
name = "terminal_size"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -5026,6 +5146,15 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "63b4d2149a2f578665ca39f8115084635847e9dd6921b5442dcafc7f87bb8e99"
|
checksum = "63b4d2149a2f578665ca39f8115084635847e9dd6921b5442dcafc7f87bb8e99"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.63"
|
version = "1.0.63"
|
||||||
|
@ -5432,6 +5561,27 @@ dependencies = [
|
||||||
"tracing-serde",
|
"tracing-serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-test"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68"
|
||||||
|
dependencies = [
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"tracing-test-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-test-macro"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.77",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "try-lock"
|
name = "try-lock"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
|
@ -5583,7 +5733,7 @@ dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"clap",
|
"clap 4.5.17",
|
||||||
"hex",
|
"hex",
|
||||||
"pgp",
|
"pgp",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -5600,7 +5750,7 @@ dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"clap",
|
"clap 4.5.17",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"teepot",
|
"teepot",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
@ -5614,12 +5764,18 @@ version = "0.2.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vec_map"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "verify-attestation"
|
name = "verify-attestation"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap 4.5.17",
|
||||||
"hex",
|
"hex",
|
||||||
"secp256k1 0.29.1",
|
"secp256k1 0.29.1",
|
||||||
"teepot",
|
"teepot",
|
||||||
|
@ -5631,7 +5787,7 @@ name = "verify-era-proof-attestation"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap 4.5.17",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
"hex",
|
"hex",
|
||||||
"jsonrpsee-types",
|
"jsonrpsee-types",
|
||||||
|
|
|
@ -17,18 +17,15 @@ homepage = "https://github.com/matter-labs/teepot"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
actix-http = "3"
|
actix-http = "3"
|
||||||
actix-tls = "3"
|
|
||||||
actix-web = { version = "4.5", features = ["rustls-0_22"] }
|
actix-web = { version = "4.5", features = ["rustls-0_22"] }
|
||||||
anyhow = "1.0.82"
|
anyhow = "1.0.82"
|
||||||
awc = { version = "3.4", features = ["rustls-0_22-webpki-roots"] }
|
awc = { version = "3.4", features = ["rustls-0_22-webpki-roots"] }
|
||||||
base64 = "0.22.0"
|
base64 = "0.22.0"
|
||||||
bitflags = "2.5"
|
|
||||||
bytemuck = { version = "1.15.0", features = ["derive", "min_const_generics", "extern_crate_std"] }
|
bytemuck = { version = "1.15.0", features = ["derive", "min_const_generics", "extern_crate_std"] }
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
clap = { version = "4.5", features = ["std", "derive", "env", "error-context", "help", "usage", "wrap_help"], default-features = false }
|
clap = { version = "4.5", features = ["std", "derive", "env", "error-context", "help", "usage", "wrap_help"], default-features = false }
|
||||||
const-oid = { version = "0.9", default-features = false }
|
const-oid = { version = "0.9", default-features = false }
|
||||||
ctrlc = "3.4"
|
ctrlc = "3.4"
|
||||||
der = "0.7.9"
|
|
||||||
enumset = { version = "1.1", features = ["serde"] }
|
enumset = { version = "1.1", features = ["serde"] }
|
||||||
futures-core = { version = "0.3.30", features = ["alloc"], default-features = false }
|
futures-core = { version = "0.3.30", features = ["alloc"], default-features = false }
|
||||||
getrandom = "0.2.14"
|
getrandom = "0.2.14"
|
||||||
|
@ -36,7 +33,6 @@ hex = { version = "0.4.3", features = ["std"], default-features = false }
|
||||||
intel-tee-quote-verification-rs = { package = "teepot-tee-quote-verification-rs", path = "crates/teepot-tee-quote-verification-rs", version = "0.3.0" }
|
intel-tee-quote-verification-rs = { package = "teepot-tee-quote-verification-rs", path = "crates/teepot-tee-quote-verification-rs", version = "0.3.0" }
|
||||||
intel-tee-quote-verification-sys = { version = "0.2.1" }
|
intel-tee-quote-verification-sys = { version = "0.2.1" }
|
||||||
jsonrpsee-types = { version = "0.23", default-features = false }
|
jsonrpsee-types = { version = "0.23", default-features = false }
|
||||||
log = "0.4"
|
|
||||||
num-integer = "0.1.46"
|
num-integer = "0.1.46"
|
||||||
num-traits = "0.2.18"
|
num-traits = "0.2.18"
|
||||||
p256 = "0.13.2"
|
p256 = "0.13.2"
|
||||||
|
@ -44,17 +40,15 @@ pgp = "0.13"
|
||||||
pkcs8 = { version = "0.10" }
|
pkcs8 = { version = "0.10" }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
reqwest = { version = "0.12", features = ["json"] }
|
reqwest = { version = "0.12", features = ["json"] }
|
||||||
ring = { version = "0.17.8", features = ["std"], default-features = false }
|
|
||||||
rsa = { version = "0.9.6", features = ["sha2", "pem"] }
|
rsa = { version = "0.9.6", features = ["sha2", "pem"] }
|
||||||
rustls = { version = "0.22" }
|
rustls = { version = "0.22" }
|
||||||
rustls-pemfile = "2"
|
|
||||||
sec1 = { version = "0.7.3", features = ["der"], default-features = false }
|
|
||||||
secp256k1 = { version = "0.29", features = ["rand-std", "global-context"] }
|
secp256k1 = { version = "0.29", features = ["rand-std", "global-context"] }
|
||||||
serde = { version = "1", features = ["derive", "rc"] }
|
serde = { version = "1", features = ["derive", "rc"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
serde_with = { version = "3.8", features = ["base64", "hex"] }
|
serde_with = { version = "3.8", features = ["base64", "hex"] }
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
signature = "2.2.0"
|
signature = "2.2.0"
|
||||||
|
tdx-attest-rs = { version = "0.1.2", git = "https://github.com/intel/SGXDataCenterAttestationPrimitives.git", rev = "aa239d25a437a28f3f4de92c38f5b6809faac842" }
|
||||||
teepot = { path = "crates/teepot" }
|
teepot = { path = "crates/teepot" }
|
||||||
testaso = "0.1.0"
|
testaso = "0.1.0"
|
||||||
thiserror = "1.0.59"
|
thiserror = "1.0.59"
|
||||||
|
@ -63,6 +57,7 @@ tracing = "0.1"
|
||||||
tracing-actix-web = "0.7"
|
tracing-actix-web = "0.7"
|
||||||
tracing-log = "0.2"
|
tracing-log = "0.2"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
tracing-test = { version = "0.2.5", features = ["no-env-filter"] }
|
||||||
url = "2.5.2"
|
url = "2.5.2"
|
||||||
webpki-roots = "0.26.1"
|
webpki-roots = "0.26.1"
|
||||||
x509-cert = { version = "0.2", features = ["builder", "signature"] }
|
x509-cert = { version = "0.2", features = ["builder", "signature"] }
|
||||||
|
|
|
@ -9,9 +9,7 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use secp256k1::{rand, Keypair, PublicKey, Secp256k1, SecretKey};
|
use secp256k1::{rand, Keypair, PublicKey, Secp256k1, SecretKey};
|
||||||
use std::ffi::OsString;
|
use std::{ffi::OsString, os::unix::process::CommandExt, process::Command};
|
||||||
use std::os::unix::process::CommandExt;
|
|
||||||
use std::process::Command;
|
|
||||||
use teepot::quote::get_quote;
|
use teepot::quote::get_quote;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use tracing_log::LogTracer;
|
use tracing_log::LogTracer;
|
||||||
|
@ -47,15 +45,15 @@ fn main_with_error() -> Result<()> {
|
||||||
let verifying_key = PublicKey::from_keypair(&keypair);
|
let verifying_key = PublicKey::from_keypair(&keypair);
|
||||||
let verifying_key_bytes = verifying_key.serialize();
|
let verifying_key_bytes = verifying_key.serialize();
|
||||||
let tee_type = match get_quote(verifying_key_bytes.as_ref()) {
|
let tee_type = match get_quote(verifying_key_bytes.as_ref()) {
|
||||||
Ok(quote) => {
|
Ok((tee_type, quote)) => {
|
||||||
// save quote to file
|
// save quote to file
|
||||||
std::fs::write(TEE_QUOTE_FILE, quote)?;
|
std::fs::write(TEE_QUOTE_FILE, quote)?;
|
||||||
"sgx"
|
tee_type.to_string()
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to get quote: {}", e);
|
error!("Failed to get quote: {}", e);
|
||||||
std::fs::write(TEE_QUOTE_FILE, [])?;
|
std::fs::write(TEE_QUOTE_FILE, [])?;
|
||||||
"none"
|
"none".to_string()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use secp256k1::{ecdsa::Signature, Message, PublicKey};
|
||||||
use std::{fs, io::Read, path::PathBuf, str::FromStr, time::UNIX_EPOCH};
|
use std::{fs, io::Read, path::PathBuf, str::FromStr, time::UNIX_EPOCH};
|
||||||
use teepot::{
|
use teepot::{
|
||||||
client::TcbLevel,
|
client::TcbLevel,
|
||||||
sgx::{tee_qv_get_collateral, verify_quote_with_collateral, QuoteVerificationResult},
|
quote::{error, tee_qv_get_collateral, verify_quote_with_collateral, QuoteVerificationResult},
|
||||||
};
|
};
|
||||||
use zksync_basic_types::H256;
|
use zksync_basic_types::H256;
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ fn verify_signature(
|
||||||
quote_verification_result: &QuoteVerificationResult,
|
quote_verification_result: &QuoteVerificationResult,
|
||||||
signature_args: &SignatureArgs,
|
signature_args: &SignatureArgs,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let reportdata = "e_verification_result.quote.report_body.reportdata;
|
let reportdata = "e_verification_result.quote.get_report_data();
|
||||||
let public_key = PublicKey::from_slice(reportdata)?;
|
let public_key = PublicKey::from_slice(reportdata)?;
|
||||||
println!("Public key from attestation quote: {}", public_key);
|
println!("Public key from attestation quote: {}", public_key);
|
||||||
let signature_bytes = fs::read(&signature_args.signature_file)?;
|
let signature_bytes = fs::read(&signature_args.signature_file)?;
|
||||||
|
@ -103,8 +103,10 @@ fn verify_attestation_quote(attestation_quote_bytes: &[u8]) -> Result<QuoteVerif
|
||||||
"Verifying quote ({} bytes)...",
|
"Verifying quote ({} bytes)...",
|
||||||
attestation_quote_bytes.len()
|
attestation_quote_bytes.len()
|
||||||
);
|
);
|
||||||
let collateral =
|
let collateral = error::QuoteContext::context(
|
||||||
tee_qv_get_collateral(attestation_quote_bytes).context("Failed to get collateral")?;
|
tee_qv_get_collateral(attestation_quote_bytes),
|
||||||
|
"Failed to get collateral",
|
||||||
|
)?;
|
||||||
let unix_time: i64 = std::time::SystemTime::now()
|
let unix_time: i64 = std::time::SystemTime::now()
|
||||||
.duration_since(UNIX_EPOCH)?
|
.duration_since(UNIX_EPOCH)?
|
||||||
.as_secs() as _;
|
.as_secs() as _;
|
||||||
|
@ -128,7 +130,6 @@ fn print_quote_verification_summary(quote_verification_result: &QuoteVerificatio
|
||||||
println!("\tInfo: Advisory ID: {advisory}");
|
println!("\tInfo: Advisory ID: {advisory}");
|
||||||
}
|
}
|
||||||
println!("Quote verification result: {}", tcblevel);
|
println!("Quote verification result: {}", tcblevel);
|
||||||
println!("mrsigner: {}", hex::encode(quote.report_body.mrsigner));
|
|
||||||
println!("mrenclave: {}", hex::encode(quote.report_body.mrenclave));
|
println!("{:#}", "e.report);
|
||||||
println!("reportdata: {}", hex::encode(quote.report_body.reportdata));
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (c) 2023-2024 Matter Labs
|
// Copyright (c) 2023-2024 Matter Labs
|
||||||
|
|
||||||
|
use crate::{args::AttestationPolicyArgs, client::JsonRpcClient};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use hex::encode;
|
use hex::encode;
|
||||||
use secp256k1::{constants::PUBLIC_KEY_SIZE, ecdsa::Signature, Message, PublicKey};
|
use secp256k1::{constants::PUBLIC_KEY_SIZE, ecdsa::Signature, Message, PublicKey};
|
||||||
use teepot::{
|
use teepot::{
|
||||||
client::TcbLevel,
|
client::TcbLevel,
|
||||||
sgx::{tee_qv_get_collateral, verify_quote_with_collateral, QuoteVerificationResult},
|
quote::{
|
||||||
|
error::QuoteContext, tee_qv_get_collateral, verify_quote_with_collateral,
|
||||||
|
QuoteVerificationResult, Report,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
use zksync_basic_types::{L1BatchNumber, H256};
|
use zksync_basic_types::{L1BatchNumber, H256};
|
||||||
|
|
||||||
use crate::args::AttestationPolicyArgs;
|
|
||||||
use crate::client::JsonRpcClient;
|
|
||||||
|
|
||||||
pub async fn verify_batch_proof(
|
pub async fn verify_batch_proof(
|
||||||
quote_verification_result: &QuoteVerificationResult<'_>,
|
quote_verification_result: &QuoteVerificationResult,
|
||||||
attestation_policy: &AttestationPolicyArgs,
|
attestation_policy: &AttestationPolicyArgs,
|
||||||
node_client: &impl JsonRpcClient,
|
node_client: &impl JsonRpcClient,
|
||||||
signature: &[u8],
|
signature: &[u8],
|
||||||
|
@ -28,7 +29,7 @@ pub async fn verify_batch_proof(
|
||||||
let batch_no = batch_number.0;
|
let batch_no = batch_number.0;
|
||||||
|
|
||||||
let public_key = PublicKey::from_slice(
|
let public_key = PublicKey::from_slice(
|
||||||
"e_verification_result.quote.report_body.reportdata[..PUBLIC_KEY_SIZE],
|
"e_verification_result.quote.get_report_data()[..PUBLIC_KEY_SIZE],
|
||||||
)?;
|
)?;
|
||||||
debug!(batch_no, "public key: {}", public_key);
|
debug!(batch_no, "public key: {}", public_key);
|
||||||
|
|
||||||
|
@ -45,8 +46,10 @@ pub async fn verify_batch_proof(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_attestation_quote(attestation_quote_bytes: &[u8]) -> Result<QuoteVerificationResult> {
|
pub fn verify_attestation_quote(attestation_quote_bytes: &[u8]) -> Result<QuoteVerificationResult> {
|
||||||
let collateral =
|
let collateral = QuoteContext::context(
|
||||||
tee_qv_get_collateral(attestation_quote_bytes).context("Failed to get collateral!")?;
|
tee_qv_get_collateral(attestation_quote_bytes),
|
||||||
|
"Failed to get collateral!",
|
||||||
|
)?;
|
||||||
let unix_time: i64 = std::time::SystemTime::now()
|
let unix_time: i64 = std::time::SystemTime::now()
|
||||||
.duration_since(std::time::UNIX_EPOCH)?
|
.duration_since(std::time::UNIX_EPOCH)?
|
||||||
.as_secs() as _;
|
.as_secs() as _;
|
||||||
|
@ -66,17 +69,19 @@ pub fn log_quote_verification_summary(quote_verification_result: &QuoteVerificat
|
||||||
warn!("Freshly fetched collateral expired!");
|
warn!("Freshly fetched collateral expired!");
|
||||||
}
|
}
|
||||||
let tcblevel = TcbLevel::from(*result);
|
let tcblevel = TcbLevel::from(*result);
|
||||||
info!(
|
let advisories = if advisories.is_empty() {
|
||||||
"Quote verification result: {}. mrsigner: {}, mrenclave: {}, reportdata: {}. Advisory IDs: {}.",
|
|
||||||
tcblevel,
|
|
||||||
hex::encode(quote.report_body.mrsigner),
|
|
||||||
hex::encode(quote.report_body.mrenclave),
|
|
||||||
hex::encode(quote.report_body.reportdata),
|
|
||||||
if advisories.is_empty() {
|
|
||||||
"None".to_string()
|
"None".to_string()
|
||||||
} else {
|
} else {
|
||||||
advisories.iter().map(ToString::to_string).collect::<Vec<_>>().join(", ")
|
advisories
|
||||||
}
|
.iter()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
};
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Quote verification result: {tcblevel}. {report}. Advisory IDs: {advisories}.",
|
||||||
|
report = "e.report
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +93,7 @@ fn verify_signature(signature: &[u8], public_key: PublicKey, root_hash: H256) ->
|
||||||
|
|
||||||
fn is_quote_matching_policy(
|
fn is_quote_matching_policy(
|
||||||
attestation_policy: &AttestationPolicyArgs,
|
attestation_policy: &AttestationPolicyArgs,
|
||||||
quote_verification_result: &QuoteVerificationResult<'_>,
|
quote_verification_result: &QuoteVerificationResult,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let quote = "e_verification_result.quote;
|
let quote = "e_verification_result.quote;
|
||||||
let tcblevel = TcbLevel::from(quote_verification_result.result);
|
let tcblevel = TcbLevel::from(quote_verification_result.result);
|
||||||
|
@ -100,17 +105,21 @@ fn is_quote_matching_policy(
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
match "e.report {
|
||||||
|
Report::SgxEnclave(report_body) => {
|
||||||
check_policy(
|
check_policy(
|
||||||
attestation_policy.sgx_mrsigners.as_deref(),
|
attestation_policy.sgx_mrsigners.as_deref(),
|
||||||
"e.report_body.mrsigner,
|
&report_body.mr_signer,
|
||||||
"mrsigner",
|
"mrsigner",
|
||||||
) && check_policy(
|
) && check_policy(
|
||||||
attestation_policy.sgx_mrenclaves.as_deref(),
|
attestation_policy.sgx_mrenclaves.as_deref(),
|
||||||
"e.report_body.mrenclave,
|
&report_body.mr_enclave,
|
||||||
"mrenclave",
|
"mrenclave",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn check_policy(policy: Option<&str>, actual_value: &[u8], field_name: &str) -> bool {
|
fn check_policy(policy: Option<&str>, actual_value: &[u8], field_name: &str) -> bool {
|
||||||
if let Some(valid_values) = policy {
|
if let Some(valid_values) = policy {
|
||||||
|
|
|
@ -37,6 +37,7 @@ serde_json.workspace = true
|
||||||
serde_with.workspace = true
|
serde_with.workspace = true
|
||||||
sha2.workspace = true
|
sha2.workspace = true
|
||||||
signature.workspace = true
|
signature.workspace = true
|
||||||
|
tdx-attest-rs.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
tracing-log.workspace = true
|
tracing-log.workspace = true
|
||||||
|
@ -48,5 +49,6 @@ zeroize.workspace = true
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
base64.workspace = true
|
base64.workspace = true
|
||||||
hex.workspace = true
|
|
||||||
testaso.workspace = true
|
testaso.workspace = true
|
||||||
|
tokio.workspace = true
|
||||||
|
tracing-test.workspace = true
|
||||||
|
|
|
@ -8,12 +8,12 @@
|
||||||
|
|
||||||
pub mod vault;
|
pub mod vault;
|
||||||
|
|
||||||
|
pub use crate::quote::verify_quote_with_collateral;
|
||||||
|
pub use crate::quote::QuoteVerificationResult;
|
||||||
|
use crate::quote::Report;
|
||||||
use crate::server::pki::{RaTlsCollateralExtension, RaTlsQuoteExtension};
|
use crate::server::pki::{RaTlsCollateralExtension, RaTlsQuoteExtension};
|
||||||
use crate::sgx::Quote;
|
use crate::sgx::Quote;
|
||||||
pub use crate::sgx::{
|
pub use crate::sgx::{parse_tcb_levels, sgx_ql_qv_result_t, EnumSet, TcbLevel};
|
||||||
parse_tcb_levels, sgx_ql_qv_result_t, verify_quote_with_collateral, EnumSet,
|
|
||||||
QuoteVerificationResult, TcbLevel,
|
|
||||||
};
|
|
||||||
use actix_web::http::header;
|
use actix_web::http::header;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use awc::{Client, Connector};
|
use awc::{Client, Connector};
|
||||||
|
@ -195,6 +195,10 @@ impl TeeConnection {
|
||||||
} = verify_quote_with_collateral(quote_bytes, collateral.as_ref(), current_time)
|
} = verify_quote_with_collateral(quote_bytes, collateral.as_ref(), current_time)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let Report::SgxEnclave(report_body) = quote.report else {
|
||||||
|
return Err(Error::General("TDX quote and not SGX quote".into()));
|
||||||
|
};
|
||||||
|
|
||||||
if collateral_expired || result != sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK {
|
if collateral_expired || result != sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK {
|
||||||
if collateral_expired {
|
if collateral_expired {
|
||||||
error!(
|
error!(
|
||||||
|
@ -230,10 +234,10 @@ impl TeeConnection {
|
||||||
if let Some(mrsigner) = &self.args.sgx_mrsigner {
|
if let Some(mrsigner) = &self.args.sgx_mrsigner {
|
||||||
let mrsigner_bytes = hex::decode(mrsigner)
|
let mrsigner_bytes = hex::decode(mrsigner)
|
||||||
.map_err(|e| Error::General(format!("Failed to decode mrsigner: {}", e)))?;
|
.map_err(|e| Error::General(format!("Failed to decode mrsigner: {}", e)))?;
|
||||||
if quote.report_body.mrsigner[..] != mrsigner_bytes {
|
if report_body.mr_signer[..] != mrsigner_bytes {
|
||||||
return Err(Error::General(format!(
|
return Err(Error::General(format!(
|
||||||
"mrsigner mismatch: got {}, expected {}",
|
"mrsigner mismatch: got {}, expected {}",
|
||||||
hex::encode(quote.report_body.mrsigner),
|
hex::encode(report_body.mr_signer),
|
||||||
&mrsigner
|
&mrsigner
|
||||||
)));
|
)));
|
||||||
} else {
|
} else {
|
||||||
|
@ -245,10 +249,10 @@ impl TeeConnection {
|
||||||
let mrenclave_bytes = hex::decode(mrenclave).map_err(|e| {
|
let mrenclave_bytes = hex::decode(mrenclave).map_err(|e| {
|
||||||
Error::General(format!("Failed to decode mrenclave: {}", e))
|
Error::General(format!("Failed to decode mrenclave: {}", e))
|
||||||
})?;
|
})?;
|
||||||
if quote.report_body.mrenclave[..] != mrenclave_bytes {
|
if report_body.mr_enclave[..] != mrenclave_bytes {
|
||||||
return Err(Error::General(format!(
|
return Err(Error::General(format!(
|
||||||
"mrenclave mismatch: got {}, expected {}",
|
"mrenclave mismatch: got {}, expected {}",
|
||||||
hex::encode(quote.report_body.mrenclave),
|
hex::encode(report_body.mr_enclave),
|
||||||
&mrenclave
|
&mrenclave
|
||||||
)));
|
)));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -7,26 +7,34 @@
|
||||||
#![deny(clippy::all)]
|
#![deny(clippy::all)]
|
||||||
|
|
||||||
use super::{AttestationArgs, TeeConnection};
|
use super::{AttestationArgs, TeeConnection};
|
||||||
use crate::json::http::{AuthRequest, AuthResponse};
|
use crate::{
|
||||||
use crate::server::pki::make_self_signed_cert;
|
json::http::{AuthRequest, AuthResponse},
|
||||||
use crate::server::{AnyHowResponseError, HttpResponseError, Status};
|
quote::error::QuoteContext,
|
||||||
pub use crate::sgx::{
|
server::{pki::make_self_signed_cert, AnyHowResponseError, HttpResponseError, Status},
|
||||||
parse_tcb_levels, sgx_gramine_get_quote, sgx_ql_qv_result_t, tee_qv_get_collateral,
|
};
|
||||||
verify_quote_with_collateral, Collateral, EnumSet, QuoteVerificationResult, TcbLevel,
|
pub use crate::{
|
||||||
|
quote::{verify_quote_with_collateral, QuoteVerificationResult},
|
||||||
|
sgx::{
|
||||||
|
parse_tcb_levels, sgx_gramine_get_quote, sgx_ql_qv_result_t, Collateral, EnumSet, TcbLevel,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use actix_http::error::PayloadError;
|
use actix_http::error::PayloadError;
|
||||||
use actix_web::http::header;
|
use actix_web::{http::header, ResponseError};
|
||||||
use actix_web::ResponseError;
|
|
||||||
use anyhow::{anyhow, bail, Context, Result};
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
use awc::error::{SendRequestError, StatusCode};
|
use awc::{
|
||||||
use awc::{Client, ClientResponse, Connector};
|
error::{SendRequestError, StatusCode},
|
||||||
|
Client, ClientResponse, Connector,
|
||||||
|
};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
|
use intel_tee_quote_verification_rs::tee_qv_get_collateral;
|
||||||
use rustls::ClientConfig;
|
use rustls::ClientConfig;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use std::fmt::{Display, Formatter};
|
use std::{
|
||||||
use std::sync::Arc;
|
fmt::{Display, Formatter},
|
||||||
use std::time;
|
sync::Arc,
|
||||||
|
time,
|
||||||
|
};
|
||||||
use tracing::{debug, error, info, trace};
|
use tracing::{debug, error, info, trace};
|
||||||
|
|
||||||
const VAULT_TOKEN_HEADER: &str = "X-Vault-Token";
|
const VAULT_TOKEN_HEADER: &str = "X-Vault-Token";
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod json;
|
pub mod json;
|
||||||
pub mod server;
|
|
||||||
pub mod sgx;
|
|
||||||
|
|
||||||
pub mod log;
|
pub mod log;
|
||||||
pub mod quote;
|
pub mod quote;
|
||||||
|
pub mod server;
|
||||||
|
pub mod sgx;
|
||||||
|
pub mod tdx;
|
||||||
|
|
85
crates/teepot/src/quote/error.rs
Normal file
85
crates/teepot/src/quote/error.rs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2024 Matter Labs
|
||||||
|
|
||||||
|
//! Quote Error type
|
||||||
|
|
||||||
|
use intel_tee_quote_verification_rs::quote3_error_t;
|
||||||
|
use std::io;
|
||||||
|
use tdx_attest_rs::tdx_attest_error_t;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Quote parsing error
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub enum QuoteError {
|
||||||
|
#[error("I/O Error")]
|
||||||
|
IoError { context: String, source: io::Error },
|
||||||
|
#[error("parsing bytes")]
|
||||||
|
ConvertError(#[from] bytemuck::PodCastError),
|
||||||
|
#[error("unsupported quote version")]
|
||||||
|
QuoteVersion,
|
||||||
|
#[error("invalid tee type")]
|
||||||
|
InvalidTeeType,
|
||||||
|
#[error("unsupported body type")]
|
||||||
|
UnsupportedBodyType,
|
||||||
|
#[error("quote verification error {msg}: {inner:?}")]
|
||||||
|
Quote3Error { inner: quote3_error_t, msg: String },
|
||||||
|
#[error("tdx_att_get_quote error {msg}: {inner:?}")]
|
||||||
|
TdxAttGetQuote {
|
||||||
|
inner: tdx_attest_error_t,
|
||||||
|
msg: String,
|
||||||
|
},
|
||||||
|
#[error("invalid version")]
|
||||||
|
InvalidVersion,
|
||||||
|
#[error("report data too long")]
|
||||||
|
ReportDataSize,
|
||||||
|
#[error("can't get a quote: unknown TEE")]
|
||||||
|
UnknownTee,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<tdx_attest_error_t> for QuoteError {
|
||||||
|
fn from(code: tdx_attest_error_t) -> Self {
|
||||||
|
Self::TdxAttGetQuote {
|
||||||
|
inner: code,
|
||||||
|
msg: "code".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Usability trait for easy QuoteError annotation
|
||||||
|
pub trait QuoteContext {
|
||||||
|
/// The Ok Type
|
||||||
|
type Ok;
|
||||||
|
/// The Context
|
||||||
|
fn context<I: Into<String>>(self, msg: I) -> Result<Self::Ok, QuoteError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> QuoteContext for Result<T, std::io::Error> {
|
||||||
|
type Ok = T;
|
||||||
|
fn context<I: Into<String>>(self, msg: I) -> Result<T, QuoteError> {
|
||||||
|
self.map_err(|e| QuoteError::IoError {
|
||||||
|
context: msg.into(),
|
||||||
|
source: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> QuoteContext for Result<T, quote3_error_t> {
|
||||||
|
type Ok = T;
|
||||||
|
fn context<I: Into<String>>(self, msg: I) -> Result<T, QuoteError> {
|
||||||
|
self.map_err(|e| QuoteError::Quote3Error {
|
||||||
|
msg: msg.into(),
|
||||||
|
inner: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> QuoteContext for Result<T, tdx_attest_error_t> {
|
||||||
|
type Ok = T;
|
||||||
|
fn context<I: Into<String>>(self, msg: I) -> Result<T, QuoteError> {
|
||||||
|
self.map_err(|e| QuoteError::TdxAttGetQuote {
|
||||||
|
msg: msg.into(),
|
||||||
|
inner: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,40 +1,733 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
// Copyright (c) 2023-2024 Matter Labs
|
// Copyright (c) 2023-2024 Matter Labs
|
||||||
|
|
||||||
|
// Parts of it are Copyright (c) 2024 Phala Network
|
||||||
|
// and copied from https://github.com/Phala-Network/dcap-qvl
|
||||||
|
|
||||||
//! Get a quote from a TEE
|
//! Get a quote from a TEE
|
||||||
|
pub mod error;
|
||||||
|
|
||||||
use crate::sgx::sgx_gramine_get_quote;
|
use crate::{
|
||||||
use std::io;
|
quote::error::{QuoteContext as _, QuoteError},
|
||||||
|
sgx::sgx_gramine_get_quote,
|
||||||
|
tdx::tgx_get_quote,
|
||||||
|
};
|
||||||
|
use bytemuck::{cast_slice, AnyBitPattern};
|
||||||
|
use intel_tee_quote_verification_rs::{
|
||||||
|
sgx_ql_qv_result_t, sgx_ql_qv_supplemental_t, tee_get_supplemental_data_version_and_size,
|
||||||
|
tee_supp_data_descriptor_t, tee_verify_quote, Collateral,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
ffi::CStr,
|
||||||
|
fmt::{Display, Formatter},
|
||||||
|
io::Read,
|
||||||
|
mem,
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
use tracing::{trace, warn};
|
||||||
|
|
||||||
|
pub use intel_tee_quote_verification_rs::tee_qv_get_collateral;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[error("{msg}")]
|
pub const TEE_TYPE_SGX: u32 = 0x00000000;
|
||||||
pub struct GetQuoteError {
|
#[allow(missing_docs)]
|
||||||
pub(crate) msg: Box<str>,
|
pub const TEE_TYPE_TDX: u32 = 0x00000081;
|
||||||
#[source] // optional if field name is `source`
|
|
||||||
pub(crate) source: io::Error,
|
#[allow(missing_docs)]
|
||||||
|
pub const BODY_SGX_ENCLAVE_REPORT_TYPE: u16 = 1;
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub const BODY_TD_REPORT10_TYPE: u16 = 2;
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub const BODY_TD_REPORT15_TYPE: u16 = 3;
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub const ENCLAVE_REPORT_BYTE_LEN: usize = 384;
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub const ECDSA_SIGNATURE_BYTE_LEN: usize = 64;
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub const ECDSA_PUBKEY_BYTE_LEN: usize = 64;
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub const QE_REPORT_SIG_BYTE_LEN: usize = ECDSA_SIGNATURE_BYTE_LEN;
|
||||||
|
|
||||||
|
mod serde_bytes {
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
pub(crate) trait FromBytes {
|
||||||
|
fn from_bytes(bytes: Vec<u8>) -> Option<Self>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
impl FromBytes for Vec<u8> {
|
||||||
|
fn from_bytes(bytes: Vec<u8>) -> Option<Self> {
|
||||||
|
Some(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<const N: usize> FromBytes for [u8; N] {
|
||||||
|
fn from_bytes(bytes: Vec<u8>) -> Option<Self> {
|
||||||
|
bytes.try_into().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn serialize<S: serde::Serializer>(
|
||||||
|
data: impl AsRef<[u8]>,
|
||||||
|
serializer: S,
|
||||||
|
) -> Result<S::Ok, S::Error> {
|
||||||
|
let hex_str = hex::encode(data);
|
||||||
|
serializer.serialize_str(&hex_str)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn deserialize<'de, D: serde::Deserializer<'de>, T: FromBytes>(
|
||||||
|
deserializer: D,
|
||||||
|
) -> Result<T, D::Error> {
|
||||||
|
let hex_str = String::deserialize(deserializer)?;
|
||||||
|
let bytes = hex::decode(hex_str).map_err(serde::de::Error::custom)?;
|
||||||
|
T::from_bytes(bytes).ok_or_else(|| serde::de::Error::custom("invalid bytes"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait that allows zero-copy read of value-references from slices in LE format.
|
||||||
|
pub trait Decode: Sized {
|
||||||
|
/// Attempt to deserialise the value from input.
|
||||||
|
fn decode<I: Read>(input: &mut I) -> Result<Self, error::QuoteError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AnyBitPattern> Decode for T {
|
||||||
|
fn decode<I: Read>(input: &mut I) -> Result<Self, error::QuoteError> {
|
||||||
|
let mut bytes = vec![0u8; size_of::<T>()];
|
||||||
|
input.read(&mut bytes).context("parsing bytes")?;
|
||||||
|
bytemuck::try_pod_read_unaligned(&bytes).map_err(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Data<T> {
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
_marker: core::marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Serialize for Data<T> {
|
||||||
|
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
serde_bytes::serialize(&self.data, serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de, T> Deserialize<'de> for Data<T> {
|
||||||
|
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
let data = serde_bytes::deserialize(deserializer)?;
|
||||||
|
Ok(Data {
|
||||||
|
data,
|
||||||
|
_marker: core::marker::PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Decode + Into<u64>> Decode for Data<T> {
|
||||||
|
fn decode<I: Read>(input: &mut I) -> Result<Self, QuoteError> {
|
||||||
|
let len = T::decode(input)?;
|
||||||
|
let mut data = vec![0u8; len.into() as usize];
|
||||||
|
input.read(&mut data).context("reading bytes")?;
|
||||||
|
Ok(Data {
|
||||||
|
data,
|
||||||
|
_marker: core::marker::PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(AnyBitPattern, Debug, Serialize, Deserialize, Copy, Clone)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct Header {
|
||||||
|
pub version: u16,
|
||||||
|
pub attestation_key_type: u16,
|
||||||
|
pub tee_type: u32,
|
||||||
|
pub qe_svn: u16,
|
||||||
|
pub pce_svn: u16,
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub qe_vendor_id: [u8; 16],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub user_data: [u8; 20],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(AnyBitPattern, Debug, Copy, Clone)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct Body {
|
||||||
|
pub body_type: u16,
|
||||||
|
pub size: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, AnyBitPattern, Debug, Clone, Copy)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct EnclaveReport {
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub cpu_svn: [u8; 16],
|
||||||
|
pub misc_select: u32,
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub reserved1: [u8; 28],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub attributes: [u8; 16],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub mr_enclave: [u8; 32],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub reserved2: [u8; 32],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub mr_signer: [u8; 32],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub reserved3: [u8; 96],
|
||||||
|
pub isv_prod_id: u16,
|
||||||
|
pub isv_svn: u16,
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub reserved4: [u8; 60],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub report_data: [u8; 64],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(AnyBitPattern, Debug, Copy, Clone, Serialize, Deserialize)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct TDReport10 {
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub tee_tcb_svn: [u8; 16],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub mr_seam: [u8; 48],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub mr_signer_seam: [u8; 48],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub seam_attributes: [u8; 8],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub td_attributes: [u8; 8],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub xfam: [u8; 8],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub mr_td: [u8; 48],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub mr_config_id: [u8; 48],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub mr_owner: [u8; 48],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub mr_owner_config: [u8; 48],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub rt_mr0: [u8; 48],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub rt_mr1: [u8; 48],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub rt_mr2: [u8; 48],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub rt_mr3: [u8; 48],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub report_data: [u8; 64],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(AnyBitPattern, Debug, Copy, Clone, Serialize, Deserialize)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct TDReport15 {
|
||||||
|
pub base: TDReport10,
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub tee_tcb_svn2: [u8; 16],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub mr_service_td: [u8; 48],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct CertificationData {
|
||||||
|
pub cert_type: u16,
|
||||||
|
pub body: Data<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for CertificationData {
|
||||||
|
fn decode<I: Read>(input: &mut I) -> Result<Self, QuoteError> {
|
||||||
|
Ok(Self {
|
||||||
|
cert_type: Decode::decode(input)?,
|
||||||
|
body: Decode::decode(input)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Debug for CertificationData {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
let body_str = String::from_utf8_lossy(&self.body.data);
|
||||||
|
f.debug_struct("CertificationData")
|
||||||
|
.field("cert_type", &self.cert_type)
|
||||||
|
.field("body", &body_str)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct QEReportCertificationData {
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub qe_report: [u8; ENCLAVE_REPORT_BYTE_LEN],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub qe_report_signature: [u8; QE_REPORT_SIG_BYTE_LEN],
|
||||||
|
pub qe_auth_data: Data<u16>,
|
||||||
|
pub certification_data: CertificationData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for QEReportCertificationData {
|
||||||
|
fn decode<I: Read>(input: &mut I) -> Result<Self, QuoteError> {
|
||||||
|
Ok(Self {
|
||||||
|
qe_report: Decode::decode(input)?,
|
||||||
|
qe_report_signature: Decode::decode(input)?,
|
||||||
|
qe_auth_data: Decode::decode(input)?,
|
||||||
|
certification_data: Decode::decode(input)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct AuthDataV3 {
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub ecdsa_signature: [u8; ECDSA_SIGNATURE_BYTE_LEN],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub ecdsa_attestation_key: [u8; ECDSA_PUBKEY_BYTE_LEN],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub qe_report: [u8; ENCLAVE_REPORT_BYTE_LEN],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub qe_report_signature: [u8; QE_REPORT_SIG_BYTE_LEN],
|
||||||
|
pub qe_auth_data: Data<u16>,
|
||||||
|
pub certification_data: CertificationData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for AuthDataV3 {
|
||||||
|
fn decode<I: Read>(input: &mut I) -> Result<Self, QuoteError> {
|
||||||
|
Ok(Self {
|
||||||
|
ecdsa_signature: Decode::decode(input)?,
|
||||||
|
ecdsa_attestation_key: Decode::decode(input)?,
|
||||||
|
qe_report: Decode::decode(input)?,
|
||||||
|
qe_report_signature: Decode::decode(input)?,
|
||||||
|
qe_auth_data: Decode::decode(input)?,
|
||||||
|
certification_data: Decode::decode(input)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct AuthDataV4 {
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub ecdsa_signature: [u8; ECDSA_SIGNATURE_BYTE_LEN],
|
||||||
|
#[serde(with = "serde_bytes")]
|
||||||
|
pub ecdsa_attestation_key: [u8; ECDSA_PUBKEY_BYTE_LEN],
|
||||||
|
pub certification_data: CertificationData,
|
||||||
|
pub qe_report_data: QEReportCertificationData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthDataV4 {
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub fn into_v3(self) -> AuthDataV3 {
|
||||||
|
AuthDataV3 {
|
||||||
|
ecdsa_signature: self.ecdsa_signature,
|
||||||
|
ecdsa_attestation_key: self.ecdsa_attestation_key,
|
||||||
|
qe_report: self.qe_report_data.qe_report,
|
||||||
|
qe_report_signature: self.qe_report_data.qe_report_signature,
|
||||||
|
qe_auth_data: self.qe_report_data.qe_auth_data,
|
||||||
|
certification_data: self.qe_report_data.certification_data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for AuthDataV4 {
|
||||||
|
fn decode<I: Read>(input: &mut I) -> Result<Self, error::QuoteError> {
|
||||||
|
let ecdsa_signature = Decode::decode(input)?;
|
||||||
|
let ecdsa_attestation_key = Decode::decode(input)?;
|
||||||
|
let certification_data: CertificationData = Decode::decode(input)?;
|
||||||
|
let qe_report_data =
|
||||||
|
QEReportCertificationData::decode(&mut &certification_data.body.data[..])?;
|
||||||
|
Ok(AuthDataV4 {
|
||||||
|
ecdsa_signature,
|
||||||
|
ecdsa_attestation_key,
|
||||||
|
certification_data,
|
||||||
|
qe_report_data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub enum AuthData {
|
||||||
|
V3(AuthDataV3),
|
||||||
|
V4(AuthDataV4),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthData {
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub fn into_v3(self) -> AuthDataV3 {
|
||||||
|
match self {
|
||||||
|
AuthData::V3(data) => data,
|
||||||
|
AuthData::V4(data) => data.into_v3(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_auth_data(ver: u16, input: &mut &[u8]) -> Result<AuthData, error::QuoteError> {
|
||||||
|
match ver {
|
||||||
|
3 => {
|
||||||
|
let auth_data = AuthDataV3::decode(input)?;
|
||||||
|
Ok(AuthData::V3(auth_data))
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
let auth_data = AuthDataV4::decode(input)?;
|
||||||
|
Ok(AuthData::V4(auth_data))
|
||||||
|
}
|
||||||
|
_ => Err(error::QuoteError::QuoteVersion),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[repr(C)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum Report {
|
||||||
|
SgxEnclave(EnclaveReport),
|
||||||
|
TD10(TDReport10),
|
||||||
|
TD15(TDReport15),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Report {
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub fn is_sgx(&self) -> bool {
|
||||||
|
matches!(self, Report::SgxEnclave(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub fn as_td10(&self) -> Option<&TDReport10> {
|
||||||
|
match self {
|
||||||
|
Report::TD10(report) => Some(report),
|
||||||
|
Report::TD15(report) => Some(&report.base),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub fn as_td15(&self) -> Option<&TDReport15> {
|
||||||
|
match self {
|
||||||
|
Report::TD15(report) => Some(report),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub fn as_sgx(&self) -> Option<&EnclaveReport> {
|
||||||
|
match self {
|
||||||
|
Report::SgxEnclave(report) => Some(report),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Report {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
fn space_or_newline(f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if f.alternate() {
|
||||||
|
writeln!(f)
|
||||||
|
} else {
|
||||||
|
write!(f, " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match self {
|
||||||
|
Report::SgxEnclave(report_body) => {
|
||||||
|
write!(f, "mrsigner: {}", hex::encode(report_body.mr_signer))?;
|
||||||
|
space_or_newline(f)?;
|
||||||
|
write!(f, "mrenclave: {}", hex::encode(report_body.mr_enclave))?;
|
||||||
|
space_or_newline(f)?;
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"reportdata: {}",
|
||||||
|
hex::encode(report_body.report_data.as_slice())
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Report::TD10(report_body) => {
|
||||||
|
write!(f, "mrtd: {}", hex::encode(report_body.mr_td))?;
|
||||||
|
space_or_newline(f)?;
|
||||||
|
write!(f, "rtmr0: {}", hex::encode(report_body.rt_mr0))?;
|
||||||
|
space_or_newline(f)?;
|
||||||
|
write!(f, "rtmr1: {}", hex::encode(report_body.rt_mr1))?;
|
||||||
|
space_or_newline(f)?;
|
||||||
|
write!(f, "rtmr2: {}", hex::encode(report_body.rt_mr2))?;
|
||||||
|
space_or_newline(f)?;
|
||||||
|
write!(f, "rtmr3: {}", hex::encode(report_body.rt_mr3))?;
|
||||||
|
space_or_newline(f)?;
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"reportdata: {}",
|
||||||
|
hex::encode(report_body.report_data.as_slice())
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Report::TD15(report_body) => {
|
||||||
|
let report_body = &report_body.base;
|
||||||
|
write!(f, "mrtd: {}", hex::encode(report_body.mr_td))?;
|
||||||
|
space_or_newline(f)?;
|
||||||
|
write!(f, "rtmr0: {}", hex::encode(report_body.rt_mr0))?;
|
||||||
|
space_or_newline(f)?;
|
||||||
|
write!(f, "rtmr1: {}", hex::encode(report_body.rt_mr1))?;
|
||||||
|
space_or_newline(f)?;
|
||||||
|
write!(f, "rtmr2: {}", hex::encode(report_body.rt_mr2))?;
|
||||||
|
space_or_newline(f)?;
|
||||||
|
write!(f, "rtmr3: {}", hex::encode(report_body.rt_mr3))?;
|
||||||
|
space_or_newline(f)?;
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"reportdata: {}",
|
||||||
|
hex::encode(report_body.report_data.as_slice())
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Quote {
|
||||||
|
pub header: Header,
|
||||||
|
pub report: Report,
|
||||||
|
pub auth_data: AuthData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for Quote {
|
||||||
|
fn decode<I: Read>(input: &mut I) -> Result<Self, error::QuoteError> {
|
||||||
|
let header = Header::decode(input)?;
|
||||||
|
trace!(?header);
|
||||||
|
let report;
|
||||||
|
match header.version {
|
||||||
|
3 => {
|
||||||
|
if header.tee_type != TEE_TYPE_SGX {
|
||||||
|
return Err(error::QuoteError::InvalidTeeType);
|
||||||
|
}
|
||||||
|
report = Report::SgxEnclave(EnclaveReport::decode(input)?);
|
||||||
|
}
|
||||||
|
4 => match header.tee_type {
|
||||||
|
TEE_TYPE_SGX => {
|
||||||
|
report = Report::SgxEnclave(EnclaveReport::decode(input)?);
|
||||||
|
}
|
||||||
|
TEE_TYPE_TDX => {
|
||||||
|
report = Report::TD10(TDReport10::decode(input)?);
|
||||||
|
}
|
||||||
|
_ => return Err(error::QuoteError::InvalidTeeType),
|
||||||
|
},
|
||||||
|
5 => {
|
||||||
|
let body = Body::decode(input)?;
|
||||||
|
match body.body_type {
|
||||||
|
BODY_SGX_ENCLAVE_REPORT_TYPE => {
|
||||||
|
report = Report::SgxEnclave(EnclaveReport::decode(input)?);
|
||||||
|
}
|
||||||
|
BODY_TD_REPORT10_TYPE => {
|
||||||
|
report = Report::TD10(TDReport10::decode(input)?);
|
||||||
|
}
|
||||||
|
BODY_TD_REPORT15_TYPE => {
|
||||||
|
report = Report::TD15(TDReport15::decode(input)?);
|
||||||
|
}
|
||||||
|
_ => return Err(error::QuoteError::UnsupportedBodyType),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return Err(error::QuoteError::QuoteVersion),
|
||||||
|
}
|
||||||
|
let data = Data::<u32>::decode(input)?;
|
||||||
|
let auth_data = decode_auth_data(header.version, &mut &data.data[..])?;
|
||||||
|
Ok(Quote {
|
||||||
|
header,
|
||||||
|
report,
|
||||||
|
auth_data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Quote {
|
||||||
|
/// Parse a TEE quote from a byte slice.
|
||||||
|
pub fn parse(quote: &[u8]) -> Result<Self, QuoteError> {
|
||||||
|
let mut input = quote;
|
||||||
|
let quote = Quote::decode(&mut input)?;
|
||||||
|
Ok(quote)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the report data
|
||||||
|
pub fn get_report_data(&self) -> &[u8] {
|
||||||
|
match &self.report {
|
||||||
|
Report::SgxEnclave(r) => r.report_data.as_slice(),
|
||||||
|
Report::TD10(r) => r.report_data.as_slice(),
|
||||||
|
Report::TD15(r) => r.base.report_data.as_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TEE type
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum TEEType {
|
||||||
|
/// Intel SGX
|
||||||
|
SGX,
|
||||||
|
/// Intel TDX
|
||||||
|
TDX,
|
||||||
|
/// AMD SEV-SNP
|
||||||
|
SNP,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TEEType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let str = match self {
|
||||||
|
TEEType::SGX => "sgx",
|
||||||
|
TEEType::TDX => "tdx",
|
||||||
|
TEEType::SNP => "snp",
|
||||||
|
};
|
||||||
|
write!(f, "{}", str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for TEEType {
|
||||||
|
type Err = String;
|
||||||
|
fn from_str(s: &str) -> Result<Self, String> {
|
||||||
|
match s.to_lowercase().as_str() {
|
||||||
|
"sgx" => Ok(TEEType::SGX),
|
||||||
|
"tdx" => Ok(TEEType::TDX),
|
||||||
|
"snp" => Ok(TEEType::SNP),
|
||||||
|
_ => Err("Invalid TEE type".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the attestation quote from a TEE
|
/// Get the attestation quote from a TEE
|
||||||
pub fn get_quote(report_data: &[u8]) -> Result<Box<[u8]>, GetQuoteError> {
|
pub fn get_quote(report_data: &[u8]) -> Result<(TEEType, Box<[u8]>), QuoteError> {
|
||||||
// check, if we are running in a TEE
|
// check, if we are running in a TEE
|
||||||
if std::fs::metadata("/dev/attestation").is_ok() {
|
if std::fs::metadata("/dev/attestation").is_ok() {
|
||||||
if report_data.len() > 64 {
|
if report_data.len() > 64 {
|
||||||
return Err(GetQuoteError {
|
return Err(QuoteError::ReportDataSize);
|
||||||
msg: "Report data too long".into(),
|
|
||||||
source: io::Error::new(io::ErrorKind::Other, "Report data too long"),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut report_data_fixed = [0u8; 64];
|
let mut report_data_fixed = [0u8; 64];
|
||||||
report_data_fixed[..report_data.len()].copy_from_slice(report_data);
|
report_data_fixed[..report_data.len()].copy_from_slice(report_data);
|
||||||
|
|
||||||
sgx_gramine_get_quote(&report_data_fixed)
|
Ok((TEEType::SGX, sgx_gramine_get_quote(&report_data_fixed)?))
|
||||||
|
} else if std::fs::metadata("/dev/tdx_guest").is_ok() {
|
||||||
|
if report_data.len() > 64 {
|
||||||
|
return Err(QuoteError::ReportDataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut report_data_fixed = [0u8; 64];
|
||||||
|
report_data_fixed[..report_data.len()].copy_from_slice(report_data);
|
||||||
|
|
||||||
|
Ok((TEEType::TDX, tgx_get_quote(&report_data_fixed)?))
|
||||||
} else {
|
} else {
|
||||||
// if not, return an error
|
// if not, return an error
|
||||||
Err(GetQuoteError {
|
Err(QuoteError::UnknownTee)
|
||||||
msg: "Not running in a TEE".into(),
|
|
||||||
source: io::Error::new(io::ErrorKind::Other, "Not running in a TEE"),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The result of the quote verification
|
||||||
|
pub struct QuoteVerificationResult {
|
||||||
|
/// the raw result
|
||||||
|
pub result: sgx_ql_qv_result_t,
|
||||||
|
/// indicates if the collateral is expired
|
||||||
|
pub collateral_expired: bool,
|
||||||
|
/// the earliest expiration date of the collateral
|
||||||
|
pub earliest_expiration_date: i64,
|
||||||
|
/// Date of the TCB level
|
||||||
|
pub tcb_level_date_tag: i64,
|
||||||
|
/// the advisory string
|
||||||
|
pub advisories: Vec<String>,
|
||||||
|
/// the quote
|
||||||
|
pub quote: Quote,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies a quote with optional collateral material
|
||||||
|
pub fn verify_quote_with_collateral(
|
||||||
|
quote: &[u8],
|
||||||
|
collateral: Option<&Collateral>,
|
||||||
|
current_time: i64,
|
||||||
|
) -> Result<QuoteVerificationResult, QuoteError> {
|
||||||
|
let mut supp_data: mem::MaybeUninit<sgx_ql_qv_supplemental_t> = mem::MaybeUninit::zeroed();
|
||||||
|
let mut supp_data_desc = tee_supp_data_descriptor_t {
|
||||||
|
major_version: 0,
|
||||||
|
data_size: 0,
|
||||||
|
p_data: supp_data.as_mut_ptr() as *mut u8,
|
||||||
|
};
|
||||||
|
trace!("tee_get_supplemental_data_version_and_size");
|
||||||
|
let (_, supp_size) =
|
||||||
|
tee_get_supplemental_data_version_and_size(quote).map_err(|e| QuoteError::Quote3Error {
|
||||||
|
msg: "tee_get_supplemental_data_version_and_size".into(),
|
||||||
|
inner: e,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
trace!(
|
||||||
|
"tee_get_supplemental_data_version_and_size supp_size: {}",
|
||||||
|
supp_size
|
||||||
|
);
|
||||||
|
|
||||||
|
if supp_size == mem::size_of::<sgx_ql_qv_supplemental_t>() as u32 {
|
||||||
|
supp_data_desc.data_size = supp_size;
|
||||||
|
} else {
|
||||||
|
supp_data_desc.data_size = 0;
|
||||||
|
trace!(
|
||||||
|
"tee_get_supplemental_data_version_and_size supp_size: {}",
|
||||||
|
supp_size
|
||||||
|
);
|
||||||
|
trace!(
|
||||||
|
"mem::size_of::<sgx_ql_qv_supplemental_t>(): {}",
|
||||||
|
mem::size_of::<sgx_ql_qv_supplemental_t>()
|
||||||
|
);
|
||||||
|
warn!("Quote supplemental data size is different between DCAP QVL and QvE, please make sure you installed DCAP QVL and QvE from same release.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let p_supplemental_data = match supp_data_desc.data_size {
|
||||||
|
0 => None,
|
||||||
|
_ => Some(&mut supp_data_desc),
|
||||||
|
};
|
||||||
|
|
||||||
|
let has_sup = p_supplemental_data.is_some();
|
||||||
|
|
||||||
|
trace!("tee_verify_quote");
|
||||||
|
|
||||||
|
let (collateral_expiration_status, result) =
|
||||||
|
tee_verify_quote(quote, collateral, current_time, None, p_supplemental_data)
|
||||||
|
.context("tee_verify_quote")?;
|
||||||
|
|
||||||
|
trace!("tee_verify_quote end");
|
||||||
|
|
||||||
|
// check supplemental data if necessary
|
||||||
|
let (advisories, earliest_expiration_date, tcb_level_date_tag) = if has_sup {
|
||||||
|
unsafe {
|
||||||
|
let supp_data = supp_data.assume_init();
|
||||||
|
// convert to valid UTF-8 string
|
||||||
|
let ads = CStr::from_bytes_until_nul(cast_slice(&supp_data.sa_list[..]))
|
||||||
|
.ok()
|
||||||
|
.and_then(|s| CStr::to_str(s).ok())
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|s| s.split(',').map(str::trim).map(String::from))
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
.collect();
|
||||||
|
(
|
||||||
|
ads,
|
||||||
|
supp_data.earliest_expiration_date,
|
||||||
|
supp_data.tcb_level_date_tag,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(vec![], 0, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!("Quote::parse");
|
||||||
|
let quote = Quote::parse(quote)?;
|
||||||
|
|
||||||
|
let res = QuoteVerificationResult {
|
||||||
|
collateral_expired: collateral_expiration_status != 0,
|
||||||
|
earliest_expiration_date,
|
||||||
|
tcb_level_date_tag,
|
||||||
|
result,
|
||||||
|
quote,
|
||||||
|
advisories,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
|
@ -3,17 +3,22 @@
|
||||||
|
|
||||||
//! Common attestation API for all TEEs
|
//! Common attestation API for all TEEs
|
||||||
|
|
||||||
use crate::client::AttestationArgs;
|
use crate::{
|
||||||
use crate::json::http::AttestationResponse;
|
client::AttestationArgs,
|
||||||
use crate::sgx::{
|
json::http::AttestationResponse,
|
||||||
parse_tcb_levels, sgx_gramine_get_quote, tee_qv_get_collateral, verify_quote_with_collateral,
|
quote::{
|
||||||
Collateral, EnumSet, QuoteVerificationResult, TcbLevel,
|
error::QuoteContext, get_quote, verify_quote_with_collateral, QuoteVerificationResult,
|
||||||
|
},
|
||||||
|
sgx::{parse_tcb_levels, Collateral, EnumSet, TcbLevel},
|
||||||
};
|
};
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
|
use intel_tee_quote_verification_rs::tee_qv_get_collateral;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::{
|
||||||
use std::time::{Duration, UNIX_EPOCH};
|
sync::{Arc, RwLock},
|
||||||
|
time::{Duration, UNIX_EPOCH},
|
||||||
|
};
|
||||||
use tracing::{debug, error, info, trace, warn};
|
use tracing::{debug, error, info, trace, warn};
|
||||||
|
|
||||||
struct Attestation {
|
struct Attestation {
|
||||||
|
@ -53,7 +58,7 @@ pub fn get_quote_and_collateral(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let myquote = sgx_gramine_get_quote(report_data).context("Failed to get own quote")?;
|
let (_tee_type, myquote) = get_quote(report_data).context("Failed to get own quote")?;
|
||||||
let collateral = tee_qv_get_collateral(&myquote).context("Failed to get own collateral")?;
|
let collateral = tee_qv_get_collateral(&myquote).context("Failed to get own collateral")?;
|
||||||
|
|
||||||
let QuoteVerificationResult {
|
let QuoteVerificationResult {
|
||||||
|
@ -89,8 +94,8 @@ pub fn get_quote_and_collateral(
|
||||||
"Earliest expiration in {:?}",
|
"Earliest expiration in {:?}",
|
||||||
Duration::from_secs((earliest_expiration_date - unix_time) as _)
|
Duration::from_secs((earliest_expiration_date - unix_time) as _)
|
||||||
);
|
);
|
||||||
info!("mrsigner: {}", hex::encode(quote.report_body.mrsigner));
|
|
||||||
info!("mrenclave: {}", hex::encode(quote.report_body.mrenclave));
|
info!("{:#}", quote.report);
|
||||||
|
|
||||||
let quote: Arc<[u8]> = Arc::from(myquote);
|
let quote: Arc<[u8]> = Arc::from(myquote);
|
||||||
let collateral = Arc::from(collateral);
|
let collateral = Arc::from(collateral);
|
||||||
|
|
|
@ -3,39 +3,38 @@
|
||||||
|
|
||||||
//! Create a private key and a signed and self-signed certificates
|
//! Create a private key and a signed and self-signed certificates
|
||||||
|
|
||||||
use crate::quote::get_quote;
|
use crate::quote::{error::QuoteContext, get_quote};
|
||||||
use crate::sgx::tee_qv_get_collateral;
|
pub use crate::sgx::{parse_tcb_levels, sgx_ql_qv_result_t, EnumSet, TcbLevel};
|
||||||
pub use crate::sgx::{
|
|
||||||
parse_tcb_levels, sgx_ql_qv_result_t, verify_quote_with_collateral, EnumSet,
|
|
||||||
QuoteVerificationResult, TcbLevel,
|
|
||||||
};
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use const_oid::db::rfc5280::{ID_KP_CLIENT_AUTH, ID_KP_SERVER_AUTH};
|
use const_oid::{
|
||||||
use const_oid::AssociatedOid;
|
db::rfc5280::{ID_KP_CLIENT_AUTH, ID_KP_SERVER_AUTH},
|
||||||
|
AssociatedOid,
|
||||||
|
};
|
||||||
use getrandom::getrandom;
|
use getrandom::getrandom;
|
||||||
use p256::ecdsa::DerSignature;
|
use intel_tee_quote_verification_rs::tee_qv_get_collateral;
|
||||||
use p256::pkcs8::EncodePrivateKey;
|
use p256::{ecdsa::DerSignature, pkcs8::EncodePrivateKey};
|
||||||
use pkcs8::der;
|
use pkcs8::der;
|
||||||
use rustls::pki_types::PrivatePkcs8KeyDer;
|
use rustls::pki_types::PrivatePkcs8KeyDer;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use signature::Signer;
|
use signature::Signer;
|
||||||
use std::str::FromStr;
|
use std::{str::FromStr, time::Duration};
|
||||||
use std::time::Duration;
|
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use x509_cert::builder::{Builder, CertificateBuilder, Profile};
|
use x509_cert::{
|
||||||
use x509_cert::der::pem::LineEnding;
|
builder::{Builder, CertificateBuilder, Profile},
|
||||||
use x509_cert::der::{asn1::OctetString, Encode as _, EncodePem as _, Length};
|
der::{asn1::OctetString, pem::LineEnding, Encode as _, EncodePem as _, Length},
|
||||||
use x509_cert::ext::pkix::name::GeneralNames;
|
ext::{
|
||||||
use x509_cert::ext::pkix::{ExtendedKeyUsage, SubjectAltName};
|
pkix::{name::GeneralNames, ExtendedKeyUsage, SubjectAltName},
|
||||||
use x509_cert::ext::{AsExtension, Extension};
|
AsExtension, Extension,
|
||||||
use x509_cert::name::{Name, RdnSequence};
|
},
|
||||||
use x509_cert::serial_number::SerialNumber;
|
name::{Name, RdnSequence},
|
||||||
use x509_cert::spki::{
|
serial_number::SerialNumber,
|
||||||
DynSignatureAlgorithmIdentifier, EncodePublicKey, ObjectIdentifier, SignatureBitStringEncoding,
|
spki::{
|
||||||
SubjectPublicKeyInfoOwned,
|
DynSignatureAlgorithmIdentifier, EncodePublicKey, ObjectIdentifier,
|
||||||
|
SignatureBitStringEncoding, SubjectPublicKeyInfoOwned,
|
||||||
|
},
|
||||||
|
time::Validity,
|
||||||
|
Certificate,
|
||||||
};
|
};
|
||||||
use x509_cert::time::Validity;
|
|
||||||
use x509_cert::Certificate;
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
/// The OID for the `gramine-ra-tls` quote extension
|
/// The OID for the `gramine-ra-tls` quote extension
|
||||||
|
@ -148,7 +147,7 @@ pub fn make_self_signed_cert(
|
||||||
let hash = Sha256::digest(verifying_key_der.as_bytes());
|
let hash = Sha256::digest(verifying_key_der.as_bytes());
|
||||||
key_hash[..32].copy_from_slice(&hash);
|
key_hash[..32].copy_from_slice(&hash);
|
||||||
|
|
||||||
let quote = get_quote(&key_hash)?;
|
let (_tee_type, quote) = get_quote(&key_hash)?;
|
||||||
debug!("quote.len: {:?}", quote.len());
|
debug!("quote.len: {:?}", quote.len());
|
||||||
// Create a relative distinguished name.
|
// Create a relative distinguished name.
|
||||||
let rdns = RdnSequence::from_str(dn)?;
|
let rdns = RdnSequence::from_str(dn)?;
|
||||||
|
@ -185,6 +184,7 @@ pub fn make_self_signed_cert(
|
||||||
.context("failed to add SubjectAltName")?;
|
.context("failed to add SubjectAltName")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: OID for tee_type
|
||||||
builder
|
builder
|
||||||
.add_extension(&RaTlsQuoteExtension {
|
.add_extension(&RaTlsQuoteExtension {
|
||||||
quote: quote.to_vec(),
|
quote: quote.to_vec(),
|
||||||
|
@ -234,7 +234,7 @@ where
|
||||||
let hash = Sha256::digest(verifying_key_der.as_bytes());
|
let hash = Sha256::digest(verifying_key_der.as_bytes());
|
||||||
key_hash[..32].copy_from_slice(&hash);
|
key_hash[..32].copy_from_slice(&hash);
|
||||||
|
|
||||||
let quote = get_quote(&key_hash).context("Failed to get own quote")?;
|
let (_tee_type, quote) = get_quote(&key_hash).context("Failed to get own quote")?;
|
||||||
|
|
||||||
// Create a relative distinguished name.
|
// Create a relative distinguished name.
|
||||||
let subject = Name::from_str(dn)?;
|
let subject = Name::from_str(dn)?;
|
||||||
|
@ -269,6 +269,7 @@ where
|
||||||
.context("failed to add SubjectAltName")?;
|
.context("failed to add SubjectAltName")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: oid according to tee_type
|
||||||
builder
|
builder
|
||||||
.add_extension(&RaTlsQuoteExtension {
|
.add_extension(&RaTlsQuoteExtension {
|
||||||
quote: quote.to_vec(),
|
quote: quote.to_vec(),
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
// Copyright (c) 2023 Matter Labs
|
|
||||||
|
|
||||||
//! Intel SGX Enclave error wrapper
|
|
||||||
|
|
||||||
use bytemuck::PodCastError;
|
|
||||||
use intel_tee_quote_verification_rs::quote3_error_t;
|
|
||||||
use std::fmt::Formatter;
|
|
||||||
|
|
||||||
/// Wrapper for the quote verification Error
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Quote3Error {
|
|
||||||
/// error message
|
|
||||||
pub msg: &'static str,
|
|
||||||
/// raw error code
|
|
||||||
pub inner: quote3_error_t,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for Quote3Error {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}: {:?}", self.msg, self.inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for Quote3Error {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}: {:?}", self.msg, self.inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for Quote3Error {}
|
|
||||||
|
|
||||||
impl From<quote3_error_t> for Quote3Error {
|
|
||||||
fn from(inner: quote3_error_t) -> Self {
|
|
||||||
Self {
|
|
||||||
msg: "Generic",
|
|
||||||
inner,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum QuoteFromError {
|
|
||||||
#[error(transparent)]
|
|
||||||
PodCastError(#[from] PodCastError),
|
|
||||||
|
|
||||||
#[error("Quote version is invalid")]
|
|
||||||
InvalidVersion,
|
|
||||||
}
|
|
|
@ -5,24 +5,18 @@
|
||||||
|
|
||||||
//! Intel SGX Enclave report structures.
|
//! Intel SGX Enclave report structures.
|
||||||
|
|
||||||
pub mod error;
|
|
||||||
pub mod sign;
|
pub mod sign;
|
||||||
pub mod tcblevel;
|
pub mod tcblevel;
|
||||||
|
|
||||||
use bytemuck::{cast_slice, try_from_bytes, AnyBitPattern, PodCastError};
|
use crate::quote::error::QuoteContext;
|
||||||
use intel_tee_quote_verification_rs::{
|
pub use crate::quote::error::QuoteError;
|
||||||
quote3_error_t, sgx_ql_qv_supplemental_t, tee_get_supplemental_data_version_and_size,
|
use bytemuck::{try_from_bytes, AnyBitPattern, PodCastError};
|
||||||
tee_supp_data_descriptor_t, tee_verify_quote,
|
|
||||||
};
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::fs::OpenOptions;
|
|
||||||
use std::io::{Read, Write};
|
|
||||||
use std::mem;
|
|
||||||
use tracing::{trace, warn};
|
|
||||||
|
|
||||||
use crate::quote::GetQuoteError;
|
|
||||||
pub use error::{Quote3Error, QuoteFromError};
|
|
||||||
pub use intel_tee_quote_verification_rs::{sgx_ql_qv_result_t, Collateral};
|
pub use intel_tee_quote_verification_rs::{sgx_ql_qv_result_t, Collateral};
|
||||||
|
use std::{
|
||||||
|
fs::OpenOptions,
|
||||||
|
io::{Read, Write},
|
||||||
|
mem,
|
||||||
|
};
|
||||||
pub use tcblevel::{parse_tcb_levels, EnumSet, TcbLevel};
|
pub use tcblevel::{parse_tcb_levels, EnumSet, TcbLevel};
|
||||||
|
|
||||||
/// Structure of a quote
|
/// Structure of a quote
|
||||||
|
@ -43,13 +37,13 @@ pub struct Quote {
|
||||||
|
|
||||||
impl Quote {
|
impl Quote {
|
||||||
/// Creates a quote from a byte slice
|
/// Creates a quote from a byte slice
|
||||||
pub fn try_from_bytes(bytes: &[u8]) -> Result<&Self, QuoteFromError> {
|
pub fn try_from_bytes(bytes: &[u8]) -> Result<&Self, QuoteError> {
|
||||||
if bytes.len() < mem::size_of::<Self>() {
|
if bytes.len() < mem::size_of::<Self>() {
|
||||||
return Err(PodCastError::SizeMismatch.into());
|
return Err(PodCastError::SizeMismatch.into());
|
||||||
}
|
}
|
||||||
let this: &Self = try_from_bytes(&bytes[..mem::size_of::<Self>()])?;
|
let this: &Self = try_from_bytes(&bytes[..mem::size_of::<Self>()])?;
|
||||||
if this.version() != 3 {
|
if this.version() != 3 {
|
||||||
return Err(QuoteFromError::InvalidVersion);
|
return Err(QuoteError::InvalidVersion);
|
||||||
}
|
}
|
||||||
Ok(this)
|
Ok(this)
|
||||||
}
|
}
|
||||||
|
@ -98,152 +92,26 @@ pub struct ReportBody {
|
||||||
pub reportdata: [u8; 64],
|
pub reportdata: [u8; 64],
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of the quote verification
|
|
||||||
pub struct QuoteVerificationResult<'a> {
|
|
||||||
/// the raw result
|
|
||||||
pub result: sgx_ql_qv_result_t,
|
|
||||||
/// indicates if the collateral is expired
|
|
||||||
pub collateral_expired: bool,
|
|
||||||
/// the earliest expiration date of the collateral
|
|
||||||
pub earliest_expiration_date: i64,
|
|
||||||
/// Date of the TCB level
|
|
||||||
pub tcb_level_date_tag: i64,
|
|
||||||
/// the advisory string
|
|
||||||
pub advisories: Vec<String>,
|
|
||||||
/// the quote
|
|
||||||
pub quote: &'a Quote,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verifies a quote with optional collateral material
|
|
||||||
pub fn verify_quote_with_collateral<'a>(
|
|
||||||
quote: &'a [u8],
|
|
||||||
collateral: Option<&Collateral>,
|
|
||||||
current_time: i64,
|
|
||||||
) -> Result<QuoteVerificationResult<'a>, Quote3Error> {
|
|
||||||
let mut supp_data: mem::MaybeUninit<sgx_ql_qv_supplemental_t> = mem::MaybeUninit::zeroed();
|
|
||||||
let mut supp_data_desc = tee_supp_data_descriptor_t {
|
|
||||||
major_version: 0,
|
|
||||||
data_size: 0,
|
|
||||||
p_data: supp_data.as_mut_ptr() as *mut u8,
|
|
||||||
};
|
|
||||||
trace!("tee_get_supplemental_data_version_and_size");
|
|
||||||
let (_, supp_size) =
|
|
||||||
tee_get_supplemental_data_version_and_size(quote).map_err(|e| Quote3Error {
|
|
||||||
msg: "tee_get_supplemental_data_version_and_size",
|
|
||||||
inner: e,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
trace!(
|
|
||||||
"tee_get_supplemental_data_version_and_size supp_size: {}",
|
|
||||||
supp_size
|
|
||||||
);
|
|
||||||
|
|
||||||
if supp_size == mem::size_of::<sgx_ql_qv_supplemental_t>() as u32 {
|
|
||||||
supp_data_desc.data_size = supp_size;
|
|
||||||
} else {
|
|
||||||
supp_data_desc.data_size = 0;
|
|
||||||
trace!(
|
|
||||||
"tee_get_supplemental_data_version_and_size supp_size: {}",
|
|
||||||
supp_size
|
|
||||||
);
|
|
||||||
trace!(
|
|
||||||
"mem::size_of::<sgx_ql_qv_supplemental_t>(): {}",
|
|
||||||
mem::size_of::<sgx_ql_qv_supplemental_t>()
|
|
||||||
);
|
|
||||||
warn!("Quote supplemental data size is different between DCAP QVL and QvE, please make sure you installed DCAP QVL and QvE from same release.")
|
|
||||||
}
|
|
||||||
|
|
||||||
let p_supplemental_data = match supp_data_desc.data_size {
|
|
||||||
0 => None,
|
|
||||||
_ => Some(&mut supp_data_desc),
|
|
||||||
};
|
|
||||||
|
|
||||||
let has_sup = p_supplemental_data.is_some();
|
|
||||||
|
|
||||||
trace!("tee_verify_quote");
|
|
||||||
|
|
||||||
let (collateral_expiration_status, result) =
|
|
||||||
tee_verify_quote(quote, collateral, current_time, None, p_supplemental_data).map_err(
|
|
||||||
|e| Quote3Error {
|
|
||||||
msg: "tee_verify_quote",
|
|
||||||
inner: e,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// check supplemental data if necessary
|
|
||||||
let (advisories, earliest_expiration_date, tcb_level_date_tag) = if has_sup {
|
|
||||||
unsafe {
|
|
||||||
let supp_data = supp_data.assume_init();
|
|
||||||
// convert to valid UTF-8 string
|
|
||||||
let ads = CStr::from_bytes_until_nul(cast_slice(&supp_data.sa_list[..]))
|
|
||||||
.ok()
|
|
||||||
.and_then(|s| CStr::to_str(s).ok())
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|s| s.split(',').map(str::trim).map(String::from))
|
|
||||||
.filter(|s| !s.is_empty())
|
|
||||||
.collect();
|
|
||||||
(
|
|
||||||
ads,
|
|
||||||
supp_data.earliest_expiration_date,
|
|
||||||
supp_data.tcb_level_date_tag,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(vec![], 0, 0)
|
|
||||||
};
|
|
||||||
|
|
||||||
let quote = Quote::try_from_bytes(quote).map_err(|_| Quote3Error {
|
|
||||||
msg: "Quote::try_from_bytes",
|
|
||||||
inner: quote3_error_t::SGX_QL_QUOTE_FORMAT_UNSUPPORTED,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let res = QuoteVerificationResult {
|
|
||||||
collateral_expired: collateral_expiration_status != 0,
|
|
||||||
earliest_expiration_date,
|
|
||||||
tcb_level_date_tag,
|
|
||||||
result,
|
|
||||||
quote,
|
|
||||||
advisories,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the attestation report in a Gramine enclave
|
/// Get the attestation report in a Gramine enclave
|
||||||
pub fn sgx_gramine_get_quote(report_data: &[u8; 64]) -> Result<Box<[u8]>, GetQuoteError> {
|
pub fn sgx_gramine_get_quote(report_data: &[u8; 64]) -> Result<Box<[u8]>, QuoteError> {
|
||||||
let mut file = OpenOptions::new()
|
let mut file = OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.open("/dev/attestation/user_report_data")
|
.open("/dev/attestation/user_report_data")
|
||||||
.map_err(|e| GetQuoteError {
|
.context("opening `/dev/attestation/user_report_data`")?;
|
||||||
msg: "Failed to open `/dev/attestation/user_report_data`".into(),
|
|
||||||
source: e,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
file.write(report_data).map_err(|e| GetQuoteError {
|
file.write(report_data)
|
||||||
msg: "Failed to write `/dev/attestation/user_report_data`".into(),
|
.context("writing `/dev/attestation/user_report_data`")?;
|
||||||
source: e,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
drop(file);
|
drop(file);
|
||||||
|
|
||||||
let mut file = OpenOptions::new()
|
let mut file = OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
.open("/dev/attestation/quote")
|
.open("/dev/attestation/quote")
|
||||||
.map_err(|e| GetQuoteError {
|
.context("opening `/dev/attestation/quote`")?;
|
||||||
msg: "Failed to open `/dev/attestation/quote`".into(),
|
|
||||||
source: e,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let mut quote = Vec::new();
|
let mut quote = Vec::new();
|
||||||
file.read_to_end(&mut quote).map_err(|e| GetQuoteError {
|
file.read_to_end(&mut quote)
|
||||||
msg: "Failed to read `/dev/attestation/quote`".into(),
|
.context("reading `/dev/attestation/quote`")?;
|
||||||
source: e,
|
|
||||||
})?;
|
|
||||||
Ok(quote.into_boxed_slice())
|
Ok(quote.into_boxed_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrapper func for error
|
|
||||||
/// TODO: move to intel_tee_quote_verification_rs
|
|
||||||
pub fn tee_qv_get_collateral(quote: &[u8]) -> Result<Collateral, Quote3Error> {
|
|
||||||
intel_tee_quote_verification_rs::tee_qv_get_collateral(quote).map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
30
crates/teepot/src/tdx/mod.rs
Normal file
30
crates/teepot/src/tdx/mod.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
// Copyright (c) 2023-2024 Matter Labs
|
||||||
|
|
||||||
|
//! Intel TDX helper functions.
|
||||||
|
|
||||||
|
pub use crate::sgx::tcblevel::{parse_tcb_levels, EnumSet, TcbLevel};
|
||||||
|
use crate::sgx::QuoteError;
|
||||||
|
pub use intel_tee_quote_verification_rs::Collateral;
|
||||||
|
use tdx_attest_rs::{tdx_att_get_quote, tdx_attest_error_t, tdx_report_data_t};
|
||||||
|
|
||||||
|
/// Get a TDX quote
|
||||||
|
pub fn tgx_get_quote(report_data_bytes: &[u8; 64]) -> Result<Box<[u8]>, QuoteError> {
|
||||||
|
let mut tdx_report_data = tdx_report_data_t { d: [0; 64usize] };
|
||||||
|
tdx_report_data.d.copy_from_slice(report_data_bytes);
|
||||||
|
|
||||||
|
let (error, quote) = tdx_att_get_quote(Some(&tdx_report_data), None, None, 0);
|
||||||
|
|
||||||
|
if error == tdx_attest_error_t::TDX_ATTEST_SUCCESS {
|
||||||
|
if let Some(quote) = quote {
|
||||||
|
Ok(quote.into())
|
||||||
|
} else {
|
||||||
|
Err(QuoteError::TdxAttGetQuote {
|
||||||
|
msg: "tdx_att_get_quote: No quote returned".into(),
|
||||||
|
inner: error,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(error.into())
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,10 +1,12 @@
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
# Copyright (c) 2024 Matter Labs
|
# Copyright (c) 2024 Matter Labs
|
||||||
{ teepotCrate }: teepotCrate.craneLib.buildPackage (
|
{ lib, pkgs, makeWrapper, teepotCrate }: teepotCrate.craneLib.buildPackage (
|
||||||
teepotCrate.commonArgs // {
|
teepotCrate.commonArgs // {
|
||||||
pname = "teepot";
|
pname = "teepot";
|
||||||
inherit (teepotCrate) cargoArtifacts;
|
inherit (teepotCrate) cargoArtifacts;
|
||||||
|
|
||||||
|
nativeBuildInputs = teepotCrate.commonArgs.nativeBuildInputs ++ [ makeWrapper ];
|
||||||
|
|
||||||
passthru = {
|
passthru = {
|
||||||
inherit (teepotCrate) rustPlatform
|
inherit (teepotCrate) rustPlatform
|
||||||
rustVersion
|
rustVersion
|
||||||
|
@ -28,6 +30,7 @@
|
||||||
"verify_attestation"
|
"verify_attestation"
|
||||||
"verify_era_proof_attestation"
|
"verify_era_proof_attestation"
|
||||||
];
|
];
|
||||||
|
|
||||||
postInstall = ''
|
postInstall = ''
|
||||||
removeReferencesToVendoredSources "$out" "$cargoVendorDir"
|
removeReferencesToVendoredSources "$out" "$cargoVendorDir"
|
||||||
removeReferencesToVendoredSources "$out" "${teepotCrate.rustVersion}/lib/rustlib/"
|
removeReferencesToVendoredSources "$out" "${teepotCrate.rustVersion}/lib/rustlib/"
|
||||||
|
@ -38,6 +41,11 @@
|
||||||
echo -n "''${!i} " >> $out/nix-support/propagated-user-env-packages
|
echo -n "''${!i} " >> $out/nix-support/propagated-user-env-packages
|
||||||
binname=''${i//_/-}
|
binname=''${i//_/-}
|
||||||
mv "$out/bin/$binname" "''${!i}/bin/"
|
mv "$out/bin/$binname" "''${!i}/bin/"
|
||||||
|
|
||||||
|
makeWrapper "''${!i}/bin/$binname" "''${!i}/bin/$binname-dcap" \
|
||||||
|
--prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath [ pkgs.nixsgx.sgx-dcap.quote_verify pkgs.nixsgx.sgx-dcap.default_qpl pkgs.curl ]}" \
|
||||||
|
--set-default QCNL_CONF_PATH "${pkgs.nixsgx.sgx-dcap.default_qpl}/etc/sgx_default_qcnl.conf"
|
||||||
|
|
||||||
done
|
done
|
||||||
rmdir "$out/bin"
|
rmdir "$out/bin"
|
||||||
'';
|
'';
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue