From 276d4bcee2469bff448d73710084626cc5d30571 Mon Sep 17 00:00:00 2001 From: yuroitaki <> Date: Thu, 31 Jul 2025 18:20:41 +0800 Subject: [PATCH 1/2] Remove notary crates. --- .github/workflows/ci.yml | 199 +-- Cargo.lock | 1135 +--------------- Cargo.toml | 16 - README.md | 9 +- crates/notary/client/Cargo.toml | 29 - crates/notary/client/src/client.rs | 530 -------- crates/notary/client/src/error.rs | 48 - crates/notary/client/src/lib.rs | 15 - crates/notary/common/Cargo.toml | 11 - crates/notary/common/src/lib.rs | 34 - crates/notary/server/Cargo.toml | 66 - crates/notary/server/README.md | 226 ---- crates/notary/server/build.rs | 55 - crates/notary/server/notary-server.Dockerfile | 19 - .../notary-server.Dockerfile.dockerignore | 4 - crates/notary/server/openapi.yaml | 223 ---- crates/notary/server/src/auth.rs | 81 -- crates/notary/server/src/auth/jwt.rs | 210 --- crates/notary/server/src/auth/whitelist.rs | 161 --- crates/notary/server/src/cli.rs | 10 - crates/notary/server/src/config.rs | 245 ---- crates/notary/server/src/error.rs | 61 - crates/notary/server/src/lib.rs | 23 - crates/notary/server/src/main.rs | 30 - crates/notary/server/src/middleware.rs | 132 -- crates/notary/server/src/server.rs | 349 ----- crates/notary/server/src/server_tracing.rs | 41 - crates/notary/server/src/service.rs | 240 ---- .../server/src/service/axum_websocket.rs | 1172 ----------------- crates/notary/server/src/service/tcp.rs | 106 -- crates/notary/server/src/service/websocket.rs | 37 - crates/notary/server/src/signing.rs | 153 --- crates/notary/server/src/tee.rs | 125 -- crates/notary/server/src/types.rs | 64 - crates/notary/server/src/util.rs | 83 -- crates/notary/server/tee/README.md | 114 -- .../server/tee/gramine-local.Dockerfile | 6 - .../server/tee/notary-server-sgx.Dockerfile | 11 - .../tee/notary-server.manifest.template | 38 - crates/notary/server/tee/run-gramine-local.sh | 48 - crates/notary/tests-integration/Cargo.toml | 39 - .../tests-integration/fixture/.gitignore | 1 - .../tests-integration/fixture/auth/jwt.key | 51 - .../fixture/auth/jwt.key.pub | 14 - .../fixture/auth/whitelist.csv | 3 - .../fixture/config/config.yaml | 46 - .../fixture/notary/notary.key | 5 - .../fixture/notary/notary.pub | 4 - .../tests-integration/fixture/tls/README.md | 14 - .../tests-integration/fixture/tls/notary.crt | 23 - .../tests-integration/fixture/tls/notary.csr | 17 - .../tests-integration/fixture/tls/notary.key | 28 - .../tests-integration/fixture/tls/openssl.cnf | 7 - .../tests-integration/fixture/tls/rootCA.crt | 22 - .../tests-integration/fixture/tls/rootCA.key | 28 - .../tests-integration/fixture/tls/rootCA.srl | 1 - .../notary/tests-integration/tests/notary.rs | 574 -------- pre-commit-check.sh | 11 +- set_tlsn_version.rs | 3 - 59 files changed, 42 insertions(+), 7008 deletions(-) delete mode 100644 crates/notary/client/Cargo.toml delete mode 100644 crates/notary/client/src/client.rs delete mode 100644 crates/notary/client/src/error.rs delete mode 100644 crates/notary/client/src/lib.rs delete mode 100644 crates/notary/common/Cargo.toml delete mode 100644 crates/notary/common/src/lib.rs delete mode 100644 crates/notary/server/Cargo.toml delete mode 100644 crates/notary/server/README.md delete mode 100644 crates/notary/server/build.rs delete mode 100644 crates/notary/server/notary-server.Dockerfile delete mode 100644 crates/notary/server/notary-server.Dockerfile.dockerignore delete mode 100644 crates/notary/server/openapi.yaml delete mode 100644 crates/notary/server/src/auth.rs delete mode 100644 crates/notary/server/src/auth/jwt.rs delete mode 100644 crates/notary/server/src/auth/whitelist.rs delete mode 100644 crates/notary/server/src/cli.rs delete mode 100644 crates/notary/server/src/config.rs delete mode 100644 crates/notary/server/src/error.rs delete mode 100644 crates/notary/server/src/lib.rs delete mode 100644 crates/notary/server/src/main.rs delete mode 100644 crates/notary/server/src/middleware.rs delete mode 100644 crates/notary/server/src/server.rs delete mode 100644 crates/notary/server/src/server_tracing.rs delete mode 100644 crates/notary/server/src/service.rs delete mode 100644 crates/notary/server/src/service/axum_websocket.rs delete mode 100644 crates/notary/server/src/service/tcp.rs delete mode 100644 crates/notary/server/src/service/websocket.rs delete mode 100644 crates/notary/server/src/signing.rs delete mode 100644 crates/notary/server/src/tee.rs delete mode 100644 crates/notary/server/src/types.rs delete mode 100644 crates/notary/server/src/util.rs delete mode 100644 crates/notary/server/tee/README.md delete mode 100644 crates/notary/server/tee/gramine-local.Dockerfile delete mode 100644 crates/notary/server/tee/notary-server-sgx.Dockerfile delete mode 100644 crates/notary/server/tee/notary-server.manifest.template delete mode 100755 crates/notary/server/tee/run-gramine-local.sh delete mode 100644 crates/notary/tests-integration/Cargo.toml delete mode 100644 crates/notary/tests-integration/fixture/.gitignore delete mode 100644 crates/notary/tests-integration/fixture/auth/jwt.key delete mode 100644 crates/notary/tests-integration/fixture/auth/jwt.key.pub delete mode 100644 crates/notary/tests-integration/fixture/auth/whitelist.csv delete mode 100644 crates/notary/tests-integration/fixture/config/config.yaml delete mode 100644 crates/notary/tests-integration/fixture/notary/notary.key delete mode 100644 crates/notary/tests-integration/fixture/notary/notary.pub delete mode 100644 crates/notary/tests-integration/fixture/tls/README.md delete mode 100644 crates/notary/tests-integration/fixture/tls/notary.crt delete mode 100644 crates/notary/tests-integration/fixture/tls/notary.csr delete mode 100644 crates/notary/tests-integration/fixture/tls/notary.key delete mode 100644 crates/notary/tests-integration/fixture/tls/openssl.cnf delete mode 100644 crates/notary/tests-integration/fixture/tls/rootCA.crt delete mode 100644 crates/notary/tests-integration/fixture/tls/rootCA.key delete mode 100644 crates/notary/tests-integration/fixture/tls/rootCA.srl delete mode 100644 crates/notary/tests-integration/tests/notary.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 419e8b956f..59c546bfb6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,6 @@ on: permissions: id-token: write contents: read - attestations: write env: CARGO_TERM_COLOR: always @@ -22,7 +21,6 @@ env: # - https://github.com/privacy-scaling-explorations/mpz/issues/178 # 32 seems to be big enough for the foreseeable future RAYON_NUM_THREADS: 32 - GIT_COMMIT_HASH: ${{ github.event.pull_request.head.sha || github.sha }} RUST_VERSION: 1.88.0 jobs: @@ -159,9 +157,6 @@ jobs: - name: Use caching uses: Swatinem/rust-cache@v2.7.7 - - name: Add custom DNS entry to /etc/hosts for notary TLS test - run: echo "127.0.0.1 tlsnotaryserver.io" | sudo tee -a /etc/hosts - - name: Run integration tests run: cargo test --locked --profile tests-integration --workspace --exclude tlsn-tls-client --exclude tlsn-tls-core --no-fail-fast -- --include-ignored @@ -186,201 +181,9 @@ jobs: files: lcov.info fail_ci_if_error: true - build-sgx: - runs-on: ubuntu-latest - needs: build-and-test - container: - image: rust:latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install Clang - run: | - apt update - apt install -y clang - - - name: Use caching - uses: Swatinem/rust-cache@v2.7.7 - - - name: Build Rust Binary - run: | - cargo build --locked --bin notary-server --release --features tee_quote - cp --verbose target/release/notary-server $GITHUB_WORKSPACE - - - name: Upload Binary for use in the Gramine Job - uses: actions/upload-artifact@v4 - with: - name: notary-server - path: notary-server - if-no-files-found: error - - gramine-sgx: - runs-on: ubuntu-latest - needs: build-sgx - container: - image: gramineproject/gramine:latest - if: github.ref == 'refs/heads/dev' || (startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '.')) - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Restore SGX signing key from secrets - run: | - mkdir -p "${HOME}/.config/gramine/" - echo "${{ secrets.SGX_SIGNING_KEY }}" > "${HOME}/.config/gramine/enclave-key.pem" - # verify key - openssl rsa -in "${HOME}/.config/gramine/enclave-key.pem" -check -noout - - - name: Download notary-server binary from build job - uses: actions/download-artifact@v4 - with: - name: notary-server - path: crates/notary/server/tee - - - name: Install jq - run: | - apt update - apt install -y jq - - - name: Use Gramine to calculate measurements - run: | - cd crates/notary/server/tee - - chmod +x notary-server - - gramine-manifest \ - -Dlog_level=debug \ - -Darch_libdir=/lib/x86_64-linux-gnu \ - -Dself_exe=notary-server \ - notary-server.manifest.template \ - notary-server.manifest - - gramine-sgx-sign \ - --manifest notary-server.manifest \ - --output notary-server.manifest.sgx - - gramine-sgx-sigstruct-view --verbose --output-format=json notary-server.sig | tee >> notary-server-sigstruct.json - - cat notary-server-sigstruct.json - - mr_enclave=$(jq -r '.mr_enclave' notary-server-sigstruct.json) - mr_signer=$(jq -r '.mr_signer' notary-server-sigstruct.json) - - echo "mrenclave=$mr_enclave" >>"$GITHUB_OUTPUT" - echo "#### sgx mrenclave" | tee >>$GITHUB_STEP_SUMMARY - echo "\`\`\`mr_enclave: ${mr_enclave}\`\`\`" | tee >>$GITHUB_STEP_SUMMARY - echo "\`\`\`mr_signer: ${mr_signer}\`\`\`" | tee >>$GITHUB_STEP_SUMMARY - - - name: Upload notary-server and signatures - id: upload-notary-server-sgx - uses: actions/upload-artifact@v4 - with: - name: notary-server-sgx.zip - path: | - crates/notary/server/tee/notary-server - crates/notary/server/tee/notary-server-sigstruct.json - crates/notary/server/tee/notary-server.sig - crates/notary/server/tee/notary-server.manifest - crates/notary/server/tee/notary-server.manifest.sgx - crates/notary/server/tee/README.md - if-no-files-found: error - - - name: Attest Build Provenance - if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/dev' - uses: actions/attest-build-provenance@v2 - with: - subject-name: notary-server-sgx.zip - subject-digest: sha256:${{ steps.upload-notary-server-sgx.outputs.artifact-digest }} - - - uses: geekyeggo/delete-artifact@v5 # Delete notary-server from the build job, It is part of the zipfile with the signature - with: - name: notary-server - - gramine-sgx-docker: - runs-on: ubuntu-latest - needs: gramine-sgx - permissions: - contents: read - packages: write - env: - CONTAINER_REGISTRY: ghcr.io - if: github.ref == 'refs/heads/dev' || (startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '.')) - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - sparse-checkout: './crates/notary/server/tee/notary-server-sgx.Dockerfile' - - - name: Download notary-server-sgx.zip from gramine-sgx job - uses: actions/download-artifact@v4 - with: - name: notary-server-sgx.zip - path: ./notary-server-sgx - - - name: Log in to the Container registry - uses: docker/login-action@v2 - with: - registry: ${{ env.CONTAINER_REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker image of notary server - id: meta-notary-server-sgx - uses: docker/metadata-action@v4 - with: - images: ${{ env.CONTAINER_REGISTRY }}/${{ github.repository }}/notary-server-sgx - - - name: Build and push Docker image of notary server - uses: docker/build-push-action@v4 - with: - context: . - push: true - tags: ${{ steps.meta-notary-server-sgx.outputs.tags }} - labels: ${{ steps.meta-notary-server-sgx.outputs.labels }} - file: ./crates/notary/server/tee/notary-server-sgx.Dockerfile - - build_and_publish_notary_server_image: - name: Build and publish notary server's image - runs-on: ubuntu-latest - needs: build-and-test - permissions: - contents: read - packages: write - env: - CONTAINER_REGISTRY: ghcr.io - if: github.ref == 'refs/heads/dev' || (startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '.')) - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Log in to the Container registry - uses: docker/login-action@v2 - with: - registry: ${{ env.CONTAINER_REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker image of notary server - id: meta-notary-server - uses: docker/metadata-action@v4 - with: - images: ${{ env.CONTAINER_REGISTRY }}/${{ github.repository }}/notary-server - - - name: Build and push Docker image of notary server - uses: docker/build-push-action@v4 - with: - context: . - push: true - tags: ${{ steps.meta-notary-server.outputs.tags }} - labels: ${{ steps.meta-notary-server.outputs.labels }} - file: ./crates/notary/server/notary-server.Dockerfile - create-release-draft: name: Create Release Draft - needs: build_and_publish_notary_server_image + needs: build-and-test runs-on: ubuntu-latest permissions: contents: write diff --git a/Cargo.lock b/Cargo.lock index 29d0a13377..8cc9482a51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -490,15 +490,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anstream" version = "0.6.19" @@ -732,12 +723,6 @@ dependencies = [ "rand 0.8.5", ] -[[package]] -name = "arraydeque" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" - [[package]] name = "arrayref" version = "0.3.9" @@ -948,26 +933,7 @@ dependencies = [ "futures-util", "log", "pin-project-lite", - "tungstenite 0.23.0", -] - -[[package]] -name = "async-tungstenite" -version = "0.28.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c348fb0b6d132c596eca3dcd941df48fb597aafcb07a738ec41c004b087dc99" -dependencies = [ - "atomic-waker", - "futures-core", - "futures-io", - "futures-task", - "futures-util", - "log", - "native-tls", - "pin-project-lite", - "tokio", - "tokio-native-tls", - "tungstenite 0.24.0", + "tungstenite", ] [[package]] @@ -979,7 +945,6 @@ dependencies = [ "futures", "pharos", "rustc_version 0.4.1", - "tokio", ] [[package]] @@ -988,17 +953,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "auto-future" version = "1.0.0" @@ -1086,7 +1040,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" dependencies = [ "axum-core 0.5.2", - "base64 0.22.1", "bytes", "form_urlencoded", "futures-util", @@ -1106,10 +1059,8 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sha1", "sync_wrapper", "tokio", - "tokio-tungstenite 0.26.2", "tower", "tower-layer", "tower-service", @@ -1157,17 +1108,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "axum-macros" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "axum-test" version = "16.4.1" @@ -1281,36 +1221,13 @@ dependencies = [ "serde", ] -[[package]] -name = "bindgen" -version = "0.66.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" -dependencies = [ - "bitflags 2.9.1", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.104", - "which 4.4.2", -] - [[package]] name = "bindgen" version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.9.1", + "bitflags", "cexpr", "clang-sys", "itertools 0.12.1", @@ -1333,7 +1250,7 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.9.1", + "bitflags", "cexpr", "clang-sys", "itertools 0.12.1", @@ -1362,20 +1279,11 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" -dependencies = [ - "serde", -] [[package]] name = "bitvec" @@ -1552,12 +1460,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "cargo-emit" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1582e1c9e755dd6ad6b224dcffb135d199399a4568d454bd89fe515ca8425695" - [[package]] name = "cast" version = "0.3.0" @@ -1611,7 +1513,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8380ce7721cc895fe8a184c49d615fe755b0c9a3d7986355cee847439fff907f" dependencies = [ "async-std", - "async-tungstenite 0.27.0", + "async-tungstenite", "base64 0.22.1", "bytes", "cfg-if", @@ -1753,21 +1655,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim 0.8.0", - "textwrap", - "unicode-width", - "vec_map", -] - [[package]] name = "clap" version = "4.5.40" @@ -1842,25 +1729,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "config" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68578f196d2a33ff61b27fae256c3164f65e36382648e30666dde05b8cc9dfdf" -dependencies = [ - "async-trait", - "convert_case", - "json5", - "nom", - "pathdiff", - "ron", - "rust-ini", - "serde", - "serde_json", - "toml", - "yaml-rust2", -] - [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -1890,26 +1758,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom 0.2.16", - "once_cell", - "tiny-keccak", -] - [[package]] name = "const_format" version = "0.2.34" @@ -1936,15 +1784,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "cooked-waker" version = "5.0.0" @@ -1961,16 +1800,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -2019,7 +1848,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.5.40", + "clap", "criterion-plot", "futures", "is-terminal", @@ -2296,7 +2125,7 @@ dependencies = [ "libc", "parking_lot", "percent-encoding", - "pin-project 1.1.10", + "pin-project", "serde", "serde_json", "serde_v8", @@ -2586,15 +2415,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "dlv-list" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" -dependencies = [ - "const-random", -] - [[package]] name = "dotenv" version = "0.15.0" @@ -2671,15 +2491,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - [[package]] name = "enum-try-as-inner" version = "0.1.1" @@ -2748,16 +2559,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "eyre" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" -dependencies = [ - "indenter", - "once_cell", -] - [[package]] name = "fastrand" version = "2.3.0" @@ -2802,18 +2603,6 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" -[[package]] -name = "filetime" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.59.0", -] - [[package]] name = "fixed-hash" version = "0.8.0" @@ -2838,21 +2627,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.1" @@ -3073,21 +2847,6 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "git2" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" -dependencies = [ - "bitflags 2.9.1", - "libc", - "libgit2-sys", - "log", - "openssl-probe", - "openssl-sys", - "url", -] - [[package]] name = "glob" version = "0.3.2" @@ -3219,24 +2978,6 @@ dependencies = [ "serde", ] -[[package]] -name = "hashlink" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" -dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "heck" version = "0.4.1" @@ -3249,15 +2990,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.5.2" @@ -3400,22 +3132,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - [[package]] name = "hyper-util" version = "0.1.14" @@ -3603,12 +3319,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "indenter" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" - [[package]] name = "indexmap" version = "1.9.3" @@ -3631,26 +3341,6 @@ dependencies = [ "serde", ] -[[package]] -name = "inotify" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" -dependencies = [ - "bitflags 1.3.2", - "inotify-sys", - "libc", -] - -[[package]] -name = "inotify-sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" -dependencies = [ - "libc", -] - [[package]] name = "inout" version = "0.1.4" @@ -3703,7 +3393,7 @@ version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "hermit-abi 0.5.2", + "hermit-abi", "libc", "windows-sys 0.59.0", ] @@ -3782,32 +3472,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "json5" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" -dependencies = [ - "pest", - "pest_derive", - "serde", -] - -[[package]] -name = "jsonwebtoken" -version = "9.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" -dependencies = [ - "base64 0.22.1", - "js-sys", - "pem", - "ring 0.17.14", - "serde", - "serde_json", - "simple_asn1", -] - [[package]] name = "k256" version = "0.13.4" @@ -3842,26 +3506,6 @@ dependencies = [ "sha3-asm", ] -[[package]] -name = "kqueue" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" -dependencies = [ - "kqueue-sys", - "libc", -] - -[[package]] -name = "kqueue-sys" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - [[package]] name = "kv-log-macro" version = "1.0.7" @@ -3889,20 +3533,6 @@ version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" -[[package]] -name = "libgit2-sys" -version = "0.17.0+1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224" -dependencies = [ - "cc", - "libc", - "libssh2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", -] - [[package]] name = "libloading" version = "0.8.8" @@ -3919,43 +3549,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" -[[package]] -name = "libredox" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" -dependencies = [ - "bitflags 2.9.1", - "libc", - "redox_syscall", -] - -[[package]] -name = "libssh2-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libz-sys" -version = "1.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -4052,82 +3645,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "mc-sgx-core-build" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94420a570e76dfc39924b4ec712c2df21a8d77e61ca5ba0857300b1c59889a5d" -dependencies = [ - "bindgen 0.66.1", - "cargo-emit", -] - -[[package]] -name = "mc-sgx-core-sys-types" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c5c33323d0f2855e63efaa538bdd7094104ff79f963734b4122f23302cff581" -dependencies = [ - "bindgen 0.66.1", - "cargo-emit", - "mc-sgx-core-build", - "serde", - "serde_with", -] - -[[package]] -name = "mc-sgx-core-types" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1191b0bb2868ff5bdf7766d3b3e9fa9bb94c23149ae21a82485e6f5f44074106" -dependencies = [ - "bitflags 2.9.1", - "displaydoc", - "getrandom 0.2.16", - "hex", - "mc-sgx-core-sys-types", - "mc-sgx-util", - "nom", - "rand_core 0.6.4", - "serde", - "subtle", -] - -[[package]] -name = "mc-sgx-dcap-sys-types" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dd3e93a316a5c142de6fb9179970abc04ca4767732b90d118c90d531db47e89" -dependencies = [ - "bindgen 0.66.1", - "mc-sgx-core-build", - "mc-sgx-core-sys-types", -] - -[[package]] -name = "mc-sgx-dcap-types" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42c618d93a3f9561bdc27231fa7965ebe855c8d74f5cd5467da194042e0671a9" -dependencies = [ - "displaydoc", - "mc-sgx-core-types", - "mc-sgx-dcap-sys-types", - "mc-sgx-util", - "nom", - "p256", - "serde", - "sha2", - "static_assertions", - "subtle", -] - -[[package]] -name = "mc-sgx-util" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3c237bec3e33530c4b1a171c8c078ad5d525d5fae177ac9d62e8e454b3fffb7" - [[package]] name = "memchr" version = "2.7.5" @@ -4165,18 +3682,6 @@ dependencies = [ "adler2", ] -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.48.0", -] - [[package]] name = "mio" version = "1.0.4" @@ -4549,23 +4054,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nohash-hasher" version = "0.2.0" @@ -4582,134 +4070,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "notary-client" -version = "0.1.0-alpha.13-pre" -dependencies = [ - "derive_builder 0.12.0", - "futures", - "http-body-util", - "hyper", - "hyper-util", - "notary-common", - "serde_json", - "thiserror 1.0.69", - "tokio", - "tokio-rustls", - "tracing", - "webpki-roots 0.26.11", -] - -[[package]] -name = "notary-common" -version = "0.1.0-alpha.13-pre" -dependencies = [ - "serde", -] - -[[package]] -name = "notary-server" -version = "0.1.0-alpha.13-pre" -dependencies = [ - "async-tungstenite 0.28.2", - "axum 0.8.4", - "axum-core 0.5.2", - "axum-macros", - "base64 0.21.7", - "chrono", - "config", - "const-oid", - "csv", - "eyre", - "futures-util", - "git2", - "hex", - "http 1.3.1", - "http-body-util", - "hyper", - "hyper-util", - "jsonwebtoken", - "k256", - "mc-sgx-dcap-types", - "notary-common", - "notify", - "p256", - "pkcs8", - "rand 0.9.1", - "rand06-compat", - "rustls 0.21.12", - "rustls-pemfile", - "serde", - "serde_json", - "serde_yaml", - "sha1", - "structopt", - "strum", - "thiserror 1.0.69", - "tlsn", - "tlsn-core", - "tokio", - "tokio-rustls", - "tokio-util", - "tower-http 0.5.2", - "tower-service", - "tower-util", - "tracing", - "tracing-subscriber", - "uuid", - "ws_stream_tungstenite", - "zeroize", -] - -[[package]] -name = "notary-tests-integration" -version = "0.0.0" -dependencies = [ - "async-tungstenite 0.28.2", - "futures", - "http 1.3.1", - "http-body-util", - "hyper", - "hyper-tls", - "hyper-util", - "jsonwebtoken", - "notary-client", - "notary-common", - "notary-server", - "rstest", - "rustls 0.21.12", - "rustls-pemfile", - "serde_json", - "tls-server-fixture", - "tlsn", - "tlsn-core", - "tlsn-tls-core", - "tokio", - "tokio-native-tls", - "tokio-util", - "tracing", - "tracing-subscriber", - "uuid", - "ws_stream_tungstenite", -] - -[[package]] -name = "notify" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" -dependencies = [ - "bitflags 2.9.1", - "filetime", - "inotify", - "kqueue", - "libc", - "log", - "mio 0.8.11", - "walkdir", - "windows-sys 0.48.0", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -4777,7 +4137,7 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ - "hermit-abi 0.5.2", + "hermit-abi", "libc", ] @@ -4827,70 +4187,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "openssl" -version = "0.10.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" -dependencies = [ - "bitflags 2.9.1", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "openssl-src" -version = "300.5.0+3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ce546f549326b0e6052b649198487d91320875da901e7bd11a06d1ee3f9c2f" -dependencies = [ - "cc", -] - -[[package]] -name = "openssl-sys" -version = "0.9.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" -dependencies = [ - "cc", - "libc", - "openssl-src", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "ordered-multimap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" -dependencies = [ - "dlv-list", - "hashbrown 0.14.5", -] - [[package]] name = "os_pipe" version = "1.2.2" @@ -4980,36 +4276,14 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pathdiff" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + "windows-targets 0.52.6", +] [[package]] -name = "pem" -version = "3.0.5" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" -dependencies = [ - "base64 0.22.1", - "serde", -] +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pem-rfc7468" @@ -5080,33 +4354,13 @@ dependencies = [ "rustc_version 0.4.1", ] -[[package]] -name = "pin-project" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a" -dependencies = [ - "pin-project-internal 0.4.30", -] - [[package]] name = "pin-project" version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ - "pin-project-internal 1.1.10", -] - -[[package]] -name = "pin-project-internal" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "pin-project-internal", ] [[package]] @@ -5153,12 +4407,6 @@ dependencies = [ "spki", ] -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - [[package]] name = "plotters" version = "0.3.7" @@ -5195,7 +4443,7 @@ checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.5.2", + "hermit-abi", "pin-project-lite", "rustix 1.0.7", "tracing", @@ -5306,30 +4554,6 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -5392,7 +4616,7 @@ checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.9.1", + "bitflags", "lazy_static", "num-traits", "rand 0.9.1", @@ -5545,7 +4769,7 @@ version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ - "bitflags 2.9.1", + "bitflags", ] [[package]] @@ -5702,18 +4926,6 @@ dependencies = [ "rustc-hex", ] -[[package]] -name = "ron" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" -dependencies = [ - "base64 0.21.7", - "bitflags 2.9.1", - "serde", - "serde_derive", -] - [[package]] name = "rs_merkle" version = "1.4.2" @@ -5782,16 +4994,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" -[[package]] -name = "rust-ini" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0698206bcb8882bf2a9ecb4c1e7785db57ff052297085a6efd4fe42302068a" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - [[package]] name = "rust-multipart-rfc7578_2" version = "0.6.1" @@ -5856,7 +5058,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.1", + "bitflags", "errno", "libc", "linux-raw-sys 0.4.15", @@ -5869,7 +5071,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags 2.9.1", + "bitflags", "errno", "libc", "linux-raw-sys 0.9.4", @@ -5888,18 +5090,6 @@ dependencies = [ "webpki", ] -[[package]] -name = "rustls" -version = "0.21.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" -dependencies = [ - "log", - "ring 0.17.14", - "rustls-webpki 0.101.7", - "sct", -] - [[package]] name = "rustls" version = "0.23.28" @@ -5910,7 +5100,7 @@ dependencies = [ "log", "once_cell", "rustls-pki-types", - "rustls-webpki 0.103.3", + "rustls-webpki", "subtle", "zeroize", ] @@ -5933,16 +5123,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring 0.17.14", - "untrusted 0.9.0", -] - [[package]] name = "rustls-webpki" version = "0.103.3" @@ -5988,15 +5168,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "schannel" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "schemars" version = "0.9.0" @@ -6052,29 +5223,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.9.1", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "semver" version = "0.11.0" @@ -6249,19 +5397,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" -dependencies = [ - "indexmap 2.10.0", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - [[package]] name = "serdect" version = "0.2.0" @@ -6404,18 +5539,6 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "simple_asn1" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" -dependencies = [ - "num-bigint", - "num-traits", - "thiserror 2.0.12", - "time", -] - [[package]] name = "slab" version = "0.4.10" @@ -6507,12 +5630,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72abeda133c49d7bddece6c154728f83eec8172380c80ab7096da9487e20d27c" -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "strsim" version = "0.10.0" @@ -6525,30 +5642,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap 2.34.0", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "strum" version = "0.27.1" @@ -6679,15 +5772,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.69" @@ -6972,7 +6056,7 @@ version = "0.0.0" dependencies = [ "bincode", "chrono", - "clap 4.5.40", + "clap", "dotenv", "futures", "hex", @@ -6980,7 +6064,6 @@ dependencies = [ "hyper", "hyper-util", "k256", - "notary-client", "serde_json", "spansy", "tls-server-fixture", @@ -7055,7 +6138,7 @@ name = "tlsn-harness-plot" version = "0.0.0" dependencies = [ "charming", - "clap 4.5.40", + "clap", "csv", "itertools 0.14.0", "tlsn-harness-core", @@ -7069,7 +6152,7 @@ dependencies = [ "anyhow", "axum 0.8.4", "chromiumoxide", - "clap 4.5.40", + "clap", "csv", "duct", "futures", @@ -7334,7 +6417,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.4", + "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -7354,26 +6437,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls 0.21.12", - "tokio", -] - [[package]] name = "tokio-tungstenite" version = "0.23.1" @@ -7383,19 +6446,7 @@ dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.23.0", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.26.2", + "tungstenite", ] [[package]] @@ -7475,7 +6526,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ - "bitflags 2.9.1", + "bitflags", "bytes", "futures-util", "http 1.3.1", @@ -7500,7 +6551,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.1", + "bitflags", "bytes", "futures-util", "http 1.3.1", @@ -7525,18 +6576,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" -[[package]] -name = "tower-util" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1093c19826d33807c72511e68f73b4a0469a3f22c2bd5f7d5212178b4b89674" -dependencies = [ - "futures-core", - "futures-util", - "pin-project 0.4.30", - "tower-service", -] - [[package]] name = "tracing" version = "0.1.41" @@ -7581,16 +6620,6 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-serde" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" -dependencies = [ - "serde", - "tracing-core", -] - [[package]] name = "tracing-subscriber" version = "0.3.19" @@ -7601,8 +6630,6 @@ dependencies = [ "nu-ansi-term", "once_cell", "regex", - "serde", - "serde_json", "sharded-slab", "smallvec", "thread_local", @@ -7610,7 +6637,6 @@ dependencies = [ "tracing", "tracing-core", "tracing-log", - "tracing-serde", ] [[package]] @@ -7675,42 +6701,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "tungstenite" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http 1.3.1", - "httparse", - "log", - "native-tls", - "rand 0.8.5", - "sha1", - "thiserror 1.0.69", - "utf-8", -] - -[[package]] -name = "tungstenite" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" -dependencies = [ - "bytes", - "data-encoding", - "http 1.3.1", - "httparse", - "log", - "rand 0.9.1", - "sha1", - "thiserror 2.0.12", - "utf-8", -] - [[package]] name = "typenum" version = "1.18.0" @@ -7776,18 +6766,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - [[package]] name = "unicode-xid" version = "0.2.6" @@ -7814,12 +6792,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "unsafe-libyaml" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" - [[package]] name = "untrusted" version = "0.7.1" @@ -7868,9 +6840,7 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ - "getrandom 0.3.3", "js-sys", - "rand 0.9.1", "wasm-bindgen", ] @@ -7881,7 +6851,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2b387c1c5731284e756c03280032068e68e5b52f6c4714492403c30f650ad52" dependencies = [ "bindgen 0.71.1", - "bitflags 2.9.1", + "bitflags", "fslock", "gzip-header", "home", @@ -7902,18 +6872,6 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.5" @@ -8132,7 +7090,7 @@ dependencies = [ "futures", "once_cell", "tokio", - "tokio-tungstenite 0.23.1", + "tokio-tungstenite", "tracing", "tracing-subscriber", ] @@ -8503,7 +7461,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.1", + "bitflags", ] [[package]] @@ -8512,26 +7470,6 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" -[[package]] -name = "ws_stream_tungstenite" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed39ff9f8b2eda91bf6390f9f49eee93d655489e15708e3bb638c1c4f07cecb4" -dependencies = [ - "async-tungstenite 0.28.2", - "async_io_stream", - "bitflags 2.9.1", - "futures-core", - "futures-io", - "futures-sink", - "futures-util", - "pharos", - "rustc_version 0.4.1", - "tokio", - "tracing", - "tungstenite 0.24.0", -] - [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -8559,17 +7497,6 @@ dependencies = [ "tap", ] -[[package]] -name = "yaml-rust2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8902160c4e6f2fb145dbe9d6760a75e3c9522d8bf796ed7047c85919ac7115f8" -dependencies = [ - "arraydeque", - "encoding_rs", - "hashlink", -] - [[package]] name = "yamux" version = "0.13.5" @@ -8580,7 +7507,7 @@ dependencies = [ "log", "nohash-hasher", "parking_lot", - "pin-project 1.1.10", + "pin-project", "rand 0.9.1", "static_assertions", "web-time 1.1.0", diff --git a/Cargo.toml b/Cargo.toml index 15618c900b..835ff0625f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,10 +9,6 @@ members = [ "crates/data-fixtures", "crates/examples", "crates/formats", - "crates/notary/client", - "crates/notary/common", - "crates/notary/server", - "crates/notary/tests-integration", "crates/server-fixture/certs", "crates/server-fixture/server", "crates/tls/backend", @@ -45,9 +41,6 @@ inherits = "release" lto = true [workspace.dependencies] -notary-client = { path = "crates/notary/client" } -notary-common = { path = "crates/notary/common" } -notary-server = { path = "crates/notary/server" } tls-server-fixture = { path = "crates/tls/server-fixture" } tlsn-attestation = { path = "crates/attestation" } tlsn-cipher = { path = "crates/components/cipher" } @@ -95,7 +88,6 @@ aes = { version = "0.8" } aes-gcm = { version = "0.9" } anyhow = { version = "1.0" } async-trait = { version = "0.1" } -async-tungstenite = { version = "0.28.2" } axum = { version = "0.8" } bcs = { version = "0.1" } bincode = { version = "1.3" } @@ -116,12 +108,10 @@ enum-try-as-inner = { version = "0.1" } env_logger = { version = "0.10" } futures = { version = "0.3" } futures-rustls = { version = "0.26" } -futures-util = { version = "0.3" } generic-array = { version = "0.14" } ghash = { version = "0.5" } hex = { version = "0.4" } hmac = { version = "0.12" } -http = { version = "1.1" } http-body-util = { version = "0.1" } hyper = { version = "1.1" } hyper-util = { version = "0.1" } @@ -134,7 +124,6 @@ log = { version = "0.4" } once_cell = { version = "1.19" } opaque-debug = { version = "0.3" } p256 = { version = "0.13" } -pkcs8 = { version = "0.10" } pin-project-lite = { version = "0.2" } pollster = { version = "0.4" } rand = { version = "0.9" } @@ -157,23 +146,18 @@ signature = { version = "2.2" } thiserror = { version = "1.0" } tiny-keccak = { version = "2.0" } tokio = { version = "1.38" } -tokio-rustls = { version = "0.24" } tokio-util = { version = "0.7" } toml = { version = "0.8" } tower = { version = "0.5" } tower-http = { version = "0.5" } tower-service = { version = "0.3" } -tower-util = { version = "0.3.1" } tracing = { version = "0.1" } tracing-subscriber = { version = "0.3" } -uuid = { version = "1.4" } wasm-bindgen = { version = "0.2" } wasm-bindgen-futures = { version = "0.4" } web-spawn = { version = "0.2" } web-time = { version = "0.2" } webpki = { version = "0.22" } webpki-roots = { version = "0.26" } -ws_stream_tungstenite = { version = "0.14" } # Use the patched ws_stream_wasm to fix the issue https://github.com/najamelan/ws_stream_wasm/issues/12#issuecomment-1711902958 ws_stream_wasm = { git = "https://github.com/tlsnotary/ws_stream_wasm", rev = "2ed12aad9f0236e5321f577672f309920b2aef51" } -zeroize = { version = "1.8" } diff --git a/README.md b/README.md index 1d5bf00596..b1da63015c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ [actions-url]: https://github.com/tlsnotary/tlsn/actions?query=workflow%3Aci+branch%3Adev [Website](https://tlsnotary.org) | -[Documentation](https://docs.tlsnotary.org) | +[Documentation](https://tlsnotary.org/docs/intro) | [API Docs](https://tlsnotary.github.io/tlsn) | [Discord](https://discord.gg/9XwESXtcN7) @@ -44,12 +44,9 @@ at your option. ## Directory - [examples](./crates/examples/): Examples on how to use the TLSNotary protocol. -- [tlsn-prover](./crates/prover/): The library for the prover component. -- [tlsn-verifier](./crates/verifier/): The library for the verifier component. -- [notary](./crates/notary/): Implements the [notary server](https://docs.tlsnotary.org/intro.html#tls-verification-with-a-general-purpose-notary) and its client. -- [components](./crates/components/): Houses low-level libraries. +- [tlsn](./crates/tlsn/): The TLSNotary library. -This repository contains the source code for the Rust implementation of the TLSNotary protocol. For additional tools and implementations related to TLSNotary, visit . This includes repositories such as [`tlsn-js`](https://github.com/tlsnotary/tlsn-js), [`tlsn-extension`](https://github.com/tlsnotary/tlsn-extension), [`explorer`](https://github.com/tlsnotary/explorer), among others. +This repository contains the source code for the Rust implementation of the TLSNotary protocol. For additional tools and implementations related to TLSNotary, visit . This includes repositories such as [`tlsn-js`](https://github.com/tlsnotary/tlsn-js), [`tlsn-extension`](https://github.com/tlsnotary/tlsn-extension), among others. ## Development diff --git a/crates/notary/client/Cargo.toml b/crates/notary/client/Cargo.toml deleted file mode 100644 index 5cf1e7fad4..0000000000 --- a/crates/notary/client/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "notary-client" -version = "0.1.0-alpha.13-pre" -edition = "2021" - -[lints] -workspace = true - -[dependencies] -notary-common = { workspace = true } - -derive_builder = { workspace = true } -futures = { workspace = true } -http-body-util = { workspace = true } -hyper = { workspace = true, features = ["client", "http1"] } -hyper-util = { workspace = true, features = ["full"] } -serde_json = { workspace = true } -thiserror = { workspace = true } -tokio = { workspace = true, features = [ - "rt", - "rt-multi-thread", - "macros", - "net", - "io-std", - "fs", -] } -tokio-rustls = { workspace = true } -tracing = { workspace = true } -webpki-roots = { workspace = true } diff --git a/crates/notary/client/src/client.rs b/crates/notary/client/src/client.rs deleted file mode 100644 index f2b82983ac..0000000000 --- a/crates/notary/client/src/client.rs +++ /dev/null @@ -1,530 +0,0 @@ -//! Notary client. -//! -//! This module sets up connection to notary server via TCP or TLS for -//! subsequent requests for notarization. - -use http_body_util::{BodyExt as _, Either, Empty, Full}; -use hyper::{ - body::{Bytes, Incoming}, - client::conn::http1::Parts, - header::AUTHORIZATION, - Request, Response, StatusCode, -}; -use hyper_util::rt::TokioIo; -use notary_common::{ - ClientType, NotarizationSessionRequest, NotarizationSessionResponse, X_API_KEY_HEADER, -}; -use std::{ - io::Error as IoError, - pin::Pin, - sync::Arc, - task::{Context, Poll}, -}; -use tokio::{ - io::{AsyncRead, AsyncWrite, ReadBuf}, - net::TcpStream, - time::{sleep, timeout, Duration}, -}; -use tokio_rustls::{ - client::TlsStream, - rustls::{self, ClientConfig, OwnedTrustAnchor, RootCertStore}, - TlsConnector, -}; -use tracing::{debug, error, info}; - -use crate::error::{ClientError, ErrorKind}; - -/// Parameters used to configure notarization. -#[derive(Debug, Clone, derive_builder::Builder)] -pub struct NotarizationRequest { - /// Maximum number of bytes that can be sent. - max_sent_data: usize, - /// Maximum number of bytes that can be received. - max_recv_data: usize, -} - -impl NotarizationRequest { - /// Creates a new builder for `NotarizationRequest`. - pub fn builder() -> NotarizationRequestBuilder { - NotarizationRequestBuilder::default() - } -} - -/// An accepted notarization request. -#[derive(Debug)] -#[non_exhaustive] -pub struct Accepted { - /// Session identifier. - pub id: String, - /// Connection to the notary server to be used by a prover. - pub io: NotaryConnection, -} - -/// A notary server connection. -#[derive(Debug)] -#[allow(clippy::large_enum_variant)] -pub enum NotaryConnection { - /// Unencrypted TCP connection. - Tcp(TcpStream), - /// TLS connection. - Tls(TlsStream), -} - -impl AsyncRead for NotaryConnection { - #[inline] - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - match self.get_mut() { - NotaryConnection::Tcp(stream) => Pin::new(stream).poll_read(cx, buf), - NotaryConnection::Tls(stream) => Pin::new(stream).poll_read(cx, buf), - } - } -} - -impl AsyncWrite for NotaryConnection { - #[inline] - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - match self.get_mut() { - NotaryConnection::Tcp(stream) => Pin::new(stream).poll_write(cx, buf), - NotaryConnection::Tls(stream) => Pin::new(stream).poll_write(cx, buf), - } - } - - #[inline] - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.get_mut() { - NotaryConnection::Tcp(stream) => Pin::new(stream).poll_flush(cx), - NotaryConnection::Tls(stream) => Pin::new(stream).poll_flush(cx), - } - } - - #[inline] - fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.get_mut() { - NotaryConnection::Tcp(stream) => Pin::new(stream).poll_shutdown(cx), - NotaryConnection::Tls(stream) => Pin::new(stream).poll_shutdown(cx), - } - } -} - -/// Client that sets up connection to notary server. -#[derive(Debug, Clone, derive_builder::Builder)] -pub struct NotaryClient { - /// Host of the notary server endpoint, either a DNS name (if TLS is used) - /// or IP address. - #[builder(setter(into))] - host: String, - /// Port of the notary server endpoint. - #[builder(default = "self.default_port()")] - port: u16, - /// URL path prefix of the notary server endpoint, e.g. "https://://...". - #[builder(setter(into), default = "String::from(\"\")")] - path_prefix: String, - /// Flag to turn on/off using TLS with notary server. - #[builder(setter(name = "enable_tls"), default = "true")] - tls: bool, - /// Root certificate store used for establishing TLS connection with notary - /// server. - #[builder(default = "default_root_store()")] - root_cert_store: RootCertStore, - /// API key used to call notary server endpoints if whitelisting is enabled - /// in notary server. - #[builder(setter(into, strip_option), default)] - api_key: Option, - /// JWT token used to call notary server endpoints if JWT authorization is - /// enabled in notary server. - #[builder(setter(into, strip_option), default)] - jwt: Option, - /// The duration of notarization request timeout in seconds. - #[builder(default = "60")] - request_timeout: usize, - /// The number of seconds to wait between notarization request retries. - /// - /// By default uses the value suggested by the server. - #[builder(default = "None")] - request_retry_override: Option, -} - -impl NotaryClientBuilder { - // Default setter of port. - fn default_port(&self) -> u16 { - // If port is not specified, set it to 80 if TLS is off, else 443 since TLS is - // on (including when self.tls = None, which means it's set to default - // (true)). - if let Some(false) = self.tls { - 80 - } else { - 443 - } - } -} - -impl NotaryClient { - /// Creates a new builder for `NotaryClient`. - pub fn builder() -> NotaryClientBuilder { - NotaryClientBuilder::default() - } - - /// Configures and requests a notarization, returning a connection to the - /// notary server if successful. - pub async fn request_notarization( - &self, - notarization_request: NotarizationRequest, - ) -> Result { - let notary_socket = tokio::net::TcpStream::connect((self.host.as_str(), self.port)) - .await - .map_err(|err| ClientError::new(ErrorKind::Connection, Some(Box::new(err))))?; - - // Setting TCP_NODELAY will improve prover latency. - let _ = notary_socket - .set_nodelay(true) - .map_err(|_| info!("An error occured when setting TCP_NODELAY. This will result in higher protocol latency.")); - - if self.tls { - debug!("Setting up tls connection..."); - - let notary_client_config = ClientConfig::builder() - .with_safe_defaults() - .with_root_certificates(self.root_cert_store.clone()) - .with_no_client_auth(); - - let notary_connector = TlsConnector::from(Arc::new(notary_client_config)); - let notary_tls_socket = notary_connector - .connect( - self.host.as_str().try_into().map_err(|err| { - error!("Failed to parse notary server DNS name: {:?}", self.host); - ClientError::new(ErrorKind::TlsSetup, Some(Box::new(err))) - })?, - notary_socket, - ) - .await - .map_err(|err| { - if is_tls_mismatch_error(&err) { - error!("Perhaps the notary server is not accepting our TLS connection"); - } - ClientError::new(ErrorKind::TlsSetup, Some(Box::new(err))) - })?; - - self.send_request(notary_tls_socket, notarization_request) - .await - .map(|(connection, session_id)| Accepted { - id: session_id, - io: NotaryConnection::Tls(connection), - }) - } else { - debug!("Setting up tcp connection..."); - - self.send_request(notary_socket, notarization_request) - .await - .map(|(connection, session_id)| Accepted { - id: session_id, - io: NotaryConnection::Tcp(connection), - }) - } - } - - /// Sends notarization request to the notary server. - async fn send_request( - &self, - notary_socket: S, - notarization_request: NotarizationRequest, - ) -> Result<(S, String), ClientError> { - let http_scheme = if self.tls { "https" } else { "http" }; - let path_prefix = if self.path_prefix.is_empty() { - String::new() - } else { - format!("/{}", self.path_prefix) - }; - - // Attach the hyper HTTP client to the notary connection to send request to the - // /session endpoint to configure notarization and obtain session id. - let (mut notary_request_sender, notary_connection) = - hyper::client::conn::http1::handshake(TokioIo::new(notary_socket)) - .await - .map_err(|err| { - error!("Failed to attach http client to notary socket"); - ClientError::new(ErrorKind::Connection, Some(Box::new(err))) - })?; - - // Create a future to poll the notary connection to completion before extracting - // the socket. - let notary_connection_fut = async { - // Claim back notary socket after HTTP exchange is done. - let Parts { - io: notary_socket, .. - } = notary_connection.without_shutdown().await.map_err(|err| { - error!("Failed to claim back notary socket after HTTP exchange is done"); - ClientError::new(ErrorKind::Internal, Some(Box::new(err))) - })?; - - Ok(notary_socket) - }; - - // Create a future to send configuration and notarization requests to the notary - // server using the connection established above. - let client_requests_fut = async { - // Build the HTTP request to configure notarization. - let configuration_request_payload = - serde_json::to_string(&NotarizationSessionRequest { - client_type: ClientType::Tcp, - max_sent_data: Some(notarization_request.max_sent_data), - max_recv_data: Some(notarization_request.max_recv_data), - }) - .map_err(|err| { - error!("Failed to serialise http request for configuration"); - ClientError::new(ErrorKind::Internal, Some(Box::new(err))) - })?; - - let mut configuration_request_builder = Request::builder() - .uri(format!( - "{http_scheme}://{}:{}{}/session", - self.host, self.port, path_prefix - )) - .method("POST") - .header("Host", &self.host) - // Need to specify application/json for axum to parse it as json. - .header("Content-Type", "application/json"); - - if let Some(api_key) = &self.api_key { - configuration_request_builder = - configuration_request_builder.header(X_API_KEY_HEADER, api_key); - } - - if let Some(jwt) = &self.jwt { - configuration_request_builder = - configuration_request_builder.header(AUTHORIZATION, format!("Bearer {jwt}")); - } - - let configuration_request = configuration_request_builder - .body(Either::Left(Full::new(Bytes::from( - configuration_request_payload, - )))) - .map_err(|err| { - error!("Failed to build http request for configuration"); - ClientError::new(ErrorKind::Internal, Some(Box::new(err))) - })?; - - debug!("Sending configuration request: {:?}", configuration_request); - - let configuration_response = notary_request_sender - .send_request(configuration_request) - .await - .map_err(|err| { - error!("Failed to send http request for configuration"); - ClientError::new(ErrorKind::Http, Some(Box::new(err))) - })?; - - debug!("Sent configuration request"); - - if configuration_response.status() != StatusCode::OK { - return Err(ClientError::new( - ErrorKind::Configuration, - Some( - format!( - "Configuration response status is not OK: {configuration_response:?}" - ) - .into(), - ), - )); - } - - let configuration_response_payload = configuration_response - .into_body() - .collect() - .await - .map_err(|err| { - error!("Failed to parse configuration response"); - ClientError::new(ErrorKind::Http, Some(Box::new(err))) - })? - .to_bytes(); - - let configuration_response_payload_parsed = - serde_json::from_str::(&String::from_utf8_lossy( - &configuration_response_payload, - )) - .map_err(|err| { - error!("Failed to parse configuration response payload"); - ClientError::new(ErrorKind::Internal, Some(Box::new(err))) - })?; - - debug!( - "Configuration response: {:?}", - configuration_response_payload_parsed - ); - - // Send notarization request via HTTP, where the underlying TCP/TLS connection - // will be extracted later. - let notarization_request = Request::builder() - // Need to specify the session_id so that notary server knows the right - // configuration to use as the configuration is set in the previous - // HTTP call. - .uri(format!( - "{http_scheme}://{}:{}{}/notarize?sessionId={}", - self.host, - self.port, - path_prefix, - &configuration_response_payload_parsed.session_id - )) - .method("GET") - .header("Host", &self.host) - .header("Connection", "Upgrade") - // Need to specify this upgrade header for server to extract TCP/TLS connection - // later. - .header("Upgrade", "TCP") - .body(Either::Right(Empty::::new())) - .map_err(|err| { - error!("Failed to build http request for notarization"); - ClientError::new(ErrorKind::Internal, Some(Box::new(err))) - })?; - - debug!("Sending notarization request: {:?}", notarization_request); - - let notarize_with_retry_fut = async { - loop { - let notarization_response = notary_request_sender - .send_request(notarization_request.clone()) - .await - .map_err(|err| { - error!("Failed to send http request for notarization"); - ClientError::new(ErrorKind::Http, Some(Box::new(err))) - })?; - - if notarization_response.status() == StatusCode::SWITCHING_PROTOCOLS { - return Ok::, ClientError>(notarization_response); - } else if notarization_response.status() == StatusCode::SERVICE_UNAVAILABLE { - let retry_after = self - .request_retry_override - .unwrap_or(parse_retry_after(¬arization_response)?); - - debug!("Retrying notarization request in {:?}", retry_after); - - sleep(Duration::from_secs(retry_after)).await; - } else { - return Err(ClientError::new( - ErrorKind::Internal, - Some( - format!( - "Server sent unexpected status code {:?}", - notarization_response.status() - ) - .into(), - ), - )); - } - } - }; - - let notarization_response = timeout( - Duration::from_secs(self.request_timeout as u64), - notarize_with_retry_fut, - ) - .await - .map_err(|_| { - ClientError::new( - ErrorKind::Internal, - Some( - "Timed out while waiting for server to accept notarization request".into(), - ), - ) - })??; - - debug!("Notarization request was accepted by the server"); - - if notarization_response.status() != StatusCode::SWITCHING_PROTOCOLS { - return Err(ClientError::new( - ErrorKind::Internal, - Some( - format!( - "Notarization response status is not SWITCHING_PROTOCOL: {notarization_response:?}" - ) - .into(), - ), - )); - } - - Ok(configuration_response_payload_parsed.session_id) - }; - - // Poll both futures simultaneously to obtain the resulting socket and - // session_id. - let (notary_socket, session_id) = - futures::try_join!(notary_connection_fut, client_requests_fut)?; - - Ok((notary_socket.into_inner(), session_id)) - } - - /// Sets notarization request timeout duration in seconds. - pub fn request_timeout(&mut self, timeout: usize) { - self.request_timeout = timeout; - } - - /// Sets the number of seconds to wait between notarization request - /// retries. - pub fn request_retry_override(&mut self, seconds: u64) { - self.request_retry_override = Some(seconds); - } -} - -/// Default root store using mozilla certs. -fn default_root_store() -> RootCertStore { - let mut root_store = RootCertStore::empty(); - root_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| { - OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject.as_ref(), - ta.subject_public_key_info.as_ref(), - ta.name_constraints.as_ref().map(|nc| nc.as_ref()), - ) - })); - - root_store -} - -// Checks whether the error is potentially related to a mismatch in TLS -// configuration between the client and the server. -fn is_tls_mismatch_error(err: &std::io::Error) -> bool { - if let Some(rustls::Error::InvalidMessage(rustls::InvalidMessage::InvalidContentType)) = err - .get_ref() - .and_then(|inner| inner.downcast_ref::()) - { - return true; - } - false -} - -// Attempts to parse the value of the "Retry-After" header from the given -// `response`. -fn parse_retry_after(response: &Response) -> Result { - let seconds = match response.headers().get("Retry-After") { - Some(value) => { - let value_str = value.to_str().map_err(|err| { - ClientError::new( - ErrorKind::Internal, - Some(format!("Invalid Retry-After header: {err}").into()), - ) - })?; - - let seconds: u64 = value_str.parse().map_err(|err| { - ClientError::new( - ErrorKind::Internal, - Some(format!("Could not parse Retry-After header as number: {err}").into()), - ) - })?; - seconds - } - None => { - return Err(ClientError::new( - ErrorKind::Internal, - Some("The expected Retry-After header was not found in server response".into()), - )); - } - }; - - Ok(seconds) -} diff --git a/crates/notary/client/src/error.rs b/crates/notary/client/src/error.rs deleted file mode 100644 index 360ed03d1b..0000000000 --- a/crates/notary/client/src/error.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! Notary client errors. -//! -//! This module handles errors that might occur during connection setup and -//! notarization requests. - -use derive_builder::UninitializedFieldError; -use std::{error::Error, fmt}; - -#[derive(Debug)] -#[allow(missing_docs)] -pub(crate) enum ErrorKind { - Internal, - Builder, - Connection, - TlsSetup, - Http, - Configuration, -} - -#[derive(Debug, thiserror::Error)] -#[allow(missing_docs)] -pub struct ClientError { - kind: ErrorKind, - #[source] - source: Option>, -} - -impl ClientError { - pub(crate) fn new(kind: ErrorKind, source: Option>) -> Self { - Self { kind, source } - } -} - -impl fmt::Display for ClientError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "client error: {:?}, source: {:?}", - self.kind, self.source - ) - } -} - -impl From for ClientError { - fn from(ufe: UninitializedFieldError) -> Self { - ClientError::new(ErrorKind::Builder, Some(Box::new(ufe))) - } -} diff --git a/crates/notary/client/src/lib.rs b/crates/notary/client/src/lib.rs deleted file mode 100644 index 445267a4aa..0000000000 --- a/crates/notary/client/src/lib.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Notary client library. -//! -//! A notary client's purpose is to establish a connection to the notary server -//! via TCP or TLS, and to configure and request notarization. -//! Note that the actual notarization is not performed by the notary client but -//! by the prover of the TLSNotary protocol. -#![deny(missing_docs, unreachable_pub, unused_must_use)] -#![deny(clippy::all)] -#![forbid(unsafe_code)] - -mod client; -mod error; - -pub use client::{Accepted, NotarizationRequest, NotaryClient, NotaryConnection}; -pub use error::ClientError; diff --git a/crates/notary/common/Cargo.toml b/crates/notary/common/Cargo.toml deleted file mode 100644 index b65e3817eb..0000000000 --- a/crates/notary/common/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "notary-common" -version = "0.1.0-alpha.13-pre" -description = "Common code shared between notary-server and notary-client" -edition = "2021" - -[lints] -workspace = true - -[dependencies] -serde = { workspace = true, features = ["derive"] } diff --git a/crates/notary/common/src/lib.rs b/crates/notary/common/src/lib.rs deleted file mode 100644 index 884c396b39..0000000000 --- a/crates/notary/common/src/lib.rs +++ /dev/null @@ -1,34 +0,0 @@ -use serde::{Deserialize, Serialize}; - -/// Custom HTTP header used for specifying a whitelisted API key. -pub const X_API_KEY_HEADER: &str = "X-API-Key"; - -/// Types of client that the prover is using. -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] -pub enum ClientType { - /// Client that has access to the transport layer. - Tcp, - /// Client that cannot directly access the transport layer, e.g. browser - /// extension. - Websocket, -} - -/// Request object of the /session API. -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct NotarizationSessionRequest { - pub client_type: ClientType, - /// Maximum data that can be sent by the prover. - pub max_sent_data: Option, - /// Maximum data that can be received by the prover. - pub max_recv_data: Option, -} - -/// Response object of the /session API. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct NotarizationSessionResponse { - /// Unique session id that is generated by the notary and shared to the - /// prover. - pub session_id: String, -} diff --git a/crates/notary/server/Cargo.toml b/crates/notary/server/Cargo.toml deleted file mode 100644 index fce4fd25ac..0000000000 --- a/crates/notary/server/Cargo.toml +++ /dev/null @@ -1,66 +0,0 @@ -[package] -name = "notary-server" -version = "0.1.0-alpha.13-pre" -edition = "2021" - -[lints] -workspace = true - -[features] -tee_quote = ["dep:mc-sgx-dcap-types", "dep:hex"] - -[dependencies] -notary-common = { workspace = true } -tlsn-core = { workspace = true } -tlsn = { workspace = true } - -async-tungstenite = { workspace = true, features = ["tokio-native-tls"] } -axum = { workspace = true, features = ["ws"] } -axum-core = { version = "0.5" } -axum-macros = { version = "0.5" } -base64 = { version = "0.21" } -config = { version = "0.14", features = ["yaml"] } -const-oid = { version = "0.9.6", features = ["db"] } -csv = { version = "1.3" } -eyre = { version = "0.6" } -futures-util = { workspace = true } -http = { workspace = true } -http-body-util = { workspace = true } -hyper = { workspace = true, features = ["client", "http1", "server"] } -hyper-util = { workspace = true, features = ["full"] } -jsonwebtoken = { version = "9.3.1", features = ["use_pem"] } -k256 = { workspace = true } -notify = { version = "6.1.1", default-features = false, features = [ - "macos_kqueue", -] } -p256 = { workspace = true } -pkcs8 = { workspace = true, features = ["pem"] } -rand = { workspace = true } -rand06-compat = { workspace = true } -rustls = { workspace = true } -rustls-pemfile = { workspace = true } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } -serde_yaml = { version = "0.9" } -sha1 = { version = "0.10" } -structopt = { version = "0.3" } -strum = { version = "0.27", features = ["derive"] } -thiserror = { workspace = true } -tokio = { workspace = true, features = ["full"] } -tokio-rustls = { workspace = true } -tokio-util = { workspace = true, features = ["compat"] } -tower-http = { workspace = true, features = ["cors"] } -tower-service = { workspace = true } -tower-util = { workspace = true } -tracing = { workspace = true } -tracing-subscriber = { workspace = true, features = ["env-filter", "json"] } -uuid = { workspace = true, features = ["v4", "fast-rng"] } -ws_stream_tungstenite = { workspace = true, features = ["tokio_io"] } -zeroize = { workspace = true } - -hex = { workspace = true, optional = true } -mc-sgx-dcap-types = { version = "0.11.0", optional = true } - -[build-dependencies] -git2 = "0.19.0" -chrono.workspace = true diff --git a/crates/notary/server/README.md b/crates/notary/server/README.md deleted file mode 100644 index 9e6a865012..0000000000 --- a/crates/notary/server/README.md +++ /dev/null @@ -1,226 +0,0 @@ -# notary-server -An implementation of the notary server in Rust. - -## ⚠️ Notice -This crate is currently under active development and should not be used in production. Expect bugs and regular major breaking changes. - ---- -## Running the server -### ⚠️ Notice -- When running this server against a prover (e.g. [Rust](../../examples/) or [browser extension](https://github.com/tlsnotary/tlsn-extension)), please ensure that the prover's version is the same as the version of this server. -- When running this server in a *production environment*, please first read this [page](https://docs.tlsnotary.org/developers/notary_server.html). - -### Using Cargo -Start the server with: -```bash -cargo run --release --bin notary-server -``` - -### Using Docker -There are two ways to obtain the notary server's Docker image. -- [GitHub](#obtaining-the-image-via-github) -- [Building from source](#building-from-source) - -#### GitHub -1. Obtain the latest image. -```bash -docker pull ghcr.io/tlsnotary/tlsn/notary-server:latest -``` -2. Run the docker container. -```bash -docker run --init -p 127.0.0.1:7047:7047 ghcr.io/tlsnotary/tlsn/notary-server:latest -``` - -#### Building from source -1. Build the docker image at the root of this *repository*. -```bash -docker build . -t notary-server:local -f crates/notary/server/notary-server.Dockerfile -``` -2. Run the docker container. -```bash -docker run --init -p 127.0.0.1:7047:7047 notary-server:local -``` ---- -## Configuration -### Default -Refer to [config.rs](./src/config.rs) for more information on the definition of these setting parameters. -```yaml -host: "0.0.0.0" -port: 7047 -html_info: | - - - - - - - - - - - - -

