mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
rust/pvimg: Add a Secure Execution header library and an example how to use
The new Rust port of genprotimg will have the name 'pvimg' as it has
more functionalities than the original genprotimg tool. As preparation
add a Secure Execution header library and an example how to use it. The
example can be used by the KVM-Unit-Tests for creating the Secure
Execution headers needed by the tests [1].
[1] See dc4f7106f3
more information how to use.
Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
committed by
Jan Höppner
parent
d73f4dc22a
commit
2ed2c608ab
406
rust/Cargo.lock
generated
406
rust/Cargo.lock
generated
@@ -13,24 +13,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.3.2"
|
||||
version = "0.6.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
|
||||
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is-terminal",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.0"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
@@ -52,12 +52,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "1.0.1"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
|
||||
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -78,6 +78,21 @@ version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -90,6 +105,18 @@ version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium",
|
||||
"tap",
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
@@ -110,24 +137,22 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.3.4"
|
||||
version = "4.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80672091db20273a15cf9fdd4e47ed43b5091ec9841bf4c6145c9dfbbcae09ed"
|
||||
checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.3.4"
|
||||
version = "4.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1458a1df40e1e2afebb7ab60ce55c1fa8f431146205aa5f4887e0b111c27636"
|
||||
checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"bitflags 1.3.2",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
"terminal_size",
|
||||
@@ -135,18 +160,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.3.1"
|
||||
version = "4.5.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6b5c519bab3ea61843a7923d074b04245624bb84a64a8c150f5deb014e388b"
|
||||
checksum = "e16bb32eaa759f7fe76c59793c4e00dad6d11705f74ddfe4887e62f459536f16"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.3.2"
|
||||
version = "4.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f"
|
||||
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -156,9 +181,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.5.0"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
|
||||
checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
@@ -211,6 +236,66 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deku"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9711031e209dc1306d66985363b4397d4c7b911597580340b93c9729b55f6eb"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"deku_derive",
|
||||
"no_std_io2",
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deku_derive"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58cb0719583cbe4e81fb40434ace2f0d22ccc3e39a74bb3796c22b451b4f139d"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum_dispatch"
|
||||
version = "0.3.13"
|
||||
@@ -223,6 +308,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.1"
|
||||
@@ -244,6 +335,18 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
@@ -259,6 +362,12 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.10"
|
||||
@@ -277,16 +386,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.1"
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
@@ -295,31 +410,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.11"
|
||||
name = "indexmap"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
|
||||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.48.0",
|
||||
"equivalent",
|
||||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.7"
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
@@ -339,6 +447,12 @@ version = "0.2.146"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.9"
|
||||
@@ -353,9 +467,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.3.8"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@@ -369,6 +483,25 @@ version = "2.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||
|
||||
[[package]]
|
||||
name = "no_std_io2"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a3564ce7035b1e4778d8cb6cacebb5d766b5e8fe5a75b9e441e33fb61a872c6"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
@@ -431,6 +564,15 @@ version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
|
||||
dependencies = [
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.75"
|
||||
@@ -440,6 +582,26 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proptest"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bit-vec",
|
||||
"bitflags 2.4.1",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"rand_chacha",
|
||||
"rand_xorshift",
|
||||
"regex-syntax",
|
||||
"rusty-fork",
|
||||
"tempfile",
|
||||
"unarray",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pvapconfig"
|
||||
version = "0.10.0"
|
||||
@@ -476,6 +638,26 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pvimg"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"deku",
|
||||
"deku_derive",
|
||||
"enum_dispatch",
|
||||
"log",
|
||||
"openssl",
|
||||
"proptest",
|
||||
"s390_pv",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pvsecret"
|
||||
version = "0.10.0"
|
||||
@@ -489,6 +671,12 @@ dependencies = [
|
||||
"utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
@@ -498,6 +686,12 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
@@ -528,6 +722,24 @@ dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.2"
|
||||
@@ -559,18 +771,35 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.37.27"
|
||||
version = "0.38.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2"
|
||||
checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bitflags 2.4.1",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
||||
|
||||
[[package]]
|
||||
name = "rusty-fork"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"quick-error",
|
||||
"tempfile",
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.13"
|
||||
@@ -664,7 +893,7 @@ version = "0.9.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"indexmap 1.9.3",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
@@ -683,9 +912,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
@@ -699,10 +928,29 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.2.6"
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall",
|
||||
"rustix",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"windows-sys 0.48.0",
|
||||
@@ -728,6 +976,29 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||
dependencies = [
|
||||
"indexmap 2.6.0",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unarray"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.9"
|
||||
@@ -763,6 +1034,15 @@ version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
@@ -980,6 +1260,24 @@ version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||
dependencies = [
|
||||
"tap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.32"
|
||||
|
||||
@@ -5,6 +5,7 @@ members = [
|
||||
"pv_core",
|
||||
"pvapconfig",
|
||||
"pvattest",
|
||||
"pvimg",
|
||||
"pvsecret",
|
||||
"utils",
|
||||
]
|
||||
|
||||
@@ -28,7 +28,6 @@ pub type Aes256GcmKey = Confidential<[u8; SymKeyType::AES_256_GCM_KEY_LEN]>;
|
||||
pub type Aes256XtsKey = Confidential<[u8; SymKeyType::AES_256_XTS_KEY_LEN]>;
|
||||
|
||||
/// SHA-512 digest length (in bytes)
|
||||
#[allow(unused)]
|
||||
pub const SHA_512_HASH_LEN: usize = 64;
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
||||
@@ -90,7 +90,7 @@ pub mod request {
|
||||
crypto::{
|
||||
decrypt_aead, derive_aes256_gcm_key, encrypt_aead, gen_ec_key, random_array,
|
||||
AeadDecryptionResult, AeadEncryptionResult, Aes256GcmKey, Aes256XtsKey, SymKey,
|
||||
SymKeyType,
|
||||
SymKeyType, SHA_512_HASH_LEN,
|
||||
},
|
||||
req::{EcPubKeyCoord, Encrypt, Keyslot, ReqEncrCtx, Request},
|
||||
verify::{CertVerifier, HkdVerifier, NoVerifyHkd},
|
||||
|
||||
36
rust/pvimg/Cargo.toml
Normal file
36
rust/pvimg/Cargo.toml
Normal file
@@ -0,0 +1,36 @@
|
||||
[package]
|
||||
name = "pvimg"
|
||||
version = "0.10.0"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0.70", features = ["std"] }
|
||||
clap = { version ="4.1", features = ["derive", "wrap_help"]}
|
||||
deku = "0.18"
|
||||
deku_derive = "0.18"
|
||||
enum_dispatch = "0.3.13"
|
||||
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] }
|
||||
openssl = "0.10.66"
|
||||
serde = { version = "1.0.139", features = ["derive"]}
|
||||
serde_json = "1.0"
|
||||
thiserror = "1.0.33"
|
||||
|
||||
pv = { path = "../pv", package = "s390_pv" }
|
||||
utils = { path = "../utils" }
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = { version = "1.0.70", features = ["std"] }
|
||||
clap = { version ="4.1", features = ["derive", "wrap_help"]}
|
||||
clap_complete = "4.5"
|
||||
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] }
|
||||
|
||||
pv = { path = "../pv", package = "s390_pv" }
|
||||
utils = { path = "../utils" }
|
||||
|
||||
[dev-dependencies]
|
||||
proptest = "1.3"
|
||||
260
rust/pvimg/examples/create-sehdr/main.rs
Normal file
260
rust/pvimg/examples/create-sehdr/main.rs
Normal file
@@ -0,0 +1,260 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
#![allow(missing_docs)]
|
||||
use std::{
|
||||
fmt::Display,
|
||||
fs::{File, OpenOptions},
|
||||
io::{BufReader, Read, Write},
|
||||
path::PathBuf,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Context, Error};
|
||||
use clap::{Parser, ValueHint};
|
||||
use log::{info, warn};
|
||||
use pv::{
|
||||
misc::{decode_hex, open_file, read_certs, read_file, try_parse_u64},
|
||||
request::SymKeyType,
|
||||
Error as PvError, Result,
|
||||
};
|
||||
use pvimg::{
|
||||
misc::PSW,
|
||||
secured_comp::{ComponentTrait, Layout, SecuredComponentBuilder},
|
||||
uvdata::{BuilderTrait, SeHdrBuilder, SeHdrVersion},
|
||||
};
|
||||
use utils::{AtomicFile, AtomicFileOperation, HexSlice, PvLogger, VerbosityOptions};
|
||||
|
||||
/// Converts the hexstring into a byte vector.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Raises an error if a non-hex character was found or the length was not a
|
||||
/// multiple of two.
|
||||
pub fn decode_hex_str<S: AsRef<str>>(s: S) -> Result<Vec<u8>> {
|
||||
let hex_str = s.as_ref();
|
||||
let hex_value = if hex_str.starts_with("0x") {
|
||||
hex_str.split_at(2).1
|
||||
} else {
|
||||
hex_str
|
||||
};
|
||||
|
||||
Ok(decode_hex(hex_value)?)
|
||||
}
|
||||
|
||||
fn decode_u64_hex_str(s: &str) -> Result<u64> {
|
||||
Ok(try_parse_u64(s, "The")?)
|
||||
}
|
||||
|
||||
impl FromStr for ComponentArg {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let parts: Vec<_> = s.split(',').collect();
|
||||
if parts.len() != 3 {
|
||||
return Err(anyhow!("Invalid component format."));
|
||||
}
|
||||
let path = parts[0].into();
|
||||
let addr = try_parse_u64(parts[1], "Invalid address")?;
|
||||
let mut tweak =
|
||||
decode_hex_str(parts[2]).with_context(|| format!("Invalid tweak {}", parts[2]))?;
|
||||
if tweak.len() > SymKeyType::AES_256_XTS_TWEAK_LEN {
|
||||
return Err(anyhow!(
|
||||
"Invalid tweak because the length of {} is greater than the expected {}.",
|
||||
tweak.len(),
|
||||
SymKeyType::AES_256_XTS_TWEAK_LEN
|
||||
));
|
||||
}
|
||||
tweak.resize(SymKeyType::AES_256_XTS_TWEAK_LEN, 0x0);
|
||||
Ok(Self { path, addr, tweak })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct ComponentArg {
|
||||
path: PathBuf,
|
||||
addr: u64,
|
||||
tweak: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Display for ComponentArg {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"component\n\
|
||||
Path ......: {:}\n\
|
||||
Address ...: {:#0x}\n\
|
||||
Tweak .....: {:#}",
|
||||
self.path.display(),
|
||||
self.addr,
|
||||
HexSlice::from(&self.tweak),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a Secure Execution header.
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct Args {
|
||||
/// Use FILE as the component, ADDR as the component address, and TWEAK as
|
||||
/// the component tweak.
|
||||
///
|
||||
/// ADDR and TWEAK must be a hex-string. TWEAK is right padded with zero
|
||||
/// bytes if the given tweak is not large enough. Can be specified multiple
|
||||
/// times and must be used at least once.
|
||||
#[arg(short, long = "component", required = true, value_name = "FILE,ADDR,TWEAK", value_hint = ValueHint::FilePath)]
|
||||
components: Vec<ComponentArg>,
|
||||
|
||||
/// Use FILE as a host key document.
|
||||
///
|
||||
/// Can be specified multiple times and must be used at least once.
|
||||
#[arg(short = 'k', long = "host-key", required = true)]
|
||||
pub host_key_documents: Vec<PathBuf>,
|
||||
|
||||
/// Plain control flags. Must be a hex value.
|
||||
#[arg(long, default_value = "0x10000000")]
|
||||
pub pcf: String,
|
||||
|
||||
/// Secret control flags. Must be a hex value.
|
||||
#[arg(long, default_value = "0x0")]
|
||||
pub scf: String,
|
||||
|
||||
/// PSW address. Must be a hex value.
|
||||
#[arg(long, default_value = "0x10000", value_parser=decode_u64_hex_str)]
|
||||
pub psw_addr: u64,
|
||||
|
||||
/// PSW mask. Must be a hex value.
|
||||
#[arg(long, default_value = "0x0000000180000000", value_parser=decode_u64_hex_str)]
|
||||
pub psw_mask: u64,
|
||||
|
||||
/// Customer communication key (CCK) file path.
|
||||
#[arg(long)]
|
||||
pub cck: Option<PathBuf>,
|
||||
|
||||
/// Secure Execution header output location.
|
||||
#[arg(short, long)]
|
||||
pub output: PathBuf,
|
||||
|
||||
#[clap(flatten)]
|
||||
pub verbosity: VerbosityOptions,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Comp {
|
||||
pub reader: BufReader<File>,
|
||||
}
|
||||
|
||||
impl Read for Comp {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
self.reader.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
enum CompType {
|
||||
Dummy = 1,
|
||||
}
|
||||
|
||||
impl ComponentTrait<CompType> for Comp {
|
||||
fn secure_mode(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn kind(&self) -> CompType {
|
||||
CompType::Dummy
|
||||
}
|
||||
}
|
||||
|
||||
static LOGGER: PvLogger = PvLogger;
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let mut args = Args::parse();
|
||||
let log_level = args.verbosity.to_level_filter();
|
||||
LOGGER
|
||||
.start(log_level)
|
||||
.with_context(|| "Failed to set-up logger")?;
|
||||
|
||||
info!("# Preparing components");
|
||||
let mut layout = Layout::new(0x0, SecuredComponentBuilder::COMPONENT_ALIGNMENT_V1)?;
|
||||
|
||||
// Don't store the prepared components anywhere as we're only interested in
|
||||
// the hashes.
|
||||
let mut writer = std::io::empty();
|
||||
let mut secure_comp_builer = SecuredComponentBuilder::new_v1(false)?;
|
||||
|
||||
// Sort components by address in ascending order
|
||||
args.components.sort_by(|a, b| a.addr.cmp(&b.addr));
|
||||
for component_arg in args.components {
|
||||
info!("## Preparing {}", component_arg);
|
||||
let mut comp = Comp {
|
||||
reader: BufReader::new(open_file(&component_arg.path)?),
|
||||
};
|
||||
|
||||
let comp_addr = component_arg.addr;
|
||||
let _ = secure_comp_builer
|
||||
.prepare_and_insert_as_secure_component(
|
||||
&mut writer,
|
||||
&mut layout,
|
||||
&mut comp,
|
||||
comp_addr,
|
||||
component_arg.tweak,
|
||||
)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Failed to prepare component '{}'",
|
||||
component_arg.path.display()
|
||||
)
|
||||
})?;
|
||||
}
|
||||
|
||||
info!("\n# Creating Secure Execution Header");
|
||||
let addr = args.psw_addr;
|
||||
let mask = args.psw_mask;
|
||||
let mut builder = SeHdrBuilder::new(
|
||||
SeHdrVersion::V1,
|
||||
PSW { addr, mask },
|
||||
secure_comp_builer.finish()?,
|
||||
)?;
|
||||
let mut target_pub_keys = vec![];
|
||||
for hkd_path in args.host_key_documents {
|
||||
info!(
|
||||
"Use the file '{}' as a host key document",
|
||||
hkd_path.display()
|
||||
);
|
||||
let hkd_data = read_file(&hkd_path, "host key document")?;
|
||||
let certs = read_certs(&hkd_data)?;
|
||||
if certs.is_empty() {
|
||||
return Err(PvError::NoHkdInFile(hkd_path.display().to_string()).into());
|
||||
}
|
||||
|
||||
if certs.len() > 1 {
|
||||
warn!("The host key document in '{}' contains more than one certificate! All keys will be used.",
|
||||
hkd_path.display());
|
||||
}
|
||||
|
||||
for cert in &certs {
|
||||
target_pub_keys.push(cert.public_key()?);
|
||||
}
|
||||
}
|
||||
builder.add_hostkeys(&target_pub_keys)?;
|
||||
|
||||
let pcf = try_parse_u64(&args.pcf, "pcf")?.into();
|
||||
let scf = try_parse_u64(&args.scf, "scf")?.into();
|
||||
info!(
|
||||
"PSW addr ............: {addr:#018x}\n\
|
||||
PSW mask ............: {mask:#018x}\n\
|
||||
PCF .................: {pcf}\n\
|
||||
SCF .................: {scf}"
|
||||
);
|
||||
builder.with_pcf(&pcf)?;
|
||||
builder.with_scf(&scf)?;
|
||||
if let Some(cck) = args.cck {
|
||||
info!("CCK ................: {}", cck.display());
|
||||
builder
|
||||
.with_cck(read_file(&cck, "CCK")?.into())
|
||||
.with_context(|| format!("Invalid CCK in '{}'", &cck.display()))?;
|
||||
}
|
||||
|
||||
let mut output = AtomicFile::new(args.output, &mut OpenOptions::new())?;
|
||||
output.write_all(&builder.build()?.as_bytes()?)?;
|
||||
Ok(output.finish(AtomicFileOperation::Replace)?)
|
||||
}
|
||||
53
rust/pvimg/src/lib.rs
Normal file
53
rust/pvimg/src/lib.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
//! # Library for Secure Execution headers
|
||||
//!
|
||||
//! This crate provides functionalities for creating and inspecting Secure
|
||||
//! Execution headers. It also provides support for preparing arbitrary
|
||||
//! components as secured components and calculating the PLD, ALD, and TLD of
|
||||
//! them.
|
||||
//!
|
||||
//! ## Secure Execution headers
|
||||
//! ### Creation
|
||||
//!
|
||||
//! [`uvdata::SeHdrBuilder`]
|
||||
//!
|
||||
//! ### Serialization and Deserialization
|
||||
//!
|
||||
//! [`uvdata::SeHdr`]
|
||||
//!
|
||||
//! ## Secured components
|
||||
//!
|
||||
//! [`secured_comp::SecuredComponentBuilder`] and
|
||||
//! [`secured_comp::SecuredComponent`].
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
mod pv_utils;
|
||||
|
||||
pub mod misc {
|
||||
pub const PAGESIZE: usize = 4096;
|
||||
pub use crate::pv_utils::{
|
||||
bytesize, round_up, serialize_to_bytes, ShortPsw, PSW, PSW_MASK_BA, PSW_MASK_EA,
|
||||
};
|
||||
}
|
||||
|
||||
pub mod uvdata {
|
||||
pub use crate::pv_utils::{
|
||||
BuilderTrait, ComponentMetadataV1, ControlFlagTrait, ControlFlagsTrait, FlagData,
|
||||
KeyExchangeTrait, PcfV1, PlaintextControlFlagsV1, ScfV1, SeHdr, SeHdrBuilder, SeHdrDataV1,
|
||||
SeHdrVersion, SecretControlFlagsV1, UvDataTrait, UvKeyHashesV1,
|
||||
};
|
||||
}
|
||||
|
||||
pub mod secured_comp {
|
||||
pub use crate::pv_utils::{
|
||||
ComponentTrait, Interval, Layout, SecuredComponent, SecuredComponentBuilder,
|
||||
};
|
||||
}
|
||||
|
||||
pub mod error {
|
||||
pub use crate::pv_utils::{Error, OwnExitCode, PvError, Result};
|
||||
}
|
||||
29
rust/pvimg/src/pv_utils.rs
Normal file
29
rust/pvimg/src/pv_utils.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
mod error;
|
||||
mod layout;
|
||||
mod misc;
|
||||
mod psw;
|
||||
mod se_hdr;
|
||||
mod secured_comp;
|
||||
mod serializing;
|
||||
mod uv_keys;
|
||||
mod uvdata;
|
||||
mod uvdata_builder;
|
||||
|
||||
pub use error::{Error, OwnExitCode, PvError, Result};
|
||||
pub use layout::{Interval, Layout};
|
||||
pub use misc::{round_up, try_copy_slice_to_array};
|
||||
pub use psw::{ShortPsw, PSW, PSW_MASK_BA, PSW_MASK_EA};
|
||||
pub use se_hdr::{
|
||||
ComponentMetadataV1, ControlFlagTrait, ControlFlagsTrait, FlagData, PcfV1,
|
||||
PlaintextControlFlagsV1, ScfV1, SeHdr, SeHdrBuilder, SeHdrDataV1, SeHdrVersion,
|
||||
SecretControlFlagsV1,
|
||||
};
|
||||
pub use secured_comp::{ComponentTrait, SecuredComponent, SecuredComponentBuilder};
|
||||
pub use serializing::{bytesize, serialize_to_bytes};
|
||||
pub use uv_keys::UvKeyHashesV1;
|
||||
pub use uvdata::{KeyExchangeTrait, UvDataTrait};
|
||||
pub use uvdata_builder::BuilderTrait;
|
||||
171
rust/pvimg/src/pv_utils/error.rs
Normal file
171
rust/pvimg/src/pv_utils/error.rs
Normal file
@@ -0,0 +1,171 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
pub use pv::Error as PvError;
|
||||
pub use pv::PvCoreError;
|
||||
use utils::{impl_exitcodetrait, ExitCodeTrait};
|
||||
|
||||
/// Result type for this crate
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
/// Error cases for this crate
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
#[error("First image component was already prepared")]
|
||||
FirstComponentAlreadyPrepared,
|
||||
|
||||
#[error(
|
||||
"Stage3b is already added so there is no possibility to add another secured component"
|
||||
)]
|
||||
ImgAlreadyFinalized,
|
||||
|
||||
#[error("Invalid target key hash")]
|
||||
InvalidTargetKeyHash,
|
||||
|
||||
#[error("Invalid UV key hashes")]
|
||||
InvalidUvKeyHashes,
|
||||
|
||||
#[error("Invalid Secure Execution header")]
|
||||
InvalidSeHdr,
|
||||
|
||||
#[error("Invalid component metadata.")]
|
||||
InvalidComponentMetadata,
|
||||
|
||||
#[error("Invalid alignment {alignment} as it's larger than the chunk size {chunk_size}.")]
|
||||
InvalidAlignment { alignment: u64, chunk_size: usize },
|
||||
|
||||
#[error("Invalid interval: Start {start} is larger than {stop}")]
|
||||
InvalidInterval { start: u64, stop: u64 },
|
||||
|
||||
#[error(
|
||||
"The given tweak size {given} is smaller than the expected tweak size, which is {expected}"
|
||||
)]
|
||||
InvalidTweakSize { given: usize, expected: usize },
|
||||
|
||||
#[error("Invalid customer communication key (CCK)")]
|
||||
InvalidCCK { source: Box<Error> },
|
||||
|
||||
#[error("Invalid stage3a")]
|
||||
InvalidStage3a,
|
||||
|
||||
#[error("Invalid stage3b")]
|
||||
InvalidStage3b,
|
||||
|
||||
#[error("Interval overlaps: {0}")]
|
||||
IntervalOverlap(String),
|
||||
|
||||
#[error("Image already finalized")]
|
||||
ImageAlreadyFinalized,
|
||||
|
||||
#[error("Provided kernel cmdline is too large: {size} > {max_size}")]
|
||||
KernelCmdlineTooLarge { size: usize, max_size: usize },
|
||||
|
||||
#[error("Cannot convert to short PSW")]
|
||||
TryToShortPSWError,
|
||||
|
||||
#[error("Address {addr:#0x} is not aligned to {alignment:#0x}")]
|
||||
UnalignedAddress { addr: u64, alignment: u64 },
|
||||
|
||||
#[error("Support for query UV host key hashes is not available")]
|
||||
UnavailableQueryUvKeyHashesSupport { source: PvCoreError },
|
||||
|
||||
#[error("ELF file found, but only raw binary kernels are supported.")]
|
||||
UnexpectedElfFile,
|
||||
|
||||
#[error("Unexpected arithmetic overflow")]
|
||||
UnexpectedOverflow,
|
||||
|
||||
#[error("Unexpected key type. Given {given}, expected {expected}")]
|
||||
UnexpectedKeyType { given: String, expected: String },
|
||||
|
||||
#[error("Unsupported message digest")]
|
||||
UnsupportMessageDigest,
|
||||
|
||||
#[error("Unexpected arithmetic underflow")]
|
||||
UnexpectedUnderflow,
|
||||
|
||||
#[error(
|
||||
"Address {addr:#0x} is smaller than the next possible address, which is {next_addr:#0x}"
|
||||
)]
|
||||
NonMonotonicallyIncreasing { addr: u64, next_addr: u64 },
|
||||
|
||||
#[error("No host key document provided")]
|
||||
NoHostkey,
|
||||
|
||||
#[error("No s390x Linux kernel provided")]
|
||||
NoS390Kernel,
|
||||
|
||||
#[error("Expert mode is not enabled")]
|
||||
NonExpertMode,
|
||||
|
||||
#[error("Tweaks can be specified in expert mode only")]
|
||||
NonExpertModeTweakGiven,
|
||||
|
||||
#[error("No plaintext control flag")]
|
||||
NoPlainTextControlFlag,
|
||||
|
||||
#[error("No secret control flag")]
|
||||
NoSecretControlFlag,
|
||||
|
||||
#[error("No Secure Execution header found.")]
|
||||
NoSeHdrFound,
|
||||
|
||||
#[error("Address {addr} is already used")]
|
||||
NoUnusedAddr { addr: u64 },
|
||||
|
||||
#[error("Prepared component is too large for the given location: {output_size} > {max_output_size}")]
|
||||
PreparedComponentTooLarge {
|
||||
output_size: usize,
|
||||
max_output_size: usize,
|
||||
},
|
||||
|
||||
// Errors from other crates
|
||||
#[error(transparent)]
|
||||
Deku(#[from] deku::DekuError),
|
||||
|
||||
#[error(transparent)]
|
||||
Crypto(#[from] openssl::error::ErrorStack),
|
||||
|
||||
#[error(transparent)]
|
||||
Pv(#[from] PvError),
|
||||
|
||||
#[error(transparent)]
|
||||
PvCore(#[from] PvCoreError),
|
||||
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
TryFromIntError(#[from] std::num::TryFromIntError),
|
||||
}
|
||||
|
||||
impl_exitcodetrait!(
|
||||
#[repr(u8)]
|
||||
#[derive(Debug)]
|
||||
pub enum OwnExitCode {
|
||||
/// Program finished successfully
|
||||
///
|
||||
/// The command was executed successfully.
|
||||
Success = 0,
|
||||
|
||||
/// Generic error
|
||||
///
|
||||
/// Something went wrong during the operation. Refer to the error
|
||||
/// message.
|
||||
GenericError = 1,
|
||||
|
||||
/// Usage error
|
||||
///
|
||||
/// The command was used incorrectly, for example: unsupported command
|
||||
/// line flag, or wrong number of arguments.
|
||||
UsageError = 2, // same exit code as used by `Clap` crate
|
||||
}
|
||||
);
|
||||
|
||||
impl From<OwnExitCode> for std::process::ExitCode {
|
||||
fn from(value: OwnExitCode) -> Self {
|
||||
Self::from(value as u8)
|
||||
}
|
||||
}
|
||||
400
rust/pvimg/src/pv_utils/layout.rs
Normal file
400
rust/pvimg/src/pv_utils/layout.rs
Normal file
@@ -0,0 +1,400 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
use std::{collections::BTreeSet, fmt::Display, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
misc::round_up,
|
||||
pv_utils::error::{Error, Result},
|
||||
};
|
||||
|
||||
/// Represents a range from [start, stop) (inclusive start, exclusive stop)
|
||||
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Interval {
|
||||
pub start: u64,
|
||||
pub stop: u64,
|
||||
}
|
||||
|
||||
impl Display for Interval {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
format!("start: {:#10x} stop: {:#10x}", self.start, self.stop - 1).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Interval {
|
||||
/// Create a new [`Interval`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if `stop` is not larger than `start`.
|
||||
const fn new(start: u64, stop: u64) -> Result<Self> {
|
||||
if stop <= start {
|
||||
return Err(Error::InvalidInterval { start, stop });
|
||||
}
|
||||
Ok(Self { start, stop })
|
||||
}
|
||||
|
||||
/// Creates a new [`Interval`] with the start address `start` and the size
|
||||
/// of `size`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if `size == 0` or if there was an
|
||||
/// unexpected overflow.
|
||||
pub fn new_with_size(start: u64, size: u64) -> Result<Self> {
|
||||
Self::new(
|
||||
start,
|
||||
start.checked_add(size).ok_or(Error::UnexpectedOverflow)?,
|
||||
)
|
||||
}
|
||||
|
||||
const fn contains(&self, addr: u64) -> bool {
|
||||
addr >= self.start && addr < self.stop
|
||||
}
|
||||
|
||||
/// Returns the size of this [`Interval`].
|
||||
pub const fn size(&self) -> u64 {
|
||||
self.stop - self.start
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Layout {
|
||||
pub next_addr: u64,
|
||||
pub alignment: u64,
|
||||
chunks: BTreeSet<Rc<Interval>>,
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
/// Creates a new [`Layout`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if `start_addr` is not aligned.
|
||||
pub fn new(start_addr: u64, alignment: u64) -> Result<Self> {
|
||||
if start_addr != round_up(start_addr, alignment)? {
|
||||
return Err(Error::UnalignedAddress {
|
||||
addr: start_addr,
|
||||
alignment,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
next_addr: start_addr,
|
||||
alignment,
|
||||
chunks: BTreeSet::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn is_aligned(&self, addr: u64) -> Result<bool> {
|
||||
Ok(addr == round_up(addr, self.alignment)?)
|
||||
}
|
||||
|
||||
fn overlaps(&self, b: &Interval) -> Option<Rc<Interval>> {
|
||||
for a in self.chunks.iter() {
|
||||
if a.start < b.stop && b.start < a.stop {
|
||||
return Some(a.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the maximum chunk size at the given address `addr`. If there is
|
||||
/// no limit `None` is returned.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the address is in use already.
|
||||
pub fn max_size_of_chunk_at_addr(&self, addr: u64) -> Result<Option<usize>> {
|
||||
if !self.is_aligned(addr)? {
|
||||
return Err(Error::UnalignedAddress {
|
||||
addr,
|
||||
alignment: self.alignment,
|
||||
});
|
||||
}
|
||||
|
||||
if addr >= self.next_addr {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
for chunk in &self.chunks {
|
||||
if chunk.contains(addr) {
|
||||
return Err(Error::NoUnusedAddr { addr });
|
||||
}
|
||||
|
||||
if chunk.start >= addr {
|
||||
let max_size = usize::try_from(chunk.start - addr).unwrap();
|
||||
return Ok(Some(max_size));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Insert an interval in the layout.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the given address was unaligned or
|
||||
/// the interval would overlap with an existing interval in the layout.
|
||||
pub fn insert_interval(&mut self, addr: u64, size: u64) -> Result<Rc<Interval>> {
|
||||
let interval = Interval::new_with_size(addr, size)?;
|
||||
|
||||
assert!(self.next_addr % self.alignment == 0);
|
||||
|
||||
if interval.start != round_up(interval.start, self.alignment)? {
|
||||
return Err(Error::UnalignedAddress {
|
||||
addr: interval.start,
|
||||
alignment: self.alignment,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(overlapped) = self.overlaps(&interval) {
|
||||
let msg = format!("{} ... {}", overlapped, interval);
|
||||
return Err(Error::IntervalOverlap(msg));
|
||||
}
|
||||
|
||||
let interval = Rc::new(interval);
|
||||
self.chunks.insert(interval.clone());
|
||||
let maybe_next_addr = interval
|
||||
.start
|
||||
.checked_add(round_up(size, self.alignment)?)
|
||||
.ok_or(Error::UnexpectedOverflow)?;
|
||||
if maybe_next_addr > self.next_addr {
|
||||
self.next_addr = maybe_next_addr;
|
||||
}
|
||||
|
||||
assert!(self.next_addr % self.alignment == 0);
|
||||
|
||||
Ok(interval)
|
||||
}
|
||||
|
||||
/// Creates and appends this newly created interval with size `size` to the
|
||||
/// layout. Returns the created interval.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if it was not possible to append the
|
||||
/// newly created interval.
|
||||
pub fn push(&mut self, size: u64) -> Result<Rc<Interval>> {
|
||||
let addr = self.next_addr;
|
||||
self.insert_interval(addr, size)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Layout {
|
||||
type IntoIter = <BTreeSet<Rc<Interval>> as IntoIterator>::IntoIter;
|
||||
type Item = Rc<Interval>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.chunks.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::shadow_unrelated)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{collections::BTreeSet, rc::Rc};
|
||||
|
||||
use proptest::{
|
||||
prelude::{Just, Strategy},
|
||||
prop_assert, prop_assert_eq, proptest,
|
||||
};
|
||||
|
||||
use crate::pv_utils::{Interval, Layout};
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn interval_new(
|
||||
(a,b) in (0..u64::MAX).prop_flat_map(|a| (Just(a), 0..a))
|
||||
) {
|
||||
prop_assert!(b < a);
|
||||
Interval::new(b, a).expect("should not fail");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interval_new_with_size(
|
||||
(start, size) in (0..u64::MAX).prop_flat_map(|a| (Just(a), 1..=u64::MAX - a))
|
||||
) {
|
||||
Interval::new_with_size(start, size).expect("should not fail");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interval_contains(
|
||||
(start, size, c) in (0_u16..4_u16).prop_flat_map(|a| (Just(a), 1_u16..4_u16)).prop_flat_map(|(a,b)| (Just(a), Just(b), a..(a + b)))
|
||||
) {
|
||||
let interval = Interval::new_with_size(start.into(), size.into()).expect("should not fail");
|
||||
prop_assert!(interval.contains(c.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interval_cmp(
|
||||
(start, size, start2) in (1..16_u64).prop_flat_map(|a| (Just(a), 1..16_u16)).prop_flat_map(|(a,b)| (Just(a), Just(b), 0..a))
|
||||
) {
|
||||
let interval = Interval::new_with_size(start, size.into()).expect("should not fail");
|
||||
let interval2 = Interval::new_with_size(start2, size.into()).expect("should not fail");
|
||||
let interval3 = Interval::new_with_size(start, <u16 as Into<u64>>::into(size) + 1).expect("should not fail");
|
||||
let interval4 = Interval::new_with_size(start, size.into()).expect("should not fail");
|
||||
prop_assert!(interval > interval2);
|
||||
prop_assert!(interval != interval2);
|
||||
prop_assert!(interval < interval3);
|
||||
prop_assert!(interval == interval4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interval_size((start, size) in (0..u64::MAX).prop_flat_map(|a| (Just(a), 1..=u64::MAX - a))
|
||||
)
|
||||
{
|
||||
let interval = Interval::new_with_size(start, size).expect("should not fail");
|
||||
prop_assert_eq!(interval.size(), size);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interval_overflow() {
|
||||
Interval::new_with_size(u64::MAX, 1).expect_err("should fail");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn memory_layout_test() {
|
||||
// Unaligned start address
|
||||
let layout = Layout::new(0x1_u64, 0x1000_u64);
|
||||
assert!(layout.is_err());
|
||||
|
||||
let mut layout = Layout::new(0x1000_u64, 0x1000_u64).unwrap();
|
||||
assert_eq!(
|
||||
layout,
|
||||
Layout {
|
||||
next_addr: 0x1000,
|
||||
alignment: 0x1000,
|
||||
chunks: BTreeSet::new(),
|
||||
}
|
||||
);
|
||||
layout.push(0x16).unwrap();
|
||||
layout.push(0x1000).unwrap();
|
||||
layout.push(0x1).unwrap();
|
||||
let mut bin = BTreeSet::from([
|
||||
Rc::new(Interval::new_with_size(0x1000, 0x16).expect("should not fail")),
|
||||
Rc::new(Interval::new_with_size(0x2000, 0x1000).expect("should not fail")),
|
||||
Rc::new(Interval::new_with_size(0x3000, 0x1).expect("should not fail")),
|
||||
]);
|
||||
assert_eq!(
|
||||
layout,
|
||||
Layout {
|
||||
next_addr: 0x4000,
|
||||
alignment: 0x1000,
|
||||
chunks: bin.clone()
|
||||
}
|
||||
);
|
||||
|
||||
// Invalid chunk size
|
||||
assert!(layout.push(0x0).is_err());
|
||||
|
||||
// NonMonolithic address
|
||||
assert!(layout.insert_interval(0x0, 0x1001).is_err());
|
||||
assert!(layout.insert_interval(0x0, 0x1000).is_ok());
|
||||
assert!(layout.insert_interval(0x10, 0x1000).is_err());
|
||||
|
||||
bin.insert(Rc::new(
|
||||
Interval::new_with_size(0x0, 0x1000).expect("should not fail"),
|
||||
));
|
||||
assert_eq!(
|
||||
layout,
|
||||
Layout {
|
||||
next_addr: 0x4000,
|
||||
alignment: 0x1000,
|
||||
chunks: bin.clone()
|
||||
}
|
||||
);
|
||||
|
||||
assert!(layout.insert_interval(0x3000, 0x400).is_err());
|
||||
assert!(layout.insert_interval(0x4000, 0x400).is_ok());
|
||||
|
||||
bin.insert(Rc::new(
|
||||
Interval::new_with_size(0x4000, 0x400).expect("should not fail"),
|
||||
));
|
||||
assert_eq!(
|
||||
layout,
|
||||
Layout {
|
||||
next_addr: 0x5000,
|
||||
alignment: 0x1000,
|
||||
chunks: bin
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max_interval_size_at_addr() {
|
||||
let mut layout = Layout::new(0x0_u64, 0x1000_u64).expect("should not fail");
|
||||
assert_eq!(
|
||||
layout,
|
||||
Layout {
|
||||
next_addr: 0x0,
|
||||
alignment: 0x1000,
|
||||
chunks: BTreeSet::new(),
|
||||
}
|
||||
);
|
||||
layout.push(0x16).unwrap();
|
||||
layout.push(0x1000).unwrap();
|
||||
layout.push(0x1).unwrap();
|
||||
layout.push(0x0).expect_err("should fail");
|
||||
let bin = BTreeSet::from([
|
||||
Rc::new(Interval::new_with_size(0x0, 0x16).expect("should not fail")),
|
||||
Rc::new(Interval::new_with_size(0x1000, 0x1000).expect("should not fail")),
|
||||
Rc::new(Interval::new_with_size(0x2000, 0x1).expect("should not fail")),
|
||||
]);
|
||||
assert_eq!(
|
||||
layout,
|
||||
Layout {
|
||||
next_addr: 0x3000,
|
||||
alignment: 0x1000,
|
||||
chunks: bin,
|
||||
}
|
||||
);
|
||||
|
||||
layout
|
||||
.max_size_of_chunk_at_addr(0x5)
|
||||
.expect_err("should fail");
|
||||
layout
|
||||
.max_size_of_chunk_at_addr(0x2000)
|
||||
.expect_err("should fail");
|
||||
layout
|
||||
.max_size_of_chunk_at_addr(0x2fff)
|
||||
.expect_err("should fail");
|
||||
assert_eq!(
|
||||
layout
|
||||
.max_size_of_chunk_at_addr(0x3000)
|
||||
.expect("should not fail"),
|
||||
None
|
||||
);
|
||||
|
||||
layout.alignment = 1;
|
||||
assert_eq!(
|
||||
layout
|
||||
.max_size_of_chunk_at_addr(0x16)
|
||||
.expect("should not fail"),
|
||||
Some(0x1000 - 0x16)
|
||||
);
|
||||
assert_eq!(
|
||||
layout
|
||||
.max_size_of_chunk_at_addr(0xfff)
|
||||
.expect("should not fail"),
|
||||
Some(0x1)
|
||||
);
|
||||
layout
|
||||
.max_size_of_chunk_at_addr(0x1000)
|
||||
.expect_err("should not fail");
|
||||
assert_eq!(
|
||||
layout
|
||||
.max_size_of_chunk_at_addr(0x2fff)
|
||||
.expect("should not fail"),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
layout
|
||||
.max_size_of_chunk_at_addr(0x3000)
|
||||
.expect("should not fail"),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
74
rust/pvimg/src/pv_utils/misc.rs
Normal file
74
rust/pvimg/src/pv_utils/misc.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use pv::PvCoreError;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
/// Rounds up the given `value` to a multiple of `multiple`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if there was an unexpected arithmetic
|
||||
/// overflow.
|
||||
pub fn round_up(value: u64, multiple: u64) -> Result<u64> {
|
||||
assert!(multiple >= 1);
|
||||
Ok((value
|
||||
.checked_add(multiple)
|
||||
.ok_or(Error::UnexpectedOverflow)?
|
||||
- 1)
|
||||
& !(multiple - 1))
|
||||
}
|
||||
|
||||
/// Try to copy a slice to an array.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the length of the slice is not equal
|
||||
/// to the length of the destination array.
|
||||
pub fn try_copy_slice_to_array<const COUNT: usize, T: Copy + Default>(
|
||||
src: &[T],
|
||||
) -> Result<[T; COUNT]> {
|
||||
if COUNT != src.len() {
|
||||
return Err(Error::PvCore(PvCoreError::LengthMismatch {
|
||||
expected: COUNT,
|
||||
actual: src.len(),
|
||||
}));
|
||||
}
|
||||
|
||||
let mut result = [T::default(); COUNT];
|
||||
result.copy_from_slice(src);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[allow(clippy::shadow_unrelated)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pv::PvCoreError;
|
||||
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
pv_utils::try_copy_slice_to_array,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_try_copy_slice_to_array() {
|
||||
let data = vec![];
|
||||
let result: [u8; 0] = try_copy_slice_to_array(data.as_slice()).expect("should not fail");
|
||||
assert_eq!(data, result);
|
||||
|
||||
let data = vec![0x1_u8, 0x2_u8, 0x3_u8];
|
||||
let result: [u8; 3] = try_copy_slice_to_array(data.as_slice()).expect("should not fail");
|
||||
assert_eq!(data, result);
|
||||
|
||||
let result: Result<[u8; 4]> = try_copy_slice_to_array(data.as_slice());
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(Error::PvCore(PvCoreError::LengthMismatch {
|
||||
expected: 4,
|
||||
actual: 3
|
||||
}))
|
||||
));
|
||||
}
|
||||
}
|
||||
138
rust/pvimg/src/pv_utils/psw.rs
Normal file
138
rust/pvimg/src/pv_utils/psw.rs
Normal file
@@ -0,0 +1,138 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use deku::{ctx::Endian, DekuRead, DekuWrite};
|
||||
use pv::request::Zeroize;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::serializing::ser_lower_hex;
|
||||
use crate::pv_utils::error::Error;
|
||||
|
||||
pub const PSW32_ADDR_MASK: u64 = 0x000000007fffffff;
|
||||
pub const PSW_MASK_BA: u64 = 0x0000000080000000;
|
||||
pub const PSW_MASK_EA: u64 = 0x0000000100000000;
|
||||
pub const PSW_MASK_BIT_12: u64 = 0x08000000000000;
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
||||
pub struct PSW {
|
||||
#[serde(serialize_with = "ser_lower_hex")]
|
||||
pub mask: u64,
|
||||
#[serde(serialize_with = "ser_lower_hex")]
|
||||
pub addr: u64,
|
||||
}
|
||||
|
||||
impl Zeroize for PSW {
|
||||
fn zeroize(&mut self) {
|
||||
self.mask.zeroize();
|
||||
self.addr.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
||||
pub struct ShortPsw(u64);
|
||||
|
||||
impl From<ShortPsw> for PSW {
|
||||
fn from(value: ShortPsw) -> Self {
|
||||
let mask = value.0 & !PSW32_ADDR_MASK & !PSW_MASK_BIT_12;
|
||||
let addr = value.0 & PSW32_ADDR_MASK;
|
||||
Self { mask, addr }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PSW> for ShortPsw {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: PSW) -> Result<Self, Self::Error> {
|
||||
// test if PSW mask can be converted
|
||||
if value.mask & PSW32_ADDR_MASK != 0 {
|
||||
return Err(Error::TryToShortPSWError);
|
||||
}
|
||||
|
||||
// test for bit 12
|
||||
if value.mask & PSW_MASK_BIT_12 != 0 {
|
||||
return Err(Error::TryToShortPSWError);
|
||||
}
|
||||
|
||||
// test if PSW addr can be converted
|
||||
if value.addr & !PSW32_ADDR_MASK != 0 {
|
||||
return Err(Error::TryToShortPSWError);
|
||||
}
|
||||
|
||||
let mut short_psw = value.mask;
|
||||
// Set bit 12 to 1
|
||||
short_psw |= PSW_MASK_BIT_12;
|
||||
short_psw |= value.addr;
|
||||
Ok(Self(short_psw))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{ShortPsw, PSW};
|
||||
use crate::pv_utils::{error::Result, psw::PSW_MASK_BIT_12};
|
||||
|
||||
#[test]
|
||||
fn test_from_psw_to_short_psw_ok() {
|
||||
let psw = PSW {
|
||||
mask: 0x180000000,
|
||||
addr: 0x11000,
|
||||
};
|
||||
|
||||
let short_psw_res: Result<ShortPsw> = psw.try_into();
|
||||
assert!(short_psw_res.is_ok());
|
||||
let short_psw = short_psw_res.unwrap();
|
||||
|
||||
assert_eq!(short_psw, ShortPsw(0x8000180011000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_psw_to_short_psw_mask_bit12_is_set() {
|
||||
let psw = PSW {
|
||||
mask: PSW_MASK_BIT_12,
|
||||
addr: 0x11000,
|
||||
};
|
||||
|
||||
let short_psw_res: Result<ShortPsw> = psw.try_into();
|
||||
assert!(short_psw_res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_psw_to_short_psw_mask_too_large() {
|
||||
let psw = PSW {
|
||||
mask: 0x8000180011000,
|
||||
addr: 0x11000,
|
||||
};
|
||||
|
||||
let short_psw_res: Result<ShortPsw> = psw.try_into();
|
||||
assert!(short_psw_res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_psw_to_short_psw_addr_too_large() {
|
||||
let psw = PSW {
|
||||
mask: 0x180000000,
|
||||
addr: 0x8000180011000,
|
||||
};
|
||||
|
||||
let short_psw_res: Result<ShortPsw> = psw.try_into();
|
||||
assert!(short_psw_res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_psw_to_short_psw_and_vice_versa() {
|
||||
let psw = PSW {
|
||||
mask: 0x180000000,
|
||||
addr: 0x11000,
|
||||
};
|
||||
|
||||
let short_psw_res: Result<ShortPsw> = psw.clone().try_into();
|
||||
assert!(short_psw_res.is_ok());
|
||||
let short_psw = short_psw_res.unwrap();
|
||||
|
||||
let new_psw: PSW = short_psw.into();
|
||||
assert_eq!(new_psw, psw);
|
||||
}
|
||||
}
|
||||
19
rust/pvimg/src/pv_utils/se_hdr.rs
Normal file
19
rust/pvimg/src/pv_utils/se_hdr.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
mod brb;
|
||||
mod builder;
|
||||
mod flags;
|
||||
mod hdr_v1;
|
||||
mod keys;
|
||||
|
||||
pub use brb::{
|
||||
ComponentMetadata, ComponentMetadataV1, SeHdr, SeHdrDataV1, SeHdrPlain, SeHdrVersion,
|
||||
};
|
||||
pub use brb::{SeHdrBinV1, SeHdrData, SeHdrVersioned};
|
||||
pub use builder::SeHdrBuilder;
|
||||
pub use flags::{
|
||||
ControlFlagTrait, ControlFlagsTrait, FlagData, PcfV1, PlaintextControlFlagsV1, ScfV1,
|
||||
SecretControlFlagsV1,
|
||||
};
|
||||
368
rust/pvimg/src/pv_utils/se_hdr/brb.rs
Normal file
368
rust/pvimg/src/pv_utils/se_hdr/brb.rs
Normal file
@@ -0,0 +1,368 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
use std::{
|
||||
io::{Read, Seek, SeekFrom},
|
||||
mem::size_of,
|
||||
};
|
||||
|
||||
use deku::{ctx::Endian, prelude::*};
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use pv::{
|
||||
request::{
|
||||
openssl::pkey::{PKey, PKeyRef, Private, Public},
|
||||
seek_se_hdr_start, Aes256XtsKey, Confidential, SymKey, SymKeyType,
|
||||
},
|
||||
static_assert,
|
||||
};
|
||||
use serde::Serialize;
|
||||
|
||||
pub use super::hdr_v1::{SeHdrBinV1, SeHdrDataV1};
|
||||
use super::{PlaintextControlFlagsV1, SecretControlFlagsV1};
|
||||
use crate::{
|
||||
misc::PAGESIZE,
|
||||
pv_utils::{
|
||||
error::{Error, Result},
|
||||
serializing::{ser_hex, serialize_to_bytes},
|
||||
uvdata::{
|
||||
AeadCipherTrait, AeadDataTrait, AeadPlainDataTrait, KeyExchangeTrait, UvDataPlainTrait,
|
||||
UvDataTrait,
|
||||
},
|
||||
uvdata_builder::{AeadCipherBuilderTrait, KeyExchangeBuilderTrait},
|
||||
PSW,
|
||||
},
|
||||
};
|
||||
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(
|
||||
endian = "endian",
|
||||
id_type = "u32",
|
||||
ctx = "endian: Endian",
|
||||
ctx_default = "Endian::Big"
|
||||
)]
|
||||
pub enum SeHdrVersion {
|
||||
/// Secure Execution header v1
|
||||
V1 = 0x100,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
||||
pub struct SeHdrCommon {
|
||||
#[serde(serialize_with = "ser_hex")]
|
||||
pub magic: [u8; 8],
|
||||
pub version: SeHdrVersion,
|
||||
}
|
||||
static_assert!(::std::mem::size_of::<SeHdrCommon>() == 12);
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
||||
pub struct SeHdrCommonWithSize {
|
||||
pub magic: [u8; 8],
|
||||
pub version: SeHdrVersion,
|
||||
pub sehs: u32,
|
||||
}
|
||||
static_assert!(::std::mem::size_of::<SeHdrCommonWithSize>() == 16);
|
||||
|
||||
impl SeHdrCommon {
|
||||
/// Magic value for a SE-header (FIXME as soon as `concat_bytes!(b"IBMSecEx`") is stable)
|
||||
pub(crate) const MAGIC: &'static [u8; 8] = &[73, 66, 77, 83, 101, 99, 69, 120];
|
||||
|
||||
pub(crate) const fn new(version: SeHdrVersion) -> Self {
|
||||
Self {
|
||||
magic: *Self::MAGIC,
|
||||
version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(endian = "Endian::Big")]
|
||||
/// Secure Execution header structure
|
||||
pub struct SeHdr {
|
||||
/// Common Secure Execution header part
|
||||
#[serde(flatten)]
|
||||
pub(crate) common: SeHdrCommon,
|
||||
#[serde(flatten)]
|
||||
#[deku(ctx = "common.version")]
|
||||
pub(crate) data: SeHdrVersioned,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(endian = "Endian::Big")]
|
||||
/// Plain data Secure Execution header structure
|
||||
pub struct SeHdrPlain {
|
||||
#[serde(flatten)]
|
||||
pub(crate) common: SeHdrCommon,
|
||||
#[serde(flatten)]
|
||||
#[deku(ctx = "common.version")]
|
||||
pub(crate) data: SeHdrData,
|
||||
}
|
||||
|
||||
#[enum_dispatch(AeadCipherTrait, AeadDataTrait, KeyExchangeTrait)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[deku(ctx = "_endian: Endian, version: SeHdrVersion", id = "version")]
|
||||
pub enum SeHdrVersioned {
|
||||
#[deku(id = "SeHdrVersion::V1")]
|
||||
SeHdrBinV1(SeHdrBinV1),
|
||||
}
|
||||
|
||||
#[enum_dispatch(
|
||||
AeadCipherTrait,
|
||||
AeadPlainDataTrait,
|
||||
KeyExchangeTrait,
|
||||
KeyExchangeBuilderTrait
|
||||
)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DekuRead, DekuWrite, Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[deku(ctx = "_endian: Endian, version: SeHdrVersion", id = "version")]
|
||||
pub enum SeHdrData {
|
||||
#[deku(id = "SeHdrVersion::V1")]
|
||||
SeHdrDataV1(SeHdrDataV1),
|
||||
}
|
||||
|
||||
impl AeadCipherBuilderTrait for SeHdrData {
|
||||
fn set_iv(&mut self, iv: &[u8]) -> Result<()> {
|
||||
match self {
|
||||
Self::SeHdrDataV1(data) => data.set_iv(iv),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[enum_dispatch(SeHdrData)]
|
||||
pub trait SeHdrPubBuilderTrait {
|
||||
// Payload related methods
|
||||
fn set_components(&mut self, meta: ComponentMetadata) -> Result<()>;
|
||||
fn set_pcf(&mut self, pcf: &PlaintextControlFlagsV1) -> Result<()>;
|
||||
}
|
||||
|
||||
#[enum_dispatch(SeHdrData)]
|
||||
pub trait SeHdrConfBuilderTrait {
|
||||
fn generate_cck(&self) -> Result<SymKey>;
|
||||
fn set_cck(&mut self, cck: Confidential<Vec<u8>>) -> Result<()>;
|
||||
fn set_psw(&mut self, psw: &PSW);
|
||||
fn set_scf(&mut self, scf: &SecretControlFlagsV1) -> Result<()>;
|
||||
}
|
||||
|
||||
#[enum_dispatch(SeHdr)]
|
||||
#[allow(unused)]
|
||||
pub trait SeHdrTrait: UvDataTrait {}
|
||||
|
||||
#[enum_dispatch(SeHdr)]
|
||||
#[allow(unused)]
|
||||
pub trait SeHdrPlainTrait: UvDataPlainTrait {}
|
||||
|
||||
impl AeadCipherTrait for SeHdr {
|
||||
fn aead_key_type(&self) -> SymKeyType {
|
||||
self.data.aead_key_type()
|
||||
}
|
||||
|
||||
fn iv(&self) -> &[u8] {
|
||||
self.data.iv()
|
||||
}
|
||||
|
||||
fn aead_tag_size(&self) -> usize {
|
||||
self.data.aead_tag_size()
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadDataTrait for SeHdr {
|
||||
fn aad(&self) -> Vec<u8> {
|
||||
[serialize_to_bytes(&self.common).unwrap(), self.data.aad()].concat()
|
||||
}
|
||||
|
||||
fn data(&self) -> Vec<u8> {
|
||||
self.data.data()
|
||||
}
|
||||
|
||||
fn tag(&self) -> Vec<u8> {
|
||||
self.data.tag()
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyExchangeTrait for SeHdr {
|
||||
fn contains_hash<H: AsRef<[u8]>>(&self, hash: H) -> bool {
|
||||
self.data.contains_hash(hash)
|
||||
}
|
||||
|
||||
fn contains<K: AsRef<PKeyRef<Public>>>(&self, key: K) -> Result<bool> {
|
||||
self.data.contains(key)
|
||||
}
|
||||
|
||||
fn cust_pub_key(&mut self) -> Result<PKey<Public>> {
|
||||
self.data.cust_pub_key()
|
||||
}
|
||||
|
||||
fn key_type(&self) -> SymKeyType {
|
||||
self.aead_key_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl UvDataTrait for SeHdr {
|
||||
type P = SeHdrPlain;
|
||||
}
|
||||
|
||||
impl SeHdr {
|
||||
/// Seek to the start of the next Secure Execution header.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if no Secure Execution header was
|
||||
/// found or the IO operation has failed.
|
||||
pub fn seek_sehdr<R: Read + Seek>(reader: &mut R, addr: Option<u64>) -> Result<()> {
|
||||
if let Some(addr) = addr {
|
||||
reader.seek(SeekFrom::Start(addr))?;
|
||||
}
|
||||
if !seek_se_hdr_start(reader)? {
|
||||
return Err(Error::NoSeHdrFound);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Serializes the [`SeHdr`] to a byte vector.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the Secure Execution header could
|
||||
/// not be serialized.
|
||||
pub fn as_bytes(&self) -> Result<Vec<u8>> {
|
||||
serialize_to_bytes(self)
|
||||
}
|
||||
|
||||
/// Deserializes a Secure Execution header from an I/O stream.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if no Secure Execution header could
|
||||
/// be read, e.g. because no Secure Execution header was found.
|
||||
pub fn try_from_io<R>(mut reader: R) -> Result<Self>
|
||||
where
|
||||
R: Read,
|
||||
{
|
||||
let common_size = size_of::<SeHdrCommonWithSize>();
|
||||
let mut data = vec![0_u8; common_size];
|
||||
|
||||
reader.read_exact(&mut data)?;
|
||||
|
||||
let (_, common) = SeHdrCommonWithSize::from_bytes((&data, 0))?;
|
||||
if &common.magic != SeHdrCommon::MAGIC {
|
||||
return Err(Error::NoSeHdrFound);
|
||||
}
|
||||
let sehs = common.sehs.try_into()?;
|
||||
|
||||
// DoS attack prevention
|
||||
if sehs > 1024 * PAGESIZE {
|
||||
return Err(Error::InvalidSeHdr);
|
||||
}
|
||||
|
||||
data.resize(sehs, 0);
|
||||
reader.read_exact(&mut data[common_size..])?;
|
||||
Self::try_from_data(&data)
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadCipherBuilderTrait for SeHdrPlain {
|
||||
fn set_iv(&mut self, iv: &[u8]) -> Result<()> {
|
||||
self.data.set_iv(iv)
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyExchangeBuilderTrait for SeHdrPlain {
|
||||
fn add_keyslot(
|
||||
&mut self,
|
||||
hostkey: &PKeyRef<Public>,
|
||||
aead_key: &SymKey,
|
||||
priv_key: &PKeyRef<Private>,
|
||||
) -> Result<()> {
|
||||
self.data.add_keyslot(hostkey, aead_key, priv_key)
|
||||
}
|
||||
|
||||
fn clear_keyslots(&mut self) -> Result<()> {
|
||||
self.data.clear_keyslots()
|
||||
}
|
||||
|
||||
fn generate_private_key(&self) -> Result<PKey<Private>> {
|
||||
self.data.generate_private_key()
|
||||
}
|
||||
|
||||
fn set_cust_public_key(&mut self, key: &PKeyRef<Private>) -> Result<()> {
|
||||
self.data.set_cust_public_key(key)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ComponentMetadataV1 {
|
||||
pub ald: [u8; 64],
|
||||
pub pld: [u8; 64],
|
||||
pub tld: [u8; 64],
|
||||
pub nep: u64,
|
||||
pub key: Aes256XtsKey,
|
||||
}
|
||||
|
||||
/// The `enum_dispatch` macros needs at least one local trait to be implemented.
|
||||
#[allow(unused)]
|
||||
#[enum_dispatch]
|
||||
trait ComponentMetadataTrait {}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[enum_dispatch(ComponentMetadataTrait)]
|
||||
#[derive(Debug)]
|
||||
pub enum ComponentMetadata {
|
||||
ComponentMetadataV1(ComponentMetadataV1),
|
||||
}
|
||||
|
||||
impl KeyExchangeTrait for SeHdrPlain {
|
||||
fn contains<K: AsRef<PKeyRef<Public>>>(&self, key: K) -> Result<bool> {
|
||||
self.data.contains(key)
|
||||
}
|
||||
|
||||
fn cust_pub_key(&mut self) -> Result<PKey<Public>> {
|
||||
self.data.cust_pub_key()
|
||||
}
|
||||
|
||||
fn key_type(&self) -> SymKeyType {
|
||||
self.data.key_type()
|
||||
}
|
||||
|
||||
fn contains_hash<H: AsRef<[u8]>>(&self, hash: H) -> bool {
|
||||
self.data.contains_hash(hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl UvDataPlainTrait for SeHdrPlain {
|
||||
type C = SeHdr;
|
||||
}
|
||||
|
||||
impl AeadPlainDataTrait for SeHdrPlain {
|
||||
fn aad(&self) -> Vec<u8> {
|
||||
let data_aad = self.data.aad();
|
||||
|
||||
[serialize_to_bytes(&self.common).unwrap(), data_aad].concat()
|
||||
}
|
||||
|
||||
fn data(&self) -> Confidential<Vec<u8>> {
|
||||
self.data.data()
|
||||
}
|
||||
|
||||
fn tag(&self) -> Vec<u8> {
|
||||
self.data.tag()
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadCipherTrait for SeHdrPlain {
|
||||
fn aead_key_type(&self) -> SymKeyType {
|
||||
self.data.aead_key_type()
|
||||
}
|
||||
|
||||
fn iv(&self) -> &[u8] {
|
||||
self.data.iv()
|
||||
}
|
||||
|
||||
fn aead_tag_size(&self) -> usize {
|
||||
self.data.aead_tag_size()
|
||||
}
|
||||
}
|
||||
276
rust/pvimg/src/pv_utils/se_hdr/builder.rs
Normal file
276
rust/pvimg/src/pv_utils/se_hdr/builder.rs
Normal file
@@ -0,0 +1,276 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use pv::request::Confidential;
|
||||
|
||||
use super::{hdr_v1::SeHdrDataV1, SeHdr};
|
||||
use crate::pv_utils::{
|
||||
error::{Error, Result},
|
||||
se_hdr::{
|
||||
brb::{SeHdrCommon, SeHdrConfBuilderTrait, SeHdrData, SeHdrPubBuilderTrait},
|
||||
ComponentMetadata, SeHdrPlain, SeHdrVersion,
|
||||
},
|
||||
uvdata::UvDataPlainTrait,
|
||||
uvdata_builder::{
|
||||
AeadCipherBuilderTrait, BuilderTrait, KeyExchangeBuilderTrait, UvDataBuilder,
|
||||
},
|
||||
PlaintextControlFlagsV1, SecretControlFlagsV1, PSW,
|
||||
};
|
||||
|
||||
/// `SeHdrBuilder`
|
||||
pub type SeHdrBuilder<'a> = UvDataBuilder<'a, SeHdrPlain>;
|
||||
|
||||
impl SeHdrBuilder<'_> {
|
||||
pub fn new<M: Into<ComponentMetadata>>(
|
||||
version: SeHdrVersion,
|
||||
psw: PSW,
|
||||
components_meta: M,
|
||||
) -> Result<Self> {
|
||||
let (data, aead_key, priv_key) = match version {
|
||||
SeHdrVersion::V1 => {
|
||||
let mut data = SeHdrDataV1::new(
|
||||
psw,
|
||||
components_meta
|
||||
.into()
|
||||
.try_into()
|
||||
.map_err(|_| Error::InvalidComponentMetadata)?,
|
||||
)?;
|
||||
let aead_key = data.generate_aead_key()?;
|
||||
let priv_key = data.generate_private_key()?;
|
||||
data.set_cust_public_key(&priv_key)?;
|
||||
(SeHdrData::SeHdrDataV1(data), aead_key, priv_key)
|
||||
}
|
||||
};
|
||||
let common = SeHdrCommon::new(version);
|
||||
let hdr = SeHdrPlain { common, data };
|
||||
Ok(Self {
|
||||
plain_data: hdr,
|
||||
target_keys: Vec::new(),
|
||||
prot_key: aead_key,
|
||||
expert_mode: false,
|
||||
priv_key,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_cck(&mut self, cck: Confidential<Vec<u8>>) -> Result<&mut Self> {
|
||||
self.plain_data
|
||||
.data
|
||||
.set_cck(cck)
|
||||
.map_err(|err| Error::InvalidCCK {
|
||||
source: Box::new(err),
|
||||
})?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn with_components<M: Into<ComponentMetadata>>(&mut self, meta: M) -> Result<&mut Self> {
|
||||
self.plain_data.data.set_components(meta.into())?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn with_pcf(&mut self, flags: &PlaintextControlFlagsV1) -> Result<&mut Self> {
|
||||
self.plain_data.data.set_pcf(flags)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn with_scf(&mut self, flags: &SecretControlFlagsV1) -> Result<&mut Self> {
|
||||
self.plain_data.data.set_scf(flags)?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl BuilderTrait for SeHdrBuilder<'_> {
|
||||
type T = SeHdr;
|
||||
|
||||
fn build(self) -> Result<Self::T> {
|
||||
// At least one target key must be set
|
||||
if self.target_keys.is_empty() {
|
||||
return Err(Error::NoHostkey);
|
||||
}
|
||||
|
||||
self.plain_data.encrypt(&self.prot_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::Cursor;
|
||||
|
||||
use pv::{
|
||||
request::{Confidential, SymKeyType, SHA_512_HASH_LEN},
|
||||
test_utils::get_test_key_and_cert,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::pv_utils::{
|
||||
se_hdr::ComponentMetadataV1,
|
||||
uvdata::{AeadDataTrait, AeadPlainDataTrait},
|
||||
UvDataTrait,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn builder_test() {
|
||||
use pv::test_utils::get_test_key_and_cert;
|
||||
|
||||
let (cust_key, host_key) = get_test_key_and_cert();
|
||||
let host_keys = [host_key.public_key().unwrap()];
|
||||
let xts_key = Confidential::new([0x3; SymKeyType::AES_256_XTS_KEY_LEN]);
|
||||
let xts_key2 = Confidential::new([0x3; SymKeyType::AES_256_XTS_KEY_LEN]);
|
||||
let mut builder = SeHdrBuilder::new(
|
||||
SeHdrVersion::V1,
|
||||
PSW {
|
||||
addr: 1234,
|
||||
mask: 5678,
|
||||
},
|
||||
ComponentMetadata::ComponentMetadataV1(ComponentMetadataV1 {
|
||||
ald: [0x1; SHA_512_HASH_LEN],
|
||||
pld: [0x2; SHA_512_HASH_LEN],
|
||||
tld: [0x3; SHA_512_HASH_LEN],
|
||||
nep: 1,
|
||||
key: xts_key,
|
||||
}),
|
||||
)
|
||||
.expect("should not fail");
|
||||
|
||||
// builder.add_comp_data(addr, tweak, )?;
|
||||
builder
|
||||
.with_components(ComponentMetadataV1 {
|
||||
ald: [0x1; SHA_512_HASH_LEN],
|
||||
pld: [0x2; SHA_512_HASH_LEN],
|
||||
tld: [0x3; SHA_512_HASH_LEN],
|
||||
nep: 1,
|
||||
key: xts_key2,
|
||||
})
|
||||
.expect("should not fail");
|
||||
builder
|
||||
.with_priv_key(&cust_key)
|
||||
.expect_err("Error expected as expert mode is not enabled");
|
||||
|
||||
builder.expert_mode = true;
|
||||
builder.with_priv_key(&cust_key).expect("should not fail");
|
||||
|
||||
// Set CCK
|
||||
// Too large key
|
||||
builder
|
||||
.with_cck([49; SymKeyType::AES_256_GCM_KEY_LEN - 1].to_vec().into())
|
||||
.expect_err("should fail");
|
||||
// Too small key
|
||||
builder
|
||||
.with_cck([49; SymKeyType::AES_256_GCM_KEY_LEN + 1].to_vec().into())
|
||||
.expect_err("should fail");
|
||||
|
||||
builder
|
||||
.with_cck([49; SymKeyType::AES_256_GCM_KEY_LEN].to_vec().into())
|
||||
.expect("should not fail");
|
||||
|
||||
// Set protection key
|
||||
// Too large key
|
||||
builder
|
||||
.with_aead_key(Confidential::new([50; 33].into()))
|
||||
.expect_err("should fail");
|
||||
// Too small key
|
||||
builder
|
||||
.with_aead_key(Confidential::new([50; 31].into()))
|
||||
.expect_err("should fail");
|
||||
|
||||
builder
|
||||
.with_aead_key(Confidential::new([50; 32].into()))
|
||||
.expect("should not fail");
|
||||
|
||||
// Set IV
|
||||
// Too large IV
|
||||
builder.with_iv(&[51; 13]).expect_err("should fail");
|
||||
// Too small IV
|
||||
builder.with_iv(&[51; 11]).expect_err("should fail");
|
||||
|
||||
builder.with_iv(&[51; 12]).expect("should not fail");
|
||||
|
||||
builder.add_hostkeys(&host_keys).expect("should not fail");
|
||||
|
||||
let prot_key = builder.prot_key().clone();
|
||||
let bin = builder.build().expect("wuhu");
|
||||
assert_eq!(bin.common.version, SeHdrVersion::V1);
|
||||
assert_eq!(bin.as_bytes().expect("should not fail").len(), 640);
|
||||
assert_eq!(
|
||||
bin.as_bytes().expect("should not fail"),
|
||||
[
|
||||
73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0, 0, 2, 128, 51, 51, 51, 51, 51, 51,
|
||||
51, 51, 51, 51, 51, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
128, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 199, 93, 52, 249, 22, 82, 219, 69, 123, 11, 32, 156, 70, 164, 145,
|
||||
164, 78, 226, 177, 110, 35, 194, 216, 218, 241, 22, 103, 138, 98, 242, 76, 227, 50,
|
||||
197, 153, 95, 8, 69, 107, 102, 177, 109, 213, 90, 146, 197, 7, 241, 227, 26, 247,
|
||||
140, 100, 168, 46, 122, 84, 27, 21, 19, 80, 21, 242, 2, 134, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 64, 128, 88, 167, 241, 165, 195, 80, 151, 83, 58, 2, 169,
|
||||
56, 121, 231, 222, 103, 186, 40, 11, 206, 131, 101, 236, 148, 178, 185, 8, 245,
|
||||
137, 195, 169, 152, 216, 190, 30, 99, 7, 215, 74, 224, 26, 220, 70, 130, 95, 246,
|
||||
187, 111, 160, 92, 17, 71, 207, 226, 204, 244, 162, 79, 61, 131, 61, 218, 112, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 65, 92, 37,
|
||||
165, 209, 156, 56, 30, 97, 151, 51, 225, 193, 183, 251, 216, 139, 221, 28, 49, 216,
|
||||
130, 213, 173, 224, 75, 151, 4, 60, 80, 16, 240, 229, 82, 102, 228, 113, 137, 5,
|
||||
64, 29, 48, 138, 18, 148, 179, 136, 59, 221, 205, 98, 76, 41, 121, 59, 220, 160,
|
||||
12, 56, 212, 171, 77, 85, 253, 38, 196, 235, 112, 49, 183, 94, 171, 221, 120, 96,
|
||||
65, 149, 102, 55, 59, 180, 25, 143, 227, 222, 144, 3, 77, 240, 4, 217, 205, 199,
|
||||
175, 123, 1, 191, 76, 78, 99, 115, 131, 5, 160, 112, 142, 117, 125, 30, 239, 7, 51,
|
||||
239, 66, 173, 61, 243, 199, 20, 71, 115, 107, 113, 139, 68, 200, 219, 233, 84, 220,
|
||||
108, 242, 133, 71, 91, 154, 160, 171, 4, 32, 67, 90, 107, 216, 149, 141, 210, 20,
|
||||
125, 4, 39, 73, 163, 75, 1, 148, 78, 245, 135, 76, 68, 42, 164, 174, 185, 216, 29,
|
||||
60, 76, 28, 232, 191, 209, 218, 134, 184, 110, 154, 227, 144, 66, 213, 145, 93,
|
||||
157, 150, 61, 80, 69, 238, 6, 190, 191, 202, 172, 221, 159, 190, 62, 253, 67, 162,
|
||||
142, 245, 109, 23, 26, 102, 113, 101, 23, 64, 85, 249, 255, 250, 30, 28, 136, 135,
|
||||
187, 109, 118, 222
|
||||
]
|
||||
);
|
||||
|
||||
let decrypted = bin.decrypt(&prot_key).expect("BUG");
|
||||
assert_eq!(bin.common, decrypted.common);
|
||||
assert_eq!(bin.aad(), decrypted.aad());
|
||||
assert_ne!(&bin.data(), decrypted.data().value());
|
||||
let _decrypted_hdrv1: SeHdrDataV1 = decrypted.data.try_into().expect("BUG");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_test() {
|
||||
let (_, host_key) = get_test_key_and_cert();
|
||||
let host_keys = [host_key.public_key().unwrap()];
|
||||
let xts_key = Confidential::new([0x3; SymKeyType::AES_256_XTS_KEY_LEN]);
|
||||
let meta = ComponentMetadataV1 {
|
||||
ald: [0x1; SHA_512_HASH_LEN],
|
||||
pld: [0x2; SHA_512_HASH_LEN],
|
||||
tld: [0x3; SHA_512_HASH_LEN],
|
||||
nep: 3,
|
||||
key: xts_key,
|
||||
};
|
||||
let cck = Confidential::new([0x42; 32].to_vec());
|
||||
let mut builder = SeHdrBuilder::new(
|
||||
SeHdrVersion::V1,
|
||||
PSW {
|
||||
addr: 1234,
|
||||
mask: 5678,
|
||||
},
|
||||
meta,
|
||||
)
|
||||
.expect("should not fail");
|
||||
|
||||
let prot_key = builder.prot_key().to_owned();
|
||||
builder
|
||||
.add_hostkeys(&host_keys)
|
||||
.expect("should not fail")
|
||||
.with_cck(cck)
|
||||
.expect("should not fail");
|
||||
let bin = builder.build().expect("should not fail");
|
||||
|
||||
let reader = Cursor::new(bin.as_bytes().expect("should not fail"));
|
||||
let hdr = SeHdr::try_from_io(reader).unwrap();
|
||||
let hdr_plain = hdr.decrypt(&prot_key).unwrap();
|
||||
assert_eq!(hdr_plain.common.version, SeHdrVersion::V1);
|
||||
assert_eq!(hdr_plain.common.version, hdr.common.version);
|
||||
let _hdr_data_v1: SeHdrDataV1 = hdr_plain.data.try_into().expect("should not fail");
|
||||
}
|
||||
}
|
||||
0
rust/pvimg/src/pv_utils/se_hdr/components.rs
Normal file
0
rust/pvimg/src/pv_utils/se_hdr/components.rs
Normal file
308
rust/pvimg/src/pv_utils/se_hdr/flags.rs
Normal file
308
rust/pvimg/src/pv_utils/se_hdr/flags.rs
Normal file
@@ -0,0 +1,308 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use std::{fmt::Display, marker::PhantomData, mem::size_of};
|
||||
|
||||
use pv::misc::{Flags, Msb0Flags64};
|
||||
|
||||
pub trait ControlFlagTrait: std::fmt::Debug + std::hash::Hash + Copy + Eq + Ord {
|
||||
fn discriminant(&self) -> u8 {
|
||||
assert!(size_of::<Self>() == size_of::<u8>());
|
||||
unsafe { *(self as *const Self as *const u8) }
|
||||
}
|
||||
|
||||
fn enabled(self) -> FlagData<Self> {
|
||||
FlagData::new(self, FlagState::Enabled)
|
||||
}
|
||||
|
||||
fn disabled(self) -> FlagData<Self> {
|
||||
FlagData::new(self, FlagState::Disabled)
|
||||
}
|
||||
|
||||
fn all_enabled<F: AsRef<[Self]>>(flags: F) -> Vec<FlagData<Self>> {
|
||||
flags
|
||||
.as_ref()
|
||||
.iter()
|
||||
.map(|flag| (*flag).enabled())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn all_disabled<F: AsRef<[Self]>>(flags: F) -> Vec<FlagData<Self>> {
|
||||
flags
|
||||
.as_ref()
|
||||
.iter()
|
||||
.map(|flag| (*flag).disabled())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
enum FlagState {
|
||||
Enabled,
|
||||
Disabled,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
pub struct FlagData<T: ControlFlagTrait> {
|
||||
value: T,
|
||||
state: FlagState,
|
||||
}
|
||||
|
||||
impl<T: ControlFlagTrait> FlagData<T> {
|
||||
const fn new(value: T, state: FlagState) -> Self {
|
||||
Self { value, state }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ControlFlagsTrait: Display {
|
||||
type T: ControlFlagTrait;
|
||||
|
||||
fn from_flags<F: AsRef<[FlagData<Self::T>]>>(flags: F) -> Self;
|
||||
fn parse_flags<F: AsRef<[FlagData<Self::T>]>>(&mut self, flags: F);
|
||||
fn is_set(&self, flag: Self::T) -> bool;
|
||||
fn is_unset(&self, flag: Self::T) -> bool {
|
||||
!self.is_set(flag)
|
||||
}
|
||||
|
||||
fn no_duplicates<F: AsRef<[FlagData<Self::T>]>>(flags: F) -> bool {
|
||||
let mut flags_sorted = flags.as_ref().to_vec();
|
||||
flags_sorted.sort_by_key(|data| data.value);
|
||||
flags_sorted.dedup_by_key(|data| data.value);
|
||||
|
||||
flags_sorted.len() == flags.as_ref().len()
|
||||
}
|
||||
|
||||
fn all_set<F: AsRef<[Self::T]>>(&self, flags: F) -> bool {
|
||||
flags.as_ref().iter().all(|flag| self.is_set(*flag))
|
||||
}
|
||||
|
||||
fn all_unset<F: AsRef<[Self::T]>>(&self, flags: F) -> bool {
|
||||
flags.as_ref().iter().all(|flag| self.is_unset(*flag))
|
||||
}
|
||||
}
|
||||
|
||||
/// Bitflags as used by the Secure Execution in MSB0 ordering
|
||||
///
|
||||
/// Wraps an u64 to set/get individual bits
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ControlFlags<T: ControlFlagTrait> {
|
||||
flags: Msb0Flags64,
|
||||
t: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: ControlFlagTrait> ControlFlags<T> {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
flags: 0x0.into(),
|
||||
t: PhantomData {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ControlFlagTrait> From<u64> for ControlFlags<T> {
|
||||
fn from(value: u64) -> Self {
|
||||
Self {
|
||||
flags: value.into(),
|
||||
t: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ControlFlagTrait> From<&ControlFlags<T>> for u64 {
|
||||
fn from(value: &ControlFlags<T>) -> Self {
|
||||
value.flags.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ControlFlagTrait> From<ControlFlags<T>> for u64 {
|
||||
fn from(value: ControlFlags<T>) -> Self {
|
||||
value.flags.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ControlFlagTrait> ControlFlagsTrait for ControlFlags<T> {
|
||||
type T = T;
|
||||
|
||||
fn from_flags<F: AsRef<[FlagData<T>]>>(flags: F) -> Self {
|
||||
let mut ret = Self::new();
|
||||
ret.parse_flags(flags);
|
||||
ret
|
||||
}
|
||||
|
||||
fn parse_flags<F: AsRef<[FlagData<T>]>>(&mut self, flags: F) {
|
||||
flags.as_ref().iter().for_each(|v| match v.state {
|
||||
FlagState::Enabled => self.flags.set_bit(v.value.discriminant()),
|
||||
FlagState::Disabled => self.flags.unset_bit(v.value.discriminant()),
|
||||
});
|
||||
}
|
||||
|
||||
fn is_set(&self, flag: T) -> bool {
|
||||
self.flags.is_set(flag.discriminant())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ControlFlagTrait> Display for ControlFlags<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let value: u64 = self.flags.into();
|
||||
write!(f, "{:#018x}", value)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum PcfV1 {
|
||||
/// PV guest dump support.
|
||||
AllowDumping = 34,
|
||||
/// The components are not decrypted during the image unpack.
|
||||
NoComponentEncryption = 35,
|
||||
/// DEA/TDEA PCKMO encryption function are allowed.
|
||||
PckmoDeaTdea = 56,
|
||||
/// AES PCKMO encryption function are allowed.
|
||||
PckmoAes = 57,
|
||||
/// ECC PCKMO encryption function are allowed.
|
||||
PckmoEcc = 58,
|
||||
/// HMAC PCKMO encryption function are allowed.
|
||||
PckmoHmac = 59,
|
||||
/// Backup target keys can be used.
|
||||
BackupTargetKeys = 62,
|
||||
}
|
||||
pub type PlaintextControlFlagsV1 = ControlFlags<PcfV1>;
|
||||
|
||||
impl Default for PlaintextControlFlagsV1 {
|
||||
fn default() -> Self {
|
||||
Self::from_flags(PcfV1::all_enabled([
|
||||
PcfV1::PckmoAes,
|
||||
PcfV1::PckmoDeaTdea,
|
||||
PcfV1::PckmoEcc,
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PcfV1 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::AllowDumping => "allow dumping",
|
||||
Self::NoComponentEncryption => "no component encryption",
|
||||
Self::PckmoDeaTdea => "DEA and TDEA PCMKO",
|
||||
Self::PckmoAes => "AES",
|
||||
Self::PckmoEcc => "ECC PCKMO",
|
||||
Self::PckmoHmac => "HMAC PCKMO",
|
||||
Self::BackupTargetKeys => "backup target keys",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ControlFlagTrait for PcfV1 {}
|
||||
|
||||
#[repr(u8)]
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum ScfV1 {
|
||||
/// All add-secret requests must provide an extension secret
|
||||
CCKExtensionSecretEnforcment = 1,
|
||||
}
|
||||
pub type SecretControlFlagsV1 = ControlFlags<ScfV1>;
|
||||
impl ControlFlagTrait for ScfV1 {}
|
||||
|
||||
impl Default for SecretControlFlagsV1 {
|
||||
fn default() -> Self {
|
||||
Self::from_flags(ScfV1::all_enabled([]))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::shadow_unrelated)]
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::{ControlFlagTrait, ControlFlagsTrait, PcfV1, PlaintextControlFlagsV1};
|
||||
|
||||
#[test]
|
||||
fn test_from_flags() {
|
||||
let flags = PlaintextControlFlagsV1::from_flags(&[]);
|
||||
assert_eq!(u64::from(flags), 0_u64);
|
||||
|
||||
let flags = PlaintextControlFlagsV1::from_flags([PcfV1::AllowDumping.enabled()]);
|
||||
assert_eq!(u64::from(&flags), 536870912);
|
||||
assert!(flags.is_set(PcfV1::AllowDumping));
|
||||
|
||||
let flags = PlaintextControlFlagsV1::from_flags([
|
||||
PcfV1::AllowDumping.enabled(),
|
||||
PcfV1::AllowDumping.disabled(),
|
||||
]);
|
||||
assert_eq!(u64::from(flags), 0);
|
||||
|
||||
let flags = PlaintextControlFlagsV1::from_flags([
|
||||
PcfV1::AllowDumping.disabled(),
|
||||
PcfV1::AllowDumping.enabled(),
|
||||
]);
|
||||
assert_eq!(u64::from(&flags), 536870912);
|
||||
|
||||
let flags = PlaintextControlFlagsV1::from_flags([
|
||||
PcfV1::AllowDumping.enabled(),
|
||||
PcfV1::BackupTargetKeys.enabled(),
|
||||
]);
|
||||
assert_eq!(u64::from(&flags), 536870914);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_set_unset() {
|
||||
let flags = PlaintextControlFlagsV1::from_flags([
|
||||
PcfV1::AllowDumping.enabled(),
|
||||
PcfV1::BackupTargetKeys.enabled(),
|
||||
]);
|
||||
assert!(flags.all_set([PcfV1::AllowDumping, PcfV1::BackupTargetKeys]));
|
||||
assert!(!flags.all_set([PcfV1::NoComponentEncryption, PcfV1::BackupTargetKeys]));
|
||||
assert!(!flags.all_unset([PcfV1::NoComponentEncryption, PcfV1::BackupTargetKeys]));
|
||||
assert!(flags.all_unset([PcfV1::NoComponentEncryption, PcfV1::PckmoHmac]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_display() {
|
||||
let flags = PlaintextControlFlagsV1::from_flags([PcfV1::NoComponentEncryption.enabled()]);
|
||||
assert_eq!("0x0000000010000000", format!("{}", flags));
|
||||
|
||||
let flags = PlaintextControlFlagsV1::from_flags([
|
||||
PcfV1::AllowDumping.enabled(),
|
||||
PcfV1::BackupTargetKeys.enabled(),
|
||||
PcfV1::NoComponentEncryption.enabled(),
|
||||
PcfV1::PckmoAes.enabled(),
|
||||
PcfV1::PckmoDeaTdea.enabled(),
|
||||
PcfV1::PckmoEcc.enabled(),
|
||||
PcfV1::PckmoHmac.enabled(),
|
||||
]);
|
||||
assert_eq!("0x00000000300000f2", format!("{}", flags));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_duplicates() {
|
||||
let flags: Vec<_> = [
|
||||
PcfV1::all_disabled([PcfV1::PckmoAes, PcfV1::PckmoDeaTdea, PcfV1::PckmoEcc]),
|
||||
PcfV1::all_enabled([PcfV1::PckmoAes, PcfV1::PckmoDeaTdea, PcfV1::PckmoEcc]),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
assert!(!PlaintextControlFlagsV1::no_duplicates(flags));
|
||||
|
||||
let flags: Vec<_> = [
|
||||
PcfV1::all_disabled([PcfV1::PckmoAes]),
|
||||
PcfV1::all_enabled([PcfV1::PckmoDeaTdea, PcfV1::PckmoEcc]),
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
assert!(PlaintextControlFlagsV1::no_duplicates(flags));
|
||||
|
||||
let flags: Vec<_> =
|
||||
std::iter::once(PcfV1::all_disabled([PcfV1::PckmoAes, PcfV1::PckmoAes]))
|
||||
.flatten()
|
||||
.collect();
|
||||
assert!(!PlaintextControlFlagsV1::no_duplicates(flags));
|
||||
}
|
||||
}
|
||||
613
rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs
Normal file
613
rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs
Normal file
@@ -0,0 +1,613 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
use std::mem::{size_of, size_of_val};
|
||||
|
||||
use deku::{ctx::Endian, prelude::*};
|
||||
use openssl::{
|
||||
nid::Nid,
|
||||
pkey::{PKeyRef, Public},
|
||||
};
|
||||
use pv::request::{
|
||||
gen_ec_key,
|
||||
openssl::pkey::{PKey, Private},
|
||||
random_array, Aes256XtsKey, Confidential, EcPubKeyCoord, Encrypt, Keyslot, SymKey, SymKeyType,
|
||||
Zeroize, SHA_512_HASH_LEN,
|
||||
};
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
use super::keys::phkh_v1;
|
||||
use crate::{
|
||||
error::Error,
|
||||
pv_utils::{
|
||||
error::Result,
|
||||
se_hdr::{
|
||||
brb::{
|
||||
ComponentMetadata, ComponentMetadataV1, SeHdrCommon, SeHdrConfBuilderTrait,
|
||||
SeHdrPlainTrait, SeHdrPubBuilderTrait, SeHdrTrait,
|
||||
},
|
||||
keys::{BinaryKeySlotV1, EcPubKeyCoordV1},
|
||||
},
|
||||
serializing::{
|
||||
bytesize, bytesize_confidential, confidential_read_slice, confidential_write_slice,
|
||||
ser_hex, ser_hex_confidential, ser_lower_hex, serialize_to_bytes,
|
||||
},
|
||||
try_copy_slice_to_array,
|
||||
uvdata::{
|
||||
AeadCipherTrait, AeadDataTrait, AeadPlainDataTrait, KeyExchangeTrait, UvDataPlainTrait,
|
||||
UvDataTrait,
|
||||
},
|
||||
uvdata_builder::{AeadCipherBuilderTrait, KeyExchangeBuilderTrait},
|
||||
PlaintextControlFlagsV1, SecretControlFlagsV1, PSW,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct HdrSizesV1 {
|
||||
pub phs: u64,
|
||||
pub sea: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
||||
struct SeHdrAadV1 {
|
||||
sehs: u32,
|
||||
#[serde(serialize_with = "ser_hex")]
|
||||
iv: [u8; SymKeyType::AES_256_GCM_IV_LEN],
|
||||
res1: u32,
|
||||
nks: u64,
|
||||
sea: u64,
|
||||
nep: u64,
|
||||
#[serde(serialize_with = "ser_lower_hex")]
|
||||
pcf: u64,
|
||||
cust_pub_key: EcPubKeyCoordV1,
|
||||
#[serde(serialize_with = "ser_hex")]
|
||||
pld: [u8; SHA_512_HASH_LEN],
|
||||
#[serde(serialize_with = "ser_hex")]
|
||||
ald: [u8; SHA_512_HASH_LEN],
|
||||
#[serde(serialize_with = "ser_hex")]
|
||||
tld: [u8; SHA_512_HASH_LEN],
|
||||
#[deku(count = "nks")]
|
||||
keyslots: Vec<BinaryKeySlotV1>,
|
||||
}
|
||||
|
||||
impl SeHdrAadV1 {
|
||||
const KEY_TYPE: SymKeyType = SymKeyType::Aes256Gcm;
|
||||
}
|
||||
|
||||
impl KeyExchangeTrait for SeHdrAadV1 {
|
||||
fn contains<K: AsRef<PKeyRef<Public>>>(&self, key: K) -> Result<bool> {
|
||||
let phkh = phkh_v1(key)?;
|
||||
Ok(self.contains_hash(phkh))
|
||||
}
|
||||
|
||||
fn cust_pub_key(&mut self) -> Result<PKey<Public>> {
|
||||
self.cust_pub_key.clone().try_into()
|
||||
}
|
||||
|
||||
fn key_type(&self) -> SymKeyType {
|
||||
Self::KEY_TYPE
|
||||
}
|
||||
|
||||
fn contains_hash<H: AsRef<[u8]>>(&self, hash: H) -> bool {
|
||||
for slot in &self.keyslots {
|
||||
if hash.as_ref() != slot.phkh {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
||||
pub struct SeHdrConfV1 {
|
||||
#[serde(serialize_with = "ser_hex_confidential")]
|
||||
#[deku(
|
||||
reader = "confidential_read_slice(deku::reader, endian)",
|
||||
writer = "confidential_write_slice(cck, deku::writer, endian)"
|
||||
)]
|
||||
cck: Confidential<[u8; 32]>,
|
||||
#[serde(serialize_with = "ser_hex_confidential")]
|
||||
#[deku(
|
||||
reader = "confidential_read_slice(deku::reader, endian)",
|
||||
writer = "confidential_write_slice(xts, deku::writer, endian)"
|
||||
)]
|
||||
xts: Aes256XtsKey,
|
||||
psw: PSW,
|
||||
#[serde(serialize_with = "ser_lower_hex")]
|
||||
scf: u64,
|
||||
noi: u32,
|
||||
res2: u32,
|
||||
#[deku(count = "noi")]
|
||||
opt_items: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Zeroize for SeHdrConfV1 {
|
||||
fn zeroize(&mut self) {
|
||||
self.cck.zeroize();
|
||||
self.xts.zeroize();
|
||||
self.psw.zeroize();
|
||||
self.scf.zeroize();
|
||||
self.noi.zeroize();
|
||||
self.res2.zeroize();
|
||||
self.opt_items.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Debug, Clone, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
||||
pub struct SeHdrTagV1 {
|
||||
#[serde(serialize_with = "ser_hex")]
|
||||
tag: [u8; SymKeyType::AES_256_GCM_TAG_LEN],
|
||||
}
|
||||
|
||||
fn ser_confidential_confv1<S: Serializer>(
|
||||
encrypted: &Confidential<SeHdrConfV1>,
|
||||
ser: S,
|
||||
) -> std::result::Result<S::Ok, S::Error> {
|
||||
encrypted.value().serialize(ser)
|
||||
}
|
||||
|
||||
/// Secure Execution Header definition
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(endian = "big")]
|
||||
pub struct SeHdrDataV1 {
|
||||
#[serde(flatten)]
|
||||
aad: SeHdrAadV1,
|
||||
#[serde(flatten, serialize_with = "ser_confidential_confv1")]
|
||||
#[deku(
|
||||
reader = "confidential_read_sehdrconf_v1(deku::reader)",
|
||||
writer = "confidential_write_sehdrconf_v1(data, deku::writer)"
|
||||
)]
|
||||
data: Confidential<SeHdrConfV1>,
|
||||
#[serde(flatten)]
|
||||
tag: SeHdrTagV1,
|
||||
}
|
||||
|
||||
/// Reads from a `reader` and creates a confidential `SeHdrConfV1`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if there was an I/O error or the
|
||||
/// `SeHdrConfV1` could not be constructed.
|
||||
fn confidential_read_sehdrconf_v1<R>(
|
||||
reader: &mut Reader<R>,
|
||||
) -> Result<Confidential<SeHdrConfV1>, DekuError>
|
||||
where
|
||||
R: std::io::Read + std::io::Seek,
|
||||
{
|
||||
Ok(Confidential::new(SeHdrConfV1::from_reader_with_ctx(
|
||||
reader,
|
||||
(),
|
||||
)?))
|
||||
}
|
||||
|
||||
/// Writes a `Confidential<SeHdrConf1>` into this `writer`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if there was an I/O error.
|
||||
fn confidential_write_sehdrconf_v1<W>(
|
||||
value: &Confidential<SeHdrConfV1>,
|
||||
writer: &mut Writer<W>,
|
||||
) -> Result<(), DekuError>
|
||||
where
|
||||
W: std::io::Write + std::io::Seek,
|
||||
{
|
||||
value.value().to_writer(writer, ())
|
||||
}
|
||||
|
||||
impl SeHdrDataV1 {
|
||||
const PCF_DEFAULT: u64 = 0x0;
|
||||
const SCF_DEFAULT: u64 = 0x0;
|
||||
|
||||
/// Creates a new `SeHdrDataV1`. It initializes the CCK and IV with random
|
||||
/// data.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if there was not enough entropy to
|
||||
/// create the random data or another error has occurred.
|
||||
pub fn new(psw: PSW, components: ComponentMetadataV1) -> Result<Self> {
|
||||
// Safety: The CCK is also 32 bytes large.
|
||||
let cck = SymKey::random(SymKeyType::Aes256Gcm)?.try_into().unwrap();
|
||||
let mut ret = Self {
|
||||
aad: SeHdrAadV1 {
|
||||
sehs: 0,
|
||||
pcf: Self::PCF_DEFAULT,
|
||||
ald: components.ald,
|
||||
pld: components.pld,
|
||||
tld: components.tld,
|
||||
nep: components.nep,
|
||||
sea: 0,
|
||||
iv: random_array()?,
|
||||
res1: 0,
|
||||
nks: 0,
|
||||
cust_pub_key: EcPubKeyCoordV1 { coord: [0_u8; 160] },
|
||||
keyslots: vec![],
|
||||
},
|
||||
data: SeHdrConfV1 {
|
||||
cck,
|
||||
scf: Self::SCF_DEFAULT,
|
||||
psw,
|
||||
xts: components.key,
|
||||
noi: 0,
|
||||
res2: 0,
|
||||
opt_items: vec![],
|
||||
}
|
||||
.into(),
|
||||
tag: SeHdrTagV1::default(),
|
||||
};
|
||||
let hdr_size = ret.size()?;
|
||||
ret.aad.sehs = hdr_size.phs.try_into()?;
|
||||
ret.aad.sea = hdr_size.sea;
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn size(&self) -> Result<HdrSizesV1> {
|
||||
let sea = bytesize_confidential(&self.data)?;
|
||||
let mut phs = bytesize(&self.aad)?
|
||||
.checked_add(size_of::<SeHdrCommon>())
|
||||
.ok_or(Error::UnexpectedOverflow)?;
|
||||
phs = phs
|
||||
.checked_add(bytesize(&self.tag)?)
|
||||
.ok_or(Error::UnexpectedOverflow)?;
|
||||
phs = phs.checked_add(sea).ok_or(Error::UnexpectedOverflow)?;
|
||||
|
||||
Ok(HdrSizesV1 {
|
||||
sea: sea.try_into()?,
|
||||
phs: phs.try_into()?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the expected size of an constructed `SeHdrDataV1` with `n` key
|
||||
/// slots.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if there was an arithmetic overflow
|
||||
/// or.
|
||||
pub fn expected_size(nks: usize) -> Result<usize> {
|
||||
let cck = [0x0; 32].into();
|
||||
let hdr = Self {
|
||||
aad: SeHdrAadV1 {
|
||||
sehs: 0,
|
||||
pcf: Self::PCF_DEFAULT,
|
||||
ald: [0x0; SHA_512_HASH_LEN],
|
||||
pld: [0x0; SHA_512_HASH_LEN],
|
||||
tld: [0x0; SHA_512_HASH_LEN],
|
||||
nep: 0,
|
||||
sea: 0,
|
||||
iv: [0x0_u8; SymKeyType::AES_256_GCM_IV_LEN],
|
||||
res1: 0,
|
||||
nks: 0,
|
||||
cust_pub_key: EcPubKeyCoordV1 { coord: [0_u8; 160] },
|
||||
keyslots: vec![],
|
||||
},
|
||||
data: SeHdrConfV1 {
|
||||
cck,
|
||||
scf: Self::SCF_DEFAULT,
|
||||
psw: PSW { mask: 0, addr: 0 },
|
||||
xts: [0x0; SymKeyType::AES_256_XTS_KEY_LEN].into(),
|
||||
noi: 0,
|
||||
res2: 0,
|
||||
opt_items: vec![],
|
||||
}
|
||||
.into(),
|
||||
tag: SeHdrTagV1::default(),
|
||||
};
|
||||
let hdr_size: usize = hdr.size()?.phs.try_into().unwrap();
|
||||
|
||||
hdr_size
|
||||
.checked_add(
|
||||
size_of::<BinaryKeySlotV1>()
|
||||
.checked_mul(nks)
|
||||
.ok_or(Error::UnexpectedOverflow)?,
|
||||
)
|
||||
.ok_or(Error::UnexpectedOverflow)
|
||||
}
|
||||
}
|
||||
|
||||
impl UvDataPlainTrait for SeHdrDataV1 {
|
||||
type C = SeHdrBinV1;
|
||||
}
|
||||
impl SeHdrPlainTrait for SeHdrDataV1 {}
|
||||
|
||||
impl KeyExchangeBuilderTrait for SeHdrDataV1 {
|
||||
fn add_keyslot(
|
||||
&mut self,
|
||||
hostkey: &PKeyRef<Public>,
|
||||
aead_key: &SymKey,
|
||||
priv_key: &PKeyRef<Private>,
|
||||
) -> Result<()> {
|
||||
let keyslot = Keyslot::new(hostkey.to_owned());
|
||||
let keyslot_bin = keyslot.encrypt(aead_key.value(), priv_key)?.try_into()?;
|
||||
let keyslot_bin_size = u32::try_from(size_of_val(&keyslot_bin)).unwrap();
|
||||
self.aad.keyslots.push(keyslot_bin);
|
||||
self.aad.nks = self
|
||||
.aad
|
||||
.nks
|
||||
.checked_add(1)
|
||||
.ok_or(Error::UnexpectedOverflow)?;
|
||||
self.aad.sehs = self
|
||||
.aad
|
||||
.sehs
|
||||
.checked_add(keyslot_bin_size)
|
||||
.ok_or(Error::UnexpectedOverflow)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_private_key(&self) -> Result<PKey<Private>> {
|
||||
Ok(gen_ec_key(Nid::SECP521R1)?)
|
||||
}
|
||||
|
||||
fn set_cust_public_key(&mut self, key: &PKeyRef<Private>) -> Result<()> {
|
||||
self.aad.cust_pub_key = TryInto::<EcPubKeyCoord>::try_into(key)?.into();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clear_keyslots(&mut self) -> Result<()> {
|
||||
let old_nks: usize = self.aad.nks.try_into().unwrap();
|
||||
let keyslot_bin_size = size_of::<BinaryKeySlotV1>();
|
||||
self.aad.keyslots.clear();
|
||||
self.aad.nks = 0;
|
||||
self.aad.sehs -= u32::try_from(
|
||||
old_nks
|
||||
.checked_mul(keyslot_bin_size)
|
||||
.ok_or(Error::UnexpectedOverflow)?,
|
||||
)
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyExchangeTrait for SeHdrDataV1 {
|
||||
fn contains<K: AsRef<PKeyRef<Public>>>(&self, key: K) -> Result<bool> {
|
||||
self.aad.contains(key)
|
||||
}
|
||||
|
||||
fn cust_pub_key(&mut self) -> Result<PKey<Public>> {
|
||||
self.aad.cust_pub_key()
|
||||
}
|
||||
|
||||
fn key_type(&self) -> SymKeyType {
|
||||
self.aad.key_type()
|
||||
}
|
||||
|
||||
fn contains_hash<H: AsRef<[u8]>>(&self, hash: H) -> bool {
|
||||
self.aad.contains_hash(hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl SeHdrConfBuilderTrait for SeHdrDataV1 {
|
||||
fn set_psw(&mut self, psw: &PSW) {
|
||||
self.data.value_mut().psw = psw.clone();
|
||||
}
|
||||
|
||||
fn set_scf(&mut self, scf: &SecretControlFlagsV1) -> Result<()> {
|
||||
self.data.value_mut().scf = scf.into();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_cck(&mut self, cck: Confidential<Vec<u8>>) -> Result<()> {
|
||||
self.data.value_mut().cck = cck.try_into()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_cck(&self) -> Result<SymKey> {
|
||||
Ok(SymKey::random(SymKeyType::Aes256Gcm)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl SeHdrPubBuilderTrait for SeHdrDataV1 {
|
||||
fn set_pcf(&mut self, pcf: &PlaintextControlFlagsV1) -> Result<()> {
|
||||
self.aad.pcf = pcf.into();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_components(&mut self, meta: ComponentMetadata) -> Result<()> {
|
||||
let ComponentMetadataV1 {
|
||||
ald,
|
||||
pld,
|
||||
tld,
|
||||
nep,
|
||||
key,
|
||||
}: ComponentMetadataV1 = meta
|
||||
.try_into()
|
||||
.map_err(|_| Error::InvalidComponentMetadata)?;
|
||||
self.data.value_mut().xts = key;
|
||||
self.aad.ald = ald;
|
||||
self.aad.pld = pld;
|
||||
self.aad.tld = tld;
|
||||
self.aad.nep = nep;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(endian = "big")]
|
||||
pub struct SeHdrBinV1 {
|
||||
#[serde(flatten)]
|
||||
aad: SeHdrAadV1,
|
||||
#[serde(serialize_with = "ser_hex")]
|
||||
#[deku(bytes_read = "aad.sea")]
|
||||
pub data: Vec<u8>,
|
||||
#[serde(flatten)]
|
||||
pub tag: SeHdrTagV1,
|
||||
}
|
||||
|
||||
impl SeHdrBinV1 {
|
||||
pub fn new(d: &[u8]) -> Result<Self> {
|
||||
Self::try_from_data(d)
|
||||
}
|
||||
|
||||
pub(crate) fn try_from_data(data: &[u8]) -> Result<Self> {
|
||||
let (_rest, val) = Self::from_bytes((data, 0))?;
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl UvDataTrait for SeHdrBinV1 {
|
||||
type P = SeHdrDataV1;
|
||||
}
|
||||
impl SeHdrTrait for SeHdrBinV1 {}
|
||||
|
||||
impl AeadCipherTrait for SeHdrBinV1 {
|
||||
fn aead_key_type(&self) -> SymKeyType {
|
||||
self.key_type()
|
||||
}
|
||||
|
||||
fn iv(&self) -> &[u8] {
|
||||
&self.aad.iv
|
||||
}
|
||||
|
||||
fn aead_tag_size(&self) -> usize {
|
||||
SymKeyType::AES_256_GCM_TAG_LEN
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadCipherBuilderTrait for SeHdrDataV1 {
|
||||
fn set_iv(&mut self, iv: &[u8]) -> Result<()> {
|
||||
self.aad.iv = try_copy_slice_to_array(iv)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyExchangeTrait for SeHdrBinV1 {
|
||||
fn contains<K: AsRef<PKeyRef<Public>>>(&self, key: K) -> Result<bool> {
|
||||
self.aad.contains(key)
|
||||
}
|
||||
|
||||
fn cust_pub_key(&mut self) -> Result<PKey<Public>> {
|
||||
self.aad.cust_pub_key()
|
||||
}
|
||||
|
||||
fn key_type(&self) -> SymKeyType {
|
||||
self.aad.key_type()
|
||||
}
|
||||
|
||||
fn contains_hash<H: AsRef<[u8]>>(&self, hash: H) -> bool {
|
||||
self.aad.contains_hash(hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadDataTrait for SeHdrBinV1 {
|
||||
fn aad(&self) -> Vec<u8> {
|
||||
serialize_to_bytes(&self.aad).unwrap()
|
||||
}
|
||||
|
||||
fn data(&self) -> Vec<u8> {
|
||||
self.data.to_owned()
|
||||
}
|
||||
|
||||
fn tag(&self) -> Vec<u8> {
|
||||
serialize_to_bytes(&self.tag).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadPlainDataTrait for SeHdrDataV1 {
|
||||
fn aad(&self) -> Vec<u8> {
|
||||
serialize_to_bytes(&self.aad).unwrap()
|
||||
}
|
||||
|
||||
fn data(&self) -> Confidential<Vec<u8>> {
|
||||
serialize_to_bytes(self.data.value()).unwrap().into()
|
||||
}
|
||||
|
||||
fn tag(&self) -> Vec<u8> {
|
||||
serialize_to_bytes(&self.tag).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl AeadCipherTrait for SeHdrDataV1 {
|
||||
fn aead_key_type(&self) -> SymKeyType {
|
||||
self.aad.key_type()
|
||||
}
|
||||
|
||||
fn iv(&self) -> &[u8] {
|
||||
&self.aad.iv
|
||||
}
|
||||
|
||||
fn aead_tag_size(&self) -> usize {
|
||||
SymKeyType::AES_256_GCM_TAG_LEN
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use std::io::Cursor;
|
||||
|
||||
use pv::test_utils::get_test_key_and_cert;
|
||||
|
||||
use super::*;
|
||||
use crate::pv_utils::{BuilderTrait, SeHdr, SeHdrBuilder, SeHdrVersion};
|
||||
|
||||
#[test]
|
||||
fn iv_keys_auto_generation_test() {
|
||||
let (_, host_key) = get_test_key_and_cert();
|
||||
let host_keys = [host_key.public_key().unwrap()];
|
||||
let mut builder = SeHdrBuilder::new(
|
||||
SeHdrVersion::V1,
|
||||
PSW {
|
||||
addr: 1234,
|
||||
mask: 5678,
|
||||
},
|
||||
ComponentMetadataV1 {
|
||||
ald: [0x1; SHA_512_HASH_LEN],
|
||||
pld: [0x2; SHA_512_HASH_LEN],
|
||||
tld: [0x3; SHA_512_HASH_LEN],
|
||||
nep: 1,
|
||||
key: Confidential::new([0x0_u8; SymKeyType::AES_256_XTS_KEY_LEN]),
|
||||
},
|
||||
)
|
||||
.expect("should not fail");
|
||||
builder.add_hostkeys(&host_keys).expect("should not fail");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_test() {
|
||||
let (_, host_key) = get_test_key_and_cert();
|
||||
let host_keys = [host_key.public_key().unwrap()];
|
||||
let xts_key = Confidential::new([0x3; SymKeyType::AES_256_XTS_KEY_LEN]);
|
||||
let meta = ComponentMetadataV1 {
|
||||
ald: [0x1; SHA_512_HASH_LEN],
|
||||
pld: [0x2; SHA_512_HASH_LEN],
|
||||
tld: [0x3; SHA_512_HASH_LEN],
|
||||
nep: 3,
|
||||
key: xts_key,
|
||||
};
|
||||
let cck: Confidential<Vec<u8>> = [0x42; 32].to_vec().into();
|
||||
let psw = PSW {
|
||||
addr: 1234,
|
||||
mask: 5678,
|
||||
};
|
||||
|
||||
let mut builder = SeHdrBuilder::new(SeHdrVersion::V1, psw.clone(), meta.clone())
|
||||
.expect("should not fail");
|
||||
|
||||
builder
|
||||
.add_hostkeys(&host_keys)
|
||||
.expect("should not fail")
|
||||
.with_components(meta.clone())
|
||||
.expect("should not fail")
|
||||
.with_cck(cck.clone())
|
||||
.expect("should not fail");
|
||||
let prot_key = builder.prot_key().to_owned();
|
||||
let bin = builder.build().expect("should not fail");
|
||||
|
||||
let reader = Cursor::new(bin.as_bytes().expect("should not fail"));
|
||||
let hdr = SeHdr::try_from_io(reader).unwrap();
|
||||
|
||||
let hdr_plain = hdr.decrypt(&prot_key).unwrap();
|
||||
assert_eq!(hdr_plain.common.version, SeHdrVersion::V1);
|
||||
let hdr_data_v1: SeHdrDataV1 = hdr_plain.data.try_into().expect("should not fail");
|
||||
assert_eq!(meta.ald, hdr_data_v1.aad.ald);
|
||||
assert_eq!(meta.pld, hdr_data_v1.aad.pld);
|
||||
assert_eq!(meta.tld, hdr_data_v1.aad.tld);
|
||||
assert_eq!(psw, hdr_data_v1.data.value().psw);
|
||||
assert_eq!(cck.value(), hdr_data_v1.data.value().cck.value());
|
||||
}
|
||||
}
|
||||
99
rust/pvimg/src/pv_utils/se_hdr/keys.rs
Normal file
99
rust/pvimg/src/pv_utils/se_hdr/keys.rs
Normal file
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use std::mem::size_of;
|
||||
|
||||
use deku::{ctx::Endian, DekuRead, DekuWrite};
|
||||
use openssl::{
|
||||
hash::{hash, MessageDigest},
|
||||
pkey::{PKey, PKeyRef, Public},
|
||||
};
|
||||
use pv::{request::EcPubKeyCoord, static_assert};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
pv_utils::{serializing::ser_hex, try_copy_slice_to_array},
|
||||
};
|
||||
|
||||
/// Try to hash the public EC key.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if OpenSSL could not hash the key.
|
||||
pub fn phkh_v1<T: AsRef<PKeyRef<Public>>>(key: T) -> Result<[u8; 32]> {
|
||||
let phk: EcPubKeyCoord = key.as_ref().try_into()?;
|
||||
let binding = hash(MessageDigest::sha256(), phk.as_ref())?;
|
||||
try_copy_slice_to_array(&binding)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
||||
pub struct EcPubKeyCoordV1 {
|
||||
#[serde(serialize_with = "ser_hex")]
|
||||
pub coord: [u8; 160],
|
||||
}
|
||||
|
||||
#[allow(clippy::fallible_impl_from)]
|
||||
impl From<EcPubKeyCoord> for EcPubKeyCoordV1 {
|
||||
fn from(value: EcPubKeyCoord) -> Self {
|
||||
// SAFETY: `EcPubKeyCoord` has the same struct definition as
|
||||
// `EcPubKeyCooardV1`.
|
||||
let coord = try_copy_slice_to_array(value.as_ref()).unwrap();
|
||||
Self { coord }
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::fallible_impl_from)]
|
||||
impl From<EcPubKeyCoordV1> for EcPubKeyCoord {
|
||||
fn from(value: EcPubKeyCoordV1) -> Self {
|
||||
// SAFETY: `EcPubKeyCoord` has the same struct definition as
|
||||
// `EcPubKeyCooardV1`.
|
||||
let coord = try_copy_slice_to_array(&value.coord).unwrap();
|
||||
// SAFETY: This call is safe because we do not expect, that
|
||||
// EcPubKeyCoord is always a valid EC pub key.
|
||||
unsafe { Self::from_data(coord) }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<EcPubKeyCoordV1> for PKey<Public> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: EcPubKeyCoordV1) -> Result<Self, Error> {
|
||||
<EcPubKeyCoordV1 as Into<EcPubKeyCoord>>::into(value)
|
||||
.try_into()
|
||||
.map_err(Error::Crypto)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug, PartialEq, Eq, Clone, DekuRead, DekuWrite, Serialize)]
|
||||
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
||||
/// Binary key slot v1
|
||||
pub struct BinaryKeySlotV1 {
|
||||
#[serde(serialize_with = "ser_hex")]
|
||||
/// Public host key hash
|
||||
pub(crate) phkh: [u8; 32],
|
||||
/// Wrapper key
|
||||
#[serde(serialize_with = "ser_hex")]
|
||||
pub(crate) wrpk: [u8; 32],
|
||||
/// Tag
|
||||
#[serde(serialize_with = "ser_hex")]
|
||||
pub(crate) kst: [u8; 16],
|
||||
}
|
||||
static_assert!(size_of::<BinaryKeySlotV1>() == 80);
|
||||
|
||||
impl TryFrom<Vec<u8>> for BinaryKeySlotV1 {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
let data: [u8; 80] = try_copy_slice_to_array(&value)?;
|
||||
let bin = Self {
|
||||
phkh: data[..32].try_into().unwrap(),
|
||||
wrpk: data[32..64].try_into().unwrap(),
|
||||
kst: data[64..].try_into().unwrap(),
|
||||
};
|
||||
Ok(bin)
|
||||
}
|
||||
}
|
||||
820
rust/pvimg/src/pv_utils/secured_comp.rs
Normal file
820
rust/pvimg/src/pv_utils/secured_comp.rs
Normal file
@@ -0,0 +1,820 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use std::{
|
||||
fmt::{Debug, Formatter},
|
||||
io::{Read, Write},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use log::debug;
|
||||
use openssl::{
|
||||
bn::BigNum,
|
||||
cipher::{Cipher, CipherRef},
|
||||
cipher_ctx::{CipherCtx, CipherCtxRef},
|
||||
hash::{Hasher, MessageDigest},
|
||||
nid::Nid,
|
||||
};
|
||||
use pv::request::{Confidential, SymKey, SymKeyType};
|
||||
|
||||
use super::{try_copy_slice_to_array, Layout};
|
||||
use crate::pv_utils::{
|
||||
error::{Error, PvError, Result},
|
||||
se_hdr::{ComponentMetadata, ComponentMetadataV1},
|
||||
Interval,
|
||||
};
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Mode {
|
||||
Encrypt,
|
||||
Decrypt,
|
||||
Padding,
|
||||
}
|
||||
|
||||
fn update_ald_digest(hasher: &mut Hasher, interval: &Interval, chunk_size: usize) -> Result<usize> {
|
||||
let mut num_chunks = 0;
|
||||
|
||||
for addr in (interval.start..interval.stop).step_by(chunk_size) {
|
||||
let addr_be_data = addr.to_be_bytes();
|
||||
|
||||
hasher.update(&addr_be_data)?;
|
||||
num_chunks += 1;
|
||||
}
|
||||
Ok(num_chunks)
|
||||
}
|
||||
|
||||
pub struct PrepareSecuredComponentArgs<'a> {
|
||||
pub(crate) addr: u64,
|
||||
pub(crate) cipher: &'a CipherRef,
|
||||
pub(crate) mode: Mode,
|
||||
pub(crate) key: &'a [u8],
|
||||
pub(crate) iv: &'a [u8],
|
||||
pub(crate) chunk_size: usize,
|
||||
}
|
||||
|
||||
pub struct MetadataArgs<'a> {
|
||||
pub(crate) content_hasher: Option<&'a mut Hasher>,
|
||||
pub(crate) tweak_hasher: Option<&'a mut Hasher>,
|
||||
pub(crate) address_hasher: Option<&'a mut Hasher>,
|
||||
pub(crate) num_chunks: Option<&'a mut usize>,
|
||||
pub(crate) max_component_size: Option<usize>,
|
||||
pub(crate) input_size: usize,
|
||||
pub(crate) padded_input_size: usize,
|
||||
pub(crate) output_size: usize,
|
||||
}
|
||||
|
||||
/// This functions tries to read the exact number of bytes required to fill
|
||||
/// `buf`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If this function encounters an EOF before completely filling the buffer, it
|
||||
/// returns an error of the kind [`std::io::ErrorKind::UnexpectedEof`]. The
|
||||
/// contents of `buf` are unspecfied in this case.
|
||||
fn own_read_exact<R: Read + ?Sized>(reader: &mut R, mut buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let mut data_read = 0;
|
||||
while !buf.is_empty() {
|
||||
match reader.read(buf) {
|
||||
Ok(0) => break,
|
||||
Ok(n) => {
|
||||
buf = &mut buf[n..];
|
||||
data_read += n;
|
||||
}
|
||||
Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(data_read)
|
||||
}
|
||||
|
||||
/// This function is used for prepare a "secured component" used in the Secure Execution
|
||||
/// context. It adds padding if needed, encrypts the components and calculates
|
||||
/// the PLD and TLD.
|
||||
pub fn prepare_component<R: Read, W: Write>(
|
||||
crypto_args: &PrepareSecuredComponentArgs,
|
||||
src: &mut R,
|
||||
dst: &mut W,
|
||||
mut opt_data: Option<&mut MetadataArgs>,
|
||||
) -> Result<()> {
|
||||
let PrepareSecuredComponentArgs {
|
||||
addr,
|
||||
cipher,
|
||||
mode,
|
||||
key,
|
||||
iv,
|
||||
chunk_size,
|
||||
} = *crypto_args;
|
||||
let mut chunk_data = vec![0_u8; chunk_size];
|
||||
let mut output_data = vec![0_u8; chunk_data.len()];
|
||||
let mut chunks_count: usize = 0;
|
||||
let mut count;
|
||||
|
||||
let mut tweak_num = BigNum::from_slice(iv)?;
|
||||
let mut ctx = if matches!(mode, Mode::Decrypt) || matches!(mode, Mode::Encrypt) {
|
||||
Some(CipherCtx::new()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let init_func = match mode {
|
||||
// The value for Mode::Padding will never be actually used.
|
||||
Mode::Encrypt | Mode::Padding => CipherCtxRef::encrypt_init,
|
||||
Mode::Decrypt => CipherCtxRef::decrypt_init,
|
||||
};
|
||||
|
||||
if let Some(ref mut ctx) = &mut ctx {
|
||||
assert!(chunk_size % cipher.block_size() == 0, "Invalid chunk size");
|
||||
|
||||
init_func(ctx, Some(cipher), None, None)?;
|
||||
|
||||
if key.len() != cipher.key_length() {
|
||||
debug!("Setting new key length: {}", key.len());
|
||||
ctx.set_key_length(key.len())?;
|
||||
}
|
||||
if iv.len() != cipher.iv_length() {
|
||||
debug!("Setting new IV length: {}", iv.len());
|
||||
ctx.set_iv_length(iv.len())?;
|
||||
}
|
||||
|
||||
// Set key
|
||||
init_func(ctx, None, Some(key), None)?;
|
||||
};
|
||||
|
||||
loop {
|
||||
let new_tweak = tweak_num.to_vec_padded(iv.len().try_into()?)?;
|
||||
|
||||
// Set a new tweak
|
||||
if let Some(ref mut ctx) = &mut ctx {
|
||||
init_func(ctx, None, None, Some(&new_tweak))?;
|
||||
}
|
||||
|
||||
// Read input data
|
||||
let read_count = own_read_exact(src, &mut chunk_data)?;
|
||||
// EOF has been reached
|
||||
if read_count == 0 {
|
||||
// A chunk was read before and EOF was reached => it was not an
|
||||
// empty file and therefore break the loop.
|
||||
if chunks_count != 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let input_slice = &chunk_data[..];
|
||||
|
||||
// Encrypt
|
||||
if let Some(ref mut ctx) = ctx {
|
||||
count = ctx.cipher_update(input_slice, Some(&mut output_data))?;
|
||||
} else {
|
||||
output_data.copy_from_slice(input_slice);
|
||||
count = input_slice.len();
|
||||
}
|
||||
|
||||
// Write output data and check if it fits in the image layout
|
||||
let output_slice = &output_data[..count];
|
||||
if let Some(ops) = opt_data.as_mut() {
|
||||
let output_size = ops
|
||||
.output_size
|
||||
.checked_add(output_slice.len())
|
||||
.ok_or(Error::UnexpectedOverflow)?;
|
||||
|
||||
if let Some(max_output_size) = ops.max_component_size {
|
||||
if output_size > max_output_size {
|
||||
return Err(Error::PreparedComponentTooLarge {
|
||||
output_size,
|
||||
max_output_size,
|
||||
});
|
||||
}
|
||||
}
|
||||
ops.output_size = output_size;
|
||||
|
||||
// Calculate input size
|
||||
ops.input_size = ops
|
||||
.input_size
|
||||
.checked_add(read_count)
|
||||
.ok_or(Error::UnexpectedOverflow)?;
|
||||
|
||||
// Calculate padded input size
|
||||
ops.padded_input_size = ops
|
||||
.padded_input_size
|
||||
.checked_add(input_slice.len())
|
||||
.ok_or(Error::UnexpectedOverflow)?;
|
||||
|
||||
// Calculate PLD
|
||||
if let Some(ref mut hasher) = ops.content_hasher {
|
||||
hasher.update(output_slice)?;
|
||||
}
|
||||
|
||||
// Calculate TLD
|
||||
if let Some(ref mut hasher) = ops.tweak_hasher {
|
||||
hasher.update(&new_tweak)?;
|
||||
}
|
||||
}
|
||||
|
||||
dst.write_all(output_slice)?;
|
||||
chunks_count = chunks_count
|
||||
.checked_add(1)
|
||||
.ok_or(Error::UnexpectedOverflow)?;
|
||||
|
||||
// Prepare for the next chunk:
|
||||
// * Calculate new tweak
|
||||
// * Reset chunk data to zeroes
|
||||
tweak_num.add_word(chunk_size.try_into()?)?;
|
||||
chunk_data.fill(0x0);
|
||||
}
|
||||
|
||||
if let Some(ref mut ctx) = &mut ctx {
|
||||
count = ctx.cipher_final(&mut output_data)?;
|
||||
} else {
|
||||
count = 0;
|
||||
}
|
||||
let output_slice = &output_data[..count];
|
||||
dst.write_all(output_slice)?;
|
||||
|
||||
if let Some(ops) = opt_data.as_mut() {
|
||||
// Calculate output size
|
||||
let output_size = ops
|
||||
.output_size
|
||||
.checked_add(output_slice.len())
|
||||
.ok_or(Error::UnexpectedOverflow)?;
|
||||
|
||||
if let Some(max_output_size) = ops.max_component_size {
|
||||
if output_size > max_output_size {
|
||||
return Err(Error::PreparedComponentTooLarge {
|
||||
output_size,
|
||||
max_output_size,
|
||||
});
|
||||
}
|
||||
}
|
||||
ops.output_size = output_size;
|
||||
|
||||
// Calculate PLD
|
||||
if let Some(ref mut hasher) = ops.content_hasher {
|
||||
hasher.update(output_slice)?;
|
||||
}
|
||||
|
||||
// Calculate ALD
|
||||
if let Some(ref mut hasher) = ops.address_hasher {
|
||||
update_ald_digest(
|
||||
hasher,
|
||||
&Interval::new_with_size(addr, output_size.try_into()?)?,
|
||||
chunk_size,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Update the total number of prepared chunks.
|
||||
if let Some(ref mut num_chunks) = ops.num_chunks {
|
||||
**num_chunks = num_chunks
|
||||
.checked_add(chunks_count)
|
||||
.ok_or(Error::UnexpectedOverflow)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A trait for dealing with (secured) components.
|
||||
pub trait ComponentTrait<T>: Debug + Read {
|
||||
/// Returns if the component is used in secure mode.
|
||||
fn secure_mode(&self) -> bool;
|
||||
|
||||
/// Returns the component type.
|
||||
fn kind(&self) -> T;
|
||||
}
|
||||
|
||||
/// Struct for representing a secured component that is going to be unpacked by
|
||||
/// the Ultravisor.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct SecuredComponent {
|
||||
/// Source of the prepared (encrypted) component
|
||||
pub src: Rc<Interval>,
|
||||
/// Size of the unprepared (unencrypted) component.
|
||||
pub original_size: usize,
|
||||
/// Tweak or IV used for the (de/en)cryption of the component.
|
||||
tweak_or_iv: Vec<u8>,
|
||||
}
|
||||
|
||||
impl SecuredComponent {
|
||||
pub fn tweak(&self) -> &[u8] {
|
||||
self.tweak_or_iv.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder that is used to prepare a [`SecuredComponent`].
|
||||
pub struct SecuredComponentBuilder {
|
||||
/// Expert mode, in example the secured components encryption key and be set
|
||||
/// manually. By default disabled.
|
||||
expert_mode: bool,
|
||||
/// Chunk size, currently only 4096 bytes is supported by the Ultravisor.
|
||||
chunk_size: usize,
|
||||
|
||||
/// Determines whether a secured component needs to be encrypted.
|
||||
encrypt: bool,
|
||||
/// Determines which cipher will be used for the encryption.
|
||||
cipher: &'static CipherRef,
|
||||
/// Key used for the encryption of the components.
|
||||
comp_key: SymKey,
|
||||
|
||||
// Cached values
|
||||
/// Number of chunks already prepared by this [`Self`].
|
||||
num_chunks: usize,
|
||||
/// ALD hasher
|
||||
ald_hasher: Hasher,
|
||||
/// PLD hasher
|
||||
pld_hasher: Hasher,
|
||||
/// TLD hasher
|
||||
tld_hasher: Hasher,
|
||||
/// Finalized image?
|
||||
finalized: bool,
|
||||
}
|
||||
|
||||
// Needs to be implemented manually as `CipherRef` and `Hasher` do not implement
|
||||
// [`Debug`].
|
||||
impl Debug for SecuredComponentBuilder {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("SecuredComponentBuilder")
|
||||
.field("chunk_size", &self.chunk_size)
|
||||
.field("cipher", &self.cipher.nid().long_name()?)
|
||||
.field("comp_key", &self.comp_key)
|
||||
.field("encrypt", &self.encrypt)
|
||||
.field("expert_mode", &self.expert_mode)
|
||||
.field("num_chunks", &self.num_chunks)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl SecuredComponentBuilder {
|
||||
/// Values used for the first (and current) Ultravisor implementation.
|
||||
const CHUNK_SIZE_V1: usize = 4096;
|
||||
const CIPHER_V1: SymKeyType = SymKeyType::Aes256Xts;
|
||||
pub const COMPONENT_ALIGNMENT_V1: u64 = 4096;
|
||||
const DIGEST_V1: Nid = Nid::SHA512;
|
||||
|
||||
fn new(
|
||||
encrypt: bool,
|
||||
key_type: SymKeyType,
|
||||
digest_nid: Nid,
|
||||
chunk_size: usize,
|
||||
) -> Result<Self> {
|
||||
let digest = MessageDigest::from_nid(digest_nid).ok_or(Error::UnsupportMessageDigest)?;
|
||||
let nid = key_type.into();
|
||||
let cipher = Cipher::from_nid(nid).ok_or_else(|| PvError::UnsupportedCipher(nid))?;
|
||||
let key = SymKey::random(key_type)?;
|
||||
|
||||
Ok(Self {
|
||||
expert_mode: false,
|
||||
comp_key: key,
|
||||
cipher,
|
||||
encrypt,
|
||||
num_chunks: 0,
|
||||
chunk_size,
|
||||
ald_hasher: Hasher::new(digest)?,
|
||||
pld_hasher: Hasher::new(digest)?,
|
||||
tld_hasher: Hasher::new(digest)?,
|
||||
finalized: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new [`Self`] that can be used for the preparation of V1
|
||||
/// secured components. AES256-XTS is used for the components encryption,
|
||||
/// SHA-512 for the ALD, PLD and TLD. The components must be aligned and a
|
||||
/// multiple of 4096 bytes.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the cipher or digest algorithm is
|
||||
/// not supported or no random key could be generated.
|
||||
pub fn new_v1(encryption: bool) -> Result<Self> {
|
||||
Self::new(
|
||||
encryption,
|
||||
Self::CIPHER_V1,
|
||||
Self::DIGEST_V1,
|
||||
Self::CHUNK_SIZE_V1,
|
||||
)
|
||||
}
|
||||
|
||||
/// Activate the expert mode. For example, it's then allowed to change
|
||||
/// security related settings like setting the encryption keys manually.
|
||||
pub fn i_know_what_i_am_doing(&mut self) {
|
||||
self.expert_mode = true;
|
||||
}
|
||||
|
||||
/// Sets the components key. This requires the expert mode to be active and
|
||||
/// there is also the restriction, that it cannot be changed after the first
|
||||
/// secured component was prepared.
|
||||
///
|
||||
/// * `key_data` - Data that is used as the new components key.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if:
|
||||
/// * the expert mode is not active (see [`Self::i_know_what_i_am_doing`])
|
||||
/// * or a first component was already prepared or finalized
|
||||
/// * the key could not created by using `key_data`.
|
||||
pub fn set_components_key(&mut self, key_data: Confidential<Vec<u8>>) -> Result<()> {
|
||||
if !self.expert_mode {
|
||||
return Err(Error::NonExpertMode);
|
||||
}
|
||||
// We have already encrypted a component, therefore reject the new
|
||||
// components key.
|
||||
if self.num_chunks > 0 || self.finalized {
|
||||
return Err(Error::FirstComponentAlreadyPrepared);
|
||||
}
|
||||
|
||||
self.comp_key = SymKey::try_from_data(self.comp_key.key_type(), key_data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prepare the given component and write it into the given writer and
|
||||
/// assume the given memory address.
|
||||
///
|
||||
/// * `writer` - Write the prepared component into this writer.
|
||||
/// * `layout` - Memory layout where the prepared component is later used.
|
||||
/// * `component` - Component to be prepared as .
|
||||
/// * `addr` - Memory address where the prepared component later will later be located
|
||||
/// (important for the Secure Execution header).
|
||||
/// * `tweak` - Tweak used for the component encryption.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if:
|
||||
/// * address is smaller than the expected next possible address.
|
||||
/// * the image was already finalized
|
||||
/// * the given tweak is invalid
|
||||
pub fn prepare_and_insert_as_secure_component<S, W: Write, T: ComponentTrait<S>>(
|
||||
&mut self,
|
||||
writer: &mut W,
|
||||
layout: &mut Layout,
|
||||
component: &mut T,
|
||||
addr: u64,
|
||||
tweak: Vec<u8>,
|
||||
) -> Result<SecuredComponent> {
|
||||
let next_possible_addr = layout.next_addr;
|
||||
if addr < next_possible_addr {
|
||||
return Err(Error::NonMonotonicallyIncreasing {
|
||||
addr,
|
||||
next_addr: next_possible_addr,
|
||||
});
|
||||
}
|
||||
|
||||
let alignment = layout.alignment;
|
||||
if (addr % alignment) != 0 {
|
||||
return Err(Error::UnalignedAddress { addr, alignment });
|
||||
}
|
||||
|
||||
if alignment > self.chunk_size.try_into().unwrap() {
|
||||
return Err(Error::InvalidAlignment {
|
||||
alignment,
|
||||
chunk_size: self.chunk_size,
|
||||
});
|
||||
}
|
||||
|
||||
let max_component_size = layout.max_size_of_chunk_at_addr(addr)?;
|
||||
let secured_comp = self.prepare_and_insert_as_secure_component_unchecked(
|
||||
writer,
|
||||
component,
|
||||
addr,
|
||||
max_component_size,
|
||||
tweak,
|
||||
)?;
|
||||
layout.insert_interval(secured_comp.src.start, secured_comp.src.size())?;
|
||||
Ok(secured_comp)
|
||||
}
|
||||
|
||||
/// Prepare the given component and insert it at the given image address.
|
||||
///
|
||||
/// * `writer` - Write the prepared component into this writer.
|
||||
/// * `component` - Component to be prepared.
|
||||
/// * `addr` - Address where the prepared component should be inserted.
|
||||
/// * `max_component_size`- Maximum possible size that the prepared component may have
|
||||
/// * `tweak` - Tweak used for the component encryption.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if:
|
||||
/// * address is smaller than the expected next possible address.
|
||||
/// * the image was already finalized
|
||||
/// * the given tweak is invalid
|
||||
fn prepare_and_insert_as_secure_component_unchecked<S, W: Write, T: ComponentTrait<S>>(
|
||||
&mut self,
|
||||
writer: &mut W,
|
||||
component: &mut T,
|
||||
addr: u64,
|
||||
max_component_size: Option<usize>,
|
||||
tweak: Vec<u8>,
|
||||
) -> Result<SecuredComponent> {
|
||||
assert!(component.secure_mode());
|
||||
assert_ne!(self.chunk_size, 0);
|
||||
|
||||
if self.finalized {
|
||||
return Err(Error::ImageAlreadyFinalized);
|
||||
}
|
||||
|
||||
let expected_tweak_len = self.cipher.iv_length();
|
||||
if expected_tweak_len != tweak.len() {
|
||||
return Err(Error::InvalidTweakSize {
|
||||
given: tweak.len(),
|
||||
expected: expected_tweak_len,
|
||||
});
|
||||
}
|
||||
|
||||
let mode = if self.encrypt && component.secure_mode() {
|
||||
Mode::Encrypt
|
||||
} else {
|
||||
Mode::Padding
|
||||
};
|
||||
|
||||
let prepare_args = PrepareSecuredComponentArgs {
|
||||
addr,
|
||||
cipher: self.cipher,
|
||||
mode,
|
||||
key: self.comp_key.value(),
|
||||
iv: &tweak,
|
||||
chunk_size: self.chunk_size,
|
||||
};
|
||||
|
||||
let mut ops = MetadataArgs {
|
||||
content_hasher: Some(&mut self.pld_hasher),
|
||||
tweak_hasher: Some(&mut self.tld_hasher),
|
||||
address_hasher: Some(&mut self.ald_hasher),
|
||||
num_chunks: Some(&mut self.num_chunks),
|
||||
max_component_size,
|
||||
input_size: 0,
|
||||
padded_input_size: 0,
|
||||
output_size: 0,
|
||||
};
|
||||
// Prepare the component and write the prepared data directly to the output
|
||||
prepare_component(&prepare_args, component, writer, Some(&mut ops))?;
|
||||
|
||||
let original_size = ops.input_size;
|
||||
let prepared_size = ops.output_size.try_into()?;
|
||||
let src = Interval::new_with_size(addr, prepared_size)?;
|
||||
|
||||
Ok(SecuredComponent {
|
||||
original_size,
|
||||
src: Rc::new(src),
|
||||
tweak_or_iv: tweak,
|
||||
})
|
||||
}
|
||||
|
||||
/// Prepare the given component and append the prepared component to the
|
||||
/// back of the image layout.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the image was already finalized or
|
||||
/// the given tweak is invalid.
|
||||
pub fn prepare_and_append_as_secure_component<S, W: Write, T: ComponentTrait<S>>(
|
||||
&mut self,
|
||||
writer: &mut W,
|
||||
layout: &mut Layout,
|
||||
component: &mut T,
|
||||
tweak: Vec<u8>,
|
||||
) -> Result<SecuredComponent> {
|
||||
let next_addr = layout.next_addr;
|
||||
self.prepare_and_insert_as_secure_component(writer, layout, component, next_addr, tweak)
|
||||
}
|
||||
|
||||
/// Finalizes the image and returns the image metadata (the digests, number
|
||||
/// of chunks) and the key that was used for the components encryption.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the builder is already finalized
|
||||
/// or there was a problem in a cryptographic operation.
|
||||
pub fn finish(&mut self) -> Result<ComponentMetadata> {
|
||||
if self.finalized {
|
||||
return Err(Error::ImageAlreadyFinalized);
|
||||
}
|
||||
|
||||
self.finalized = true;
|
||||
|
||||
Ok(ComponentMetadata::ComponentMetadataV1(
|
||||
ComponentMetadataV1 {
|
||||
ald: try_copy_slice_to_array(self.ald_hasher.finish()?.as_ref())?,
|
||||
pld: try_copy_slice_to_array(self.pld_hasher.finish()?.as_ref())?,
|
||||
tld: try_copy_slice_to_array(self.tld_hasher.finish()?.as_ref())?,
|
||||
nep: self.num_chunks.try_into()?,
|
||||
key: try_copy_slice_to_array(self.comp_key.value())?.into(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns if encryption is used.
|
||||
pub const fn encryption_enabled(&self) -> bool {
|
||||
self.encrypt
|
||||
}
|
||||
|
||||
/// Returns the chunk size.
|
||||
pub const fn chunk_size(&self) -> usize {
|
||||
self.chunk_size
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::shadow_unrelated)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{fmt::Debug, io::Cursor};
|
||||
|
||||
use pv::request::Aes256XtsKey;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn prepare_aligned_component_test() {
|
||||
#[derive(Debug)]
|
||||
struct TestComp<T: Read + Debug> {
|
||||
reader: T,
|
||||
}
|
||||
|
||||
impl<T: Read + Debug> ComponentTrait<()> for TestComp<T> {
|
||||
fn secure_mode(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn kind(&self) {}
|
||||
}
|
||||
|
||||
impl<T: Read + Debug> Read for TestComp<T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
self.reader.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
let start_addr = 0x10000;
|
||||
let encryption = true;
|
||||
let mut writer = Cursor::new(Vec::new());
|
||||
let mut ctx = SecuredComponentBuilder::new_v1(encryption).expect("should work");
|
||||
let mut key = vec![0x42; 32];
|
||||
key.extend([0x43; 32]);
|
||||
ctx.i_know_what_i_am_doing();
|
||||
ctx.set_components_key(
|
||||
Aes256XtsKey::new(<[u8; 64]>::try_from(key.as_slice()).unwrap()).into(),
|
||||
)
|
||||
.unwrap();
|
||||
let input_data1 = vec![0x1; 0x3400];
|
||||
let input_data2 = vec![0x2; 0x3000];
|
||||
|
||||
let mut comp1 = TestComp {
|
||||
reader: Cursor::new(input_data1),
|
||||
};
|
||||
let tweak1 = vec![
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
];
|
||||
|
||||
let img_comp_res = ctx.prepare_and_insert_as_secure_component_unchecked(
|
||||
&mut writer,
|
||||
&mut comp1,
|
||||
start_addr,
|
||||
None,
|
||||
tweak1,
|
||||
);
|
||||
assert!(img_comp_res.is_ok());
|
||||
assert_eq!(ctx.num_chunks, 4);
|
||||
|
||||
let reader2 = Cursor::new(input_data2);
|
||||
let mut comp2 = TestComp { reader: reader2 };
|
||||
let tweak2 = vec![
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x42, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
];
|
||||
let img_comp_res = ctx.prepare_and_insert_as_secure_component_unchecked(
|
||||
&mut writer,
|
||||
&mut comp2,
|
||||
0x20000,
|
||||
None,
|
||||
tweak2,
|
||||
);
|
||||
assert!(img_comp_res.is_ok());
|
||||
assert_eq!(ctx.num_chunks, 7);
|
||||
|
||||
let metav1: ComponentMetadataV1 = ctx
|
||||
.finish()
|
||||
.expect("should not fail")
|
||||
.try_into()
|
||||
.expect("should not fail");
|
||||
|
||||
// Check ALD
|
||||
assert_eq!(
|
||||
metav1.ald,
|
||||
[
|
||||
195, 145, 222, 87, 39, 160, 130, 18, 234, 47, 234, 156, 55, 249, 207, 9, 11, 229,
|
||||
31, 147, 198, 213, 33, 184, 144, 99, 50, 206, 114, 12, 95, 56, 173, 160, 231, 62,
|
||||
105, 102, 62, 82, 17, 208, 21, 254, 244, 29, 198, 38, 6, 245, 19, 94, 97, 153, 4,
|
||||
212, 244, 80, 171, 136, 159, 73, 202, 173
|
||||
],
|
||||
);
|
||||
|
||||
// Check PLD
|
||||
assert_eq!(
|
||||
metav1.pld,
|
||||
[
|
||||
162, 79, 243, 10, 138, 241, 41, 88, 136, 222, 223, 233, 54, 158, 181, 9, 41, 3, 9,
|
||||
169, 1, 89, 235, 195, 44, 162, 106, 83, 249, 212, 54, 74, 120, 24, 87, 226, 89, 5,
|
||||
135, 83, 108, 62, 118, 115, 85, 199, 183, 96, 63, 43, 12, 106, 64, 127, 22, 51, 13,
|
||||
130, 18, 141, 9, 100, 250, 210
|
||||
]
|
||||
);
|
||||
|
||||
// Check TLD
|
||||
let digest = MessageDigest::sha512();
|
||||
let mut hasher_new = Hasher::new(digest).expect("should work");
|
||||
// Tweaks for comp1
|
||||
hasher_new
|
||||
.update(&[0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||
.expect("should work");
|
||||
hasher_new
|
||||
.update(&[0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 16, 0])
|
||||
.expect("should work");
|
||||
hasher_new
|
||||
.update(&[0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 32, 0])
|
||||
.expect("should work");
|
||||
hasher_new
|
||||
.update(&[0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 48, 0])
|
||||
.expect("should work");
|
||||
|
||||
// Tweaks for comp2
|
||||
hasher_new
|
||||
.update(&[0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||
.expect("should work");
|
||||
hasher_new
|
||||
.update(&[0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 16, 0])
|
||||
.expect("should work");
|
||||
hasher_new
|
||||
.update(&[0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 32, 0])
|
||||
.expect("should work");
|
||||
let exp = hasher_new.finish().expect("should work");
|
||||
|
||||
assert_eq!(metav1.tld, *exp);
|
||||
assert_eq!(
|
||||
metav1.tld,
|
||||
[
|
||||
66, 79, 227, 207, 4, 166, 246, 74, 122, 239, 24, 92, 59, 78, 246, 7, 192, 228, 245,
|
||||
75, 183, 225, 70, 32, 181, 116, 163, 211, 30, 239, 49, 199, 212, 98, 235, 4, 13,
|
||||
69, 238, 105, 24, 230, 184, 9, 104, 186, 68, 84, 249, 226, 237, 194, 111, 105, 41,
|
||||
237, 98, 77, 0, 85, 242, 53, 86, 89
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_ald_digest() {
|
||||
let start = 0x10000;
|
||||
let stop = 0x13400;
|
||||
let digest = MessageDigest::sha512();
|
||||
let mut hasher = Hasher::new(digest).expect("should work");
|
||||
let mut hasher_new = Hasher::new(digest).expect("should work");
|
||||
|
||||
hasher_new
|
||||
.update(&0x10000_u64.to_be_bytes())
|
||||
.expect("should work");
|
||||
hasher_new
|
||||
.update(&0x11000_u64.to_be_bytes())
|
||||
.expect("should work");
|
||||
hasher_new
|
||||
.update(&0x12000_u64.to_be_bytes())
|
||||
.expect("should work");
|
||||
hasher_new
|
||||
.update(&0x13000_u64.to_be_bytes())
|
||||
.expect("should work");
|
||||
hasher_new
|
||||
.update(&0x20000_u64.to_be_bytes())
|
||||
.expect("should work");
|
||||
hasher_new
|
||||
.update(&0x21000_u64.to_be_bytes())
|
||||
.expect("should work");
|
||||
hasher_new
|
||||
.update(&0x22000_u64.to_be_bytes())
|
||||
.expect("should work");
|
||||
let exp = hasher_new.finish().expect("should work");
|
||||
|
||||
let chunks_count_res = update_ald_digest(&mut hasher, &Interval { start, stop }, 4096);
|
||||
assert!(chunks_count_res.is_ok());
|
||||
assert_eq!(chunks_count_res.unwrap(), 4);
|
||||
|
||||
let chunks_count_res = update_ald_digest(
|
||||
&mut hasher,
|
||||
&Interval {
|
||||
start: 0x20000,
|
||||
stop: 0x23000,
|
||||
},
|
||||
4096,
|
||||
);
|
||||
assert!(chunks_count_res.is_ok());
|
||||
assert_eq!(chunks_count_res.unwrap(), 3);
|
||||
|
||||
let res_ret = hasher.finish();
|
||||
assert!(res_ret.is_ok());
|
||||
let res = res_ret.unwrap();
|
||||
assert_eq!(&*exp, &*res,);
|
||||
assert_eq!(
|
||||
&*res,
|
||||
[
|
||||
195, 145, 222, 87, 39, 160, 130, 18, 234, 47, 234, 156, 55, 249, 207, 9, 11, 229,
|
||||
31, 147, 198, 213, 33, 184, 144, 99, 50, 206, 114, 12, 95, 56, 173, 160, 231, 62,
|
||||
105, 102, 62, 82, 17, 208, 21, 254, 244, 29, 198, 38, 6, 245, 19, 94, 97, 153, 4,
|
||||
212, 244, 80, 171, 136, 159, 73, 202, 173
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
149
rust/pvimg/src/pv_utils/serializing.rs
Normal file
149
rust/pvimg/src/pv_utils/serializing.rs
Normal file
@@ -0,0 +1,149 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use std::fmt::LowerHex;
|
||||
|
||||
use deku::{
|
||||
reader::Reader, writer::Writer, DekuContainerRead, DekuContainerWrite, DekuError, DekuReader,
|
||||
DekuWriter,
|
||||
};
|
||||
use pv::request::{Confidential, Zeroize};
|
||||
use serde::{Serialize, Serializer};
|
||||
use utils::HexSlice;
|
||||
|
||||
use crate::pv_utils::error::Result;
|
||||
|
||||
pub fn ser_hex<A: AsRef<[u8]>, S: Serializer>(
|
||||
data: A,
|
||||
ser: S,
|
||||
) -> std::result::Result<S::Ok, S::Error> {
|
||||
HexSlice::from(data.as_ref()).serialize(ser)
|
||||
}
|
||||
|
||||
pub fn ser_lower_hex<S: Serializer, B: LowerHex>(
|
||||
data: &B,
|
||||
ser: S,
|
||||
) -> std::result::Result<S::Ok, S::Error> {
|
||||
format!("{:#018x}", data).serialize(ser)
|
||||
}
|
||||
|
||||
pub fn ser_hex_confidential<S: Serializer, const COUNT: usize>(
|
||||
data: &Confidential<[u8; COUNT]>,
|
||||
ser: S,
|
||||
) -> std::result::Result<S::Ok, S::Error> {
|
||||
ser_hex(data.value(), ser)
|
||||
}
|
||||
|
||||
/// Read a slice into a confidential array of type [`T`] and length [`N`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the result could not be constructed or
|
||||
/// if there was an I/O error.
|
||||
pub fn confidential_read_slice<'a, Ctx, T, const COUNT: usize, R>(
|
||||
reader: &mut Reader<R>,
|
||||
ctx: Ctx,
|
||||
) -> Result<Confidential<[T; COUNT]>, DekuError>
|
||||
where
|
||||
Ctx: Copy,
|
||||
T: Default + DekuReader<'a, Ctx>,
|
||||
R: std::io::Read + std::io::Seek,
|
||||
{
|
||||
Ok(Confidential::new(<[T; COUNT]>::from_reader_with_ctx(
|
||||
reader, ctx,
|
||||
)?))
|
||||
}
|
||||
|
||||
/// Writes a confidential array into this writer.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if there was an I/O error.
|
||||
pub fn confidential_write_slice<Ctx, T, const COUNT: usize, W>(
|
||||
value: &Confidential<[T; COUNT]>,
|
||||
writer: &mut Writer<W>,
|
||||
ctx: Ctx,
|
||||
) -> Result<(), DekuError>
|
||||
where
|
||||
Ctx: Copy,
|
||||
T: Default + DekuWriter<Ctx>,
|
||||
W: std::io::Write + std::io::Seek,
|
||||
{
|
||||
value.value().to_writer(writer, ctx)
|
||||
}
|
||||
|
||||
/// Serializes `value` to bytes.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the value could not be serialized.
|
||||
pub fn serialize_to_bytes<T>(value: &T) -> Result<Vec<u8>>
|
||||
where
|
||||
T: DekuContainerWrite,
|
||||
{
|
||||
Ok(value.to_bytes()?)
|
||||
}
|
||||
|
||||
/// Deserializes `value` to `T`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the given value could not be
|
||||
/// deserialized.
|
||||
pub fn deserialize_from_bytes<'a, T>(value: &'a [u8]) -> Result<T>
|
||||
where
|
||||
T: DekuContainerRead<'a>,
|
||||
{
|
||||
let ((_, rest), obj) = T::from_bytes((value, 0))?;
|
||||
assert_eq!(rest, 0);
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
/// Returns the size (in bytes) of the serialized `value`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the given value could not be
|
||||
/// serialized.
|
||||
pub fn bytesize<T>(value: &T) -> Result<usize>
|
||||
where
|
||||
T: DekuContainerWrite,
|
||||
{
|
||||
let data = serialize_to_bytes(value)?;
|
||||
Ok(data.len())
|
||||
}
|
||||
|
||||
pub fn bytesize_confidential<T>(value: &Confidential<T>) -> Result<usize>
|
||||
where
|
||||
T: DekuContainerWrite + Zeroize,
|
||||
{
|
||||
let data = Confidential::new(serialize_to_bytes(value.value())?);
|
||||
Ok(data.value().len())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use deku::{ctx::Endian, DekuContainerWrite, DekuRead, DekuWrite};
|
||||
use pv::request::Confidential;
|
||||
|
||||
use crate::pv_utils::serializing::{confidential_read_slice, confidential_write_slice};
|
||||
|
||||
#[test]
|
||||
fn read_and_write() {
|
||||
#[derive(DekuRead, DekuWrite)]
|
||||
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
|
||||
struct Test {
|
||||
#[deku(
|
||||
reader = "confidential_read_slice(deku::reader, endian)",
|
||||
writer = "confidential_write_slice(test, deku::writer, endian)"
|
||||
)]
|
||||
test: Confidential<[u32; 1]>,
|
||||
}
|
||||
|
||||
const DATA: [u8; 4] = [15_u8, 1_u8, 2_u8, 3_u8];
|
||||
let test = Test::try_from(DATA.as_ref()).unwrap();
|
||||
assert_eq!(test.test.value()[0], 0x0f010203);
|
||||
assert_eq!(test.to_bytes().unwrap().as_slice(), &DATA);
|
||||
}
|
||||
}
|
||||
159
rust/pvimg/src/pv_utils/uv_keys.rs
Normal file
159
rust/pvimg/src/pv_utils/uv_keys.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use std::io::{BufRead, BufReader, Read};
|
||||
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use pv::misc::decode_hex;
|
||||
|
||||
use super::try_copy_slice_to_array;
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
/// The `enum_dispatch` macros needs at least one local trait to be implemented.
|
||||
#[allow(unused)]
|
||||
#[enum_dispatch(UvKeyHashes)]
|
||||
trait UvKeyHashTrait: AsRef<[u8]> {}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct UvKeyHashV1([u8; 32]);
|
||||
|
||||
#[non_exhaustive]
|
||||
#[enum_dispatch]
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub enum UvKeyHash {
|
||||
UvKeyHashV1(UvKeyHashV1),
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for UvKeyHash {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
match self {
|
||||
Self::UvKeyHashV1(hash) => hash.as_ref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UvKeyHashV1 {
|
||||
pub fn new<T: AsRef<[u8]>>(data: T) -> Result<Self> {
|
||||
let array = try_copy_slice_to_array(data.as_ref())?;
|
||||
Ok(Self(array))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct UvKeyHashesV1 {
|
||||
pub pchkh: UvKeyHashV1,
|
||||
pub pbhkh: UvKeyHashV1,
|
||||
pub res: [UvKeyHashV1; 13],
|
||||
}
|
||||
|
||||
impl UvKeyHashV1 {
|
||||
pub const UV_KEY_HASH_NULL: Self = Self([0x0_u8; 32]);
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for UvKeyHashV1 {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for UvKeyHashV1 {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
let bytes = decode_hex(value)?;
|
||||
if bytes.len() != 32 {
|
||||
return Err(Error::InvalidTargetKeyHash);
|
||||
}
|
||||
|
||||
Ok(Self(bytes.try_into().unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for UvKeyHashV1 {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
value.as_str().try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl UvKeyHashesV1 {
|
||||
pub const SYS_UV_KEYS_ALL: &'static str = "/sys/firmware/uv/keys/all";
|
||||
|
||||
/// Reads a `UvKeyHashesV1` from an [`std::io::Read`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if this functions encounters an I/O
|
||||
/// error, if a line could not be interpreted as `UvKeyHashV1` or if the
|
||||
/// count of hashes is less than 15.
|
||||
#[allow(clippy::similar_names)]
|
||||
pub fn read_from_io<R>(reader: R) -> Result<Self>
|
||||
where
|
||||
R: Read,
|
||||
{
|
||||
let buf_reader = BufReader::new(reader);
|
||||
let lines: Vec<String> = buf_reader
|
||||
.lines()
|
||||
.collect::<std::result::Result<Vec<_>, std::io::Error>>()?;
|
||||
let hashes: Vec<UvKeyHashV1> = lines
|
||||
.into_iter()
|
||||
.map(UvKeyHashV1::try_from)
|
||||
.collect::<std::result::Result<Vec<UvKeyHashV1>, Error>>()?;
|
||||
let hashes_count = hashes.len();
|
||||
if hashes_count < 15 {
|
||||
return Err(Error::InvalidUvKeyHashes);
|
||||
}
|
||||
|
||||
let [pchkh, pbhkh, res @ ..]: [UvKeyHashV1; 15] =
|
||||
hashes.try_into().map_err(|_| Error::InvalidUvKeyHashes)?;
|
||||
Ok(Self { pchkh, pbhkh, res })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::Cursor;
|
||||
|
||||
use pv::misc::decode_hex;
|
||||
|
||||
use crate::{pv_utils::uv_keys::UvKeyHashV1, uvdata::UvKeyHashesV1};
|
||||
|
||||
#[test]
|
||||
fn from_reader() {
|
||||
let data = "0b729fd62241b339840d61b964a06bb6a1fd4976d9ebea2b4fb48d44de3a2461
|
||||
8ec6bc2f77d5d6474b1417cf0a8c914f576245a5b9bb0eefacc7b821483ece7d
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000000000000000000000000000000000000000000
|
||||
";
|
||||
let result = UvKeyHashesV1::read_from_io(Cursor::new(data)).expect("should not fail");
|
||||
assert_eq!(
|
||||
result,
|
||||
UvKeyHashesV1 {
|
||||
pchkh: UvKeyHashV1::new(
|
||||
decode_hex("0b729fd62241b339840d61b964a06bb6a1fd4976d9ebea2b4fb48d44de3a2461")
|
||||
.unwrap()
|
||||
)
|
||||
.unwrap(),
|
||||
pbhkh: UvKeyHashV1::new(
|
||||
decode_hex("8ec6bc2f77d5d6474b1417cf0a8c914f576245a5b9bb0eefacc7b821483ece7d")
|
||||
.unwrap()
|
||||
)
|
||||
.unwrap(),
|
||||
res: [UvKeyHashV1::UV_KEY_HASH_NULL; 13],
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
193
rust/pvimg/src/pv_utils/uvdata.rs
Normal file
193
rust/pvimg/src/pv_utils/uvdata.rs
Normal file
@@ -0,0 +1,193 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use pv::request::{
|
||||
decrypt_aead, derive_aes256_gcm_key, encrypt_aead,
|
||||
openssl::pkey::{PKey, PKeyRef, Private, Public},
|
||||
Confidential, SymKey, SymKeyType,
|
||||
};
|
||||
|
||||
use super::se_hdr::{SeHdrBinV1, SeHdrData, SeHdrVersioned};
|
||||
use crate::pv_utils::{
|
||||
error::{Error, Result},
|
||||
serializing::deserialize_from_bytes,
|
||||
};
|
||||
|
||||
/// Trait to be used for Authenticated Encryption with Associated Data (AEAD)
|
||||
/// data structures.
|
||||
#[enum_dispatch]
|
||||
pub trait AeadCipherTrait {
|
||||
/// Returns the AEAD key type used by the data structure.
|
||||
fn aead_key_type(&self) -> SymKeyType;
|
||||
|
||||
/// Returns the AEAD tag size used by the data structure.
|
||||
fn aead_tag_size(&self) -> usize;
|
||||
|
||||
/// Returns the initialization vector (IV) used for AEAD
|
||||
/// encryption/decryption.
|
||||
fn iv(&self) -> &[u8];
|
||||
}
|
||||
|
||||
/// Trait to be used for AEAD cipher data
|
||||
#[enum_dispatch]
|
||||
pub trait AeadDataTrait {
|
||||
/// Returns the authenticated associated data.
|
||||
fn aad(&self) -> Vec<u8>;
|
||||
|
||||
/// Returns the encrypted data.
|
||||
fn data(&self) -> Vec<u8>;
|
||||
|
||||
/// Returns the tag data.
|
||||
fn tag(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
/// Trait to be used for AEAD plaintext data
|
||||
#[enum_dispatch]
|
||||
pub trait AeadPlainDataTrait {
|
||||
/// Returns the authenticated associated data.
|
||||
fn aad(&self) -> Vec<u8>;
|
||||
|
||||
/// Returns the unencrypted data.
|
||||
fn data(&self) -> Confidential<Vec<u8>>;
|
||||
|
||||
/// Returns the tag data.
|
||||
fn tag(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
/// Key exchange related methods
|
||||
#[enum_dispatch]
|
||||
pub trait KeyExchangeTrait {
|
||||
/// Checks if a public key was used.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the public key cannot be converted
|
||||
/// into a hash.
|
||||
fn contains<K: AsRef<PKeyRef<Public>>>(&self, key: K) -> Result<bool>;
|
||||
|
||||
/// Checks if the hash of a public key was used.
|
||||
fn contains_hash<H: AsRef<[u8]>>(&self, hash: H) -> bool;
|
||||
|
||||
/// Returns the public key from the party (e.g. guest owner) that is stored
|
||||
/// in the data structed.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the public key cannot be
|
||||
/// reconstructed.
|
||||
fn cust_pub_key(&mut self) -> Result<PKey<Public>>;
|
||||
|
||||
/// Returns the key type of the exchanged key.
|
||||
fn key_type(&self) -> SymKeyType;
|
||||
|
||||
/// Derive the key.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if there is no customer public key is
|
||||
/// available or the key derivations fails.
|
||||
fn derive_key<K: AsRef<PKeyRef<Private>>>(&mut self, other_priv_key: K) -> Result<SymKey> {
|
||||
match self.key_type() {
|
||||
SymKeyType::Aes256Gcm => Ok(derive_aes256_gcm_key(
|
||||
other_priv_key.as_ref(),
|
||||
self.cust_pub_key()?.as_ref(),
|
||||
)?
|
||||
.into()),
|
||||
_ => unreachable!("BUG"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait to be used for plain UV data.
|
||||
#[enum_dispatch]
|
||||
pub trait UvDataPlainTrait:
|
||||
AeadPlainDataTrait + AeadCipherTrait + KeyExchangeTrait + Clone
|
||||
{
|
||||
/// Returned type by [`Self::encrypt`].
|
||||
type C: UvDataTrait;
|
||||
|
||||
/// Encrypt the plain data.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the passed `key` has the wrong
|
||||
/// key type or the encryption fails.
|
||||
fn encrypt(&self, key: &SymKey) -> Result<Self::C>
|
||||
where
|
||||
<Self as UvDataPlainTrait>::C: for<'a> deku::DekuContainerRead<'a>,
|
||||
{
|
||||
if key.key_type() != self.aead_key_type() {
|
||||
return Err(Error::UnexpectedKeyType {
|
||||
given: self.key_type().to_string(),
|
||||
expected: self.aead_key_type().to_string(),
|
||||
});
|
||||
}
|
||||
let aad = self.aad();
|
||||
let unecrypted_data = self.data();
|
||||
let iv = self.iv();
|
||||
let result = encrypt_aead(key, iv, &aad, unecrypted_data.value())?;
|
||||
Self::C::try_from_data(&result.into_buf())
|
||||
}
|
||||
|
||||
/// Parses and converts the data into an instance of [`Self`] if possible.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the data could not parsed or
|
||||
/// converted.
|
||||
fn try_from_data<'a>(data: &'a [u8]) -> Result<Self>
|
||||
where
|
||||
Self: deku::DekuContainerRead<'a> + Sized,
|
||||
{
|
||||
deserialize_from_bytes(data)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait to be used for (cipher) UV data.
|
||||
#[enum_dispatch]
|
||||
pub trait UvDataTrait: AeadDataTrait + AeadCipherTrait + KeyExchangeTrait + Clone {
|
||||
/// Returned type by [`Self::decrypt`].
|
||||
type P: UvDataPlainTrait;
|
||||
|
||||
/// Decrypt the UV data.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the passed `key` has the wrong key
|
||||
/// type or the decryption fails.
|
||||
fn decrypt(&self, key: &SymKey) -> Result<Self::P>
|
||||
where
|
||||
<Self as UvDataTrait>::P: for<'a> deku::DekuContainerRead<'a>,
|
||||
{
|
||||
if key.key_type() != self.aead_key_type() {
|
||||
return Err(Error::UnexpectedKeyType {
|
||||
given: key.key_type().to_string(),
|
||||
expected: self.aead_key_type().to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let tag_size = self.aead_tag_size();
|
||||
let aad = self.aad();
|
||||
let unecrypted_data = self.data();
|
||||
let iv = self.iv();
|
||||
let tag = self.tag();
|
||||
assert_eq!(tag.len(), tag_size);
|
||||
let result = decrypt_aead(key, iv, &aad, &unecrypted_data, &tag)?;
|
||||
Self::P::try_from_data(result.into_buf().value())
|
||||
}
|
||||
|
||||
/// Parses and converts the data into an instance of [`Self`] if possible.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the data could not parsed or
|
||||
/// converted.
|
||||
fn try_from_data<'a>(data: &'a [u8]) -> Result<Self>
|
||||
where
|
||||
Self: deku::DekuContainerRead<'a> + Sized,
|
||||
{
|
||||
deserialize_from_bytes(data)
|
||||
}
|
||||
}
|
||||
144
rust/pvimg/src/pv_utils/uvdata_builder.rs
Normal file
144
rust/pvimg/src/pv_utils/uvdata_builder.rs
Normal file
@@ -0,0 +1,144 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Copyright IBM Corp. 2024
|
||||
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use openssl::pkey::{PKey, PKeyRef, Private, Public};
|
||||
use pv::request::{Confidential, SymKey};
|
||||
|
||||
use super::Error;
|
||||
use crate::pv_utils::{
|
||||
error::Result,
|
||||
se_hdr::SeHdrData,
|
||||
uvdata::{AeadCipherTrait, UvDataPlainTrait},
|
||||
};
|
||||
|
||||
#[enum_dispatch]
|
||||
pub trait AeadCipherBuilderTrait: AeadCipherTrait {
|
||||
fn set_iv(&mut self, iv: &[u8]) -> Result<()>;
|
||||
fn generate_aead_key(&self) -> Result<SymKey> {
|
||||
Ok(SymKey::random(self.aead_key_type())?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Key exchange related methods
|
||||
#[enum_dispatch]
|
||||
pub trait KeyExchangeBuilderTrait {
|
||||
fn add_keyslot(
|
||||
&mut self,
|
||||
hostkey: &PKeyRef<Public>,
|
||||
aead_key: &SymKey,
|
||||
priv_key: &PKeyRef<Private>,
|
||||
) -> Result<()>;
|
||||
fn clear_keyslots(&mut self) -> Result<()>;
|
||||
fn generate_private_key(&self) -> Result<PKey<Private>>;
|
||||
fn set_cust_public_key(&mut self, key: &PKeyRef<Private>) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct UvDataBuilder<
|
||||
'a,
|
||||
T: KeyExchangeBuilderTrait + AeadCipherBuilderTrait,
|
||||
K = PKeyRef<Public>,
|
||||
P = PKey<Private>,
|
||||
> {
|
||||
pub(crate) expert_mode: bool,
|
||||
pub(crate) prot_key: SymKey,
|
||||
pub(crate) priv_key: P,
|
||||
pub(crate) target_keys: Vec<&'a K>,
|
||||
pub(crate) plain_data: T,
|
||||
}
|
||||
|
||||
impl<T: KeyExchangeBuilderTrait + AeadCipherBuilderTrait, K, P> UvDataBuilder<'_, T, K, P> {
|
||||
/// Enable expert mode - this is required for specifying PSW, etc.
|
||||
pub fn i_know_what_i_am_doing(&mut self) {
|
||||
self.expert_mode = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: std::fmt::Debug + KeyExchangeBuilderTrait + AeadCipherBuilderTrait + UvDataPlainTrait>
|
||||
std::fmt::Debug for UvDataBuilder<'_, T>
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("UvDataBuilder")
|
||||
.field("expert_mode", &self.expert_mode)
|
||||
.field("prot_key", &self.prot_key)
|
||||
.field("plain_data", &self.plain_data)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: KeyExchangeBuilderTrait + AeadCipherBuilderTrait> UvDataBuilder<'a, T> {
|
||||
pub fn add_hostkeys<P: AsRef<PKeyRef<Public>>>(
|
||||
&mut self,
|
||||
hostkeys: &'a [P],
|
||||
) -> Result<&mut Self> {
|
||||
for hk in hostkeys {
|
||||
self.plain_data
|
||||
.add_keyslot(hk.as_ref(), &self.prot_key, &self.priv_key)?;
|
||||
self.target_keys.push(hk.as_ref());
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn with_iv(&mut self, iv: &[u8]) -> Result<&mut Self> {
|
||||
if !self.expert_mode {
|
||||
return Err(Error::NonExpertMode);
|
||||
}
|
||||
self.plain_data.set_iv(iv)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn update_target_key_slots(&mut self) -> Result<()> {
|
||||
self.plain_data.clear_keyslots()?;
|
||||
for hk in &self.target_keys {
|
||||
self.plain_data
|
||||
.add_keyslot(hk, &self.prot_key, &self.priv_key)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn with_aead_key(&mut self, data: Confidential<Vec<u8>>) -> Result<&mut Self> {
|
||||
if !self.expert_mode {
|
||||
return Err(Error::NonExpertMode);
|
||||
}
|
||||
let key = SymKey::try_from_data(self.plain_data.aead_key_type(), data)?;
|
||||
self.prot_key = key;
|
||||
self.update_target_key_slots()?;
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn with_priv_key(&mut self, priv_key: &PKeyRef<Private>) -> Result<&mut Self> {
|
||||
if !self.expert_mode {
|
||||
return Err(Error::NonExpertMode);
|
||||
}
|
||||
self.plain_data.set_cust_public_key(priv_key)?;
|
||||
self.priv_key = priv_key.to_owned();
|
||||
self.update_target_key_slots()?;
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub const fn prot_key(&self) -> &SymKey {
|
||||
&self.prot_key
|
||||
}
|
||||
|
||||
pub fn priv_key(&self) -> &PKeyRef<Private> {
|
||||
self.priv_key.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for the builder pattern.
|
||||
pub trait BuilderTrait {
|
||||
/// Data structure to construct
|
||||
type T;
|
||||
|
||||
/// Builds the type [`Self::T`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the data structure could not be
|
||||
/// build.
|
||||
fn build(self) -> Result<Self::T>;
|
||||
}
|
||||
Reference in New Issue
Block a user