diff --git a/CHANGELOG.md b/CHANGELOG.md index ae5c2d55..02ddfad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,19 @@ +*Unreleased* + +Dependency upgrades, logging and backup/recovery improvements. +## v0.2.0 +### Breaking changes +* [68](https://github.com/crypto-com/tmkms-light/pull/68) production logging in NE +* [55](https://github.com/crypto-com/tmkms-light/pull/55) SGX init and recovery commands paramater change +* [80](https://github.com/crypto-com/tmkms-light/pull/80) SGX backup and recovery in cloud environments + *March 18, 2021* + Tendermint-rs and prost dependencies upgraded. ## v0.1.2 *March 16, 2021* + The same as the initial released version, but with a few dependency upgrades ## v0.1.1 diff --git a/Cargo.lock b/Cargo.lock index 17b798ed..af71616d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,6 +62,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "aes-keywrap-rs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0179d260398111b7879868cabc56bc4045ad8111f0b4e5f656ba7f87d616e2fd" +dependencies = [ + "crypto2", + "hex", +] + [[package]] name = "aesm-client" version = "0.5.3" @@ -129,9 +139,9 @@ version = "0.1.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", ] [[package]] @@ -145,6 +155,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + [[package]] name = "autocfg" version = "1.0.1" @@ -429,7 +445,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ - "autocfg", + "autocfg 1.0.1", "cfg-if 0.1.10", "crossbeam-utils 0.7.2", "lazy_static", @@ -468,7 +484,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg", + "autocfg 1.0.1", "cfg-if 0.1.10", "lazy_static", ] @@ -493,6 +509,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "crypto2" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a161713d9e77a76b166714fc1efd2a7d0a6830e8be57eb727b215076fb62e7a4" + [[package]] name = "ctr" version = "0.7.0" @@ -515,6 +537,34 @@ dependencies = [ "zeroize", ] +[[package]] +name = "dcap-ql" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0ebefb9625fa74940e07aeeddefda9f8081bd7dc9dbe782caff25b6f369f57" +dependencies = [ + "byteorder", + "dcap-ql-sys", + "failure", + "lazy_static", + "libc", + "num-derive 0.2.5", + "num-traits", + "sgx-isa", + "sgxs-loaders", +] + +[[package]] +name = "dcap-ql-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031b628528f3ae7e4fd99130b48066def8280d4c8e8576232e001af5a9df0232" +dependencies = [ + "num-derive 0.2.5", + "num-traits", + "sgx-isa", +] + [[package]] name = "digest" version = "0.9.0" @@ -570,12 +620,11 @@ dependencies = [ [[package]] name = "ed25519-dalek" version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +source = "git+https://github.com/crypto-com/ed25519-dalek?rev=5e6c9362ff33c2bbfe189a55e44fd2d68b463227#5e6c9362ff33c2bbfe189a55e44fd2d68b463227" dependencies = [ "curve25519-dalek", "ed25519", - "rand 0.7.3", + "rand", "serde", "serde_bytes", "sha2", @@ -610,6 +659,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "env_logger" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" +dependencies = [ + "log", + "regex", +] + [[package]] name = "eyre" version = "0.6.5" @@ -636,9 +695,9 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", "synstructure", ] @@ -761,11 +820,11 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121" dependencies = [ - "autocfg", + "autocfg 1.0.1", "proc-macro-hack", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", ] [[package]] @@ -786,7 +845,7 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" dependencies = [ - "autocfg", + "autocfg 1.0.1", "futures 0.1.31", "futures-channel", "futures-core", @@ -1004,7 +1063,7 @@ version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" dependencies = [ - "autocfg", + "autocfg 1.0.1", "hashbrown", ] @@ -1053,6 +1112,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -1070,6 +1132,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "libm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" + [[package]] name = "log" version = "0.4.14" @@ -1118,7 +1186,7 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" dependencies = [ - "autocfg", + "autocfg 1.0.1", ] [[package]] @@ -1127,7 +1195,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" dependencies = [ - "autocfg", + "autocfg 1.0.1", ] [[package]] @@ -1149,7 +1217,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" dependencies = [ "adler", - "autocfg", + "autocfg 1.0.1", ] [[package]] @@ -1390,15 +1458,56 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "num-bigint" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512" +dependencies = [ + "autocfg 1.0.1", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4547ee5541c18742396ae2c895d0717d0f886d8823b8399cdaf7b07d63ad0480" +dependencies = [ + "autocfg 0.1.7", + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "serde", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-derive" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eafd0b45c5537c3ba526f79d3e75120036502bebacbb3f3220914067ce39dbf2" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + [[package]] name = "num-derive" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", ] [[package]] @@ -1407,7 +1516,18 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ - "autocfg", + "autocfg 1.0.1", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg 1.0.1", + "num-integer", "num-traits", ] @@ -1417,7 +1537,8 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ - "autocfg", + "autocfg 1.0.1", + "libm", ] [[package]] @@ -1477,13 +1598,24 @@ version = "0.9.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6b0d6fb7d80f877617dfcb014e605e2b5ab2fb0afdf27935219bb6bd984cb98" dependencies = [ - "autocfg", + "autocfg 1.0.1", "cc", "libc", "pkg-config", "vcpkg", ] +[[package]] +name = "pem" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +dependencies = [ + "base64", + "once_cell", + "regex", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -1514,9 +1646,9 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", ] [[package]] @@ -1578,9 +1710,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", "version_check", ] @@ -1590,8 +1722,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.27", + "quote 1.0.9", "version_check", ] @@ -1607,13 +1739,22 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + [[package]] name = "proc-macro2" version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" dependencies = [ - "unicode-xid", + "unicode-xid 0.2.2", ] [[package]] @@ -1634,9 +1775,9 @@ checksum = "169a15f3008ecb5160cba7d37bcd690a7601b6d30cfb87a117d45e59d52af5d4" dependencies = [ "anyhow", "itertools", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", ] [[package]] @@ -1686,26 +1827,44 @@ dependencies = [ "tempfile", ] +[[package]] +name = "quickcheck" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" +dependencies = [ + "env_logger", + "log", + "rand", +] + +[[package]] +name = "quickcheck_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9" +dependencies = [ + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", +] + [[package]] name = "quote" -version = "1.0.9" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" dependencies = [ - "proc-macro2", + "proc-macro2 0.4.30", ] [[package]] -name = "rand" -version = "0.7.3" +name = "quote" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc 0.2.0", + "proc-macro2 1.0.27", ] [[package]] @@ -1715,19 +1874,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" dependencies = [ "libc", - "rand_chacha 0.3.0", + "rand_chacha", "rand_core 0.6.2", - "rand_hc 0.3.0", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_hc", ] [[package]] @@ -1758,15 +1907,6 @@ dependencies = [ "getrandom 0.2.3", ] -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - [[package]] name = "rand_hc" version = "0.3.0" @@ -1782,7 +1922,7 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" dependencies = [ - "autocfg", + "autocfg 1.0.1", "crossbeam-deque 0.8.0", "either", "rayon-core", @@ -1855,6 +1995,27 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rsa" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ef841a26fc5d040ced0417c6c6a64ee851f42489df11cdf0218e545b6f8d28" +dependencies = [ + "byteorder", + "digest", + "lazy_static", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pem", + "rand", + "serde", + "simple_asn1", + "subtle", + "zeroize", +] + [[package]] name = "rustc-demangle" version = "0.1.19" @@ -1901,16 +2062,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "secrecy" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0673d6a6449f5e7d12a1caf424fd9363e2af3a4953023ed455e3c4beef4597c0" -dependencies = [ - "serde", - "zeroize", -] - [[package]] name = "security-framework" version = "2.2.0" @@ -1991,9 +2142,9 @@ version = "1.0.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", ] [[package]] @@ -2013,9 +2164,9 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", ] [[package]] @@ -2025,6 +2176,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55adf0940448a33eac98b3c84ab2e567c1fbc086750bcfd72702d6facc39bb77" dependencies = [ "bitflags", + "serde", ] [[package]] @@ -2109,6 +2261,18 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f0242b8e50dd9accdd56170e94ca1ebd223b098eb9c83539a6e367d0f36ae68" +[[package]] +name = "simple_asn1" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0e9076e5242ff5a58e854cb478ea9caebce01088f86d3d9c6ad336b7655263" +dependencies = [ + "chrono", + "num-bigint", + "num-traits", + "thiserror", +] + [[package]] name = "slab" version = "0.4.3" @@ -2132,6 +2296,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "standback" version = "0.2.17" @@ -2161,11 +2331,11 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.27", + "quote 1.0.9", "serde", "serde_derive", - "syn", + "syn 1.0.72", ] [[package]] @@ -2175,13 +2345,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" dependencies = [ "base-x", - "proc-macro2", - "quote", + "proc-macro2 1.0.27", + "quote 1.0.9", "serde", "serde_derive", "serde_json", "sha1", - "syn", + "syn 1.0.72", ] [[package]] @@ -2215,9 +2385,9 @@ checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" dependencies = [ "heck", "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", ] [[package]] @@ -2235,15 +2405,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", +] + [[package]] name = "syn" version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", + "proc-macro2 1.0.27", + "quote 1.0.9", + "unicode-xid 0.2.2", ] [[package]] @@ -2252,10 +2433,10 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", + "unicode-xid 0.2.2", ] [[package]] @@ -2282,7 +2463,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ "cfg-if 1.0.0", "libc", - "rand 0.8.3", + "rand", "redox_syscall", "remove_dir_all", "winapi 0.3.9", @@ -2353,7 +2534,7 @@ dependencies = [ "anomaly", "bytes 1.0.1", "chrono", - "num-derive", + "num-derive 0.3.3", "num-traits", "prost", "prost-types", @@ -2387,9 +2568,9 @@ version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", ] [[package]] @@ -2443,10 +2624,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" dependencies = [ "proc-macro-hack", - "proc-macro2", - "quote", + "proc-macro2 1.0.27", + "quote 1.0.9", "standback", - "syn", + "syn 1.0.72", ] [[package]] @@ -2466,7 +2647,7 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tmkms-light" -version = "0.1.2" +version = "0.2.0" dependencies = [ "anomaly", "ed25519-dalek", @@ -2482,15 +2663,21 @@ dependencies = [ [[package]] name = "tmkms-light-sgx-app" -version = "0.1.2" +version = "0.2.0" dependencies = [ + "aes", "aes-gcm-siv", + "aes-keywrap-rs", "anomaly", + "base64", "ed25519-dalek", - "rand 0.7.3", - "secrecy", + "quickcheck", + "quickcheck_macros", + "rand", + "rsa", "serde_json", "sgx-isa", + "sha2", "subtle", "subtle-encoding", "tendermint-p2p", @@ -2503,14 +2690,16 @@ dependencies = [ [[package]] name = "tmkms-light-sgx-runner" -version = "0.1.2" +version = "0.2.0" dependencies = [ "aesm-client", "anomaly", + "base64", + "dcap-ql", "ed25519", "ed25519-dalek", "enclave-runner", - "secrecy", + "rsa", "serde", "serde_json", "sgx-isa", @@ -2530,7 +2719,7 @@ dependencies = [ [[package]] name = "tmkms-nitro-enclave" -version = "0.1.2" +version = "0.2.0" dependencies = [ "anomaly", "aws-ne-sys", @@ -2550,7 +2739,7 @@ dependencies = [ [[package]] name = "tmkms-nitro-helper" -version = "0.1.2" +version = "0.2.0" dependencies = [ "anomaly", "bytes 0.5.6", @@ -2561,8 +2750,7 @@ dependencies = [ "mz_rusoto_credential", "mz_rusoto_kms", "nix 0.20.0", - "rand_core 0.5.1", - "secrecy", + "rand_core 0.6.2", "serde", "serde_json", "structopt", @@ -2583,11 +2771,11 @@ dependencies = [ [[package]] name = "tmkms-softsign" -version = "0.1.2" +version = "0.2.0" dependencies = [ "anomaly", "ed25519-dalek", - "rand_core 0.5.1", + "rand_core 0.6.2", "serde", "serde_json", "structopt", @@ -2644,9 +2832,9 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", ] [[package]] @@ -2707,9 +2895,9 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", ] [[package]] @@ -2822,6 +3010,12 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "unicode-xid" version = "0.2.2" @@ -2934,9 +3128,9 @@ dependencies = [ "bumpalo", "lazy_static", "log", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", "wasm-bindgen-shared", ] @@ -2946,7 +3140,7 @@ version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" dependencies = [ - "quote", + "quote 1.0.9", "wasm-bindgen-macro-support", ] @@ -2956,9 +3150,9 @@ version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3055,8 +3249,8 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2c1e130bebaeab2f23886bf9acbaca14b092408c452543c857f66399cd6dab1" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.27", + "quote 1.0.9", + "syn 1.0.72", "synstructure", ] diff --git a/Cargo.toml b/Cargo.toml index f6ad4f95..1b1fce28 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tmkms-light" -version = "0.1.2" +version = "0.2.0" authors = ["Tomas Tauber <2410580+tomtau@users.noreply.github.com>"] edition = "2018" @@ -20,4 +20,8 @@ tracing = "0.1" [workspace] members = ["providers/softsign", "providers/sgx/sgx-app", "providers/sgx/sgx-runner", "providers/nitro/nitro-enclave", "providers/nitro/nitro-helper"] -default-members = ["providers/softsign"] \ No newline at end of file +default-members = ["providers/softsign"] + +[patch.crates-io] +# TODO: use upstream when this is merged: https://github.com/dalek-cryptography/ed25519-dalek/pull/160 +ed25519-dalek = { git = "https://github.com/crypto-com/ed25519-dalek", rev = "5e6c9362ff33c2bbfe189a55e44fd2d68b463227" } \ No newline at end of file diff --git a/README.md b/README.md index 3db5de2b..534b3d84 100644 --- a/README.md +++ b/README.md @@ -78,20 +78,8 @@ $ tmkms-light-sgx-runner init -b bech32_prefix -p "bech32"
With cloud backup key -One may provide flag `-e backup_key_path` which is to encrypt and decrypt `consensus-key.backup` in directory specified in `-k backup_data_path` - -One may also want to use [Azure Key Vault secret](https://docs.microsoft.com/en-us/azure/key-vault/secrets/quick-create-python) or its equivalents to store your backup key. - -> :warning: After storing your backup key in Azure Key Vault or its equivalents, make sure to delete the backup key from any local environments to prevent its potential leakage. - -> :warning: The instructions do not cover the adequate secure policy setup in Azure Key Vault or its equivalents, so make sure the policy restricts the backup key access only to particular instances during a recovery process. - -> :warning: The current cloud key backup implementation is very rudimentary and potentially dangerous -- *use at your own risk* -- we plan to enhance this implementation once solutions for "cloud sealing" become generally available. -### Init -```bash -$ tmkms-light-sgx-runner init -b bech32_prefix -p "bech32" -e backup_key_path -k backup_data_path -``` -Or follow the example python script to run [init](script/tmkms-sgx/init.py) +TODO: these instructions are to be enhanced later on. +One may provide flag `-e backup_key_path` which is to encrypt and decrypt `consensus-key.backup` in directory specified in `-k backup_data_path`. Before `init`, you will need to run `tmkms-light-sgx-runner cloud-wrap -s wrap_key_path -d` ### Recover ```bash diff --git a/providers/nitro/nitro-enclave/Cargo.toml b/providers/nitro/nitro-enclave/Cargo.toml index 8a046994..0ac69c83 100644 --- a/providers/nitro/nitro-enclave/Cargo.toml +++ b/providers/nitro/nitro-enclave/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tmkms-nitro-enclave" -version = "0.1.2" +version = "0.2.0" authors = [ "Tomas Tauber <2410580+tomtau@users.noreply.github.com>" ] edition = "2018" diff --git a/providers/nitro/nitro-helper/Cargo.toml b/providers/nitro/nitro-helper/Cargo.toml index 413fb221..f33636e7 100644 --- a/providers/nitro/nitro-helper/Cargo.toml +++ b/providers/nitro/nitro-helper/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tmkms-nitro-helper" -version = "0.1.2" +version = "0.2.0" authors = [ "Tomas Tauber <2410580+tomtau@users.noreply.github.com>" ] edition = "2018" @@ -18,8 +18,7 @@ mz_rusoto_credential = { version = "0.46", optional = true } mz_rusoto_kms = { version = "0.46", optional = true } mz_rusoto_core = { version = "0.46", optional = true } nix = "0.20" -rand_core = { version = "0.5", features = [ "std" ] } -secrecy = { version = "0.7", features = [ "serde" ] } +rand_core = { version = "0.6", features = [ "std" ] } serde = { version = "1", features = [ "derive" ] } serde_json = "1" structopt = "0.3" diff --git a/providers/sgx/sgx-app/Cargo.toml b/providers/sgx/sgx-app/Cargo.toml index d44d6919..118199b6 100644 --- a/providers/sgx/sgx-app/Cargo.toml +++ b/providers/sgx/sgx-app/Cargo.toml @@ -1,16 +1,19 @@ [package] name = "tmkms-light-sgx-app" -version = "0.1.2" +version = "0.2.0" authors = ["Tomas Tauber <2410580+tomtau@users.noreply.github.com>", "Linfeng Yuan "] edition = "2018" [target.'cfg(target_env = "sgx")'.dependencies] +aes = "0.7" aes-gcm-siv = "0.10" anomaly = "0.2" +base64 = "0.13" ed25519-dalek = "1" -rand = "0.7" -secrecy = "0.7" +rand = "0.8" +rsa = "0.4" serde_json = "1" +sha2 = "0.9" sgx-isa = { version = "0.3", features = ["sgxstd"] } subtle = "2" subtle-encoding = "0.5" @@ -19,4 +22,9 @@ tmkms-light-sgx-runner = { path = "../sgx-runner" } tmkms-light = { path = "../../.." } tracing = "0.1" tracing-subscriber = "0.2" -zeroize = "1" \ No newline at end of file +zeroize = "1" + +[dev-dependencies] +aes-keywrap-rs = "0.2" +quickcheck = "1" +quickcheck_macros = "1" diff --git a/providers/sgx/sgx-app/src/main.rs b/providers/sgx/sgx-app/src/main.rs index e616aa2e..1925d994 100644 --- a/providers/sgx/sgx-app/src/main.rs +++ b/providers/sgx/sgx-app/src/main.rs @@ -20,19 +20,11 @@ fn main() -> std::io::Result<()> { return Err(std::io::ErrorKind::Other.into()); } let request = request.unwrap(); - let mcloud_key = args.next(); - let cloud_key = if let Some(k) = mcloud_key { - let decoded_vec = subtle_encoding::hex::decode(k) - .map_err(|_e| std::io::Error::from(std::io::ErrorKind::Other))?; - sgx_app::keypair_seal::CloudWrapKey::new(decoded_vec) - } else { - None - }; // "init" stream is provided by the enclave runner // user call extension (in sgx-runner) let init_conn = std::net::TcpStream::connect("init")?; tracing::info!("connected to init stream"); - if let Err(e) = sgx_app::entry(init_conn, request, cloud_key) { + if let Err(e) = sgx_app::entry(init_conn, request) { tracing::error!("error: {}", e); Err(e) } else { diff --git a/providers/sgx/sgx-app/src/sgx_app.rs b/providers/sgx/sgx-app/src/sgx_app.rs index b17b7349..0e13afa8 100644 --- a/providers/sgx/sgx-app/src/sgx_app.rs +++ b/providers/sgx/sgx-app/src/sgx_app.rs @@ -2,9 +2,12 @@ pub(crate) mod keypair_seal; /// state persistence helper; mod state; + +/// helpers for cloud deployments (where CPU affinitity isn't guaranteed) +mod cloud; use ed25519_dalek::Keypair; -use keypair_seal::CloudWrapKey; use rand::rngs::OsRng; +use sgx_isa::{Report, Targetinfo}; use std::{io, net::TcpStream, thread, time::Duration}; use subtle::ConstantTimeEq; use tendermint_p2p::secret_connection::{self, PublicKey, SecretConnection}; @@ -88,20 +91,38 @@ pub fn get_connection(secret_connection: Option<&RemoteConnectionConfig>) -> Box /// a simple req-rep handling loop /// `TcpStream` is either provided in tests or from the "init" /// enclave runner's user call extension. -/// TODO: no need to pass the host_response stream + cloud_backup_key for "Start" -pub fn entry( - mut host_response: TcpStream, - request: SgxInitRequest, - cloud_backup_key: Option, -) -> io::Result<()> { +/// TODO: no need to pass the host_response stream for "Start" +pub fn entry(mut host_response: TcpStream, request: SgxInitRequest) -> io::Result<()> { let mut csprng = OsRng {}; - match (request, cloud_backup_key) { - (SgxInitRequest::KeyGen, cbk) => { + match request { + SgxInitRequest::GenWrapKey { targetinfo } => { + let targetinfo = targetinfo.unwrap_or_else(|| Targetinfo::from(Report::for_self())); + let rsa_kp = cloud::generate_keypair(&mut csprng, targetinfo); + if let Ok((wrap_pub_key, wrap_key_sealed, pub_key_report)) = rsa_kp { + let response = SgxInitResponse::WrapKey { + wrap_key_sealed, + wrap_pub_key, + pub_key_report, + }; + match serde_json::to_vec(&response) { + Ok(v) => { + debug!("writing response"); + write_u16_payload(&mut host_response, &v)?; + } + Err(e) => { + error!("keygen error: {}", e); + } + } + } else { + error!("sealing failed"); + } + } + SgxInitRequest::KeyGen { cloud_backup } => { let kp = Keypair::generate(&mut csprng); let cloud_backup_key_data = - cbk.and_then(|key| keypair_seal::cloud_backup(&mut csprng, key, &kp).ok()); + cloud_backup.and_then(|key| cloud::cloud_backup(&mut csprng, key, &kp).ok()); if let Ok(sealed_key_data) = keypair_seal::seal(&mut csprng, &kp) { - let response = SgxInitResponse { + let response = SgxInitResponse::GenOrRecover { sealed_key_data, cloud_backup_key_data, }; @@ -118,11 +139,14 @@ pub fn entry( error!("sealing failed"); } } - (SgxInitRequest::CloudRecover { key_data }, Some(backup_key)) => { + SgxInitRequest::CloudRecover { + cloud_backup, + key_data, + } => { if let Ok(sealed_key_data) = - keypair_seal::seal_recover_cloud_backup(&mut csprng, backup_key, key_data) + cloud::reseal_recover_cloud(&mut csprng, cloud_backup, key_data) { - let response = SgxInitResponse { + let response = SgxInitResponse::GenOrRecover { sealed_key_data, cloud_backup_key_data: None, }; @@ -139,15 +163,12 @@ pub fn entry( error!("recovery failed"); } } - ( - SgxInitRequest::Start { - sealed_key, - config, - secret_connection, - initial_state, - }, - None, - ) => { + SgxInitRequest::Start { + sealed_key, + config, + secret_connection, + initial_state, + } => { let state_holder = state::StateHolder::new()?; if let Ok(keypair) = keypair_seal::unseal(&sealed_key) { let conn: Box = get_connection(secret_connection.as_ref()); @@ -170,10 +191,6 @@ pub fn entry( return Err(io::ErrorKind::Other.into()); } } - _ => { - error!("invalid command arguments"); - return Err(io::ErrorKind::Other.into()); - } } Ok(()) } @@ -181,49 +198,69 @@ pub fn entry( #[cfg(test)] mod tests { use super::*; - use rand::RngCore; use std::net::{TcpListener, TcpStream}; use tmkms_light::utils::read_u16_payload; + use tmkms_light_sgx_runner::CloudBackupKey; // can be run with `cargo test --target x86_64-fortanix-unknown-sgx` #[test] fn test_recover_flow() { - let mut csprng = OsRng {}; - let mut backup_key = vec![0u8; 16]; - csprng.fill_bytes(&mut backup_key); - let bk1 = CloudWrapKey::new(backup_key.clone()).unwrap(); - let bk2 = CloudWrapKey::new(backup_key).unwrap(); let listener = TcpListener::bind("127.0.0.1:0").unwrap(); let addr = listener.local_addr().unwrap(); - + let (sender, receiver) = std::sync::mpsc::channel(); let handler = std::thread::spawn(move || { - entry( - TcpStream::connect(addr).unwrap(), - SgxInitRequest::KeyGen, - Some(bk1), - ) + while let Ok(Some(req)) = receiver.recv() { + entry(TcpStream::connect(addr).unwrap(), req).expect("ok entry"); + } }); + sender + .send(Some(SgxInitRequest::GenWrapKey { targetinfo: None })) + .expect("send request0"); + let (mut stream_signer, _) = listener.accept().unwrap(); + let resp1 = read_u16_payload(&mut stream_signer).expect("response0"); + let response1: SgxInitResponse = serde_json::from_slice(&resp1).expect("response0"); + let (sealed_rsa_key, pubkey) = match response1 { + SgxInitResponse::WrapKey { + wrap_key_sealed, + wrap_pub_key, + .. + } => (wrap_key_sealed, wrap_pub_key), + _ => panic!("wrong response"), + }; + let mut csprng = OsRng {}; + + let backup_key = cloud::tests::get_wrapped_key(&mut csprng, pubkey); + let cloud_backup = CloudBackupKey { + sealed_rsa_key, + backup_key, + }; + let cloud_backup2 = cloud_backup.clone(); + sender + .send(Some(SgxInitRequest::KeyGen { + cloud_backup: Some(cloud_backup), + })) + .expect("send request1"); let (mut stream_signer, _) = listener.accept().unwrap(); let resp1 = read_u16_payload(&mut stream_signer).expect("response1"); let response1: SgxInitResponse = serde_json::from_slice(&resp1).expect("response1"); - let r1_seal = response1.sealed_key_data.clone(); - let _ = handler.join(); - let handler = std::thread::spawn(move || { - entry( - TcpStream::connect(addr).unwrap(), - SgxInitRequest::CloudRecover { - key_data: response1.cloud_backup_key_data.expect("backup"), - }, - Some(bk2), - ) - }); + let (seal_key_request, cloud_backup_key_data) = + response1.get_gen_response().expect("response1"); + sender + .send(Some(SgxInitRequest::CloudRecover { + cloud_backup: cloud_backup2, + key_data: cloud_backup_key_data.expect("backup"), + })) + .expect("send request2"); + let (mut stream_signer, _) = listener.accept().unwrap(); let resp2 = read_u16_payload(&mut stream_signer).expect("response2"); let response2: SgxInitResponse = serde_json::from_slice(&resp2).expect("response2"); + let (seal_key_request2, _) = response2.get_gen_response().expect("response2"); + sender.send(None).expect("send request3"); let _ = handler.join(); assert_eq!( - r1_seal.seal_key_request.keyid, - response2.sealed_key_data.seal_key_request.keyid + seal_key_request.seal_key_request.keyid, + seal_key_request2.seal_key_request.keyid ); } @@ -240,26 +277,4 @@ mod tests { kp.public ); } - - #[test] - fn test_recover() { - let mut csprng = OsRng {}; - let kp = Keypair::generate(&mut csprng); - let sealed_data = keypair_seal::seal(&mut csprng, &kp).unwrap(); - let mut backup_key = vec![0u8; 16]; - csprng.fill_bytes(&mut backup_key); - let bk1 = CloudWrapKey::new(backup_key.clone()).unwrap(); - let bk2 = CloudWrapKey::new(backup_key).unwrap(); - let backup_data = keypair_seal::cloud_backup(&mut csprng, bk1, &kp).unwrap(); - let recovered_sealed_data = - keypair_seal::seal_recover_cloud_backup(&mut csprng, bk2, backup_data).unwrap(); - assert_eq!( - keypair_seal::unseal(&sealed_data).unwrap().public, - kp.public - ); - assert_eq!( - keypair_seal::unseal(&recovered_sealed_data).unwrap().public, - kp.public - ); - } } diff --git a/providers/sgx/sgx-app/src/sgx_app/cloud.rs b/providers/sgx/sgx-app/src/sgx_app/cloud.rs new file mode 100644 index 00000000..847bffe6 --- /dev/null +++ b/providers/sgx/sgx-app/src/sgx_app/cloud.rs @@ -0,0 +1,350 @@ +/// RFC3394/RFC5649 key unwrapping of 32-byte keys +mod keywrap; + +use crate::sgx_app::keypair_seal::seal; +use aes::cipher::generic_array::typenum::U32; +use aes::cipher::generic_array::GenericArray; +use aes_gcm_siv::{ + aead::{Aead, NewAead, Payload}, + Aes256GcmSiv, +}; +use ed25519_dalek::{Keypair, PublicKey, SecretKey}; +use rand::rngs::OsRng; +use rand::RngCore; +use rsa::{PaddingScheme, PrivateKeyEncoding, RSAPrivateKey, RSAPublicKey}; +use sgx_isa::ErrorCode; +use sgx_isa::{Report, Targetinfo}; +use sha2::{Digest, Sha256}; +use tmkms_light_sgx_runner::{ + get_claim, CloudBackupKey, CloudBackupKeyData, CloudBackupSeal, SealedKeyData, +}; +use zeroize::Zeroize; + +/// Possible errors in cloud backup or recovery +#[derive(Debug)] +pub enum CloudError { + /// RSA generation or PKCS1 issues + SecretGenerationError, + /// errors from local CPU sealing + SealingError(ErrorCode), + /// RFC3394/RFC5649 AES-KWP unwrap failed + UnwrapError(keywrap::UnwrapError), + /// RSA-OAEP+SHA-256 decryption failed + DecryptionError, + /// AES-GCM-SIV encryption failed + EncryptionError, +} + +/// Generates an RSA keypair (2048bit with e=65537), +/// seals the private key to a local CPU, +/// and gets a report for sha256 of the public key in JWE. +pub fn generate_keypair( + csprng: &mut OsRng, + targetinfo: Targetinfo, +) -> Result<(RSAPublicKey, SealedKeyData, Report), CloudError> { + const BITS: usize = 2048; + // default exponent 65537 + let mut priv_key = + RSAPrivateKey::new(csprng, BITS).map_err(|_| CloudError::SecretGenerationError)?; + let pub_key = RSAPublicKey::from(&priv_key); + + let claim_payload = get_claim(&pub_key); + let mut hasher = Sha256::new(); + hasher.update(claim_payload); + let result: GenericArray = hasher.finalize(); + let mut report_data = [0u8; 64]; + report_data[0..32].copy_from_slice(&result); + let report = Report::for_target(&targetinfo, &report_data); + let pkcs1 = priv_key.to_pkcs1(); + priv_key.zeroize(); + if let Ok(mut secret) = pkcs1 { + match crate::sgx_app::keypair_seal::seal_secret(csprng, &secret, result.into()) { + Ok(sealed_secret) => { + secret.zeroize(); + Ok((pub_key, sealed_secret, report)) + } + Err(e) => { + secret.zeroize(); + Err(CloudError::SealingError(e)) + } + } + } else { + Err(CloudError::SecretGenerationError) + } +} + +/// helper to decrypt the backup key +fn decrypt_wrapped_key( + priv_key: &RSAPrivateKey, + backup_data_seal: CloudBackupSeal, +) -> Result, CloudError> { + let mut wrapping_key = priv_key + .decrypt( + PaddingScheme::new_oaep::(), + &backup_data_seal.encrypted_symmetric_key, + ) + .map_err(|_| CloudError::DecryptionError)?; + let msealing_key = keywrap::aes256_unwrap_key_with_pad( + &wrapping_key, + &backup_data_seal.wrapped_cloud_sealing_key, + ); + wrapping_key.zeroize(); + msealing_key.map_err(CloudError::UnwrapError) +} + +/// As cloud vendors may not guarantee HW affinity, +/// this is optionally used to seal the keypair with the externally provided +/// key (e.g. injected from cloud HSM) with `Aes256GcmSiv`. +/// The payload encrypted with this key can then be used to recover +/// when the instance is relocated etc. +pub fn cloud_backup( + csprng: &mut OsRng, + backup: CloudBackupKey, + keypair: &Keypair, +) -> Result { + let mut priv_key_raw = crate::sgx_app::keypair_seal::unseal_secret(&backup.sealed_rsa_key) + .map_err(CloudError::SealingError)?; + let mpriv_key = RSAPrivateKey::from_pkcs1(&priv_key_raw); + priv_key_raw.zeroize(); + let mut priv_key = mpriv_key.map_err(|_| CloudError::SecretGenerationError)?; + let mbackup_key = decrypt_wrapped_key(&priv_key, backup.backup_key); + priv_key.zeroize(); + let mut gk = mbackup_key?; + let mut nonce = [0u8; 12]; + csprng.fill_bytes(&mut nonce); + let payload = Payload { + msg: keypair.secret.as_bytes(), + aad: keypair.public.as_bytes(), + }; + let nonce_ga = GenericArray::from_slice(&nonce); + let aead = Aes256GcmSiv::new(&gk); + let r = aead + .encrypt(nonce_ga, payload) + .map(|sealed_secret| CloudBackupKeyData { + sealed_secret, + nonce, + public_key: keypair.public, + }) + .map_err(|_| CloudError::EncryptionError); + gk.zeroize(); + r +} + +/// tries to recover the secret from the cloud backup data +/// and (re-)seal it for the current CPU +pub fn reseal_recover_cloud( + csprng: &mut OsRng, + backup: CloudBackupKey, + cloud_backup: CloudBackupKeyData, +) -> Result { + let mut priv_key_raw = crate::sgx_app::keypair_seal::unseal_secret(&backup.sealed_rsa_key) + .map_err(CloudError::SealingError)?; + let mpriv_key = RSAPrivateKey::from_pkcs1(&priv_key_raw); + priv_key_raw.zeroize(); + let mut priv_key = mpriv_key.map_err(|_| CloudError::SecretGenerationError)?; + let mbackup_key = decrypt_wrapped_key(&priv_key, backup.backup_key); + priv_key.zeroize(); + let backup_key = mbackup_key?; + seal_recover_cloud_backup(csprng, backup_key, cloud_backup).map_err(CloudError::SealingError) +} + +/// Recovers the backed up keypair (decrypt it using the externally +/// provided key, e.g. injected from cloud HSM) and seals it on that CPU. +pub fn seal_recover_cloud_backup( + csprng: &mut OsRng, + mut seal_key: GenericArray, + backup_data: CloudBackupKeyData, +) -> Result { + let nonce_ga = GenericArray::from_slice(&backup_data.nonce); + let aead = Aes256GcmSiv::new(&seal_key); + let payload = Payload { + msg: &backup_data.sealed_secret, + aad: backup_data.public_key.as_bytes(), + }; + if let Ok(mut secret_key) = aead.decrypt(nonce_ga, payload) { + seal_key.zeroize(); + let secret = SecretKey::from_bytes(&secret_key).map_err(|_| ErrorCode::InvalidSignature)?; + secret_key.zeroize(); + let public = PublicKey::from(&secret); + let mut kp = Keypair { secret, public }; + let sealed = seal(csprng, &kp); + kp.secret.zeroize(); + sealed + } else { + seal_key.zeroize(); + Err(ErrorCode::MacCompareFail) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use ed25519_dalek::Keypair; + use rand::RngCore; + use rsa::BigUint; + use rsa::PublicKey; + use rsa::PublicKeyParts; + + pub fn get_wrapped_key(csprng: &mut OsRng, rsa_pub: RSAPublicKey) -> CloudBackupSeal { + let mut wrap_key1 = [0u8; 32]; + csprng.fill_bytes(&mut wrap_key1); + + let mut wrap_key2 = [0u8; 32]; + csprng.fill_bytes(&mut wrap_key2); + let wrapped_key = aes_keywrap_rs::Aes256Kw::aes_wrap_key_with_pad(&wrap_key1, &wrap_key2) + .expect("wrap key"); + let mut wrapped_cloud_sealing_key = [0u8; 40]; + wrapped_cloud_sealing_key.copy_from_slice(&wrapped_key); + let enc_data = rsa_pub + .encrypt( + csprng, + PaddingScheme::new_oaep::(), + &wrap_key1[..], + ) + .expect("failed to encrypt"); + let mut encrypted_symmetric_key = [0u8; 256]; + encrypted_symmetric_key.copy_from_slice(&enc_data); + CloudBackupSeal { + encrypted_symmetric_key, + wrapped_cloud_sealing_key, + } + } + + #[test] + fn test_hash() { + let payload = "{\"kid\":\"wrapping-key\",\"kty\":\"RSA\",\"e\":\"AQAB\",\"n\":\"kyk2V71OnhuVJAZq5occtRdYxX6eGiR3qQ04UKTZQxesiU3UsnbCq7FURSEr5NTKaU1L3die6VzPn829jdVYiht55hiqsEPYrRutNtmc-dI111lPGmkSaN_WcrK9rScbNn1btnBytf6KkST5Qmeri_Ue_BBjdg_G_WPNFKy1Ds_8lDqDMl3JLHaEjtKA-OtCjNsClzqtavgMJcbxdvHqUB1grbYePM6HrlMyIY1wZUvmdZw3_gwKbNkj5_whq6jYHSG68HdH3QGdbbV8_LFdB4IcfdN0ERXbuo1_0ZXoSd-koSjhfafuBbzrKGwiyzbDm9bSaocnECqENXASMt-YLQ\"}"; + let mut hasher = Sha256::new(); + + hasher.update(payload); + + let result = hasher.finalize(); + let mine = subtle_encoding::hex::encode(&result); + assert_eq!( + mine, + "f353c435a23153ed43bc832c230a705b6d2b16b04732844d82538e8e691fcca9" + .as_bytes() + .to_vec() + ); + } + + #[test] + fn test_rsa() { + let nb64 = "kyk2V71OnhuVJAZq5occtRdYxX6eGiR3qQ04UKTZQxesiU3UsnbCq7FURSEr5NTKaU1L3die6VzPn829jdVYiht55hiqsEPYrRutNtmc-dI111lPGmkSaN_WcrK9rScbNn1btnBytf6KkST5Qmeri_Ue_BBjdg_G_WPNFKy1Ds_8lDqDMl3JLHaEjtKA-OtCjNsClzqtavgMJcbxdvHqUB1grbYePM6HrlMyIY1wZUvmdZw3_gwKbNkj5_whq6jYHSG68HdH3QGdbbV8_LFdB4IcfdN0ERXbuo1_0ZXoSd-koSjhfafuBbzrKGwiyzbDm9bSaocnECqENXASMt-YLQ=="; + let eb64 = "AQAB"; + + let nb = base64::decode_config(&nb64, base64::URL_SAFE).unwrap(); + let eb = base64::decode_config(&eb64, base64::URL_SAFE).unwrap(); + let n = BigUint::from_bytes_be(&nb); + let e = BigUint::from_bytes_be(&eb); + let pubkey = RSAPublicKey::new(n, e).unwrap(); + let n = pubkey.n().to_bytes_be(); + let e = pubkey.e().to_bytes_be(); + let encoded_n = base64::encode_config(&n, base64::URL_SAFE); + let encoded_e = base64::encode_config(&e, base64::URL_SAFE); + assert_eq!(nb64, encoded_n); + assert_eq!(eb64, encoded_e); + } + + #[test] + fn test_report() { + let rhex = "11110305FF80060000000000000000000000000000000000000000000000000000000000000000000000000000000000050000000000000007000000000000002E2BAC88632809B802B56A638D33824191AE9C3DAADA50A9875AC3EB6F8F202100000000000000000000000000000000000000000000000000000000000000005356F230E3BEC1436E7D7E023663BA453F69E2EDAD820865B098BE457D1B6B32000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007192385C3C0605DE55BB9476CE1D90748190ECB32A8EED7F5207B30CF6A1FE890000000000000000000000000000000000000000000000000000000000000000C88FDB58A572BAC124E1CD4B6BC59C7F00000000000000000000000000000000FB3D7DBC9826DC535BDA22B0F6A38FB5"; + let rb = subtle_encoding::hex::decode_upper(&rhex).unwrap(); + let report = Report::try_copy_from(&rb).unwrap(); + let mut hasher = Sha256::new(); + + hasher.update(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]); + + let rdata = hasher.finalize(); + let x: &[u8] = &rdata; + let y: &[u8] = &report.reportdata[..32]; + assert_eq!(x, y); + } + + #[test] + fn test_rsa2() { + // test-only private key + let file_content = r#" +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAtPy5JuUM9yKTpC1msedjJv9aFt33vATkopleDa+FctypZwUc +IfA9ZZk0DZhLRnMnp3Du6V+/KF8E8UMVXrkD0PLT2FBWY2lJJP+596ZFl9g02Y/x +lB4Irv3F06RkVgtuUR83W+w+XFCAzkWhT6Wsrf1LFvgeBxi86DBxJOnDGl4TRr4/ +AgkX6U4LTRCsd58//XUSEMknz7sxabh/xxRcqHMT661nNDCbvgmSv4jsZnm2/M6g +tno5exr2dmtFqN5C4N4APZdf/lIAe3h0CXN/VyU/w66mZ1IBDxhvj7TY47kfztjC +bgh0F4e7aZkBMsy34ca03wtGk7uBAAeGrtM7dwIDAQABAoIBAANyc1ybEX5c7N7O +j2s8J3jL40IEGGe+/PgV1mxlBQ+5xcGiexDnRPeFMkKI4j2TDAuDlcXhaAlKTpRv +NALHfAmfZ4HyIcQ58lgNINRC4j0nxfXp+wDYjLzKNLUmlQpayPO+umONBrEItFfn +HpoxYxvthU2tDD8TpC8nE1QGlTBZsxLU5LhuR0IOblbr3NsPHB15IgVHO8//bJsi +REAdbL6QrQYc2i0W7LTl2Ch+XKXzOjUJJwlv8hH41Ob2hOxzP9rQTOt7QCMpn873 +yeL+eCGf4AWEIuIwmN7gkIL8ObOoRkW7zBtc3worJoZTYH9uwmhVE7gPKH3WKCoe +xDpmEAkCgYEA9dTHOCjykJVh6qtMVCV036l8vFY86iUJPO7tz6f8a4UxVXcT3K7i +SPnuR+TmalzEGGrxamYzHmNvOPk0WgOOQ7bZKHjEniQBSYOrrV1+OlVsOKSdyvPe +9ecGLdLSjZUk8fGEEMLDNsmggjG37+xcw3hPXmWhe91AZ4HzqHTD/lkCgYEAvHlI +JR7g4yuNtBhBLNe3qg57Bjq8xU2QTGifiiUbmpcuFKrMjM2nYB5sym0CK124dV2J +G/DTQYy2EIkJw8LtddOoV4rkvLMHwGIipuXHp5Qg6CFzXcs/b4aHqoA3ORAlxAFx +NJLLrurr5qiZEnT0kMMND07wtA0WadFFiBzJ7k8CgYALQrXlcqq5yL31e+dBK34R +CLh4ABNGPnAP5HnsOyuq2S0LVysHvtMKuLgbfva3BIzO+YcZcpkA2Vks6O1m+ia4 +H1YPLokDHW8ZqPhiNpgjn+oXJiM8OrOJ3A1CaBfQ+HX6xy9ffSxoBBBgJlrgmJkf +MxGfp1QgUmAy3ZcFrmOT8QKBgQCDSC+6u6GGW4YfFm3/oFsst112X1+yR27l6lKG +1YY+zmOovbgxs+aMi2TYM8o5DtU322lv7vYYSL1hEzOcCqGBW2d9YyAlWMdjeHgO +rSu/TO0HBJXplXOgaaMCXsEYnGjR+Pcz2bTLKJQdXP8S3iik1Vi5exErOZqNJto6 +D2OQ/QKBgDqYXNWmWWxYZrIrv/ju3IuxgxgWkyrFwTv3gcx1d/ppA/k317gr8YZI +BTDF9yEwtrEQ1/xuPQMv8x6cnZFYH0ljjbXcTh6VJNv03MSC30pAQCrLSLl3nvHk +0DQm0TNriAxlF7X5zwR8v2b9yJkbwyyY8IbRQfImWFrzHPF4XzLe +-----END RSA PRIVATE KEY----- +"#; + let der_encoded = file_content + .lines() + .filter(|line| !line.starts_with("-")) + .fold(String::new(), |mut data, line| { + data.push_str(&line); + data + }); + let der_bytes = base64::decode(&der_encoded).expect("failed to decode base64 content"); + let secret = RSAPrivateKey::from_pkcs1(&der_bytes).expect("failed to parse key"); + + let encrypted_key = "nsohDxPYH9/iyLQvMujkFiTUJr5IiYfNrnYo0hoqoo0rFlUYw995X2REbsogKYcfqWQz7Rld6zEqLfBnD2ess+6pN6O+e1HXxmp6mNXxSAMl2rKGf08Ahl3XbQw0vQ7TNOGGdTlQoli2zbIKE/OsUl5DMqpjSyjDxNDJsKDis65tlnf39k1iMqjYLZMzUZEKTv5Sv16sKhk6TKyvaScNOOd+ctzVay8YFCh0cV9TT4rF/pq64RmX/T8VUTRAgUq/kAt7ZJYw/f2kILMBbBvugc00dq0WxQ+QRC6vDLhxZTxkLDBLHxLxeLlYpVbMMe83uxLcfCoXNOg5og3XeNRMpBNs/asA+eScIlW50xPbpzel9l0AZ9PEu2LAjBjcTAikGdigjKMuDEc="; + let enckey_payload = base64::decode(&encrypted_key).expect("decode enc key"); + assert_eq!(enckey_payload.len(), 296); + let seal = CloudBackupSeal::new(enckey_payload).expect("seal"); + let sealing_key = decrypt_wrapped_key(&secret, seal).expect("sealing key"); + let sealing_key_hex = "924a14cdcb1b8b0e630cbe643bfbd83ed070879562fe51ece24de27af2a77e5a"; + let expected_sealing_key = subtle_encoding::hex::decode(&sealing_key_hex).unwrap(); + let sealkey_ref: &[u8] = sealing_key.as_ref(); + assert_eq!(sealkey_ref, &expected_sealing_key); + } + + #[test] + fn test_cloud_reseal() { + let mut csprng = OsRng {}; + let (rsa_pub, sealed_rsa_priv, _report) = + generate_keypair(&mut csprng, Targetinfo::from(Report::for_self())).expect("gen rsa"); + let cloud_seal = get_wrapped_key(&mut csprng, rsa_pub); + let cbk = CloudBackupKey { + sealed_rsa_key: sealed_rsa_priv, + backup_key: cloud_seal, + }; + let kp = Keypair::generate(&mut csprng); + let backup = cloud_backup(&mut csprng, cbk.clone(), &kp).expect("backup"); + reseal_recover_cloud(&mut csprng, cbk, backup).expect("reseal"); + } + + #[test] + fn test_recover_fail() { + let mut csprng = OsRng {}; + let (rsa_pub, sealed_rsa_priv, _report) = + generate_keypair(&mut csprng, Targetinfo::from(Report::for_self())).expect("gen rsa"); + let cloud_seal = get_wrapped_key(&mut csprng, rsa_pub); + let cbk = CloudBackupKey { + sealed_rsa_key: sealed_rsa_priv, + backup_key: cloud_seal, + }; + let kp = Keypair::generate(&mut csprng); + let backup = cloud_backup(&mut csprng, cbk.clone(), &kp).expect("backup"); + let mut bm = backup.clone(); + bm.sealed_secret[0] ^= 1; + assert!(reseal_recover_cloud(&mut csprng, cbk.clone(), bm).is_err()); + let mut cbkm = cbk.clone(); + cbkm.backup_key.encrypted_symmetric_key[0] ^= 1; + assert!(reseal_recover_cloud(&mut csprng, cbkm, backup).is_err()); + } +} diff --git a/providers/sgx/sgx-app/src/sgx_app/cloud/keywrap.rs b/providers/sgx/sgx-app/src/sgx_app/cloud/keywrap.rs new file mode 100644 index 00000000..ebd6d4d1 --- /dev/null +++ b/providers/sgx/sgx-app/src/sgx_app/cloud/keywrap.rs @@ -0,0 +1,140 @@ +use aes::{ + cipher::generic_array::{ + typenum::{U32, U40}, + GenericArray, + }, + Aes256, BlockDecrypt, NewBlockCipher, +}; +use std::convert::TryInto; +use subtle::ConstantTimeEq; +use zeroize::Zeroize; + +/// helper for `aes256_unwrap_key_with_pad` +fn aes256_unwrap_key_and_iv( + kek: &GenericArray, + input: &GenericArray, +) -> (GenericArray, [u8; 8]) { + // https://www.ietf.org/rfc/rfc3394.txt + // Section 2.2.2 step 1) init + let n = input.len() / 8 - 1; + + let mut r = vec![0u8; input.len()]; + r[8..].copy_from_slice(&input[8..]); + + let cipher = Aes256::new(kek); + // SAFETY: lengths are statically checked + let mut a = u64::from_be_bytes(input[..8].try_into().unwrap()); + // Section 2.2.2 step 2) intermediate values + for j in (0..6).rev() { + for i in (1..n + 1).rev() { + let t = (n * j + i) as u64; + let mut ciphertext = [0u8; 16]; + let b_i = i * 8; + ciphertext[..8].copy_from_slice(&(a ^ t).to_be_bytes()); + ciphertext[8..].copy_from_slice(&r[b_i..][0..8]); + let mut block = GenericArray::from_mut_slice(&mut ciphertext); + cipher.decrypt_block(&mut block); + // SAFETY: static allocation above + a = u64::from_be_bytes(ciphertext[..8].try_into().unwrap()); + r[b_i..][0..8].copy_from_slice(&ciphertext[8..]); + } + } + let key = GenericArray::clone_from_slice(&r[8..]); + r.zeroize(); + (key, a.to_be_bytes()) +} + +/// Possible errors in unwrapping +#[derive(Debug, PartialEq, Eq)] +pub enum UnwrapError { + /// key or payload incorrect length + IncorrectLength, + /// AIV mismmatch + AuthFailed, +} + +/// This unwraps the key as per RFC3394/RFC5649 +/// but hardcodes the length assumptions for kek to be 32-bytes +/// and the unwrapped key to be 32-bytes (as that's what's provided +/// in cloud environments). +/// +/// NOTE: RFC3394/RFC5649-compliant AES-KW / AES-KWP +/// is implemented in the `aes-keywrap-rs` +/// (`aes-keywrap` doesn't implement it: https://github.com/jedisct1/rust-aes-keywrap/issues/2), +/// but 1) it depends on the `crypto2` crate which may not have received as much scrutiny +/// as the `aes` crate (which is used everywhere else here), +/// 2) we don't need most of its functionality. +/// Hence, this custom partial implementation. +pub fn aes256_unwrap_key_with_pad( + kek: &[u8], + wrapped: &[u8], +) -> Result, UnwrapError> { + if kek.len() != 32 || wrapped.len() != 40 { + return Err(UnwrapError::IncorrectLength); + } + let kek = GenericArray::from_slice(kek); + let wrapped = GenericArray::from_slice(wrapped); + let (mut key, key_iv) = aes256_unwrap_key_and_iv(kek, wrapped); + + // Alternate initial value for aes key wrapping, as defined in RFC 5649 section 3 + // http://www.ietf.org/rfc/rfc5649.txt + // big-ending length hardcoded to 32 (as here, it's always unwrapping 32-byte sealing key) + const IV_5649: [u8; 8] = [0xa6, 0x59, 0x59, 0xa6, 0, 0, 0, 32]; + + if IV_5649.ct_eq(&key_iv).unwrap_u8() == 0u8 { + key.zeroize(); + return Err(UnwrapError::AuthFailed); + } + // no need to remove padding, as the length is hardcoded to 32 + Ok(key) +} + +#[cfg(test)] +mod tests { + use super::*; + use quickcheck::{Arbitrary, Gen}; + use quickcheck_macros::quickcheck; + + #[derive(Clone, Copy, Debug)] + struct KeyWrap(pub [u8; 32]); + + impl Arbitrary for KeyWrap { + fn arbitrary(g: &mut Gen) -> Self { + let mut raw = [0u8; 32]; + for i in 0..raw.len() { + let b = u8::arbitrary(g); + raw[i] = b; + } + Self(raw) + } + } + + #[test] + fn test_wrong_len() { + assert_eq!( + aes256_unwrap_key_with_pad(&[0u8; 10], &[0u8; 40]), + Err(UnwrapError::IncorrectLength) + ); + assert_eq!( + aes256_unwrap_key_with_pad(&[0u8; 32], &[0u8; 10]), + Err(UnwrapError::IncorrectLength) + ); + } + + #[test] + fn test_wrong_auth() { + assert_eq!( + aes256_unwrap_key_with_pad(&[0u8; 32], &[0u8; 40]), + Err(UnwrapError::AuthFailed) + ); + } + + #[quickcheck] + fn wrap_unwrap(kek: KeyWrap, key: KeyWrap) -> bool { + let wrapped = + aes_keywrap_rs::Aes256Kw::aes_wrap_key_with_pad(&kek.0, &key.0).expect("wrap key"); + let unwrap = aes256_unwrap_key_with_pad(&kek.0, &wrapped).expect("unwrap"); + let uref: &[u8] = unwrap.as_ref(); + uref == &key.0 + } +} diff --git a/providers/sgx/sgx-app/src/sgx_app/keypair_seal.rs b/providers/sgx/sgx-app/src/sgx_app/keypair_seal.rs index ac685ecf..5a1660ad 100644 --- a/providers/sgx/sgx-app/src/sgx_app/keypair_seal.rs +++ b/providers/sgx/sgx-app/src/sgx_app/keypair_seal.rs @@ -4,84 +4,40 @@ use aes_gcm_siv::{ }; use ed25519_dalek::{Keypair, PublicKey, SecretKey}; use rand::{rngs::OsRng, RngCore}; -use secrecy::{ExposeSecret, SecretVec}; use sgx_isa::{ErrorCode, Keyname, Keypolicy, Keyrequest, Report}; use std::convert::TryInto; -use tmkms_light_sgx_runner::{CloudBackupKeyData, SealedKeyData, CLOUD_KEY_LEN}; +use tmkms_light_sgx_runner::SealedKeyData; use zeroize::Zeroize; -/// symmetric key wrap -- e.g. from cloud KMS -pub struct CloudWrapKey(SecretVec); - -impl ExposeSecret> for CloudWrapKey { - fn expose_secret(&self) -> &Vec { - self.0.expose_secret() - } -} - -impl CloudWrapKey { - /// creates the new wrapper if the length is correct - pub fn new(secret: Vec) -> Option { - if secret.len() == CLOUD_KEY_LEN { - Some(Self(SecretVec::new(secret))) - } else { - None - } - } -} - -/// As cloud vendors may not guarantee HW affinity, -/// this is optionally used to seal the keypair with the externally provided -/// key (e.g. injected from cloud HSM) with `Aes128GcmSiv`. -/// The payload encrypted with this key can then be used to recover -/// when the instance is relocated etc. -pub fn cloud_backup( +fn seal_payload( csprng: &mut OsRng, - seal_key: CloudWrapKey, - keypair: &Keypair, -) -> Result { + payload: Payload, + keyid: [u8; 32], +) -> Result { let mut nonce = [0u8; 12]; csprng.fill_bytes(&mut nonce); - let payload = Payload { - msg: keypair.secret.as_bytes(), - aad: keypair.public.as_bytes(), + let report = Report::for_self(); + let key_request = Keyrequest { + keyname: Keyname::Seal as _, + keypolicy: Keypolicy::MRSIGNER, + isvsvn: report.isvsvn, + cpusvn: report.cpusvn, + keyid, + ..Default::default() }; let nonce_ga = GenericArray::from_slice(&nonce); - let gk = GenericArray::from_slice(seal_key.expose_secret()); + let mut key = key_request.egetkey()?; + let gk = GenericArray::from_slice(&key); let aead = Aes128GcmSiv::new(gk); - aead.encrypt(nonce_ga, payload) - .map(|sealed_secret| CloudBackupKeyData { - sealed_secret, + if let Ok(sealed_secret) = aead.encrypt(nonce_ga, payload) { + key.zeroize(); + Ok(SealedKeyData { + seal_key_request: key_request.into(), nonce, - public_key: keypair.public, + sealed_secret, }) -} - -/// Recovers the backed up keypair (decrypt it using the externally -/// provided key, e.g. injected from cloud HSM) and seals it on that CPU. -pub fn seal_recover_cloud_backup( - csprng: &mut OsRng, - seal_key: CloudWrapKey, - backup_data: CloudBackupKeyData, -) -> Result { - let nonce_ga = GenericArray::from_slice(&backup_data.nonce); - let gk = GenericArray::from_slice(&seal_key.expose_secret()); - let aead = Aes128GcmSiv::new(gk); - let payload = Payload { - msg: &backup_data.sealed_secret, - aad: backup_data.public_key.as_bytes(), - }; - if let Ok(mut secret_key) = aead.decrypt(nonce_ga, payload) { - drop(seal_key); - let secret = SecretKey::from_bytes(&secret_key).map_err(|_| ErrorCode::InvalidSignature)?; - secret_key.zeroize(); - let public = PublicKey::from(&secret); - let mut kp = Keypair { secret, public }; - let sealed = seal(csprng, &kp); - kp.secret.zeroize(); - sealed } else { - drop(seal_key); + key.zeroize(); Err(ErrorCode::MacCompareFail) } } @@ -90,32 +46,44 @@ pub fn seal_recover_cloud_backup( /// via a key request against MRSIGNER (so that versions with higher `isvsvn` /// can unseal the keypair) pub fn seal(csprng: &mut OsRng, keypair: &Keypair) -> Result { - let mut nonce = [0u8; 12]; - csprng.fill_bytes(&mut nonce); - let report = Report::for_self(); - let key_request = Keyrequest { - keyname: Keyname::Seal as _, - keypolicy: Keypolicy::MRSIGNER, - isvsvn: report.isvsvn, - cpusvn: report.cpusvn, - keyid: keypair.public.to_bytes(), - ..Default::default() - }; let payload = Payload { msg: keypair.secret.as_bytes(), aad: keypair.public.as_bytes(), }; - let nonce_ga = GenericArray::from_slice(&nonce); + seal_payload(csprng, payload, keypair.public.to_bytes()) +} + +pub fn seal_secret( + csprng: &mut OsRng, + secret: &[u8], + keyid: [u8; 32], +) -> Result { + let payload = Payload { + msg: secret, + aad: &keyid, + }; + seal_payload(csprng, payload, keyid) +} + +pub fn unseal_secret(sealed_data: &SealedKeyData) -> Result, ErrorCode> { + if sealed_data.seal_key_request.keyname != (Keyname::Seal as u16) { + return Err(ErrorCode::InvalidKeyname); + } + let key_request: Keyrequest = sealed_data + .seal_key_request + .try_into() + .map_err(|_| ErrorCode::InvalidAttribute)?; + let payload = Payload { + msg: &sealed_data.sealed_secret, + aad: &sealed_data.seal_key_request.keyid, + }; + let nonce_ga = GenericArray::from_slice(&sealed_data.nonce); let mut key = key_request.egetkey()?; let gk = GenericArray::from_slice(&key); let aead = Aes128GcmSiv::new(gk); - if let Ok(sealed_secret) = aead.encrypt(nonce_ga, payload) { + if let Ok(secret_key) = aead.decrypt(nonce_ga, payload) { key.zeroize(); - Ok(SealedKeyData { - seal_key_request: key_request.into(), - nonce, - sealed_secret, - }) + Ok(secret_key) } else { key.zeroize(); Err(ErrorCode::MacCompareFail) @@ -125,32 +93,11 @@ pub fn seal(csprng: &mut OsRng, keypair: &Keypair) -> Result Result { - if sealed_data.seal_key_request.keyname != (Keyname::Seal as u16) { - return Err(ErrorCode::InvalidKeyname); - } if let Ok(public) = PublicKey::from_bytes(&sealed_data.seal_key_request.keyid) { - let key_request: Keyrequest = sealed_data - .seal_key_request - .try_into() - .map_err(|_| ErrorCode::InvalidAttribute)?; - let payload = Payload { - msg: &sealed_data.sealed_secret, - aad: &sealed_data.seal_key_request.keyid, - }; - let nonce_ga = GenericArray::from_slice(&sealed_data.nonce); - let mut key = key_request.egetkey()?; - let gk = GenericArray::from_slice(&key); - let aead = Aes128GcmSiv::new(gk); - if let Ok(mut secret_key) = aead.decrypt(nonce_ga, payload) { - key.zeroize(); - let secret = - SecretKey::from_bytes(&secret_key).map_err(|_| ErrorCode::InvalidSignature)?; - secret_key.zeroize(); - Ok(Keypair { secret, public }) - } else { - key.zeroize(); - Err(ErrorCode::MacCompareFail) - } + let mut secret_key = unseal_secret(sealed_data)?; + let secret = SecretKey::from_bytes(&secret_key).map_err(|_| ErrorCode::InvalidSignature)?; + secret_key.zeroize(); + Ok(Keypair { secret, public }) } else { Err(ErrorCode::InvalidSignature) } diff --git a/providers/sgx/sgx-runner/Cargo.toml b/providers/sgx/sgx-runner/Cargo.toml index 8258bbe9..0febf578 100644 --- a/providers/sgx/sgx-runner/Cargo.toml +++ b/providers/sgx/sgx-runner/Cargo.toml @@ -1,23 +1,25 @@ [package] name = "tmkms-light-sgx-runner" -version = "0.1.2" +version = "0.2.0" authors = ["Tomas Tauber <2410580+tomtau@users.noreply.github.com>", "Linfeng Yuan "] edition = "2018" [dependencies] +base64 = "0.13" serde = { version = "1", features = ["derive"] } ed25519 = { version = "1", features = ["serde"] } ed25519-dalek = "1" -sgx-isa = "0.3" +rsa = { version = "0.4", features = ["serde"] } +sgx-isa = { version = "0.3", features = ["serde"] } thiserror = "1" tendermint = { version = "0.19" } tmkms-light = { path = "../../.." } -secrecy = { version = "0.7" } zeroize = "1" [target.'cfg(not(target_env = "sgx"))'.dependencies] anomaly = "0.2" aesm-client = { version = "0.5", features = ["sgxs"] } +dcap-ql = "0.3" enclave-runner = "0.4" serde_json = "1" sgxs-loaders = "0.3" diff --git a/providers/sgx/sgx-runner/src/command.rs b/providers/sgx/sgx-runner/src/command.rs index 79b1493f..49dc3c44 100644 --- a/providers/sgx/sgx-runner/src/command.rs +++ b/providers/sgx/sgx-runner/src/command.rs @@ -1,15 +1,87 @@ use std::{fs, path::PathBuf}; -use crate::{SgxInitRequest, CLOUD_KEY_LEN}; +use crate::shared::{CloudBackupKey, CloudBackupSeal, SealedKeyData}; +use crate::{config, runner::TmkmsSgxSigner}; +use crate::{shared::get_claim, shared::SgxInitResponse, SgxInitRequest}; +use rsa::PublicKeyPemEncoding; use tendermint::net; use tmkms_light::{ config::validator::ValidatorConfig, utils::{print_pubkey, PubkeyDisplay}, }; use tracing::debug; -use zeroize::Zeroizing; -use crate::{config, runner::TmkmsSgxSigner}; +/// generate a key wrap for cloud backups +pub fn keywrap( + enclave_path: PathBuf, + sealed_output_path: PathBuf, + dcap: bool, +) -> Result<(), String> { + let targetinfo = if dcap { + if dcap_ql::is_loaded() { + let ti = dcap_ql::target_info().map_err(|e| format!("dcap target info: {:?}", e))?; + Some(ti) + } else { + return Err("DCAP QL not loaded".to_owned()); + } + } else { + None + }; + let request = SgxInitRequest::GenWrapKey { targetinfo }; + let request_bytes = serde_json::to_vec(&request) + .map_err(|e| format!("failed to convert request to json: {:?}", e))?; + let (state_syncer, _, state_stream) = TmkmsSgxSigner::get_state_syncer("/tmp/state.json") + .map_err(|e| format!("state persistence error: {:?}", e))?; + let enclave_args: Vec<&[u8]> = vec![request_bytes.as_ref()]; + let runner = TmkmsSgxSigner::launch_enclave_app( + &enclave_path, + None, + state_syncer, + state_stream, + &enclave_args, + ) + .map_err(|e| format!("failed to launch the enclave app: {:?}", e))?; + debug!("waiting for keygen"); + let sealed_key = runner + .get_init_response() + .map_err(|e| format!("failed to generate a wrapping key: {:?}", e))?; + match sealed_key { + SgxInitResponse::WrapKey { + wrap_key_sealed, + wrap_pub_key, + pub_key_report, + } => { + let quote = if dcap { + let q = + dcap_ql::quote(&pub_key_report).map_err(|e| format!("dcap quote: {:?}", e))?; + Some(q) + } else { + None + }; + + config::write_sealed_file(sealed_output_path, &wrap_key_sealed) + .map_err(|e| format!("failed to write wrapping key: {:?}", e))?; + if let Some(quote) = quote { + let claim_payload = get_claim(&wrap_pub_key); + let encoded_claim = base64::encode_config(&claim_payload, base64::URL_SAFE); + let encoded_quote = base64::encode_config("e, base64::URL_SAFE); + print!("{{\"quote\": \"{}\",", encoded_quote); + println!( + "\"runtimeData\": {{ \"data\": \"{}\", \"dataType\": \"Binary\" }}}}", + encoded_claim + ); + } else { + let pkcs1 = wrap_pub_key + .to_pem_pkcs1() + .map_err(|e| format!("pubkey err: {:?}", e))?; + println!("wrap public key in PKCS1:\n{}\n", pkcs1); + } + + Ok(()) + } + _ => Err("unexpected enclave response".to_owned()), + } +} /// write tmkms.toml + generate keys (sealed for machine CPU /// + backup if an external key is provided) @@ -17,9 +89,33 @@ pub fn init( config_path: Option, pubkey_display: Option, bech32_prefix: Option, - external_backup_key_path: Option, + wrap_backup_key_path: Option, + external_cloud_key_path: Option, key_backup_data_path: Option, ) -> Result<(), String> { + let cloud_backup = match (wrap_backup_key_path, external_cloud_key_path) { + (Some(p1), Some(p2)) => { + let sealed_rsa_key: SealedKeyData = serde_json::from_slice( + &fs::read(p1) + .map_err(|e| format!("failed to read sealed wrap key for external backup key: {:?}", e))?, + ) + .map_err(|e| format!("failed to parse sealed wrap key for external backup key: {:?}", e))?; + let backup_key: CloudBackupSeal = serde_json::from_slice( + &fs::read(p2) + .map_err(|e| format!("failed to read external backup key: {:?}", e))?, + ) + .map_err(|e| format!("failed to parse external backup key: {:?}", e))?; + let cloud_backup = CloudBackupKey { + sealed_rsa_key, + backup_key, + }; + Some(cloud_backup) + } + (Some(_), None) | (None, Some(_)) => { + return Err("Both `wrap_backup_key_path` (-w) and `external_cloud_key_path` (-e) need to be provided.".to_owned()) + } + _ => None, + }; let cp = config_path.unwrap_or_else(|| "tmkms.toml".into()); let config = config::SgxSignOpt::default(); let t = @@ -39,27 +135,15 @@ pub fn init( .ok_or_else(|| "cannot create a dir in a root directory".to_owned())?, ) .map_err(|e| format!("failed to create dirs for state storage: {:?}", e))?; - let request = SgxInitRequest::KeyGen; + let request = SgxInitRequest::KeyGen { cloud_backup }; let request_bytes = serde_json::to_vec(&request) .map_err(|e| format!("failed to convert request to json: {:?}", e))?; - let backup_key = if let Some(bkp) = external_backup_key_path { - let key_bytes = Zeroizing::new( - fs::read(bkp).map_err(|e| format!("failed to read backup key: {:?}", e))?, - ); - if key_bytes.len() != CLOUD_KEY_LEN { - return Err("incorrect backup key length".to_owned()); - } - Some(Zeroizing::new(subtle_encoding::hex::encode(&*key_bytes))) - } else { - None - }; + debug!("launching enclave"); let (state_syncer, _, state_stream) = TmkmsSgxSigner::get_state_syncer(&config.state_file_path) .map_err(|e| format!("state persistence error: {:?}", e))?; - let mut enclave_args: Vec<&[u8]> = vec![request_bytes.as_ref()]; - if let Some(ref bkp) = backup_key { - enclave_args.push(&*bkp); - } + let enclave_args: Vec<&[u8]> = vec![request_bytes.as_ref()]; + let runner = TmkmsSgxSigner::launch_enclave_app( &config.enclave_path, None, @@ -72,17 +156,16 @@ pub fn init( let sealed_key = runner .get_init_response() .map_err(|e| format!("failed to generate consensus key: {:?}", e))?; - config::write_sealed_file( - config.sealed_consensus_key_path, - &sealed_key.sealed_key_data, - ) - .map_err(|e| format!("failed to write consensus key: {:?}", e))?; - let public_key = - ed25519_dalek::PublicKey::from_bytes(&sealed_key.sealed_key_data.seal_key_request.keyid) - .map_err(|e| format!("invalid keyid: {:?}", e))?; + let (sealed_key_data, cloud_backup_key_data) = sealed_key + .get_gen_response() + .ok_or_else(|| "failed to generate consensus key".to_owned())?; + config::write_sealed_file(config.sealed_consensus_key_path, &sealed_key_data) + .map_err(|e| format!("failed to write consensus key: {:?}", e))?; + let public_key = ed25519_dalek::PublicKey::from_bytes(&sealed_key_data.seal_key_request.keyid) + .map_err(|e| format!("invalid keyid: {:?}", e))?; print_pubkey(bech32_prefix, pubkey_display, public_key); let base_backup_path = key_backup_data_path.unwrap_or_else(|| "".into()); - if let Some(bkp) = sealed_key.cloud_backup_key_data { + if let Some(bkp) = cloud_backup_key_data { config::write_backup_file(base_backup_path.join("consensus-key.backup"), &bkp) .map_err(|e| format!("failed to write consensus key backup: {:?}", e))?; } @@ -102,9 +185,13 @@ pub fn init( let sealed_key = runner .get_init_response() .map_err(|e| format!("failed to generate id key: {:?}", e))?; - config::write_sealed_file(id_path, &sealed_key.sealed_key_data) + let (sealed_key_data, cloud_backup_key_data) = sealed_key + .get_gen_response() + .ok_or_else(|| "failed to generate id key".to_owned())?; + + config::write_sealed_file(id_path, &sealed_key_data) .map_err(|e| format!("failed to write id key: {:?}", e))?; - if let Some(bkp) = sealed_key.cloud_backup_key_data { + if let Some(bkp) = cloud_backup_key_data { config::write_backup_file(base_backup_path.join("id-key.backup"), &bkp) .map_err(|e| format!("failed to write id key backup: {:?}", e))?; } @@ -172,7 +259,8 @@ pub fn recover( config_path: Option, pubkey_display: Option, bech32_prefix: Option, - external_backup_key_path: PathBuf, + wrap_backup_key_path: PathBuf, + external_cloud_key_path: PathBuf, key_backup_data_path: PathBuf, recover_consensus_key: bool, ) -> Result<(), String> { @@ -187,22 +275,40 @@ pub fn recover( if !recover_consensus_key && config.sealed_id_key_path.is_none() { return Err("empty id key path in config".to_owned()); } - let backup_key = { - let key_bytes = Zeroizing::new( - fs::read(external_backup_key_path) - .map_err(|e| format!("failed to read backup key: {:?}", e))?, - ); - if key_bytes.len() != CLOUD_KEY_LEN { - return Err("incorrect backup key length".to_owned()); + let cloud_backup = { + let sealed_rsa_key: SealedKeyData = + serde_json::from_slice(&fs::read(wrap_backup_key_path).map_err(|e| { + format!( + "failed to read sealed wrap key for external backup key: {:?}", + e + ) + })?) + .map_err(|e| { + format!( + "failed to parse sealed wrap key for external backup key: {:?}", + e + ) + })?; + let backup_key: CloudBackupSeal = serde_json::from_slice( + &fs::read(external_cloud_key_path) + .map_err(|e| format!("failed to read external backup key: {:?}", e))?, + ) + .map_err(|e| format!("failed to parse external backup key: {:?}", e))?; + CloudBackupKey { + sealed_rsa_key, + backup_key, } - Zeroizing::new(subtle_encoding::hex::encode(&*key_bytes)) }; + let key_data = serde_json::from_str( &fs::read_to_string(key_backup_data_path.join("consensus-key.backup")) .map_err(|e| format!("failed to read backup data: {:?}", e))?, ) .map_err(|e| format!("failed to parse backup data: {:?}", e))?; - let request = SgxInitRequest::CloudRecover { key_data }; + let request = SgxInitRequest::CloudRecover { + cloud_backup, + key_data, + }; let request_bytes = serde_json::to_vec(&request) .map_err(|e| format!("failed to convert request to json: {:?}", e))?; debug!("launching enclave"); @@ -214,29 +320,29 @@ pub fn recover( None, state_syncer, state_stream, - &[request_bytes.as_ref(), &*backup_key], + &[request_bytes.as_ref()], ) .map_err(|e| format!("failed to launch the enclave app: {:?}", e))?; debug!("waiting for recover"); let sealed_key = runner .get_init_response() .map_err(|e| format!("failed to recover key: {:?}", e))?; + let (sealed_key_data, _) = sealed_key + .get_gen_response() + .ok_or_else(|| "failed to recover key".to_owned())?; + if recover_consensus_key { - config::write_sealed_file( - config.sealed_consensus_key_path, - &sealed_key.sealed_key_data, - ) - .map_err(|e| format!("failed to write consensus key: {:?}", e))?; - let public_key = ed25519_dalek::PublicKey::from_bytes( - &sealed_key.sealed_key_data.seal_key_request.keyid, - ) - .map_err(|e| format!("ivalid keyid: {:?}", e))?; + config::write_sealed_file(config.sealed_consensus_key_path, &sealed_key_data) + .map_err(|e| format!("failed to write consensus key: {:?}", e))?; + let public_key = + ed25519_dalek::PublicKey::from_bytes(&sealed_key_data.seal_key_request.keyid) + .map_err(|e| format!("ivalid keyid: {:?}", e))?; println!("recovered key"); print_pubkey(bech32_prefix, pubkey_display, public_key); } else { // checked above after config parsing let id_path = config.sealed_id_key_path.unwrap(); - config::write_sealed_file(id_path, &sealed_key.sealed_key_data) + config::write_sealed_file(id_path, &sealed_key_data) .map_err(|e| format!("failed to write id key: {:?}", e))?; } Ok(()) diff --git a/providers/sgx/sgx-runner/src/main.rs b/providers/sgx/sgx-runner/src/main.rs index 99b1f24e..acb0f9ce 100644 --- a/providers/sgx/sgx-runner/src/main.rs +++ b/providers/sgx/sgx-runner/src/main.rs @@ -3,7 +3,7 @@ mod config; mod runner; mod shared; mod state; -use shared::{SgxInitRequest, CLOUD_KEY_LEN}; +use shared::SgxInitRequest; use std::fmt::Debug; use std::path::PathBuf; use structopt::StructOpt; @@ -16,6 +16,16 @@ use tracing_subscriber::FmtSubscriber; about = "runner for signing backend app using SGX" )] enum TmkmsLight { + #[structopt(name = "cloud-wrap", about = "Generate a wrap key for cloud backups")] + /// Create config + keygen + CloudWrapKeyGen { + #[structopt(short)] + enclave_path: Option, + #[structopt(short)] + sealed_wrap_key_path: Option, + #[structopt(short)] + dcap: bool, + }, #[structopt(name = "init", about = "Create config and generate keys")] /// Create config + keygen Init { @@ -26,7 +36,9 @@ enum TmkmsLight { #[structopt(short)] bech32_prefix: Option, #[structopt(short)] - external_backup_key_path: Option, + wrap_backup_key_path: Option, + #[structopt(short)] + external_cloud_key_path: Option, #[structopt(short)] key_backup_data_path: Option, }, @@ -40,7 +52,9 @@ enum TmkmsLight { #[structopt(short)] bech32_prefix: Option, #[structopt(short)] - external_backup_key_path: PathBuf, + wrap_backup_key_path: PathBuf, + #[structopt(short)] + external_cloud_key_path: PathBuf, #[structopt(short)] key_backup_data_path: PathBuf, #[structopt(short)] @@ -62,17 +76,30 @@ fn main() { tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); let result = match opt { + TmkmsLight::CloudWrapKeyGen { + enclave_path, + sealed_wrap_key_path, + dcap, + } => { + let enclave_path = + enclave_path.unwrap_or_else(|| "enclave/tmkms-light-sgx-app.sgxs".into()); + let sealed_wrap_key_path = + sealed_wrap_key_path.unwrap_or_else(|| "sealed-wrap.key".into()); + command::keywrap(enclave_path, sealed_wrap_key_path, dcap) + } TmkmsLight::Init { config_path, pubkey_display, bech32_prefix, - external_backup_key_path, + wrap_backup_key_path, + external_cloud_key_path, key_backup_data_path, } => command::init( config_path, pubkey_display, bech32_prefix, - external_backup_key_path, + wrap_backup_key_path, + external_cloud_key_path, key_backup_data_path, ), TmkmsLight::Start { config_path } => command::start(config_path), @@ -80,14 +107,16 @@ fn main() { config_path, pubkey_display, bech32_prefix, - external_backup_key_path, + wrap_backup_key_path, + external_cloud_key_path, key_backup_data_path, recover_consensus_key, } => command::recover( config_path, pubkey_display, bech32_prefix, - external_backup_key_path, + wrap_backup_key_path, + external_cloud_key_path, key_backup_data_path, recover_consensus_key, ), diff --git a/providers/sgx/sgx-runner/src/shared.rs b/providers/sgx/sgx-runner/src/shared.rs index efaa46cb..b51c984e 100644 --- a/providers/sgx/sgx-runner/src/shared.rs +++ b/providers/sgx/sgx-runner/src/shared.rs @@ -1,14 +1,17 @@ -use serde::{Deserialize, Serialize}; -use sgx_isa::{Keypolicy, Keyrequest}; +use rsa::PublicKeyParts; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +use sgx_isa::{Keypolicy, Keyrequest, Report, Targetinfo}; use std::convert::TryInto; +use std::fmt; +use std::str::FromStr; use tendermint::consensus; use tendermint::node; use tmkms_light::config::validator::ValidatorConfig; /// keyseal is fixed in the enclave app -pub type AesGcm128SivNonce = [u8; 12]; +pub type AesGcmSivNonce = [u8; 12]; -/// it can potentially be fixed size, as one always seals the ed25519 keypairs +/// from sealing the ed25519 keypairs + RSA PKCS1 pub type Ciphertext = Vec; /// this partially duplicates `Keyrequest` from sgx-isa, @@ -61,16 +64,13 @@ impl From for KeyRequestWrap { #[derive(Debug, Serialize, Deserialize, Clone)] pub struct SealedKeyData { pub seal_key_request: KeyRequestWrap, - pub nonce: AesGcm128SivNonce, + pub nonce: AesGcmSivNonce, pub sealed_secret: Ciphertext, } /// ed25519 pubkey alias pub type PublicKey = [u8; 32]; -/// length of symmetric key wrap for cloud backup using e.g. cloud KMS -pub const CLOUD_KEY_LEN: usize = 16; - /// Returned from the enclave app after keygen /// if the cloud backup option is requested. /// This may be needed in cloud settings @@ -78,7 +78,7 @@ pub const CLOUD_KEY_LEN: usize = 16; /// may be relocated and fail to unseal `SealedKeyData` #[derive(Debug, Serialize, Deserialize, Clone)] pub struct CloudBackupKeyData { - pub nonce: AesGcm128SivNonce, + pub nonce: AesGcmSivNonce, pub sealed_secret: Ciphertext, pub public_key: ed25519_dalek::PublicKey, } @@ -99,13 +99,115 @@ pub struct RemoteConnectionConfig { pub sealed_key: SealedKeyData, } +/// package for cloud backups +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct CloudBackupKey { + pub sealed_rsa_key: SealedKeyData, + pub backup_key: CloudBackupSeal, +} + +/// payload from cloud environments for backups +#[derive(Debug, Clone)] +pub struct CloudBackupSeal { + /// kek encrypted using RSA-OAEP+SHA-256 + pub encrypted_symmetric_key: [u8; 256], + /// 32-byte key that can be unwrapped with the decrypted kek + /// using RFC3394/RFC5649 AES-KWP + pub wrapped_cloud_sealing_key: [u8; 40], +} + +impl CloudBackupSeal { + /// returns the struct if the payload length matches the expected on + pub fn new(payload: Vec) -> Option { + if payload.len() != 296 { + None + } else { + let mut encrypted_symmetric_key = [0u8; 256]; + encrypted_symmetric_key.copy_from_slice(&payload[..256]); + let mut wrapped_cloud_sealing_key = [0u8; 40]; + wrapped_cloud_sealing_key.copy_from_slice(&payload[256..]); + Some(Self { + encrypted_symmetric_key, + wrapped_cloud_sealing_key, + }) + } + } +} + +impl Serialize for CloudBackupSeal { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for CloudBackupSeal { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct StrVisitor; + + impl<'de> de::Visitor<'de> for StrVisitor { + type Value = CloudBackupSeal; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("CloudBackupSeal") + } + + #[inline] + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + CloudBackupSeal::from_str(value).map_err(|err| de::Error::custom(err.to_string())) + } + } + + deserializer.deserialize_str(StrVisitor) + } +} + +impl fmt::Display for CloudBackupSeal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let full = [ + &self.encrypted_symmetric_key[..], + &self.wrapped_cloud_sealing_key[..], + ] + .concat(); + let full_str = base64::encode_config(&full, base64::URL_SAFE); + write!(f, "{}", full_str) + } +} + +impl FromStr for CloudBackupSeal { + type Err = base64::DecodeError; + + fn from_str(s: &str) -> Result { + let bytes = base64::decode_config(s, base64::URL_SAFE)?; + CloudBackupSeal::new(bytes).ok_or(base64::DecodeError::InvalidLength) + } +} + /// request sent to the enclave app #[derive(Debug, Serialize, Deserialize)] pub enum SgxInitRequest { + /// generates a wrapping key for cloud backups + GenWrapKey { + /// if dcap is used + targetinfo: Option, + }, /// generate a new keypair - KeyGen, + KeyGen { + cloud_backup: Option, + }, /// reseal the keypair from a backup - CloudRecover { key_data: CloudBackupKeyData }, + CloudRecover { + cloud_backup: CloudBackupKey, + key_data: CloudBackupKeyData, + }, /// start the main loop for processing Tendermint privval requests Start { sealed_key: SealedKeyData, @@ -117,9 +219,47 @@ pub enum SgxInitRequest { /// response sent from the enclave app #[derive(Debug, Serialize, Deserialize)] -pub struct SgxInitResponse { - /// freshly generated or recovered sealed keypair - pub sealed_key_data: SealedKeyData, - /// if requested, keypair encrypted with the provided key - pub cloud_backup_key_data: Option, +pub enum SgxInitResponse { + WrapKey { + /// key sealed for local CPU + wrap_key_sealed: SealedKeyData, + /// wrapping public key + wrap_pub_key: rsa::RSAPublicKey, + /// report attesting the wrapping public key + /// (to be used for a quote) + pub_key_report: Report, + }, + /// response to key generation or recovery + GenOrRecover { + /// freshly generated or recovered sealed keypair + sealed_key_data: SealedKeyData, + /// if requested, keypair encrypted with the provided key + cloud_backup_key_data: Option, + }, +} + +/// obtain a json claim for RSA pubkey +/// (bigendian values are encoded in URL-safe base64) +pub fn get_claim(wrap_pub_key: &rsa::RSAPublicKey) -> String { + let n = wrap_pub_key.n().to_bytes_be(); + let e = wrap_pub_key.e().to_bytes_be(); + let encoded_n = base64::encode_config(&n, base64::URL_SAFE); + let encoded_e = base64::encode_config(&e, base64::URL_SAFE); + format!( + "{{\"kid\":\"wrapping-key\",\"kty\":\"RSA\",\"e\":\"{}\",\"n\":\"{}\"}}", + encoded_e, encoded_n + ) +} + +impl SgxInitResponse { + /// get key generation or recovery response + pub fn get_gen_response(self) -> Option<(SealedKeyData, Option)> { + match self { + SgxInitResponse::GenOrRecover { + sealed_key_data, + cloud_backup_key_data, + } => Some((sealed_key_data, cloud_backup_key_data)), + _ => None, + } + } } diff --git a/providers/softsign/Cargo.toml b/providers/softsign/Cargo.toml index e63b4c8f..db8cfd82 100644 --- a/providers/softsign/Cargo.toml +++ b/providers/softsign/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tmkms-softsign" -version = "0.1.2" +version = "0.2.0" authors = ["Tomas Tauber <2410580+tomtau@users.noreply.github.com>"] edition = "2018" @@ -9,7 +9,7 @@ edition = "2018" [dependencies] anomaly = "0.2" ed25519-dalek = "1" -rand_core = { version = "0.5", features = ["std"] } +rand_core = { version = "0.6", features = ["std"] } serde = { version = "1", features = ["serde_derive"] } serde_json = "1" structopt = "0.3" diff --git a/script/tmkms-sgx/init.py b/script/tmkms-sgx/init.py deleted file mode 100644 index cc4b4018..00000000 --- a/script/tmkms-sgx/init.py +++ /dev/null @@ -1,31 +0,0 @@ -import cmd -import os -import secrets -import subprocess -import base64 - -from azure.identity import ManagedIdentityCredential -from azure.keyvault.secrets import SecretClient - -tmkms_path = os.environ.get("TMKMS_RUNNER", "tmkms-light-sgx-runner") -backup_key_path = os.environ.get("BACKUP_KEY", "cloudbackup.key") -backup_data_path = os.environ.get("BACKUP_DATA", "/tmp/") -key_vault_name = os.environ["KEY_VAULT_NAME"] -backup_prefix = os.environ.get("CLOUD_BACKUP_PREFIX", "cloud-backup") -pubkey_display = os.environ.get("PUBKEY_DISPLAY", "bech32") -bech32_prefix = os.environ.get("BECH32_PREFIX", "crocnclconspub") -kv_uri = f"https://{key_vault_name}.vault.azure.net" -credential = ManagedIdentityCredential() -client = SecretClient(vault_url=kv_uri, credential=credential) -secret_name = backup_prefix + "-key" -secret_value = secrets.token_bytes(16) -base64_secret_value = base64.b64encode(secret_value).decode() -client.set_secret(secret_name, base64_secret_value) -try: - with open(backup_key_path, "wb") as f: - f.write(secret_value) - os.chmod(backup_key_path, 0o400) - command = [tmkms_path, "init", "-b", bech32_prefix, "-p", pubkey_display, "-e", backup_key_path, "-k", backup_data_path] - subprocess.run(command) -finally: - os.remove(backup_key_path) \ No newline at end of file diff --git a/script/tmkms-sgx/pyproject.toml b/script/tmkms-sgx/pyproject.toml deleted file mode 100644 index 2892c0b7..00000000 --- a/script/tmkms-sgx/pyproject.toml +++ /dev/null @@ -1,16 +0,0 @@ -[tool.poetry] -name = "sgx" -version = "0.1.0" -description = "" -authors = ["allthatjazzleo "] - -[tool.poetry.dependencies] -python = "^3.6" -azure-identity = "^1.5.0" -azure-keyvault-secrets = "^4.2.0" - -[tool.poetry.dev-dependencies] - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/script/tmkms-sgx/recover.py b/script/tmkms-sgx/recover.py deleted file mode 100644 index e8dceb68..00000000 --- a/script/tmkms-sgx/recover.py +++ /dev/null @@ -1,31 +0,0 @@ -import os -import base64 -import subprocess - -from azure.identity import ManagedIdentityCredential -from azure.keyvault.secrets import SecretClient - -tmkms_path = os.environ.get("TMKMS_RUNNER", "tmkms-light-sgx-runner") -backup_key_path = os.environ.get("BACKUP_KEY", "cloudbackup.key") -backup_data_path = os.environ.get("BACKUP_DATA", "/tmp/") -recover_consensus_key = os.environ.get("RECOVER_CONSENSUS_KEY", None) != None -key_vault_name = os.environ["KEY_VAULT_NAME"] -backup_prefix = os.environ.get("CLOUD_BACKUP_PREFIX", "cloud-backup") -pubkey_display = os.environ.get("PUBKEY_DISPLAY", "bech32") -bech32_prefix = os.environ.get("BECH32_PREFIX", "crocnclconspub") -kv_uri = f"https://{key_vault_name}.vault.azure.net" -credential = ManagedIdentityCredential() -client = SecretClient(vault_url=kv_uri, credential=credential) -secret_name = backup_prefix + "-key" -base64_secret_value = client.get_secret(secret_name).value -secret_value = base64.b64decode(base64_secret_value.encode()) -try: - with open(backup_key_path, "wb") as f: - f.write(secret_value) # change - os.chmod(backup_key_path, 0o400) - command = [tmkms_path, "recover", "-p", pubkey_display, "-b", bech32_prefix, "-e", backup_key_path, "-k", backup_data_path] - if recover_consensus_key: - command.append("-r") - subprocess.run(command) -finally: - os.remove(backup_key_path) \ No newline at end of file