Notary Server {version}!

- - - -concurrency: 32 - -notarization: - max_sent_data: 4096 - max_recv_data: 16384 - timeout: 1800 - private_key_path: null - signature_algorithm: secp256k1 - -tls: - enabled: false - private_key_path: null - certificate_path: null - -log: - level: DEBUG - filter: null - format: COMPACT - -auth: - enabled: false - whitelist: null -``` -⚠️ By default, `notarization.private_key_path` is `null`, which means a **random, ephemeral** signing key will be generated at runtime (see [Signing](#signing) for more details). - -### Overriding default -The default setting can be overriden with either (1) environment variables, or (2) a configuration file (yaml). - -#### Environment Variables -Default values can be overriden by setting environment variables. The variables have a `NS_`-prefix followed by the configuration key in uppercase. Double underscores are used for nested configuration keys, e.g. `tls.enabled` will be `NS_TLS__ENABLED`. - -Example: -```bash -NS_PORT=8080 NS_NOTARIZATION__MAX_SENT_DATA=2048 cargo run --release --bin notary-server -``` - -#### Configuration File -This will override all the default values, hence it needs to **contain all compulsory** configuration keys and values (refer to the [default yaml](#default)). The config file has precedence over environment variables. -```bash -cargo run --release --bin notary-server -- --config -``` - -### When using Docker -1. Override the port. -```bash -docker run --init -p 127.0.0.1:7070:7070 -e NS_PORT=7070 notary-server:local -``` -2. Override the notarization private key path, and map a local private key into the container. -```bash -docker run --init -p 127.0.0.1:7047:7047 -e NS_NOTARIZATION__PRIVATE_KEY_PATH="/root/.notary/notary.key" -v :/root/.notary/notary.key notary-server:local -``` -3. Override with a configuration file. -```bash -docker run --init -p 127.0.0.1:7047:7047 -v :/root/.notary/config.yaml notary-server:local --config /root/.notary/config.yaml -``` -⚠️ The default `workdir` of the container is `/root/.notary`. - ---- -## API -### HTTP APIs -Defined in the [OpenAPI specification](./openapi.yaml). - -### WebSocket APIs -#### /notarize -##### Description -To perform a notarization using a session id — an unique id returned upon calling the `/session` endpoint successfully. - -##### Query Parameter -`sessionId` - -##### Query Parameter Type -String - ---- -## Features -### Notarization Configuration -To perform a notarization, some parameters need to be configured by the prover and the notary server (more details in the [OpenAPI specification](./openapi.yaml)), i.e. -- maximum data that can be sent and received. -- unique session id. - -To streamline this process, a single HTTP endpoint (`/session`) is used by both TCP and WebSocket clients. - -### Notarization -After calling the configuration endpoint above, the prover can proceed to start the notarization. For a TCP client, that means calling the `/notarize` endpoint using HTTP, while a WebSocket client should call the same endpoint but using WebSocket. Example implementations of these clients can be found in the [integration test](../tests-integration/tests/notary.rs). - -### Signing -To sign the notarized transcript, the notary server requires a signing key. If this signing key (`notarization.private_key_path` in the config) is not provided by the user, then **by default, a random, ephemeral** signing key will be generated at runtime. - -This ephemeral key, along with its public key, are not persisted. The keys disappear once the server stops. This makes the keys only suitable for testing. - -### TLS -TLS needs to be turned on between the prover and the notary for security purposes. It can be turned off though, if any of the following is true. - -1. This server is run locally. -2. TLS is to be handled by an external environment, e.g. reverse proxy, cloud setup. - -The toggle to turn on TLS, as well as paths to the TLS private key and certificate can be defined in the config (`tls` field). - -### Authorization -An optional authorization module is available to only allow requests with a valid credential attached. Currently, two modes are supported: whitelist and JWT. - -Please note that only *one* mode can be active at any one time. - -#### Whitelist mode -In whitelist mode, a valid API key needs to be attached in the custom HTTP header `X-API-Key`. The path of the API key whitelist, as well as the flag to enable/disable this module, can be changed in the config (`auth` field). - -Hot reloading of the whitelist is supported, i.e. changes to the whitelist file are automatically applied without needing to restart the server. - -#### JWT mode -In JWT mode, JSON Web Token is attached in the standard `Authorization` HTTP header as a bearer token. The algorithm, the path to verifying key, as well as custom user claims, can be changed in the config (`auth` field). - -Care should be taken when defining custom user claims as the middleware will: -- accept any claim if no custom claim is defined, -- as long as user defined claims are found, other unknown claims will be ignored. - -An example JWT config may look something like this: - -```yaml -auth: - enabled: true - jwt: - algorithm: "RS256" - public_key_path: "./fixture/auth/jwt.key.pub" - claims: - - name: sub - values: ["tlsnotary"] -``` - -### Logging -The default logging strategy of this server is set to `DEBUG` verbosity level for the crates that are useful for most debugging scenarios, i.e. using the following filtering logic. - -`notary_server=DEBUG,tlsn_verifier=DEBUG,mpc_tls=DEBUG,tls_client_async=DEBUG` - -In the configuration, one can toggle the verbosity level for these crates using the `level` field under `logging`. - -One can also provide a custom filtering logic by adding a `filter` field under `logging`, and use a value that follows the tracing crate's [filter directive syntax](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax). - -Logs can be printed in two formats. Compact and JSON. Compact is human-readable and is best suited for console. JSON is machine-readable and is used to send logs to log collection services. One can change log format by switching the `format` field under `logging`. Accepted values are `COMPACT` and `JSON`. `COMPACT` is used by default. - -### Concurrency -One can limit the number of concurrent notarization requests from provers via `concurrency` in the config. This is to limit resource utilization and mitigate potential DoS attacks. - ---- -## Architecture -### Objective -The main objective of a notary server is to perform notarizations together with a prover. In this case, the prover can either be a -1. TCP client — which has access and control over the transport layer, i.e. TCP. -2. WebSocket client — which has no access over TCP and instead uses WebSocket for notarizations. - -### Design Choices -#### Web Framework -Axum is chosen as the framework to serve HTTP and WebSocket requests from the prover clients due to its rich and well supported features, e.g. native integration with Tokio/Hyper/Tower, customizable middleware, the ability to support lower level integrations of TLS ([example](https://github.com/tokio-rs/axum/blob/main/examples/low-level-rustls/src/main.rs)). To simplify the notary server setup, a single Axum router is used to support both HTTP and WebSocket connections, i.e. all requests can be made to the same port of the notary server. - -#### WebSocket -Axum's internal implementation of WebSocket uses [tokio_tungstenite](https://docs.rs/tokio-tungstenite/latest/tokio_tungstenite/), which provides a WebSocket struct that doesn't implement [AsyncRead](https://docs.rs/futures/latest/futures/io/trait.AsyncRead.html) and [AsyncWrite](https://docs.rs/futures/latest/futures/io/trait.AsyncWrite.html). Both these traits are required by the TLSN core libraries for the prover and the notary. To overcome this, a [slight modification](./src/service/axum_websocket.rs) of Axum's implementation of WebSocket is used, where [async_tungstenite](https://docs.rs/async-tungstenite/latest/async_tungstenite/) is used instead so that [ws_stream_tungstenite](https://docs.rs/ws_stream_tungstenite/latest/ws_stream_tungstenite/index.html) can be used to wrap on top of the WebSocket struct to get AsyncRead and AsyncWrite implemented. diff --git a/crates/notary/server/build.rs b/crates/notary/server/build.rs deleted file mode 100644 index 6f97204af1..0000000000 --- a/crates/notary/server/build.rs +++ /dev/null @@ -1,55 +0,0 @@ -use chrono::DateTime; -use git2::{Commit, Repository, StatusOptions}; -use std::{env, error::Error}; - -fn main() -> Result<(), Box> { - if env::var("GIT_COMMIT_HASH").is_err() { - match get_commithash_with_dirty_suffix() { - Ok(commit_hash_with_suffix) => { - // Pass value as env var to the notary server - println!("cargo:rustc-env=GIT_COMMIT_HASH={commit_hash_with_suffix}"); - } - Err(e) => { - eprintln!("Failed to get commit hash in notary server build"); - eprintln!("Fix the error or configure GIT_COMMIT_HASH as environment variable"); - return Err(e.message().into()); - } - }; - } - Ok(()) -} - -fn get_commithash_with_dirty_suffix() -> Result { - let repo = Repository::discover(".")?; - let commit = get_commit(&repo)?; - let commit_hash = commit.id().to_string(); - let _timestamp = get_commit_timestamp(&commit)?; - let has_changes = check_local_changes(&repo)?; - - if has_changes { - Ok(format!("{commit_hash} (with local changes)")) - } else { - Ok(commit_hash) - } -} - -fn get_commit(repo: &Repository) -> Result { - let head = repo.head()?; - head.peel_to_commit() -} - -fn get_commit_timestamp(commit: &Commit) -> Result { - let timestamp = commit.time().seconds(); - let date_time = DateTime::from_timestamp(timestamp, 0) - .ok_or_else(|| git2::Error::from_str("Invalid timestamp"))?; - Ok(date_time.to_rfc2822()) -} - -fn check_local_changes(repo: &Repository) -> Result { - let mut status_options = StatusOptions::new(); - status_options - .include_untracked(false) - .include_ignored(false); - let statuses = repo.statuses(Some(&mut status_options))?; - Ok(!statuses.is_empty()) -} diff --git a/crates/notary/server/notary-server.Dockerfile b/crates/notary/server/notary-server.Dockerfile deleted file mode 100644 index 98bee3f06d..0000000000 --- a/crates/notary/server/notary-server.Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -# !!! To use this file, please run docker run at the root level of this repository -FROM rust:latest AS builder -RUN apt-get update && apt-get install -y clang libclang-dev -WORKDIR /usr/src/tlsn -COPY . . -RUN cargo install --locked --path crates/notary/server - -FROM ubuntu:latest -WORKDIR /root/.notary -RUN apt-get update && apt-get -y upgrade && apt-get install -y --no-install-recommends \ - pkg-config \ - libssl-dev \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* -COPY --from=builder /usr/local/cargo/bin/notary-server /usr/local/bin/notary-server -# Label to link this image with the repository in Github Container Registry (https://docs.github.com/en/packages/learn-github-packages/connecting-a-repository-to-a-package#connecting-a-repository-to-a-container-image-using-the-command-line) -LABEL org.opencontainers.image.source=https://github.com/tlsnotary/tlsn -LABEL org.opencontainers.image.description="An implementation of the notary server in Rust." -ENTRYPOINT [ "notary-server" ] diff --git a/crates/notary/server/notary-server.Dockerfile.dockerignore b/crates/notary/server/notary-server.Dockerfile.dockerignore deleted file mode 100644 index 9d37928df8..0000000000 --- a/crates/notary/server/notary-server.Dockerfile.dockerignore +++ /dev/null @@ -1,4 +0,0 @@ -# exclude Rust build artifacts -./target -./crates/wasm/pkg/ -./crates/harness/static/generated/ diff --git a/crates/notary/server/openapi.yaml b/crates/notary/server/openapi.yaml deleted file mode 100644 index 96eb206c07..0000000000 --- a/crates/notary/server/openapi.yaml +++ /dev/null @@ -1,223 +0,0 @@ -openapi: 3.0.0 -info: - title: Notary Server - description: Notary server written in Rust to provide notarization service. - version: 0.1.0-alpha.13-pre -tags: -- name: General -- name: Notarization -paths: - /healthcheck: - get: - tags: - - General - description: Healthcheck endpoint - security: - - {} # make security optional - - ApiKeyAuth: [] - - BearerAuth: [] - responses: - '200': - description: Ok response from server - content: - text/plain: - schema: - type: string - example: Ok - '401': - description: API key is invalid - content: - text/plain: - schema: - type: string - example: 'Unauthorized request from prover: Invalid API key.' - /info: - get: - tags: - - General - description: General information about the notary server - security: - - {} # make security optional - - ApiKeyAuth: [] - - BearerAuth: [] - responses: - '200': - description: Info response from server - content: - application/json: - schema: - $ref: '#/components/schemas/InfoResponse' - '401': - description: API key is invalid - content: - text/plain: - schema: - type: string - example: 'Unauthorized request from prover: Invalid API key.' - /session: - post: - tags: - - Notarization - description: Initialize and configure notarization for both TCP and WebSocket clients - security: - - {} # make security optional - - ApiKeyAuth: [] - - BearerAuth: [] - parameters: - - in: header - name: Content-Type - description: The value must be application/json - schema: - type: string - enum: - - application/json - required: true - requestBody: - description: Notarization session request to server - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/NotarizationSessionRequest' - responses: - '200': - description: Notarization session response from server - content: - application/json: - schema: - $ref: '#/components/schemas/NotarizationSessionResponse' - '400': - description: Configuration parameters or headers provided by prover are invalid - content: - text/plain: - schema: - type: string - example: 'Invalid request from prover: Failed to deserialize the JSON body into the target type' - '401': - description: API key is invalid - content: - text/plain: - schema: - type: string - example: 'Unauthorized request from prover: Invalid API key.' - '500': - description: There was some internal error when processing - content: - text/plain: - schema: - type: string - example: Something is wrong - /notarize: - get: - tags: - - Notarization - description: Start notarization for TCP client - parameters: - - in: header - name: Connection - description: The value should be 'Upgrade' - schema: - type: string - enum: - - Upgrade - required: true - - in: header - name: Upgrade - description: The value should be 'TCP' - schema: - type: string - enum: - - TCP - required: true - - in: query - name: sessionId - description: Unique ID returned from server upon calling POST /session - schema: - type: string - required: true - responses: - '101': - description: Switching protocol response - '400': - description: Headers provided by prover are invalid - content: - text/plain: - schema: - type: string - example: 'Invalid request from prover: Upgrade header is not set for client' - '500': - description: There was some internal error when processing - content: - text/plain: - schema: - type: string - example: Something is wrong -components: - schemas: - NotarizationSessionRequest: - type: object - properties: - clientType: - description: Types of client that the prover is using - type: string - enum: - - Tcp - - Websocket - maxSentData: - description: Maximum data that can be sent by the prover in bytes - type: integer - maxRecvData: - description: Maximum data that can be received by the prover in bytes - type: integer - required: - - clientType - NotarizationSessionResponse: - type: object - properties: - sessionId: - description: Unique ID returned from server upon calling POST /session - type: string - required: - - sessionId - InfoResponse: - type: object - properties: - version: - description: Current version of notary server - type: string - publicKey: - description: Public key of notary server for its notarization transcript signature - type: string - gitCommitHash: - description: The git commit hash of source code that this notary server is running - type: string - quote: - type: object - properties: - rawQuote: - description: Hex bytes representing the signed-by-intel quote - type: string - mrsigner: - description: Represents the public key of the enclave signer - type: string - mrenclave: - description: The enclave image hash, including gramine and the notary server itself - type: string - error: - description: Error that occurs when generating this quote - type: string - required: - - version - - publicKey - - gitCommitHash - securitySchemes: - ApiKeyAuth: - type: apiKey - in: header - name: X-API-Key - description: Whitelisted API key if auth module is turned on and in whitelist mode - BearerAuth: - type: http - scheme: bearer - bearerFormat: JWT - description: JSON Web Token if auth module is turned on and in JWT mode diff --git a/crates/notary/server/src/auth.rs b/crates/notary/server/src/auth.rs deleted file mode 100644 index d3a984cc4e..0000000000 --- a/crates/notary/server/src/auth.rs +++ /dev/null @@ -1,81 +0,0 @@ -pub(crate) mod jwt; -pub(crate) mod whitelist; - -use eyre::{eyre, Result}; -use jwt::load_jwt_key; -use std::{ - str::FromStr, - sync::{Arc, Mutex}, -}; -use strum::VariantNames; -use tracing::debug; -use whitelist::load_authorization_whitelist; - -pub use jwt::{Algorithm, Jwt}; -pub use whitelist::{ - watch_and_reload_authorization_whitelist, AuthorizationWhitelistRecord, Whitelist, -}; - -use crate::{AuthorizationModeProperties, NotaryServerProperties}; - -/// Supported authorization modes. -#[derive(Clone)] -pub enum AuthorizationMode { - Jwt(Jwt), - Whitelist(Whitelist), -} - -impl AuthorizationMode { - pub fn as_whitelist(&self) -> Option<&Whitelist> { - match self { - Self::Jwt(..) => None, - Self::Whitelist(whitelist) => Some(whitelist), - } - } -} - -/// Load authorization mode if it is enabled -pub async fn load_authorization_mode( - config: &NotaryServerProperties, -) -> Result> { - if !config.auth.enabled { - debug!("Skipping authorization as it is turned off."); - return Ok(None); - } - - let auth_mode = match config.auth.mode.as_ref().ok_or_else(|| { - eyre!( - "Authorization enabled but failed to load either whitelist or jwt properties. They are either absent or malformed." - ) - })? { - AuthorizationModeProperties::Jwt(jwt_opts) => { - debug!("Using JWT for authorization"); - let algorithm = Algorithm::from_str(&jwt_opts.algorithm).map_err(|_| { - eyre!( - "Unexpected JWT signing algorithm specified: '{}'. Possible values are: {:?}", - jwt_opts.algorithm, - Algorithm::VARIANTS, - ) - })?; - let claims = jwt_opts.claims.clone(); - let key = load_jwt_key(&jwt_opts.public_key_path, algorithm) - .await - .map_err(|err| eyre!("Failed to parse JWT public key: {:?}", err))?; - AuthorizationMode::Jwt(Jwt { - key, - claims, - algorithm, - }) - } - AuthorizationModeProperties::Whitelist(whitelist_csv_path) => { - debug!("Using whitelist for authorization"); - let entries = load_authorization_whitelist(whitelist_csv_path)?; - AuthorizationMode::Whitelist(Whitelist { - entries: Arc::new(Mutex::new(entries)), - csv_path: whitelist_csv_path.clone(), - }) - } - }; - - Ok(Some(auth_mode)) -} diff --git a/crates/notary/server/src/auth/jwt.rs b/crates/notary/server/src/auth/jwt.rs deleted file mode 100644 index 9d1bb8a1b8..0000000000 --- a/crates/notary/server/src/auth/jwt.rs +++ /dev/null @@ -1,210 +0,0 @@ -use eyre::Result; -use jsonwebtoken::{Algorithm as JwtAlgorithm, DecodingKey}; -use serde_json::Value; -use strum::{EnumString, VariantNames}; -use tracing::error; - -use crate::JwtClaim; - -/// Custom error for JWT handling -#[derive(Debug, thiserror::Error, PartialEq)] -#[error("JWT validation error: {0}")] -pub struct JwtValidationError(String); - -type JwtResult = std::result::Result; - -/// JWT config which also encapsulates claims validation logic. -#[derive(Clone)] -pub struct Jwt { - pub algorithm: Algorithm, - pub key: DecodingKey, - pub claims: Vec, -} - -impl Jwt { - pub fn validate(&self, claims: &Value) -> JwtResult<()> { - Jwt::validate_claims(&self.claims, claims) - } - - fn validate_claims(expected: &[JwtClaim], claims: &Value) -> JwtResult<()> { - expected - .iter() - .try_for_each(|expected| Self::validate_claim(expected, claims)) - } - - fn validate_claim(expected: &JwtClaim, given: &Value) -> JwtResult<()> { - let pointer = format!("/{}", expected.name.replace(".", "/")); - let field = given.pointer(&pointer).ok_or(JwtValidationError(format!( - "missing claim '{}'", - expected.name - )))?; - - let field_typed = field.as_str().ok_or(JwtValidationError(format!( - "unexpected type for claim '{}': only strings are supported for claim values", - expected.name, - )))?; - if !expected.values.is_empty() { - expected.values.iter().any(|exp| exp == field_typed).then_some(()).ok_or_else(|| { - let expected_values = expected.values.iter().map(|x| format!("'{x}'")).collect::>().join(", "); - JwtValidationError(format!( - "unexpected value for claim '{}': expected one of [ {expected_values} ], received '{field_typed}'", expected.name - )) - })?; - } - - Ok(()) - } -} - -#[derive(EnumString, Debug, Clone, Copy, PartialEq, Eq, VariantNames)] -#[strum(ascii_case_insensitive)] -/// Supported JWT signing algorithms -pub enum Algorithm { - /// RSASSA-PKCS1-v1_5 using SHA-256 - RS256, - /// RSASSA-PKCS1-v1_5 using SHA-384 - RS384, - /// RSASSA-PKCS1-v1_5 using SHA-512 - RS512, - /// RSASSA-PSS using SHA-256 - PS256, - /// RSASSA-PSS using SHA-384 - PS384, - /// RSASSA-PSS using SHA-512 - PS512, - /// ECDSA using SHA-256 - ES256, - /// ECDSA using SHA-384 - ES384, - /// Edwards-curve Digital Signature Algorithm (EdDSA) - EdDSA, -} - -impl From for JwtAlgorithm { - fn from(value: Algorithm) -> Self { - match value { - Algorithm::RS256 => Self::RS256, - Algorithm::RS384 => Self::RS384, - Algorithm::RS512 => Self::RS512, - Algorithm::PS256 => Self::PS256, - Algorithm::PS384 => Self::PS384, - Algorithm::PS512 => Self::PS512, - Algorithm::ES256 => Self::ES256, - Algorithm::ES384 => Self::ES384, - Algorithm::EdDSA => Self::EdDSA, - } - } -} - -/// Load JWT public key -pub(super) async fn load_jwt_key( - public_key_pem_path: &str, - algorithm: Algorithm, -) -> Result { - let key_pem_bytes = tokio::fs::read(public_key_pem_path).await?; - let key = match algorithm { - Algorithm::RS256 - | Algorithm::RS384 - | Algorithm::RS512 - | Algorithm::PS256 - | Algorithm::PS384 - | Algorithm::PS512 => DecodingKey::from_rsa_pem(&key_pem_bytes)?, - Algorithm::ES256 | Algorithm::ES384 => DecodingKey::from_ec_pem(&key_pem_bytes)?, - Algorithm::EdDSA => DecodingKey::from_ed_pem(&key_pem_bytes)?, - }; - Ok(key) -} - -#[cfg(test)] -mod test { - use super::*; - - use serde_json::json; - - #[test] - fn validates_presence() { - let expected = JwtClaim { - name: "sub".to_string(), - ..Default::default() - }; - let given = json!({ - "exp": 12345, - "sub": "test", - }); - Jwt::validate_claim(&expected, &given).unwrap(); - } - - #[test] - fn validates_expected_value() { - let expected = JwtClaim { - name: "custom.host".to_string(), - values: vec!["tlsn.com".to_string(), "api.tlsn.com".to_string()], - }; - let given = json!({ - "exp": 12345, - "custom": { - "host": "api.tlsn.com", - }, - }); - Jwt::validate_claim(&expected, &given).unwrap(); - } - - #[test] - fn validates_with_unknown_claims() { - let given = json!({ - "exp": 12345, - "sub": "test", - "what": "is_this", - }); - Jwt::validate_claims(&[], &given).unwrap(); - } - - #[test] - fn fails_if_claim_missing() { - let expected = JwtClaim { - name: "sub".to_string(), - ..Default::default() - }; - let given = json!({ - "exp": 12345, - "host": "localhost", - }); - assert_eq!( - Jwt::validate_claim(&expected, &given), - Err(JwtValidationError("missing claim 'sub'".to_string())) - ) - } - - #[test] - fn fails_if_claim_has_unknown_value() { - let expected = JwtClaim { - name: "sub".to_string(), - values: vec!["tlsn_prod".to_string(), "tlsn_test".to_string()], - }; - let given = json!({ - "sub": "tlsn", - }); - assert_eq!( - Jwt::validate_claim(&expected, &given), - Err(JwtValidationError("unexpected value for claim 'sub': expected one of [ 'tlsn_prod', 'tlsn_test' ], received 'tlsn'".to_string())) - ) - } - - #[test] - fn fails_if_claim_has_invalid_value_type() { - let expected = JwtClaim { - name: "sub".to_string(), - ..Default::default() - }; - let given = json!({ - "sub": { "name": "john" } - }); - assert_eq!( - Jwt::validate_claim(&expected, &given), - Err(JwtValidationError( - "unexpected type for claim 'sub': only strings are supported for claim values" - .to_string() - )) - ) - } -} diff --git a/crates/notary/server/src/auth/whitelist.rs b/crates/notary/server/src/auth/whitelist.rs deleted file mode 100644 index 72aed9dc53..0000000000 --- a/crates/notary/server/src/auth/whitelist.rs +++ /dev/null @@ -1,161 +0,0 @@ -use eyre::{eyre, Result}; -use notify::{ - event::ModifyKind, Error, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher, -}; -use serde::{Deserialize, Serialize}; -use std::{ - collections::HashMap, - path::Path, - sync::{Arc, Mutex}, -}; -use tracing::{debug, error, info}; - -use crate::util::parse_csv_file; - -#[derive(Clone)] -pub struct Whitelist { - pub entries: Arc>>, - pub csv_path: String, -} - -/// Structure of each whitelisted record of the API key whitelist for -/// authorization purpose -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "PascalCase")] -pub struct AuthorizationWhitelistRecord { - pub name: String, - pub api_key: String, - pub created_at: String, -} - -/// Convert whitelist data structure from vector to hashmap using api_key as the -/// key to speed up lookup -pub(crate) fn authorization_whitelist_vec_into_hashmap( - authorization_whitelist: Vec, -) -> HashMap { - let mut hashmap = HashMap::new(); - authorization_whitelist.iter().for_each(|record| { - hashmap.insert(record.api_key.clone(), record.to_owned()); - }); - hashmap -} - -/// Load authorization whitelist -pub(super) fn load_authorization_whitelist( - whitelist_csv_path: &str, -) -> Result> { - // Load the csv - let whitelist_csv = parse_csv_file::(whitelist_csv_path) - .map_err(|err| eyre!("Failed to parse authorization whitelist csv: {:?}", err))?; - // Convert the whitelist record into hashmap for faster lookup - let whitelist_hashmap = authorization_whitelist_vec_into_hashmap(whitelist_csv); - Ok(whitelist_hashmap) -} - -// Setup a watcher to detect any changes to authorization whitelist -// When the list file is modified, the watcher thread will reload the whitelist -// The watcher is setup in a separate thread by the notify library which is -// synchronous -pub fn watch_and_reload_authorization_whitelist( - whitelist: &Whitelist, -) -> Result { - let whitelist_csv_path_cloned = whitelist.csv_path.clone(); - let entries = whitelist.entries.clone(); - // Setup watcher by giving it a function that will be triggered when an event is - // detected - let mut watcher = RecommendedWatcher::new( - move |event: Result| { - match event { - Ok(event) => { - // Only reload whitelist if it's an event that modified the file data - if let EventKind::Modify(ModifyKind::Data(_)) = event.kind { - debug!("Authorization whitelist is modified"); - match load_authorization_whitelist(&whitelist_csv_path_cloned) { - Ok(new_authorization_whitelist) => { - *entries.lock().unwrap() = new_authorization_whitelist; - info!("Successfully reloaded authorization whitelist!"); - } - // Ensure that error from reloading doesn't bring the server down - Err(err) => error!("{err}"), - } - } - } - Err(err) => { - error!("Error occured when watcher detected an event: {err}") - } - } - }, - notify::Config::default(), - ) - .map_err(|err| eyre!("Error occured when setting up watcher for hot reload: {err}"))?; - - // Start watcher to listen to any changes on the whitelist file - watcher - .watch(Path::new(&whitelist.csv_path), RecursiveMode::Recursive) - .map_err(|err| eyre!("Error occured when starting up watcher for hot reload: {err}"))?; - - // Need to return the watcher to parent function, else it will be dropped and - // stop listening - Ok(watcher) -} - -#[cfg(test)] -mod test { - use std::{fs::OpenOptions, time::Duration}; - - use csv::WriterBuilder; - - use super::*; - - #[tokio::test] - async fn test_watch_and_reload_authorization_whitelist() { - // Clone fixture auth whitelist for testing - let original_whitelist_csv_path = "../tests-integration/fixture/auth/whitelist.csv"; - let whitelist_csv_path = - "../tests-integration/fixture/auth/whitelist_copied.csv".to_string(); - std::fs::copy(original_whitelist_csv_path, &whitelist_csv_path).unwrap(); - - // Setup watcher - let entries = load_authorization_whitelist(&whitelist_csv_path).expect( - "Authorization whitelist csv from fixture should be able - to be loaded", - ); - let whitelist = Whitelist { - entries: Arc::new(Mutex::new(entries)), - csv_path: whitelist_csv_path.clone(), - }; - let _watcher = watch_and_reload_authorization_whitelist(&whitelist) - .expect("Watcher should be able to be setup successfully"); - - // Sleep to buy a bit of time for hot reload task and watcher thread to run - tokio::time::sleep(Duration::from_millis(50)).await; - - // Write a new record to the whitelist to trigger modify event - let new_record = AuthorizationWhitelistRecord { - name: "unit-test-name".to_string(), - api_key: "unit-test-api-key".to_string(), - created_at: "unit-test-created-at".to_string(), - }; - let file = OpenOptions::new() - .append(true) - .open(&whitelist_csv_path) - .unwrap(); - let mut wtr = WriterBuilder::new() - .has_headers(false) // Set to false to avoid writing header again - .from_writer(file); - wtr.serialize(new_record).unwrap(); - wtr.flush().unwrap(); - - // Sleep to buy a bit of time for updated whitelist to be hot reloaded - tokio::time::sleep(Duration::from_millis(50)).await; - - assert!(whitelist - .entries - .lock() - .unwrap() - .contains_key("unit-test-api-key")); - - // Delete the cloned whitelist - std::fs::remove_file(&whitelist_csv_path).unwrap(); - } -} diff --git a/crates/notary/server/src/cli.rs b/crates/notary/server/src/cli.rs deleted file mode 100644 index 983f587649..0000000000 --- a/crates/notary/server/src/cli.rs +++ /dev/null @@ -1,10 +0,0 @@ -use structopt::StructOpt; - -// Fields loaded from the command line when launching this server. -#[derive(Clone, Debug, StructOpt)] -#[structopt(name = "Notary Server")] -pub struct CliFields { - /// Configuration file location (optional). - #[structopt(long)] - pub config: Option, -} diff --git a/crates/notary/server/src/config.rs b/crates/notary/server/src/config.rs deleted file mode 100644 index 8f14b10007..0000000000 --- a/crates/notary/server/src/config.rs +++ /dev/null @@ -1,245 +0,0 @@ -use config::{Config, Environment}; -use eyre::{eyre, Result}; -use serde::{Deserialize, Serialize}; -use std::path::Path; - -use crate::{parse_config_file, util::prepend_file_path, CliFields}; - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct NotaryServerProperties { - pub host: String, - pub port: u16, - /// Static html response returned from API root endpoint "/". Default html - /// response contains placeholder strings that will be replaced with - /// actual values in server.rs, e.g. {version}, {public_key} - pub html_info: String, - /// The maximum number of concurrent notarization sessions - pub concurrency: usize, - /// Setting for notarization - pub notarization: NotarizationProperties, - /// Setting for TLS connection between prover and notary - pub tls: TLSProperties, - /// Setting for logging - pub log: LogProperties, - /// Setting for authorization - pub auth: AuthorizationProperties, -} - -impl NotaryServerProperties { - pub fn new(cli_fields: &CliFields) -> Result { - // Uses config file if given. - if let Some(config_path) = &cli_fields.config { - let mut config: NotaryServerProperties = parse_config_file(config_path)?; - - // Ensures all relative file paths in the config file are prepended with - // the config file's parent directory, so that server binary can be run from - // anywhere. - let parent_dir = Path::new(config_path) - .parent() - .ok_or(eyre!("Failed to get parent directory of config file"))? - .to_str() - .ok_or_else(|| eyre!("Failed to convert path to str"))? - .to_string(); - - // Prepend notarization key path. - if let Some(path) = config.notarization.private_key_path { - config.notarization.private_key_path = Some(prepend_file_path(&path, &parent_dir)?); - } - // Prepend TLS key paths. - if let Some(path) = config.tls.private_key_path { - config.tls.private_key_path = Some(prepend_file_path(&path, &parent_dir)?); - } - if let Some(path) = config.tls.certificate_path { - config.tls.certificate_path = Some(prepend_file_path(&path, &parent_dir)?); - } - // Prepend auth file path. - if let Some(mode) = config.auth.mode { - config.auth.mode = Some(match mode { - AuthorizationModeProperties::Jwt(JwtAuthorizationProperties { - algorithm, - public_key_path, - claims, - }) => AuthorizationModeProperties::Jwt(JwtAuthorizationProperties { - algorithm, - public_key_path: prepend_file_path(&public_key_path, &parent_dir)?, - claims, - }), - AuthorizationModeProperties::Whitelist(path) => { - AuthorizationModeProperties::Whitelist(prepend_file_path( - &path, - &parent_dir, - )?) - } - }); - } - - Ok(config) - } else { - let default_config = Config::try_from(&NotaryServerProperties::default())?; - - let config = Config::builder() - .add_source(default_config) - // Add in settings from environment variables (with a prefix of NS and '_' as - // separator). - .add_source( - Environment::with_prefix("NS") - .try_parsing(true) - .prefix_separator("_") - .separator("__"), - ) - .build()? - .try_deserialize()?; - - Ok(config) - } - } -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct NotarizationProperties { - /// Global limit for maximum number of bytes that can be sent - pub max_sent_data: usize, - /// Global limit for maximum number of bytes that can be received - pub max_recv_data: usize, - /// Number of seconds before notarization timeouts to prevent unreleased - /// memory - pub timeout: u64, - /// File path of private key (in PEM format) used to sign the notarization - pub private_key_path: Option, - /// Signature algorithm used to generate a random private key when - /// private_key_path is not set - pub signature_algorithm: String, - /// Flag to allow any custom extensions from the prover. - pub allow_extensions: bool, -} - -#[derive(Clone, Debug, Serialize, Deserialize, Default)] -pub struct TLSProperties { - /// Flag to turn on/off TLS between prover and notary — should always be - /// turned on unless either - /// (1) TLS is handled by external setup e.g. reverse proxy, cloud; or - /// (2) For local testing - pub enabled: bool, - /// File path of TLS private key (in PEM format) - pub private_key_path: Option, - /// File path of TLS cert (in PEM format) - pub certificate_path: Option, -} - -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] -#[serde(rename_all = "UPPERCASE")] -pub enum LogFormat { - Compact, - Json, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct LogProperties { - /// Log verbosity level of the default filtering logic, which is - /// notary_server=,tlsn_verifier=,mpc_tls= - /// Must be either of - pub level: String, - /// Custom filtering logic, refer to the syntax here https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax - /// This will override the default filtering logic above - pub filter: Option, - /// Log format. Available options are "COMPACT" and "JSON" - pub format: LogFormat, -} - -#[derive(Clone, Debug, Serialize, Deserialize, Default)] -pub struct AuthorizationProperties { - /// Flag to turn on or off auth middleware - pub enabled: bool, - /// Authorization mode to use: JWT or Whitelist - #[serde(flatten)] - pub mode: Option, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub enum AuthorizationModeProperties { - /// JWT authorization properties - Jwt(JwtAuthorizationProperties), - /// File path of the API key whitelist (in CSV format) - Whitelist(String), -} - -#[derive(Clone, Debug, Serialize, Deserialize, Default)] -pub struct JwtAuthorizationProperties { - /// Algorithm used for signing the JWT - pub algorithm: String, - /// File path to JWT public key (in PEM format) for verifying token - /// signatures - pub public_key_path: String, - /// Optional set of required JWT claims - #[serde(default)] - pub claims: Vec, -} - -#[derive(Clone, Debug, Serialize, Deserialize, Default)] -pub struct JwtClaim { - /// Name of the claim - pub name: String, - /// Optional set of expected values for the claim - #[serde(default)] - pub values: Vec, -} - -impl Default for NotaryServerProperties { - fn default() -> Self { - Self { - host: "0.0.0.0".to_string(), - port: 7047, - html_info: r#" - - - - - - - - - - - - -

Notary Server {version}!

- - - "#.to_string(), - concurrency: 32, - notarization: Default::default(), - tls: Default::default(), - log: Default::default(), - auth: Default::default(), - } - } -} - -impl Default for NotarizationProperties { - fn default() -> Self { - Self { - max_sent_data: 4096, - max_recv_data: 16384, - timeout: 1800, - private_key_path: None, - signature_algorithm: "secp256k1".to_string(), - allow_extensions: false, - } - } -} - -impl Default for LogProperties { - fn default() -> Self { - Self { - level: "DEBUG".to_string(), - filter: None, - format: LogFormat::Compact, - } - } -} diff --git a/crates/notary/server/src/error.rs b/crates/notary/server/src/error.rs deleted file mode 100644 index 4acce8a99d..0000000000 --- a/crates/notary/server/src/error.rs +++ /dev/null @@ -1,61 +0,0 @@ -use axum::http::StatusCode; -use axum_core::response::{IntoResponse as AxumCoreIntoResponse, Response}; -use eyre::Report; -use std::error::Error; -use tlsn::{ - config::ProtocolConfigValidatorBuilderError, - verifier::{VerifierConfigBuilderError, VerifierError}, -}; - -#[derive(Debug, thiserror::Error)] -pub enum NotaryServerError { - #[error(transparent)] - Unexpected(#[from] Report), - #[error("Failed to connect to prover: {0}")] - Connection(String), - #[error("Error occurred during notarization: {0}")] - Notarization(Box), - #[error("Invalid request from prover: {0}")] - BadProverRequest(String), - #[error("Unauthorized request from prover: {0}")] - UnauthorizedProverRequest(String), -} - -impl From for NotaryServerError { - fn from(error: VerifierError) -> Self { - Self::Notarization(Box::new(error)) - } -} - -impl From for NotaryServerError { - fn from(error: VerifierConfigBuilderError) -> Self { - Self::Notarization(Box::new(error)) - } -} - -impl From for NotaryServerError { - fn from(error: ProtocolConfigValidatorBuilderError) -> Self { - Self::Notarization(Box::new(error)) - } -} - -/// Trait implementation to convert this error into an axum http response -impl AxumCoreIntoResponse for NotaryServerError { - fn into_response(self) -> Response { - match self { - bad_request_error @ NotaryServerError::BadProverRequest(_) => { - (StatusCode::BAD_REQUEST, bad_request_error.to_string()).into_response() - } - unauthorized_request_error @ NotaryServerError::UnauthorizedProverRequest(_) => ( - StatusCode::UNAUTHORIZED, - unauthorized_request_error.to_string(), - ) - .into_response(), - _ => ( - StatusCode::INTERNAL_SERVER_ERROR, - "Something wrong happened.", - ) - .into_response(), - } - } -} diff --git a/crates/notary/server/src/lib.rs b/crates/notary/server/src/lib.rs deleted file mode 100644 index a57d9031f5..0000000000 --- a/crates/notary/server/src/lib.rs +++ /dev/null @@ -1,23 +0,0 @@ -mod auth; -mod cli; -mod config; -mod error; -mod middleware; -mod server; -mod server_tracing; -mod service; -mod signing; -#[cfg(feature = "tee_quote")] -mod tee; -mod types; -mod util; - -pub use cli::CliFields; -pub use config::{ - AuthorizationModeProperties, AuthorizationProperties, JwtAuthorizationProperties, JwtClaim, - LogProperties, NotarizationProperties, NotaryServerProperties, TLSProperties, -}; -pub use error::NotaryServerError; -pub use server::{read_pem_file, run_server}; -pub use server_tracing::init_tracing; -pub use util::parse_config_file; diff --git a/crates/notary/server/src/main.rs b/crates/notary/server/src/main.rs deleted file mode 100644 index 0a301ec2da..0000000000 --- a/crates/notary/server/src/main.rs +++ /dev/null @@ -1,30 +0,0 @@ -use eyre::{eyre, Result}; -use notary_server::{ - init_tracing, run_server, CliFields, NotaryServerError, NotaryServerProperties, -}; -use structopt::StructOpt; -use tracing::debug; - -#[tokio::main] -async fn main() -> Result<(), NotaryServerError> { - // Load command line arguments - let cli_fields: CliFields = CliFields::from_args(); - - let config = NotaryServerProperties::new(&cli_fields) - .map_err(|err| eyre!("Failed to load config: {}", err))?; - - // Set up tracing for logging - init_tracing(&config).map_err(|err| eyre!("Failed to set up tracing: {err}"))?; - - // debug!("Server config loaded: \n{}", config); - - debug!( - "Server config loaded: \n{}", - serde_yaml::to_string(&config).map_err(|err| eyre!("Failed to print config: {err}"))? - ); - - // Run the server - run_server(&config).await?; - - Ok(()) -} diff --git a/crates/notary/server/src/middleware.rs b/crates/notary/server/src/middleware.rs deleted file mode 100644 index d8d395b8ea..0000000000 --- a/crates/notary/server/src/middleware.rs +++ /dev/null @@ -1,132 +0,0 @@ -use axum::http::{header, request::Parts}; -use axum_core::extract::{FromRef, FromRequestParts}; -use jsonwebtoken::{decode, TokenData, Validation}; -use notary_common::X_API_KEY_HEADER; -use serde_json::Value; -use std::collections::HashMap; -use tracing::{error, trace}; - -use crate::{ - auth::{AuthorizationMode, AuthorizationWhitelistRecord}, - types::NotaryGlobals, - NotaryServerError, -}; - -/// Auth middleware to prevent DOS -pub struct AuthorizationMiddleware; - -impl FromRequestParts for AuthorizationMiddleware -where - NotaryGlobals: FromRef, - S: Send + Sync, -{ - type Rejection = NotaryServerError; - - async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { - let notary_globals = NotaryGlobals::from_ref(state); - let Some(mode) = notary_globals.authorization_mode else { - trace!("Skipping authorization as it's not enabled."); - return Ok(Self); - }; - - match mode { - AuthorizationMode::Whitelist(whitelist) => { - let Some(auth_header) = parts - .headers - .get(X_API_KEY_HEADER) - .and_then(|value| std::str::from_utf8(value.as_bytes()).ok()) - else { - return Err(unauthorized("Missing API key")); - }; - let entries = whitelist.entries.lock().unwrap(); - if api_key_is_valid(auth_header, &entries) { - trace!("Request authorized."); - Ok(Self) - } else { - Err(unauthorized("Invalid API key")) - } - } - AuthorizationMode::Jwt(jwt_config) => { - let Some(auth_header) = parts - .headers - .get(header::AUTHORIZATION) - .and_then(|value| std::str::from_utf8(value.as_bytes()).ok()) - else { - return Err(unauthorized("Missing JWT token")); - }; - let raw_token = auth_header.strip_prefix("Bearer ").ok_or_else(|| { - unauthorized("Invalid Authorization header: expected 'Bearer '") - })?; - let validation = Validation::new(jwt_config.algorithm.into()); - let claims = match decode::(raw_token, &jwt_config.key, &validation) { - Ok(TokenData { claims, .. }) => claims, - Err(err) => { - error!("Decoding JWT failed with error: {err:?}"); - return Err(unauthorized("Invalid JWT token")); - } - }; - if let Err(err) = jwt_config.validate(&claims) { - error!("Validating JWT failed with error: {err:?}"); - return Err(unauthorized("Invalid JWT token")); - }; - trace!("Request authorized."); - Ok(Self) - } - } - } -} - -fn unauthorized(err_msg: impl ToString) -> NotaryServerError { - let err_msg = err_msg.to_string(); - error!(err_msg); - NotaryServerError::UnauthorizedProverRequest(err_msg) -} - -/// Helper function to check if an API key is in whitelist -fn api_key_is_valid( - api_key: &str, - whitelist: &HashMap, -) -> bool { - whitelist.get(api_key).is_some() -} - -#[cfg(test)] -mod test { - use super::{api_key_is_valid, HashMap}; - use crate::auth::{ - whitelist::authorization_whitelist_vec_into_hashmap, AuthorizationWhitelistRecord, - }; - use std::sync::Arc; - - fn get_whitelist_fixture() -> HashMap { - authorization_whitelist_vec_into_hashmap(vec![ - AuthorizationWhitelistRecord { - name: "test-name-0".to_string(), - api_key: "test-api-key-0".to_string(), - created_at: "2023-10-18T07:38:53Z".to_string(), - }, - AuthorizationWhitelistRecord { - name: "test-name-1".to_string(), - api_key: "test-api-key-1".to_string(), - created_at: "2023-10-11T07:38:53Z".to_string(), - }, - AuthorizationWhitelistRecord { - name: "test-name-2".to_string(), - api_key: "test-api-key-2".to_string(), - created_at: "2022-10-11T07:38:53Z".to_string(), - }, - ]) - } - - #[test] - fn test_api_key_is_present() { - let whitelist = get_whitelist_fixture(); - assert!(api_key_is_valid("test-api-key-0", &Arc::new(whitelist))); - } - - #[test] - fn test_api_key_is_absent() { - let whitelist = get_whitelist_fixture(); - assert!(!api_key_is_valid("test-api-keY-0", &Arc::new(whitelist))); - } -} diff --git a/crates/notary/server/src/server.rs b/crates/notary/server/src/server.rs deleted file mode 100644 index 67c6886ea1..0000000000 --- a/crates/notary/server/src/server.rs +++ /dev/null @@ -1,349 +0,0 @@ -use axum::{ - extract::Request, - http::StatusCode, - middleware::from_extractor_with_state, - response::{Html, IntoResponse}, - routing::{get, post}, - Json, Router, -}; -use eyre::{ensure, eyre, Result}; -use futures_util::future::poll_fn; -use hyper::{body::Incoming, server::conn::http1}; -use hyper_util::rt::TokioIo; -use pkcs8::DecodePrivateKey; -use rustls::{Certificate, PrivateKey, ServerConfig}; -use std::{ - fs::File as StdFile, - io::BufReader, - net::{IpAddr, SocketAddr}, - pin::Pin, - sync::Arc, -}; -use tlsn::attestation::CryptoProvider; -use tokio::{fs::File, io::AsyncReadExt, net::TcpListener}; -use tokio_rustls::{rustls, TlsAcceptor}; -use tower_http::cors::CorsLayer; -use tower_service::Service; -use tracing::{debug, error, info, warn}; -use zeroize::Zeroize; - -use crate::{ - auth::{load_authorization_mode, watch_and_reload_authorization_whitelist, AuthorizationMode}, - config::{NotarizationProperties, NotaryServerProperties}, - error::NotaryServerError, - middleware::AuthorizationMiddleware, - service::{initialize, upgrade_protocol}, - signing::AttestationKey, - types::{InfoResponse, NotaryGlobals}, -}; - -#[cfg(feature = "tee_quote")] -use crate::tee::quote; - -use tokio::sync::Semaphore; - -/// Start a TCP server (with or without TLS) to accept notarization request for -/// both TCP and WebSocket clients -#[tracing::instrument(skip(config))] -pub async fn run_server(config: &NotaryServerProperties) -> Result<(), NotaryServerError> { - let attestation_key = get_attestation_key(&config.notarization).await?; - let verifying_key_pem = attestation_key - .verifying_key_pem() - .map_err(|err| eyre!("Failed to get verifying key in PEM format: {err}"))?; - - #[cfg(feature = "tee_quote")] - let verifying_key_bytes = attestation_key.verifying_key_bytes(); - - let crypto_provider = build_crypto_provider(attestation_key); - - // Build TLS acceptor if it is turned on - let tls_acceptor = if !config.tls.enabled { - debug!("Skipping TLS setup as it is turned off."); - None - } else { - let private_key_pem_path = config - .tls - .private_key_path - .as_deref() - .ok_or_else(|| eyre!("TLS is enabled but private key PEM path is not set"))?; - let certificate_pem_path = config - .tls - .certificate_path - .as_deref() - .ok_or_else(|| eyre!("TLS is enabled but certificate PEM path is not set"))?; - - let (tls_private_key, tls_certificates) = - load_tls_key_and_cert(private_key_pem_path, certificate_pem_path).await?; - - let mut server_config = ServerConfig::builder() - .with_safe_defaults() - .with_no_client_auth() - .with_single_cert(tls_certificates, tls_private_key) - .map_err(|err| eyre!("Failed to instantiate notary server tls config: {err}"))?; - - // Set the http protocols we support - server_config.alpn_protocols = vec![b"http/1.1".to_vec()]; - let tls_config = Arc::new(server_config); - Some(TlsAcceptor::from(tls_config)) - }; - - // Set up authorization if it is turned on - let authorization_mode = load_authorization_mode(config).await?; - // Enable hot reload if authorization whitelist is available - let watcher = authorization_mode - .as_ref() - .and_then(AuthorizationMode::as_whitelist) - .map(watch_and_reload_authorization_whitelist) - .transpose()?; - if watcher.is_some() { - debug!("Successfully setup watcher for hot reload of authorization whitelist!"); - } - - let notary_address = SocketAddr::new( - IpAddr::V4(config.host.parse().map_err(|err| { - eyre!("Failed to parse notary host address from server config: {err}") - })?), - config.port, - ); - let mut listener = TcpListener::bind(notary_address) - .await - .map_err(|err| eyre!("Failed to bind server address to tcp listener: {err}"))?; - - info!("Listening for TCP traffic at {}", notary_address); - - let protocol = Arc::new(http1::Builder::new()); - let notary_globals = NotaryGlobals::new( - Arc::new(crypto_provider), - config.notarization.clone(), - authorization_mode, - Arc::new(Semaphore::new(config.concurrency)), - ); - - // Parameters needed for the info endpoint - let version = env!("CARGO_PKG_VERSION").to_string(); - let git_commit_hash = env!("GIT_COMMIT_HASH").to_string(); - - // Parameters needed for the root / endpoint - let html_string = config.html_info.clone(); - let html_info = Html( - html_string - .replace("{version}", &version) - .replace("{git_commit_hash}", &git_commit_hash) - .replace("{public_key}", &verifying_key_pem), - ); - - let router = Router::new() - .route( - "/", - get(|| async move { (StatusCode::OK, html_info).into_response() }), - ) - .route( - "/healthcheck", - get(|| async move { (StatusCode::OK, "Ok").into_response() }), - ) - .route( - "/info", - get(|| async move { - ( - StatusCode::OK, - Json(InfoResponse { - version, - public_key: verifying_key_pem, - git_commit_hash, - #[cfg(feature = "tee_quote")] - quote: quote(verifying_key_bytes).await, - }), - ) - .into_response() - }), - ) - .route("/session", post(initialize)) - // Not applying auth middleware to /notarize endpoint for now as we can rely on our - // short-lived session id generated from /session endpoint, as it is not possible - // to use header for API key for websocket /notarize endpoint due to browser restriction - // ref: https://stackoverflow.com/a/4361358; And putting it in url query param - // seems to be more insecured: https://stackoverflow.com/questions/5517281/place-api-key-in-headers-or-url - .route_layer(from_extractor_with_state::< - AuthorizationMiddleware, - NotaryGlobals, - >(notary_globals.clone())) - .route("/notarize", get(upgrade_protocol)) - .layer(CorsLayer::permissive()) - .with_state(notary_globals); - - loop { - // Poll and await for any incoming connection, ensure that all operations inside - // are infallible to prevent bringing down the server - let stream = match poll_fn(|cx| Pin::new(&mut listener).poll_accept(cx)).await { - Ok((stream, _)) => stream, - Err(err) => { - error!("{}", NotaryServerError::Connection(err.to_string())); - continue; - } - }; - // Setting TCP_NODELAY will improve notary latency. - let _ = stream.set_nodelay(true).map_err(|_| { - info!("An error occured when setting TCP_NODELAY. This will result in higher protocol latency."); - }); - - debug!("Received a prover's TCP connection"); - - let tower_service = router.clone(); - let tls_acceptor = tls_acceptor.clone(); - let protocol = protocol.clone(); - - // Spawn a new async task to handle the new connection - tokio::spawn(async move { - // When TLS is enabled - if let Some(acceptor) = tls_acceptor { - match acceptor.accept(stream).await { - Ok(stream) => { - info!("Accepted prover's TLS-secured TCP connection"); - // Reference: https://github.com/tokio-rs/axum/blob/5201798d4e4d4759c208ef83e30ce85820c07baa/examples/low-level-rustls/src/main.rs#L67-L80 - let io = TokioIo::new(stream); - let hyper_service = - hyper::service::service_fn(move |request: Request| { - tower_service.clone().call(request) - }); - // Serve different requests using the same hyper protocol and axum router - let _ = protocol - .serve_connection(io, hyper_service) - // use with_upgrades to upgrade connection to websocket for websocket - // clients and to extract tcp connection for - // tcp clients - .with_upgrades() - .await; - } - - Err(err) => { - error!("{}", NotaryServerError::Connection(err.to_string())); - - if let Some(rustls::Error::InvalidMessage( - rustls::InvalidMessage::InvalidContentType, - )) = err - .get_ref() - .and_then(|inner| inner.downcast_ref::()) - { - error!("Perhaps the client is connecting without TLS"); - } - } - } - } else { - // When TLS is disabled - info!("Accepted prover's TCP connection",); - // Reference: https://github.com/tokio-rs/axum/blob/5201798d4e4d4759c208ef83e30ce85820c07baa/examples/low-level-rustls/src/main.rs#L67-L80 - let io = TokioIo::new(stream); - let hyper_service = - hyper::service::service_fn(move |request: Request| { - tower_service.clone().call(request) - }); - // Serve different requests using the same hyper protocol and axum router - let _ = protocol - .serve_connection(io, hyper_service) - // use with_upgrades to upgrade connection to websocket for websocket clients - // and to extract tcp connection for tcp clients - .with_upgrades() - .await; - } - }); - } -} - -fn build_crypto_provider(attestation_key: AttestationKey) -> CryptoProvider { - let mut provider = CryptoProvider::default(); - provider.signer.set_signer(attestation_key.into_signer()); - provider -} - -/// Get notary signing key for attestations. -/// Generate a random key if user does not provide a static key. -async fn get_attestation_key(config: &NotarizationProperties) -> Result { - let key = if let Some(private_key_path) = &config.private_key_path { - debug!("Loading notary server's signing key"); - - let mut file = File::open(private_key_path).await?; - let mut pem = String::new(); - file.read_to_string(&mut pem) - .await - .map_err(|_| eyre!("pem file does not contain valid UTF-8"))?; - - let key = AttestationKey::from_pkcs8_pem(&pem) - .map_err(|err| eyre!("Failed to load notary signing key for notarization: {err}"))?; - - pem.zeroize(); - - key - } else { - warn!( - "⚠️ Using a random, ephemeral signing key because `notarization.private_key_path` is not set." - ); - AttestationKey::random(&config.signature_algorithm)? - }; - - Ok(key) -} - -/// Read a PEM-formatted file and return its buffer reader -pub async fn read_pem_file(file_path: &str) -> Result> { - let key_file = File::open(file_path).await?.into_std().await; - Ok(BufReader::new(key_file)) -} - -/// Load notary tls private key and cert from static files -async fn load_tls_key_and_cert( - private_key_pem_path: &str, - certificate_pem_path: &str, -) -> Result<(PrivateKey, Vec)> { - debug!("Loading notary server's tls private key and certificate"); - - let mut private_key_file_reader = read_pem_file(private_key_pem_path).await?; - let mut private_keys = rustls_pemfile::pkcs8_private_keys(&mut private_key_file_reader)?; - ensure!( - private_keys.len() == 1, - "More than 1 key found in the tls private key pem file" - ); - let private_key = PrivateKey(private_keys.remove(0)); - - let mut certificate_file_reader = read_pem_file(certificate_pem_path).await?; - let certificates = rustls_pemfile::certs(&mut certificate_file_reader)? - .into_iter() - .map(Certificate) - .collect(); - - debug!("Successfully loaded notary server's tls private key and certificate!"); - Ok((private_key, certificates)) -} - -#[cfg(test)] -mod test { - use super::*; - - #[tokio::test] - async fn test_load_tls_key_and_cert() { - let private_key_pem_path = "../tests-integration/fixture/tls/notary.key"; - let certificate_pem_path = "../tests-integration/fixture/tls/notary.crt"; - let result: Result<(PrivateKey, Vec)> = - load_tls_key_and_cert(private_key_pem_path, certificate_pem_path).await; - assert!(result.is_ok(), "Could not load tls private key and cert"); - } - - #[tokio::test] - async fn test_load_attestation_key() { - let config = NotarizationProperties { - private_key_path: Some("../tests-integration/fixture/notary/notary.key".to_string()), - ..Default::default() - }; - let result = get_attestation_key(&config).await; - assert!(result.is_ok(), "Could not load attestation key"); - } - - #[tokio::test] - async fn test_generate_attestation_key() { - let config = NotarizationProperties { - private_key_path: None, - ..Default::default() - }; - let result = get_attestation_key(&config).await; - assert!(result.is_ok(), "Could not generate attestation key"); - } -} diff --git a/crates/notary/server/src/server_tracing.rs b/crates/notary/server/src/server_tracing.rs deleted file mode 100644 index 424928e9c3..0000000000 --- a/crates/notary/server/src/server_tracing.rs +++ /dev/null @@ -1,41 +0,0 @@ -use eyre::Result; -use std::str::FromStr; -use tracing::{Level, Subscriber}; -use tracing_subscriber::{ - fmt, layer::SubscriberExt, registry::LookupSpan, util::SubscriberInitExt, EnvFilter, Layer, - Registry, -}; - -use crate::config::{LogFormat, NotaryServerProperties}; - -fn format_layer(format: LogFormat) -> Box + Send + Sync> -where - S: Subscriber + for<'a> LookupSpan<'a>, -{ - let f = fmt::layer().with_thread_ids(true).with_thread_names(true); - match format { - LogFormat::Compact => f.compact().boxed(), - LogFormat::Json => f.json().boxed(), - } -} - -pub fn init_tracing(config: &NotaryServerProperties) -> Result<()> { - // Retrieve log filtering logic from config - let directives = match &config.log.filter { - // Use custom filter that is provided by user - Some(filter) => filter.clone(), - // Use the default filter when only verbosity level is provided - None => { - let level = Level::from_str(&config.log.level)?; - format!("notary_server={level},tlsn_verifier={level},mpc_tls={level}") - } - }; - let filter_layer = EnvFilter::builder().parse(directives)?; - - Registry::default() - .with(filter_layer) - .with(format_layer(config.log.format)) - .try_init()?; - - Ok(()) -} diff --git a/crates/notary/server/src/service.rs b/crates/notary/server/src/service.rs deleted file mode 100644 index bf2cab4670..0000000000 --- a/crates/notary/server/src/service.rs +++ /dev/null @@ -1,240 +0,0 @@ -pub mod axum_websocket; -pub mod tcp; -pub mod websocket; - -use axum::{ - body::Body, - extract::{rejection::JsonRejection, FromRequestParts, Query, State}, - http::{header, request::Parts, StatusCode}, - response::{IntoResponse, Json, Response}, -}; -use axum_macros::debug_handler; -use eyre::eyre; -use notary_common::{NotarizationSessionRequest, NotarizationSessionResponse}; -use std::time::Duration; -use tlsn::{ - attestation::AttestationConfig, - config::ProtocolConfigValidator, - verifier::{Verifier, VerifierConfig}, -}; -use tokio::{ - io::{AsyncRead, AsyncWrite}, - time::timeout, -}; -use tokio_util::compat::TokioAsyncReadCompatExt; -use tracing::{debug, error, info, trace}; -use uuid::Uuid; - -use crate::{ - error::NotaryServerError, - service::{ - axum_websocket::{header_eq, WebSocketUpgrade}, - tcp::{tcp_notarize, TcpUpgrade}, - websocket::websocket_notarize, - }, - types::{NotarizationRequestQuery, NotaryGlobals}, -}; - -/// A wrapper enum to facilitate extracting TCP connection for either WebSocket -/// or TCP clients, so that we can use a single endpoint and handler for -/// notarization for both types of clients -pub enum ProtocolUpgrade { - Tcp(TcpUpgrade), - Ws(WebSocketUpgrade), -} - -impl FromRequestParts for ProtocolUpgrade -where - S: Send + Sync, -{ - type Rejection = NotaryServerError; - - async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { - // Extract tcp connection for websocket client - if header_eq(&parts.headers, header::UPGRADE, "websocket") { - let extractor = WebSocketUpgrade::from_request_parts(parts, state) - .await - .map_err(|err| NotaryServerError::BadProverRequest(err.to_string()))?; - Ok(Self::Ws(extractor)) - // Extract tcp connection for tcp client - } else if header_eq(&parts.headers, header::UPGRADE, "tcp") { - let extractor = TcpUpgrade::from_request_parts(parts, state) - .await - .map_err(|err| NotaryServerError::BadProverRequest(err.to_string()))?; - Ok(Self::Tcp(extractor)) - } else { - Err(NotaryServerError::BadProverRequest( - "Upgrade header is not set for client".to_string(), - )) - } - } -} - -/// Handler to upgrade protocol from http to either websocket or underlying tcp -/// depending on the type of client the session_id parameter is also extracted -/// here to fetch the configuration parameters that have been submitted in the -/// previous request to /session made by the same client -pub async fn upgrade_protocol( - protocol_upgrade: ProtocolUpgrade, - State(notary_globals): State, - Query(params): Query, -) -> Response { - let permit = if let Ok(permit) = notary_globals.semaphore.clone().try_acquire_owned() { - permit - } else { - // TODO: estimate the time more precisely to avoid unnecessary retries. - return Response::builder() - .status(StatusCode::SERVICE_UNAVAILABLE) - .header("Retry-After", 5) - .body(Body::default()) - .expect("Builder should not fail"); - }; - - info!("Received upgrade protocol request"); - let session_id = params.session_id; - // Check if session_id exists in the store, this also removes session_id from - // the store as each session_id can only be used once - if notary_globals - .store - .lock() - .unwrap() - .remove(&session_id) - .is_none() - { - let err_msg = format!("Session id {session_id} does not exist"); - error!(err_msg); - return NotaryServerError::BadProverRequest(err_msg).into_response(); - }; - // This completes the HTTP Upgrade request and returns a successful response to - // the client, meanwhile initiating the websocket or tcp connection - match protocol_upgrade { - ProtocolUpgrade::Ws(ws) => ws.on_upgrade(move |socket| async move { - websocket_notarize(socket, notary_globals, session_id).await; - drop(permit); - }), - ProtocolUpgrade::Tcp(tcp) => tcp.on_upgrade(move |stream| async move { - tcp_notarize(stream, notary_globals, session_id).await; - drop(permit); - }), - } -} - -/// Handler to initialize and configure notarization for both TCP and WebSocket -/// clients -#[debug_handler(state = NotaryGlobals)] -pub async fn initialize( - State(notary_globals): State, - payload: Result, JsonRejection>, -) -> impl IntoResponse { - info!( - ?payload, - "Received request for initializing a notarization session" - ); - - // Parse the body payload - let payload = match payload { - Ok(payload) => payload, - Err(err) => { - error!("Malformed payload submitted for initializing notarization: {err}"); - return NotaryServerError::BadProverRequest(err.to_string()).into_response(); - } - }; - - // Ensure that the max_sent_data, max_recv_data submitted is not larger than the - // global max limits configured in notary server - if payload.max_sent_data.is_some() || payload.max_recv_data.is_some() { - if payload.max_sent_data.unwrap_or_default() - > notary_globals.notarization_config.max_sent_data - { - error!( - "Max sent data requested {:?} exceeds the global maximum threshold {:?}", - payload.max_sent_data.unwrap_or_default(), - notary_globals.notarization_config.max_sent_data - ); - return NotaryServerError::BadProverRequest( - "Max sent data requested exceeds the global maximum threshold".to_string(), - ) - .into_response(); - } - if payload.max_recv_data.unwrap_or_default() - > notary_globals.notarization_config.max_recv_data - { - error!( - "Max recv data requested {:?} exceeds the global maximum threshold {:?}", - payload.max_recv_data.unwrap_or_default(), - notary_globals.notarization_config.max_recv_data - ); - return NotaryServerError::BadProverRequest( - "Max recv data requested exceeds the global maximum threshold".to_string(), - ) - .into_response(); - } - } - - let prover_session_id = Uuid::new_v4().to_string(); - - // Store the configuration data in a temporary store - notary_globals - .store - .lock() - .unwrap() - .insert(prover_session_id.clone(), ()); - - trace!("Latest store state: {:?}", notary_globals.store); - - // Return the session id in the response to the client - ( - StatusCode::OK, - Json(NotarizationSessionResponse { - session_id: prover_session_id, - }), - ) - .into_response() -} - -/// Run the notarization -pub async fn notary_service( - socket: T, - notary_globals: NotaryGlobals, - session_id: &str, -) -> Result<(), NotaryServerError> { - debug!(?session_id, "Starting notarization..."); - - let crypto_provider = notary_globals.crypto_provider.clone(); - - let mut att_config_builder = AttestationConfig::builder(); - att_config_builder - .supported_signature_algs(Vec::from_iter(crypto_provider.signer.supported_algs())); - - // If enabled, accepts any custom extensions from the prover. - if notary_globals.notarization_config.allow_extensions { - att_config_builder.extension_validator(|_| Ok(())); - } - - let att_config = att_config_builder - .build() - .map_err(|err| NotaryServerError::Notarization(Box::new(err)))?; - - let config = VerifierConfig::builder() - .protocol_config_validator( - ProtocolConfigValidator::builder() - .max_sent_data(notary_globals.notarization_config.max_sent_data) - .max_recv_data(notary_globals.notarization_config.max_recv_data) - .build()?, - ) - .build()?; - - #[allow(deprecated)] - timeout( - Duration::from_secs(notary_globals.notarization_config.timeout), - Verifier::new(config).notarize_with_provider( - socket.compat(), - &att_config, - &crypto_provider, - ), - ) - .await - .map_err(|_| eyre!("Timeout reached before notarization completes"))??; - - Ok(()) -} diff --git a/crates/notary/server/src/service/axum_websocket.rs b/crates/notary/server/src/service/axum_websocket.rs deleted file mode 100644 index 53d1c3aa93..0000000000 --- a/crates/notary/server/src/service/axum_websocket.rs +++ /dev/null @@ -1,1172 +0,0 @@ -//! The following code is adapted from https://github.com/tokio-rs/axum/blob/axum-v0.8.0/axum/src/extract/ws.rs -//! where we swapped out tokio_tungstenite (https://docs.rs/tokio-tungstenite/latest/tokio_tungstenite/) -//! with async_tungstenite (https://docs.rs/async-tungstenite/latest/async_tungstenite/) so that we can use -//! ws_stream_tungstenite (https://docs.rs/ws_stream_tungstenite/latest/ws_stream_tungstenite/index.html) -//! to get AsyncRead and AsyncWrite implemented for the WebSocket. Any other -//! modification is commented with the prefix "NOTARY_MODIFICATION:" -//! -//! The code is under the following license: -//! -//! Copyright (c) 2019 Axum Contributors -//! -//! Permission is hereby granted, free of charge, to any -//! person obtaining a copy of this software and associated -//! documentation files (the "Software"), to deal in the -//! Software without restriction, including without -//! limitation the rights to use, copy, modify, merge, -//! publish, distribute, sublicense, and/or sell copies of -//! the Software, and to permit persons to whom the Software -//! is furnished to do so, subject to the following -//! conditions: -//! -//! The above copyright notice and this permission notice -//! shall be included in all copies or substantial portions -//! of the Software. -//! -//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -//! ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -//! TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -//! PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -//! SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -//! CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -//! OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -//! IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -//! DEALINGS IN THE SOFTWARE. -//! -//! -//! Handle WebSocket connections. -//! -//! # Example -//! -//! ``` -//! use axum::{ -//! extract::ws::{WebSocketUpgrade, WebSocket}, -//! routing::any, -//! response::{IntoResponse, Response}, -//! Router, -//! }; -//! -//! let app = Router::new().route("/ws", any(handler)); -//! -//! async fn handler(ws: WebSocketUpgrade) -> Response { -//! ws.on_upgrade(handle_socket) -//! } -//! -//! async fn handle_socket(mut socket: WebSocket) { -//! while let Some(msg) = socket.recv().await { -//! let msg = if let Ok(msg) = msg { -//! msg -//! } else { -//! // client disconnected -//! return; -//! }; -//! -//! if socket.send(msg).await.is_err() { -//! // client disconnected -//! return; -//! } -//! } -//! } -//! # let _: Router = app; -//! ``` -//! -//! # Passing data and/or state to an `on_upgrade` callback -//! -//! ``` -//! use axum::{ -//! extract::{ws::{WebSocketUpgrade, WebSocket}, State}, -//! response::Response, -//! routing::any, -//! Router, -//! }; -//! -//! #[derive(Clone)] -//! struct AppState { -//! // ... -//! } -//! -//! async fn handler(ws: WebSocketUpgrade, State(state): State) -> Response { -//! ws.on_upgrade(|socket| handle_socket(socket, state)) -//! } -//! -//! async fn handle_socket(socket: WebSocket, state: AppState) { -//! // ... -//! } -//! -//! let app = Router::new() -//! .route("/ws", any(handler)) -//! .with_state(AppState { /* ... */ }); -//! # let _: Router = app; -//! ``` -//! -//! # Read and write concurrently -//! -//! If you need to read and write concurrently from a [`WebSocket`] you can use -//! [`StreamExt::split`]: -//! -//! ```rust,no_run -//! use axum::{Error, extract::ws::{WebSocket, Message}}; -//! use futures_util::{sink::SinkExt, stream::{StreamExt, SplitSink, SplitStream}}; -//! -//! async fn handle_socket(mut socket: WebSocket) { -//! let (mut sender, mut receiver) = socket.split(); -//! -//! tokio::spawn(write(sender)); -//! tokio::spawn(read(receiver)); -//! } -//! -//! async fn read(receiver: SplitStream) { -//! // ... -//! } -//! -//! async fn write(sender: SplitSink) { -//! // ... -//! } -//! ``` -//! -//! [`StreamExt::split`]: https://docs.rs/futures/0.3.17/futures/stream/trait.StreamExt.html#method.split - -#![allow(unused)] // NOTARY_MODIFICATION - -use self::rejection::*; -/// NOTARY_MODIFICATION: async_tungstenite instead of tokio_tungstenite -use async_tungstenite::{ - tokio::TokioAdapter, - tungstenite::{ - self as ts, - protocol::{self, WebSocketConfig}, - }, - WebSocketStream, -}; -/// NOTARY_MODIFICATION: axum -use axum::{body::Bytes, extract::FromRequestParts, response::Response, Error}; -use axum_core::body::Body; -use futures_util::{ - sink::{Sink, SinkExt}, - stream::{Stream, StreamExt}, -}; -use http::{ - header::{self, HeaderMap, HeaderName, HeaderValue}, - request::Parts, - Method, StatusCode, Version, -}; -use hyper_util::rt::TokioIo; -use sha1::{Digest, Sha1}; -use std::{ - borrow::Cow, - future::Future, - pin::Pin, - task::{Context, Poll}, -}; -use tracing::error; - -/// Extractor for establishing WebSocket connections. -/// -/// For HTTP/1.1 requests, this extractor requires the request method to be -/// `GET`; in later versions, `CONNECT` is used instead. -/// To support both, it should be used with [`any`](crate::routing::any). -/// -/// See the [module docs](self) for an example. -/// -/// [`MethodFilter`]: crate::routing::MethodFilter -#[cfg_attr(docsrs, doc(cfg(feature = "ws")))] -pub struct WebSocketUpgrade { - config: WebSocketConfig, - /// The chosen protocol sent in the `Sec-WebSocket-Protocol` header of the - /// response. - protocol: Option, - /// `None` if HTTP/2+ WebSockets are used. - sec_websocket_key: Option, - on_upgrade: hyper::upgrade::OnUpgrade, - on_failed_upgrade: F, - sec_websocket_protocol: Option, -} - -impl std::fmt::Debug for WebSocketUpgrade { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("WebSocketUpgrade") - .field("config", &self.config) - .field("protocol", &self.protocol) - .field("sec_websocket_key", &self.sec_websocket_key) - .field("sec_websocket_protocol", &self.sec_websocket_protocol) - .finish_non_exhaustive() - } -} - -impl WebSocketUpgrade { - /// The target minimum size of the write buffer to reach before writing the - /// data to the underlying stream. - /// - /// The default value is 128 KiB. - /// - /// If set to `0` each message will be eagerly written to the underlying - /// stream. It is often more optimal to allow them to buffer a little, - /// hence the default value. - /// - /// Note: [`flush`](SinkExt::flush) will always fully write the buffer - /// regardless. - pub fn write_buffer_size(mut self, size: usize) -> Self { - self.config.write_buffer_size = size; - self - } - - /// The max size of the write buffer in bytes. Setting this can provide - /// backpressure in the case the write buffer is filling up due to write - /// errors. - /// - /// The default value is unlimited. - /// - /// Note: The write buffer only builds up past - /// [`write_buffer_size`](Self::write_buffer_size) when writes to the - /// underlying stream are failing. So the **write buffer can not fill up - /// if you are not observing write errors even if not flushing**. - /// - /// Note: Should always be at least [`write_buffer_size + 1 - /// message`](Self::write_buffer_size) and probably a little more - /// depending on error handling strategy. - pub fn max_write_buffer_size(mut self, max: usize) -> Self { - self.config.max_write_buffer_size = max; - self - } - - /// Set the maximum message size (defaults to 64 megabytes) - pub fn max_message_size(mut self, max: usize) -> Self { - self.config.max_message_size = Some(max); - self - } - - /// Set the maximum frame size (defaults to 16 megabytes) - pub fn max_frame_size(mut self, max: usize) -> Self { - self.config.max_frame_size = Some(max); - self - } - - /// Allow server to accept unmasked frames (defaults to false) - pub fn accept_unmasked_frames(mut self, accept: bool) -> Self { - self.config.accept_unmasked_frames = accept; - self - } - - /// Set the known protocols. - /// - /// If the protocol name specified by `Sec-WebSocket-Protocol` header - /// to match any of them, the upgrade response will include - /// `Sec-WebSocket-Protocol` header and return the protocol name. - /// - /// The protocols should be listed in decreasing order of preference: if the - /// client offers multiple protocols that the server could support, the - /// server will pick the first one in this list. - /// - /// # Examples - /// - /// ``` - /// use axum::{ - /// extract::ws::{WebSocketUpgrade, WebSocket}, - /// routing::any, - /// response::{IntoResponse, Response}, - /// Router, - /// }; - /// - /// let app = Router::new().route("/ws", any(handler)); - /// - /// async fn handler(ws: WebSocketUpgrade) -> Response { - /// ws.protocols(["graphql-ws", "graphql-transport-ws"]) - /// .on_upgrade(|socket| async { - /// // ... - /// }) - /// } - /// # let _: Router = app; - /// ``` - pub fn protocols(mut self, protocols: I) -> Self - where - I: IntoIterator, - I::Item: Into>, - { - if let Some(req_protocols) = self - .sec_websocket_protocol - .as_ref() - .and_then(|p| p.to_str().ok()) - { - self.protocol = protocols - .into_iter() - // FIXME: This will often allocate a new `String` and so is less efficient than it - // could be. But that can't be fixed without breaking changes to the public API. - .map(Into::into) - .find(|protocol| { - req_protocols - .split(',') - .any(|req_protocol| req_protocol.trim() == protocol) - }) - .map(|protocol| match protocol { - Cow::Owned(s) => HeaderValue::from_str(&s).unwrap(), - Cow::Borrowed(s) => HeaderValue::from_static(s), - }); - } - - self - } - - /// Provide a callback to call if upgrading the connection fails. - /// - /// The connection upgrade is performed in a background task. If that fails - /// this callback will be called. - /// - /// By default any errors will be silently ignored. - /// - /// # Example - /// - /// ``` - /// use axum::{ - /// extract::{WebSocketUpgrade}, - /// response::Response, - /// }; - /// - /// async fn handler(ws: WebSocketUpgrade) -> Response { - /// ws.on_failed_upgrade(|error| { - /// report_error(error); - /// }) - /// .on_upgrade(|socket| async { /* ... */ }) - /// } - /// # - /// # fn report_error(_: axum::Error) {} - /// ``` - pub fn on_failed_upgrade(self, callback: C) -> WebSocketUpgrade - where - C: OnFailedUpgrade, - { - WebSocketUpgrade { - config: self.config, - protocol: self.protocol, - sec_websocket_key: self.sec_websocket_key, - on_upgrade: self.on_upgrade, - on_failed_upgrade: callback, - sec_websocket_protocol: self.sec_websocket_protocol, - } - } - - /// Finalize upgrading the connection and call the provided callback with - /// the stream. - #[must_use = "to set up the WebSocket connection, this response must be returned"] - pub fn on_upgrade(self, callback: C) -> Response - where - C: FnOnce(WebSocket) -> Fut + Send + 'static, - Fut: Future + Send + 'static, - F: OnFailedUpgrade, - { - let on_upgrade = self.on_upgrade; - let config = self.config; - let on_failed_upgrade = self.on_failed_upgrade; - - let protocol = self.protocol.clone(); - - tokio::spawn(async move { - let upgraded = match on_upgrade.await { - Ok(upgraded) => upgraded, - Err(err) => { - // NOTARY_MODIFICATION: log error - error!("Something wrong with on_upgrade: {:?}", err); - on_failed_upgrade.call(Error::new(err)); - return; - } - }; - let upgraded = TokioIo::new(upgraded); - - let socket = WebSocketStream::from_raw_socket( - // NOTARY_MODIFICATION: Need to use TokioAdapter to wrap Upgraded which doesn't - // implement futures crate's AsyncRead and AsyncWrite - TokioAdapter::new(upgraded), - protocol::Role::Server, - Some(config), - ) - .await; - let socket = WebSocket { - inner: socket, - protocol, - }; - callback(socket).await; - }); - - if let Some(sec_websocket_key) = &self.sec_websocket_key { - // If `sec_websocket_key` was `Some`, we are using HTTP/1.1. - - #[allow(clippy::declare_interior_mutable_const)] - const UPGRADE: HeaderValue = HeaderValue::from_static("upgrade"); - #[allow(clippy::declare_interior_mutable_const)] - const WEBSOCKET: HeaderValue = HeaderValue::from_static("websocket"); - - let mut builder = Response::builder() - .status(StatusCode::SWITCHING_PROTOCOLS) - .header(header::CONNECTION, UPGRADE) - .header(header::UPGRADE, WEBSOCKET) - .header( - header::SEC_WEBSOCKET_ACCEPT, - sign(sec_websocket_key.as_bytes()), - ); - - if let Some(protocol) = self.protocol { - builder = builder.header(header::SEC_WEBSOCKET_PROTOCOL, protocol); - } - - builder.body(Body::empty()).unwrap() - } else { - // Otherwise, we are HTTP/2+. As established in RFC 9113 section 8.5, we just - // respond with a 2XX with an empty body: - // . - Response::new(Body::empty()) - } - } -} - -/// What to do when a connection upgrade fails. -/// -/// See [`WebSocketUpgrade::on_failed_upgrade`] for more details. -pub trait OnFailedUpgrade: Send + 'static { - /// Call the callback. - fn call(self, error: Error); -} - -impl OnFailedUpgrade for F -where - F: FnOnce(Error) + Send + 'static, -{ - fn call(self, error: Error) { - self(error) - } -} - -/// The default `OnFailedUpgrade` used by `WebSocketUpgrade`. -/// -/// It simply ignores the error. -#[non_exhaustive] -#[derive(Debug)] -pub struct DefaultOnFailedUpgrade; - -impl OnFailedUpgrade for DefaultOnFailedUpgrade { - #[inline] - fn call(self, _error: Error) {} -} - -impl FromRequestParts for WebSocketUpgrade -where - S: Send + Sync, -{ - type Rejection = WebSocketUpgradeRejection; - - async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { - let sec_websocket_key = if parts.version <= Version::HTTP_11 { - if parts.method != Method::GET { - return Err(MethodNotGet.into()); - } - - if !header_contains(&parts.headers, header::CONNECTION, "upgrade") { - return Err(InvalidConnectionHeader.into()); - } - - if !header_eq(&parts.headers, header::UPGRADE, "websocket") { - return Err(InvalidUpgradeHeader.into()); - } - - Some( - parts - .headers - .get(header::SEC_WEBSOCKET_KEY) - .ok_or(WebSocketKeyHeaderMissing)? - .clone(), - ) - } else { - if parts.method != Method::CONNECT { - return Err(MethodNotConnect.into()); - } - - // NOTARY_MODIFICATION: ignore http2 feature - - // if this feature flag is disabled, we won’t be receiving an HTTP/2 request to - // begin with. - // #[cfg(feature = "http2")] - // if parts - // .extensions - // .get::() - // .map_or(true, |p| p.as_str() != "websocket") - // { - // return Err(InvalidProtocolPseudoheader.into()); - // } - - None - }; - - if !header_eq(&parts.headers, header::SEC_WEBSOCKET_VERSION, "13") { - return Err(InvalidWebSocketVersionHeader.into()); - } - - let on_upgrade = parts - .extensions - .remove::() - .ok_or(ConnectionNotUpgradable)?; - - let sec_websocket_protocol = parts.headers.get(header::SEC_WEBSOCKET_PROTOCOL).cloned(); - - Ok(Self { - config: Default::default(), - protocol: None, - sec_websocket_key, - on_upgrade, - sec_websocket_protocol, - on_failed_upgrade: DefaultOnFailedUpgrade, - }) - } -} - -/// NOTARY_MODIFICATION: Made this function public to be used in service.rs -pub fn header_eq(headers: &HeaderMap, key: HeaderName, value: &'static str) -> bool { - if let Some(header) = headers.get(&key) { - header.as_bytes().eq_ignore_ascii_case(value.as_bytes()) - } else { - false - } -} - -fn header_contains(headers: &HeaderMap, key: HeaderName, value: &'static str) -> bool { - let header = if let Some(header) = headers.get(&key) { - header - } else { - return false; - }; - - if let Ok(header) = std::str::from_utf8(header.as_bytes()) { - header.to_ascii_lowercase().contains(value) - } else { - false - } -} - -/// A stream of WebSocket messages. -/// -/// See [the module level documentation](self) for more details. -#[derive(Debug)] -pub struct WebSocket { - // NOTARY_MODIFICATION: TokioAdapter - inner: WebSocketStream>>, - protocol: Option, -} - -impl WebSocket { - /// NOTARY_MODIFICATION: Consume `self` and get the inner - /// [`async_tungstenite::WebSocketStream`]. - pub fn into_inner(self) -> WebSocketStream>> { - self.inner - } - - /// Receive another message. - /// - /// Returns `None` if the stream has closed. - pub async fn recv(&mut self) -> Option> { - self.next().await - } - - /// Send a message. - pub async fn send(&mut self, msg: Message) -> Result<(), Error> { - self.inner - .send(msg.into_tungstenite()) - .await - .map_err(Error::new) - } - - /// Return the selected WebSocket subprotocol, if one has been chosen. - pub fn protocol(&self) -> Option<&HeaderValue> { - self.protocol.as_ref() - } -} - -impl Stream for WebSocket { - type Item = Result; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - loop { - match futures_util::ready!(self.inner.poll_next_unpin(cx)) { - Some(Ok(msg)) => { - if let Some(msg) = Message::from_tungstenite(msg) { - return Poll::Ready(Some(Ok(msg))); - } - } - Some(Err(err)) => return Poll::Ready(Some(Err(Error::new(err)))), - None => return Poll::Ready(None), - } - } - } -} - -impl Sink for WebSocket { - type Error = Error; - - fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.inner).poll_ready(cx).map_err(Error::new) - } - - fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> { - Pin::new(&mut self.inner) - .start_send(item.into_tungstenite()) - .map_err(Error::new) - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.inner).poll_flush(cx).map_err(Error::new) - } - - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut self.inner).poll_close(cx).map_err(Error::new) - } -} - -/// UTF-8 wrapper for [Bytes]. -/// -/// An [Utf8Bytes] is always guaranteed to contain valid UTF-8. -/// The following NOTARY_MODIFICATION(s) are required because -/// `async_tungstenite` (v0.28.2) is using an older version of `tungstenite` -/// than `tokio_tungstenite` (v0.26.1). This older version of `tungstenite` -/// (v0.26.0) doesn't have `Utf8Bytes`. -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct Utf8Bytes(axum::extract::ws::Utf8Bytes); // NOTARY_MODIFICATION - -impl Utf8Bytes { - /// Creates from a static str. - #[inline] - pub const fn from_static(str: &'static str) -> Self { - Self(axum::extract::ws::Utf8Bytes::from_static(str)) // NOTARY_MODIFICATION - } - - /// Returns as a string slice. - #[inline] - pub fn as_str(&self) -> &str { - self.0.as_str() - } - - fn into_tungstenite(self) -> axum::extract::ws::Utf8Bytes { - // NOTARY_MODIFICATION - self.0 - } -} - -impl std::ops::Deref for Utf8Bytes { - type Target = str; - - /// ``` - /// /// Example fn that takes a str slice - /// fn a(s: &str) {} - /// - /// let data = axum::extract::ws::Utf8Bytes::from_static("foo123"); - /// - /// // auto-deref as arg - /// a(&data); - /// - /// // deref to str methods - /// assert_eq!(data.len(), 6); - /// ``` - #[inline] - fn deref(&self) -> &Self::Target { - self.as_str() - } -} - -impl std::fmt::Display for Utf8Bytes { - #[inline] - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(self.as_str()) - } -} - -impl TryFrom for Utf8Bytes { - type Error = std::str::Utf8Error; - - #[inline] - fn try_from(bytes: Bytes) -> Result { - Ok(Self(bytes.try_into()?)) - } -} - -impl TryFrom> for Utf8Bytes { - type Error = std::str::Utf8Error; - - #[inline] - fn try_from(v: Vec) -> Result { - Ok(Self(v.try_into()?)) - } -} - -impl From for Utf8Bytes { - #[inline] - fn from(s: String) -> Self { - Self(s.into()) - } -} - -impl From<&str> for Utf8Bytes { - #[inline] - fn from(s: &str) -> Self { - Self(s.into()) - } -} - -impl From<&String> for Utf8Bytes { - #[inline] - fn from(s: &String) -> Self { - Self(s.into()) - } -} - -impl From for Bytes { - #[inline] - fn from(Utf8Bytes(bytes): Utf8Bytes) -> Self { - bytes.into() - } -} - -impl PartialEq for Utf8Bytes -where - for<'a> &'a str: PartialEq, -{ - /// ``` - /// let payload = axum::extract::ws::Utf8Bytes::from_static("foo123"); - /// assert_eq!(payload, "foo123"); - /// assert_eq!(payload, "foo123".to_string()); - /// assert_eq!(payload, &"foo123".to_string()); - /// assert_eq!(payload, std::borrow::Cow::from("foo123")); - /// ``` - #[inline] - fn eq(&self, other: &T) -> bool { - self.as_str() == *other - } -} - -/// Status code used to indicate why an endpoint is closing the WebSocket -/// connection. -pub type CloseCode = u16; - -/// A struct representing the close command. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct CloseFrame { - /// The reason as a code. - pub code: CloseCode, - /// The reason as text string. - pub reason: Utf8Bytes, -} - -/// A WebSocket message. -// -// This code comes from https://github.com/snapview/tungstenite-rs/blob/master/src/protocol/message.rs and is under following license: -// Copyright (c) 2017 Alexey Galakhov -// Copyright (c) 2016 Jason Housley -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum Message { - /// A text WebSocket message - Text(Utf8Bytes), - /// A binary WebSocket message - Binary(Bytes), - /// A ping message with the specified payload - /// - /// The payload here must have a length less than 125 bytes. - /// - /// Ping messages will be automatically responded to by the server, so you - /// do not have to worry about dealing with them yourself. - Ping(Bytes), - /// A pong message with the specified payload - /// - /// The payload here must have a length less than 125 bytes. - /// - /// Pong messages will be automatically sent to the client if a ping message - /// is received, so you do not have to worry about constructing them - /// yourself unless you want to implement a [unidirectional heartbeat](https://tools.ietf.org/html/rfc6455#section-5.5.3). - Pong(Bytes), - /// A close message with the optional close frame. - /// - /// You may "uncleanly" close a WebSocket connection at any time - /// by simply dropping the [`WebSocket`]. - /// However, you may also use the graceful closing protocol, in which - /// 1. peer A sends a close frame, and does not send any further messages; - /// 2. peer B responds with a close frame, and does not send any further - /// messages; - /// 3. peer A processes the remaining messages sent by peer B, before - /// finally - /// 4. both peers close the connection. - /// - /// After sending a close frame, - /// you may still read messages, - /// but attempts to send another message will error. - /// After receiving a close frame, - /// axum will automatically respond with a close frame if necessary - /// (you do not have to deal with this yourself). - /// Since no further messages will be received, - /// you may either do nothing - /// or explicitly drop the connection. - Close(Option), -} - -/// The following NOTARY_MODIFICATION(s) are required because -/// `async_tungstenite` (v0.28.2) is using an older version of `tungstenite` -/// than `tokio_tungstenite` (v0.26.1). -impl Message { - fn into_tungstenite(self) -> ts::Message { - match self { - Self::Text(text) => ts::Message::Text(text.into_tungstenite().to_string()), /* NOTARY_MODIFICATION */ - Self::Binary(binary) => ts::Message::Binary(binary.to_vec()), /* NOTARY_MODIFICATION */ - Self::Ping(ping) => ts::Message::Ping(ping.to_vec()), /* NOTARY_MODIFICATION */ - Self::Pong(pong) => ts::Message::Pong(pong.to_vec()), /* NOTARY_MODIFICATION */ - Self::Close(Some(close)) => ts::Message::Close(Some(ts::protocol::CloseFrame { - code: ts::protocol::frame::coding::CloseCode::from(close.code), - reason: Cow::Owned(close.reason.into_tungstenite().to_string()), /* NOTARY_MODIFICATION */ - })), - Self::Close(None) => ts::Message::Close(None), - } - } - - fn from_tungstenite(message: ts::Message) -> Option { - match message { - ts::Message::Text(text) => Some(Self::Text(Utf8Bytes(text.into()))), /* NOTARY_MODIFICATION */ - ts::Message::Binary(binary) => Some(Self::Binary(binary.into())), /* NOTARY_MODIFICATION */ - ts::Message::Ping(ping) => Some(Self::Ping(ping.into())), /* NOTARY_MODIFICATION */ - ts::Message::Pong(pong) => Some(Self::Pong(pong.into())), /* NOTARY_MODIFICATION */ - ts::Message::Close(Some(close)) => Some(Self::Close(Some(CloseFrame { - code: close.code.into(), - reason: Utf8Bytes(close.reason.to_string().into()), /* NOTARY_MODIFICATION */ - }))), - ts::Message::Close(None) => Some(Self::Close(None)), - // we can ignore `Frame` frames as recommended by the tungstenite maintainers - // https://github.com/snapview/tungstenite-rs/issues/268 - ts::Message::Frame(_) => None, - } - } - - /// Consume the WebSocket and return it as binary data. - pub fn into_data(self) -> Bytes { - match self { - Self::Text(string) => Bytes::from(string), - Self::Binary(data) | Self::Ping(data) | Self::Pong(data) => data, - Self::Close(None) => Bytes::new(), - Self::Close(Some(frame)) => Bytes::from(frame.reason), - } - } - - /// Attempt to consume the WebSocket message and convert it to a Utf8Bytes. - pub fn into_text(self) -> Result { - match self { - Self::Text(string) => Ok(string), - Self::Binary(data) | Self::Ping(data) | Self::Pong(data) => { - Ok(Utf8Bytes::try_from(data).map_err(Error::new)?) - } - Self::Close(None) => Ok(Utf8Bytes::default()), - Self::Close(Some(frame)) => Ok(frame.reason), - } - } - - /// Attempt to get a &str from the WebSocket message, - /// this will try to convert binary data to utf8. - pub fn to_text(&self) -> Result<&str, Error> { - match *self { - Self::Text(ref string) => Ok(string.as_str()), - Self::Binary(ref data) | Self::Ping(ref data) | Self::Pong(ref data) => { - Ok(std::str::from_utf8(data).map_err(Error::new)?) - } - Self::Close(None) => Ok(""), - Self::Close(Some(ref frame)) => Ok(&frame.reason), - } - } - - /// Create a new text WebSocket message from a stringable. - pub fn text(string: S) -> Message - where - S: Into, - { - Message::Text(string.into()) - } - - /// Create a new binary WebSocket message by converting to `Bytes`. - pub fn binary(bin: B) -> Message - where - B: Into, - { - Message::Binary(bin.into()) - } -} - -impl From for Message { - fn from(string: String) -> Self { - Message::Text(string.into()) - } -} - -impl<'s> From<&'s str> for Message { - fn from(string: &'s str) -> Self { - Message::Text(string.into()) - } -} - -impl<'b> From<&'b [u8]> for Message { - fn from(data: &'b [u8]) -> Self { - Message::Binary(Bytes::copy_from_slice(data)) - } -} - -impl From> for Message { - fn from(data: Vec) -> Self { - Message::Binary(data.into()) - } -} - -impl From for Vec { - fn from(msg: Message) -> Self { - msg.into_data().to_vec() - } -} - -fn sign(key: &[u8]) -> HeaderValue { - use base64::engine::Engine as _; - - let mut sha1 = Sha1::default(); - sha1.update(key); - sha1.update(&b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"[..]); - let b64 = Bytes::from(base64::engine::general_purpose::STANDARD.encode(sha1.finalize())); - HeaderValue::from_maybe_shared(b64).expect("base64 is a valid value") -} - -pub mod rejection { - //! WebSocket specific rejections. - - use axum_core::{ - __composite_rejection as composite_rejection, __define_rejection as define_rejection, - }; - - define_rejection! { - #[status = METHOD_NOT_ALLOWED] - #[body = "Request method must be `GET`"] - /// Rejection type for [`WebSocketUpgrade`](super::WebSocketUpgrade). - pub struct MethodNotGet; - } - - define_rejection! { - #[status = METHOD_NOT_ALLOWED] - #[body = "Request method must be `CONNECT`"] - /// Rejection type for [`WebSocketUpgrade`](super::WebSocketUpgrade). - pub struct MethodNotConnect; - } - - define_rejection! { - #[status = BAD_REQUEST] - #[body = "Connection header did not include 'upgrade'"] - /// Rejection type for [`WebSocketUpgrade`](super::WebSocketUpgrade). - pub struct InvalidConnectionHeader; - } - - define_rejection! { - #[status = BAD_REQUEST] - #[body = "`Upgrade` header did not include 'websocket'"] - /// Rejection type for [`WebSocketUpgrade`](super::WebSocketUpgrade). - pub struct InvalidUpgradeHeader; - } - - define_rejection! { - #[status = BAD_REQUEST] - #[body = "`:protocol` pseudo-header did not include 'websocket'"] - /// Rejection type for [`WebSocketUpgrade`](super::WebSocketUpgrade). - pub struct InvalidProtocolPseudoheader; - } - - define_rejection! { - #[status = BAD_REQUEST] - #[body = "`Sec-WebSocket-Version` header did not include '13'"] - /// Rejection type for [`WebSocketUpgrade`](super::WebSocketUpgrade). - pub struct InvalidWebSocketVersionHeader; - } - - define_rejection! { - #[status = BAD_REQUEST] - #[body = "`Sec-WebSocket-Key` header missing"] - /// Rejection type for [`WebSocketUpgrade`](super::WebSocketUpgrade). - pub struct WebSocketKeyHeaderMissing; - } - - define_rejection! { - #[status = UPGRADE_REQUIRED] - #[body = "WebSocket request couldn't be upgraded since no upgrade state was present"] - /// Rejection type for [`WebSocketUpgrade`](super::WebSocketUpgrade). - /// - /// This rejection is returned if the connection cannot be upgraded for example if the - /// request is HTTP/1.0. - /// - /// See [MDN] for more details about connection upgrades. - /// - /// [MDN]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Upgrade - pub struct ConnectionNotUpgradable; - } - - composite_rejection! { - /// Rejection used for [`WebSocketUpgrade`](super::WebSocketUpgrade). - /// - /// Contains one variant for each way the [`WebSocketUpgrade`](super::WebSocketUpgrade) - /// extractor can fail. - pub enum WebSocketUpgradeRejection { - MethodNotGet, - MethodNotConnect, - InvalidConnectionHeader, - InvalidUpgradeHeader, - InvalidProtocolPseudoheader, - InvalidWebSocketVersionHeader, - WebSocketKeyHeaderMissing, - ConnectionNotUpgradable, - } - } -} - -pub mod close_code { - //! Constants for [`CloseCode`]s. - //! - //! [`CloseCode`]: super::CloseCode - - /// Indicates a normal closure, meaning that the purpose for which the - /// connection was established has been fulfilled. - pub const NORMAL: u16 = 1000; - - /// Indicates that an endpoint is "going away", such as a server going down - /// or a browser having navigated away from a page. - pub const AWAY: u16 = 1001; - - /// Indicates that an endpoint is terminating the connection due to a - /// protocol error. - pub const PROTOCOL: u16 = 1002; - - /// Indicates that an endpoint is terminating the connection because it has - /// received a type of data that it cannot accept. - /// - /// For example, an endpoint MAY send this if it understands only text data, - /// but receives a binary message. - pub const UNSUPPORTED: u16 = 1003; - - /// Indicates that no status code was included in a closing frame. - pub const STATUS: u16 = 1005; - - /// Indicates an abnormal closure. - pub const ABNORMAL: u16 = 1006; - - /// Indicates that an endpoint is terminating the connection because it has - /// received data within a message that was not consistent with the type - /// of the message. - /// - /// For example, an endpoint received non-UTF-8 RFC3629 data within a text - /// message. - pub const INVALID: u16 = 1007; - - /// Indicates that an endpoint is terminating the connection because it has - /// received a message that violates its policy. - /// - /// This is a generic status code that can be returned when there is - /// no other more suitable status code (e.g., `UNSUPPORTED` or `SIZE`) or if - /// there is a need to hide specific details about the policy. - pub const POLICY: u16 = 1008; - - /// Indicates that an endpoint is terminating the connection because it has - /// received a message that is too big for it to process. - pub const SIZE: u16 = 1009; - - /// Indicates that an endpoint (client) is terminating the connection - /// because the server did not respond to extension negotiation - /// correctly. - /// - /// Specifically, the client has expected the server to negotiate one or - /// more extension(s), but the server didn't return them in the response - /// message of the WebSocket handshake. The list of extensions that are - /// needed should be given as the reason for closing. Note that this - /// status code is not used by the server, because it can fail the - /// WebSocket handshake instead. - pub const EXTENSION: u16 = 1010; - - /// Indicates that a server is terminating the connection because it - /// encountered an unexpected condition that prevented it from - /// fulfilling the request. - pub const ERROR: u16 = 1011; - - /// Indicates that the server is restarting. - pub const RESTART: u16 = 1012; - - /// Indicates that the server is overloaded and the client should either - /// connect to a different IP (when multiple targets exist), or - /// reconnect to the same IP when a user has performed an action. - pub const AGAIN: u16 = 1013; -} - -#[cfg(test)] -mod tests { - use super::*; - use async_tungstenite::tungstenite; // NOTARY_MODIFICATION - use axum::routing::any; - use axum::{body::Body, routing::get, Router}; // NOTARY_MODIFICATION - use http::{Request, Version}; - use http_body_util::BodyExt as _; - use hyper_util::rt::TokioExecutor; - use std::future::ready; - use tokio::{ - io::{AsyncRead, AsyncWrite}, - net::TcpStream, - }; - use tower_util::ServiceExt; - - #[tokio::test] // NOTARY_MODIFICATION - async fn rejects_http_1_0_requests() { - let svc = any(|ws: Result| { - let rejection = ws.unwrap_err(); - assert!(matches!( - rejection, - WebSocketUpgradeRejection::ConnectionNotUpgradable(_) - )); - std::future::ready(()) - }); - - let req = Request::builder() - .version(Version::HTTP_10) - .method(Method::GET) - .header("upgrade", "websocket") - .header("connection", "Upgrade") - .header("sec-websocket-key", "6D69KGBOr4Re+Nj6zx9aQA==") - .header("sec-websocket-version", "13") - .body(Body::empty()) - .unwrap(); - - let res = svc.oneshot(req).await.unwrap(); - - assert_eq!(res.status(), StatusCode::OK); - } - - #[allow(dead_code)] - fn default_on_failed_upgrade() { - async fn handler(ws: WebSocketUpgrade) -> Response { - ws.on_upgrade(|_| async {}) - } - let _: Router = Router::new().route("/", any(handler)); - } - - #[allow(dead_code)] - fn on_failed_upgrade() { - async fn handler(ws: WebSocketUpgrade) -> Response { - ws.on_failed_upgrade(|_error: Error| println!("oops!")) - .on_upgrade(|_| async {}) - } - let _: Router = Router::new().route("/", any(handler)); - } - - // NOTARY_MODIFICATION: removed integration test -} diff --git a/crates/notary/server/src/service/tcp.rs b/crates/notary/server/src/service/tcp.rs deleted file mode 100644 index ef8a8d97eb..0000000000 --- a/crates/notary/server/src/service/tcp.rs +++ /dev/null @@ -1,106 +0,0 @@ -use axum::{ - extract::FromRequestParts, - http::{header, request::Parts, HeaderValue, StatusCode}, - response::Response, -}; -use axum_core::body::Body; -use hyper::upgrade::{OnUpgrade, Upgraded}; -use hyper_util::rt::TokioIo; -use std::future::Future; -use tokio::time::Instant; -use tracing::{debug, error, info}; - -use crate::{service::notary_service, types::NotaryGlobals, NotaryServerError}; - -/// Custom extractor used to extract underlying TCP connection for TCP client — -/// using the same upgrade primitives used by the WebSocket implementation where -/// the underlying TCP connection (wrapped in an Upgraded object) only gets -/// polled as an OnUpgrade future after the ongoing HTTP request is finished (ref: https://github.com/tokio-rs/axum/blob/a6a849bb5b96a2f641fa077fe76f70ad4d20341c/axum/src/extract/ws.rs#L122) -/// -/// More info on the upgrade primitives: https://docs.rs/hyper/latest/hyper/upgrade/index.html -pub struct TcpUpgrade { - pub on_upgrade: OnUpgrade, -} - -impl FromRequestParts for TcpUpgrade -where - S: Send + Sync, -{ - type Rejection = NotaryServerError; - - async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { - let on_upgrade = - parts - .extensions - .remove::() - .ok_or(NotaryServerError::BadProverRequest( - "Upgrade header is not set for TCP client".to_string(), - ))?; - - Ok(Self { on_upgrade }) - } -} - -impl TcpUpgrade { - /// Utility function to complete the http upgrade protocol by - /// (1) Return 101 switching protocol response to client to indicate the - /// switching to TCP (2) Spawn a new thread to await on the OnUpgrade - /// object to claim the underlying TCP connection - pub fn on_upgrade(self, callback: C) -> Response - where - C: FnOnce(TokioIo) -> Fut + Send + 'static, - Fut: Future + Send + 'static, - { - let on_upgrade = self.on_upgrade; - tokio::spawn(async move { - let upgraded = match on_upgrade.await { - Ok(upgraded) => upgraded, - Err(err) => { - error!("Something wrong with upgrading HTTP: {:?}", err); - return; - } - }; - let upgraded = TokioIo::new(upgraded); - - callback(upgraded).await; - }); - - #[allow(clippy::declare_interior_mutable_const)] - const UPGRADE: HeaderValue = HeaderValue::from_static("upgrade"); - #[allow(clippy::declare_interior_mutable_const)] - const TCP: HeaderValue = HeaderValue::from_static("tcp"); - - let builder = Response::builder() - .status(StatusCode::SWITCHING_PROTOCOLS) - .header(header::CONNECTION, UPGRADE) - .header(header::UPGRADE, TCP); - - builder.body(Body::empty()).unwrap() - } -} - -/// Perform notarization using the extracted tcp connection -pub async fn tcp_notarize( - stream: TokioIo, - notary_globals: NotaryGlobals, - session_id: String, -) { - let start = Instant::now(); - debug!(?session_id, "Upgraded to tcp connection"); - match notary_service(stream, notary_globals, &session_id).await { - Ok(_) => { - info!( - ?session_id, - elapsed_time_millis = start.elapsed().as_millis(), - "Successful notarization using tcp!" - ); - } - Err(err) => { - error!( - ?session_id, - elapsed_time_millis = start.elapsed().as_millis(), - "Failed notarization using tcp: {err}" - ); - } - } -} diff --git a/crates/notary/server/src/service/websocket.rs b/crates/notary/server/src/service/websocket.rs deleted file mode 100644 index 76e6a9f78c..0000000000 --- a/crates/notary/server/src/service/websocket.rs +++ /dev/null @@ -1,37 +0,0 @@ -use tokio::time::Instant; -use tracing::{debug, error, info}; -use ws_stream_tungstenite::WsStream; - -use crate::{ - service::{axum_websocket::WebSocket, notary_service}, - types::NotaryGlobals, -}; - -/// Perform notarization using the established websocket connection -pub async fn websocket_notarize( - socket: WebSocket, - notary_globals: NotaryGlobals, - session_id: String, -) { - let start = Instant::now(); - debug!(?session_id, "Upgraded to websocket connection"); - // Wrap the websocket in WsStream so that we have AsyncRead and AsyncWrite - // implemented - let stream = WsStream::new(socket.into_inner()); - match notary_service(stream, notary_globals, &session_id).await { - Ok(_) => { - info!( - ?session_id, - elapsed_time_millis = start.elapsed().as_millis(), - "Successful notarization using websocket!" - ); - } - Err(err) => { - error!( - ?session_id, - elapsed_time_millis = start.elapsed().as_millis(), - "Failed notarization using websocket: {err}" - ); - } - } -} diff --git a/crates/notary/server/src/signing.rs b/crates/notary/server/src/signing.rs deleted file mode 100644 index c45b75e3ec..0000000000 --- a/crates/notary/server/src/signing.rs +++ /dev/null @@ -1,153 +0,0 @@ -use const_oid::db::rfc5912::ID_EC_PUBLIC_KEY as OID_EC_PUBLIC_KEY; -use core::fmt; -use eyre::{eyre, Result}; -use pkcs8::{ - der::{self, pem::PemLabel, Encode}, - spki::{DynAssociatedAlgorithmIdentifier, SubjectPublicKeyInfoRef}, - AssociatedOid, DecodePrivateKey, LineEnding, PrivateKeyInfo, -}; -use rand06_compat::Rand0_6CompatExt; -use tlsn::attestation::signing::{Secp256k1Signer, Secp256r1Signer, SignatureAlgId, Signer}; -use tracing::error; - -/// A cryptographic key used for signing attestations. -pub struct AttestationKey { - alg_id: SignatureAlgId, - key: SigningKey, -} - -impl TryFrom> for AttestationKey { - type Error = pkcs8::Error; - - fn try_from(pkcs8: PrivateKeyInfo<'_>) -> Result { - // For now we only support elliptic curve keys. - if pkcs8.algorithm.oid != OID_EC_PUBLIC_KEY { - error!("unsupported key algorithm OID: {:?}", pkcs8.algorithm.oid); - - return Err(pkcs8::Error::KeyMalformed); - } - - let (alg_id, key) = match pkcs8.algorithm.parameters_oid()? { - k256::Secp256k1::OID => { - let key = k256::ecdsa::SigningKey::from_pkcs8_der(&pkcs8.to_der()?) - .map_err(|_| pkcs8::Error::KeyMalformed)?; - (SignatureAlgId::SECP256K1, SigningKey::Secp256k1(key)) - } - p256::NistP256::OID => { - let key = p256::ecdsa::SigningKey::from_pkcs8_der(&pkcs8.to_der()?) - .map_err(|_| pkcs8::Error::KeyMalformed)?; - (SignatureAlgId::SECP256R1, SigningKey::Secp256r1(key)) - } - oid => { - error!("unsupported curve OID: {:?}", oid); - - return Err(pkcs8::Error::KeyMalformed); - } - }; - - Ok(Self { alg_id, key }) - } -} - -impl AttestationKey { - /// Samples a new attestation key of the given signature algorithm. - pub fn random(alg_id: &str) -> Result { - match alg_id.to_uppercase().as_str() { - "SECP256K1" => Ok(Self { - alg_id: SignatureAlgId::SECP256K1, - key: SigningKey::Secp256k1(k256::ecdsa::SigningKey::random( - &mut rand::rng().compat(), - )), - }), - "SECP256R1" => Ok(Self { - alg_id: SignatureAlgId::SECP256R1, - key: SigningKey::Secp256r1(p256::ecdsa::SigningKey::random( - &mut rand::rng().compat(), - )), - }), - alg_id => Err(eyre!("unsupported signature algorithm: {alg_id} — only secp256k1 and secp256r1 are supported")), - } - } - - /// Creates a new signer using this key. - pub fn into_signer(self) -> Box { - match self.key { - SigningKey::Secp256k1(key) => { - Box::new(Secp256k1Signer::new(&key.to_bytes()).expect("key should be valid")) - } - SigningKey::Secp256r1(key) => { - Box::new(Secp256r1Signer::new(&key.to_bytes()).expect("key should be valid")) - } - } - } - - /// Returns the verifying key in compressed bytes. - pub fn verifying_key_bytes(&self) -> Vec { - match self.key { - SigningKey::Secp256k1(ref key) => key - .verifying_key() - .to_encoded_point(true) - .as_bytes() - .to_vec(), - SigningKey::Secp256r1(ref key) => key - .verifying_key() - .to_encoded_point(true) - .as_bytes() - .to_vec(), - } - } - - /// Returns the verifying key in compressed PEM format. - pub fn verifying_key_pem(&self) -> Result { - let algorithm = match &self.key { - SigningKey::Secp256k1(key) => key.verifying_key().algorithm_identifier()?, - SigningKey::Secp256r1(key) => key.verifying_key().algorithm_identifier()?, - }; - let verifying_key_bytes = self.verifying_key_bytes(); - let subject_public_key = der::asn1::BitStringRef::new(0, &verifying_key_bytes)?; - - let der: der::Document = pkcs8::SubjectPublicKeyInfo { - algorithm, - subject_public_key, - } - .try_into()?; - - let pem = der.to_pem(SubjectPublicKeyInfoRef::PEM_LABEL, LineEnding::LF)?; - - Ok(pem) - } -} - -impl fmt::Debug for AttestationKey { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("AttestationKey") - .field("alg_id", &self.alg_id) - .finish_non_exhaustive() - } -} - -enum SigningKey { - Secp256k1(k256::ecdsa::SigningKey), - Secp256r1(p256::ecdsa::SigningKey), -} -#[cfg(test)] -mod tests { - use super::*; - - use std::fs::read_to_string; - - #[test] - fn test_verifying_key_pem() { - let attestation_key_pem = - read_to_string("../tests-integration/fixture/notary/notary.key").unwrap(); - - let attestation_key = AttestationKey::from_pkcs8_pem(&attestation_key_pem).unwrap(); - - let verifying_key_pem = attestation_key.verifying_key_pem().unwrap(); - - let expected_verifying_key_pem = - read_to_string("../tests-integration/fixture/notary/notary.pub").unwrap(); - - assert_eq!(verifying_key_pem, expected_verifying_key_pem); - } -} diff --git a/crates/notary/server/src/tee.rs b/crates/notary/server/src/tee.rs deleted file mode 100644 index 1c098a6454..0000000000 --- a/crates/notary/server/src/tee.rs +++ /dev/null @@ -1,125 +0,0 @@ -use mc_sgx_dcap_types::{QlError, Quote3}; -use serde::{Deserialize, Serialize}; -use std::{ - fs, - fs::File, - io::{self, Read}, - path::Path, -}; -use tracing::{debug, error, instrument}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Quote { - raw_quote: Option, - mrsigner: Option, - mrenclave: Option, - error: Option, -} - -impl Default for Quote { - fn default() -> Quote { - Quote { - raw_quote: Some("".to_string()), - mrsigner: None, - mrenclave: None, - error: None, - } - } -} - -impl std::fmt::Debug for QuoteError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - QuoteError::IoError(err) => write!(f, "IoError: {err:?}"), - QuoteError::IntelQuoteLibrary(err) => { - write!(f, "IntelQuoteLibrary: {err}") - } - } - } -} - -impl From for QuoteError { - fn from(err: io::Error) -> QuoteError { - QuoteError::IoError(err) - } -} - -enum QuoteError { - IoError(io::Error), - IntelQuoteLibrary(QlError), -} - -impl From for QuoteError { - fn from(src: QlError) -> Self { - Self::IntelQuoteLibrary(src) - } -} - -#[instrument(level = "debug", skip_all)] -async fn gramine_quote(public_key: Vec) -> Result { - //// Check if the the gramine pseudo-hardware exists - if !Path::new("/dev/attestation/quote").exists() { - return Ok(Quote::default()); - } - - // Reading attestation type - let mut attestation_file = File::open("/dev/attestation/attestation_type")?; - let mut attestation_type = String::new(); - attestation_file.read_to_string(&mut attestation_type)?; - debug!("Detected attestation type: {}", attestation_type); - - // Read `/dev/attestation/my_target_info` - let my_target_info = fs::read("/dev/attestation/my_target_info")?; - - // Write to `/dev/attestation/target_info` - fs::write("/dev/attestation/target_info", my_target_info)?; - - //// Writing the pubkey to bind the instance to the hw (note: this is not - //// mrsigner) - fs::write("/dev/attestation/user_report_data", public_key)?; - - //// Reading from the gramine quote pseudo-hardware `/dev/attestation/quote` - let mut quote_file = File::open("/dev/attestation/quote")?; - let mut quote = Vec::new(); - let _ = quote_file.read_to_end(&mut quote); - //// todo: wire up Qlerror and drop .expect() - let quote3 = Quote3::try_from(quote.as_ref()).expect("quote3 error"); - let mrenclave = quote3.app_report_body().mr_enclave().to_string(); - let mrsigner = quote3.app_report_body().mr_signer().to_string(); - - debug!("mrenclave: {}", mrenclave); - debug!("mrsigner: {}", mrsigner); - - //// Return the Quote struct with the extracted data - Ok(Quote { - raw_quote: Some(hex::encode(quote)), - mrsigner: Some(mrsigner), - mrenclave: Some(mrenclave), - error: None, - }) -} - -pub async fn quote(public_key: Vec) -> Quote { - //// tee-detection logic will live here, for now its only gramine-sgx - match gramine_quote(public_key).await { - Ok(quote) => quote, - Err(err) => { - error!("Failed to retrieve quote: {:?}", err); - match err { - QuoteError::IoError(_) => Quote { - raw_quote: None, - mrsigner: None, - mrenclave: None, - error: Some("io".to_owned()), - }, - QuoteError::IntelQuoteLibrary(_) => Quote { - raw_quote: None, - mrsigner: None, - mrenclave: None, - error: Some("hw".to_owned()), - }, - } - } - } -} diff --git a/crates/notary/server/src/types.rs b/crates/notary/server/src/types.rs deleted file mode 100644 index 9e9bc9d4bc..0000000000 --- a/crates/notary/server/src/types.rs +++ /dev/null @@ -1,64 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::{ - collections::HashMap, - sync::{Arc, Mutex}, -}; -use tlsn::attestation::CryptoProvider; -use tokio::sync::Semaphore; - -#[cfg(feature = "tee_quote")] -use crate::tee::Quote; -use crate::{auth::AuthorizationMode, config::NotarizationProperties}; - -/// Response object of the /info API -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct InfoResponse { - /// Current version of notary-server - pub version: String, - /// Public key of the notary signing key - pub public_key: String, - /// Current git commit hash of notary-server - pub git_commit_hash: String, - /// Hardware attestation - #[cfg(feature = "tee_quote")] - pub quote: Quote, -} - -/// Request query of the /notarize API -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct NotarizationRequestQuery { - /// Session id that is returned from /session API - pub session_id: String, -} - -/// Global data that needs to be shared with the axum handlers -#[derive(Clone)] -pub struct NotaryGlobals { - pub crypto_provider: Arc, - pub notarization_config: NotarizationProperties, - /// A temporary storage to store session_id - pub store: Arc>>, - /// Selected authorization mode if any - pub authorization_mode: Option, - /// A semaphore to acquire a permit for notarization - pub semaphore: Arc, -} - -impl NotaryGlobals { - pub fn new( - crypto_provider: Arc, - notarization_config: NotarizationProperties, - authorization_mode: Option, - semaphore: Arc, - ) -> Self { - Self { - crypto_provider, - notarization_config, - store: Default::default(), - authorization_mode, - semaphore, - } - } -} diff --git a/crates/notary/server/src/util.rs b/crates/notary/server/src/util.rs deleted file mode 100644 index b9cf6d973f..0000000000 --- a/crates/notary/server/src/util.rs +++ /dev/null @@ -1,83 +0,0 @@ -use eyre::{eyre, Result}; -use serde::de::DeserializeOwned; -use std::path::Path; - -/// Parse a yaml configuration file into a struct -pub fn parse_config_file(location: &str) -> Result { - let file = std::fs::File::open(location)?; - let config: T = serde_yaml::from_reader(file)?; - Ok(config) -} - -/// Parse a csv file into a vec of structs -pub fn parse_csv_file(location: &str) -> Result> { - let file = std::fs::File::open(location)?; - let mut reader = csv::Reader::from_reader(file); - let mut table: Vec = Vec::new(); - for result in reader.deserialize() { - let record: T = result?; - table.push(record); - } - Ok(table) -} - -/// Prepend a file path with a base directory if the path is not absolute. -pub fn prepend_file_path>(file_path: S, base_dir: S) -> Result { - let path = Path::new(file_path.as_ref()); - if !path.is_absolute() { - Ok(Path::new(base_dir.as_ref()) - .join(path) - .to_str() - .ok_or_else(|| eyre!("Failed to convert path to str"))? - .to_string()) - } else { - Ok(file_path.as_ref().to_string()) - } -} - -#[cfg(test)] -mod test { - - use crate::{ - auth::AuthorizationWhitelistRecord, - config::NotaryServerProperties, - util::{parse_csv_file, prepend_file_path}, - }; - - use super::{parse_config_file, Result}; - - #[test] - fn test_parse_config_file() { - let location = "../tests-integration/fixture/config/config.yaml"; - let config: Result = parse_config_file(location); - assert!( - config.is_ok(), - "Could not open file or read the file's values." - ); - } - - #[test] - fn test_parse_csv_file() { - let location = "../tests-integration/fixture/auth/whitelist.csv"; - let table: Result> = parse_csv_file(location); - assert!( - table.is_ok(), - "Could not open csv or read the csv's values." - ); - } - - #[test] - fn test_prepend_file_path() { - let base_dir = "/base/dir"; - let relative_path = "relative/path"; - let absolute_path = "/absolute/path"; - - let result = prepend_file_path(relative_path, base_dir); - assert!(result.is_ok()); - assert_eq!(result.unwrap(), "/base/dir/relative/path"); - - let result = prepend_file_path(absolute_path, base_dir); - assert!(result.is_ok()); - assert_eq!(result.unwrap(), "/absolute/path"); - } -} diff --git a/crates/notary/server/tee/README.md b/crates/notary/server/tee/README.md deleted file mode 100644 index ae3e77e848..0000000000 --- a/crates/notary/server/tee/README.md +++ /dev/null @@ -1,114 +0,0 @@ -This folder contains the necessary files to build a Docker image for running the Notary Server on Intel SGX-enabled hardware. - -## Compile the Notary Server for Intel SGX - -We use [Gramine](https://github.com/gramineproject/gramine) to run the Notary Server on Intel SGX. Gramine allows the Notary Server to run in an isolated environment with minimal host requirements. - -The isolated environment is defined via the manifest template (`notary-server.manifest.template`). - -The Notary Server for SGX is compiled with the Rust feature flag `tee_quote`. This enables the server to add the SGX *quote* to the server's `/info` endpoint. - -### CI - -The [notary-server-sgx Docker container](https://github.com/tlsnotary/tlsn/pkgs/container/tlsn%2Fnotary-server-sgx) is built as part of the CI pipeline. For details on the build process, refer to the [CI workflow configuration](../../../../.github/workflows/ci.yml). - -CI builds a zip file named `notary-server-sgx.zip`, which contains the compiled binary and the signed manifest. This zip file is available for all releases and `dev` builds in the build artifacts. We also publish a Docker image `notary-server-sgx` at . Check the section below for details on running this container. - -### Development - -You can also build everything locally using the `run-gramine-local.sh` script. - -This script creates and signs the Gramine manifest for the Notary Server in a local development environment. It requires the Gramine SDK, so the most convenient way to use it is within a Docker container that includes the necessary dependencies and tools. - -> ⚠️ This script assumes that the `notary-server` binary is already built (for `linux/amd64`) and available in the current directory. Make sure it is built with the `tee_quote` feature: -> `cargo build --bin notary-server --release --features tee_quote` - -#### Build the Docker Image - -To build the Docker image for local development, run: -```sh -docker build -f gramine-local.Dockerfile -t gramine-local . -``` -#### Run the Gramine Script - -Once the image is built, you can run the `run-gramine-local.sh` script inside the container: -``` -docker run --rm -it \ - --platform=linux/amd64 \ - -v "${PWD}:/app" \ - -w /app/ \ - gramine-local \ - "bash -c ./run-gramine-local.sh" -``` - -If successful, the script will generate the following files: -* `notary-server.sig` -* `notary-server-sigstruct.json` -* `notary-server.manifest` -* `notary-server.manifest.sgx` - - -You can verify that the provided **enclave signature (`notary-server.sig`)** matches the expected **`MR_ENCLAVE` and `MR_SIGNER`** values in `notary-server-sigstruct.json`, by running the following command inside a **Gramine Docker container** to inspect the enclave's signature: - -```sh -docker run --rm -v "$(pwd):/work" -w /work gramineproject/gramine:latest \ - "gramine-sgx-sigstruct-view --verbose --output-format=json notary-server.sig" -``` - -The output should be the same as `notary-server-sigstruct.json` - -## How to Run TLSNotary on Intel SGX? - -Before running the Notary Server on Intel SGX hardware, ensure your system has the required Intel SGX components installed: -```sh -wget https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key -cat intel-sgx-deb.key | sudo tee /etc/apt/keyrings/intel-sgx-keyring.asc > /dev/null - -# Add the repository to your sources: -echo 'deb [signed-by=/etc/apt/keyrings/intel-sgx-keyring.asc arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu noble main' | sudo tee /etc/apt/sources.list.d/intel-sgx.list - -sudo apt-get update -sudo apt-get install libsgx-epid libsgx-quote-ex libsgx-dcap-ql -y -``` - -For more details, refer to the official **[Intel SGX Installation Guide](https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_SW_Installation_Guide_for_Linux.pdf).** - -### Docker Compose - -To run the Notary Server using Docker Compose, create a docker-compose.yml file like the following: -```yaml -services: - dev: - container_name: dev - image: ghcr.io/tlsnotary/tlsn/notary-server-sgx:dev - restart: unless-stopped - devices: - - /dev/sgx_enclave - - /dev/sgx_provision - volumes: - - /var/run/aesmd/aesm.socket:/var/run/aesmd/aesm.socket - ports: - - "7047:7047" - entrypoint: [ "gramine-sgx", "notary-server" ] -``` - -To retrieve the SGX attestation quote, query the `/info` endpoint: -```sh -curl localhost:7047/info | jq -``` - -### Run local build directly with Gramine - -To run a locally built Notary Server inside a Gramine-protected SGX enclave, execute: -```sh -docker run --detach \ - --restart=unless-stopped \ - --device=/dev/sgx_enclave \ - --device=/dev/sgx_provision \ - --volume=/var/run/aesmd/aesm.socket:/var/run/aesmd/aesm.socket \ - --publish=7047:7047 \ - --volume="$(pwd):/work" \ - --workdir=/work \ - gramineproject/gramine:latest \ - "gramine-sgx notary-server" -``` \ No newline at end of file diff --git a/crates/notary/server/tee/gramine-local.Dockerfile b/crates/notary/server/tee/gramine-local.Dockerfile deleted file mode 100644 index f782a506cb..0000000000 --- a/crates/notary/server/tee/gramine-local.Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -FROM --platform=linux/amd64 gramineproject/gramine:latest - -RUN apt update && \ - apt install -y jq openssl zip && \ - apt clean && \ - rm -rf /var/lib/apt/lists/* \ No newline at end of file diff --git a/crates/notary/server/tee/notary-server-sgx.Dockerfile b/crates/notary/server/tee/notary-server-sgx.Dockerfile deleted file mode 100644 index 7eda443c21..0000000000 --- a/crates/notary/server/tee/notary-server-sgx.Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM gramineproject/gramine:latest -WORKDIR /work - -# Copies `notary-server-sgx.zip` from the CI build or created locally via `run-gramine-local.sh`. -COPY ./notary-server-sgx /work -RUN chmod +x /work/notary-server - -LABEL org.opencontainers.image.source=https://github.com/tlsnotary/tlsn -LABEL org.opencontainers.image.description="TLSNotary notary server in SGX/Gramine." - -ENTRYPOINT ["gramine-sgx", "notary-server"] diff --git a/crates/notary/server/tee/notary-server.manifest.template b/crates/notary/server/tee/notary-server.manifest.template deleted file mode 100644 index 887f69c956..0000000000 --- a/crates/notary/server/tee/notary-server.manifest.template +++ /dev/null @@ -1,38 +0,0 @@ -libos.entrypoint = "{{ self_exe }}" -loader.log_level = "{{ log_level }}" - -loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}" - -# See https://gramine.readthedocs.io/en/stable/performance.html#glibc-malloc-tuning -loader.env.MALLOC_ARENA_MAX = "1" - -# encrypted type not used -fs.mounts = [ - { path = "/lib", uri = "file:{{ gramine.runtimedir() }}" }, - { path = "{{ arch_libdir }}", uri = "file:{{ arch_libdir }}" }, - { type = "tmpfs", path = "/ephemeral" }, - { type = "encrypted", path = "/vault", uri = "file:vault", key_name = "_sgx_mrenclave" }, - -] - -# hashed @ buildtime. at runtime => these files are +ro -# and can be accessed if hash matches manifest -# !!!! hashed !!!! -# https://gramine.readthedocs.io/en/stable/manifest-syntax.html#trusted-files -sgx.trusted_files = [ - "file:{{ self_exe }}", - "file:{{ gramine.runtimedir() }}/", - "file:{{ arch_libdir }}/", -] - -sgx.edmm_enable = false -sgx.remote_attestation = "dcap" -sgx.max_threads = 64 -sgx.enclave_size = "2G" -sys.disallow_subprocesses = true - - -#### tlsn rev -sgx.isvprodid = 7 -#### F -sgx.isvsvn = 1 diff --git a/crates/notary/server/tee/run-gramine-local.sh b/crates/notary/server/tee/run-gramine-local.sh deleted file mode 100755 index 70b62e7197..0000000000 --- a/crates/notary/server/tee/run-gramine-local.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -echo "[*] Generating SGX signing key..." -gramine-sgx-gen-private-key - -if [ ! -f notary-server ]; then - echo "[!] notary-server binary not found. Please copy it from ci, or build it first." - echo "Note that notary-server must be built for linux/amd64 with tee_quote feature enabled" - exit 1 -fi - -chmod +x notary-server - -echo "[*] Creating Gramine manifest..." -gramine-manifest \ - -Dlog_level=debug \ - -Darch_libdir=/lib/x86_64-linux-gnu \ - -Dself_exe=notary-server \ - notary-server.manifest.template \ - notary-server.manifest - -echo "[*] Signing manifest..." -gramine-sgx-sign \ - --manifest notary-server.manifest \ - --output notary-server.manifest.sgx - -echo "[*] Viewing SIGSTRUCT..." -gramine-sgx-sigstruct-view --verbose --output-format=json notary-server.sig >notary-server-sigstruct.json - -cat notary-server-sigstruct.json | jq . - -mr_enclave=$(jq -r ".mr_enclave" notary-server-sigstruct.json) -mr_signer=$(jq -r ".mr_signer" notary-server-sigstruct.json) - -echo "==============================" -echo "MRENCLAVE: $mr_enclave" -echo "MRSIGNER: $mr_signer" -echo "==============================" - -zip -r notary-server-sgx.zip \ - notary-server \ - notary-server-sigstruct.json \ - notary-server.sig \ - notary-server.manifest \ - notary-server.manifest.sgx \ - README.md diff --git a/crates/notary/tests-integration/Cargo.toml b/crates/notary/tests-integration/Cargo.toml deleted file mode 100644 index c1e19fa50f..0000000000 --- a/crates/notary/tests-integration/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -[package] -name = "notary-tests-integration" -version = "0.0.0" -edition = "2021" -publish = false - -[lints] -workspace = true - -[dev-dependencies] -notary-client = { workspace = true } -notary-common = { workspace = true } -notary-server = { workspace = true } -tls-server-fixture = { workspace = true } -tlsn = { workspace = true } -tlsn-tls-core = { workspace = true } -tlsn-core = { workspace = true } - -async-tungstenite = { workspace = true, features = ["tokio-native-tls"] } -futures = { workspace = true } -http = { workspace = true } -http-body-util = { workspace = true } -hyper = { workspace = true, features = ["client", "http1", "server"] } -hyper-tls = { version = "0.6", features = [ - "vendored", -] } # specify vendored feature to use statically linked copy of OpenSSL -hyper-util = { workspace = true, features = ["full"] } -jsonwebtoken = { version = "9.3.1", features = ["use_pem"] } -rstest = { workspace = true } -rustls = { workspace = true } -rustls-pemfile = { workspace = true } -serde_json = { workspace = true } -tokio = { workspace = true, features = ["full"] } -tokio-native-tls = { version = "0.3.1", features = ["vendored"] } -tokio-util = { workspace = true, features = ["compat"] } -tracing = { workspace = true } -tracing-subscriber = { workspace = true, features = ["env-filter"] } -uuid = { workspace = true, features = ["v4", "fast-rng"] } -ws_stream_tungstenite = { workspace = true, features = ["tokio_io"] } diff --git a/crates/notary/tests-integration/fixture/.gitignore b/crates/notary/tests-integration/fixture/.gitignore deleted file mode 100644 index 74e5b1d3a9..0000000000 --- a/crates/notary/tests-integration/fixture/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!* \ No newline at end of file diff --git a/crates/notary/tests-integration/fixture/auth/jwt.key b/crates/notary/tests-integration/fixture/auth/jwt.key deleted file mode 100644 index 56ca0a0a6e..0000000000 --- a/crates/notary/tests-integration/fixture/auth/jwt.key +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKAIBAAKCAgEAxkfGQo2iyUK6sV84rvsb6d4IlorFaX4WDwDnEP/zU2Pduwf7 -kV39x6oqJzNjmfXm/RkcaAZXQdbvjBA9uwM7cd2Z7hMWLT3oix66Qv9d3+PWcdJt -TUVK710+QflZJqOEFOt30eNHm/8pN6z1P6YSZvYpHMVlCC7tL8OLWcMH9gYmUH6c -WOdzCaCigdQD1CqE9TG7jZlIepiWI7QMD7v/yN8tZoV8EzW25JdGPe4gtetBl1O+ -QoAUpuQp7JSUxV4RR8HArGGSQ0OHHZMcfWx/CoLBUyXlmSCC21sSl634l7+HUM6U -/dHo1b+XSMpjjVCTjd0lDFCU+kiLtapdcCiJDijSj+a4xkJP+uvxpTZuB6A4W31e -DeKPIeutPAwcnw8UktdX6eiAt1ONAxB+ytZNcdrAUbMdIcocrMxgyPtCWBgDX1LK -fpPlRTBRI50mdFkIOp9dh02ijpWAYXaFA65aI8Tqh9j3wzbvFsCWpBfK7Zcbl7BZ -skAvXCjnPHDdup5sesnYrhOOG4jo2/nho7AmEEkvZVDyH9jDPrA6imljFX1gpKvW -mtERY7eor3gPI6FFOUh8qwEOB4lTsj9DUd75vvRlaPU7ibvFTdLVoRiXkAraKR59 -WmcOpAfut9yOxNaE+M25jY/Jj+crptEt0MmezfowRimt3wpQ0C7i6hCBgrMCAwEA -AQKCAgAx05WR4e/XbapmqkwfRMEV+xLjacoEIYg/ivWGAxvNh9oPhwkD1b/RbgSb -x0EvTmkWjznhNj61L+MQqoAov74vdgWZmzhGdDk8xKL/9RZNDf80qTGIanJTRnY/ -s/5gRFULwMRifR/gprVf5VnX/c7ACvn33e7uqIQ4LYaWLvmQLKlyLu7xNHBnKfPM -dk/kAC9bQn0kLzHUhQWtwTAKwC6d9t981OyCE0x7kzw2keGsdYsNESFNqswFyG50 -oj3kfygOhTT63KYZux14JCDTr/EY3hTg5TQWT+IyZ2d7sF85GwtRFijAxAAjvrqw -sxNjTq1VyA3oU1OstZBOPZqvdbBC6ZpIiWyPE5j+H4R2/rxnKc/nwI+PXU5L1qKf -kB8yUsXxZQA9KY48VU3Z3WZxGWZwoU8Z+WUN6rJknZkVfk14GdQrf6BNyUVQk/Rd -W1bGZB11CHH80LRdsx5T7B2gwq41EJ33+8Hd8S/9YeSWMnHKkpH39bjMu328jn8U -0TaXQ8H/ZMwEmDZ74nmct4VnmF09dxdIHILKyohjGuU9nUBXnXw8orJPXgFlOkmn -G55/sMqDwnnz9O7wGptY3Vx7xCO4I9N4CijUw065dyZaY6wzyph9dAurnDu0MmA7 -o0JwnhI2iKwPU8hq6nm2Ku2YNz7f0O5v5NMtw5z4lrOo4TX6MQKCAQEA/eLuS+dz -mrLEpKCDi63y1G6SYDM+mHWaN3B/y6XVgVjGyZWvebMIol5nGo0URdUHqXVw4krt -Hjr3AulSASr4s75wfk2bVwVuUQfCQrM+zvqBcApWJq+Ve7LEIYRchr8+vlyqiBBV -IV/XyL/KshSXKDscf/1J881M+ZxuGCfqQ0TADJ7592nHCXcDJ8XJXkPRQQEN2QwT -DGdYDIo276IfbiY2MAjCQRvzdGUocfeNZ5SYXOODhS2n99aLWsK3uXG74+tzFZhl -5fJVjxuipZVO4ycEHX8BYqikXQzQKe8UW2Z2Xhb4CQCDw58KXbLShhtRB16m7HJN -2nPXQYN2S6OMawKCAQEAx+5Ww2MPGLa5gvuJTE/qK4pXHrOGA/QM55qqtMlCPZEz -8/3qgcbkKAQL0GJe8Xlsuu0oKBYxIZQnimxlUAFq776b62s5DJuKMA5WhgTRO7Zg -mV5FZUtx+1H9W7GQsDWYlMjUmZOCvefu5qXOLH5gr9AS3Ckyfq1i8xwvAyXRr/4B -jAFtSUgQpbkFhjQtjEVcFEdJhz4OtIbXD0AWgMPSysH2ABZf+tht27mEvAuBCKzn -qa3aQuR5+D7fuDIN9To5QlFUX52vY+xLiaHgUuqC1Ud7y5TKlfNuG+IbYAUWTddS -j62m1G7xBAAAn+D6PX8egQe8EeTWBUaX159YlX102QKCAQEA4C5atsF4DfietLNb -lKITksrUC4gUVLE7bIq0/ZDAV0eZuHSpDrAtBpqPNh2u8f6qllKyS89XU2NDq9l0 -ZL2Z/7VARfanHQ8Zmwlb2mPGKSN/2fv2mJBgUWrHzsS+oukKMTNIDX9GfILR2lyo -UdjmpEqV3to8S8BToPElMcVFEQMLBdn25SYM72mcaqk2JzuA8YJJxQbpZwF1+RSu -b6jbUfsBzCZfyPgyX+vW69NolDbc1uC6yIVJFQnn4UugyWoJO7cy1rXL/GCgdg4z -7zxI/UD9XEJCaeh5wgRHZ0/JzO9Lw8dKW0COGNU9ZQE67dn/EZ/di1lfL28sepfn -g+C1YwKCAQBnOzJDeq991ENfVV+kLpM73hdzu8BT5DyRjbPc2xo/zeykbBQc5ERE -QSqUc2aQimDQ98lHQYYmz2fHOobpU4ISvjmlydxQHTOx8oVMd8pNabLhHeL5FYaJ -/OCz6rBJu7LICBZ2IctdIReisjQNl0d3IBnM4dy3ufEglAnWNz3ZAG9uCgKS1wn5 -d9pZXDG0fs+3jMNzeGCBaCo9Lpsv62y40oOhsevnCr9Wt6jIq6v5fcW0QBc1eOFd -g6Fiaz33xBNyoanOIQ5Bqu2p6BJ63ammVF2gVXhxCpts/EekQZwtnyN7Gm/Mumfp -59JquvCatjta5lJ+bsjvOm8Gn7lOntOpAoIBAFQqUgq5XllVEAyvsdUrXhv0zTb+ -AeM49hHcGPL3S/pOkiHqsbCfjJe1v5Jqcm579s/O4lqtuL2e4INNqOVmxkOfRbFh -oRioUrdAsWv8t2Q6CkXhwoK59kJwitOaF00OyixxdCO9WY6qhg+ZZgZDiLnM7V7b -u8zMvwgqDKD3+7tTjM318bEyE4MCooh9vVD3CxOcdc7oe9TnZvxyuUIRB6UBEyTg -jfvGcyDTSzW3P4SetKOqenk0HuDTPGHtGjYpRnKFfRRcHOqo3p/l1Z+l08alLNAS -wAREawpeuKGx9/ZrhTrqgLTkbx7lSwP9aTKPQka1CtGvgSUohqQ3OPrG0Jk= ------END RSA PRIVATE KEY----- diff --git a/crates/notary/tests-integration/fixture/auth/jwt.key.pub b/crates/notary/tests-integration/fixture/auth/jwt.key.pub deleted file mode 100644 index d1887b7abd..0000000000 --- a/crates/notary/tests-integration/fixture/auth/jwt.key.pub +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxkfGQo2iyUK6sV84rvsb -6d4IlorFaX4WDwDnEP/zU2Pduwf7kV39x6oqJzNjmfXm/RkcaAZXQdbvjBA9uwM7 -cd2Z7hMWLT3oix66Qv9d3+PWcdJtTUVK710+QflZJqOEFOt30eNHm/8pN6z1P6YS -ZvYpHMVlCC7tL8OLWcMH9gYmUH6cWOdzCaCigdQD1CqE9TG7jZlIepiWI7QMD7v/ -yN8tZoV8EzW25JdGPe4gtetBl1O+QoAUpuQp7JSUxV4RR8HArGGSQ0OHHZMcfWx/ -CoLBUyXlmSCC21sSl634l7+HUM6U/dHo1b+XSMpjjVCTjd0lDFCU+kiLtapdcCiJ -DijSj+a4xkJP+uvxpTZuB6A4W31eDeKPIeutPAwcnw8UktdX6eiAt1ONAxB+ytZN -cdrAUbMdIcocrMxgyPtCWBgDX1LKfpPlRTBRI50mdFkIOp9dh02ijpWAYXaFA65a -I8Tqh9j3wzbvFsCWpBfK7Zcbl7BZskAvXCjnPHDdup5sesnYrhOOG4jo2/nho7Am -EEkvZVDyH9jDPrA6imljFX1gpKvWmtERY7eor3gPI6FFOUh8qwEOB4lTsj9DUd75 -vvRlaPU7ibvFTdLVoRiXkAraKR59WmcOpAfut9yOxNaE+M25jY/Jj+crptEt0Mme -zfowRimt3wpQ0C7i6hCBgrMCAwEAAQ== ------END PUBLIC KEY----- diff --git a/crates/notary/tests-integration/fixture/auth/whitelist.csv b/crates/notary/tests-integration/fixture/auth/whitelist.csv deleted file mode 100644 index 719f273111..0000000000 --- a/crates/notary/tests-integration/fixture/auth/whitelist.csv +++ /dev/null @@ -1,3 +0,0 @@ -"Name","ApiKey","CreatedAt" -"Jonas Nielsen","test_api_key_0","2023-09-18T07:38:53Z" -"Eren Jaeger","test_api_key_1","2023-10-18T07:38:53Z" diff --git a/crates/notary/tests-integration/fixture/config/config.yaml b/crates/notary/tests-integration/fixture/config/config.yaml deleted file mode 100644 index 5f310ae401..0000000000 --- a/crates/notary/tests-integration/fixture/config/config.yaml +++ /dev/null @@ -1,46 +0,0 @@ -host: "0.0.0.0" -port: 7047 -html_info: | - - - - - - - - - - - - -

