rpm-sequoia-1.8.0/.cargo/config.toml000064400000000000000000000002561046102023000154120ustar 00000000000000[target.'cfg(all())'] # Note: if the RUSTFLAGS environment variable is set, this will be # ignored. rustflags = [ "-Aunused-parens", "-Aunused-macros", ] rpm-sequoia-1.8.0/.cargo_vcs_info.json0000644000000001360000000000100133040ustar { "git": { "sha1": "29545a41b24cb09e37aa85722a333e0924166d13" }, "path_in_vcs": "" }rpm-sequoia-1.8.0/.ci/all_commits.sh000075500000000000000000000022711046102023000154110ustar 00000000000000#!/usr/bin/env bash # Test all commits on this branch but the last one. # # Used in the all_commits ci job to ensure all commits build # and tests pass at least for the sequoia-openpgp crate. # NOTE: under gitlab's Settings, "CI/CD", General Pipelines ensure # that the "git shallow clone" setting is set to 0. Otherwise other # branch are not fetched. set -e set -x # Use dummy identity to make git rebase happy. git config user.name "C.I. McTestface" git config user.email "ci.mctestface@example.com" # Make sure the gitlab project is configured. if ! git describe --all origin/main then echo "origin/main is not present. Configure the gitlab project (see .ci/all_commits.sh)." exit 1 fi # If the previous commit already is on main we're done. git merge-base --is-ancestor HEAD~ origin/main && echo "All commits tested already" && exit 0 # Leave out the last commit - it has already been checked. git checkout HEAD~ git status git rebase origin/main \ --exec 'echo ===; echo ===; echo ===; git log -n 1;' \ --exec 'cargo test --all' && echo "All commits passed tests" && exit 0 # The rebase failed - probably because a test failed. git rebase --abort; exit 1 rpm-sequoia-1.8.0/.codespellrc000064400000000000000000000004051046102023000143730ustar 00000000000000[codespell] skip = *.bin,*.gpg,*.pgp,./.git,data,highlight.js,*/target,Makefile,*.html,*/cargo,*.xml,*.xmlv2,Cargo.lock, ignore-words-list = crate,ede,iff,mut,nd,te,uint,KeyServer,keyserver,Keyserver,keyservers,Keyservers,keypair,keypairs,KeyPair,fpr,dedup,ba, rpm-sequoia-1.8.0/.github/workflows/authenticate-commits.yml000064400000000000000000000005141046102023000223430ustar 00000000000000name: authenticate-commits on: pull_request: types: [opened, reopened, synchronize] jobs: authenticate-commits: runs-on: ubuntu-latest permissions: contents: read pull-requests: write issues: write steps: - name: Authenticating commits uses: sequoia-pgp/authenticate-commits@v1 rpm-sequoia-1.8.0/.github/workflows/ci.yml000064400000000000000000000122531046102023000166120ustar 00000000000000name: ci on: push: env: CARGO_TERM_COLOR: always jobs: codespell: name: Codespell runs-on: ubuntu-22.04 steps: - name: Setup | Checkout rpm-sequoia uses: actions/checkout@v2 - name: Setup | Dependencies run: sudo apt update && sudo apt install codespell - name: Codespell run: codespell --version && codespell --config .codespellrc --summary compile: name: Compile runs-on: ubuntu-latest steps: - name: Setup | Checkout rpm-sequoia uses: actions/checkout@v2 with: fetch-depth: 0 - name: Setup | Build Cache rpm-sequoia uses: actions/cache@v3 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Setup | Dependencies rpm-sequoia run: sudo apt update && sudo apt install cargo clang git nettle-dev pkg-config libssl-dev - name: Build | Compile rpm-sequoia run: cargo build - name: Build | Test rpm-sequoia run: cargo test - name: Build | Doc rpm-sequoia run: cargo doc --no-deps all_commits: name: All Commits runs-on: ubuntu-latest needs: ["Compile"] steps: - name: Setup | Checkout rpm-sequoia uses: actions/checkout@v2 with: fetch-depth: 0 - name: Setup | Build Cache rpm-sequoia uses: actions/cache@v3 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Setup | Dependencies rpm-sequoia run: sudo apt update && sudo apt install cargo clang git nettle-dev pkg-config libssl-dev - name: Build | Compile rpm-sequoia run: cargo build - name: Build | Test other commits run: .ci/all_commits.sh rpm: name: RPM runs-on: ubuntu-latest needs: ["Compile"] steps: - name: Setup | Checkout rpm-sequoia uses: actions/checkout@v2 with: fetch-depth: 0 - name: Setup | Build Cache rpm-sequoia uses: actions/cache@v3 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Setup | Dependencies rpm-sequoia run: sudo apt update && sudo apt install cargo clang git nettle-dev pkg-config libssl-dev - name: Build | Compile rpm-sequoia run: cargo build - name: Setup | rpm Dependencies run: sudo apt install automake autoconf autopoint gettext libtool tar zlib1g-dev libpopt-dev libsqlite3-dev liblua5.4-dev fakechroot libarchive-dev libmagic-dev - name: Setup | Checkout rpm uses: actions/checkout@v2 with: repository: rpm-software-management/rpm.git ref: rpm-4.18.x fetch-depth: 1 path: rpm-pristine - name: Setup | Build Cache rpm uses: actions/cache@v3 with: path: | rpm/ rpm-build/ key: ${{ runner.os }}-rpm-${{ hashFiles('rpm-pristine/.git/HEAD', 'rpm-pristine/.git/refs/heads/master') }} - name: Test | rpm run: | export PKG_CONFIG_PATH=$(pwd)/target/debug if ! test -e $PKG_CONFIG_PATH/rpm-sequoia-uninstalled.pc then echo "$PKG_CONFIG_PATH/rpm-sequoia-uninstalled.pc is missing. Did you build librpm-sequoia?" exit 1 fi export LD_LIBRARY_PATH=$PKG_CONFIG_PATH if ! test -e $LD_LIBRARY_PATH/librpm_sequoia.so then echo "$LD_LIBRARY_PATH/librpm_sequoia.so is missing. Did you build librpm-sequoia?" exit 1 fi echo "::group::configure" # If rpm doesn't exist, then we don't have a cache of an # rpm build. if ! test -e rpm then cp -a rpm-pristine rpm cd rpm autoreconf -is cd .. mkdir -p rpm-build cd rpm-build ../rpm/configure --prefix=/ --with-crypto=sequoia else cd rpm-build fi echo "::endgroup::" echo "::group::make" make echo "::endgroup::" echo "::group::make check" cd tests if ! make check TESTSUITEFLAGS="-k OpenPGP -k signature -k rpmkeys -k digest" then echo "::endgroup::" for log in rpmtests.dir/*/rpmtests.log do echo "::group::$log" cat $log || true echo "::endgroup::" done exit 1 else echo "::endgroup::" fi rpm-sequoia-1.8.0/.github/workflows/fast-forward.yml000064400000000000000000000007761046102023000206250ustar 00000000000000name: fast-forward on: issue_comment: types: [created, edited] jobs: fast-forward: # Only run if the comment contains the /fast-forward command. if: ${{ contains(github.event.comment.body, '/fast-forward') && github.event.issue.pull_request }} runs-on: ubuntu-latest permissions: contents: write pull-requests: write issues: write steps: - name: Fast forwarding uses: sequoia-pgp/fast-forward@main with: merge: true rpm-sequoia-1.8.0/.github/workflows/pull-request.yml000064400000000000000000000007201046102023000206550ustar 00000000000000name: pull-request on: pull_request: types: [opened, reopened, synchronize] jobs: check-fast-forward: runs-on: ubuntu-latest permissions: contents: read # We appear to need write permission for both pull-requests and # issues in order to post a comment to a pull request. pull-requests: write issues: write steps: - name: Checking if fast forwarding is possible uses: sequoia-pgp/fast-forward@main rpm-sequoia-1.8.0/Cargo.lock0000644000001712500000000000100112650ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aead" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", "generic-array", ] [[package]] name = "aes" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", "cpufeatures", "zeroize", ] [[package]] name = "aes-gcm" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", "cipher", "ctr", "ghash", "subtle", ] [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "android-tzdata" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "anstyle" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anyhow" version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "argon2" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" dependencies = [ "base64ct", "blake2", "cpufeatures", "password-hash", ] [[package]] name = "ascii-canvas" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" dependencies = [ "term", ] [[package]] name = "assert_cmd" version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" dependencies = [ "anstyle", "bstr", "doc-comment", "libc", "predicates", "predicates-core", "predicates-tree", "wait-timeout", ] [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bindgen" version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ "bitflags", "cexpr", "clang-sys", "itertools 0.13.0", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", "syn", ] [[package]] name = "bit-set" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "blake2" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ "digest", ] [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "block-padding" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ "generic-array", ] [[package]] name = "blowfish" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" dependencies = [ "byteorder", "cipher", ] [[package]] name = "botan" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d4c7647d67c53194fa0740404c6c508880aef2bfe99a9868dbb4b86f090377" dependencies = [ "botan-sys", ] [[package]] name = "botan-sys" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04285fa0c094cc9961fe435b1b279183db9394844ad82ce483aa6196c0e6da38" [[package]] name = "bstr" version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", "regex-automata", "serde", ] [[package]] name = "buffered-reader" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db26bf1f092fd5e05b5ab3be2f290915aeb6f3f20c4e9f86ce0f07f336c2412f" dependencies = [ "libc", ] [[package]] name = "bumpalo" version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "camellia" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3264e2574e9ef2b53ce6f536dea83a69ac0bc600b762d1523ff83fe07230ce30" dependencies = [ "byteorder", "cipher", ] [[package]] name = "cast5" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b07d673db1ccf000e90f54b819db9e75a8348d6eb056e9b8ab53231b7a9911" dependencies = [ "cipher", ] [[package]] name = "cc" version = "1.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" dependencies = [ "shlex", ] [[package]] name = "cdylib-link-lines" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98eabef08bbdf5afd0b9c0cabb1ac335f7c70447ef095eed85dffd9628b20bc" [[package]] name = "cexpr" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ "nom", ] [[package]] name = "cfb-mode" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "738b8d467867f80a71351933f70461f5b56f24d5c93e0cf216e59229c968d330" dependencies = [ "cipher", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", "windows-link", ] [[package]] name = "cipher" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", "zeroize", ] [[package]] name = "clang-sys" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", "libloading", ] [[package]] name = "cmac" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8543454e3c3f5126effff9cd44d562af4e31fb8ce1cc0d3dcd8f084515dbc1aa" dependencies = [ "cipher", "dbl", "digest", ] [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[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.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] [[package]] name = "crunchy" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-bigint" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", "rand_core", "subtle", "zeroize", ] [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "rand_core", "typenum", ] [[package]] name = "ctr" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ "cipher", ] [[package]] name = "curve25519-dalek" version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest", "fiat-crypto", "rustc_version", "subtle", "zeroize", ] [[package]] name = "curve25519-dalek-derive" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "dbl" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd2735a791158376708f9347fe8faba9667589d82427ef3aed6794a8981de3d9" dependencies = [ "generic-array", ] [[package]] name = "der" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "pem-rfc7468", "zeroize", ] [[package]] name = "des" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" dependencies = [ "cipher", ] [[package]] name = "difflib" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "const-oid", "crypto-common", "subtle", ] [[package]] name = "dirs-next" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ "cfg-if", "dirs-sys-next", ] [[package]] name = "dirs-sys-next" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", "redox_users", "winapi", ] [[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 = "doc-comment" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "dsa" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" dependencies = [ "digest", "num-bigint-dig", "num-traits", "pkcs8", "rfc6979", "sha2", "signature", "zeroize", ] [[package]] name = "dyn-clone" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" [[package]] name = "eax" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9954fabd903b82b9d7a68f65f97dc96dd9ad368e40ccc907a7c19d53e6bfac28" dependencies = [ "aead", "cipher", "cmac", "ctr", "subtle", ] [[package]] name = "ecb" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a8bfa975b1aec2145850fcaa1c6fe269a16578c44705a532ae3edc92b8881c7" dependencies = [ "cipher", ] [[package]] name = "ecdsa" version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", "digest", "elliptic-curve", "rfc6979", "signature", "spki", ] [[package]] name = "ed25519" version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8", "signature", ] [[package]] name = "ed25519-dalek" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", "rand_core", "serde", "sha2", "subtle", "zeroize", ] [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elliptic-curve" version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", "digest", "ff", "generic-array", "group", "hkdf", "pem-rfc7468", "pkcs8", "rand_core", "sec1", "subtle", "zeroize", ] [[package]] name = "ena" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" dependencies = [ "log", ] [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", "windows-sys", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "ff" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ "rand_core", "subtle", ] [[package]] name = "fiat-crypto" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "fixedbitset" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ "foreign-types-shared", ] [[package]] name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", "zeroize", ] [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] [[package]] name = "getrandom" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", ] [[package]] name = "ghash" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" dependencies = [ "opaque-debug", "polyval", ] [[package]] name = "glob" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", "rand_core", "subtle", ] [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "hkdf" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ "hmac", ] [[package]] name = "hmac" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ "digest", ] [[package]] name = "iana-time-zone" version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "log", "wasm-bindgen", "windows-core", ] [[package]] name = "iana-time-zone-haiku" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ "cc", ] [[package]] name = "icu_collections" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ "displaydoc", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_locid" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", "litemap", "tinystr", "writeable", "zerovec", ] [[package]] name = "icu_locid_transform" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" dependencies = [ "displaydoc", "icu_locid", "icu_locid_transform_data", "icu_provider", "tinystr", "zerovec", ] [[package]] name = "icu_locid_transform_data" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", "utf16_iter", "utf8_iter", "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ "displaydoc", "icu_collections", "icu_locid_transform", "icu_properties_data", "icu_provider", "tinystr", "zerovec", ] [[package]] name = "icu_properties_data" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ "displaydoc", "icu_locid", "icu_provider_macros", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_provider_macros" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "idea" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "075557004419d7f2031b8bb7f44bb43e55a83ca7b63076a8fb8fe75753836477" dependencies = [ "cipher", ] [[package]] name = "idna" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ "idna_adapter", "smallvec", "utf8_iter", ] [[package]] name = "idna_adapter" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ "icu_normalizer", "icu_properties", ] [[package]] name = "indexmap" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "inout" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "block-padding", "generic-array", ] [[package]] name = "itertools" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ "either", ] [[package]] name = "itertools" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] [[package]] name = "js-sys" version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "keccak" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] [[package]] name = "lalrpop" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" dependencies = [ "ascii-canvas", "bit-set", "ena", "itertools 0.11.0", "lalrpop-util", "petgraph", "regex", "regex-syntax", "string_cache", "term", "tiny-keccak", "unicode-xid", "walkdir", ] [[package]] name = "lalrpop-util" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ "regex-automata", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ "spin", ] [[package]] name = "libc" version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libloading" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets", ] [[package]] name = "libm" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libredox" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags", "libc", ] [[package]] name = "linux-raw-sys" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" [[package]] name = "litemap" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "lock_api" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "md-5" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", "digest", ] [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memsec" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c797b9d6bb23aab2fc369c65f871be49214f5c759af65bde26ffaaa2b646b492" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nettle" version = "7.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44e6ff4a94e5d34a1fd5abbd39418074646e2fa51b257198701330f22fcd6936" dependencies = [ "getrandom 0.2.15", "libc", "nettle-sys", "thiserror 1.0.69", "typenum", ] [[package]] name = "nettle-sys" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61a3f5406064d310d59b1a219d3c5c9a49caf4047b6496032e3f930876488c34" dependencies = [ "bindgen", "cc", "libc", "pkg-config", "tempfile", "vcpkg", ] [[package]] name = "new_debug_unreachable" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[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-dig" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" dependencies = [ "byteorder", "lazy_static", "libm", "num-integer", "num-iter", "num-traits", "rand", "smallvec", "zeroize", ] [[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-iter" version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", ] [[package]] name = "ocb3" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c196e0276c471c843dd5777e7543a36a298a4be942a2a688d8111cd43390dedb" dependencies = [ "aead", "cipher", "ctr", "subtle", ] [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "opaque-debug" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ "bitflags", "cfg-if", "foreign-types", "libc", "once_cell", "openssl-macros", "openssl-sys", ] [[package]] name = "openssl-macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "openssl-sys" version = "0.9.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", "libc", "pkg-config", "vcpkg", ] [[package]] name = "p256" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ "ecdsa", "elliptic-curve", "primeorder", "sha2", ] [[package]] name = "p384" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" dependencies = [ "ecdsa", "elliptic-curve", "primeorder", "sha2", ] [[package]] name = "p521" version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" dependencies = [ "base16ct", "ecdsa", "elliptic-curve", "primeorder", "rand_core", "sha2", ] [[package]] name = "parking_lot" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-targets", ] [[package]] name = "password-hash" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" dependencies = [ "base64ct", "rand_core", "subtle", ] [[package]] name = "pem-rfc7468" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" dependencies = [ "base64ct", ] [[package]] name = "petgraph" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", "indexmap", ] [[package]] name = "phf_shared" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ "siphasher", ] [[package]] name = "pkcs1" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ "der", "pkcs8", "spki", ] [[package]] name = "pkcs8" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", "spki", ] [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "polyval" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", "opaque-debug", "universal-hash", ] [[package]] name = "ppv-lite86" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] [[package]] name = "precomputed-hash" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "predicates" version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" dependencies = [ "anstyle", "difflib", "predicates-core", ] [[package]] name = "predicates-core" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" [[package]] name = "predicates-tree" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" dependencies = [ "predicates-core", "termtree", ] [[package]] name = "primeorder" version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ "elliptic-curve", ] [[package]] name = "proc-macro2" version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom 0.2.15", ] [[package]] name = "redox_syscall" version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" dependencies = [ "bitflags", ] [[package]] name = "redox_users" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom 0.2.15", "libredox", "thiserror 1.0.69", ] [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rfc6979" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ "hmac", "subtle", ] [[package]] name = "ripemd" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ "digest", ] [[package]] name = "rpm-sequoia" version = "1.8.0" dependencies = [ "anyhow", "assert_cmd", "cdylib-link-lines", "chrono", "lazy_static", "libc", "sequoia-openpgp", "sequoia-policy-config", "thiserror 1.0.69", ] [[package]] name = "rsa" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" dependencies = [ "const-oid", "digest", "num-bigint-dig", "num-integer", "num-traits", "pkcs1", "pkcs8", "rand_core", "signature", "spki", "subtle", "zeroize", ] [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustix" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "rustversion" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[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 = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", "generic-array", "pkcs8", "subtle", "zeroize", ] [[package]] name = "semver" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "sequoia-openpgp" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "015e5fc3d023418b9db98ca9a7f3e90b305872eeafe5ca45c5c32b5eb335c1e8" dependencies = [ "aes", "aes-gcm", "anyhow", "argon2", "base64", "block-padding", "blowfish", "botan", "buffered-reader", "camellia", "cast5", "cfb-mode", "chrono", "cipher", "des", "digest", "dsa", "dyn-clone", "eax", "ecb", "ecdsa", "ed25519", "ed25519-dalek", "getrandom 0.2.15", "hkdf", "idea", "idna", "lalrpop", "lalrpop-util", "libc", "md-5", "memsec", "nettle", "num-bigint-dig", "ocb3", "openssl", "openssl-sys", "p256", "p384", "p521", "rand", "rand_core", "regex", "regex-syntax", "ripemd", "rsa", "sha1collisiondetection", "sha2", "sha3", "thiserror 2.0.12", "twofish", "typenum", "win-crypto-ng", "winapi", "x25519-dalek", "xxhash-rust", ] [[package]] name = "sequoia-policy-config" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e016b708d64857b6a97e1a331d9471b73e30ed450d247628e1a0ce236b1e597" dependencies = [ "anyhow", "chrono", "sequoia-openpgp", "serde", "thiserror 1.0.69", "toml", ] [[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "sha1collisiondetection" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f606421e4a6012877e893c399822a4ed4b089164c5969424e1b9d1e66e6964b" dependencies = [ "const-oid", "digest", "generic-array", ] [[package]] name = "sha2" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "sha3" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ "digest", "keccak", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signature" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", "rand_core", ] [[package]] name = "siphasher" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "smallvec" version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "spki" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", ] [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "string_cache" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", "parking_lot", "phf_shared", "precomputed-hash", ] [[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.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "synstructure" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tempfile" version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ "fastrand", "getrandom 0.3.2", "once_cell", "rustix", "windows-sys", ] [[package]] name = "term" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" dependencies = [ "dirs-next", "rustversion", "winapi", ] [[package]] name = "termtree" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ "thiserror-impl 2.0.12", ] [[package]] name = "thiserror-impl" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "thiserror-impl" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tiny-keccak" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" dependencies = [ "crunchy", ] [[package]] name = "tinystr" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", ] [[package]] name = "toml" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] [[package]] name = "twofish" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a78e83a30223c757c3947cd144a31014ff04298d8719ae10d03c31c0448c8013" dependencies = [ "cipher", ] [[package]] name = "typenum" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "universal-hash" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ "crypto-common", "subtle", ] [[package]] name = "utf16_iter" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wait-timeout" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" dependencies = [ "libc", ] [[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 = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] [[package]] name = "wasm-bindgen" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ "unicode-ident", ] [[package]] name = "win-crypto-ng" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99abfb435a71e54ab2971d8d8c32f1a7e006cdbf527f71743b1d45b93517bb92" dependencies = [ "cipher", "doc-comment", "rand_core", "winapi", "zeroize", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ "windows-implement", "windows-interface", "windows-link", "windows-result", "windows-strings", ] [[package]] name = "windows-implement" version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "windows-interface" version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "windows-link" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" [[package]] name = "windows-result" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" dependencies = [ "windows-link", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[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-rt" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags", ] [[package]] name = "write16" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] name = "writeable" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "x25519-dalek" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek", "rand_core", "zeroize", ] [[package]] name = "xxhash-rust" version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" [[package]] name = "yoke" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", "yoke-derive", "zerofrom", ] [[package]] name = "yoke-derive" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zerocopy" version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "zerofrom" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "zerovec" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ "yoke", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", "syn", ] rpm-sequoia-1.8.0/Cargo.toml0000644000000040730000000000100113060ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.79" name = "rpm-sequoia" version = "1.8.0" authors = ["Neal H. Walfield "] build = "build.rs" autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "An implementation of the RPM PGP interface using Sequoia." homepage = "https://sequoia-pgp.org/" readme = "README.md" keywords = [ "cryptography", "openpgp", "pgp", "signing", ] categories = [ "cryptography", "authentication", ] license = "LGPL-2.0-or-later" repository = "https://github.com/rpm-software-management/rpm-sequoia" [badges.maintenance] status = "actively-developed" [features] crypto-botan = ["sequoia-openpgp/crypto-botan"] crypto-botan2 = ["sequoia-openpgp/crypto-botan2"] crypto-cng = ["sequoia-openpgp/crypto-cng"] crypto-nettle = ["sequoia-openpgp/crypto-nettle"] crypto-openssl = ["sequoia-openpgp/crypto-openssl"] crypto-rust = ["sequoia-openpgp/crypto-rust"] default = ["crypto-nettle"] [lib] name = "rpm_sequoia" crate-type = ["cdylib"] path = "src/lib.rs" [[test]] name = "symbols" path = "tests/symbols.rs" [dependencies.anyhow] version = "1" [dependencies.chrono] version = "0.4" features = ["std"] default-features = false [dependencies.lazy_static] version = "1" [dependencies.libc] version = "0.2" [dependencies.sequoia-openpgp] version = "2" default-features = false [dependencies.sequoia-policy-config] version = "0.8" [dependencies.thiserror] version = "1" [dev-dependencies.assert_cmd] version = "2.0" [build-dependencies.anyhow] version = "1" [build-dependencies.cdylib-link-lines] version = "0.1.4" rpm-sequoia-1.8.0/Cargo.toml.orig000064400000000000000000000027561046102023000147750ustar 00000000000000[package] name = "rpm-sequoia" description = "An implementation of the RPM PGP interface using Sequoia." version = "1.8.0" authors = ["Neal H. Walfield "] homepage = "https://sequoia-pgp.org/" repository = "https://github.com/rpm-software-management/rpm-sequoia" readme = "README.md" keywords = ["cryptography", "openpgp", "pgp", "signing"] categories = ["cryptography", "authentication"] license = "LGPL-2.0-or-later" edition = "2021" rust-version = "1.79" build = "build.rs" [badges] maintenance = { status = "actively-developed" } [dependencies] anyhow = "1" chrono = { version = "0.4", default-features = false, features = [ "std" ] } lazy_static = "1" libc = "0.2" sequoia-openpgp = { version = "2", default-features = false } sequoia-policy-config = "0.8" thiserror = "1" [build-dependencies] anyhow = "1" cdylib-link-lines = "0.1.4" [dev-dependencies] assert_cmd = "2.0" [lib] crate-type = ["cdylib"] [features] # To use a different cryptographic backend, e.g., OpenSSL, do: # # cargo build --release --no-default-features --features crypto-openssl # We explicitly do not want to enable Sequoia's decompression support. # Hence we only select a crypto backend. default = ["crypto-nettle"] crypto-nettle = ["sequoia-openpgp/crypto-nettle"] crypto-rust = ["sequoia-openpgp/crypto-rust"] crypto-cng = ["sequoia-openpgp/crypto-cng"] crypto-openssl = ["sequoia-openpgp/crypto-openssl"] crypto-botan = ["sequoia-openpgp/crypto-botan"] crypto-botan2 = ["sequoia-openpgp/crypto-botan2"] rpm-sequoia-1.8.0/LICENSE.txt000064400000000000000000000627341046102023000137330ustar 00000000000000rpm-sequoia is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. rpm-sequoia is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. --- GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! rpm-sequoia-1.8.0/README.md000064400000000000000000000117341046102023000133610ustar 00000000000000This library provides an implementation of the [rpm]'s [pgp interface] using [Sequoia]. [rpm]: https://github.com/rpm-software-management/rpm [pgp interface]: https://github.com/rpm-software-management/rpm/blob/master/include/rpm/rpmpgp.h [Sequoia]: https://sequoia-pgp.org # Configuration This library's [crypto policy] can be customized. It finds the configuration file by checking the following in turn: - the `RPM_SEQUOIA_CRYPTO_POLICY` environment variable, - `/etc/crypto-policies/back-ends/rpm-sequoia.config`, - the `SEQUOIA_CRYPTO_POLICY` environment variable, and finally, - `/etc/crypto-policies/back-ends/sequoia.config`. Only the first configuration file that is present is used. If an environment is set to the empty string, then an empty configuration file is used. That is, the default policy is used. Thus, if `RPM_SEQUOIA_CRYPTO_POLICY` is not set, and `/etc/crypto-policies/back-ends/rpm-sequoia.config`, the latter will be used. In this case, `SEQUOIA_CRYPTO_POLICY` and `/etc/crypto-policies/back-ends/sequoia.config` will be completely ignored. Refer to the [Fedora Crypto Policy] project for information about the crypto policy. [crypto policy]: https://docs.rs/sequoia-policy-config/latest/sequoia_policy_config/ [Sequoia's default policy]: https://docs.sequoia-pgp.org/sequoia_openpgp/policy/struct.StandardPolicy.html [Fedora Crypto Policy]: https://gitlab.com/redhat-crypto/fedora-crypto-policies/ # Building To build, you need [rustc] (version 1.73 or later), cargo, and [nettle-devel], which is the cryptographic library that Sequoia uses by default. [rustc]: https://packages.fedoraproject.org/pkgs/rust/rust/ [nettle-devel]: https://packages.fedoraproject.org/pkgs/nettle/nettle-devel Here's how to build rpm-sequoia and a version of rpm that uses it: ``` $ sudo dnf install cargo rustc clang pkg-config nettle-devel $ mkdir /tmp/rpm $ cd /tmp/rpm $ git clone git@github.com:rpm-software-management/rpm-sequoia.git Cloning into 'rpm-sequoia'... done. $ cd rpm-sequoia $ PREFIX=/usr LIBDIR="\${prefix}/lib64" \ cargo build --release && cargo test --release Updating crates.io index ... test result: ok. ... $ cd /tmp/rpm $ git clone git@github.com:rpm-software-management/rpm.git Cloning into 'rpm'... done. $ cd rpm $ git checkout rpm-4.18.1-release Switched to a new branch 'rpm-4.18.1-release' $ sudo dnf install automake autoconf gettext-devel libtool tar zlib-devel file-devel libarchive-devel popt-devel sqlite-devel lua-devel fakechroot $ autoreconf -fis ... $ mkdir b $ cd b $ export PKG_CONFIG_PATH=/tmp/rpm/rpm-sequoia/target/release $ export LD_LIBRARY_PATH=/tmp/rpm/rpm-sequoia/target/release $ ../configure --prefix=/ --with-crypto=sequoia $ make $ make check ``` Note: this builds version 4.18 of `rpm`, which is the current stable release of `rpm`. The current development branch of `rpm` has switched to using `cmake` instead of `autoconf`. Please refer to [rpm's `INSTALL`] file for how to build `master`. [rpm's `INSTALL`]: https://github.com/rpm-software-management/rpm/blob/master/INSTALL To use a different cryptographic backend, you need to disable the default backend, and select your preferred backend. For instance, to use Sequoia's OpenSSL backend, you would compile `rpm-sequoia` as follows: ``` $ cargo build --release --no-default-features --features crypto-openssl ``` See [`sequoia-openpgp`'s README] for the list of currently supported cryptographic backends. [`sequoia-openpgp`'s README]: https://gitlab.com/sequoia-pgp/sequoia#features The rpm-sequoia artifacts (the .a, .so, and the .pc files) are placed in the build directory, which, in this case, is `/tmp/rpm/rpm-sequoia/target/release`. We also set two environment variables when calling `cargo build`: * `PREFIX` is the prefix that will be used in the generated `rpm-sequoia.pc` file. It defaults to `/usr/local`. * `LIBDIR` is the installed library path listed in the generated metadata. It can be an absolute path or one based on `${prefix}`, and defaults to `${prefix}/lib`. To run just one or two tests, do something like the following: Note: when building or running the test suite, it is essential to make sure `PKG_CONFIG_PATH` and `LD_LIBRARY_PATH` are set appropriately (as in the above transcript). ``` $ cd /tmp/rpm/rpm/b/tests $ export PKG_CONFIG_PATH=/tmp/rpm/rpm-sequoia/target/release $ export LD_LIBRARY_PATH=/tmp/rpm/rpm-sequoia/target/release $ make populate_testing $ T="266 273"; for t in $T; do if ! ../../tests/rpmtests $t; then cat rpmtests.dir/$t/rpmtests.log; fi; done ``` To get tracing output, set RPM_TRACE to 1: ``` $ cd /tmp/rpm/rpm/b/tests $ export PKG_CONFIG_PATH=/tmp/rpm/rpm-sequoia/target/release $ export LD_LIBRARY_PATH=/tmp/rpm/rpm-sequoia/target/release $ make populate_testing $ export RPM_TRACE=1 $ ../../tests/rpmtests 273 $ cat rpmtests.dir/273/rpmtests.log ... +pgpDigParamsFree: -> success +rpmFreeCrypto: entered +rpmFreeCrypto: -> success 273. rpmsigdig.at:495: 273. rpmsign --addsign (rpmsigdig.at:495): FAILED (rpmsigdig.at:503) ... ``` rpm-sequoia-1.8.0/build.rs000064400000000000000000000151131046102023000135420ustar 00000000000000use std::env; use std::fs::File; use std::io::Write; use std::path::{Path, PathBuf}; use std::collections::HashMap; use anyhow::Result; struct PkgConfigTemplate { cargo_toml: HashMap, pc_in: String, } impl PkgConfigTemplate { /// Read the pkg-config template file. fn new(src: P, pc_in: S) -> Result where P: AsRef, S: AsRef { let src = src.as_ref(); let mut pc_in_ = PathBuf::from(src); pc_in_.push(pc_in.as_ref()); let pc_in = pc_in_; let pc_in = std::fs::read_to_string(pc_in)?; let cargo_toml = HashMap::from([ ("NAME".to_string(), env!("CARGO_PKG_NAME").to_string()), ("DESCRIPTION".to_string(), env!("CARGO_PKG_DESCRIPTION").to_string()), ("VERSION".to_string(), env!("CARGO_PKG_VERSION").to_string()), ("HOMEPAGE".to_string(), env!("CARGO_PKG_HOMEPAGE").to_string()), ("REQUIRES".to_string(), if cfg!(feature = "crypto-botan") { "botan-3" } else if cfg!(feature = "crypto-botan2") { "botan-2" } else if cfg!(feature = "crypto-nettle") { "nettle" } else if cfg!(feature = "crypto-openssl") { "libssl" } else if cfg!(feature = "crypto-cng") { "" } else if cfg!(feature = "crypto-rust") { "" } else { panic!("No cryptographic backend selected. Try: \ \"cargo build --no-default-features \ --features crypto-openssl\"") }.to_string()), ]); Ok(PkgConfigTemplate { cargo_toml, pc_in, }) } /// Perform substitutions on the pkg-config file based on what was /// read from the Cargo.toml file and the provided substitution /// map. /// /// The mappings in the substitution map are preferred to those in /// the Cargo.toml file. /// /// Substitutions take the form of keys and values where the /// string @KEY@ is substituted with the value of KEY. So, /// @VERSION@ is substituted with the value of VERSION. fn substitute(&self, map: HashMap) -> Result { let mut pc: String = self.pc_in.clone(); for (key, value) in map.iter().chain(self.cargo_toml.iter()) { pc = pc.replace(&format!("@{}@", key), value); } Ok(pc) } } fn main() -> Result<(), anyhow::Error> { // Generate // ${CARGO_TARGET_DIR}/${PROFILE}/rpm-sequoia{-uninstalled}.pc // from ${SRC}/rpm-sequoia.pc.in. let src = env::current_dir()?; // Location of the build directory (e.g., // `/tmp/rpm-sequoia/debug`). let mut build_dir = PathBuf::from(&src); if let Some(target_dir) = env::var_os("CARGO_TARGET_DIR") { // Note: if CARGO_TARGET_DIR is absolute, this will first // clear build_dir, which is what we want. build_dir.push(target_dir); } else { build_dir.push("target"); } let profile = env::var_os("PROFILE").expect("PROFILE not set"); build_dir.push(&profile); let pc_in = PkgConfigTemplate::new(&src, "rpm-sequoia.pc.in")?; // Generate rpm-sequoia.pc. let mut pc = build_dir.clone(); pc.push("rpm-sequoia.pc"); let prefix = env::var_os("PREFIX"); let prefix: &str = match prefix.as_ref().map(|s| s.to_str()) { Some(Some(s)) => s, Some(None) => Err(anyhow::anyhow!("PREFIX contains invalid UTF-8"))?, None => "/usr/local", }; let libdir = env::var_os("LIBDIR"); let libdir: &str = match libdir.as_ref().map(|s| s.to_str()) { Some(Some(s)) => s, Some(None) => Err(anyhow::anyhow!("LIBDIR contains invalid UTF-8"))?, None => "${prefix}/lib", }; let content = pc_in.substitute(HashMap::from([ ("PREFIX".to_string(), prefix.into()), ("LIBDIR".to_string(), libdir.into()), ]))?; let mut pc = File::create(&pc).expect( &format!("Creating {:?} (CARGO_TARGET_DIR: {:?})", pc, env::var_os("CARGO_TARGET_DIR"))); pc.write_all(content.as_bytes())?; // Generate rpm-sequoia-uninstalled.pc. let mut pc = build_dir.clone(); pc.push("rpm-sequoia-uninstalled.pc"); let content = pc_in.substitute(HashMap::from([ ("PREFIX".to_string(), build_dir.to_str() .expect("build directory is not valid UTF-8").to_string()), ("LIBDIR".to_string(), "${prefix}".into()), ]))?; let mut pc = File::create(&pc).expect( &format!("Creating {:?} (CARGO_TARGET_DIR: {:?})", pc, env::var_os("CARGO_TARGET_DIR"))); pc.write_all(content.as_bytes())?; // Rerun if... println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=Cargo.toml"); println!("cargo:rerun-if-changed=rpm-sequoia.pc.in"); println!("cargo:rerun-if-env-changed=PREFIX"); println!("cargo:rerun-if-env-changed=LIBDIR"); println!("cargo:rerun-if-env-changed=PROFILE"); println!("cargo:rerun-if-env-changed=CARGO_TARGET_DIR"); // Set the soname. let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); let os = env::var("CARGO_CFG_TARGET_OS").unwrap(); let env = env::var("CARGO_CFG_TARGET_ENV").unwrap(); // We do not care about `_pre` and such. let major = env::var("CARGO_PKG_VERSION_MAJOR").unwrap(); let minor = env::var("CARGO_PKG_VERSION_MINOR").unwrap(); let patch = env::var("CARGO_PKG_VERSION_PATCH").unwrap(); // libdir might contain "${prefix}". Replace it with // the actual prefix value if found. let libdir_resolved = libdir.replace("${prefix}", prefix); let linker_lines = cdylib_link_lines::shared_object_link_args( "rpm_sequoia", &major, &minor, &patch, &arch, &os, &env, PathBuf::from(libdir_resolved), build_dir.clone(), ); for line in linker_lines { println!("cargo:rustc-cdylib-link-arg={}", line); } #[cfg(unix)] { // Create a symlink. let mut create = true; let mut link = build_dir.clone(); link.push(format!("librpm_sequoia.so.{}", major)); if let Ok(current) = std::fs::read_link(&link) { if current.to_str() == Some("librpm_sequoia.so") { // Do nothing. create = false; } else { // Invalid. std::fs::remove_file(&link)?; } } if create { std::os::unix::fs::symlink("librpm_sequoia.so", link)?; } } Ok(()) } rpm-sequoia-1.8.0/deny.toml000064400000000000000000000221011046102023000137240ustar 00000000000000# This template contains all of the possible sections and their default values # Note that all fields that take a lint level have these possible values: # * deny - An error will be produced and the check will fail # * warn - A warning will be produced, but the check will not fail # * allow - No warning or error will be produced, though in some cases a note # will be # The values provided in this template are the default values that will be used # when any section or field is not specified in your own configuration # If 1 or more target triples (and optionally, target_features) are specified, # only the specified targets will be checked when running `cargo deny check`. # This means, if a particular package is only ever used as a target specific # dependency, such as, for example, the `nix` crate only being used via the # `target_family = "unix"` configuration, that only having windows targets in # this list would mean the nix crate, as well as any of its exclusive # dependencies not shared by any other crates, would be ignored, as the target # list here is effectively saying which targets you are building for. targets = [ # The triple can be any string, but only the target triples built in to # rustc (as of 1.40) can be checked against actual config expressions #{ triple = "x86_64-unknown-linux-musl" }, # You can also specify which target_features you promise are enabled for a # particular target. target_features are currently not validated against # the actual valid features supported by the target architecture. #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, ] # This section is considered when running `cargo deny check advisories` # More documentation for the advisories section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html [advisories] # The path where the advisory database is cloned/fetched into db-path = "~/.cargo/advisory-db" # The url(s) of the advisory databases to use db-urls = ["https://github.com/rustsec/advisory-db"] # The lint level for security vulnerabilities vulnerability = "deny" # The lint level for unmaintained crates unmaintained = "warn" # The lint level for crates that have been yanked from their source registry yanked = "warn" # The lint level for crates with security notices. Note that as of # 2019-12-17 there are no security notice advisories in # https://github.com/rustsec/advisory-db notice = "warn" # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. ignore = [ "RUSTSEC-2020-0159", # We do not use local timezones (only UTC). So we are not # impacted by this. "RUSTSEC-2020-0071", ] # Threshold for security vulnerabilities, any vulnerability with a CVSS score # lower than the range specified will be ignored. Note that ignored advisories # will still output a note when they are encountered. # * None - CVSS Score 0.0 # * Low - CVSS Score 0.1 - 3.9 # * Medium - CVSS Score 4.0 - 6.9 # * High - CVSS Score 7.0 - 8.9 # * Critical - CVSS Score 9.0 - 10.0 #severity-threshold = # This section is considered when running `cargo deny check licenses` # More documentation for the licenses section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html [licenses] # The lint level for crates which do not have a detectable license unlicensed = "deny" # List of explicitly allowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. allow = [ #"MIT", #"Apache-2.0", #"Apache-2.0 WITH LLVM-exception", ] # List of explicitly disallowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. deny = [ #"Nokia", ] # Lint level for licenses considered copyleft copyleft = "warn" # Blanket approval or denial for OSI-approved or FSF Free/Libre licenses # * both - The license will be approved if it is both OSI-approved *AND* FSF # * either - The license will be approved if it is either OSI-approved *OR* FSF # * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF # * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved # * neither - This predicate is ignored and the default lint level is used allow-osi-fsf-free = "either" # Lint level used when no other predicates are matched # 1. License isn't in the allow or deny lists # 2. License isn't copyleft # 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" default = "deny" # The confidence threshold for detecting a license from license text. # The higher the value, the more closely the license text must be to the # canonical license text of a valid SPDX license file. # [possible values: any between 0.0 and 1.0]. confidence-threshold = 0.95 # Allow 1 or more licenses on a per-crate basis, so that particular licenses # aren't accepted for every possible crate as with the normal allow list exceptions = [ # Each entry is the crate and version constraint, and its specific allow # list #{ allow = ["Zlib"], name = "adler32", version = "*" }, ] # Some crates don't have (easily) machine readable licensing information, # adding a clarification entry for it allows you to manually specify the # licensing information #[[licenses.clarify]] # The name of the crate the clarification applies to #name = "ring" # The optional version constraint for the crate #version = "*" # The SPDX expression for the license requirements of the crate #expression = "MIT AND ISC AND OpenSSL" # One or more files in the crate's source used as the "source of truth" for # the license expression. If the contents match, the clarification will be used # when running the license check, otherwise the clarification will be ignored # and the crate will be checked normally, which may produce warnings or errors # depending on the rest of your configuration #license-files = [ # Each entry is a crate relative path, and the (opaque) hash of its contents #{ path = "LICENSE", hash = 0xbd0eed23 } #] [licenses.private] # If true, ignores workspace crates that aren't published, or are only # published to private registries ignore = true # One or more private registries that you might publish crates to, if a crate # is only published to private registries, and ignore is true, the crate will # not have its license(s) checked registries = [ #"https://sekretz.com/registry ] # This section is considered when running `cargo deny check bans`. # More documentation about the 'bans' section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html [bans] # Lint level for when multiple versions of the same crate are detected multiple-versions = "warn" # Lint level for when a crate version requirement is `*` wildcards = "deny" # The graph highlighting used when creating dotgraphs for crates # with multiple versions # * lowest-version - The path to the lowest versioned duplicate is highlighted # * simplest-path - The path to the version with the fewest edges is highlighted # * all - Both lowest-version and simplest-path are used highlight = "all" # List of crates that are allowed. Use with care! allow = [ #{ name = "ansi_term", version = "=0.11.0" }, ] # List of crates to deny deny = [ # Each entry the name of a crate and a version range. If version is # not specified, all versions will be matched. #{ name = "ansi_term", version = "=0.11.0" }, # # Wrapper crates can optionally be specified to allow the crate when it # is a direct dependency of the otherwise banned crate #{ name = "ansi_term", version = "=0.11.0", wrappers = [] }, ] # Certain crates/versions that will be skipped when doing duplicate detection. skip = [ #{ name = "ansi_term", version = "=0.11.0" }, ] # Similarly to `skip` allows you to skip certain crates during duplicate # detection. Unlike skip, it also includes the entire tree of transitive # dependencies starting at the specified crate, up to a certain depth, which is # by default infinite skip-tree = [ #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, ] # This section is considered when running `cargo deny check sources`. # More documentation about the 'sources' section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html [sources] # Lint level for what to happen when a crate from a crate registry that is not # in the allow list is encountered unknown-registry = "warn" # Lint level for what to happen when a crate from a git repository that is not # in the allow list is encountered unknown-git = "warn" # List of URLs for allowed crate registries. Defaults to the crates.io index # if not specified. If it is specified but empty, no registries are allowed. allow-registry = ["https://github.com/rust-lang/crates.io-index"] # List of URLs for allowed Git repositories allow-git = [] [sources.allow-org] # 1 or more github.com organizations to allow git sources for #github = [""] # 1 or more gitlab.com organizations to allow git sources for #gitlab = [""] # 1 or more bitbucket.org organizations to allow git sources for #bitbucket = [""] rpm-sequoia-1.8.0/doc/release-checklist.md000064400000000000000000000031061046102023000165520ustar 00000000000000This is a checklist for doing releases. 1. Start from `origin/main`, create a branch `staging`. 1. Switch to the branch. 1. Bump the version in `Cargo.toml` to `XXX`. 1. Bump the version in `README.md` to `XXX`. 1. Run `cargo check` (this implicitly updates `Cargo.lock`). 1. Update dependencies and run tests. - Use the exact Rust toolchain version of the current Sequoia MSRV (refer to `Cargo.toml`): `rustup default 1.xx` - Run `cargo update` to update the dependencies. If some dependency is updated and breaks due to our MSRV, find a good version of that dependency and select it using e.g. `cargo update -p backtrace --precise 3.46`. - Run `cargo build && cargo check` 1. Commit changes to `Cargo.toml` and `Cargo.lock`. 1. Make a commit with the message `Release XXX.`. - Push to github, and create a merge request. Don't auto merge!!! 1. Make sure `cargo publish` works: - `mkdir -p /tmp/sequoia-staging` - `cd /tmp/sequoia-staging` - `git clone git@github.com:rpm-software-management/rpm-sequoia.git` - `cd rpm-sequoia` - `git checkout origin/staging` - `cargo publish --features crypto-nettle --dry-run` 1. Wait until CI and `cargo publish ... --dry-run` are successful. In case of errors, correct them, and restart. 1. Merge the merge request. 1. Run `cargo publish --features crypto-nettle`. 1. Make a tag `vXXX` with the message `Release XXX.` signed with an offline-key, which has been certified by our `openpgp-ca@sequoia-pgp.org` key. 1. Push the signed tag `vXXX`. rpm-sequoia-1.8.0/openpgp-policy.toml000064400000000000000000001526341046102023000157510ustar 00000000000000version = 0 commit_goodlist = [] [authorization."Neal H. Walfield "] sign_commit = true sign_tag = true sign_archive = true add_user = true retire_user = true audit = true keyring = """ -----BEGIN PGP PUBLIC KEY BLOCK----- Comment: F717 3B3C 7C68 5CD9 ECC4 191B 74E4 45BA 0E15 C957 Comment: Neal H. Walfield (Code Signing Key) Comment: Neal H. Walfield Comment: Neal H. Walfield Comment: Neal H. Walfield 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 =DRsl -----END PGP PUBLIC KEY BLOCK----- """ rpm-sequoia-1.8.0/rpm-sequoia.pc.in000064400000000000000000000002611046102023000152660ustar 00000000000000prefix=@PREFIX@ libdir=@LIBDIR@ Name: @NAME@ Description: @DESCRIPTION@ URL: @HOMEPAGE@ Version: @VERSION@ Requires.private: @REQUIRES@ Cflags: Libs: -L${libdir} -lrpm_sequoia rpm-sequoia-1.8.0/src/digest.rs000064400000000000000000000065121046102023000145140ustar 00000000000000// This is a reimplementation of rpm/rpmio/digest_openssl.c / // rpm/rpmio/digest_libgcrypt.c using Sequoia. use libc::{ c_int, size_t, }; use sequoia_openpgp as openpgp; use openpgp::crypto::{HashAlgorithm, hash}; use crate::Error; use crate::Result; #[derive(Clone)] pub struct DigestContext { pub(crate) ctx: hash::Context, } impl DigestContext { pub(crate) fn digest_size(&self) -> usize { self.ctx.digest_size() } pub(crate) fn update>(&mut self, data: T) { self.ctx.update(data.as_ref()); } pub(crate) fn digest(&mut self, digest: &mut [u8]) -> Result<()> { Ok(self.ctx.digest(digest)?) } pub(crate) fn into_digest(self) -> Result> { Ok(self.ctx.into_digest()?) } } ffi!( /// DIGEST_CTX rpmDigestInit(int hashalgo, rpmDigestFlags flags) /// /// rpmDigestFlags currently does not define any flags. fn _rpmDigestInit(hashalgo: c_int, flags: c_int) -> *mut DigestContext { if hashalgo < 0 || hashalgo > u8::MAX as c_int { return Err(Error::Fail("Out of range".into())); } let hashalgo = HashAlgorithm::from(hashalgo as u8); if flags != 0 { return Err(Error::Fail(format!("Unsupported flags: {}", flags))); } let ctx = DigestContext { ctx: hashalgo.context()?.for_digest(), }; Ok(move_to_c!(ctx)) }); ffi!( /// DIGEST_CTX rpmDigestDup(DIGEST_CTX octx) fn _rpmDigestDup(ctx: *const DigestContext) -> *mut DigestContext { let ctx = check_ptr!(ctx); Ok(Box::into_raw(Box::new(ctx.clone()))) }); ffi!( /// size_t rpmDigestLength(int hashalgo) fn _rpmDigestLength(hashalgo: c_int) -> size_t[0] { if hashalgo < 0 || hashalgo > u8::MAX as c_int { return Ok(0); } let hashalgo = HashAlgorithm::from(hashalgo as u8); use HashAlgorithm::*; let len = match hashalgo { MD5 => 16, SHA1 => 20, RipeMD => 20, SHA256 => 32, SHA384 => 48, SHA512 => 64, SHA224 => 28, _ => 0, }; Ok(len) }); ffi!( /// int rpmDigestUpdate(DIGEST_CTX ctx, const void * data, size_t len) fn _rpmDigestUpdate(ctx: *mut DigestContext, data: *const u8, len: size_t) -> ErrorCode { let ctx = check_mut!(ctx); let data = check_slice!(data, len); ctx.update(data); Ok(()) }); ffi!( /// int rpmDigestFinal(DIGEST_CTX ctx, void ** datap, size_t *lenp, int asAscii) fn _rpmDigestFinal(ctx: *mut DigestContext, datap: *mut *mut u8, lenp: *mut size_t, as_ascii: c_int) -> Binary { let ctx = claim_from_c!(ctx); let datap = check_optional_mut!(datap); let lenp = check_optional_mut!(lenp); let mut digest = ctx.into_digest()?; if as_ascii != 0 { digest = digest .iter() .map(|x| { let x = format!("{:02x}", x); let x = x.as_bytes(); std::iter::once(x[0]).chain(std::iter::once(x[1])) }) .flatten() // Add a NUL. .chain(std::iter::once(0)) .collect(); } digest.shrink_to_fit(); if let Some(lenp) = lenp { *lenp = digest.len() as size_t; } if let Some(datap) = datap { *datap = digest.as_mut_ptr(); // Pass ownership to the caller. std::mem::forget(digest); } Ok(()) }); rpm-sequoia-1.8.0/src/ffi.rs000064400000000000000000000254171046102023000140060ustar 00000000000000pub(crate) fn idempotent(x: T) -> T { x } pub(crate) fn zero(_: T) -> libc::c_int { 0 } pub(crate) fn minus_one(_: T) -> libc::c_int { -1 } pub(crate) fn unit(_: T) -> () { () } // Wraps an ffi function, which returns an arbitrary type. // // The inner function returns `Result<$rt>`. This wrapper maps // `Ok($rt)` to `$Crt` using `$rt_to_crt` and `Err(err)` to // `$err_to_crt`. macro_rules! ffi { // Wraps an ffi function, which returns 0 on success and -1 on error. // // fn func(...) -> Binary ($(#[$outer:meta])* fn $f:ident($($v:ident: $t:ty),*) -> Binary $body:block) => { ffi!($(#[$outer])* fn $f($($v: $t),*) -> Result<(), crate::Error> -> (crate::ErrorCode; $crate::ffi::zero; $crate::ffi::minus_one) { $body }); }; // Wraps an ffi function, which returns an RC. // // fn func(...) -> ErrorCode ($(#[$outer:meta])* fn $f:ident($($v:ident: $t:ty),*) -> ErrorCode $body:block) => { ffi!($(#[$outer])* fn $f($($v: $t),*) -> Result<(), crate::Error> -> (crate::ErrorCode; $crate::ffi::zero; |err| crate::ErrorCode::from(err)) { $body }); }; // Wraps an ffi function, which returns a PgpArmorError. // // fn func(...) -> PgpArmor ($(#[$outer:meta])* fn $f:ident($($v:ident: $t:ty),*) -> PgpArmor $body:block) => { ffi!($(#[$outer])* fn $f($($v: $t),*) -> Result -> (crate::ErrorCode; c_int::from; c_int::from) { $body }); }; // Wraps an ffi function, which returns an object whose type is // *const T. Returns NULL on error. // // fn func(...) -> *const u8 ($(#[$outer:meta])* fn $f:ident($($v:ident: $t:ty),*) -> *const $value:ty $body:block) => { ffi!($(#[$outer])* fn $f($($v: $t),*) -> Result<*const $value, crate::Error> -> (*const $value; $crate::ffi::idempotent; |_| std::ptr::null()) { $body }); }; // Wraps an ffi function, which returns an object whose type is // *mut T. Returns NULL on error. // // fn func(...) -> *mut u8 ($(#[$outer:meta])* fn $f:ident($($v:ident: $t:ty),*) -> *mut $value:ty $body:block) => { ffi!($(#[$outer])* fn $f($($v: $t),*) -> Result<*mut $value, crate::Error> -> (*mut $value; $crate::ffi::idempotent; |_| std::ptr::null_mut()) { $body }); }; // Wraps an ffi function, which returns a value. The value is passed // through as is and errors are mapped to `$err`. // // Example: A function that returns an int. If the function // returns Err, that is mapped to 1: // // fn func(...) -> c_int[1] ($(#[$outer:meta])* fn $f:ident($($v:ident: $t:ty),*) -> $value:ty[$err:expr] $body:block) => { ffi!($(#[$outer])* fn $f($($v: $t),*) -> Result<$value, crate::Error> -> ($value; $crate::ffi::idempotent; |_| $err) { $body }); }; // Wraps an ffi function, which returns void. // // The inner function returns `Result<()>` and this is mapped to `()`. // // Note: inner body returns Ok(()) by default. // // Example: // // fn func(...) // // Note: there is no default type in the declaration. ($(#[$outer:meta])* fn $f:ident($($v:ident: $t:ty),*) $body:block) => { ffi!( $(#[$outer])* fn $f($($v: $t),*) -> Result<(), crate::Error> -> ((); $crate::ffi::unit; $crate::ffi::unit) { let () = $body; Ok(()) }); }; // $Crt is the C function's return type. It must be possible to // convert an Error value v of type $rt to a value of type $Crt by doing: // $Crt::from($rt). // // $ok is the value (of type $rt) to map Ok to. ($(#[$outer:meta])* fn $f:ident($($v:ident: $t:ty),*) -> Result<$rt:ty, $et:ty> -> ($Crt:ty; $rt_to_crt:expr; $err_to_crt: expr) $body:block ) => { // The wrapper. It calls $f and turns the result into an // error code. $(#[$outer])* #[allow(unused)] #[no_mangle] pub extern "C" fn $f($($v: $t),*) -> $Crt { tracer!(*crate::TRACE, stringify!($f)); // The actual function. fn inner($($v: $t),*) -> std::result::Result<$rt, $et> { $body } t!("entered"); // We use AssertUnwindSafe. This is safe, because if we // catch a panic, we abort. If we turn the panic into an // error, then we need to reexamine this assumption. let r = std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| { match inner($($v,)*) { Ok(v) => { t!("-> success"); let rt: $Crt = $rt_to_crt(v); rt } Err(err) => { t!("-> error: {}{}", err, { use std::error::Error; let mut causes = String::new(); let mut cause = err.source(); while let Some(e) = cause { causes.push_str("\n because: "); causes.push_str(&e.to_string()); cause = e.source(); } causes }); let rt: $Crt = $err_to_crt(err); rt } } })); match r { Ok(code) => code, Err(_) => { t!("-> panic!"); unsafe { ::libc::abort() }; } } } } } // Creates a stub for a ffi, which returns an error. #[allow(unused_macros)] macro_rules! stub { ($f:ident) => { #[no_mangle] pub extern "C" fn $f() -> crate::ErrorCode { tracer!(*crate::TRACE, stringify!($f)); t!("{} is a stub", stringify!($f)); crate::Error::Fail( format!("Unimplemented: {}", stringify!($f))).into() } }; } // Checks if a `*const T` pointer is NULL if so, returns an error. // Otherwise, returns `&T`. macro_rules! check_ptr { ($p:ident) => {{ let p: *const _ = $p; if p.is_null() { return Err(Error::Fail( format!("{} must not be NULL", stringify!($p))).into()); } else { t!("{}: & <- {:?}", stringify!($p), $p); unsafe { &*p } } }} } // Returns an Option<&T> from a *const T. macro_rules! check_optional_ptr { ($p:ident) => {{ let p: *const _ = $p; if p.is_null() { None } else { t!("{}: Option<&> <- {:?}", stringify!($p), $p); Some(unsafe { &*p }) } }} } // Checks if a `*mut T` pointer is NULL if so, returns an error. // Otherwise, returns `&mut T`. macro_rules! check_mut { ($p:ident) => {{ let p: *mut _ = $p; if p.is_null() { return Err(Error::Fail( format!("{} must not be NULL", stringify!($p))).into()); } else { t!("{}: &mut <- {:?}", stringify!($p), $p); unsafe { &mut *p } } }} } // Returns an Option<&mut T> from a *mut T. macro_rules! check_optional_mut { ($p:ident) => {{ let p: *mut _ = $p; if p.is_null() { None } else { t!("{}: Option<&mut> <- {:?}", stringify!($p), $p); Some(unsafe { &mut *p }) } }} } // Checks if a `*const T` pointer is NULL if so, returns an error. // Otherwise, returns a slice `&[T]` with `l` elements. macro_rules! check_slice { ($p:ident, $l:expr) => { if $p.is_null() { return Err(Error::Fail( format!("{} must not be NULL", stringify!($p)))); } else { t!("{}: &[] <- {:?}", stringify!($p), $p); unsafe { std::slice::from_raw_parts($p as *const u8, $l) } } } } // Checks if a `*mut T` pointer is NULL if so, returns an error. // Otherwise, returns a slice `&mut [T]` with `l` elements. macro_rules! check_mut_slice { ($p:ident, $l:expr) => {{ let p: *mut _ = $p; if p.is_null() { return Err(Error::Fail( format!("{} must not be NULL", stringify!($p)))); } else { t!("{}: &[] <- {:?}", stringify!($p), p); unsafe { std::slice::from_raw_parts_mut($p as *mut u8, $l) } } }} } // Checks if a `*const c_char` pointer is NULL if so, returns an // error. Otherwise, returns a CStr. macro_rules! check_cstr { ($s:ident) => {{ let _: *const libc::c_char = $s; let s = check_ptr!($s); unsafe { std::ffi::CStr::from_ptr(s) } }} } // Moves ownership of a parameter of type T to C. // // Given a T, returns a *mut T. macro_rules! move_to_c { ($expr:expr) => {{ let p = Box::into_raw(Box::new($expr)); t!("{}: returning {:?}", stringify!($expr), p); p }} } // Moves ownership of an object owned by C. // // This is the opposite of move_to_c. macro_rules! claim_from_c { ($p:ident) => {{ if $p.is_null() { return Err(Error::Fail( format!("{} must not be NULL", stringify!($p)))); } unsafe { t!("{}: owned <- {:?}", stringify!($p), $p); Box::from_raw($p) } }}; } // Moves ownership of a parameter of type Option to C. macro_rules! move_option_to_c { ($expr:expr) => { $expr.map(|x| box_raw!(x)).unwrap_or(::std::ptr::null_mut()) } } /// Transfers ownership from C to Rust, then frees the object. /// /// NOP if called with NULL. macro_rules! free { ($p:ident) => {{ if let Some(ptr) = $p { let ptr = unsafe { Box::from_raw(ptr) }; drop(ptr); } }}; } rpm-sequoia-1.8.0/src/lib.rs000064400000000000000000002354441046102023000140130ustar 00000000000000//! An implementation of RPM's OpenPGP interface. //! //! This library provides an implementation of [RPM's OpenPGP //! interface](https://github.com/rpm-software-management/rpm/blob/master/include/rpm/rpmpgp.h). //! //! **You should not link to this library directly**. //! //! If you are looking for an OpenPGP interface, consider using //! [Sequoia], which this library is based on. If you want to use //! RPM's OpenPGP interface, which you should only do if you are //! interacting with RPM, then you should link against [RPM], which //! reexports this interface. //! //! [Sequoia]: https://gitlab.com/sequoia-pgp/sequoia //! [RPM]: http://rpm.org //! //! If you are investigating a bug in this library, set the //! `RPM_TRACE` environment variable to 1 to get a verbose trace of //! the library's execution: //! //! ```sh //! $ LD_LIBRARY_PATH=/tmp/rpm-sequoia/release RPM_TRACE=1 ./rpmkeys \ //! --import ../tests/data/keys/CVE-2021-3521-badbind.asc //! _rpmInitCrypto: entered //! _rpmInitCrypto: -> success //! _pgpParsePkts: entered //! ... //! ``` //! //! # Policy //! //! When Sequoia evaluates the validity of an object (e.g., a //! cryptographic signature) it consults a policy. The policy is user //! defined. This library uses [Sequoia's standard policy]. //! //! [Sequoia's standard policy]: https://docs.sequoia-pgp.org/sequoia_openpgp/policy/struct.StandardPolicy.html //! //! Sequoia's standard policy allows self-signatures (i.e., the //! signatures that bind a User ID or subkey to a certificate) made //! with SHA-1 until February 2023. It completely disallows data //! signatures made with SHA-1. The reason for this is that SHA-1 //! collision resistance is broken, but its second pre-image //! resistance is still okay. //! //! As an added protection, Sequoia uses [SHA-1 collision detection], //! which is a variant of SHA-1, which mitigates known attacks against //! SHA-1. SHA-1 CD has a very low [false positive rate] (2^-90) so //! it can be treated as a drop-in, fully compatible replacement for //! SHA-1. //! //! [SHA-1 collision detection]: https://github.com/cr-marcstevens/sha1collisiondetection //! [false positive rate]: https://github.com/cr-marcstevens/sha1collisiondetection#about //! //! # Configuration File //! //! This library reads the [crypto policy configuration] in //! `/etc/crypto-policies/back-ends/sequoia.config`. If that file //! doesn't exist, it tries //! `/usr/share/crypto-policies/back-ends/rpm-sequoia.config`. This //! can be overridden using the `SEQUOIA_CRYPTO_POLICY` environment //! variable. If set to the empty string, then no crypto policy will //! be read and instead [Sequoia's default policy] will be used. //! //! Refer to the [Fedora Crypto Policy] project for information about //! the crypto policy. //! //! [crypto policy configuration]: https://docs.rs/sequoia-policy-config/latest/sequoia_policy_config/ //! [Sequoia's default policy]: https://docs.sequoia-pgp.org/sequoia_openpgp/policy/struct.StandardPolicy.html //! [Fedora Crypto Policy]: https://gitlab.com/redhat-crypto/fedora-crypto-policies/ use std::env; use std::ffi::{ CString, }; use std::fmt::Debug; use std::io::Read; use std::io::Write; use std::path::PathBuf; use std::sync::RwLock; use std::time::{ Duration, SystemTime, UNIX_EPOCH, }; #[allow(unused_imports)] use anyhow::Context; use libc::{ c_char, c_int, c_uint, c_void, size_t, }; use chrono::{ DateTime, Utc, }; use sequoia_openpgp as openpgp; use openpgp::armor; use openpgp::Cert; use openpgp::cert::prelude::*; use openpgp::Fingerprint; use openpgp::packet::key::{ PublicParts, }; use openpgp::packet::{ Packet, Signature, Tag, }; use openpgp::parse::{ PacketParser, PacketParserResult, PacketParserBuilder, Dearmor, }; use openpgp::parse::Parse; use openpgp::policy::{ NullPolicy, StandardPolicy, Policy, }; use openpgp::serialize::SerializeInto; use openpgp::types::RevocationStatus; use openpgp::parse::buffered_reader; #[macro_use] mod log; #[macro_use] mod ffi; #[macro_use] pub mod rpm; use rpm::{ Error, ErrorCode, PgpArmor, PgpArmorError, Result, }; pub mod digest; lazy_static::lazy_static! { static ref P: RwLock> = RwLock::new(StandardPolicy::new()); } const NP: &NullPolicy = unsafe { &NullPolicy::new() }; // Set according to the RPM_TRACE environment variable (enabled if // non-zero), or if we are built in debug mode. lazy_static::lazy_static! { static ref TRACE: bool = { if let Ok(v) = env::var("RPM_TRACE") { let v: isize = v.parse().unwrap_or(1); v != 0 } else { false } }; } /// Prints the error and causes, if any. pub fn print_error_chain(err: &anyhow::Error) { eprintln!(" {}", err); err.chain().skip(1).for_each(|cause| eprintln!(" because: {}", cause)); } // Sometimes the same error cascades, e.g.: // // ``` // $ sq-wot --time 20230110T0406 --keyring sha1.pgp path B5FA089BA76FE3E17DC11660960E53286738F94C 231BC4AB9D8CAB86D1622CE02C0CE554998EECDB FABA8485B2D4D5BF1582AA963A8115E774FA9852 "" // [ ] FABA8485B2D4D5BF1582AA963A8115E774FA9852 : not authenticated (0%) // ◯ B5FA089BA76FE3E17DC11660960E53286738F94C ("") // │ No adequate certification found. // │ No binding signature at time 2023-01-10T04:06:00Z // │ No binding signature at time 2023-01-10T04:06:00Z // │ No binding signature at time 2023-01-10T04:06:00Z // ... // ``` // // Although technically correct, it's just noise. Compress them. fn error_chain(err: &anyhow::Error) -> Vec { let mut errs = std::iter::once(err.to_string()) .chain(err.chain().map(|source| source.to_string())) .collect::>(); errs.dedup(); errs } // Generate macros for working with lints. // // Note: $dollar is a hack, which we use because nested macros with // repetitions don't currently work. See: // https://github.com/rust-lang/rust/pull/95860 macro_rules! linter { ($dollar:tt, $lints:ident) => { // A helper macro to add a lint. // // If `$err` is `None`, `$msg` is turned into an `anyhow::Error` and // appended to `lints`. // // If `$err` is `Some`, `$msg` is added as context to `$err` and is // then appended to `lints`. macro_rules! add_lint { ($err:expr, $msg:expr $dollar(, $args:expr)*) => {{ let err: Option = $err; let msg = format!("{}", format_args!($msg $dollar(, $args)*)); let err = if let Some(err) = err { err.context(msg) } else { anyhow::anyhow!(msg) }; $lints.push(err); }}; } // A helper to return an error. // // This adds a lint using `lint!` and then returns // `Error::Fail($msg)`. macro_rules! return_err { ($err:expr, $msg:expr $dollar(, $args:expr)*) => {{ add_lint!($err, $msg $dollar(, $args)*); return Err(Error::Fail( format!("{}", format_args!($msg $dollar(, $args)*)))); }}; } } } // By default we prefer this environment variable and this file, but // if that is not present, we fallback to the default configuration. const RPM_SEQUOIA_CONFIG_ENV: &'static str = "RPM_SEQUOIA_CRYPTO_POLICY"; const RPM_SEQUOIA_CONFIG: &[&str] = &[ "/etc/crypto-policies/back-ends/rpm-sequoia.config", "/usr/share/crypto-policies/back-ends/rpm-sequoia.config", ]; ffi!( /// int rpmInitCrypto(void) fn _rpmInitCrypto() -> Binary { // XXX: Remove this once v4 signatures are ubiquitous. // // Unfortunately, much of the rpm ecosystem is still (2022) // generating v3 signatures. As they aren't completely broken, // accept them by default, but still let them be overridden by the // system policy. // // See https://bugzilla.redhat.com/show_bug.cgi?id=2141686 let mut p = openpgp::policy::StandardPolicy::new(); p.accept_packet_tag_version(openpgp::packet::Tag::Signature, 3); let mut p = sequoia_policy_config::ConfiguredStandardPolicy ::from_policy(p); // We can only specify a single file to // `ConfiguredStandardPolicy::parse_config_file`. We work around // it (for now) by taking the first file that exists. let rpm_sequoia_config = RPM_SEQUOIA_CONFIG .iter() .find(|path| { PathBuf::from(path).exists() }) .unwrap_or(&RPM_SEQUOIA_CONFIG[0]); match p.parse_config(RPM_SEQUOIA_CONFIG_ENV, rpm_sequoia_config) { Ok(false) => { // Fallback to the default configuration. if let Err(err) = p.parse_default_config() { print_error_chain(&err); return Err(err.into()); } } Ok(true) => (), Err(err) => { print_error_chain(&err); return Err(err.into()); } } *crate::P.write().unwrap() = p.build(); Ok(()) }); ffi!( /// int rpmFreeCrypto(void) fn _rpmFreeCrypto() -> Binary { Ok(()) }); // These are still implemented in C due to internationalization, and // to avoid translating the string tables, which is a fair amount of // error prone work, and doesn't improve safety. // // stub!(pgpValString); // stub!(pgpIdentItem); // This is implemented in C: it is just a wrapper around pgpParsePkts, // which uses some internal rpm functions. // // stub!(pgpReadPkts); /// An OpenPGP object. /// /// This data structure can hold either a signature, a certificate, or /// a subkey. enum PgpDigParamsObj { Cert(Cert), Subkey(Cert, Fingerprint), Signature(Signature), } pub struct PgpDigParams { obj: PgpDigParamsObj, signid: [u8; 8], userid: Option, } impl PgpDigParams { fn cert(&self) -> Option<&Cert> { match &self.obj { PgpDigParamsObj::Cert(cert) => Some(cert), PgpDigParamsObj::Subkey(cert, _) => Some(cert), PgpDigParamsObj::Signature(_) => None, } } fn key(&self) -> Option> { match &self.obj { PgpDigParamsObj::Cert(cert) => { Some(cert.primary_key().into()) } PgpDigParamsObj::Subkey(cert, fpr) => { Some(cert.keys().subkeys() .key_handle(fpr) .next() .expect("subkey missing") .into()) } PgpDigParamsObj::Signature(_) => None, } } fn signature(&self) -> Option<&Signature> { match &self.obj { PgpDigParamsObj::Cert(_) => None, PgpDigParamsObj::Subkey(_, _) => None, PgpDigParamsObj::Signature(sig) => Some(sig), } } } ffi!( /// Returns the signature's type. /// /// If `dig` is NULL or does not contain a signature, then this /// function returns -1. fn _pgpSignatureType(dig: *const PgpDigParams) -> c_int[-1] { let dig = check_ptr!(dig); dig.signature() .ok_or_else(|| Error::Fail("Not a signature".into())) .map(|sig| { u8::from(sig.typ()).into() }) }); ffi!( /// Frees the parameters. fn _pgpDigParamsFree(dig: Option<&mut PgpDigParams>) { free!(dig); }); ffi!( /// "Compares" the two parameters and returns 1 if they differ and 0 if /// they match. /// /// Two signatures are considered the same if they have the same /// parameters (version, signature type, public key and hash /// algorithms, and the first issuer packet). Note: this function /// explicitly does not check that the MPIs are the same, nor that the /// signature creation time is the same! This is intended. The only /// use of this function in the rpm code base is to check whether a key /// has already made a signature (cf. sign/rpmgensig.c:haveSignature). /// /// Two certificates are considered the same if they have the same /// fingerprint. (rpm does not currently use this functionality.) /// /// Two subkeys are considered the same if they have the same /// fingerprint. (rpm does not currently use this functionality.) fn _pgpDigParamsCmp(p1: *const PgpDigParams, p2: *const PgpDigParams) -> c_int[1] { let p1 = check_ptr!(p1); let p2 = check_ptr!(p2); let r = match (&p1.obj, &p2.obj) { (PgpDigParamsObj::Cert(c1), PgpDigParamsObj::Cert(c2)) => { c1.fingerprint() == c2.fingerprint() } (PgpDigParamsObj::Subkey(_, f1), PgpDigParamsObj::Subkey(_, f2)) => { f1 == f2 } (PgpDigParamsObj::Signature(s1), PgpDigParamsObj::Signature(s2)) => { t!("s1: {:?}", s1); t!("s2: {:?}", s2); s1.hash_algo() == s2.hash_algo() && s1.pk_algo() == s2.pk_algo() && s1.version() == s2.version() && s1.typ() == s2.typ() && p1.signid == p2.signid } _ => { false } }; Ok(if r { 0 } else { 1 }) }); const PGPVAL_PUBKEYALGO: c_uint = 6; const PGPVAL_HASHALGO: c_uint = 9; ffi!( /// Returns the object's public key or algorithm algorithm. /// /// `algotype` is either `PGPVAL_PUBKEYALGO` or `PGPVAL_HASHALGO`. /// Other algo types are not support and cause this function to return /// 0. fn _pgpDigParamsAlgo(dig: *const PgpDigParams, algotype: c_uint) -> c_uint[0] { let dig = check_ptr!(dig); match (algotype, &dig.obj) { // pubkey algo. (PGPVAL_PUBKEYALGO, PgpDigParamsObj::Cert(cert)) => { Ok(u8::from(cert.primary_key().key().pk_algo()).into()) } (PGPVAL_PUBKEYALGO, PgpDigParamsObj::Subkey(_, _)) => { Ok(u8::from(dig.key().expect("valid").key().pk_algo()).into()) } (PGPVAL_PUBKEYALGO, PgpDigParamsObj::Signature(sig)) => { Ok(u8::from(sig.pk_algo()).into()) } // hash algo. (PGPVAL_HASHALGO, PgpDigParamsObj::Cert(cert)) => { match cert.with_policy(&*P.read().unwrap(), None) { Ok(vc) => { let algo = vc.primary_key().binding_signature().hash_algo(); Ok(u8::from(algo).into()) } Err(err) => { Err(Error::Fail( format!("Using {}: {}", cert.fingerprint(), err))) } } } (PGPVAL_HASHALGO, PgpDigParamsObj::Subkey(_, fpr)) => { let ka = dig.key().expect("valid"); match ka.with_policy(&*P.read().unwrap(), None) { Ok(ka) => { let algo = ka.binding_signature().hash_algo(); Ok(u8::from(algo).into()) } Err(err) => { Err(Error::Fail( format!("Using {}: {}", fpr, err))) } } } (PGPVAL_HASHALGO, PgpDigParamsObj::Signature(sig)) => { Ok(u8::from(sig.hash_algo()).into()) } // Unknown algo. (t, PgpDigParamsObj::Cert(_)) | (t, PgpDigParamsObj::Subkey(_, _)) | (t, PgpDigParamsObj::Signature(_)) => { Err(Error::Fail(format!("Invalid algorithm type: {}", t))) } } }); ffi!( /// Returns the issuer or the Key ID. /// /// If `dig` is a signature, then this returns the Key ID stored in the /// first Issuer or Issuer Fingerprint subpacket as a hex string. /// (This is not authenticated!) /// /// If `dig` is a certificate or a subkey, then this returns the key's /// Key ID. /// /// The caller must *not* free the returned buffer. fn _pgpDigParamsSignID(dig: *const PgpDigParams) -> *const u8 { let dig = check_ptr!(dig); t!("SignID: {}", dig.signid.iter().map(|v| format!("{:02X}", v)).collect::()); Ok(dig.signid.as_ptr()) }); ffi!( /// Returns the primary User ID, if any. /// /// If `dig` is a signature, then this returns `NULL`. /// /// If `dig` is a certificate or a subkey, then this returns the /// certificate's primary User ID, if any. /// /// This interface does not provide a way for the caller to recognize /// any embedded `NUL` characters. /// /// The caller must *not* free the returned buffer. fn _pgpDigParamsUserID(dig: *const PgpDigParams) -> *const c_char { let dig = check_ptr!(dig); if let Some(ref userid) = dig.userid { Ok(userid.as_ptr()) } else { Ok(std::ptr::null()) } }); ffi!( /// Returns the object's version. /// /// If `dig` is a signature, then this returns the version of the /// signature packet. /// /// If `dig` is a certificate, then this returns the version of the /// primary key packet. /// /// If `dig` is a subkey, then this returns the version of the subkey's /// key packet. fn _pgpDigParamsVersion(dig: *const PgpDigParams) -> c_int[0] { let dig = check_ptr!(dig); let version = match &dig.obj { PgpDigParamsObj::Cert(cert) => { cert.primary_key().key().version() } PgpDigParamsObj::Subkey(_, _) => { dig.key().unwrap().key().version() } PgpDigParamsObj::Signature(sig) => { sig.version() } }; Ok(version as c_int) }); ffi!( /// Returns the object's time. /// /// If `dig` is a signature, then this returns the signature's creation /// time. /// /// If `dig` is a certificate, then this returns the primary key's key /// creation time. /// /// If `dig` is a subkey, then this returns the subkey's key creation /// time. fn _pgpDigParamsCreationTime(dig: *const PgpDigParams) -> u32[0] { let dig = check_ptr!(dig); let t = match &dig.obj { PgpDigParamsObj::Cert(cert) => { cert.primary_key().key().creation_time() } PgpDigParamsObj::Subkey(cert, fpr) => { cert.keys().subkeys() .key_handle(fpr) .next() .expect("subkey missing") .key() .creation_time() } PgpDigParamsObj::Signature(sig) => { sig.signature_creation_time().unwrap_or(UNIX_EPOCH) } }; Ok(t.duration_since(UNIX_EPOCH) .map_err(|_| Error::Fail("time".into()))? .as_secs() as u32) }); ffi!( /// Verifies the signature. /// /// If `key` is `NULL`, then this computes the hash and checks it /// against the hash prefix. /// /// If `key` is not `NULL`, then this checks that the signature is /// correct. /// /// This function does not modify `ctx`. Instead, it first duplicates /// `ctx` and then hashes the the meta-data into that context. /// /// This function fails if the signature is not valid, or a supplied /// key is not valid. /// /// A signature is valid if: /// /// - The signature is alive now (not created in the future, and not /// yet expired) /// /// - It is accepted by the [policy]. /// /// A key is valid if as of the *signature's* creation time if: /// /// - The certificate is valid according to the [policy]. /// /// - The certificate is alive /// /// - The certificate is not revoke /// /// - The key is alive /// /// - The key is not revoke /// /// - The key has the signing capability set. /// /// [policy]: index.html#policy fn _pgpVerifySignature(key: *const PgpDigParams, sig: *const PgpDigParams, ctx: *const digest::DigestContext) -> ErrorCode { match _pgpVerifySignature2(key, sig, ctx, std::ptr::null_mut()) { 0 => Ok(()), ec => Err(Error::from(ec)), } }); ffi!( /// Like _pgpVerifySignature, but returns error messages and lints in /// `lint_str`. fn _pgpVerifySignature2(key: *const PgpDigParams, sig: *const PgpDigParams, ctx: *const digest::DigestContext, lint_str: *mut *mut c_char) -> ErrorCode { let key: Option<&PgpDigParams> = check_optional_ptr!(key); let sig: &PgpDigParams = check_ptr!(sig); // This function MUST NOT free or even change ctx. let mut ctx = check_ptr!(ctx).clone(); let mut lint_str: Option<&mut _> = check_optional_mut!(lint_str); if let Some(lint_str) = lint_str.as_mut() { **lint_str = std::ptr::null_mut(); } let mut lints = Vec::new(); let r = pgp_verify_signature(key, sig, ctx, &mut lints); // Return any lint / error messages. if lints.len() > 0 { let mut s: String = if let Some(key) = key { format!( "Verifying a signature using certificate {} ({}):", key.cert() .map(|cert| cert.fingerprint().to_string()) .unwrap_or_else(|| "".to_string()), key.cert() .and_then(|cert| { cert.userids().next() .map(|userid| { String::from_utf8_lossy(userid.userid().value()).into_owned() }) }) .unwrap_or_else(|| { "".into() })) } else { format!( "Verifying a signature, but no certificate was \ provided:") }; // Indent the lints. let sep = "\n "; let lints_count = lints.len(); for (err_i, err) in lints.into_iter().enumerate() { for (cause_i, cause) in error_chain(&err).into_iter().enumerate() { if cause_i == 0 { s.push_str(sep); if lints_count > 1 { s.push_str(&format!("{}. ", err_i + 1)); } } else { s.push_str(sep); s.push_str(" because: "); } s.push_str(&cause); } } t!("Lints: {}", s); if let Some(lint_str) = lint_str.as_mut() { // Add a trailing NUL. s.push('\0'); **lint_str = s.as_mut_ptr() as *mut c_char; // Pass ownership to the caller. std::mem::forget(s); } } r }); // Verifies the signature. // // Lints are appended to `lints`. Note: multiple lints may be added. fn pgp_verify_signature(key: Option<&PgpDigParams>, sig: &PgpDigParams, mut ctx: digest::DigestContext, lints: &mut Vec) -> Result<()> { tracer!(*crate::TRACE, "pgp_verify_signature"); linter!($, lints); // Whether the verification relies on legacy cryptography. let mut legacy = false; let sig = sig.signature().ok_or_else(|| { Error::Fail("sig parameter does not designate a signature".into()) })?; let sig_id = || { let digest_prefix = sig.digest_prefix(); format!("{:02x}{:02x} created at {}", digest_prefix[0], digest_prefix[1], if let Some(t) = sig.signature_creation_time() { DateTime::::from(t) .format("%c").to_string() } else { "".to_string() }) }; let sig_time = if let Some(t) = sig.signature_creation_time() { t } else { return_err!( None, "Signature {} invalid: signature missing a creation time", sig_id()); }; // Allow some clock skew. if let Err(err) = sig.signature_alive(None, Duration::new(5 * 60, 0)) { return_err!( Some(err), "Signature {} invalid: signature is not alive", sig_id()); } { let policy = P.read().unwrap(); if let Err(err) = policy.signature(sig, Default::default()) { if NP.signature(sig, Default::default()).is_ok() { legacy = true; add_lint!( Some(err), "Signature {} invalid: signature relies on legacy cryptography", sig_id()); } else { return_err!( Some(err), "Signature {} invalid: policy violation", sig_id()); } } // XXX: As of sequoia-openpgp v1.11.0, this check is not done // by `policy.signature` (see issue #953). We do it manually, // but once rpm-sequoia depends on a newer version of // sequoia-openpgp that does this, remove this code. if let Err(err) = policy.packet(&Packet::from(sig.clone())) { if NP.packet(&Packet::from(sig.clone())).is_ok() { legacy = true; add_lint!( Some(err), "Signature {} invalid: signature relies on legacy cryptography", sig_id()); } else { return_err!( Some(err), "Signature {} invalid: policy violation", sig_id()); } } } // XXX: rpm only cares about the first issuer // subpacket. let issuer = match sig.get_issuers().into_iter().next() { Some(issuer) => issuer, None => return_err!( None, "Signature {} invalid: signature has no issuer subpacket", sig_id()), }; if let Some(key) = key { // Actually verify the signature. let cert = key.cert().ok_or_else(|| { Error::Fail("key parameter is not a cert".into()) })?; let subkey = key.key().expect("is a certificate").key().fingerprint(); t!("Checking signature {} using {} with {} / {}", sig_id(), sig.hash_algo(), cert.fingerprint(), subkey); // We evaluate the certificate as of the signature creation // time. let p = &*P.read().unwrap(); let vc = cert.with_policy(p, sig_time) .or_else(|err| { // Try again, but use the current time as a reference // time. It is quite common for old self-signatures // to be stripped. match cert.with_policy(p, None) { Ok(vc) => { // We'd really like to emit the following // lint, but for most users it is not // actionable. When the ecosystem changes so // that certificates include older self // signatures, enable it again. // add_lint!( // None, // "Certificate has no valid binding signature \ // as of the signature's creation time, but \ // is valid now. The certificate has probably \ // been stripped or minimized."); Ok(vc) } Err(err2) => { add_lint!( Some(err), "Certificate {} invalid: policy violation", cert.keyid()); Err(err2) } } }) .or_else(|err| { legacy = true; add_lint!( Some(err), "Certificate {} invalid: policy violation", cert.keyid()); cert.with_policy(NP, sig_time) })?; if let Err(err) = vc.alive() { legacy = true; add_lint!( Some(err), "Certificate {} invalid: certificate is not alive", vc.keyid()); } if let RevocationStatus::Revoked(_) = vc.revocation_status() { legacy = true; add_lint!( None, "Certificate {} invalid: certificate is revoked", vc.keyid()); } // Find the key. match vc.keys().key_handle(issuer.clone()).next() { Some(ka) => { if ka.key().fingerprint() != subkey { return_err!(None, "Key {} invalid: wrong subkey ({})", ka.key().keyid(), subkey); } if ! ka.for_signing() { return_err!(None, "Key {} invalid: not signing capable", ka.key().keyid()); } if let Err(err) = ka.alive() { legacy = true; add_lint!(Some(err), "Key {} invalid: key is not alive", ka.key().keyid()); } if let RevocationStatus::Revoked(_) = ka.revocation_status() { legacy = true; add_lint!(None, "Key {} is invalid: key is revoked", ka.key().keyid()); } // Finally we can verify the signature. sig.clone().verify_hash(ka.key(), ctx.ctx.clone())?; if legacy { return Err(Error::NotTrusted( "Verification relies on legacy crypto".into()) .into()); } else { return Ok(()); } } None => { return_err!(None, "Certificate {} does not contain key {} \ or it is not valid", vc.keyid(), issuer); } } } else { // We don't have a key, but we still check that the prefix is // correct. // These traits should be imported only where needed to avoid // bugs. use openpgp::serialize::Marshal; use openpgp::serialize::MarshalInto; // See https://datatracker.ietf.org/doc/html/rfc4880#section-5.2.4 let mut sig_data = Vec::with_capacity(128); // Hash the signature into the context. match sig.version() { 4 => { sig_data.push(sig.version()); sig_data.push(sig.typ().into()); sig_data.push(sig.pk_algo().into()); sig_data.push(sig.hash_algo().into()); let l = sig.hashed_area().serialized_len() as u16; sig_data.push((l >> 8) as u8); sig_data.push((l >> 0) as u8); sig.hashed_area().serialize(&mut sig_data).expect("vec"); let sig_len = sig_data.len(); // Trailer. sig_data.push(sig.version()); sig_data.push(0xFF); sig_data.push((sig_len >> 24) as u8); sig_data.push((sig_len >> 16) as u8); sig_data.push((sig_len >> 8) as u8); sig_data.push((sig_len >> 0) as u8); } 3 => { sig_data.push(sig.typ().into()); let ct = sig.signature_creation_time().unwrap_or(UNIX_EPOCH); let ct = ct.duration_since(UNIX_EPOCH) .map_err(|_| Error::Fail("time".into()))? .as_secs() as u32; sig_data.push((ct >> 24) as u8); sig_data.push((ct >> 16) as u8); sig_data.push((ct >> 8) as u8); sig_data.push((ct >> 0) as u8); } v => { return Err(Error::Fail( format!("Unsupported signature version: {}", v))); } } ctx.update(&sig_data); let digest_size = ctx.digest_size(); let mut digest: Vec = Vec::with_capacity(digest_size); for _ in 0..digest_size { digest.push(0); } ctx.digest(&mut digest[..])?; let p = sig.digest_prefix(); if p[0] != digest[0] || p[1] != digest[1] { return Err(Error::Fail("digest prefix mismatch".into())); } else { t!("digest prefix matches"); } return Err(Error::NoKey( format!("Not provided (issuer: {})", issuer).into())); } } ffi!( /// Returns the Key ID of the public key or the secret key stored in /// `pkt`. /// /// Returns -1 if `pkt` is not a public key or secret key. /// /// Note: this function does not handle public subkeys or secret /// subkeys! /// /// `keyid` must be allocated by the caller and points to at least 8 /// bytes of memory. /// /// Returns 0 on success and -1 on failure. fn _pgpPubkeyKeyID(pkt: *const u8, pktlen: size_t, keyid: *mut u8) -> Binary { let pkt = check_slice!(pkt, pktlen); let ppr = PacketParser::from_bytes(pkt)?; let k = if let PacketParserResult::Some(ref pp) = ppr { match &pp.packet { Packet::PublicKey(key) => Some(key.keyid()), Packet::SecretKey(key) => Some(key.keyid()), _ => None, } } else { None }; t!("Key ID: {}", k.as_ref() .map(|k| k.to_string()) .unwrap_or_else(|| String::from("none"))); if let Some(k) = k { let buffer = check_mut_slice!(keyid, 8); buffer.copy_from_slice(k.as_bytes()); Ok(()) } else { Err(Error::Fail("Not a key".into())) } }); ffi!( /// Calculate OpenPGP public key fingerprint. /// /// Returns -1 if `pkt` is not a public key or secret key. /// /// Note: this function does not handle public subkeys or secret /// subkeys! /// /// `*fprout` is allocated using `malloc` and must be allocated by the /// caller. /// /// Returns 0 on success and -1 on failure. fn _pgpPubkeyFingerprint(pkt: *const u8, pktlen: size_t, fprout: *mut *mut u8, fprlen: *mut size_t) -> Binary { let pkt = check_slice!(pkt, pktlen); let ppr = PacketParserBuilder::from_bytes(pkt)? .dearmor(Dearmor::Disabled) // Disable dearmoring. .build()?; let fpr = if let PacketParserResult::Some(ref pp) = ppr { match &pp.packet { Packet::PublicKey(key) => Some(key.fingerprint()), Packet::SecretKey(key) => Some(key.fingerprint()), _ => None, } } else { None }; t!("Fingerprint: {}", fpr.as_ref() .map(|fpr| fpr.to_string()) .unwrap_or_else(|| String::from("none"))); if let Some(fpr) = fpr { let fpr = fpr.as_bytes(); unsafe { let buffer = libc::malloc(fpr.len()); libc::memcpy(buffer, fpr.as_ptr() as *const c_void, fpr.len()); *fprout = buffer as *mut u8; *fprlen = fpr.len(); } Ok(()) } else { Err(Error::Fail("Not a key".into())) } }); ffi!( /// Wraps the data in ASCII armor. /// /// `atype` is the armor type. /// /// The caller must free the returned buffer. /// /// Returns `NULL` on failure. fn _pgpArmorWrap(atype: c_int, s: *const c_char, ns: size_t) -> *mut c_char { let atype = armor::Kind::try_from(PgpArmor::from(atype))?; let s = check_slice!(s, ns); let mut writer = armor::Writer::new(Vec::new(), atype) .map_err(|err| Error::Fail(format!("creating armor writer: {}", err)))?; writer.write(s) .map_err(|err| Error::Fail(format!("writing armor body: {}", err)))?; let mut buffer = writer.finalize() .map_err(|err| Error::Fail(format!("finalizing armor: {}", err)))?; // Add a trailing NUL. buffer.push(0); let ptr = buffer.as_mut_ptr() as *mut c_char; std::mem::forget(buffer); Ok(ptr) }); ffi!( /// Returns the length of the certificate in bytes. /// /// `pkts` points to a buffer. Fails if `pkts` does not point to /// exactly one valid OpenPGP certificate. /// /// Returns 0 on failure. fn _pgpPubKeyCertLen(pkts: *const u8, pktslen: size_t, certlen: *mut size_t) -> Binary { use openpgp::packet::Header; use openpgp::packet::header::PacketLengthType; use openpgp::packet::header::BodyLength; use openpgp::packet::header::CTB; use buffered_reader::BufferedReader; let pkts = check_slice!(pkts, pktslen); let certlen = check_mut!(certlen); // XXX: These functions are more or less copied from // sequoia/openpgp/src/parse.rs. When sequoia-openpgp makes them // public, we drop this copy. fn body_length_parse_new_format(bio: &mut T) -> openpgp::Result where T: BufferedReader, C: Debug + Send + Sync { let octet1 : u8 = bio.data_consume_hard(1)?[0]; match octet1 { 0..=191 => // One octet. Ok(BodyLength::Full(octet1 as u32)), 192..=223 => { // Two octets length. let octet2 = bio.data_consume_hard(1)?[0]; Ok(BodyLength::Full(((octet1 as u32 - 192) << 8) + octet2 as u32 + 192)) }, 224..=254 => // Partial body length. Ok(BodyLength::Partial(1 << (octet1 & 0x1F))), 255 => // Five octets. Ok(BodyLength::Full(bio.read_be_u32()?)), } } /// Decodes an old format body length as described in [Section /// 4.2.1 of RFC 4880]. /// /// [Section 4.2.1 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-4.2.1 fn body_length_parse_old_format(bio: &mut T, length_type: PacketLengthType) -> openpgp::Result where T: BufferedReader, C: Debug + Send + Sync { match length_type { PacketLengthType::OneOctet => Ok(BodyLength::Full(bio.data_consume_hard(1)?[0] as u32)), PacketLengthType::TwoOctets => Ok(BodyLength::Full(bio.read_be_u16()? as u32)), PacketLengthType::FourOctets => Ok(BodyLength::Full(bio.read_be_u32()? as u32)), PacketLengthType::Indeterminate => Ok(BodyLength::Indeterminate), } } fn parse_header(bio: &mut T) -> openpgp::Result
where T: BufferedReader, C: Debug + Send + Sync { let ctb = CTB::try_from(bio.data_consume_hard(1)?[0])?; let length = match ctb { CTB::New(_) => body_length_parse_new_format(bio)?, CTB::Old(ref ctb) => body_length_parse_old_format(bio, ctb.length_type())?, }; return Ok(Header::new(ctb, length)); } let mut br = buffered_reader::Memory::new(pkts); let mut found_cert = false; let len: Option = loop { // The start of this packet as a byte offset into buffer. let start_of_packet = br.total_out(); if start_of_packet == pkts.len() { // We're done. break Some(start_of_packet); } let header = match parse_header(&mut br) { Ok(header) => header, Err(err) => { t!("Error reading certificate at offset {}: {}", start_of_packet, err); break None; } }; use Tag::*; let t = header.ctb().tag(); t!("Found a {:?} at offset {}, length: {:?}", t, start_of_packet, header.length()); match t { // Start of a new certificate. PublicKey | SecretKey => { if found_cert { break Some(start_of_packet); } else { found_cert = true; } } // The body of a certificate. PublicSubkey | SecretSubkey | UserID | UserAttribute | Signature | Marker | Trust | Unknown(_) | Private(_) => { if start_of_packet == 0 { t!("Encountered a ({:?}) at offset {}, \ which is not a valid start of a certificate", t, start_of_packet); break None; } } Reserved | PKESK | SKESK | OnePassSig | CompressedData | SED | Literal | SEIP | MDC | AED => { t!("Encountered a ({:?}) at offset {}, \ which does not belong in a certificate", t, start_of_packet); break None; } t => if t.is_critical() { t!("Encountered a ({:?}) at offset {}, \ which does not belong in a certificate", t, start_of_packet); break None; } else { // Ignore unknown non-critical packet. }, } // Advance to the next packet. match header.length() { BodyLength::Full(l) => { let l = *l as usize; if let Err(err) = br.data_consume_hard(l) { t!("Error while reading packet: {}", err); break None; } } BodyLength::Partial(_) => { t!("Packet {} has partial body length, \ which is unsupported by keyring splitter", t); break None; } BodyLength::Indeterminate => { t!("Packet {} has intedeterminite length, \ which is unsupported by keyring splitter", t); break None; } } }; if let Some(len) = len { *certlen = len; Ok(()) } else { Err(Error::Fail("No certificate found".into())) } }); ffi!( /// Parses OpenPGP data. /// /// If `pkts` contains a signature and `pkttype` is 0 or /// `Tag::Signature`, this returns a `PgpDigParams` containing a /// signature. /// /// If `pkts` contains a certificate and `pkttype` is 0, /// `Tag::PublicKey`, or `Tag::SecretKey`, this returns a /// `PgpDigParams` containing a certificate. The certificate is /// checked for validity in the sense that it only contains packets /// that belong to a certificate; this function does **not** check the /// binding signatures, etc. That check is done when the key is used /// in [_pgpVerifySignature]. /// /// Returns 0 on success, -1 on failure. fn _pgpPrtParams(pkts: *const u8, pktlen: size_t, pkttype: c_uint, paramsp: *mut *mut PgpDigParams) -> Binary { match _pgpPrtParams2(pkts, pktlen, pkttype, paramsp, std::ptr::null_mut()) { 0 => Ok(()), ec => Err(Error::from(ec)), } }); ffi!( /// Like _pgpPrtParams, but returns error messages and lints in /// `lint_str`. fn _pgpPrtParams2(pkts: *const u8, pktlen: size_t, pkttype: c_uint, paramsp: *mut *mut PgpDigParams, lint_str: *mut *mut c_char) -> Binary { let mut lint_str: Option<&mut _> = check_optional_mut!(lint_str); if let Some(lint_str) = lint_str.as_mut() { **lint_str = std::ptr::null_mut(); } let mut lints = Vec::new(); let r = pgp_prt_params(pkts, pktlen, pkttype, paramsp, &mut lints); // Return any lint / error messages. if lints.len() > 0 { let mut s: String = format!("Parsing an OpenPGP packet:"); // Indent the lints. let sep = "\n "; let lints_count = lints.len(); for (err_i, err) in lints.into_iter().enumerate() { for (cause_i, cause) in error_chain(&err).into_iter().enumerate() { if cause_i == 0 { s.push_str(sep); if lints_count > 1 { s.push_str(&format!("{}. ", err_i + 1)); } } else { s.push_str(sep); s.push_str(" because: "); } s.push_str(&cause); } } t!("Lints: {}", s); if let Some(lint_str) = lint_str.as_mut() { // Add a trailing NUL. s.push('\0'); **lint_str = s.as_mut_ptr() as *mut c_char; // Pass ownership to the caller. std::mem::forget(s); } } r }); fn pgp_prt_params(pkts: *const u8, pktlen: size_t, pkttype: c_uint, paramsp: *mut *mut PgpDigParams, lints: &mut Vec) -> Result<()> { tracer!(*crate::TRACE, "pgp_prt_params"); linter!($, lints); let pkttype: Option = if pkttype == 0 { None } else { Some(Tag::from(pkttype as u8)) }; let pkts = check_slice!(pkts, pktlen); let paramsp = check_mut!(paramsp); *paramsp = std::ptr::null_mut(); let ppr = PacketParser::from_bytes(pkts)?; let (obj, issuer, userid) = if let PacketParserResult::Some(pp) = ppr { // Process the packet. match pp.packet { Packet::Signature(_) if pkttype.is_none() || pkttype == Some(Tag::Signature) => { let (packet, next_ppr) = pp.next()?; if let PacketParserResult::Some(p) = next_ppr { return_err!(None, "Expected a bare OpenPGP signature, \ but it's followed by a {}", p.packet.tag()); } let sig = if let Packet::Signature(sig) = packet { sig } else { panic!("it's a sig"); }; (PgpDigParamsObj::Signature(sig.clone()), // XXX: Although there is normally only one issuer // subpacket, there may be multiple such subpackets. // Unfortunately, the API only allows us to return // one. sig.get_issuers().into_iter().next() .map(|i| i.as_bytes().to_vec()), None) } Packet::PublicKey(_) | Packet::SecretKey(_) if pkttype.is_none() || pkttype == Some(Tag::PublicKey) || pkttype == Some(Tag::SecretKey) => { let cert = match CertParser::from(PacketParserResult::Some(pp)).next() { Some(Ok(cert)) => cert, Some(Err(err)) => return_err!( Some(err), "Failed to read an OpenPGP certificate"), None => return_err!( None, "Failed to read an OpenPGP certificate"), }; let keyid = cert.keyid().as_bytes().to_vec(); let userid = if let Ok(vc) = cert.with_policy(&*P.read().unwrap(), None) { vc.primary_userid() .ok() .and_then(|u| { CString::new(u.userid().value()).ok() }) } else { None }; (PgpDigParamsObj::Cert(cert), Some(keyid), userid) } Packet::Unknown(mut u) => { let mut err = u.set_error(anyhow::anyhow!("Error")); if let Some(openpgp::Error::MalformedMPI(_)) = err.downcast_ref::() { err = err.context("\ Signature appears to be created by a \ non-conformant OpenPGP implementation, see \ ."); } return_err!(Some(err), "Failed to parse {}", u.tag()); } ref p => { return_err!( None, "Unexpected OpenPGP packet in this context {}", p.tag()); } } } else { return_err!( None, "Expected an OpenPGP packet, encountered the end of the file"); }; let mut buffer: [u8; 8] = [0; 8]; if let Some(issuer) = issuer { let issuer = if issuer.len() > buffer.len() { // We've got a fingerprint. For v4 keys, the last 16 // bytes is the key id. &issuer[issuer.len() - buffer.len()..] } else { &issuer[..] }; for (i, c) in issuer.into_iter().enumerate() { buffer[i] = *c as u8; } } *paramsp = move_to_c!(PgpDigParams { obj, signid: buffer, userid: userid, }); Ok(()) } ffi!( /// Returns a `PgpDigParams` data structure for each subkey. /// /// This does not return a `PgpDigParams` for the primary (just use /// this one). The subkeys are **not** checked for validity. That /// check is done when the key is used in [_pgpVerifySignature]. fn _pgpPrtParamsSubkeys(pkts: *const u8, pktlen: size_t, _mainkey: *const PgpDigParams, subkeys: *mut *mut PgpDigParams, subkeys_count: *mut c_int) -> Binary { let pkts = check_slice!(pkts, pktlen); let subkeys = check_mut!(subkeys); *subkeys = std::ptr::null_mut(); let subkeys_count = check_mut!(subkeys_count); let ppr = PacketParser::from_bytes(pkts)?; let cert = match ppr { PacketParserResult::Some(ref pp) => { match pp.packet { Packet::PublicKey(_) | Packet::SecretKey(_) => { let cert = CertParser::from(ppr) .next() .ok_or(Error::Fail("Not an OpenPGP certificate".into()))??; cert } ref p => { return Err(Error::Fail(format!("{}", p.tag()))); } } } _ => return Err(Error::Fail("Not an OpenPGP message".into())), }; let userid = if let Ok(vc) = cert.with_policy(&*P.read().unwrap(), None) { vc.primary_userid() .ok() .and_then(|u| { CString::new(u.userid().value()).ok() }) } else { None }; // We return all subkeys here. Subkeys are checked for validity // on demand. let mut keys: Vec<*mut PgpDigParams> = cert .keys().subkeys() .map(|ka| { t!("Subkey: {}", ka.key().keyid()); let zeros = [0; 8]; let mut dig = PgpDigParams { obj: PgpDigParamsObj::Subkey(cert.clone(), ka.key().fingerprint()), signid: zeros, userid: userid.clone(), }; dig.signid.copy_from_slice(ka.key().keyid().as_bytes()); move_to_c!(dig) }) .collect(); t!("Got {} subkeys", keys.len()); *subkeys_count = keys.len() as c_int; if keys.len() == 0 { *subkeys = std::ptr::null_mut(); } else { *subkeys = keys.as_mut_ptr() as *mut PgpDigParams; // Pass ownership to the caller. std::mem::forget(keys); } Ok(()) }); ffi!( /// Strips the ASCII armor and returns the decoded data in `pkt`. /// /// Despite its name, this function does not actually parse any OpenPGP /// packets; it just strips the ASCII armor encoding. /// /// Returns the type of armor on success (>0) or an error code /// indicating the type of failure (<0). fn _pgpParsePkts(armor: *const c_char, pkt: *mut *mut c_char, pktlen: *mut size_t) -> PgpArmor { let armor = check_cstr!(armor); let pkt = check_mut!(pkt); *pkt = std::ptr::null_mut(); let pktlen = check_mut!(pktlen); let mut reader = armor::Reader::from_reader( std::io::BufReader::new( armor.to_str().map_err(|_| PgpArmorError::BodyDecode)?.as_bytes()), armor::ReaderMode::Tolerant(None)); let mut buf = Vec::new(); reader.read_to_end(&mut buf).map_err(|_| PgpArmorError::BodyDecode)?; let kind = reader.kind(); *pktlen = buf.len() as size_t; *pkt = buf.as_mut_ptr() as *mut c_char; // Pass ownership to the caller. std::mem::forget(buf); Ok(kind.into()) }); ffi!( /// Lints the first certificate in pkts. /// /// This function links the certificate according to the current /// [policy]. It warns about things like unusable subkeys, because they /// do not have a valid binding signature. It will also generate a /// warning if there are no valid, signing-capable keys. /// /// There are four cases: /// /// - The packets do not describe a certificate: returns an error and /// sets `*explanation` to `NULL`. /// /// - The packets describe a certificate and the certificate is /// completely unusable: returns an error and sets `*explanation` to /// a human readable explanation. /// /// - The packets describe a certificate and some components are not /// usable: returns success, and sets `*explanation` to a human /// readable explanation. /// /// - The packets describe a certificate and there are no lints: /// returns success, and sets `*explanation` to `NULL`. /// /// [policy]: index.html#policy fn _pgpPubKeyLint(pkts: *const c_char, pktslen: size_t, explanation: *mut *mut c_char) -> ErrorCode { let pkts = check_slice!(pkts, pktslen); let explanation = check_mut!(explanation); // Make sure we always set explanation to something. *explanation = std::ptr::null_mut(); let cert = CertParser::from_bytes(pkts)?.next() .ok_or(Error::Fail("Not an OpenPGP certificate".into()))??; let mut lints: Vec = Vec::new(); let mut lint = |l: &str| { lints.push(l.into()); }; let usable = 'done : loop { match cert.with_policy(&*P.read().unwrap(), None) { Err(err) => { lint(&format!("Policy rejects {}: {}", cert.keyid(), err)); break 'done false; } Ok(vc) => { if let RevocationStatus::Revoked(revs) = vc.revocation_status() { for rev in revs { if let Some((reason, msg)) = rev.reason_for_revocation() { let mut l = format!( "The certificate was revoked: {}", reason); if ! msg.is_empty() { l.push_str(&format!( ", {}", String::from_utf8_lossy(msg))); } lint(&l); } else { lint("The certificate was revoked: \ unspecified reason"); } } } if let Err(err) = vc.alive() { if let Some(e) = vc.primary_key().key_expiration_time() { if e <= SystemTime::now() { lint(&format!("The certificate is expired: {}", err)); } else { lint(&format!("The certificate is not live: {}", err)); } } } } }; let mut have_signing = false; for ka in cert.keys() { let keyid = ka.key().keyid(); match ka.with_policy(&*P.read().unwrap(), None) { Err(err) => { lint(&format!("Policy rejects subkey {}: {}", keyid, err)); continue; } Ok(ka) => { if ! ka.for_signing() { // Silently ignore non-signing capable // subkeys. We don't care about them. continue; } if let RevocationStatus::Revoked(revs) = ka.revocation_status() { for rev in revs { if let Some((reason, msg)) = rev.reason_for_revocation() { let mut l = format!( "Subkey {} was revoked: {}", keyid, reason); if ! msg.is_empty() { l.push_str(&format!( ", {}", String::from_utf8_lossy(msg))); } lint(&l); } else { lint(&format!( "Subkey {} was revoked: \ unspecified reason", keyid)); } } continue; } if let Err(err) = ka.alive() { if let Some(e) = ka.key_expiration_time() { if e <= SystemTime::now() { lint(&format!("Subkey {} is expired: {}", keyid, err)); } else { lint(&format!("Subkey {} is not live: {}", keyid, err)); } } continue; } if ! ka.key().pk_algo().is_supported() { lint(&format!("Subkey {} is not supported \ (no support for {})", keyid, ka.key().pk_algo())); continue; } have_signing = true; } } } if ! have_signing { lint("Certificate does not have any usable signing keys"); } break true; }; if ! lints.is_empty() { // Indent the lints. let sep = "\n "; let mut s: String = format!("Certificate {}:{}", cert.keyid(), sep); s.push_str(&lints.join(sep)); s.push('\0'); *explanation = s.as_mut_ptr() as *mut c_char; // Pass ownership to the caller. std::mem::forget(s); } if usable { Ok(()) } else { Err(Error::Fail(format!("Certificate {} is unusable", cert.keyid()))) } }); /// An optional OpenPGP certificate *and* an optional signature. /// /// This data structure is deprecated and is scheduled for removal in /// rpm 4.19. pub struct PgpDig { cert: Option>, sig: Option>, } /// Dump the packets to stderr. /// /// This is used by _pgpPrtPkts, which is deprecated and is scheduled /// for removal in rpm 4.19. It is intended to be bug compatible with /// rpm's internal implementation. fn dump_packets(pkts: &[u8]) -> Result<()> { use openpgp::types::CompressionAlgorithm; use openpgp::types::KeyServerPreferences; use openpgp::types::PublicKeyAlgorithm; use openpgp::types::SignatureType; use openpgp::types::SymmetricAlgorithm; use openpgp::packet::signature::subpacket::Subpacket; use openpgp::packet::signature::subpacket::SubpacketTag; use openpgp::packet::signature::subpacket::SubpacketValue; let mut ppr = PacketParser::from_bytes(pkts)?; fn pk_algo(a: PublicKeyAlgorithm) -> &'static str { use PublicKeyAlgorithm::*; #[allow(deprecated)] match a { RSAEncryptSign => "RSA", RSAEncrypt => "RSA(Encrypt-Only)", RSASign => "RSA(Sign-Only)", ElGamalEncrypt => "Elgamal(Encrypt-Only)", DSA => "DSA", ECDH => "Elliptic Curve", ECDSA => "ECDSA", ElGamalEncryptSign => "Elgamal", EdDSA => "EdDSA", _ => "Unknown public key algorithm", } } fn sigtype(t: SignatureType) -> &'static str { use SignatureType::*; match t { Binary => "Binary document signature", Text => "Text document signature", Standalone => "Standalone signature", GenericCertification => "Generic certification of a User ID and Public Key", PersonaCertification => "Persona certification of a User ID and Public Key", CasualCertification => "Casual certification of a User ID and Public Key", PositiveCertification => "Positive certification of a User ID and Public Key", SubkeyBinding => "Subkey Binding Signature", PrimaryKeyBinding => "Primary Key Binding Signature", DirectKey => "Signature directly on a key", KeyRevocation => "Key revocation signature", SubkeyRevocation => "Subkey revocation signature", CertificationRevocation => "Certification revocation signature", Timestamp => "Timestamp signature", _ => "Unknown signature type", } } fn symalgo(a: SymmetricAlgorithm) -> &'static str { use SymmetricAlgorithm::*; #[allow(deprecated)] match a { Unencrypted => "Plaintext", IDEA => "IDEA", TripleDES => "3DES", CAST5 => "CAST5", Blowfish => "BLOWFISH", AES128 => "AES(128-bit key)", AES192 => "AES(192-bit key)", AES256 => "AES(256-bit key)", Twofish => "TWOFISH(256-bit key)", _ => "Unknown symmetric key algorithm", } } fn compalgo(a: CompressionAlgorithm) -> &'static str { use CompressionAlgorithm::*; match a { Uncompressed => "Uncompressed", Zip => "ZIP", Zlib => "ZLIB", BZip2 => "BZIP2", _ => "Unknown compression algorithm", } } fn ksprefs(prefs: KeyServerPreferences) -> &'static str { // This is wrong, but this is what the internal implementation // does. if prefs.no_modify() { "No-modify(128)" } else if KeyServerPreferences::empty().normalized_eq(&prefs) { "" } else { "Unknown key server preference" } } fn subpacket(sp: &Subpacket) -> String { let mut output: Vec = Vec::new(); let tag = sp.tag(); let s = { use SubpacketTag::*; match tag { SignatureCreationTime => "signature creation time", SignatureExpirationTime => "signature expiration time", ExportableCertification => "exportable certification", TrustSignature => "trust signature", RegularExpression => "regular expression", Revocable => "revocable", KeyExpirationTime => "key expiration time", PlaceholderForBackwardCompatibility => "additional recipient request", PreferredSymmetricAlgorithms => "preferred symmetric algorithms", RevocationKey => "revocation key", Issuer => "issuer key ID", NotationData => "notation data", PreferredHashAlgorithms => "preferred hash algorithms", PreferredCompressionAlgorithms => "preferred compression algorithms", KeyServerPreferences => "key server preferences", PreferredKeyServer => "preferred key server", PrimaryUserID => "primary user id", PolicyURI => "policy URL", KeyFlags => "key flags", SignersUserID => "signer's user id", ReasonForRevocation => "reason for revocation", Features => "features", EmbeddedSignature => "embedded signature", _ => "Unknown signature subkey type", } }; output.push(s.into()); output.push(format!("({})", Into::::into(tag))); if sp.critical() { output.push(" *CRITICAL*".into()); } { use SubpacketValue::*; match sp.value() { PreferredSymmetricAlgorithms(algos) => { output.push(" ".into()); output.push( algos.iter() .map(|a| { format!("{}({})", symalgo(*a), Into::::into(*a)) }) .collect::>() .join(" ")) } PreferredHashAlgorithms(algos) => { output.push(" ".into()); output.push( algos.iter() .map(|a| { format!("{}({})", a.to_string(), Into::::into(*a)) }) .collect::>() .join(" ")) } PreferredCompressionAlgorithms(algos) => { output.push(" ".into()); output.push( algos.iter() .map(|a| { format!("{}({})", compalgo(*a), Into::::into(*a)) }) .collect::>() .join(" ")) } KeyServerPreferences(prefs) => { output.push(format!(" {}", ksprefs(prefs.clone()))) } SignatureExpirationTime(d) | KeyExpirationTime(d) => { // expiration time is an offset from the creation // time, but rpm's internal OpenPGP implementation // treats it as an absolute time. As we're going // for bug-for-bug compatibility here, we do the // same. let t = DateTime::from_timestamp( d.as_secs() as i64, 0) // This is just compatibility, debugging // output. Fallback to the unix epoch. .unwrap_or_default(); output.push(format!(" {}(0x{:08x})", t.format("%c"), d.as_secs())); } SignatureCreationTime(_) | Issuer(_) | KeyFlags(_) => (), _ => { use sequoia_openpgp::serialize::MarshalInto; output.push(" ".into()); output.extend( sp.value() .to_vec() .unwrap_or(Vec::new()) .into_iter() .map(|b| format!("{:02x}", b))) } } } output.join("") } while let PacketParserResult::Some(pp) = ppr { let (packet, next_ppr) = pp.recurse()?; ppr = next_ppr; // We only dump what rpm's internal OpenPGP implementation // dumps. Other packets we silently ignore. #[allow(deprecated)] match packet { Packet::Signature(sig) => { // V4 Signature(2) DSA(17) SHA512(10) Generic certification of a User ID and Public Key(16) // signature creation time(2) // issuer key ID(16) // signhash16 1418 eprintln!("V{} Signature(2) {}({}) {}({}) {}({})", sig.version(), pk_algo(sig.pk_algo()), Into::::into(sig.pk_algo()), sig.hash_algo().to_string(), Into::::into(sig.hash_algo()), sigtype(sig.typ()), Into::::into(sig.typ())); sig.hashed_area().iter().for_each(|sb| { eprintln!(" {}", subpacket(sb)); }); sig.unhashed_area().iter().for_each(|sb| { eprintln!(" {}", subpacket(sb)); }); eprintln!(" signhash16 {:02x}{:02x}", sig.digest_prefix()[0], sig.digest_prefix()[1]); }, Packet::PublicKey(key) => { // V4 Public Key(6) RSA(1) Tue Apr 7 08:52:57 2015(0x55239ae9) let secs = key.creation_time() .duration_since(SystemTime::UNIX_EPOCH) .map(|d| d.as_secs()) .unwrap_or(0); let t: DateTime:: = key.creation_time().into(); eprintln!("V{} Public Key(6) {}({}) {}(0x{:08x})", key.version(), pk_algo(key.pk_algo()), Into::::into(key.pk_algo()), t.format("%c"), secs); } Packet::PublicSubkey(key) => { // Public Subkey(14) 045523a696010... use sequoia_openpgp::serialize::MarshalInto; let secs = key.creation_time() .duration_since(SystemTime::UNIX_EPOCH) .map(|d| d.as_secs()) .unwrap_or(0); eprintln!("Public Subkey(14) {:02}{:08x}{:02x}{}", key.version(), secs, Into::::into(key.pk_algo()), key.mpis().to_vec() .unwrap_or_else(|_| Vec::new()) .into_iter() .map(|b| format!("{:02x}", b)) .collect::()); } Packet::UserID(userid) => { // User ID(13) "Neal H. Walfield " eprintln!("User ID(13) {:?}", String::from_utf8_lossy(userid.value())); } Packet::Unknown(_pkt) => (), Packet::OnePassSig(_ops) => (), Packet::SecretKey(_key) => (), Packet::SecretSubkey(_key) => (), Packet::Marker(_marker) => (), Packet::Trust(_trust) => (), Packet::UserAttribute(_ua) => (), Packet::Literal(_lit) => (), Packet::CompressedData(_cd) => (), Packet::PKESK(_pkesk) => (), Packet::SKESK(_skesk) => (), Packet::SEIP(_seip) => (), Packet::MDC(_mdc) => (), _ => (), } } Ok(()) } ffi!( /// Parses and optionally prints to stdout a OpenPGP packet(s). /// /// This function is deprecated and is scheduled for removal in rpm /// 4.19. /// /// @param pkts OpenPGP packet(s) /// @param pktlen OpenPGP packet(s) length (no. of bytes) /// @param(out) dig parsed output of signature/pubkey packet parameters /// @param printing should packets be printed? /// /// Returns 0 on success, -1 on failure. fn _pgpPrtPkts(pkts: *const u8, pktslen: size_t, dig: *mut PgpDig, printing: c_int) -> Binary { let dig = check_mut!(dig); let mut params: *mut PgpDigParams = std::ptr::null_mut(); if printing != 0 { // We ignore any error here as this printing should not change // the functions semantics. let _ = dump_packets(check_slice!(pkts, pktslen)); } let result = _pgpPrtParams(pkts, pktslen, 0, &mut params); if result == -1 { return Err(Error::Fail("Parse error".into())); } let params = claim_from_c!(params); match params.obj { PgpDigParamsObj::Cert(_) => dig.cert = Some(params), PgpDigParamsObj::Subkey(_, _) => dig.cert = Some(params), PgpDigParamsObj::Signature(_) => dig.sig = Some(params), } Ok(()) }); ffi!( /// Create a container for parsed OpenPGP packet(s). /// /// This function is deprecated and is scheduled for removal in rpm /// 4.19. /// /// @return container fn _pgpNewDig() -> *mut PgpDig { Ok(move_to_c!(PgpDig { cert: None, sig: None, })) }); ffi!( /// Release (malloc'd) data from container. /// /// This function is deprecated and is scheduled for removal in rpm /// 4.19. /// /// @param dig container fn _pgpCleanDig(dig: *mut PgpDig) { let dig = check_mut!(dig); dig.cert = None; dig.sig = None; }); ffi!( /// Destroy a container for parsed OpenPGP packet(s). /// /// This function is deprecated and is scheduled for removal in rpm /// 4.19. /// /// @param dig container /// @return NULL always fn _pgpFreeDig(dig: Option<&mut PgpDig>) -> *mut PgpDig { free!(dig); Ok(std::ptr::null_mut()) }); ffi!( /// Retrieve parameters for parsed OpenPGP packet(s). /// /// This function is deprecated and is scheduled for removal in rpm /// 4.19. /// /// @param dig container /// @param pkttype type of params to retrieve (signature / pubkey) /// @return pointer to OpenPGP parameters, NULL on error/not found fn _pgpDigGetParams(dig: *const PgpDig, pkttype: c_uint) -> *const PgpDigParams { let dig = check_ptr!(dig); let ptr = match Tag::from(pkttype as u8) { Tag::PublicKey => { if let Some(ref cert) = dig.cert { cert.as_ref() } else { std::ptr::null() } } Tag::Signature => { if let Some(ref sig) = dig.sig { sig.as_ref() } else { std::ptr::null() } } _ => { std::ptr::null() } }; Ok(ptr) }); ffi!( /// Verify a PGP signature. /// /// This function is deprecated and is scheduled for removal in rpm /// 4.19. /// /// @param dig container /// @param hashctx digest context /// @return RPMRC_OK on success fn _pgpVerifySig(dig: *const PgpDig, ctx: *const digest::DigestContext) -> ErrorCode { Err( _pgpVerifySignature( _pgpDigGetParams(dig, u8::from(Tag::PublicKey) as u32), _pgpDigGetParams(dig, u8::from(Tag::Signature) as u32), ctx).into()) }); ffi!( /// Merge the PGP packets of two certificates /// /// The certificates must describe the same public key. The call should merge /// important pgp packets (self-signatures, new subkeys, ...) and remove duplicates. /// /// - `pkts1` - OpenPGP pointer to a buffer with the first certificate /// - `pkts1len` - length of the buffer with the first certificate /// - `pkts2` - OpenPGP pointer to a buffer with the second certificate /// - `pkts2len` - length of the buffer with the second certificate /// - `pktsm` - merged certificate (malloced) /// - `pktsmlen` - length of merged certificate /// - `flags` - merge flags (currently not used, must be zero) /// /// Returns `RPMRC_OK` on success. fn _pgpPubkeyMerge( pkts1: *const u8, pkts1len: size_t, pkts2: *const u8, pkts2len: size_t, pktsm: *mut *mut u8, pktsmlen: *mut size_t, flags: c_int) -> ErrorCode { let pkts1 = check_slice!(pkts1, pkts1len); let pkts2 = check_slice!(pkts2, pkts2len); let cert1 = Cert::from_bytes(pkts1)?; let cert2 = Cert::from_bytes(pkts2)?; if cert1.fingerprint() != cert2.fingerprint() { return Err(Error::Fail("Can't merge different certificates".into())); } let (merged, updated) = cert1.clone().insert_packets(cert2.into_packets())?; let merged_bytes_; let (result, result_len) = if ! updated { // The certificate is unchanged. Nevertheless, // Cert::from_bytes may have changed the bit representation, // e.g., by canonicalizing it. To avoid making rpm think that // the certificate has changed when it hasn't (it does a // memcmp), we return pkts1. (pkts1.as_ptr(), pkts1len) } else { merged_bytes_ = merged.to_vec()?; (merged_bytes_.as_ptr(), merged_bytes_.len()) }; unsafe { let buffer = libc::malloc(result_len); libc::memcpy(buffer, result as *const c_void, result_len); *pktsmlen = result_len as size_t; *pktsm = buffer as *mut u8; } Ok(()) }); #[cfg(test)] mod tests { use super::*; use openpgp::cert::CertBuilder; use openpgp::serialize::SerializeInto; use openpgp::types::KeyFlags; // Check that we can successfully merge two certificates. #[test] fn merge_certs() { let p = openpgp::policy::StandardPolicy::new(); let (cert, _rev) = CertBuilder::new() .add_userid("Alice") .generate() .unwrap(); let vc = cert.with_policy(&p, None).unwrap(); // We should only have a primary, which is certification capable. assert_eq!(vc.keys().for_signing().count(), 0); assert_eq!(vc.keys().for_transport_encryption().count(), 0); assert_eq!(vc.keys().for_storage_encryption().count(), 0); // Add a signing subkey. let cert2 = KeyBuilder::new( KeyFlags::empty().set_signing()) .subkey(vc.clone()).unwrap() .attach_cert().unwrap(); // Add an encryption subkey. let cert3 = KeyBuilder::new( KeyFlags::empty().set_transport_encryption()) .subkey(vc.clone()).unwrap() .attach_cert().unwrap(); let cert2_bytes = cert2.to_vec().unwrap(); let cert3_bytes = cert3.to_vec().unwrap(); let mut result: *mut u8 = std::ptr::null_mut(); let mut result_len: size_t = 0; eprintln!("About to run pgpPubkeyMerge"); let ec = _pgpPubkeyMerge( cert2_bytes.as_ptr(), cert2_bytes.len(), cert3_bytes.as_ptr(), cert3_bytes.len(), &mut result, &mut result_len, 0); assert_eq!(ec, 0); assert!(! result.is_null()); let result = unsafe { std::slice::from_raw_parts(result as *const u8, result_len) }; let result = Cert::from_bytes(result).expect("valid cert"); assert_eq!(cert.fingerprint(), result.fingerprint()); assert!(result != cert); assert!(result != cert2); assert!(result != cert3); let expected = cert2.clone().merge_public(cert3.clone()).unwrap(); assert_eq!(result, expected); let result_vc = result.with_policy(&p, None).unwrap(); assert_eq!(result_vc.keys().for_signing().count(), 1); assert_eq!(result_vc.keys().for_transport_encryption().count(), 1); assert_eq!(result_vc.keys().for_storage_encryption().count(), 0); } // Check that when we attempt to merge two different certificates, // we return an error. #[test] fn merge_certs_mismatch() { let (cert, _rev) = CertBuilder::new() .add_userid("Alice") .generate() .unwrap(); let (cert2, _rev) = CertBuilder::new() .add_userid("Bob") .generate() .unwrap(); let cert_bytes = cert.to_vec().unwrap(); let cert2_bytes = cert2.to_vec().unwrap(); let mut result: *mut u8 = std::ptr::null_mut(); let mut result_len: size_t = 0; eprintln!("About to run pgpPubkeyMerge"); let ec = _pgpPubkeyMerge( cert_bytes.as_ptr(), cert_bytes.len(), cert2_bytes.as_ptr(), cert2_bytes.len(), &mut result, &mut result_len, 0); assert_ne!(ec, 0); } } rpm-sequoia-1.8.0/src/log.rs000064400000000000000000000073371046102023000140240ustar 00000000000000use std::cell::RefCell; // Like eprintln! macro_rules! log { ($dst:expr $(,)?) => ( eprintln!("{}", $dst) ); ($dst:expr, $($arg:tt)*) => ( eprintln!("{}", std::format!($dst, $($arg)*)) ); } // The indent level. It is increased with each call to tracer and // decremented when the tracker goes out of scope. thread_local! { pub static INDENT_LEVEL: RefCell = RefCell::new(0); } // Like eprintln!, but the first argument is a boolean, which // indicates if the string should actually be printed. macro_rules! trace { ( $TRACE:expr, $fmt:expr, $($pargs:expr),* ) => { if $TRACE { let indent_level = crate::log::INDENT_LEVEL.with(|i| { *i.borrow() }); let ws = " "; log!("{}{}", &ws[0..std::cmp::min(ws.len(), std::cmp::max(1, indent_level) - 1)], format!($fmt, $($pargs),*)); } }; ( $TRACE:expr, $fmt:expr ) => { trace!($TRACE, $fmt, ); }; } macro_rules! tracer { ( $TRACE:expr, $func:expr ) => { // Currently, Rust doesn't support $( ... ) in a nested // macro's definition. See: // https://users.rust-lang.org/t/nested-macros-issue/8348/2 macro_rules! t { ( $fmt:expr ) => { trace!($TRACE, "{}: {}", $func, $fmt) }; ( $fmt:expr, $a:expr ) => { trace!($TRACE, "{}: {}", $func, format!($fmt, $a)) }; ( $fmt:expr, $a:expr, $b:expr ) => { trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b)) }; ( $fmt:expr, $a:expr, $b:expr, $c:expr ) => { trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c)) }; ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr ) => { trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d)) }; ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr ) => { trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d, $e)) }; ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr ) => { trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d, $e, $f)) }; ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr ) => { trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d, $e, $f, $g)) }; ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr ) => { trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d, $e, $f, $g, $h)) }; ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr ) => { trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d, $e, $f, $g, $h, $i)) }; ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr, $j:expr ) => { trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d, $e, $f, $g, $h, $i, $j)) }; ( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr, $j:expr, $k:expr ) => { trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k)) }; } struct Indent {} impl Indent { fn init() -> Self { crate::log::INDENT_LEVEL.with(|i| { i.replace_with(|i| *i + 1); }); Indent {} } } impl Drop for Indent { fn drop(&mut self) { crate::log::INDENT_LEVEL.with(|i| { i.replace_with(|i| *i - 1); }); } } let _indent = Indent::init(); } } rpm-sequoia-1.8.0/src/rpm.rs000064400000000000000000000117301046102023000140310ustar 00000000000000use libc::c_int; use sequoia_openpgp as openpgp; use openpgp::armor; // We use Error rather than anyhow's error so that we force the // function to convert the error into a form that we can easily pass // back to the engine. pub type Result = std::result::Result; pub type ErrorCode = c_int; #[non_exhaustive] #[derive(thiserror::Error, Debug, Clone)] #[allow(unused)] pub enum Error { #[error("Success")] Ok, #[error("Not found: {0}")] NotFound(String), #[error("Failure: {0}")] Fail(String), #[error("Signature is OK, but key is not trusted: {0}")] NotTrusted(String), #[error("Public key is unavailable: {0}")] NoKey(String), } impl From for ErrorCode { fn from(err: Error) -> ErrorCode { match err { Error::Ok => 0, Error::NotFound(_) => 1, Error::Fail(_) => 2, Error::NotTrusted(_) => 3, Error::NoKey(_) => 4, } } } impl From for Error { fn from(err: ErrorCode) -> Error { match err { 0 => Error::Ok, 1 => Error::NotFound("".into()), 2 => Error::Fail("".into()), 3 => Error::NotTrusted("".into()), 4 => Error::NoKey("".into()), _ => Error::Fail("".into()), } } } impl From for Error { fn from(err: anyhow::Error) -> Error { Error::Fail(format!("{}", err)) } } #[non_exhaustive] #[derive(thiserror::Error, Debug, Clone)] #[allow(unused)] pub enum PgpArmorError { #[error("Success")] Ok, #[error("unknown error")] UnknownError, #[error("armor crc check")] CrcCheck, #[error("armor body decode")] BodyDecode, #[error("armor crc decode")] CrcDecode, #[error("armor no end pgp")] NoEndPgp, #[error("armor unknown preamble tag")] UnknownPreambleTag, #[error("armor unknown armor type")] UnknownArmorType, #[error("armor no begin pgp")] NoBeginPgp, } impl From for ErrorCode { fn from(err: PgpArmorError) -> ErrorCode { match err { PgpArmorError::Ok => 0, PgpArmorError::UnknownError => -1, PgpArmorError::CrcCheck => -7, PgpArmorError::BodyDecode => -6, PgpArmorError::CrcDecode => -5, PgpArmorError::NoEndPgp => -4, PgpArmorError::UnknownPreambleTag => -3, PgpArmorError::UnknownArmorType => -2, PgpArmorError::NoBeginPgp => -1, } } } impl From for PgpArmorError { fn from(_err: Error) -> PgpArmorError { PgpArmorError::UnknownError } } impl From for PgpArmorError { fn from(_err: anyhow::Error) -> PgpArmorError { PgpArmorError::UnknownError } } #[non_exhaustive] #[allow(unused)] #[derive(Debug, Clone)] pub enum PgpArmor { None, Message, Pubkey, Signature, SignedMessage, File, Privkey, Seckey, } impl From for c_int { fn from(a: PgpArmor) -> c_int { match a { PgpArmor::None => 0, PgpArmor::Message => 1, PgpArmor::Pubkey => 2, PgpArmor::Signature => 3, PgpArmor::SignedMessage => 4, PgpArmor::File => 5, PgpArmor::Privkey => 6, PgpArmor::Seckey => 7, } } } impl From for PgpArmor { fn from(a: c_int) -> PgpArmor { match a { 0 => PgpArmor::None, 1 => PgpArmor::Message, 2 => PgpArmor::Pubkey, 3 => PgpArmor::Signature, 4 => PgpArmor::SignedMessage, 5 => PgpArmor::File, 6 => PgpArmor::Privkey, 7 => PgpArmor::Seckey, _ => PgpArmor::None, } } } impl TryFrom for armor::Kind { type Error = Error; fn try_from(a: PgpArmor) -> Result { let err = || Err(Error::Fail(format!("Unsupported armor type: {:?}", a))); match a { PgpArmor::None => err(), PgpArmor::Message => Ok(armor::Kind::Message), PgpArmor::Pubkey => Ok(armor::Kind::PublicKey), PgpArmor::Signature => Ok(armor::Kind::Signature), PgpArmor::SignedMessage => err(), PgpArmor::File => Ok(armor::Kind::File), PgpArmor::Privkey => err(), PgpArmor::Seckey => Ok(armor::Kind::SecretKey), } } } impl From> for PgpArmor { fn from(k: Option) -> PgpArmor { use armor::Kind::*; match k { None => PgpArmor::None, Some(Message) => PgpArmor::Message, Some(PublicKey) => PgpArmor::Pubkey, Some(SecretKey) => PgpArmor::Seckey, // XXX: PgpArmor::Privkey Some(Signature) => PgpArmor::Signature, // XXX: PgpArmor::SignedMessage Some(File) => PgpArmor::File, _ => PgpArmor::File, // XXX } } } rpm-sequoia-1.8.0/src/symbols.txt000064400000000000000000000017741046102023000151250ustar 00000000000000# #include # Implemented by rpmpgp.c. # pgpValString _pgpPubkeyFingerprint _pgpPubkeyKeyID _pgpPrtParams _pgpPrtParams2 _pgpPrtParamsSubkeys _pgpPrtPkts # Implemented by rpmpgp.c. # pgpReadPkts _pgpParsePkts _pgpPubKeyCertLen _pgpPubKeyLint _pgpArmorWrap _pgpNewDig _pgpCleanDig _pgpFreeDig _pgpDigGetParams _pgpDigParamsCmp _pgpDigParamsAlgo _pgpDigParamsSignID _pgpDigParamsUserID _pgpDigParamsVersion _pgpDigParamsCreationTime _pgpDigParamsFree _pgpPubkeyMerge _pgpVerifySignature _pgpVerifySignature2 _pgpVerifySig _pgpSignatureType # Implemented by rpmpgp.c. # pgpIdentItem # #include _rpmInitCrypto _rpmFreeCrypto _rpmDigestDup _rpmDigestLength _rpmDigestInit _rpmDigestUpdate _rpmDigestFinal # These are implemented in terms of the above. # # rpmDigestBundleFree # rpmDigestBundleAdd # rpmDigestBundleAddID # rpmDigestBundleUpdate # rpmDigestBundleDupCtx # Threse symbols are exposed by Rust :/ # See: https://gitlab.com/sequoia-pgp/rpm-sequoia/-/issues/3 ?rust_eh_personality rpm-sequoia-1.8.0/tests/symbols.rs000064400000000000000000000101311046102023000152700ustar 00000000000000use std::env; use std::fs::File; use std::io::Read; use std::path::PathBuf; use assert_cmd::Command; use assert_cmd::assert::OutputAssertExt; #[test] fn symbols() -> anyhow::Result<()> { // Make sure the library is built. Command::new("cargo").arg("build").ok()?; // We want the location of the build directory (e.g., // `/tmp/rpm-sequoia/debug`). // // OUT_DIR gives us // `/tmp/rpm-sequoia/debug/build/rpm-sequoia-HASH/out`. let out_dir = PathBuf::from(env!("OUT_DIR")); let mut build_dir = out_dir; let lib = loop { let mut lib = build_dir.clone(); lib.push("librpm_sequoia.so"); if lib.exists() { break lib; } if ! build_dir.pop() { panic!("Failed to find librpm_sequoia.so"); } }; let cmd = Command::new("objdump") .arg("-T") .arg(lib) .unwrap(); let assert = cmd.assert().success(); let output = String::from_utf8_lossy(&assert.get_output().stdout); let mut symbols = Vec::new(); for line in output.split("\n") { if line.contains("g DF .text") || line.contains("g DO .data") || line.contains("g DF .opd") { let symbol = line.split(' ').last().expect("a word"); symbols.push(symbol); } } symbols.sort(); eprintln!("Found {} symbols:", symbols.len()); for symbol in symbols.iter() { eprintln!(" {}", symbol); } let mut expected_symbols_txt_fn = PathBuf::from(env!("CARGO_MANIFEST_DIR")); expected_symbols_txt_fn.push("src/symbols.txt"); let mut expected_symbols_txt = Vec::new(); File::open(expected_symbols_txt_fn) .expect("src/symbols.txt exists") .read_to_end(&mut expected_symbols_txt) .unwrap(); let expected_symbols_txt = String::from_utf8_lossy(&expected_symbols_txt); let mut expected_symbols = Vec::new(); for symbol in expected_symbols_txt.split("\n") { if symbol.starts_with("#") { continue; } let symbol = symbol.trim(); if symbol.is_empty() { continue; } if symbol.chars().nth(0) == Some('?') { expected_symbols.push((&symbol[1..], true)); } else { expected_symbols.push((symbol, false)); } } expected_symbols.sort(); eprintln!("Expected {} symbols:", expected_symbols.len()); for (symbol, optional) in expected_symbols.iter() { eprint!(" {}", symbol); if *optional { eprintln!(" (optional)"); } else { eprintln!(""); } } let mut i = 0; let mut j = 0; let mut bad = false; loop { if i == symbols.len() && j == expected_symbols.len() { break; } if i < symbols.len() && j < expected_symbols.len() && symbols[i] == expected_symbols[j].0 { i += 1; j += 1; } else if (i < symbols.len() && j < expected_symbols.len() && symbols[i] < expected_symbols[j].0) || j == expected_symbols.len() { eprintln!("Found unexpected symbol {}", symbols[i]); if symbols[i] == "bz_internal_error" { eprintln!(" It looks like you forgot to disable compression.") } i += 1; bad = true; } else if (i < symbols.len() && j < expected_symbols.len() && symbols[i] > expected_symbols[j].0) || i == symbols.len() { if ! expected_symbols[j].1 { eprintln!("Missing expected symbol {}", expected_symbols[j].0); bad = true; } j += 1; } else { unreachable!(); } } if bad { eprintln!("\ *** If you see unexpected symbols like SHA1DCInit..., \ then you need version 0.2.6 or later of \ sha1collisiondetection. ***"); Err(anyhow::anyhow!("symbol mismatch")) } else { Ok(()) } }