pax_global_header00006660000000000000000000000064151245121030014504gustar00rootroot0000000000000052 comment=71e4dd82ad584ac93c41a5ec694f255530da97d3 rustls-rustls-native-certs-3cc7153/000077500000000000000000000000001512451210300173265ustar00rootroot00000000000000rustls-rustls-native-certs-3cc7153/.github/000077500000000000000000000000001512451210300206665ustar00rootroot00000000000000rustls-rustls-native-certs-3cc7153/.github/dependabot.yml000066400000000000000000000004151512451210300235160ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: cargo directory: "/" schedule: interval: weekly open-pull-requests-limit: 10 groups: crates-io: patterns: - "*" - package-ecosystem: github-actions directory: "/" schedule: interval: weekly rustls-rustls-native-certs-3cc7153/.github/workflows/000077500000000000000000000000001512451210300227235ustar00rootroot00000000000000rustls-rustls-native-certs-3cc7153/.github/workflows/rust.yml000066400000000000000000000115711512451210300244500ustar00rootroot00000000000000name: rustls permissions: contents: read on: push: branches: ['main', 'ci/*'] pull_request: merge_group: schedule: - cron: '0 18 * * *' workflow_dispatch: jobs: build: name: Build+test runs-on: ${{ matrix.os }} timeout-minutes: 15 strategy: fail-fast: false matrix: # test a bunch of toolchains on ubuntu rust: - stable - beta - nightly os: [ubuntu-latest] # but only stable on macos/windows (slower platforms) include: - os: macos-latest rust: stable - os: windows-latest rust: stable steps: - name: Checkout sources uses: actions/checkout@v6 with: persist-credentials: false - name: Install ${{ matrix.rust }} toolchain uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - name: Install NASM for aws-lc-rs on Windows if: runner.os == 'Windows' uses: ilammy/setup-nasm@v1 - name: cargo build (debug; default features) run: cargo build --locked - name: cargo test (debug; default features) run: cargo test --locked env: RUST_BACKTRACE: 1 - name: cargo test (debug; all features) run: cargo test --locked --all-features env: RUST_BACKTRACE: 1 - name: cargo build (debug; no default features) run: cargo build --locked --no-default-features - name: cargo test (debug; no default features; no run) run: cargo test --locked --no-default-features --no-run - name: cargo test (release; no run) run: cargo test --locked --release --no-run # https://github.com/rustls/rustls-native-certs/issues/179 # - name: run macOS integration test # if: matrix.os == 'macos-latest' # run: sudo bash integration-tests/macos.sh msrv: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: - name: Checkout sources uses: actions/checkout@v6 with: persist-credentials: false - name: Install MSRV toolchain uses: dtolnay/rust-toolchain@master with: toolchain: "1.71" - run: cargo check --locked --lib --all-features minimal-versions: name: Check minimum versions of direct dependencies runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v6 with: persist-credentials: false - name: Install rust toolchain uses: dtolnay/rust-toolchain@nightly - name: Install cargo-minimal-versions uses: taiki-e/install-action@cargo-minimal-versions # cargo-minimal-versions requires cargo-hack - name: Install cargo-hack uses: taiki-e/install-action@cargo-hack - name: Check direct-minimal-versions run: cargo minimal-versions --direct --ignore-private check semver: name: Check semver compatibility runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v6 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@v6 with: persist-credentials: false - name: Install rust toolchain uses: dtolnay/rust-toolchain@nightly - name: cargo doc (rustls; all features) run: cargo doc --locked --all-features --no-deps --document-private-items env: RUSTDOCFLAGS: -Dwarnings format: name: Format runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v6 with: persist-credentials: false - name: Install rust toolchain uses: dtolnay/rust-toolchain@stable with: components: rustfmt - name: Check formatting run: cargo fmt --all -- --check clippy: name: Clippy runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v6 with: persist-credentials: false - name: Install rust toolchain uses: dtolnay/rust-toolchain@stable with: components: clippy - run: cargo clippy --locked --all-features --all-targets -- --deny warnings clippy-nightly: name: Clippy (Nightly) runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v6 with: persist-credentials: false - name: Install rust toolchain uses: dtolnay/rust-toolchain@nightly with: components: clippy - run: cargo clippy --locked --all-features --all-targets -- --deny warnings rustls-rustls-native-certs-3cc7153/.github/workflows/smoke-tests.yaml000066400000000000000000000024371512451210300260730ustar00rootroot00000000000000name: smoke-tests permissions: contents: read on: workflow_dispatch: schedule: # We run these tests on a daily basis (at a time slightly offset from the # top of the hour), because they rely on external 3rd party services that # can be flaky. - cron: '15 18 * * *' jobs: smoke-tests: name: Smoke Tests runs-on: ${{ matrix.os }} strategy: matrix: # test a bunch of toolchains on ubuntu rust: - stable - beta - nightly os: [ ubuntu-latest ] # but only stable on macos/windows (slower platforms) include: - os: macos-latest rust: stable - os: windows-latest rust: stable steps: - name: Checkout sources uses: actions/checkout@v6 with: persist-credentials: false - name: Install ${{ matrix.rust }} toolchain uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - name: Install NASM for aws-lc-rs on Windows if: runner.os == 'Windows' uses: ilammy/setup-nasm@v1 - name: Build main crate run: cargo build --locked - name: Run smoke tests run: cargo test --locked -- --ignored env: RUST_BACKTRACE: 1 rustls-rustls-native-certs-3cc7153/.gitignore000066400000000000000000000000441512451210300213140ustar00rootroot00000000000000target/ *.gcda *.gcno *.info /.idea rustls-rustls-native-certs-3cc7153/CONTRIBUTING.md000066400000000000000000000035371512451210300215670ustar00rootroot00000000000000# Contributing Thanks for considering helping this project. There are many ways you can help: using the library and reporting bugs, reporting usability issues, making additions and improvements to the library, documentation and finding security bugs. ## Reporting bugs Please file a github issue. Include as much information as possible. Suspected protocol bugs are easier debugged with a pcap or reproduction steps. Feel free to file github issues to get help, or ask a question. ## Code changes Some ideas and guidelines for contributions: - For large features, file an issue prior to starting work. This means everyone can see what is in progress prior to a PR. - Feel free to submit a PR even if the work is not totally finished, for feedback or to hand-over. - Prefer not to reference github issue or PR numbers in commits. - Try to keep code formatting commits separate from functional commits. - I run `cargo outdated` prior to major releases; but PRs to update specific dependencies are welcome. ## Security bugs Please report security bugs by filing a github issue, or by email to jbp@jbp.io if you want to disclose privately. I'll then: - Prepare a fix and regression tests. - Backport the fix and make a patch release for most recent release. - Submit an advisory to [rustsec/advisory-db](https://github.com/RustSec/advisory-db). - Refer to the advisory on the main README.md and release notes. ## Testing - Features involving additions to the public API should have (at least) API-level tests (see [`tests/api.rs`](tests/api.rs)). - Protocol additions should have some coverage -- consider enabling corresponding tests in the bogo suite, or writing some adhoc tests. PRs which cause test failures or a significant coverage decrease are unlikely to be accepted. ## Licensing Contributions are made under [rustls-native-certs's licenses](LICENSE). rustls-rustls-native-certs-3cc7153/Cargo.lock000066400000000000000000000603761512451210300212470ustar00rootroot00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "asn1-rs" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" 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", "synstructure", ] [[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", ] [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" dependencies = [ "aws-lc-sys", "zeroize", ] [[package]] name = "aws-lc-sys" version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" dependencies = [ "cc", "cmake", "dunce", "fs_extra", ] [[package]] name = "bitflags" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "cc" version = "1.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" dependencies = [ "find-msvc-tools", "jobserver", "libc", "shlex", ] [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cmake" version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" dependencies = [ "cc", ] [[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 = "data-encoding" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[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", "num-traits", "rusticata-macros", ] [[package]] name = "deranged" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", ] [[package]] name = "displaydoc" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "dunce" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "errno" version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", "windows-sys 0.61.2", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "find-msvc-tools" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" [[package]] name = "fs_extra" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-sink" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "getrandom" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "getrandom" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", "wasip2", ] [[package]] name = "itoa" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jobserver" version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ "getrandom 0.3.4", "libc", ] [[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.178" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "linux-raw-sys" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "lock_api" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ "scopeguard", ] [[package]] name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[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.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", ] [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 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.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "openssl-probe" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" [[package]] name = "parking_lot" version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-link", ] [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[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.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "redox_syscall" version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ "bitflags", ] [[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.16", "libc", "untrusted", "windows-sys 0.52.0", ] [[package]] name = "rusticata-macros" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" dependencies = [ "nom", ] [[package]] name = "rustix" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.61.2", ] [[package]] name = "rustls" version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "aws-lc-rs", "log", "once_cell", "rustls-pki-types", "rustls-webpki", "subtle", "zeroize", ] [[package]] name = "rustls-native-certs" version = "0.8.3" dependencies = [ "openssl-probe", "ring", "rustls", "rustls-pki-types", "rustls-webpki", "schannel", "security-framework", "serial_test", "tempfile", "untrusted", "webpki-roots", "x509-parser", ] [[package]] name = "rustls-pki-types" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", ] [[package]] name = "scc" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" dependencies = [ "sdd", ] [[package]] name = "schannel" version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ "windows-sys 0.61.2", ] [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sdd" version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" [[package]] name = "security-framework" version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ "bitflags", "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", ] [[package]] name = "security-framework-sys" version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "serde" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serial_test" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9" dependencies = [ "futures", "log", "once_cell", "parking_lot", "scc", "serial_test_derive", ] [[package]] name = "serial_test_derive" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "slab" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[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.111" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "synstructure" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tempfile" version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", "getrandom 0.3.4", "once_cell", "rustix", "windows-sys 0.61.2", ] [[package]] name = "thiserror" version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "time" version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", ] [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ "wit-bindgen", ] [[package]] name = "webpki-roots" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" dependencies = [ "rustls-pki-types", ] [[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 = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "x509-parser" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb3e137310115a65136898d2079f003ce33331a6c4b0d51f1531d1be082b6425" dependencies = [ "asn1-rs", "data-encoding", "der-parser", "lazy_static", "nom", "oid-registry", "rusticata-macros", "thiserror", "time", ] [[package]] name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" rustls-rustls-native-certs-3cc7153/Cargo.toml000066400000000000000000000016121512451210300212560ustar00rootroot00000000000000[package] name = "rustls-native-certs" version = "0.8.3" edition = "2021" rust-version = "1.71" license = "Apache-2.0 OR ISC OR MIT" readme = "README.md" description = "rustls-native-certs allows rustls to use the platform native certificate store" homepage = "https://github.com/rustls/rustls-native-certs" repository = "https://github.com/rustls/rustls-native-certs" categories = ["network-programming", "cryptography"] [dependencies] pki-types = { package = "rustls-pki-types", version = "1.10", features = ["std"] } [dev-dependencies] ring = "0.17" rustls = "0.23" rustls-webpki = "0.103" serial_test = "3" tempfile = "3.5" untrusted = "0.9" webpki-roots = "1" x509-parser = "0.18" [target.'cfg(windows)'.dependencies] schannel = "0.1" [target.'cfg(all(unix, not(target_os = "macos")))'.dependencies] openssl-probe = "0.2" [target.'cfg(target_os = "macos")'.dependencies] security-framework = "3" rustls-rustls-native-certs-3cc7153/LICENSE000066400000000000000000000004361512451210300203360ustar00rootroot00000000000000Rustls is distributed under the following three licenses: - Apache License version 2.0. - MIT license. - ISC license. These are included as LICENSE-APACHE, LICENSE-MIT and LICENSE-ISC respectively. You may use this software under the terms of any of these licenses, at your option. rustls-rustls-native-certs-3cc7153/LICENSE-APACHE000066400000000000000000000251371512451210300212620ustar00rootroot00000000000000 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. rustls-rustls-native-certs-3cc7153/LICENSE-ISC000066400000000000000000000014071512451210300207510ustar00rootroot00000000000000ISC License (ISC) Copyright (c) 2016, Joseph Birr-Pixton Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. rustls-rustls-native-certs-3cc7153/LICENSE-MIT000066400000000000000000000020721512451210300207630ustar00rootroot00000000000000Copyright (c) 2016 Joseph Birr-Pixton 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. rustls-rustls-native-certs-3cc7153/README.md000066400000000000000000000061731512451210300206140ustar00rootroot00000000000000![Logo](https://raw.githubusercontent.com/rustls/rustls/main/admin/rustls-logo-web.png) **rustls-native-certs** allows [rustls](https://github.com/rustls/rustls) to use the platform's native certificate store when operating as a TLS client. > [!IMPORTANT] > Instead of this crate, we suggest using [rustls-platform-verifier](https://github.com/rustls/rustls-platform-verifier), > which provides a more robust solution with a simpler API. This crate is still maintained, > but mostly for use inside the platform verifier on platforms where no other > solution is available. For more context, see > [deployment considerations](https://github.com/rustls/rustls-platform-verifier?tab=readme-ov-file#deployment-considerations). # Status rustls-native-certs is mature and widely used. If you'd like to help out, please see [CONTRIBUTING.md](CONTRIBUTING.md). [![rustls](https://github.com/rustls/rustls-native-certs/actions/workflows/rust.yml/badge.svg)](https://github.com/rustls/rustls-native-certs/actions/workflows/rust.yml) [![Documentation](https://docs.rs/rustls-native-certs/badge.svg)](https://docs.rs/rustls-native-certs) Release notes can be found [on GitHub](https://github.com/rustls/rustls-native-certs/releases). # API This library exposes a single function with this signature: ```rust pub fn load_native_certs() -> Result>, std::io::Error> ``` On success, this returns a `Vec>` loaded with a snapshot of the root certificates found on this platform. This function fails in a platform-specific way, expressed in a `std::io::Error`. This function can be expensive: on some platforms it involves loading and parsing a ~300KB disk file. It's therefore prudent to call this sparingly. # Platform support This is supported on Windows, macOS and Linux: - On all platforms, the `SSL_CERT_FILE` environment variable is checked first. If that's set, certificates are loaded from the path specified by that variable, or an error is returned if certificates cannot be loaded from the given path. If it's not set, then the platform-specific certificate source is used. - On Windows, certificates are loaded from the system certificate store. The [`schannel`](https://github.com/steffengy/schannel-rs) crate is used to access the Windows certificate store APIs. - On macOS, certificates are loaded from the keychain. The user, admin and system trust settings are merged together as documented by Apple. The [`security-framework`](https://github.com/kornelski/rust-security-framework) crate is used to access the keystore APIs. - On Linux and other UNIX-like operating systems, the [`openssl-probe`](https://github.com/alexcrichton/openssl-probe) crate is used to discover the filename of the system CA bundle. # Worked example See [`examples/google.rs`](examples/google.rs). # License rustls-native-certs is distributed under the following three licenses: - Apache License version 2.0. - MIT license. - ISC license. These are included as LICENSE-APACHE, LICENSE-MIT and LICENSE-ISC respectively. You may use this software under the terms of any of these licenses, at your option. rustls-rustls-native-certs-3cc7153/RELEASING.md000066400000000000000000000026651512451210300211720ustar00rootroot00000000000000# Making a rustls-native-certs release This is a checklist for steps to make before/after making a rustls release. 1. Attend to the README.md: this appears on crates.io for the release, and can't be edited after the fact. - Ensure the version has a good set of release notes. Move old release notes to OLDCHANGES.md if this is getting excessively long. - Write the version and date of the release. 2. Run `cargo update` followed by `cargo outdated`, to check if we have any dependency updates which are not already automatically taken by their semver specs. - If we do, take them if possible with separate commits (but there should've been dependabot PRs submitted for these already.) 3. Now run `cargo test --all-features` to ensure our tests continue to pass with the updated dependencies. 4. Update `Cargo.toml` to set the correct version. 5. Make a commit with the above changes, something like 'Prepare $VERSION'. This should not contain functional changes: just versions numbers, and markdown changes. 6. Do a dry run: check `cargo publish --dry-run` 7. Push the above commit. Wait for CI to confirm it as green. - Any red _should_ naturally block the release. - If rustc nightly is broken, this _may_ be acceptable if the reason is understood and does not point to a defect. 8. Tag the released version: `git tag -m '0.20.0' v/0.20.0` 9. Push the tag: `git push --tags` 10. Do the release: `cargo publish`. rustls-rustls-native-certs-3cc7153/examples/000077500000000000000000000000001512451210300211445ustar00rootroot00000000000000rustls-rustls-native-certs-3cc7153/examples/google.rs000066400000000000000000000024471512451210300227750ustar00rootroot00000000000000use std::io::{stdout, Read, Write}; use std::net::TcpStream; use std::sync::Arc; fn main() { let mut roots = rustls::RootCertStore::empty(); for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") { roots.add(cert).unwrap(); } let config = rustls::ClientConfig::builder() .with_root_certificates(roots) .with_no_client_auth(); let mut conn = rustls::ClientConnection::new(Arc::new(config), "google.com".try_into().unwrap()).unwrap(); let mut sock = TcpStream::connect("google.com:443").expect("cannot connect"); let mut tls = rustls::Stream::new(&mut conn, &mut sock); tls.write_all( concat!( "GET / HTTP/1.1\r\n", "Host: google.com\r\n", "Connection: close\r\n", "Accept-Encoding: identity\r\n", "\r\n" ) .as_bytes(), ) .expect("write failed"); let ciphersuite = tls .conn .negotiated_cipher_suite() .expect("tls handshake failed"); writeln!( &mut std::io::stderr(), "Current ciphersuite: {:?}", ciphersuite.suite() ) .unwrap(); let mut plaintext = Vec::new(); tls.read_to_end(&mut plaintext).unwrap(); stdout().write_all(&plaintext).unwrap(); } rustls-rustls-native-certs-3cc7153/examples/print-trust-anchors.rs000066400000000000000000000007331512451210300254630ustar00rootroot00000000000000//! Print the Subject of all extracted trust anchors. use std::error::Error; use x509_parser::prelude::*; fn main() -> Result<(), Box> { for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") { match parse_x509_certificate(cert.as_ref()) { Ok((_, cert)) => println!("{}", cert.tbs_certificate.subject), Err(e) => eprintln!("error parsing certificate: {e}"), }; } Ok(()) } rustls-rustls-native-certs-3cc7153/integration-tests/000077500000000000000000000000001512451210300230115ustar00rootroot00000000000000rustls-rustls-native-certs-3cc7153/integration-tests/macos.sh000066400000000000000000000015021512451210300244450ustar00rootroot00000000000000#!/bin/bash set -ex ANY_CA_PEM=integration-tests/one-existing-ca.pem ANY_CA_SUBJECT="OU=GlobalSign Root CA - R3, O=GlobalSign, CN=GlobalSign" reset() { security remove-trusted-cert -d $ANY_CA_PEM || true list | grep "$ANY_CA_SUBJECT" } list() { cargo test util_list_certs -- --nocapture 2>/dev/null } assert_missing() { set +e list | grep "$1" ret=$? set -e test $ret -eq 1 } assert_exists() { list | grep "$1" > /dev/null } test_distrust_existing_root() { assert_exists "$ANY_CA_SUBJECT" security add-trusted-cert -d -r deny $ANY_CA_PEM assert_missing "$ANY_CA_SUBJECT" reset } # https://developer.apple.com/forums/thread/671582?answerId=693632022#693632022 security authorizationdb write com.apple.trust-settings.admin allow reset test_distrust_existing_root printf "\n*** All tests passed ***\n" rustls-rustls-native-certs-3cc7153/integration-tests/one-existing-ca.pem000066400000000000000000000023151512451210300265070ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK 6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH WD9f -----END CERTIFICATE----- rustls-rustls-native-certs-3cc7153/rustfmt.toml000066400000000000000000000000171512451210300217250ustar00rootroot00000000000000chain_width=40 rustls-rustls-native-certs-3cc7153/src/000077500000000000000000000000001512451210300201155ustar00rootroot00000000000000rustls-rustls-native-certs-3cc7153/src/lib.rs000066400000000000000000000405611512451210300212370ustar00rootroot00000000000000//! rustls-native-certs allows rustls to use the platform's native certificate //! store when operating as a TLS client. //! //! It provides a single function [`load_native_certs()`], which returns a //! collection of certificates found by reading the platform-native //! certificate store. //! //! If the SSL_CERT_FILE environment variable is set, certificates (in PEM //! format) are read from that file instead. //! //! If you want to load these certificates into a `rustls::RootCertStore`, //! you'll likely want to do something like this: //! //! ```no_run //! let mut roots = rustls::RootCertStore::empty(); //! for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") { //! roots.add(cert).unwrap(); //! } //! ``` // Enable documentation for all features on docs.rs #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use std::error::Error as StdError; use std::path::{Path, PathBuf}; use std::{env, fmt, fs, io}; use pki_types::pem::{self, PemObject}; use pki_types::CertificateDer; #[cfg(all(unix, not(target_os = "macos")))] mod unix; #[cfg(all(unix, not(target_os = "macos")))] use unix as platform; #[cfg(windows)] mod windows; #[cfg(windows)] use windows as platform; #[cfg(target_os = "macos")] mod macos; #[cfg(target_os = "macos")] use macos as platform; /// Load root certificates found in the platform's native certificate store. /// /// ## Environment Variables /// /// | Env. Var. | Description | /// |---------------|---------------------------------------------------------------------------------------| /// | SSL_CERT_FILE | File containing an arbitrary number of certificates in PEM format. | /// | SSL_CERT_DIR | Colon separated list of directories containing certificate files. | /// /// If **either** (or **both**) are set, certificates are only loaded from /// the locations specified via environment variables and not the platform- /// native certificate store. /// /// ## Certificate Validity /// /// All certificates are expected to be in PEM format. A file may contain /// multiple certificates. /// /// Example: /// /// ```text /// -----BEGIN CERTIFICATE----- /// MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw /// CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg /// R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 /// MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT /// ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw /// EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW /// +1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 /// ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T /// AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI /// zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW /// tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 /// /q4AaOeMSQ+2b1tbFfLn /// -----END CERTIFICATE----- /// -----BEGIN CERTIFICATE----- /// MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 /// MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g /// Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG /// A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg /// Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl /// ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j /// QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr /// ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr /// BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM /// YyRIHN8wfdVoOw== /// -----END CERTIFICATE----- /// /// ``` /// /// For reasons of compatibility, an attempt is made to skip invalid sections /// of a certificate file but this means it's also possible for a malformed /// certificate to be skipped. /// /// If a certificate isn't loaded, and no error is reported, check if: /// /// 1. the certificate is in PEM format (see example above) /// 2. *BEGIN CERTIFICATE* line starts with exactly five hyphens (`'-'`) /// 3. *END CERTIFICATE* line ends with exactly five hyphens (`'-'`) /// 4. there is a line break after the certificate. /// /// ## Errors /// /// This function fails in a platform-specific way, expressed in a `std::io::Error`. /// /// ## Caveats /// /// This function can be expensive: on some platforms it involves loading /// and parsing a ~300KB disk file. It's therefore prudent to call /// this sparingly. /// /// [c_rehash]: https://www.openssl.org/docs/manmaster/man1/c_rehash.html pub fn load_native_certs() -> CertificateResult { let paths = CertPaths::from_env(); match (&paths.dirs, &paths.file) { (v, _) if !v.is_empty() => paths.load(), (_, Some(_)) => paths.load(), _ => platform::load_native_certs(), } } /// Results from trying to load certificates from the platform's native store. #[non_exhaustive] #[derive(Debug, Default)] pub struct CertificateResult { /// Any certificates that were successfully loaded. pub certs: Vec>, /// Any errors encountered while loading certificates. pub errors: Vec, } impl CertificateResult { /// Return the found certificates if no error occurred, otherwise panic. #[track_caller] pub fn expect(self, msg: &str) -> Vec> { match self.errors.is_empty() { true => self.certs, false => panic!("{msg}: {:?}", self.errors), } } /// Return the found certificates if no error occurred, otherwise panic. #[track_caller] pub fn unwrap(self) -> Vec> { match self.errors.is_empty() { true => self.certs, false => panic!( "errors occurred while loading certificates: {:?}", self.errors ), } } fn pem_error(&mut self, err: pem::Error, path: &Path) { self.errors.push(Error { context: "failed to read PEM from file", kind: match err { pem::Error::Io(err) => ErrorKind::Io { inner: err, path: path.to_owned(), }, _ => ErrorKind::Pem(err), }, }); } fn io_error(&mut self, err: io::Error, path: &Path, context: &'static str) { self.errors.push(Error { context, kind: ErrorKind::Io { inner: err, path: path.to_owned(), }, }); } #[cfg(any(windows, target_os = "macos"))] fn os_error(&mut self, err: Box, context: &'static str) { self.errors.push(Error { context, kind: ErrorKind::Os(err), }); } } /// Certificate paths from `SSL_CERT_FILE` and/or `SSL_CERT_DIR`. struct CertPaths { file: Option, dirs: Vec, } impl CertPaths { fn from_env() -> Self { Self { file: env::var_os(ENV_CERT_FILE).map(PathBuf::from), // Read `SSL_CERT_DIR`, split it on the platform delimiter (`:` on Unix, `;` on Windows), // and return the entries as `PathBuf`s. // // See dirs: match env::var_os(ENV_CERT_DIR) { Some(dirs) => env::split_paths(&dirs).collect(), None => Vec::new(), }, } } /// Load certificates from the paths. /// /// See [`load_certs_from_paths()`]. fn load(&self) -> CertificateResult { load_certs_from_paths_internal(self.file.as_deref(), &self.dirs) } } /// Load certificates from the given paths. /// /// If both are `None`, returns an empty [`CertificateResult`]. /// /// If `file` is `Some`, it is always used, so it must be a path to an existing, /// accessible file from which certificates can be loaded successfully. While parsing, /// the rustls-pki-types PEM parser will ignore parts of the file which are /// not considered part of a certificate. Certificates which are not in the right /// format (PEM) or are otherwise corrupted may get ignored silently. /// /// If `dir` is defined, a directory must exist at this path, and all files /// contained in it must be loaded successfully, subject to the rules outlined above for `file`. /// The directory is not scanned recursively and may be empty. pub fn load_certs_from_paths(file: Option<&Path>, dir: Option<&Path>) -> CertificateResult { let dir = match dir { Some(d) => vec![d], None => Vec::new(), }; load_certs_from_paths_internal(file, dir.as_ref()) } fn load_certs_from_paths_internal( file: Option<&Path>, dir: &[impl AsRef], ) -> CertificateResult { let mut out = CertificateResult::default(); if file.is_none() && dir.is_empty() { return out; } if let Some(cert_file) = file { load_pem_certs(cert_file, &mut out); } for cert_dir in dir.iter() { load_pem_certs_from_dir(cert_dir.as_ref(), &mut out); } out.certs .sort_unstable_by(|a, b| a.cmp(b)); out.certs.dedup(); out } /// Load certificate from certificate directory (what OpenSSL calls CAdir) fn load_pem_certs_from_dir(dir: &Path, out: &mut CertificateResult) { let dir_reader = match fs::read_dir(dir) { Ok(reader) => reader, Err(err) => { out.io_error(err, dir, "opening directory"); return; } }; for entry in dir_reader { let entry = match entry { Ok(entry) => entry, Err(err) => { out.io_error(err, dir, "reading directory entries"); continue; } }; let path = entry.path(); // `openssl rehash` used to create this directory uses symlinks. So, // make sure we resolve them. let metadata = match fs::metadata(&path) { Ok(metadata) => metadata, Err(e) if e.kind() == io::ErrorKind::NotFound => { // Dangling symlink continue; } Err(e) => { out.io_error(e, &path, "failed to open file"); continue; } }; if metadata.is_file() { load_pem_certs(&path, out); } } } fn load_pem_certs(path: &Path, out: &mut CertificateResult) { let iter = match CertificateDer::pem_file_iter(path) { Ok(iter) => iter, Err(err) => { out.pem_error(err, path); return; } }; for result in iter { match result { Ok(cert) => out.certs.push(cert), Err(err) => out.pem_error(err, path), } } } #[derive(Debug)] pub struct Error { pub context: &'static str, pub kind: ErrorKind, } impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { Some(match &self.kind { ErrorKind::Io { inner, .. } => inner, ErrorKind::Os(err) => &**err, ErrorKind::Pem(err) => err, }) } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.context)?; f.write_str(": ")?; match &self.kind { ErrorKind::Io { inner, path } => { write!(f, "{inner} at '{}'", path.display()) } ErrorKind::Os(err) => err.fmt(f), ErrorKind::Pem(err) => err.fmt(f), } } } #[non_exhaustive] #[derive(Debug)] pub enum ErrorKind { Io { inner: io::Error, path: PathBuf }, Os(Box), Pem(pem::Error), } const ENV_CERT_FILE: &str = "SSL_CERT_FILE"; const ENV_CERT_DIR: &str = "SSL_CERT_DIR"; #[cfg(test)] mod tests { use super::*; use std::fs::File; #[cfg(unix)] use std::fs::Permissions; use std::io::Write; #[cfg(unix)] use std::os::unix::fs::PermissionsExt; #[test] fn deduplication() { let temp_dir = tempfile::TempDir::new().unwrap(); let cert1 = include_str!("../tests/badssl-com-chain.pem"); let cert2 = include_str!("../integration-tests/one-existing-ca.pem"); let file_path = temp_dir .path() .join("ca-certificates.crt"); let dir_path = temp_dir.path().to_path_buf(); { let mut file = File::create(&file_path).unwrap(); write!(file, "{}", &cert1).unwrap(); write!(file, "{}", &cert2).unwrap(); } { // Duplicate (already in `file_path`) let mut file = File::create(dir_path.join("71f3bb26.0")).unwrap(); write!(file, "{}", &cert1).unwrap(); } { // Duplicate (already in `file_path`) let mut file = File::create(dir_path.join("912e7cd5.0")).unwrap(); write!(file, "{}", &cert2).unwrap(); } let result = CertPaths { file: Some(file_path.clone()), dirs: vec![], } .load(); assert_eq!(result.certs.len(), 2); let result = CertPaths { file: None, dirs: vec![dir_path.clone()], } .load(); assert_eq!(result.certs.len(), 2); let result = CertPaths { file: Some(file_path), dirs: vec![dir_path], } .load(); assert_eq!(result.certs.len(), 2); } #[test] fn malformed_file_from_env() { // Certificate parser tries to extract certs from file ignoring // invalid sections. let mut result = CertificateResult::default(); load_pem_certs(Path::new(file!()), &mut result); assert_eq!(result.certs.len(), 0); assert!(result.errors.is_empty()); } #[test] fn from_env_missing_file() { let mut result = CertificateResult::default(); load_pem_certs(Path::new("no/such/file"), &mut result); match &first_error(&result).kind { ErrorKind::Io { inner, .. } => assert_eq!(inner.kind(), io::ErrorKind::NotFound), _ => panic!("unexpected error {:?}", result.errors), } } #[test] fn from_env_missing_dir() { let mut result = CertificateResult::default(); load_pem_certs_from_dir(Path::new("no/such/directory"), &mut result); match &first_error(&result).kind { ErrorKind::Io { inner, .. } => assert_eq!(inner.kind(), io::ErrorKind::NotFound), _ => panic!("unexpected error {:?}", result.errors), } } #[test] #[cfg(unix)] fn from_env_with_non_regular_and_empty_file() { let mut result = CertificateResult::default(); load_pem_certs(Path::new("/dev/null"), &mut result); assert_eq!(result.certs.len(), 0); assert!(result.errors.is_empty()); } #[test] #[cfg(unix)] fn from_env_bad_dir_perms() { // Create a temp dir that we can't read from. let temp_dir = tempfile::TempDir::new().unwrap(); fs::set_permissions(temp_dir.path(), Permissions::from_mode(0o000)).unwrap(); test_cert_paths_bad_perms(CertPaths { file: None, dirs: vec![temp_dir.path().into()], }) } #[test] #[cfg(unix)] fn from_env_bad_file_perms() { // Create a tmp dir with a file inside that we can't read from. let temp_dir = tempfile::TempDir::new().unwrap(); let file_path = temp_dir.path().join("unreadable.pem"); let cert_file = File::create(&file_path).unwrap(); cert_file .set_permissions(Permissions::from_mode(0o000)) .unwrap(); test_cert_paths_bad_perms(CertPaths { file: Some(file_path.clone()), dirs: vec![], }); } #[cfg(unix)] fn test_cert_paths_bad_perms(cert_paths: CertPaths) { let result = cert_paths.load(); if let (None, true) = (cert_paths.file, cert_paths.dirs.is_empty()) { panic!("only one of file or dir should be set"); }; let error = first_error(&result); match &error.kind { ErrorKind::Io { inner, .. } => { assert_eq!(inner.kind(), io::ErrorKind::PermissionDenied); inner } _ => panic!("unexpected error {:?}", result.errors), }; } fn first_error(result: &CertificateResult) -> &Error { result.errors.first().unwrap() } } rustls-rustls-native-certs-3cc7153/src/macos.rs000066400000000000000000000050751512451210300215740ustar00rootroot00000000000000use std::collections::HashMap; use pki_types::CertificateDer; use security_framework::trust_settings::{Domain, TrustSettings, TrustSettingsForCertificate}; use super::CertificateResult; pub fn load_native_certs() -> CertificateResult { // The various domains are designed to interact like this: // // "Per-user Trust Settings override locally administered // Trust Settings, which in turn override the System Trust // Settings." // // So we collect the certificates in this order; as a map of // their DER encoding to what we'll do with them. We don't // overwrite existing elements, which means User settings // trump Admin trump System, as desired. let mut result = CertificateResult::default(); let mut all_certs = HashMap::new(); for domain in &[Domain::User, Domain::Admin, Domain::System] { let ts = TrustSettings::new(*domain); let iter = match ts.iter() { Ok(iter) => iter, Err(err) => { result.os_error( err.into(), match domain { Domain::User => "failed to load user trust settings", Domain::Admin => "failed to load admin trust settings", Domain::System => "failed to load system trust settings", }, ); continue; } }; for cert in iter { let der = cert.to_der(); // If there are no specific trust settings, the default // is to trust the certificate as a root cert. Weird API but OK. // The docs say: // // "Note that an empty Trust Settings array means 'always trust this cert, // with a resulting kSecTrustSettingsResult of kSecTrustSettingsResultTrustRoot'." let trusted = match ts.tls_trust_settings_for_certificate(&cert) { Ok(trusted) => trusted.unwrap_or(TrustSettingsForCertificate::TrustRoot), Err(err) => { result.os_error(err.into(), "certificate not trusted"); continue; } }; all_certs.entry(der).or_insert(trusted); } } // Now we have all the certificates and an idea of whether // to use them. for (der, trusted) in all_certs.drain() { use TrustSettingsForCertificate::*; if let TrustRoot | TrustAsRoot = trusted { result .certs .push(CertificateDer::from(der)); } } result } rustls-rustls-native-certs-3cc7153/src/unix.rs000066400000000000000000000004101512451210300214410ustar00rootroot00000000000000use crate::{CertPaths, CertificateResult}; pub fn load_native_certs() -> CertificateResult { let likely_locations = openssl_probe::probe(); CertPaths { file: likely_locations.cert_file, dirs: likely_locations.cert_dir, } .load() } rustls-rustls-native-certs-3cc7153/src/windows.rs000066400000000000000000000020201512451210300221470ustar00rootroot00000000000000use pki_types::CertificateDer; use schannel::cert_context::ValidUses; use schannel::cert_store::CertStore; use super::CertificateResult; pub fn load_native_certs() -> CertificateResult { let mut result = CertificateResult::default(); let current_user_store = match CertStore::open_current_user("ROOT") { Ok(store) => store, Err(err) => { result.os_error(err.into(), "failed to open current user certificate store"); return result; } }; for cert in current_user_store.certs() { if usable_for_rustls(cert.valid_uses().unwrap()) && cert.is_time_valid().unwrap() { result .certs .push(CertificateDer::from(cert.to_der().to_vec())); } } result } fn usable_for_rustls(uses: ValidUses) -> bool { match uses { ValidUses::All => true, ValidUses::Oids(strs) => strs .iter() .any(|x| x == PKIX_SERVER_AUTH), } } static PKIX_SERVER_AUTH: &str = "1.3.6.1.5.5.7.3.1"; rustls-rustls-native-certs-3cc7153/tests/000077500000000000000000000000001512451210300204705ustar00rootroot00000000000000rustls-rustls-native-certs-3cc7153/tests/badssl-com-chain.pem000066400000000000000000000023611512451210300243010ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDeTCCAmGgAwIBAgIJAMnA8BB8xT6wMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNV BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNp c2NvMQ8wDQYDVQQKDAZCYWRTU0wxFTATBgNVBAMMDCouYmFkc3NsLmNvbTAeFw0y MTEwMTEyMDAzNTRaFw0yMzEwMTEyMDAzNTRaMGIxCzAJBgNVBAYTAlVTMRMwEQYD VQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ8wDQYDVQQK DAZCYWRTU0wxFTATBgNVBAMMDCouYmFkc3NsLmNvbTCCASIwDQYJKoZIhvcNAQEB BQADggEPADCCAQoCggEBAMIE7PiM7gTCs9hQ1XBYzJMY61yoaEmwIrX5lZ6xKyx2 PmzAS2BMTOqytMAPgLaw+XLJhgL5XEFdEyt/ccRLvOmULlA3pmccYYz2QULFRtMW hyefdOsKnRFSJiFzbIRMeVXk0WvoBj1IFVKtsyjbqv9u/2CVSndrOfEk0TG23U3A xPxTuW1CrbV8/q71FdIzSOciccfCFHpsKOo3St/qbLVytH5aohbcabFXRNsKEqve ww9HdFxBIuGa+RuT5q0iBikusbpJHAwnnqP7i/dAcgCskgjZjFeEU4EFy+b+a1SY QCeFxxC7c3DvaRhBB0VVfPlkPz0sw6l865MaTIbRyoUCAwEAAaMyMDAwCQYDVR0T BAIwADAjBgNVHREEHDAaggwqLmJhZHNzbC5jb22CCmJhZHNzbC5jb20wDQYJKoZI hvcNAQELBQADggEBAC4DensZ5tCTeCNJbHABYPwwqLUFOMITKOOgF3t8EqOan0CH ST1NNi4jPslWrVhQ4Y3UbAhRBdqXl5N/NFfMzDosPpOjFgtifh8Z2s3w8vdlEZzf A4mYTC8APgdpWyNgMsp8cdXQF7QOfdnqOfdnY+pfc8a8joObR7HEaeVxhJs+XL4E CLByw5FR+svkYgCbQGWIgrM1cRpmXemt6Gf/XgFNP2PdubxqDEcnWlTMk8FCBVb1 nVDSiPjYShwnWsOOshshCRCAiIBPCKPX0QwKDComQlRrgMIvddaSzFFTKPoNZjC+ CUspSNnL7V9IIHvqKlRSmu+zIpm2VJCp1xLulk8= -----END CERTIFICATE----- rustls-rustls-native-certs-3cc7153/tests/common/000077500000000000000000000000001512451210300217605ustar00rootroot00000000000000rustls-rustls-native-certs-3cc7153/tests/common/mod.rs000066400000000000000000000010751512451210300231100ustar00rootroot00000000000000use std::env; /// Cargo, at least sometimes, sets SSL_CERT_FILE and SSL_CERT_DIR internally, as /// it uses OpenSSL. So, always unset both at the beginning of a test even if /// the test doesn't use either. /// /// # Safety /// /// This is only safe if used together with `#[serial]` because calling /// `[env::remove_var()]` is unsafe if another thread is running. /// /// Note that `env::remove_var()` is scheduled to become unsafe in Rust /// Edition 2024. pub(crate) unsafe fn clear_env() { env::remove_var("SSL_CERT_FILE"); env::remove_var("SSL_CERT_DIR"); } rustls-rustls-native-certs-3cc7153/tests/compare_mozilla.rs000066400000000000000000000127301512451210300242160ustar00rootroot00000000000000//! This test attempts to verify that the set of 'native' //! certificates produced by this crate is roughly similar //! to the set of certificates in the mozilla root program //! as expressed by the `webpki-roots` crate. //! //! This is, obviously, quite a heuristic test. mod common; use std::collections::HashMap; use pki_types::Der; use ring::io::der; use serial_test::serial; use webpki::anchor_from_trusted_cert; fn stringify_x500name(subject: &Der<'_>) -> String { let mut parts = vec![]; let mut reader = untrusted::Reader::new(subject.as_ref().into()); while !reader.at_end() { let (tag, contents) = der::read_tag_and_get_value(&mut reader).unwrap(); assert!(tag == 0x31); // sequence, constructed, context=1 let mut inner = untrusted::Reader::new(contents); let pair = der::expect_tag_and_get_value(&mut inner, der::Tag::Sequence).unwrap(); let mut pair = untrusted::Reader::new(pair); let oid = der::expect_tag_and_get_value(&mut pair, der::Tag::OID).unwrap(); let (value_ty, value) = der::read_tag_and_get_value(&mut pair).unwrap(); let name = match oid.as_slice_less_safe() { [0x55, 0x04, 0x03] => "CN", [0x55, 0x04, 0x05] => "serialNumber", [0x55, 0x04, 0x06] => "C", [0x55, 0x04, 0x07] => "L", [0x55, 0x04, 0x08] => "ST", [0x55, 0x04, 0x09] => "STREET", [0x55, 0x04, 0x0a] => "O", [0x55, 0x04, 0x0b] => "OU", [0x55, 0x04, 0x11] => "postalCode", [0x55, 0x04, 0x61] => "organizationIdentifier", [0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19] => "domainComponent", [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01] => "emailAddress", _ => panic!("unhandled x500 attr {oid:?}"), }; let str_value = match value_ty { // PrintableString, UTF8String, TeletexString or IA5String 0x0c | 0x13 | 0x14 | 0x16 => std::str::from_utf8(value.as_slice_less_safe()).unwrap(), _ => panic!("unhandled x500 value type {value_ty:?}"), }; parts.push(format!("{name}={str_value}")); } parts.join(", ") } #[test] fn test_does_not_have_many_roots_unknown_by_mozilla() { let native = rustls_native_certs::load_native_certs(); let mozilla = webpki_roots::TLS_SERVER_ROOTS .iter() .map(|ta| (ta.subject_public_key_info.as_ref(), ta)) .collect::>(); let mut missing_in_moz_roots = 0; for cert in &native.certs { let cert = anchor_from_trusted_cert(cert).unwrap(); if let Some(moz) = mozilla.get(cert.subject_public_key_info.as_ref()) { assert_eq!(cert.subject, moz.subject, "subjects differ for public key"); } else { println!( "Native anchor {:?} is missing from mozilla set", stringify_x500name(&cert.subject) ); missing_in_moz_roots += 1; } } #[cfg(windows)] let threshold = 2.0; // no more than 160% extra roots; windows CI vm has lots of extra roots #[cfg(target_os = "macos")] let threshold = 0.6; // macOS has a bunch of extra roots, too #[cfg(not(any(windows, target_os = "macos")))] let threshold = 0.5; // no more than 50% extra roots let diff = (missing_in_moz_roots as f64) / (mozilla.len() as f64); println!("mozilla: {:?}", mozilla.len()); println!("native: {:?}", native.certs.len()); println!( "{missing_in_moz_roots:?} anchors present in native set but not mozilla ({}%)", diff * 100. ); assert!(diff < threshold, "too many unknown roots"); } #[test] fn test_contains_most_roots_known_by_mozilla() { let native = rustls_native_certs::load_native_certs(); let mut native_map = HashMap::new(); for anchor in &native.certs { let cert = anchor_from_trusted_cert(anchor).unwrap(); let spki = cert.subject_public_key_info.as_ref(); native_map.insert(spki.to_owned(), anchor); } let mut missing_in_native_roots = 0; let mozilla = webpki_roots::TLS_SERVER_ROOTS; for cert in mozilla { if !native_map.contains_key(cert.subject_public_key_info.as_ref()) { println!( "Mozilla anchor {:?} is missing from native set", stringify_x500name(&cert.subject) ); missing_in_native_roots += 1; } } #[cfg(windows)] let threshold = 0.95; // no more than 95% extra roots; windows misses *many* roots #[cfg(target_os = "macos")] let threshold = 0.6; // no more than 60% extra roots; macOS has a bunch of extra roots, too #[cfg(not(any(windows, target_os = "macos")))] let threshold = 0.5; // no more than 50% extra roots let diff = (missing_in_native_roots as f64) / (mozilla.len() as f64); println!("mozilla: {:?}", mozilla.len()); println!("native: {:?}", native.certs.len()); println!( "{missing_in_native_roots:?} anchors present in mozilla set but not native ({}%)", diff * 100. ); assert!(diff < threshold, "too many missing roots"); } #[test] #[serial] fn util_list_certs() { unsafe { // SAFETY: safe because of #[serial] common::clear_env(); } let native = rustls_native_certs::load_native_certs(); for (i, cert) in native.certs.iter().enumerate() { let cert = anchor_from_trusted_cert(cert).unwrap(); println!("cert[{i}] = {}", stringify_x500name(&cert.subject)); } } rustls-rustls-native-certs-3cc7153/tests/smoketests.rs000066400000000000000000000205741512451210300232470ustar00rootroot00000000000000mod common; use std::io::{ErrorKind, Read, Write}; use std::net::TcpStream; #[cfg(unix)] use std::os::unix::fs::symlink; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::{env, panic}; // #[serial] is used on all these tests to run them sequentially. If they're run in parallel, // the global env var configuration in the env var test interferes with the others. use serial_test::serial; /// Check if connection to site works using native roots. /// /// Yields an Err if and only if there is an issue connecting that /// appears to be due to a certificate problem. /// /// # Panics /// /// Panics on errors unrelated to the TLS connection like errors during /// certificate loading, or connecting via TCP. fn check_site(domain: &str) -> Result<(), ()> { check_site_with_roots(domain, rustls_native_certs::load_native_certs().unwrap()) } /// Check if connection to site works using the given roots. /// /// Yields an Err if and only if there is an issue connecting that /// appears to be due to a certificate problem. /// /// # Panics /// /// Panics on errors unrelated to the TLS connection like connecting via TCP. fn check_site_with_roots( domain: &str, root_certs: Vec>, ) -> Result<(), ()> { let mut roots = rustls::RootCertStore::empty(); roots.add_parsable_certificates(root_certs); let config = rustls::ClientConfig::builder() .with_root_certificates(roots) .with_no_client_auth(); let mut conn = rustls::ClientConnection::new( Arc::new(config), pki_types::ServerName::try_from(domain) .unwrap() .to_owned(), ) .unwrap(); let mut sock = TcpStream::connect(format!("{domain}:443")).unwrap(); let mut tls = rustls::Stream::new(&mut conn, &mut sock); let result = tls.write_all( format!( "GET / HTTP/1.1\r\n\ Host: {domain}\r\n\ Connection: close\r\n\ Accept-Encoding: identity\r\n\ \r\n" ) .as_bytes(), ); match result { Ok(()) => (), Err(e) if e.kind() == ErrorKind::InvalidData => return Err(()), // TLS error Err(e) => panic!("{e}"), } let mut plaintext = [0u8; 1024]; let len = tls.read(&mut plaintext).unwrap(); assert!(plaintext[..len].starts_with(b"HTTP/1.1 ")); // or whatever Ok(()) } #[test] #[serial] #[ignore] fn google() { unsafe { // SAFETY: safe because of #[serial] common::clear_env(); } check_site("google.com").unwrap(); } #[test] #[serial] #[ignore] fn amazon() { unsafe { // SAFETY: safe because of #[serial] common::clear_env(); } check_site("amazon.com").unwrap(); } #[test] #[serial] #[ignore] fn facebook() { unsafe { // SAFETY: safe because of #[serial] common::clear_env(); } check_site("facebook.com").unwrap(); } #[test] #[serial] #[ignore] fn netflix() { unsafe { // SAFETY: safe because of #[serial] common::clear_env(); } check_site("netflix.com").unwrap(); } #[test] #[serial] #[ignore] fn ebay() { unsafe { // SAFETY: safe because of #[serial] common::clear_env(); } check_site("ebay.com").unwrap(); } #[test] #[serial] #[ignore] fn apple() { unsafe { // SAFETY: safe because of #[serial] common::clear_env(); } check_site("apple.com").unwrap(); } #[test] #[serial] #[ignore] fn badssl_with_env() { unsafe { // SAFETY: safe because of #[serial] common::clear_env(); } // Self-signed certs should never be trusted by default: assert!(check_site("self-signed.badssl.com").is_err()); // But they should be trusted if SSL_CERT_FILE is set: env::set_var( "SSL_CERT_FILE", // The CA cert, downloaded directly from the site itself: PathBuf::from("./tests/badssl-com-chain.pem"), ); check_site("self-signed.badssl.com").unwrap(); } #[test] #[serial] #[ignore] fn badssl_with_dir_from_env() { unsafe { // SAFETY: safe because of #[serial] common::clear_env(); } let temp_dir = tempfile::TempDir::new().unwrap(); let original = Path::new("tests/badssl-com-chain.pem") .canonicalize() .unwrap(); let link1 = temp_dir.path().join("5d30f3c5.3"); #[cfg(unix)] let link2 = temp_dir.path().join("fd3003c5.0"); env::set_var( "SSL_CERT_DIR", // The CA cert, downloaded directly from the site itself: temp_dir.path(), ); assert!(check_site("self-signed.badssl.com").is_err()); // OpenSSL uses symlinks too. So, use one for testing too, if possible. #[cfg(unix)] symlink(original, link1).unwrap(); #[cfg(not(unix))] std::fs::copy(original, link1).unwrap(); // Dangling symlink #[cfg(unix)] symlink("/a/path/which/does/not/exist/hopefully", link2).unwrap(); check_site("self-signed.badssl.com").unwrap(); } #[test] #[serial] #[ignore] fn ssl_cert_dir_multiple_paths_are_respected() { unsafe { // SAFETY: safe because of #[serial] common::clear_env(); } // Create 2 temporary directories let temp_dir1 = tempfile::TempDir::new().unwrap(); let temp_dir2 = tempfile::TempDir::new().unwrap(); // Copy the certificate to the 2nd dir, leaving the 1st one // empty. let original = Path::new("tests/badssl-com-chain.pem") .canonicalize() .unwrap(); let cert = temp_dir2.path().join("5d30f3c5.3"); std::fs::copy(original, cert).unwrap(); let list_sep = if cfg!(windows) { ';' } else { ':' }; let value = format!( "{}{}{}", temp_dir1.path().display(), list_sep, temp_dir2.path().display() ); env::set_var("SSL_CERT_DIR", value); check_site("self-signed.badssl.com").unwrap(); } #[test] #[serial] #[ignore] fn ssl_cert_dir_non_hash_based_name() { unsafe { // SAFETY: safe because of #[serial] common::clear_env(); } // Create temporary directory let temp_dir = tempfile::TempDir::new().unwrap(); // Copy the certificate to the dir let original = Path::new("tests/badssl-com-chain.pem") .canonicalize() .unwrap(); let cert = temp_dir.path().join("test.pem"); std::fs::copy(original, cert).unwrap(); env::set_var( "SSL_CERT_DIR", // The CA cert, downloaded directly from the site itself: temp_dir.path(), ); check_site("self-signed.badssl.com").unwrap(); } #[test] #[serial] #[ignore] #[cfg(target_os = "linux")] fn google_with_dir_but_broken_file() { unsafe { // SAFETY: safe because of #[serial] common::clear_env(); } env::set_var("SSL_CERT_DIR", "/etc/ssl/certs"); env::set_var("SSL_CERT_FILE", "not-exist"); let res = rustls_native_certs::load_native_certs(); let first_err = res.errors.first().unwrap().to_string(); dbg!(&first_err); assert!(first_err.contains("from file")); assert!(first_err.contains("not-exist")); check_site_with_roots("google.com", res.certs).unwrap(); } #[test] #[serial] #[ignore] #[cfg(target_os = "linux")] fn google_with_file_but_broken_dir() { unsafe { // SAFETY: safe because of #[serial] common::clear_env(); } env::set_var("SSL_CERT_DIR", "/not-exist"); env::set_var("SSL_CERT_FILE", "/etc/ssl/certs/ca-certificates.crt"); let res = rustls_native_certs::load_native_certs(); let first_err = res.errors.first().unwrap().to_string(); dbg!(&first_err); assert!(first_err.contains("opening directory")); assert!(first_err.contains("/not-exist")); check_site_with_roots("google.com", res.certs).unwrap(); } #[test] #[serial] #[ignore] #[cfg(target_os = "linux")] fn nothing_works_with_broken_file_and_dir() { unsafe { // SAFETY: safe because of #[serial] common::clear_env(); } env::set_var("SSL_CERT_DIR", "/not-exist"); env::set_var("SSL_CERT_FILE", "not-exist"); let res = rustls_native_certs::load_native_certs(); assert_eq!(res.errors.len(), 2); let first_err = res.errors.first().unwrap().to_string(); dbg!(&first_err); assert!(first_err.contains("from file")); assert!(first_err.contains("not-exist")); let second_err = res.errors.get(1).unwrap().to_string(); dbg!(&second_err); assert!(second_err.contains("opening directory")); assert!(second_err.contains("/not-exist")); }