Notary Server {version}!

- - - -concurrency: 32 - -notarization: - max_sent_data: 4096 - max_recv_data: 16384 - timeout: 1800 - private_key_path: "../notary/notary.key" - signature_algorithm: secp256k1 - allow_extensions: false - -tls: - enabled: false - private_key_path: "../tls/key.pem" - certificate_path: "../tls/cert.pem" - -log: - level: DEBUG - format: COMPACT - -auth: - enabled: false - whitelist: "../auth/whitelist.csv" diff --git a/crates/notary/tests-integration/fixture/notary/notary.key b/crates/notary/tests-integration/fixture/notary/notary.key deleted file mode 100644 index 716507fdc3..0000000000 --- a/crates/notary/tests-integration/fixture/notary/notary.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgbGCmm+WHxwlKKKRWddfO -02TmpM787BJQuoVrHeCI5v6hRANCAAR7SPGcE5toiPteODpNcsIzUYb9WFjnrnQ6 -tL+OBxsG5+j9AN8W8v+KvMi/UlKaEaJVywIcLCiWENdZyB7u/Yix ------END PRIVATE KEY----- diff --git a/crates/notary/tests-integration/fixture/notary/notary.pub b/crates/notary/tests-integration/fixture/notary/notary.pub deleted file mode 100644 index 2e44ba7010..0000000000 --- a/crates/notary/tests-integration/fixture/notary/notary.pub +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MDYwEAYHKoZIzj0CAQYFK4EEAAoDIgADe0jxnBObaIj7Xjg6TXLCM1GG/VhY5650 -OrS/jgcbBuc= ------END PUBLIC KEY----- diff --git a/crates/notary/tests-integration/fixture/tls/README.md b/crates/notary/tests-integration/fixture/tls/README.md deleted file mode 100644 index a3775890eb..0000000000 --- a/crates/notary/tests-integration/fixture/tls/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Create a private key for the root CA -openssl genpkey -algorithm RSA -out rootCA.key -pkeyopt rsa_keygen_bits:2048 - -# Create a self-signed root CA certificate (100 years validity) -openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 36525 -out rootCA.crt -subj "/C=US/ST=State/L=City/O=tlsnotary/OU=IT/CN=tlsnotary.org" - -# Create a private key for the end entity certificate -openssl genpkey -algorithm RSA -out notary.key -pkeyopt rsa_keygen_bits:2048 - -# Create a certificate signing request (CSR) for the end entity certificate -openssl req -new -key notary.key -out notary.csr -subj "/C=US/ST=State/L=City/O=tlsnotary/OU=IT/CN=tlsnotaryserver.io" - -# Sign the CSR with the root CA to create the end entity certificate (100 years validity) -openssl x509 -req -in notary.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out notary.crt -days 36525 -sha256 -extfile openssl.cnf -extensions v3_req diff --git a/crates/notary/tests-integration/fixture/tls/notary.crt b/crates/notary/tests-integration/fixture/tls/notary.crt deleted file mode 100644 index 8df0d5b4ef..0000000000 --- a/crates/notary/tests-integration/fixture/tls/notary.crt +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDzTCCArWgAwIBAgIJALo+PtyTmxENMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNV -BAYTAlVTMQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTESMBAGA1UECgwJ -dGxzbm90YXJ5MQswCQYDVQQLDAJJVDEWMBQGA1UEAwwNdGxzbm90YXJ5Lm9yZzAg -Fw0yNDA4MDIxMTE1MzZaGA8yMTI0MDgwMzExMTUzNlowajELMAkGA1UEBhMCVVMx -DjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5MRIwEAYDVQQKDAl0bHNub3Rh -cnkxCzAJBgNVBAsMAklUMRswGQYDVQQDDBJ0bHNub3RhcnlzZXJ2ZXIuaW8wggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEzkZE9X7Utn3by4sFG8KcDrdV -3szzPP9eA8U4cVmrWQAS0lsrEeHDv0KGKMFKOi3FDgyF1I8OWMIvnWj4LQ1zKYny -fufOkAv4UcYY0E9/VonqPKY0Xo9lbbl5Xu/E55gfJhAPZzoV73uXjvlhSVdhaypZ -ibSZm9t5izTiK1pcKDuvubB5zhmldt1+f0wbBxhLWVlf8T8GaPVZ37NCJGeeUf6Z -GL6Fq4jBYfvjzUQl6P72Zk0FCpIq2W/z2yBfWnNRRPjQuzIxk7cB6ssVpQF52cXZ -OF5YJhc7C/hr4rfWLshGQxkmwNktBSHrQUBm3LQHaT9ccPy0xgdIAD9Avf0BAgMB -AAGjeTB3MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMB0GA1UdEQQWMBSCEnRsc25v -dGFyeXNlcnZlci5pbzAdBgNVHQ4EFgQULo1DGRbjA/+zX9AvRk6YcO2AYHowHwYD -VR0jBBgwFoAUKmfDzMNGdJr5blSUarmhRIiI88IwDQYJKoZIhvcNAQELBQADggEB -AFgTVLHCfaswU8pTwgRK1xWTGlMDQmZU//Lbatel6HTH0zMF4wj/hVkGikHWpJLt -1UipGRPUgyjFtDoPag8jrSDeK1ahtjNzkGEuz5wXM0zYqIv1xkXPatEbCV4LLI3Q -Yxf2YI7Nh599+2I/oZ+8YKUMn6EI58PgiSjyG7vzRoQKGAoE82FpBFyEUpcUXQDa -MIr/D8Xcv+RPpdHxi4cyHJAy+irzs9ghF7WdmFEOATYNF8EsP/doiskXWl68t2Hn -sDflDIbOH1xId3zJIwE/5IG3NrNqhVm2va06TNWURo3v8h+7bnD8Rxq107ObflKj -i1MwBiwdf7/w5Dw9o3K21ic= ------END CERTIFICATE----- diff --git a/crates/notary/tests-integration/fixture/tls/notary.csr b/crates/notary/tests-integration/fixture/tls/notary.csr deleted file mode 100644 index e8d88ef185..0000000000 --- a/crates/notary/tests-integration/fixture/tls/notary.csr +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIICrzCCAZcCAQAwajELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYD -VQQHDARDaXR5MRIwEAYDVQQKDAl0bHNub3RhcnkxCzAJBgNVBAsMAklUMRswGQYD -VQQDDBJ0bHNub3RhcnlzZXJ2ZXIuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQDEzkZE9X7Utn3by4sFG8KcDrdV3szzPP9eA8U4cVmrWQAS0lsrEeHD -v0KGKMFKOi3FDgyF1I8OWMIvnWj4LQ1zKYnyfufOkAv4UcYY0E9/VonqPKY0Xo9l -bbl5Xu/E55gfJhAPZzoV73uXjvlhSVdhaypZibSZm9t5izTiK1pcKDuvubB5zhml -dt1+f0wbBxhLWVlf8T8GaPVZ37NCJGeeUf6ZGL6Fq4jBYfvjzUQl6P72Zk0FCpIq -2W/z2yBfWnNRRPjQuzIxk7cB6ssVpQF52cXZOF5YJhc7C/hr4rfWLshGQxkmwNkt -BSHrQUBm3LQHaT9ccPy0xgdIAD9Avf0BAgMBAAGgADANBgkqhkiG9w0BAQsFAAOC -AQEAups2oJRV5x/BZcZvRseWpGToqr5pO3ESXUEEbCpeHDKLIav4aWfYUkY4UGGN -2m1XYN7nEytwygJmMRWS8kjJzacII9j+dCysqCmm71T2L4BszCCVYGwTAigZuZ1R -WmULhso1tXXUF7ggEdTUpxMa5VijkbpZ5iQfBbslpSo0mjgM2bL4hO3Y8dl7a1Bn -0LNasWzWaizp6SkMU2BDNVF+i5blR4p8Bk0GQpPzGqwZf2tKcqmvutPqEm4rcOOC -U5j/U6uZpCYc8VQOklOUkDUSAZzCSJxeGHykddtMFte5+HkqBZoMCQwHeZl1g0qZ -/NLvHB8YO7U2XRJTfxloHhj3WQ== ------END CERTIFICATE REQUEST----- diff --git a/crates/notary/tests-integration/fixture/tls/notary.key b/crates/notary/tests-integration/fixture/tls/notary.key deleted file mode 100644 index 71f8b04401..0000000000 --- a/crates/notary/tests-integration/fixture/tls/notary.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDEzkZE9X7Utn3b -y4sFG8KcDrdV3szzPP9eA8U4cVmrWQAS0lsrEeHDv0KGKMFKOi3FDgyF1I8OWMIv -nWj4LQ1zKYnyfufOkAv4UcYY0E9/VonqPKY0Xo9lbbl5Xu/E55gfJhAPZzoV73uX -jvlhSVdhaypZibSZm9t5izTiK1pcKDuvubB5zhmldt1+f0wbBxhLWVlf8T8GaPVZ -37NCJGeeUf6ZGL6Fq4jBYfvjzUQl6P72Zk0FCpIq2W/z2yBfWnNRRPjQuzIxk7cB -6ssVpQF52cXZOF5YJhc7C/hr4rfWLshGQxkmwNktBSHrQUBm3LQHaT9ccPy0xgdI -AD9Avf0BAgMBAAECggEACAuCldkPOTTIik6UvT24Q9baKbl02VCaA8bVrgv8JWP6 -+8n7jhQqDW1pE8Dgvd8I9fAwFNxuiKCaN4YQv2xgC2AcUnxbj3cV9i2pkmQZi9QG -yTt3c9aVuAi3Nz3pQTxSXJuatnZ6ymDCxZxDl3V/C+1sisJ1Tn4vh5VoMQKiq/eq -sgmNF73VvKiHUeJGpMixho9AeFfE/o+HTAXcycmHlXBvJzOMsgmgTxinBNt54ROc -WKtt4GNvkxN72e/qu2rNPJvi/Hdq9cG03LmuaOn9dSbHWOdeLZFR2OkO6jvrUKv0 -doDOYUsdCBIC/LaLvtOBzts3d3BZou9MOcz9cXUBDQKBgQDrFomWXDEnqH6o4ixg -1VpZfaK+fRHasni9txZC0zK44HOL4qnUjLLQ7GPyam44exq2/B2ouTCrV+TicoKy -GYAjCL+/rhZfiMoWrXO/SIU73TiXbYQiNEV2FYuAbvCsW9rLRH2/GzYu+fb/hUBP -vYLn9gf2u/nCiqGZ4dJnmrEOIwKBgQDWT/eE6+4HQB8Zhz6Dxu6/EOlwZtC9QsiT -CEEYja7yl0MDnQsQzv2nbCs5li6359IQJR1+L6G3kcd0z6EumWpB0JVQzMtKwlVe -WodfpPOWrectftfGUr+3xZIRbSV/hj89RhG2uhQ8xJYDLWHjP3LF0menvW/1JP0K -xhlGKqRwiwKBgQCtzz3uYz8ceSEcMAxrk5J3M8JNYB8BOI64hVL6GTgZJCmJtQ2n -TlcuzHeg1Tukmq/HtmMfSbxIEnXxToR+tQfd3ywVxdpYy8POPHOlazLGberXWmsk -9sycX5WCYYOji04alwr5bl8DIGCTzqsbyZutcGO28ofYY7LTGPj9DIv3TQKBgHiM -gq5CB6IMb3HsoT1+qMzQtn6DVuceqbQK8JLfH4lVjFx7+b16sTN7pNS/pYfM3lw2 -hGB2aoDXf1o1cHTF1v8uVM8eYzuqFFr+kSc7ockgCOmOb9EeurikaYVj37Pbz7an -s08VXEzSR4+B943cIrMjpyqzZEaAh9WHmK/fTKABAoGACsgGUB84KSV+LBz/LY9M -5xYJNuf11Jucx6bahX6wPZyssLnZ5o+x7QmFIVXyPnQ7wn69C7EfvKJIgdXmjEEk -P4oUh7Osc5UwTR5s7Kr9iCqcDIR5NW68AFHEddMEXpOyFn4QrjrdYSlO4CQAiQUU -Nudz2KSI148F/vzo0I78A7c= ------END PRIVATE KEY----- diff --git a/crates/notary/tests-integration/fixture/tls/openssl.cnf b/crates/notary/tests-integration/fixture/tls/openssl.cnf deleted file mode 100644 index dffdf04f9d..0000000000 --- a/crates/notary/tests-integration/fixture/tls/openssl.cnf +++ /dev/null @@ -1,7 +0,0 @@ -[ v3_req ] -basicConstraints = CA:FALSE -keyUsage = nonRepudiation, digitalSignature, keyEncipherment -subjectAltName = @alt_names - -[ alt_names ] -DNS.1 = tlsnotaryserver.io \ No newline at end of file diff --git a/crates/notary/tests-integration/fixture/tls/rootCA.crt b/crates/notary/tests-integration/fixture/tls/rootCA.crt deleted file mode 100644 index 090534e1a4..0000000000 --- a/crates/notary/tests-integration/fixture/tls/rootCA.crt +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDrTCCApWgAwIBAgIUUmpF/+i9EcDpciV0s1Okh4Wx/QswDQYJKoZIhvcNAQEL -BQAwZTELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 -MRIwEAYDVQQKDAl0bHNub3RhcnkxCzAJBgNVBAsMAklUMRYwFAYDVQQDDA10bHNu -b3Rhcnkub3JnMCAXDTI0MDgwMjExMDU1MloYDzIxMjQwODAzMTEwNTUyWjBlMQsw -CQYDVQQGEwJVUzEOMAwGA1UECAwFU3RhdGUxDTALBgNVBAcMBENpdHkxEjAQBgNV -BAoMCXRsc25vdGFyeTELMAkGA1UECwwCSVQxFjAUBgNVBAMMDXRsc25vdGFyeS5v -cmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDNGFXBMov4HBr4F/W -+9mzM4t+ww4jURyF/7O1puyhz0gueAu5/kzh6d5r+P2xwP0tpqtITvwfo2tHCNTg -dKBNPO7NnRnW8QtommHhafHUfj+4cR7G1xxSZD34mwuBnYW3cmxCbi0l5dClWfHA -G7GRHv5aPBBYbeF2ACYBesaCJLa5OMkab/N7DwPTWuSjoQqrMeodaQ1Q5Ro09cbt -WlL+ywRVq1gKZvgs3RogwDt6NUEZ8Hkz/BZzbo2HlX1+XUpMP7ucHGUQIt7F2Z+6 -iYkMJfP+BflBR+qOzoMbgHo1SD5uIv1/iXi3UoddpCnzsretkcNs2pnpiPWoEhdA -fNuxAgMBAAGjUzBRMB0GA1UdDgQWBBQqZ8PMw0Z0mvluVJRquaFEiIjzwjAfBgNV -HSMEGDAWgBQqZ8PMw0Z0mvluVJRquaFEiIjzwjAPBgNVHRMBAf8EBTADAQH/MA0G -CSqGSIb3DQEBCwUAA4IBAQA7HR1mmHe5jT52EhSjwePvzvW7Tx6VGSUrhzkhnRVv -IbYjX0jWPSSXvc2NG3LyxyDLLOTkM0xWQLGEQ9LYYuH9Sy1ZUK4Mv7qWO23LaM2s -dYjWDKM9N23XhtgkbzFX6+X1Q93wU5KIibVMkPSzmaxDbhoiKYozznmSjOBt2HR2 -UbpjNPjzN7BL+Gv+8hBhS0UeE2zgN0XcZmyiZQlfL7XTVoszjNd6HeKyCHX1Tk4a -/vYn3B1cFK8u4gRyjPKr8QH/uju4T+0gp8GtB1eQ9erdBkehPgb8x1QwdXWKPp4m -woJDTdgJhMu3w0InHtQztCtiTPphjrN/as2rw9hyYU4C ------END CERTIFICATE----- diff --git a/crates/notary/tests-integration/fixture/tls/rootCA.key b/crates/notary/tests-integration/fixture/tls/rootCA.key deleted file mode 100644 index 8e0fe87edc..0000000000 --- a/crates/notary/tests-integration/fixture/tls/rootCA.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDNGFXBMov4HBr -4F/W+9mzM4t+ww4jURyF/7O1puyhz0gueAu5/kzh6d5r+P2xwP0tpqtITvwfo2tH -CNTgdKBNPO7NnRnW8QtommHhafHUfj+4cR7G1xxSZD34mwuBnYW3cmxCbi0l5dCl -WfHAG7GRHv5aPBBYbeF2ACYBesaCJLa5OMkab/N7DwPTWuSjoQqrMeodaQ1Q5Ro0 -9cbtWlL+ywRVq1gKZvgs3RogwDt6NUEZ8Hkz/BZzbo2HlX1+XUpMP7ucHGUQIt7F -2Z+6iYkMJfP+BflBR+qOzoMbgHo1SD5uIv1/iXi3UoddpCnzsretkcNs2pnpiPWo -EhdAfNuxAgMBAAECggEAGlol5z4e9XD9JvMMEn++wfHBcS7FPStOsyBJPcqibgMH -oY5UjEVc/QU6IPq6H5cIFsjwnTsHJQDwveQz/iErICzg/Xep7K8ZyoNHl3YFTu8Z -jGgTruWMo0AjxZNYwvoQT9WYm9c318KQn4yRlaJSHwnqGHsR/H4eTnRyrQcgE/gY -V7TNEqS7CMvuKqY+rnRhRjXlnKD0p6iT68QF5RVfWH4Qedk5t09JohfTjCK+5+Zo -TXFkpltNv6qHXpZoq5LTo4HZL/l9AnvUU5sjHbzfB6FJtZ0wYtI4q0EIchTusIw8 -cJttSsIHzDnWaw2HLRm7dIHyrk7WqbLUtJRn+Bu9SQKBgQDwKJM7EoH0ZzzG+D24 -lnSV+zjcBeMB4VLRAt5uabWZhmEa0lcb1nv73RU6vmU71UeuzlErSSRPqutr7Ajk -f37xQCeuQKFLrY1OGmiBp4CBOFLe/l2mwPjnDgccgaWPrkj7QoqMdDAK7sdWnUO1 -uo9mKhzX08DuLxlU5VxxarzFqwKBgQDQFLGH38rg2BcYV5TjE2SZHAWZSz5TQTNj -8PMqzZWqbY0WtmEnJEh6I99l0Y4MguuFVjO9WD0kssiQtL+kQvVJkR1WPaOAviFl -PppWyA4BKGcdXSGKsXY08I4KXJaWVzolYZLA/y1zT+7JSrBd2QILyrZvF4DiPv5Y -Jm1LMd6QEwKBgBOnDl1QJ2hLpnKVz98yGLpJQ57lsGzv9mn6NR+N8PluQLYELnKt -u5mhvuH+wKQD0QjiA0xqgNkwIHHFb/ja4hV17YlZ6pkZy61vhcvOXDq21DlBUYKa -2gN2Z2iSx2yZk4lUKahSvbe3UIKq/eZ6LM/sdE3JG0miew0yc70oQehfAoGAOvTy -DEabjDON76a5F9Hh2gP3jiSkpyA9OF8H9yPC+UQLCtloE5gTNRA+9vF2JxNdOi1f -gZGj2WcSrvWXqyoRp+OHBW13iz3T5oTjZB1Q4oEZHlfJ7is0C/HwvPzY6gYTAo5v -72Ed9qM6TCxuZljbXI32POnS6cfhdwaERx79KaMCgYAJzrJBGEd194gVewUsoeiL -fB8eERgvvPCZwfKMh4H0Q8i6RsECNrZJOnNq6xG/Pf1ubasxNYZwSP4yOB+syhA7 -NlvIP8Wps+c0M0oAAhF8q//eduUHyS1o/BbTL44ZkINVlmO5WuQ2pB1QdaBunrnF -GbrTaj5XbaeHwD4CKq5q0w== ------END PRIVATE KEY----- diff --git a/crates/notary/tests-integration/fixture/tls/rootCA.srl b/crates/notary/tests-integration/fixture/tls/rootCA.srl deleted file mode 100644 index 460f6f23fa..0000000000 --- a/crates/notary/tests-integration/fixture/tls/rootCA.srl +++ /dev/null @@ -1 +0,0 @@ -BA3E3EDC939B110D diff --git a/crates/notary/tests-integration/tests/notary.rs b/crates/notary/tests-integration/tests/notary.rs deleted file mode 100644 index 95b6b04311..0000000000 --- a/crates/notary/tests-integration/tests/notary.rs +++ /dev/null @@ -1,574 +0,0 @@ -use async_tungstenite::{ - tokio::connect_async_with_tls_connector_and_config, tungstenite::protocol::WebSocketConfig, -}; -use futures::future::join_all; -use http_body_util::{BodyExt as _, Full}; -use hyper::{body::Bytes, Request, StatusCode}; -use hyper_tls::HttpsConnector; -use hyper_util::{ - client::legacy::{connect::HttpConnector, Builder}, - rt::{TokioExecutor, TokioIo}, -}; -use jsonwebtoken::{encode, get_current_timestamp, Algorithm, EncodingKey, Header}; -use notary_client::{Accepted, ClientError, NotarizationRequest, NotaryClient, NotaryConnection}; -use notary_common::{ClientType, NotarizationSessionRequest, NotarizationSessionResponse}; -use rstest::rstest; -use rustls::{Certificate, RootCertStore}; -use std::{string::String, time::Duration}; -use tls_server_fixture::{bind_test_server_hyper, CA_CERT_DER, SERVER_DOMAIN}; -use tlsn::{ - attestation::request::RequestConfig, - config::ProtocolConfig, - prover::{Prover, ProverConfig, TlsConfig}, - transcript::TranscriptCommitConfig, -}; -use tokio::{ - io::{AsyncRead, AsyncWrite, AsyncWriteExt}, - time::sleep, -}; -use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; -use tracing::debug; -use tracing_subscriber::EnvFilter; -use ws_stream_tungstenite::WsStream; - -use notary_server::{ - read_pem_file, run_server, AuthorizationModeProperties, AuthorizationProperties, - JwtAuthorizationProperties, JwtClaim, NotarizationProperties, NotaryServerProperties, - TLSProperties, -}; - -const MAX_SENT_DATA: usize = 1 << 13; -const MAX_RECV_DATA: usize = 1 << 13; - -const NOTARY_HOST: &str = "127.0.0.1"; -const NOTARY_DNS: &str = "tlsnotaryserver.io"; -const NOTARY_CA_CERT_PATH: &str = "./fixture/tls/rootCA.crt"; -const NOTARY_CA_CERT_BYTES: &[u8] = include_bytes!("../fixture/tls/rootCA.crt"); -const API_KEY: &str = "test_api_key_0"; -const JWT_PRIVATE_KEY: &[u8] = include_bytes!("../fixture/auth/jwt.key"); - -enum AuthMode { - Jwt, - Whitelist, -} - -fn get_jwt() -> String { - let priv_key = EncodingKey::from_rsa_pem(JWT_PRIVATE_KEY).unwrap(); - let timestamp = get_current_timestamp() as i64 + 1000; - encode( - &Header::new(Algorithm::RS256), - &serde_json::json!({ "exp": timestamp, "sub": "test"}), - &priv_key, - ) - .unwrap() -} - -fn get_server_config( - port: u16, - tls_enabled: bool, - auth: Option, - concurrency: usize, -) -> NotaryServerProperties { - NotaryServerProperties { - host: NOTARY_HOST.to_string(), - port, - notarization: NotarizationProperties { - max_sent_data: 1 << 13, - max_recv_data: 1 << 14, - private_key_path: Some("./fixture/notary/notary.key".to_string()), - ..Default::default() - }, - tls: TLSProperties { - enabled: tls_enabled, - private_key_path: Some("./fixture/tls/notary.key".to_string()), - certificate_path: Some("./fixture/tls/notary.crt".to_string()), - }, - auth: AuthorizationProperties { - enabled: auth.is_some(), - mode: auth.map(|mode| match mode { - AuthMode::Jwt => AuthorizationModeProperties::Jwt(JwtAuthorizationProperties { - algorithm: "rs256".to_string(), - public_key_path: "./fixture/auth/jwt.key.pub".to_string(), - claims: vec![JwtClaim { - name: "sub".to_string(), - ..Default::default() - }], - }), - AuthMode::Whitelist => AuthorizationModeProperties::Whitelist( - "./fixture/auth/whitelist.csv".to_string(), - ), - }), - }, - concurrency, - ..Default::default() - } -} - -async fn setup_config_and_server( - sleep_ms: u64, - port: u16, - tls_enabled: bool, - auth: Option, - concurrency: usize, -) -> NotaryServerProperties { - let notary_config = get_server_config(port, tls_enabled, auth, concurrency); - - // Abruptly closed connections will cause the server to log errors. We - // prevent that by excluding the noisy modules from logging. - let _ = tracing_subscriber::fmt() - .with_env_filter(EnvFilter::new( - "error,uid_mux::yamux=off,tlsn_verifier=off,notary_server::service::tcp=off", - )) - .try_init(); - // Note: since only one global subscriber is allowed for the entire - // testsuite, the above filter will have an effect on all tests. - - let config = notary_config.clone(); - - // Run the notary server - tokio::spawn(async move { - run_server(&config).await.unwrap(); - }); - - // Sleep for a while to allow notary server to finish set up and start listening - tokio::time::sleep(Duration::from_millis(sleep_ms)).await; - - notary_config -} - -// Returns `NotaryClient` configured for proving over TCP. -fn tcp_prover_client(notary_config: NotaryServerProperties) -> NotaryClient { - let mut notary_client_builder = NotaryClient::builder(); - - notary_client_builder - .host(¬ary_config.host) - .port(notary_config.port) - .enable_tls(false); - - if notary_config.auth.enabled { - match notary_config.auth.mode.unwrap() { - AuthorizationModeProperties::Jwt(..) => { - notary_client_builder.jwt(get_jwt()); - } - AuthorizationModeProperties::Whitelist(..) => { - notary_client_builder.api_key(API_KEY); - } - } - } - - notary_client_builder.build().unwrap() -} - -// Tries to put the client in an `Accepted` state. -async fn accepted_client(client: NotaryClient) -> Result { - let notarization_request = NotarizationRequest::builder() - .max_sent_data(MAX_SENT_DATA) - .max_recv_data(MAX_RECV_DATA) - .build() - .unwrap(); - - client.request_notarization(notarization_request).await -} - -async fn tcp_prover(notary_config: NotaryServerProperties) -> (NotaryConnection, String) { - let accepted = accepted_client(tcp_prover_client(notary_config)) - .await - .unwrap(); - (accepted.io, accepted.id) -} - -async fn tls_prover(notary_config: NotaryServerProperties) -> (NotaryConnection, String) { - let mut certificate_file_reader = read_pem_file(NOTARY_CA_CERT_PATH).await.unwrap(); - let mut certificates: Vec = rustls_pemfile::certs(&mut certificate_file_reader) - .unwrap() - .into_iter() - .map(Certificate) - .collect(); - let certificate = certificates.remove(0); - - let mut root_cert_store = RootCertStore::empty(); - root_cert_store.add(&certificate).unwrap(); - - let notary_client = NotaryClient::builder() - .host(NOTARY_DNS) - .port(notary_config.port) - .root_cert_store(root_cert_store) - .build() - .unwrap(); - - let accepted = accepted_client(notary_client).await.unwrap(); - (accepted.io, accepted.id) -} - -#[rstest] -// For `tls_without_auth` test to pass, one needs to add " " in /etc/hosts -// so that this test programme can resolve the self-named NOTARY_DNS to NOTARY_HOST IP successfully. -#[case::tls_without_auth({ - tls_prover(setup_config_and_server(100, 7047, true, None, 100).await) -})] -#[case::tcp_with_whitelist_auth({ - tcp_prover(setup_config_and_server(100, 7048, false, Some(AuthMode::Whitelist), 100).await) -})] -#[case::tcp_with_jwt_auth({ - tcp_prover(setup_config_and_server(100, 7049, false, Some(AuthMode::Jwt), 100).await) -})] -#[case::tcp_without_auth({ - tcp_prover(setup_config_and_server(100, 7050, false, None, 100).await) -})] -#[awt] -#[tokio::test] -#[ignore = "expensive"] -async fn test_tcp_prover( - #[future] - #[case] - requested_notarization: (S, String), -) { - let (notary_socket, _) = requested_notarization; - - let mut root_store = tls_core::anchors::RootCertStore::empty(); - root_store - .add(&tls_core::key::Certificate(CA_CERT_DER.to_vec())) - .unwrap(); - - let protocol_config = ProtocolConfig::builder() - .max_sent_data(MAX_SENT_DATA) - .max_recv_data(MAX_RECV_DATA) - .build() - .unwrap(); - - // Set up prover config. - let prover_config = ProverConfig::builder() - .server_name(SERVER_DOMAIN) - .tls_config(TlsConfig::builder().root_store(root_store).build().unwrap()) - .protocol_config(protocol_config) - .build() - .unwrap(); - - // Create a new Prover. - let prover = Prover::new(prover_config) - .setup(notary_socket.compat()) - .await - .unwrap(); - - // Connect to the Server. - let (client_socket, server_socket) = tokio::io::duplex(1 << 16); - let server_task = tokio::spawn(bind_test_server_hyper(server_socket.compat())); - - let (tls_connection, prover_fut) = prover.connect(client_socket.compat()).await.unwrap(); - - // Spawn the Prover task to be run concurrently. - let prover_task = tokio::spawn(prover_fut); - - let (mut request_sender, connection) = - hyper::client::conn::http1::handshake(TokioIo::new(tls_connection.compat())) - .await - .unwrap(); - - tokio::spawn(connection); - - let request = Request::builder() - .uri(format!("https://{SERVER_DOMAIN}/echo")) - .method("POST") - .header("Host", SERVER_DOMAIN) - .header("Connection", "close") - .body(Full::::new("echo".into())) - .unwrap(); - - debug!("Sending request to server: {:?}", request); - - let response = request_sender.send_request(request).await.unwrap(); - - assert!(response.status() == StatusCode::OK); - - let payload = response.into_body().collect().await.unwrap().to_bytes(); - debug!( - "Received response from server: {:?}", - &String::from_utf8_lossy(&payload) - ); - - server_task.await.unwrap().unwrap(); - - let mut prover = prover_task.await.unwrap().unwrap(); - - let (sent_len, recv_len) = prover.transcript().len(); - - let mut builder = TranscriptCommitConfig::builder(prover.transcript()); - - builder.commit_sent(&(0..sent_len)).unwrap(); - builder.commit_recv(&(0..recv_len)).unwrap(); - - let transcript_commit = builder.build().unwrap(); - - let mut builder = RequestConfig::builder(); - - builder.transcript_commit(transcript_commit); - - let request = builder.build().unwrap(); - - #[allow(deprecated)] - prover.notarize(&request).await.unwrap(); - prover.close().await.unwrap(); - - debug!("Done notarization!"); -} - -#[tokio::test] -#[ignore = "expensive"] -async fn test_websocket_prover() { - // Notary server configuration setup - let notary_config = setup_config_and_server(100, 7051, true, None, 100).await; - let notary_host = notary_config.host.clone(); - let notary_port = notary_config.port; - - // Connect to the notary server via TLS-WebSocket - // Try to avoid dealing with transport layer directly to mimic the limitation of - // a browser extension that uses websocket - // - // Establish TLS setup for connections later - let certificate = - tokio_native_tls::native_tls::Certificate::from_pem(NOTARY_CA_CERT_BYTES).unwrap(); - let notary_tls_connector = tokio_native_tls::native_tls::TlsConnector::builder() - .add_root_certificate(certificate) - .use_sni(false) - .danger_accept_invalid_certs(true) - .build() - .unwrap(); - - // Call the /session HTTP API to configure notarization and obtain session id - let mut hyper_http_connector = HttpConnector::new(); - hyper_http_connector.enforce_http(false); - let mut hyper_tls_connector = - HttpsConnector::from((hyper_http_connector, notary_tls_connector.clone().into())); - hyper_tls_connector.https_only(true); - let https_client = Builder::new(TokioExecutor::new()).build(hyper_tls_connector); - - // Build the HTTP request to configure notarization - let payload = serde_json::to_string(&NotarizationSessionRequest { - client_type: ClientType::Websocket, - max_sent_data: Some(MAX_SENT_DATA), - max_recv_data: Some(MAX_RECV_DATA), - }) - .unwrap(); - - let request = Request::builder() - .uri(format!("https://{notary_host}:{notary_port}/session")) - .method("POST") - .header("Host", notary_host.clone()) - // Need to specify application/json for axum to parse it as json - .header("Content-Type", "application/json") - .body(Full::new(Bytes::from(payload))) - .unwrap(); - - debug!("Sending request"); - - let response = https_client.request(request).await.unwrap(); - - debug!("Sent request"); - - assert!(response.status() == StatusCode::OK); - - debug!("Response OK"); - - // Pretty printing :) - let payload = response.into_body().collect().await.unwrap().to_bytes(); - let notarization_response = - serde_json::from_str::(&String::from_utf8_lossy(&payload)) - .unwrap(); - - debug!("Notarization response: {:?}", notarization_response,); - - // Connect to the Notary via TLS-Websocket - // - // Note: This will establish a new TLS-TCP connection instead of reusing the - // previous TCP connection used in the previous HTTP POST request because we - // cannot claim back the tcp connection used in hyper client while using its - // high level request function — there does not seem to have a crate that can - // let you make a request without establishing TCP connection where you can - // claim the TCP connection later after making the request - let request = http::Request::builder() - // Need to specify the session_id so that notary server knows the right configuration to use - // as the configuration is set in the previous HTTP call - .uri(format!( - "wss://{}:{}/notarize?sessionId={}", - notary_host, - notary_port, - notarization_response.session_id.clone() - )) - .header("Host", notary_host.clone()) - .header("Sec-WebSocket-Key", uuid::Uuid::new_v4().to_string()) - .header("Sec-WebSocket-Version", "13") - .header("Connection", "Upgrade") - .header("Upgrade", "Websocket") - .body(()) - .unwrap(); - - let (notary_ws_stream, _) = connect_async_with_tls_connector_and_config( - request, - Some(notary_tls_connector.into()), - Some(WebSocketConfig::default()), - ) - .await - .unwrap(); - - // Wrap the socket with the adapter so that we get AsyncRead and AsyncWrite - // implemented - let notary_ws_socket = WsStream::new(notary_ws_stream); - - // Connect to the Server - let (client_socket, server_socket) = tokio::io::duplex(1 << 16); - let server_task = tokio::spawn(bind_test_server_hyper(server_socket.compat())); - - let mut root_store = tls_core::anchors::RootCertStore::empty(); - root_store - .add(&tls_core::key::Certificate(CA_CERT_DER.to_vec())) - .unwrap(); - - let mut tls_config_builder = TlsConfig::builder(); - tls_config_builder.root_store(root_store); - let tls_config = tls_config_builder.build().unwrap(); - - let protocol_config = ProtocolConfig::builder() - .max_sent_data(MAX_SENT_DATA) - .max_recv_data(MAX_RECV_DATA) - .build() - .unwrap(); - - // Set up prover config. - let prover_config = ProverConfig::builder() - .server_name(SERVER_DOMAIN) - .protocol_config(protocol_config) - .tls_config(tls_config) - .build() - .unwrap(); - - // Bind the Prover to the sockets - let prover = Prover::new(prover_config) - .setup(notary_ws_socket) - .await - .unwrap(); - let (tls_connection, prover_fut) = prover.connect(client_socket.compat()).await.unwrap(); - - // Spawn the Prover and Mux tasks to be run concurrently - let prover_task = tokio::spawn(prover_fut); - - let (mut request_sender, connection) = - hyper::client::conn::http1::handshake(TokioIo::new(tls_connection.compat())) - .await - .unwrap(); - - tokio::spawn(connection); - - let request = Request::builder() - .uri(format!("https://{SERVER_DOMAIN}/echo")) - .header("Host", SERVER_DOMAIN) - .header("Connection", "close") - .method("POST") - .body(Full::::new("echo".into())) - .unwrap(); - - debug!("Sending request to server: {:?}", request); - - let response = request_sender.send_request(request).await.unwrap(); - - assert!(response.status() == StatusCode::OK); - - let payload = response.into_body().collect().await.unwrap().to_bytes(); - debug!( - "Received response from server: {:?}", - &String::from_utf8_lossy(&payload) - ); - - server_task.await.unwrap().unwrap(); - - let mut prover = prover_task.await.unwrap().unwrap(); - - let (sent_len, recv_len) = prover.transcript().len(); - - let mut builder = TranscriptCommitConfig::builder(prover.transcript()); - - builder.commit_sent(&(0..sent_len)).unwrap(); - builder.commit_recv(&(0..recv_len)).unwrap(); - - let transcript_commit = builder.build().unwrap(); - - let mut builder = RequestConfig::builder(); - - builder.transcript_commit(transcript_commit); - - let request = builder.build().unwrap(); - - #[allow(deprecated)] - prover.notarize(&request).await.unwrap(); - prover.close().await.unwrap(); - debug!("Done notarization!"); -} - -#[tokio::test] -async fn test_concurrency_limit() { - const CONCURRENCY: usize = 5; - - let notary_config = setup_config_and_server(100, 7052, false, None, CONCURRENCY).await; - - async fn do_test(config: NotaryServerProperties) -> Vec<(NotaryConnection, String)> { - // Start notarization requests in parallel. - let connections = (0..CONCURRENCY).map(|_| tcp_prover(config.clone())); - - // Wait for all requests to become accepted. - let mut connections = join_all(connections).await; - - // Start a new request which will time out. - let mut client = tcp_prover_client(config.clone()); - client.request_timeout(1); - assert_eq!(accepted_client(client.clone()).await.err().unwrap().to_string(), "client error: Internal, source: Some(\"Timed out while waiting for server to accept notarization request\")"); - - // Close one of the connections. - connections.pop().unwrap().0.shutdown().await.unwrap(); - - // Start a new request which will be accepted this time. - let accepted = accepted_client(client).await.unwrap(); - connections.push((accepted.io, accepted.id)); - - connections - } - - let connections = do_test(notary_config.clone()).await; - // Close all connections. - for mut c in connections { - c.0.shutdown().await.unwrap(); - } - - // Test again to make sure the server's semaphore was restored to the initial - // state. - _ = do_test(notary_config).await; -} - -#[tokio::test] -async fn test_notarization_request_retry() { - const CONCURRENCY: usize = 5; - - let config = setup_config_and_server(100, 7053, false, None, CONCURRENCY).await; - - // Max out the concurrency limit. - let connections = (0..CONCURRENCY).map(|_| tcp_prover(config.clone())); - let mut connections = join_all(connections).await; - - // Start a new request which will retry every second. - let mut client = tcp_prover_client(config.clone()); - client.request_retry_override(1); - let client_fut = accepted_client(client.clone()); - tokio::pin!(client_fut); - - tokio::select! { - _ = &mut client_fut => panic!("Expected timeout to complete first"), - _ = sleep(Duration::from_secs(2)) => {} - } - - // Close one of the connections. - connections.pop().unwrap().0.shutdown().await.unwrap(); - - // Now the request will be accepted. - tokio::select! { - _ = client_fut => {}, - _ = sleep(Duration::from_secs(2)) => panic!("Expected client future to complete first") - } -} diff --git a/pre-commit-check.sh b/pre-commit-check.sh index 3f05327ea2..1eec573a12 100755 --- a/pre-commit-check.sh +++ b/pre-commit-check.sh @@ -7,13 +7,16 @@ set -e # Check formatting -cargo +nightly fmt --all +cargo +nightly fmt --check --all # Check clippy -cargo clippy --all-features --all-targets -- -D warnings +cargo clippy --all-features --all-targets --locked -- -D warnings # Build all targets -# cargo build --all-targets +cargo build --all-targets --locked # Run tests -# cargo test +cargo test --locked + +# Run integration tests, excluding specific targets +cargo test --locked --profile tests-integration --workspace --exclude tlsn-tls-client --exclude tlsn-tls-core -- --include-ignored diff --git a/set_tlsn_version.rs b/set_tlsn_version.rs index 16b3171d02..feb62fea97 100755 --- a/set_tlsn_version.rs +++ b/set_tlsn_version.rs @@ -55,9 +55,6 @@ fn main() -> Result<(), Box> { } } - let open_api_path = Path::new(&args.workspace).join("crates/notary/server/openapi.yaml"); - replace_version_with_regex(&open_api_path, r"(?m)^(\s*version:\s*)([^\s]+)($)", &args.version)?; - let releng_workflow_path = Path::new(&args.workspace).join(".github/workflows/releng.yml"); replace_version_with_regex(&releng_workflow_path,r#"(?m)^(\s*default:\s*'v)([^']+)(')"#, &args.version)?; From ddb2f36eb292c791e526f7be782e8b66aa49e971 Mon Sep 17 00:00:00 2001 From: Hendrik Eeckhaut Date: Mon, 4 Aug 2025 09:28:23 +0200 Subject: [PATCH 2/2] Removed more notary mentions --- crates/attestation/src/fixtures.rs | 5 - crates/examples/Cargo.toml | 13 -- crates/examples/attestation/README.md | 111 ----------- crates/examples/attestation/present.rs | 117 ------------ crates/examples/attestation/prove.rs | 244 ------------------------- crates/examples/attestation/verify.rs | 94 ---------- crates/tlsn/src/verifier.rs | 4 +- 7 files changed, 2 insertions(+), 586 deletions(-) delete mode 100644 crates/examples/attestation/README.md delete mode 100644 crates/examples/attestation/present.rs delete mode 100644 crates/examples/attestation/prove.rs delete mode 100644 crates/examples/attestation/verify.rs diff --git a/crates/attestation/src/fixtures.rs b/crates/attestation/src/fixtures.rs index 28dd516ae0..f586603ea6 100644 --- a/crates/attestation/src/fixtures.rs +++ b/crates/attestation/src/fixtures.rs @@ -16,11 +16,6 @@ use crate::{ signing::SignatureAlgId, }; -/// Returns a notary signing key fixture. -pub fn notary_signing_key() -> p256::ecdsa::SigningKey { - p256::ecdsa::SigningKey::from_slice(&[1; 32]).unwrap() -} - /// A Request fixture used for testing. #[allow(missing_docs)] pub struct RequestFixture { diff --git a/crates/examples/Cargo.toml b/crates/examples/Cargo.toml index 737243344c..2107772684 100644 --- a/crates/examples/Cargo.toml +++ b/crates/examples/Cargo.toml @@ -8,7 +8,6 @@ version = "0.0.0" workspace = true [dependencies] -notary-client = { workspace = true } tlsn-core = { workspace = true } tlsn = { workspace = true } tlsn-formats = { workspace = true } @@ -41,18 +40,6 @@ tokio-util = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } -[[example]] -name = "attestation_prove" -path = "attestation/prove.rs" - -[[example]] -name = "attestation_present" -path = "attestation/present.rs" - -[[example]] -name = "attestation_verify" -path = "attestation/verify.rs" - [[example]] name = "interactive" path = "interactive/interactive.rs" diff --git a/crates/examples/attestation/README.md b/crates/examples/attestation/README.md deleted file mode 100644 index 9f1a7dfc7f..0000000000 --- a/crates/examples/attestation/README.md +++ /dev/null @@ -1,111 +0,0 @@ -## Simple Attestation Example: Notarize Public Data from example.com (Rust) - -This example demonstrates the simplest possible use case for TLSNotary. A Prover notarizes data from a local test server with a local Notary. - -**Overview**: -1. Notarize a request and response from the test server and acquire an attestation of its content. -2. Create a redacted, verifiable presentation using the attestation. -3. Verify the presentation. - -### 1. Notarize - -Before starting the notarization, set up the local test server and local notary. -Run the following commands from the root of this repository (not from this example's folder): - -1. Run the test server: - ```shell - RUST_LOG=info PORT=4000 cargo run --bin tlsn-server-fixture - ``` -2. Run the notary server: - ```shell - cargo run --release --bin notary-server - ``` -3. Run the prove example: - ```shell - SERVER_PORT=4000 cargo run --release --example attestation_prove - ``` - -To see more details, run with additional debug information: -```shell -RUST_LOG=debug,yamux=info,uid_mux=info SERVER_PORT=4000 cargo run --release --example attestation_prove -``` - -If notarization is successful, you should see the following output in the console: -```log -Starting an MPC TLS connection with the server -Got a response from the server: 200 OK -Notarization complete! -Notarization completed successfully! -The attestation has been written to `example-json.attestation.tlsn` and the corresponding secrets to `example-json.secrets.tlsn`. -``` - -⚠️ Note: In this example, we run a local Notary server for demonstration purposes. In real-world applications, the Notary should be operated by a trusted third party. Refer to the [Notary Server Documentation](https://docs.tlsnotary.org/developers/notary_server.html) for more details on running a Notary server. - -### 2. Build a Verifiable Presentation - -This step creates a verifiable presentation with optional redactions, which can be shared with any verifier. - -Run the present example: -```shell -cargo run --release --example attestation_present -``` - -If successful, you’ll see this output in the console: - -```log -Presentation built successfully! -The presentation has been written to `example-json.presentation.tlsn`. -``` - -You can create multiple presentations from the attestation and secrets in the notarization step, each with customized data redactions. You are invited to experiment! - -### 3. Verify the Presentation - -This step reads the presentation created above, verifies it, and prints the disclosed data to the console. - -Run the verify binary: -```shell -cargo run --release --example attestation_verify -``` - -Upon success, you should see output similar to: -```log -Verifying presentation with {key algorithm} key: { hex encoded key } - -**Ask yourself, do you trust this key?** - -------------------------------------------------------------------- -Successfully verified that the data below came from a session with test-server.io at { time }. -Note that the data which the Prover chose not to disclose are shown as X. - -Data sent: -... -``` - -⚠️ The presentation includes a “verifying key,” which the Notary used when issuing the attestation. If you trust this key, you can trust the authenticity of the presented data. - -### HTML - -In the example above, we notarized a JSON response. TLSNotary also supports notarizing HTML content. To run an HTML example, use: - -```shell -# notarize -SERVER_PORT=4000 cargo run --release --example attestation_prove -- html -# present -cargo run --release --example attestation_present -- html -# verify -cargo run --release --example attestation_verify -- html -``` - -### Private Data - -The examples above demonstrate how to use TLSNotary with publicly accessible data. TLSNotary can also be utilized for private data that requires authentication. To access this data, you can add the necessary headers (such as an authentication token) or cookies to your request. To run an example that uses an authentication token, execute the following command: - -```shell -# notarize -SERVER_PORT=4000 cargo run --release --example attestation_prove -- authenticated -# present -cargo run --release --example attestation_present -- authenticated -# verify -cargo run --release --example attestation_verify -- authenticated -``` \ No newline at end of file diff --git a/crates/examples/attestation/present.rs b/crates/examples/attestation/present.rs deleted file mode 100644 index 60be20e2ff..0000000000 --- a/crates/examples/attestation/present.rs +++ /dev/null @@ -1,117 +0,0 @@ -// This example demonstrates how to build a verifiable presentation from an -// attestation and the corresponding connection secrets. See the `prove.rs` -// example to learn how to acquire an attestation from a Notary. - -use clap::Parser; -use hyper::header; - -use tlsn::attestation::{presentation::Presentation, Attestation, CryptoProvider, Secrets}; -use tlsn_examples::ExampleType; -use tlsn_formats::http::HttpTranscript; - -#[derive(Parser, Debug)] -#[command(version, about, long_about = None)] -struct Args { - /// What data to notarize - #[clap(default_value_t, value_enum)] - example_type: ExampleType, -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - let args = Args::parse(); - - create_presentation(&args.example_type).await -} - -async fn create_presentation(example_type: &ExampleType) -> Result<(), Box> { - let attestation_path = tlsn_examples::get_file_path(example_type, "attestation"); - let secrets_path = tlsn_examples::get_file_path(example_type, "secrets"); - - // Read attestation from disk. - let attestation: Attestation = bincode::deserialize(&std::fs::read(attestation_path)?)?; - - // Read secrets from disk. - let secrets: Secrets = bincode::deserialize(&std::fs::read(secrets_path)?)?; - - // Parse the HTTP transcript. - let transcript = HttpTranscript::parse(secrets.transcript())?; - - // Build a transcript proof. - let mut builder = secrets.transcript_proof_builder(); - - // Here is where we reveal all or some of the parts we committed in `prove.rs` - // previously. - let request = &transcript.requests[0]; - // Reveal the structure of the request without the headers or body. - builder.reveal_sent(&request.without_data())?; - // Reveal the request target. - builder.reveal_sent(&request.request.target)?; - // Reveal all request headers except the values of User-Agent and Authorization. - for header in &request.headers { - if !(header - .name - .as_str() - .eq_ignore_ascii_case(header::USER_AGENT.as_str()) - || header - .name - .as_str() - .eq_ignore_ascii_case(header::AUTHORIZATION.as_str())) - { - builder.reveal_sent(header)?; - } else { - builder.reveal_sent(&header.without_value())?; - } - } - - // Reveal only parts of the response. - let response = &transcript.responses[0]; - // Reveal the structure of the response without the headers or body. - builder.reveal_recv(&response.without_data())?; - // Reveal all response headers. - for header in &response.headers { - builder.reveal_recv(header)?; - } - - let content = &response.body.as_ref().unwrap().content; - match content { - tlsn_formats::http::BodyContent::Json(json) => { - // For experimentation, reveal the entire response or just a selection. - let reveal_all = false; - if reveal_all { - builder.reveal_recv(response)?; - } else { - builder.reveal_recv(json.get("id").unwrap())?; - builder.reveal_recv(json.get("information.name").unwrap())?; - builder.reveal_recv(json.get("meta.version").unwrap())?; - } - } - tlsn_formats::http::BodyContent::Unknown(span) => { - builder.reveal_recv(span)?; - } - _ => {} - } - - let transcript_proof = builder.build()?; - - // Use default crypto provider to build the presentation. - let provider = CryptoProvider::default(); - - let mut builder = attestation.presentation_builder(&provider); - - builder - .identity_proof(secrets.identity_proof()) - .transcript_proof(transcript_proof); - - let presentation: Presentation = builder.build()?; - - let presentation_path = tlsn_examples::get_file_path(example_type, "presentation"); - - // Write the presentation to disk. - std::fs::write(&presentation_path, bincode::serialize(&presentation)?)?; - - println!("Presentation built successfully!"); - println!("The presentation has been written to `{presentation_path}`."); - - Ok(()) -} diff --git a/crates/examples/attestation/prove.rs b/crates/examples/attestation/prove.rs deleted file mode 100644 index cc6018ff75..0000000000 --- a/crates/examples/attestation/prove.rs +++ /dev/null @@ -1,244 +0,0 @@ -// This example demonstrates how to use the Prover to acquire an attestation for -// an HTTP request sent to example.com. The attestation and secrets are saved to -// disk. - -use std::env; - -use clap::Parser; -use http_body_util::Empty; -use hyper::{body::Bytes, Request, StatusCode}; -use hyper_util::rt::TokioIo; -use spansy::Spanned; -use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; -use tracing::debug; - -use notary_client::{Accepted, NotarizationRequest, NotaryClient}; -use tls_server_fixture::{CA_CERT_DER, SERVER_DOMAIN}; -use tlsn::{ - attestation::request::RequestConfig, - config::ProtocolConfig, - prover::{Prover, ProverConfig, TlsConfig}, - transcript::TranscriptCommitConfig, -}; -use tlsn_examples::ExampleType; -use tlsn_formats::http::{DefaultHttpCommitter, HttpCommit, HttpTranscript}; -use tlsn_server_fixture::DEFAULT_FIXTURE_PORT; -use tlsn_server_fixture_certs::{CLIENT_CERT, CLIENT_KEY}; - -// Setting of the application server. -const USER_AGENT: &str = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"; - -#[derive(Parser, Debug)] -#[command(version, about, long_about = None)] -struct Args { - /// What data to notarize. - #[clap(default_value_t, value_enum)] - example_type: ExampleType, -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - let args = Args::parse(); - - let (uri, extra_headers) = match args.example_type { - ExampleType::Json => ("/formats/json", vec![]), - ExampleType::Html => ("/formats/html", vec![]), - ExampleType::Authenticated => ("/protected", vec![("Authorization", "random_auth_token")]), - }; - - notarize(uri, extra_headers, &args.example_type).await -} - -async fn notarize( - uri: &str, - extra_headers: Vec<(&str, &str)>, - example_type: &ExampleType, -) -> Result<(), Box> { - tracing_subscriber::fmt::init(); - - let notary_host: String = env::var("NOTARY_HOST").unwrap_or("127.0.0.1".into()); - let notary_port: u16 = env::var("NOTARY_PORT") - .map(|port| port.parse().expect("port should be valid integer")) - .unwrap_or(7047); - let server_host: String = env::var("SERVER_HOST").unwrap_or("127.0.0.1".into()); - let server_port: u16 = env::var("SERVER_PORT") - .map(|port| port.parse().expect("port should be valid integer")) - .unwrap_or(DEFAULT_FIXTURE_PORT); - - // Build a client to connect to the notary server. - let notary_client = NotaryClient::builder() - .host(notary_host) - .port(notary_port) - // WARNING: Always use TLS to connect to notary server, except if notary is running locally - // e.g. this example, hence `enable_tls` is set to False (else it always defaults to True). - .enable_tls(false) - .build() - .unwrap(); - - // Send requests for configuration and notarization to the notary server. - let notarization_request = NotarizationRequest::builder() - // We must configure the amount of data we expect to exchange beforehand, which will - // be preprocessed prior to the connection. Reducing these limits will improve - // performance. - .max_sent_data(tlsn_examples::MAX_SENT_DATA) - .max_recv_data(tlsn_examples::MAX_RECV_DATA) - .build()?; - - let Accepted { - io: notary_connection, - id: _session_id, - .. - } = notary_client - .request_notarization(notarization_request) - .await - .expect("Could not connect to notary. Make sure it is running."); - - // Create a crypto provider accepting the server-fixture's self-signed - // root certificate. - // - // This is only required for offline testing with the server-fixture. In - // production, use `CryptoProvider::default()` instead. - let mut root_store = tls_core::anchors::RootCertStore::empty(); - root_store - .add(&tls_core::key::Certificate(CA_CERT_DER.to_vec())) - .unwrap(); - - // Set up protocol configuration for prover. - let mut prover_config_builder = ProverConfig::builder(); - prover_config_builder - .server_name(SERVER_DOMAIN) - .protocol_config( - ProtocolConfig::builder() - // We must configure the amount of data we expect to exchange beforehand, which will - // be preprocessed prior to the connection. Reducing these limits will improve - // performance. - .max_sent_data(tlsn_examples::MAX_SENT_DATA) - .max_recv_data(tlsn_examples::MAX_RECV_DATA) - .build()?, - ); - - // (Optional) Set up TLS client authentication if required by the server. - prover_config_builder.tls_config( - TlsConfig::builder() - .client_auth_pem((vec![CLIENT_CERT.to_vec()], CLIENT_KEY.to_vec())) - .unwrap() - .build()?, - ); - - let prover_config = prover_config_builder.build()?; - - // Create a new prover and perform necessary setup. - let prover = Prover::new(prover_config) - .setup(notary_connection.compat()) - .await?; - - // Open a TCP connection to the server. - let client_socket = tokio::net::TcpStream::connect((server_host, server_port)).await?; - - // Bind the prover to the server connection. - // The returned `mpc_tls_connection` is an MPC TLS connection to the server: all - // data written to/read from it will be encrypted/decrypted using MPC with - // the notary. - let (mpc_tls_connection, prover_fut) = prover.connect(client_socket.compat()).await?; - let mpc_tls_connection = TokioIo::new(mpc_tls_connection.compat()); - - // Spawn the prover task to be run concurrently in the background. - let prover_task = tokio::spawn(prover_fut); - - // Attach the hyper HTTP client to the connection. - let (mut request_sender, connection) = - hyper::client::conn::http1::handshake(mpc_tls_connection).await?; - - // Spawn the HTTP task to be run concurrently in the background. - tokio::spawn(connection); - - // Build a simple HTTP request with common headers. - let request_builder = Request::builder() - .uri(uri) - .header("Host", SERVER_DOMAIN) - .header("Accept", "*/*") - // Using "identity" instructs the Server not to use compression for its HTTP response. - // TLSNotary tooling does not support compression. - .header("Accept-Encoding", "identity") - .header("Connection", "close") - .header("User-Agent", USER_AGENT); - let mut request_builder = request_builder; - for (key, value) in extra_headers { - request_builder = request_builder.header(key, value); - } - let request = request_builder.body(Empty::::new())?; - - println!("Starting an MPC TLS connection with the server"); - - // Send the request to the server and wait for the response. - let response = request_sender.send_request(request).await?; - - println!("Got a response from the server: {}", response.status()); - - assert!(response.status() == StatusCode::OK); - - // The prover task should be done now, so we can await it. - let mut prover = prover_task.await??; - - // Parse the HTTP transcript. - let transcript = HttpTranscript::parse(prover.transcript())?; - - let body_content = &transcript.responses[0].body.as_ref().unwrap().content; - let body = String::from_utf8_lossy(body_content.span().as_bytes()); - - match body_content { - tlsn_formats::http::BodyContent::Json(_json) => { - let parsed = serde_json::from_str::(&body)?; - debug!("{}", serde_json::to_string_pretty(&parsed)?); - } - tlsn_formats::http::BodyContent::Unknown(_span) => { - debug!("{}", &body); - } - _ => {} - } - - // Commit to the transcript. - let mut builder = TranscriptCommitConfig::builder(prover.transcript()); - - // This commits to various parts of the transcript separately (e.g. request - // headers, response headers, response body and more). See https://docs.tlsnotary.org//protocol/commit_strategy.html - // for other strategies that can be used to generate commitments. - DefaultHttpCommitter::default().commit_transcript(&mut builder, &transcript)?; - - let transcript_commit = builder.build()?; - - // Build an attestation request. - let mut builder = RequestConfig::builder(); - - builder.transcript_commit(transcript_commit); - - // Optionally, add an extension to the attestation if the notary supports it. - // builder.extension(Extension { - // id: b"example.name".to_vec(), - // value: b"Bobert".to_vec(), - // }); - - let request_config = builder.build()?; - - #[allow(deprecated)] - let (attestation, secrets) = prover.notarize(&request_config).await?; - - println!("Notarization complete!"); - - // Write the attestation to disk. - let attestation_path = tlsn_examples::get_file_path(example_type, "attestation"); - let secrets_path = tlsn_examples::get_file_path(example_type, "secrets"); - - tokio::fs::write(&attestation_path, bincode::serialize(&attestation)?).await?; - - // Write the secrets to disk. - tokio::fs::write(&secrets_path, bincode::serialize(&secrets)?).await?; - - println!("Notarization completed successfully!"); - println!( - "The attestation has been written to `{attestation_path}` and the \ - corresponding secrets to `{secrets_path}`." - ); - - Ok(()) -} diff --git a/crates/examples/attestation/verify.rs b/crates/examples/attestation/verify.rs deleted file mode 100644 index 6cede614b3..0000000000 --- a/crates/examples/attestation/verify.rs +++ /dev/null @@ -1,94 +0,0 @@ -// This example demonstrates how to verify a presentation. See `present.rs` for -// an example of how to build a presentation from an attestation and connection -// secrets. - -use std::time::Duration; - -use clap::Parser; - -use tls_core::verify::WebPkiVerifier; -use tls_server_fixture::CA_CERT_DER; -use tlsn::attestation::{ - presentation::{Presentation, PresentationOutput}, - signing::VerifyingKey, - CryptoProvider, -}; -use tlsn_examples::ExampleType; - -#[derive(Parser, Debug)] -#[command(version, about, long_about = None)] -struct Args { - /// What data to notarize. - #[clap(default_value_t, value_enum)] - example_type: ExampleType, -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - let args = Args::parse(); - - verify_presentation(&args.example_type).await -} - -async fn verify_presentation(example_type: &ExampleType) -> Result<(), Box> { - // Read the presentation from disk. - let presentation_path = tlsn_examples::get_file_path(example_type, "presentation"); - - let presentation: Presentation = bincode::deserialize(&std::fs::read(presentation_path)?)?; - - // Create a crypto provider accepting the server-fixture's self-signed - // root certificate. - // - // This is only required for offline testing with the server-fixture. In - // production, use `CryptoProvider::default()` instead. - let mut root_store = tls_core::anchors::RootCertStore::empty(); - root_store - .add(&tls_core::key::Certificate(CA_CERT_DER.to_vec())) - .unwrap(); - let crypto_provider = CryptoProvider { - cert: WebPkiVerifier::new(root_store, None), - ..Default::default() - }; - - let VerifyingKey { - alg, - data: key_data, - } = presentation.verifying_key(); - - println!( - "Verifying presentation with {alg} key: {}\n\n**Ask yourself, do you trust this key?**\n", - hex::encode(key_data) - ); - - // Verify the presentation. - let PresentationOutput { - server_name, - connection_info, - transcript, - // extensions, // Optionally, verify any custom extensions from prover/notary. - .. - } = presentation.verify(&crypto_provider).unwrap(); - - // The time at which the connection was started. - let time = chrono::DateTime::UNIX_EPOCH + Duration::from_secs(connection_info.time); - let server_name = server_name.unwrap(); - let mut partial_transcript = transcript.unwrap(); - // Set the unauthenticated bytes so they are distinguishable. - partial_transcript.set_unauthed(b'X'); - - let sent = String::from_utf8_lossy(partial_transcript.sent_unsafe()); - let recv = String::from_utf8_lossy(partial_transcript.received_unsafe()); - - println!("-------------------------------------------------------------------"); - println!( - "Successfully verified that the data below came from a session with {server_name} at {time}.", - ); - println!("Note that the data which the Prover chose not to disclose are shown as X.\n"); - println!("Data sent:\n"); - println!("{sent}\n"); - println!("Data received:\n"); - println!("{recv}\n"); - println!("-------------------------------------------------------------------"); - - Ok(()) -} diff --git a/crates/tlsn/src/verifier.rs b/crates/tlsn/src/verifier.rs index 62f764371c..7006721955 100644 --- a/crates/tlsn/src/verifier.rs +++ b/crates/tlsn/src/verifier.rs @@ -513,11 +513,11 @@ impl Verifier { if server_name.is_some() { return Err(VerifierError::attestation( - "server name can not be revealed to a notary", + "server name can not be revealed to a verifier", )); } else if transcript.is_some() { return Err(VerifierError::attestation( - "transcript data can not be revealed to a notary", + "transcript data can not be revealed to a verifier", )); }