diff --git a/Cargo.lock b/Cargo.lock index 74c3ca6d..91e197a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,11 +4,11 @@ version = 3 [[package]] name = "actix-codec" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.2", "bytes", "futures-core", "futures-sink", @@ -66,7 +66,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -202,7 +202,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -222,9 +222,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", @@ -233,9 +233,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" dependencies = [ "cfg-if", "getrandom", @@ -285,9 +285,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anyhow" @@ -387,7 +387,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.48", + "syn 2.0.51", "which", ] @@ -460,9 +460,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", "serde", @@ -480,9 +480,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" [[package]] name = "bytemuck" @@ -501,7 +501,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -546,11 +546,10 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" dependencies = [ - "jobserver", "libc", ] @@ -580,15 +579,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.32" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41daef31d7a747c5c847246f36de49ced6f7403b4cdabc807a97b5cc184cda7a" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -614,9 +613,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ "clap_builder", "clap_derive", @@ -624,9 +623,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ "anstyle", "clap_lex", @@ -642,7 +641,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -697,9 +696,9 @@ checksum = "fd121741cf3eb82c08dd3023eb55bf2665e5f60ec20f89760cf836ae4562e6a0" [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -751,7 +750,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -766,12 +765,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" dependencies = [ - "darling_core 0.20.3", - "darling_macro 0.20.3", + "darling_core 0.20.8", + "darling_macro 0.20.8", ] [[package]] @@ -790,16 +789,16 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -815,13 +814,13 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ - "darling_core 0.20.3", + "darling_core 0.20.8", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -845,7 +844,7 @@ checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -965,9 +964,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", @@ -979,9 +978,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "elliptic-curve" @@ -1029,11 +1028,11 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" dependencies = [ - "darling 0.20.3", - "proc-macro-crate", + "darling 0.20.8", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -1064,9 +1063,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" +checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382" [[package]] name = "flagset" @@ -1187,7 +1186,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.11", - "indexmap 2.1.0", + "indexmap 2.2.3", "slab", "tokio", "tokio-util", @@ -1214,9 +1213,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.4" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60" [[package]] name = "hex" @@ -1287,9 +1286,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "iana-time-zone" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1352,9 +1351,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -1399,20 +1398,11 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" -[[package]] -name = "jobserver" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" -dependencies = [ - "libc", -] - [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ "wasm-bindgen", ] @@ -1463,9 +1453,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" @@ -1561,9 +1551,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] @@ -1624,6 +1614,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" version = "0.1.46" @@ -1635,9 +1631,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -1679,10 +1675,10 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -1838,22 +1834,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -1891,9 +1887,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "platforms" @@ -1920,7 +1916,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -1939,7 +1935,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", ] [[package]] @@ -2007,7 +2012,7 @@ checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.4", + "regex-automata 0.4.5", "regex-syntax 0.8.2", ] @@ -2022,9 +2027,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", @@ -2121,9 +2126,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ "bitflags 2.4.2", "errno", @@ -2148,9 +2153,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4" +checksum = "3c333bb734fcdedcea57de1602543590f545f127dc8b533324318fd492c5c70b" dependencies = [ "base64", "rustls-pki-types", @@ -2158,9 +2163,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a716eb65e3158e90e17cd93d855216e27bde02745ab842f2cab4a39dba1bacf" +checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" [[package]] name = "rustls-webpki" @@ -2175,9 +2180,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "safemem" @@ -2207,35 +2212,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -2256,16 +2261,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.6.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b0ed1662c5a68664f45b76d18deb0e234aff37207086803165c961eb695e981" +checksum = "15d167997bd841ec232f5b2b8e0e26606df2e7caa4c31b95ea9ca52b200bd270" dependencies = [ "base64", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.1.0", + "indexmap 2.2.3", "serde", + "serde_derive", "serde_json", "serde_with_macros", "time", @@ -2273,14 +2279,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.6.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568577ff0ef47b879f736cd66740e022f3672788cdf002a05a4e609ea5a6fb15" +checksum = "865f9743393e638991566a8b7a479043c2c8da94a33e0a31f18214c9cae0a64d" dependencies = [ - "darling 0.20.3", + "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -2366,12 +2372,12 @@ checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2421,9 +2427,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c" dependencies = [ "proc-macro2", "quote", @@ -2444,6 +2450,22 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "tee-ratls-preexec" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "rsa", + "rustls", + "rustls-pemfile", + "teepot", + "tracing", + "tracing-log", + "tracing-subscriber", + "x509-cert", +] + [[package]] name = "tee-self-attestation-test" version = "0.1.0" @@ -2548,6 +2570,7 @@ dependencies = [ "intel-tee-quote-verification-rs", "num-integer", "num-traits", + "p256", "pgp", "pkcs8", "rand", @@ -2559,9 +2582,11 @@ dependencies = [ "serde_json", "serde_with", "sha2", + "signature", "testaso", "thiserror", "tracing", + "webpki-roots", "x509-cert", "zeroize", ] @@ -2633,14 +2658,14 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -2648,12 +2673,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -2668,10 +2694,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] @@ -2692,9 +2719,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tls_codec" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38a1d5fcfa859f0ec2b5e111dc903890bd7dac7f34713232bf9aa4fd7cad7b2" +checksum = "b5e78c9c330f8c85b2bae7c8368f2739157db9991235123aa1b15ef9502bfb6a" dependencies = [ "tls_codec_derive", "zeroize", @@ -2702,20 +2729,20 @@ dependencies = [ [[package]] name = "tls_codec_derive" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e00e3e7a54e0f1c8834ce72ed49c8487fbd3f801d8cfe1a0ad0640382f8e15" +checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -2738,7 +2765,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -2778,7 +2805,18 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.3", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.2.3", "toml_datetime", "winnow", ] @@ -2816,7 +2854,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -2887,9 +2925,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -2989,9 +3027,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2999,24 +3037,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3024,28 +3062,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "webpki-roots" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de2cfda980f21be5a7ed2eadb3e6fe074d56022bea2cdeb1a62eb220fc04188" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" dependencies = [ "rustls-pki-types", ] @@ -3090,7 +3128,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -3108,7 +3146,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -3128,17 +3166,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.3", + "windows_aarch64_msvc 0.52.3", + "windows_i686_gnu 0.52.3", + "windows_i686_msvc 0.52.3", + "windows_x86_64_gnu 0.52.3", + "windows_x86_64_gnullvm 0.52.3", + "windows_x86_64_msvc 0.52.3", ] [[package]] @@ -3149,9 +3187,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" [[package]] name = "windows_aarch64_msvc" @@ -3161,9 +3199,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" [[package]] name = "windows_i686_gnu" @@ -3173,9 +3211,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" [[package]] name = "windows_i686_msvc" @@ -3185,9 +3223,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" [[package]] name = "windows_x86_64_gnu" @@ -3197,9 +3235,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" [[package]] name = "windows_x86_64_gnullvm" @@ -3209,9 +3247,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" [[package]] name = "windows_x86_64_msvc" @@ -3221,15 +3259,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" [[package]] name = "winnow" -version = "0.5.34" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] @@ -3254,6 +3292,8 @@ checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" dependencies = [ "const-oid", "der", + "sha1", + "signature", "spki", "tls_codec", ] @@ -3275,7 +3315,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -3296,7 +3336,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d24a8c2a..8d223578 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ hex.workspace = true intel-tee-quote-verification-rs.workspace = true num-integer.workspace = true num-traits.workspace = true +p256.workspace = true pgp.workspace = true pkcs8.workspace = true rand.workspace = true @@ -38,8 +39,10 @@ serde.workspace = true serde_json.workspace = true serde_with.workspace = true sha2.workspace = true +signature.workspace = true thiserror.workspace = true tracing.workspace = true +webpki-roots.workspace = true x509-cert.workspace = true zeroize.workspace = true @@ -86,10 +89,11 @@ mio = "0.8.10" num-integer = "0.1.46" num-traits = "0.2.18" pgp = "0.11" +p256 = "0.13.2" pkcs8 = { version = "0.10" } rand = "0.8" ring = { version = "0.17.8", features = ["std"], default-features = false } -rsa = { version = "0.9.6", features = ["sha2"] } +rsa = { version = "0.9.6", features = ["sha2", "pem"] } rustls = { version = "0.22" } rustls-pemfile = "2" sec1 = { version = "0.7.3", features = ["der"], default-features = false } @@ -97,6 +101,7 @@ serde = { version = "1", features = ["derive", "rc"] } serde_json = "1" serde_with = { version = "3.6", features = ["base64", "hex"] } sha2 = "0.10.8" +signature = "2.2.0" teepot = { path = "." } testaso = "0.1.0" thiserror = "1.0.57" @@ -105,6 +110,6 @@ tracing = "0.1" tracing-actix-web = "0.7" tracing-log = "0.2" tracing-subscriber = { version = "0.3", features = ["env-filter"] } -x509 = { version = "0.2", package = "x509-cert", default-features = false } -x509-cert = "0.2.5" +x509-cert = { version = "0.2", features = ["builder", "signature"] } zeroize = { version = "1.7.0", features = ["serde"] } +webpki-roots = "0.26.1" diff --git a/bin/tee-ratls-preexec/Cargo.toml b/bin/tee-ratls-preexec/Cargo.toml new file mode 100644 index 00000000..5eecb658 --- /dev/null +++ b/bin/tee-ratls-preexec/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "tee-ratls-preexec" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow.workspace = true +clap.workspace = true +rsa.workspace = true +rustls-pemfile.workspace = true +rustls.workspace = true +teepot.workspace = true +tracing-log.workspace = true +tracing-subscriber.workspace = true +tracing.workspace = true +x509-cert.workspace = true diff --git a/bin/tee-ratls-preexec/src/main.rs b/bin/tee-ratls-preexec/src/main.rs new file mode 100644 index 00000000..1fdd12dc --- /dev/null +++ b/bin/tee-ratls-preexec/src/main.rs @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024 Matter Labs + +//! Pre-exec for binary running in a TEE needing attestation of a secret signing key + +#![deny(missing_docs)] +#![deny(clippy::all)] + +use anyhow::{Context, Result}; +use clap::Parser; +use rsa::pkcs1v15::SigningKey; +use rsa::pkcs8::DecodePrivateKey; +use rsa::sha2::Sha256; +use rsa::RsaPrivateKey; +use std::fs::File; +use std::io::Write; +use std::os::unix::process::CommandExt; +use std::path::PathBuf; +use std::process::Command; +use teepot::server::pki::make_signed_cert; +use tracing::error; +use tracing_log::LogTracer; +use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry}; +use x509_cert::der::asn1::Ia5String; +use x509_cert::der::DecodePem; +use x509_cert::ext::pkix::name::GeneralName; +use x509_cert::Certificate; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + /// ca cert file + #[arg(long, env = "CA_CERT_FILE", default_value = "/opt/vault/cacert.pem")] + ca_cert_file: PathBuf, + /// ca key file + #[arg(long, env = "CA_KEY_FILE", default_value = "/opt/vault/cakey.pem")] + ca_key_file: PathBuf, + /// out cert file + #[arg(long, env = "TLS_CERT_FILE", default_value = "/opt/vault/tls/tls.crt")] + tls_cert_file: PathBuf, + /// out key file + #[arg(long, env = "TLS_KEY_FILE", default_value = "/opt/vault/tls/tls.key")] + tls_key_file: PathBuf, + /// DNS names, comma separated + #[arg(long, env = "DNS_NAMES", required = true)] + dns_names: String, + /// program to exec [args...] (required) + #[arg(required = true, allow_hyphen_values = true, last = true)] + cmd_args: Vec, +} + +fn main_with_error() -> Result<()> { + LogTracer::init().context("Failed to set logger")?; + + let subscriber = Registry::default() + .with(EnvFilter::from_default_env()) + .with(fmt::layer().with_writer(std::io::stderr)); + tracing::subscriber::set_global_default(subscriber).context("Failed to set logger")?; + + let args = Args::parse(); + + // read `issuer_cert_bytes` from file + let ca_cert = std::fs::read(args.ca_cert_file).context("Failed to read ca_cert")?; + + let issuer_cert = Certificate::from_pem(&ca_cert)?; + let issuer_key = + RsaPrivateKey::read_pkcs8_pem_file(args.ca_key_file).context("Failed to read ca_key")?; + let issuer_key_pair = SigningKey::::new(issuer_key); + + // TODO: read values from config file or env or args + let dn = "O=system:nodes,CN=system:node"; + let mut an = vec![std::net::IpAddr::from(std::net::Ipv4Addr::LOCALHOST).into()]; + an.extend( + args.dns_names + .split(',') + .map(|s| GeneralName::DnsName(Ia5String::try_from(s.to_string()).unwrap())), + ); + + let (_report_data, cert, priv_key) = + make_signed_cert(dn, Some(an), &issuer_cert, &issuer_key_pair)?; + + // open args.tls_cert_file and write cert and ca_cert + let mut file = File::create(&args.tls_cert_file).context("Failed to create tls_cert")?; + file.write_all(cert.as_bytes()) + .context("Failed to write tls_cert")?; + file.write_all(&ca_cert) + .context("Failed to write tls_cert")?; + + std::fs::write(args.tls_key_file, priv_key).context("Failed to write tls_cert")?; + + let err = Command::new(&args.cmd_args[0]) + .args(&args.cmd_args[1..]) + .exec(); + + Err(err).with_context(|| format!("exec of `{cmd}` failed", cmd = args.cmd_args.join(" "))) +} + +fn main() -> Result<()> { + let ret = main_with_error(); + if let Err(e) = &ret { + error!("Error: {}", e); + } + ret +} diff --git a/bin/tee-stress-client/src/main.rs b/bin/tee-stress-client/src/main.rs index e4a4e10f..f691401f 100644 --- a/bin/tee-stress-client/src/main.rs +++ b/bin/tee-stress-client/src/main.rs @@ -1,4 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 +// Copyright (c) 2024 Matter Labs //! Server to handle requests to the Vault TEE @@ -45,7 +46,7 @@ async fn main() -> Result<()> { let args = Arguments::parse(); - let (report_data, _cert_chain, _priv_key) = make_self_signed_cert()?; + let (report_data, _cert_chain, _priv_key) = make_self_signed_cert("CN=localhost", None)?; if let Err(e) = get_quote_and_collateral(Some(args.my_sgx_allowed_tcb_levels), &report_data) { error!("failed to get quote and collateral: {e:?}"); // don't return for now, we can still serve requests but we won't be able to attest diff --git a/bin/tee-vault-admin/Dockerfile-azure b/bin/tee-vault-admin/Dockerfile-azure deleted file mode 100644 index ca3fce7f..00000000 --- a/bin/tee-vault-admin/Dockerfile-azure +++ /dev/null @@ -1,85 +0,0 @@ -FROM docker.io/ubuntu:20.04 AS azuredcap -WORKDIR /build -ADD https://github.com/microsoft/Azure-DCAP-Client/archive/refs/tags/1.12.0.tar.gz ./Azure-DCAP-Client.tar.gz -RUN tar -xvf Azure-DCAP-Client.tar.gz -COPY assets/Azure-DCAP-Client.patch ./Azure-DCAP-Client.patch -RUN set -eux; \ - apt-get update; \ - apt-get install -y software-properties-common; \ - add-apt-repository ppa:team-xbmc/ppa -y; \ - apt-get update; \ - apt-get install -y \ - build-essential \ - cmake \ - libssl-dev \ - libcurl4-openssl-dev \ - pkg-config \ - nlohmann-json3-dev \ - wget \ - dos2unix \ - ; - -WORKDIR /build/Azure-DCAP-Client-1.12.0 -RUN dos2unix src/dcap_provider.cpp && patch -p1 < ../Azure-DCAP-Client.patch -WORKDIR /build/Azure-DCAP-Client-1.12.0/src/Linux -RUN ./configure && make && make install - -FROM docker.io/rust:1-bullseye AS buildtee -RUN curl -fsSLo /usr/share/keyrings/intel.asc https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key \ - && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/intel.asc] https://download.01.org/intel-sgx/sgx_repo/ubuntu focal main" > /etc/apt/sources.list.d/intel-sgx.list \ - && apt-get update \ - && apt-get install -y --no-install-recommends \ - build-essential \ - cmake \ - rsync \ - pkg-config \ - libssl-dev \ - libcurl4-openssl-dev \ - libprotobuf-dev \ - protobuf-compiler \ - clang \ - libsgx-headers \ - libsgx-dcap-quote-verify-dev - -WORKDIR /opt/vault/plugins - -WORKDIR /build -RUN --mount=type=bind,target=/data rsync --exclude='/.git' --filter="dir-merge,- .gitignore" --exclude "Dockerfile-*" --exclude 'tee-vault-admin.manifest.template' -av /data/ ./ -RUN --mount=type=cache,target=/usr/local/cargo/registry --mount=type=cache,target=target \ - RUSTFLAGS="-C target-cpu=icelake-server --cfg mio_unsupported_force_waker_pipe" \ - cargo build --locked --target x86_64-unknown-linux-gnu --release -p tee-vault-admin --bin tee-vault-admin \ - && mv ./target/x86_64-unknown-linux-gnu/release/tee-vault-admin ./ - -FROM docker.io/gramineproject/gramine:v1.5 - -RUN curl -fsSLo /usr/share/keyrings/microsoft.asc https://packages.microsoft.com/keys/microsoft.asc \ - && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft.asc] https://packages.microsoft.com/ubuntu/20.04/prod focal main" > /etc/apt/sources.list.d/msprod.list \ - && apt-get update \ - && apt purge -y libsgx-dcap-default-qpl \ - && apt-get install -y az-dcap-client - -RUN apt purge -y libsgx-ae-qve -# libsgx-urts - -RUN rm -rf /var/lib/apt/lists/* - -# So we only have to use one gramine template -RUN touch /etc/sgx_default_qcnl.conf - -WORKDIR /app - -COPY --from=buildtee /build/tee-vault-admin . -COPY ./bin/tee-vault-admin/tee-vault-admin.manifest.template . -COPY vault/enclave-key.pem . - -# The original Azure library is still delivering expired collateral, so we have to use a patched version -COPY --from=azuredcap /usr/local/lib/libdcap_quoteprov.so /usr/lib/ - -RUN gramine-manifest -Darch_libdir=/lib/x86_64-linux-gnu -Dexecdir=/usr/bin -Dlog_level=warning tee-vault-admin.manifest.template tee-vault-admin.manifest \ - && gramine-sgx-sign --manifest tee-vault-admin.manifest --output tee-vault-admin.manifest.sgx --key enclave-key.pem \ - && rm enclave-key.pem - -EXPOSE 8443 - -ENTRYPOINT ["/bin/sh", "-c"] -CMD [ "/restart_aesm.sh ; exec gramine-sgx tee-vault-admin" ] diff --git a/bin/tee-vault-admin/src/attestation.rs b/bin/tee-vault-admin/src/attestation.rs deleted file mode 100644 index 66f410a2..00000000 --- a/bin/tee-vault-admin/src/attestation.rs +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs - -//! attestation - -use crate::ServerState; -use actix_web::http::StatusCode; -use actix_web::web::{Data, Json}; -use anyhow::{Context, Result}; -use std::sync::Arc; -use teepot::json::http::AttestationResponse; -use teepot::server::attestation::get_quote_and_collateral; -use teepot::server::{HttpResponseError, Status}; -use tracing::instrument; - -/// Get attestation -#[instrument(level = "info", name = "/v1/sys/attestation", skip_all)] -pub async fn get_attestation( - worker: Data>, -) -> Result, HttpResponseError> { - get_quote_and_collateral(None, &worker.report_data) - .context("Error getting attestation") - .map(Json) - .status(StatusCode::INTERNAL_SERVER_ERROR) -} diff --git a/bin/tee-vault-admin/src/main.rs b/bin/tee-vault-admin/src/main.rs index 7a1e35f8..274c5f0d 100644 --- a/bin/tee-vault-admin/src/main.rs +++ b/bin/tee-vault-admin/src/main.rs @@ -1,12 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2024 Matter Labs //! Server to handle requests to the Vault TEE #![deny(missing_docs)] #![deny(clippy::all)] - -mod attestation; mod command; mod digest; mod sign; @@ -14,7 +12,6 @@ mod sign; use actix_web::web::Data; use actix_web::{web, App, HttpServer}; use anyhow::{Context, Result}; -use attestation::get_attestation; use clap::Parser; use command::post_command; use digest::get_digest; @@ -22,7 +19,7 @@ use rustls::ServerConfig; use sign::post_sign; use std::net::Ipv6Addr; use std::sync::Arc; -use teepot::json::http::{SignRequest, VaultCommandRequest, ATTESTATION_URL, DIGEST_URL}; +use teepot::json::http::{SignRequest, VaultCommandRequest, DIGEST_URL}; use teepot::server::attestation::{get_quote_and_collateral, VaultAttestationArgs}; use teepot::server::new_json_cfg; use teepot::server::pki::make_self_signed_cert; @@ -65,7 +62,7 @@ async fn main() -> Result<()> { let args = Arguments::parse(); - let (report_data, cert_chain, priv_key) = make_self_signed_cert()?; + let (report_data, cert_chain, priv_key) = make_self_signed_cert("CN=localhost", None)?; if let Err(e) = get_quote_and_collateral(Some(args.server_sgx_allowed_tcb_levels), &report_data) { @@ -94,7 +91,6 @@ async fn main() -> Result<()> { .wrap(TracingLogger::default()) .app_data(new_json_cfg()) .app_data(Data::new(server_state.clone())) - .service(web::resource(ATTESTATION_URL).route(web::get().to(get_attestation))) .service(web::resource(VaultCommandRequest::URL).route(web::post().to(post_command))) .service(web::resource(SignRequest::URL).route(web::post().to(post_sign))) .service(web::resource(DIGEST_URL).route(web::get().to(get_digest))) diff --git a/bin/tee-vault-admin/tee-vault-admin.manifest.template b/bin/tee-vault-admin/tee-vault-admin.manifest.template deleted file mode 100644 index 97c6ab5e..00000000 --- a/bin/tee-vault-admin/tee-vault-admin.manifest.template +++ /dev/null @@ -1,66 +0,0 @@ -libos.entrypoint = "/app/tee-vault-admin" - -[loader] -argv = [ "/app/tee-vault-admin" ] -entrypoint = "file:{{ gramine.libos }}" -env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr{{ arch_libdir }}:/lib" -env.HOME = "/app" -env.MALLOC_ARENA_MAX = "1" -env.AZDCAP_DEBUG_LOG_LEVEL = "ignore" -env.AZDCAP_COLLATERAL_VERSION = "v4" - -### Admin Config ### -env.PORT = { passthrough = true } - -### VAULT attestation ### -env.VAULT_ADDR = { passthrough = true } -env.VAULT_SGX_MRENCLAVE = { passthrough = true } -env.VAULT_SGX_MRSIGNER = { passthrough = true } -env.VAULT_SGX_ALLOWED_TCB_LEVELS = { passthrough = true } - -### DEBUG ### -env.RUST_BACKTRACE = "1" -env.RUST_LOG="info,tee_vault_admin=trace,teepot=trace,vault_tee_client=trace,tee_client=trace,awc=debug" - -[fs] -root.uri = "file:/" -start_dir = "/app" -mounts = [ - { path = "{{ execdir }}", uri = "file:{{ execdir }}" }, - { path = "/lib", uri = "file:{{ gramine.runtimedir() }}" }, - { path = "{{ arch_libdir }}", uri = "file:{{ arch_libdir }}" }, - { path = "/etc", uri = "file:/etc" }, - { type = "tmpfs", path = "/var/tmp" }, - { type = "tmpfs", path = "/tmp" }, - { type = "tmpfs", path = "/app/.dcap-qcnl" }, - { type = "tmpfs", path = "/app/.az-dcap-client" }, - { path = "/lib/libdcap_quoteprov.so", uri = "file:/lib/libdcap_quoteprov.so" }, -] - -[sgx] -trusted_files = [ - "file:/etc/ld.so.cache", - "file:/app/", - "file:{{ execdir }}/", - "file:{{ arch_libdir }}/", - "file:/usr/{{ arch_libdir }}/", - "file:{{ gramine.libos }}", - "file:{{ gramine.runtimedir() }}/", - "file:/usr/lib/ssl/openssl.cnf", - "file:/etc/ssl/", - "file:/etc/sgx_default_qcnl.conf", - "file:/lib/libdcap_quoteprov.so", -] -remote_attestation = "dcap" -max_threads = 64 -edmm_enable = false -## max enclave size -enclave_size = "8G" - -[sys] -enable_extra_runtime_domain_names_conf = true -enable_sigterm_injection = true - -# possible tweak option, if problems with mio -# currently mio is compiled with `mio_unsupported_force_waker_pipe` -# insecure__allow_eventfd = true diff --git a/bin/tee-vault-unseal/Dockerfile-azure b/bin/tee-vault-unseal/Dockerfile-azure deleted file mode 100644 index cd7c28d4..00000000 --- a/bin/tee-vault-unseal/Dockerfile-azure +++ /dev/null @@ -1,92 +0,0 @@ -FROM ghcr.io/matter-labs/vault-auth-tee:latest AS vault-auth-tee - -FROM docker.io/ubuntu:20.04 AS azuredcap -WORKDIR /build -ADD https://github.com/microsoft/Azure-DCAP-Client/archive/refs/tags/1.12.0.tar.gz ./Azure-DCAP-Client.tar.gz -RUN tar -xvf Azure-DCAP-Client.tar.gz -COPY assets/Azure-DCAP-Client.patch ./Azure-DCAP-Client.patch -RUN set -eux; \ - apt-get update; \ - apt-get install -y software-properties-common; \ - add-apt-repository ppa:team-xbmc/ppa -y; \ - apt-get update; \ - apt-get install -y \ - build-essential \ - cmake \ - libssl-dev \ - libcurl4-openssl-dev \ - pkg-config \ - nlohmann-json3-dev \ - wget \ - dos2unix \ - ; - -WORKDIR /build/Azure-DCAP-Client-1.12.0 -RUN dos2unix src/dcap_provider.cpp && patch -p1 < ../Azure-DCAP-Client.patch -WORKDIR /build/Azure-DCAP-Client-1.12.0/src/Linux -RUN ./configure && make && make install - -FROM docker.io/rust:1-bullseye AS buildtee -RUN curl -fsSLo /usr/share/keyrings/intel.asc https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key \ - && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/intel.asc] https://download.01.org/intel-sgx/sgx_repo/ubuntu focal main" > /etc/apt/sources.list.d/intel-sgx.list \ - && apt-get update \ - && apt-get install -y --no-install-recommends \ - build-essential \ - cmake \ - rsync \ - pkg-config \ - libssl-dev \ - libcurl4-openssl-dev \ - libprotobuf-dev \ - protobuf-compiler \ - clang \ - libsgx-headers \ - libsgx-dcap-quote-verify-dev - -WORKDIR /opt/vault/plugins -COPY --from=vault-auth-tee /opt/vault/plugins/vault-auth-tee ./ - -WORKDIR /build -RUN --mount=type=bind,target=/data rsync --exclude='/.git' --filter="dir-merge,- .gitignore" --exclude "Dockerfile-*" --exclude 'tee-vault-unseal.manifest.template' -av /data/ ./ -RUN sha256sum /opt/vault/plugins/vault-auth-tee | ( read a _ ; echo -n $a ) | tee assets/vault-auth-tee.sha256 -RUN --mount=type=cache,target=/usr/local/cargo/registry --mount=type=cache,target=target \ - RUSTFLAGS="-C target-cpu=icelake-server --cfg mio_unsupported_force_waker_pipe" \ - cargo build --locked --target x86_64-unknown-linux-gnu --release -p tee-vault-unseal --bin tee-vault-unseal \ - && mv ./target/x86_64-unknown-linux-gnu/release/tee-vault-unseal ./ - -FROM docker.io/gramineproject/gramine:v1.5 - -RUN curl -fsSLo /usr/share/keyrings/microsoft.asc https://packages.microsoft.com/keys/microsoft.asc \ - && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft.asc] https://packages.microsoft.com/ubuntu/20.04/prod focal main" > /etc/apt/sources.list.d/msprod.list \ - && apt-get update \ - && apt purge -y libsgx-dcap-default-qpl \ - && apt-get install -y az-dcap-client - -RUN apt purge -y libsgx-ae-qve -# libsgx-urts - -RUN rm -rf /var/lib/apt/lists/* - -# So we only have to use one gramine template -RUN touch /etc/sgx_default_qcnl.conf - -WORKDIR /app - -COPY --from=buildtee /build/tee-vault-unseal . -COPY ./bin/tee-vault-unseal/tee-vault-unseal.manifest.template . -COPY vault/enclave-key.pem . -RUN mkdir -p /opt/vault/tls && rm -rf /opt/vault/tls/* - -# The original Azure library is still delivering expired collateral, so we have to use a patched version -COPY --from=azuredcap /usr/local/lib/libdcap_quoteprov.so /usr/lib/ - -RUN gramine-manifest -Darch_libdir=/lib/x86_64-linux-gnu -Dexecdir=/usr/bin -Dlog_level=warning tee-vault-unseal.manifest.template tee-vault-unseal.manifest \ - && gramine-sgx-sign --manifest tee-vault-unseal.manifest --output tee-vault-unseal.manifest.sgx --key enclave-key.pem \ - && rm enclave-key.pem - -VOLUME /opt/vault/tls - -EXPOSE 8443 - -ENTRYPOINT ["/bin/sh", "-c"] -CMD [ "/restart_aesm.sh ; exec gramine-sgx tee-vault-unseal" ] diff --git a/bin/tee-vault-unseal/src/attestation.rs b/bin/tee-vault-unseal/src/attestation.rs deleted file mode 100644 index bf556fc1..00000000 --- a/bin/tee-vault-unseal/src/attestation.rs +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs - -use crate::Worker; -use actix_web::http::StatusCode; -use actix_web::web::{Data, Json}; -use anyhow::{Context, Result}; -use teepot::json::http::AttestationResponse; -use teepot::server::attestation::get_quote_and_collateral; -use teepot::server::{HttpResponseError, Status}; -use tracing::instrument; - -#[instrument(level = "info", name = "/v1/sys/attestation", skip_all)] -pub async fn get_attestation( - worker: Data, -) -> Result, HttpResponseError> { - let report_data: [u8; 64] = worker - .config - .report_data - .clone() - .try_into() - .map_err(|_| "Error getting attestation")?; - get_quote_and_collateral(None, &report_data) - .context("Error getting attestation") - .map(Json) - .status(StatusCode::INTERNAL_SERVER_ERROR) -} diff --git a/bin/tee-vault-unseal/src/init.rs b/bin/tee-vault-unseal/src/init.rs index df7ad47d..302c5fa7 100644 --- a/bin/tee-vault-unseal/src/init.rs +++ b/bin/tee-vault-unseal/src/init.rs @@ -1,12 +1,13 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2024 Matter Labs -use crate::{create_https_client, get_vault_status, UnsealServerState, Worker}; +use crate::{get_vault_status, UnsealServerState, Worker}; use actix_web::error::ErrorBadRequest; use actix_web::{web, HttpResponse}; use anyhow::{anyhow, Context, Result}; use awc::http::StatusCode; use serde_json::json; +use teepot::client::TeeConnection; use teepot::json::http::{Init, InitResponse, VaultInitRequest}; use teepot::json::secrets::AdminConfig; use teepot::server::{HttpResponseError, Status}; @@ -25,7 +26,8 @@ pub async fn post_init( admin_threshold, admin_tee_mrenclave, } = init.into_inner(); - let client = create_https_client(worker.client_tls_config.clone()); + let conn = TeeConnection::new(&worker.vault_attestation); + let client = conn.client(); let vault_url = &worker.config.vault_url; let vault_init = VaultInitRequest { @@ -62,7 +64,7 @@ pub async fn post_init( return Err(anyhow!("Vault already initialized")).status(StatusCode::BAD_REQUEST); } UnsealServerState::Undefined => { - let state = get_vault_status(vault_url, client.clone()).await; + let state = get_vault_status(vault_url, client).await; *worker.state.write().unwrap() = state; continue; } diff --git a/bin/tee-vault-unseal/src/main.rs b/bin/tee-vault-unseal/src/main.rs index 0467d221..9db3a500 100644 --- a/bin/tee-vault-unseal/src/main.rs +++ b/bin/tee-vault-unseal/src/main.rs @@ -1,55 +1,45 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2024 Matter Labs //! Server to initialize and unseal the Vault TEE. #![deny(missing_docs)] #![deny(clippy::all)] -mod attestation; mod init; mod unseal; -use actix_web::http::header; use actix_web::rt::time::sleep; use actix_web::web::Data; use actix_web::{web, App, HttpServer}; -use anyhow::{bail, Context, Result}; -use attestation::get_attestation; -use awc::{Client, Connector}; +use anyhow::{Context, Result}; +use awc::Client; use clap::Parser; use init::post_init; -use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; -use rustls::client::WebPkiServerVerifier; -use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; -use rustls::{ClientConfig, DigitallySignedStruct, Error, ServerConfig, SignatureScheme}; -use rustls_pemfile::{certs, read_one}; -use sha2::{Digest, Sha256}; +use rustls::ServerConfig; use std::fmt::Debug; use std::net::Ipv6Addr; use std::sync::{Arc, RwLock}; use std::time::Duration; -use std::{fs::File, io::BufReader}; -use teepot::json::http::{Init, Unseal, ATTESTATION_URL}; +use teepot::client::{AttestationArgs, TeeConnection}; +use teepot::json::http::{Init, Unseal}; use teepot::json::secrets::AdminConfig; -use teepot::server::attestation::get_quote_and_collateral; +use teepot::server::attestation::{get_quote_and_collateral, VaultAttestationArgs}; use teepot::server::new_json_cfg; +use teepot::server::pki::make_self_signed_cert; use teepot::sgx::{parse_tcb_levels, EnumSet, TcbLevel}; -use tracing::{error, info, trace}; +use tracing::{error, info}; use tracing_log::LogTracer; use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry}; use unseal::post_unseal; -use x509_cert::der::Decode as _; -use x509_cert::der::Encode as _; -use x509_cert::Certificate; -const VAULT_AUTH_TEE_SHA256: &str = include_str!("../../../assets/vault-auth-tee.sha256"); const VAULT_TOKEN_HEADER: &str = "X-Vault-Token"; /// Worker thread state and data +#[derive(Debug, Clone)] pub struct Worker { /// TLS config for the HTTPS client - pub client_tls_config: Arc, + pub vault_attestation: Arc, /// Server config pub config: Arc, /// Server state @@ -62,9 +52,13 @@ pub struct UnsealServerConfig { /// Vault URL pub vault_url: String, /// The expected report_data for the Vault TEE - pub report_data: Vec, + pub report_data: Box<[u8]>, /// allowed TCB levels pub allowed_tcb_levels: Option>, + /// SHA256 of the vault_auth_tee plugin binary + pub vault_auth_tee_sha: String, + /// version string of the vault_auth_tee plugin + pub vault_auth_tee_version: String, } /// Server state @@ -90,21 +84,6 @@ pub enum UnsealServerState { VaultUnsealed, } -impl UnsealServerConfig { - /// Create a new ServerState - pub fn new( - vault_url: String, - report_data: [u8; 64], - allowed_tcb_levels: Option>, - ) -> Self { - Self { - report_data: report_data.to_vec(), - vault_url, - allowed_tcb_levels, - } - } -} - #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct Args { @@ -114,9 +93,12 @@ struct Args { /// port to listen on #[arg(long, env = "PORT", default_value = "8443")] port: u16, - /// vault url - #[arg(long, env = "VAULT_ADDR", default_value = "https://vault:8210")] - vault_url: String, + #[arg(long, env = "VAULT_AUTH_TEE_SHA256")] + vault_auth_tee_sha: String, + #[arg(long, env = "VAULT_AUTH_TEE_VERSION")] + vault_auth_tee_version: String, + #[clap(flatten)] + pub attestation: VaultAttestationArgs, } #[actix_web::main] @@ -134,55 +116,50 @@ async fn main() -> Result<()> { let args = Args::parse(); - let tls_ok = std::path::Path::new("/opt/vault/tls/tls.ok"); - loop { - info!("Waiting for TLS key/cert files to be generated"); - - // Wait for the file `data/tls.key` to exist - if tls_ok.exists() { - break; - } - sleep(Duration::from_secs(1)).await; - } - info!("Starting up"); - let (config, client_tls_config, report_data) = load_rustls_config().or_else(|e| { - error!("failed to load rustls config: {e:?}"); - Err(e).context("Failed to load rustls config") - })?; - - if let Err(e) = get_quote_and_collateral(Some(args.allowed_tcb_levels), &report_data) { + if let Err(e) = get_quote_and_collateral(Some(args.allowed_tcb_levels), &[0u8; 64]) { error!("failed to get quote and collateral: {e:?}"); // don't return for now, we can still serve requests but we won't be able to attest } - let client = create_https_client(client_tls_config.clone()); + let (report_data, cert_chain, priv_key) = make_self_signed_cert("CN=localhost", None)?; - let server_state = get_vault_status(&args.vault_url, client).await; + // init server config builder with safe defaults + let config = ServerConfig::builder() + .with_no_client_auth() + .with_single_cert([cert_chain].into(), priv_key) + .context("Failed to load TLS key/cert files")?; + + let attestation_args: AttestationArgs = args.attestation.clone().into(); + + let conn = TeeConnection::new(&attestation_args); + + let server_state = get_vault_status(&args.attestation.vault_addr, conn.client()).await; info!("Starting HTTPS server at port {}", args.port); - let server_config = Arc::new(UnsealServerConfig::new( - args.vault_url, - report_data, - Some(args.allowed_tcb_levels), - )); + let server_config = Arc::new(UnsealServerConfig { + vault_url: args.attestation.vault_addr, + report_data: Box::from(report_data), + allowed_tcb_levels: Some(args.allowed_tcb_levels), + vault_auth_tee_sha: args.vault_auth_tee_sha, + vault_auth_tee_version: args.vault_auth_tee_version, + }); let server_state = Arc::new(RwLock::new(server_state)); - let server = match HttpServer::new(move || { - let worker = Worker { - client_tls_config: client_tls_config.clone(), - config: server_config.clone(), - state: server_state.clone(), - }; + let worker = Worker { + vault_attestation: Arc::new(attestation_args), + config: server_config, + state: server_state, + }; + let server = match HttpServer::new(move || { App::new() // enable logger //.wrap(TracingLogger::default()) .app_data(new_json_cfg()) - .app_data(Data::new(worker)) - .service(web::resource(ATTESTATION_URL).route(web::get().to(get_attestation))) + .app_data(Data::new(worker.clone())) .service(web::resource(Init::URL).route(web::post().to(post_init))) .service(web::resource(Unseal::URL).route(web::post().to(post_unseal))) }) @@ -203,7 +180,7 @@ async fn main() -> Result<()> { Ok(()) } -async fn get_vault_status(vault_url: &str, client: Client) -> UnsealServerState { +async fn get_vault_status(vault_url: &str, client: &Client) -> UnsealServerState { loop { let r = client .get(format!("{}/v1/sys/health", vault_url)) @@ -234,130 +211,3 @@ async fn get_vault_status(vault_url: &str, client: Client) -> UnsealServerState sleep(Duration::from_secs(1)).await; } } - -// Save the hash of the public server key to `REPORT_DATA` to check -// the attestations against it and it does not change on reconnect. -fn make_verifier(server_cert: Box<[u8]>) -> impl ServerCertVerifier { - #[derive(Debug)] - struct V { - server_cert: Box<[u8]>, - server_verifier: Arc, - } - impl ServerCertVerifier for V { - fn verify_server_cert( - &self, - end_entity: &CertificateDer, - _intermediates: &[CertificateDer], - _server_name: &ServerName, - _ocsp_response: &[u8], - _now: UnixTime, - ) -> std::result::Result { - let data = &self.server_cert; - - if data.as_ref() == end_entity.as_ref() { - info!("Server certificate matches expected certificate"); - Ok(ServerCertVerified::assertion()) - } else { - error!("Server certificate does not match expected certificate"); - Err(rustls::Error::General( - "Server certificate does not match expected certificate".to_string(), - )) - } - } - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> std::result::Result { - self.server_verifier - .verify_tls12_signature(message, cert, dss) - } - - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> std::result::Result { - self.server_verifier - .verify_tls13_signature(message, cert, dss) - } - - fn supported_verify_schemes(&self) -> Vec { - self.server_verifier.supported_verify_schemes() - } - } - let root_store = Arc::new(rustls::RootCertStore::empty()); - let server_verifier = WebPkiServerVerifier::builder(root_store).build().unwrap(); - V { - server_cert, - server_verifier, - } -} - -/// Load TLS key/cert files -pub fn load_rustls_config() -> Result<(ServerConfig, Arc, [u8; 64])> { - // init server config builder with safe defaults - let config = ServerConfig::builder().with_no_client_auth(); - - // load TLS key/cert files - let cert_file = &mut BufReader::new( - File::open("/opt/vault/tls/tls.crt").context("Failed to open TLS cert file")?, - ); - let key_file = &mut BufReader::new( - File::open("/opt/vault/tls/tls.key").context("Failed to open TLS key file")?, - ); - - // convert files to key/cert objects - let cert_chain = certs(cert_file) - .collect::, _>>() - .context("Failed to load TLS cert file")?; - - let priv_key: rustls::pki_types::PrivateKeyDer = - match read_one(key_file).context("Failed to read TLS key file")? { - Some(rustls_pemfile::Item::Sec1Key(key)) => key.into(), - Some(rustls_pemfile::Item::Pkcs1Key(key)) => key.into(), - Some(rustls_pemfile::Item::Pkcs8Key(key)) => key.into(), - _ => bail!("no keys found in TLS key file"), - }; - - let tls_config = Arc::new( - ClientConfig::builder() - .dangerous() - .with_custom_certificate_verifier(Arc::new(make_verifier( - cert_chain[0].as_ref().into(), - ))) - .with_no_client_auth(), - ); - - let cert = Certificate::from_der(cert_chain[0].as_ref()).unwrap(); - let pub_key = cert - .tbs_certificate - .subject_public_key_info - .to_der() - .unwrap(); - - let hash = Sha256::digest(pub_key); - let mut report_data = [0u8; 64]; - report_data[..32].copy_from_slice(&hash[..32]); - - let report_data_hex = hex::encode(report_data); - trace!(report_data_hex); - - let config = config - .with_single_cert(cert_chain, priv_key) - .context("Failed to load TLS key/cert files")?; - - Ok((config, tls_config, report_data)) -} - -/// Create an HTTPS client with the default headers and config -pub fn create_https_client(client_tls_config: Arc) -> Client { - Client::builder() - .add_default_header((header::USER_AGENT, "teepot/1.0")) - // a "connector" wraps the stream into an encrypted connection - .connector(Connector::new().rustls_0_22(client_tls_config)) - .timeout(Duration::from_secs(12000)) - .finish() -} diff --git a/bin/tee-vault-unseal/src/unseal.rs b/bin/tee-vault-unseal/src/unseal.rs index e9996085..c6fb1932 100644 --- a/bin/tee-vault-unseal/src/unseal.rs +++ b/bin/tee-vault-unseal/src/unseal.rs @@ -1,10 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2024 Matter Labs -use crate::{ - create_https_client, get_vault_status, UnsealServerConfig, UnsealServerState, Worker, - VAULT_AUTH_TEE_SHA256, VAULT_TOKEN_HEADER, -}; +use crate::{get_vault_status, UnsealServerConfig, UnsealServerState, Worker, VAULT_TOKEN_HEADER}; use actix_web::http::StatusCode; use actix_web::rt::time::sleep; use actix_web::{web, HttpResponse}; @@ -16,6 +13,7 @@ use std::future::Future; use std::io::Read; use std::time::Duration; use teepot::client::vault::VaultConnection; +use teepot::client::TeeConnection; use teepot::json::http::Unseal; use teepot::json::secrets::{AdminConfig, AdminState}; use teepot::server::{HttpResponseError, Status}; @@ -26,7 +24,8 @@ pub async fn post_unseal( worker: web::Data, item: web::Json, ) -> Result { - let client = create_https_client(worker.client_tls_config.clone()); + let conn = TeeConnection::new(&worker.vault_attestation); + let client = conn.client(); let app = &worker.config; let vault_url = &app.vault_url; @@ -46,7 +45,7 @@ pub async fn post_unseal( break; } UnsealServerState::Undefined => { - let state = get_vault_status(vault_url, client.clone()).await; + let state = get_vault_status(vault_url, client).await; *worker.state.write().unwrap() = state; continue; } @@ -106,8 +105,6 @@ pub async fn post_unseal( } => { debug!(root_token); info!("Vault is unsealed"); - let app = &worker.config; - let client = create_https_client(worker.client_tls_config.clone()); vault_configure_unsealed( app, @@ -204,9 +201,9 @@ pub async fn vault_configure_unsealed( )), root_token, json!({ - "sha256": VAULT_AUTH_TEE_SHA256, + "sha256": app.vault_auth_tee_sha, "command": "vault-auth-tee", - "version": "0.1.0+dev" + "version": app.vault_auth_tee_version }), ) .await @@ -374,7 +371,7 @@ async fn plugin_is_already_running( .and_then(|v| v.as_str()) .and_then(|v| if v.is_empty() { None } else { Some(v) }) .and_then(|v| { - if v == VAULT_AUTH_TEE_SHA256 { + if v == app.vault_auth_tee_sha { Some(v) } else { None diff --git a/bin/tee-vault-unseal/tee-vault-unseal.manifest.template b/bin/tee-vault-unseal/tee-vault-unseal.manifest.template deleted file mode 100644 index b30bf2a8..00000000 --- a/bin/tee-vault-unseal/tee-vault-unseal.manifest.template +++ /dev/null @@ -1,62 +0,0 @@ -libos.entrypoint = "/app/tee-vault-unseal" - -[loader] -argv = [ "/app/tee-vault-unseal" ] -entrypoint = "file:{{ gramine.libos }}" -env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}:/usr{{ arch_libdir }}:/lib" -env.HOME = "/app" -env.MALLOC_ARENA_MAX = "1" -env.AZDCAP_DEBUG_LOG_LEVEL = "ignore" -env.AZDCAP_COLLATERAL_VERSION = "v4" - -### Required configuration ### -env.ALLOWED_TCB_LEVELS = { passthrough = true } -env.VAULT_ADDR = { passthrough = true } - -### DEBUG ### -env.RUST_BACKTRACE = "1" -env.RUST_LOG="info,tee_vault_unseal=trace,teepot=trace,awc=debug" - -[fs] -root.uri = "file:/" -start_dir = "/app" -mounts = [ - { path = "{{ execdir }}", uri = "file:{{ execdir }}" }, - { path = "/lib", uri = "file:{{ gramine.runtimedir() }}" }, - { path = "{{ arch_libdir }}", uri = "file:{{ arch_libdir }}" }, - { path = "/etc", uri = "file:/etc" }, - { type = "tmpfs", path = "/var/tmp" }, - { type = "tmpfs", path = "/tmp" }, - { type = "tmpfs", path = "/app/.dcap-qcnl" }, - { type = "tmpfs", path = "/app/.az-dcap-client" }, - { type = "encrypted", path = "/opt/vault/tls", uri = "file:/opt/vault/tls", key_name = "_sgx_mrsigner" }, - { path = "/lib/libdcap_quoteprov.so", uri = "file:/lib/libdcap_quoteprov.so" }, -] - -[sgx] -trusted_files = [ - "file:/etc/ld.so.cache", - "file:/app/", - "file:{{ execdir }}/", - "file:{{ arch_libdir }}/", - "file:/usr/{{ arch_libdir }}/", - "file:{{ gramine.libos }}", - "file:{{ gramine.runtimedir() }}/", - "file:/usr/lib/ssl/openssl.cnf", - "file:/etc/ssl/", - "file:/etc/sgx_default_qcnl.conf", - "file:/lib/libdcap_quoteprov.so", -] -remote_attestation = "dcap" -max_threads = 64 -edmm_enable = false -## max enclave size -enclave_size = "2G" - -[sys] -enable_extra_runtime_domain_names_conf = true -enable_sigterm_injection = true - -# possible tweak option, if problems with mio -# currently mio is compiled with `mio_unsupported_force_waker_pipe` -# insecure__allow_eventfd = true diff --git a/bin/vault-admin/src/main.rs b/bin/vault-admin/src/main.rs index b6d93df7..e2f63811 100644 --- a/bin/vault-admin/src/main.rs +++ b/bin/vault-admin/src/main.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2024 Matter Labs use anyhow::{anyhow, bail, Context, Result}; use clap::{Args, Parser, Subcommand}; @@ -13,7 +13,7 @@ use std::path::{Path, PathBuf}; use teepot::client::{AttestationArgs, TeeConnection}; use teepot::json::http::{ SignRequest, SignRequestData, SignResponse, VaultCommandRequest, VaultCommands, - VaultCommandsResponse, ATTESTATION_URL, DIGEST_URL, + VaultCommandsResponse, DIGEST_URL, }; use teepot::server::signatures::verify_sig; use teepot::sgx::sign::Signature; @@ -221,7 +221,7 @@ async fn send_commands(args: SendArgs) -> Result<()> { signatures, }; - let conn = TeeConnection::new(&args.attestation, ATTESTATION_URL).await?; + let conn = TeeConnection::new(&args.attestation); let mut response = conn .client() @@ -299,7 +299,7 @@ async fn send_sig_request(args: SignTeeArgs) -> Result<()> { signatures, }; - let conn = TeeConnection::new(&args.attestation, ATTESTATION_URL).await?; + let conn = TeeConnection::new(&args.attestation); let mut response = conn .client() @@ -338,7 +338,7 @@ async fn send_sig_request(args: SignTeeArgs) -> Result<()> { } async fn digest(args: DigestArgs) -> Result<()> { - let conn = TeeConnection::new(&args.attestation, ATTESTATION_URL).await?; + let conn = TeeConnection::new(&args.attestation); let mut response = conn .client() diff --git a/bin/vault-unseal/src/main.rs b/bin/vault-unseal/src/main.rs index 0e3142fe..cace80ed 100644 --- a/bin/vault-unseal/src/main.rs +++ b/bin/vault-unseal/src/main.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2024 Matter Labs use anyhow::{anyhow, bail, Context, Result}; use base64::{engine::general_purpose, Engine as _}; @@ -8,7 +8,7 @@ use serde_json::Value; use std::fs::File; use std::io::Read; use teepot::client::{AttestationArgs, TeeConnection}; -use teepot::json::http::{Init, InitResponse, Unseal, ATTESTATION_URL}; +use teepot::json::http::{Init, InitResponse, Unseal}; use tracing::{error, info, trace, warn}; use tracing_log::LogTracer; use tracing_subscriber::Registry; @@ -70,7 +70,7 @@ async fn main() -> Result<()> { } async fn init(args: Arguments) -> Result<()> { - let conn = TeeConnection::new(&args.attestation, ATTESTATION_URL).await?; + let conn = TeeConnection::new(&args.attestation); info!("Quote verified! Connection secure!"); @@ -177,7 +177,7 @@ async fn unseal(args: Arguments) -> Result<()> { bail!("Error reading key from stdin"); } - let conn = TeeConnection::new(&args.attestation, ATTESTATION_URL).await?; + let conn = TeeConnection::new(&args.attestation); info!("Quote verified! Connection secure!"); diff --git a/src/client/mod.rs b/src/client/mod.rs index 96496eed..ab5fd78f 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -8,26 +8,27 @@ pub mod vault; -use crate::json::http::AttestationResponse; -use crate::sgx::Collateral; +use crate::server::pki::{RaTlsCollateralExtension, RaTlsQuoteExtension}; +use crate::sgx::Quote; pub use crate::sgx::{ parse_tcb_levels, sgx_ql_qv_result_t, verify_quote_with_collateral, EnumSet, QuoteVerificationResult, TcbLevel, }; use actix_web::http::header; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::Result; use awc::{Client, Connector}; use clap::Args; +use const_oid::AssociatedOid; +use intel_tee_quote_verification_rs::Collateral; use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerifier}; use rustls::client::WebPkiServerVerifier; use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; use rustls::{ClientConfig, DigitallySignedStruct, Error, SignatureScheme}; -use serde_json::Value; use sha2::{Digest, Sha256}; -use std::sync::{Arc, OnceLock}; +use std::sync::Arc; use std::time; use std::time::Duration; -use tracing::{error, info, warn}; +use tracing::{debug, error, info, trace, warn}; use x509_cert::der::{Decode as _, Encode as _}; use x509_cert::Certificate; @@ -61,13 +62,11 @@ impl TeeConnection { /// /// This will verify the attestation report and check that the enclave /// is running the expected code. - pub async fn new(args: &AttestationArgs, attestation_url: &str) -> Result { - let pk_hash = Arc::new(OnceLock::new()); - + pub fn new(args: &AttestationArgs) -> Self { let tls_config = Arc::new( ClientConfig::builder() .dangerous() - .with_custom_certificate_verifier(Arc::new(Self::make_verifier(pk_hash.clone()))) + .with_custom_certificate_verifier(Arc::new(Self::make_verifier(args.clone()))) .with_no_client_auth(), ); @@ -78,15 +77,10 @@ impl TeeConnection { .timeout(Duration::from_secs(12000)) .finish(); - let this = Self { + Self { server: args.server.clone(), client: agent, - }; - - this.check_attestation(args, attestation_url, pk_hash) - .await?; - - Ok(this) + } } /// Create a new connection to a TEE @@ -110,131 +104,12 @@ impl TeeConnection { &self.server } - async fn check_attestation( - &self, - args: &AttestationArgs, - attestation_url: &str, - pk_hash: Arc>, - ) -> Result<()> { - info!("Getting attestation report"); - - let mut response = self - .client - .get(&format!("{}{attestation_url}", args.server)) - .send() - .await - .map_err(|e| anyhow!("Error sending attestation request: {}", e))?; - - let status_code = response.status(); - if !status_code.is_success() { - error!("Failed to get attestation: {}", status_code); - if let Ok(r) = response.json::().await { - eprintln!("Failed to get attestation: {}", r); - } - bail!("failed to get attestation: {}", status_code); - } - - let attestation: AttestationResponse = - response.json().await.context("failed to get attestation")?; - - let current_time: i64 = time::SystemTime::now() - .duration_since(time::UNIX_EPOCH) - .unwrap() - .as_secs() as _; - - info!("Verifying attestation report"); - - let quote: &[u8] = &attestation.quote; - let collateral: Option<&Collateral> = Some(&attestation.collateral); - let pk_hash = pk_hash.get().unwrap(); - - Self::check_attestation_args(args, current_time, quote, collateral, pk_hash)?; - - Ok(()) - } - - /// Check the attestation report against `AttestationArgs` - pub fn check_attestation_args( - args: &AttestationArgs, - current_time: i64, - quote: &[u8], - collateral: Option<&Collateral>, - pk_hash: &[u8; 32], - ) -> Result<()> { - let QuoteVerificationResult { - collateral_expired, - result, - quote, - advisories, - .. - } = verify_quote_with_collateral(quote, collateral, current_time).unwrap(); - - if collateral_expired || !matches!(result, sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK) { - if collateral_expired { - error!("Collateral is out of date!"); - bail!("Collateral is out of date!"); - } - - let tcblevel = TcbLevel::from(result); - if args - .sgx_allowed_tcb_levels - .map_or(true, |levels| !levels.contains(tcblevel)) - { - error!("Quote verification result: {}", tcblevel); - bail!("Quote verification result: {}", tcblevel); - } - - info!("TcbLevel is allowed: {}", tcblevel); - } - - for advisory in advisories { - warn!("Info: Advisory ID: {advisory}"); - } - - if "e.report_body.reportdata[..32] != pk_hash { - error!("Report data mismatch"); - bail!("Report data mismatch"); - } else { - info!( - "Report data matches `{}`", - hex::encode("e.report_body.reportdata[..32]) - ); - } - - if let Some(mrsigner) = &args.sgx_mrsigner { - let mrsigner_bytes = hex::decode(mrsigner).context("Failed to decode mrsigner")?; - if quote.report_body.mrsigner[..] != mrsigner_bytes { - bail!( - "mrsigner mismatch: got {}, expected {}", - hex::encode(quote.report_body.mrsigner), - &mrsigner - ); - } else { - info!("mrsigner `{mrsigner}` matches"); - } - } - - if let Some(mrenclave) = &args.sgx_mrenclave { - let mrenclave_bytes = hex::decode(mrenclave).context("Failed to decode mrenclave")?; - if quote.report_body.mrenclave[..] != mrenclave_bytes { - bail!( - "mrenclave mismatch: got {}, expected {}", - hex::encode(quote.report_body.mrenclave), - &mrenclave - ); - } else { - info!("mrenclave `{mrenclave}` matches"); - } - } - Ok(()) - } - /// Save the hash of the public server key to `REPORT_DATA` to check /// the attestations against it and it does not change on reconnect. - pub fn make_verifier(pk_hash: Arc>) -> impl ServerCertVerifier { + pub fn make_verifier(args: AttestationArgs) -> impl ServerCertVerifier { #[derive(Debug)] struct V { - pk_hash: Arc>, + args: AttestationArgs, server_verifier: Arc, } impl ServerCertVerifier for V { @@ -246,6 +121,8 @@ impl TeeConnection { _ocsp_response: &[u8], _now: UnixTime, ) -> Result { + // TODO: DO RA-TLS verification here + let cert = Certificate::from_der(end_entity.as_ref()) .map_err(|e| Error::General(format!("Failed get certificate {e:?}")))?; let pub_key = cert @@ -255,20 +132,134 @@ impl TeeConnection { .unwrap(); let hash = Sha256::digest(pub_key); - let data = self.pk_hash.get_or_init(|| hash[..32].try_into().unwrap()); - if data == &hash[..32] { + let exts = cert + .tbs_certificate + .extensions + .ok_or_else(|| Error::General("Failed get quote in certificate".into()))?; + + trace!("Get quote bytes!"); + + let quote_bytes = exts + .iter() + .find(|ext| ext.extn_id == RaTlsQuoteExtension::OID) + .ok_or_else(|| Error::General("Failed get quote in certificate".into()))? + .extn_value + .as_bytes(); + + trace!("Get collateral bytes!"); + + let collateral = exts + .iter() + .find(|ext| ext.extn_id == RaTlsCollateralExtension::OID) + .and_then(|ext| { + serde_json::from_slice::(ext.extn_value.as_bytes()) + .map_err(|e| { + debug!("Failed to get collateral in certificate {e:?}"); + trace!( + "Failed to get collateral in certificate {:?}", + String::from_utf8_lossy(ext.extn_value.as_bytes()) + ); + }) + .ok() + }); + + if collateral.is_none() { + debug!("Failed to get collateral in certificate"); + } + + let quote = Quote::try_from_bytes(quote_bytes).map_err(|e| { + Error::General(format!("Failed get quote in certificate {e:?}")) + })?; + + if "e.report_body.reportdata[..32] != hash.as_slice() { + error!("Report data mismatch"); + return Err(Error::General("Report data mismatch".to_string())); + } else { info!( - "Checked or set server certificate public key hash `{}`", - hex::encode(&hash[..32]) + "Report data matches `{}`", + hex::encode("e.report_body.reportdata[..32]) ); - Ok(rustls::client::danger::ServerCertVerified::assertion()) - } else { - error!("Server certificate does not match expected certificate"); - Err(rustls::Error::General( - "Server certificate does not match expected certificate".to_string(), - )) } + + let current_time: i64 = time::SystemTime::now() + .duration_since(time::UNIX_EPOCH) + .unwrap() + .as_secs() as _; + + let QuoteVerificationResult { + collateral_expired, + result, + quote, + advisories, + earliest_expiration_date, + .. + } = verify_quote_with_collateral(quote_bytes, collateral.as_ref(), current_time) + .unwrap(); + + if collateral_expired || !matches!(result, sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK) + { + if collateral_expired { + error!( + "Collateral is out of date! Expired {}", + earliest_expiration_date + ); + return Err(Error::General(format!( + "Collateral is out of date! Expired {}", + earliest_expiration_date + ))); + } + + let tcblevel = TcbLevel::from(result); + if self + .args + .sgx_allowed_tcb_levels + .map_or(true, |levels| !levels.contains(tcblevel)) + { + error!("Quote verification result: {}", tcblevel); + return Err(Error::General(format!( + "Quote verification result: {}", + tcblevel + ))); + } + + info!("TcbLevel is allowed: {}", tcblevel); + } + + for advisory in advisories { + warn!("Info: Advisory ID: {advisory}"); + } + + if let Some(mrsigner) = &self.args.sgx_mrsigner { + let mrsigner_bytes = hex::decode(mrsigner) + .map_err(|e| Error::General(format!("Failed to decode mrsigner: {}", e)))?; + if quote.report_body.mrsigner[..] != mrsigner_bytes { + return Err(Error::General(format!( + "mrsigner mismatch: got {}, expected {}", + hex::encode(quote.report_body.mrsigner), + &mrsigner + ))); + } else { + info!("mrsigner `{mrsigner}` matches"); + } + } + + if let Some(mrenclave) = &self.args.sgx_mrenclave { + let mrenclave_bytes = hex::decode(mrenclave).map_err(|e| { + Error::General(format!("Failed to decode mrenclave: {}", e)) + })?; + if quote.report_body.mrenclave[..] != mrenclave_bytes { + return Err(Error::General(format!( + "mrenclave mismatch: got {}, expected {}", + hex::encode(quote.report_body.mrenclave), + &mrenclave + ))); + } else { + info!("mrenclave `{mrenclave}` matches"); + } + } + + Ok(rustls::client::danger::ServerCertVerified::assertion()) } fn verify_tls12_signature( @@ -295,10 +286,14 @@ impl TeeConnection { self.server_verifier.supported_verify_schemes() } } - let root_store = Arc::new(rustls::RootCertStore::empty()); - let server_verifier = WebPkiServerVerifier::builder(root_store).build().unwrap(); + let mut root_store = rustls::RootCertStore::empty(); + root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); + let server_verifier = WebPkiServerVerifier::builder(Arc::new(root_store)) + .build() + .unwrap(); + V { - pk_hash, + args, server_verifier, } } diff --git a/src/client/vault.rs b/src/client/vault.rs index 6f8a5c6a..8d7ef439 100644 --- a/src/client/vault.rs +++ b/src/client/vault.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2024 Matter Labs //! Helper functions for CLI clients to verify Intel SGX enclaves and other TEEs. @@ -22,11 +22,10 @@ use awc::error::{SendRequestError, StatusCode}; use awc::{Client, ClientResponse, Connector}; use bytes::Bytes; use futures_core::Stream; -use getrandom::getrandom; use rustls::ClientConfig; use serde_json::{json, Value}; use std::fmt::{Display, Formatter}; -use std::sync::{Arc, OnceLock}; +use std::sync::Arc; use std::time; use tracing::{debug, error, info, trace}; @@ -85,15 +84,14 @@ impl VaultConnection { /// This will verify the attestation report and check that the enclave /// is running the expected code. pub async fn new(args: &AttestationArgs, name: String) -> Result { - let pk_hash = Arc::new(OnceLock::new()); - - let (key_hash, rustls_certificate, rustls_pk) = make_self_signed_cert()?; + let (key_hash, rustls_certificate, rustls_pk) = + make_self_signed_cert("CN=localhost", None)?; let tls_config = Arc::new( ClientConfig::builder() .dangerous() .with_custom_certificate_verifier(Arc::new(TeeConnection::make_verifier( - pk_hash.clone(), + args.clone(), ))) .with_client_auth_cert(vec![rustls_certificate], rustls_pk)?, ); @@ -114,7 +112,7 @@ impl VaultConnection { client_token: Default::default(), }; - this.client_token = this.auth(args).await?.auth.client_token; + this.client_token = this.auth().await?.auth.client_token; trace!("Got Token: {:#?}", &this.client_token); @@ -147,24 +145,18 @@ impl VaultConnection { self.conn.client() } - async fn auth(&self, args: &AttestationArgs) -> Result { + async fn auth(&self) -> Result { info!("Getting attestation report"); let attestation_url = AuthRequest::URL; let quote = sgx_gramine_get_quote(&self.key_hash).context("Failed to get own quote")?; let collateral = tee_qv_get_collateral("e).context("Failed to get own collateral")?; - let mut challenge_bytes = [0u8; 32]; - getrandom(&mut challenge_bytes)?; - let challenge = hex::encode(challenge_bytes); - info!("Challenging Vault with: {}", challenge); - let challenge = Some(challenge_bytes); - let auth_req = AuthRequest { name: self.name.clone(), tee_type: "sgx".to_string(), quote, collateral: serde_json::to_string(&collateral)?, - challenge, + challenge: None, }; let mut response = self @@ -197,26 +189,6 @@ impl VaultConnection { trace!("Got AuthResponse: {:#?}", &auth_response); - let current_time: i64 = time::SystemTime::now() - .duration_since(time::UNIX_EPOCH) - .unwrap() - .as_secs() as _; - - info!("Verifying attestation report"); - - let collateral: Option = - serde_json::from_str(&auth_response.data.collateral).ok(); - let collateral = collateral.as_ref(); - - TeeConnection::check_attestation_args( - args, - current_time, - &auth_response.data.quote, - collateral, - &challenge_bytes, - ) - .context("Failed to verify Vault attestation report")?; - Ok(auth_response) } @@ -306,7 +278,10 @@ impl VaultConnection { } // check if rel_path is alphanumeric - if !rel_path.chars().all(|c| c.is_alphanumeric() || c == '_') { + if !rel_path + .chars() + .all(|c| c.is_alphanumeric() || c == '_' || c == '/') + { return Err(anyhow!("path is not alphanumeric")).status(StatusCode::BAD_REQUEST); } diff --git a/src/json/http.rs b/src/json/http.rs index ea3456de..af5d28ed 100644 --- a/src/json/http.rs +++ b/src/json/http.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2024 Matter Labs //! Common types for the teepot http JSON API @@ -23,9 +23,6 @@ impl Unseal { pub const URL: &'static str = "/v1/sys/unseal"; } -/// The attestation URL -pub const ATTESTATION_URL: &str = "/v1/sys/attestation"; - /// The attestation response #[derive(Debug, Serialize, Deserialize)] pub struct AttestationResponse { diff --git a/src/server/attestation.rs b/src/server/attestation.rs index f24f2a31..e390187f 100644 --- a/src/server/attestation.rs +++ b/src/server/attestation.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2024 Matter Labs //! Common attestation API for all TEEs diff --git a/src/server/mod.rs b/src/server/mod.rs index e21e999f..2657e041 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2024 Matter Labs //! # tee-server @@ -78,9 +78,15 @@ impl HttpResponseError { S: Stream>, { let status_code = response.status(); - error!("Vault returned server error: {}", status_code); let body = response.body().await.ok(); let content_type = response.content_type().to_string(); + + error!( + "Vault returned server error: {status_code} {}", + body.as_ref() + .map_or("", |b| std::str::from_utf8(b).unwrap_or("")) + ); + Self::Proxy(ProxyResponseError { status_code, body, diff --git a/src/server/pki.rs b/src/server/pki.rs index 332922f0..06566d94 100644 --- a/src/server/pki.rs +++ b/src/server/pki.rs @@ -3,213 +3,284 @@ //! Some cryptographic utilities +use crate::quote::get_quote; +use crate::sgx::tee_qv_get_collateral; pub use crate::sgx::{ parse_tcb_levels, sgx_ql_qv_result_t, verify_quote_with_collateral, EnumSet, QuoteVerificationResult, TcbLevel, }; -use anyhow::{anyhow, Context, Result}; -use const_oid::db::rfc5280::{ - ID_CE_BASIC_CONSTRAINTS, ID_CE_EXT_KEY_USAGE, ID_CE_KEY_USAGE, ID_KP_CLIENT_AUTH, - ID_KP_SERVER_AUTH, -}; -use const_oid::db::rfc5912::SECP_256_R_1; +use anyhow::{Context, Result}; +use const_oid::db::rfc5280::{ID_KP_CLIENT_AUTH, ID_KP_SERVER_AUTH}; +use const_oid::AssociatedOid; use getrandom::getrandom; -use pkcs8::der::asn1::OctetString; -use pkcs8::der::referenced::OwnedToRef; -use pkcs8::der::referenced::RefToOwned; -use pkcs8::{ - AlgorithmIdentifierRef, ObjectIdentifier, PrivateKeyInfo, SubjectPublicKeyInfo, - SubjectPublicKeyInfoRef, -}; +use p256::ecdsa::DerSignature; +use p256::pkcs8::EncodePrivateKey; +use pkcs8::der; use rustls::pki_types::PrivatePkcs8KeyDer; -use sec1::EcPrivateKey; use sha2::{Digest, Sha256}; +use signature::Signer; use std::str::FromStr; use std::time::Duration; -use x509_cert::der::asn1::BitString; -use x509_cert::der::{Decode as _, Encode as _}; -use x509_cert::ext::pkix::{BasicConstraints, ExtendedKeyUsage, KeyUsage, KeyUsages}; -use x509_cert::name::RdnSequence; +use tracing::debug; +use x509_cert::builder::{Builder, CertificateBuilder, Profile}; +use x509_cert::der::pem::LineEnding; +use x509_cert::der::{asn1::OctetString, Encode as _, EncodePem as _, Length}; +use x509_cert::ext::pkix::name::GeneralNames; +use x509_cert::ext::pkix::{ExtendedKeyUsage, SubjectAltName}; +use x509_cert::ext::{AsExtension, Extension}; +use x509_cert::name::{Name, RdnSequence}; use x509_cert::serial_number::SerialNumber; +use x509_cert::spki::{ + DynSignatureAlgorithmIdentifier, EncodePublicKey, ObjectIdentifier, SignatureBitStringEncoding, + SubjectPublicKeyInfoOwned, +}; use x509_cert::time::Validity; -use x509_cert::{Certificate, TbsCertificate}; +use x509_cert::Certificate; use zeroize::Zeroizing; -use const_oid::db::rfc5912::{ - ECDSA_WITH_SHA_256, ECDSA_WITH_SHA_384, ID_EC_PUBLIC_KEY as ECPK, SECP_256_R_1 as P256, - SECP_384_R_1 as P384, -}; -use pkcs8::der::asn1::BitStringRef; +/// The OID for the `gramine-ra-tls` quote extension +pub const ID_GRAMINE_RA_TLS_QUOTE: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.2.840.113741.1337.6"); -const ES256: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef { - oid: ECDSA_WITH_SHA_256, - parameters: None, -}; +/// The OID for the `enarx-ra-tls` collateral extension +/// TODO: this OID is just made up in `enarx` OID namespace, reserve it somehow +pub const ID_GRAMINE_RA_TLS_COLLATERAL: ObjectIdentifier = + ObjectIdentifier::new_unwrap("1.3.6.1.4.1.58270.1.99"); -const ES384: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef { - oid: ECDSA_WITH_SHA_384, - parameters: None, -}; +/// The `gramine-ra-tls` x509 extension +pub struct RaTlsQuoteExtension { + /// The hash of the certificate's public key + pub quote: Vec, +} -/// Utility trait for signing with a private key -pub trait PrivateKeyInfoExt { - /// Generates a keypair - /// - /// Returns the DER encoding of the `PrivateKeyInfo` type. - fn generate(oid: ObjectIdentifier) -> Result>>; - - /// Get the public key - /// - /// This function creates a `SubjectPublicKeyInfo` which corresponds with - /// this private key. Note that this function does not do any cryptographic - /// calculations. It expects that the `PrivateKeyInfo` already contains the - /// public key. - fn public_key(&self) -> Result>; - - /// Get the default signing algorithm for this `SubjectPublicKeyInfo` - fn signs_with(&self) -> Result>; - - /// Signs the body with the specified algorithm - /// - /// Note that the signature is returned in its encoded form as it will - /// appear in an X.509 certificate or PKCS#10 certification request. - fn sign(&self, body: &[u8], algo: AlgorithmIdentifierRef<'_>) -> Result>; +impl AssociatedOid for RaTlsQuoteExtension { + const OID: ObjectIdentifier = ID_GRAMINE_RA_TLS_QUOTE; } -impl<'a> PrivateKeyInfoExt for PrivateKeyInfo<'a> { - fn generate(oid: ObjectIdentifier) -> Result>> { - let rand = ring::rand::SystemRandom::new(); +impl x509_cert::der::Encode for RaTlsQuoteExtension { + fn encoded_len(&self) -> pkcs8::der::Result { + unimplemented!() + } + + fn encode( + &self, + _writer: &mut impl x509_cert::der::Writer, + ) -> Result<(), x509_cert::der::Error> { + unimplemented!() + } +} - let doc = match oid { - P256 => { - use ring::signature::{EcdsaKeyPair, ECDSA_P256_SHA256_ASN1_SIGNING as ALG}; - EcdsaKeyPair::generate_pkcs8(&ALG, &rand)? - } +impl AsExtension for RaTlsQuoteExtension { + fn critical(&self, _: &x509_cert::name::Name, _: &[x509_cert::ext::Extension]) -> bool { + false + } + fn to_extension( + &self, + _subject: &Name, + _extensions: &[Extension], + ) -> std::result::Result { + Ok(Extension { + extn_id: ::OID, + critical: false, + extn_value: OctetString::new(self.quote.as_slice())?, + }) + } +} - P384 => { - use ring::signature::{EcdsaKeyPair, ECDSA_P384_SHA384_ASN1_SIGNING as ALG}; - EcdsaKeyPair::generate_pkcs8(&ALG, &rand)? - } +/// The `gramine-ra-tls` x509 extension +pub struct RaTlsCollateralExtension { + /// The hash of the certificate's public key + pub collateral: Vec, +} - _ => return Err(anyhow!("unsupported")), - }; +impl AssociatedOid for RaTlsCollateralExtension { + const OID: ObjectIdentifier = ID_GRAMINE_RA_TLS_COLLATERAL; +} - Ok(doc.as_ref().to_vec().into()) +impl x509_cert::der::Encode for RaTlsCollateralExtension { + fn encoded_len(&self) -> pkcs8::der::Result { + unimplemented!() } - fn public_key(&self) -> Result> { - match self.algorithm.oids()? { - (ECPK, ..) => { - let ec = EcPrivateKey::from_der(self.private_key)?; - let pk = ec.public_key.ok_or_else(|| anyhow!("missing public key"))?; - Ok(SubjectPublicKeyInfo { - algorithm: self.algorithm, - subject_public_key: BitStringRef::new(0, pk)?, - }) - } - _ => Err(anyhow!("unsupported")), - } + fn encode( + &self, + _writer: &mut impl x509_cert::der::Writer, + ) -> Result<(), x509_cert::der::Error> { + unimplemented!() } +} - fn signs_with(&self) -> Result> { - match self.algorithm.oids()? { - (ECPK, Some(P256)) => Ok(ES256), - (ECPK, Some(P384)) => Ok(ES384), - _ => Err(anyhow!("unsupported")), - } +impl AsExtension for RaTlsCollateralExtension { + fn critical(&self, _: &x509_cert::name::Name, _: &[x509_cert::ext::Extension]) -> bool { + false } - - fn sign(&self, body: &[u8], algo: AlgorithmIdentifierRef<'_>) -> Result> { - let rng = ring::rand::SystemRandom::new(); - match (self.algorithm.oids()?, algo) { - ((ECPK, Some(P256)), ES256) => { - use ring::signature::{EcdsaKeyPair, ECDSA_P256_SHA256_ASN1_SIGNING as ALG}; - let kp = EcdsaKeyPair::from_pkcs8(&ALG, &self.to_der()?, &rng)?; - Ok(kp.sign(&rng, body)?.as_ref().to_vec()) - } - - ((ECPK, Some(P384)), ES384) => { - use ring::signature::{EcdsaKeyPair, ECDSA_P384_SHA384_ASN1_SIGNING as ALG}; - let kp = EcdsaKeyPair::from_pkcs8(&ALG, &self.to_der()?, &rng)?; - Ok(kp.sign(&rng, body)?.as_ref().to_vec()) - } - - _ => Err(anyhow!("unsupported")), - } + fn to_extension( + &self, + _subject: &Name, + _extensions: &[Extension], + ) -> std::result::Result { + Ok(Extension { + extn_id: ::OID, + critical: false, + extn_value: OctetString::new(self.collateral.as_slice())?, + }) } } /// Create a private key and a self-signed certificate -pub fn make_self_signed_cert() -> Result<( +pub fn make_self_signed_cert( + dn: &str, + an: Option, +) -> Result<( [u8; 64], rustls::pki_types::CertificateDer<'static>, rustls::pki_types::PrivateKeyDer<'static>, )> { // Generate a keypair. - let raw = PrivateKeyInfo::generate(SECP_256_R_1).context("failed to generate a private key")?; - let pki = PrivateKeyInfo::from_der(raw.as_ref()) - .context("failed to parse DER-encoded private key")?; - let der = pki.public_key().unwrap().to_der().unwrap(); + let mut rng = rand::thread_rng(); + let signing_key = p256::ecdsa::SigningKey::random(&mut rng); + let verifying_key = signing_key.verifying_key(); + let verifying_key_der = verifying_key + .to_public_key_der() + .context("failed to create public key der")?; let mut key_hash = [0u8; 64]; - let hash = Sha256::digest(der); + let hash = Sha256::digest(verifying_key_der.as_bytes()); key_hash[..32].copy_from_slice(&hash); + let quote = get_quote(&key_hash)?; + debug!("quote.len: {:?}", quote.len()); // Create a relative distinguished name. - let rdns = RdnSequence::from_str("CN=localhost")?; - - // Create the extensions. - let ku = KeyUsage(KeyUsages::DigitalSignature | KeyUsages::KeyEncipherment).to_der()?; - let eu = ExtendedKeyUsage(vec![ID_KP_SERVER_AUTH, ID_KP_CLIENT_AUTH]).to_der()?; - let bc = BasicConstraints { - ca: false, - path_len_constraint: None, - } - .to_der()?; + let rdns = RdnSequence::from_str(dn)?; + let collateral = tee_qv_get_collateral("e).context("Failed to get own collateral")?; let mut serial = [0u8; 16]; getrandom(&mut serial)?; - // Create the certificate body. - let tbs = TbsCertificate { - version: x509_cert::Version::V3, - serial_number: SerialNumber::new(&serial)?, - signature: pki.signs_with()?.ref_to_owned(), - issuer: rdns.clone(), - validity: Validity::from_now(Duration::from_secs(60 * 60 * 24 * 365))?, - subject: rdns, - subject_public_key_info: pki.public_key()?.ref_to_owned(), - issuer_unique_id: None, - subject_unique_id: None, - extensions: Some(vec![ - x509_cert::ext::Extension { - extn_id: ID_CE_KEY_USAGE, - critical: true, - extn_value: OctetString::new(ku)?, - }, - x509_cert::ext::Extension { - extn_id: ID_CE_BASIC_CONSTRAINTS, - critical: true, - extn_value: OctetString::new(bc)?, - }, - x509_cert::ext::Extension { - extn_id: ID_CE_EXT_KEY_USAGE, - critical: false, - extn_value: OctetString::new(eu)?, - }, - ]), - }; - - // Self-sign the certificate. - let alg = tbs.signature.clone(); - let sig = pki.sign(&tbs.to_der()?, alg.owned_to_ref())?; - let crt = Certificate { - tbs_certificate: tbs, - signature_algorithm: alg, - signature: BitString::from_bytes(&sig)?, - }; + let mut builder = CertificateBuilder::new( + Profile::Leaf { + issuer: rdns.clone(), + enable_key_agreement: true, + enable_key_encipherment: true, + }, + SerialNumber::new(&serial)?, + Validity::from_now(Duration::from_secs(60 * 60 * 24 * 365 * 10))?, + rdns, + SubjectPublicKeyInfoOwned::try_from(verifying_key_der.as_bytes()) + .context("failed to create SubjectPublicKeyInfo")?, + &signing_key, + ) + .context("failed to create CertificateBuilder")?; + + builder + .add_extension(&ExtendedKeyUsage(vec![ + ID_KP_SERVER_AUTH, + ID_KP_CLIENT_AUTH, + ])) + .context("failed to add ExtendedKeyUsage")?; + + if let Some(an) = an { + builder + .add_extension(&SubjectAltName(an)) + .context("failed to add SubjectAltName")?; + } + + builder + .add_extension(&RaTlsQuoteExtension { + quote: quote.to_vec(), + }) + .context("failed to add GRAMINE_RA_TLS")?; + + builder + .add_extension(&RaTlsCollateralExtension { + collateral: serde_json::to_string(&collateral) + .context("failed to add GRAMINE_RA_TLS")? + .as_bytes() + .into(), + }) + .context("failed to add GRAMINE_RA_TLS")?; + let crt = builder.build::().unwrap(); let rustls_certificate = rustls::pki_types::CertificateDer::from(crt.to_der()?); - let rustls_pk = rustls::pki_types::PrivateKeyDer::from(PrivatePkcs8KeyDer::from(pki.to_der()?)); + let signing_key_der = signing_key + .to_pkcs8_der() + .context("failed to encode PKCS#8")?; + + let rustls_pk = rustls::pki_types::PrivateKeyDer::from(PrivatePkcs8KeyDer::from( + signing_key_der.as_bytes(), + )) + .clone_key(); Ok((key_hash, rustls_certificate, rustls_pk)) } + +/// Create a private key and a self-signed certificate +pub fn make_signed_cert<'s, S, Signature>( + dn: &str, + an: Option, + issuer_cert: &Certificate, + issuer_key: &'s S, +) -> Result<([u8; 64], String, Zeroizing)> +where + Signature: SignatureBitStringEncoding, + S: signature::Keypair + DynSignatureAlgorithmIdentifier + Signer, + S::VerifyingKey: EncodePublicKey, +{ + // Generate a keypair. + let mut rng = rand::thread_rng(); + let signing_key = p256::ecdsa::SigningKey::random(&mut rng); + let verifying_key = signing_key.verifying_key(); + let verifying_key_der = verifying_key + .to_public_key_der() + .context("failed to create public key der")?; + + let mut key_hash = [0u8; 64]; + let hash = Sha256::digest(verifying_key_der.as_bytes()); + key_hash[..32].copy_from_slice(&hash); + + let quote = get_quote(&key_hash).context("Failed to get own quote")?; + + // Create a relative distinguished name. + let subject = Name::from_str(dn)?; + + let mut serial = [0u8; 16]; + getrandom(&mut serial)?; + + let mut builder = CertificateBuilder::new( + Profile::Leaf { + issuer: issuer_cert.tbs_certificate.subject.clone(), + enable_key_agreement: true, + enable_key_encipherment: true, + }, + SerialNumber::new(&serial)?, + Validity::from_now(Duration::from_secs(60 * 60 * 24 * 365 * 10))?, + subject, + SubjectPublicKeyInfoOwned::try_from(verifying_key_der.as_bytes()) + .context("failed to create SubjectPublicKeyInfo")?, + issuer_key, + ) + .context("failed to create CertificateBuilder")?; + builder + .add_extension(&ExtendedKeyUsage(vec![ + ID_KP_SERVER_AUTH, + ID_KP_CLIENT_AUTH, + ])) + .context("failed to add ExtendedKeyUsage")?; + + if let Some(an) = an { + builder + .add_extension(&SubjectAltName(an)) + .context("failed to add SubjectAltName")?; + } + + builder + .add_extension(&RaTlsQuoteExtension { + quote: quote.to_vec(), + }) + .context("failed to add GRAMINE_RA_TLS")?; + + let crt = builder.build::().unwrap(); + let cert_pem = crt.to_pem(LineEnding::LF)?; + let key_pem = signing_key.to_pkcs8_pem(LineEnding::LF)?; + + Ok((key_hash, cert_pem, key_pem)) +} diff --git a/src/sgx/mod.rs b/src/sgx/mod.rs index a841f617..2fc289b8 100644 --- a/src/sgx/mod.rs +++ b/src/sgx/mod.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (c) 2023 Matter Labs +// Copyright (c) 2023-2024 Matter Labs // Copyright (c) The Enarx Project Developers https://github.com/enarx/sgx @@ -114,7 +114,7 @@ pub struct QuoteVerificationResult<'a> { pub quote: &'a Quote, } -/// Verifies a quote with collateral material +/// Verifies a quote with optional collateral material pub fn verify_quote_with_collateral<'a>( quote: &'a [u8], collateral: Option<&Collateral>,