diff --git a/rtc_auth_enclave/Cargo.lock b/rtc_auth_enclave/Cargo.lock index 02e76914..b5fb647e 100644 --- a/rtc_auth_enclave/Cargo.lock +++ b/rtc_auth_enclave/Cargo.lock @@ -50,8 +50,8 @@ dependencies = [ "log", "proc-macro2", "quote", - "serde", - "serde_json", + "serde 1.0.125", + "serde_json 1.0.64", "syn", "tempfile", "toml", @@ -141,6 +141,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "index-fixed" version = "0.3.1" @@ -157,6 +163,14 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "itoa" +version = "0.4.5" +source = "git+https://github.com/mesalock-linux/itoa-sgx#295ee451f5ec74f25c299552b481beb445ea3eb7" +dependencies = [ + "sgx_tstd", +] + [[package]] name = "itoa" version = "0.4.7" @@ -348,10 +362,13 @@ name = "rtc_tenclave" version = "0.1.0" dependencies = [ "cfg-if 1.0.0", + "hex", "rand 0.7.3", "ring", "rtc_types", "secrecy", + "serde 1.0.118", + "serde_json 1.0.60", "sgx_tcrypto", "sgx_tse", "sgx_tstd", @@ -386,6 +403,14 @@ dependencies = [ "zeroize", ] +[[package]] +name = "serde" +version = "1.0.118" +source = "git+https://github.com/mesalock-linux/serde-sgx#db0226f1d5d70fca6b96af2c285851502204e21c" +dependencies = [ + "sgx_tstd", +] + [[package]] name = "serde" version = "1.0.125" @@ -406,15 +431,26 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.60" +source = "git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3#380893814ad2a057758d825bab798aa117f7362a" +dependencies = [ + "itoa 0.4.5", + "ryu", + "serde 1.0.118", + "sgx_tstd", +] + [[package]] name = "serde_json" version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" dependencies = [ - "itoa", + "itoa 0.4.7", "ryu", - "serde", + "serde 1.0.125", ] [[package]] @@ -613,7 +649,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" dependencies = [ - "serde", + "serde 1.0.125", ] [[package]] diff --git a/rtc_data_enclave/Cargo.lock b/rtc_data_enclave/Cargo.lock index d53d93de..3c86c441 100644 --- a/rtc_data_enclave/Cargo.lock +++ b/rtc_data_enclave/Cargo.lock @@ -75,7 +75,7 @@ dependencies = [ "proc-macro2", "quote", "serde 1.0.125", - "serde_json", + "serde_json 1.0.64", "syn", "tempfile", "toml", @@ -181,6 +181,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "index-fixed" version = "0.3.1" @@ -206,6 +212,14 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "0.4.5" +source = "git+https://github.com/mesalock-linux/itoa-sgx#295ee451f5ec74f25c299552b481beb445ea3eb7" +dependencies = [ + "sgx_tstd", +] + [[package]] name = "itoa" version = "0.4.7" @@ -445,10 +459,13 @@ name = "rtc_tenclave" version = "0.1.0" dependencies = [ "cfg-if 1.0.0", + "hex", "rand 0.7.3", "ring", "rtc_types", "secrecy", + "serde 1.0.118", + "serde_json 1.0.60", "sgx_tcrypto", "sgx_tse", "sgx_tstd", @@ -531,13 +548,24 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.60" +source = "git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3#380893814ad2a057758d825bab798aa117f7362a" +dependencies = [ + "itoa 0.4.5", + "ryu", + "serde 1.0.118", + "sgx_tstd", +] + [[package]] name = "serde_json" version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" dependencies = [ - "itoa", + "itoa 0.4.7", "ryu", "serde 1.0.125", ] diff --git a/rtc_data_enclave/Cargo.toml b/rtc_data_enclave/Cargo.toml index 6ece2fb4..3c1da1e3 100644 --- a/rtc_data_enclave/Cargo.toml +++ b/rtc_data_enclave/Cargo.toml @@ -29,7 +29,7 @@ rtc_types = { path = "../rtc_types", features = ["teaclave_sgx"]} rtc_tenclave = { path = "../rtc_tenclave" } [dependencies] -# The build fails when specifying rev here, I don't know what is causing it. +# XXX: See comment about serde-sgx dependency versioning in rtc_tenclave/Cargo.toml serde_derive = { git = "https://github.com/mesalock-linux/serde-sgx" } serde = { git = "https://github.com/mesalock-linux/serde-sgx", features = ["derive"]} bincode = { git = "https://github.com/mesalock-linux/bincode-sgx.git" } diff --git a/rtc_tenclave/Cargo.lock b/rtc_tenclave/Cargo.lock index 3dd970af..7d9e4657 100644 --- a/rtc_tenclave/Cargo.lock +++ b/rtc_tenclave/Cargo.lock @@ -1,11 +1,44 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +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.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + [[package]] name = "bumpalo" version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "cc" version = "1.0.67" @@ -24,6 +57,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "getrandom" version = "0.1.14" @@ -43,7 +82,18 @@ checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1", ] [[package]] @@ -51,12 +101,32 @@ name = "hashbrown_tstd" version = "0.9.0" source = "git+https://github.com/apache/teaclave-sgx-sdk.git?rev=v1.1.3#a6a172e652b4db4eaa17e4faa078fda8922abdd0" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "index-fixed" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161ceaf2f41b6cd3f6502f5da085d4ad4393a51e0c70ed2fce1d5698d798fae" +[[package]] +name = "itoa" +version = "0.4.5" +source = "git+https://github.com/mesalock-linux/itoa-sgx#295ee451f5ec74f25c299552b481beb445ea3eb7" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + [[package]] name = "js-sys" version = "0.3.50" @@ -87,6 +157,15 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.7.2" @@ -113,6 +192,38 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "proptest" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5" +dependencies = [ + "bit-set", + "bitflags", + "byteorder", + "lazy_static", + "num-traits", + "quick-error 2.0.1", + "rand 0.8.3", + "rand_chacha 0.3.0", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quote" version = "1.0.9" @@ -132,7 +243,7 @@ dependencies = [ "libc", "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc", + "rand_hc 0.2.0", ] [[package]] @@ -146,6 +257,18 @@ dependencies = [ "sgx_tstd", ] +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha 0.3.0", + "rand_core 0.6.2", + "rand_hc 0.3.0", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -166,6 +289,16 @@ dependencies = [ "sgx_tstd", ] +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86 0.2.10", + "rand_core 0.6.2", +] + [[package]] name = "rand_core" version = "0.3.1" @@ -199,6 +332,15 @@ dependencies = [ "sgx_tstd", ] +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom 0.2.2", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -208,6 +350,24 @@ dependencies = [ "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core 0.6.2", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.2", +] + [[package]] name = "rdrand" version = "0.6.0" @@ -217,6 +377,30 @@ dependencies = [ "rand_core 0.4.2", ] +[[package]] +name = "redox_syscall" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "ring" version = "0.17.0-alpha.10" @@ -237,17 +421,24 @@ name = "rtc_tenclave" version = "0.1.0" dependencies = [ "cfg-if 1.0.0", + "hex", + "proptest", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (git+https://github.com/mesalock-linux/rand-sgx?tag=v0.7.3_sgx1.1.3)", "ring", "rtc_types", "secrecy", + "serde 1.0.118", + "serde 1.0.126", + "serde_json 1.0.60", + "serde_json 1.0.64", "sgx_tcrypto", "sgx_tse", "sgx_tstd", "sgx_types", "sgx_ucrypto", "sodalite", + "tempfile", "thiserror 1.0.24", "thiserror 1.0.9", "zeroize", @@ -263,6 +454,24 @@ dependencies = [ "thiserror 1.0.9", ] +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error 1.2.3", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + [[package]] name = "secrecy" version = "0.7.0" @@ -272,6 +481,42 @@ dependencies = [ "zeroize", ] +[[package]] +name = "serde" +version = "1.0.118" +source = "git+https://github.com/mesalock-linux/serde-sgx#db0226f1d5d70fca6b96af2c285851502204e21c" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "serde" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" + +[[package]] +name = "serde_json" +version = "1.0.60" +source = "git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3#380893814ad2a057758d825bab798aa117f7362a" +dependencies = [ + "itoa 0.4.5", + "ryu", + "serde 1.0.118", + "sgx_tstd", +] + +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa 0.4.7", + "ryu", + "serde 1.0.126", +] + [[package]] name = "sgx_alloc" version = "1.1.3" @@ -308,7 +553,7 @@ dependencies = [ [[package]] name = "sgx_tcrypto" version = "1.1.3" -source = "git+https://github.com/apache/teaclave-sgx-sdk.git#c2698dc2685f8dcd9550086c62077bceff15ded0" +source = "git+https://github.com/apache/teaclave-sgx-sdk.git#d1e2a909ee0a4e95437ba75149d12ab9d07fcfe1" dependencies = [ "sgx_types", ] @@ -363,7 +608,7 @@ source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk.git#c2698dc26 [[package]] name = "sgx_ucrypto" version = "1.1.3" -source = "git+https://github.com/apache/teaclave-sgx-sdk.git#c2698dc2685f8dcd9550086c62077bceff15ded0" +source = "git+https://github.com/apache/teaclave-sgx-sdk.git#d1e2a909ee0a4e95437ba75149d12ab9d07fcfe1" dependencies = [ "libc", "rand_core 0.3.1", @@ -405,6 +650,20 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "rand 0.8.3", + "redox_syscall", + "remove_dir_all", + "winapi", +] + [[package]] name = "thiserror" version = "1.0.9" @@ -456,12 +715,27 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[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.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "wasm-bindgen" version = "0.2.73" diff --git a/rtc_tenclave/Cargo.toml b/rtc_tenclave/Cargo.toml index d7bc98a0..19104a54 100644 --- a/rtc_tenclave/Cargo.toml +++ b/rtc_tenclave/Cargo.toml @@ -11,7 +11,7 @@ doctest = false crate-type = ["lib"] [features] -default = ["sgx_tstd", "sgx_tse", "rtc_types/teaclave_sgx", "rand", "thiserror", "sgx_tcrypto"] +default = ["sgx_tstd", "sgx_tse", "rtc_types/teaclave_sgx", "rand", "thiserror", "sgx_tcrypto", "serde", "serde_json"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -23,6 +23,33 @@ rand = { git = "https://github.com/mesalock-linux/rand-sgx", tag = "v0.7.3_sgx1. thiserror = { git = "https://github.com/mesalock-linux/thiserror-sgx.git", optional = true } sgx_tcrypto = { git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +# XXX: Work around serde-json-sgx / Cargo version handling issue +# +# We want to specify tag = "sgx_1.1.3" for the serde dependency here, +# but this makes Cargo resolve the dependency to a different crate +# version identity than what serde-json-sgx's dependency resolves to, +# even though both "master" and "sgx_1.1.3" point to same git revision. +# +# This causes build failures due to serde_json referring to a different +# versions of the Serialize / Deserialize traits than this project's code. +# +# We cannot use [patch] to override serde-json-sgx's dependency to use +# the same tag for serde-sgx as here, because of this Cargo limitation: +# +# * "Cannot patch underspecified git dependency" +# https://github.com/rust-lang/cargo/issues/7670 +# +# Comment: https://github.com/rust-lang/cargo/issues/7670#issuecomment-841722488 +# +# To work around this problem, the git reference here must match +# the upstream dependency line exactly: +# +# * https://github.com/mesalock-linux/serde-json-sgx/blob/sgx_1.1.3/Cargo.toml#L17 +# +serde = { git = "https://github.com/mesalock-linux/serde-sgx", optional = true } + +serde_json = { git = "https://github.com/mesalock-linux/serde-json-sgx", tag = "sgx_1.1.3", optional = true } + rtc_types = { path = "../rtc_types" } sgx_types = { git = "https://github.com/apache/teaclave-sgx-sdk.git", features = ["extra_traits"]} @@ -32,12 +59,19 @@ secrecy = { version = "0.7.0", default-features = false } ring = { version = "0.17.0-alpha.8", default-features = false } sodalite = { version = "0.4.0", default-features = false } cfg-if = "1.0.0" +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } [dev-dependencies] thiserror_std = { package = "thiserror", version = "1.0.9" } rand_std = { package = "rand", version = "0.7.3" } sgx_ucrypto = { git = "https://github.com/apache/teaclave-sgx-sdk.git" } +serde_std = { package = "serde", version = "1.0.0" } +serde_json_std = { package = "serde_json", version = "1.0.0" } + +# Test-only dependencies +proptest = "1.0.0" +tempfile = "3.2.0" [patch."https://github.com/apache/teaclave-sgx-sdk.git"] diff --git a/rtc_tenclave/src/kv_store/fs/inspect.rs b/rtc_tenclave/src/kv_store/fs/inspect.rs new file mode 100644 index 00000000..f49ccb06 --- /dev/null +++ b/rtc_tenclave/src/kv_store/fs/inspect.rs @@ -0,0 +1,49 @@ +//! [`InspectStore`] for [`FsStore`] + +use std::collections::HashMap; +use std::ffi::OsStr; +use std::fs::DirEntry; +use std::io; +use std::iter::Iterator; + +use serde::de::DeserializeOwned; +use serde::Serialize; + +use crate::kv_store::fs::decode_from_fs_safe; +use crate::kv_store::inspect::InspectStore; +use crate::kv_store::KvStore; + +use super::std_filer::StdFiler; +use super::FsStore; +use std::path::PathBuf; + +impl InspectStore for FsStore +where + V: Serialize + DeserializeOwned, +{ + fn to_map(&self) -> HashMap { + let entries /* impl Iterator> */ = self + .root_dir + .read_dir() + .unwrap_or_else(|_| panic!("read_dir {:?} failed", self.root_dir)); + + let keys /* impl Iterator */ = entries.map(|entry: io::Result| { + let entry: DirEntry = entry.expect("read_dir entry failed"); + let file_path: PathBuf = entry.path(); + let os_file_name: &OsStr = file_path + .file_name() + .unwrap_or_else(|| panic!("directory entry lacks file_name: {:?}", file_path)); + let file_name: &str = os_file_name.to_str().expect("OsStr.to_str failed"); + decode_from_fs_safe(file_name).expect("decode_from_fs_safe failed") + }); + + keys.map(|k| { + let loaded: Option = self + .load(&k) + .unwrap_or_else(|_| panic!("load {:?} failed!", k)); + let v: V = loaded.unwrap_or_else(|| panic!("key missing! {:?}", k)); + (k, v) + }) + .collect() + } +} diff --git a/rtc_tenclave/src/kv_store/fs/mod.rs b/rtc_tenclave/src/kv_store/fs/mod.rs new file mode 100644 index 00000000..c199a6f6 --- /dev/null +++ b/rtc_tenclave/src/kv_store/fs/mod.rs @@ -0,0 +1,162 @@ +//! Filesystem-based [`KvStore`] implementation + +pub mod std_filer; + +#[cfg(not(test))] +pub mod sgx_filer; + +// sgx_tstd (v1.1.3) does not support `fs::read_dir`, so limit the following to tests, for now. +// +// See: https://github.com/apache/incubator-teaclave-sgx-sdk/blob/v1.1.3/release_notes.md#partially-supported-modstraits-in-sgx_tstd + +#[cfg(not(test))] +use std::prelude::v1::*; + +use std::io; +use std::path::{Path, PathBuf}; + +use serde::de::DeserializeOwned; +use serde::Serialize; + +use super::KvStore; + +/// Simplified interface for reading and writing files. +pub trait Filer { + /// Read content of `path`, if any. + /// + /// Return [`None`] if `path` doesn't exist. + /// + fn get(&self, path: impl AsRef) -> io::Result>>; + + /// Write `content` to `path`. Discard any existing content. + fn put(&self, path: impl AsRef, content: impl AsRef<[u8]>) -> io::Result<()>; + + /// Delete `path`. Discard any existing content. + fn delete(&self, path: impl AsRef) -> io::Result<()>; +} + +/// [`KvStore`] using a file per key under `root_dir`. +pub struct FsStore { + pub(crate) root_dir: PathBuf, + pub(crate) filer: F, +} + +impl FsStore +where + F: Filer, +{ + /// # Note + /// + /// The caller must ensure that `root` exists as a directory. + /// + #[cfg_attr(not(test), allow(dead_code))] // currently only referenced in tests + pub fn new(root: impl AsRef, filer: F) -> Self { + let root_dir = root.as_ref().to_path_buf(); + FsStore { root_dir, filer } + } + + /// Resolve file name for the value of `key`. + fn value_path(&self, key: &str) -> PathBuf { + let file_name = encode_to_fs_safe(key); + self.root_dir.join(file_name) + } +} + +impl KvStore for FsStore +where + F: Filer, + V: Serialize + DeserializeOwned, +{ + // XXX: More explicit handling of serde_json::Error? + type Error = io::Error; + + fn load(&self, key: &str) -> Result, Self::Error> { + let value_file_name = self.value_path(key); + + // Note: Read all the data into memory first, then deserialize, for efficiency. + // See the docs for [`serde_json::de::from_reader`], + // and https://github.com/serde-rs/json/issues/160 + let loaded: Option> = self.filer.get(&value_file_name).map_err(|err| { + // XXX: Annotate err with some basic debugging context, for now. + Self::Error::new( + err.kind(), + format!("FsStore: read from {:?} failed: {}", value_file_name, err), + ) + })?; + let value: Option = loaded + .map(|serialised: Vec| serde_json::from_slice(serialised.as_slice())) + .transpose()?; + Ok(value) + } + + fn save(&mut self, key: &str, value: &V) -> Result<(), Self::Error> { + let value_file_name = self.value_path(key); + let serialized: Vec = serde_json::to_vec(&value)?; + self.filer + .put(&value_file_name, serialized) + .map_err(|err| { + // XXX: Annotate err with some basic debugging context, for now. + Self::Error::new( + err.kind(), + format!("FsStore: write to {:?} failed: {}", value_file_name, err), + ) + })?; + Ok(()) + } + + fn delete(&mut self, key: &str) -> Result<(), Self::Error> { + let path = self.value_path(key); + self.filer.delete(path)?; + Ok(()) + } +} + +/// Helper: Make `key` filesystem-safe. +pub(crate) fn encode_to_fs_safe(key: &str) -> String { + let encoded = hex::encode(key); + format!("x{}", encoded) +} + +/// Inverse of [`encode_to_fs_safe`]. +// FIXME: Just use a generic String as the error type, for now. +#[cfg_attr(not(test), allow(dead_code))] // currently only referenced in tests +pub(crate) fn decode_from_fs_safe(file_name: &str) -> Result { + let encoded: &str = file_name + .strip_prefix("x") + .ok_or_else(|| format!("decode_from_fs_safe: missing x prefix for {:?}", file_name))?; + let bytes: Vec = hex::decode(encoded).map_err(|err| err.to_string())?; + let decoded = String::from_utf8(bytes).map_err(|err| err.to_string())?; + Ok(decoded) +} + +#[cfg(test)] +mod inspect; + +#[cfg(test)] +mod tests { + use proptest::prelude::*; + + use super::decode_from_fs_safe; + use super::encode_to_fs_safe; + + /// [`encode_to_fs_safe`] encodes to filesystem-safe, and [`decode_from_fs_safe`] round-trips. + #[test] + fn prop_fs_safe_roundtrip() { + let test = |key: &String| { + let encoded = &encode_to_fs_safe(key); + assert!( + is_fs_safe(encoded), + "expected filesystem-safe, got encoded = {:?}", + encoded + ); + let decoded = &decode_from_fs_safe(encoded).unwrap(); + assert_eq!(key, decoded); + }; + proptest!(|(key in ".*")| test(&key)); + } + + /// Helper: Very conservative definition of filesystem-safe. + fn is_fs_safe(encoded: &str) -> bool { + !encoded.is_empty() && encoded.chars().all(|c| c.is_ascii_alphanumeric()) + } +} diff --git a/rtc_tenclave/src/kv_store/fs/sgx_filer.rs b/rtc_tenclave/src/kv_store/fs/sgx_filer.rs new file mode 100644 index 00000000..fa50689f --- /dev/null +++ b/rtc_tenclave/src/kv_store/fs/sgx_filer.rs @@ -0,0 +1,56 @@ +//! [`SgxFile`] support + +use std::prelude::v1::Vec; + +use std::io::ErrorKind::NotFound; +use std::io::Read; +use std::io::Result; +use std::io::Write; +use std::path::Path; + +use sgx_tstd::sgxfs; +use sgx_tstd::sgxfs::SgxFile; + +use super::Filer; + +/// TODO: key management policy +pub struct SgxFiler; + +// Default key management: +// +// * `protected_fs_file::generate_random_meta_data_key` +// https://github.com/intel/linux-sgx/blob/sgx_2.13.3/sdk/protected_fs/sgx_tprotected_fs/file_crypto.cpp#L197 +// +impl Filer for SgxFiler { + fn get(&self, path: impl AsRef) -> Result>> { + // TODO: open_ex with key + let value_file = SgxFile::open(path)?; + match read_all(value_file) { + Ok(contents) => Ok(Some(contents)), + Err(error) if error.kind() == NotFound => Ok(None), + Err(error) => Err(error), + } + } + + fn put(&self, path: impl AsRef, content: impl AsRef<[u8]>) -> Result<()> { + let contents: &[u8] = content.as_ref(); + // TODO: create_ex with key + let mut value_file = SgxFile::create(path)?; + value_file.write_all(contents) + } + + fn delete(&self, path: impl AsRef) -> Result<()> { + match sgxfs::remove(path) { + Err(error) if error.kind() == NotFound => Ok(()), + result => result, + } + } +} + +/// Helper: Like [`fs::read`], but take an open file. +fn read_all(mut file: SgxFile) -> Result> { + // XXX: No metadata for initial_buffer_size in sgxfs + let mut buf = Vec::new(); + file.read_to_end(&mut buf)?; + Ok(buf) +} diff --git a/rtc_tenclave/src/kv_store/fs/std_filer.rs b/rtc_tenclave/src/kv_store/fs/std_filer.rs new file mode 100644 index 00000000..587f53fc --- /dev/null +++ b/rtc_tenclave/src/kv_store/fs/std_filer.rs @@ -0,0 +1,109 @@ +//! [`std::fs::File`] support + +use std::prelude::v1::Vec; + +use std::io::ErrorKind::NotFound; +use std::io::Result; +use std::io::Write; + +use std::path::Path; + +// Under sgx_tstd, fs needs the std::untrusted prefix: +#[cfg(not(test))] +use std::untrusted::{fs, fs::File}; +#[cfg(test)] +use std::{fs, fs::File}; + +use super::Filer; + +pub struct StdFiler; + +impl Filer for StdFiler { + fn get(&self, path: impl AsRef) -> Result>> { + match fs::read(path) { + Ok(contents) => Ok(Some(contents)), + Err(error) if error.kind() == NotFound => Ok(None), + Err(error) => Err(error), + } + } + + fn put(&self, path: impl AsRef, content: impl AsRef<[u8]>) -> Result<()> { + let contents: &[u8] = content.as_ref(); + let mut value_file = File::create(path)?; + value_file.write_all(contents) + } + + fn delete(&self, path: impl AsRef) -> Result<()> { + match fs::remove_file(path) { + Err(error) if error.kind() == NotFound => Ok(()), + result => result, + } + } +} + +#[cfg(test)] +mod tests { + use tempfile::TempDir; + + use super::*; + + // Helper: Run `f` with a non-existent file path inside a temporary directory. + fn with_temp_path(f: impl FnOnce(&Path)) { + let temp_dir = TempDir::new().unwrap(); + f(&temp_dir.path().join("foo")); + temp_dir.close().unwrap() + } + + #[test] + fn get_not_found() { + with_temp_path(|path: &Path| { + assert!(!path.exists()); + assert_eq!(StdFiler.get(path).unwrap(), None); + }) + } + + #[test] + fn get_empty() { + with_temp_path(|path: &Path| { + File::create(path).unwrap(); + assert_eq!(StdFiler.get(path).unwrap().unwrap(), "".as_bytes()); + }) + } + + #[test] + fn put_get() { + with_temp_path(|path| { + StdFiler.put(path, "spam").unwrap(); + assert_eq!(StdFiler.get(path).unwrap().unwrap(), "spam".as_bytes()) + }) + } + + #[test] + fn put_get_overwrite() { + with_temp_path(|path| { + StdFiler.put(path, "spam").unwrap(); + StdFiler.put(path, "ham").unwrap(); + assert_eq!(StdFiler.get(path).unwrap().unwrap(), "ham".as_bytes()) + }) + } + + #[test] + fn delete_missing() { + with_temp_path(|path: &Path| { + assert!(!path.exists()); + assert!(StdFiler.delete(path).is_ok()); + assert!(!path.exists()); + }) + } + + #[test] + fn delete_present() { + with_temp_path(|path: &Path| { + StdFiler.put(path, "spam").unwrap(); + assert_eq!(StdFiler.get(path).unwrap().unwrap(), "spam".as_bytes()); + assert!(path.exists()); + assert!(StdFiler.delete(path).is_ok()); + assert!(!path.exists()); + }) + } +} diff --git a/rtc_tenclave/src/kv_store/in_memory.rs b/rtc_tenclave/src/kv_store/in_memory.rs new file mode 100644 index 00000000..93122353 --- /dev/null +++ b/rtc_tenclave/src/kv_store/in_memory.rs @@ -0,0 +1,76 @@ +//! In-memory implementations of [`KvStore`] (for testing) + +use serde::de::DeserializeOwned; +use serde::Serialize; +use std::collections::HashMap; +use std::prelude::v1::*; + +use super::KvStore; + +/// In-memory [`KvStore`] using [`HashMap`] +#[derive(Default)] +pub struct InMemoryStore { + pub(crate) map: HashMap, +} + +impl KvStore for InMemoryStore +where + V: Clone, +{ + type Error = Never; + + fn load(&self, key: &str) -> Result, Self::Error> { + Ok(self.map.get(key).cloned()) + } + + fn save(&mut self, key: &str, value: &V) -> Result<(), Self::Error> { + self.map.insert(key.to_string(), value.clone()); + Ok(()) + } + + fn delete(&mut self, key: &str) -> Result<(), Self::Error> { + self.map.remove(key); + Ok(()) + } +} + +/// In-memory [`KvStore`] using [`HashMap`] and [`serde_json`] serialization +#[derive(Default)] +pub struct InMemoryJsonStore { + pub(crate) map: HashMap>, +} + +impl KvStore for InMemoryJsonStore +where + V: Serialize + DeserializeOwned, +{ + type Error = serde_json::Error; + + fn load(&self, key: &str) -> Result, Self::Error> { + let loaded: Option<&[u8]> = self.map.get(key).map(|v| v.as_slice()); + let deserialized: Option = loaded.map(serde_json::from_slice).transpose()?; + Ok(deserialized) + } + + fn save(&mut self, key: &str, value: &V) -> Result<(), Self::Error> { + let serialized = serde_json::to_vec(&value)?; + self.map.insert(key.to_string(), serialized); + Ok(()) + } + + fn delete(&mut self, key: &str) -> Result<(), Self::Error> { + self.map.remove(key); + Ok(()) + } +} + +/// TODO: Replace with ! once stabilized. +/// +/// See: +/// +/// * https://doc.rust-lang.org/beta/unstable-book/language-features/never-type.html +/// * https://github.com/rust-lang/rfcs/blob/master/text/1216-bang-type.md +/// * https://github.com/rust-lang/rust/issues/35121 +/// +#[derive(Debug)] +pub enum Never {} diff --git a/rtc_tenclave/src/kv_store/inspect.rs b/rtc_tenclave/src/kv_store/inspect.rs new file mode 100644 index 00000000..b37ec8dc --- /dev/null +++ b/rtc_tenclave/src/kv_store/inspect.rs @@ -0,0 +1,44 @@ +//! Support for inspecting [`KvStore`] instances (for testing and debugging) + +#[cfg(not(test))] +use std::prelude::v1::*; + +use std::borrow::ToOwned; +use std::collections::HashMap; + +use serde::de::DeserializeOwned; +use serde::Serialize; + +use super::in_memory::{InMemoryJsonStore, InMemoryStore}; +use super::KvStore; + +pub trait InspectStore { + fn to_map(&self) -> HashMap; +} + +impl InspectStore for InMemoryStore +where + V: Clone, +{ + fn to_map(&self) -> HashMap { + self.map.clone() + } +} + +impl InspectStore for InMemoryJsonStore +where + V: Serialize + DeserializeOwned, +{ + fn to_map(&self) -> HashMap { + self.map + .keys() + .map(|k| { + let loaded: Option = self + .load(k) + .unwrap_or_else(|_| panic!("load {:?} failed!", k)); + let v: V = loaded.unwrap_or_else(|| panic!("key missing! {:?}", k)); + (k.to_owned(), v) + }) + .collect() + } +} diff --git a/rtc_tenclave/src/kv_store/mod.rs b/rtc_tenclave/src/kv_store/mod.rs new file mode 100644 index 00000000..6985944e --- /dev/null +++ b/rtc_tenclave/src/kv_store/mod.rs @@ -0,0 +1,51 @@ +//! Simple key-value store abstraction + +mod fs; +mod in_memory; + +/// A key-value store. +/// +/// These methods borrow key and value references, +/// to suit cloning / serialising implementations. +/// +pub trait KvStore { + type Error; + + /// Load the saved value for `key`, if any. + /// + /// Return [`None`] if `key` has no previous value. + /// + fn load(&self, key: &str) -> Result, Self::Error>; + + /// Save a new value for `key`. + /// + /// This will replace any existing value. + /// + fn save(&mut self, key: &str, value: &V) -> Result<(), Self::Error>; + + /// Delete the saved value for `key`. + fn delete(&mut self, key: &str) -> Result<(), Self::Error>; + + /// Alter the value of `key`. + /// + /// This operation is a generalisation of [`Self::load`], [`Self::save`], and [`Self::delete`]. + /// + fn alter(&mut self, key: &str, alter_fn: F) -> Result, Self::Error> + where + F: FnOnce(Option) -> Option, + { + let loaded: Option = self.load(key)?; + let altered: Option = alter_fn(loaded); + match &altered { + None => self.delete(key)?, + Some(value) => self.save(key, value)?, + }; + Ok(altered) + } +} + +#[cfg(test)] +mod inspect; + +#[cfg(test)] +mod tests; diff --git a/rtc_tenclave/src/kv_store/tests.rs b/rtc_tenclave/src/kv_store/tests.rs new file mode 100644 index 00000000..14a4b45f --- /dev/null +++ b/rtc_tenclave/src/kv_store/tests.rs @@ -0,0 +1,144 @@ +//! Tests for [`rtc_tenclave::kv_store`] + +#[cfg(not(test))] +use std::prelude::v1::*; + +use std::collections::HashMap; + +use proptest::prelude::*; +use proptest::test_runner::TestCaseResult; +use tempfile::TempDir; + +use super::fs::{std_filer::StdFiler, FsStore}; +use super::in_memory::{InMemoryJsonStore, InMemoryStore}; +use super::inspect::InspectStore; +use super::KvStore; + +/// Verify that executing a sequence of store operations matches a simple model. +#[test] +fn prop_store_ops_match_model() { + // FIXME: This value type parameter needs better handling. + type V = String; + + /// Helper: Represent store operations. + #[derive(Debug)] + enum StoreOp { + Save { key: String, value: V }, + Delete { key: String }, + AlterId { key: String }, + AlterConst { key: String, replacement: Option }, + AlterUpdate { key: String, new_value: V }, + } + use StoreOp::*; + impl StoreOp { + /// Apply operation, and also check some invariants. + fn apply(&self, store: &mut S) -> Result<(), S::Error> + where + S: KvStore, + { + match self { + Save { key, value } => { + store.save(key, value)?; + assert_eq!(store.load(key)?.as_ref(), Some(value)); + } + Delete { key } => { + store.delete(key)?; + assert_eq!(store.load(key)?, None); + } + AlterId { key } => { + let previous = store.load(key)?; + store.alter(key, |loaded| loaded)?; + assert_eq!(store.load(key)?, previous); + } + AlterConst { key, replacement } => { + store.alter(key, |_| replacement.clone())?; + assert_eq!(store.load(key)?.as_ref(), replacement.as_ref()); + } + AlterUpdate { key, new_value } => { + let previous = store.load(key)?; + store.alter(key, |existing: Option| { + existing.map(|_| new_value.clone()) + })?; + assert_eq!(store.load(key)?.as_ref(), previous.map(|_| new_value)); + } + }; + Ok(()) + } + } + + // Strategy to generate lists of store operations. + let store_ops_strategy = { + let keys = prop_oneof!(r"[a-z]{0,5}", ".*"); + let values = prop_oneof!(keys.clone()); // TODO: Non-string values + let half_ops = prop_oneof!( + (Just("Save"), values.clone().prop_map(Some)), + (Just("Delete"), Just(None)), + (Just("AlterId"), Just(None)), + (Just("AlterConst"), proptest::option::of(values.clone())), + (Just("AlterUpdate"), values.clone().prop_map(Some)), + ); + + // This strategy will generate key / value pairs with multiple values per key, + // and shuffle them to have some interleaving, for bugs that depend on that. + proptest::collection::hash_map(keys, proptest::collection::vec(half_ops, 0..10), 0..10) + .prop_map(flatten_key_values) + .prop_map(|pairs: Vec<_>| -> Vec { + pairs + .into_iter() + .map(|(key, half_op)| -> StoreOp { + match half_op { + ("Save", Some(value)) => Save { key, value }, + ("Delete", None) => Delete { key }, + ("AlterId", None) => AlterId { key }, + ("AlterConst", replacement) => AlterConst { key, replacement }, + ("AlterUpdate", Some(new_value)) => AlterUpdate { key, new_value }, + unexpected => panic!("unexpected: {:?}", unexpected), + } + }) + .collect() + }) + .prop_shuffle() + }; + + /// Helper: Check that store state matches model. + fn check_state(store1: &impl InspectStore, store2: &impl InspectStore) -> TestCaseResult { + prop_assert_eq!(store1.to_map(), store2.to_map()); + Ok(()) + } + + fn test(store_ops_vec: Vec) -> TestCaseResult { + // Init the models + let store_model = &mut InMemoryStore::default(); + let store_model_json = &mut InMemoryJsonStore::default(); + + // Init the store under test + let temp_dir = TempDir::new().unwrap(); + let store_fs = &mut FsStore::new(&temp_dir, StdFiler); + + for ref op in store_ops_vec { + op.apply(store_model).unwrap(); + op.apply(store_model_json).unwrap(); + op.apply(store_fs).unwrap(); + + // Models match each other + check_state(store_model, store_model_json)?; + // Models match store_fs + check_state(store_model, store_fs)?; + check_state(store_model_json, store_fs)?; + } + + temp_dir.close()?; + Ok(()) + } + + proptest!(|(store_ops_vec in store_ops_strategy)| { + test(store_ops_vec)?; + }); +} + +/// Helper: Flatten `{K => [V, …], …}` to `[(K, V), …]`, cloning `K` for each `V`. +fn flatten_key_values(kvs: HashMap>) -> Vec<(K, V)> { + kvs.into_iter() + .flat_map(|(k, vs)| vs.into_iter().map(move |v| (k.clone(), v))) + .collect() +} diff --git a/rtc_tenclave/src/lib.rs b/rtc_tenclave/src/lib.rs index db0d3873..4cfb33f7 100644 --- a/rtc_tenclave/src/lib.rs +++ b/rtc_tenclave/src/lib.rs @@ -16,9 +16,12 @@ cfg_if::cfg_if! { extern crate thiserror_std as thiserror; extern crate rand_std as rand; extern crate sgx_ucrypto as sgx_tcrypto; + extern crate serde_std as serde; + extern crate serde_json_std as serde_json; } } pub mod crypto; pub mod enclave; +pub mod kv_store; pub mod util;