From e32a32492efa555568948c0314da2921e59f6c50 Mon Sep 17 00:00:00 2001 From: ComplexSpaces Date: Fri, 18 Sep 2026 16:59:19 -0500 Subject: [PATCH 1/2] Prepare initial state of Maven Archive branch --- .cargo/config.toml | 3 - .github/dependabot.yml | 6 - .github/workflows/audit.yml | 20 - .github/workflows/ci.yml | 310 --- .gitignore | 8 +- CHANGELOG | 1 - Cargo.lock | 2034 ----------------- Cargo.toml | 6 - LICENSE-APACHE | 201 -- LICENSE-MIT | 21 - README.md | 332 +-- admin/MAINTENANCE.md | 31 - admin/RELEASING.md | 44 - android-release-support/Cargo.toml | 19 - android-release-support/LICENSE-APACHE | 1 - android-release-support/LICENSE-MIT | 1 - android-release-support/pom-template.xml | 10 - android-release-support/src/lib.rs | 67 - android-release-support/tests/codegen.rs | 62 - android/.gitignore | 11 - android/.run/All Tests.run.xml | 51 - android/build.gradle | 16 - android/gradle.properties | 23 - android/gradle/libraries.versions.toml | 6 - android/gradle/wrapper/gradle-wrapper.jar | Bin 61624 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 - android/gradlew | 244 -- android/gradlew.bat | 92 - android/rustls-platform-verifier/.gitignore | 2 - android/rustls-platform-verifier/build.gradle | 109 - .../proguard-rules.pro | 41 - .../CertificateVerifierTests.kt | 72 - .../src/main/AndroidManifest.xml | 7 - .../platformverifier/CertificateVerifier.kt | 460 ---- .../main/res/xml/network_security_config.xml | 387 ---- android/settings.gradle | 22 - ci/package_android_release.sh | 57 - ci/verify_android_release.sh | 41 - deny.toml | 18 - rustls-platform-verifier/Cargo.toml | 64 - rustls-platform-verifier/LICENSE-APACHE | 1 - rustls-platform-verifier/LICENSE-MIT | 1 - rustls-platform-verifier/README.md | 1 - .../examples/update-certs.rs | 53 - rustls-platform-verifier/src/android.rs | 250 -- rustls-platform-verifier/src/lib.rs | 92 - rustls-platform-verifier/src/tests/ffi.rs | 142 -- rustls-platform-verifier/src/tests/mod.rs | 60 - .../src/tests/verification_mock/ca.go | 314 --- .../src/tests/verification_mock/go.mod | 5 - .../src/tests/verification_mock/go.sum | 2 - .../src/tests/verification_mock/mod.rs | 412 ---- .../root1-int1-ee_1-good.crt | Bin 414 -> 0 bytes .../root1-int1-ee_1-good.ocsp | Bin 299 -> 0 bytes .../root1-int1-ee_1-revoked.crt | Bin 412 -> 0 bytes .../root1-int1-ee_1-revoked.ocsp | Bin 316 -> 0 bytes .../root1-int1-ee_1-wrong_eku.crt | Bin 413 -> 0 bytes .../root1-int1-ee_127.0.0.1-good.crt | Bin 401 -> 0 bytes .../root1-int1-ee_127.0.0.1-good.ocsp | Bin 299 -> 0 bytes .../root1-int1-ee_127.0.0.1-revoked.crt | Bin 401 -> 0 bytes .../root1-int1-ee_127.0.0.1-revoked.ocsp | Bin 316 -> 0 bytes .../root1-int1-ee_127.0.0.1-wrong_eku.crt | Bin 402 -> 0 bytes .../root1-int1-ee_example.com-good.crt | Bin 409 -> 0 bytes .../root1-int1-ee_example.com-good.ocsp | Bin 298 -> 0 bytes .../root1-int1-ee_example.com-revoked.crt | Bin 409 -> 0 bytes .../root1-int1-ee_example.com-revoked.ocsp | Bin 315 -> 0 bytes .../root1-int1-ee_example.com-wrong_eku.crt | Bin 408 -> 0 bytes .../tests/verification_mock/root1-int1.crt | Bin 441 -> 0 bytes .../src/tests/verification_mock/root1.crt | Bin 402 -> 0 bytes .../aws_amazon_com_valid_1.crt | Bin 1620 -> 0 bytes .../aws_amazon_com_valid_2.crt | Bin 1122 -> 0 bytes .../aws_amazon_com_valid_3.crt | Bin 1174 -> 0 bytes .../aws_amazon_com_valid_4.crt | Bin 1145 -> 0 bytes .../letsencrypt_org_valid_1.crt | Bin 1098 -> 0 bytes .../letsencrypt_org_valid_2.crt | Bin 656 -> 0 bytes .../letsencrypt_org_valid_3.crt | Bin 682 -> 0 bytes .../letsencrypt_org_valid_4.crt | Bin 1140 -> 0 bytes .../src/tests/verification_real_world/mod.rs | 290 --- .../revoked_badssl_com_1.crt | Bin 1674 -> 0 bytes .../revoked_badssl_com_1.ocsp | Bin 490 -> 0 bytes .../revoked_badssl_com_2.crt | Bin 1365 -> 0 bytes .../src/verification/android.rs | 340 --- .../src/verification/apple.rs | 313 --- .../src/verification/mod.rs | 86 - .../src/verification/others.rs | 184 -- .../src/verification/windows.rs | 798 ------- 86 files changed, 6 insertions(+), 8244 deletions(-) delete mode 100644 .cargo/config.toml delete mode 100644 .github/dependabot.yml delete mode 100644 .github/workflows/audit.yml delete mode 100644 .github/workflows/ci.yml delete mode 100644 CHANGELOG delete mode 100644 Cargo.lock delete mode 100644 Cargo.toml delete mode 100644 LICENSE-APACHE delete mode 100644 LICENSE-MIT delete mode 100644 admin/MAINTENANCE.md delete mode 100644 admin/RELEASING.md delete mode 100644 android-release-support/Cargo.toml delete mode 120000 android-release-support/LICENSE-APACHE delete mode 120000 android-release-support/LICENSE-MIT delete mode 100644 android-release-support/pom-template.xml delete mode 100644 android-release-support/src/lib.rs delete mode 100644 android-release-support/tests/codegen.rs delete mode 100644 android/.gitignore delete mode 100644 android/.run/All Tests.run.xml delete mode 100644 android/build.gradle delete mode 100644 android/gradle.properties delete mode 100644 android/gradle/libraries.versions.toml delete mode 100644 android/gradle/wrapper/gradle-wrapper.jar delete mode 100644 android/gradle/wrapper/gradle-wrapper.properties delete mode 100755 android/gradlew delete mode 100644 android/gradlew.bat delete mode 100644 android/rustls-platform-verifier/.gitignore delete mode 100644 android/rustls-platform-verifier/build.gradle delete mode 100644 android/rustls-platform-verifier/proguard-rules.pro delete mode 100644 android/rustls-platform-verifier/src/androidTest/java/org/rustls/platformverifier/CertificateVerifierTests.kt delete mode 100644 android/rustls-platform-verifier/src/main/AndroidManifest.xml delete mode 100644 android/rustls-platform-verifier/src/main/java/org/rustls/platformverifier/CertificateVerifier.kt delete mode 100644 android/rustls-platform-verifier/src/main/res/xml/network_security_config.xml delete mode 100644 android/settings.gradle delete mode 100755 ci/package_android_release.sh delete mode 100755 ci/verify_android_release.sh delete mode 100644 deny.toml delete mode 100644 rustls-platform-verifier/Cargo.toml delete mode 120000 rustls-platform-verifier/LICENSE-APACHE delete mode 120000 rustls-platform-verifier/LICENSE-MIT delete mode 120000 rustls-platform-verifier/README.md delete mode 100644 rustls-platform-verifier/examples/update-certs.rs delete mode 100644 rustls-platform-verifier/src/android.rs delete mode 100644 rustls-platform-verifier/src/lib.rs delete mode 100644 rustls-platform-verifier/src/tests/ffi.rs delete mode 100644 rustls-platform-verifier/src/tests/mod.rs delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/ca.go delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/go.mod delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/go.sum delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/mod.rs delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-good.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-good.ocsp delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-revoked.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-revoked.ocsp delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-wrong_eku.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-good.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-good.ocsp delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-revoked.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-revoked.ocsp delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-wrong_eku.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-good.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-good.ocsp delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-revoked.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-revoked.ocsp delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-wrong_eku.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1-int1.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_mock/root1.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_real_world/aws_amazon_com_valid_1.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_real_world/aws_amazon_com_valid_2.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_real_world/aws_amazon_com_valid_3.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_real_world/aws_amazon_com_valid_4.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_real_world/letsencrypt_org_valid_1.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_real_world/letsencrypt_org_valid_2.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_real_world/letsencrypt_org_valid_3.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_real_world/letsencrypt_org_valid_4.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_real_world/mod.rs delete mode 100644 rustls-platform-verifier/src/tests/verification_real_world/revoked_badssl_com_1.crt delete mode 100644 rustls-platform-verifier/src/tests/verification_real_world/revoked_badssl_com_1.ocsp delete mode 100644 rustls-platform-verifier/src/tests/verification_real_world/revoked_badssl_com_2.crt delete mode 100644 rustls-platform-verifier/src/verification/android.rs delete mode 100644 rustls-platform-verifier/src/verification/apple.rs delete mode 100644 rustls-platform-verifier/src/verification/mod.rs delete mode 100644 rustls-platform-verifier/src/verification/others.rs delete mode 100644 rustls-platform-verifier/src/verification/windows.rs diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 42bbb13f..00000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,3 +0,0 @@ -[alias] -clippy-ci = "clippy --all-features --all-targets --all" -clippy-msrv-ci = "clippy --all-features --lib --all" # Only check --lib target w/ MSRV toolchain. diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index e5903e0b..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: 2 -updates: -- package-ecosystem: github-actions - directory: "/" - schedule: - interval: weekly diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml deleted file mode 100644 index 9d7be827..00000000 --- a/.github/workflows/audit.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: cargo deny -permissions: - contents: read -on: - schedule: - - cron: '0 0 * * 0' - push: - paths: - - '**/Cargo.toml' - - '**/Cargo.lock' - pull_request: - -jobs: - audit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - uses: EmbarkStudios/cargo-deny-action@v2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 0dd8924a..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,310 +0,0 @@ -on: - push: - branches: ['main', 'ci/*'] - pull_request: - merge_group: - schedule: - - cron: '0 18 * * *' - workflow_dispatch: - -name: CI -permissions: - contents: read - -env: - RUSTFLAGS: -D warnings - -jobs: - clippy-build-std: - name: Clippy (-Zbuild-std) - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - - uses: dtolnay/rust-toolchain@nightly - with: - components: clippy - - - name: Clippy (tvOS) - run: | - rustup component add rust-src --toolchain nightly-aarch64-apple-darwin - cargo +nightly clippy -Zbuild-std --target aarch64-apple-tvos - - - name: Clippy (watchOS) - run: | - rustup component add rust-src --toolchain nightly-aarch64-apple-darwin - cargo +nightly clippy -Zbuild-std --target aarch64-apple-watchos - - - name: Clippy (visionOS) - run: | - rustup component add rust-src --toolchain nightly-aarch64-apple-darwin - cargo +nightly clippy -Zbuild-std --target aarch64-apple-visionos - - clippy: - name: Clippy (stable) - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: - - ubuntu-latest - - macos-latest - - windows-latest - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - - uses: dtolnay/rust-toolchain@stable - with: - components: clippy - - - name: Clippy (${{ matrix.os }}) - run: cargo clippy-ci - - - name: Clippy (Android) - if: matrix.os == 'ubuntu-latest' - run: | - rustup target add aarch64-linux-android - cargo install cargo-ndk - cargo ndk -t arm64-v8a clippy-ci - - - name: Clippy (iOS) - if: matrix.os == 'macos-latest' - run: | - rustup target add x86_64-apple-ios - cargo clippy-ci --target x86_64-apple-ios - - - name: Clippy (WASM) - if: matrix.os == 'ubuntu-latest' - run: | - rustup target add wasm32-wasip1 - cargo clippy --lib --target wasm32-wasip1 -- -D warnings - - # Update any flexible dependencies that may have SemVer applicable changes - # to ensure the project keeps building with the whole version range. This is: - # - windows-sys 0.52, which `rustls-platform-verifier` also should accept at 0.59. - - name: Clippy (dependency version checks) - run: | - cargo update -p windows-sys@0.52.0 - cargo clippy-ci - - clippy-msrv: - name: Clippy (MSRV) - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: - - ubuntu-latest - - macos-latest - - windows-latest - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - - uses: dtolnay/rust-toolchain@master - with: - toolchain: "1.85.0" # MSRV - components: clippy - - - name: Install cargo-ndk. - run: | - cargo install cargo-ndk --locked --version 2.12.7 - rustup target add aarch64-linux-android - - - name: Clippy (${{ matrix.os }}) - run: cargo clippy-msrv-ci - - - name: Clippy (Android) - if: matrix.os == 'ubuntu-latest' - run: | - cargo ndk -t arm64-v8a clippy-msrv-ci - - - name: Clippy (iOS) - if: matrix.os == 'macos-latest' - run: | - rustup target add aarch64-apple-ios - cargo clippy-msrv-ci --target aarch64-apple-ios - - # TODO: Consider WASM. See note on "clippy" job. - - test: - name: Test - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: - - ubuntu-latest - - macos-latest - - windows-latest - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - - uses: dtolnay/rust-toolchain@stable - - - name: Test (${{ matrix.os }}) - run: cargo test - - name: Update Android security manifest - if: matrix.os == 'ubuntu-latest' - run: cargo test --manifest-path android-release-support/Cargo.toml --test codegen -- --ignored - - test_android: - name: "Test (Android)" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - # Turn on Linux KVM features/support for faster Android emulation. - # References: - # - https://github.com/DeterminateSystems/nix-installer-action/blob/de22e16c4711fca50c816cc9081563429d1cf563/src/main.ts#L756 - # - https://github.com/ReactiveCircus/android-emulator-runner#running-hardware-accelerated-emulators-on-linux-runners - - name: Enable KVM - run: | - echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules - sudo udevadm control --reload-rules - sudo udevadm trigger --name-match=kvm - - - name: Setup Java - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '17' - - - name: Run Android tests - uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # 2.38.0 - with: - api-level: 28 # Android 9, Pie. - arch: x86_64 - profile: pixel - emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: true - working-directory: ./android - script: | - rustup target add x86_64-linux-android - cargo install cargo-ndk - env | grep '^JAVA' - touch emulator.log - chmod 770 emulator.log - adb logcat --clear - adb logcat | grep 'rustls' | tee emulator.log & - ./gradlew connectedDebugAndroidTest - - - name: Upload Android test results - uses: actions/upload-artifact@v4 - # Upload test results if they fail - if: failure() - with: - name: android-test-results - retention-days: 7 - path: | - ./android/emulator.log - /Users/runner/work/rustls-platform-verifier/rustls-platform-verifier/android/rustls-platform-verifier/build/outputs/androidTest-results/connected/test-result.pb - - test_ios: - name: "Test iOS (Catalyst)" - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - name: Run iOS tests - run: | - rustup target add aarch64-apple-ios-macabi - cargo test --target aarch64-apple-ios-macabi - - test-freebsd: - name: Test (FreeBSD) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - name: test on freebsd - uses: vmactions/freebsd-vm@v1 - # Settings adopted from https://github.com/quinn-rs/quinn - with: - usesh: true - mem: 4096 - copyback: false - prepare: | - pkg install -y curl - curl https://sh.rustup.rs -sSf --output rustup.sh - sh rustup.sh -y --profile minimal --default-toolchain stable - echo "~~~~ rustc --version ~~~~" - $HOME/.cargo/bin/rustc --version - echo "~~~~ freebsd-version ~~~~" - freebsd-version - run: $HOME/.cargo/bin/cargo test - - fmt: - name: Rustfmt - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt - - - run: cargo fmt --all -- --check - - android_fmt: - name: Ktlint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - - name: Ktlint - run: | - cd ./android - ./gradlew ktlint - - verify_android: - name: Verify Android artifacts - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - persist-credentials: false - - - name: Verify release artifact - run: ./ci/verify_android_release.sh - - semver: - name: Check semver compatibility - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v4 - with: - persist-credentials: false - - - name: Check semver - uses: obi1kenobi/cargo-semver-checks-action@v2 - - docs: - name: Check for documentation errors - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v4 - with: - persist-credentials: false - - - name: Install rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - name: Build documentation - run: cargo doc --locked --all-features --no-deps --document-private-items - env: - RUSTDOCFLAGS: -Dwarnings --cfg=docsrs diff --git a/.gitignore b/.gitignore index 1263ab66..03f1d64c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,13 +2,7 @@ .DS_Store /.idea -/android/verification/ - -# Ignore all generated Maven local repository files and folders -/android-release-support/maven/org/rustls/rustls-platform-verifier/* -# These two must be kept since the state must be shared between normal branches and the Maven archive one. -!/android-release-support/maven/org/rustls/rustls-platform-verifier/maven-metadata.xml -!/android-release-support/maven/org/rustls/rustls-platform-verifier/maven-metadata-local.xml +/android/ # Nix /result diff --git a/CHANGELOG b/CHANGELOG deleted file mode 100644 index 028e6f5c..00000000 --- a/CHANGELOG +++ /dev/null @@ -1 +0,0 @@ -The detailed list of changes in each release can be found at https://github.com/rustls/rustls-platform-verifier/releases. \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 611a5704..00000000 --- a/Cargo.lock +++ /dev/null @@ -1,2034 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "aho-corasick" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" -dependencies = [ - "memchr", -] - -[[package]] -name = "android_log-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" - -[[package]] -name = "android_logger" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb4e440d04be07da1f1bf44fb4495ebd58669372fe0cffa6e48595ac5bd88a3" -dependencies = [ - "android_log-sys", - "env_filter", - "log", -] - -[[package]] -name = "asn1-rs" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8" -dependencies = [ - "asn1-rs-derive", - "asn1-rs-impl", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror", - "time", -] - -[[package]] -name = "asn1-rs-derive" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", - "synstructure 0.13.2", -] - -[[package]] -name = "asn1-rs-impl" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" - -[[package]] -name = "aws-lc-rs" -version = "1.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", - "pkg-config", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" - -[[package]] -name = "bstr" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" -dependencies = [ - "memchr", - "regex-automata", - "serde_core", -] - -[[package]] -name = "bumpalo" -version = "3.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" - -[[package]] -name = "bytes" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" - -[[package]] -name = "cc" -version = "1.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54413ede23c2daf518f35156dfde027feb2374004d63bd497f983c8db9c0e313" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" - -[[package]] -name = "cfg_aliases" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" - -[[package]] -name = "chacha20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" -dependencies = [ - "cfg-if", - "cpufeatures", - "rand_core", -] - -[[package]] -name = "cmake" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" -dependencies = [ - "cc", -] - -[[package]] -name = "combine" -version = "4.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "console" -version = "0.16.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96a4956774c13c126a8b5af4daa79384f4d826534c95a02d76afb39e2ab64e3" -dependencies = [ - "encode_unicode", - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" -dependencies = [ - "libc", -] - -[[package]] -name = "csv" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde_core", -] - -[[package]] -name = "csv-core" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" -dependencies = [ - "memchr", -] - -[[package]] -name = "data-encoding" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" - -[[package]] -name = "defmt" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" -dependencies = [ - "bitflags 1.3.2", - "defmt-macros", -] - -[[package]] -name = "defmt-macros" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" -dependencies = [ - "defmt-parser", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "defmt-parser" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" -dependencies = [ - "thiserror", -] - -[[package]] -name = "der-parser" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" -dependencies = [ - "asn1-rs", - "displaydoc", - "nom", - "num-bigint 0.4.8", - "num-traits", - "rusticata-macros", -] - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" - -[[package]] -name = "displaydoc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", -] - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "encode_unicode" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" - -[[package]] -name = "env_filter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25905e51abafe4dcea6c15fec58c57b601cdbd0ee53d22ea1d3016c587d39b" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "futures-channel" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" - -[[package]] -name = "futures-task" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" - -[[package]] -name = "futures-util" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "rand_core", - "wasm-bindgen", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "http" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "hyper" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "icu_collections" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" - -[[package]] -name = "icu_properties" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" - -[[package]] -name = "icu_provider" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "ipnet" -version = "2.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jiff" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1baf72f08796de0260609515130699b890ac25f30e610ad894bc5856cafdb" -dependencies = [ - "defmt", - "jiff-core", - "jiff-static", - "log", - "portable-atomic", - "portable-atomic-util", - "serde_core", -] - -[[package]] -name = "jiff-core" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e52fe76043ccecc9005d2305ebaadf7d7fc0cc89ca6baa10a94d6bc68c7128c" -dependencies = [ - "defmt", - "log", -] - -[[package]] -name = "jiff-static" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "378268a1116ad67ae6228701118ac9f491d78fda38a40a1f1a9e1348de6f7212" -dependencies = [ - "jiff-core", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "jni" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" -dependencies = [ - "cfg-if", - "combine", - "jni-macros", - "jni-sys", - "log", - "simd_cesu8", - "thiserror", - "walkdir", - "windows-link", -] - -[[package]] -name = "jni-macros" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" -dependencies = [ - "proc-macro2", - "quote", - "rustc_version", - "simd_cesu8", - "syn 2.0.119", -] - -[[package]] -name = "jni-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" -dependencies = [ - "jni-sys-macros", -] - -[[package]] -name = "jni-sys-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" -dependencies = [ - "quote", - "syn 2.0.119", -] - -[[package]] -name = "jobserver" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" -dependencies = [ - "getrandom 0.4.3", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" -dependencies = [ - "cfg-if", - "futures-util", - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "libc" -version = "0.2.189" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" - -[[package]] -name = "litemap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" - -[[package]] -name = "log" -version = "0.4.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" - -[[package]] -name = "lru-slab" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f" - -[[package]] -name = "memchr" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "mio" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-bigint" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e7820bc0a80a0238e650327316f929ba18d5be054b647490a3a6a339f3e7c0" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" - -[[package]] -name = "num-integer" -version = "0.1.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "oid-registry" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" -dependencies = [ - "asn1-rs", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "pkg-config" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" - -[[package]] -name = "portable-atomic" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" - -[[package]] -name = "portable-atomic-util" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" -dependencies = [ - "portable-atomic", -] - -[[package]] -name = "potential_utf" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "proc-macro2" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quinn" -version = "0.11.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4051e23e9185c255a7e33ef59cdbca87a22d359052eecd22fc6b901fb37d9d11" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9746dbde176634f4f2f1faf2404e30a31b2bc1e9cafb5329c95d8177a18c9fc" -dependencies = [ - "aws-lc-rs", - "bytes", - "getrandom 0.4.3", - "lru-slab", - "rand", - "rand_pcg", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.61.2", -] - -[[package]] -name = "quote" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rand" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" -dependencies = [ - "chacha20", - "getrandom 0.4.3", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" - -[[package]] -name = "rand_pcg" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" -dependencies = [ - "rand_core", -] - -[[package]] -name = "regex" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" - -[[package]] -name = "reqwest" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029" -dependencies = [ - "base64 0.23.1", - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde", - "serde_json", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustc-hash" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rusticata-macros" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" -dependencies = [ - "nom", -] - -[[package]] -name = "rustls" -version = "0.23.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" -dependencies = [ - "aws-lc-rs", - "once_cell", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pki-types" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-platform-verifier" -version = "0.7.0" -dependencies = [ - "android_logger", - "base64 0.22.1", - "core-foundation", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android 0.1.1", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" -dependencies = [ - "core-foundation", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -dependencies = [ - "rustls", - "similar-asserts", - "tokio", - "webpki-ccadb", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - -[[package]] -name = "rustls-webpki" -version = "0.103.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" -dependencies = [ - "aws-lc-rs", - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags 2.13.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - -[[package]] -name = "serde" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", -] - -[[package]] -name = "serde_json" -version = "1.0.151" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "simd_cesu8" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" -dependencies = [ - "rustc_version", - "simdutf8", -] - -[[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - -[[package]] -name = "similar" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f66ca1f7aca2474dc10c942eb22feffc897735f54cd1db90138c2fddb490987" -dependencies = [ - "bstr", - "unicode-segmentation", -] - -[[package]] -name = "similar-asserts" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "997e6ca38e97437973fc9f7f50a50d1274cacd874341a4960fea90067291038c" -dependencies = [ - "console", - "similar", -] - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba467056f1b547ed52077911161fc86985becbc60e8e1857c8a144dab0def891" - -[[package]] -name = "socket2" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.119" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "synstructure" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", -] - -[[package]] -name = "thiserror" -version = "2.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", -] - -[[package]] -name = "time" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" -dependencies = [ - "deranged", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" - -[[package]] -name = "time-macros" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" - -[[package]] -name = "tokio" -version = "1.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c85f2c3ef0b1cd58b36682f4b17aaa995f0e5db534d85692b4903abce21f67" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" -dependencies = [ - "bitflags 2.13.2", - "bytes", - "futures-util", - "http", - "http-body", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", - "url", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "unicode-ident" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" - -[[package]] -name = "unicode-segmentation" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasm-bindgen" -version = "0.2.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef4c5d3d2cdf5c54f4231181768f5510842e350db025faf1f7163b1030ed928" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 3.0.6", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "web-sys" -version = "0.3.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbddc4a036f00ec4f18c83445bd3115cb306a91da554919a099d9222fe4a7f8" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-ccadb" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b22a93e00e215769c99c46f74e90a20153f97a21c0f9881530d27ae2738c9010" -dependencies = [ - "csv", - "hex", - "jiff", - "num-bigint 0.5.1", - "reqwest", - "rustls-pki-types", - "rustls-webpki", - "serde", - "serde_json", - "url", - "x509-parser", - "yasna", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "writeable" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" - -[[package]] -name = "x509-parser" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" -dependencies = [ - "asn1-rs", - "data-encoding", - "der-parser", - "lazy_static", - "nom", - "oid-registry", - "rusticata-macros", - "thiserror", - "time", -] - -[[package]] -name = "yasna" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282" - -[[package]] -name = "yoke" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33811428bee40dbceb6d545e95754741d17a6aef9a4849f0fd62e2ba4f412a78" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", - "synstructure 0.14.0", -] - -[[package]] -name = "zerofrom" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", - "synstructure 0.14.0", -] - -[[package]] -name = "zeroize" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" - -[[package]] -name = "zerotrie" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.6", -] - -[[package]] -name = "zmij" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 91d9d305..00000000 --- a/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[workspace] -members = [ - "android-release-support", - "rustls-platform-verifier", -] -resolver = "2" diff --git a/LICENSE-APACHE b/LICENSE-APACHE deleted file mode 100644 index 261eeb9e..00000000 --- a/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT deleted file mode 100644 index 99641226..00000000 --- a/LICENSE-MIT +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 1Password - -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. diff --git a/README.md b/README.md index f77c9ba0..3c933e99 100644 --- a/README.md +++ b/README.md @@ -1,329 +1,7 @@ -# rustls-platform-verifier +# rustls-platform-verifier Maven Archive -[![crates.io version](https://img.shields.io/crates/v/rustls-platform-verifier.svg)](https://crates.io/crates/rustls-platform-verifier) -[![crate documentation](https://docs.rs/rustls-platform-verifier/badge.svg)](https://docs.rs/rustls-platform-verifier) -![MSRV](https://img.shields.io/badge/rustc-1.85+-blue.svg) -[![crates.io downloads](https://img.shields.io/crates/d/rustls-platform-verifier.svg)](https://crates.io/crates/rustls-platform-verifier) -![CI](https://github.com/1Password/rustls-platform-verifier/workflows/CI/badge.svg) +This branch hosts a Maven package repository for the native Android component of `rustls-platform-verifier`. Source code and other parts of +this repository's normal branches are not included here. -A Rust library to verify the validity of TLS certificates based on the operating system's certificate facilities. -On operating systems that don't have these, `webpki` and/or `rustls-native-certs` is used instead. - -This crate is advantageous over `rustls-native-certs` on its own for a few reasons: -- Improved correctness and security, as the OSes [CA constraints](https://support.apple.com/en-us/HT212865) will be taken into account. -- Better integration with OS certificate stores and enterprise CA deployments. -- Revocation support via verifying validity via OCSP and CRLs. -- Less I/O and memory overhead because all the platform CAs don't need to be loaded and parsed. - -This library supports the following platforms and flows: - -| OS | Certificate Store | Verification Method | Revocation Support | -|----------------|-----------------------------------------------|--------------------------------------|--------------------| -| Windows | Windows platform certificate store | Windows API certificate verification | Yes | -| macOS (10.14+) | macOS platform roots and keychain certificate | macOS `Security.framework` | Yes | -| iOS | iOS platform roots and keychain certificates | iOS `Security.framework` | Yes | -| Android | Android System Trust Store | Android Trust Manager | Sometimes[^1] | -| Linux | System CA bundle, or user-provided certs[^3] | webpki | No[^2] | -| WASM | webpki roots | webpki | No[^2] | - -[^1]: On Android, revocation checking requires API version >= 24 (e.g. at least Android 7.0, August 2016). -When available, revocation checking is only performed for the end-entity certificate. If a stapled OCSP -response for the end-entity cert isn't provided, and the certificate omits both a OCSP responder URL and -CRL distribution point to fetch revocation information from, revocation checking may fail. - -[^2]: The fall-back webpki verifier configured for Linux/WASM does not support providing CRLs for revocation -checking. If you require revocation checking on these platforms, prefer constructing your own -`WebPkiServerVerifier`, providing necessary CRLs. See the Rustls [`ServerCertVerifierBuilder`] docs for more -information. - -[^3]: On Linux the [rustls-native-certs] and [openssl-probe] crates are used to try and discover the system CA bundle. -Users may wish to augment these certificates with [webpki-roots] using [`Verifier::new_with_extra_roots`] in case -a system CA bundle is unavailable. - -[`ServerCertVerifierBuilder`]: https://docs.rs/rustls/latest/rustls/client/struct.ServerCertVerifierBuilder.html -[`Verifier::new_with_extra_roots`]: https://docs.rs/rustls-platform-verifier/latest/rustls_platform_verifier/struct.Verifier.html#method.new_with_extra_roots -[rustls-native-certs]: https://github.com/rustls/rustls-native-certs -[openssl-probe]: https://github.com/alexcrichton/openssl-probe -[webpki-roots]: https://github.com/rustls/webpki-roots - -## Deployment Considerations - -When choosing to use `rustls-platform-verifier` or another trust store option, these differences are important to consider. They -are primarily about root certificate availability: - -| Backend | Updates | Roots used | Supports system-local roots | -|-------------------------------------------------|---------------------------------|-------------------------------------------------------------------------------------------------------|------------------------------| -| `rustls-platform-verifier` (non-Linux/BSD) | Updated by OS | System store, with full (dis)trust decisions from every source available. | Yes | -| `rustls-native-certs` + `webpki` | Updated by OS | System store, with no (dis)trust decisions. All roots are treated equally regardless of their status. | Yes, with exceptions | -| `webpki-roots` + `webpki` | Static, manual updates required | Hardcoded Mozilla CA roots, limited support for constrained roots. | No | - -**In general**: It is the opinion of the `rustls` and `rustls-platform-verifier` teams that this is the best default available for client-side libraries and applications -making connections to TLS servers when running on common operating systems. This is because it gets both live trust information (new roots, explicit markers, and auto-managed CRLs) -and better matches the common expectation of apps running on that platform (to use proxies, for example). Otherwise, it becomes your maintenance burden to -ship updates right away in order to handle increasing numbers of positive and negative trust events in the WebPKI/certificate ecosystem, or risk availability and security concerns. - -#### Linux/BSD -As of the time of writing, `rustls-platform-verifier` on these OSes only loads the trust stores from the OS once upon startup. This is the same behavior as `rustls-native-certs`, but the -abstraction allows better behavior on the other platforms without extra work for downstreams. - -#### Other - -Alternatively, there is a clear answer to use static `webpki-roots` in your application instead if you are deploying containerized applications frequently, where root store changes -will make it to production faster and any possibly used trust root is static by definition. - -Even though platform verifiers are sometimes implemented in memory-unsafe languages, it is very unlikely that Rust apps using this library will become a point of weakness. -This is due to either using a smaller set of servers or just being less exposed then other critical functions of the operating system, default web browser, etc. -But if your activity is identical or close to one of the following examples that process large amounts of untrusted input, a 100% Rust option like `webpki` is a more secure option: -- Seeing how many TLS servers `rustls` with a specific configuration can connect to. -- Harvesting data from various untrusted TLS endpoints exposed on the internet. -- Extracting info from a known-evil endpoint. -- Scanning all TLS certificates on the open internet. - -`rustls-platform-verifier` is widely deployed by several applications that use the `rustls` stack, such as 1Password, Bitwarden, Signal, and `rustup`, on a wide set of OSes. -This means that it has received lots of exposure to edge cases and has real-world experience/expertise invested into it to ensure optimal compatibility and security. - -## Installation and setup -On most platforms, no setup should be required beyond adding the dependency via `cargo`: -```toml -rustls-platform-verifier = "0.5" -``` - -To get a rustls `ClientConfig` configured to use the platform verifier use: - -```rust -use rustls::ClientConfig; -use rustls_platform_verifier::ConfigVerifierExt; -let config = ClientConfig::with_platform_verifier(); -``` - -This crate will use the [rustls process-default crypto provider](https://docs.rs/rustls/latest/rustls/crypto/struct.CryptoProvider.html#using-the-per-process-default-cryptoprovider). To construct a `ClientConfig` with a different `CryptoProvider`, use: - -```rust -use rustls::ClientConfig; -use rustls_platform_verifier::BuilderVerifierExt; -let arc_crypto_provider = std::sync::Arc::new(rustls::crypto::aws_lc_rs::default_provider()); -let config = ClientConfig::builder_with_provider(arc_crypto_provider) - .with_safe_default_protocol_versions() - .unwrap() - .with_platform_verifier() - .unwrap() - .with_no_client_auth(); -``` - -### Android -Some manual setup is required, outside of `cargo`, to use this crate on Android. In order to -use Android's certificate verifier, the crate needs to call into the JVM. A small Kotlin -component must be included in your app's build to support `rustls-platform-verifier`. - -#### Gradle Setup - -`rustls-platform-verifier` distributes the required native components in a Maven-compatible format via GitHub, but the project must be setup to locate them -automatically and correctly. These steps assume you are using `.gradle` Groovy files because they're the most common, but if you are using -Kotlin scripts (`.gradle.kts`) for configuration instead, an example snippet is included towards the end of this section. - -Each snippet includes a [`ValueSource`](https://docs.gradle.org/current/javadoc/org/gradle/api/provider/ValueSource.html) implementation that obtains a -Cargo-synchronized dependency version performantly, and is also friendly to Gradle's configuration cache. The version can be be selected manually instead, -but runtime crashes may occur if a SemVer incompatible version is used. - -Inside of your project's `build.gradle` file, add the following code and Maven repository definition: - -`$PATH_TO_LOCK_FILE` is the relative path to the Cargo lockfile of your crate or workspace (`Cargo.lock`). - -```groovy - -repositories { - maven { - url = "https://github.com/rustls/rustls-platform-verifier/raw/maven-archive/android-release-support/maven/" - } -} - -abstract class RustlsVersion implements ValueSource { - interface Params extends ValueSourceParameters { - RegularFileProperty getLockFile() - } - - static final String CRATE_NAME = "rustls-platform-verifier-android" - - @Override - String obtain() { - def lockFile = parameters.lockFile.get().asFile - def lines = lockFile.readLines() - def idx = lines.findIndexOf { it.trim() == "name = \"$CRATE_NAME\"" } - def version = idx < 0 ? null : lines.drop(idx + 1) - .find { it.stripLeading().startsWith("version = ") } - ?.find(/"([^"]*)"/) { match, v -> v } - if (!version) throw new GradleException("$CRATE_NAME not found in $lockFile") - return version - } -} - -def rustlsPlatformVerifierVersion = providers.of(RustlsVersion) { spec -> - spec.parameters.lockFile.set(layout.projectDirectory.file($PATH_TO_LOCK_FILE)) -} - -configurations.configureEach { configuration -> - configuration.resolutionStrategy.eachDependency { details -> - if (details.requested.group == "org.rustls" && details.requested.name == "rustls-platform-verifier") { - details.useVersion(rustlsPlatformVerifierVersion.get()) - details.because("native component version must be identical to version of ${RustlsVersion.CRATE_NAME}") - } - } -} -``` - -Then, wherever you declare your dependencies, add the following: -```groovy -implementation "rustls:rustls-platform-verifier" -``` - -The dependency intentionally has no static version, it is only resolved dynamically at configuration time by the build script. - -Cargo automatically handles finding the downloaded crate in the correct location for your project. It also handles updating the version when -new releases of `rustls-platform-verifier` are published. If you only use published releases, no extra maintenance should be required. - -These script snippets can be tweaked as best suits your project, but the `cargo metadata` invocation must be included so that the Android -implementation part can be located on-disk. - -##### Kotlin and Gradle - -`build.gradle.kts`: -```kotlin - -repositories { - maven { - url = uri("https://github.com/rustls/rustls-platform-verifier/raw/maven-archive/android-release-support/maven/") - } -} - -abstract class RustlsVersion : ValueSource { - interface Params : ValueSourceParameters { - val lockFile: RegularFileProperty - } - - companion object { - const val CRATE_NAME = "rustls-platform-verifier-android" - } - - override fun obtain(): String { - val version = parameters.lockFile.get().asFile.readLines().let { lines -> - val nameIdx = lines.indexOfFirst { it.trim() == "name = \"$CRATE_NAME\"" } - if (nameIdx < 0) { - null - } else { - lines.drop(nameIdx + 1) - .firstOrNull { it.trimStart().startsWith("version = ") } - ?.substringAfter('"', "") - ?.substringBefore('"', "") - ?.takeIf { it.isNotEmpty() } - } - } - return version?: error("$CRATE_NAME not found in Cargo.lock") - } -} - -val rustlsPlatformVerifierVersion = providers.of(RustlsVersion::class.java) { - parameters.lockFile.set(layout.projectDirectory.file($PATH_TO_LOCK_FILE)) -} - -configurations.configureEach { - resolutionStrategy.eachDependency { - if (requested.group == "org.rustls" && requested.name == "rustls-platform-verifier") { - useVersion(rustlsPlatformVerifierVersion.get()) - because("native component version must be identical to version of ${RustlsVersion.CRATE_NAME}") - } - } -} - -dependencies { - // `rustls-platform-verifier` is a Rust crate, but it also has a Kotlin component. - implementation(libs.rustls.platform.verifier) -} -``` - -`libs.version.toml`: -```toml -# We keep the dependency unversioned because its version is selected dynamically during configuration. -rustls-platform-verifier = { group = "rustls", name = "rustls-platform-verifier" } -``` - -#### Proguard - -If your Android application makes use of Proguard for optimizations, its important to make sure that the Android verifier component isn't optimized -out because it looks like dead code. Proguard is unable to see any JNI usage, so your rules must manually opt into keeping it. The following rule -can do this for you: -```text --keep, includedescriptorclasses class org.rustls.platformverifier.** { *; } -``` - -#### Crate initialization - -In order for the crate to call into the JVM, it needs handles from Android. These -are provided by one of the `init_with_env`, `init_with_refs` or `init_with_runtime` functions. These give `rustls-platform-verifier` -the resources it needs to make calls into the Android certificate verifier. - -As an example, if your Rust Android component which the "native" Android -part of your app calls at startup has an initialization, like this: - -```rust ,ignore -use jni::jni_mangle; -use jni::objects::{JClass, JObject}; -use jni::EnvUnowned; - -#[jni_mangle("com.orgname.application.Application")] -pub fn init<'caller>( - mut unowned_env: EnvUnowned<'caller>, - _class: JClass<'caller>, - context: JObject<'caller>, -) { - // ... initialize your app's other parts here. -} -``` - -In the simplest case, you should to insert a call to `rustls_platform_verifier::android::init_with_env()` here, -before any networking has a chance to run. This only needs to be called once and -the verifier will be valid for the lifetime of your app's process. - -```rust ,ignore -use jni::errors::ThrowRuntimeExAndDefault; -use jni::jni_mangle; -use jni::objects::{JClass, JObject}; -use jni::EnvUnowned; - -#[jni_mangle("com.orgname.application.Application")] -pub fn init<'caller>( - mut unowned_env: EnvUnowned<'caller>, - _class: JClass<'caller>, - context: JObject<'caller>, -) { - // ... initialize your app's other parts here. - - // Then, initialize the certificate verifier for future use. - unowned_env - .with_env(|env| rustls_platform_verifier::android::init_with_env(env, context)) - .resolve::(); -} -``` - -In more advanced cases, such as where your code already stores long-lived handles into -the Android environment, you can alternatively use `init_with_runtime`. This function takes -a `&'static` reference to something that implements the `android::Runtime` trait, which the -crate then uses to obtain the access when required to the JVM. - -## Credits -Made with ❤️ by the [1Password](https://1password.com/) and `rustls` teams. Portions of the Android and Windows verifier -implementations were adapted and referenced from Chromium's previous verifier implementations as well. - -#### License - - -Licensed under either of Apache License, Version -2.0 or MIT license at your option. - - -
- - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in this crate by you, as defined in the Apache-2.0 license, shall -be dual licensed as above, without any additional terms or conditions. - +See the [Gradle setup documentation](https://github.com/rustls/rustls-platform-verifier#gradle-setup) to learn how to include this repository +in your Android app or library's build. diff --git a/admin/MAINTENANCE.md b/admin/MAINTENANCE.md deleted file mode 100644 index dd5c926c..00000000 --- a/admin/MAINTENANCE.md +++ /dev/null @@ -1,31 +0,0 @@ -## How to handle certificate expiry - -When CI starts spuriously failing, it is usually caused by the certificates inside `src/tests/vertification_real_world` reaching their max issuance lifetime and becoming expired. While most -of our tested platforms are able to handle this better by mocking out the verification time, some can't. At the time of writing these are: -- Android ([1](https://github.com/rustls/rustls-platform-verifier/issues/59), [2](https://github.com/rustls/rustls-platform-verifier/issues/183)) -- Windows ([1](https://github.com/rustls/rustls-platform-verifier/issues/117)) - -The other case that can cause failures (much less often) is the mock certificates expiring. Due to platform verifier security restrictions, we can't place absurdly high/unlimited expiry dates -on our mock CA and the certificates issued by it. As such, they will expire about every 2 years and need updated by hand. - -Thankfully, updating these has become easy: -- If the `verification_real_world` tests are failing, do the following: - 1. Run `cargo run --example update-certs` - 2. Using your tool of choice, update the hardcoded time in `verification_time` to match the current datetime. - 3. Commit your changes and push up a fix branch/PR. -- If the `verification_mock` tests are failing, do the following: - 1. Run `cd rustls-platform-verifier/src/tests/verification_mock` - 2. Run `go run ca.go` - 3. Using your tool of choice, update the hardcoded time in `verification_time` to match the current datetime. - 4. Commit your changes and push up a fix branch/PR. - -## Updated CRL host list - -In order to facilitate Android downloading CRLs over unsecured HTTP, we have a list of CRL hosts in -`/android/rustls-platform-verifier/src/main/res/xml/network_security_config.xml`. This list is populated -by the codegen test in `/android-release-support/tests/codegen.rs` in CI. - -To update: - - 1. Run `cargo test --manifest-path android-release-support/Cargo.toml --test codegen -- --ignored` - 2. Commit the changes to `network_security_config.xml` and push up a fix branch/PR. diff --git a/admin/RELEASING.md b/admin/RELEASING.md deleted file mode 100644 index 9c5c5bf2..00000000 --- a/admin/RELEASING.md +++ /dev/null @@ -1,44 +0,0 @@ -# How-to release `rustls-platform-verifier` - -This document records the steps to publish new versions of the crate since it requires non-trivial preparation and ordering -that needs to be accounted for due to the Android component's distribution. - -The Rustls repo also has [RELEASING] guidance for more information (e.g. on best practices for creating a GitHub release with a changelog) -and other steps. - -In the release preparation PR, the releaser may include the following checklist in the description so post-merge actions can be tracked: -```markdown -### Post-merge steps - -- [ ] Generate Android Maven artifacts locally -- [ ] Create and push Git tag -- [ ] `cargo publish` for each required crate, based on release steps -- [ ] Create companion GitHub release -``` - -## Steps - -1. Update main crate's version in `rustls-platform-verifier/Cargo.toml`. -2. If any non-test changes have been made to the `android` directory since the last release: - 1. Update Android artifact version in `android-release-support/Cargo.toml`, and in the main crate if creating an incompatible SemVer release. - 2. Commit version increase changes on the release branch - * We typically name these branches `rel-xxx` where `xxx` is the major version. - * We typically leave these branches around for future maintenance releases. - 3. Run `ci/package_android_release.sh` in a UNIX compatible shell - 4. Commit the Maven metadata updates on their own: `git commit -am "Bump Maven release to x.x.x"`. Copy the new commit's short ID. - 5. **Ensure that all version changes are committed to the correct branch before proceeding**. All version increases should be checked in prior - to publishing on crates.io. - 6. Checkout the Maven storage branch: `git checkout maven-archive`. The newly built artifacts are now ready to check in. - 7. Add the new artifacts to storage: `git add . && git commit -m "Prepare Maven release x.x.x"` - 8. Sync the Maven metadata to make the new artifacts visible: `git cherry-pick $MAVEN_BUMP_COMMIT_ID` - 9. Publish the new changes: - * `git push && git checkout rel-xxx` - * Publish the new Android marker version: `cargo publish -p rustls-platform-verifier-android` - -3. Commit main crate's version increase on the release branch -4. **Ensure that all version changes are committed to the correct branch before proceeding**. All version increases should be checked in prior - to publishing on crates.io. -5. Publish the main crate's new version: `cargo publish -p rustls-platform-verifier` -6. Follow the remaining steps in [RELEASING] to create the appropiate version tag. - -[RELEASING]: https://github.com/rustls/rustls/blob/main/RELEASING.md diff --git a/android-release-support/Cargo.toml b/android-release-support/Cargo.toml deleted file mode 100644 index 0ea6279c..00000000 --- a/android-release-support/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "rustls-platform-verifier-android" -version = "0.1.1" -description = "The internal JVM support component of the rustls-platform-verifier crate. You shouldn't depend on this directly." -repository = "https://github.com/rustls/rustls-platform-verifier" -license = "MIT OR Apache-2.0" -edition = "2021" - -include = [ - "src/*", -] - -[dependencies] - -[dev-dependencies] -rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs"] } -similar-asserts = "2" -tokio = { version = "1", features = ["macros"] } -webpki-ccadb = "0.2.2" diff --git a/android-release-support/LICENSE-APACHE b/android-release-support/LICENSE-APACHE deleted file mode 120000 index 965b606f..00000000 --- a/android-release-support/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/android-release-support/LICENSE-MIT b/android-release-support/LICENSE-MIT deleted file mode 120000 index 76219eb7..00000000 --- a/android-release-support/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/android-release-support/pom-template.xml b/android-release-support/pom-template.xml deleted file mode 100644 index 04e43c69..00000000 --- a/android-release-support/pom-template.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - 4.0.0 - org.rustls - rustls-platform-verifier - $VERSION - aar - The internal JVM support component of the rustls-platform-verifier Rust crate - \ No newline at end of file diff --git a/android-release-support/src/lib.rs b/android-release-support/src/lib.rs deleted file mode 100644 index 6bb66dbc..00000000 --- a/android-release-support/src/lib.rs +++ /dev/null @@ -1,67 +0,0 @@ -//! # rustls-platform-verifier-android -//! -//! This crate is an implementation detail of the actual [rustls-platform-verifier](https://github.com/rustls/rustls-platform-verifier) crate. -//! -//! It contains no Rust code and is solely intended as a convenient mechanism to synchronize a SemVer version managed by `cargo` to Gradle in -//! Android build systems in such a way that a SemVer incompatible version of the native component is never used. -//! -//! Other crates should not directly depend on this crate in any way, as nothing about it is considered stable and it is probably useless elsewhere. -//! -//! ## Details -//! -//! Note: Everything in this section is subject to change at any time. Semver may not be followed. -//! -//! ### Why? -//! -//! It is the best known middle ground between several tradeoffs. The important ones, in priority order, are: -//! - Automatically keeping component versions in sync -//! - Allowing well-tested and well-known `cargo` dependency management patterns to apply everywhere -//! - Providing a smooth developer experience as an Android consumer of `rustls-platform-verifier` -//! -//! Firstly, what alternatives are available for distributing the component? The other known ones are: -//! - Source distribution in some form (here, it will be through crates.io) -//! - Maven Central (or another hosted package registry) -//! - Bundling Android release artifacts inside crates.io releases -//! -//! Starting with the first, its infeasible due to toolchain syncing requirements. If the Android component is built as part of the host -//! app's Gradle build, then it becomes subject to any Gradle or Android Gradle Plugin incompatibilities/requirements. In practice this means -//! the AGP version between this project and the main application have to match all the time. Sometimes this works, but it becomes challenging/unfeasible -//! during yearly toolchain/SDK upgrades and is not maintainable long term. -//! -//! Next, Maven Central. This is considered the standard way of distributing public Android dependencies. There are two downsides to this -//! approach: version synchronization and publishing overhead. Version syncing is the hardest part: There's not a good way to know what version -//! a crate is that doesn't hurt the Cargo part of the build or damage functionality. So instead of making assumptions at runtime, we would need to do -//! clunky and manual version counting with an extra error case. Less importantly, the admin overhead of Maven Central is non-zero so its good to avoid -//! if possible for such a small need. -//! -//! It is also worth calling out a third set of much worse options: requiring users to manually download and install the Android component -//! on each update, which magnifies the version syncing problem with lots of user overhead and then deleting the component outright. A rewrite -//! could be done with raw JNI calls, but this would easily be 3x the size of the existing implementation and require huge amounts of `unsafe` -//! to review then audit. -//! -//! ### The solution -//! -//! The current design was built after running into several painpoints with the previous attempted distribution implementations and the need to start including -//! more than just Android code in releases. To produce the release, we rely on packaging scripts to build the Android component into a prebuilt AAR file. -//! Next, a [on-disk Maven repository](https://maven.apache.org/repositories/local.html) is hosted inside of this repository with a special branch on GitHub. -//! Using GitHub's ability to serve raw files, this local repository creates an emulated Maven package repository that can be queried and downloaded from like a hosted registry. -//! -//! The remaining parts are filled in during the packaging/release process, with artifacts being pushed from release branches into the special archive branch. -//! The main crate ensures it always uses a compatible version from this local repository by declaring a standard platform-specific dependency on this shim crate. -//! Cargo lockfile resolution takes care of the rest. -//! -//! On [the Gradle side](https://github.com/rustls/rustls-platform-verifier/tree/main#gradle-setup), we instruct users to include a small code snippet in their `settings.gradle` file -//! to dynamically resolve a correct Android library's version to download like any other. When the snippet is run, it finds the version inside the workspace's `Cargo.lock` -//! and provides that to Gradle's version resolution. When the lockfile is changed, the configuration cache is invalidated and the version is calculated again. -//! This happens after any version updates (semver, Git refs, etc). -//! -//! ## Summary -//! -//! In summary, the selected distribution method avoids most of the previous pitfalls while still balancing a good experience for `cargo` and Gradle users. Some of its -//! positive properties include: -//! - Full compatibility with Cargo's dependency management, including Git patching[^1] -//! - No version checking or manual synchronization required -//! - Painless and harmless to integrate into an Android app's build system -//! - Low maintenance for the main crate maintainers' -//! -//! [^1]: The Git reference being used must have an equivalent Maven repository branch inside of it and the Maven repository URL must be switched too. diff --git a/android-release-support/tests/codegen.rs b/android-release-support/tests/codegen.rs deleted file mode 100644 index 870fefd7..00000000 --- a/android-release-support/tests/codegen.rs +++ /dev/null @@ -1,62 +0,0 @@ -use std::env; -use std::fmt::Write; -use std::fs; - -use webpki_ccadb::{crl_hosts, RootStore}; - -/// Regenerates the Android network security config listing hosts that serve CRLs. -/// -/// The generated file is checked in; this test fails with a diff whenever the -/// data returned by CCADB no longer matches it. -/// -/// Note that this reaches out to the network to query the CCADB. -#[ignore] // Ignored by default because it requires network access and is slow. -#[tokio::test] -async fn update_security_config() -> Result<(), Box> { - let mut hosts = crl_hosts(RootStore::Chrome) - .await? - .into_iter() - .collect::>(); - hosts.sort(); - - let mut out = String::from(HEADER); - for host in hosts { - writeln!( - out, - " {host}" - ) - .unwrap(); - } - out.push_str(FOOTER); - - let stored = fs::read_to_string(OUTPUT).unwrap_or_default(); - fs::write(OUTPUT, &out)?; - if stored != out { - println!("run `cargo test --manifest-path android-release-support/Cargo.toml --test codegen -- --ignored` to update the checked-in file"); - similar_asserts::assert_eq!(stored, out); - } - - Ok(()) -} - -const HEADER: &str = "\ - - - - - -"; - -const FOOTER: &str = "\ - - -"; - -const OUTPUT: &str = concat!( - env!("CARGO_MANIFEST_DIR"), - "/../android/rustls-platform-verifier/src/main/res/xml/network_security_config.xml" -); diff --git a/android/.gitignore b/android/.gitignore deleted file mode 100644 index 6c988242..00000000 --- a/android/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/ -.DS_Store -/build -/captures -.externalNativeBuild -.cxx -local.properties -emulator.log \ No newline at end of file diff --git a/android/.run/All Tests.run.xml b/android/.run/All Tests.run.xml deleted file mode 100644 index f50b23c0..00000000 --- a/android/.run/All Tests.run.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index 6d52ea5e..00000000 --- a/android/build.gradle +++ /dev/null @@ -1,16 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { - dependencies { - classpath libs.kotlin.gradle.plugin - classpath libs.android.gradle.plugin - } - - repositories { - google() - mavenCentral() - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} \ No newline at end of file diff --git a/android/gradle.properties b/android/gradle.properties deleted file mode 100644 index cd0519bb..00000000 --- a/android/gradle.properties +++ /dev/null @@ -1,23 +0,0 @@ -# Project-wide Gradle settings. -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app"s APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true -# Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official -# Enables namespacing of each library's R class so that its R class includes only the -# resources declared in the library itself and none from the library's dependencies, -# thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file diff --git a/android/gradle/libraries.versions.toml b/android/gradle/libraries.versions.toml deleted file mode 100644 index 6cf1e1d1..00000000 --- a/android/gradle/libraries.versions.toml +++ /dev/null @@ -1,6 +0,0 @@ -[versions] -kotlin = "2.2.10" - -[libraries] -android-gradle-plugin = { group = "com.android.tools.build", name = "gradle", version = "8.3.2" } -kotlin-gradle-plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index afba109285af78dbd2a1d187e33ac4f87c76e392..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61624 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�Zy z^6AJh1X3pXq!CBB#`$5K8SM`A8- zu91@KW`jScvm}!^xaOr;l$}&)!qA=c4=tjb*AM^d9ZpDQjv*NDBXOUm9fM235A&Im zWb|jcBV^{}f>q*lY$s)A{g3K~i*dC}iz|ddMG+h2%gJJkYA%43!xj8A# zx}S=RPcxSSrC^je-O9-uG*4zN`%yO%D|8Y(M!;etj}#5<%)tweodG864mERu+wUwi zqO?7XNoGj5REy(>@FR?cmjdtzHh0Uyxc{bl7pq)x$iETy-gSOl4<=ay@B=!9(wjJhfW}ymgfT)tNU6b0S)wq zMeKw$AI+3w&@(KkXo2zZi+rD-;<`>S;(xh}N&A!yleW!DXaff`xq(&MU0v$=thsf{ zg(^n}x}gz%(ZMmnHv?lM149>hnCRcQl$2k+_R4YyxfW?lIfN`D`XCfH^dukp(N-@j zMOjDZSdpW2Zto4Xiwh$>MX#mx)#OxcM|qz7llutxlZ_J1E-I`Y&pzh)RfL03EK;d5 zsT1+B_S@MLCz)zQys)rDnV4a5!lT8<#kf<49)lNk;@0XW#dWoeCWlSU+e{zMyS1wNXB%6Un^?S8n~Jr%mk_^NT02xU zcTMjr6I|wbWAcf|&V@-_UA*XcHhl7mB~=D;T8nHdVRQX{LQT~{H7`n|hq82!6^^Qw zk3=bdrx(+2sKb?>S1*r#`#OK-jkDlW+^JkfcM1$YFJ9fi*s(8+3Ci?UHN7bY? zh4N;Ruf^YWl3Qug_Tt8ssOAr0u~l&@T3xKa)~WpBgpn}4a($+RfpKJts{-~X3lBbV zc}00$dp*~Rd#{MEJ)=}o%Ba+MxXj)G#S95An)W3pi<`?g$LYqs4y$@&P;h2dic|#Y zLG)4ki^^AYUpsZAtoN-`*PqRPm+BW{Sv93rQm8yHt2BO(SDmGJrDwCJ{h{LXJS+K? zT1`EUhgnKGwTy3CHN7c~OstGDJK;&0nUisI+TC|(NNeXbcpIy&DJ~-gy%PgMJwLdo zM-N=_#u(Fd`$DV<|BjAmhg*xPy8UhsziP>UzRJia${pQz)OyY|sn2Gsb@F5HMbeG4MJ)A6 zip8_D9EG_-mY)rt>E9tGKb6fE<=v;PY4-MR6_G!&r%+)@O^Sbo&N-QmW{8WLEyL}XI25|Lqcq;31FtfOg)YjO+kPkZx<1Xmr5EtjPCpi(FSH)6*cL~Wd3u@NkeeRsqV;PX~8DoAyr~*@QZEkWN8=j68 zK#oirFgtzpre!U$S(>lCULpEEsv^+Ew$A>6ZcsaAzLnn&J!{=Ke|!u)B`dFIl( z?vlF5euE?z5|cU)OPbl|@}Y3*ZkOOxEGXmrJOU-KoLFT{TuqWvZCG2==*;<06n)skW(dvAJ*9=S9v^7qHS$`Dl`eJ81@Mlj~ z%Bo)zV6lv$?7RyQZk6arskVWO0fvBrre8Jb*1R-cnz|i~~_ZLzp^Z zdUn~P6=9O$!Q)VJRz{VIA?$9b0acoc>g7?zFWpmZ`LCh`ie2bgsRy+C*Kf9A&<|h` zsZ76F{`l!LU2>tQjr$3#kYM{%d`Isn`WyaKUjrDwRSP0!kYpX9^R#RX!bjqmXkl!N zs))gf1ol~L3Xef4B?`<1GD_lBnuW{~+??9GRAgt)(@DZTFH|4Pb1o4CG6_f6rtEL@s<5ctjNIRvCMi=l?B-P+D8i*$H^-jz8Z{US(1{-DrHKNdc1xhp*${Nt%oj8oK2`gW#Eln z_W0bDj>|ck)XEBq1P`QeJDFebd}11SLV)K$4t+l=Q{P6MQl7?TD{C;U&*dbLVA^+O|OPt6jn6n7E<+DFOlud1?|k`TpU64 z;$jlu4;R1(yvFk@WgytV_g~pmB`+$<$!chFsmh@uY-a&yhCdS66WdAK#PQ(!wie!> za^US|K-U#D3pwGEmZaAO5FGbBetWB&z!hL(Y#21lO< z==S{#=CQN3-q!B>xq*jTqmfoF$8F`mZFNt^eYl~ZfNo4ZesiHf6ckDWcr$E=Jljnf2>9=rB~7>G4$a`w_O`ZQ>r=(b4ho+AfwCzm=D{`` zxKUQ313J(GXdjVXY;es$Y=PrSl(Ox@gV<_27CbzWPkyI|JZNrZP?!DnC<2`dh3H?f zl1?xeTOery;+#Pp_VzDOo33PR@(U$^hXMHgO(zGQ-u@f@FXqv(zXpH6P(7H2 z_BZ4J^&wCtEkGBMvvP8VYq*&1nE&7&Q|V%yoCd7S0*oDU|z z;;3i(25RC0#+>LbI=E&a?3fNgAO*FscLLGy4pEgQ+a;py{$7t;FDno1Gd|q8GdaBptjT1bT9H=(4$xg(a^;9al$zc!KrKq zG}eBa?`J81tSKCNupu9b9huAk)ms5{`wf}KcL*v~D`#g=p`T=682*7N*bv<$7ceyg zru~&l5j+Ib4uzYE6ZEf@!Y__6tN~QHfa>f%`(*+Ln!mQ$PpZE)QXFUfR5qAR(m^-e zcFWmK8Hh44whl@1*Qy9}vM%I+s+5DNeg8-*21Yz2%g21|mWF5LAD))kxG9Vie$C1GCQds%bZ6Ads?$z`tU5 z?SB|JXQy=zH6(LHy8kTU;v!ohrDI+JF=6#HPj6L z|5+8_zB(ti&9ez=A-s>L*YYw(a_ang3D#00_4+d%7%~TH_MtMMYJ%-CwE6y#;b4P%poCH0gPXelM>tU415{2?ON$z{cn`ie z;z0Pn#V|%CK#d2vM=<>0K!X2{4v7kl8m4a#Iw|o$Xq2FRsCcNs@b>U-CLN5oKQtaH z9%}rWJv`>@KjQr!%?1_vJW5cJJ?QzIKS3Yd$56fS_t3Dxe#5^OH@lP3zkTvii-zhZ zy$4p>cp%t5huZ&gnnqa?_nIo@#~ChARYp9>ReiBVku_RyDJ v9f-cOr*eQp04g-<;pZOo<=#I*?>`DvQ^o}A^zD`USu`GEG&HBt?O*=~soeXc diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 7a04a2dc..00000000 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.5-bin.zip -networkTimeout=10000 -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew deleted file mode 100755 index 65dcd68d..00000000 --- a/android/gradlew +++ /dev/null @@ -1,244 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat deleted file mode 100644 index 6689b85b..00000000 --- a/android/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/android/rustls-platform-verifier/.gitignore b/android/rustls-platform-verifier/.gitignore deleted file mode 100644 index 71252b62..00000000 --- a/android/rustls-platform-verifier/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/build -/src/androidTest/jniLibs/ \ No newline at end of file diff --git a/android/rustls-platform-verifier/build.gradle b/android/rustls-platform-verifier/build.gradle deleted file mode 100644 index 8a89d097..00000000 --- a/android/rustls-platform-verifier/build.gradle +++ /dev/null @@ -1,109 +0,0 @@ -plugins { - id 'com.android.library' - id 'org.jetbrains.kotlin.android' -} - -def isTest = gradle.startParameter.taskNames.any { it.contains("Test") } - -static def getOsArch() { - final String hostArch = System.getProperty("os.arch") - - if (("x86" == hostArch) || ("x86_64" == hostArch)) { - return hostArch - } else if (hostArch.contains("i386")) { - return "x86" - } else if (("ia64" == hostArch) || ("amd64" == hostArch)) { - return "x86_64" - } else if ("arm" == hostArch) { - return "armeabi-v7a" - } else if ("aarch64" == hostArch) { - return "arm64-v8a" - } - - return "UNSUPPORTED_HOST_ARCH" -} - -android { - compileSdk 33 - - defaultConfig { - minSdk 22 - targetSdk 33 - - buildConfigField "boolean", "TEST", "$isTest" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - namespace "org.rustls.platformverifier" - - buildTypes { - release { - minifyEnabled true - proguardFiles "proguard-rules.pro" - } - - debug { - debuggable true - } - } - - task buildTestLib(type: Exec) { - workingDir "../../" - commandLine "cargo", "ndk", "-t", getOsArch(), "-o", "android/rustls-platform-verifier/src/androidTest/jniLibs", "rustc", "-p", "rustls-platform-verifier", "--features", "ffi-testing", "--crate-type", "cdylib" - } - - // Only compile the test library if this package is being built for testing by itself. - tasks.whenTaskAdded { task -> - if (project.name.startsWith(gradle.rootProject.name) && task.name.contains("connectedDebugAndroidTest")) { - task.dependsOn([buildTestLib]) - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } - sourceSets { - main { - jni { - srcDirs 'src\\androidTest\\jni', 'src\\androidTest\\jniLibs' - } - } - } - buildFeatures { - buildConfig true - } -} - -configurations { - ktlint -} - -task ktlint(type: JavaExec, group: "verification") { - description = "Check Kotlin code style." - mainClass = "com.pinterest.ktlint.Main" - classpath = configurations.ktlint - args "src/**/*.kt" -} - -check.dependsOn ktlint - -task ktlintFormat(type: JavaExec, group: "formatting") { - description = "Fix Kotlin code style deviations." - mainClass = "com.pinterest.ktlint.Main" - classpath = configurations.ktlint - args "-F", "src/**/*.kt" -} - -dependencies { - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.3.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${libs.versions.kotlin.get()}" - - ktlint 'com.pinterest:ktlint:0.50.0' -} diff --git a/android/rustls-platform-verifier/proguard-rules.pro b/android/rustls-platform-verifier/proguard-rules.pro deleted file mode 100644 index 1bb20d54..00000000 --- a/android/rustls-platform-verifier/proguard-rules.pro +++ /dev/null @@ -1,41 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - --keepattributes SourceFile,LineNumberTable,MethodAttributes -# Everything is called via the JNI, so code must not be removed or renamed -# in a way Rust can't see. --dontobfuscate - -# We need this function (and class) in all builds for Rust to call because its the JNI entrypoint -# for the verifier functionality. --keep class org.rustls.platformverifier.CertificateVerifier { - verifyCertificateChain( - android.content.Context, - java.lang.String, - java.lang.String, - java.lang.String[], - byte[], - long, - byte[][] - ); -} - -# We need these classes so Rust can load their class definitions at runtime -# and access their fields at runtime. --keep class org.rustls.platformverifier.StatusCode { *; } --keep class org.rustls.platformverifier.VerificationResult { *; } - -# Note: We don't explicitly tell Proguard to remove test-only methods. They are instead -# removed as dead code because `javac` removes all references to them when not building -# in a test configuration. - -# This can be uncommented during development if needed to quickly check if -# a few test-only methods/fields are being removed by build time. -# -whyareyoukeeping class org.rustls.platformverifier.CertificateVerifier { -# private java.security.KeyStore mockKeystore; -# addMockRoot(byte[]); -#} diff --git a/android/rustls-platform-verifier/src/androidTest/java/org/rustls/platformverifier/CertificateVerifierTests.kt b/android/rustls-platform-verifier/src/androidTest/java/org/rustls/platformverifier/CertificateVerifierTests.kt deleted file mode 100644 index 238b636d..00000000 --- a/android/rustls-platform-verifier/src/androidTest/java/org/rustls/platformverifier/CertificateVerifierTests.kt +++ /dev/null @@ -1,72 +0,0 @@ -package org.rustls.platformverifier - -import android.content.Context -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.BeforeClass -import org.junit.Test -import org.junit.runner.RunWith - -private const val SUCCESS_MARKER: String = "success" -private const val FAILURE_MSG: String = "A test failed. Check the logs above for Rust panics." - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class CertificateVerifierTests { - private external fun mockTests(applicationContext: Context): String - private external fun realWorldTests(applicationContext: Context): String - private external fun verifyMockRootUsage(applicationContext: Context): String - - companion object { - @BeforeClass - @JvmStatic - fun init() { - System.loadLibrary("rustls_platform_verifier") - } - } - - @Test - fun runMockTestSuite() { - val context = InstrumentationRegistry.getInstrumentation().targetContext - val result = mockTests(context) - assertEquals(FAILURE_MSG, SUCCESS_MARKER, result) - } - - @Test - fun runRealWorldTestSuite() { - val context = InstrumentationRegistry.getInstrumentation().targetContext - val result = realWorldTests(context) - assertEquals(FAILURE_MSG, SUCCESS_MARKER, result) - } - - @Test - fun runVerifyMockRootUsage() { - val context = InstrumentationRegistry.getInstrumentation().targetContext - val result = verifyMockRootUsage(context) - assertEquals(FAILURE_MSG, SUCCESS_MARKER, result) - } - - // Note: - // - // - Full negative path (`CertificateVerifier`'s flow for unknown roots, - // are already exercised via `runMockTestSuite`). - // - // - Full positive path (`CertificateVerifier`'s flow for known roots, - // partial-chain revocation checks) already exercised via `runRealWorldTestSuite`. - @Test - fun runTestIsPublicRoot() { - val rootCAs = CertificateVerifier.getSystemRootCAs() - - // Positive - can ID known roots - assertTrue(rootCAs.isNotEmpty()) - for (ca in rootCAs) { - assertTrue(CertificateVerifier.isKnownRoot(ca)) - } - } -} diff --git a/android/rustls-platform-verifier/src/main/AndroidManifest.xml b/android/rustls-platform-verifier/src/main/AndroidManifest.xml deleted file mode 100644 index acb84c07..00000000 --- a/android/rustls-platform-verifier/src/main/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/android/rustls-platform-verifier/src/main/java/org/rustls/platformverifier/CertificateVerifier.kt b/android/rustls-platform-verifier/src/main/java/org/rustls/platformverifier/CertificateVerifier.kt deleted file mode 100644 index 618f5eef..00000000 --- a/android/rustls-platform-verifier/src/main/java/org/rustls/platformverifier/CertificateVerifier.kt +++ /dev/null @@ -1,460 +0,0 @@ -package org.rustls.platformverifier - -import android.annotation.SuppressLint -import android.content.Context -import android.net.http.X509TrustManagerExtensions -import android.os.Build -import android.util.Log -import java.io.ByteArrayInputStream -import java.io.File -import java.security.KeyStore -import java.security.KeyStoreException -import java.security.MessageDigest -import java.security.PublicKey -import java.security.cert.CertPathValidator -import java.security.cert.CertPathValidatorException -import java.security.cert.CertificateException -import java.security.cert.CertificateExpiredException -import java.security.cert.CertificateFactory -import java.security.cert.CertificateNotYetValidException -import java.security.cert.CertificateParsingException -import java.security.cert.PKIXBuilderParameters -import java.security.cert.PKIXRevocationChecker -import java.security.cert.X509Certificate -import java.util.Date -import java.util.EnumSet -import javax.net.ssl.TrustManagerFactory -import javax.net.ssl.X509TrustManager -import javax.security.auth.x500.X500Principal - -// If this is updated, update the Rust definition too. -// Marked private as this is not meant to be used in Android code. -private enum class StatusCode(val value: Int) { - Ok(0), - Unavailable(1), - Expired(2), - UnknownCert(3), - Revoked(4), - InvalidEncoding(5), - InvalidExtension(6), -} - -// Marked private as this is not meant to be used in Android code. -private class VerificationResult( - status: StatusCode, - @Suppress("unused") val message: String? = null, -) { - @Suppress("unused") - private val code: Int = status.value -} - -// NOTE: All TrustManager and certificate validation methods are not thread safe. These -// are all guarded by Kotlin's `Synchronized` accessors to prevent undefined behavior. - -// Only JNI and test code calls this, so unused code warnings are suppressed. -// Internal for test code - no other Kotlin code should use this object directly. -@Suppress("unused") -// We want to show a difference between Kotlin-side logs and those in Rust code -@SuppressLint("LongLogTag") -internal object CertificateVerifier { - private const val TAG = "rustls-platform-verifier-android" - - private fun createTrustManager(keystore: KeyStore?): X509TrustManagerExtensions? { - // This can never throw since the default algorithm is used. - val factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) - - factory.init(keystore) - - val availableTrustManagers = try { - factory.trustManagers - } catch (e: RuntimeException) { - Log.w(TAG, "exception thrown creating a TrustManager: $e") - return null - } - - for (manager in availableTrustManagers) { - if (manager is X509TrustManager) { - // Kotlin ensures this can't throw at runtime since it knows that - // it must be the correct type by now. - return X509TrustManagerExtensions(manager) - } - } - - Log.e(TAG, "failed to find a usable trust manager") - return null - } - - private fun makeLazyTrustManager(keystore: KeyStore?): Lazy { - // Ensure the keystore is loaded. Since all of the trust managers are initialized in a - // `Lazy`, this will only run once. - keystore?.load(null) - - return lazy { createTrustManager(keystore) } - } - - // -- Test only -- - // Ideally, all of this will be optimized out at compile time due to not being accessed - // in release builds. - - @get:Synchronized - private val mockKeystore: KeyStore = KeyStore.getInstance(KeyStore.getDefaultType()) - - @get:Synchronized - private var mockTrustManager: Lazy = - makeLazyTrustManager(mockKeystore) - - @JvmStatic - private fun addMockRoot(root: ByteArray) { - if (!BuildConfig.TEST) { - throw Exception("attempted to add a mock root outside a test!") - } - - val alias = "root_${mockKeystore.size()}" - // Throwing here is fine since test roots should always be well-formed - val cert = certFactory.generateCertificate(ByteArrayInputStream(root)) - mockKeystore.setCertificateEntry(alias, cert) - - reloadMockData() - } - - @JvmStatic - private fun clearMockRoots() { - // Reload to get a completely fresh internal state - mockKeystore.load(null) - reloadMockData() - } - - @JvmStatic - private fun reloadMockData() { - if (mockTrustManager.isInitialized()) { - mockTrustManager = makeLazyTrustManager(mockKeystore) - } - } - - // Get a list of the system's root CAs. - // Function is public for testing only. - @JvmStatic - fun getSystemRootCAs(): List { - val rootCAs = mutableListOf() - - val factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) - factory.init(systemKeystore) - - val availableTrustManagers = try { - factory.trustManagers - } catch (e: RuntimeException) { - Log.w(TAG, "exception thrown creating a TrustManager: $e") - return rootCAs - } - - availableTrustManagers.forEach { trustManager -> - if (trustManager is X509TrustManager) { - rootCAs.addAll(trustManager.acceptedIssuers) - } - } - - return rootCAs - } - - // -- End testing requirements -- - - private val certFactory: CertificateFactory = CertificateFactory.getInstance("X.509") - - private var systemTrustAnchorCache = hashSetOf>() - - @get:Synchronized - private var systemCertificateDirectory: File? = System.getenv("ANDROID_ROOT")?.let { rootPath -> - File("$rootPath/etc/security/cacerts") - } - - @get:Synchronized - private val systemKeystore: KeyStore? = try { - KeyStore.getInstance("AndroidCAStore") - } catch (_: KeyStoreException) { - null - } - - @get:Synchronized - private val systemTrustManager: Lazy = - makeLazyTrustManager(systemKeystore) - - @JvmStatic - private fun verifyCertificateChain( - @Suppress("UNUSED_PARAMETER") context: Context, - serverName: String, - authMethod: String, - allowedEkus: Array, - ocspResponse: ByteArray?, - time: Long, - certChain: Array, - ): VerificationResult { - // Convert the array of (supposedly) DER bytes into certificates. - val certificateChain = mutableListOf() - certChain.forEach { certBytes -> - val certificate = try { - certFactory.generateCertificate(ByteArrayInputStream(certBytes)) - } catch (e: CertificateException) { - return VerificationResult(StatusCode.InvalidEncoding) - } - certificateChain.add(certificate as X509Certificate) - } - - // Will never throw `ArrayIndexOutOfBoundsException` because `rustls`'s `ServerCertVerifier` trait - // has a mandatory `end_entity` parameter in `verify_server_cert`. - val endEntity = certificateChain[0] - - // Check that the certificate is valid at the point of time provided by `rustls`. - try { - endEntity.checkValidity(Date(time)) - } catch (e: CertificateExpiredException) { - return VerificationResult(StatusCode.Expired) - } catch (e: CertificateNotYetValidException) { - return VerificationResult(StatusCode.Expired) - } - - // Check that this certificate can be used in a TLS server. - if (!verifyCertUsage(endEntity, allowedEkus)) { - return VerificationResult(StatusCode.InvalidExtension) - } - - // Select the trust manager to use. - // - // We select them as follows: - // - If built for release, only use the system trust manager. This should let all test-related - // code be optimized out. - // - If built for tests: - // - If the mock CA store has any values, use the mock trust manager. - // - Otherwise, use the system trust manager. - val (trustManager, keystore) = if (!BuildConfig.TEST) { - val trustManager = - systemTrustManager.value ?: return VerificationResult(StatusCode.Unavailable) - Pair(trustManager, systemKeystore) - } else { - if (mockKeystore.size() != 0) { - val trustManager = mockTrustManager.value!! - Pair(trustManager, mockKeystore) - } else { - val trustManager = - systemTrustManager.value ?: return VerificationResult(StatusCode.Unavailable) - Pair(trustManager, systemKeystore) - } - } - - // Verify that the certificate chain is valid and correct, and nothing more. - // - // NOTE: This does not validate `serverName` is valid for the end-entity certificate. - // That is handled in Rust as Android/Java do not currently provide a RFC 6125 compliant - // hostname verifier. Additionally, even the RFC 2818 verifier is not available until API 24. - // - // `serverName` is only used for pinning/CT requirements. - // - // Returns the "the properly ordered chain used for verification as a list of X509Certificates.", - // meaning a list from end-entity certificate to trust-anchor. - val validChain = try { - trustManager.checkServerTrusted(certificateChain.toTypedArray(), authMethod, serverName) - } catch (e: CertificateException) { - // In test configurations we may see `checkServerTrusted` fail once vendored test - // certificates pass their expiry date. We try to avoid that by using a fixed - // verification time when calling `endEntity.checkValidity` above, however we can't - // fix the time for the `checkServerTrusted` call. - // - // To make diagnosing CI test failures easier we try to find the root cause of - // checkServerTrusted failing, returning a different `StatusCode` as appropriate. - if (BuildConfig.TEST) { - var rootCause: Throwable? = e - while (rootCause?.cause != null && rootCause.cause != rootCause) { - rootCause = rootCause.cause - } - return when (rootCause) { - is CertificateExpiredException, is CertificateNotYetValidException -> VerificationResult( - StatusCode.Expired, - rootCause.toString(), - ) - - else -> VerificationResult(StatusCode.UnknownCert, rootCause.toString()) - } - } - // In non-test configurations we should have caught expiry errors earlier and - // can simply return an unknown cert error without digging through the exception - // cause chain. - return VerificationResult(StatusCode.UnknownCert, e.toString()) - } - - // TEST ONLY: Mock test suite cannot attempt to check revocation status if no OSCP data has been stapled, - // because Android requires certificates to an specify OCSP responder for network fetch in this case. - // If in testing w/o OCSP stapled, short-circuit here - only prior checks apply. - if (BuildConfig.TEST && (mockKeystore.size() != 0) && (ocspResponse == null)) { - return VerificationResult(StatusCode.Ok) - } - - // Try to check the revocation status of the cert, if it is supported. - // - // This is supported at >= API 24, but we're supporting 22 (Android 5) for the best - // compatibility. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - // Note: - // - // 1. Android does not provide any way only to attempt to validate revocation from cached - // data like the other platforms do. This means it will always use the network for - // certificates which had no stapled response. - // - // 2: Likely because of 1, Android requires all issued certificates to have some form of - // revocation included in their authority information. This doesn't work universally as - // issuing certificates in use may omit authority access information (for example the - // Let's Encrypt R3 Intermediate Certificate). - // - // Given these constraints, the best option is to only check revocation information - // at the end-entity depth. We will prefer OCSP (to use stapled information if possible). - // If there is no stapled OCSP response, Android may use the network to attempt to fetch - // one. If OCSP checking fails, it may fall back to fetching CRLs. We allow "soft" - // failures, for example transient network errors. - // - // In the case of a non-public root, such as an internal CA or self-signed certificate, - // we opt to skip revocation checks entirely. The only exception is if the server - // provided stapled OCSP data, which is an explicit signal and won't introduce non-ideal - // platform behavior when attempting validation. - // - // This is because these are cases where a user or administrator has explicitly opted to - // trust a certificate they (at least believe) have control over. These certificates rarely - // contain revocation information as well, so these cases don't lose much. - // See https://github.com/rustls/rustls-platform-verifier/issues/69 as well. - if (ocspResponse == null && !isKnownRoot(validChain.last())) { - // Chain validation must have succeeded by this point. - return VerificationResult(StatusCode.Ok) - } - - val parameters = PKIXBuilderParameters(keystore, null) - - val validator = CertPathValidator.getInstance("PKIX") - val revocationChecker = validator.revocationChecker as PKIXRevocationChecker - - revocationChecker.options = EnumSet.of( - PKIXRevocationChecker.Option.SOFT_FAIL, - PKIXRevocationChecker.Option.ONLY_END_ENTITY, - ) - - // Use the OCSP data `rustls` provided, if present. - // Its expected that the server only sends revocation data for its own leaf certificate. - // - // If this field is set, then Android will use it and skip any networking to - // attempt a fetch for that certificate. Otherwise, it will attempt to fetch it from the network. - // Ref: https://cs.android.com/android/platform/superproject/+/master:libcore/ojluni/src/main/java/sun/security/provider/certpath/RevocationChecker.java;l=694 - ocspResponse?.let { providedResponse -> - revocationChecker.ocspResponses = mapOf(endEntity to providedResponse) - } - - // Use the custom revocation definition. - // "Note that when a `PKIXRevocationChecker` is added to `PKIXParameters`, it clones the `PKIXRevocationChecker`; - // thus any subsequent modifications to the `PKIXRevocationChecker` have no effect." - // - https://developer.android.com/reference/java/security/cert/PKIXRevocationChecker - parameters.certPathCheckers = listOf(revocationChecker) - // "When supplying a revocation checker in this manner, it will be used to check revocation - // irrespective of the setting of the `RevocationEnabled` flag." - // - https://developer.android.com/reference/java/security/cert/PKIXRevocationChecker - parameters.isRevocationEnabled = false - - // Validate the revocation status of the end entity certificate. - try { - validator.validate(certFactory.generateCertPath(validChain), parameters) - } catch (e: CertPathValidatorException) { - return VerificationResult(StatusCode.Revoked, e.toString()) - } - } else { - // This is allowed to be skipped since revocation checking is best-effort. - Log.w(TAG, "did not attempt to validate OCSP due to Android version") - } - - return VerificationResult(StatusCode.Ok) - } - - private fun verifyCertUsage(certificate: X509Certificate, allowedEkus: Array): Boolean { - val ekus = try { - certificate.extendedKeyUsage - } - // This should be unreachable, but could happen. - catch (_: CertificateParsingException) { - return false - } catch (_: NullPointerException) { - // According to Chromium's implementation, this can crash when the EKU data is malformed. - Log.w(TAG, "exception handling certificate EKU") - return false - } ?: return true // If the list is empty, we have nothing to do. - - return ekus.any { allowedEkus.contains(it) } - } - - // Android hashes a principal using the first four bytes of its MD5 digest, encoded in - // lowercase hex and reversed. - // - // Ref: https://source.chromium.org/chromium/chromium/src/+/main:net/android/java/src/org/chromium/net/X509Util.java;l=339 - private fun hashPrincipal(principal: X500Principal): String { - val hexDigits = "0123456789abcdef".toCharArray() - val digest = MessageDigest.getInstance("MD5").digest(principal.encoded) - val hexChars = CharArray(8) - - for (i in 0..3) { - // Kotlin doesn't support bitwise operators for bytes, only Int and Long. - val digestByte = digest[3 - i].toInt() - hexChars[2 * i] = hexDigits[(digestByte shr 4) and 0xf] - hexChars[2 * i + 1] = hexDigits[digestByte and 0xf] - } - - return String(hexChars) - } - - // Check if CA root is known or not. - // Known means installed in root CA store, either a preset public CA or a custom one installed by an enterprise/user. - // - // Ref: https://source.chromium.org/chromium/chromium/src/+/main:net/android/java/src/org/chromium/net/X509Util.java;l=351 - fun isKnownRoot(root: X509Certificate): Boolean { - // System keystore and cert directory must be non-null to perform checking - systemKeystore?.let { loadedSystemKeystore -> - systemCertificateDirectory?.let { loadedSystemCertificateDirectory -> - - // Check the in-memory cache first - val key = Pair(root.subjectX500Principal, root.publicKey) - if (systemTrustAnchorCache.contains(key)) { - return true - } - - // System trust anchors are stored under a hash of the principal. - // In case of collisions, append number. - val hash = hashPrincipal(root.subjectX500Principal) - var i = 0 - while (true) { - val alias = "$hash.$i" - i += 1 - - if (!File(loadedSystemCertificateDirectory, alias).exists()) { - break - } - - val anchor = loadedSystemKeystore.getCertificate("system:$alias") - - // It's possible for `anchor` to be `null` if the user deleted a trust anchor. - // Continue iterating as there may be further collisions after the deleted anchor. - if (anchor == null) { - continue - // This should never happen - } else if (anchor !is X509Certificate) { - // SAFETY: This logs a unique identifier (hash value) only in cases where a file within the - // system's root trust store is not a valid X509 certificate (extremely unlikely error). - // The hash doesn't tell us any sensitive information about the invalid cert or reveal any of - // its contents - it just lets us ID the bad file if a user is having TLS failure issues. - Log.e(TAG, "anchor is not a certificate, alias: $alias") - continue - // If subject and public key match, it's a system root. - } else { - if ((root.subjectX500Principal == anchor.subjectX500Principal) && (root.publicKey == anchor.publicKey)) { - systemTrustAnchorCache.add(key) - return true - } - } - } - } - } - - // Not found in cache or store: non-public - return false - } -} diff --git a/android/rustls-platform-verifier/src/main/res/xml/network_security_config.xml b/android/rustls-platform-verifier/src/main/res/xml/network_security_config.xml deleted file mode 100644 index b245638d..00000000 --- a/android/rustls-platform-verifier/src/main/res/xml/network_security_config.xml +++ /dev/null @@ -1,387 +0,0 @@ - - - - - - abitabdv.crl.certum.pl - abitabev.crl.certum.pl - abitabov.crl.certum.pl - alpiro.crl.sectigo.com - anticdv.crl.certum.pl - antrustovsslg2r30ca.crl.certum.pl - autontrustprodvtlsg2r31ca.crl.certum.pl - bitcert.crl.sectigo.com - btdvtlsr35g2ca.crl.certum.pl - c.cf-b.ssl.com - c.cf-i.ssl.com - c.pki.goog - ca1.agid.gov.it - cdn.d-trust-cloudcrl.net - cdp.disig.sk - cdp.elektronicznypodpis.pl - cdp.geotrust.com - cdp.rapidssl.com - cdp.thawte.com - cdp1.disig.sk - cdp1.pca.dfn.de - cdpa.digitalcertvalidation.com - cdpb.digitalcertvalidation.com - cdpc.digitalcertvalidation.com - cdpd.digitalcertvalidation.com - cdpe.digitalcertvalidation.com - cdpf.digitalcertvalidation.com - cdpg.digitalcertvalidation.com - cdph.digitalcertvalidation.com - cdpi.digitalcertvalidation.com - certcloud.crl.trust-provider.com - certdata.crl.sectigo.com - certera.crl.sectigo.com - certificates.godaddy.com - certum-crl.wt.trustasia.com - certum.crl.sheca.com - certumdvtlsg2e39ca.crl.certum.pl - certumdvtlsg2r39ca.crl.certum.pl - certumevtlsg2e39ca.crl.certum.pl - certumevtlsg2r39ca.crl.certum.pl - certumovtlsg2e39ca.crl.certum.pl - certumovtlsg2r39ca.crl.certum.pl - cfcadveccca.crl.certum.pl - cfcadvrsaca.crl.certum.pl - cfcaeveccca.crl.certum.pl - cfcaevrsaca.crl.certum.pl - cfcaoveccca.crl.certum.pl - cfcaovrsaca.crl.certum.pl - cnssldvtlsg2r36ca.crl.certum.pl - cnsslovtlsg2r36ca.crl.certum.pl - crl-01.r1.ica.amazontrust.com - crl-01.r1.ica.amznts.eu - crl-c.emsign.com - crl-pro.litessl.com - crl.anf.es - crl.apple.com - crl.businessid.telesec.de - crl.buypass.no - crl.catrust.oemssl.cn - crl.certigna.fr - crl.certsign.ro - crl.certum.pl - crl.cfca.com.cn - crl.cntrus.oemssl.cn - crl.cnwebtrust.oemssl.cn - crl.comodoca.com - crl.crlocsp.cn - crl.cybertrust.ne.jp - crl.d-trust.net - crl.dhimyotis.com - crl.digicert.cn - crl.digicert.eu - crl.e-szigno.hu - crl.e2m01.amazontrust.com - crl.e2m01.eu.amazontrust.com - crl.e2m02.amazontrust.com - crl.e2m03.amazontrust.com - crl.e2m04.amazontrust.com - crl.e2s14.amazontrust.com - crl.e2s15.amazontrust.com - crl.e2s16.amazontrust.com - crl.e2s17.amazontrust.com - crl.e2s18.amazontrust.com - crl.e2s19.amazontrust.com - crl.e2s20.amazontrust.com - crl.e2s21.amazontrust.com - crl.e3m01.amazontrust.com - crl.e3m01.eu.amazontrust.com - crl.e3m02.amazontrust.com - crl.e3m03.amazontrust.com - crl.e3m04.amazontrust.com - crl.e3s22.amazontrust.com - crl.e3s23.amazontrust.com - crl.e3s24.amazontrust.com - crl.e3s25.amazontrust.com - crl.e3s26.amazontrust.com - crl.e3s27.amazontrust.com - crl.e3s28.amazontrust.com - crl.e3s29.amazontrust.com - crl.emsign.com - crl.ensuredca.com - crl.firmaprofesional.com - crl.gdca.com.cn - crl.global.sheca.com - crl.globalsign.com - crl.globalsign.net - crl.godaddy.com - crl.harica.gr - crl.izenpe.com - crl.litessl.com - crl.netsolssl.com - crl.oem.trustca.net - crl.pcsp.telesec.de - crl.pki.goog - crl.quovadisglobal.com - crl.r1001.amazontrust.com - crl.r2001.amazontrust.com - crl.r2m01.amazontrust.com - crl.r2m01.eu.amazontrust.com - crl.r2m02.amazontrust.com - crl.r2m03.amazontrust.com - crl.r2m04.amazontrust.com - crl.r2s18.amazontrust.com - crl.r2s19.amazontrust.com - crl.r2s20.amazontrust.com - crl.r2s21.amazontrust.com - crl.r2s22.amazontrust.com - crl.r2s23.amazontrust.com - crl.r2s24.amazontrust.com - crl.r2s25.amazontrust.com - crl.r3001.amazontrust.com - crl.r4001.amazontrust.com - crl.r4m01.amazontrust.com - crl.r4m02.amazontrust.com - crl.r4m03.amazontrust.com - crl.r4m04.amazontrust.com - crl.root-e1.certainly.com - crl.root-r1.certainly.com - crl.rootca1.amazontrust.com - crl.rootca2.amazontrust.com - crl.rootca3.amazontrust.com - crl.rootca4.amazontrust.com - crl.rztrust.oemssl.cn - crl.sbca.telesec.de - crl.sectigo.com - crl.sectigochina.com - crl.serverid.telesec.de - crl.starfieldtech.com - crl.swisssign.ch - crl.trust-provider.cn - crl.trust-provider.com - crl.tuntrust.tn - crl.usertrust.com - crl.wt.trustasia.com - crl.xinchacha.oemssl.cn - crl.zw.oemssl.cn - crl05.actalis.it - crl06.actalis.it - crl09.actalis.it - crl1.ecert.gov.hk - crl13.actalis.it - crl14.actalis.it - crl15.actalis.it - crl2.microsoft.com - crl3.digicert.com - crl4.digicert.com - crls.certainly.com - crls.ssl.com - cyberfolks2.crl.certum.pl - depo.kamusm.gov.tr - digitaltrust.crl.sectigo.com - dnencrypt.crl.sectigo.com - dvtlsca2025-crl.e-szigno.hu - dvtlsca2026-crl.e-szigno.hu - e5.c.lencr.org - e6.c.lencr.org - e7.c.lencr.org - e8.c.lencr.org - ec2ca2017-crl1.e-szigno.hu - ec2ca2017-crl2.e-szigno.hu - ec2ca2017-crl3.e-szigno.hu - ec2sslca2017-crl1.e-szigno.hu - ec2sslca2017-crl2.e-szigno.hu - ec2sslca2017-crl3.e-szigno.hu - ec3ca2017-crl1.e-szigno.hu - ec3ca2017-crl2.e-szigno.hu - ec3ca2017-crl3.e-szigno.hu - ec3sslca2017-crl1.e-szigno.hu - ec3sslca2017-crl2.e-szigno.hu - ec3sslca2017-crl3.e-szigno.hu - eca.hinet.net - edvtlsca2023-crl.e-szigno.hu - edvtlsca2025-crl.e-szigno.hu - edvtlsca2026-crl.e-szigno.hu - emudhra.crl.sectigo.com - eosslca2017-crl1.e-szigno.hu - eosslca2017-crl2.e-szigno.hu - eosslca2017-crl3.e-szigno.hu - eovtlsca2023-crl.e-szigno.hu - eovtlsca2025-crl.e-szigno.hu - eovtlsca2026-crl.e-szigno.hu - eqca2017-crl1.e-szigno.hu - eqca2017-crl2.e-szigno.hu - eqca2017-crl3.e-szigno.hu - eqcpca2017-crl1.e-szigno.hu - eqcpca2017-crl2.e-szigno.hu - eqcpca2017-crl3.e-szigno.hu - eqoca2017-crl1.e-szigno.hu - eqoca2017-crl2.e-szigno.hu - eqoca2017-crl3.e-szigno.hu - eqtlsca2018-crl1.e-szigno.hu - eqtlsca2018-crl2.e-szigno.hu - eqtlsca2018-crl3.e-szigno.hu - eqtlsca2023-crl.e-szigno.hu - eqtlsca2025-crl.e-szigno.hu - eqtlsca2026-crl.e-szigno.hu - esafer.crl.sectigo.com - eue2m1.crl.root.amznts.eu - eue3m1.crl.root.amznts.eu - eur2m1.crl.root.amznts.eu - eveccca.crl.certum.pl - evrsaca.crl.certum.pl - gdcadv.crl.certum.pl - gdcaev.crl.certum.pl - gdcaov.crl.certum.pl - gdcatrustauthdvtlsg3r31ca.crl.certum.pl - gdcatrustauthevtlsg3r31ca.crl.certum.pl - gdcatrustauthovtlsg3r31ca.crl.certum.pl - geant.crl.sectigo.com - genious.crl.sectigo.com - geossldvtlsr34g2ca.crl.certum.pl - geosslevtlsr34g2ca.crl.certum.pl - geosslovtlsr34g2ca.crl.certum.pl - globaltrust.crl.sectigo.com - globessl.crl.sectigo.com - gogetssldv.crl.certum.pl - gogetsslev.crl.certum.pl - gogetsslov.crl.certum.pl - homepldvtlsg2r35ca.crl.certum.pl - homepldvtlsg2r36ca.crl.certum.pl - homeplovtlsg2r35ca.crl.certum.pl - homeplovtlsg2r36ca.crl.certum.pl - httpcrl.trust.telia.com - ica.navercloudtrust.com - ica.navercorp.com - issauth.crl.sectigo.com - itrusdv2.crl.certum.pl - itrusdvtlsr35g2ca.crl.certum.pl - itrusov2.crl.certum.pl - itrusovtlsr35g2ca.crl.certum.pl - itso.crl.sectigo.com - joyssldvtlsg2r33ca.crl.certum.pl - joysslovtlsg2r33ca.crl.certum.pl - kingnettechdv.crl.certum.pl - kingnettechev.crl.certum.pl - kingnettechov.crl.certum.pl - kr.crl.digicert.com - lh2.crl.certum.pl - namecheap.crl.sectigo.com - nazwassldvtlsg2e29ca.crl.certum.pl - nazwassldvtlsg2r29ca.crl.certum.pl - netartcadv2.crl.certum.pl - netartssldvtlsg2e31ca.crl.certum.pl - nijimo.crl.sectigo.com - nyalabscadv.crl.certum.pl - nyatworkdv.crl.certum.pl - omitsecurity.crl.sectigo.com - onesign.crl.sectigo.com - osslca2016-crl1.e-szigno.hu - osslca2016-crl2.e-szigno.hu - osslca2016-crl3.e-szigno.hu - ovtlsca2025-crl.e-szigno.hu - ovtlsca2026-crl.e-szigno.hu - pki-crl.atos.net - pki.telesec.de - pkipro.certsign.ro - positiwise.crl.sectigo.com - psw.crl.sectigo.com - public.wisekey.com - qiduodv.crl.certum.pl - qtlsca2018-crl1.e-szigno.hu - qtlsca2018-crl2.e-szigno.hu - qtlsca2018-crl3.e-szigno.hu - qtlsca2026-crl.e-szigno.hu - r10.c.lencr.org - r11.c.lencr.org - r12.c.lencr.org - r13.c.lencr.org - rca.navercloudtrust.com - rca.navercorp.com - repo.pubcert.jprs.jp - repo1.secomtrust.net - repository.secomtrust.net - repository.tls.hinet.net - rootca.twca.com.tw - rootglobaldv.crl.certum.pl - rootnetworksdv2.crl.certum.pl - shenzhendv.crl.certum.pl - shenzhenev.crl.certum.pl - shenzhenov.crl.certum.pl - shoperdvtlsg2r34ca.crl.certum.pl - shuididv.crl.certum.pl - shuidiev.crl.certum.pl - shuidiov.crl.certum.pl - soomadv.crl.certum.pl - ssl2ca2016-crl1.e-szigno.hu - ssl2ca2016-crl2.e-szigno.hu - ssl2ca2016-crl3.e-szigno.hu - sslca2014-crl1.e-szigno.hu - sslca2014-crl2.e-szigno.hu - sslca2014-crl3.e-szigno.hu - sslcom.crl.certum.pl - ssllimiteddv.crl.certum.pl - sslserver.twca.com.tw - subca.crl.certum.pl - titrust.crl.sectigo.com - tlcdvtlsr34g2ca.crl.certum.pl - tlcevtlsr34g2ca.crl.certum.pl - tlcovtlsr34g2ca.crl.certum.pl - tlsrootca2025-crl.e-szigno.hu - trustasia.crl.certum.pl - trustasiadv.crl.certum.pl - trustasiadvtlsr35g2ca.crl.certum.pl - trustasiaev.crl.certum.pl - trustasiaevtlse35g2ca.crl.certum.pl - trustasiaevtlsr35g2ca.crl.certum.pl - trustasiaov.crl.certum.pl - trustasiaovtlse35g2ca.crl.certum.pl - trustasiaovtlsr35g2ca.crl.certum.pl - trustocean.crl.certum.pl - tstlser20.crl.telesec.de - tstlsrr23.crl.telesec.de - unitedtrustov.crl.certum.pl - validation.identrust.com - verokey.crl.sectigo.com - vtrusdvtlsg3r31ca.crl.certum.pl - vtrusovtlsg3r31ca.crl.certum.pl - webnic.crl.sectigo.com - whitessldvtlsg2e36ca.crl.certum.pl - wosign.crl.certum.pl - wotrus-dvca.crl.certum.pl - wotrus-evca.crl.certum.pl - wotrus-ovca.crl.certum.pl - wotrus.crl.sectigo.com - wtca-cafiles.itrus.com.cn - wtca-crl.itrus.com.cn - www.accv.es - www.cert.fnmt.es - www.d-trust.net - www.microsoft.com - x1.c.lencr.org - x2.c.lencr.org - xcctrustdvtlsg3r31ca.crl.certum.pl - xcctrustevtlsg3r31ca.crl.certum.pl - xcctrustovtlsg3r31ca.crl.certum.pl - xinchacha2dv.crl.certum.pl - xinchacha2ov.crl.certum.pl - xinchachatrustdvtlsg2r34ca.crl.certum.pl - xinchachatrustevtlsg2r34ca.crl.certum.pl - xinchachatrustovtlsg2r34ca.crl.certum.pl - xinnetdvtlsr34g2ca.crl.certum.pl - xinnetevtlsr34g2ca.crl.certum.pl - xinnetovtlsr34g2ca.crl.certum.pl - ye.c.lencr.org - ye1.c.lencr.org - ye2.c.lencr.org - yektadv.crl.certum.pl - yektaov.crl.certum.pl - yr.c.lencr.org - yr1.c.lencr.org - yr2.c.lencr.org - zerossl.crl.sectigo.com - ziwit.crl.sectigo.com - zycatrustdvtlsg3r31ca.crl.certum.pl - zycatrustevtlsg3r31ca.crl.certum.pl - zycatrustovtlsg3r31ca.crl.certum.pl - - diff --git a/android/settings.gradle b/android/settings.gradle deleted file mode 100644 index e00d83fe..00000000 --- a/android/settings.gradle +++ /dev/null @@ -1,22 +0,0 @@ -pluginManagement { - repositories { - gradlePluginPortal() - google() - mavenCentral() - } -} -dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) - repositories { - google() - mavenCentral() - } - - versionCatalogs { - libs { - from(files("gradle/libraries.versions.toml")) - } - } -} -rootProject.name = "rustls" -include ':rustls-platform-verifier' diff --git a/ci/package_android_release.sh b/ci/package_android_release.sh deleted file mode 100755 index bbda1d25..00000000 --- a/ci/package_android_release.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash - -# This script's purpose is to automate the build + packaging steps for the pre-compiled Android verifier component. -# It works with template files and directories inside the `android-release-support/` part of the repository to setup -# a Maven local repository and then add the pre-compiled AAR file into it for distribution. The results of this packaging -# are then published to dedicated artifacts Git branch on GitHub, emulating an actual online Mavan package repository. -# -# Gradle and other clients download the artifacts from thier native build systems later on with the requested files lining up -# with the structure of the Git repo's contents. This idea was originally inspired by https://github.com/RiV-chain/github-publish-maven-action. - -set -euo pipefail - -if ! type mvn > /dev/null; then - echo "The maven CLI, mvn, is required to run this script." - echo "Download it from: https://maven.apache.org/download.cgi" - exit 1 -fi - -version=$(grep -m 1 "version = " android-release-support/Cargo.toml | tr -d "version= " | tr -d '"') - -echo "Packaging v$version of the Android support component" - -pushd ./android - -./gradlew assembleRelease - -popd - -package_name="rustls-platform-verifier" - -artifact_name="$package_name-release.aar" - -pushd ./android-release-support - -artifact_path="../android/$package_name/build/outputs/aar/$artifact_name" - -cp ./pom-template.xml ./maven/pom.xml - -# This sequence is meant to workaround the incompatibilites between macOS's sed -# command and the GNU command. Referenced from the following: -# https://stackoverflow.com/questions/5694228/sed-in-place-flag-that-works-both-on-mac-bsd-and-linux -sed -i.bak "s/\$VERSION/$version/" ./maven/pom.xml -rm ./maven/pom.xml.bak - -mvn install:install-file -Dfile="$artifact_path" -Dpackaging="aar" -DpomFile="./maven/pom.xml" -DlocalRepositoryPath="./maven/" - -rm ./maven/pom.xml - -pushd ./maven/ - -artifacts_folder="org/rustls/$package_name/$version" - -rm "$artifacts_folder/_remote.repositories" - -sha1sum "$artifacts_folder/$package_name-$version.aar" > "$artifacts_folder/$package_name-$version.aar.sha1" -sha1sum "$artifacts_folder/$package_name-$version.pom" > "$artifacts_folder/$package_name-$version.pom.sha1" - diff --git a/ci/verify_android_release.sh b/ci/verify_android_release.sh deleted file mode 100755 index abd0fa14..00000000 --- a/ci/verify_android_release.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash - -# This script's purpose is to verify that no test-only code is present inside of the release-mode Android artifact. -# It is validating that `javac` is performing the dead-code elimiation we expect and that `proguard` is deleting the -# unreferenced test code. This can be ran both locally and in CI. -# -# It accomplishes this goal by building the artifact and then running a decompiler on it to look for names we expect or do not. - -set -euo pipefail - -mkdir -p ./android/verification - -pushd ./android/ - -./gradlew clean -./gradlew assembleRelease - -pushd ./verification - -if [ ! -f "./bin/jadx" ]; then - echo "Decompiler not yet installed, downloading jadx" - curl -L https://github.com/skylot/jadx/releases/download/v1.4.7/jadx-1.4.7.zip --output jadx.zip - unzip jadx.zip - echo "jadx downloaded" -fi - -./bin/jadx -d decompiled ../rustls-platform-verifier/build/outputs/aar/rustls-platform-verifier-release.aar - -if grep -r -q "mock" ./decompiled; then - echo "❌ Test-only code exists in release artifact! Please review changes made to locate the cause". - exit 1 -else - echo "✅ No test-only code found in release artifact" -fi - -if grep -r -q "verifyCertificateChain" ./decompiled; then - echo "✅ JNI entrypoint present in release artifact" -else - echo "❌ JNI entrypoint not found in release artifact! Please review changes made to optimization rules which might cause this" - exit 1 -fi diff --git a/deny.toml b/deny.toml deleted file mode 100644 index 7a687b71..00000000 --- a/deny.toml +++ /dev/null @@ -1,18 +0,0 @@ -[advisories] -yanked = "deny" - -[licenses] -allow = [ - "Apache-2.0", - "BSD-3-Clause", - "CDLA-Permissive-2.0", - "ISC", - "MIT", - "Unicode-3.0", -] -exceptions = [{ allow = ["ISC", "MIT", "OpenSSL"], name = "ring" }] - -[[licenses.clarify]] -name = "ring" -expression = "ISC AND MIT AND OpenSSL" -license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] diff --git a/rustls-platform-verifier/Cargo.toml b/rustls-platform-verifier/Cargo.toml deleted file mode 100644 index 6e8f46b6..00000000 --- a/rustls-platform-verifier/Cargo.toml +++ /dev/null @@ -1,64 +0,0 @@ -[package] -name = "rustls-platform-verifier" -version = "0.7.0" -description = "rustls-platform-verifier supports verifying TLS certificates in rustls with the operating system verifier" -keywords = ["tls", "certificate", "verification", "os", "native"] -repository = "https://github.com/rustls/rustls-platform-verifier" -license = "MIT OR Apache-2.0" -edition = "2021" -rust-version = "1.85" - -[lib] -name = "rustls_platform_verifier" - -[features] -# Enables a C interface to use for testing where `cargo` can't be used. -# This feature is not stable, nor is the interface exported when it is enabled. -# Do not rely on this or use it in production. -ffi-testing = ["android_logger", "rustls/aws-lc-rs"] -# Enables APIs that expose lower-level verifier types for debugging purposes. -dbg = [] -# Enables `log::debug` base64-encoded logging of all end-entity certificates processed -# by the platform's verifier. -cert-logging = ["base64"] -# Used for nicely documenting the Android-specific APIs. This feature is not stable. -docsrs = ["jni", "once_cell"] - -[dependencies] -rustls = { version = "0.23.27", default-features = false, features = ["std"] } -log = { version = "0.4" } -base64 = { version = "0.22", optional = true } # Only used when the `cert-logging` feature is enabled. -jni = { version = "0.22.4", default-features = false, optional = true } # Only used during doc generation -once_cell = { version = "1.9", optional = true } # Only used during doc generation - -[target.'cfg(all(unix, not(target_os = "android"), not(target_vendor = "apple"), not(target_arch = "wasm32")))'.dependencies] -rustls-native-certs = "0.8" -webpki = { package = "rustls-webpki", version = "0.103", default-features = false } - -[target.'cfg(target_os = "android")'.dependencies] -once_cell = "1.9" -rustls-platform-verifier-android = { path = "../android-release-support", version = "0.1.0" } -jni = { version = "0.22", default-features = false } -webpki = { package = "rustls-webpki", version = "0.103", default-features = false } -android_logger = { version = "0.15", optional = true } # Only used during testing. - -[target.'cfg(target_arch = "wasm32")'.dependencies] -webpki = { package = "rustls-webpki", version = "0.103", default-features = false } -webpki-root-certs = "1" - -[target.'cfg(any(target_vendor = "apple"))'.dependencies] -core-foundation = "0.10" -core-foundation-sys = "0.8" -security-framework = "3.5.0" -security-framework-sys = "2.15" - -[target.'cfg(windows)'.dependencies] -windows-sys = { version = ">=0.52.0, <0.62.0", default-features = false, features = ["Win32_Foundation", "Win32_Security_Cryptography"] } - -[dev-dependencies] -rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs"] } -webpki-root-certs = "1" - -[package.metadata.docs.rs] -rustdoc-args = ["--cfg", "docsrs"] -features = ["dbg", "docsrs"] diff --git a/rustls-platform-verifier/LICENSE-APACHE b/rustls-platform-verifier/LICENSE-APACHE deleted file mode 120000 index 965b606f..00000000 --- a/rustls-platform-verifier/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE \ No newline at end of file diff --git a/rustls-platform-verifier/LICENSE-MIT b/rustls-platform-verifier/LICENSE-MIT deleted file mode 120000 index 76219eb7..00000000 --- a/rustls-platform-verifier/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT \ No newline at end of file diff --git a/rustls-platform-verifier/README.md b/rustls-platform-verifier/README.md deleted file mode 120000 index 32d46ee8..00000000 --- a/rustls-platform-verifier/README.md +++ /dev/null @@ -1 +0,0 @@ -../README.md \ No newline at end of file diff --git a/rustls-platform-verifier/examples/update-certs.rs b/rustls-platform-verifier/examples/update-certs.rs deleted file mode 100644 index 1970f97d..00000000 --- a/rustls-platform-verifier/examples/update-certs.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::{fs, io::Write, net::TcpStream, sync::Arc}; - -use rustls::{pki_types::ServerName, ClientConfig, ClientConnection, RootCertStore, Stream}; -use webpki_root_certs::TLS_SERVER_ROOT_CERTS; - -fn main() -> Result<(), Box> { - let mut roots = RootCertStore::empty(); - let (_, ignored) = roots.add_parsable_certificates(TLS_SERVER_ROOT_CERTS.iter().cloned()); - assert_eq!(ignored, 0, "{ignored} root certificates were ignored"); - let config = Arc::new( - ClientConfig::builder() - .with_root_certificates(roots) - .with_no_client_auth(), - ); - - for &host in HOSTS { - let server_name = ServerName::try_from(host)?; - let mut conn = ClientConnection::new(config.clone(), server_name)?; - let mut sock = TcpStream::connect((host, 443))?; - let mut stream = Stream::new(&mut conn, &mut sock); - - eprintln!("connecting to {host}..."); - if let Err(err) = stream.write_all(b"GET / HTTP/1.1\r\n\r\n") { - eprintln!("failed to write to {host}: {err}"); - } - - let Some(certs) = conn.peer_certificates() else { - eprintln!("no certificates received for {host}"); - continue; - }; - - for (i, der) in certs.iter().enumerate() { - let host_name = host.replace('.', "_"); - let fname = format!( - "{}/src/tests/verification_real_world/{host_name}_valid_{}.crt", - env!("CARGO_MANIFEST_DIR"), - i + 1 - ); - fs::write(&fname, der.as_ref())?; - eprintln!("wrote certificate to {fname}"); - } - } - - Ok(()) -} - -// We use two different CAs for better coverage and... -const HOSTS: &[&str] = &[ - // This host is using EC-based certificates for coverage. - "letsencrypt.org", - // This host is using RSA-based certificates for coverage. - "aws.amazon.com", -]; diff --git a/rustls-platform-verifier/src/android.rs b/rustls-platform-verifier/src/android.rs deleted file mode 100644 index 1e36bedf..00000000 --- a/rustls-platform-verifier/src/android.rs +++ /dev/null @@ -1,250 +0,0 @@ -//! On Android, initialization must be done before any verification is attempted. -//! -//!

-//! Some manual setup is required outside of cargo to use this crate on Android. In order to use -//! Android’s certificate verifier, the crate needs to call into the JVM. A small Kotlin component -//! must be included in your app’s build to support rustls-platform-verifier. -//! -//! See the [crate's Android section][crate#android] for more details. -//!
-//! -//! # Examples -//! -//! ``` -//! // A typical entrypoint signature for obtaining the necessary pointers -//! pub fn android_init(raw_env: *mut c_void, raw_context: *mut c_void) -> Result<(), jni::errors::Error> { -//! let mut env = unsafe { JNIEnv::from_raw(raw_env as *mut jni::sys::JNIEnv).unwrap() }; -//! let context = unsafe { JObject::from_raw(raw_context as jni::sys::jobject) }; -//! rustls_platform_verifier::android::init_with_env(&mut env, context)?; -//! } -//! ``` - -use jni::errors::Error as JNIError; -use jni::objects::{Global, JClass, JClassLoader, JObject}; -use jni::strings::JNIStr; -use jni::{jni_sig, jni_str, Env, JavaVM}; -use once_cell::sync::OnceCell; - -static GLOBAL: OnceCell = OnceCell::new(); - -/// A layer to access the Android runtime which is hosting the current -/// application process. -/// -/// Generally this trait should be implemented in your Rust app component's FFI -/// initialization layer. -pub trait Runtime: Send + Sync { - /// Returns a handle to the current process' JVM. - fn java_vm(&self) -> &JavaVM; - /// Returns a reference to the current app's [Context]. - /// - /// [Context]: - fn context(&self) -> &Global>; - /// Returns a reference to the class returned by the current JVM's `getClassLoader` call. - fn class_loader(&self) -> &Global>; -} - -enum GlobalStorage { - Internal { - java_vm: JavaVM, - context: Global>, - loader: Global>, - }, - External(&'static dyn Runtime), -} - -impl GlobalStorage { - fn vm(&self) -> &JavaVM { - match self { - GlobalStorage::Internal { java_vm, .. } => java_vm, - GlobalStorage::External(runtime) => runtime.java_vm(), - } - } - - fn context(&self, env: &mut Env) -> Result { - let context = match self { - Self::Internal { context, .. } => context, - Self::External(global) => global.context(), - }; - - let loader = match self { - Self::Internal { loader, .. } => loader, - Self::External(global) => global.class_loader(), - }; - - Ok(GlobalContext { - context: env.new_global_ref(context)?, - loader: env.new_global_ref(loader)?, - }) - } -} - -pub(super) struct GlobalContext { - /// The Android application [Context](https://developer.android.com/reference/android/app/Application). - pub(super) context: Global>, - loader: Global>, -} - -fn global() -> &'static GlobalStorage { - GLOBAL - .get() - .expect("Expect rustls-platform-verifier to be initialized") -} - -/// Initialize given a typical Android NDK [`Env`] and [`JObject`] context. -/// -/// This method will setup and store an environment locally. This is useful if nothing else in your -/// application needs to access the Android runtime. -pub fn init_with_env(env: &mut Env, context: JObject) -> Result<(), JNIError> { - GLOBAL.get_or_try_init(|| -> Result<_, JNIError> { - let loader = env - .call_method( - &context, - jni_str!("getClassLoader"), - jni_sig!(() -> JClassLoader), - &[], - )? - .l()?; - let loader = env.cast_local::(loader)?; - - Ok(GlobalStorage::Internal { - java_vm: env.get_java_vm()?, - context: env.new_global_ref(context)?, - loader: env.new_global_ref(loader)?, - }) - })?; - Ok(()) -} - -/// Initialize with a runtime that can dynamically serve references to -/// the JVM, context, and class loader. -/// -/// This is the most flexible option, and is useful for advanced use cases. -/// -/// This function will never panic. -pub fn init_with_runtime(runtime: &'static dyn Runtime) { - GLOBAL.get_or_init(|| GlobalStorage::External(runtime)); -} - -/// Initialize with references to the JVM, context, and class loader. -/// -/// This is useful when you're already interacting with `jni-rs` wrapped objects and want to use -/// global references to objects for efficiency. -/// -/// This function will never panic. -/// -/// # Examples -/// -/// ``` -/// pub fn android_init(raw_env: *mut c_void, raw_context: *mut c_void) -> Result<(), jni::errors::Error> { -/// let mut env = unsafe { jni::EnvUnowned::from_raw(raw_env as *mut jni::sys::JNIEnv).unwrap() }; -/// let context = unsafe { JObject::from_raw(raw_context as jni::sys::jobject) }; -/// let loader = env.get_object_class(&context)?.get_class_loader(env)?; -/// -/// env.with_env(|env| { -/// rustls_platform_verifier::android::init_with_refs( -/// env.get_java_vm(), -/// env.new_global_ref(context)?, -/// env.new_global_ref(loader)?, -/// ); -/// }); -/// } -/// ``` -pub fn init_with_refs( - java_vm: JavaVM, - context: Global>, - loader: Global>, -) { - GLOBAL.get_or_init(|| GlobalStorage::Internal { - java_vm, - context, - loader, - }); -} - -/// Wrapper for JNI errors that will log and clear exceptions -/// It should generally be preferred to `jni::errors::Error` -#[derive(Debug)] -pub(super) struct Error; - -impl From for Error { - #[track_caller] - fn from(cause: JNIError) -> Self { - if let JNIError::JavaException = cause { - let _ = global() - .vm() - .with_top_local_frame(|env| -> Result<(), JNIError> { - env.exception_describe(); - env.exception_clear(); - Ok(()) - }); - } - - Self - } -} - -pub(super) struct LocalContext<'a, 'env> { - pub(super) env: &'a mut Env<'env>, - pub(super) global: GlobalContext, -} - -impl<'env> LocalContext<'_, 'env> { - /// Load a class from the application class loader - /// - /// This should be used instead of `JNIEnv::find_class` to ensure all classes - /// in the application can be found. - fn load_class(&mut self, name: &'static JNIStr) -> Result, Error> { - let name = self.env.new_string(name.to_str())?; - self.global - .loader - .load_class(self.env, name) - .map_err(Error::from) - } -} - -/// Borrow the Android application context and execute the closure -/// `with_context, ensuring locals are properly freed and exceptions -/// are cleared. -pub(super) fn with_context(f: F) -> Result -where - F: FnOnce(&mut LocalContext) -> Result, -{ - let global = global(); - // Use `attach_current_thread_for_scope()` to avoid permanently attaching any threads. - // See https://github.com/rustls/rustls-platform-verifier/pull/185. - global.vm().attach_current_thread_for_scope(|env| { - let global_context = global.context(env)?; - let mut context = LocalContext { - env, - global: global_context, - }; - f(&mut context) - }) -} - -/// Loads and caches a class on first use -pub(super) struct CachedClass { - name: &'static JNIStr, - class: OnceCell>>, -} - -impl CachedClass { - /// Creates a lazily initialized class reference to the class with `name`. - pub(super) const fn new(name: &'static JNIStr) -> Self { - Self { - name, - class: OnceCell::new(), - } - } - - /// Gets the cached class reference, loaded on first use - pub(super) fn get(&self, cx: &mut LocalContext) -> Result<&JClass<'static>, Error> { - let class = self.class.get_or_try_init(|| -> Result<_, Error> { - let class = cx.load_class(self.name)?; - - Ok(cx.env.new_global_ref(class)?) - })?; - - Ok(class) - } -} diff --git a/rustls-platform-verifier/src/lib.rs b/rustls-platform-verifier/src/lib.rs deleted file mode 100644 index 3c1da291..00000000 --- a/rustls-platform-verifier/src/lib.rs +++ /dev/null @@ -1,92 +0,0 @@ -#![cfg_attr(docsrs, feature(doc_cfg))] -#![doc = include_str!("../README.md")] -#![warn(missing_docs)] - -use std::sync::Arc; - -#[cfg(feature = "dbg")] -use rustls::crypto::CryptoProvider; -#[cfg(feature = "dbg")] -use rustls::pki_types::CertificateDer; -use rustls::{client::WantsClientCert, ClientConfig, ConfigBuilder, WantsVerifier}; - -mod verification; -pub use verification::Verifier; - -// Build the Android module when generating docs so that -// the Android-specific functions are included regardless of -// the host. -#[cfg(any(all(doc, docsrs), target_os = "android"))] -#[cfg_attr(docsrs, doc(cfg(target_os = "android")))] -pub mod android; - -/// Fixures and data to support testing the server -/// certificate verifier. -#[cfg(any(test, feature = "ffi-testing"))] -mod tests; - -// Re-export any exported functions that are required for -// tests to run in a platform-native environment. -#[cfg(feature = "ffi-testing")] -#[cfg_attr(feature = "ffi-testing", allow(unused_imports))] -pub use tests::ffi::*; - -/// Exposed for debugging certificate issues with standalone tools. -/// -/// This is not intended for production use, you should use [`BuilderVerifierExt`] or -/// [`ConfigVerifierExt`] instead. -#[cfg(feature = "dbg")] -pub fn verifier_for_dbg( - root: CertificateDer<'static>, - crypto_provider: Arc, -) -> Arc { - Arc::new(Verifier::new_with_fake_root(root, crypto_provider)) -} - -/// Extension trait to help configure [`ClientConfig`]s with the platform verifier. -pub trait BuilderVerifierExt { - /// Configures the `ClientConfig` with the platform verifier. - /// - /// ```rust - /// use rustls::ClientConfig; - /// use rustls_platform_verifier::BuilderVerifierExt; - /// let config = ClientConfig::builder() - /// .with_platform_verifier() - /// .unwrap() - /// .with_no_client_auth(); - /// ``` - fn with_platform_verifier( - self, - ) -> Result, rustls::Error>; -} - -impl BuilderVerifierExt for ConfigBuilder { - fn with_platform_verifier( - self, - ) -> Result, rustls::Error> { - let verifier = Verifier::new(self.crypto_provider().clone())?; - Ok(self - .dangerous() - .with_custom_certificate_verifier(Arc::new(verifier))) - } -} - -/// Extension trait to help build a [`ClientConfig`] with the platform verifier. -pub trait ConfigVerifierExt { - /// Build a [`ClientConfig`] with the platform verifier and the default `CryptoProvider`. - /// - /// ```rust - /// use rustls::ClientConfig; - /// use rustls_platform_verifier::ConfigVerifierExt; - /// let config = ClientConfig::with_platform_verifier(); - /// ``` - fn with_platform_verifier() -> Result; -} - -impl ConfigVerifierExt for ClientConfig { - fn with_platform_verifier() -> Result { - Ok(ClientConfig::builder() - .with_platform_verifier()? - .with_no_client_auth()) - } -} diff --git a/rustls-platform-verifier/src/tests/ffi.rs b/rustls-platform-verifier/src/tests/ffi.rs deleted file mode 100644 index c35da370..00000000 --- a/rustls-platform-verifier/src/tests/ffi.rs +++ /dev/null @@ -1,142 +0,0 @@ -//! Thin wrappers ontop the existing test suites that allow them to be ran -//! in the context of a platform-native environment as required by the verifier implementation. -#![allow(missing_docs)] - -#[cfg(target_os = "android")] -pub use android::*; -#[cfg(target_os = "android")] -mod android { - //! Tests which run inside the context of a Android device, typically an emulator. - //! - //! Note: These tests run inside the same application context, so they share the same mock test - //! store. This will remain non-problematic as long as roots are different enough (for the use case) and - //! real roots are never removed from the store. - //! - //! It is intentional that the tests run sequentially, as dropping a `Verifier` will reset its mock - //! root store. - use crate::tests; - use jni::{ - objects::{JClass, JObject, JString}, - sys::jstring, - EnvUnowned, Outcome, - }; - use std::sync::Once; - - static ANDROID_INIT: Once = Once::new(); - - /// A marker that the Kotlin test runner looks for to determine - /// if a set of integration tests passed or not. - const SUCCESS_MARKER: &str = "success"; - - fn run_android_test<'caller>( - env: &mut EnvUnowned<'caller>, - cx: JObject, - suite_name: &'static str, - test_cases: &'static [fn()], - ) -> JString<'caller> { - let outcome = env - .with_env(|env| { - // These can't fail, and even if they did, Android will crash the process like we want. - ANDROID_INIT.call_once(|| { - let log_filter = android_logger::FilterBuilder::new() - .parse("trace") - .filter_module("jni", log::LevelFilter::Off) - .build(); - - android_logger::init_once( - android_logger::Config::default() - .with_max_level(log::Level::Trace.to_level_filter()) - .with_filter(log_filter), - ); - crate::android::init_with_env(env, cx).unwrap(); - }); - - for test in test_cases { - test(); - } - - env.new_string(SUCCESS_MARKER) - }) - .into_outcome(); - - match outcome { - Outcome::Ok(success) => success, - Outcome::Err(_) | Outcome::Panic(_) => { - panic!("failed to run test suite '{suite_name}'") - } - } - } - - #[export_name = "Java_org_rustls_platformverifier_CertificateVerifierTests_mockTests"] - pub extern "C" fn rustls_platform_verifier_mock_test_suite( - mut env: EnvUnowned<'_>, - _class: JClass, - cx: JObject, - ) -> jstring { - log::info!("running mock test suite..."); - - run_android_test( - &mut env, - cx, - "mock tests", - tests::verification_mock::ALL_TEST_CASES, - ) - .into_raw() - } - - #[export_name = "Java_org_rustls_platformverifier_CertificateVerifierTests_verifyMockRootUsage"] - pub extern "C" fn rustls_platform_verifier_verify_mock_root_usage( - mut env: EnvUnowned<'_>, - _class: JClass, - cx: JObject, - ) -> jstring { - log::info!("verifying mock roots are not used by default..."); - - run_android_test( - &mut env, - cx, - "mock root verification", - &[tests::verification_mock::verification_without_mock_root], - ) - .into_raw() - } - - #[export_name = "Java_org_rustls_platformverifier_CertificateVerifierTests_realWorldTests"] - pub extern "C" fn rustls_platform_verifier_real_world_test_suite( - mut env: EnvUnowned<'_>, - _class: JClass, - cx: JObject, - ) -> jstring { - log::info!("running real world suite..."); - - run_android_test( - &mut env, - cx, - "real world", - tests::verification_real_world::ALL_TEST_CASES, - ) - .into_raw() - } -} - -#[cfg(not(target_os = "android"))] -mod dummy { - //! A module to prevent dead-code warnings all over - //! the `tests` module due to the weird combination of - //! feature flags and `--all-features`. These test case - //! lists are only used via the FFI. - - use crate::tests; - - #[allow(dead_code)] - fn dummy() { - #[cfg(any( - windows, - target_os = "android", - target_vendor = "apple", - target_os = "linux" - ))] - let _ = tests::verification_mock::ALL_TEST_CASES; - let _ = tests::verification_real_world::ALL_TEST_CASES; - } -} diff --git a/rustls-platform-verifier/src/tests/mod.rs b/rustls-platform-verifier/src/tests/mod.rs deleted file mode 100644 index f464a75e..00000000 --- a/rustls-platform-verifier/src/tests/mod.rs +++ /dev/null @@ -1,60 +0,0 @@ -#[cfg(feature = "ffi-testing")] -pub mod ffi; - -use std::{error::Error as StdError, sync::Arc}; - -mod verification_real_world; - -mod verification_mock; - -use rustls::{ - crypto::CryptoProvider, - pki_types, CertificateError, - Error::{self as TlsError, InvalidCertificate}, -}; - -struct TestCase<'a, E: StdError> { - /// The name of the server we're connecting to. - pub reference_id: &'a str, - - /// The certificates presented by the TLS server, in the same order. - pub chain: &'a [&'a [u8]], - - /// The stapled OCSP response given to us by Rustls, if any. - pub stapled_ocsp: Option<&'a [u8]>, - - /// The time to use as the current time for verification. - pub verification_time: pki_types::UnixTime, - - pub expected_result: Result<(), TlsError>, - - /// An error that should be present inside an expected `CertificateError::Other` variant. - /// - /// Set this if the error being tested uses `CertificateError::Other` and not statically known - /// variants in [TlsError] - #[allow(dead_code)] - pub other_error: Option, -} - -pub fn assert_cert_error_eq( - result: &Result<(), TlsError>, - expected: &Result<(), TlsError>, - expected_err: Option<&E>, -) { - // If the expected error is an "Other" CertificateError we can't directly assert equality, we rely - // on the test caller to provide the correct value to compare. - if let Err(InvalidCertificate(CertificateError::Other(err))) = &expected { - let expected_err = expected_err.expect("error not provided for `Other` case handling"); - let err: &E = err - .0 - .downcast_ref() - .expect("incorrect `Other` inner error kind"); - assert_eq!(err, expected_err); - } else { - assert_eq!(result, expected); - } -} - -fn test_provider() -> Arc { - Arc::new(rustls::crypto::aws_lc_rs::default_provider()) -} diff --git a/rustls-platform-verifier/src/tests/verification_mock/ca.go b/rustls-platform-verifier/src/tests/verification_mock/ca.go deleted file mode 100644 index ec3b4de1..00000000 --- a/rustls-platform-verifier/src/tests/verification_mock/ca.go +++ /dev/null @@ -1,314 +0,0 @@ -// Generates the test data files used in the tests in verification_mock.rs. -// -// After re-generating mock certificates be sure to also update the fixed -// verification timestamp in `mod.rs`'s `verification_time` fn to match -// the current time. -// -// The primary point of this program is to fully automate the creation of the -// test data, with minimal tool dependencies (e.g. no OpenSSL), with low effort. -// -// This program isn't run as part of the build. Instead, it generates data files -// that are valid for a long time, so they don't need to be regenerated to avoid -// expiration. -// -// Files generated by this program are named "A-B-ee_C[-D].{crt, ocsp}" where -// A is the (subject) name the root certificate, B is the (subject) name of the -// intermediate certificate, C is the (subjectAltName DNS name) name of the -// end-entity certificate, and D is some distinguishing feature (e.g. "revoked"). -// -// When this program was first written, it was thought that such conventions, -// and the structure of the program, would make it easy to create certificates -// that are similar but slightly different, e.g. same hostname, same issuer -// name, but different roots. It's still to be determined if this structure -// actually facilitates that. -// -// The other goal of this program is to serve as a model for the `webpki` -// crate's planned self-contained all-Rust test suite. In particular, this -// program was originally developed to accelerate the Rust test data generator -// for the `webpki` crate. - -package main - -import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/x509" - "crypto/x509/pkix" - "errors" - "fmt" - "io/ioutil" - "math/big" - "net" - "os" - "strings" - "time" - - "golang.org/x/crypto/ocsp" -) - -const ( - OneDay = time.Hour * 24 - OneYear = OneDay * 365 -) - -func main() { - err := doIt() - if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) - os.Exit(1) - } -} - -func doIt() error { - now := time.Now().Truncate(time.Minute).UTC() - - // "ee_1" -> "::1" is IPv6 localhost, omitting ":" characters b/c invalid for file paths on Windows - end_entities := [3]string{"ee_example.com", "ee_127.0.0.1", "ee_1"} - - var err error = nil - - root1_key, err := generateRoot("root1", now) - if err != nil { - return err - } - - root1_int1_key, err := generateInt("root1-int1", 2, now, root1_key) - if err != nil { - return err - } - - for _, ee := range end_entities { - err = generateEndEntity("root1-int1-"+ee+"-good", 1, now, root1_int1_key) - if err != nil { - return err - } - - err = generateEndEntity("root1-int1-"+ee+"-revoked", 2, now, root1_int1_key) - if err != nil { - return err - } - - err = generateEndEntity("root1-int1-"+ee+"-wrong_eku", 3, now, root1_int1_key) - if err != nil { - return err - } - } - - return nil -} - -// Generates a binary DER X.509 file with name `eeName` + ".crt". The certificate will have -// the given serial number (which should be unique per issuer), OCSP status (ocsp.Good, -// ocsp.Revoked, etc.), signed by the given key. -func generateEndEntity(eeName string, serial int64, now time.Time, caKey crypto.Signer) error { - nameParts := strings.Split(eeName, "-") - caName := nameParts[0] + "-" + nameParts[1] - eeBaseName := nameParts[2] - label := nameParts[3] - - caCert, err := readCert(caName) - if err != nil { - return err - } - eePubKey, err := generatePubKey() - if err != nil { - return err - } - - // macOS requirements reference: https://support.apple.com/en-us/HT210176 - template := x509.Certificate{ - NotBefore: now.Add(-OneDay), - // macOS >=10.15 requires that certificates must have a - // validity period of 825 days or fewer. - NotAfter: now.Add(2 * OneYear), - // macOS >=10.15 requires that server certificates must have the - // id-kp-serverAuth OID present in the EKU. - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - } - - switch eeBaseName { - case "ee_example.com": - template.SerialNumber = big.NewInt(serial) - template.DNSNames = []string{"example.com"} - case "ee_127.0.0.1": // IPv4 localhost - template.SerialNumber = big.NewInt(serial) - template.IPAddresses = []net.IP{net.IPv4(127, 0, 0, 1)} - case "ee_1": // IPv6 localhost, e.g. "::1" - template.SerialNumber = big.NewInt(serial) - template.IPAddresses = []net.IP{net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}} - default: - return errors.New("Unrecognized end entity certificate:" + eeName) - } - - ocspStatus := ocsp.Unknown // Don't generate an OCSP response. - - switch label { - case "good": - ocspStatus = ocsp.Good - case "revoked": - ocspStatus = ocsp.Revoked - case "wrong_eku": - template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection} - } - - cert, err := x509.CreateCertificate(rand.Reader, &template, caCert, eePubKey, caKey) - if err != nil { - return err - } - err = ioutil.WriteFile(eeName+".crt", cert, 0666) - if err != nil { - return err - } - - if ocspStatus != ocsp.Unknown { - err = generateOCSPResponse(eeName, ocspStatus, now, caKey) - if err != nil { - return err - } - } - - return nil -} - -// Generates a binary DER X.509 file with name `intName` + ".crt". -func generateInt(intName string, serial int64, now time.Time, caKey crypto.Signer) (crypto.Signer, error) { - nameParts := strings.Split(intName, "-") - caName := nameParts[0] - - caCert, err := readCert(caName) - if err != nil { - return nil, err - } - intKey, err := generateKey() - if err != nil { - return nil, err - } - - template := x509.Certificate{ - Subject: pkix.Name{ - Organization: []string{intName}, - }, - NotBefore: now.Add(-OneDay), - NotAfter: now.Add(OneYear), - IsCA: true, - KeyUsage: x509.KeyUsageCertSign, - BasicConstraintsValid: true, - SerialNumber: big.NewInt(serial), - } - - cert, err := x509.CreateCertificate(rand.Reader, &template, caCert, intKey.Public(), caKey) - if err != nil { - return nil, err - } - err = ioutil.WriteFile(intName+".crt", cert, 0666) - if err != nil { - return nil, err - } - - return intKey, nil -} - -func generateRoot(name string, now time.Time) (crypto.Signer, error) { - caKey, err := generateKey() - if err != nil { - return nil, err - } - template := x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - Organization: []string{name}, - }, - NotBefore: now.Add(-OneDay), - NotAfter: now.Add(OneYear), - IsCA: true, - KeyUsage: x509.KeyUsageCertSign, - BasicConstraintsValid: true, - } - - cert, err := x509.CreateCertificate(rand.Reader, &template, &template, caKey.Public(), caKey) - if err != nil { - return nil, err - } - return caKey, ioutil.WriteFile(name+".crt", cert, 0666) -} - -func generateOCSPResponse(name string, status int, now time.Time, caKey crypto.Signer) error { - nameParts := strings.Split(name, "-") - caName := nameParts[0] + "-" + nameParts[1] - - caCert, err := readCert(caName) - if err != nil { - return err - } - eeCert, err := readCert(name) - if err != nil { - return err - } - - // It seems we must have `thisUpdate >= eeCert.NotBefore` or else - // Windows won't trust the OCSP response. In particular, if the - // response is `revoked` but this date is too early, then it will - // not consider the response revoked! - thisUpdate := eeCert.NotBefore.Add(1) - - template := ocsp.Response{ - Status: status, - SerialNumber: eeCert.SerialNumber, - ThisUpdate: thisUpdate, - NextUpdate: thisUpdate.Add(1 * OneYear), - } - - if status == ocsp.Revoked { - template.RevokedAt = thisUpdate - } - - response, err := ocsp.CreateResponse(caCert, caCert, template, caKey) - if err != nil { - return err - } - - return ioutil.WriteFile(name+".ocsp", response, 0666) -} - -func generateKey() (crypto.Signer, error) { - key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - if err != nil { - return nil, err - } - return key, nil -} - -func generatePubKey() (*ecdsa.PublicKey, error) { - privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - if err != nil { - return nil, err - } - return &privateKey.PublicKey, nil -} - -func readCert(name string) (*x509.Certificate, error) { - der, err := ioutil.ReadFile(name + ".crt") - if err != nil { - return nil, err - } - return x509.ParseCertificate(der) -} - -func readKey(name string) (*ecdsa.PrivateKey, error) { - pkcs8, err := ioutil.ReadFile(name + ".p8") - if err != nil { - return nil, err - } - privateKey, err := x509.ParsePKCS8PrivateKey(pkcs8) - if err != nil { - return nil, err - } - switch k := privateKey.(type) { - case *ecdsa.PrivateKey: - return k, nil - default: - return nil, errors.New("Unexpected private key type") - } -} diff --git a/rustls-platform-verifier/src/tests/verification_mock/go.mod b/rustls-platform-verifier/src/tests/verification_mock/go.mod deleted file mode 100644 index fd0986f3..00000000 --- a/rustls-platform-verifier/src/tests/verification_mock/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module briansmith.org/webpki-test-certs - -go 1.17 - -require golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 diff --git a/rustls-platform-verifier/src/tests/verification_mock/go.sum b/rustls-platform-verifier/src/tests/verification_mock/go.sum deleted file mode 100644 index 666a1bd6..00000000 --- a/rustls-platform-verifier/src/tests/verification_mock/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI= -golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= diff --git a/rustls-platform-verifier/src/tests/verification_mock/mod.rs b/rustls-platform-verifier/src/tests/verification_mock/mod.rs deleted file mode 100644 index 83f272dc..00000000 --- a/rustls-platform-verifier/src/tests/verification_mock/mod.rs +++ /dev/null @@ -1,412 +0,0 @@ -//! Tests of certificate verification that require our own test CA to be -//! trusted. -//! -//! # Re-generating the test data -//! -//! `cd src/tests/verification_mock && go run ca.go` -//! -//! # Repeatability and Self-containedness -//! -//! These tests are only supported on platforms where we have implemented the -//! ability to trust a CA (only) for the duration of a test, without modifying -//! the operating system's trust store--i.e. without affecting the security of -//! any parts of the system outside of these tests. See the `#![cfg(...)]` -//! immediately below to see which platforms run these tests. - -#![cfg(all( - any(windows, unix, target_os = "android"), - // These OSes require a simulator runtime and bundle. - not(target_os = "tvos"), - not(target_os = "watchos"), - not(target_os = "visionos") -))] - -use core::time::Duration; -use std::convert::TryFrom; -use std::net::IpAddr; -#[cfg(not(any(target_vendor = "apple", windows)))] -use std::net::{Ipv4Addr, Ipv6Addr}; -use std::sync::Arc; - -use rustls::client::danger::ServerCertVerifier; -use rustls::pki_types; -#[cfg(not(any(target_vendor = "apple", windows)))] -use rustls::pki_types::{DnsName, ServerName}; -use rustls::{CertificateError, Error as TlsError, OtherError}; - -use super::TestCase; -use crate::tests::{assert_cert_error_eq, test_provider}; -use crate::verification::{EkuError, Verifier}; - -macro_rules! mock_root_test_cases { - { $( $name:ident [ $target:meta ] => $test_case:expr ),+ , } => { - mock_root_test_cases!(@ $($name [ $target ] => $test_case),+,); - - #[cfg(test)] - mod tests { - $( - #[cfg($target)] - #[test] - pub fn $name() { - super::$name() - } - )+ - } - - #[cfg(feature = "ffi-testing")] - pub static ALL_TEST_CASES: &'static [fn()] = &[ - $( - #[cfg($target)] - $name, - )+ - - ]; - }; - - {@ $( $name:ident [ $target:meta ] => $test_case:expr ),+ , } => { - $( - #[cfg($target)] - pub(super) fn $name() { - test_with_mock_root(&$test_case, Roots::OnlyExtra); - #[cfg(all($target, not(target_os = "android")))] - test_with_mock_root(&$test_case, Roots::ExtraAndPlatform); - } - )+ - }; -} - -macro_rules! no_error { - () => { - None:: - }; -} - -const ROOT1: pki_types::CertificateDer<'static> = - pki_types::CertificateDer::from_slice(include_bytes!("root1.crt")); -const ROOT1_INT1: &[u8] = include_bytes!("root1-int1.crt"); -const ROOT1_INT1_EXAMPLE_COM_GOOD: &[u8] = include_bytes!("root1-int1-ee_example.com-good.crt"); -const ROOT1_INT1_LOCALHOST_IPV4_GOOD: &[u8] = include_bytes!("root1-int1-ee_127.0.0.1-good.crt"); -const ROOT1_INT1_LOCALHOST_IPV6_GOOD: &[u8] = include_bytes!("root1-int1-ee_1-good.crt"); - -const EXAMPLE_COM: &str = "example.com"; -const LOCALHOST_IPV4: &str = "127.0.0.1"; -const LOCALHOST_IPV6: &str = "::1"; - -#[cfg(any(test, feature = "ffi-testing"))] -#[cfg_attr(feature = "ffi-testing", allow(dead_code))] -pub(super) fn verification_without_mock_root() { - let crypto_provider = test_provider(); - - // Since Rustls 0.22 constructing a webpki verifier (like the one backing Verifier on unix - // systems) without any roots produces `OtherError(NoRootAnchors)` - since our FreeBSD CI - // runner fails to find any roots with openssl-probe we need to provide webpki-root-certs here - // or the test will fail with the `OtherError` instead of the expected `CertificateError`. - #[cfg(target_os = "freebsd")] - let verifier = Verifier::new_with_extra_roots( - webpki_root_certs::TLS_SERVER_ROOT_CERTS.iter().cloned(), - crypto_provider, - ) - .unwrap(); - - #[cfg(not(target_os = "freebsd"))] - let verifier = Verifier::new(crypto_provider).unwrap(); - - let server_name = pki_types::ServerName::try_from(EXAMPLE_COM).unwrap(); - let end_entity = pki_types::CertificateDer::from(ROOT1_INT1_EXAMPLE_COM_GOOD); - let intermediates = [pki_types::CertificateDer::from(ROOT1_INT1)]; - - // Fails because the server cert has no trust root in Windows, and can't since it uses a self-signed CA. - // Similarly on UNIX platforms using the Webpki verifier, it can't fetch extra certificates through - // AIA chasing or other mechanisms, and so we know this test will correctly verify an unknown - // root in a chain fails validation. - let result = verifier.verify_server_cert( - &end_entity, - &intermediates, - &server_name, - &[], - verification_time(), - ); - - assert_eq!( - result.map(|_| ()), - Err(TlsError::InvalidCertificate( - CertificateError::UnknownIssuer - )) - ); -} - -#[test] -fn test_verification_without_mock_root() { - verification_without_mock_root() -} - -// Note: Android does not currently support IP address hosts, so these tests are disabled for -// Android. -// Verifies that our test trust anchor(s) are not trusted when `Verifier::new()` -// is used. -mock_root_test_cases! { - valid_no_stapling_dns [ any(windows, unix) ] => TestCase { - reference_id: EXAMPLE_COM, - chain: &[ROOT1_INT1_EXAMPLE_COM_GOOD, ROOT1_INT1], - stapled_ocsp: None, - verification_time: verification_time(), - expected_result: Ok(()), - other_error: no_error!(), - }, - valid_no_stapling_dns_trailing_label [ any(windows, unix) ] => TestCase { - // XXX: `pki_types` validates that the label is still valid and that cases such as two `.` characters - // are still correctly rejected. - reference_id: "example.com.", - chain: &[ROOT1_INT1_EXAMPLE_COM_GOOD, ROOT1_INT1], - stapled_ocsp: None, - verification_time: verification_time(), - expected_result: Ok(()), - other_error: no_error!(), - }, - valid_no_stapling_ipv4 [ any(windows, unix) ] => TestCase { - reference_id: LOCALHOST_IPV4, - chain: &[ROOT1_INT1_LOCALHOST_IPV4_GOOD, ROOT1_INT1], - stapled_ocsp: None, - verification_time: verification_time(), - expected_result: Ok(()), - other_error: no_error!(), - }, - valid_no_stapling_ipv6 [ any(windows, unix) ] => TestCase { - reference_id: LOCALHOST_IPV6, - chain: &[ROOT1_INT1_LOCALHOST_IPV6_GOOD, ROOT1_INT1], - stapled_ocsp: None, - verification_time: verification_time(), - expected_result: Ok(()), - other_error: no_error!(), - }, - valid_stapled_good_dns [ any(windows, unix) ] => TestCase { - reference_id: EXAMPLE_COM, - chain: &[ROOT1_INT1_EXAMPLE_COM_GOOD, ROOT1_INT1], - stapled_ocsp: Some(include_bytes!("root1-int1-ee_example.com-good.ocsp")), - verification_time: verification_time(), - expected_result: Ok(()), - other_error: no_error!(), - }, - valid_stapled_good_ipv4 [ any(windows, unix) ] => TestCase { - reference_id: LOCALHOST_IPV4, - chain: &[ROOT1_INT1_LOCALHOST_IPV4_GOOD, ROOT1_INT1], - stapled_ocsp: Some(include_bytes!("root1-int1-ee_127.0.0.1-good.ocsp")), - verification_time: verification_time(), - expected_result: Ok(()), - other_error: no_error!(), - }, - valid_stapled_good_ipv6 [ any(windows, unix) ] => TestCase { - reference_id: LOCALHOST_IPV6, - chain: &[ROOT1_INT1_LOCALHOST_IPV6_GOOD, ROOT1_INT1], - stapled_ocsp: Some(include_bytes!("root1-int1-ee_1-good.ocsp")), - verification_time: verification_time(), - expected_result: Ok(()), - other_error: no_error!(), - }, - - // The revocation tests use a separate certificate from the one used in the "good" case to deal - // with operating systems with validation data caches (e.g. Windows). - // Linux is not included, since the webpki verifier does not presently support OCSP revocation - // checking. - - // Check that self-signed certificates, which may or may not be revokved, do not return any - // kind of revocation error. It is expected that non-public certificates without revocation information - // have no revocation checking performed across platforms. - revoked_dns [ any(windows, target_os = "android", target_vendor = "apple") ] => TestCase { - reference_id: EXAMPLE_COM, - chain: &[include_bytes!("root1-int1-ee_example.com-revoked.crt"), ROOT1_INT1], - stapled_ocsp: None, - verification_time: verification_time(), - expected_result: Ok(()), - other_error: no_error!(), - }, - stapled_revoked_dns [ any(windows, target_os = "android", target_vendor = "apple") ] => TestCase { - reference_id: EXAMPLE_COM, - chain: &[include_bytes!("root1-int1-ee_example.com-revoked.crt"), ROOT1_INT1], - stapled_ocsp: Some(include_bytes!("root1-int1-ee_example.com-revoked.ocsp")), - verification_time: verification_time(), - expected_result: Err(TlsError::InvalidCertificate(CertificateError::Revoked)), - other_error: no_error!(), - }, - stapled_revoked_ipv4 [ any(windows, target_os = "android", target_vendor = "apple") ] => TestCase { - reference_id: LOCALHOST_IPV4, - chain: &[include_bytes!("root1-int1-ee_127.0.0.1-revoked.crt"), ROOT1_INT1], - stapled_ocsp: Some(include_bytes!("root1-int1-ee_127.0.0.1-revoked.ocsp")), - verification_time: verification_time(), - expected_result: Err(TlsError::InvalidCertificate(CertificateError::Revoked)), - other_error: no_error!(), - }, - stapled_revoked_ipv6 [ any(windows, target_os = "android", target_vendor = "apple") ] => TestCase { - reference_id: LOCALHOST_IPV6, - chain: &[include_bytes!("root1-int1-ee_1-revoked.crt"), ROOT1_INT1], - stapled_ocsp: Some(include_bytes!("root1-int1-ee_1-revoked.ocsp")), - verification_time: verification_time(), - expected_result: Err(TlsError::InvalidCertificate(CertificateError::Revoked)), - other_error: no_error!(), - }, - // Validation fails with no intermediate (that can't be fetched - // with AIA because there's no AIA issuer field in the certificate). - // (AIA is an extension that allows downloading of missing data, - // like missing certificates, during validation; see - // https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.2.1). - ee_only_dns [ any(windows, unix) ] => TestCase { - reference_id: EXAMPLE_COM, - chain: &[ROOT1_INT1_EXAMPLE_COM_GOOD], - stapled_ocsp: None, - verification_time: verification_time(), - expected_result: Err(TlsError::InvalidCertificate(CertificateError::UnknownIssuer)), - other_error: no_error!(), - }, - ee_only_ipv4 [ any(windows, unix) ] => TestCase { - reference_id: LOCALHOST_IPV4, - chain: &[ROOT1_INT1_LOCALHOST_IPV4_GOOD], - stapled_ocsp: None, - verification_time: verification_time(), - expected_result: Err(TlsError::InvalidCertificate(CertificateError::UnknownIssuer)), - other_error: no_error!(), - }, - ee_only_ipv6 [ any(windows, unix) ] => TestCase { - reference_id: LOCALHOST_IPV6, - chain: &[ROOT1_INT1_LOCALHOST_IPV6_GOOD], - stapled_ocsp: None, - verification_time: verification_time(), - expected_result: Err(TlsError::InvalidCertificate(CertificateError::UnknownIssuer)), - other_error: no_error!(), - }, - // Validation fails when the certificate isn't valid for the reference ID. - domain_mismatch_dns [ any(windows, unix) ] => TestCase { - reference_id: "example.org", - chain: &[ROOT1_INT1_EXAMPLE_COM_GOOD, ROOT1_INT1], - stapled_ocsp: None, - verification_time: verification_time(), - #[cfg(not(any(target_vendor = "apple", windows)))] - expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForNameContext { - expected: ServerName::DnsName(DnsName::try_from("example.org").unwrap()), - presented: vec!["DnsName(\"example.com\")".to_owned()] - })), - #[cfg(any(target_vendor = "apple", windows))] - expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForName)), - other_error: no_error!(), - }, - domain_mismatch_ipv4 [ any(windows, unix) ] => TestCase { - reference_id: "198.168.0.1", - chain: &[ROOT1_INT1_LOCALHOST_IPV4_GOOD, ROOT1_INT1], - stapled_ocsp: None, - verification_time: verification_time(), - #[cfg(not(any(target_vendor = "apple", windows)))] - expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForNameContext { - expected: ServerName::IpAddress(pki_types::IpAddr::V4(Ipv4Addr::from([198, 168, 0, 1]).into())), - presented: vec!["IpAddress(127.0.0.1)".to_owned()], - })), - #[cfg(any(target_vendor = "apple", windows))] - expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForName)), - other_error: no_error!(), - }, - domain_mismatch_ipv6 [ any(windows, unix) ] => TestCase { - reference_id: "::ffff:c6a8:1", - chain: &[ROOT1_INT1_LOCALHOST_IPV6_GOOD, ROOT1_INT1], - stapled_ocsp: None, - verification_time: verification_time(), - #[cfg(not(any(target_vendor = "apple", windows)))] - expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForNameContext { - expected: ServerName::IpAddress(pki_types::IpAddr::V6(Ipv6Addr::from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 198, 168, 0, 1]).into())), - presented: vec!["IpAddress(0::1)".to_owned()], - })), - #[cfg(any(target_vendor = "apple", windows))] - expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForName)), - other_error: no_error!(), - }, - wrong_eku_dns [ any(windows, unix) ] => TestCase { - reference_id: EXAMPLE_COM, - chain: &[include_bytes!("root1-int1-ee_example.com-wrong_eku.crt"), ROOT1_INT1], - stapled_ocsp: None, - verification_time: verification_time(), - expected_result: Err(TlsError::InvalidCertificate( - CertificateError::Other(OtherError(Arc::from(EkuError))))), - other_error: Some(EkuError), - }, - wrong_eku_ipv4 [ any(windows, unix) ] => TestCase { - reference_id: LOCALHOST_IPV4, - chain: &[include_bytes!("root1-int1-ee_127.0.0.1-wrong_eku.crt"), ROOT1_INT1], - stapled_ocsp: None, - verification_time: verification_time(), - expected_result: Err(TlsError::InvalidCertificate( - CertificateError::Other(OtherError(Arc::from(EkuError))))), - other_error: Some(EkuError), - }, - wrong_eku_ipv6 [ any(windows, unix) ] => TestCase { - reference_id: LOCALHOST_IPV6, - chain: &[include_bytes!("root1-int1-ee_1-wrong_eku.crt"), ROOT1_INT1], - stapled_ocsp: None, - verification_time: verification_time(), - expected_result: Err(TlsError::InvalidCertificate( - CertificateError::Other(OtherError(Arc::from(EkuError))))), - other_error: Some(EkuError), - }, -} - -fn test_with_mock_root( - test_case: &TestCase, - root_src: Roots, -) { - log::info!("verifying {:?}", test_case.expected_result); - - let provider = test_provider(); - let verifier = match root_src { - Roots::OnlyExtra => Verifier::new_with_fake_root(ROOT1, provider), // TODO: time - #[cfg(not(target_os = "android"))] - Roots::ExtraAndPlatform => Verifier::new_with_extra_roots([ROOT1], provider).unwrap(), - }; - let mut chain = test_case - .chain - .iter() - .map(|bytes| pki_types::CertificateDer::from(*bytes)); - - let end_entity = chain.next().unwrap(); - let intermediates: Vec> = chain.collect(); - - let server_name = pki_types::ServerName::try_from(test_case.reference_id).unwrap(); - - if test_case.reference_id.parse::().is_ok() { - assert!(matches!(server_name, pki_types::ServerName::IpAddress(_))); - } else { - assert!(matches!(server_name, pki_types::ServerName::DnsName(_))); - } - - let result = verifier.verify_server_cert( - &end_entity, - &intermediates, - &server_name, - test_case.stapled_ocsp.unwrap_or(&[]), - test_case.verification_time, - ); - - assert_cert_error_eq( - &result.map(|_| ()), - &test_case.expected_result, - test_case.other_error.as_ref(), - ); - // TODO: get into specifics of errors returned when it fails. -} - -enum Roots { - /// Test with only extra roots, without loading the platform trust store. - /// - /// We want to keep things reproducible given the background-managed nature of trust roots on platforms. - OnlyExtra, - /// Test with loading the extra roots and the platform trust store. - /// - /// Right now, not all platforms are supported. - #[cfg(not(target_os = "android"))] - ExtraAndPlatform, -} - -/// Return a fixed [`pki_types::UnixTime`] for certificate validation purposes. -/// -/// We fix the "now" value used for certificate validation to a fixed point in time at which -/// we know the test certificates are valid. This must be updated if the mock certificates -/// are regenerated. -pub(crate) fn verification_time() -> pki_types::UnixTime { - // Wed, Sep 9 2026 11:52 UTC - pki_types::UnixTime::since_unix_epoch(Duration::from_secs(1_788_954_730)) -} diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-good.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-good.crt deleted file mode 100644 index 510b3e2c1dd6044ee0463c3d4eb6f08e7f4c54d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 414 zcmXqLVw`2r#3;XjnTe5!iILHOi;Y98&EuRc3p2BUsG+cdARBWi3zslgQGR}jp>Aeg ziJ^g>v6{y+fpvSU%&6@0+x*bC7p+Y z`&WEB_|Wr|*JAo333vSjcn75@B3cyWY5n1L|R z!Lq6>JRqNFvoW%=vNJOp$b$rxStJa^8bo-sthKfp+g}O)T6MgCeodCI`7vVyIgkQD zM#ldvA_hY30t~1CC0Lj<84S{x3>j|nXhfRt|1^J!)LRQd>B;906^rsSzk2A`c*H_? zmH8SAanZkLr78_Ky^zV?bm)WUKA@JFF7;(^c$Arh#T>TJe_L&u{(ah_eQexidxG}& Zw@-MeUc5<;LzY)*bI%J^oi_*61ORd!iOv83 diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-good.ocsp b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-good.ocsp deleted file mode 100644 index b55d9b9ce8591b81741d363812e51e6c24b0d01e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmXqLVpQj1WLVI|s9?~Q6+(6V&*g%ktIh2J< zn5!s1zr;{CGq1!@g5Su%$jrdfz|zpr#KgcL${@uc$-v5hlZ{oIkC{n|m4QWMMp&$X zbt>DFB`f^@+kUt?bziCdet=rb8I+*tpvTzlDxnkkx$Y~e&0*I3`Ir-n%05j1ArD{& diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-revoked.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-revoked.crt deleted file mode 100644 index b113904bd95646192420ad955429a6239df048e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 412 zcmXqLVw_>n#3;XjnTe4Jhzz*cIJDY4&e^gsGaHB+3L6NrF^95n33CQvOpI)-+Kns>O3X~&mK!0Bu@A#D8;Oc zxz(b1?D*W(VP4z!y*X$!C-KM8)JJ=wXBKbUC3VemMeWJg$&+8y-f(eU9AOYjXyZhN|d(ps2jU8bRvaif}d+!6!pP2%0 z7cOa?eV&^&^goYpQr3kKrw0l_Oa^LS)$cubj#s~KETp&d$+9JfzIbcR7b>aSy)EdZ ZNifsZAF{_)O+Ly!b&j8ZyQpA?F918?kEH+r diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-revoked.ocsp b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-revoked.ocsp deleted file mode 100644 index a804705ea8baa6beeaf1cb24a66b92b7894b03e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 316 zcmXqLVzl65WLVI|XlT&HsLRHw&Bn;e%5K2O$kN0p2NaSrXk55Z+(6V&*g%ktIh2J< zn5!s1zr;{CGq1!@g5Su%$jrdfz|zpr#KgcL%AnMs*uctwlZ{oIkC{n|m4QWMMp&$X zbt>DFB`f^@+kUt?bz^!2$*&_kiuQ~4|J7N=xB@I7`V;BEbf zgYg?CEI-!uY0>ZByL67JZQRzsD*XSKBfn*t3>o&i^>Su2>h2FR;5i}0_;uqC-yEZF gX**<pT2Aeg ziJ^gQfPA9O#>mRb&dg#U4-!;nkuVTz5aH3X*4k=pep{@bwVgR{mL1*n;KbZhXRZPO3IvNY diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-good.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-good.crt deleted file mode 100644 index bbf745ce9e7cf857757a52072020a8f34d00fdbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 401 zcmXqLV(c|&ViaD$%*4pV#K>sC#m1r4=5fxJg_+qv)KJ(!kc~N%g-e*LC_lf%P&YHL z#Lz%aoY%2(Yn(jbUPBW7TeCVNhaDVqjS*q1^0O zUscgoe7j(ZVi5D9Urr|;#_hkVvD)X)vq)tH6QlbUCpKSXZ8DQu)5iCzp;(K{pfOUq za{9i-9}dQB+5WSy!Q|D#XFVkv@*O%{pZ8z2w_aINqJE?@C2Y0uuAyDQAx7TVtXoWt8D zc}bDAj$XIb+RV#~X3gr!cfZPH$nclX?9=pO-&p=@@0FFg72RHU|1xiQ__b)w2J`G^ YZ^c6OG@L9S9cB5^w;{R6-~!h^04JM;Z2$lO diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-good.ocsp b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-good.ocsp deleted file mode 100644 index 5bdd13cc88bf90691e3311692e33b0524fafd3ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmXqLVpQj1WLVI|s9?~Q6+(6V&*g%ktIh2J< zn5!s1zr;{CGq1!@g5Su%$jrdfz|zpr#KgcL${@uc$-v5hlZ{oIkC{n|m4QWMMp&$X zbt>DFB`f^@+kUt?bz6w80oZYsYr5+uz z{`m4~VRZf1i(lSFot)y>)3tDd5`$ia6_X*uj#u*O%|+{PRmfd1PkTGnx?5jBwN{|k e_J7mE7>zlu0h@kY_T+k&o6-`+;4YG-&mfpJRyl6ITAFALXxC?cr(aa<&bhFj++M6swi6 z)3^OUj?~7Sj{l@JSNZ~9{|t^E`_r@c2$V=pD2-q2yJK7Eblu0A?*te77;~KC^l`ox6Q%ajqSwiylMW1K#D~2VdBvT+Uy! z@Y8%fX@$ezZds)kn@scfn8f#h$slvpRHo)PW!d79ci1276n*;P<*DV11)e&-zbfKW Z9{%#F_RK>q{uYi)Ov~p9`gHkE0|0`phB*KL diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-revoked.ocsp b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-revoked.ocsp deleted file mode 100644 index d739981b3caaa70c1d013e8a616db4567c114891..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 316 zcmXqLVzl65WLVI|XlT&HsLRHw&Bn;e%5K2O$kN0p2NaSrXk55Z+(6V&*g%ktIh2J< zn5!s1zr;{CGq1!@g5Su%$jrdfz|zpr#KgcL%AnMs*uctwlZ{oIkC{n|m4QWMMp&$X zbt>DFB`f^@+kUt?bz&slCWXAY&-62o gM!b~nUu|%;j%(Gz8Mm~~x|c*P4ffpmz}3?X0K^_*yZ`_I diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-wrong_eku.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-wrong_eku.crt deleted file mode 100644 index 9c190311511b74475f8220619ca05517c7727a04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 402 zcmXqLV(c?$ViaD$%*4pV#K>&G#m1r4=5fxJg_+qv)KJ(!kc~N%g-e*LC_lf%P&YHL z#Lz%aoY%2(Yn(jbUPBW7TeCVNhaDVqn?96=I{Y zapU6o4h=898_GS9W0r8bEw@pxT7PbWPRz@T{}pa6y=?kX?2_l3BY{?vbN}Q-S}E%a z`y0OB?|e0RkXffNWb zGX7`bFkowEsb^qdL=Fn(Oa_BACPRh;inFYnKkG)G?8<8VurKQ0m$z3!J(sEGJ)L#X zJNDI^`ZbGxi&p0@em*%^|Avx&fg(`Lhm!b9uk3H~R{q`9s(5Vvp+m7M3!Gn1I_AB6 b_sy7jid|;sb~(BvI~ZwCPdK)Aeg ziJ^gQfPA9O#>mRb&dg{a4-!;nkuVTz5aH3X*4k=peXu|2oUP0-@9<(fGiCQA3FvN5FT ho^6=eRsMvx-eA+2H#$;{7MFLrGzV-dp1)+{HUQ#Pi~0Zn diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-good.ocsp b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-good.ocsp deleted file mode 100644 index 4196cc7dc358ac359c1acff22c2ba3f1e0f1796e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 298 zcmXqLVpQW|WLVI|C~wfjD9y&H&Bn;e%5K2O$kN2f0~F#gXq>Q6+(6V&*g%ktIh2J< zn5!s1zr;{CGq1!@g5Su%$jrdfz|zpr#KgcL${@uc$-v5hlZ{oIkC{n|m4QWMMp&$X zbt>DFB`f^@+kUt?bzm<)U-PZi|rTWR&{n!1T*T%x1FK>_PDb|bE- z4_F*tu3N;P!L&uvZcDq|j77f`=Nd#EVKT5dy@Qi;!@mygi`Q?LriP0#o>t6yel>mR eQC`iYW8Qxiy-)056}UL@okjm^*5_|RuLA(_1Y7+8 diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-revoked.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-revoked.crt deleted file mode 100644 index 5c6f7ff5f1e9818d962e1a27d36ed95d52eb5e9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 409 zcmXqLVw`Hw#3;3ZnTe4Jhzz*cIJDY4&e^gsGaHB+3L6NrF^95n33CQvOpI)-+Kns>O3XK39u~Tr|L@bD!yhv;y{DG$=;A*cn6z$b{7xKvf9SHG1>oSRf3!Mt((29wP8Z5Yqo1QFAtl!>s{NAmwbzZ4FU~>few~c zW#Iw&M4OF~m6e^D(Lf#~sLUc^Al4wlqh+nN)!6<@_}8lA{qt+Ge9ey;8%Tl_2r@GM zXW=*CZQ@R?NX#wBN!3fv&qWRq=1c~IG$uoauZ5cff+n-}PqaRN+{Aq9Yvv03Y|SFp zu7j;ta`b~AG4*;hzL=1tcX@ZN;sUuKCsUx7z40fVL5KOR1v(*eHzZ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-revoked.ocsp b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-revoked.ocsp deleted file mode 100644 index 20de8ba7d3f82014fc580158fba92f70b2df93b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 315 zcmXqLVl?MsWLVI|XkgI9sKdso&Bn;e%5K2O$kN0p3lx$xXk55Z+(6V&*g%ktIh2J< zn5!s1zr;{CGq1!@g5Su%$jrdfz|zpr#KgcL%AnMs*uctwlZ{oIkC{n|m4QWMMp&$X zbt>DFB`f^@+kUt?bzgX(`J}-zmBc! za_E}bPqzftB{Z`ITluv<5L!Q5eZ6&cuQ!uH8FT)>sq-2fZK|T3`>Ov~tq}O%zf
@n6KmKd)^NymUjSOtCV!bcXFY@go4yJ7F*Y diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-wrong_eku.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-wrong_eku.crt deleted file mode 100644 index b041eb0335b1294728e95a3f7b2285c64f2b41da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 408 zcmXqLVw_^o#3;3ZnTe5!iILfWi;Y98&EuRc3p2BUsG+cdARBWi3zslgQGR}jp>Aeg ziJ^g{QNu%BWmClUfz>e(2!3Kc_!axVh zs#f9Fc51H;nA|z+G=cnCH!mE@&5TWS-$4Sj143~3IrJ$ z|FiHL@HTO$RwU*YTErpv2TO z-#hHu^8QEtw?F>> gD%|nQxckX;i*`{xD<`d@&}PLcVdqtjm-Jr&06gQ2M*si- diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1.crt deleted file mode 100644 index 4b0bc94d5d57574a07561ff51513642de5a80ad3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 441 zcmXqLV%%!b#AvmEnTe4Jhzz*cIJDY4&e^gsGaCpP@)_{3F^95n39}aE=a(27$cghB znHg9bSQr|bm>3vD0lDTtt|gRfAZjRVAPCXH1=gXPnFrKXW+1@E4mOsFk&RWmk%d8t zIf;Qq`p)`Etf%@!)^`U!eG{hmEi+B^V_?_9ugR)$vu^vBIk218xu3YR#(lxu+e~8L ze@Bb5ifzl}FW5Q9w`JY2%W5*ovmSe^_Z2C|o)BlSI{rVuIlcR-c3ARG@k5Q3wR{e} zi<1o!4fuf0k>zJ({LjL|%*4cEzz^aJgZQil%s|RO79_yOBE}-Zqh+nN)!6<@_}8lA z{qt+Ge9ey;8_0vCm02VV#2Q4>n@+x0c1>=vbb8`aaA9%3Yf4=_a)>c!G8m*W88W=M zY5P}d`<(^B3qRdo;Ar*h{nql0oO0hw1m+%!+LfLlCT1>lZRfTHyplI$y-GK`vjMf- zy0k@Mh92wD-pD`G1ZJ_>^Q&3d1nqs|k!)Vuq3c>2GOH`&70W7?1$@HY`c=9?y8+Gs BkGTK< diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1.crt b/rustls-platform-verifier/src/tests/verification_mock/root1.crt deleted file mode 100644 index addcbe344851a8f22f50ff874b7fe0fa5c1e1e55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 402 zcmXqLV(c?$ViZ}x%*4pV#K>sC#m1r4=5fxJg_+qvz>v>?hmARug-e*VC_lf%&_GU{ z*T~Gk(!j#d(8R>RAPUGe2XZZ;Tr4`u3vaxD6vM?wyCo!-zie)ul;@2^K zAhu|R#>UM~t|=iI6Wg=015e!7iM-Z(ORU8}xc@wdmcJ;A&I$zpf=x?;x$-u#Y59oGTen!Us zEG*1SOe_ZcAigk&&uYL7qzq(10(>lDEF$SmC*Lc(Cbw8RJ#i_xu(;nfr7j*hESNJG z3{sg46joNWt+lv*cvk!L!Vr(>nc`8UhmtzXeohf?*nh`b#;$3jkc`=zYq^)d>~Q^( z9>v9E$Z)%1qifECUB9PZuHJIoUcGYJ$2s?mmZwI1leleHWV$?b8BJx-Ad& J`IK|70sztOge?F7 diff --git a/rustls-platform-verifier/src/tests/verification_real_world/aws_amazon_com_valid_1.crt b/rustls-platform-verifier/src/tests/verification_real_world/aws_amazon_com_valid_1.crt deleted file mode 100644 index 3e6174d29f680a71a70ca0e1d2d6b909c5699bcd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1620 zcmXqLVhb>6VzpSn%*4pVB*0w0_4UN5sF5G!!=wh3Mx?EHBne1RJWCoS$pZ#HfVq z7DiSE<|amd2B0_>QxhX2!$YMynfz(T@-E36ucS4OL&rQxc|%A@9?!*1XP#;N&^!HTGGjSIT$;Tn@5yq;iIT!1?A-n52bzF8~F6Xr1nW?93s@MwwrIz?C^SY$<(+;C9|@LPn@Zf zsXcC&<^&D>Rr}0rcduEpY-R5CJq}wpdR=IIb3J9x!B`dZ*E1EopY5oa{P)qO&5hOy z*Q^dHo@(aN76@9L-+WO1m-ttQK537K{8?)?E_ldF*l{fJF!#MvJwtfIt7R{(HO-eC zxmkJ8Pxvy++M7!pce))~|FH4h`^|f&KNDnPW@KPo+{B!3(8Qc=AP)>gS!EUp1F;4X z`JhROVND_RjcGSzZMjw%#kAk$Fpvc);A0VE5ebw|d@<>IS@e<%u2XZkw*9^R_JxE& z<0_E6AWP#SgU0zyXmQab0gM~n(qi56)Z!9dBcxbr5-Kk**F%lDCUHZMh;B|&5mY5e z9vG!f!jR|%NhW7x<|Tp!#la?k;~iB3W@&07&{Ck0h@>G5a;pLhj{%nf2OC>@BM&1J zqX8dCke`w9KMM;p6YBy4up(8EA~p_fHbz!fc4kHcYmg#&783&_1A_&63v}AFGD=Dc ztn~Ghi*odejB*W3p!Sp$l@^zP+@}u_1@vr)SLQFD$iht|s4L| zyN$Q9ZzQ>|e;qBNVEZ6?rItbNYmdOE6M7H3i>iCfWm05V`dE79y?Yvg{HxzOhvrmV zT7LJ`w$Iu{H}7n}@#$6A{&ipn>+r2@lu0oPOc6n1$*ncCcBg} z3%0bhdzW5oTC7n=a>o#N-*{_D-b_l=(=Rn%5Uq%t1A9B;wI zXkV0G8)~j6c>`+iV#2eT8Gz zn1A0JFV8afzEsF|^?48XOWs?)Jn877Dbnkf`|V*5b&{GOl(vpFWqRlzuNfL*m3ywF z{aBiRH_ym#Q}{pYknrbR-*a(tOm9_SL~w)#Za=hxeX_OCd$v#h=9c!HF>RrQUJy61Zjs)iZd(7R{9YGYO= z!^y2Nfm?MLb3z~V{&&~e{h=}Kq>W1YEnd#jBc8v^mu{0+n>%;X!_?mMA0J%TGuV@{ zf6k-L58fQlm3G{$crokN`n?$st$0!;cJe!!v~5|XZYMdhv>_(V{mqPmSx3!oe<{yj GQVRf@x?d## diff --git a/rustls-platform-verifier/src/tests/verification_real_world/aws_amazon_com_valid_2.crt b/rustls-platform-verifier/src/tests/verification_real_world/aws_amazon_com_valid_2.crt deleted file mode 100644 index 46289c19da4673c0f13859e5253e7824f0a2acab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1122 zcmXqLVu>?oVs=}=%*4pVB+Onc4uMDj~a- zk(GhDiIJZHD9**y#K_2S_GPGC!&K&#kHvStUl?2Q^;4Av!~K&pjz{Tz|M7Zh+@!kt z{ficOHFHF~eEi0t@Itm~qw0n9{jHntuYKjxCmtl9b3oAZddTziqE{BDkI&57_A83v zBJ*3zuXokckF)GLxUNE1{bS>$c^Ok!Bi5=V>}Bk~z9jzR^;~lu7a=jVJJkzk+}O8Q zy)PFr%L`)tD&(Mxp`Cp9-A!6zpAilR(37x*ZkKXIc~M@%$1B}VrFDuT-?MM1q_ie10i5Y$_g_w{%7GZ zU;|Q2j0^^RATfTB7z;2tv>C{P_^K>o1|n=6+H8!htnAE;a269pnvX?{MMOSmQes$B zNPT144Ov^RRYo!GcR38?LAsS$Bn-qFL|P=yw3@ux!!u=y9!KPyg|F#Rzy*j;%SvVpC4XYNyZqueDIH?Wn(er1#pyOY_$c_K4Ti)rRD@5@bx zr}-aJ-<_!GoDx@bBKVD%DAP^;Je4wgPSMi*86F*pUw*IO@Z<+qXRlAx)5#wLq|^)x z_r36WyZ&Cy-O0+lVdYDkrY{rhI=#G?d9}y?b@LYNVV-BAa%bk1$u0Lxv$F1tXVPPoa5(NU)o=9z8c*#C+E+W52cZFWFnv3dMc40eXyjw=E|+} z&+UZl<~-s2pj@@YB0^{q?~V7u^LU!rWDO)!4PX05Yx#vz#l cJ6|<^mzXJf`@*AJcA0C6q7TkEc1`mN0BYTpH2?qr diff --git a/rustls-platform-verifier/src/tests/verification_real_world/aws_amazon_com_valid_3.crt b/rustls-platform-verifier/src/tests/verification_real_world/aws_amazon_com_valid_3.crt deleted file mode 100644 index 1dfb0e70faadbce1e2af09df6a528e061e25c471..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1174 zcmXqLVwq&n#9Xz2nTe5!NtmsEikFu9+0VZYtXt&%@HGdA42zO&3iHgVpPe@$jfpL&Ho zaeesn?-sFNCAa3xHnr6MwLu|(EvM>2!=`0F*S~R>><_SDzq6>_GUCUst1Ewcq#I0o zH>DyjzjJZm0fV-*g|2D4LS8REJn^T;o&OhK80gmOmK2y??@;+Z??`0c?&5awPeyJU zJDh)QmtA#g%cuD&e7fG>3uEFk|G%2bblRe8U4TXY?ccgd(npWWg*+^OkbZ)HPP_6o zdk2Mjg@z*yK6chBYJDv0a%P=a#mmIZ$iTR`i4hc1x(58fV3ZYRWc<&BXZ91O#*&g90u|rX=N4(1F;5?Iq?kl zS212U(6;DUv?JGR&L_c%%?1^09NKJ*tgP&ej4U|@Sq6GAz5!#KQbtKhft9{~esXbv zUJ)?Kq#Nlaf+M)3sI<65FF8NgzyhwGsZ9r}KDnp_hjM+WDaolt2DTul$+MUlm>3u> zFj%06q&gUl2?wuSxc5gSU+HB1acyiV>Yx!i8X-0}GJzQ4Ld2736RdMcw_D3L6N5MA(J7f|K)0N{Ukwb5eYzHCKm)Z-)bolIh;O^CBy1|pF@brm-wq|5 z7mj4oZa8D&et+k^38}eE=XEA7TlV~3tAuAwn%`op+?8A3ndt4}W!_|WKI zv(GhNbagYDR?vQriOL92MMqM(_))}EJy(# zix`W@oOp)&s~E2vXj^nF+L7xu=ab;XW&?SUv@(l?fmnmc{`l<|&)t=8`w|_x=FWA2 zLwqYco*VeHacHwKva+%>GO{=uI2y>n_y&w^0vRPG1y=g{`Fh30MtY^i`UZ+{S*A8o zsB8sHwn8sCwa7pXWTQNbyn(EN^a9BR;&64vFm*+G$wfKf#3!r3!ezi=z{bWBDyzVN zoG5^q379At8I)xocZHv-eJNh)^HDDJ@0s8{J9qy3$3j1CVljK$ARl}r)90=P*H74Fd{#O_po!_n5B;yg30GLpeQe}7cz1c}bbpDRx%a+Lp2)GQ&Oztx!B@M> z<{7h<%kpy62j7)WS>2@mG4rZ#+`2Cd50@^FaMAr_Q!r!AuFP#y%j@|K<8=NkXt#3D z{7@PHMI>a&)HuNkp#=4F^ADB(R}L%Sm?WBn diff --git a/rustls-platform-verifier/src/tests/verification_real_world/letsencrypt_org_valid_1.crt b/rustls-platform-verifier/src/tests/verification_real_world/letsencrypt_org_valid_1.crt deleted file mode 100644 index b63ff84e5a14471408aea0ab91b8bd97ececf77c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1098 zcmXqLVsSHQVm`BgnTe5!Nr;tox90oD??km1zkJrNA+Yt|P6IAB4y`tibG9tZ%m&7W z+yuTN@;da;6QUUE@oL5U#`P!mv`S(rJ})yP0joY%fm$gJ!E1qYUi6|3% zo%YMP{-IC1x1@7a-si6wKc}zUeQNvSCZ_8KO-z>!_<&Z+@-s62XJKJxVs8Li2;{4> zfc(Ir&Bn;e%FfJazylH#1}S1PU@(vc@%UK8SVS0KA5&wP_iyP1uAYTcr+jspV0Lky zfjmfBnFZ*p29ceSpZd-#eQ3LE?(x4&L}FLdjnH-jW0=W|j4bK~ss_q1z5!F4ct%M{ zft9{~WvY>0rd|#xFhC)pZ_s!dWUL@d<7tD&6HP+N1wcuZz-p2LiWTMOm*`d)V#o<6 z7ZhU%N`nM(>EMLh-GmY?$@#gc0zlU_@s^jDBOKc#4B;Z22hxNg2#R2EXe)q1+kk_O zExnP4k%`ej59CvMNYE@$Tp-^jixeoy2z&HRE%cI$atxXndDyssamB)Tt&)j_iKX!` zOXDww9}J}oQ~aFUf4w>QzVWl9irNZ^RL0|%<1LsN?TgZDL(TOhZ!j=0&bP4b%wk|* zVP&lijNQ<U)T_J^vUu7#LU?)NaIf0Il~haAQ(rICD?t&lB}|Z{y^zmM6})6^hlX z3vIKK|8+!tT~V^RPbW~x-@yk;ZwvkS`lE1pweI8f>DQ36 zEOQ2fK`N6W!ye0*X9YiLeGk4NZnnHb>96b~C6SXC*-Lj#wkeuH+|em4Rab diff --git a/rustls-platform-verifier/src/tests/verification_real_world/letsencrypt_org_valid_2.crt b/rustls-platform-verifier/src/tests/verification_real_world/letsencrypt_org_valid_2.crt deleted file mode 100644 index 16d0fc4bb88794aa5562560d1227f39168844d08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 656 zcmXqLV(KwyViH`y%*4pVB;fmbW9+p96+Qtqm#ZV5eH1m|V&l+i^EhYA!pv--XUJ{9 z$;KSY!Y0fV8f?gGzzyPX3A1UiEDTD_NenE7v$hydymmxj@0pd=07GiZDb_M$95BjbM-7GQw283+SiCacN_S4M;IDG8o8$#Q0dmSVVS4e(F1~^r7vtxyS!95s6(*H$vMD=CPo8wkWKO|3I=ipG7F>@NFeJ@M%IlK^URqH z25C%&3@3_eEK84hn_s_ws`U9hr^ElWgH(LxsC@grQPQ|-LB^q1)q7qu99*@6t2q6i zMeSjApq5VK1^rVOH$9pnk+>;0Wti8IqAbzWr0T&yGR-4B;TNY+!1Ajwq z15P&PP!={}rqEzRO#^ihhf7$&Gp{7IC@-}{AviU;v?#NrQXwd{I5n{-IYYs{D8ID8 zP}D#eq?B2h$1^y{T_GqxzeFLz$Ush<*T~Gk)X>-f45Gw&jg1T}4UCM8O)X6=qYU&= zE#x)ehFHh~GR089fDd9QJJ`HPSA#MG0XBB9bD0>~ShX8j7?hZk7+7qC{;z(x_Gnh& zhK%1FvdZNj=HIF2x#Vft|J3Jo(xsmUJB4fBO1QKdetqM2{Y1pI4c`dEdC07!{ z3k*dKgh5J~g?T)KgWMH@^7Bg+A`A`W#CeU(3``A;4Zt8uoY&aMz|z3T$k^1<)G~_N z78)6p83?ek1HE+4mWh##RlAXeL5Vquf#vM%tMeP0x!!PkeHQbwi#cDu=3Y80uPpnv zJ7T}1xji35)LQt~a={@}4o2|7HrT zRl32IH!HgQ{)brx$|7bU z!p5P^#>mRb&ddmBF&XfKq=iAsSq+$h6hu8Aix`VYjnlMs_q;upodih1^`rvqzRbb&U;4JXs>u2_sf`d z;W~-B^o#B zdJyklw}LZluWKD+4hKGAJjVPlGqSG4tn)$ZjU&7JOB45R^5H!g1Cnpp8a01r``Lh{Pq>+0za=_#PKK5r~H%9>>F#htnck>Ix?5{PHbiC z97Ekp`71Z5KA9=l<9Um1`W%z~j1`G%Zb}@A_!P>s`oXPz22Mp=i-YG>&0;)t_TbFW zrH8z)EJ!!ntZcu1*1WF<`h^kw<*e6tyv=KR`;O^FcmKAvX|DU~H$3lnR{qgrmqcWN z+m!#@S6+zn8eJD%_0&yn-lMJT&D-RXe-?f3Z|%IPz Vec { - const VALID_AWS_NAMES: &[&str] = &[ - "aws.amazon.com", - "aws-us-west-2.amazon.com", - "www.aws.amazon.com", - "1.aws-lbr.amazonaws.com", - "amazonaws-china.com", - "www.amazonaws-china.com", - "aws-us-east-1.amazon.com", - ]; - - VALID_AWS_NAMES - .iter() - .copied() - .map(|name| format!("DnsName(\"{name}\")")) - .collect() -} - -const AWS_AMAZON_COM: &str = "aws.amazon.com"; - -// Domain names for which `VALID_AWS_AMAZON_COM_CHAIN` isn't valid. -const VALID_UNRELATED_DOMAIN: &str = "my.1password.com"; -const VALID_UNRELATED_SUBDOMAIN: &str = "www.amazon.com"; - -const LETSENCRYPT_ORG: &str = "letsencrypt.org"; - -const VALID_LETSENCRYPT_ORG_CHAIN: &[&[u8]] = &[ - include_bytes!("letsencrypt_org_valid_1.crt"), - include_bytes!("letsencrypt_org_valid_2.crt"), - include_bytes!("letsencrypt_org_valid_3.crt"), - include_bytes!("letsencrypt_org_valid_4.crt"), -]; - -macro_rules! real_world_test_cases { - { $( $name:ident => $test_case:expr ),+ , } => { - real_world_test_cases!(@ $($name => $test_case),+,); - - #[cfg(test)] - mod tests { - $( - #[test] - pub fn $name() { - super::$name() - } - )+ - - } - - #[cfg(feature = "ffi-testing")] - pub static ALL_TEST_CASES: &'static [fn()] = &[ - $($name),+ - ]; - }; - - {@ $( $name:ident => $test_case:expr ),+ , } => { - $( - pub(super) fn $name() { - real_world_test(&$test_case); - } - )+ - } -} - -macro_rules! no_error { - () => { - None:: - }; -} - -fn real_world_test(test_case: &TestCase) { - log::info!( - "verifying ref ID {:?} expected {:?}", - test_case.reference_id, - test_case.expected_result - ); - - let crypto_provider = test_provider(); - - // On BSD systems openssl-probe fails to find the system CA bundle, - // so we must provide extra roots from webpki-root-cert. - #[cfg(target_os = "freebsd")] - let verifier = Verifier::new_with_extra_roots( - webpki_root_certs::TLS_SERVER_ROOT_CERTS.iter().cloned(), - crypto_provider, - ) - .unwrap(); - - #[cfg(not(target_os = "freebsd"))] - let verifier = Verifier::new(crypto_provider).unwrap(); - - let mut chain = test_case - .chain - .iter() - .map(|bytes| pki_types::CertificateDer::from(*bytes)); - - let end_entity_cert = chain.next().unwrap(); - let intermediates: Vec> = chain.collect(); - - let server_name = pki_types::ServerName::try_from(test_case.reference_id).unwrap(); - - let stapled_ocsp = test_case.stapled_ocsp.unwrap_or(&[]); - - let result = verifier - .verify_server_cert( - &end_entity_cert, - &intermediates, - &server_name, - stapled_ocsp, - test_case.verification_time, - ) - .map(|_| ()); - - assert_cert_error_eq( - &result.map(|_| ()), - &test_case.expected_result, - None::<&std::convert::Infallible>, - ); - // TODO: get into specifics of errors returned when it fails. -} - -// Prefer to staple the OCSP response for the end-entity certificate for -// performance and repeatability. -real_world_test_cases! { - // The certificate is valid for *.aws.amazon.com. - aws_amazon_com_valid => TestCase { - reference_id: AWS_AMAZON_COM, - chain: VALID_AWS_AMAZON_COM_CHAIN, - stapled_ocsp: None, - verification_time: verification_time(), - expected_result: Ok(()), - other_error: no_error!(), - }, - // Same as above but without stapled OCSP. - aws_amazon_com_valid_no_stapled => TestCase { - reference_id: AWS_AMAZON_COM, - chain: VALID_AWS_AMAZON_COM_CHAIN, - stapled_ocsp: None, - verification_time: verification_time(), - expected_result: Ok(()), - other_error: no_error!(), - }, - // Valid also for www.amazon.amazon.com (extra subdomain). - _aws_amazon_com_valid => TestCase { - reference_id: "www.aws.amazon.com", - chain: VALID_AWS_AMAZON_COM_CHAIN, - stapled_ocsp: None, - verification_time: verification_time(), - expected_result: Ok(()), - other_error: no_error!(), - }, - // The certificate isn't valid for an unrelated subdomain. - unrelated_domain_invalid => TestCase { - reference_id: VALID_UNRELATED_SUBDOMAIN, - chain: VALID_AWS_AMAZON_COM_CHAIN, - stapled_ocsp: None, - verification_time: verification_time(), - #[cfg(not(any(target_vendor = "apple", windows)))] - expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForNameContext { - expected: ServerName::DnsName(DnsName::try_from(VALID_UNRELATED_SUBDOMAIN).unwrap()), - presented: valid_aws_chain_names(), - })), - #[cfg(any(target_vendor = "apple", windows))] - expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForName)), - other_error: no_error!(), - }, - // The certificate chain for the unrelated domain is not valid for - // my.1password.com. - unrelated_chain_not_valid_for_my_1password_com => TestCase { - reference_id: VALID_UNRELATED_DOMAIN, - chain: VALID_AWS_AMAZON_COM_CHAIN, - stapled_ocsp: None, - verification_time: verification_time(), - #[cfg(not(any(target_vendor = "apple", windows)))] - expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForNameContext { - expected: ServerName::DnsName(DnsName::try_from(VALID_UNRELATED_DOMAIN).unwrap()), - presented: valid_aws_chain_names(), - })), - #[cfg(any(target_vendor = "apple", windows))] - expected_result: Err(TlsError::InvalidCertificate(CertificateError::NotValidForName)), - other_error: no_error!(), - }, - letsencrypt => TestCase { - reference_id: LETSENCRYPT_ORG, - chain: VALID_LETSENCRYPT_ORG_CHAIN, - stapled_ocsp: None, - verification_time: verification_time(), - expected_result: Ok(()), - other_error: no_error!(), - }, - - // OCSP stapling works. - // - // XXX: This test is commented-out because it is a time-bomb due to the - // short lifetime of the OCSP responses for the certificate. - // - // TODO: If/when we can validate a certificate for a specific point in time - // during a test, re-enable this and have it test the certificate validity - // at a point in time where the OCSP response is valid. - // - // revoked_badssl_com_stapled => TestCase { - // reference_id: "revoked.badssl.com", - // chain: &[ - // include_bytes!("revoked_badssl_com_1.crt"), - // include_bytes!("revoked_badssl_com_2.crt"), - // ], - // stapled_ocsp: Some(include_bytes!("revoked_badssl_com_1.ocsp")), - // // XXX: We only do OCSP stapling on Windows. - // valid: !cfg!(windows), - // }, -} - -/// Return a fixed [`pki_types::UnixTime`] for certificate validation purposes. -/// -/// We fix the "now" value used for certificate validation to a fixed point in time at which -/// we know the test certificates are valid. This must be updated if the mock certificates -/// are regenerated. -pub(crate) fn verification_time() -> pki_types::UnixTime { - // Wed, 12 Aug 2026 10:23 UTC - pki_types::UnixTime::since_unix_epoch(Duration::from_secs(1_786_530_173)) -} diff --git a/rustls-platform-verifier/src/tests/verification_real_world/revoked_badssl_com_1.crt b/rustls-platform-verifier/src/tests/verification_real_world/revoked_badssl_com_1.crt deleted file mode 100644 index 63e7692a0e41ee8e87f554a4f03d14a5d4751bc3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1674 zcmXqLVrw&KV$EB?%*4pVB*3eezGz0P^{kMf$BSB*gzFpdvTJYeyw*t;_0bO}_pW6-vG!%>miKby(Z;S~3AM%+87$XNHaj)nH1H1b z6gl;FA5Yn?_+>H1750{&JXdrWy$eD7lz@5wqes@~~xZ~Aw(##@JF_ht34zy*n)E}ahLP}OR^t;5#Kn|LVNZnoQ< zi2uUs`?y@>GX6dMw*B7xzMLalC4XJN>>s)!vGR7Je%2hFk4GAJ&%4CLx@(Tef}fv5 zDr+-u?n;wspXwOE_cQL{_Pt*XRgNkuGIyQNt%$gH)1a2wDd^e;>76G-q@!M(2>!4} zyGcNO*~$Xdbw8FfF*7nSE^cCOF=%3LFpviZq^vTFgn?Lt$dcZt`)Vqm6e#EFX_~B6 zj=1yHu--rxq=1h_j74O_iE|2@D$d9%zyI*>8lR^38qp#fh_oP!n1M(Ww#eWE>E>r- z{LjL|%*46?qFfcEoQ*@9jggg=otY8NVlrr)4N@=9(m2_mae{%{0+$6&Z4MbFB?VUc z`pHE(#(F8h)RhcOU7(oOhovcCl5zps^LEl97?6(xBWx1;#gEY?FZM z%ug;ZK+$U84_C$1<_rsrL{KOcqbMXYJW9a%R#upW$pDloYT3Ae5y8TEt&)j_iKU4# z7l<<%(;3PbG%NRgm}hCJ_#v!#e)7`tr~ATd=Ol3b_~{#~7NZ(_yoG^*vF7lS@8%2) zEX)k<2ChsBTdjl75`1+Tza!y$84)cnBo+VXhq}5M< zpV#3ui}&A^Zq}zzds7hh`Y8QRwal)o7FDctIrMsmkJibh$Jd;T&x(DqUFB%m8%dzO zQ{tH3i6k^k+rwum#<;?KzUa=KyZ9IJM2SyW`q0?S^%FxWgX4))8}BQ*c?ld_XvOK& z9_MG+EzSJ;wfFIi4-=B6v3-Ns`|Up1UN-|5CWVZy_<-$Yx9UucmX`-DZB#M$V$5t?E;y2#3>)yZrwxtZd9~?!Sp#QUQxq zU`fTuaJ6Uez9)C4EZP3qbN!K-YPZxgZoSxXaMv+|ZDOke{(UKDe?9Zlt-Iy2l1<-L zw5%5-Ke)}gPoVy&eB-Y5^E}TP8F-5KmmIrxHtUA9d-aD0Ji8C{S;;Q0pR}f~_s){7 z23q05%FExW9y5RIvB~QKZA ze*Gir$;yrQWzAM|-JQ5zx6o$wbP47=F;Xix-rnZna9nbEqX;+eIg9*T=g;w{2y1-y UO_$cWC>A1_zltMdv2S_`0MOHFK>z>% diff --git a/rustls-platform-verifier/src/tests/verification_real_world/revoked_badssl_com_1.ocsp b/rustls-platform-verifier/src/tests/verification_real_world/revoked_badssl_com_1.ocsp deleted file mode 100644 index 5693ed6f8f9f87c29cac1ad9e4dde1b9f72c5ccf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 490 zcmXqLVtmHM$grS^@xDP5<83xhZ8k<$R(1nMMwTYVGeDse28|mRiLr<*>3zDdrt(RF za;~1H$y()zJ6{d!CHRdDj0_Edgn^NfiK%&%L1U{yW0QfW0Vf-)HXk#S6e|OZNXeGx z%96)ZCy6?^R3ATQH{p;(*=y|PF$wVMr7xP%YCS6?=<%Y~CE@xD1)+8r7?~Rx8e5oH zL?K&hU}<0&wE(8Z2*foq0SX!LvT;pP^ykIoEm0P1}2&UwxF>cp>MW z^M|7UUTgkW+byeSu6cFxM?i&qg(%~y#|OSJEZ{2FKE3nDe$i>)V_a`s6E=M+S$-zf zT|$l}DEyQ`n8f@obH0SP^q-y+H0!<($N!0fF_vvRkEi*)k7rXo_3t3h#04G4C-jJ@ zmCjfYwWNMdnBTg$;*vW$Qm=oWxpdt#X^9PK?y={!|Ihnw7R}MAov@;I*2(8=?QL#r zCQ5HGYl;5NYo^M7#_hM(z5C4*?lrYsU#rX)_gq5iu;r(H4ELC`=!HC-EC1pvG< B!&CqO diff --git a/rustls-platform-verifier/src/tests/verification_real_world/revoked_badssl_com_2.crt b/rustls-platform-verifier/src/tests/verification_real_world/revoked_badssl_com_2.crt deleted file mode 100644 index ff02b2d3557bea2224249404e28227d84142fece..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1365 zcmXqLVhuECVzFGn%*4pVB)~qyjCt+jnH-997VxO=>B=$SW#iOp^Jx3d%gD&h%3zRa z$Zf#M#vIDRCd?EXY$$3V4B~JJ^SETDXF8`Al_+@TB^yc_h=YW=g$2sX%k@%#QprFm zz2y8{Lj?mlkQB49I9!K&PJU8ijzUm=eu;v!qk)__uaSX)xuKb%k&&s9c@&UqY+!0^ zXk=_^X=)i|5J{91j13GS_Gkqq7G$Ob2m2_5_yjAsgee3CJ1Y2QR-~pV1ba9dnVKmW z85kJ=E!H(OXkt`C_AnzW19KB2KLb#li>Zl`k>OUJb7<(y>YGy+r5#C)Sag9$+=^jY zN3;ri?{ee1$Cqw5d7aVwyEZR~<3pYKo_klsUTzHE=~?oJ+dg+ywzuflGfeB3NG?d! z$i24j(d_xsZj)+Cn4F}SW-VH2drZRO$K^v@oy_MLL_7XW|0L&p^2^njrd^vkWcaK< z-8Pe6*tW%|War5vwXqXqoqj!V*mg?pQ|>N}XaChjQ!A#+e=Piyam+C|tboIf@wI&R zm68`zzn%2!mi_!>@ta$lw&iY{(|%;(lawV*bKBio_RrSUBei_Y|+CM!c{*R`W*lie8YmGZQl-1LNW*CT@c! zCUyf^U;xYVv52vVEa`o^ucq=zfpV^%rpa36h&x{m>kZ^V(#k9n24W2&%=ZFJ!!G*l zPW!O6>gXh8pmJ6!0@L{%2tUCcZX^d8#a81|n=6+H8!htnAE;a2At+5J#$wfKFC`$EVi4>AXK}povQ4gpLQ=bTKm+D@CPpzCkc*0eu1C*6`pyBt28~^CrA!8OJT;Dal?tWBsS5dN3MCnt#R{N| zn3!st}Z#Q<<5Ut`Lw|R8pznm|m2c znwy$eqL7mh(ww4@SfYT_B}D~^x}}N883tHF05c3DgVN(WxgM|kPB9b}>y@l}Fu5hq z@<@9K(-Ef3!cf=sQ5(KB{xdpZTI79iX|1~B!kgQCF8LW*9B(k3SG_6umzq0gmxP~K z_=W6vscGhQN3L7tKb-!mF@~dX^)GIg6Q6BX{q&L+xAdRh9&TFIH{<1L%c>BkmDz&7 z8ZS;=%9_yUJn_4lZ49fbSj|%lt*lLXuWwG}T4MgX)${7v+T|W+D`Pc&eA?>B!g)+J zliNg)*B#Bb)J%K3ZqwfP9{{N7;9&p& diff --git a/rustls-platform-verifier/src/verification/android.rs b/rustls-platform-verifier/src/verification/android.rs deleted file mode 100644 index 3916a5fa..00000000 --- a/rustls-platform-verifier/src/verification/android.rs +++ /dev/null @@ -1,340 +0,0 @@ -use jni::{ - jni_sig, jni_str, - objects::{JByteArray, JObject, JObjectArray, JString, JValue}, - signature::MethodSignature, - Env, -}; -use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerifier}; -use rustls::crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider}; -use rustls::pki_types; -use rustls::Error::InvalidCertificate; -use rustls::{ - CertificateError, DigitallySignedStruct, Error as TlsError, OtherError, SignatureScheme, -}; -use std::sync::Arc; - -use super::{log_server_cert, ALLOWED_EKUS}; -use crate::android::{with_context, CachedClass}; - -static CERT_VERIFIER_CLASS: CachedClass = - CachedClass::new(jni_str!("org.rustls.platformverifier.CertificateVerifier")); - -// Note: Keep these in sync with the Kotlin enum. -#[derive(Debug)] -enum VerifierStatus { - Ok, - Unavailable, - Expired, - UnknownCert, - Revoked, - InvalidEncoding, - InvalidExtension, -} - -// Android's certificate verifier ignores this outright and this is considered the -// official recommendation. See https://bugs.chromium.org/p/chromium/issues/detail?id=627154. -const AUTH_TYPE: &str = "RSA"; - -/// A TLS certificate verifier that utilizes the Android platform verifier. -#[derive(Debug)] -pub struct Verifier { - /// Testing only: The root CA certificate to trust. - #[cfg(any(test, feature = "ffi-testing"))] - test_only_root_ca_override: Option>, - crypto_provider: Arc, -} - -#[cfg(any(test, feature = "ffi-testing"))] -impl Drop for Verifier { - fn drop(&mut self) { - with_context::<_, ()>(|cx| { - let cert_verifier_class = CERT_VERIFIER_CLASS.get(cx)?; - cx.env - .call_static_method( - cert_verifier_class, - jni_str!("clearMockRoots"), - jni_sig!(() -> void), - &[], - )? - .v()?; - Ok(()) - }) - .expect("failed to clear test roots") - } -} - -impl Verifier { - /// Creates a new instance of a TLS certificate verifier that utilizes the - /// Android certificate facilities. - #[cfg_attr(docsrs, doc(cfg(all())))] - pub fn new(crypto_provider: Arc) -> Result { - Ok(Self { - #[cfg(any(test, feature = "ffi-testing"))] - test_only_root_ca_override: None, - crypto_provider, - }) - } - - /// Creates a test-only TLS certificate verifier which trusts our fake root CA cert. - #[cfg(any(test, feature = "ffi-testing"))] - pub(crate) fn new_with_fake_root( - root: pki_types::CertificateDer<'static>, - crypto_provider: Arc, - ) -> Self { - Self { - test_only_root_ca_override: Some(root), - crypto_provider, - } - } - - fn verify_certificate( - &self, - end_entity: &pki_types::CertificateDer<'_>, - intermediates: &[pki_types::CertificateDer<'_>], - server_name: &pki_types::ServerName, - ocsp_response: Option<&[u8]>, - now: pki_types::UnixTime, - ) -> Result<(), TlsError> { - let certificate_chain = std::iter::once(end_entity) - .chain(intermediates) - .map(|cert| cert.as_ref()) - .enumerate(); - - // Convert the unix timestamp into milliseconds, expressed as - // an i64 to later be converted into a Java Long used for a Date - // constructor. - let now: i64 = (now.as_secs() * 1000) - .try_into() - .map_err(|_| TlsError::FailedToGetCurrentTime)?; - - let verification_result = with_context(|cx| { - let cert_verifier_class = CERT_VERIFIER_CLASS.get(cx)?; - - let cert_list = { - let array = JObjectArray::::new( - cx.env, - intermediates.len() + 1, - &JByteArray::null(), - )?; - - for (idx, cert) in certificate_chain { - let cert_buffer = cx.env.byte_array_from_slice(cert)?; - array.set_element(cx.env, idx, cert_buffer)?; - } - - array - }; - - let allowed_ekus = { - let array = - JObjectArray::::new(cx.env, ALLOWED_EKUS.len(), &JString::null())?; - - for (idx, eku) in ALLOWED_EKUS.iter().enumerate() { - let eku = cx.env.new_string(eku.to_str().expect( - "ALLOWED_EKUS entries are ASCII constants -- always valid UTF-8", - ))?; - array.set_element(cx.env, idx, eku)? - } - - array - }; - - let ocsp_response = match ocsp_response { - Some(b) => cx.env.byte_array_from_slice(b)?, - None => JByteArray::null(), - }; - - #[cfg(any(test, feature = "ffi-testing"))] - { - if let Some(mock_root) = &self.test_only_root_ca_override { - let mock_root = cx.env.byte_array_from_slice(mock_root)?; - cx.env - .call_static_method( - cert_verifier_class, - jni_str!("addMockRoot"), - jni_sig!((byte[]) -> void), - &[JValue::from(&mock_root)], - )? - .v() - .expect("failed to add test root") - } - } - - const VERIFIER_CALL: MethodSignature<'static, 'static> = jni_sig!( - ( - android.content.Context, - JString, - JString, - JString[], - byte[], - jlong, - byte[][] - ) -> org.rustls.platformverifier.VerificationResult - ); - - let server_name = server_name.to_str(); - // Android's verifier doesn't require this but trim trailing `.` labels for consistency across platforms. - let server_name = server_name.strip_suffix('.').unwrap_or(&server_name); - - let server_name = cx.env.new_string(server_name)?; - let auth_type = cx.env.new_string(AUTH_TYPE)?; - - let result = cx - .env - .call_static_method( - cert_verifier_class, - jni_str!("verifyCertificateChain"), - VERIFIER_CALL, - &[ - JValue::from(cx.global.context.as_ref()), - JValue::from(&server_name), - JValue::from(&auth_type), - JValue::from(&JObject::from(allowed_ekus)), - JValue::from(&ocsp_response), - JValue::Long(now), - JValue::from(&JObject::from(cert_list)), - ], - )? - .l()?; - - Ok(extract_result_info(cx.env, result)) - }); - - match verification_result { - Ok((status, maybe_msg)) => { - // `maybe_msg` is safe to log as its exactly what the system told us. - // - // The branches which unwrap it will never fail since the Kotlin side always sets it - // for the variants. - match status { - VerifierStatus::Ok => { - // If everything else was OK, check the hostname. - rustls::client::verify_server_name( - &rustls::server::ParsedCertificate::try_from(end_entity)?, - server_name, - ) - } - VerifierStatus::Unavailable => Err(TlsError::General(String::from( - "No system trust stores available", - ))), - VerifierStatus::Expired => Err(InvalidCertificate(CertificateError::Expired)), - VerifierStatus::UnknownCert => { - log::warn!("certificate was not trusted: {}", maybe_msg.unwrap()); - Err(InvalidCertificate(CertificateError::UnknownIssuer)) - } - VerifierStatus::Revoked => { - log::warn!("certificate was revoked: {}", maybe_msg.unwrap()); - Err(InvalidCertificate(CertificateError::Revoked)) - } - VerifierStatus::InvalidEncoding => { - Err(InvalidCertificate(CertificateError::BadEncoding)) - } - VerifierStatus::InvalidExtension => Err(InvalidCertificate( - CertificateError::Other(OtherError(std::sync::Arc::new(super::EkuError))), - )), - } - } - Err(e) => Err(TlsError::General(format!( - "failed to call native verifier: {e:?}", - ))), - } - } -} - -fn extract_result_info(env: &mut Env<'_>, result: JObject<'_>) -> (VerifierStatus, Option) { - let status_code = env - .get_field(&result, jni_str!("code"), jni_sig!(jint)) - .and_then(|code| code.i()) - .unwrap(); - - let status = match status_code { - 0 => VerifierStatus::Ok, - 1 => VerifierStatus::Unavailable, - 2 => VerifierStatus::Expired, - 3 => VerifierStatus::UnknownCert, - 4 => VerifierStatus::Revoked, - 5 => VerifierStatus::InvalidEncoding, - 6 => VerifierStatus::InvalidExtension, - i => unreachable!("unknown status code: {i}"), - }; - - // Extract the `String?`. - let msg = env - .get_field(result, jni_str!("message"), jni_sig!(java.lang.String)) - .and_then(|m| m.l()) - .map(|s| { - if s.is_null() { - None - } else { - env.cast_local::(s) - .and_then(|s| s.try_to_string(env)) - .ok() - } - }) - .unwrap(); - (status, msg) -} - -#[cfg_attr(docsrs, doc(cfg(all())))] -impl ServerCertVerifier for Verifier { - fn verify_server_cert( - &self, - end_entity: &pki_types::CertificateDer<'_>, - intermediates: &[pki_types::CertificateDer<'_>], - server_name: &pki_types::ServerName, - ocsp_response: &[u8], - now: pki_types::UnixTime, - ) -> Result { - log_server_cert(end_entity); - - let ocsp_data = if !ocsp_response.is_empty() { - Some(ocsp_response) - } else { - None - }; - - match self.verify_certificate(end_entity, intermediates, server_name, ocsp_data, now) { - Ok(()) => Ok(rustls::client::danger::ServerCertVerified::assertion()), - Err(e) => { - // This error only tells us what the system errored with, so it doesn't leak anything - // sensitive. - log::error!("failed to verify TLS certificate: {}", e); - Err(e) - } - } - } - - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - verify_tls12_signature( - message, - cert, - dss, - &self.crypto_provider.signature_verification_algorithms, - ) - } - - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - verify_tls13_signature( - message, - cert, - dss, - &self.crypto_provider.signature_verification_algorithms, - ) - } - - fn supported_verify_schemes(&self) -> Vec { - self.crypto_provider - .signature_verification_algorithms - .supported_schemes() - } -} diff --git a/rustls-platform-verifier/src/verification/apple.rs b/rustls-platform-verifier/src/verification/apple.rs deleted file mode 100644 index b4e34e7d..00000000 --- a/rustls-platform-verifier/src/verification/apple.rs +++ /dev/null @@ -1,313 +0,0 @@ -use std::sync::Arc; - -use core_foundation::date::CFDate; -use core_foundation_sys::date::kCFAbsoluteTimeIntervalSince1970; -use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerifier}; -use rustls::crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider}; -use rustls::pki_types; -use rustls::{ - CertificateError, DigitallySignedStruct, Error as TlsError, OtherError, SignatureScheme, -}; -use security_framework::{ - certificate::SecCertificate, policy::SecPolicy, secure_transport::SslProtocolSide, - trust::SecTrust, -}; - -use super::log_server_cert; -use crate::verification::invalid_certificate; - -mod errors { - pub(super) use security_framework_sys::base::{ - errSecCertificateRevoked, errSecCreateChainFailed, errSecHostNameMismatch, - errSecInvalidExtendedKeyUsage, - }; -} - -#[allow(clippy::as_conversions)] -fn system_time_to_cfdate(time: pki_types::UnixTime) -> Result { - // SAFETY: The interval is defined by macOS externally, but is always present and never modified at runtime - // since its a global variable. - // - // See https://developer.apple.com/documentation/corefoundation/kcfabsolutetimeintervalsince1970. - let unix_adjustment = unsafe { kCFAbsoluteTimeIntervalSince1970 as u64 }; - - // Convert a system timestamp based off the UNIX epoch into the - // Apple epoch used by all `CFAbsoluteTime` values. - // Subtracting Durations with sub() will panic on overflow - time.as_secs() - .checked_sub(unix_adjustment) - .ok_or(TlsError::FailedToGetCurrentTime) - .map(|epoch| CFDate::new(epoch as f64)) -} - -/// A TLS certificate verifier that utilizes the Apple platform certificate facilities. -#[derive(Debug)] -pub struct Verifier { - /// Extra trust anchors to add to the verifier above and beyond those provided by - /// the system-provided trust stores. - extra_roots: Vec, - /// Testing only: The root CA certificate to trust. - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - test_only_root_ca_override: Option, - crypto_provider: Arc, -} - -impl Verifier { - /// Creates a new instance of a TLS certificate verifier that utilizes the Apple certificate - /// facilities. - #[cfg_attr(docsrs, doc(cfg(all())))] - pub fn new(crypto_provider: Arc) -> Result { - Ok(Self { - extra_roots: Vec::new(), - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - test_only_root_ca_override: None, - crypto_provider, - }) - } - - /// Creates a new instance of a TLS certificate verifier that utilizes the Apple certificate - /// facilities with the addition of extra root certificates to trust. - /// - /// See [Verifier::new] for the external requirements the verifier needs. - #[cfg_attr(docsrs, doc(cfg(not(target_os = "android"))))] - pub fn new_with_extra_roots( - roots: impl IntoIterator>, - crypto_provider: Arc, - ) -> Result { - let extra_roots = roots - .into_iter() - .map(|root| { - SecCertificate::from_der(&root) - .map_err(|_| TlsError::InvalidCertificate(CertificateError::BadEncoding)) - }) - .collect::, _>>()?; - Ok(Self { - extra_roots, - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - test_only_root_ca_override: None, - crypto_provider, - }) - } - - /// Creates a test-only TLS certificate verifier which trusts our fake root CA cert. - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - pub(crate) fn new_with_fake_root( - root: pki_types::CertificateDer<'static>, - crypto_provider: Arc, - ) -> Self { - Self { - extra_roots: Vec::new(), - test_only_root_ca_override: Some(SecCertificate::from_der(root.as_ref()).unwrap()), - crypto_provider, - } - } - - fn verify_certificate( - &self, - end_entity: &pki_types::CertificateDer<'_>, - intermediates: &[pki_types::CertificateDer<'_>], - server_name: &str, - ocsp_response: Option<&[u8]>, - now: pki_types::UnixTime, - ) -> Result<(), TlsError> { - let certificates: Vec = std::iter::once(end_entity.as_ref()) - .chain(intermediates.iter().map(|cert| cert.as_ref())) - .map(|cert| { - SecCertificate::from_der(cert) - .map_err(|_| TlsError::InvalidCertificate(CertificateError::BadEncoding)) - }) - .collect::, _>>()?; - - // Create our verification policy suitable for verifying TLS chains. - // This uses the "default" verification engine and parameters, the same as Windows. - // - // The protocol side should be set to `server` for a client to verify server TLS - // certificates. - // - // The server name will be required to match what the end-entity certificate reports - // - // Ref: https://developer.apple.com/documentation/security/1392592-secpolicycreatessl - let policy = SecPolicy::create_ssl(SslProtocolSide::SERVER, Some(server_name)); - - // Create our trust evaluation context/chain. - // - // Apple requires that the certificate to be verified is always first in the array, and we - // always place the end-entity certificate at the start. - // - // Ref: https://developer.apple.com/documentation/security/1401555-sectrustcreatewithcertificates - let mut trust_evaluation = SecTrust::create_with_certificates(&certificates, &[policy]) - .map_err(|e| TlsError::General(e.to_string()))?; - - // Tell the system that we want to consider the certificates evaluation at the point - // in time that `rustls` provided. - let now = system_time_to_cfdate(now)?; - trust_evaluation - .set_trust_verify_date(&now) - .map_err(|e| invalid_certificate(e.to_string()))?; - - // If we have OCSP response data, make sure the system makes use of it. - if let Some(ocsp_response) = ocsp_response { - trust_evaluation - .set_trust_ocsp_response(std::iter::once(ocsp_response)) - .map_err(|e| invalid_certificate(e.to_string()))?; - } - - #[cfg(not(any(test, feature = "ffi-testing", feature = "dbg")))] - let extra_roots = self.extra_roots.as_slice(); - - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - let extra_roots: Vec<_> = self - .extra_roots - .iter() - .chain(self.test_only_root_ca_override.as_ref()) - .cloned() - .collect(); - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - let extra_roots = extra_roots.as_slice(); - - // If any extra roots were provided by the user (or tests), provide them to the trust - // evaluation regardless of their system trust settings or status. - if !extra_roots.is_empty() { - trust_evaluation - .set_anchor_certificates(extra_roots) - .map_err(|e| TlsError::Other(OtherError(Arc::new(e))))?; - - // We want to trust both the system-installed and the extra roots. This must be set - // since calling `SecTrustSetAnchorCertificates` "disables the trusting of any - // anchors other than the ones specified by this function call" by default. - trust_evaluation - .set_trust_anchor_certificates_only(false) - .map_err(|e| TlsError::Other(OtherError(Arc::new(e))))?; - } - - // When testing, support using fake roots and ignoring default roots present on the system for - // consistency/reproducibility reasons. - // - // XXX: This does not currently limit revocation from fetching information online, or prevent - // the downloading of root CAs. - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - { - if self.test_only_root_ca_override.is_some() { - // XXX: The test root was already provided to the trust evaluation as an extra root. - // We only need to stop use of the default system-installed roots. - - // As per [Apple's docs], building and verifying a certificate chain will - // search through the system and keychain to find certificates that it - // needs to try and construct a trust chain back to the root. - // - // `SecTrustSetAnchorCertificatesOnly` must be called after setting custom - // anchor certificates, which "disables trusting any other anchors than the ones passed in - // with the `SecTrustSetAnchorCertificates` function". - // - // [Apple's docs]: https://developer.apple.com/documentation/security/certificate_key_and_trust_services/trust/creating_a_trust_object - trust_evaluation - .set_trust_anchor_certificates_only(true) - .expect("failed to tell trust to only use provided anchors"); - } - } - - let trust_error = match trust_evaluation.evaluate_with_error() { - Ok(()) => return Ok(()), - Err(e) => e, - }; - - let err_code = trust_error.code(); - - let err = err_code - .try_into() - .map_err(|_| ()) - .and_then(|code| { - // Only map the errors we need for tests. - match code { - errors::errSecHostNameMismatch => Ok(TlsError::InvalidCertificate( - CertificateError::NotValidForName, - )), - errors::errSecCreateChainFailed => Ok(TlsError::InvalidCertificate( - CertificateError::UnknownIssuer, - )), - errors::errSecInvalidExtendedKeyUsage => Ok(TlsError::InvalidCertificate( - CertificateError::Other(OtherError(Arc::new(super::EkuError))), - )), - errors::errSecCertificateRevoked => { - Ok(TlsError::InvalidCertificate(CertificateError::Revoked)) - } - _ => Err(()), - } - }) - // Fallback to an error containing the description and specific error code so that - // the exact error cause can be looked up easily. - .unwrap_or_else(|_| invalid_certificate(format!("{trust_error}: {err_code}"))); - - Err(err) - } -} - -#[cfg_attr(docsrs, doc(cfg(all())))] -impl ServerCertVerifier for Verifier { - fn verify_server_cert( - &self, - end_entity: &pki_types::CertificateDer<'_>, - intermediates: &[pki_types::CertificateDer<'_>], - server_name: &pki_types::ServerName, - ocsp_response: &[u8], - now: pki_types::UnixTime, - ) -> Result { - log_server_cert(end_entity); - - // Convert IP addresses to name strings to ensure match check on leaf certificate. - // Ref: https://developer.apple.com/documentation/security/1392592-secpolicycreatessl - let server = server_name.to_str(); - // Apple's verifier doesn't require this but trim trailing `.` labels for consistency across platforms. - let server = server.strip_suffix('.').unwrap_or(&server); - - let ocsp_data = if !ocsp_response.is_empty() { - Some(ocsp_response) - } else { - None - }; - - match self.verify_certificate(end_entity, intermediates, server, ocsp_data, now) { - Ok(()) => Ok(rustls::client::danger::ServerCertVerified::assertion()), - Err(e) => { - // This error only tells us what the system errored with, so it doesn't leak anything - // sensitive. - log::error!("failed to verify TLS certificate: {}", e); - Err(e) - } - } - } - - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - verify_tls12_signature( - message, - cert, - dss, - &self.crypto_provider.signature_verification_algorithms, - ) - } - - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - verify_tls13_signature( - message, - cert, - dss, - &self.crypto_provider.signature_verification_algorithms, - ) - } - - fn supported_verify_schemes(&self) -> Vec { - self.crypto_provider - .signature_verification_algorithms - .supported_schemes() - } -} diff --git a/rustls-platform-verifier/src/verification/mod.rs b/rustls-platform-verifier/src/verification/mod.rs deleted file mode 100644 index 9d947ff2..00000000 --- a/rustls-platform-verifier/src/verification/mod.rs +++ /dev/null @@ -1,86 +0,0 @@ -#[cfg(any(windows, target_vendor = "apple"))] -use std::sync::Arc; - -#[cfg(all( - any(unix, target_arch = "wasm32"), - not(target_os = "android"), - not(target_vendor = "apple"), -))] -mod others; - -#[cfg(all( - any(unix, target_arch = "wasm32"), - not(target_os = "android"), - not(target_vendor = "apple"), -))] -pub use others::Verifier; - -#[cfg(target_vendor = "apple")] -mod apple; - -#[cfg(target_vendor = "apple")] -pub use apple::Verifier; - -#[cfg(target_os = "android")] -pub(crate) mod android; - -#[cfg(target_os = "android")] -pub use android::Verifier; - -#[cfg(windows)] -mod windows; - -#[cfg(windows)] -pub use windows::Verifier; - -/// An EKU was invalid for the use case of verifying a server certificate. -/// -/// This error is used primarily for tests. -#[cfg_attr(windows, allow(dead_code))] // not used by windows verifier -#[derive(Debug, PartialEq)] -pub(crate) struct EkuError; - -impl std::fmt::Display for EkuError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("certificate had invalid extensions") - } -} - -impl std::error::Error for EkuError {} - -// Log the certificate we are verifying so that we can try and find what may be wrong with it -// if we need to debug a user's situation. -fn log_server_cert(_end_entity: &rustls::pki_types::CertificateDer<'_>) { - #[cfg(feature = "cert-logging")] - { - use base64::Engine; - log::debug!( - "verifying certificate: {}", - base64::engine::general_purpose::STANDARD.encode(_end_entity.as_ref()) - ); - } -} - -// Unknown certificate error shorthand. Used when we need to construct an "Other" certificate -// error with a platform specific error message. -#[cfg(any(windows, target_vendor = "apple"))] -fn invalid_certificate(reason: impl Into) -> rustls::Error { - rustls::Error::InvalidCertificate(rustls::CertificateError::Other(rustls::OtherError( - Arc::from(Box::from(reason.into())), - ))) -} - -/// List of EKUs that one or more of that *must* be in the end-entity certificate. -/// -/// Legacy server-gated crypto OIDs are assumed to no longer be in use. -/// -/// Currently supported: -/// - id-kp-serverAuth -// TODO: Chromium also allows for `OID_ANY_EKU` on Android. -#[cfg(target_os = "windows")] -// XXX: Windows requires that we NUL terminate EKU strings. -// See https://github.com/rustls/rustls-platform-verifier/issues/126#issuecomment-2306232794. -const ALLOWED_EKUS: &[windows_sys::core::PCSTR] = - &[windows_sys::Win32::Security::Cryptography::szOID_PKIX_KP_SERVER_AUTH]; -#[cfg(target_os = "android")] -pub const ALLOWED_EKUS: &[&std::ffi::CStr] = &[c"1.3.6.1.5.5.7.3.1"]; diff --git a/rustls-platform-verifier/src/verification/others.rs b/rustls-platform-verifier/src/verification/others.rs deleted file mode 100644 index 6e4796fb..00000000 --- a/rustls-platform-verifier/src/verification/others.rs +++ /dev/null @@ -1,184 +0,0 @@ -use std::fmt::Debug; -use std::sync::Arc; - -use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; -use rustls::client::WebPkiServerVerifier; -use rustls::pki_types; -use rustls::{ - crypto::CryptoProvider, CertificateError, DigitallySignedStruct, Error as TlsError, OtherError, - SignatureScheme, -}; - -use super::log_server_cert; - -/// A TLS certificate verifier that uses the system's root store and WebPKI. -#[derive(Debug)] -pub struct Verifier { - // We currently keep one set of certificates per-verifier so that - // recreating the verifier will pull fresh root certificates from disk, - // picking up on any changes that might have been made since. - inner: Arc, -} - -impl Verifier { - /// Creates a new verifier whose certificate validation is provided by - /// WebPKI, using root certificates provided by the platform. - #[cfg_attr(docsrs, doc(cfg(all())))] - pub fn new(crypto_provider: Arc) -> Result { - Self::new_inner([], None, crypto_provider) - } - - /// Creates a new verifier whose certificate validation is provided by - /// WebPKI, using root certificates provided by the platform and augmented by - /// the provided extra root certificates. - #[cfg_attr(docsrs, doc(cfg(not(target_os = "android"))))] - pub fn new_with_extra_roots( - extra_roots: impl IntoIterator>, - crypto_provider: Arc, - ) -> Result { - Self::new_inner(extra_roots, None, crypto_provider) - } - - /// Creates a test-only TLS certificate verifier which trusts our fake root CA cert. - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - pub(crate) fn new_with_fake_root( - root: pki_types::CertificateDer<'static>, - crypto_provider: Arc, - ) -> Self { - Self::new_inner([], Some(root), crypto_provider) - .expect("failed to create verifier with fake root") - } - - /// Creates a new verifier whose certificate validation is provided by - /// WebPKI, using root certificates provided by the platform and augmented by - /// the provided extra root certificates. - fn new_inner( - extra_roots: impl IntoIterator>, - #[allow(unused)] // test_root is only used in tests - test_root: Option>, - crypto_provider: Arc, - ) -> Result { - let mut root_store = rustls::RootCertStore::empty(); - - // For testing only: load fake root cert, instead of native/WebPKI roots - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - { - if let Some(test_root) = test_root { - root_store.add(test_root)?; - return Ok(Self { - inner: WebPkiServerVerifier::builder_with_provider( - root_store.into(), - crypto_provider.clone(), - ) - .build() - .map_err(|e| TlsError::Other(OtherError(Arc::new(e))))?, - }); - } - } - - // While we ignore invalid certificates from the system, we forward errors from - // parsing the extra roots to the caller. - for cert in extra_roots { - root_store.add(cert)?; - } - - #[cfg(all( - unix, - not(target_os = "android"), - not(target_vendor = "apple"), - not(target_arch = "wasm32"), - ))] - { - let result = rustls_native_certs::load_native_certs(); - let (added, ignored) = root_store.add_parsable_certificates(result.certs); - if ignored > 0 { - log::warn!("{ignored} platform CA root certificates were ignored due to errors"); - } - - for error in result.errors { - log::warn!("Error loading CA root certificate: {error}"); - } - - // Don't return an error if this fails when other roots have already been loaded via - // `new_with_extra_roots`. It leads to extra failure cases where connections would otherwise still work. - if root_store.is_empty() { - return Err(rustls::Error::General( - "No CA certificates were loaded from the system".to_owned(), - )); - } else { - log::debug!("Loaded {added} CA root certificates from the system"); - } - } - - #[cfg(target_arch = "wasm32")] - { - root_store.add_parsable_certificates( - webpki_root_certs::TLS_SERVER_ROOT_CERTS.iter().cloned(), - ); - }; - - Ok(Self { - inner: WebPkiServerVerifier::builder_with_provider(root_store.into(), crypto_provider) - .build() - .map_err(|e| TlsError::Other(OtherError(Arc::new(e))))?, - }) - } -} - -#[cfg_attr(docsrs, doc(cfg(all())))] -impl ServerCertVerifier for Verifier { - fn verify_server_cert( - &self, - end_entity: &pki_types::CertificateDer<'_>, - intermediates: &[pki_types::CertificateDer<'_>], - server_name: &pki_types::ServerName, - ocsp_response: &[u8], - now: pki_types::UnixTime, - ) -> Result { - log_server_cert(end_entity); - - self.inner - .verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now) - .map_err(map_webpki_errors) - // This only contains information from the system or other public - // bits of the TLS handshake, so it can't leak anything. - .map_err(|e| { - log::error!("failed to verify TLS certificate: {}", e); - e - }) - } - - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - self.inner.verify_tls12_signature(message, cert, dss) - } - - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - self.inner.verify_tls13_signature(message, cert, dss) - } - - fn supported_verify_schemes(&self) -> Vec { - self.inner.supported_verify_schemes() - } -} - -fn map_webpki_errors(err: TlsError) -> TlsError { - match &err { - TlsError::InvalidCertificate(CertificateError::InvalidPurpose) - | TlsError::InvalidCertificate(CertificateError::InvalidPurposeContext { .. }) => { - TlsError::InvalidCertificate(CertificateError::Other(OtherError(Arc::new( - super::EkuError, - )))) - } - _ => err, - } -} diff --git a/rustls-platform-verifier/src/verification/windows.rs b/rustls-platform-verifier/src/verification/windows.rs deleted file mode 100644 index 24fadc9b..00000000 --- a/rustls-platform-verifier/src/verification/windows.rs +++ /dev/null @@ -1,798 +0,0 @@ -//! `Verifier` implementation for Windows targets. -//! -//! The design of the rustls-native-certs crate for Windows doesn't work -//! completely enough. In general it is hard to emulate enough of what -//! Windows does to be compatible with all users' configurations, especially -//! when corporate MitM proxies or custom CAs or complex trust policies are -//! used. Instead, delegate to Windows's own certificate validation engine -//! directly. -//! -//! This implementation was modeled on: -//! * Chromium's [cert_verify_proc_win.cc] and [x509_util_win.cc] -//! * Golang's [root_windows.go] -//! * [Microsoft's Documentation] and [Microsoft's Example] -//! -//! [cert_verify_proc_win.cc]: -//! [x509_util_win.cc]: -//! [root_windows.go]: -//! [Microsoft's Documentation]: -//! [Microsoft's Example]: - -use std::{ - convert::TryInto, - mem::{self, MaybeUninit}, - os::raw::c_void, - ptr::{self, NonNull}, - sync::Arc, -}; - -use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerifier}; -use rustls::crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider}; -use rustls::pki_types; -use rustls::{ - CertificateError, DigitallySignedStruct, Error as TlsError, Error::InvalidCertificate, - SignatureScheme, -}; -use windows_sys::Win32::{ - Foundation::{ - CERT_E_CN_NO_MATCH, CERT_E_EXPIRED, CERT_E_INVALID_NAME, CERT_E_UNTRUSTEDROOT, - CERT_E_WRONG_USAGE, CRYPT_E_REVOKED, FILETIME, TRUE, - }, - Security::Cryptography::{ - CertAddEncodedCertificateToStore, CertCloseStore, CertCreateCertificateChainEngine, - CertFreeCertificateChain, CertFreeCertificateChainEngine, CertFreeCertificateContext, - CertGetCertificateChain, CertOpenStore, CertSetCertificateContextProperty, - CertVerifyCertificateChainPolicy, HTTPSPolicyCallbackData, AUTHTYPE_SERVER, - CERT_CHAIN_CACHE_END_CERT, CERT_CHAIN_CONTEXT, - CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS, CERT_CHAIN_POLICY_PARA, - CERT_CHAIN_POLICY_SSL, CERT_CHAIN_POLICY_STATUS, - CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT, CERT_CHAIN_REVOCATION_CHECK_END_CERT, - CERT_CONTEXT, CERT_OCSP_RESPONSE_PROP_ID, CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, - CERT_STORE_ADD_ALWAYS, CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, CERT_STORE_PROV_MEMORY, - CERT_STRONG_SIGN_PARA, CERT_TRUST_IS_PARTIAL_CHAIN, CERT_TRUST_IS_UNTRUSTED_ROOT, - CERT_USAGE_MATCH, CRYPT_INTEGER_BLOB, CTL_USAGE, HCERTSTORE, USAGE_MATCH_TYPE_AND, - X509_ASN_ENCODING, - }, -}; - -use super::{log_server_cert, ALLOWED_EKUS}; - -// The `windows-sys` definition for `CERT_CHAIN_PARA` does not take old OS versions -// into account so we define it ourselves for better OS backwards compat. -// In the future a compile-time size assertion can be added against the upstream type to help stay in sync. -#[allow(non_camel_case_types, non_snake_case)] -#[repr(C)] -struct CERT_CHAIN_PARA { - pub cbSize: u32, - pub RequestedUsage: CERT_USAGE_MATCH, - pub RequestedIssuancePolicy: CERT_USAGE_MATCH, - pub dwUrlRetrievalTimeout: u32, - pub fCheckRevocationFreshnessTime: i32, // BOOL - pub dwRevocationFreshnessTime: u32, - pub pftCacheResync: *mut FILETIME, - // XXX: `pStrongSignPara` and `dwStrongSignFlags` might or might not be defined on the current system. It started - // being available in Windows 8. See https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_chain_para - #[cfg(not(target_vendor = "win7"))] - pub pStrongSignPara: *const CERT_STRONG_SIGN_PARA, - #[cfg(not(target_vendor = "win7"))] - pub dwStrongSignFlags: u32, -} - -// Same workaround with CERT_CHAIN_PARA -#[allow(non_camel_case_types, non_snake_case)] -#[repr(C)] -#[derive(Clone, Copy)] -pub struct CERT_CHAIN_ENGINE_CONFIG { - pub cbSize: u32, - pub hRestrictedRoot: HCERTSTORE, - pub hRestrictedTrust: HCERTSTORE, - pub hRestrictedOther: HCERTSTORE, - pub cAdditionalStore: u32, - pub rghAdditionalStore: *mut HCERTSTORE, - pub dwFlags: u32, - pub dwUrlRetrievalTimeout: u32, - pub MaximumCachedCertificates: u32, - pub CycleDetectionModulus: u32, - pub hExclusiveRoot: HCERTSTORE, - pub hExclusiveTrustedPeople: HCERTSTORE, - // XXX: `dwExclusiveFlags` started being available in Windows 8 and Windows Server 2012 - // See https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_chain_engine_config - #[cfg(not(target_vendor = "win7"))] - pub dwExclusiveFlags: u32, -} - -use crate::verification::invalid_certificate; - -// SAFETY: see method implementation -unsafe impl ZeroedWithSize for CERT_CHAIN_PARA { - fn zeroed_with_size() -> Self { - // SAFETY: `CERT_CHAIN_PARA` only contains pointers and integers, which are safe to zero. - // Additionally, MSDN states you *MUST* zero all unused fields. - let mut new: Self = unsafe { mem::zeroed() }; - new.cbSize = Self::SIZE; - new - } -} - -// SAFETY: see method implementation -unsafe impl ZeroedWithSize for HTTPSPolicyCallbackData { - fn zeroed_with_size() -> Self { - // SAFETY: zeroed is needed here since it contains a union. - let mut new: Self = unsafe { mem::zeroed() }; - new.Anonymous.cbSize = Self::SIZE; - new - } -} - -// SAFETY: see method implementation -unsafe impl ZeroedWithSize for CERT_CHAIN_POLICY_PARA { - fn zeroed_with_size() -> Self { - // SAFETY: This structure only contains integers and pointers. - let mut new: Self = unsafe { mem::zeroed() }; - new.cbSize = Self::SIZE; - new - } -} - -// SAFETY: see method implementation -unsafe impl ZeroedWithSize for CERT_CHAIN_ENGINE_CONFIG { - fn zeroed_with_size() -> Self { - // SAFETY: This structure only contains integers and pointers. - let mut new: Self = unsafe { mem::zeroed() }; - new.cbSize = Self::SIZE; - new - } -} - -struct CertChain { - inner: NonNull, -} - -impl CertChain { - fn verify_chain_policy( - &self, - mut server_null_terminated: Vec, - ) -> Result { - let mut extra_params = HTTPSPolicyCallbackData::zeroed_with_size(); - extra_params.dwAuthType = AUTHTYPE_SERVER; - // `server_null_terminated` outlives `extra_params`. - extra_params.pwszServerName = server_null_terminated.as_mut_ptr(); - - let mut params = CERT_CHAIN_POLICY_PARA::zeroed_with_size(); - // Ignore any errors when trying to obtain OCSP revocation information. - // This is also done in OpenSSL, Secure Transport from Apple, etc. - params.dwFlags = CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS; - // `extra_params` outlives `params`. - params.pvExtraPolicyPara = NonNull::from(&mut extra_params).cast::().as_ptr(); - - let mut status: MaybeUninit = MaybeUninit::uninit(); - - // SAFETY: The certificate chain is non-null, `params` is valid for reads, and its valid to write to `status`. - let res = unsafe { - CertVerifyCertificateChainPolicy( - CERT_CHAIN_POLICY_SSL, - self.inner.as_ptr(), - ¶ms, - status.as_mut_ptr(), - ) - }; - - // This should rarely, if ever, be false since it would imply no TLS verification - // is currently possible on the system: https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certverifycertificatechainpolicy#return-value - if res != TRUE { - return Err(TlsError::General(String::from( - "TLS certificate verification was unavailable on the system!", - ))); - } - - // SAFETY: The verification call was checked to have succeeded, so the status - // is written correctly and initialized. - let status = unsafe { status.assume_init() }; - Ok(status) - } -} - -impl Drop for CertChain { - fn drop(&mut self) { - // SAFETY: The pointer is guaranteed to be non-null. - unsafe { CertFreeCertificateChain(self.inner.as_ptr()) } - } -} - -/// A representation of a certificate. -/// -/// The `CertificateStore` must be opened with the correct flags to ensure the -/// certificate may outlive it; see the `CertificateStore` documentation. -struct Certificate { - inner: NonNull, -} - -impl Certificate { - /// Sets the specified property of this certificate context. - /// - /// ### Safety - /// `prop_data` must be a valid pointer for the property type. - unsafe fn set_property( - &mut self, - prop_id: u32, - prop_data: *const c_void, - ) -> Result<(), TlsError> { - // SAFETY: `cert` points to a valid certificate context and the OCSP data is valid to read. - call_with_last_error(|| { - (CertSetCertificateContextProperty( - self.inner.as_ptr(), - prop_id, - CERT_SET_PROPERTY_IGNORE_PERSIST_ERROR_FLAG, - prop_data, - ) == TRUE) - .then_some(()) - }) - } -} - -impl Drop for Certificate { - fn drop(&mut self) { - // SAFETY: The certificate context is non-null and points to a valid location. - unsafe { CertFreeCertificateContext(self.inner.as_ptr()) }; - } -} - -#[derive(Debug)] -struct CertEngine { - inner: NonNull, // HCERTENGINECONTEXT -} - -impl CertEngine { - fn new_with_extra_roots( - roots: impl IntoIterator>, - ) -> Result { - let mut exclusive_store = CertificateStore::new()?; - for root in roots { - exclusive_store.add_cert(&root)?; - } - - let mut config = CERT_CHAIN_ENGINE_CONFIG::zeroed_with_size(); - config.hExclusiveRoot = exclusive_store.inner.as_ptr(); - - let mut engine = EnginePtr::NULL; - - // XXX: Due to the redefinition of `CERT_CHAIN_ENGINE_CONFIG`, we need to do pointer casts - // in order to pass our expanded structure into `CertCreateCertificateChainEngine`. - // See also `CERT_CHAIN_PARA` casting below. - let config = NonNull::from(&config).cast().as_ptr(); - // SAFETY: `engine` is valid to be written to and the config is valid to be read. - let res = unsafe { CertCreateCertificateChainEngine(config, &mut engine) }; - - #[allow(clippy::as_conversions)] - let engine = call_with_last_error(|| match NonNull::new(engine as *mut c_void) { - Some(c) if res == TRUE => Some(c), - _ => None, - })?; - Ok(Self { inner: engine }) - } - - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - fn new_with_fake_root(root: &[u8]) -> Result { - use windows_sys::Win32::Security::Cryptography::{ - CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL, CERT_CHAIN_ENABLE_CACHE_AUTO_UPDATE, - }; - - let mut root_store = CertificateStore::new()?; - root_store.add_cert(root)?; - - let mut config = CERT_CHAIN_ENGINE_CONFIG::zeroed_with_size(); - // We use these flags for the following reasons: - // - // - CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL is used in an attempt to stop Windows from using the internet to - // fetch anything during the tests, regardless of what test data is used. - // - // - CERT_CHAIN_ENABLE_CACHE_AUTO_UPDATE is used as a minor performance optimization to allow Windows to reuse - // data inside of a test and avoid any extra parsing, etc, it might need to do pulling directly from the store each time. - // - // Ref: https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_chain_engine_config - config.dwFlags = CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL | CERT_CHAIN_ENABLE_CACHE_AUTO_UPDATE; - config.hExclusiveRoot = root_store.inner.as_ptr(); - - let mut engine = EnginePtr::NULL; - // Same workaround with as above when creating the engine. - let config = NonNull::from(&config).cast().as_ptr(); - // SAFETY: `engine` is valid to be written to and the config is valid to be read. - let res = unsafe { CertCreateCertificateChainEngine(config, &mut engine) }; - - #[allow(clippy::as_conversions)] - let engine = call_with_last_error(|| match NonNull::new(engine as *mut c_void) { - Some(c) if res == TRUE => Some(c), - _ => None, - })?; - - Ok(Self { inner: engine }) - } -} - -impl Drop for CertEngine { - fn drop(&mut self) { - // SAFETY: The engine pointer is guaranteed to be non-null. - unsafe { CertFreeCertificateChainEngine(EnginePtr::from_raw(self.inner)) }; - } -} - -// SAFETY: We know no other threads is mutating the `CertEngine`, because it would require `unsafe`. -// Across the FFI, `CertGetCertificateChain` don't mutate it either. -unsafe impl Sync for CertEngine {} -// SAFETY: All methods of `CertEngine`, including `Drop`, are safe to be called from other -// threads, because all contained resources are owned by Windows and we only maintain reference counted handles to them. -unsafe impl Send for CertEngine {} - -/// An in-memory Windows certificate store. -/// -/// # Safety -/// -/// `CertificateStore` creates `Certificate` objects that may outlive the -/// `CertificateStore`. This is only safe to do if the certificate store is -/// constructed with `CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG`. -struct CertificateStore { - inner: NonNull, // HCERTSTORE - // In production code, this is always `None`. - // - // During tests, we set this to `Some` as the tests use a - // custom verification engine that only uses specific roots. - engine: Option, // HCERTENGINECONTEXT -} - -impl Drop for CertificateStore { - fn drop(&mut self) { - // SAFETY: See the `CertificateStore` documentation. - unsafe { CertCloseStore(self.inner.as_ptr(), 0) }; - } -} - -impl CertificateStore { - /// Creates a new, in-memory certificate store. - fn new() -> Result { - let store = call_with_last_error(|| { - // SAFETY: Called with valid constants and result is checked to be non-null. - // The `CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG` flag is critical; - // see the `CertificateStore` documentation for more info. - NonNull::new(unsafe { - CertOpenStore( - CERT_STORE_PROV_MEMORY, - 0, // Set to zero since this uses `PROV_MEMORY`. - 0, // This field shouldn't be used. - CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, - ptr::null(), - ) - }) - })?; - - // Use the system's default root store and rules. - Ok(Self { - inner: store, - engine: None, - }) - } - - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - fn new_with_fake_root(root: &[u8]) -> Result { - let mut inner = Self::new()?; - - let mut root_store = CertificateStore::new()?; - root_store.add_cert(root)?; - - let engine = CertEngine::new_with_fake_root(root)?; - inner.engine = Some(engine); - - Ok(inner) - } - - /// Adds the provided certificate to the store. - /// - /// The certificate must be encoded as ASN.1 DER. - /// - /// Errors if the certificate was malformed and couldn't be added. - fn add_cert(&mut self, cert: &[u8]) -> Result { - let mut cert_context: *mut CERT_CONTEXT = ptr::null_mut(); - - // SAFETY: `inner` is a valid certificate store, and `cert` is a valid a byte array valid - // for reads, the correct length is being provided, and `cert_context` is valid to write to. - let res = unsafe { - CertAddEncodedCertificateToStore( - self.inner.as_ptr(), - X509_ASN_ENCODING, - cert.as_ptr(), - cert.len() - .try_into() - .map_err(|_| InvalidCertificate(CertificateError::BadEncoding))?, - CERT_STORE_ADD_ALWAYS, - &mut cert_context, - ) - }; - - // SAFETY: Constructing a `Certificate` is only safe if the store was - // created with the right flags; see the `CertificateStore` docs. - match (res, NonNull::new(cert_context)) { - (TRUE, Some(cert)) => Ok(Certificate { inner: cert }), - _ => Err(InvalidCertificate(CertificateError::BadEncoding)), - } - } - - fn new_chain_in( - &self, - certificate: &Certificate, - now: pki_types::UnixTime, - engine: Option<&CertEngine>, - ) -> Result { - let mut cert_chain = ptr::null_mut(); - - let mut parameters = CERT_CHAIN_PARA::zeroed_with_size(); - - #[allow(clippy::as_conversions)] - // https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-cert_usage_match - let usage = CERT_USAGE_MATCH { - dwType: USAGE_MATCH_TYPE_AND, - Usage: CTL_USAGE { - cUsageIdentifier: ALLOWED_EKUS.len() as u32, - rgpszUsageIdentifier: ALLOWED_EKUS.as_ptr() as *mut windows_sys::core::PSTR, - }, - }; - parameters.RequestedUsage = usage; - - #[allow(clippy::as_conversions)] - let time = { - /// Seconds between Jan 1st, 1601 and Jan 1, 1970. - const UNIX_ADJUSTMENT: std::time::Duration = - std::time::Duration::from_secs(11_644_473_600); - - let since_unix_epoch = now.as_secs(); - - // Convert the duration from the UNIX epoch to the Window one, and then convert - // the result into a `FILETIME` structure. - - let since_windows_epoch = since_unix_epoch + UNIX_ADJUSTMENT.as_secs(); - let intervals = (since_windows_epoch * 1_000_000_000) / 100; - - FILETIME { - dwLowDateTime: (intervals & u32::MAX as u64) as u32, - dwHighDateTime: (intervals >> 32) as u32, - } - }; - - // `CERT_CHAIN_REVOCATION_CHECK_END_CERT` only checks revocation for end cert. See the crate's revocation documentation - // for more details. - // `CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT` accumulates network retrievals timeouts - // to limit network time and improve performance. - // `CERT_CHAIN_CACHE_END_CERT` speeds up the common case of multiple connections to same server. - const FLAGS: u32 = CERT_CHAIN_REVOCATION_CHECK_END_CERT - | CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT - | CERT_CHAIN_CACHE_END_CERT; - - // Lowering URL retrieval timeout from default 15s to 10s to account for higher internet speeds - parameters.dwUrlRetrievalTimeout = 10 * 1000; // milliseconds - - // SAFETY: `cert` points to a valid certificate context, parameters is valid for reads, `cert_chain` is valid - // for writes, and the certificate store is valid and initialized. - let res = unsafe { - // XXX: Due to the redefinition of `CERT_CHAIN_PARA`, we need to do pointer casts - // in order to pass our expanded structure into `CertGetCertificateChain`. - // This is safe because the OS uses `cbSize` to know if the extra parameters - // are present or not. As we set `cbSize` correctly, the fields can be read from correctly. - let parameters = NonNull::from(¶meters).cast().as_ptr(); - - CertGetCertificateChain( - match engine { - Some(eng) => EnginePtr::from_raw(eng.inner), - None => EnginePtr::NULL, - }, - certificate.inner.as_ptr(), - &time, - self.inner.as_ptr(), - parameters, - FLAGS, - ptr::null_mut(), - &mut cert_chain, - ) - }; - - // XXX: Windows will internally map the chain's `TrustStatus.dwErrorStatus` to a `dwError` when - // a chain policy is verified, so we only check for errors there. - call_with_last_error(|| match NonNull::new(cert_chain) { - Some(c) if res == TRUE => Some(CertChain { inner: c }), - _ => None, - }) - } -} - -// `windows-sys` >= 0.60 -impl EnginePtr for *mut c_void { - fn from_raw(val: NonNull) -> Self { - val.as_ptr() - } - - const NULL: Self = ptr::null_mut(); -} - -// `windows-sys` 0.52-0.59 -impl EnginePtr for isize { - #[allow(clippy::as_conversions)] - fn from_raw(val: NonNull) -> Self { - val.as_ptr() as isize - } - - const NULL: Self = 0; -} - -/// An abstraction trait over the different ways various `windows-sys` versions represent -/// the type of `HCERTCHAINENGINE`. -trait EnginePtr: Sized { - fn from_raw(val: NonNull) -> Self; - - const NULL: Self; -} - -fn call_with_last_error Option>(mut call: F) -> Result { - if let Some(res) = call() { - Ok(res) - } else { - Err(TlsError::General( - std::io::Error::last_os_error().to_string(), - )) - } -} - -/// A TLS certificate verifier that utilizes the Windows certificate facilities. -#[derive(Debug)] -pub struct Verifier { - /// Testing only: The root CA certificate to trust. - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - test_only_root_ca_override: Option>, - crypto_provider: Arc, - /// Extra trust anchors to add to the verifier above and beyond those provided by - /// the system-provided trust stores. - extra_roots: Option, -} - -impl Verifier { - /// Creates a new instance of a TLS certificate verifier that utilizes the - /// Windows certificate facilities. - #[cfg_attr(docsrs, doc(cfg(all())))] - pub fn new(crypto_provider: Arc) -> Result { - Ok(Self { - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - test_only_root_ca_override: None, - crypto_provider, - extra_roots: None, - }) - } - - /// Creates a new instance of a TLS certificate verifier that utilizes the - /// Windows certificate facilities and augmented by the provided extra root certificates. - #[cfg_attr(docsrs, doc(cfg(not(target_os = "android"))))] - pub fn new_with_extra_roots( - roots: impl IntoIterator>, - crypto_provider: Arc, - ) -> Result { - let cert_engine = CertEngine::new_with_extra_roots(roots)?; - Ok(Self { - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - test_only_root_ca_override: None, - crypto_provider, - extra_roots: Some(cert_engine), - }) - } - - /// Creates a test-only TLS certificate verifier which trusts our fake root CA cert. - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - pub(crate) fn new_with_fake_root( - root: pki_types::CertificateDer<'static>, - crypto_provider: Arc, - ) -> Self { - Self { - test_only_root_ca_override: Some(root), - crypto_provider, - extra_roots: None, - } - } - - /// Verifies a certificate and its chain for the specified `server`. - /// - /// Return `Ok(())` if the certificate was valid. - fn verify_certificate( - &self, - primary_cert: &[u8], - intermediate_certs: &[&[u8]], - server: &[u8], - ocsp_data: Option<&[u8]>, - now: pki_types::UnixTime, - ) -> Result<(), TlsError> { - #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] - let mut store = match self.test_only_root_ca_override.as_ref() { - Some(test_only_root_ca_override) => { - CertificateStore::new_with_fake_root(test_only_root_ca_override)? - } - None => CertificateStore::new()?, - }; - - #[cfg(not(any(test, feature = "ffi-testing", feature = "dbg")))] - let mut store = CertificateStore::new()?; - - let mut primary_cert = store.add_cert(primary_cert)?; - - for cert in intermediate_certs.iter().copied() { - store.add_cert(cert)?; - } - - if let Some(ocsp_data) = ocsp_data { - #[allow(clippy::as_conversions)] - let data = CRYPT_INTEGER_BLOB { - cbData: ocsp_data.len().try_into().map_err(|_| { - invalid_certificate("Malformed OCSP response stapled to server certificate") - })?, - pbData: ocsp_data.as_ptr() as *mut u8, - }; - - // SAFETY: `data` is a valid pointer and matches the property ID. - unsafe { - primary_cert.set_property( - CERT_OCSP_RESPONSE_PROP_ID, - NonNull::from(&data).cast::().as_ptr(), - )?; - } - } - - // Encode UTF-16, null-terminated - let server: Vec = server - .iter() - .map(|c| u16::from(*c)) - .chain(Some(0)) - .collect(); - - let mut cert_chain = store.new_chain_in(&primary_cert, now, store.engine.as_ref())?; - - // We only use `TrustStatus` here because it hasn't had verification performed on it. - // SAFETY: The pointer is guaranteed to be non-null. - let cert_error_status = unsafe { *cert_chain.inner.as_ptr() } - .TrustStatus - .dwErrorStatus; - - let extra_roots_may_needed = - (cert_error_status & (CERT_TRUST_IS_PARTIAL_CHAIN | CERT_TRUST_IS_UNTRUSTED_ROOT)) != 0; - - // If we have extra roots and building the chain gave us an error, we try to build a - // new one with the extra roots. - if extra_roots_may_needed && self.extra_roots.is_some() { - let mut store = CertificateStore::new()?; - - for cert in intermediate_certs.iter().copied() { - store.add_cert(cert)?; - } - - cert_chain = store.new_chain_in(&primary_cert, now, self.extra_roots.as_ref())?; - } - - let status = cert_chain.verify_chain_policy(server)?; - - if status.dwError == 0 { - return Ok(()); - } - - // Only map the errors we have tests for. - #[allow(clippy::as_conversions)] - let win_error = status.dwError as i32; - Err(match win_error { - CERT_E_CN_NO_MATCH | CERT_E_INVALID_NAME => { - InvalidCertificate(CertificateError::NotValidForName) - } - CRYPT_E_REVOKED => InvalidCertificate(CertificateError::Revoked), - CERT_E_EXPIRED => InvalidCertificate(CertificateError::Expired), - CERT_E_UNTRUSTEDROOT => InvalidCertificate(CertificateError::UnknownIssuer), - CERT_E_WRONG_USAGE => InvalidCertificate(CertificateError::InvalidPurpose), - error_num => { - let err = std::io::Error::from_raw_os_error(error_num); - // The included error message has both the description and raw OS error code. - invalid_certificate(err.to_string()) - } - }) - } -} - -#[cfg_attr(docsrs, doc(cfg(all())))] -impl ServerCertVerifier for Verifier { - fn verify_server_cert( - &self, - end_entity: &pki_types::CertificateDer<'_>, - intermediates: &[pki_types::CertificateDer<'_>], - server_name: &pki_types::ServerName, - ocsp_response: &[u8], - now: pki_types::UnixTime, - ) -> Result { - log_server_cert(end_entity); - - let name = server_name.to_str(); - // Trim trailing `.` labels to remove compatibility hazards with the Windows verifier. - // It performs exact quality comparisions in most cases (see https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-httpspolicycallbackdata) - // which causes problems with hostnames that are considered equivalent for security purposes in other verifier and TLS implementations. - // - // Ref: https://github.com/rustls/rustls-platform-verifier/issues/240 - let name = name.strip_suffix('.').unwrap_or(&name); - - let intermediate_certs: Vec<&[u8]> = intermediates.iter().map(|c| c.as_ref()).collect(); - - let ocsp_data = if !ocsp_response.is_empty() { - Some(ocsp_response) - } else { - None - }; - - match self.verify_certificate( - end_entity.as_ref(), - &intermediate_certs, - name.as_bytes(), - ocsp_data, - now, - ) { - Ok(()) => Ok(rustls::client::danger::ServerCertVerified::assertion()), - Err(e) => { - // SAFETY: - // Errors are our own custom errors, WinAPI errors, or static strings. - log::error!("failed to verify TLS certificate: {}", e); - Err(e) - } - } - } - - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - verify_tls12_signature( - message, - cert, - dss, - &self.crypto_provider.signature_verification_algorithms, - ) - } - - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - verify_tls13_signature( - message, - cert, - dss, - &self.crypto_provider.signature_verification_algorithms, - ) - } - - fn supported_verify_schemes(&self) -> Vec { - self.crypto_provider - .signature_verification_algorithms - .supported_schemes() - } -} - -/// A trait to represent an object that can be safely created with all zero values -/// and have a size assigned to it. -/// -/// # Safety -/// -/// This has the same safety requirements as [std::mem::zeroed]. -unsafe trait ZeroedWithSize: Sized { - const SIZE: u32 = { - let size = core::mem::size_of::(); - - // NB: `TryInto` isn't stable in const yet. - #[allow(clippy::as_conversions)] - if size <= u32::MAX as usize { - size as u32 - } else { - panic!("structure was larger then DWORD") - } - }; - - /// Returns a zeroed structure with its structure size (`cbSize`) field set to the correct value. - fn zeroed_with_size() -> Self; -} From e521b817376f6acd7d95710615106dfaf7cbf50d Mon Sep 17 00:00:00 2001 From: ComplexSpaces Date: Fri, 18 Sep 2026 17:08:27 -0500 Subject: [PATCH 2/2] Backfill previous Cargo-published AAR artifacts --- .../0.1.0/rustls-platform-verifier-0.1.0.aar | Bin 0 -> 8217 bytes .../rustls-platform-verifier-0.1.0.aar.sha1 | 1 + .../0.1.0/rustls-platform-verifier-0.1.0.pom | 10 ++++++++++ .../rustls-platform-verifier-0.1.0.pom.sha1 | 1 + .../0.1.1/rustls-platform-verifier-0.1.1.aar | Bin 0 -> 9287 bytes .../rustls-platform-verifier-0.1.1.aar.sha1 | 1 + .../0.1.1/rustls-platform-verifier-0.1.1.pom | 10 ++++++++++ .../rustls-platform-verifier-0.1.1.pom.sha1 | 1 + 8 files changed, 24 insertions(+) create mode 100644 android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.aar create mode 100644 android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.aar.sha1 create mode 100644 android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.pom create mode 100644 android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.pom.sha1 create mode 100644 android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.1/rustls-platform-verifier-0.1.1.aar create mode 100644 android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.1/rustls-platform-verifier-0.1.1.aar.sha1 create mode 100644 android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.1/rustls-platform-verifier-0.1.1.pom create mode 100644 android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.1/rustls-platform-verifier-0.1.1.pom.sha1 diff --git a/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.aar b/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.aar new file mode 100644 index 0000000000000000000000000000000000000000..7b43101c352507d62e9ad13aa936a1364b2c3bdd GIT binary patch literal 8217 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 PP7F{GkgLDRKtTQrPq%Y+ literal 0 HcmV?d00001 diff --git a/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.aar.sha1 b/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.aar.sha1 new file mode 100644 index 00000000..7dd3ea65 --- /dev/null +++ b/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.aar.sha1 @@ -0,0 +1 @@ +d47d4f1397843908b7130aec2a32043532179e51 org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.aar \ No newline at end of file diff --git a/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.pom b/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.pom new file mode 100644 index 00000000..204bb324 --- /dev/null +++ b/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.pom @@ -0,0 +1,10 @@ + + + 4.0.0 + org.rustls + rustls-platform-verifier + 0.1.0 + aar + The internal JVM support component of the rustls-platform-verifier Rust crate + \ No newline at end of file diff --git a/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.pom.sha1 b/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.pom.sha1 new file mode 100644 index 00000000..3d0485a1 --- /dev/null +++ b/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.pom.sha1 @@ -0,0 +1 @@ +8c821e411aeacf2422207310f081a1314ec308ac org/rustls/rustls-platform-verifier/0.1.0/rustls-platform-verifier-0.1.0.pom \ No newline at end of file diff --git a/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.1/rustls-platform-verifier-0.1.1.aar b/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.1/rustls-platform-verifier-0.1.1.aar new file mode 100644 index 0000000000000000000000000000000000000000..8acc8b5fe0b36db4fb45ad8ac4a8f8c3b24fc0c5 GIT binary patch literal 9287 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 + + 4.0.0 + org.rustls + rustls-platform-verifier + 0.1.1 + aar + The internal JVM support component of the rustls-platform-verifier Rust crate + \ No newline at end of file diff --git a/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.1/rustls-platform-verifier-0.1.1.pom.sha1 b/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.1/rustls-platform-verifier-0.1.1.pom.sha1 new file mode 100644 index 00000000..1ae7a456 --- /dev/null +++ b/android-release-support/maven/org/rustls/rustls-platform-verifier/0.1.1/rustls-platform-verifier-0.1.1.pom.sha1 @@ -0,0 +1 @@ +87e5707521957cf21990213144a74578c0d78bbf org/rustls/rustls-platform-verifier/0.1.1/rustls-platform-verifier-0.1.1.pom \ No newline at end of file