diff --git a/Cargo.lock b/Cargo.lock index c89d9660..672e423b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,9 +85,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "block-buffer" @@ -100,9 +100,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" @@ -112,15 +112,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" [[package]] name = "cc" -version = "1.2.1" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ "shlex", ] @@ -161,9 +161,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -304,25 +304,25 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "fastrand" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "ff" @@ -466,7 +466,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", ] [[package]] @@ -507,15 +519,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" - -[[package]] -name = "hermit-abi" -version = "0.3.9" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "hex" @@ -565,9 +571,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -577,9 +583,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.31" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", @@ -753,9 +759,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown", @@ -769,41 +775,42 @@ checksum = "d8972d5be69940353d5347a1344cb375d9b457d6809b428b05bb1ca2fb9ce007" [[package]] name = "ipnet" -version = "2.10.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "itoa" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "kvm-bindings" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4933174d0cc4b77b958578cd45784071cc5ae212c2d78fbd755aaaa6dfa71a" +checksum = "501bc0717c6a9fc409f29047ebeb6040a4d304344698abb268c4c6a440e6a09a" dependencies = [ "vmm-sys-util", ] [[package]] name = "kvm-ioctls" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "337d1afa126368bbd6a5c328048f71a69a737e9afe7e436b392a8f8d770c9171" +checksum = "3f9120f23310f01dd7b4fbb4ae1fd4eae3e09a7aa5b77038b08a6b37099d8ef4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "kvm-bindings", "libc", "vmm-sys-util", @@ -820,9 +827,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.164" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "libm" @@ -836,21 +843,21 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "libc", ] [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "lock_api" @@ -864,9 +871,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "memchr" @@ -882,30 +889,29 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] [[package]] name = "native-tls" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ "libc", "log", @@ -967,26 +973,26 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "openssl" -version = "0.10.68" +version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "cfg-if", "foreign-types", "libc", @@ -1008,15 +1014,15 @@ dependencies = [ [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", "libc", @@ -1032,9 +1038,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "p384" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" dependencies = [ "ecdsa", "elliptic-curve", @@ -1082,9 +1088,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -1115,9 +1121,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "ppv-lite86" @@ -1139,18 +1145,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" dependencies = [ "proc-macro2", ] @@ -1181,7 +1187,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -1195,11 +1201,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", ] [[package]] @@ -1208,7 +1214,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror", ] @@ -1265,9 +1271,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" dependencies = [ "const-oid", "digest", @@ -1291,15 +1297,15 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.41" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1311,17 +1317,23 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "scc" -version = "2.2.5" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b202022bb57c049555430e11fc22fea12909276a80a4c3d368da36ac1d88ed" +checksum = "ea091f6cac2595aa38993f04f4ee692ed43757035c36e67c180b6828356385b1" dependencies = [ "sdd", ] @@ -1343,9 +1355,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sdd" -version = "3.0.4" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49c1eeaf4b6a87c7479688c6d52b9f1153cedd3c489300564f932b065c6eab95" +checksum = "b07779b9b918cc05650cb30f404d4d7835d26df37c235eded8a6832e2fb82cca" [[package]] name = "sec1" @@ -1367,7 +1379,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.9.0", "core-foundation", "core-foundation-sys", "libc", @@ -1376,9 +1388,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -1386,9 +1398,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.215" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] @@ -1402,20 +1414,29 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_arrays" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38636132857f68ec3d5f3eb121166d2af33cb55174c4d5ff645db6165cbef0fd" +dependencies = [ + "serde", +] + [[package]] name = "serde_bytes" -version = "0.11.15" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +checksum = "364fec0df39c49a083c9a8a18a23a6bcfd9af130fe9fe321d18520a0d113e09e" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", @@ -1424,9 +1445,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -1495,6 +1516,7 @@ dependencies = [ "rsa", "serde", "serde-big-array", + "serde_arrays", "serde_bytes", "serial_test", "sha2", @@ -1542,15 +1564,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1592,9 +1614,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.87" +version = "2.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" dependencies = [ "proc-macro2", "quote", @@ -1641,12 +1663,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.14.0" +version = "3.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" dependencies = [ "cfg-if", "fastrand", + "getrandom 0.3.1", "once_cell", "rustix", "windows-sys 0.59.0", @@ -1684,9 +1707,9 @@ dependencies = [ [[package]] name = "tls_codec" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e78c9c330f8c85b2bae7c8368f2739157db9991235123aa1b15ef9502bfb6a" +checksum = "0de2e01245e2bb89d6f05801c564fa27624dbd7b1846859876c7dad82e90bf6b" dependencies = [ "tls_codec_derive", "zeroize", @@ -1694,9 +1717,9 @@ dependencies = [ [[package]] name = "tls_codec_derive" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c" +checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" dependencies = [ "proc-macro2", "quote", @@ -1705,9 +1728,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.41.1" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -1730,9 +1753,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -1749,9 +1772,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-core", @@ -1759,9 +1782,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] @@ -1774,21 +1797,21 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "url" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -1809,9 +1832,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.11.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" dependencies = [ "serde", ] @@ -1853,26 +1876,35 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -1881,21 +1913,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1903,9 +1936,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -1916,15 +1949,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -2088,6 +2124,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.9.0", +] + [[package]] name = "write16" version = "1.0.0" @@ -2114,9 +2159,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", @@ -2126,9 +2171,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", @@ -2159,18 +2204,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 675dddd7..89aa8287 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,12 +45,13 @@ snp = [] crypto_nossl = ["dep:p384", "dep:rsa", "dep:sha2", "dep:x509-cert"] [target.'cfg(target_os = "linux")'.dependencies] -iocuddle = "0.1" +iocuddle = "^0.1" [dependencies] openssl = { version = "0.10", optional = true } serde = { version = "1.0", features = ["derive"] } serde_bytes = "0.11" +serde_arrays = "0.1.0" bitflags = "1.2" codicon = "3.0" dirs = "5.0" @@ -69,12 +70,12 @@ x509-cert = { version = "0.2.5", optional = true } byteorder = "1.4.3" base64 = "0.22.1" rdrand = { version = "^0.8", optional = true } -reqwest = { version="0.11.10", features = ["blocking"], optional = true } -tokio = {version = "1.29.1", features =["rt-multi-thread"], optional = true } +reqwest = { version = "0.11.10", features = ["blocking"], optional = true } +tokio = { version = "1.29.1", features = ["rt-multi-thread"], optional = true } [target.'cfg(target_os = "linux")'.dev-dependencies] kvm-ioctls = ">=0.16" +kvm-bindings = ">=0.9.1" [dev-dependencies] -kvm-bindings = ">=0.9.1" serial_test = "3.0" diff --git a/src/certs/sev/sev/cert/v1/body/mod.rs b/src/certs/sev/sev/cert/v1/body/mod.rs index 6bbc7936..62675c5d 100644 --- a/src/certs/sev/sev/cert/v1/body/mod.rs +++ b/src/certs/sev/sev/cert/v1/body/mod.rs @@ -7,7 +7,7 @@ use super::*; #[repr(C, packed)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Data { - pub firmware: crate::Version, + pub firmware: crate::firmware::host::Version, pub reserved: u16, pub key: key::PubKey, } diff --git a/src/certs/sev/sev/cert/v1/sig/ecdsa.rs b/src/certs/sev/sev/cert/v1/sig/ecdsa.rs index 8b4ace9d..24b3bb51 100644 --- a/src/certs/sev/sev/cert/v1/sig/ecdsa.rs +++ b/src/certs/sev/sev/cert/v1/sig/ecdsa.rs @@ -3,23 +3,21 @@ #[cfg(feature = "openssl")] use {super::*, openssl::ecdsa}; -use crate::util::hexdump; +use crate::util::array::Array; use serde::{Deserialize, Serialize}; -use serde_big_array::BigArray; const SIG_PIECE_SIZE: usize = std::mem::size_of::<[u8; 72]>(); /// An ECDSA Signature. #[repr(C)] -#[derive(Copy, Clone, Deserialize, Serialize)] +#[derive(Default, Copy, Clone, Deserialize, Serialize)] pub struct Signature { - #[serde(with = "BigArray")] - r: [u8; 72], - #[serde(with = "BigArray")] - s: [u8; 72], - #[serde(with = "BigArray")] - _reserved: [u8; 512 - (SIG_PIECE_SIZE * 2)], + r: Array, + + s: Array, + + _reserved: Array, } impl std::fmt::Debug for Signature { @@ -40,17 +38,6 @@ impl PartialEq for Signature { } } -#[allow(clippy::derivable_impls)] -impl Default for Signature { - fn default() -> Self { - Signature { - r: [0u8; 72], - s: [0u8; 72], - _reserved: [0u8; (512 - (SIG_PIECE_SIZE * 2))], - } - } -} - impl std::fmt::Display for Signature { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -60,8 +47,7 @@ Signature: R: {} S: {} "#, - hexdump(&self.r), - hexdump(&self.s) + self.r, self.s ) } } @@ -71,9 +57,9 @@ impl From for Signature { #[inline] fn from(value: ecdsa::EcdsaSig) -> Self { Signature { - r: value.r().as_le_bytes(), - s: value.s().as_le_bytes(), - _reserved: [0; 512 - (SIG_PIECE_SIZE * 2)], + r: Array(value.r().as_le_bytes()), + s: Array(value.s().as_le_bytes()), + _reserved: Array([0; 512 - (SIG_PIECE_SIZE * 2)]), } } } @@ -94,8 +80,8 @@ impl TryFrom<&Signature> for ecdsa::EcdsaSig { #[inline] fn try_from(value: &Signature) -> Result { - let r = bn::BigNum::from_le(&value.r)?; - let s = bn::BigNum::from_le(&value.s)?; + let r = bn::BigNum::from_le(&*value.r)?; + let s = bn::BigNum::from_le(&*value.s)?; Ok(ecdsa::EcdsaSig::from_private_components(r, s)?) } } diff --git a/src/certs/snp/builtin/mod.rs b/src/certs/snp/builtin/mod.rs index 26ec27ab..cf52e00e 100644 --- a/src/certs/snp/builtin/mod.rs +++ b/src/certs/snp/builtin/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -/// Interfaces for retrieving builtin ARKs and ASKs for their respective generations. +//! Interfaces for retrieving builtin ARKs and ASKs for their respective generations. /// Genoa generation. pub mod genoa; diff --git a/src/certs/snp/ca/mod.rs b/src/certs/snp/ca/mod.rs index 637dad9b..ce47cbf0 100644 --- a/src/certs/snp/ca/mod.rs +++ b/src/certs/snp/ca/mod.rs @@ -1,11 +1,11 @@ // SPDX-License-Identifier: Apache-2.0 +//! Operations for a Certificate Authority (CA) chain. + #[cfg(feature = "openssl")] use openssl::x509::X509; use super::*; -/// Operations for a Certificate Authority (CA) chain. - /// A Certificate Authority (CA) chain. #[derive(Clone, Debug)] pub struct Chain { diff --git a/src/certs/snp/ecdsa/mod.rs b/src/certs/snp/ecdsa/mod.rs index c6da6fbe..d2f6f8c5 100644 --- a/src/certs/snp/ecdsa/mod.rs +++ b/src/certs/snp/ecdsa/mod.rs @@ -3,7 +3,7 @@ #[cfg(any(feature = "openssl", feature = "crypto_nossl"))] use super::*; -use crate::util::hexdump; +use crate::{firmware::parser::ByteParser, util::array::Array}; #[cfg(feature = "openssl")] use crate::certs::snp::{AsLeBytes, FromLe}; @@ -12,27 +12,33 @@ use crate::certs::snp::{AsLeBytes, FromLe}; use std::convert::TryFrom; use serde::{Deserialize, Serialize}; -use serde_big_array::BigArray; #[cfg(feature = "openssl")] use openssl::{bn, ecdsa}; -const SIG_PIECE_SIZE: usize = std::mem::size_of::<[u8; 72]>(); -const R_S_SIZE: usize = SIG_PIECE_SIZE * 2usize; +pub(crate) const SIG_PIECE_SIZE: usize = std::mem::size_of::<[u8; 72]>(); +pub(crate) const R_S_SIZE: usize = SIG_PIECE_SIZE * 2usize; #[repr(C)] -#[derive(Copy, Clone, Deserialize, Serialize, PartialOrd, Ord)] +#[derive(Default, Copy, Clone, Deserialize, Serialize, PartialOrd, Ord)] /// ECDSA signature. pub struct Signature { - #[serde(with = "BigArray")] - r: [u8; 72], - #[serde(with = "BigArray")] - s: [u8; 72], - #[serde(with = "BigArray")] - _reserved: [u8; 512 - R_S_SIZE], + r: Array, + + s: Array, + + _reserved: Array, } impl Signature { + /// Creates a new signature from the values specified + pub fn new(r: Array, s: Array) -> Self { + Self { + r, + s, + ..Default::default() + } + } /// Returns the signatures `r` component pub fn r(&self) -> &[u8; 72] { &self.r @@ -55,6 +61,30 @@ impl std::fmt::Debug for Signature { } } +impl ByteParser for Signature { + type Bytes = [u8; 512]; + #[inline(always)] + fn from_bytes(bytes: Self::Bytes) -> Self { + let mut r = [0; 72]; + let mut s = [0; 72]; + r.copy_from_slice(&bytes[0..72]); + s.copy_from_slice(&bytes[72..144]); + + Self::new(Array::from_bytes(r), Array::from_bytes(s)) + } + #[inline(always)] + fn to_bytes(&self) -> Self::Bytes { + let mut bytes = [0u8; 512]; + bytes[0..72].copy_from_slice(&*self.r); + bytes[72..144].copy_from_slice(&*self.s); + bytes + } + #[inline(always)] + fn default() -> Self { + Default::default() + } +} + impl Eq for Signature {} impl PartialEq for Signature { fn eq(&self, other: &Signature) -> bool { @@ -62,16 +92,6 @@ impl PartialEq for Signature { } } -impl Default for Signature { - fn default() -> Self { - Signature { - r: [0u8; 72], - s: [0u8; 72], - _reserved: [0u8; (512 - (SIG_PIECE_SIZE * 2))], - } - } -} - impl std::fmt::Display for Signature { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -81,8 +101,7 @@ Signature: R: {} S: {} "#, - hexdump(&self.r), - hexdump(&self.s) + self.r, self.s ) } } @@ -92,9 +111,9 @@ impl From for Signature { #[inline] fn from(value: ecdsa::EcdsaSig) -> Self { Signature { - r: value.r().as_le_bytes(), - s: value.s().as_le_bytes(), - _reserved: [0; 512 - (SIG_PIECE_SIZE * 2)], + r: Array(value.r().as_le_bytes()), + s: Array(value.s().as_le_bytes()), + _reserved: Array([0; 512 - (SIG_PIECE_SIZE * 2)]), } } } @@ -115,8 +134,8 @@ impl TryFrom<&Signature> for ecdsa::EcdsaSig { #[inline] fn try_from(value: &Signature) -> Result { - let r = bn::BigNum::from_le(&value.r)?; - let s = bn::BigNum::from_le(&value.s)?; + let r = bn::BigNum::from_le(&*value.r)?; + let s = bn::BigNum::from_le(&*value.s)?; Ok(ecdsa::EcdsaSig::from_private_components(r, s)?) } } @@ -159,7 +178,7 @@ mod tests { #[test] fn test_signature_default() { - let sig: Signature = Signature::default(); + let sig: Signature = Default::default(); assert_eq!(sig.r(), &[0u8; 72]); assert_eq!(sig.s(), &[0u8; 72]); } @@ -167,9 +186,9 @@ mod tests { #[test] fn test_signature_getters() { let sig: Signature = Signature { - r: [1u8; 72], - s: [2u8; 72], - _reserved: [0u8; 512 - (SIG_PIECE_SIZE * 2)], + r: Array([1u8; 72]), + s: Array([2u8; 72]), + _reserved: Array([0u8; 512 - (SIG_PIECE_SIZE * 2)]), }; assert_eq!(sig.r(), &[1u8; 72]); assert_eq!(sig.s(), &[2u8; 72]); @@ -177,12 +196,12 @@ mod tests { #[test] fn test_signature_eq() { - let sig1: Signature = Signature::default(); - let sig2: Signature = Signature::default(); + let sig1: Signature = Default::default(); + let sig2: Signature = Default::default(); let sig3: Signature = Signature { - r: [1u8; 72], - s: [0u8; 72], - _reserved: [0u8; 512 - (SIG_PIECE_SIZE * 2)], + r: Array([1u8; 72]), + s: Array([0u8; 72]), + _reserved: Array([0u8; 512 - (SIG_PIECE_SIZE * 2)]), }; assert_eq!(sig1, sig2); @@ -191,11 +210,11 @@ mod tests { #[test] fn test_signature_ord() { - let sig1: Signature = Signature::default(); + let sig1: Signature = Default::default(); let sig2: Signature = Signature { - r: [1u8; 72], - s: [0u8; 72], - _reserved: [0u8; 512 - (SIG_PIECE_SIZE * 2)], + r: Array([1u8; 72]), + s: Array([0u8; 72]), + _reserved: Array([0u8; 512 - (SIG_PIECE_SIZE * 2)]), }; assert!(sig1 < sig2); @@ -203,7 +222,7 @@ mod tests { #[test] fn test_signature_debug() { - let sig: Signature = Signature::default(); + let sig: Signature = Default::default(); let debug_str: String = format!("{:?}", sig); assert!(debug_str.starts_with("Signature { r: ")); assert!(debug_str.contains(", s: ")); @@ -211,7 +230,7 @@ mod tests { #[test] fn test_signature_display() { - let sig: Signature = Signature::default(); + let sig: Signature = Default::default(); let display_str: String = format!("{}", sig); assert!(display_str.contains("Signature:")); assert!(display_str.contains("R:")); @@ -247,7 +266,7 @@ mod tests { #[test] fn test_try_into_ecdsa_sig() { - let sig = Signature::default(); + let sig: Signature = Default::default(); let ecdsa_sig: ecdsa::EcdsaSig = (&sig).try_into().unwrap(); assert_eq!(ecdsa_sig.r().to_vec(), vec![]); assert_eq!(ecdsa_sig.s().to_vec(), vec![]); @@ -255,7 +274,7 @@ mod tests { #[test] fn test_try_into_vec() { - let sig = Signature::default(); + let sig: Signature = Default::default(); let der: Vec = (&sig).try_into().unwrap(); assert!(!der.is_empty()); } @@ -269,7 +288,7 @@ mod tests { #[test] #[should_panic] fn test_try_into_p384_signature_failure() { - let signature: Signature = Signature::default(); + let signature: Signature = Default::default(); let _p384_sig: p384::ecdsa::Signature = (&signature).try_into().unwrap(); } @@ -278,9 +297,9 @@ mod tests { fn test_try_into_p384_signature() { // Test with non-zero values let sig = Signature { - r: [1u8; 72], - s: [2u8; 72], - _reserved: [0u8; 512 - (SIG_PIECE_SIZE * 2)], + r: Array([1u8; 72]), + s: Array([2u8; 72]), + _reserved: Array([0u8; 512 - (SIG_PIECE_SIZE * 2)]), }; let p384_sig: p384::ecdsa::Signature = (&sig).try_into().unwrap(); assert_eq!(p384_sig.r().to_bytes().as_slice(), &[1u8; 48]); @@ -290,7 +309,7 @@ mod tests { #[test] fn test_signature_serde() { - let sig: Signature = Signature::default(); + let sig: Signature = Default::default(); let serialized: Vec = bincode::serialize(&sig).unwrap(); let deserialized: Signature = bincode::deserialize(&serialized).unwrap(); assert_eq!(sig, deserialized); @@ -299,9 +318,9 @@ mod tests { #[test] fn test_signature_max_values() { let sig: Signature = Signature { - r: [0xFF; 72], - s: [0xFF; 72], - _reserved: [0u8; 512 - (SIG_PIECE_SIZE * 2)], + r: Array([0xFF; 72]), + s: Array([0xFF; 72]), + _reserved: Array([0u8; 512 - (SIG_PIECE_SIZE * 2)]), }; assert_eq!(sig.r(), &[0xFF; 72]); assert_eq!(sig.s(), &[0xFF; 72]); diff --git a/src/error.rs b/src/error.rs index 0bf58ce0..b62007c3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -117,7 +117,7 @@ impl Display for VmmError { /// The raw firmware error. #[derive(Debug)] -pub(crate) struct RawFwError(u64); +pub(crate) struct RawFwError(pub(crate) u64); impl std::error::Error for RawFwError {} diff --git a/src/firmware/guest/mod.rs b/src/firmware/guest/mod.rs index 2a2f21dc..4e6be030 100644 --- a/src/firmware/guest/mod.rs +++ b/src/firmware/guest/mod.rs @@ -21,7 +21,6 @@ use crate::firmware::{ }, }; -use std::convert::TryFrom; #[cfg(target_os = "linux")] use std::fs::{File, OpenOptions}; @@ -44,6 +43,7 @@ fn map_fw_err(raw_error: RawFwError) -> UserApiError { /// A handle to the SEV-SNP guest device. #[cfg(target_os = "linux")] +#[derive(Debug)] pub struct Firmware(File); #[cfg(target_os = "linux")] @@ -110,7 +110,7 @@ impl Firmware { let raw_report = response.report.as_array(); - Ok(AttestationReport::try_from(raw_report.as_slice())?) + Ok(AttestationReport::from_bytes(&raw_report)?) } /// Request an extended attestation report from the AMD Secure Processor. @@ -182,9 +182,9 @@ impl Firmware { Err(FirmwareError::from(report_response.status))? } - let raw_report = report_response.report.as_array(); + let raw_report = report_response.report; - let report = AttestationReport::try_from(raw_report.as_slice())?; + let report = AttestationReport::from_bytes(&*raw_report)?; if ext_report_request.certs_len == 0 { return Ok((report, None)); @@ -241,3 +241,26 @@ impl Firmware { Ok(ffi_derived_key_response.key) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_firmware_error_mapping() { + let raw_error = RawFwError(1); // Lower byte error + let error = map_fw_err(raw_error); + assert!(matches!(error, UserApiError::FirmwareError(_))); + + let raw_error = RawFwError(0x100000000u64); // Upper byte error + let error = map_fw_err(raw_error); + assert!(matches!(error, UserApiError::VmmError(_))); + + let raw_error = RawFwError(0x0u64); // lower byte error + let error = map_fw_err(raw_error); + assert!(matches!( + error, + UserApiError::FirmwareError(FirmwareError::UnknownSevError(0)) + )); + } +} diff --git a/src/firmware/guest/types/snp.rs b/src/firmware/guest/types/snp.rs index fcc8d7cd..740d0fc1 100644 --- a/src/firmware/guest/types/snp.rs +++ b/src/firmware/guest/types/snp.rs @@ -1,21 +1,32 @@ // SPDX-License-Identifier: Apache-2.0 -use crate::error::AttestationReportError; -use crate::{certs::snp::ecdsa::Signature, firmware::host::TcbVersion, util::hexdump}; - #[cfg(any(feature = "openssl", feature = "crypto_nossl"))] use crate::certs::snp::{Certificate, Chain, Verifiable}; -use crate::util::sealed; -use bitfield::bitfield; +use crate::{ + certs::snp::ecdsa::Signature, + error::AttestationReportError, + firmware::{ + host::TcbVersion, + parser::{ByteParser, ReadExt, WriteExt}, + }, + util::array::Array, +}; + use serde::{Deserialize, Serialize, Serializer}; -use serde_big_array::BigArray; -use std::convert::{TryFrom, TryInto}; -use std::fmt::Display; +use std::{fmt::Display, io::Write}; + +#[cfg(any(feature = "openssl", feature = "crypto_nossl"))] +use std::{ + convert::TryFrom, + io::{self, ErrorKind}, +}; + +#[cfg(feature = "openssl")] +use std::io::Error; -// #[cfg(any(feature = "openssl", feature = "crypto_nossl"))] -use std::io::{self, Error, ErrorKind}; +use bitfield::bitfield; #[cfg(feature = "openssl")] use openssl::{ecdsa::EcdsaSig, sha::Sha384}; @@ -102,20 +113,66 @@ bitfield! { pub get_tcb_version, set_tcb_version: 5, 5; } -/// Trait shared between attestation reports to be able to verify them against the VEK. -pub trait Attestable: Serialize + sealed::Sealed { - /// Serialize the provided Attestation Report and get the measurable bytes from it - fn measurable_bytes(&self) -> io::Result> { - let measurable_bytes: &[u8] = &bincode::serialize(self).map_err(|e| { - Error::new( - ErrorKind::Other, - format!("Unable to serialize bytes: {}", e), - ) - })?; - Ok(measurable_bytes[..0x2a0].to_vec()) +#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord)] +/// A semver formatted version. +pub struct Version { + /// Major Version + pub major: u8, + /// Minor Version + pub minor: u8, + /// Build Version + pub build: u8, +} + +impl Version { + /// Create a new version. + pub fn new(major: u8, minor: u8, build: u8) -> Self { + Self { + major, + minor, + build, + } + } +} + +impl Default for Version { + fn default() -> Self { + ByteParser::default() + } +} + +impl std::fmt::Display for Version { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}.{}.{}", self.major, self.minor, self.build) + } +} + +impl ByteParser for Version { + type Bytes = [u8; 3]; + + #[inline(always)] + fn from_bytes(bytes: Self::Bytes) -> Self { + let [build, minor, major] = bytes; + Self { + major, + minor, + build, + } + } + + #[inline(always)] + fn to_bytes(&self) -> Self::Bytes { + [self.build, self.minor, self.major] + } + + #[inline(always)] + fn default() -> Self { + Self { + major: 0, + minor: 0, + build: 0, + } } - /// Get the attestation report signature - fn signature(&self) -> &Signature; } /// The guest can request that the firmware construct an attestation report. External entities can use an @@ -141,7 +198,7 @@ pub trait Attestable: Serialize + sealed::Sealed { /// /// Since the release of the 1.56 ABI, the Attestation Report was bumped from version 2 to 3. /// Due to content differences, both versions are kept separately in order to provide backwards compatibility and most reliable security. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum AttestationReport { /// Version 2 of the Attestation Report V2(AttestationReportV2), @@ -149,27 +206,6 @@ pub enum AttestationReport { V3(AttestationReportV3), } -impl TryFrom<&[u8]> for AttestationReport { - type Error = AttestationReportError; - - fn try_from(raw_report: &[u8]) -> Result { - let version = - u32::from_le_bytes([raw_report[0], raw_report[1], raw_report[2], raw_report[3]]); - // Return the appropriate report version - match version { - 2 => { - let report_v2: AttestationReportV2 = raw_report.try_into()?; - Ok(AttestationReport::V2(report_v2)) - } - 3 => { - let report_v3: AttestationReportV3 = raw_report.try_into()?; - Ok(AttestationReport::V3(report_v3)) - } - _ => Err(AttestationReportError::UnsupportedReportVersion(version))?, - } - } -} - /// Implement custom serialization for AttestationReport /// This will ensure that the Attestation Report enum gets serialized into raw bytes, /// not the serde default that tags the enum with 4 extra bytes for its own tagging mechanism. @@ -185,35 +221,19 @@ impl Serialize for AttestationReport { } } -impl sealed::Sealed for AttestationReport {} - -impl Attestable for AttestationReport { - fn measurable_bytes(&self) -> io::Result> { - //Return measurable bytes for the report - match self { - Self::V2(v2) => Ok(v2.measurable_bytes()?), - - Self::V3(v3) => Ok(v3.measurable_bytes()?), - } - } - fn signature(&self) -> &Signature { - match self { - Self::V2(v2) => v2.signature(), - Self::V3(v3) => v3.signature(), - } +impl From<&AttestationReportV2> for AttestationReport { + fn from(value: &AttestationReportV2) -> Self { + AttestationReport::V2(*value) } } -impl AttestationReport { - /// Convert bytes to an Attestation Report Enum - pub fn from_bytes(bytes: &[u8]) -> Result { - AttestationReport::try_from(bytes) +impl From<&AttestationReportV3> for AttestationReport { + fn from(value: &AttestationReportV3) -> Self { + AttestationReport::V3(*value) } +} - /// Serialize the Attestation Report enum to raw bytes - pub fn to_bytes(&self) -> Result, AttestationReportError> { - bincode::serialize(self).map_err(|e| AttestationReportError::BincodeError(*e)) - } +impl AttestationReport { /// Get Attestation Report Version pub fn version(&self) -> u32 { match self { @@ -240,15 +260,15 @@ impl AttestationReport { /// Get Family ID pub fn family_id(&self) -> [u8; 16] { match self { - Self::V2(report) => report.family_id, - Self::V3(report) => report.family_id, + Self::V2(report) => *report.family_id, + Self::V3(report) => *report.family_id, } } /// Get Image ID pub fn image_id(&self) -> [u8; 16] { match self { - Self::V2(report) => report.image_id, - Self::V3(report) => report.image_id, + Self::V2(report) => *report.image_id, + Self::V3(report) => *report.image_id, } } @@ -294,56 +314,56 @@ impl AttestationReport { /// Get the guest provided report data pub fn report_data(&self) -> [u8; 64] { match self { - Self::V2(report) => report.report_data, - Self::V3(report) => report.report_data, + Self::V2(report) => *report.report_data, + Self::V3(report) => *report.report_data, } } /// Get the measurement calculated at launch. pub fn measurement(&self) -> [u8; 48] { match self { - Self::V2(report) => report.measurement, - Self::V3(report) => report.measurement, + Self::V2(report) => *report.measurement, + Self::V3(report) => *report.measurement, } } /// Get data provided by the hypervisor at launch. pub fn host_data(&self) -> [u8; 32] { match self { - Self::V2(report) => report.host_data, - Self::V3(report) => report.host_data, + Self::V2(report) => *report.host_data, + Self::V3(report) => *report.host_data, } } /// Get the SHA-384 digest of the ID public key that signed the ID block. pub fn id_key_digest(&self) -> [u8; 48] { match self { - Self::V2(report) => report.id_key_digest, - Self::V3(report) => report.id_key_digest, + Self::V2(report) => *report.id_key_digest, + Self::V3(report) => *report.id_key_digest, } } /// Get the SHA-384 digest of the author public key that certified the ID key,. pub fn author_key_digest(&self) -> [u8; 48] { match self { - Self::V2(report) => report.author_key_digest, - Self::V3(report) => report.author_key_digest, + Self::V2(report) => *report.author_key_digest, + Self::V3(report) => *report.author_key_digest, } } /// Get report ID of this guest pub fn report_id(&self) -> [u8; 32] { match self { - Self::V2(report) => report.report_id, - Self::V3(report) => report.report_id, + Self::V2(report) => *report.report_id, + Self::V3(report) => *report.report_id, } } /// Get report ID of this guest's migration agent (if applicable). pub fn report_id_ma(&self) -> [u8; 32] { match self { - Self::V2(report) => report.report_id_ma, - Self::V3(report) => report.report_id_ma, + Self::V2(report) => *report.report_id_ma, + Self::V3(report) => *report.report_id_ma, } } @@ -356,25 +376,23 @@ impl AttestationReport { } /// Get the CPUID info from the report (V3 only) - pub fn cpuid(&self) -> Result<(u8, u8, u8), AttestationReportError> { + pub fn cpuid(&self) -> Option<(u8, u8, u8)> { match self { - Self::V2(_) => Err(AttestationReportError::UnsupportedField( - "cpuid information".to_string(), - )), - Self::V3(report) => Ok((report.cpuid_fam_id, report.cpuid_mod_id, report.cpuid_step)), + Self::V2(_) => None, + Self::V3(report) => Some((report.cpuid_fam_id, report.cpuid_mod_id, report.cpuid_step)), } } /// Get the CHIP ID of the attestation report pub fn chip_id(&self) -> [u8; 64] { match self { - Self::V2(report) => report.chip_id, - Self::V3(report) => report.chip_id, + Self::V2(report) => *report.chip_id, + Self::V3(report) => *report.chip_id, } } /// Get commited TCB - pub fn commited_tcb(&self) -> TcbVersion { + pub fn committed_tcb(&self) -> TcbVersion { match self { Self::V2(report) => report.committed_tcb, Self::V3(report) => report.committed_tcb, @@ -382,34 +400,18 @@ impl AttestationReport { } /// Get the current version in the report (major,minor,build) - pub fn current_version(&self) -> (u8, u8, u8) { + pub fn current(&self) -> Version { match self { - Self::V2(report) => ( - report.current_major, - report.current_minor, - report.current_build, - ), - Self::V3(report) => ( - report.current_major, - report.current_minor, - report.current_build, - ), + Self::V2(report) => report.current, + Self::V3(report) => report.current, } } /// Get the committed version in the report (major,minor,build) - pub fn commited_version(&self) -> (u8, u8, u8) { + pub fn committed(&self) -> Version { match self { - Self::V2(report) => ( - report.committed_major, - report.committed_minor, - report.committed_build, - ), - Self::V3(report) => ( - report.committed_major, - report.committed_minor, - report.committed_build, - ), + Self::V2(report) => report.committed, + Self::V3(report) => report.committed, } } @@ -420,6 +422,14 @@ impl AttestationReport { Self::V3(report) => report.launch_tcb, } } + + /// Get signature + pub fn signature(&self) -> Signature { + match self { + Self::V2(report) => report.signature, + Self::V3(report) => report.signature, + } + } } impl Display for AttestationReport { @@ -435,7 +445,7 @@ impl Display for AttestationReport { /// The first upstream supported attestation report /// Systems that contain firmware prior to the spec release 1.56 will use this attestation report. #[repr(C)] -#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Default, Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord)] pub struct AttestationReportV2 { /// Version number of this attestation report. Set to 2h for this specification. pub version: u32, @@ -444,9 +454,9 @@ pub struct AttestationReportV2 { /// The guest policy. pub policy: GuestPolicy, /// The family ID provided at launch. - pub family_id: [u8; 16], + pub family_id: Array, /// The image ID provided at launch. - pub image_id: [u8; 16], + pub image_id: Array, /// The request VMPL for the attestation report. pub vmpl: u32, /// The signature algorithm used to sign this report. @@ -457,96 +467,161 @@ pub struct AttestationReportV2 { pub plat_info: PlatformInfoV1, /// Information related to signing keys in the report. See KeyInfo pub key_info: KeyInfo, - _reserved_0: u32, - #[serde(with = "BigArray")] + /// Guest-provided 512 Bits of Data - pub report_data: [u8; 64], - #[serde(with = "BigArray")] + pub report_data: Array, + /// The measurement calculated at launch. - pub measurement: [u8; 48], + pub measurement: Array, /// Data provided by the hypervisor at launch. - pub host_data: [u8; 32], - #[serde(with = "BigArray")] + pub host_data: Array, + /// SHA-384 digest of the ID public key that signed the ID block provided /// in SNP_LANUNCH_FINISH. - pub id_key_digest: [u8; 48], - #[serde(with = "BigArray")] + pub id_key_digest: Array, + /// SHA-384 digest of the Author public key that certified the ID key, /// if provided in SNP_LAUNCH_FINSIH. Zeroes if AUTHOR_KEY_EN is 1. - pub author_key_digest: [u8; 48], + pub author_key_digest: Array, /// Report ID of this guest. - pub report_id: [u8; 32], + pub report_id: Array, /// Report ID of this guest's migration agent (if applicable). - pub report_id_ma: [u8; 32], + pub report_id_ma: Array, /// Reported TCB version used to derive the VCEK that signed this report. pub reported_tcb: TcbVersion, - _reserved_1: [u8; 24], - #[serde(with = "BigArray")] + /// If MaskChipId is set to 0, Identifier unique to the chip. /// Otherwise set to 0h. - pub chip_id: [u8; 64], + pub chip_id: Array, /// CommittedTCB pub committed_tcb: TcbVersion, - /// The build number of CurrentVersion - pub current_build: u8, - /// The minor number of CurrentVersion - pub current_minor: u8, - /// The major number of CurrentVersion - pub current_major: u8, - _reserved_2: u8, - /// The build number of CommittedVersion - pub committed_build: u8, - /// The minor number of CommittedVersion - pub committed_minor: u8, - /// The major number of CommittedVersion - pub committed_major: u8, - _reserved_3: u8, + /// The Current FW Version + pub current: Version, + /// The Committed FW Version + pub committed: Version, /// The CurrentTcb at the time the guest was launched or imported. pub launch_tcb: TcbVersion, - #[serde(with = "BigArray")] - _reserved_4: [u8; 168], /// Signature of bytes 0 to 0x29F inclusive of this report. /// The format of the signature is found within Signature. pub signature: Signature, } -impl Default for AttestationReportV2 { - fn default() -> Self { - Self { - version: Default::default(), - guest_svn: Default::default(), - policy: Default::default(), - family_id: Default::default(), - image_id: Default::default(), - vmpl: Default::default(), - sig_algo: Default::default(), - current_tcb: Default::default(), - plat_info: Default::default(), - key_info: Default::default(), - _reserved_0: Default::default(), - report_data: [0; 64], - measurement: [0; 48], - host_data: Default::default(), - id_key_digest: [0; 48], - author_key_digest: [0; 48], - report_id: Default::default(), - report_id_ma: Default::default(), - reported_tcb: Default::default(), - _reserved_1: Default::default(), - chip_id: [0; 64], - committed_tcb: Default::default(), - current_build: Default::default(), - current_minor: Default::default(), - current_major: Default::default(), - _reserved_2: Default::default(), - committed_build: Default::default(), - committed_minor: Default::default(), - committed_major: Default::default(), - _reserved_3: Default::default(), - launch_tcb: Default::default(), - _reserved_4: [0; 168], - signature: Default::default(), +impl AttestationReport { + /// Attempt to build an attestation report form raw bytes. + pub fn from_bytes(mut bytes: &[u8]) -> Result { + let stepper = &mut bytes; + let version: u32 = stepper.parse_bytes::<_, 0>()?; + match version { + 2 => Ok(Self::V2(AttestationReportV2 { + version, + guest_svn: stepper.parse_bytes::<_, 0>()?, + policy: stepper.parse_bytes::<_, 0>()?, + family_id: stepper.parse_bytes::<_, 0>()?, + image_id: stepper.parse_bytes::<_, 0>()?, + vmpl: stepper.parse_bytes::<_, 0>()?, + sig_algo: stepper.parse_bytes::<_, 0>()?, + current_tcb: stepper.parse_bytes::<_, 0>()?, + plat_info: stepper.parse_bytes::<_, 0>()?, + key_info: stepper.parse_bytes::<_, 0>()?, + report_data: stepper.parse_bytes::<_, 4>()?, + measurement: stepper.parse_bytes::<_, 0>()?, + host_data: stepper.parse_bytes::<_, 0>()?, + id_key_digest: stepper.parse_bytes::<_, 0>()?, + author_key_digest: stepper.parse_bytes::<_, 0>()?, + report_id: stepper.parse_bytes::<_, 0>()?, + report_id_ma: stepper.parse_bytes::<_, 0>()?, + reported_tcb: stepper.parse_bytes::<_, 0>()?, + chip_id: stepper.parse_bytes::<_, 24>()?, + committed_tcb: stepper.parse_bytes::<_, 0>()?, + current: stepper.parse_bytes::<_, 0>()?, + committed: stepper.parse_bytes::<_, 1>()?, + launch_tcb: stepper.parse_bytes::<_, 1>()?, + signature: stepper.parse_bytes::<_, 168>()?, + })), + 3 => Ok(Self::V3(AttestationReportV3 { + version, + guest_svn: stepper.parse_bytes::<_, 0>()?, + policy: stepper.parse_bytes::<_, 0>()?, + family_id: stepper.parse_bytes::<_, 0>()?, + image_id: stepper.parse_bytes::<_, 0>()?, + vmpl: stepper.parse_bytes::<_, 0>()?, + sig_algo: stepper.parse_bytes::<_, 0>()?, + current_tcb: stepper.parse_bytes::<_, 0>()?, + plat_info: stepper.parse_bytes::<_, 0>()?, + key_info: stepper.parse_bytes::<_, 0>()?, + report_data: stepper.parse_bytes::<_, 4>()?, + measurement: stepper.parse_bytes::<_, 0>()?, + host_data: stepper.parse_bytes::<_, 0>()?, + id_key_digest: stepper.parse_bytes::<_, 0>()?, + author_key_digest: stepper.parse_bytes::<_, 0>()?, + report_id: stepper.parse_bytes::<_, 0>()?, + report_id_ma: stepper.parse_bytes::<_, 0>()?, + reported_tcb: stepper.parse_bytes::<_, 0>()?, + cpuid_fam_id: stepper.parse_bytes::<_, 0>()?, + cpuid_mod_id: stepper.parse_bytes::<_, 0>()?, + cpuid_step: stepper.parse_bytes::<_, 0>()?, + chip_id: stepper.parse_bytes::<_, 21>()?, + committed_tcb: stepper.parse_bytes::<_, 0>()?, + current: stepper.parse_bytes::<_, 0>()?, + committed: stepper.parse_bytes::<_, 1>()?, + launch_tcb: stepper.parse_bytes::<_, 1>()?, + signature: stepper.parse_bytes::<_, 168>()?, + })), + _ => Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Invalid attestation report version", + )), + } + } + + /// Build a byte slice from an AttestationReport structure. + pub fn write_bytes(self, mut handle: impl Write) -> Result<(), std::io::Error> { + handle.write_bytes::<_, 0>(self.version())?; + handle.write_bytes::<_, 0>(self.guest_svn())?; + handle.write_bytes::<_, 0>(self.policy())?; + handle.write_bytes::<_, 0>(self.family_id())?; + handle.write_bytes::<_, 0>(self.image_id())?; + handle.write_bytes::<_, 0>(self.vmpl())?; + handle.write_bytes::<_, 0>(self.sig_algo())?; + handle.write_bytes::<_, 0>(self.current_tcb())?; + + match self { + Self::V2(report) => handle.write_bytes::<_, 0>(report.plat_info)?, + Self::V3(report) => handle.write_bytes::<_, 0>(report.plat_info)?, + } + + handle.write_bytes::<_, 0>(self.key_info())?; + handle.write_bytes::<_, 4>(self.report_data())?; + handle.write_bytes::<_, 0>(self.measurement())?; + handle.write_bytes::<_, 0>(self.host_data())?; + handle.write_bytes::<_, 0>(self.id_key_digest())?; + handle.write_bytes::<_, 0>(self.author_key_digest())?; + handle.write_bytes::<_, 0>(self.report_id())?; + handle.write_bytes::<_, 0>(self.report_id_ma())?; + handle.write_bytes::<_, 0>(self.reported_tcb())?; + + match self { + Self::V2(report) => handle.write_bytes::<_, 24>(report.chip_id)?, + Self::V3(report) => { + handle.write_bytes::<_, 0>(report.cpuid_fam_id)?; + handle.write_bytes::<_, 0>(report.cpuid_mod_id)?; + handle.write_bytes::<_, 0>(report.cpuid_step)?; + handle.write_bytes::<_, 21>(report.chip_id)?; + } } + + handle.write_bytes::<_, 0>(self.committed_tcb())?; + handle.write_bytes::<_, 0>(self.current())?; + handle.write_bytes::<_, 1>(self.committed())?; + handle.write_bytes::<_, 1>(self.launch_tcb())?; + handle.write_bytes::<_, 168>(self.signature())?; + Ok(()) + } +} + +impl Default for AttestationReport { + fn default() -> Self { + Self::V3(Default::default()) } } @@ -555,7 +630,7 @@ impl Display for AttestationReportV2 { write!( f, r#" -Attestation Report ({} bytes): +Attestation Report: Version: {} Guest SVN: {} {} @@ -578,72 +653,47 @@ Reported TCB: {} Chip ID: {} Committed TCB: {} -Current Build: {} -Current Minor: {} -Current Major: {} -Committed Build: {} -Committed Minor: {} -Committed Major: {} +Current Version: {} +Committed Version: {} Launch TCB: {} {} "#, - std::mem::size_of_val(self), self.version, self.guest_svn, self.policy, - hexdump(&self.family_id), - hexdump(&self.image_id), + self.family_id, + self.image_id, self.vmpl, self.sig_algo, self.current_tcb, self.plat_info, self.key_info, - hexdump(&self.report_data), - hexdump(&self.measurement), - hexdump(&self.host_data), - hexdump(&self.id_key_digest), - hexdump(&self.author_key_digest), - hexdump(&self.report_id), - hexdump(&self.report_id_ma), + self.report_data, + self.measurement, + self.host_data, + self.id_key_digest, + self.author_key_digest, + self.report_id, + self.report_id_ma, self.reported_tcb, - hexdump(&self.chip_id), + self.chip_id, self.committed_tcb, - self.current_build, - self.current_minor, - self.current_major, - self.committed_build, - self.committed_minor, - self.committed_major, + self.current, + self.committed, self.launch_tcb, self.signature ) } } -impl sealed::Sealed for AttestationReportV2 {} - -impl Attestable for AttestationReportV2 { - fn signature(&self) -> &Signature { - &self.signature - } -} - -impl TryFrom<&[u8]> for AttestationReportV2 { - type Error = AttestationReportError; - - fn try_from(bytes: &[u8]) -> Result { - bincode::deserialize(bytes).map_err(|e| AttestationReportError::BincodeError(*e)) - } -} - /// Version 3 of the attestation report /// Systems that contain firmware starting from the spec release 1.56 will use this attestation report. /// This version adds: /// The CPUID Family, Model and Stepping fields /// The Alias_Check_Complete field in the PlatformInfo field #[repr(C)] -#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Default, Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord)] pub struct AttestationReportV3 { /// Version number of this attestation report. Set to 2h for this specification. pub version: u32, @@ -652,9 +702,9 @@ pub struct AttestationReportV3 { /// The guest policy. pub policy: GuestPolicy, /// The family ID provided at launch. - pub family_id: [u8; 16], + pub family_id: Array, /// The image ID provided at launch. - pub image_id: [u8; 16], + pub image_id: Array, /// The request VMPL for the attestation report. pub vmpl: u32, /// The signature algorithm used to sign this report. @@ -665,27 +715,26 @@ pub struct AttestationReportV3 { pub plat_info: PlatformInfoV2, /// Information related to signing keys in the report. See KeyInfo pub key_info: KeyInfo, - _reserved_0: u32, - #[serde(with = "BigArray")] + /// Guest-provided 512 Bits of Data - pub report_data: [u8; 64], - #[serde(with = "BigArray")] + pub report_data: Array, + /// The measurement calculated at launch. - pub measurement: [u8; 48], + pub measurement: Array, /// Data provided by the hypervisor at launch. - pub host_data: [u8; 32], - #[serde(with = "BigArray")] + pub host_data: Array, + /// SHA-384 digest of the ID public key that signed the ID block provided /// in SNP_LANUNCH_FINISH. - pub id_key_digest: [u8; 48], - #[serde(with = "BigArray")] + pub id_key_digest: Array, + /// SHA-384 digest of the Author public key that certified the ID key, /// if provided in SNP_LAUNCH_FINSIH. Zeroes if AUTHOR_KEY_EN is 1. - pub author_key_digest: [u8; 48], + pub author_key_digest: Array, /// Report ID of this guest. - pub report_id: [u8; 32], + pub report_id: Array, /// Report ID of this guest's migration agent (if applicable). - pub report_id_ma: [u8; 32], + pub report_id_ma: Array, /// Reported TCB version used to derive the VCEK that signed this report. pub reported_tcb: TcbVersion, /// CPUID Familiy ID (Combined Extended Family ID and Family ID) @@ -694,85 +743,29 @@ pub struct AttestationReportV3 { pub cpuid_mod_id: u8, /// CPUID Stepping pub cpuid_step: u8, - _reserved_1: [u8; 21], - #[serde(with = "BigArray")] + /// If MaskChipId is set to 0, Identifier unique to the chip. /// Otherwise set to 0h. - pub chip_id: [u8; 64], + pub chip_id: Array, /// CommittedTCB pub committed_tcb: TcbVersion, /// The build number of CurrentVersion - pub current_build: u8, - /// The minor number of CurrentVersion - pub current_minor: u8, - /// The major number of CurrentVersion - pub current_major: u8, - _reserved_2: u8, + pub current: Version, /// The build number of CommittedVersion - pub committed_build: u8, - /// The minor number of CommittedVersion - pub committed_minor: u8, - /// The major number of CommittedVersion - pub committed_major: u8, - _reserved_3: u8, + pub committed: Version, /// The CurrentTcb at the time the guest was launched or imported. pub launch_tcb: TcbVersion, - #[serde(with = "BigArray")] - _reserved_4: [u8; 168], /// Signature of bytes 0 to 0x29F inclusive of this report. /// The format of the signature is found within Signature. pub signature: Signature, } -impl Default for AttestationReportV3 { - fn default() -> Self { - Self { - version: Default::default(), - guest_svn: Default::default(), - policy: Default::default(), - family_id: Default::default(), - image_id: Default::default(), - vmpl: Default::default(), - sig_algo: Default::default(), - current_tcb: Default::default(), - plat_info: Default::default(), - key_info: Default::default(), - _reserved_0: Default::default(), - report_data: [0; 64], - measurement: [0; 48], - host_data: Default::default(), - id_key_digest: [0; 48], - author_key_digest: [0; 48], - report_id: Default::default(), - report_id_ma: Default::default(), - reported_tcb: Default::default(), - cpuid_fam_id: Default::default(), - cpuid_mod_id: Default::default(), - cpuid_step: Default::default(), - _reserved_1: Default::default(), - chip_id: [0; 64], - committed_tcb: Default::default(), - current_build: Default::default(), - current_minor: Default::default(), - current_major: Default::default(), - _reserved_2: Default::default(), - committed_build: Default::default(), - committed_minor: Default::default(), - committed_major: Default::default(), - _reserved_3: Default::default(), - launch_tcb: Default::default(), - _reserved_4: [0; 168], - signature: Default::default(), - } - } -} - impl Display for AttestationReportV3 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, r#" -Attestation Report ({} bytes): +Attestation Report: Version: {} Guest SVN: {} {} @@ -798,83 +791,78 @@ CPUID Stepping: {} Chip ID: {} Committed TCB: {} -Current Build: {} -Current Minor: {} -Current Major: {} -Committed Build: {} -Committed Minor: {} -Committed Major: {} +Current Version: {} +Committed Version: {} Launch TCB: {} {} "#, - std::mem::size_of_val(self), self.version, self.guest_svn, self.policy, - hexdump(&self.family_id), - hexdump(&self.image_id), + self.family_id, + self.image_id, self.vmpl, self.sig_algo, self.current_tcb, self.plat_info, self.key_info, - hexdump(&self.report_data), - hexdump(&self.measurement), - hexdump(&self.host_data), - hexdump(&self.id_key_digest), - hexdump(&self.author_key_digest), - hexdump(&self.report_id), - hexdump(&self.report_id_ma), + self.report_data, + self.measurement, + self.host_data, + self.id_key_digest, + self.author_key_digest, + self.report_id, + self.report_id_ma, self.reported_tcb, self.cpuid_fam_id, self.cpuid_mod_id, self.cpuid_step, - hexdump(&self.chip_id), + self.chip_id, self.committed_tcb, - self.current_build, - self.current_minor, - self.current_major, - self.committed_build, - self.committed_minor, - self.committed_major, + self.current, + self.committed, self.launch_tcb, self.signature ) } } -impl sealed::Sealed for AttestationReportV3 {} +#[cfg(any(feature = "openssl", feature = "crypto_nossl"))] +impl Verifiable for (&Chain, &AttestationReportV2) { + type Output = (); -impl Attestable for AttestationReportV3 { - fn signature(&self) -> &Signature { - &self.signature + fn verify(self) -> io::Result { + let report: AttestationReport = self.1.into(); + (self.0, &report).verify() } } +#[cfg(any(feature = "openssl", feature = "crypto_nossl"))] +impl Verifiable for (&Chain, &AttestationReportV3) { + type Output = (); -impl TryFrom<&[u8]> for AttestationReportV3 { - type Error = AttestationReportError; - - fn try_from(bytes: &[u8]) -> Result { - bincode::deserialize(bytes).map_err(|e| AttestationReportError::BincodeError(*e)) + fn verify(self) -> io::Result { + let report: AttestationReport = self.1.into(); + (self.0, &report).verify() } } #[cfg(feature = "openssl")] -impl Verifiable for (&Chain, &T) -where - T: Attestable, -{ +impl Verifiable for (&Chain, &AttestationReport) { type Output = (); fn verify(self) -> io::Result { let vek = self.0.verify()?; - let sig = EcdsaSig::try_from(self.1.signature())?; - let measurable_bytes = self.1.measurable_bytes()?; + let sig = EcdsaSig::try_from(&self.1.signature())?; + + let mut raw_report_bytes: Vec = Vec::with_capacity(1184usize); + self.1.write_bytes(&mut raw_report_bytes)?; + + let measurable_bytes: &[u8] = &raw_report_bytes[..0x2a0]; let mut hasher = Sha384::new(); - hasher.update(&measurable_bytes); + hasher.update(measurable_bytes); let base_digest = hasher.finish(); let ec = vek.public_key()?.ec_key()?; @@ -890,20 +878,20 @@ where } #[cfg(feature = "openssl")] -impl Verifiable for (&Certificate, &T) -where - T: Attestable, -{ +impl Verifiable for (&Certificate, &AttestationReport) { type Output = (); fn verify(self) -> io::Result { let vek = self.0; - let sig = EcdsaSig::try_from(self.1.signature())?; - let measurable_bytes = self.1.measurable_bytes()?; + let sig = EcdsaSig::try_from(&self.1.signature())?; + let mut raw_report_bytes: Vec = Vec::with_capacity(1184usize); + self.1.write_bytes(&mut raw_report_bytes).unwrap(); + + let measurable_bytes: &[u8] = &raw_report_bytes[..0x2a0]; let mut hasher = Sha384::new(); - hasher.update(&measurable_bytes); + hasher.update(measurable_bytes); let base_digest = hasher.finish(); let ec = vek.public_key()?.ec_key()?; @@ -919,10 +907,7 @@ where } #[cfg(feature = "crypto_nossl")] -impl Verifiable for (&Chain, &T) -where - T: Attestable, -{ +impl Verifiable for (&Chain, &AttestationReport) { type Output = (); fn verify(self) -> io::Result { @@ -932,9 +917,12 @@ where let vek = self.0.verify()?; - let sig = p384::ecdsa::Signature::try_from(self.1.signature())?; + let sig = p384::ecdsa::Signature::try_from(&self.1.signature())?; - let measurable_bytes = self.1.measurable_bytes()?; + let mut raw_report_bytes: Vec = Vec::with_capacity(1184usize); + self.1.write_bytes(&mut raw_report_bytes).unwrap(); + + let measurable_bytes: &[u8] = &raw_report_bytes[..0x2a0]; use sha2::Digest; let base_digest = sha2::Sha384::new_with_prefix(measurable_bytes); @@ -956,10 +944,7 @@ where } #[cfg(feature = "crypto_nossl")] -impl Verifiable for (&Certificate, &T) -where - T: Attestable, -{ +impl Verifiable for (&Certificate, &AttestationReport) { type Output = (); fn verify(self) -> io::Result { @@ -970,9 +955,12 @@ where let vek = self.0; - let sig = p384::ecdsa::Signature::try_from(self.1.signature())?; + let sig = p384::ecdsa::Signature::try_from(&self.1.signature())?; + + let mut raw_report_bytes: Vec = Vec::with_capacity(1184usize); + self.1.write_bytes(&mut raw_report_bytes).unwrap(); - let measurable_bytes = self.1.measurable_bytes()?; + let measurable_bytes: &[u8] = &raw_report_bytes[..0x2a0]; use sha2::Digest; let base_digest = sha2::Sha384::new_with_prefix(measurable_bytes); @@ -1018,7 +1006,7 @@ bitfield! { /// | 63:25 | - | Reserved. MBZ. > /// #[repr(C)] - #[derive(Default, Deserialize, Clone, Copy, Eq, PartialEq, Serialize, PartialOrd, Ord)] + #[derive(Deserialize, Clone, Copy, Eq, PartialEq, Serialize, PartialOrd, Ord)] pub struct GuestPolicy(u64); impl Debug; /// ABI_MINOR field: Indicates the minor API version. @@ -1044,6 +1032,28 @@ bitfield! { pub ciphertext_hiding, set_ciphertext_hiding: 24, 24; } +impl Default for GuestPolicy { + fn default() -> Self { + Self(ByteParser::default()) + } +} + +impl ByteParser for GuestPolicy { + type Bytes = [u8; 8]; + + fn from_bytes(bytes: Self::Bytes) -> Self { + Self(u64::from_le_bytes(bytes)) + } + + fn to_bytes(&self) -> Self::Bytes { + self.0.to_le_bytes() + } + + fn default() -> Self { + Self(Default::default()) + } +} + impl Display for GuestPolicy { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -1135,6 +1145,15 @@ impl PlatformInfo { } } +impl From for GuestPolicy { + fn from(value: u64) -> Self { + // Bit 17 of the guest policy is reserved and must always be set to 1. + let reserved: u64 = 1 << 17; + + GuestPolicy(value | reserved) + } +} + bitfield! { /// Version 1 PlatformInfo bitfield /// A structure with a bit-field unsigned 64 bit integer: @@ -1145,7 +1164,7 @@ bitfield! { /// Bit 4 indicates if ciphertext hiding is enabled /// Bits 5-63 are reserved. #[repr(C)] - #[derive(Default, Deserialize, Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord)] + #[derive(Deserialize, Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord)] pub struct PlatformInfoV1(u64); impl Debug; /// Returns the bit state of SMT @@ -1162,6 +1181,40 @@ bitfield! { reserved, _: 63, 5; } +impl Default for PlatformInfoV1 { + fn default() -> Self { + Self(ByteParser::default()) + } +} + +impl From for PlatformInfoV1 { + fn from(value: u64) -> Self { + PlatformInfoV1(value) + } +} + +impl From for u64 { + fn from(value: PlatformInfoV1) -> Self { + value.0 + } +} + +impl ByteParser for PlatformInfoV1 { + type Bytes = [u8; 8]; + + fn from_bytes(bytes: Self::Bytes) -> Self { + Self(u64::from_le_bytes(bytes)) + } + + fn to_bytes(&self) -> Self::Bytes { + self.0.to_le_bytes() + } + + fn default() -> Self { + Self(Default::default()) + } +} + impl Display for PlatformInfoV1 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -1195,7 +1248,7 @@ bitfield! { /// Bit 5 indicates that alias detection has completed since the last system reset and there are no aliasing addresses. Resets to 0. /// Bits 5-63 are reserved. #[repr(C)] - #[derive(Default, Deserialize, Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord)] + #[derive(Deserialize, Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord)] pub struct PlatformInfoV2(u64); impl Debug; /// Returns the bit state of SMT @@ -1238,6 +1291,40 @@ Platform Info ({}): } } +impl Default for PlatformInfoV2 { + fn default() -> Self { + Self(ByteParser::default()) + } +} + +impl From for PlatformInfoV2 { + fn from(value: u64) -> Self { + PlatformInfoV2(value) + } +} + +impl From for u64 { + fn from(value: PlatformInfoV2) -> Self { + value.0 + } +} + +impl ByteParser for PlatformInfoV2 { + type Bytes = [u8; 8]; + + fn from_bytes(bytes: Self::Bytes) -> Self { + Self(u64::from_le_bytes(bytes)) + } + + fn to_bytes(&self) -> Self::Bytes { + self.0.to_le_bytes() + } + + fn default() -> Self { + Self(Default::default()) + } +} + bitfield! { /// When an attestation report is requested, the user can request to have the report to not be signed, or sign with different keys. The user may also /// pass in the author key when launching the guest. This field provides that information and will be present in the attestation report. @@ -1249,7 +1336,7 @@ bitfield! { /// | 4:2 | SIGNING_KEY | Encodes the key used to sign this report. > /// | 5:31 | - | Reserved. Must be zero. > #[repr(C)] - #[derive(Default, Deserialize, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize)] + #[derive(Deserialize, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Serialize)] pub struct KeyInfo(u32); impl Debug; /// AUTHOR_KEY_EN field: Indicates that the digest of the author key is present in AUTHOR_KEY_DIGEST @@ -1268,6 +1355,40 @@ bitfield! { reserved, _: 31, 5; } +impl Default for KeyInfo { + fn default() -> Self { + Self(ByteParser::default()) + } +} + +impl ByteParser for KeyInfo { + type Bytes = [u8; 4]; + + fn from_bytes(bytes: Self::Bytes) -> Self { + Self(u32::from_le_bytes(bytes)) + } + + fn to_bytes(&self) -> Self::Bytes { + self.0.to_le_bytes() + } + + fn default() -> Self { + Self(Default::default()) + } +} + +impl From for KeyInfo { + fn from(value: u32) -> Self { + KeyInfo(value) + } +} + +impl From for u32 { + fn from(value: KeyInfo) -> Self { + value.0 + } +} + impl Display for KeyInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let signing_key = match self.signing_key() { @@ -1296,6 +1417,10 @@ Key Information: mod tests { use super::*; + use std::{ + convert::TryInto, + io::{ErrorKind, Write}, + }; #[test] fn test_derive_key_new() { @@ -1356,180 +1481,10 @@ mod tests { assert_eq!(actual.get_tcb_version(), 0); } - #[test] - fn test_attestation_report_v2() { - let expected: AttestationReportV2 = AttestationReportV2 { - version: 0, - guest_svn: 0, - policy: GuestPolicy(0), - family_id: [0; 16], - image_id: [0; 16], - vmpl: 0, - sig_algo: 0, - current_tcb: TcbVersion::default(), - plat_info: PlatformInfoV1::default(), - key_info: KeyInfo::default(), - _reserved_0: 0, - report_data: [0; 64], - measurement: [0; 48], - host_data: [0; 32], - id_key_digest: [0; 48], - author_key_digest: [0; 48], - report_id: [0; 32], - report_id_ma: [0; 32], - reported_tcb: TcbVersion::default(), - _reserved_1: [0; 24], - chip_id: [0; 64], - committed_tcb: TcbVersion::default(), - current_build: 0, - current_minor: 0, - current_major: 0, - _reserved_2: 0, - committed_build: 0, - committed_minor: 0, - committed_major: 0, - _reserved_3: 0, - launch_tcb: TcbVersion::default(), - _reserved_4: [0; 168], - signature: Signature::default(), - }; - - assert_eq!(AttestationReportV2::default(), expected); - } - - #[test] - fn test_attestation_report_v3() { - let expected: AttestationReportV3 = AttestationReportV3 { - version: 0, - guest_svn: 0, - policy: GuestPolicy(0), - family_id: [0; 16], - image_id: [0; 16], - vmpl: 0, - sig_algo: 0, - current_tcb: TcbVersion::default(), - plat_info: PlatformInfoV2::default(), - key_info: KeyInfo::default(), - _reserved_0: 0, - report_data: [0; 64], - measurement: [0; 48], - host_data: [0; 32], - id_key_digest: [0; 48], - author_key_digest: [0; 48], - report_id: [0; 32], - report_id_ma: [0; 32], - reported_tcb: TcbVersion::default(), - cpuid_fam_id: 0, - cpuid_mod_id: 0, - cpuid_step: 0, - _reserved_1: [0; 21], - chip_id: [0; 64], - committed_tcb: TcbVersion::default(), - current_build: 0, - current_minor: 0, - current_major: 0, - _reserved_2: 0, - committed_build: 0, - committed_minor: 0, - committed_major: 0, - _reserved_3: 0, - launch_tcb: TcbVersion::default(), - _reserved_4: [0; 168], - signature: Signature::default(), - }; - - assert_eq!(AttestationReportV3::default(), expected); - } - - #[test] - fn test_attestation_report_v2_default() { - let expected: AttestationReportV2 = AttestationReportV2 { - version: Default::default(), - guest_svn: Default::default(), - policy: GuestPolicy::default(), - family_id: Default::default(), - image_id: Default::default(), - vmpl: Default::default(), - sig_algo: Default::default(), - current_tcb: TcbVersion::default(), - plat_info: PlatformInfoV1::default(), - key_info: KeyInfo::default(), - _reserved_0: Default::default(), - report_data: [0; 64], - measurement: [0; 48], - host_data: Default::default(), - id_key_digest: [0; 48], - author_key_digest: [0; 48], - report_id: Default::default(), - report_id_ma: Default::default(), - reported_tcb: TcbVersion::default(), - _reserved_1: Default::default(), - chip_id: [0; 64], - committed_tcb: TcbVersion::default(), - current_build: Default::default(), - current_minor: Default::default(), - current_major: Default::default(), - _reserved_2: Default::default(), - committed_build: Default::default(), - committed_minor: Default::default(), - committed_major: Default::default(), - _reserved_3: Default::default(), - launch_tcb: TcbVersion::default(), - _reserved_4: [0; 168], - signature: Signature::default(), - }; - - assert_eq!(AttestationReportV2::default(), expected); - } - - #[test] - fn test_attestation_report_v3_default() { - let expected: AttestationReportV3 = AttestationReportV3 { - version: Default::default(), - guest_svn: Default::default(), - policy: GuestPolicy::default(), - family_id: Default::default(), - image_id: Default::default(), - vmpl: Default::default(), - sig_algo: Default::default(), - current_tcb: TcbVersion::default(), - plat_info: PlatformInfoV2::default(), - key_info: KeyInfo::default(), - _reserved_0: Default::default(), - report_data: [0; 64], - measurement: [0; 48], - host_data: Default::default(), - id_key_digest: [0; 48], - author_key_digest: [0; 48], - report_id: Default::default(), - report_id_ma: Default::default(), - reported_tcb: TcbVersion::default(), - cpuid_fam_id: Default::default(), - cpuid_mod_id: Default::default(), - cpuid_step: Default::default(), - _reserved_1: Default::default(), - chip_id: [0; 64], - committed_tcb: TcbVersion::default(), - current_build: Default::default(), - current_minor: Default::default(), - current_major: Default::default(), - _reserved_2: Default::default(), - committed_build: Default::default(), - committed_minor: Default::default(), - committed_major: Default::default(), - _reserved_3: Default::default(), - launch_tcb: TcbVersion::default(), - _reserved_4: [0; 168], - signature: Signature::default(), - }; - - assert_eq!(AttestationReportV3::default(), expected); - } - #[test] fn test_attestation_report_v2_fmt() { let expected: &str = r#" -Attestation Report (1184 bytes): +Attestation Report: Version: 0 Guest SVN: 0 @@ -1624,12 +1579,8 @@ TCB Version: TEE: 0 Boot Loader: 0 -Current Build: 0 -Current Minor: 0 -Current Major: 0 -Committed Build: 0 -Committed Minor: 0 -Committed Major: 0 +Current Version: 0.0.0 +Committed Version: 0.0.0 Launch TCB: TCB Version: @@ -1663,7 +1614,7 @@ Signature: #[test] fn test_attestation_report_v3_fmt() { let expected: &str = r#" -Attestation Report (1184 bytes): +Attestation Report: Version: 0 Guest SVN: 0 @@ -1762,12 +1713,8 @@ TCB Version: TEE: 0 Boot Loader: 0 -Current Build: 0 -Current Minor: 0 -Current Major: 0 -Committed Build: 0 -Committed Minor: 0 -Committed Major: 0 +Current Version: 0.0.0 +Committed Version: 0.0.0 Launch TCB: TCB Version: @@ -1849,7 +1796,7 @@ Signature: #[test] fn test_set_guest_policy_max() { - let mut gp: GuestPolicy = GuestPolicy::default(); + let mut gp: GuestPolicy = Default::default(); assert_eq!(gp.abi_minor(), 0); gp.set_abi_minor(1); @@ -2084,7 +2031,7 @@ Key Information: #[test] fn test_guest_policy_serialization() { - let mut original = GuestPolicy::default(); + let mut original: GuestPolicy = Default::default(); original.set_abi_major(2); original.set_abi_minor(1); original.set_smt_allowed(1); @@ -2102,8 +2049,8 @@ Key Information: version: 2, guest_svn: 1, policy: GuestPolicy(3), - family_id: [1; 16], - image_id: [2; 16], + family_id: [1; 16].try_into().unwrap(), + image_id: [2; 16].try_into().unwrap(), ..Default::default() }; @@ -2119,8 +2066,8 @@ Key Information: version: 2, guest_svn: 1, policy: GuestPolicy(3), - family_id: [1; 16], - image_id: [2; 16], + family_id: [1; 16].try_into().unwrap(), + image_id: [2; 16].try_into().unwrap(), ..Default::default() }; @@ -2130,113 +2077,6 @@ Key Information: assert_eq!(original, from_binary); } - #[test] - fn test_attestation_report_enum_v2_serialization() { - let report_v2 = AttestationReportV2 { - version: 2, - guest_svn: 1, - policy: GuestPolicy::default(), - family_id: Default::default(), - image_id: Default::default(), - vmpl: Default::default(), - sig_algo: Default::default(), - current_tcb: TcbVersion::default(), - plat_info: PlatformInfoV1::default(), - key_info: KeyInfo::default(), - _reserved_0: Default::default(), - report_data: [0; 64], - measurement: [0; 48], - host_data: Default::default(), - id_key_digest: [0; 48], - author_key_digest: [0; 48], - report_id: Default::default(), - report_id_ma: Default::default(), - reported_tcb: TcbVersion::default(), - _reserved_1: Default::default(), - chip_id: [0; 64], - committed_tcb: TcbVersion::default(), - current_build: Default::default(), - current_minor: Default::default(), - current_major: Default::default(), - _reserved_2: Default::default(), - committed_build: Default::default(), - committed_minor: Default::default(), - committed_major: Default::default(), - _reserved_3: Default::default(), - launch_tcb: TcbVersion::default(), - _reserved_4: [0; 168], - signature: Signature::default(), - }; - - let attestation_report = AttestationReport::V2(report_v2); - - let serialized = attestation_report.to_bytes().expect("Serialization failed"); - - assert_eq!(serialized, attestation_report.to_bytes().unwrap()); - - let deserialized = - AttestationReport::from_bytes(&serialized).expect("Deserialization failed"); - - assert_eq!(attestation_report.version(), deserialized.version()); - assert_eq!(attestation_report.guest_svn(), deserialized.guest_svn()); - assert_eq!(attestation_report.policy(), deserialized.policy()); - } - - #[test] - fn test_attestation_report_enum_v3_serialization() { - let report_v3 = AttestationReportV3 { - version: 2, - guest_svn: 1, - policy: GuestPolicy::default(), - family_id: Default::default(), - image_id: Default::default(), - vmpl: Default::default(), - sig_algo: Default::default(), - current_tcb: TcbVersion::default(), - plat_info: PlatformInfoV2::default(), - key_info: KeyInfo::default(), - _reserved_0: Default::default(), - report_data: [0; 64], - measurement: [0; 48], - host_data: Default::default(), - id_key_digest: [0; 48], - author_key_digest: [0; 48], - report_id: Default::default(), - report_id_ma: Default::default(), - reported_tcb: TcbVersion::default(), - cpuid_fam_id: Default::default(), - cpuid_mod_id: Default::default(), - cpuid_step: Default::default(), - _reserved_1: Default::default(), - chip_id: [0; 64], - committed_tcb: TcbVersion::default(), - current_build: Default::default(), - current_minor: Default::default(), - current_major: Default::default(), - _reserved_2: Default::default(), - committed_build: Default::default(), - committed_minor: Default::default(), - committed_major: Default::default(), - _reserved_3: Default::default(), - launch_tcb: TcbVersion::default(), - _reserved_4: [0; 168], - signature: Signature::default(), - }; - - let attestation_report = AttestationReport::V3(report_v3); - - let serialized = attestation_report.to_bytes().expect("Serialization failed"); - - assert_eq!(serialized, attestation_report.to_bytes().unwrap()); - - let deserialized = - AttestationReport::from_bytes(&serialized).expect("Deserialization failed"); - - assert_eq!(attestation_report.version(), deserialized.version()); - assert_eq!(attestation_report.guest_svn(), deserialized.guest_svn()); - assert_eq!(attestation_report.policy(), deserialized.policy()); - } - #[test] fn test_invalid_version_deserialization() { let invalid_bytes = [5, 0, 0, 0, 0, 1, 2, 3]; // Version 5 is unsupported @@ -2331,7 +2171,7 @@ Key Information: assert_eq!(report.version, 2); assert_eq!(report.guest_svn, 1); assert_eq!(report.vmpl, 3); - assert_eq!(report.measurement, [0; 48]); + assert_eq!(report.measurement, [0; 48].try_into().unwrap()); } #[test] @@ -2342,7 +2182,7 @@ Key Information: #[test] fn test_guest_policy_combined_fields() { - let mut policy = GuestPolicy::default(); + let mut policy: GuestPolicy = Default::default(); policy.set_abi_major(2); policy.set_abi_minor(1); @@ -2357,4 +2197,295 @@ Key Information: let policy_u64: u64 = policy.into(); assert_eq!(policy_u64 & (1 << 17), 1 << 17); // Reserved bit 17 must be 1 } + + #[test] + fn test_version_display() { + let version = Version::new(3, 2, 1); + assert_eq!(version.to_string(), "3.2.1"); + + let max_version = Version::new(255, 255, 255); + assert_eq!(max_version.to_string(), "255.255.255"); + + let min_version = Version::new(0, 0, 0); + assert_eq!(min_version.to_string(), "0.0.0"); + } + + #[test] + fn test_version_byte_parser() { + // Test from_bytes + let bytes = [1, 2, 3]; + let version = Version::from_bytes(bytes); + assert_eq!(version, Version::new(3, 2, 1)); + + // Test to_bytes + let version = Version::new(4, 5, 6); + let bytes = version.to_bytes(); + assert_eq!(bytes, [6, 5, 4]); + + // Test roundtrip + let original = Version::new(7, 8, 9); + let bytes = original.to_bytes(); + let roundtrip = Version::from_bytes(bytes); + assert_eq!(original, roundtrip); + + // Test default + assert_eq!(::default(), Version::new(0, 0, 0)); + } + + #[test] + fn test_attestation_report_from_bytes() { + // Create a valid attestation report bytes minus one byte. + let mut bytes: Vec = vec![0; 1183]; + + // Push the version byte at the beginning. + bytes.insert(0, 2); + + // Test valid input + let result = AttestationReport::from_bytes(&bytes); + assert!(result.is_ok()); + + // Test invalid input (too short) + let result = AttestationReport::from_bytes(&bytes[..100]); + assert!(result.is_err()); + } + + #[test] + fn test_attestation_report_write_bytes() { + let report = AttestationReport::default(); + let mut buffer = Vec::new(); + + // Test successful write + let result = report.write_bytes(&mut buffer); + assert!(result.is_ok()); + + // Test writing to a failing writer + struct FailingWriter; + impl Write for FailingWriter { + fn write(&mut self, _buf: &[u8]) -> std::io::Result { + Err(std::io::Error::new(ErrorKind::Other, "test error")) + } + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } + } + + let mut writer = FailingWriter; + + let result = report.write_bytes(FailingWriter); + assert!(result.is_err()); + assert!(writer.flush().is_ok()); + } + + #[test] + fn test_version_edge_cases() { + // Test max values + let version = Version::new(255, 255, 255); + let bytes = version.to_bytes(); + assert_eq!(bytes, [255, 255, 255]); + + // Test mixed values + let version = Version::new(0, 255, 0); + let bytes = version.to_bytes(); + assert_eq!(bytes, [0, 255, 0]); + } + + #[test] + fn test_attestation_report_from_bytes_errors() { + let empty_bytes = [0; 1184]; + // Test with empty input + let result = AttestationReport::from_bytes(&empty_bytes); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidData); + + // Test with partial input + let partial_bytes = vec![0u8; 100]; + let result = AttestationReport::from_bytes(&partial_bytes); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidData); + } + + #[test] + fn test_attestation_report_write_bytes_validation() { + let report: AttestationReport = AttestationReport::V2(AttestationReportV2 { + version: 2, + ..Default::default() + }); + let mut buffer = Vec::new(); + + // Write report to buffer + report.write_bytes(&mut buffer).unwrap(); + + // Verify the written data can be read back + let read_back = AttestationReport::from_bytes(&buffer); + assert!(read_back.is_ok()); + assert_eq!(read_back.unwrap(), report); + } + + #[test] + fn test_version_ordering() { + let v1 = Version::new(1, 0, 0); + let v2 = Version::new(1, 0, 1); + let v3 = Version::new(1, 1, 0); + + assert!(v1 < v2); + assert!(v2 < v3); + assert!(v1 < v3); + + // Test equality + assert_eq!(Version::new(1, 2, 3), Version::new(1, 2, 3)); + assert_ne!(Version::new(1, 2, 3), Version::new(1, 2, 4)); + } + + #[test] + fn test_version_copy() { + let original = Version::new(1, 2, 3); + let cloned = original; + + assert_eq!(original, cloned); + assert_eq!(original.to_bytes(), cloned.to_bytes()); + } + + #[test] + fn test_attestation_report_complex_write() { + let report = AttestationReportV2 { + version: 2, + guest_svn: 1, + policy: GuestPolicy::from(0xFF), + family_id: [0xAA; 16].try_into().unwrap(), + image_id: [0xBB; 16].try_into().unwrap(), + ..Default::default() + }; + let report = AttestationReport::V2(report); + + let mut buffer = Vec::new(); + assert!(report.write_bytes(&mut buffer).is_ok()); + + // Read back and verify + let read_back = AttestationReport::from_bytes(&buffer).unwrap(); + assert_eq!(read_back.version(), 2); + assert_eq!(read_back.guest_svn(), 1); + assert_eq!(read_back.family_id(), [0xAA; 16]); + assert_eq!(read_back.image_id(), [0xBB; 16]); + } + + #[test] + fn test_write_with_limited_writer() { + let report = AttestationReport::default(); + + // Writer that can only write small chunks + struct LimitedWriter { + data: Vec, + max_write: usize, + } + + impl Write for LimitedWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + let write_size = std::cmp::min(self.max_write, buf.len()); + self.data.extend_from_slice(&buf[..write_size]); + Ok(write_size) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } + } + + let mut writer = LimitedWriter { + data: Vec::new(), + max_write: 16, // Only write 16 bytes at a time + }; + + assert!(report.write_bytes(&mut writer).is_ok()); + assert!(writer.flush().is_ok()); + } + + #[test] + fn test_platform_v1_info_from_u64() { + let value: u64 = 0xFFFF; + let platform_info = PlatformInfoV1::from(value); + assert_eq!(platform_info.0, value); + + let value: u64 = 0; + let platform_info = PlatformInfoV1::from(value); + assert_eq!(platform_info.0, value); + + let value: u64 = u64::MAX; + let platform_info = PlatformInfoV1::from(value); + assert_eq!(platform_info.0, value); + } + + #[test] + fn test_platform_v2_info_from_u64() { + let value: u64 = 0xFFFF; + let platform_info = PlatformInfoV2::from(value); + assert_eq!(platform_info.0, value); + + let value: u64 = 0; + let platform_info = PlatformInfoV2::from(value); + assert_eq!(platform_info.0, value); + + let value: u64 = u64::MAX; + let platform_info = PlatformInfoV2::from(value); + assert_eq!(platform_info.0, value); + } + + #[test] + fn test_platform_v1_info_into_u64() { + let platform_info = PlatformInfoV1(0xFFFF); + let value: u64 = platform_info.into(); + assert_eq!(value, 0xFFFF); + + let platform_info = PlatformInfoV1(0); + let value: u64 = platform_info.into(); + assert_eq!(value, 0); + + let platform_info = PlatformInfoV1(u64::MAX); + let value: u64 = platform_info.into(); + assert_eq!(value, u64::MAX); + } + + #[test] + fn test_platform_v2_info_into_u64() { + let platform_info = PlatformInfoV2(0xFFFF); + let value: u64 = platform_info.into(); + assert_eq!(value, 0xFFFF); + + let platform_info = PlatformInfoV2(0); + let value: u64 = platform_info.into(); + assert_eq!(value, 0); + + let platform_info = PlatformInfoV2(u64::MAX); + let value: u64 = platform_info.into(); + assert_eq!(value, u64::MAX); + } + + #[test] + fn test_key_info_from_u32() { + let value: u32 = 0xFFFF; + let key_info = KeyInfo::from(value); + assert_eq!(key_info.0, value); + + let value: u32 = 0; + let key_info = KeyInfo::from(value); + assert_eq!(key_info.0, value); + + let value: u32 = u32::MAX; + let key_info = KeyInfo::from(value); + assert_eq!(key_info.0, value); + } + + #[test] + fn test_key_info_into_u32() { + let key_info = KeyInfo(0xFFFF); + let value: u32 = key_info.into(); + assert_eq!(value, 0xFFFF); + + let key_info = KeyInfo(0); + let value: u32 = key_info.into(); + assert_eq!(value, 0); + + let key_info = KeyInfo(u32::MAX); + let value: u32 = key_info.into(); + assert_eq!(value, u32::MAX); + } } diff --git a/src/firmware/host/mod.rs b/src/firmware/host/mod.rs index fdee3ff2..371eecd4 100644 --- a/src/firmware/host/mod.rs +++ b/src/firmware/host/mod.rs @@ -18,10 +18,7 @@ use crate::error::*; #[cfg(feature = "sev")] #[cfg(target_os = "linux")] -use crate::{ - certs::sev::sev::{Certificate, Chain}, - Build as CertBuild, Version as CertVersion, -}; +use crate::certs::sev::sev::{Certificate, Chain}; #[cfg(target_os = "linux")] use std::{ @@ -94,8 +91,8 @@ impl Firmware { .map_err(|_| cmd_buf.encapsulate())?; Ok(Status { - build: CertBuild { - version: CertVersion { + build: crate::firmware::host::types::Build { + version: crate::firmware::host::types::Version { major: info.version.major, minor: info.version.minor, }, diff --git a/src/firmware/host/types/sev.rs b/src/firmware/host/types/sev.rs index 084bc428..dba558a7 100644 --- a/src/firmware/host/types/sev.rs +++ b/src/firmware/host/types/sev.rs @@ -4,7 +4,7 @@ pub use crate::firmware::linux::host::types::PlatformStatusFlags; -use crate::{firmware::host::State, Build}; +use crate::firmware::host::State; #[cfg(feature = "openssl")] use std::convert::TryInto; @@ -18,10 +18,15 @@ use crate::certs::sev::{ #[cfg(feature = "openssl")] use openssl::{ec::EcKey, ecdsa::EcdsaSig, pkey::Public}; +use crate::util::{TypeLoad, TypeSave}; + use crate::certs::sev::sev::EcdsaSignature; use serde::{Deserialize, Serialize}; -use std::fmt::Debug; +use std::{ + fmt::Debug, + io::{Read, Write}, +}; const MNONCE_SIZE: usize = 128 / 8; const DIGEST_SIZE: usize = 256 / 8; @@ -29,6 +34,65 @@ const POLICY_SIZE: usize = 32 / 8; const POLICY_OFFSET: usize = MNONCE_SIZE + DIGEST_SIZE; const MEASURABLE_BYTES: usize = MNONCE_SIZE + DIGEST_SIZE + POLICY_SIZE; +/// Information about the SEV platform version. +#[repr(C)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Version { + /// The major version number. + pub major: u8, + + /// The minor version number. + pub minor: u8, +} + +impl std::fmt::Display for Version { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}.{}", self.major, self.minor) + } +} + +impl From for Version { + fn from(v: u16) -> Self { + Self { + major: ((v & 0xF0) >> 4) as u8, + minor: (v & 0x0F) as u8, + } + } +} + +/// A description of the SEV platform's build information. +#[repr(C)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Build { + /// The version information. + pub version: Version, + + /// The build number. + pub build: u8, +} + +impl std::fmt::Display for Build { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}.{}", self.version, self.build) + } +} + +impl codicon::Decoder<()> for Build { + type Error = std::io::Error; + + fn decode(mut reader: impl Read, _: ()) -> std::io::Result { + reader.load() + } +} + +impl codicon::Encoder<()> for Build { + type Error = std::io::Error; + + fn encode(&self, mut writer: impl Write, _: ()) -> std::io::Result<()> { + writer.save(self) + } +} + /// Information regarding the SEV platform's current status. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Status { @@ -65,8 +129,6 @@ pub struct LegacyAttestationReport { /// Reserved _reserved_0: u32, // 0x3C /// Signature of the report. - // #[serde(with = "BigArray")] - // pub signature: [u8; 144], // 0x40 - 0xCF pub signature: EcdsaSignature, } diff --git a/src/firmware/host/types/snp.rs b/src/firmware/host/types/snp.rs index 6e9131bd..68f74dd2 100644 --- a/src/firmware/host/types/snp.rs +++ b/src/firmware/host/types/snp.rs @@ -5,33 +5,38 @@ pub use crate::firmware::linux::host::types::RawData; pub(crate) use crate::firmware::linux::host as FFI; -use crate::Version; - #[cfg(target_os = "linux")] use crate::error::CertError; +use crate::firmware::parser::ByteParser; use std::{ convert::{TryFrom, TryInto}, fmt::{self, Display, Formatter}, + ops::BitOrAssign, }; use bitfield::bitfield; -use bitflags; - use serde::{Deserialize, Serialize}; use self::FFI::types::SnpSetConfig; -bitflags::bitflags! { +bitfield! { /// The platform's status flags. #[derive(Default)] - pub struct SnpPlatformStatusFlags: u32 { - /// If set, this platform is owned. Otherwise, it is self-owned. - const OWNED = 1 << 0; + pub struct SnpPlatformStatusFlags(u32); + impl Debug; + + /// If set, this platform is owned. Otherwise, it is self-owned. + pub is_owned, _: 0; - /// If set, encrypted state functionality is present. - const ENCRYPTED_STATE = 1 << 8; + /// If set, encrypted state functionality is present. + pub is_encrypted_state_present, _: 8; +} + +impl BitOrAssign for SnpPlatformStatusFlags { + fn bitor_assign(&mut self, rhs: Self) { + self.0 |= rhs.0; } } @@ -164,11 +169,10 @@ impl CertTableEntry { /// Generates a certificate from the str GUID and data provided. pub fn from_guid(guid: &uuid::Uuid, data: Vec) -> Result { - let cert_type: CertType = match guid.try_into() { - Ok(guid) => guid, - Err(error) => return Err(error), - }; - Ok(Self { cert_type, data }) + Ok(Self { + cert_type: guid.try_into()?, + data, + }) } /// Generates a certificate from the CertType and data provided. @@ -205,7 +209,7 @@ impl PartialOrd for CertTableEntry { } /// Information regarding the SEV-SNP platform's TCB version. -#[derive(Clone, Debug, PartialEq, Eq, Default)] +#[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct TcbStatus { /// Installed TCB version. pub platform_version: TcbVersion, @@ -214,32 +218,30 @@ pub struct TcbStatus { pub reported_version: TcbVersion, } -/// A description of the SEV-SNP platform's build information. -#[repr(C)] -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] -pub struct Build { - /// The version information. - pub version: Version, +bitfield! { + #[derive(Default)] + /// Various platform initialization configuration data. Byte 0x3 in SEV-SNP's + /// STRUCT_PLATFORM_STATUS. + pub struct PlatformInit(u8); + impl Debug; - /// The build ID. - pub build: u32, + /// Indicates if RMP is initialized. + pub is_rmp_init, _: 0; + + /// Indicates that alias detection has completed since the last system reset + /// and there are no aliasing addresses. Resets to 0. + /// Added in firmware version: + /// Milan family: 1.55.22 + /// Genoa family: 1.55.38 + pub alias_check_complete, _: 1; + + /// Indicates TIO is enabled. Present if SevTio feature bit is set. + pub is_tio_en, _: 3; } -bitflags::bitflags! { - /// Various platform initialization configuration data. Byte 0x3 in SEV-SNP's - /// STRUCT_PLATFORM_STATUS. - #[derive(Default)] - pub struct PlatformInit: u8 { - /// Indicates if RMP is initialized. - const IS_RMP_INIT = 1 << 0; - /// Indicates that alias detection has completed since the last system reset - /// and there are no aliasing addresses. Resets to 0. - /// Added in firmware version: - /// Milan family: 1.55.22 - /// Genoa family: 1.55.38 - const ALIAS_CHECK_COMPLETE = 1 << 1; - /// Indicates TIO is enabled. Present if SevTio feature bit is set. - const IS_TIO_EN = 1 << 3; +impl BitOrAssign for PlatformInit { + fn bitor_assign(&mut self, rhs: Self) { + self.0 |= rhs.0; } } @@ -250,7 +252,7 @@ bitflags::bitflags! { #[repr(C)] pub struct SnpPlatformStatus { /// The firmware API version (major.minor) - pub version: Version, + pub version: (u8, u8), /// The platform state. pub state: u8, @@ -340,7 +342,7 @@ impl TryFrom for Config { /// TcbVersion represents the version of the firmware. /// /// (Chapter 2.2; Table 3) -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[repr(C)] pub struct TcbVersion { /// Current bootloader version. @@ -357,6 +359,77 @@ pub struct TcbVersion { pub microcode: u8, } +impl Default for TcbVersion { + fn default() -> Self { + ByteParser::default() + } +} + +impl ByteParser for TcbVersion { + type Bytes = [u8; 8]; + + fn from_bytes(bytes: Self::Bytes) -> Self { + Self { + bootloader: bytes[0], + tee: bytes[1], + _reserved: [bytes[2], bytes[3], bytes[4], bytes[5]], + snp: bytes[6], + microcode: bytes[7], + } + } + + fn to_bytes(&self) -> Self::Bytes { + [ + self.bootloader, + self.tee, + self._reserved[0], + self._reserved[1], + self._reserved[2], + self._reserved[3], + self.snp, + self.microcode, + ] + } + + fn default() -> Self { + Self { + bootloader: Default::default(), + tee: Default::default(), + _reserved: Default::default(), + snp: Default::default(), + microcode: Default::default(), + } + } +} + +impl From for TcbVersion { + fn from(value: u64) -> Self { + let bytes = value.to_le_bytes(); + Self { + bootloader: bytes[0], + tee: bytes[1], + _reserved: [bytes[2], bytes[3], bytes[4], bytes[5]], + snp: bytes[6], + microcode: bytes[7], + } + } +} + +impl From for u64 { + fn from(value: TcbVersion) -> Self { + u64::from_le_bytes([ + value.bootloader, + value.tee, + value._reserved[0], + value._reserved[1], + value._reserved[2], + value._reserved[3], + value.snp, + value.microcode, + ]) + } +} + impl Display for TcbVersion { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -386,6 +459,23 @@ impl TcbVersion { } } +impl TryFrom<&[u8]> for TcbVersion { + type Error = std::io::ErrorKind; + + fn try_from(value: &[u8]) -> Result { + if value.len() != 8 { + return Err(std::io::ErrorKind::InvalidData); + } + Ok(Self { + bootloader: value[0], + tee: value[1], + _reserved: [value[2], value[3], value[4], value[5]], + snp: value[6], + microcode: value[7], + }) + } +} + bitfield! { /// Mask ID values that would go into an SNP CONFIG /// @@ -394,7 +484,7 @@ bitfield! { /// |0|MASK_CHIP_ID|Indicates that the CHIP_ID field in the attestation report will alwaysbe zero.| /// |1|MASK_CHIP_KEY|Indicates that the VCEK is not used in attestation and guest key derivation.| #[repr(C)] - #[derive(Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] + #[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct MaskId(u32); impl Debug; /// Indicates that the CHIP_ID field in the attestation report will alwaysbe zero. @@ -403,6 +493,28 @@ bitfield! { pub mask_chip_key, _: 1, 1; } +impl Default for MaskId { + fn default() -> Self { + Self(ByteParser::default()) + } +} + +impl ByteParser for MaskId { + type Bytes = [u8; 4]; + + fn from_bytes(bytes: Self::Bytes) -> Self { + Self(u32::from_le_bytes(bytes)) + } + + fn to_bytes(&self) -> Self::Bytes { + self.0.to_le_bytes() + } + + fn default() -> Self { + Self(0) + } +} + impl Display for MaskId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -479,24 +591,20 @@ mod tests { #[test] fn test_snp_platform_status_flags_zeroed() { - let actual: SnpPlatformStatusFlags = SnpPlatformStatusFlags { bits: 0 }; + let actual: SnpPlatformStatusFlags = SnpPlatformStatusFlags(0); - assert_eq!((actual & SnpPlatformStatusFlags::OWNED).bits(), 0); - assert_eq!((actual & SnpPlatformStatusFlags::ENCRYPTED_STATE).bits(), 0); + assert!(!actual.is_owned()); + assert!(!actual.is_encrypted_state_present()); } #[test] fn test_snp_platform_status_flags_full() { - let mut actual: SnpPlatformStatusFlags = SnpPlatformStatusFlags { bits: 0 }; + let mut actual: SnpPlatformStatusFlags = SnpPlatformStatusFlags(0); - actual |= SnpPlatformStatusFlags::OWNED; - actual |= SnpPlatformStatusFlags::ENCRYPTED_STATE; - - assert_eq!((actual & SnpPlatformStatusFlags::OWNED).bits(), 1); - assert_eq!( - (actual & SnpPlatformStatusFlags::ENCRYPTED_STATE).bits(), - 1 << 8 - ); + actual.0 |= 1; + actual.0 |= 1 << 8; + assert!(actual.is_owned()); + assert!(actual.is_encrypted_state_present()); } #[test] @@ -653,14 +761,19 @@ mod tests { // Test PlatformInit flags #[test] fn test_platform_init() { - let mut init = PlatformInit::empty(); - assert!(!init.contains(PlatformInit::IS_RMP_INIT)); + let mut init = PlatformInit(0); - init.insert(PlatformInit::IS_RMP_INIT); - assert!(init.contains(PlatformInit::IS_RMP_INIT)); + assert!(!init.is_rmp_init()); + init.0 |= 1; + assert!(init.is_rmp_init()); - init.insert(PlatformInit::IS_TIO_EN); - assert!(init.contains(PlatformInit::IS_TIO_EN)); + assert!(!init.alias_check_complete()); + init.0 |= 1 << 1; + assert!(init.alias_check_complete()); + + assert!(!init.is_tio_en()); + init.0 |= 1 << 3; + assert!(init.is_tio_en()); } // Test MaskId bitfield operations @@ -678,19 +791,6 @@ mod tests { assert!(display_output.contains("MaskID (3)")); } - // Test Build struct - #[test] - fn test_build() { - let build = Build { - version: Version { major: 1, minor: 2 }, - build: 42, - }; - - assert_eq!(build.version.major, 1); - assert_eq!(build.version.minor, 2); - assert_eq!(build.build, 42); - } - // Test SnpPlatformStatus #[test] fn test_platform_status() { @@ -699,10 +799,10 @@ mod tests { assert_eq!(status.guest_count, 0); let init_status = SnpPlatformStatus { - is_rmp_init: PlatformInit::IS_RMP_INIT, + is_rmp_init: PlatformInit(1), ..Default::default() }; - assert!(init_status.is_rmp_init.contains(PlatformInit::IS_RMP_INIT)); + assert!(init_status.is_rmp_init.is_rmp_init()); } #[test] @@ -720,36 +820,6 @@ mod tests { assert!(display.contains("Boot Loader: 1")); } - // Build Tests - #[test] - fn test_build_ordering_and_comparison() { - let build1 = Build { - version: Version { major: 1, minor: 0 }, - build: 1, - }; - let build2 = Build { - version: Version { major: 1, minor: 1 }, - build: 1, - }; - assert!(build1 < build2); - - let default_build = Build::default(); - assert_eq!(default_build.version.major, 0); - assert_eq!(default_build.build, 0); - } - - // PlatformInit Tests - #[test] - fn test_platform_init_flags() { - let mut flags = PlatformInit::empty(); - assert!(!flags.contains(PlatformInit::IS_RMP_INIT)); - - flags.insert(PlatformInit::IS_RMP_INIT | PlatformInit::IS_TIO_EN); - assert!(flags.contains(PlatformInit::IS_RMP_INIT)); - assert!(flags.contains(PlatformInit::IS_TIO_EN)); - assert!(!flags.contains(PlatformInit::ALIAS_CHECK_COMPLETE)); - } - // MaskId Tests #[test] fn test_mask_id_operations() { @@ -792,29 +862,13 @@ mod tests { assert_eq!(status.state, 0); assert_eq!(status.guest_count, 0); - status.is_rmp_init = PlatformInit::IS_RMP_INIT; - assert!(status.is_rmp_init.contains(PlatformInit::IS_RMP_INIT)); + status.is_rmp_init = PlatformInit(1); + assert!(status.is_rmp_init.is_rmp_init()); status.platform_tcb_version = TcbVersion::new(1, 2, 3, 4); assert_eq!(status.platform_tcb_version.snp, 3); } - #[test] - fn test_platform_status_flags_operations() { - let mut flags = SnpPlatformStatusFlags::empty(); - assert!(!flags.contains(SnpPlatformStatusFlags::OWNED)); - - flags.insert(SnpPlatformStatusFlags::OWNED); - assert!(flags.contains(SnpPlatformStatusFlags::OWNED)); - assert!(!flags.contains(SnpPlatformStatusFlags::ENCRYPTED_STATE)); - - flags.insert(SnpPlatformStatusFlags::ENCRYPTED_STATE); - assert!(flags.contains(SnpPlatformStatusFlags::ENCRYPTED_STATE)); - - flags.remove(SnpPlatformStatusFlags::OWNED); - assert!(!flags.contains(SnpPlatformStatusFlags::OWNED)); - } - #[test] fn test_tcb_status() { let status = TcbStatus { @@ -826,7 +880,7 @@ mod tests { assert_eq!(status.reported_version.bootloader, 5); let default_status = TcbStatus::default(); - assert_eq!(default_status.platform_version, TcbVersion::default()); + assert_eq!(default_status.platform_version, Default::default()); } #[test] @@ -840,9 +894,9 @@ mod tests { assert!(ffi_result.is_ok()); let default_config = Config::default(); - assert_eq!(default_config.reported_tcb, TcbVersion::default()); + assert_eq!(default_config.reported_tcb, Default::default()); let default_config_mask_id = default_config.mask_id; - assert_eq!(default_config_mask_id, MaskId::default()); + assert_eq!(default_config_mask_id, Default::default()); } #[test] @@ -858,21 +912,6 @@ mod tests { assert!(v1.partial_cmp(&v2).unwrap().is_lt()); } - #[test] - fn test_build_version_comparisons() { - let b1 = Build { - version: Version { major: 1, minor: 0 }, - build: 100, - }; - let b2 = Build { - version: Version { major: 1, minor: 1 }, - build: 50, - }; - - assert!(b1 < b2); - assert_ne!(b1, b2); - } - #[test] fn test_platform_status_boundary() { let status = SnpPlatformStatus { @@ -897,17 +936,6 @@ mod tests { assert_eq!(mask.mask_chip_key(), 0); } - #[test] - fn test_platform_init_combinations() { - let mut init = PlatformInit::empty(); - init.insert(PlatformInit::IS_RMP_INIT | PlatformInit::IS_TIO_EN); - assert!(init.contains(PlatformInit::IS_RMP_INIT | PlatformInit::IS_TIO_EN)); - - init.remove(PlatformInit::IS_RMP_INIT); - assert!(!init.contains(PlatformInit::IS_RMP_INIT)); - assert!(init.contains(PlatformInit::IS_TIO_EN)); - } - #[test] fn test_tcb_version_reserved() { let tcb = TcbVersion::new(1, 2, 3, 4); @@ -923,14 +951,14 @@ mod tests { #[test] fn test_platform_status_all_fields() { let status: SnpPlatformStatus = SnpPlatformStatus { - version: Version { major: 1, minor: 2 }, + version: (1, 2), build_id: 0xDEADBEEF, mask_chip_id: 0x1, state: 0xFF, ..Default::default() }; - assert_eq!(status.version.major, 1); - assert_eq!(status.version.minor, 2); + assert_eq!(status.version.0, 1); + assert_eq!(status.version.1, 2); assert_eq!(status.build_id, 0xDEADBEEF); assert_eq!(status.mask_chip_id, 0x1); assert_eq!(status.state, 0xFF); @@ -1032,19 +1060,6 @@ mod tests { assert_eq!(sorted[3].cert_type, CertType::Empty); } - #[test] - fn test_build_deserialization() { - let build = Build { - version: Version { major: 1, minor: 2 }, - build: 42, - }; - - let serialized = bincode::serialize(&build).unwrap(); - let deserialized: Build = bincode::deserialize(&serialized).unwrap(); - - assert_eq!(build, deserialized); - } - #[test] fn test_tcb_version_deserialization() { let tcb = TcbVersion::new(1, 2, 3, 4); @@ -1248,22 +1263,6 @@ mod tests { ); } - #[test] - fn test_build_deserialize() { - use bincode::{deserialize, serialize}; - - let original = Build { - version: 1.into(), - build: 2, - }; - - let serialized = serialize(&original).expect("Failed to serialize"); - let deserialized: Build = deserialize(&serialized).expect("Failed to deserialize"); - - assert_eq!(deserialized.version, original.version); - assert_eq!(deserialized.build, original.build); - } - #[test] fn test_chain_visitor_methods() { use bincode::{deserialize, serialize}; @@ -1298,4 +1297,221 @@ mod tests { assert_eq!(deserialized, text); } + + #[test] + fn test_snp_platform_status_flags_bitor_assign() { + let mut flags1 = SnpPlatformStatusFlags::default(); + let flags2 = SnpPlatformStatusFlags::default(); + flags1 |= flags2; + assert_eq!(flags1.0, 0); + + let mut flags1 = SnpPlatformStatusFlags(1); + let flags2 = SnpPlatformStatusFlags(2); + flags1 |= flags2; + assert_eq!(flags1.0, 3); + } + + #[test] + fn test_platform_init_bitor_assign() { + let mut init1 = PlatformInit::default(); + let init2 = PlatformInit::default(); + init1 |= init2; + assert_eq!(init1.0, 0); + + let mut init1 = PlatformInit(1); + let init2 = PlatformInit(2); + init1 |= init2; + assert_eq!(init1.0, 3); + } + + #[test] + fn test_tcb_version_from_bytes() { + let bytes: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; + let tcb_version = TcbVersion::from_bytes(bytes); + assert_eq!(tcb_version.bootloader, 1); + assert_eq!(tcb_version.tee, 2); + assert_eq!(tcb_version._reserved, [3, 4, 5, 6]); + assert_eq!(tcb_version.snp, 7); + assert_eq!(tcb_version.microcode, 8); + } + + #[test] + fn test_tcb_version_to_bytes() { + let tcb_version = TcbVersion { + bootloader: 1, + tee: 2, + _reserved: [3, 4, 5, 6], + snp: 7, + microcode: 8, + }; + let bytes = tcb_version.to_bytes(); + assert_eq!(bytes, [1, 2, 3, 4, 5, 6, 7, 8]); + } + + #[test] + fn test_tcb_version_default() { + let tcb_version: TcbVersion = Default::default(); + assert_eq!(tcb_version.bootloader, 0); + assert_eq!(tcb_version.tee, 0); + assert_eq!(tcb_version._reserved, [0, 0, 0, 0]); + assert_eq!(tcb_version.snp, 0); + assert_eq!(tcb_version.microcode, 0); + } + + #[test] + fn test_mask_id_from_bytes() { + let bytes: [u8; 4] = [0b11, 0b11, 0b11, 0b11]; + let mask_id = MaskId::from_bytes(bytes); + assert_eq!(mask_id.mask_chip_id(), 1); + assert_eq!(mask_id.mask_chip_key(), 1); + } + + #[test] + fn test_mask_id_to_bytes() { + let mask_id = MaskId(0x01020304); + let bytes = mask_id.to_bytes(); + assert_eq!(bytes, [0x04, 0x03, 0x02, 0x01]); + } + + #[test] + fn test_from_u64() { + // Test normal case + let value = 0x0807060504030201u64; + let tcb = TcbVersion::from(value); + assert_eq!(tcb.bootloader, 0x01); + assert_eq!(tcb.tee, 0x02); + assert_eq!(tcb._reserved, [0x03, 0x04, 0x05, 0x06]); + assert_eq!(tcb.snp, 0x07); + assert_eq!(tcb.microcode, 0x08); + + // Test zero case + let tcb = TcbVersion::from(0u64); + assert_eq!(tcb.bootloader, 0); + assert_eq!(tcb.tee, 0); + assert_eq!(tcb._reserved, [0, 0, 0, 0]); + assert_eq!(tcb.snp, 0); + assert_eq!(tcb.microcode, 0); + + // Test maximum values + let value = 0xFFFFFFFFFFFFFFFFu64; + let tcb = TcbVersion::from(value); + assert_eq!(tcb.bootloader, 0xFF); + assert_eq!(tcb.tee, 0xFF); + assert_eq!(tcb._reserved, [0xFF, 0xFF, 0xFF, 0xFF]); + assert_eq!(tcb.snp, 0xFF); + assert_eq!(tcb.microcode, 0xFF); + } + + #[test] + fn test_to_u64() { + // Test normal case + let tcb = TcbVersion { + bootloader: 0x01, + tee: 0x02, + _reserved: [0x03, 0x04, 0x05, 0x06], + snp: 0x07, + microcode: 0x08, + }; + let value: u64 = tcb.into(); + assert_eq!(value, 0x0807060504030201); + + // Test zero case + let tcb: TcbVersion = Default::default(); + let value: u64 = tcb.into(); + assert_eq!(value, 0); + + // Test maximum values + let tcb = TcbVersion { + bootloader: 0xFF, + tee: 0xFF, + _reserved: [0xFF, 0xFF, 0xFF, 0xFF], + snp: 0xFF, + microcode: 0xFF, + }; + let value: u64 = tcb.into(); + assert_eq!(value, 0xFFFFFFFFFFFFFFFF); + } + + #[test] + fn test_try_from_bytes() { + // Test normal case + let bytes = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; + let tcb = TcbVersion::try_from(&bytes[..]).unwrap(); + assert_eq!(tcb.bootloader, 0x01); + assert_eq!(tcb.tee, 0x02); + assert_eq!(tcb._reserved, [0x03, 0x04, 0x05, 0x06]); + assert_eq!(tcb.snp, 0x07); + assert_eq!(tcb.microcode, 0x08); + + // Test error case - wrong length + let bytes = [0x01, 0x02, 0x03]; + assert!(TcbVersion::try_from(&bytes[..]).is_err()); + + // Test zero case + let bytes = [0; 8]; + let tcb = TcbVersion::try_from(&bytes[..]).unwrap(); + assert_eq!(tcb, Default::default()); + + // Test maximum values + let bytes = [0xFF; 8]; + let tcb = TcbVersion::try_from(&bytes[..]).unwrap(); + assert_eq!(tcb.bootloader, 0xFF); + assert_eq!(tcb.tee, 0xFF); + assert_eq!(tcb._reserved, [0xFF, 0xFF, 0xFF, 0xFF]); + assert_eq!(tcb.snp, 0xFF); + assert_eq!(tcb.microcode, 0xFF); + } + + #[test] + fn test_roundtrip_conversions() { + // u64 -> TcbVersion -> u64 + let original = 0x0807060504030201u64; + let tcb = TcbVersion::from(original); + let roundtrip: u64 = tcb.into(); + assert_eq!(original, roundtrip); + + // TcbVersion -> u64 -> TcbVersion + let original = TcbVersion { + bootloader: 0x01, + tee: 0x02, + _reserved: [0x03, 0x04, 0x05, 0x06], + snp: 0x07, + microcode: 0x08, + }; + let u64_val: u64 = original.into(); + let roundtrip = TcbVersion::from(u64_val); + assert_eq!(original, roundtrip); + + // &[u8] -> TcbVersion -> u64 -> TcbVersion + let bytes = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; + let tcb = TcbVersion::try_from(&bytes[..]).unwrap(); + let u64_val: u64 = tcb.into(); + let roundtrip = TcbVersion::from(u64_val); + assert_eq!(tcb, roundtrip); + } + + #[test] + fn test_byte_order() { + // Test little-endian byte order for u64 + let value = 0x0102030405060708u64; + let tcb = TcbVersion::from(value.to_le()); + assert_eq!(tcb.bootloader, 0x08); + assert_eq!(tcb.tee, 0x07); + assert_eq!(tcb._reserved, [0x06, 0x05, 0x04, 0x03]); + assert_eq!(tcb.snp, 0x02); + assert_eq!(tcb.microcode, 0x01); + + // Test byte order preservation in roundtrip + let original_bytes = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; + let tcb = TcbVersion::try_from(&original_bytes[..]).unwrap(); + let u64_val: u64 = tcb.into(); + let final_bytes = u64_val.to_le_bytes(); + assert_eq!(original_bytes, final_bytes); + } + + #[test] + fn test_mask_id_default() { + let mask_id: MaskId = Default::default(); + assert_eq!(mask_id.0, 0); + } } diff --git a/src/firmware/linux/guest/types.rs b/src/firmware/linux/guest/types.rs index 341f0b23..514ef18a 100644 --- a/src/firmware/linux/guest/types.rs +++ b/src/firmware/linux/guest/types.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -use crate::{error::*, firmware::guest::*, util::large_array::LargeArray}; +use crate::{error::*, firmware::guest::*, util::array::Array}; use static_assertions::const_assert; @@ -185,7 +185,7 @@ pub struct ReportRsp { pub report_size: u32, reserved_0: [u8; 24], /// The attestation report generated by the firmware. - pub report: LargeArray, + pub report: Array, /// Padding bits to meet the memory page alignment. reserved_1: [u8; 4000 - (REPORT_SIZE + (std::mem::size_of::() * 2) + std::mem::size_of::<[u8; 24]>())], diff --git a/src/firmware/linux/host/types/sev.rs b/src/firmware/linux/host/types/sev.rs index 3ee46610..39bac3d3 100644 --- a/src/firmware/linux/host/types/sev.rs +++ b/src/firmware/linux/host/types/sev.rs @@ -3,7 +3,7 @@ #[cfg(target_os = "linux")] use crate::certs::sev::sev; -use crate::Version; +use crate::firmware::host::Version; #[cfg(target_os = "linux")] use std::marker::PhantomData; diff --git a/src/firmware/mod.rs b/src/firmware/mod.rs index c9dd5fce..1845d38a 100644 --- a/src/firmware/mod.rs +++ b/src/firmware/mod.rs @@ -9,5 +9,8 @@ pub mod host; #[cfg(feature = "snp")] pub mod guest; +#[cfg(feature = "snp")] +pub(crate) mod parser; + #[cfg(any(feature = "sev", feature = "snp"))] pub(crate) mod linux; diff --git a/src/firmware/parser/byte_parser.rs b/src/firmware/parser/byte_parser.rs new file mode 100644 index 00000000..90615105 --- /dev/null +++ b/src/firmware/parser/byte_parser.rs @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: Apache-2.0 + +pub(crate) trait ByteParser { + type Bytes: AsRef<[u8]>; + + fn from_bytes(bytes: Self::Bytes) -> Self; + + fn to_bytes(&self) -> Self::Bytes; + + fn default() -> Self; +} + +impl ByteParser for [u8; N] { + type Bytes = [u8; N]; + + #[inline(always)] + fn from_bytes(bytes: Self::Bytes) -> Self { + bytes + } + #[inline(always)] + fn to_bytes(&self) -> Self::Bytes { + *self + } + #[inline(always)] + fn default() -> Self { + [0u8; N] + } +} + +macro_rules! impl_byte_parser +{ + ($($t:ty), *) => { + $( + impl ByteParser for $t { + type Bytes = [u8; std::mem::size_of::<$t>()]; + #[inline(always)] + fn from_bytes(bytes: Self::Bytes) -> Self { + <$t>::from_le_bytes(bytes) + } + #[inline(always)] + fn to_bytes(&self) -> Self::Bytes { + <$t>::to_le_bytes(*self) + } + #[inline(always)] + fn default() -> Self { + 0 + } + } + )* + }; +} + +impl_byte_parser!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); + +#[cfg(test)] +mod tests { + use super::*; + + // Array tests + #[test] + fn test_array_conversions() { + let arr: [u8; 4] = [1, 2, 3, 4]; + assert_eq!(arr.to_bytes(), [1, 2, 3, 4]); + assert_eq!(<[u8; 4]>::from_bytes([1, 2, 3, 4]), [1, 2, 3, 4]); + assert_eq!(<[u8; 4] as ByteParser>::default(), [0, 0, 0, 0]); + } + + // Numeric type tests + #[test] + fn test_u8_conversions() { + let val: u8 = 42; + assert_eq!(val.to_bytes(), [42]); + assert_eq!(u8::from_bytes([42]), 42); + assert_eq!(::default(), 0); + } + + #[test] + fn test_u16_conversions() { + let val: u16 = 0x1234; + assert_eq!(val.to_bytes(), [0x34, 0x12]); + assert_eq!(u16::from_bytes([0x34, 0x12]), 0x1234); + assert_eq!(::default(), 0); + } + + #[test] + fn test_u32_conversions() { + let val: u32 = 0x12345678; + assert_eq!(val.to_bytes(), [0x78, 0x56, 0x34, 0x12]); + assert_eq!(u32::from_bytes([0x78, 0x56, 0x34, 0x12]), 0x12345678); + assert_eq!(::default(), 0); + } + + // Edge cases + #[test] + fn test_numeric_edge_cases() { + assert_eq!(u8::from_bytes(u8::MAX.to_bytes()), u8::MAX); + assert_eq!(u16::from_bytes(u16::MAX.to_bytes()), u16::MAX); + assert_eq!(u32::from_bytes(u32::MAX.to_bytes()), u32::MAX); + assert_eq!(i8::from_bytes(i8::MIN.to_bytes()), i8::MIN); + assert_eq!(i16::from_bytes(i16::MIN.to_bytes()), i16::MIN); + assert_eq!(i32::from_bytes(i32::MIN.to_bytes()), i32::MIN); + } + + // Round-trip tests + #[test] + fn test_round_trip_conversions() { + let values = [ + 0i32, + 1, + -1, + i32::MAX, + i32::MIN, + 42, + -42, + 0x12345678, + -0x12345678, + ]; + + for &val in &values { + assert_eq!(i32::from_bytes(val.to_bytes()), val); + } + } + + #[test] + fn test_array_operations() { + let arr1: [u8; 1] = [1]; + let arr4: [u8; 4] = [1, 2, 3, 4]; + let arr8: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; + + assert_eq!(arr1.to_bytes(), [1]); + assert_eq!(arr4.to_bytes(), [1, 2, 3, 4]); + assert_eq!(arr8.to_bytes(), [1, 2, 3, 4, 5, 6, 7, 8]); + + assert_eq!(<[u8; 4] as ByteParser>::default(), [0; 4]); + assert_eq!( + <[u8; 8]>::from_bytes([1, 2, 3, 4, 5, 6, 7, 8]), + [1, 2, 3, 4, 5, 6, 7, 8] + ); + } + + #[test] + fn test_unsigned_integers() { + // u8 + assert_eq!(255u8.to_bytes(), [255]); + assert_eq!(u8::from_bytes([128]), 128); + assert_eq!(::default(), 0); + + // u16 + assert_eq!(0xFF00u16.to_bytes(), [0x00, 0xFF]); + assert_eq!(u16::from_bytes([0x34, 0x12]), 0x1234); + assert_eq!(::default(), 0); + + // u32 + assert_eq!(0x12345678u32.to_bytes(), [0x78, 0x56, 0x34, 0x12]); + assert_eq!(u32::from_bytes([0x78, 0x56, 0x34, 0x12]), 0x12345678); + assert_eq!(::default(), 0); + + // u64 + assert_eq!( + 0x123456789ABCDEFu64.to_bytes(), + [0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01] + ); + assert_eq!(::default(), 0); + + // u128 + assert_eq!(u128::MAX.to_bytes()[15], 0xFF); + assert_eq!(::default(), 0); + } + + #[test] + fn test_signed_integers() { + // i8 + assert_eq!((-128i8).to_bytes(), [0x80]); + assert_eq!(i8::from_bytes([0x80]), -128); + assert_eq!(::default(), 0); + + // i16 + assert_eq!((-32768i16).to_bytes(), [0x00, 0x80]); + assert_eq!(i16::from_bytes([0x00, 0x80]), -32768); + assert_eq!(::default(), 0); + + // i32, i64, i128 + assert_eq!(i32::MIN.to_bytes()[3], 0x80); + assert_eq!(i64::MIN.to_bytes()[7], 0x80); + assert_eq!(i128::MIN.to_bytes()[15], 0x80); + } + + #[test] + fn test_edge_cases() { + // Size-specific tests + assert_eq!(::default(), 0); + assert_eq!(::default(), 0); + + // Max values + assert_eq!(u8::from_bytes(u8::MAX.to_bytes()), u8::MAX); + assert_eq!(u16::from_bytes(u16::MAX.to_bytes()), u16::MAX); + assert_eq!(u32::from_bytes(u32::MAX.to_bytes()), u32::MAX); + assert_eq!(u64::from_bytes(u64::MAX.to_bytes()), u64::MAX); + assert_eq!(u128::from_bytes(u128::MAX.to_bytes()), u128::MAX); + + // Min values + assert_eq!(i8::from_bytes(i8::MIN.to_bytes()), i8::MIN); + assert_eq!(i16::from_bytes(i16::MIN.to_bytes()), i16::MIN); + assert_eq!(i32::from_bytes(i32::MIN.to_bytes()), i32::MIN); + assert_eq!(i64::from_bytes(i64::MIN.to_bytes()), i64::MIN); + assert_eq!(i128::from_bytes(i128::MIN.to_bytes()), i128::MIN); + } +} diff --git a/src/firmware/parser/mod.rs b/src/firmware/parser/mod.rs new file mode 100644 index 00000000..be607a0f --- /dev/null +++ b/src/firmware/parser/mod.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(feature = "snp")] +mod byte_parser; + +#[cfg(feature = "snp")] +mod read_ext; + +#[cfg(feature = "snp")] +mod write_ext; + +pub(crate) use self::{byte_parser::ByteParser, read_ext::ReadExt, write_ext::WriteExt}; diff --git a/src/firmware/parser/read_ext.rs b/src/firmware/parser/read_ext.rs new file mode 100644 index 00000000..94d06447 --- /dev/null +++ b/src/firmware/parser/read_ext.rs @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: Apache-2.0 +use super::byte_parser::ByteParser; +use std::io::Read; + +pub(crate) trait ReadExt { + fn parse_bytes(&mut self) -> Result + where + T: ByteParser>; +} + +impl ReadExt for R +where + R: Read, +{ + #[inline(always)] + fn parse_bytes(&mut self) -> Result + where + T: ByteParser>, + { + if SKIP != 0 { + let mut skipped_bytes = [0; SKIP]; + self.read_exact(&mut skipped_bytes)?; + + if skipped_bytes != [0; SKIP] { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "Skipped bytes were expected to be zeroed.", + )); + } + } + + let mut bytes = T::default().to_bytes(); + self.read_exact(bytes.as_mut())?; + Ok(T::from_bytes(bytes)) + } +} + +#[cfg(test)] +mod read_ext_tests { + use super::*; + use std::io::{self, Read}; + + // Mock Reader implementation + struct MockReader { + data: Vec, + position: usize, + } + + impl MockReader { + fn new(data: Vec) -> Self { + MockReader { data, position: 0 } + } + } + + impl Read for MockReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let len = std::cmp::min(buf.len(), self.data.len() - self.position); + if len > 0 { + buf[..len].copy_from_slice(&self.data[self.position..self.position + len]); + self.position += len; + Ok(len) + } else { + Ok(0) // Indicate EOF + } + } + } + + // Test case 1: No Skip, Valid Data + #[test] + fn test_no_skip_valid_data() { + let data = vec![0x12, 0x34, 0x56, 0x78]; + let mut reader = MockReader::new(data); + let result: Result = reader.parse_bytes::(); + assert_eq!(result.unwrap(), 0x78563412); + } + + // Test case 2: Skip, Valid Data + #[test] + fn test_skip_valid_data() { + let data = vec![0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78]; + let mut reader = MockReader::new(data); + let result: Result = reader.parse_bytes::(); + assert_eq!(result.unwrap(), 0x78563412); + } + + // Test case 3: Skip, Invalid Data + #[test] + fn test_skip_invalid_data() { + let data = vec![0, 0, 1, 0, 0x12, 0x34, 0x56, 0x78]; + let mut reader = MockReader::new(data); + let result: Result = reader.parse_bytes::(); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidData); + } + + // Test case 4: Read Fails (UnexpectedEof) + #[test] + fn test_read_fails() { + let data = vec![0x12, 0x34]; + let mut reader = MockReader::new(data); + let result: Result = reader.parse_bytes::(); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().kind(), io::ErrorKind::UnexpectedEof); + } + + // Test case 5: Zero Length Read + #[test] + fn test_zero_length_read() { + let data: Vec = vec![]; + let mut reader = MockReader::new(data); + let result: Result<[u8; 0], _> = reader.parse_bytes::<[u8; 0], 0>(); + assert!(result.is_ok()); + } + + // Test case 6: Empty Reader + #[test] + fn test_empty_reader() { + let data: Vec = vec![]; + let mut reader = MockReader::new(data); + let result: Result = reader.parse_bytes::(); + assert!(result.is_err()); + assert_eq!(result.unwrap_err().kind(), io::ErrorKind::UnexpectedEof); + } +} diff --git a/src/firmware/parser/write_ext.rs b/src/firmware/parser/write_ext.rs new file mode 100644 index 00000000..708d913a --- /dev/null +++ b/src/firmware/parser/write_ext.rs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: Apache-2.0 +use super::ByteParser; +use std::io::Write; + +pub(crate) trait WriteExt { + fn write_bytes( + &mut self, + value: T, + ) -> Result<(), std::io::Error>; +} +impl WriteExt for W +where + W: Write, +{ + #[inline(always)] + fn write_bytes( + &mut self, + value: T, + ) -> Result<(), std::io::Error> { + if SKIP != 0 { + self.write_all(&[0; SKIP])?; + } + + self.write_all(value.to_bytes().as_ref()) + } +} + +#[cfg(test)] +mod write_ext_tests { + use super::*; + + // Mock writer to capture written bytes + #[derive(Debug, Default)] + struct MockWriter { + written: Vec, + } + + impl Write for MockWriter { + fn write(&mut self, buf: &[u8]) -> Result { + self.written.extend_from_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<(), std::io::Error> { + Ok(()) + } + } + + #[test] + fn test_write_bytes_no_skip() -> Result<(), std::io::Error> { + let mut writer = MockWriter::default(); + let value: u32 = 0x12345678; + writer.write_bytes::<_, 0>(value)?; + + assert_eq!(writer.written, [0x78, 0x56, 0x34, 0x12]); + Ok(()) + } + + #[test] + fn test_write_bytes_with_skip() -> Result<(), std::io::Error> { + let mut writer = MockWriter::default(); + let value: u16 = 0xABCD; + writer.write_bytes::<_, 2>(value)?; + + assert_eq!(writer.written, [0, 0, 0xCD, 0xAB]); + Ok(()) + } + + #[test] + fn test_mock_writer_flush() -> Result<(), std::io::Error> { + let mut writer = MockWriter::default(); + writer.flush()?; + // Flush doesn't modify the written buffer, so we just check that it executes without error. + assert_eq!(writer.written.len(), 0); + Ok(()) + } +} diff --git a/src/launch/sev.rs b/src/launch/sev.rs index eafca6f8..d4e9a2bc 100644 --- a/src/launch/sev.rs +++ b/src/launch/sev.rs @@ -4,7 +4,11 @@ //! This ensures (at compile time) that the right steps are called in the //! right order. -use crate::error::{FirmwareError, SevError}; +use crate::{ + error::{FirmwareError, SevError}, + firmware::host::Version, + util::{TypeLoad, TypeSave}, +}; #[cfg(target_os = "linux")] use crate::launch::linux::ioctl::*; diff --git a/src/launch/snp.rs b/src/launch/snp.rs index 9f3ccfe6..c991023e 100644 --- a/src/launch/snp.rs +++ b/src/launch/snp.rs @@ -147,7 +147,7 @@ impl Launcher { } /// Encapsulates the various data needed to begin the launch process. -#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] +#[derive(Default, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct Start { /// Describes a policy that the AMD Secure Processor will enforce. pub(crate) policy: GuestPolicy, diff --git a/src/lib.rs b/src/lib.rs index 9de4be75..46125958 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,7 +112,6 @@ pub mod error; #[cfg(all(feature = "sev", feature = "dangerous_hw_tests"))] pub use util::cached_chain; -use util::{TypeLoad, TypeSave}; #[cfg(all(feature = "openssl", feature = "sev"))] use certs::sev::sev; @@ -136,65 +135,6 @@ use std::io::{Read, Write}; use serde::{Deserialize, Serialize}; -/// Information about the SEV platform version. -#[repr(C)] -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Version { - /// The major version number. - pub major: u8, - - /// The minor version number. - pub minor: u8, -} - -impl std::fmt::Display for Version { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}.{}", self.major, self.minor) - } -} - -impl From for Version { - fn from(v: u16) -> Self { - Self { - major: ((v & 0xF0) >> 4) as u8, - minor: (v & 0x0F) as u8, - } - } -} - -/// A description of the SEV platform's build information. -#[repr(C)] -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Build { - /// The version information. - pub version: Version, - - /// The build number. - pub build: u8, -} - -impl std::fmt::Display for Build { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}.{}", self.version, self.build) - } -} - -impl codicon::Decoder<()> for Build { - type Error = std::io::Error; - - fn decode(mut reader: impl Read, _: ()) -> std::io::Result { - reader.load() - } -} - -impl codicon::Encoder<()> for Build { - type Error = std::io::Error; - - fn encode(&self, mut writer: impl Write, _: ()) -> std::io::Result<()> { - writer.save(self) - } -} - /// A representation for EPYC generational product lines. /// /// Implements type conversion traits to determine which generation diff --git a/src/measurement/idblock_types.rs b/src/measurement/idblock_types.rs index 329357f6..0fc1b2db 100644 --- a/src/measurement/idblock_types.rs +++ b/src/measurement/idblock_types.rs @@ -16,7 +16,7 @@ use std::convert::{TryFrom, TryInto}; use crate::{ error::IdBlockError, firmware::guest::GuestPolicy, measurement::snp::SnpLaunchDigest, - util::large_array::LargeArray, + util::array::Array, }; pub(crate) const DEFAULT_ID_VERSION: u32 = 1; @@ -68,9 +68,9 @@ pub type ImageId = FamilyId; #[repr(C)] #[derive(Default, Serialize, Deserialize, Clone, Copy)] pub struct SevEcdsaSig { - r: LargeArray, - s: LargeArray, - reserved: LargeArray, + r: Array, + s: Array, + reserved: Array, } // Derive SEV ECDSA signature from a private EC KEY @@ -131,11 +131,11 @@ impl TryFrom<(EcKey, &[u8])> for SevEcdsaSig { #[derive(Default, Serialize, Deserialize, Clone, Copy)] pub struct SevEcdsaKeyData { /// QX component of the ECDSA public key - pub qx: LargeArray, + pub qx: Array, /// QY component of the ECDSA public key - pub qy: LargeArray, + pub qy: Array, /// Reserved - reserved: LargeArray, + reserved: Array, } /// SEV ECDSA public key. Need it in this format to calculate the AUTH-ID. @@ -267,19 +267,19 @@ pub struct IdAuth { /// The algorithm of the Author Key. Defaults to P-384 pub author_key_algo: u32, /// Reserved - reserved1: LargeArray, + reserved1: Array, /// The signature of all bytes of the ID block pub id_block_sig: SevEcdsaSig, /// The public component of the ID key pub id_pubkey: SevEcdsaPubKey, /// Reserved - reserved2: LargeArray, + reserved2: Array, /// The signature of the ID_KEY pub id_key_sig: SevEcdsaSig, /// The public component of the Author key pub author_pub_key: SevEcdsaPubKey, /// Reserved - reserved3: LargeArray, + reserved3: Array, } impl IdAuth { diff --git a/src/measurement/snp.rs b/src/measurement/snp.rs index 90c1a517..9deebdc0 100644 --- a/src/measurement/snp.rs +++ b/src/measurement/snp.rs @@ -11,7 +11,7 @@ use crate::{ vcpu_types::CpuType, vmsa::{GuestFeatures, VMMType, VMSA}, }, - util::large_array::LargeArray, + util::array::Array, }; use hex::FromHex; use serde::{Deserialize, Serialize}; @@ -27,7 +27,7 @@ pub(crate) const LD_BYTES: usize = LD_BITS / 8; /// The expected launch digest of the guest #[repr(C)] #[derive(Debug, Default, Serialize, Deserialize, Clone, Copy)] -pub struct SnpLaunchDigest(LargeArray); +pub struct SnpLaunchDigest(Array); // Try from slice impl TryFrom<&[u8]> for SnpLaunchDigest { @@ -51,13 +51,13 @@ impl TryInto> for SnpLaunchDigest { impl SnpLaunchDigest { /// Create Launch Digest from large array - pub fn new(data: LargeArray) -> Self { + pub fn new(data: Array) -> Self { Self(data) } /// Get the launch digest as a hex string pub fn get_hex_ld(self) -> String { - hex::encode::<&[u8]>(self.0.as_slice()) + format!("{:x}", self.0) } } diff --git a/src/measurement/vmsa.rs b/src/measurement/vmsa.rs index dad3cc20..5eebfa78 100644 --- a/src/measurement/vmsa.rs +++ b/src/measurement/vmsa.rs @@ -1,9 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 //! Operations to build and interact with an SEV-ES VMSA -use crate::{ - error::MeasurementError, measurement::vcpu_types::CpuType, util::large_array::LargeArray, -}; +use crate::{error::MeasurementError, measurement::vcpu_types::CpuType, util::array::Array}; use bitfield::bitfield; use serde::{Deserialize, Serialize}; use std::{convert::TryFrom, fmt, str::FromStr}; @@ -206,7 +204,7 @@ struct SevEsSaveArea { cpl: u8, reserved_0xcc: [u8; 4], efer: u64, - reserved_0xd8: LargeArray, + reserved_0xd8: Array, xss: u64, cr4: u64, cr3: u64, @@ -245,7 +243,7 @@ struct SevEsSaveArea { br_to: u64, last_excp_from: u64, last_excp_to: u64, - reserved_0x298: LargeArray, + reserved_0x298: Array, pkru: u32, tsc_aux: u32, reserved_0x2f0: [u8; 24], @@ -289,10 +287,10 @@ struct SevEsSaveArea { x87_ds: u16, x87_cs: u16, x87_rip: u64, - fpreg_x87: LargeArray, - fpreg_xmm: LargeArray, - fpreg_ymm: LargeArray, - manual_padding: LargeArray, + fpreg_x87: Array, + fpreg_xmm: Array, + fpreg_ymm: Array, + manual_padding: Array, } const BSP_EIP: u64 = 0xffff_fff0; diff --git a/src/session/key.rs b/src/session/key.rs index d046b93e..ae6e1556 100644 --- a/src/session/key.rs +++ b/src/session/key.rs @@ -86,10 +86,10 @@ impl Key { let _ = u32::try_from(hbytes * 8).or(Err(ErrorKind::InvalidInput))?; let lbits = u32::try_from(size * 8).or(Err(ErrorKind::InvalidInput))?; - let mut out = Key::zeroed((size + hbytes - 1) / hbytes * hbytes); + let mut out = Key::zeroed(size.div_ceil(hbytes) * hbytes); let mut buf = &mut out[..]; - for i in 1..=((size + hbytes - 1) / hbytes) as u32 { + for i in 1..=size.div_ceil(hbytes) as u32 { let mut sig = sign::Signer::new(hsh, &key)?; sig.update(&i.to_le_bytes())?; diff --git a/src/session/mod.rs b/src/session/mod.rs index 70d0de0a..3d04a5d8 100644 --- a/src/session/mod.rs +++ b/src/session/mod.rs @@ -5,7 +5,7 @@ mod key; -use crate::error::SessionError; +use crate::{error::SessionError, firmware::host::Build}; use super::*; @@ -281,7 +281,11 @@ impl Session { #[cfg(test)] mod initialized { use super::*; - use crate::{launch, session::Session, Build, Version}; + use crate::{ + firmware::host::{Build, Version}, + launch, + session::Session, + }; #[test] fn session() { diff --git a/src/util/array.rs b/src/util/array.rs new file mode 100644 index 00000000..343445fb --- /dev/null +++ b/src/util/array.rs @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: Apache-2.0 + +//! Helpful structure to deal with arrays with a size larger than 32 bytes + +use crate::{error::LargeArrayError, firmware::parser::ByteParser}; +use serde::{de::Visitor, Deserialize, Serialize}; +use std::{ + convert::{TryFrom, TryInto}, + fmt::{Debug, LowerHex, UpperHex}, + ops::{Deref, DerefMut}, +}; + +struct ArrayVisitor(std::marker::PhantomData<[T; N]>); + +impl<'de, T, const N: usize> Visitor<'de> for ArrayVisitor +where + T: Deserialize<'de> + Default + Copy, +{ + type Value = Array; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "An array with length of {}", N) + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut array = [T::default(); N]; + for (i, item) in array.iter_mut().enumerate() { + *item = seq + .next_element()? + .ok_or_else(|| serde::de::Error::invalid_length(i, &self))?; + } + Ok(Array(array)) + } +} + +impl Serialize for Array +where + T: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.0.serialize(serializer) + } +} + +impl<'de, T, const N: usize> Deserialize<'de> for Array +where + T: Deserialize<'de> + Default + Copy, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_seq(ArrayVisitor(std::marker::PhantomData)) + } +} + +/// Large array structure to serialize and default arrays larger than 32 bytes. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Array(pub [T; N]); + +impl LowerHex for Array { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + for byte in self.0.iter() { + write!(f, "{byte:02x}")?; + } + Ok(()) + } +} + +impl UpperHex for Array { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + for byte in self.0.iter() { + write!(f, "{byte:02X}")?; + } + Ok(()) + } +} + +impl std::fmt::Display for Array { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + // Generate a hexdump-like format of the bytes using the follow criteria: + // - 16 bytes per line + // - Each byte is represented by 2 hex digits + // - Each byte is separated by a space + // - No tailing white space + + for chunk in self.0.chunks(16) { + writeln!( + f, + "{}", + chunk + .iter() + .map(|byte| format!("{byte:02x}")) + .collect::>() + .join(" ") + )?; + } + // Remove the trailing newline + if !self.0.is_empty() { + f.write_str("")?; + } + Ok(()) + } +} + +impl ByteParser for Array { + type Bytes = [u8; N]; + + #[inline] + fn from_bytes(bytes: Self::Bytes) -> Self { + Self(bytes) + } + + #[inline] + fn to_bytes(&self) -> Self::Bytes { + self.0 + } + + #[inline] + fn default() -> Self { + Self([0; N]) + } +} + +impl Default for Array +where + T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, +{ + fn default() -> Self { + Self([T::default(); N]) + } +} + +impl TryFrom> for Array +where + T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, +{ + type Error = LargeArrayError; + + fn try_from(vec: Vec) -> Result { + Ok(Array(vec.try_into().map_err(|_| { + LargeArrayError::VectorError("Vector is the wrong size".to_string()) + })?)) + } +} + +impl TryFrom<[T; N]> for Array +where + T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, +{ + type Error = LargeArrayError; + + fn try_from(array: [T; N]) -> Result { + Ok(Array(array)) + } +} + +impl TryFrom<&[T]> for Array +where + T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, +{ + type Error = LargeArrayError; + + fn try_from(slice: &[T]) -> Result { + Ok(Array(slice.try_into()?)) + } +} + +impl Array +where + T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, +{ + /// Get the large array as a regular array format + pub fn as_array(&self) -> [T; N] { + self.0 + } +} +impl AsRef for Array +where + [T; N]: AsRef, +{ + #[inline] + fn as_ref(&self) -> &U { + self.0.as_ref() + } +} + +impl AsMut for Array +where + [T; N]: AsMut, +{ + #[inline] + fn as_mut(&mut self) -> &mut U { + self.0.as_mut() + } +} + +impl Deref for Array +where + T: std::marker::Copy + std::default::Default + Serialize + for<'a> Deserialize<'a>, +{ + type Target = [T; N]; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Array +where + T: std::marker::Copy + std::default::Default + Serialize + for<'a> Deserialize<'a>, +{ + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/src/util/large_array.rs b/src/util/large_array.rs deleted file mode 100644 index 7c915625..00000000 --- a/src/util/large_array.rs +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -//! Helpful structure to deal with arrays with a size larger than 32 bytes - -use crate::error::LargeArrayError; -use serde::{Deserialize, Serialize}; -use serde_big_array::BigArray; -use std::convert::{TryFrom, TryInto}; - -/// Large array structure to serialize and default arrays larger than 32 bytes. -#[derive(Debug, Serialize, Deserialize, Clone, Copy)] -#[repr(C)] -pub struct LargeArray(#[serde(with = "BigArray")] [T; N]) -where - T: for<'a> Deserialize<'a> + Serialize; - -impl Default for LargeArray -where - T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, -{ - fn default() -> Self { - Self([T::default(); N]) - } -} - -impl TryFrom> for LargeArray -where - T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, -{ - type Error = LargeArrayError; - - fn try_from(vec: Vec) -> Result { - Ok(LargeArray(vec.try_into().map_err(|_| { - LargeArrayError::VectorError("Vector is the wrong size".to_string()) - })?)) - } -} - -impl TryFrom<[T; N]> for LargeArray -where - T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, -{ - type Error = LargeArrayError; - - fn try_from(array: [T; N]) -> Result { - Ok(LargeArray(array)) - } -} - -impl TryFrom<&[T]> for LargeArray -where - T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, -{ - type Error = LargeArrayError; - - fn try_from(slice: &[T]) -> Result { - Ok(LargeArray(slice.try_into()?)) - } -} - -impl LargeArray -where - T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, -{ - /// Get the large array as a regular array format - pub fn as_array(&self) -> [T; N] { - self.0 - } - - /// Get the large array as a slice - pub fn as_slice(&self) -> &[T; N] { - &self.0 - } - - /// Get the large array as a mutable slice - pub fn as_mut_slice(&mut self) -> &mut [T; N] { - &mut self.0 - } -} diff --git a/src/util/mod.rs b/src/util/mod.rs index e2215142..1de7127e 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -2,10 +2,9 @@ //! Helpful primitives for developing the crate. +pub mod array; pub mod cached_chain; mod impl_const_id; -pub mod large_array; -pub(crate) mod sealed; use std::{ io::{Read, Result, Write}, @@ -13,19 +12,6 @@ use std::{ slice::{from_raw_parts, from_raw_parts_mut}, }; -#[cfg(any(feature = "sev", feature = "snp"))] -pub fn hexdump(bytes: &[u8]) -> String { - let mut retval: String = String::new(); - for (i, byte) in bytes.iter().enumerate() { - if (i % 16) == 0 { - retval.push('\n'); - } - retval.push_str(&format!("{byte:02x} ")); - } - retval.push('\n'); - retval -} - pub trait TypeLoad: Read { fn load(&mut self) -> Result { #[allow(clippy::uninit_assumed_init)] diff --git a/src/util/sealed.rs b/src/util/sealed.rs deleted file mode 100644 index 8b7c0ce2..00000000 --- a/src/util/sealed.rs +++ /dev/null @@ -1,4 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -//! Trait to use to seal other traits in crate. -pub trait Sealed {} diff --git a/src/vmsa/mod.rs b/src/vmsa/mod.rs index 7469dab3..99d6d15d 100644 --- a/src/vmsa/mod.rs +++ b/src/vmsa/mod.rs @@ -4,12 +4,16 @@ #![allow(dead_code)] -use super::*; +use codicon::{Decoder, Encoder}; -use std::{fs, io, mem::size_of}; +use crate::util::array::Array; -use codicon::{Decoder, Encoder}; -use serde_big_array::BigArray; +use super::{ + util::{TypeLoad, TypeSave}, + *, +}; + +use std::{fs, io, mem::size_of}; const ATTR_G_SHIFT: usize = 23; const ATTR_B_SHIFT: usize = 22; @@ -66,7 +70,7 @@ pub struct VmcbSegment { /// The layout of a VMCB struct is documented in Table B-4 of the /// AMD64 Architecture Programmer’s Manual, Volume 2: System Programming #[repr(C, packed)] -#[derive(Copy, Clone, Serialize, Deserialize)] +#[derive(Default, Copy, Clone, Serialize, Deserialize)] pub struct Vmsa { /// Extra segment. es: VmcbSegment, @@ -99,8 +103,7 @@ pub struct Vmsa { tr: VmcbSegment, /// Reserved. - #[serde(with = "BigArray")] - reserved_1: [u8; 43], + reserved_1: Array, /// Current privilege level. cpl: u8, @@ -112,8 +115,7 @@ pub struct Vmsa { efer: u64, /// Reserved. - #[serde(with = "BigArray")] - reserved_3: [u8; 104], + reserved_3: Array, /// Bitmap of supervisor-level state components. System software sets bits /// in the XSS register bitmap to enable management of corresponding state @@ -143,8 +145,7 @@ pub struct Vmsa { rip: u64, /// Reserved. - #[serde(with = "BigArray")] - reserved_4: [u8; 88], + reserved_4: Array, /// Stack pointer. rsp: u64, @@ -210,8 +211,7 @@ pub struct Vmsa { last_excp_to: u64, /// Reserved. - #[serde(with = "BigArray")] - reserved_7: [u8; 72], + reserved_7: Array, /// Speculation Control of MSRs. Documented in Section 3.2.9 of the /// AMD64 Architecture Programmer’s Manual, Volume 2: System Programming @@ -292,8 +292,7 @@ pub struct Vmsa { sw_scratch: u64, /// Reserved. - #[serde(with = "BigArray")] - reserved_11: [u8; 56], + reserved_11: Array, /// XCR0 register. xcr0: u64, @@ -472,9 +471,3 @@ impl Vmsa { Ok(()) } } - -impl Default for Vmsa { - fn default() -> Self { - unsafe { std::mem::zeroed() } - } -} diff --git a/tests/api.rs b/tests/api.rs index f7700833..58bd40fd 100644 --- a/tests/api.rs +++ b/tests/api.rs @@ -1,13 +1,15 @@ // SPDX-License-Identifier: Apache-2.0 #[cfg(all(feature = "sev", target_os = "linux"))] - mod sev { #[cfg(feature = "dangerous_hw_tests")] use serial_test::serial; #[cfg(feature = "dangerous_hw_tests")] use sev::cached_chain; - use sev::{certs::sev::sev::Usage, firmware::host::Firmware, Build, Version}; + use sev::{ + certs::sev::sev::Usage, + firmware::host::{Build, Firmware, Version}, + }; #[cfg(feature = "dangerous_hw_tests")] #[cfg_attr(not(host), ignore)] @@ -147,8 +149,8 @@ mod snp { reported tcb tee version: {} reported tcb bootloader version: {} state: {}", - status.version.major, - status.version.minor, + status.version.0, + status.version.1, status.build_id, status.guest_count, status.platform_tcb_version.microcode, diff --git a/tests/certs.rs b/tests/certs.rs index e3046352..202dc14c 100644 --- a/tests/certs.rs +++ b/tests/certs.rs @@ -24,7 +24,6 @@ mod sev { #[cfg(all(feature = "snp", any(feature = "openssl", feature = "crypto_nossl")))] mod snp { - use std::convert::TryFrom; use sev::certs::snp::{builtin::milan, ca, Certificate, Chain, Verifiable}; @@ -87,8 +86,7 @@ mod snp { let chain = Chain { ca, vek: vcek }; let report_bytes = hex::decode(TEST_MILAN_ATTESTATION_REPORT).unwrap(); - - let report = AttestationReport::try_from(report_bytes.as_slice()).unwrap(); + let report: AttestationReport = AttestationReport::from_bytes(&report_bytes).unwrap(); assert_eq!((&chain, &report).verify().ok(), Some(())); } @@ -107,7 +105,7 @@ mod snp { let mut report_bytes = hex::decode(TEST_MILAN_ATTESTATION_REPORT).unwrap(); report_bytes[21] ^= 0x80; - let report = AttestationReport::try_from(report_bytes.as_slice()).unwrap(); + let report = AttestationReport::from_bytes(&report_bytes).unwrap(); assert_eq!((&chain, &report).verify().ok(), None); }