diff --git a/.github/actions/lint-rust/action.yml b/.github/actions/lint-rust/action.yml
index 0d1a3f9325..a945dc911c 100644
--- a/.github/actions/lint-rust/action.yml
+++ b/.github/actions/lint-rust/action.yml
@@ -11,6 +11,9 @@ inputs:
features:
description: Features to enable, separated by comma
required: false
+ exclude:
+ description: Packages to exclude, separated by comma
+ required: false
runs:
using: composite
steps:
@@ -19,6 +22,7 @@ runs:
env:
MANIFEST_PATH: ${{ inputs.manifest_path }}
FEATURES: ${{ inputs.features }}
+ EXCLUDE: ${{ inputs.exclude }}
run: |
if [[ -z "$FEATURES" ]]; then
FEATURES_ARG=("--all-features")
@@ -26,8 +30,18 @@ runs:
FEATURES_ARG=("--features" "$FEATURES")
fi
+ if [[ -z "$EXCLUDE" ]]; then
+ EXCLUDE_ARG=("")
+ else
+ EXCLUDE_ARG=("--workspace")
+ for PKG in ${EXCLUDE//,/ }; do
+ EXCLUDE_ARG+=("--exclude" "$PKG")
+ done
+ fi
+
cargo clippy \
${FEATURES_ARG[@]} \
+ ${EXCLUDE_ARG[@]} \
--locked \
--manifest-path "$MANIFEST_PATH" \
-- \
diff --git a/.github/workflows/ci-lint.yml b/.github/workflows/ci-lint.yml
index 3d8dc847ad..4f993bf5a9 100644
--- a/.github/workflows/ci-lint.yml
+++ b/.github/workflows/ci-lint.yml
@@ -32,9 +32,16 @@ jobs:
run: rustup show
- name: Lint Rust code
+ uses: ./.github/actions/lint-rust
+ with:
+ features: debug-logging,test,unsafe-allow-debug
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Lint Rust code (mock TEE)
uses: ./.github/actions/lint-rust
with:
features: debug-logging,debug-mock-sgx,test,unsafe-allow-debug
+ exclude: rofl-containers,rofl-appd
token: ${{ secrets.GITHUB_TOKEN }}
- name: Lint Rust code (TDX)
diff --git a/.github/workflows/ci-test.yaml b/.github/workflows/ci-test.yaml
index 1c2f4a7d38..623bd39ae2 100644
--- a/.github/workflows/ci-test.yaml
+++ b/.github/workflows/ci-test.yaml
@@ -61,16 +61,20 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update
- sudo apt-get install gcc-multilib clang-11
+ sudo apt-get install gcc-multilib clang-18
- name: Set up Rust
run: rustup show
- - name: Build for SGX
- run: cargo build --target x86_64-fortanix-unknown-sgx
+ - name: Build select crates for SGX
+ run: |
+ cargo build --target x86_64-fortanix-unknown-sgx \
+ --package oasis-runtime-sdk \
+ --package oasis-runtime-sdk-contracts \
+ --package oasis-runtime-sdk-evm
env:
CFLAGS_x86_64_fortanix_unknown_sgx: -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening
- CC_x86_64_fortanix_unknown_sgx: clang-11
+ CC_x86_64_fortanix_unknown_sgx: clang-18
check-core-versions:
# NOTE: This name appears in GitHub's Checks API.
@@ -304,6 +308,12 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
+ - name: Disable AppArmor
+ run: |
+ sudo systemctl stop apparmor.service
+ sudo systemctl disable apparmor.service
+ sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
+
- name: Set up Node.js LTS
uses: actions/setup-node@v4
with:
@@ -383,6 +393,12 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
+ - name: Disable AppArmor
+ run: |
+ sudo systemctl stop apparmor.service
+ sudo systemctl disable apparmor.service
+ sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
+
- name: Set up Rust
run: rustup show
@@ -493,6 +509,12 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
+ - name: Disable AppArmor
+ run: |
+ sudo systemctl stop apparmor.service
+ sudo systemctl disable apparmor.service
+ sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
+
- name: Set up Rust
run: rustup show
diff --git a/.github/workflows/release-rofl-containers.yml b/.github/workflows/release-rofl-containers.yml
new file mode 100644
index 0000000000..757b127c5a
--- /dev/null
+++ b/.github/workflows/release-rofl-containers.yml
@@ -0,0 +1,31 @@
+name: release-rofl-containers
+
+on:
+ push:
+ tags:
+ - 'rofl-containers/v[0-9]+.[0-9]+*'
+
+permissions:
+ contents: write
+
+jobs:
+ release-rofl-containers:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Build rofl-containers
+ id: build
+ uses: oasisprotocol/oasis-sdk/.github/actions/hash-rust@main
+ with:
+ image: ghcr.io/oasisprotocol/runtime-builder:main
+ pkg-dirs: rofl-containers
+ binaries: rofl-containers
+ clean: no
+
+ - name: Publish the release
+ uses: ncipollo/release-action@v1
+ with:
+ artifacts: ${{ github.workspace }}/${{ steps.build.outputs.build-path }}/rofl-containers
+ token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/Cargo.lock b/Cargo.lock
index e7b5978311..f6f5d45a74 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -230,6 +230,28 @@ dependencies = [
"serde_json",
]
+[[package]]
+name = "async-stream"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.90",
+]
+
[[package]]
name = "async-trait"
version = "0.1.83"
@@ -241,6 +263,21 @@ dependencies = [
"syn 2.0.90",
]
+[[package]]
+name = "atomic"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
[[package]]
name = "auto_impl"
version = "1.2.0"
@@ -258,6 +295,33 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+[[package]]
+name = "aws-lc-rs"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f409eb70b561706bf8abba8ca9c112729c481595893fd06a2dd9af8ed8441148"
+dependencies = [
+ "aws-lc-sys",
+ "paste",
+ "untrusted 0.7.1",
+ "zeroize",
+]
+
+[[package]]
+name = "aws-lc-sys"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8478a5c29ead3f3be14aff8a202ad965cf7da6856860041bfca271becf8ba48b"
+dependencies = [
+ "bindgen 0.69.5",
+ "cc",
+ "cmake",
+ "dunce",
+ "fs_extra",
+ "libc",
+ "paste",
+]
+
[[package]]
name = "backtrace"
version = "0.3.74"
@@ -303,6 +367,12 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d"
+[[package]]
+name = "binascii"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72"
+
[[package]]
name = "bincode"
version = "1.3.3"
@@ -457,6 +527,12 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c"
+[[package]]
+name = "bytemuck"
+version = "1.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a"
+
[[package]]
name = "byteorder"
version = "1.5.0"
@@ -484,6 +560,8 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc"
dependencies = [
+ "jobserver",
+ "libc",
"shlex",
]
@@ -668,6 +746,17 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "cookie"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
+dependencies = [
+ "percent-encoding",
+ "time",
+ "version_check",
+]
+
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
@@ -797,7 +886,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
dependencies = [
"generic-array",
- "rand_core",
+ "rand_core 0.6.4",
"subtle",
"zeroize",
]
@@ -809,7 +898,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
- "rand_core",
+ "rand_core 0.6.4",
"typenum",
]
@@ -828,6 +917,17 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
+[[package]]
+name = "cuckoofilter"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b810a8449931679f64cd7eef1bbd0fa315801b6d5d9cdc1ace2804d6529eee18"
+dependencies = [
+ "byteorder",
+ "fnv",
+ "rand 0.7.3",
+]
+
[[package]]
name = "curve25519-dalek"
version = "4.1.3"
@@ -863,7 +963,7 @@ checksum = "1c359b7249347e46fb28804470d071c921156ad62b3eef5d34e2ba867533dec8"
dependencies = [
"byteorder",
"digest 0.9.0",
- "rand_core",
+ "rand_core 0.6.4",
"subtle-ng",
"zeroize",
]
@@ -1045,6 +1145,39 @@ dependencies = [
"syn 2.0.90",
]
+[[package]]
+name = "devise"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1d90b0c4c777a2cad215e3c7be59ac7c15adf45cf76317009b7d096d46f651d"
+dependencies = [
+ "devise_codegen",
+ "devise_core",
+]
+
+[[package]]
+name = "devise_codegen"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71b28680d8be17a570a2334922518be6adc3f58ecc880cbb404eaeb8624fd867"
+dependencies = [
+ "devise_core",
+ "quote",
+]
+
+[[package]]
+name = "devise_core"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7"
+dependencies = [
+ "bitflags 2.6.0",
+ "proc-macro2",
+ "proc-macro2-diagnostics",
+ "quote",
+ "syn 2.0.90",
+]
+
[[package]]
name = "diff"
version = "0.1.13"
@@ -1089,6 +1222,12 @@ dependencies = [
"syn 2.0.90",
]
+[[package]]
+name = "dunce"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+
[[package]]
name = "ecdsa"
version = "0.16.9"
@@ -1121,7 +1260,7 @@ checksum = "3c8465edc8ee7436ffea81d21a019b16676ee3db267aa8d5a8d729581ecf998b"
dependencies = [
"curve25519-dalek-ng",
"hex",
- "rand_core",
+ "rand_core 0.6.4",
"sha2 0.9.9",
"zeroize",
]
@@ -1134,7 +1273,7 @@ checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
dependencies = [
"curve25519-dalek",
"ed25519",
- "rand_core",
+ "rand_core 0.6.4",
"serde",
"sha2 0.10.8",
"signature",
@@ -1163,7 +1302,7 @@ dependencies = [
"hkdf",
"pem-rfc7468",
"pkcs8",
- "rand_core",
+ "rand_core 0.6.4",
"sec1",
"subtle",
"zeroize",
@@ -1344,13 +1483,19 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
+[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
[[package]]
name = "ff"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
dependencies = [
- "rand_core",
+ "rand_core 0.6.4",
"subtle",
]
@@ -1360,6 +1505,20 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
+[[package]]
+name = "figment"
+version = "0.10.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3"
+dependencies = [
+ "atomic",
+ "pear",
+ "serde",
+ "toml",
+ "uncased",
+ "version_check",
+]
+
[[package]]
name = "fixed-hash"
version = "0.8.0"
@@ -1367,7 +1526,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534"
dependencies = [
"byteorder",
- "rand",
+ "rand 0.8.5",
"rustc-hex",
"static_assertions",
]
@@ -1412,6 +1571,12 @@ dependencies = [
"percent-encoding",
]
+[[package]]
+name = "fs_extra"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
+
[[package]]
name = "funty"
version = "2.0.0"
@@ -1507,6 +1672,19 @@ dependencies = [
"slab",
]
+[[package]]
+name = "generator"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
+dependencies = [
+ "cc",
+ "libc",
+ "log",
+ "rustversion",
+ "windows",
+]
+
[[package]]
name = "generic-array"
version = "0.14.7"
@@ -1518,6 +1696,17 @@ dependencies = [
"zeroize",
]
+[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
+]
+
[[package]]
name = "getrandom"
version = "0.2.15"
@@ -1527,7 +1716,7 @@ dependencies = [
"cfg-if 1.0.0",
"js-sys",
"libc",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
"wasm-bindgen",
]
@@ -1537,8 +1726,8 @@ version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea1015b5a70616b688dc230cfe50c8af89d972cb132d5a622814d29773b10b9"
dependencies = [
- "rand",
- "rand_core",
+ "rand 0.8.5",
+ "rand_core 0.6.4",
]
[[package]]
@@ -1581,10 +1770,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
dependencies = [
"ff",
- "rand_core",
+ "rand_core 0.6.4",
"subtle",
]
+[[package]]
+name = "h2"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http",
+ "indexmap 2.7.0",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "h3"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e7675a0963b47a6d12fe44c279918b4ffb19baee838ac37f48d2722ad5bc6ab"
+dependencies = [
+ "bytes",
+ "fastrand",
+ "futures-util",
+ "http",
+ "pin-project-lite",
+ "tokio",
+]
+
[[package]]
name = "half"
version = "1.8.3"
@@ -1622,6 +1844,12 @@ dependencies = [
"crunchy",
]
+[[package]]
+name = "hash_hasher"
+version = "2.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74721d007512d0cb3338cd20f0654ac913920061a4c4d0d8708edb3f2a698c0c"
+
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -1648,6 +1876,12 @@ dependencies = [
"unicode-segmentation",
]
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
[[package]]
name = "hermit-abi"
version = "0.4.0"
@@ -1660,6 +1894,12 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+[[package]]
+name = "hex-literal"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
+
[[package]]
name = "hkdf"
version = "0.12.4"
@@ -1722,12 +1962,63 @@ dependencies = [
"itoa",
]
+[[package]]
+name = "http-body"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+dependencies = [
+ "bytes",
+ "http",
+]
+
[[package]]
name = "httparse"
version = "1.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "pin-project-lite",
+ "tokio",
+]
+
[[package]]
name = "iana-time-zone"
version = "0.1.61"
@@ -1969,8 +2260,15 @@ checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
dependencies = [
"equivalent",
"hashbrown 0.15.2",
+ "serde",
]
+[[package]]
+name = "inlinable_string"
+version = "0.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
+
[[package]]
name = "inout"
version = "0.1.3"
@@ -2004,7 +2302,7 @@ version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.4.0",
"libc",
"windows-sys 0.52.0",
]
@@ -2042,6 +2340,15 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
+[[package]]
+name = "jobserver"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "js-sys"
version = "0.3.74"
@@ -2109,7 +2416,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if 1.0.0",
- "windows-targets 0.48.5",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -2146,6 +2453,21 @@ version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+[[package]]
+name = "loom"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
+dependencies = [
+ "cfg-if 1.0.0",
+ "generator",
+ "scoped-tls",
+ "serde",
+ "serde_json",
+ "tracing",
+ "tracing-subscriber",
+]
+
[[package]]
name = "lru"
version = "0.12.5"
@@ -2155,6 +2477,15 @@ dependencies = [
"hashbrown 0.15.2",
]
+[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata 0.1.10",
+]
+
[[package]]
name = "mbedtls"
version = "0.12.3"
@@ -2240,10 +2571,16 @@ checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d"
dependencies = [
"byteorder",
"keccak",
- "rand_core",
+ "rand_core 0.6.4",
"zeroize",
]
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@@ -2266,7 +2603,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
- "wasi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0",
]
@@ -2281,13 +2618,33 @@ dependencies = [
"httparse",
"lazy_static",
"log",
- "rand",
+ "rand 0.8.5",
"regex",
"serde_json",
"serde_urlencoded",
"similar",
]
+[[package]]
+name = "multer"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
+dependencies = [
+ "bytes",
+ "encoding_rs",
+ "futures-util",
+ "http",
+ "httparse",
+ "log",
+ "memchr",
+ "mime",
+ "spin",
+ "tokio",
+ "tokio-util",
+ "version_check",
+]
+
[[package]]
name = "nix"
version = "0.29.0"
@@ -2311,6 +2668,16 @@ dependencies = [
"minimal-lexical",
]
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
[[package]]
name = "num"
version = "0.4.3"
@@ -2358,7 +2725,7 @@ dependencies = [
"num-integer",
"num-iter",
"num-traits",
- "rand",
+ "rand 0.8.5",
"smallvec",
"zeroize",
]
@@ -2430,6 +2797,16 @@ dependencies = [
"libm",
]
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi 0.3.9",
+ "libc",
+]
+
[[package]]
name = "oasis-cbor"
version = "0.5.1"
@@ -2472,7 +2849,7 @@ dependencies = [
"oasis-contract-sdk-macros",
"oasis-contract-sdk-types",
"oasis-runtime-sdk",
- "rand_core",
+ "rand_core 0.6.4",
"rand_xorshift",
"thiserror",
"wee_alloc",
@@ -2539,7 +2916,7 @@ dependencies = [
"oasis-core-runtime",
"p256",
"p384",
- "rand",
+ "rand 0.8.5",
"rustc-hex",
"secret-sharing",
"sgx-isa",
@@ -2588,7 +2965,7 @@ dependencies = [
"oasis-cbor",
"oid-registry 0.7.1",
"percent-encoding",
- "rand",
+ "rand 0.8.5",
"rsa",
"rustc-hex",
"serde",
@@ -2643,8 +3020,8 @@ dependencies = [
"once_cell",
"p256",
"p384",
- "rand",
- "rand_core",
+ "rand 0.8.5",
+ "rand_core 0.6.4",
"schnorrkel",
"sha2 0.10.8",
"sha3",
@@ -2673,7 +3050,7 @@ dependencies = [
"oasis-runtime-sdk",
"once_cell",
"pretty_assertions",
- "rand_core",
+ "rand_core 0.6.4",
"snap",
"thiserror",
"walrus",
@@ -2703,8 +3080,8 @@ dependencies = [
"oasis-runtime-sdk",
"once_cell",
"primitive-types",
- "rand",
- "rand_core",
+ "rand 0.8.5",
+ "rand_core 0.6.4",
"ripemd",
"rlp",
"serde",
@@ -2774,8 +3151,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
-name = "p256"
-version = "0.13.2"
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "p256"
+version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
dependencies = [
@@ -2852,6 +3235,29 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+[[package]]
+name = "pear"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467"
+dependencies = [
+ "inlinable_string",
+ "pear_codegen",
+ "yansi",
+]
+
+[[package]]
+name = "pear_codegen"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147"
+dependencies = [
+ "proc-macro2",
+ "proc-macro2-diagnostics",
+ "quote",
+ "syn 2.0.90",
+]
+
[[package]]
name = "peeking_take_while"
version = "0.1.2"
@@ -3091,6 +3497,19 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "proc-macro2-diagnostics"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.90",
+ "version_check",
+ "yansi",
+]
+
[[package]]
name = "prost"
version = "0.13.3"
@@ -3129,6 +3548,19 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
+[[package]]
+name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "getrandom 0.1.16",
+ "libc",
+ "rand_chacha 0.2.2",
+ "rand_core 0.5.1",
+ "rand_hc",
+]
+
[[package]]
name = "rand"
version = "0.8.5"
@@ -3136,8 +3568,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
- "rand_chacha",
- "rand_core",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
+[[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",
]
[[package]]
@@ -3147,7 +3589,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
- "rand_core",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom 0.1.16",
]
[[package]]
@@ -3156,7 +3607,16 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
- "getrandom",
+ "getrandom 0.2.15",
+]
+
+[[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]]
@@ -3165,7 +3625,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
dependencies = [
- "rand_core",
+ "rand_core 0.6.4",
]
[[package]]
@@ -3197,6 +3657,32 @@ dependencies = [
"bitflags 2.6.0",
]
+[[package]]
+name = "ref-cast"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931"
+dependencies = [
+ "ref-cast-impl",
+]
+
+[[package]]
+name = "ref-cast-impl"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.90",
+]
+
+[[package]]
+name = "ref-swap"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09c30c54dffee5b40af088d5d50aa3455c91a0127164b51f0215efc4cb28fb3c"
+
[[package]]
name = "regex"
version = "1.11.1"
@@ -3205,8 +3691,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
- "regex-automata",
- "regex-syntax",
+ "regex-automata 0.4.9",
+ "regex-syntax 0.8.5",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax 0.6.29",
]
[[package]]
@@ -3217,9 +3712,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
- "regex-syntax",
+ "regex-syntax 0.8.5",
]
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+
[[package]]
name = "regex-syntax"
version = "0.8.5"
@@ -3244,10 +3745,10 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
dependencies = [
"cc",
"cfg-if 1.0.0",
- "getrandom",
+ "getrandom 0.2.15",
"libc",
"spin",
- "untrusted",
+ "untrusted 0.9.0",
"windows-sys 0.52.0",
]
@@ -3282,6 +3783,115 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "rocket"
+version = "0.6.0-dev"
+source = "git+https://github.com/rwf2/Rocket?rev=28891e8072136f4641a33fb8c3f2aafce9d88d5b#28891e8072136f4641a33fb8c3f2aafce9d88d5b"
+dependencies = [
+ "async-stream",
+ "async-trait",
+ "binascii",
+ "bytes",
+ "cookie",
+ "either",
+ "figment",
+ "futures",
+ "http",
+ "hyper",
+ "hyper-util",
+ "indexmap 2.7.0",
+ "libc",
+ "memchr",
+ "multer",
+ "num_cpus",
+ "parking_lot",
+ "pin-project-lite",
+ "rand 0.8.5",
+ "ref-cast",
+ "ref-swap",
+ "rocket_codegen",
+ "rocket_http",
+ "rustls",
+ "s2n-quic-h3",
+ "serde",
+ "serde_json",
+ "state",
+ "tempfile",
+ "thread_local",
+ "time",
+ "tinyvec",
+ "tokio",
+ "tokio-rustls",
+ "tokio-stream",
+ "tokio-util",
+ "tracing",
+ "tracing-subscriber",
+ "ubyte",
+ "version_check",
+ "yansi",
+]
+
+[[package]]
+name = "rocket_codegen"
+version = "0.6.0-dev"
+source = "git+https://github.com/rwf2/Rocket?rev=28891e8072136f4641a33fb8c3f2aafce9d88d5b#28891e8072136f4641a33fb8c3f2aafce9d88d5b"
+dependencies = [
+ "devise",
+ "glob",
+ "indexmap 2.7.0",
+ "proc-macro2",
+ "quote",
+ "rocket_http",
+ "syn 2.0.90",
+ "unicode-xid",
+ "version_check",
+]
+
+[[package]]
+name = "rocket_http"
+version = "0.6.0-dev"
+source = "git+https://github.com/rwf2/Rocket?rev=28891e8072136f4641a33fb8c3f2aafce9d88d5b#28891e8072136f4641a33fb8c3f2aafce9d88d5b"
+dependencies = [
+ "cookie",
+ "either",
+ "indexmap 2.7.0",
+ "memchr",
+ "pear",
+ "percent-encoding",
+ "ref-cast",
+ "serde",
+ "stable-pattern",
+ "state",
+ "time",
+ "tinyvec",
+ "uncased",
+]
+
+[[package]]
+name = "rofl-appd"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "oasis-runtime-sdk",
+ "rocket",
+ "rustc-hex",
+ "serde",
+ "sp800-185",
+ "thiserror",
+ "tokio",
+ "tokio-retry",
+ "zeroize",
+]
+
+[[package]]
+name = "rofl-containers"
+version = "0.1.0"
+dependencies = [
+ "base64",
+ "oasis-runtime-sdk",
+ "rofl-appd",
+]
+
[[package]]
name = "rofl-utils"
version = "0.1.1"
@@ -3319,7 +3929,7 @@ dependencies = [
"num-traits",
"pkcs1",
"pkcs8",
- "rand_core",
+ "rand_core 0.6.4",
"signature",
"spki",
"subtle",
@@ -3381,8 +3991,10 @@ version = "0.23.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1"
dependencies = [
+ "aws-lc-rs",
"log",
"once_cell",
+ "ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
@@ -3427,6 +4039,15 @@ dependencies = [
"rustls",
]
+[[package]]
+name = "rustls-pemfile"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
+dependencies = [
+ "rustls-pki-types",
+]
+
[[package]]
name = "rustls-pki-types"
version = "1.10.0"
@@ -3439,17 +4060,154 @@ version = "0.102.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
dependencies = [
+ "aws-lc-rs",
"ring",
"rustls-pki-types",
- "untrusted",
+ "untrusted 0.9.0",
]
+[[package]]
+name = "rustversion"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
+
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+[[package]]
+name = "s2n-codec"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "385bc02730502bb5f4b37927c235163e7790d1ba7a9fd4e7ac595d0ce655d6a4"
+dependencies = [
+ "byteorder",
+ "bytes",
+ "zerocopy",
+]
+
+[[package]]
+name = "s2n-quic"
+version = "1.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a69911cb4b405536c2db69bf3465810db62927d83a3a837026b29e34596c6590"
+dependencies = [
+ "bytes",
+ "cfg-if 1.0.0",
+ "cuckoofilter",
+ "futures",
+ "hash_hasher",
+ "rand 0.8.5",
+ "rand_chacha 0.3.1",
+ "s2n-codec",
+ "s2n-quic-core",
+ "s2n-quic-crypto",
+ "s2n-quic-platform",
+ "s2n-quic-rustls",
+ "s2n-quic-transport",
+ "tokio",
+ "zerocopy",
+ "zeroize",
+]
+
+[[package]]
+name = "s2n-quic-core"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "361a7ca960a4a1a70e2de4c39c419bfaf3a21e37da944ae2bedbb37ebf534a73"
+dependencies = [
+ "atomic-waker",
+ "byteorder",
+ "bytes",
+ "cfg-if 1.0.0",
+ "crossbeam-utils",
+ "hex-literal",
+ "num-rational",
+ "num-traits",
+ "once_cell",
+ "pin-project-lite",
+ "s2n-codec",
+ "subtle",
+ "tracing",
+ "zerocopy",
+]
+
+[[package]]
+name = "s2n-quic-crypto"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bab6c2181a3dc7670dde5015737b0b2300860eb3e347bbcd0e011587bb4f583"
+dependencies = [
+ "aws-lc-rs",
+ "cfg-if 1.0.0",
+ "lazy_static",
+ "s2n-codec",
+ "s2n-quic-core",
+ "zeroize",
+]
+
+[[package]]
+name = "s2n-quic-h3"
+version = "0.1.0"
+source = "git+https://github.com/SergioBenitez/s2n-quic-h3.git?rev=f832471#f83247128132c968d57a99fa5e76ac7f1528ea10"
+dependencies = [
+ "bytes",
+ "futures",
+ "h3",
+ "s2n-quic",
+ "tracing",
+]
+
+[[package]]
+name = "s2n-quic-platform"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fc8a114d2ad26329ae37a0484299be631768afd60bc04723194b653c58e5679"
+dependencies = [
+ "cfg-if 1.0.0",
+ "futures",
+ "lazy_static",
+ "libc",
+ "s2n-quic-core",
+ "socket2",
+ "tokio",
+]
+
+[[package]]
+name = "s2n-quic-rustls"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c513d6071c7264661199d0c7d6811e413a95c5929aa4bb7eda75deda9f8ca29d"
+dependencies = [
+ "bytes",
+ "rustls",
+ "rustls-pemfile",
+ "s2n-codec",
+ "s2n-quic-core",
+ "s2n-quic-crypto",
+]
+
+[[package]]
+name = "s2n-quic-transport"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2cdfa2ccd20371cdff09735b4a3f6bee7a01008b1431854c06e8915df5fdec4"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "hashbrown 0.15.2",
+ "intrusive-collections",
+ "once_cell",
+ "s2n-codec",
+ "s2n-quic-core",
+ "siphasher",
+ "smallvec",
+]
+
[[package]]
name = "same-file"
version = "1.0.6"
@@ -3496,13 +4254,19 @@ dependencies = [
"curve25519-dalek",
"getrandom_or_panic",
"merlin",
- "rand_core",
+ "rand_core 0.6.4",
"serde_bytes",
"sha2 0.10.8",
"subtle",
"zeroize",
]
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -3532,8 +4296,8 @@ dependencies = [
"group",
"honggfuzz",
"p384",
- "rand",
- "rand_core",
+ "rand 0.8.5",
+ "rand_core 0.6.4",
"sha3",
"subtle",
"thiserror",
@@ -3671,6 +4435,15 @@ dependencies = [
"keccak",
]
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
[[package]]
name = "shlex"
version = "1.3.0"
@@ -3693,7 +4466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"digest 0.10.7",
- "rand_core",
+ "rand_core 0.6.4",
]
[[package]]
@@ -3702,6 +4475,12 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
+[[package]]
+name = "siphasher"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
+
[[package]]
name = "slab"
version = "0.4.9"
@@ -3773,7 +4552,7 @@ dependencies = [
"blake2",
"chacha20poly1305",
"curve25519-dalek",
- "rand_core",
+ "rand_core 0.6.4",
"rustc_version",
"sha2 0.10.8",
"subtle",
@@ -3815,12 +4594,30 @@ dependencies = [
"der",
]
+[[package]]
+name = "stable-pattern"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+[[package]]
+name = "state"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8"
+dependencies = [
+ "loom",
+]
+
[[package]]
name = "static_assertions"
version = "1.1.0"
@@ -3848,7 +4645,7 @@ dependencies = [
"byteorder",
"crunchy",
"lazy_static",
- "rand",
+ "rand 0.8.5",
"rustc-hex",
]
@@ -3924,6 +4721,19 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
+[[package]]
+name = "tempfile"
+version = "3.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
+dependencies = [
+ "cfg-if 1.0.0",
+ "fastrand",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.59.0",
+]
+
[[package]]
name = "tendermint"
version = "0.39.1"
@@ -4027,10 +4837,10 @@ dependencies = [
"async-trait",
"bytes",
"flex-error",
- "getrandom",
+ "getrandom 0.2.15",
"peg",
"pin-project",
- "rand",
+ "rand 0.8.5",
"semver",
"serde",
"serde_bytes",
@@ -4145,6 +4955,16 @@ dependencies = [
"syn 2.0.90",
]
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if 1.0.0",
+ "once_cell",
+]
+
[[package]]
name = "time"
version = "0.3.37"
@@ -4214,6 +5034,21 @@ dependencies = [
"serde_json",
]
+[[package]]
+name = "tinyvec"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
[[package]]
name = "tokio"
version = "1.42.0"
@@ -4250,7 +5085,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f"
dependencies = [
"pin-project",
- "rand",
+ "rand 0.8.5",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
"tokio",
]
@@ -4306,14 +5175,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"pin-project-lite",
+ "tracing-attributes",
"tracing-core",
]
+[[package]]
+name = "tracing-attributes"
+version = "0.1.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.90",
+]
+
[[package]]
name = "tracing-core"
version = "0.1.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "parking_lot",
+ "regex",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
[[package]]
name = "trie-root"
@@ -4340,6 +5255,15 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+[[package]]
+name = "ubyte"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "uint"
version = "0.9.5"
@@ -4352,6 +5276,16 @@ dependencies = [
"static_assertions",
]
+[[package]]
+name = "uncased"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697"
+dependencies = [
+ "serde",
+ "version_check",
+]
+
[[package]]
name = "unicode-ident"
version = "1.0.14"
@@ -4386,6 +5320,12 @@ dependencies = [
"subtle",
]
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
[[package]]
name = "untrusted"
version = "0.9.0"
@@ -4448,6 +5388,12 @@ version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
[[package]]
name = "version_check"
version = "0.9.5"
@@ -4502,6 +5448,12 @@ dependencies = [
"syn 1.0.109",
]
+[[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.11.0+wasi-snapshot-preview1"
@@ -4739,6 +5691,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+[[package]]
+name = "windows"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
[[package]]
name = "windows-core"
version = "0.52.0"
@@ -4942,7 +5903,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
dependencies = [
"curve25519-dalek",
- "rand_core",
+ "rand_core 0.6.4",
"serde",
"zeroize",
]
@@ -4986,6 +5947,9 @@ name = "yansi"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
+dependencies = [
+ "is-terminal",
+]
[[package]]
name = "yasna"
diff --git a/Cargo.toml b/Cargo.toml
index fe30e0714a..0196aa4eda 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,6 +17,8 @@ members = [
# ROFL.
"rofl-utils",
+ "rofl-appd",
+ "rofl-containers",
# Test runtimes.
"tests/runtimes/benchmarking",
diff --git a/client-sdk/go/modules/rofl/types.go b/client-sdk/go/modules/rofl/types.go
index 478c34d38f..580fddc5fa 100644
--- a/client-sdk/go/modules/rofl/types.go
+++ b/client-sdk/go/modules/rofl/types.go
@@ -20,6 +20,8 @@ type Create struct {
Policy AppAuthPolicy `json:"policy"`
// Scheme is the identifier generation scheme.
Scheme IdentifierScheme `json:"scheme"`
+ // Metadata are arbitrary key/value pairs.
+ Metadata map[string]string `json:"metadata,omitempty"`
}
// IdentifierScheme is a ROFL application identifier generation scheme.
@@ -38,6 +40,11 @@ type Update struct {
Policy AppAuthPolicy `json:"policy"`
// Admin is the application administrator address.
Admin *types.Address `json:"admin"`
+
+ // Metadata are arbitrary key/value pairs.
+ Metadata map[string]string `json:"metadata,omitempty"`
+ // Secrets are arbitrary encrypted key/value pairs.
+ Secrets map[string][]byte `json:"secrets,omitempty"`
}
// Remove an existing ROFL application call.
@@ -84,6 +91,13 @@ type AppConfig struct {
Admin *types.Address `json:"admin"`
// Stake is the staked amount.
Stake types.BaseUnits `json:"stake"`
+
+ // Metadata are arbitrary key/value pairs.
+ Metadata map[string]string `json:"metadata,omitempty"`
+ // Secrets are arbitrary SEK-encrypted key/value pairs.
+ Secrets map[string][]byte `json:"secrets,omitempty"`
+ // SEK is the secrets encryption (public) key.
+ SEK x25519.PublicKey `json:"sek"`
}
// Registration is a ROFL enclave registration descriptor.
diff --git a/rofl-appd/Cargo.toml b/rofl-appd/Cargo.toml
new file mode 100644
index 0000000000..9a1c8c6d72
--- /dev/null
+++ b/rofl-appd/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "rofl-appd"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+# Oasis SDK.
+oasis-runtime-sdk = { path = "../runtime-sdk", features = ["tdx"] }
+
+# Third party.
+anyhow = "1.0.86"
+# TODO: Replace with a released version once it includes UNIX socket support.
+rocket = { git = "https://github.com/rwf2/Rocket", rev = "28891e8072136f4641a33fb8c3f2aafce9d88d5b", features = ["json"] }
+serde = { version = "1.0", features = ["derive"] }
+sp800-185 = "0.2.0"
+thiserror = "1.0"
+tokio = { version = "1.38", features = ["rt", "rt-multi-thread", "sync", "time", "macros"] }
+tokio-retry = "0.3.0"
+zeroize = "1.7"
+
+[dev-dependencies]
+rustc-hex = "2.0.1"
diff --git a/rofl-appd/src/lib.rs b/rofl-appd/src/lib.rs
new file mode 100644
index 0000000000..9b5bc5037b
--- /dev/null
+++ b/rofl-appd/src/lib.rs
@@ -0,0 +1,39 @@
+//! REST API daemon accessible by ROFL apps.
+
+mod routes;
+pub(crate) mod services;
+pub(crate) mod state;
+
+use std::sync::Arc;
+
+use rocket::{figment::Figment, routes};
+
+use oasis_runtime_sdk::modules::rofl::app::{App, Environment};
+
+/// Start the REST API server.
+pub async fn start(address: &str, env: Environment) -> Result<(), rocket::Error>
+where
+ A: App,
+{
+ // KMS service.
+ let kms_service: Arc =
+ Arc::new(services::kms::OasisKmsService::new(env.clone()));
+ let kms_service_task = kms_service.clone();
+ tokio::spawn(async move { kms_service_task.start().await });
+
+ // Oasis runtime environment.
+ let env: Arc = Arc::new(state::EnvImpl::new(env));
+
+ // Server configuration.
+ let cfg = Figment::new().join(("address", address));
+
+ rocket::custom(cfg)
+ .manage(env)
+ .manage(kms_service)
+ .mount("/rofl/v1/app", routes![routes::app::id,])
+ .mount("/rofl/v1/keys", routes![routes::keys::generate,])
+ .launch()
+ .await?;
+
+ Ok(())
+}
diff --git a/rofl-appd/src/routes/app.rs b/rofl-appd/src/routes/app.rs
new file mode 100644
index 0000000000..8a81fa4c92
--- /dev/null
+++ b/rofl-appd/src/routes/app.rs
@@ -0,0 +1,10 @@
+use std::sync::Arc;
+
+use rocket::State;
+
+use crate::state::Env;
+
+#[rocket::get("/id")]
+pub fn id(env: &State>) -> String {
+ env.app_id().to_bech32()
+}
diff --git a/rofl-appd/src/routes/keys.rs b/rofl-appd/src/routes/keys.rs
new file mode 100644
index 0000000000..51aef8b019
--- /dev/null
+++ b/rofl-appd/src/routes/keys.rs
@@ -0,0 +1,17 @@
+use std::sync::Arc;
+
+use rocket::{http::Status, serde::json::Json, State};
+
+use crate::services::kms::{GenerateRequest, GenerateResponse, KmsService};
+
+/// Key generation endpoint.
+#[rocket::post("/generate", data = "")]
+pub async fn generate(
+ body: Json>,
+ kms: &State>,
+) -> Result, (Status, String)> {
+ kms.generate(&body)
+ .await
+ .map(Json)
+ .map_err(|err| (Status::BadRequest, err.to_string()))
+}
diff --git a/rofl-appd/src/routes/mod.rs b/rofl-appd/src/routes/mod.rs
new file mode 100644
index 0000000000..361c0e8769
--- /dev/null
+++ b/rofl-appd/src/routes/mod.rs
@@ -0,0 +1,2 @@
+pub mod app;
+pub mod keys;
diff --git a/rofl-appd/src/services/kms.rs b/rofl-appd/src/services/kms.rs
new file mode 100644
index 0000000000..fccd9b3d82
--- /dev/null
+++ b/rofl-appd/src/services/kms.rs
@@ -0,0 +1,379 @@
+use std::sync::{
+ atomic::{AtomicBool, Ordering},
+ Arc, Mutex,
+};
+
+use sp800_185::KMac;
+
+use oasis_runtime_sdk::{
+ core::common::logger::get_logger,
+ crypto::signature::{ed25519, secp256k1, Signer},
+ modules::rofl::app::{client::DeriveKeyRequest, prelude::*},
+};
+
+/// A key management service.
+#[async_trait]
+pub trait KmsService: Send + Sync {
+ /// Start the KMS service.
+ async fn start(&self) -> Result<(), Error>;
+
+ /// Generate a key based on the passed parameters.
+ async fn generate(&self, request: &GenerateRequest<'_>) -> Result;
+}
+
+/// Error returned by the key management service.
+#[derive(thiserror::Error, Debug)]
+pub enum Error {
+ #[error("not initialized yet")]
+ NotInitialized,
+
+ #[error("internal error")]
+ Internal,
+
+ #[error(transparent)]
+ Other(#[from] anyhow::Error),
+}
+
+/// Kind of key to generate.
+#[derive(Copy, Clone, Debug, Default, serde::Deserialize)]
+pub enum KeyKind {
+ #[default]
+ #[serde(rename = "raw-256")]
+ Raw256,
+
+ #[serde(rename = "raw-384")]
+ Raw384,
+
+ #[serde(rename = "ed25519")]
+ Ed25519,
+
+ #[serde(rename = "secp256k1")]
+ Secp256k1,
+}
+
+impl KeyKind {
+ /// Kind of the key as a stable u8 encoding.
+ ///
+ /// This is used during key derivation so any changes will change generated keys.
+ pub fn as_stable_u8(&self) -> u8 {
+ match self {
+ Self::Raw256 => 1,
+ Self::Raw384 => 2,
+ Self::Ed25519 => 3,
+ Self::Secp256k1 => 4,
+ }
+ }
+}
+
+/// Key generation request body.
+#[derive(Clone, Debug, Default, serde::Deserialize)]
+pub struct GenerateRequest<'r> {
+ /// Domain separator for deriving different keys inside the application.
+ pub key_id: &'r str,
+ /// Key kind.
+ pub kind: KeyKind,
+}
+
+/// Key generation response.
+#[derive(Clone, Default, serde::Serialize, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
+pub struct GenerateResponse {
+ /// Generated key.
+ pub key: Vec,
+}
+
+/// Key identifier for the root key from which all per-app keys are derived. The root key is
+/// retrieved from the Oasis runtime key manager on initialization and all subsequent keys are
+/// derived from that key.
+///
+/// Changing this identifier will change all generated keys.
+const OASIS_KMS_ROOT_KEY_ID: &[u8] = b"oasis-runtime-sdk/rofl-appd: root key v1";
+
+/// A key management service backed by the Oasis runtime.
+pub struct OasisKmsService {
+ running: AtomicBool,
+ root_key: Arc>>>,
+ env: Environment,
+ logger: slog::Logger,
+}
+
+impl OasisKmsService {
+ pub fn new(env: Environment) -> Self {
+ Self {
+ running: AtomicBool::new(false),
+ root_key: Arc::new(Mutex::new(None)),
+ env,
+ logger: get_logger("appd/services/kms"),
+ }
+ }
+}
+
+#[async_trait]
+impl KmsService for OasisKmsService {
+ async fn start(&self) -> Result<(), Error> {
+ let is_running = self
+ .running
+ .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
+ .is_err();
+ if is_running {
+ return Ok(());
+ }
+
+ slog::info!(self.logger, "starting KMS service");
+
+ // Ensure we keep retrying until the root key is derived.
+ let retry_strategy = tokio_retry::strategy::ExponentialBackoff::from_millis(4)
+ .max_delay(std::time::Duration::from_millis(1000))
+ .map(tokio_retry::strategy::jitter);
+
+ slog::info!(
+ self.logger,
+ "attempting to obtain the root key for our application"
+ );
+
+ // Generate the root key for the application and store it in memory to derive all other
+ // requested keys.
+ let root_key = tokio_retry::Retry::spawn(retry_strategy, || {
+ self.env.client().derive_key(
+ self.env.signer(),
+ DeriveKeyRequest {
+ key_id: OASIS_KMS_ROOT_KEY_ID.to_vec(),
+ ..Default::default()
+ },
+ )
+ })
+ .await?;
+
+ // Store the key in memory.
+ *self.root_key.lock().unwrap() = Some(root_key.key);
+
+ slog::info!(self.logger, "KMS service initialized");
+
+ Ok(())
+ }
+
+ async fn generate(&self, request: &GenerateRequest<'_>) -> Result {
+ let root_key_guard = self.root_key.lock().unwrap();
+ let root_key = root_key_guard.as_ref().ok_or(Error::NotInitialized)?;
+
+ let key = Kdf::derive_key(root_key.as_ref(), request.kind, request.key_id.as_bytes())?;
+
+ Ok(GenerateResponse { key })
+ }
+}
+
+/// Insecure mock root key used to derive keys in the mock KMS.
+const INSECURE_MOCK_ROOT_KEY: &[u8] = b"oasis-runtime-sdk/rofl-appd: mock root key";
+
+/// A mock in-memory key management service.
+pub struct MockKmsService;
+
+#[async_trait]
+impl KmsService for MockKmsService {
+ async fn start(&self) -> Result<(), Error> {
+ Ok(())
+ }
+
+ async fn generate(&self, request: &GenerateRequest<'_>) -> Result {
+ let key = Kdf::derive_key(
+ INSECURE_MOCK_ROOT_KEY,
+ request.kind,
+ request.key_id.as_bytes(),
+ )?;
+
+ Ok(GenerateResponse { key })
+ }
+}
+
+/// Domain separation tag for deriving a key-derivation key from a shared secret.
+const DERIVE_KDK_CUSTOM: &[u8] = b"oasis-runtime-sdk/rofl-appd: derive key derivation key";
+/// Domain separation tag for deriving a raw-256 subkey from a key-derivation key.
+const DERIVE_SUBKEY_RAW256_CUSTOM: &[u8] = b"oasis-runtime-sdk/rofl-appd: derive subkey raw-256";
+/// Domain separation tag for deriving a raw-384 subkey from a key-derivation key.
+const DERIVE_SUBKEY_RAW384_CUSTOM: &[u8] = b"oasis-runtime-sdk/rofl-appd: derive subkey raw-384";
+/// Domain separation tag for deriving a ed25519 subkey from a key-derivation key.
+const DERIVE_SUBKEY_ED25519_CUSTOM: &[u8] = b"oasis-runtime-sdk/rofl-appd: derive subkey ed25519";
+/// Domain separation tag for deriving a secp256k1 subkey from a key-derivation key.
+const DERIVE_SUBKEY_SECP256K1_CUSTOM: &[u8] =
+ b"oasis-runtime-sdk/rofl-appd: derive subkey secp256k1";
+
+/// Key derivation function which derives keys from a root secret.
+struct Kdf;
+
+impl Kdf {
+ /// Derive a key of the given kind from a root key and key identifier.
+ fn derive_key(root_key: &[u8], kind: KeyKind, key_id: &[u8]) -> Result, Error> {
+ let key_id = Self::generate_key_id(kind, key_id);
+
+ // Generate a 256-bit key-derivation key.
+ let mut entropy = vec![0; 32];
+ Kdf::extract_randomness(root_key, &key_id, DERIVE_KDK_CUSTOM, &mut entropy);
+
+ // Generate requested subkey.
+ let key = match kind {
+ KeyKind::Raw256 => {
+ let mut key = vec![0; 32];
+ Kdf::expand_key(&entropy, &key_id, DERIVE_SUBKEY_RAW256_CUSTOM, &mut key);
+
+ key
+ }
+ KeyKind::Raw384 => {
+ let mut key = vec![0; 48];
+ Kdf::expand_key(&entropy, &key_id, DERIVE_SUBKEY_RAW384_CUSTOM, &mut key);
+
+ key
+ }
+ KeyKind::Ed25519 => {
+ let mut key = vec![0; 32];
+ Kdf::expand_key(&entropy, &key_id, DERIVE_SUBKEY_ED25519_CUSTOM, &mut key);
+
+ ed25519::MemorySigner::new_from_seed(&key)
+ .map_err(|_| Error::Internal)?
+ .to_bytes()
+ }
+ KeyKind::Secp256k1 => {
+ let mut key = vec![0; 32];
+ Kdf::expand_key(&entropy, &key_id, DERIVE_SUBKEY_SECP256K1_CUSTOM, &mut key);
+
+ secp256k1::MemorySigner::new_from_seed(&key)
+ .map_err(|_| Error::Internal)?
+ .to_bytes()
+ }
+ };
+
+ Ok(key)
+ }
+
+ /// Generate the key identifier as:
+ ///
+ /// ```text
+ /// key_id = u8(request.kind) || request.key_id
+ /// ```
+ fn generate_key_id(kind: KeyKind, key_id: &[u8]) -> Vec {
+ [&[kind.as_stable_u8()], key_id].concat()
+ }
+
+ /// Derives secret keying material from a shared secret established during
+ /// a key-establishment scheme using KMAC256 as the key-derivation method.
+ ///
+ /// ```text
+ /// keying_material = KMAC256(salt, secret, length, custom)
+ /// ```
+ ///
+ /// The output produced by this method shall only be used as secret keying
+ /// material – such as a symmetric key used for data encryption or message
+ /// integrity, a secret initialization vector, or, perhaps, a key-derivation
+ /// key that will be used to generate additional keying material.
+ ///
+ /// For more details, see: NIST SP 800-56Cr2.
+ fn extract_randomness(secret: &[u8], salt: &[u8], custom: &[u8], buf: &mut [u8]) {
+ let mut kmac = KMac::new_kmac256(salt, custom);
+ kmac.update(secret);
+ kmac.finalize(buf);
+ }
+
+ /// Derives secret keying material from a key-derivation key using KMAC256
+ /// as the pseudo-random function.
+ ///
+ /// ```text
+ /// keying_material = KMAC256(key, salt, length, custom)
+ /// ```
+ /// The derived keying material may subsequently be segmented into multiple
+ /// disjoint (i.e., non-overlapping) keys.
+ ///
+ /// For more details, see: NIST SP 800-108r1-upd1.
+ fn expand_key(key: &[u8], salt: &[u8], custom: &[u8], buf: &mut [u8]) {
+ let mut kmac = KMac::new_kmac256(key, custom);
+ kmac.update(salt);
+ kmac.finalize(buf);
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use rustc_hex::ToHex;
+
+ use super::*;
+
+ const KEY: &[u8] = b"key";
+ const SALT: &[u8] = b"salt";
+ const CUSTOM: &[u8] = b"custom";
+ const SECRET: &[u8] = b"secret";
+ const KEY_ID: &[u8] = b"key id";
+
+ #[test]
+ fn test_derive_key_consistency() {
+ // Different kinds.
+ let tcs = [
+ (KeyKind::Raw256, "1cba05338c243901f5784a57746ad972e4aa609ab632b3431d85cb8c2f1d72b3"),
+ (KeyKind::Raw384, "89b9c608809516f749489e2c78d98e4cddda04c7a5695dbf212036aeb80ed2c193f6dccd74745dc2fc0e1799073ce9cf"),
+ (KeyKind::Ed25519, "f5d58be0f6df3b91e8c1e918d71c0cf717a4efc35790c9534b58f8c3b2b6163f"),
+ (KeyKind::Secp256k1, "97d87b946892a8cfbc2773f13f89a98ef1db9fa15ef799ee9e9841a6f7f28842"),
+ ];
+ for tc in tcs {
+ let key = Kdf::derive_key(KEY, tc.0, KEY_ID).unwrap();
+ assert_eq!(key.to_hex::(), tc.1,);
+ }
+
+ // Different root keys.
+ let tcs = [
+ (
+ KEY,
+ "1cba05338c243901f5784a57746ad972e4aa609ab632b3431d85cb8c2f1d72b3",
+ ),
+ (
+ b"root key 2",
+ "1b381b7f55e71d60807b799b8bca726e9a00a13f7c49e0496b84458c28273741",
+ ),
+ (
+ b"root key 3",
+ "a9344d82255c7f29f83030d49db678021381197ae0c252e8d564cd903c9c66f4",
+ ),
+ ];
+ for tc in tcs {
+ let key = Kdf::derive_key(tc.0, KeyKind::Raw256, KEY_ID).unwrap();
+ assert_eq!(key.to_hex::(), tc.1,);
+ }
+
+ // Different key IDs.
+ let tcs = [
+ (
+ KEY_ID,
+ "1cba05338c243901f5784a57746ad972e4aa609ab632b3431d85cb8c2f1d72b3",
+ ),
+ (
+ b"key id 2",
+ "7c042ebcd9f154191e8be9e75b32f81a9dfab48bf2b776db63ae1f8d7304eef6",
+ ),
+ (
+ b"key id 3",
+ "59939554aa7768e01eb786903856803ab48b0c582d66a97e2a33ca369bbf8c6f",
+ ),
+ ];
+ for tc in tcs {
+ let key = Kdf::derive_key(KEY, KeyKind::Raw256, tc.0).unwrap();
+ assert_eq!(key.to_hex::(), tc.1,);
+ }
+ }
+
+ #[test]
+ fn test_extract_randomness_consistency() {
+ let mut buf = [0u8; 32];
+ Kdf::extract_randomness(SECRET, SALT, CUSTOM, &mut buf);
+
+ assert_eq!(
+ buf.to_hex::(),
+ "33a1d8b06e8c08c762caf2abc6a6ccdaad9b1bab4f89bead69de66d3e165612c"
+ );
+ }
+
+ #[test]
+ fn test_expand_key_consistency() {
+ let mut buf = [0u8; 32];
+ Kdf::expand_key(KEY, SALT, CUSTOM, &mut buf);
+
+ assert_eq!(
+ buf.to_hex::(),
+ "c80574bfd7c5a3f5234c2cf7b72ac457204ee6cf9f75c1e15ce3a6d992a11d29"
+ );
+ }
+}
diff --git a/rofl-appd/src/services/mod.rs b/rofl-appd/src/services/mod.rs
new file mode 100644
index 0000000000..5550fd59a3
--- /dev/null
+++ b/rofl-appd/src/services/mod.rs
@@ -0,0 +1 @@
+pub mod kms;
diff --git a/rofl-appd/src/state.rs b/rofl-appd/src/state.rs
new file mode 100644
index 0000000000..3123cde897
--- /dev/null
+++ b/rofl-appd/src/state.rs
@@ -0,0 +1,24 @@
+use oasis_runtime_sdk::modules::rofl::app::prelude::*;
+
+/// ROFL app environment.
+#[async_trait]
+pub trait Env: Send + Sync {
+ /// ROFL app identifier of the running application.
+ fn app_id(&self) -> AppId;
+}
+
+pub(crate) struct EnvImpl {
+ _env: Environment,
+}
+
+impl EnvImpl {
+ pub fn new(env: Environment) -> Self {
+ Self { _env: env }
+ }
+}
+
+impl Env for EnvImpl {
+ fn app_id(&self) -> AppId {
+ A::id()
+ }
+}
diff --git a/rofl-containers/Cargo.toml b/rofl-containers/Cargo.toml
new file mode 100644
index 0000000000..51e2250ca3
--- /dev/null
+++ b/rofl-containers/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "rofl-containers"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+# Oasis SDK.
+oasis-runtime-sdk = { path = "../runtime-sdk", features = ["tdx"] }
+rofl-appd = { path = "../rofl-appd" }
+
+# Third party.
+base64 = "0.22.1"
diff --git a/rofl-containers/src/main.rs b/rofl-containers/src/main.rs
new file mode 100644
index 0000000000..5f65213d4a
--- /dev/null
+++ b/rofl-containers/src/main.rs
@@ -0,0 +1,53 @@
+//! The rofl-containers runtime is a generic ROFL app that is used when building all TDX
+//! container-based ROFL apps (e.g. using the Oasis CLI).
+//!
+//! It expects `ROFL_APP_ID` and `ROFL_CONSENSUS_TRUST_ROOT` to be passed via environment variables.
+//! Usually these would be set in the kernel command-line so that they are part of the runtime
+//! measurements.
+//!
+//! It currently just starts a REST API server (rofl-appd) that exposes information about the
+//! application together with a simple KMS interface. In the future it will also manage secrets and
+//! expose other interfaces.
+use std::env;
+
+use base64::prelude::*;
+use oasis_runtime_sdk::{cbor, modules::rofl::app::prelude::*};
+
+/// UNIX socket address where the REST API server will listen on.
+const ROFL_APPD_ADDRESS: &str = "unix:/run/rofl-appd.sock";
+
+struct ContainersApp;
+
+#[async_trait]
+impl App for ContainersApp {
+ const VERSION: Version = sdk::version_from_cargo!();
+
+ fn id() -> AppId {
+ // Fetch application ID from the ROFL_APP_ID environment variable.
+ // This would usually be passed via the kernel cmdline.
+ AppId::from_bech32(&env::var("ROFL_APP_ID").expect("Must configure ROFL_APP_ID."))
+ .expect("Corrupted ROFL_APP_ID (must be Bech32-encoded ROFL app ID).")
+ }
+
+ fn consensus_trust_root() -> Option {
+ // Fetch consensus trust root from the ROFL_CONSENSUS_TRUST_ROOT environment variable.
+ // This would usually be passed via the kernel cmdline.
+ let raw_trust_root = env::var("ROFL_CONSENSUS_TRUST_ROOT")
+ .expect("Must configure ROFL_CONSENSUS_TRUST_ROOT.");
+ cbor::from_slice(
+ &BASE64_STANDARD
+ .decode(raw_trust_root)
+ .expect("Corrupted ROFL_CONSENSUS_TRUST_ROOT (must be Base64-encoded CBOR)."),
+ )
+ .expect("Corrupted ROFL_CONSENSUS_TRUST_ROOT (must be Base64-encoded CBOR).")
+ }
+
+ async fn run(self: Arc, env: Environment) {
+ // Start the REST API server.
+ let _ = rofl_appd::start(ROFL_APPD_ADDRESS, env).await;
+ }
+}
+
+fn main() {
+ ContainersApp.start();
+}
diff --git a/runtime-sdk/src/modules/rofl/app/client.rs b/runtime-sdk/src/modules/rofl/app/client.rs
index 8517ed0115..b6f24f8a5d 100644
--- a/runtime-sdk/src/modules/rofl/app/client.rs
+++ b/runtime-sdk/src/modules/rofl/app/client.rs
@@ -8,10 +8,12 @@ use std::{
};
use anyhow::{anyhow, Result};
+use rand::{rngs::OsRng, Rng};
use tokio::sync::{mpsc, oneshot};
use crate::{
core::{
+ common::crypto::mrae::deoxysii,
consensus::{
registry::{SGXConstraints, TEEHardware},
state::{
@@ -23,12 +25,16 @@ use crate::{
},
crypto::signature::{PublicKey, Signer},
enclave_rpc::{QueryRequest, METHOD_QUERY},
- modules::{accounts::types::NonceQuery, core::types::EstimateGasQuery},
+ modules::{
+ self,
+ accounts::types::NonceQuery,
+ core::types::{CallDataPublicKeyQueryResponse, EstimateGasQuery},
+ },
state::CurrentState,
storage::HostStore,
types::{
address::{Address, SignatureAddressSpec},
- token,
+ callformat, token,
transaction::{self, CallerAddress},
},
};
@@ -47,18 +53,33 @@ pub struct SubmitTxOpts {
/// Optional timeout when submitting a transaction. Setting this to `None` means that the host
/// node timeout will be used.
pub timeout: Option,
+ /// Whether the call data should be encrypted (true by default).
+ pub encrypt: bool,
}
impl Default for SubmitTxOpts {
fn default() -> Self {
Self {
timeout: Some(Duration::from_millis(15_000)), // 15 seconds.
+ encrypt: true,
}
}
}
+/// App-specific key derivation request.
+#[derive(Clone, Debug, Default)]
+pub struct DeriveKeyRequest {
+ /// Key kind.
+ pub kind: modules::rofl::types::KeyKind,
+ /// Key generation.
+ pub generation: u64,
+ /// Key identifier.
+ pub key_id: Vec,
+}
+
/// A runtime client meant for use within runtimes.
pub struct Client {
+ state: Arc>,
imp: ClientImpl,
submission_mgr: Arc>,
}
@@ -72,11 +93,12 @@ where
state: Arc>,
cmdq: mpsc::WeakSender,
) -> Self {
- let imp = ClientImpl::new(state, cmdq);
+ let imp = ClientImpl::new(state.clone(), cmdq);
let mut submission_mgr = SubmissionManager::new(imp.clone());
submission_mgr.start();
Self {
+ state,
imp,
submission_mgr: Arc::new(submission_mgr),
}
@@ -111,6 +133,11 @@ where
self.imp.estimate_gas(req).await
}
+ /// Retrieves application configuration.
+ pub async fn app_cfg(&self) -> Result {
+ self.imp.app_cfg().await
+ }
+
/// Sign a given transaction, submit it and wait for block inclusion.
///
/// This method supports multiple transaction signers.
@@ -159,6 +186,25 @@ where
pub async fn store_for_round(&self, round: u64) -> Result {
self.imp.store_for_round(round).await
}
+
+ /// Derive an application-specific key.
+ pub async fn derive_key(
+ &self,
+ signer: Arc,
+ request: DeriveKeyRequest,
+ ) -> Result {
+ let tx = self.state.app.new_transaction(
+ "rofl.DeriveKey",
+ modules::rofl::types::DeriveKey {
+ app: A::id(),
+ kind: request.kind,
+ generation: request.generation,
+ key_id: request.key_id,
+ },
+ );
+ let response = self.sign_and_submit_tx(signer, tx).await?;
+ Ok(cbor::from_value(response.ok()?)?)
+ }
}
impl Clone for Client
@@ -167,6 +213,7 @@ where
{
fn clone(&self) -> Self {
Self {
+ state: self.state.clone(),
imp: self.imp.clone(),
submission_mgr: self.submission_mgr.clone(),
}
@@ -234,6 +281,12 @@ where
.copied()
}
+ /// Retrieve the calldata encryption public key.
+ async fn call_data_public_key(&self) -> Result {
+ let round = self.latest_round().await?;
+ self.query(round, "core.CallDataPublicKey", ()).await
+ }
+
/// Securely query the on-chain runtime component.
async fn query(&self, round: u64, method: &str, args: Rq) -> Result
where
@@ -289,6 +342,17 @@ where
self.query(round, "core.EstimateGas", req).await
}
+ /// Retrieves application configuration.
+ async fn app_cfg(&self) -> Result {
+ let round = self.latest_round().await?;
+ self.query(
+ round,
+ "rofl.App",
+ modules::rofl::types::AppQuery { id: A::id() },
+ )
+ .await
+ }
+
/// Run a closure inside a `CurrentState` context with store for the given round.
async fn with_store_for_round(&self, round: u64, f: F) -> Result
where
@@ -500,11 +564,65 @@ where
// The estimate may be off due to current limitations in confidential gas estimation.
// Inflate the estimated gas by 20%.
- let gas = gas.saturating_add(gas.saturating_mul(20).saturating_div(100));
+ let mut gas = gas.saturating_add(gas.saturating_mul(20).saturating_div(100));
+
+ // When encrypting transactions, also add the cost of calldata encryption.
+ if opts.encrypt {
+ let envelope_size_estimate = cbor::to_vec(callformat::CallEnvelopeX25519DeoxysII {
+ epoch: u64::MAX,
+ ..Default::default()
+ })
+ .len()
+ .try_into()
+ .unwrap();
+
+ let params: modules::core::Parameters =
+ client.query(round, "core.Parameters", ()).await?;
+ gas = gas.saturating_add(params.gas_costs.callformat_x25519_deoxysii);
+ gas = gas.saturating_add(
+ params
+ .gas_costs
+ .tx_byte
+ .saturating_mul(envelope_size_estimate),
+ );
+ }
tx.set_fee_gas(gas);
}
+ // Optionally perform calldata encryption.
+ let meta = if opts.encrypt {
+ // Obtain runtime's current ephemeral public key.
+ let runtime_pk = client.call_data_public_key().await?;
+ // Generate local key pair and nonce.
+ let client_kp = deoxysii::generate_key_pair();
+ let mut nonce = [0u8; deoxysii::NONCE_SIZE];
+ OsRng.fill(&mut nonce);
+ // Encrypt and encode call.
+ let call = transaction::Call {
+ format: transaction::CallFormat::EncryptedX25519DeoxysII,
+ method: "".to_string(),
+ body: cbor::to_value(callformat::CallEnvelopeX25519DeoxysII {
+ pk: client_kp.0.into(),
+ nonce,
+ epoch: runtime_pk.epoch,
+ data: deoxysii::box_seal(
+ &nonce,
+ cbor::to_vec(std::mem::take(&mut tx.call)),
+ vec![],
+ &runtime_pk.public_key.key.0,
+ &client_kp.1,
+ )?,
+ }),
+ ..Default::default()
+ };
+ tx.call = call;
+
+ Some((runtime_pk, client_kp))
+ } else {
+ None
+ };
+
// Determine gas price. Currently we always use the native denomination.
let mgp = client
.gas_price(round, &token::Denomination::NATIVE)
@@ -539,6 +657,26 @@ where
.latest_round
.fetch_max(result.round, Ordering::SeqCst);
- cbor::from_slice(&result.output).map_err(|_| anyhow!("malformed result"))
+ // Decrypt result if it is encrypted.
+ let result: transaction::CallResult =
+ cbor::from_slice(&result.output).map_err(|_| anyhow!("malformed result"))?;
+ match result {
+ transaction::CallResult::Unknown(raw) => {
+ let meta = meta.ok_or(anyhow!("unknown result but calldata was not encrypted"))?;
+ let envelope: callformat::ResultEnvelopeX25519DeoxysII =
+ cbor::from_value(raw).map_err(|_| anyhow!("malformed encrypted result"))?;
+ let data = deoxysii::box_open(
+ &envelope.nonce,
+ envelope.data,
+ vec![],
+ &meta.0.public_key.key.0,
+ &meta.1 .1,
+ )
+ .map_err(|_| anyhow!("malformed encrypted result"))?;
+
+ cbor::from_slice(&data).map_err(|_| anyhow!("malformed encrypted result"))
+ }
+ _ => Ok(result),
+ }
}
}
diff --git a/runtime-sdk/src/modules/rofl/app/env.rs b/runtime-sdk/src/modules/rofl/app/env.rs
index 7ab6e5267c..c58ae5686f 100644
--- a/runtime-sdk/src/modules/rofl/app/env.rs
+++ b/runtime-sdk/src/modules/rofl/app/env.rs
@@ -9,6 +9,7 @@ use super::{client, processor, App};
/// Application environment.
pub struct Environment {
+ app: Arc,
client: client::Client,
signer: Arc,
cmdq: mpsc::WeakSender,
@@ -24,12 +25,18 @@ where
cmdq: mpsc::WeakSender,
) -> Self {
Self {
+ app: state.app.clone(),
signer: state.signer.clone(),
client: client::Client::new(state, cmdq.clone()),
cmdq,
}
}
+ /// Application instance.
+ pub fn app(&self) -> Arc {
+ self.app.clone()
+ }
+
/// Runtime client.
pub fn client(&self) -> &client::Client {
&self.client
@@ -57,6 +64,7 @@ where
{
fn clone(&self) -> Self {
Self {
+ app: self.app.clone(),
signer: self.signer.clone(),
client: self.client.clone(),
cmdq: self.cmdq.clone(),
diff --git a/runtime-sdk/src/modules/rofl/app/mod.rs b/runtime-sdk/src/modules/rofl/app/mod.rs
index c77f0189b3..b0bf320681 100644
--- a/runtime-sdk/src/modules/rofl/app/mod.rs
+++ b/runtime-sdk/src/modules/rofl/app/mod.rs
@@ -3,6 +3,7 @@ use std::sync::Arc;
use anyhow::Result;
use async_trait::async_trait;
+use base64::prelude::*;
use tokio::sync::mpsc;
use crate::{
@@ -18,7 +19,7 @@ use crate::{
types::transaction,
};
-mod client;
+pub mod client;
mod env;
mod init;
mod notifier;
@@ -38,11 +39,29 @@ pub trait App: Send + Sync + 'static {
const VERSION: version::Version;
/// Identifier of the application (used for registrations).
- fn id() -> AppId;
+ fn id() -> AppId {
+ // By default we fetch the application identifier from the build-time environment.
+ #[allow(clippy::option_env_unwrap)]
+ AppId::from_bech32(
+ option_env!("ROFL_APP_ID").expect("Override App::id or specify ROFL_APP_ID."),
+ )
+ .expect("Corrupted ROFL_APP_ID (must be Bech32-encoded ROFL app ID).")
+ }
/// Return the consensus layer trust root for this runtime; if `None`, consensus layer integrity
/// verification will not be performed.
- fn consensus_trust_root() -> Option;
+ fn consensus_trust_root() -> Option {
+ // By default we fetch the trust root from the build-time environment.
+ option_env!("ROFL_CONSENSUS_TRUST_ROOT").map(|raw_trust_root| {
+ // Parse from base64-encoded CBOR.
+ cbor::from_slice(
+ &BASE64_STANDARD
+ .decode(raw_trust_root)
+ .expect("Corrupted ROFL_CONSENSUS_TRUST_ROOT (must be Base64-encoded CBOR)."),
+ )
+ .expect("Corrupted ROFL_CONSENSUS_TRUST_ROOT (must be Base64-encoded CBOR).")
+ })
+ }
/// Create a new unsigned transaction.
fn new_transaction(&self, method: &str, body: B) -> transaction::Transaction
diff --git a/runtime-sdk/src/modules/rofl/app/prelude.rs b/runtime-sdk/src/modules/rofl/app/prelude.rs
index b649864dc4..3465e42509 100644
--- a/runtime-sdk/src/modules/rofl/app/prelude.rs
+++ b/runtime-sdk/src/modules/rofl/app/prelude.rs
@@ -3,6 +3,7 @@ pub use std::sync::Arc;
pub use anyhow::Result;
pub use async_trait::async_trait;
+pub use slog;
pub use crate::{self as sdk, core::consensus::verifier::TrustRoot, Version};
diff --git a/runtime-sdk/src/modules/rofl/app/processor.rs b/runtime-sdk/src/modules/rofl/app/processor.rs
index 9cdb6771ba..455acf9df2 100644
--- a/runtime-sdk/src/modules/rofl/app/processor.rs
+++ b/runtime-sdk/src/modules/rofl/app/processor.rs
@@ -176,13 +176,17 @@ where
async fn cmd_initial_registration_completed(&self) -> Result<()> {
slog::info!(self.logger, "initial registration completed");
- // Perform post-registration initialization.
- init::post_registration_init();
-
// Start application after first registration.
slog::info!(self.logger, "starting application");
tokio::spawn(self.state.app.clone().run(self.env.clone()));
+ // Perform post-registration initialization.
+ slog::info!(
+ self.logger,
+ "performing additional post-registration initialization"
+ );
+ init::post_registration_init();
+
// Notify notifier task.
self.tasks
.notifier
diff --git a/runtime-sdk/src/modules/rofl/app/registration.rs b/runtime-sdk/src/modules/rofl/app/registration.rs
index 64e78ae444..973dbf1ce7 100644
--- a/runtime-sdk/src/modules/rofl/app/registration.rs
+++ b/runtime-sdk/src/modules/rofl/app/registration.rs
@@ -13,7 +13,7 @@ use crate::{
modules::rofl::types::Register,
};
-use super::{processor, App, Environment};
+use super::{client::SubmitTxOpts, processor, App, Environment};
/// Registration task.
pub(super) struct Task {
@@ -124,7 +124,14 @@ where
let result = self
.env
.client()
- .multi_sign_and_submit_tx(&[self.state.identity.clone(), self.env.signer()], tx)
+ .multi_sign_and_submit_tx_opts(
+ &[self.state.identity.clone(), self.env.signer()],
+ tx,
+ SubmitTxOpts {
+ encrypt: false, // Needed for initial fee payments.
+ ..Default::default()
+ },
+ )
.await?
.ok()?;
diff --git a/runtime-sdk/src/modules/rofl/config.rs b/runtime-sdk/src/modules/rofl/config.rs
index 407b50fffc..6b3d9e674e 100644
--- a/runtime-sdk/src/modules/rofl/config.rs
+++ b/runtime-sdk/src/modules/rofl/config.rs
@@ -18,6 +18,8 @@ pub trait Config: 'static {
const GAS_COST_CALL_AUTHORIZED_ORIGIN_ENTITY: u64 = 2000;
/// Gas cost of rofl.StakeThresholds call.
const GAS_COST_CALL_STAKE_THRESHOLDS: u64 = 10;
+ /// Gas cost of rofl.DeriveKey call.
+ const GAS_COST_CALL_DERIVE_KEY: u64 = 10_000;
/// Amount of stake required for maintaining an application.
///
@@ -25,4 +27,7 @@ pub trait Config: 'static {
/// removed.
const STAKE_APP_CREATE: token::BaseUnits =
token::BaseUnits::new(0, token::Denomination::NATIVE);
+
+ /// Maximum key identifier length for rofl.DeriveKey call.
+ const DERIVE_KEY_MAX_KEY_ID_LENGTH: usize = 128;
}
diff --git a/runtime-sdk/src/modules/rofl/error.rs b/runtime-sdk/src/modules/rofl/error.rs
index d27d3eb69a..7ef6574da2 100644
--- a/runtime-sdk/src/modules/rofl/error.rs
+++ b/runtime-sdk/src/modules/rofl/error.rs
@@ -1,4 +1,4 @@
-use crate::modules;
+use crate::{dispatcher, modules};
use super::MODULE_NAME;
@@ -53,6 +53,10 @@ pub enum Error {
#[sdk_error(code = 12)]
UnknownInstance,
+ #[error("must use non-plain call format")]
+ #[sdk_error(code = 13)]
+ PlainCallFormatNotAllowed,
+
#[error("core: {0}")]
#[sdk_error(transparent)]
Core(#[from] modules::core::Error),
@@ -60,4 +64,8 @@ pub enum Error {
#[error("accounts: {0}")]
#[sdk_error(transparent)]
Accounts(#[from] modules::accounts::Error),
+
+ #[error("{0}")]
+ #[sdk_error(transparent, abort)]
+ Abort(#[source] dispatcher::Error),
}
diff --git a/runtime-sdk/src/modules/rofl/mod.rs b/runtime-sdk/src/modules/rofl/mod.rs
index 7410044540..ea8e2d806a 100644
--- a/runtime-sdk/src/modules/rofl/mod.rs
+++ b/runtime-sdk/src/modules/rofl/mod.rs
@@ -13,7 +13,7 @@ use crate::{
},
},
crypto::signature::PublicKey,
- handler, migration,
+ dispatcher, handler, keymanager, migration,
module::{self, Module as _, Parameters as _},
modules::{self, accounts::API as _, core::API as _},
sdk_derive,
@@ -107,6 +107,11 @@ pub trait API {
pub static ADDRESS_APP_STAKE_POOL: Lazy =
Lazy::new(|| Address::from_module(MODULE_NAME, "app-stake-pool"));
+/// Key derivation context.
+pub static ROFL_DERIVE_KEY_CONTEXT: &[u8] = b"oasis-runtime-sdk/rofl: derive key v1";
+/// Secrets encryption key identifier.
+pub static ROFL_KEY_ID_SEK: &[u8] = b"oasis-runtime-sdk/rofl: secrets encryption key v1";
+
pub struct Module {
_cfg: std::marker::PhantomData,
}
@@ -211,12 +216,20 @@ impl Module {
&Cfg::STAKE_APP_CREATE,
)?;
+ // Generate the secret encryption (public) key.
+ let sek = Self::derive_app_key(ctx, &app_id, types::KeyKind::X25519, ROFL_KEY_ID_SEK)?
+ .input_keypair
+ .pk;
+
// Register the application.
let cfg = types::AppConfig {
id: app_id,
policy: body.policy,
admin: Some(creator),
stake: Cfg::STAKE_APP_CREATE,
+ metadata: body.metadata,
+ sek,
+ ..Default::default()
};
state::set_app(cfg);
@@ -248,13 +261,17 @@ impl Module {
return Ok(());
}
- // Return early if nothing has actually changed.
- if cfg.policy == body.policy && cfg.admin == body.admin {
- return Ok(());
+ // If there is no SEK defined, regenerate it.
+ if cfg.sek == Default::default() {
+ cfg.sek = Self::derive_app_key(ctx, &body.id, types::KeyKind::X25519, ROFL_KEY_ID_SEK)?
+ .input_keypair
+ .pk;
}
cfg.policy = body.policy;
cfg.admin = body.admin;
+ cfg.metadata = body.metadata;
+ cfg.secrets = body.secrets;
state::set_app(cfg);
CurrentState::with(|state| state.emit_event(Event::AppUpdated { id: body.id }));
@@ -361,6 +378,71 @@ impl Module {
Ok(())
}
+ /// Derive a ROFL application-specific key.
+ #[handler(call = "rofl.DeriveKey")]
+ fn tx_derive_key(
+ ctx: &C,
+ body: types::DeriveKey,
+ ) -> Result {
+ ::Core::use_tx_gas(Cfg::GAS_COST_CALL_DERIVE_KEY)?;
+
+ // Ensure call is encrypted to avoid leaking any keys by accident.
+ let call_format = CurrentState::with_env(|env| env.tx_call_format());
+ if !call_format.is_encrypted() {
+ return Err(Error::PlainCallFormatNotAllowed);
+ }
+
+ // Currently only generation zero keys are supported.
+ if body.generation != 0 {
+ return Err(Error::InvalidArgument);
+ }
+
+ // Ensure key identifier is not too long.
+ if body.key_id.len() > Cfg::DERIVE_KEY_MAX_KEY_ID_LENGTH {
+ return Err(Error::InvalidArgument);
+ }
+
+ if CurrentState::with_env(|env| env.is_check_only()) {
+ return Ok(Default::default());
+ }
+
+ // Ensure caller is an authorized instance of the given application.
+ if !Self::is_authorized_origin(body.app) {
+ return Err(Error::Forbidden);
+ }
+
+ // Derive application key.
+ let key = Self::derive_app_key(ctx, &body.app, body.kind, &body.key_id)?;
+ let key = match body.kind {
+ types::KeyKind::EntropyV0 => key.state_key.0.into(),
+ types::KeyKind::X25519 => key.input_keypair.sk.as_ref().into(),
+ };
+
+ Ok(types::DeriveKeyResponse { key })
+ }
+
+ fn derive_app_key(
+ ctx: &C,
+ app: &app_id::AppId,
+ kind: types::KeyKind,
+ key_id: &[u8],
+ ) -> Result {
+ let key_id = keymanager::get_key_pair_id([
+ ROFL_DERIVE_KEY_CONTEXT,
+ app.as_ref(),
+ &[kind as u8],
+ key_id,
+ ]);
+
+ let km = ctx
+ .key_manager()
+ .ok_or(Error::Abort(dispatcher::Error::KeyManagerFailure(
+ keymanager::KeyManagerError::NotInitialized,
+ )))?;
+ km.get_or_create_keys(key_id)
+ .map_err(|err| Error::Abort(dispatcher::Error::KeyManagerFailure(err)))
+ }
+
/// Verify whether the given endorsement is allowed by the application policy.
///
/// Returns an optional endorsing node descriptor when available.
diff --git a/runtime-sdk/src/modules/rofl/state.rs b/runtime-sdk/src/modules/rofl/state.rs
index 31a440e03d..4c37abbd99 100644
--- a/runtime-sdk/src/modules/rofl/state.rs
+++ b/runtime-sdk/src/modules/rofl/state.rs
@@ -269,9 +269,8 @@ mod test {
let cfg = types::AppConfig {
id: app_id,
- policy: Default::default(),
admin: Some(keys::alice::address()),
- stake: Default::default(),
+ ..Default::default()
};
set_app(cfg.clone());
let app = get_app(app_id).expect("application config should be created");
diff --git a/runtime-sdk/src/modules/rofl/test.rs b/runtime-sdk/src/modules/rofl/test.rs
index 67039bcf83..79373f41fa 100644
--- a/runtime-sdk/src/modules/rofl/test.rs
+++ b/runtime-sdk/src/modules/rofl/test.rs
@@ -6,7 +6,10 @@ use crate::{
accounts::{self, API as _},
core,
},
- testing::{keys, mock},
+ testing::{
+ keys,
+ mock::{self, CallOptions},
+ },
types::{
address::Address,
token::{BaseUnits, Denomination},
@@ -14,7 +17,7 @@ use crate::{
Runtime, Version,
};
-use super::{app_id::AppId, types, Genesis, Module, ADDRESS_APP_STAKE_POOL, API as _};
+use super::{app_id::AppId, state, types, Genesis, Module, ADDRESS_APP_STAKE_POOL, API as _};
type Accounts = accounts::Module;
type Core = core::Module;
@@ -72,13 +75,14 @@ fn test_app_stake_pool_address() {
#[test]
fn test_management_ops() {
let mut mock = mock::Mock::default();
- let ctx = mock.create_ctx_for_runtime::(false);
+ let ctx = mock.create_ctx_for_runtime::(true);
TestRuntime::migrate(&ctx);
let create = types::Create {
policy: Default::default(),
scheme: Default::default(),
+ metadata: BTreeMap::from([("foo".into(), "bar".into())]),
};
// Bob attempts to create a new ROFL application, but he doesn't have enough to stake.
@@ -122,7 +126,7 @@ fn test_management_ops() {
// Simulate round advancing as application ID is generated from it.
mock.runtime_header.round += 1;
- let ctx = mock.create_ctx_for_runtime::(false);
+ let ctx = mock.create_ctx_for_runtime::(true);
// Creating another application should get a different application ID.
let dispatch_result = signer_alice.call(&ctx, "rofl.Create", create.clone());
@@ -147,6 +151,9 @@ fn test_management_ops() {
policy: create.policy,
admin: Some(keys::alice::address()),
stake: BaseUnits::new(1_000, Denomination::NATIVE),
+ metadata: BTreeMap::from([("foo".into(), "bar".into())]),
+ sek: app_cfg.sek.clone(),
+ ..Default::default()
}
);
let instances = Module::::get_instances(app_id).unwrap();
@@ -157,6 +164,7 @@ fn test_management_ops() {
id: app_id,
policy: Default::default(),
admin: Some(keys::bob::address()), // Transfer admin to bob.
+ ..Default::default()
};
let dispatch_result = signer_bob.call(&ctx, "rofl.Update", update.clone());
@@ -184,6 +192,8 @@ fn test_management_ops() {
policy: update.policy,
admin: Some(keys::bob::address()),
stake: BaseUnits::new(1_000, Denomination::NATIVE),
+ sek: app_cfg.sek.clone(),
+ ..Default::default()
}
);
@@ -211,13 +221,13 @@ fn test_management_ops() {
#[test]
fn test_create_scheme() {
let mut mock = mock::Mock::default();
- let ctx = mock.create_ctx_for_runtime::(false);
+ let ctx = mock.create_ctx_for_runtime::(true);
TestRuntime::migrate(&ctx);
let create = types::Create {
- policy: Default::default(),
scheme: types::IdentifierScheme::CreatorNonce,
+ ..Default::default()
};
let mut signer_alice = mock::Signer::new(0, keys::alice::sigspec());
@@ -231,3 +241,106 @@ fn test_create_scheme() {
"rofl1qqfuf7u556prwv0wkdt398prhrpat7r3rvr97khf"
);
}
+
+#[test]
+fn test_key_derivation() {
+ let mut mock = mock::Mock::default();
+ let ctx = mock.create_ctx_for_runtime::(true);
+
+ TestRuntime::migrate(&ctx);
+
+ let create = types::Create {
+ scheme: types::IdentifierScheme::CreatorNonce,
+ ..Default::default()
+ };
+
+ let mut signer_alice = mock::Signer::new(0, keys::alice::sigspec());
+ let dispatch_result = signer_alice.call(&ctx, "rofl.Create", create.clone());
+ assert!(dispatch_result.result.is_success(), "call should succeed");
+ let app: AppId = cbor::from_value(dispatch_result.result.unwrap()).unwrap();
+
+ let derive = types::DeriveKey {
+ app,
+ kind: types::KeyKind::EntropyV0,
+ generation: 0,
+ key_id: b"my test key".into(),
+ };
+
+ // First try with plain calls which should fail.
+ let dispatch_result = signer_alice.call(&ctx, "rofl.DeriveKey", derive.clone());
+ let (err_module, err_code) = dispatch_result.result.unwrap_failed();
+ assert_eq!(&err_module, "rofl");
+ assert_eq!(err_code, 13); // Must use non-plain call format.
+
+ // Use encrypted calls.
+ let dispatch_result = signer_alice.call_opts(
+ &ctx,
+ "rofl.DeriveKey",
+ derive.clone(),
+ CallOptions {
+ encrypted: true,
+ ..Default::default()
+ },
+ );
+ let (err_module, err_code) = dispatch_result.result.unwrap_failed();
+ assert_eq!(&err_module, "rofl");
+ assert_eq!(err_code, 11); // Forbidden (not an authorized key for the given app).
+
+ // Manually create an application with alice being an authorized key.
+ let fake_registration = types::Registration {
+ app,
+ extra_keys: vec![keys::alice::pk()],
+ ..Default::default()
+ };
+ state::update_registration(fake_registration).unwrap();
+
+ // The call should succeed now.
+ let dispatch_result = signer_alice.call_opts(
+ &ctx,
+ "rofl.DeriveKey",
+ derive.clone(),
+ CallOptions {
+ encrypted: true,
+ ..Default::default()
+ },
+ );
+ let dispatch_result = dispatch_result.result.unwrap();
+
+ // In mock mode all the keys are deterministic.
+ let result: types::DeriveKeyResponse = cbor::from_value(dispatch_result).unwrap();
+ assert_eq!(&result.key, &[0x33; 32]);
+
+ // Also try X25519 derivation.
+ let dispatch_result = signer_alice.call_opts(
+ &ctx,
+ "rofl.DeriveKey",
+ types::DeriveKey {
+ kind: types::KeyKind::X25519,
+ ..derive.clone()
+ },
+ CallOptions {
+ encrypted: true,
+ ..Default::default()
+ },
+ );
+ let dispatch_result = dispatch_result.result.unwrap();
+ let result: types::DeriveKeyResponse = cbor::from_value(dispatch_result).unwrap();
+ assert!(!result.key.is_empty());
+
+ // Ensure key identifier length limit is respected.
+ let dispatch_result = signer_alice.call_opts(
+ &ctx,
+ "rofl.DeriveKey",
+ types::DeriveKey {
+ key_id: vec![0x01; 256],
+ ..derive.clone()
+ },
+ CallOptions {
+ encrypted: true,
+ ..Default::default()
+ },
+ );
+ let (err_module, err_code) = dispatch_result.result.unwrap_failed();
+ assert_eq!(&err_module, "rofl");
+ assert_eq!(err_code, 1);
+}
diff --git a/runtime-sdk/src/modules/rofl/types.rs b/runtime-sdk/src/modules/rofl/types.rs
index de79b9ca02..3baebf80fd 100644
--- a/runtime-sdk/src/modules/rofl/types.rs
+++ b/runtime-sdk/src/modules/rofl/types.rs
@@ -1,3 +1,5 @@
+use std::collections::BTreeMap;
+
use crate::{
core::{
common::crypto::{signature, x25519},
@@ -16,6 +18,9 @@ pub struct Create {
pub policy: AppAuthPolicy,
/// Identifier generation scheme.
pub scheme: IdentifierScheme,
+ /// Metadata (arbitrary key/value pairs).
+ pub metadata: BTreeMap,
+ // Note that we cannot pass secrets here as the SEK is not yet available.
}
/// ROFL application identifier generation scheme.
@@ -36,6 +41,11 @@ pub struct Update {
pub policy: AppAuthPolicy,
/// Application administrator address.
pub admin: Option,
+
+ /// Metadata (arbitrary key/value pairs).
+ pub metadata: BTreeMap,
+ /// Secrets (arbitrary encrypted key/value pairs).
+ pub secrets: BTreeMap>,
}
/// Remove an existing ROFL application call.
@@ -46,6 +56,16 @@ pub struct Remove {
}
/// ROFL application configuration.
+///
+/// # Metadata
+///
+/// Metadata contains arbitrary key-value pairs.
+///
+/// # Secrets
+///
+/// In addition to metadata, the configuration can also contain secrets which are encrypted with a
+/// shared secret derived from the secret encryption key (SEK). Since the SEK is only available once
+/// the application has been registered, the initial create cannot contain secrets.
#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub struct AppConfig {
@@ -57,6 +77,14 @@ pub struct AppConfig {
pub admin: Option,
/// Staked amount.
pub stake: token::BaseUnits,
+
+ /// Metadata (arbitrary key/value pairs).
+ pub metadata: BTreeMap,
+ /// Secrets (arbitrary encrypted key/value pairs).
+ pub secrets: BTreeMap>,
+ /// Secret encryption public key. The key is used to derive a shared secret used for symmetric
+ /// encryption (e.g. using Deoxys-II or similar).
+ pub sek: x25519::PublicKey,
}
/// Register ROFL call.
@@ -74,6 +102,38 @@ pub struct Register {
pub extra_keys: Vec,
}
+/// Kind of key for derivation.
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, cbor::Encode, cbor::Decode)]
+#[repr(u8)]
+pub enum KeyKind {
+ /// Raw entropy derivation.
+ #[default]
+ EntropyV0 = 0,
+
+ /// X25519 key pair.
+ X25519 = 1,
+}
+
+/// Derive key call.
+#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
+pub struct DeriveKey {
+ /// ROFL application identifier.
+ pub app: AppId,
+ /// Key kind.
+ pub kind: KeyKind,
+ /// Key generation.
+ pub generation: u64,
+ /// Key identifier.
+ pub key_id: Vec,
+}
+
+/// Response from the derive key call.
+#[derive(Clone, Default, cbor::Encode, cbor::Decode)]
+pub struct DeriveKeyResponse {
+ /// Derived key.
+ pub key: Vec,
+}
+
/// ROFL registration descriptor.
#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
pub struct Registration {
diff --git a/runtime-sdk/src/types/transaction.rs b/runtime-sdk/src/types/transaction.rs
index 648cb79dda..4c9fb0eddd 100644
--- a/runtime-sdk/src/types/transaction.rs
+++ b/runtime-sdk/src/types/transaction.rs
@@ -290,6 +290,16 @@ pub enum CallFormat {
EncryptedX25519DeoxysII = 1,
}
+impl CallFormat {
+ /// Whether this call format is end-to-end encrypted.
+ pub fn is_encrypted(&self) -> bool {
+ match self {
+ Self::Plain => false,
+ Self::EncryptedX25519DeoxysII => true,
+ }
+ }
+}
+
/// Method call.
#[derive(Clone, Debug, cbor::Encode, cbor::Decode)]
pub struct Call {
diff --git a/tests/runtimes/components-ronl/src/lib.rs b/tests/runtimes/components-ronl/src/lib.rs
index b147be1a3b..ad25aff931 100644
--- a/tests/runtimes/components-ronl/src/lib.rs
+++ b/tests/runtimes/components-ronl/src/lib.rs
@@ -3,6 +3,8 @@ use std::collections::BTreeMap;
use once_cell::sync::Lazy;
+#[cfg(feature = "debug-mock-sgx")]
+use oasis_runtime_sdk::keymanager::TrustedSigners;
use oasis_runtime_sdk::{
self as sdk, config, modules,
modules::rofl::app_id::AppId,
@@ -59,6 +61,11 @@ impl sdk::Runtime for Runtime {
oracle::Module,
);
+ #[cfg(feature = "debug-mock-sgx")]
+ fn trusted_signers() -> Option {
+ Some(TrustedSigners::unsafe_mock())
+ }
+
fn genesis_state() -> ::Genesis {
use modules::rofl::policy::*;
use sdk::core::common::sgx::{pcs, EnclaveIdentity, QuotePolicy};