openpgp-card-rpgp-0.3.0/.cargo_vcs_info.json0000644000000001360000000000100143610ustar { "git": { "sha1": "944ac56dc08c5f60f730de174a486a8c9e51295f" }, "path_in_vcs": "" }openpgp-card-rpgp-0.3.0/.gitignore000064400000000000000000000002211046102023000151340ustar 00000000000000# SPDX-FileCopyrightText: Heiko Schaefer # SPDX-License-Identifier: CC0-1.0 /target .idea Cargo.lock tests/config/*.toml openpgp-card-rpgp-0.3.0/.reuse/dep5000064400000000000000000000004621046102023000151340ustar 00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: openpgp-card-rpgp Upstream-Contact: Heiko Schaefer Source: https://codeberg.org/heiko/openpgp-card-rpgp Files: tests/keys/*.tsk Copyright: Heiko Schaefer License: CC0-1.0 openpgp-card-rpgp-0.3.0/.woodpecker.yml000064400000000000000000000063231046102023000161200ustar 00000000000000# SPDX-FileCopyrightText: Heiko Schaefer # SPDX-License-Identifier: CC0-1.0 steps: reuse: image: fsfe/reuse:latest rustfmt: image: rust commands: - rustup toolchain install nightly && rustup component add --toolchain nightly rustfmt - cargo +nightly fmt -- --check clippy: image: rust commands: - rustup component add clippy - apt update -y -qq && apt install -y -qq --no-install-recommends libpcsclite-dev nettle-dev libclang-dev - cargo clippy --no-deps --tests --examples virtual-opcard: image: registry.gitlab.com/openpgp-card/virtual-cards/opcard-rs-tools pull: true depends_on: [ clippy ] commands: - apt update -y -qq && DEBIAN_FRONTEND=noninteractive apt install -y -qq nettle-dev - sh /start.sh # Start pcscd, virtual OpenPGP card - cargo --version # Run tests - TEST_CONFIG=tests/ci/virt-opcard-rs.toml cargo test import -- --ignored --nocapture - TEST_CONFIG=tests/ci/virt-opcard-rs.toml cargo test keygen -- --ignored --nocapture virtual-smartpgp: image: registry.gitlab.com/openpgp-card/virtual-cards/smartpgp-tools pull: true depends_on: [ clippy ] commands: - apt update -y -qq && DEBIAN_FRONTEND=noninteractive apt install -y -qq nettle-dev - sh /start.sh # Start pcscd, virtual OpenPGP card - . "$HOME/.cargo/env" - cargo --version # Run tests - TEST_CONFIG=tests/ci/virt-smartpgp.toml cargo test import -- --ignored --nocapture - TEST_CONFIG=tests/ci/virt-smartpgp.toml cargo test keygen -- --ignored --nocapture virtual-canokey: image: registry.gitlab.com/openpgp-card/virtual-cards/canokey-builddeps pull: true depends_on: [ clippy ] commands: - apt update -y -qq && DEBIAN_FRONTEND=noninteractive apt install -y -qq nettle-dev - sh /start.sh # Start pcscd, virtual OpenPGP card - . "$HOME/.cargo/env" - cargo --version # Run tests - TEST_CONFIG=tests/ci/virt-canokey.toml cargo test import -- --ignored --nocapture - TEST_CONFIG=tests/ci/virt-canokey.toml cargo test keygen -- --ignored --nocapture virtual-ykneo: image: registry.gitlab.com/openpgp-card/virtual-cards/ykneo-builddeps pull: true depends_on: [ clippy ] commands: - apt update -y -qq && DEBIAN_FRONTEND=noninteractive apt install -y -qq nettle-dev - sh /start.sh # Start pcscd, virtual OpenPGP card - . "$HOME/.cargo/env" - cargo --version # Run tests - TEST_CONFIG=tests/ci/virt-ykneo.toml cargo test import -- --ignored --nocapture - TEST_CONFIG=tests/ci/virt-ykneo.toml cargo test keygen -- --ignored --nocapture virtual-fluffypgp: image: registry.gitlab.com/openpgp-card/virtual-cards/fluffypgp-builddeps pull: true depends_on: [ clippy ] commands: - apt update -y -qq && DEBIAN_FRONTEND=noninteractive apt install -y -qq nettle-dev - sh /start.sh # Start pcscd, virtual OpenPGP card - . "$HOME/.cargo/env" - cargo --version # Run tests - TEST_CONFIG=tests/ci/virt-fluffypgp.toml cargo test import -- --ignored --nocapture - TEST_CONFIG=tests/ci/virt-fluffypgp.toml cargo test keygen -- --ignored --nocapture openpgp-card-rpgp-0.3.0/Cargo.lock0000644000002354000000000000100123400ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "addr2line" version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] name = "adler2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aead" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", "generic-array 0.14.7", ] [[package]] name = "aes" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", "cpufeatures", ] [[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 = "aes-kw" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69fa2b352dcefb5f7f3a5fb840e02665d311d878955380515e4fd50095dd3d8c" dependencies = [ "aes", ] [[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 = "anstream" version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", "once_cell", "windows-sys 0.59.0", ] [[package]] name = "anyhow" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[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", "zeroize", ] [[package]] name = "ascii-canvas" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" dependencies = [ "term 0.7.0", ] [[package]] name = "ascii-canvas" version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef1e3e699d84ab1b0911a1010c5c106aa34ae89aeac103be5ce0c3859db1e891" dependencies = [ "term 1.0.1", ] [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", "windows-targets", ] [[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 2.0.96", ] [[package]] name = "bit-set" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ "bit-vec 0.6.3", ] [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ "bit-vec 0.8.0", ] [[package]] name = "bit-vec" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bit-vec" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitfield" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f798d2d157e547aa99aab0967df39edd0b70307312b6f8bd2848e6abe40896e0" [[package]] name = "bitflags" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[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 0.14.7", ] [[package]] name = "block-padding" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ "generic-array 0.14.7", ] [[package]] name = "blowfish" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" dependencies = [ "byteorder", "cipher", ] [[package]] name = "bstr" version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", "serde", ] [[package]] name = "buffer-redux" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e8acf87c5b9f5897cd3ebb9a327f420e0cae9dd4e5c1d2e36f2c84c571a58f1" dependencies = [ "memchr", ] [[package]] name = "buffered-reader" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabd1c5e55587a8e8526172d63ad2ba665fa18c8acb39ec9a77af1708c982b9b" dependencies = [ "bzip2", "flate2", "lazy_static", "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 = "bytes" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "bzip2" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bafdbf26611df8c14810e268ddceda071c297570a5fb360ceddf617fe417ef58" dependencies = [ "bzip2-sys", "libc", ] [[package]] name = "bzip2-sys" version = "0.1.11+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" dependencies = [ "cc", "libc", "pkg-config", ] [[package]] name = "camellia" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3264e2574e9ef2b53ce6f536dea83a69ac0bc600b762d1523ff83fe07230ce30" dependencies = [ "byteorder", "cipher", ] [[package]] name = "capnp" version = "0.14.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dca085c2c7d9d65ad749d450b19b551efaa8e3476a439bdca07aca8533097f3" [[package]] name = "capnp-futures" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06d7115c9c31cc85c6d21aea9417059f9d6ee87762a36693a2afcfb8ba23009b" dependencies = [ "capnp", "futures", ] [[package]] name = "capnp-rpc" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c4f17f96f68f2c1168ed7105d9e5cb4a095a5bef3578aee0f9c0644b85ca95e" dependencies = [ "capnp", "capnp-futures", "futures", ] [[package]] name = "card-backend" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd3ee3a298842065dc489180c34a4fe4bbbb8643bb422009d79558a099fb42e5" dependencies = [ "thiserror 1.0.69", ] [[package]] name = "card-backend-pcsc" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68bb0b707b1b6b058ed93abd70ef65703ed6fd4150d32a0d735b78cfa61cbb35" dependencies = [ "card-backend", "iso7816-tlv", "log", "pcsc", ] [[package]] name = "card-backend-scdc" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82b3d73845e045640248c0915ba92bf7656b82e57b3b163a9371e56bc7ac74e5" dependencies = [ "card-backend", "futures", "hex", "lazy_static", "log", "sequoia-ipc", "tokio", ] [[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.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" dependencies = [ "shlex", ] [[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.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", "windows-targets", ] [[package]] name = "cipher" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", ] [[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 = "colorchoice" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[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 = "crc24" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd121741cf3eb82c08dd3023eb55bf2665e5f60ec20f89760cf836ae4562e6a0" [[package]] name = "crc32fast" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[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 0.14.7", "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 0.14.7", "rand_core", "typenum", ] [[package]] name = "ctor" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", "syn 1.0.109", ] [[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 2.0.96", ] [[package]] name = "darling" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", ] [[package]] name = "darling_core" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", "syn 2.0.96", ] [[package]] name = "darling_macro" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", "syn 2.0.96", ] [[package]] name = "dbl" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd2735a791158376708f9347fe8faba9667589d82427ef3aed6794a8981de3d9" dependencies = [ "generic-array 0.14.7", ] [[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 = "derive_builder" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ "darling", "proc-macro2", "quote", "syn 2.0.96", ] [[package]] name = "derive_builder_macro" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", "syn 2.0.96", ] [[package]] name = "derive_more" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", "syn 2.0.96", "unicode-xid", ] [[package]] name = "des" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" dependencies = [ "cipher", ] [[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" version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" dependencies = [ "dirs-sys", ] [[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" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", "winapi", ] [[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 2.0.96", ] [[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.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[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 = "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", "serde", "sha2", "subtle", "zeroize", ] [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[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 0.14.7", "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 = "env_filter" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", ] [[package]] name = "env_logger" version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" dependencies = [ "anstream", "anstyle", "env_filter", "humantime", "log", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", "windows-sys 0.59.0", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "ff" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ "rand_core", "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 = "fixedbitset" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fs2" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" dependencies = [ "libc", "winapi", ] [[package]] name = "futures" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", "syn 2.0.96", ] [[package]] name = "futures-sink" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[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 = "generic-array" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8c8444bc9d71b935156cc0ccab7f622180808af7867b1daae6547d773591703" dependencies = [ "typenum", ] [[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.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" dependencies = [ "cfg-if", "libc", "wasi 0.13.3+wasi-0.2.2", "windows-targets", ] [[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 = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[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 = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-slice" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5491a308e0214554f07a81d8944abe45f552871c12e3c3c6e7e5d354039a6c4c" [[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 = "home" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "iana-time-zone" version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" [[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 2.0.96", ] [[package]] name = "idea" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "075557004419d7f2031b8bb7f44bb43e55a83ca7b63076a8fb8fe75753836477" dependencies = [ "cipher", ] [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[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.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "inout" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ "generic-array 0.14.7", ] [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "iso7816-tlv" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7660d28d24a831d690228a275d544654a30f3b167a8e491cf31af5fe5058b546" dependencies = [ "untrusted", ] [[package]] name = "iter-read" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071ed4cc1afd86650602c7b11aa2e1ce30762a1c27193201cb5cee9c6ebb1294" [[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 = "itertools" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" 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 = "k256" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", "once_cell", "sha2", "signature", ] [[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 3.0.0", "bit-set 0.5.3", "ena", "itertools 0.11.0", "lalrpop-util 0.20.2", "petgraph 0.6.5", "regex", "regex-syntax", "string_cache", "term 0.7.0", "tiny-keccak", "unicode-xid", "walkdir", ] [[package]] name = "lalrpop" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7047a26de42016abf8f181b46b398aef0b77ad46711df41847f6ed869a2a1d5b" dependencies = [ "ascii-canvas 4.0.0", "bit-set 0.8.0", "ena", "itertools 0.14.0", "lalrpop-util 0.22.1", "petgraph 0.7.1", "regex", "regex-syntax", "sha3", "string_cache", "term 1.0.1", "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 = "lalrpop-util" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8d05b3fe34b8bd562c338db725dfa9beb9451a48f65f129ccb9538b48d2c93b" dependencies = [ "regex-automata", "rustversion", ] [[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.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[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.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[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.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[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 = "miniz_oxide" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] [[package]] name = "mio" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] [[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", "serde", "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 = "num_enum" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.96", ] [[package]] name = "object" version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[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.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opaque-debug" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openpgp-card" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e80b16bc0178ae2c4af372c986721385ddee2df4b9622612911b5f4e3ffb4c1" dependencies = [ "card-backend", "chrono", "hex-slice", "log", "nom", "secrecy", "sha2", "thiserror 1.0.69", ] [[package]] name = "openpgp-card-rpgp" version = "0.3.0" dependencies = [ "anyhow", "card-backend-pcsc", "card-backend-scdc", "chrono", "env_logger", "hex", "log", "openpgp-card", "pgp", "rand", "rsa", "secrecy", "serde", "testresult", "thiserror 2.0.11", "toml", ] [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" 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 = "pcsc" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd833ecf8967e65934c49d3521a175929839bf6d0e497f3bd0d3a2ca08943da" dependencies = [ "bitflags", "pcsc-sys", ] [[package]] name = "pcsc-sys" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14ef017e15d2e5592a9e39a346c1dbaea5120bab7ed7106b210ef58ebd97003" dependencies = [ "pkg-config", ] [[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 0.4.2", "indexmap", ] [[package]] name = "petgraph" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset 0.5.7", "indexmap", ] [[package]] name = "pgp" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30249ac8a98b356b473b04bc5358c75a260aa96a295d0743ce752fe7b173f235" dependencies = [ "aes", "aes-gcm", "aes-kw", "argon2", "base64", "bitfield", "block-padding", "blowfish", "bstr", "buffer-redux", "byteorder", "camellia", "cast5", "cfb-mode", "chrono", "cipher", "const-oid", "crc24", "curve25519-dalek", "derive_builder", "derive_more", "des", "digest", "dsa", "eax", "ecdsa", "ed25519-dalek", "elliptic-curve", "flate2", "generic-array 0.14.7", "hex", "hkdf", "idea", "iter-read", "k256", "log", "md-5", "nom", "num-bigint-dig", "num-traits", "num_enum", "ocb3", "p256", "p384", "p521", "rand", "ripemd", "rsa", "sha1", "sha1-checked", "sha2", "sha3", "signature", "smallvec", "thiserror 2.0.11", "twofish", "x25519-dalek", "zeroize", ] [[package]] name = "phf_shared" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" dependencies = [ "siphasher", ] [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "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.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[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.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ "zerocopy", ] [[package]] name = "precomputed-hash" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[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-macro-crate" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "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.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" 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 = "rsa" version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" dependencies = [ "const-oid", "digest", "num-bigint-dig", "num-integer", "num-traits", "pkcs1", "pkcs8", "rand_core", "signature", "spki", "subtle", "zeroize", ] [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[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 = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.59.0", ] [[package]] name = "rustversion" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[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 0.14.7", "pkcs8", "subtle", "zeroize", ] [[package]] name = "secrecy" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" dependencies = [ "zeroize", ] [[package]] name = "semver" version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "sequoia-ipc" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c10e9be28f170d20cebc3dec906b6c2e961b0626062410e621b0b725994ee065" dependencies = [ "anyhow", "buffered-reader", "capnp-rpc", "ctor", "dirs", "fs2", "futures", "lalrpop 0.22.1", "lalrpop-util 0.22.1", "lazy_static", "libc", "memsec", "rand", "sequoia-openpgp", "socket2 0.4.10", "tempfile", "thiserror 1.0.69", "tokio", "tokio-util", "winapi", ] [[package]] name = "sequoia-openpgp" version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e858e4e9e48ff079cede92e1b45c942a5466ce9a4e3cc0c2a7e66586a718ef59" dependencies = [ "anyhow", "base64", "buffered-reader", "bzip2", "chrono", "dyn-clone", "flate2", "getrandom 0.2.15", "idna", "lalrpop 0.20.2", "lalrpop-util 0.20.2", "lazy_static", "libc", "memsec", "nettle", "once_cell", "regex", "regex-syntax", "sha1collisiondetection", "thiserror 1.0.69", "xxhash-rust", ] [[package]] name = "serde" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", "syn 2.0.96", ] [[package]] name = "serde_spanned" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] [[package]] name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "sha1-checked" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423" dependencies = [ "digest", "sha1", "zeroize", ] [[package]] name = "sha1collisiondetection" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f606421e4a6012877e893c399822a4ed4b089164c5969424e1b9d1e66e6964b" dependencies = [ "digest", "generic-array 1.2.0", ] [[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 = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", ] [[package]] name = "socket2" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", ] [[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.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ "new_debug_unreachable", "once_cell", "parking_lot", "phf_shared", "precomputed-hash", ] [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" 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 2.0.96", ] [[package]] name = "tempfile" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" dependencies = [ "cfg-if", "fastrand", "getrandom 0.3.1", "once_cell", "rustix", "windows-sys 0.59.0", ] [[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 = "term" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3bb6001afcea98122260987f8b7b5da969ecad46dbf0b5453702f776b491a41" dependencies = [ "home", "windows-sys 0.52.0", ] [[package]] name = "testresult" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "614b328ff036a4ef882c61570f72918f7e9c5bee1da33f8e7f91e01daee7e56c" [[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.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ "thiserror-impl 2.0.11", ] [[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 2.0.96", ] [[package]] name = "thiserror-impl" version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", "syn 2.0.96", ] [[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 = "tokio" version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", "libc", "mio", "pin-project-lite", "socket2 0.5.8", "windows-sys 0.52.0", ] [[package]] name = "tokio-util" version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", "pin-project-lite", "tokio", ] [[package]] name = "toml" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit", ] [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.22.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow", ] [[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.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[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 = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[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 = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[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 = "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.13.3+wasi-0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" 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 2.0.96", "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 2.0.96", "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 = "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 0.59.0", ] [[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.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" version = "0.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 = "winnow" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e49d2d35d3fad69b39b94139037ecfb4f359f08958b9c11e7315ce770462419" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen-rt" version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" 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", "serde", "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 2.0.96", "synstructure", ] [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", "syn 2.0.96", ] [[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.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", "syn 2.0.96", "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 2.0.96", ] [[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 2.0.96", ] openpgp-card-rpgp-0.3.0/Cargo.toml0000644000000041410000000000100123570ustar # 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" name = "openpgp-card-rpgp" version = "0.3.0" authors = [ "Wiktor Kwapisiewicz ", "Heiko Schaefer ", ] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Companion crate for using openpgp-card with rPGP" documentation = "https://docs.rs/openpgp-card-rpgp" readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://codeberg.org/openpgp-card/rpgp" [lib] name = "openpgp_card_rpgp" path = "src/lib.rs" [[example]] name = "decrypt" path = "examples/decrypt.rs" [[example]] name = "sign" path = "examples/sign.rs" [[example]] name = "verify" path = "examples/verify.rs" [[test]] name = "cards" path = "tests/cards.rs" [[test]] name = "import" path = "tests/import.rs" [[test]] name = "keygen" path = "tests/keygen.rs" [[test]] name = "other" path = "tests/other.rs" [[test]] name = "util" path = "tests/util.rs" [dependencies.chrono] version = "0.4" [dependencies.openpgp-card] version = "0.5" [dependencies.pgp] version = "0.15" [dependencies.rand] version = "0.8" [dependencies.rsa] version = "0.9" [dependencies.secrecy] version = "0.8" [dependencies.thiserror] version = "2" [dev-dependencies.anyhow] version = "1" [dev-dependencies.card-backend-pcsc] version = "0.5" [dev-dependencies.card-backend-scdc] version = "0.5" [dev-dependencies.env_logger] version = "0.11" [dev-dependencies.hex] version = "0.4" [dev-dependencies.log] version = "0.4" [dev-dependencies.serde] version = "1" features = ["derive"] [dev-dependencies.testresult] version = "0.4" [dev-dependencies.toml] version = "0.8" openpgp-card-rpgp-0.3.0/Cargo.toml.orig000064400000000000000000000014531046102023000160430ustar 00000000000000# SPDX-FileCopyrightText: Heiko Schaefer # SPDX-License-Identifier: CC0-1.0 [package] name = "openpgp-card-rpgp" description = "Companion crate for using openpgp-card with rPGP" version = "0.3.0" edition = "2021" authors = [ "Wiktor Kwapisiewicz ", "Heiko Schaefer ", ] license = "MIT OR Apache-2.0" repository = "https://codeberg.org/openpgp-card/rpgp" documentation = "https://docs.rs/openpgp-card-rpgp" [dependencies] chrono = "0.4" openpgp-card = "0.5" pgp = "0.15" rand = "0.8" rsa = "0.9" secrecy = "0.8" thiserror = "2" [dev-dependencies] anyhow = "1" card-backend-pcsc = "0.5" card-backend-scdc = "0.5" env_logger = "0.11" hex = "0.4" log = "0.4" serde = { version = "1", features = ["derive"] } testresult = "0.4" toml = "0.8" openpgp-card-rpgp-0.3.0/LICENSES/Apache-2.0.txt000064400000000000000000000240451046102023000166020ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2021 Heiko Schäfer Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. openpgp-card-rpgp-0.3.0/LICENSES/CC0-1.0.txt000064400000000000000000000154041046102023000157640ustar 00000000000000Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. openpgp-card-rpgp-0.3.0/LICENSES/MIT.txt000064400000000000000000000020701046102023000155470ustar 00000000000000The MIT License (MIT) Copyright (c) 2021 Heiko Schäfer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. openpgp-card-rpgp-0.3.0/README.md000064400000000000000000000011721046102023000144310ustar 00000000000000 # OpenPGP card client library for use with rPGP This crate implements OpenPGP card support for use with [rPGP](https://github.com/rpgp/rpgp/). This is a convenience layer on top of the implementation-agnostic OpenPGP card client library . ```mermaid flowchart TD OCR["openpgp-card-rpgp"] --> OC["openpgp-card
(OpenPGP card client library)"] OCR --> RPGP["rPGP
(OpenPGP implementation)"] OC --> PCSC["card-backend-pcsc
(access cards via PC/SC)"] ```openpgp-card-rpgp-0.3.0/examples/decrypt.rs000064400000000000000000000022671046102023000170160ustar 00000000000000// SPDX-FileCopyrightText: Wiktor Kwapisiewicz // SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: Apache-2.0 OR MIT use std::fs::File; use card_backend_pcsc::PcscBackend; use openpgp_card::ocard::KeyType; use openpgp_card_rpgp::CardSlot; use pgp::{Deserializable, Message}; fn main() -> testresult::TestResult { let card = PcscBackend::cards(None)?.next().unwrap()?; let mut card = openpgp_card::Card::new(card)?; let mut tx = card.transaction()?; let pwd = &std::env::args().collect::>()[1]; eprintln!("with pwd = {pwd}"); tx.card() .verify_pw1_user(pwd.as_bytes().to_vec().into()) .expect("Verify"); let cs = CardSlot::init_from_card(&mut tx, KeyType::Decryption, &|| { eprintln!("touch confirmation needed") })?; let (message, _headers) = Message::from_armor_single(File::open("message2.asc")?)?; eprintln!("message: {:?}", &message); let decrypted = cs.decrypt_message(&message)?; eprintln!("decrypted: {:?}", &decrypted); if let Message::Literal(data) = decrypted { println!("{}", String::from_utf8_lossy(data.data())); } Ok(()) } openpgp-card-rpgp-0.3.0/examples/sign.rs000064400000000000000000000040011046102023000162700ustar 00000000000000// SPDX-FileCopyrightText: Wiktor Kwapisiewicz // SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: Apache-2.0 OR MIT use card_backend_pcsc::PcscBackend; use openpgp_card::ocard::KeyType; use openpgp_card_rpgp::CardSlot; use pgp::crypto::hash::HashAlgorithm; use pgp::packet::{SignatureConfig, SignatureType, Subpacket, SubpacketData}; use pgp::types::PublicKeyTrait; use pgp::{ArmorOptions, StandaloneSignature}; fn main() -> testresult::TestResult { const DATA: &[u8] = b"Hello World"; let pwd = &std::env::args().collect::>()[1]; eprintln!("with pwd = {pwd}"); // -- set up card signer let card = PcscBackend::cards(None) .expect("cards") .next() .unwrap() .expect("card"); let mut card = openpgp_card::Card::new(card).expect("card new"); let mut tx = card.transaction().expect("tx"); tx.card() .verify_pw1_sign(pwd.as_bytes().to_vec().into()) .expect("Verify"); let cs = CardSlot::init_from_card(&mut tx, KeyType::Signing, &|| { eprintln!("touch confirmation needed") })?; // -- use card signer let mut config = SignatureConfig::v4( SignatureType::Binary, cs.public_key().algorithm(), HashAlgorithm::SHA2_256, ); config .hashed_subpackets .push(Subpacket::regular(SubpacketData::SignatureCreationTime( std::time::SystemTime::now().into(), ))); config .hashed_subpackets .push(Subpacket::regular(SubpacketData::Issuer(cs.key_id()))); config .hashed_subpackets .push(Subpacket::regular(SubpacketData::IssuerFingerprint( cs.fingerprint(), ))); let signature = config.sign(&cs, String::new, DATA)?; let signature = StandaloneSignature { signature }; signature .to_armored_writer( &mut std::fs::File::create("sig.asc").unwrap(), ArmorOptions::default(), ) .unwrap(); Ok(()) } openpgp-card-rpgp-0.3.0/examples/verify.rs000064400000000000000000000020201046102023000166330ustar 00000000000000// SPDX-FileCopyrightText: Wiktor Kwapisiewicz // SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: Apache-2.0 OR MIT use pgp::composed::signed_key::SignedPublicKey; use pgp::composed::Deserializable; use pgp::types::PublicKeyTrait; use pgp::StandaloneSignature; fn main() -> testresult::TestResult { const DATA: &[u8] = b"Hello World"; let cert = &std::env::args().collect::>()[1]; eprintln!("with cert = {cert}"); let key = SignedPublicKey::from_armor_single(std::fs::File::open(cert)?)?.0; let sig = StandaloneSignature::from_armor_single(std::fs::File::open("sig.asc")?)?.0; if let Ok(()) = sig.verify(&key, DATA) { eprintln!("Looks OK here: {:x}", key.key_id()); return Ok(()); } for subkey in key.public_subkeys { if let Ok(()) = sig.verify(&subkey, DATA) { eprintln!("Looks OK here: {:x}", subkey.key_id()); return Ok(()); } } panic!("no good signature found") } openpgp-card-rpgp-0.3.0/rustfmt.toml000064400000000000000000000005041046102023000155510ustar 00000000000000# SPDX-FileCopyrightText: 2023 Wiktor Kwapisiewicz # SPDX-License-Identifier: CC0-1.0 # CHECK: https://github.com/rust-lang/rustfmt/issues/5083 state == open group_imports = "StdExternalCrate" # CHECK: https://github.com/rust-lang/rustfmt/issues/3348 state == open format_code_in_doc_comments = true openpgp-card-rpgp-0.3.0/src/cardslot.rs000064400000000000000000000347241046102023000161330ustar 00000000000000// SPDX-FileCopyrightText: Wiktor Kwapisiewicz // SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: Apache-2.0 OR MIT use std::fmt::{Debug, Formatter}; use std::sync::Mutex; use chrono::{DateTime, Utc}; use openpgp_card::ocard::crypto::Hash; use openpgp_card::ocard::KeyType; use openpgp_card::state::Transaction; use openpgp_card::Card; use pgp::crypto::checksum; use pgp::crypto::ecc_curve::ECCCurve; use pgp::crypto::hash::HashAlgorithm; use pgp::crypto::public_key::PublicKeyAlgorithm; use pgp::crypto::sym::SymmetricKeyAlgorithm; use pgp::packet::PublicKey; use pgp::types::{ EcdhPublicParams, EcdsaPublicParams, Fingerprint, KeyId, KeyVersion, Mpi, PkeskBytes, PublicKeyTrait, PublicParams, SecretKeyTrait, SignatureBytes, }; use pgp::{Esk, Message, PlainSessionKey}; use rand::{CryptoRng, Rng}; use crate::rpgp::map_card_err; /// An individual OpenPGP card key slot, which can be used for private key operations. pub struct CardSlot<'cs, 't> { tx: Mutex<&'cs mut Card>>, // Which key slot does this OpenPGP card operate on key_type: KeyType, // The public key material that corresponds to the key slot of this signer // // The distinction between primary and subkey is irrelevant here, but we have to use some type. // So we model the key data as a public primary key packet. public_key: PublicKey, touch_prompt: &'cs (dyn Fn() + Send + Sync), } impl<'cs, 't> CardSlot<'cs, 't> { /// Set up a CardSigner for the card behind `tx`, using the key slot for `key_type`. /// /// Initializes the CardSigner based on public key information obtained from `public_key`. pub fn with_public_key( tx: &'cs mut Card>, key_type: KeyType, public_key: PublicKey, touch_prompt: &'cs (dyn Fn() + Send + Sync), ) -> Result { // FIXME: compare the fingerprint between card slot and public_key? Ok(Self { tx: Mutex::new(tx), public_key, key_type, touch_prompt, }) } /// Set up a CardSlot for the card behind `tx`, using the key slot for `key_type`. /// /// Initializes the CardSigner based on public key information obtained from the card. pub fn init_from_card( tx: &'cs mut Card>, key_type: KeyType, touch_prompt: &'cs (dyn Fn() + Send + Sync), ) -> Result { let pk = crate::rpgp::pubkey_from_card(tx, key_type)?; Self::with_public_key(tx, key_type, pk, touch_prompt) } } impl CardSlot<'_, '_> { /// The OpenPGP public key material that corresponds to the key in this CardSlot pub fn public_key(&self) -> &PublicKey { &self.public_key } /// The card slot that this CardSlot uses pub fn key_type(&self) -> KeyType { self.key_type } fn touch_required(&self, tx: &mut Card>) -> bool { // Touch is required if: // - the card supports the feature // - and the policy is set to a value other than 'Off' if let Ok(Some(uif)) = tx.user_interaction_flag(self.key_type) { uif.touch_policy().touch_required() } else { false } } pub fn decrypt( &self, values: &PkeskBytes, ) -> pgp::errors::Result<(Vec, SymmetricKeyAlgorithm)> { #[allow(clippy::unwrap_used)] let mut tx = self.tx.lock().unwrap(); let decrypted_key = match (self.public_key.public_params(), values) { (PublicParams::RSA { n, .. }, PkeskBytes::Rsa { mpi }) => { let mut ciphertext = mpi.to_vec(); // RSA modulus length. We use this length to pad the ciphertext, in case it was // zero truncated. // // FIXME: There might be a problematic corner case when the modulus itself is // zero-stripped, and as a consequence we don't pad enough? // (We might want to use rounding to "round" to a typical RSA modulus size?) let modulus_len = n.len(); // Left zero-pad `ciphertext` to the length of the RSA modulus // (that is: if the ciphertext was zero-stripped, undo that stripping). // // This padding is not required in most cases. Typically, the ciphertext and // modulus should be the same length. However, there is a 1:256 likelihood that // the ciphertext happens to start with a 0x0 byte, which OpenPGP will truncate. while modulus_len > ciphertext.len() { ciphertext.insert(0, 0u8); } let cryptogram = openpgp_card::ocard::crypto::Cryptogram::RSA(&ciphertext); if self.touch_required(&mut tx) { (self.touch_prompt)(); } tx.card().decipher(cryptogram).map_err(|e| { pgp::errors::Error::Message(format!( "RSA decipher operation on card failed: {}", e )) })? } ( PublicParams::ECDH(EcdhPublicParams::Known { curve, alg_sym, hash, .. }), PkeskBytes::Ecdh { public_point, encrypted_session_key, }, ) => { let ciphertext = public_point.as_bytes(); let ciphertext = if *curve == ECCCurve::Curve25519 { assert_eq!( ciphertext[0], 0x40, "Unexpected shape of Cv25519 encrypted data" ); // Strip trailing 0x40 &ciphertext[1..] } else { // For NIST and brainpool: we decrypt the ciphertext as is ciphertext }; let cryptogram = openpgp_card::ocard::crypto::Cryptogram::ECDH(ciphertext); if self.touch_required(&mut tx) { (self.touch_prompt)(); } let shared_secret: Vec = tx.card().decipher(cryptogram).map_err(|e| { pgp::errors::Error::Message(format!( "ECDH decipher operation on card failed {}", e )) })?; let encrypted_key_len = encrypted_session_key.len(); let decrypted_key: Vec = pgp::crypto::ecdh::derive_session_key( &shared_secret, encrypted_session_key, encrypted_key_len, &(curve.clone(), *alg_sym, *hash), self.public_key.fingerprint().as_bytes(), )?; decrypted_key } pp => { return Err(pgp::errors::Error::Message(format!( "decrypt: Unsupported key type {:?}", pp ))); } }; // strip off the leading session key algorithm octet, and the two trailing checksum octets let dec_len = decrypted_key.len(); let (sessionkey, checksum) = ( &decrypted_key[1..dec_len - 2], &decrypted_key[dec_len - 2..], ); // ... check the checksum, while we have it at hand checksum::simple(checksum, sessionkey)?; let session_key_algorithm = decrypted_key[0].into(); Ok((sessionkey.to_vec(), session_key_algorithm)) } pub fn decrypt_message(&self, message: &Message) -> Result { let Message::Encrypted { esk, edata } = message else { return Err(pgp::errors::Error::Message( "message must be Message::Encrypted".to_string(), )); }; let values = match &esk[0] { Esk::PublicKeyEncryptedSessionKey(ref k) => k.values()?, _ => { return Err(pgp::errors::Error::Message( "Expected PublicKeyEncryptedSessionKey".to_string(), )) } }; let (session_key, session_key_algorithm) = self.unlock(String::new, |priv_key| priv_key.decrypt(values))?; let plain_session_key = PlainSessionKey::V3_4 { key: session_key, sym_alg: session_key_algorithm, }; let decrypted = edata.decrypt(plain_session_key)?; Ok(decrypted) } } impl Debug for CardSlot<'_, '_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { // FIXME: also show card identifier write!(f, "CardSlot for {:?}", self.public_key)?; Ok(()) } } impl PublicKeyTrait for CardSlot<'_, '_> { fn version(&self) -> KeyVersion { KeyVersion::V4 // FIXME? } fn fingerprint(&self) -> Fingerprint { self.public_key.fingerprint() } fn key_id(&self) -> KeyId { self.public_key.key_id() } fn algorithm(&self) -> PublicKeyAlgorithm { self.public_key.algorithm() } fn created_at(&self) -> &DateTime { self.public_key.created_at() } fn expiration(&self) -> Option { None } fn verify_signature( &self, hash: HashAlgorithm, data: &[u8], sig: &SignatureBytes, ) -> pgp::errors::Result<()> { self.public_key.verify_signature(hash, data, sig) } fn encrypt( &self, rng: R, plain: &[u8], typ: pgp::types::EskType, ) -> pgp::errors::Result { self.public_key.encrypt(rng, plain, typ) } fn serialize_for_hashing(&self, writer: &mut impl std::io::Write) -> pgp::errors::Result<()> { self.public_key.serialize_for_hashing(writer) } fn public_params(&self) -> &PublicParams { self.public_key.public_params() } } impl SecretKeyTrait for CardSlot<'_, '_> { // We model the key data as a public primary key packet for this type. // FIXME: The choice of this type is a bit arbitrary. type PublicKey = PublicKey; type Unlocked = Self; fn unlock(&self, _pw: F, work: G) -> pgp::errors::Result where F: FnOnce() -> String, G: FnOnce(&Self::Unlocked) -> pgp::errors::Result, { work(self) } fn create_signature( &self, _key_pw: F, hash: HashAlgorithm, data: &[u8], ) -> pgp::errors::Result where F: FnOnce() -> String, { #[allow(clippy::unwrap_used)] let mut tx = self.tx.lock().unwrap(); let hash = match self.public_key.algorithm() { PublicKeyAlgorithm::RSA => to_hash_rsa(data, hash)?, PublicKeyAlgorithm::ECDSA => Hash::ECDSA({ match self.public_key.public_params() { PublicParams::ECDSA(EcdsaPublicParams::P256 { .. }) => &data[..32], PublicParams::ECDSA(EcdsaPublicParams::P384 { .. }) => &data[..48], PublicParams::ECDSA(EcdsaPublicParams::P521 { .. }) => &data[..64], _ => data, } }), PublicKeyAlgorithm::EdDSALegacy => Hash::EdDSA(data), _ => { return Err(pgp::errors::Error::Unimplemented(format!( "Unsupported PublicKeyAlgorithm for signature creation: {:?}", self.public_key.algorithm() ))) } }; if self.touch_required(&mut tx) { (self.touch_prompt)(); } let sig = match self.key_type { KeyType::Signing => tx.card().signature_for_hash(hash).map_err(map_card_err)?, KeyType::Authentication => tx .card() .authenticate_for_hash(hash) .map_err(map_card_err)?, _ => { return Err(pgp::errors::Error::Unimplemented(format!( "Unsupported KeyType for signature creation: {:?}", self.key_type ))) } }; let mpis = match self.public_key.algorithm() { PublicKeyAlgorithm::RSA => vec![Mpi::from_raw(sig)], PublicKeyAlgorithm::ECDSA => { let mid = sig.len() / 2; vec![Mpi::from_slice(&sig[..mid]), Mpi::from_slice(&sig[mid..])] } PublicKeyAlgorithm::EdDSALegacy => { assert_eq!(sig.len(), 64); // FIXME: check curve; add error handling vec![Mpi::from_slice(&sig[..32]), Mpi::from_slice(&sig[32..])] } alg => { return Err(pgp::errors::Error::Unimplemented(format!( "Unsupported algorithm for signature creation: {:?}", alg ))) } }; Ok(SignatureBytes::Mpis(mpis)) } fn public_key(&self) -> Self::PublicKey { self.public_key.clone() } } fn to_hash_rsa(data: &[u8], hash: HashAlgorithm) -> pgp::errors::Result { match hash { HashAlgorithm::SHA2_256 => { if data.len() == 0x20 { #[allow(clippy::unwrap_used)] Ok(Hash::SHA256(data.try_into().unwrap())) } else { Err(pgp::errors::Error::Message(format!( "Illegal digest len for SHA256: {}", data.len() ))) } } HashAlgorithm::SHA2_384 => { if data.len() == 0x30 { #[allow(clippy::unwrap_used)] Ok(Hash::SHA384(data.try_into().unwrap())) } else { Err(pgp::errors::Error::Message(format!( "Illegal digest len for SHA384: {}", data.len() ))) } } HashAlgorithm::SHA2_512 => { if data.len() == 0x40 { #[allow(clippy::unwrap_used)] Ok(Hash::SHA512(data.try_into().unwrap())) } else { Err(pgp::errors::Error::Message(format!( "Illegal digest len for SHA512: {}", data.len() ))) } } _ => Err(pgp::errors::Error::Message(format!( "Unsupported HashAlgorithm for RSA: {:?}", hash ))), } } openpgp-card-rpgp-0.3.0/src/lib.rs000064400000000000000000000040471046102023000150610ustar 00000000000000// SPDX-FileCopyrightText: Wiktor Kwapisiewicz // SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: Apache-2.0 OR MIT //! This is a crate for using //! [OpenPGP card devices](https://en.wikipedia.org/wiki/OpenPGP_card) with //! the [`rPGP`](https://crates.io/crates/pgp) OpenPGP library. //! //! In fact, this crate is a supplement for the //! [`openpgp-card`](https://crates.io/crates/openpgp-card) crate. //! This crate, `openpgp-card-rpgp`, enables performing OpenPGP-specific //! operations on cards, by leveraging both the `rPGP` library and `openpgp-card`. //! If you want to use this crate, you will probably also want to use //! `openpgp-card` itself: //! //! Much of the functionality of an OpenPGP card device doesn't actually //! involve the OpenPGP format. All of that functionality is available in //! `openpgp-card`, without requiring support for the OpenPGP format. //! //! This crate implements additional support for operations that *do* require //! handling the OpenPGP format: //! //! - Creating OpenPGP signatures //! - Decryption of OpenPGP data //! - Import of OpenPGP private key material //! //! See this project's "examples" for some pointers on how to use this crate. mod cardslot; mod private; mod rpgp; use std::fmt::Debug; pub use cardslot::CardSlot; pub use private::UploadableKey; pub use rpgp::{ bind_into_certificate, public_key_material_and_fp_to_key, public_key_material_to_key, public_to_fingerprint, }; /// Enum wrapper for the error types of this crate #[derive(thiserror::Error, Debug)] #[non_exhaustive] pub enum Error { #[error("rPGP error: {0}")] Rpgp(pgp::errors::Error), #[error("OpenPGP card error: {0}")] Ocard(openpgp_card::Error), #[error("Internal error: {0}")] Message(String), } impl From for Error { fn from(value: pgp::errors::Error) -> Self { Error::Rpgp(value) } } impl From for Error { fn from(value: openpgp_card::Error) -> Self { Error::Ocard(value) } } openpgp-card-rpgp-0.3.0/src/private.rs000064400000000000000000000230441046102023000157630ustar 00000000000000// SPDX-FileCopyrightText: Wiktor Kwapisiewicz // SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: Apache-2.0 OR MIT use openpgp_card::ocard::crypto::{CardUploadableKey, EccKey, EccType, PrivateKeyMaterial, RSAKey}; use openpgp_card::ocard::data::{Fingerprint, KeyGenerationTime}; use openpgp_card::Error; use pgp::crypto::ecc_curve::ECCCurve; use pgp::types::{ EcdhPublicParams, EcdsaPublicParams, Mpi, PlainSecretParams, PublicKeyTrait, PublicParams, SecretParams, }; use rsa::traits::PrivateKeyParts; /// Shared type for rPGP "secret key" packets (primary or subkey) enum Sec { Key(pgp::packet::SecretKey), SubKey(pgp::packet::SecretSubkey), } impl Sec { fn public_params(&self) -> &PublicParams { match self { Sec::Key(sk) => sk.public_params(), Sec::SubKey(ssk) => ssk.public_params(), } } fn secret_params(&self) -> &SecretParams { match self { Sec::Key(sk) => sk.secret_params(), Sec::SubKey(ssk) => ssk.secret_params(), } } } /// Private key material that can be uploaded to a card slot. /// /// The data in an `UploadableKey` corresponds to an OpenPGP secret key packet. pub struct UploadableKey { key: Sec, unlocked: Option, } impl From for UploadableKey { fn from(value: pgp::packet::SecretKey) -> Self { Self { key: Sec::Key(value), unlocked: None, } } } impl From for UploadableKey { fn from(value: pgp::packet::SecretSubkey) -> Self { Self { key: Sec::SubKey(value), unlocked: None, } } } impl UploadableKey { pub fn is_locked(&self) -> bool { match self.key.secret_params() { SecretParams::Plain(_) => false, SecretParams::Encrypted(_) => true, } } /// Returns: /// - `Ok(false)` for keys that are not password protected /// - `Ok(true)` for protected keys that were successfully unlocked /// - `Err` when the password did not unlock the key (in this case, the key cannot be imported to a card). pub fn try_unlock(&mut self, pw: &str) -> Result { match self.key.secret_params() { SecretParams::Plain(_) => Ok(false), SecretParams::Encrypted(esp) => { match &self.key { // FIXME: this duplication is unfortunate Sec::Key(sk) => { if let Ok(psp) = esp.unlock(|| pw.to_string(), sk, None) { self.unlocked = Some(psp); return Ok(true); } } Sec::SubKey(ssk) => { if let Ok(psp) = esp.unlock(|| pw.to_string(), ssk, None) { self.unlocked = Some(psp); return Ok(true); } } } Err(Error::InternalError("Could not unlock key".to_string())) } } } } impl CardUploadableKey for UploadableKey { fn private_key(&self) -> Result { fn to_privatekeymaterial( psp: &PlainSecretParams, pp: &PublicParams, ) -> Result { match (psp, pp) { (PlainSecretParams::RSA { p, q, d, .. }, PublicParams::RSA { n, e }) => { let rsa_key = Rsa::new(e.clone(), d.clone(), n.clone(), p.clone(), q.clone())?; Ok(PrivateKeyMaterial::R(Box::new(rsa_key))) } (PlainSecretParams::ECDSA(m), PublicParams::ECDSA(ecdsa)) => { let (curve, p) = match ecdsa { EcdsaPublicParams::P256 { p, .. } => (ECCCurve::P256, p), EcdsaPublicParams::P384 { p, .. } => (ECCCurve::P384, p), EcdsaPublicParams::P521 { p, .. } => (ECCCurve::P521, p), EcdsaPublicParams::Secp256k1 { .. } => { return Err(Error::UnsupportedAlgo( "ECDSA with curve Secp256k1 is unsupported".to_string(), )) } EcdsaPublicParams::Unsupported { curve, .. } => { return Err(Error::UnsupportedAlgo(format!( "ECDSA with curve {} is unsupported", curve.name() ))) } }; let ecc = Ecc::new(curve, m.clone(), p.clone(), EccType::ECDSA); Ok(PrivateKeyMaterial::E(Box::new(ecc))) } (PlainSecretParams::EdDSALegacy(m), PublicParams::EdDSALegacy { curve, q }) => { let ecc = Ecc::new(curve.clone(), m.clone(), q.clone(), EccType::EdDSA); Ok(PrivateKeyMaterial::E(Box::new(ecc))) } ( PlainSecretParams::ECDH(m), PublicParams::ECDH(EcdhPublicParams::Known { curve, p, .. }), ) => { let ecc = Ecc::new(curve.clone(), m.clone(), p.clone(), EccType::ECDH); Ok(PrivateKeyMaterial::E(Box::new(ecc))) } _ => Err(Error::UnsupportedAlgo(format!( "Unsupported key material {:?}", pp ))), } } let pp = self.key.public_params(); let psp = match self.key.secret_params() { SecretParams::Plain(psp) => psp, SecretParams::Encrypted(_) => { if let Some(psp) = &self.unlocked { psp } else { return Err(Error::InternalError( "Secret key packet wasn't unlocked".to_string(), )); } } }; to_privatekeymaterial(psp, pp) } fn timestamp(&self) -> KeyGenerationTime { let ts = match &self.key { Sec::Key(sk) => sk.created_at(), Sec::SubKey(ssk) => ssk.created_at(), }; let ts = ts.timestamp() as u32; ts.into() } fn fingerprint(&self) -> Result { let fp = match &self.key { Sec::Key(sk) => sk.fingerprint(), Sec::SubKey(ssk) => ssk.fingerprint(), }; Fingerprint::try_from(fp.as_bytes()) } } struct Rsa { e: Mpi, n: Mpi, p: Mpi, q: Mpi, pq: Mpi, dp1: Mpi, dq1: Mpi, } impl Rsa { fn new(e: Mpi, d: Mpi, n: Mpi, p: Mpi, q: Mpi) -> Result { let key = rsa::RsaPrivateKey::from_components( n.clone().into(), e.clone().into(), d.into(), vec![p.clone().into(), q.clone().into()], ) .map_err(|e| Error::InternalError(format!("rsa error {e:?}")))?; let pq = Mpi::from_raw( key.qinv() .ok_or_else(|| Error::InternalError("pq value missing".into()))? .to_biguint() .ok_or_else(|| Error::InternalError("conversion to bigunit failed".into()))? .to_bytes_be(), ); let dp1 = Mpi::from_raw( key.dp() .ok_or_else(|| Error::InternalError("dp1 value missing".into()))? .to_bytes_be(), ); let dq1 = Mpi::from_raw( key.dq() .ok_or_else(|| Error::InternalError("dq1 value missing".into()))? .to_bytes_be(), ); Ok(Self { e, n, p, q, pq, dp1, dq1, }) } } impl RSAKey for Rsa { fn e(&self) -> &[u8] { self.e.as_bytes() } fn p(&self) -> &[u8] { self.p.as_bytes() } fn q(&self) -> &[u8] { self.q.as_bytes() } fn pq(&self) -> Box<[u8]> { Box::from(self.pq.as_bytes()) } fn dp1(&self) -> Box<[u8]> { Box::from(self.dp1.as_bytes()) } fn dq1(&self) -> Box<[u8]> { Box::from(self.dq1.as_bytes()) } fn n(&self) -> &[u8] { self.n.as_bytes() } } /// ECC-specific data-structure to hold private (sub)key material for upload /// with the `openpgp-card` crate. struct Ecc { curve: ECCCurve, private: Mpi, public: Mpi, ecc_type: EccType, oid: Vec, } impl Ecc { fn new(curve: ECCCurve, private: Mpi, public: Mpi, ecc_type: EccType) -> Self { let oid = curve.oid(); Ecc { curve, private, public, ecc_type, oid, } } } fn pad(mut v: Vec, len: usize) -> Vec { while v.len() < len { v.insert(0, 0) } v } impl EccKey for Ecc { fn oid(&self) -> &[u8] { &self.oid } fn private(&self) -> Vec { match self.curve { ECCCurve::P256 => pad(self.private.to_vec(), 0x20), ECCCurve::P384 => pad(self.private.to_vec(), 0x30), ECCCurve::P521 => pad(self.private.to_vec(), 0x42), ECCCurve::Curve25519 | ECCCurve::Ed25519 => pad(self.private.to_vec(), 0x20), _ => self.private.to_vec(), } } fn public(&self) -> Vec { // FIXME: padding? self.public.to_vec() } fn ecc_type(&self) -> EccType { self.ecc_type } } openpgp-card-rpgp-0.3.0/src/rpgp.rs000064400000000000000000000350171046102023000152640ustar 00000000000000// SPDX-FileCopyrightText: Wiktor Kwapisiewicz // SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: Apache-2.0 OR MIT use chrono::{DateTime, SubsecRound, Utc}; use openpgp_card::ocard::algorithm::{AlgorithmAttributes, Curve}; use openpgp_card::ocard::crypto::{EccType, PublicKeyMaterial}; use openpgp_card::ocard::data::{Fingerprint, KeyGenerationTime}; use openpgp_card::ocard::KeyType; use pgp::crypto::ecc_curve::ECCCurve; use pgp::crypto::hash::HashAlgorithm; use pgp::crypto::public_key::PublicKeyAlgorithm; use pgp::crypto::sym::SymmetricKeyAlgorithm; use pgp::packet::{ KeyFlags, PacketTrait, PublicKey, PublicSubkey, SignatureConfig, SignatureType, Subpacket, SubpacketData, UserId, }; use pgp::types::{ EcdhPublicParams, EcdsaPublicParams, KeyVersion, Mpi, PublicKeyTrait, PublicParams, SecretKeyTrait, SignedUser, Version, }; use pgp::{SignedKeyDetails, SignedPublicKey, SignedPublicSubKey}; use secrecy::SecretString; use crate::{CardSlot, Error}; /// value pairs that we'll consider in ECDH parameter auto-detection const ECDH_PARAM: &[(Option, Option)] = &[ ( Some(HashAlgorithm::SHA2_256), Some(SymmetricKeyAlgorithm::AES128), ), ( Some(HashAlgorithm::SHA2_512), Some(SymmetricKeyAlgorithm::AES256), ), ( Some(HashAlgorithm::SHA2_384), Some(SymmetricKeyAlgorithm::AES256), ), ( Some(HashAlgorithm::SHA2_384), Some(SymmetricKeyAlgorithm::AES192), ), ( Some(HashAlgorithm::SHA2_256), Some(SymmetricKeyAlgorithm::AES256), ), ]; fn pubkey( algo: PublicKeyAlgorithm, created: DateTime, param: PublicParams, ) -> Result { PublicKey::new( Version::New, KeyVersion::V4, // FIXME: handle other OpenPGP key versions, later algo, created, None, param, ) } fn map_curve(c: &Curve) -> Result { Ok(match c { Curve::NistP256r1 => ECCCurve::P256, Curve::NistP384r1 => ECCCurve::P384, Curve::NistP521r1 => ECCCurve::P521, Curve::BrainpoolP256r1 => ECCCurve::BrainpoolP256r1, Curve::BrainpoolP384r1 => ECCCurve::BrainpoolP384r1, Curve::BrainpoolP512r1 => ECCCurve::BrainpoolP512r1, Curve::Ed25519 => ECCCurve::Ed25519, Curve::Curve25519 => ECCCurve::Curve25519, _ => { return Err(pgp::errors::Error::Unimplemented(format!( "Can't map curve {:?}", c ))) } }) } pub(crate) fn map_card_err(e: openpgp_card::Error) -> pgp::errors::Error { pgp::errors::Error::Message(format!("openpgp_card error: {:?}", e)) } /// Get PublicKey for an openpgp-card PublicKeyMaterial, KeyGenerationTime and Fingerprint. /// /// For ECC decryption keys, possible values for the parameters `hash` and `alg_sym` will be tested. /// If a key with matching fingerprint is found in this way, it is considered the correct key, /// and returned. /// /// The Fingerprint of the retrieved PublicKey is always validated against the `Fingerprint` as /// stored on the card. If the fingerprints don't match, an Error is returned. pub fn public_key_material_and_fp_to_key( pkm: &PublicKeyMaterial, key_type: KeyType, created: &KeyGenerationTime, fingerprint: &Fingerprint, ) -> Result { // Possible hash/sym parameters based on statistics over 2019-12 SKS dump: // https://gitlab.com/sequoia-pgp/sequoia/-/issues/838#note_909813463 let param: &[_] = match (pkm, key_type) { (PublicKeyMaterial::E(_), KeyType::Decryption) => ECDH_PARAM, _ => &[(None, None)], }; for (hash, alg_sym) in param { if let Ok(key) = public_key_material_to_key(pkm, key_type, created, *hash, *alg_sym) { // check FP if key.fingerprint().as_bytes() == fingerprint.as_bytes() { // return if match return Ok(key); } } } Err(pgp::errors::Error::Message( "Couldn't find key with matching fingerprint".to_string(), )) } /// Transform an openpgp-card `PublicKeyMaterial` and `KeyGenerationTime` /// into an rpgp `PublicKey` representation. /// /// For ECDH decryption keys, `hash` and `alg_sym` can be optionally specified. /// /// If `hash` and `alg_sym` are required, and the caller passes `None`, /// curve-specific default parameters are used. pub fn public_key_material_to_key( pkm: &PublicKeyMaterial, key_type: KeyType, created: &KeyGenerationTime, hash: Option, alg_sym: Option, ) -> Result { #[allow(clippy::expect_used)] let created = DateTime::::from_timestamp(created.get() as i64, 0).expect("u32 time from card"); match pkm { PublicKeyMaterial::R(rsa) => pubkey( PublicKeyAlgorithm::RSA, created, PublicParams::RSA { n: Mpi::from_slice(rsa.n()), e: Mpi::from_slice(rsa.v()), }, ), PublicKeyMaterial::E(ecc) => match ecc.algo() { AlgorithmAttributes::Ecc(ecc_attr) => { let typ = ecc_attr.ecc_type(); let curve = map_curve(ecc_attr.curve())?; let (pka, pp) = match typ { EccType::ECDH => { if key_type != KeyType::Decryption { return Err(pgp::errors::Error::Message(format!( "ECDH is unsupported in key slot {:?}", key_type ))); } let mut p = ecc.data().to_vec(); if curve == ECCCurve::Curve25519 && p.len() == 32 { // prepend OpenPGP 0x40 prefix for curve 25519 MPI p.insert(0, 0x40); } let hash = hash.unwrap_or(curve.hash_algo()?); let alg_sym = alg_sym.unwrap_or(curve.sym_algo()?); let pp = PublicParams::ECDH(EcdhPublicParams::Known { curve: curve.clone(), p: Mpi::from_raw(p), hash, alg_sym, }); (PublicKeyAlgorithm::ECDH, pp) } EccType::ECDSA => ( PublicKeyAlgorithm::ECDSA, PublicParams::ECDSA(EcdsaPublicParams::try_from_mpi( Mpi::from_slice(ecc.data()).as_ref(), curve, )?), ), EccType::EdDSA => { let mut q = ecc.data().to_vec(); if q.len() == 32 { // Add prefix to mark that this MPI uses EdDSA point representation. // See https://datatracker.ietf.org/doc/draft-koch-eddsa-for-openpgp/ q.insert(0, 0x40); } ( PublicKeyAlgorithm::EdDSALegacy, PublicParams::EdDSALegacy { curve, q: Mpi::from_raw(q), }, ) } }; pubkey(pka, created, pp) } _ => Err(pgp::errors::Error::Message(format!( "Unexpected AlgorithmAttributes type in Ecc {:?}", ecc.algo(), ))), }, } } pub(crate) fn pubkey_from_card( tx: &mut openpgp_card::Card, key_type: KeyType, ) -> Result { let pkm = tx.public_key_material(key_type).map_err(map_card_err)?; let Some(created) = tx.key_generation_time(key_type).map_err(map_card_err)? else { // KeyGenerationTime is None return Err(pgp::errors::Error::Message(format!( "No creation time set for OpenPGP card key type {:?}", key_type, ))); }; let Some(fingerprint) = tx.fingerprint(key_type).map_err(map_card_err)? else { // Fingerprint is None return Err(pgp::errors::Error::Message(format!( "No fingerprint found for key slot {:?}", key_type ))); }; public_key_material_and_fp_to_key(&pkm, key_type, &created, &fingerprint) } /// Calculate the `Fingerprint` for `PublicKeyMaterial` and `KeyGenerationTime` pub fn public_to_fingerprint( pkm: &PublicKeyMaterial, kgt: KeyGenerationTime, kt: KeyType, ) -> Result { let key = public_key_material_to_key(pkm, kt, &kgt, None, None).map_err(|e| { openpgp_card::Error::InternalError(format!("public_key_material_to_key: {}", e)) })?; let fp = key.fingerprint(); openpgp_card::ocard::data::Fingerprint::try_from(fp.as_bytes()) } // FIXME: upstream? fn pri_to_sub(pubkey: PublicKey) -> pgp::errors::Result { PublicSubkey::new( pubkey.packet_version(), pubkey.version(), pubkey.algorithm(), *pubkey.created_at(), pubkey.expiration(), pubkey.public_params().clone(), ) } /// Bind the subkeys of a card into a `SignedPublicKey`. /// /// At least one User ID is required. /// /// This function assumes that the signing slot of the card serves as the /// primary key, and uses it to issue binding self-signatures. /// /// If `user_pin` is None, pinpad verification is attempted. /// `pinpad_prompt` is called to notify the user when pinpad input (of the /// User PIN) is required. /// /// `touch_prompt` is called to notify the user when touch confirmation is /// required for a signing operation. /// /// FIXME: Accept optional metadata for user_id binding(s)? #[allow(clippy::too_many_arguments)] pub fn bind_into_certificate( tx: &mut openpgp_card::Card, sig: PublicKey, dec: Option, aut: Option, user_ids: &[String], user_pin: Option, pinpad_prompt: &dyn Fn(), touch_prompt: &(dyn Fn() + Send + Sync), ) -> Result { let primary = sig; if user_ids.is_empty() { return Err(Error::Message( "At least one User ID must be added to create a valid certificate".to_string(), )); } // helper: use the card to perform a signing operation let verify_signing_pin = |txx: &mut openpgp_card::Card< openpgp_card::state::Transaction, >| -> Result<(), openpgp_card::Error> { // Allow signing on the card if let Some(pw1) = user_pin.clone() { txx.verify_user_signing_pin(pw1)?; } else { txx.verify_user_signing_pinpad(pinpad_prompt)?; } Ok(()) }; let mut subkeys = vec![]; if let Some(dec) = dec { // add decryption key as subkey let key = pri_to_sub(dec)?; verify_signing_pin(tx)?; // make binding signature, sign with cs let cs = CardSlot::with_public_key(tx, KeyType::Signing, primary.clone(), touch_prompt)?; let mut kf = KeyFlags::default(); kf.set_encrypt_comms(true); kf.set_encrypt_storage(true); let mut config = SignatureConfig::v4(SignatureType::SubkeyBinding, cs.algorithm(), cs.hash_alg()); config.hashed_subpackets = vec![ Subpacket::regular(SubpacketData::SignatureCreationTime( Utc::now().trunc_subsecs(0), )), Subpacket::regular(SubpacketData::IssuerFingerprint(cs.fingerprint())), Subpacket::regular(SubpacketData::KeyFlags(kf.into())), ]; config.unhashed_subpackets = vec![Subpacket::regular(SubpacketData::Issuer(cs.key_id()))]; let sig = config.sign_key_binding(&cs, String::default, &key)?; let sps = SignedPublicSubKey { key, signatures: vec![sig], }; subkeys.push(sps); } if let Some(aut) = aut { // add decryption key as subkey let key = pri_to_sub(aut)?; verify_signing_pin(tx)?; // make binding signature, sign with cs let cs = CardSlot::with_public_key(tx, KeyType::Signing, primary.clone(), touch_prompt)?; let mut kf = KeyFlags::default(); kf.set_authentication(true); let mut config = SignatureConfig::v4(SignatureType::SubkeyBinding, cs.algorithm(), cs.hash_alg()); config.hashed_subpackets = vec![ Subpacket::regular(SubpacketData::SignatureCreationTime( Utc::now().trunc_subsecs(0), )), Subpacket::regular(SubpacketData::IssuerFingerprint(cs.fingerprint())), Subpacket::regular(SubpacketData::KeyFlags(kf.into())), ]; config.unhashed_subpackets = vec![Subpacket::regular(SubpacketData::Issuer(cs.key_id()))]; let sig = config.sign_key_binding(&cs, String::default, &key)?; let sps = SignedPublicSubKey { key, signatures: vec![sig], }; subkeys.push(sps); } let mut users = vec![]; // add `user_ids`. for uid in user_ids.iter().map(|uid| uid.as_bytes()) { let uid = UserId::from_slice(Version::New, uid)?; let mut kf = KeyFlags::default(); kf.set_certify(true); kf.set_sign(true); verify_signing_pin(tx)?; let cs = CardSlot::with_public_key(tx, KeyType::Signing, primary.clone(), touch_prompt)?; let mut config = SignatureConfig::v4(SignatureType::CertPositive, cs.algorithm(), cs.hash_alg()); config.hashed_subpackets = vec![ Subpacket::regular(SubpacketData::SignatureCreationTime( Utc::now().trunc_subsecs(0), )), Subpacket::regular(SubpacketData::KeyFlags(kf.into())), ]; config.unhashed_subpackets = vec![Subpacket::regular(SubpacketData::Issuer(cs.key_id()))]; let sig = config.sign_certification(&cs, String::default, uid.tag(), &uid)?; let suid = SignedUser::new(uid, vec![sig]); users.push(suid); } // FIXME: generate a direct key signature? let details = SignedKeyDetails::new(vec![], vec![], users, vec![]); let spk = SignedPublicKey::new(primary, details, subkeys); Ok(spk) } openpgp-card-rpgp-0.3.0/tests/README.md000064400000000000000000000133631046102023000156000ustar 00000000000000 Integration tests for openpgp-card-rpgp. 1) Tests openpgp-card and its backends, as well as openpgp-card-rpgp. 2) Compare the behavior of different OpenPGP card firmware implementations. # Building / Dependencies To build this crate on Debian 11, these packages are needed: `apt install rustc libpcsclite-dev`. (Fedora 34: `dnf install rustc cargo pcsc-lite-devel`) # Purpose These tests fail in cases when essential and expected functionality of a card is not working. The scope of what is expected from a specific card is defined in `config/test-cards.toml` (in particular, this configuration specifies which algorithms we expect a card to handle for on-card key generation and for key import, respectively - cards can't signal in detail what they support: e.g. a card may support RSA4096 when importing keys, but not be able to generate such keys on the card). However, in practice, behavior of cards often diverges from the spec in various small ways. In many of those cases, it's not ok for these tests to fail and reject the card's output - even when a card contradicts the OpenPGP card spec. In such cases, these tests return information about the return values of the card in the `TestOutput` data structure, to document that card's behavior. ## Example for card-specific behavior that contradicts the spec YubiKey 5 fails to handle the VERIFY command with empty data (see OpenPGP card spec, 7.2.2: "If the command is called without data, the actual access status of the addressed password is returned or the access status is set to 'not verified'"). The YubiKey 5 erroneously returns Status 0x6a80 ("Incorrect parameters in the command data field"). # Running To access a card via pcsc, we need to install and start `pcscd`. ``` apt install pcscd systemctl enable pcscd systemctl start pcscd ``` (Alternatively, you could use the experimental [scdaemon backend](https://gitlab.com/openpgp-card/openpgp-card/-/tree/scdc)) # Running tests (against emulated Gnuk via PC/SC) In this section we will set up an "emulated Gnuk" OpenPGP card on your computer that you can run the tests against. ## About Gnuk Gnuk is a free implementation of the OpenPGP card spec by [Gniibe](https://www.gniibe.org/), see: http://www.fsij.org/doc-gnuk/ (Gniibe also designs and produces [an open hardware USB token for Gnuk](https://www.gniibe.org/tag/fst-01.html)) For testing purposes, we will run the Gnuk code in "emulated" mode (here, "emulated" means: Gnuk will run directly on our host system, instead of on a USB-connected device as one would usually use Gnuk). ## Building Gnuk Install additional dependencies (Debian 11: `# apt install make usbip`, Fedora 34: `# dnf install make usbip`) We'll use the stable 1.2 branch of Gnuk, with the latest patches for chopstx (which are necessary for emulated Gnuk to work with PC/SC). Get the Gnuk source code: ``` git clone https://git.gniibe.org/cgit/gnuk/gnuk.git/ cd gnuk git checkout STABLE-BRANCH-1-2 git submodule update --init cd chopstx git checkout master cd ../src ``` Now we can build the emulated Gnuk. We don't want to use KDF in our tests, so we disable Gnuk's default behavior (by default, emulated Gnuk considers KDF "required") with the `kdf_do=optional` variable (I am not aware of any OpenPGP card that requires KDF by default, so the tests currently don't use KDF). ``` kdf_do=optional ./configure --target=GNU_LINUX --enable-factory-reset make ``` ## Running the emulated Gnuk Emulated Gnuk connects to the system via http://usbip.sourceforge.net/. This means that we need to load the kernel module `vhci_hcd` to connect to a running emulated Gnuk instance. First, we start the emulated Gnuk from a non-root account: ``` ./build/gnuk --vidpid=234b:0000 ``` Then, as root, we attach to the emulated Gnuk device: ``` # modprobe vhci_hcd # usbip attach -r 127.0.0.1 -b 1-1 ``` Afterwards, emulated Gnuk is connected to the system. We can now talk to it, e.g. we can look it up with `pcsc_scan`: ``` $ pcsc_scan Using reader plug'n play mechanism Scanning present readers... [..] 2: Free Software Initiative of Japan Gnuk (FSIJ-1.2.18-EMULATED) 00 00 [..] ``` ## Run the card-functionality tests against the emulated Gnuk First, we determine the `ident` of all connected OpenPGP cards (we're looking specifically for our emulated Gnuk instance, which will show up with the manufacturer code "FFFE", from the "Range reserved for randomly assigned serial numbers"): ``` $ cargo run --bin list-cards [...] The following OpenPGP cards are connected to your system: FFFE:F1420A7A ``` Then we edit the test config file in `config/test-cards.toml` to use this emulated Gnuk. The configuration should look like this: ``` [card.gnuk_emu] backend.pcsc = "FFFE:F1420A7A" config.keygen = ["RSA2k/32", "NIST256", "Curve25519"] config.import = ["data/rsa2k.sec", "data/rsa4k.sec", "data/nist256.sec", "data/25519.sec"] ``` Now we can run the `card-functionality` tests, e.g. import and key generation: ``` $ cargo run --bin import [...] $ cargo run --bin keygen [...] ``` # Running tests against a physical OpenPGP card token Instead of running these tests against an emulated Gnuk, you can of course use a physical OpenPGP card. This is actually easier to do: you can just plug in a physical card, without needing to build or run any code. However, be aware that these tests **delete all state on your card**! Any **keys on your card will be deleted** when you run these tests. To run the tests against any card, you need to explicitly configure that card in the configuration file `config/test-cards.toml`. Without specifying a card's identifier in the test configuration, tests will not be run against a card, even if you have the card plugged in while running tests. openpgp-card-rpgp-0.3.0/tests/cards.rs000064400000000000000000000076261046102023000157700ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 //! Wrapping of cards for tests. Open a list of cards, based on a //! TestConfig configuration file use std::collections::BTreeMap; use anyhow::Result; use card_backend_pcsc::PcscBackend; use card_backend_scdc::ScdBackend; use openpgp_card::state::Open; use openpgp_card::Error; use serde::Deserialize; // const SHARE_MODE: Option = Some(ShareMode::Shared); #[derive(Debug, Deserialize)] pub struct TestConfig { card: BTreeMap, } #[derive(Debug, Deserialize)] pub struct Card { backend: BTreeMap, config: Config, } #[derive(Clone, Debug, Deserialize)] pub struct Config { pub keygen: Option>, pub import: Option>, } /// An "opened" card, via one particular backend, with test-metadata #[derive(Debug)] pub struct TestCardData { name: String, tc: TestCard, config: Config, } impl TestCardData { pub fn get_card(&self) -> Result> { self.tc.open() } pub fn get_config(&self) -> &Config { &self.config } pub fn get_name(&self) -> &str { &self.name } } impl TestConfig { pub fn load(file: &str) -> Result { let config_file = std::fs::read_to_string(file)?; let config: Self = toml::from_str(&config_file)?; Ok(config) } pub fn into_cardapps(self) -> Vec { let mut cards = vec![]; for (name, card) in self.card { for (backend, id) in &card.backend { let tc: TestCard = match backend.as_str() { "pcsc" => TestCard::Pcsc(id.to_string()), "scdc" => TestCard::Scdc(id.to_string()), _ => panic!("unexpected backend {}", backend), }; cards.push(TestCardData { name: name.clone(), tc, config: card.config.clone(), }) } } cards } } #[derive(Debug)] pub enum TestCard { Pcsc(String), Scdc(String), } impl TestCard { pub fn open(&self) -> Result> { match self { Self::Pcsc(ident) => { // Attempt to shutdown SCD, if it is running. // Ignore any errors that occur during that shutdown attempt. let res = ScdBackend::shutdown_scd(None); log::trace!(" Attempt to shutdown scd: {:?}", res); // Make three attempts to open the card before failing // (this can be useful in ShareMode::Exclusive) let mut i = 1; let card: Result, Error> = loop { i += 1; let cards = PcscBackend::card_backends(None)?; let res = openpgp_card::Card::::open_by_ident(cards, ident); println!("Got result for card: {}", ident); if let Err(e) = &res { println!("Result is an error: {:x?}", e); } else { println!("Result is a happy card"); } if let Ok(res) = res { break Ok(res); } if i > 3 { break Err(Error::NotFound(format!("Couldn't open card {}", ident))); } // sleep for 100ms println!("Will sleep for 100ms"); std::thread::sleep(std::time::Duration::from_millis(100)); }; Ok(card?) } Self::Scdc(serial) => { let backend = ScdBackend::open_by_serial(None, serial)?; Ok(openpgp_card::Card::::new(backend)?) } } } } openpgp-card-rpgp-0.3.0/tests/ci/virt-canokey.toml000064400000000000000000000006561046102023000202250ustar 00000000000000# SPDX-FileCopyrightText: Heiko Schaefer # SPDX-License-Identifier: CC0-1.0 [card.canokey] backend.pcsc = "F1D0:00000000" config.keygen = [ "RSA2k", "RSA3k", "RSA4k", "NIST256", "NIST384", "Curve25519" ] config.import = [ "tests/keys/rsa2k.tsk", "tests/keys/rsa3k.tsk", "tests/keys/rsa4k.tsk", "tests/keys/nist256.tsk", "tests/keys/nist384.tsk", "tests/keys/25519.tsk", ]openpgp-card-rpgp-0.3.0/tests/ci/virt-fluffypgp.toml000064400000000000000000000003421046102023000205660ustar 00000000000000# SPDX-FileCopyrightText: Heiko Schaefer # SPDX-License-Identifier: CC0-1.0 [card.fluffypgp] backend.pcsc = "FFF1:00000001" config.keygen = [ "RSA2k" ] config.import = [ "tests/keys/rsa2k.tsk", ]openpgp-card-rpgp-0.3.0/tests/ci/virt-opcard-rs.toml000064400000000000000000000006101046102023000204540ustar 00000000000000# SPDX-FileCopyrightText: Heiko Schaefer # SPDX-License-Identifier: CC0-1.0 [card.opcard-rs] backend.pcsc = "0000:00000000" config.keygen = [ "RSA2k", "RSA3k", "RSA4k", "NIST256", "Curve25519" ] config.import = [ "tests/keys/rsa2k.tsk", "tests/keys/rsa3k.tsk", "tests/keys/rsa4k.tsk", "tests/keys/nist256.tsk", "tests/keys/25519.tsk", ] openpgp-card-rpgp-0.3.0/tests/ci/virt-smartpgp.toml000064400000000000000000000006521046102023000204250ustar 00000000000000# SPDX-FileCopyrightText: Heiko Schaefer # SPDX-License-Identifier: CC0-1.0 [card.smartpgp] backend.pcsc = "AFAF:00001234" config.keygen = [ "RSA2k", "RSA3k", "RSA4k", "NIST256", "NIST384", "NIST521" ] config.import = [ "tests/keys/rsa2k.tsk", "tests/keys/rsa3k.tsk", "tests/keys/rsa4k.tsk", "tests/keys/nist256.tsk", "tests/keys/nist384.tsk", "tests/keys/nist521.tsk" ] openpgp-card-rpgp-0.3.0/tests/ci/virt-ykneo.toml000064400000000000000000000003361046102023000177140ustar 00000000000000# SPDX-FileCopyrightText: Heiko Schaefer # SPDX-License-Identifier: CC0-1.0 [card.ykneo] backend.pcsc = "0006:11112222" config.keygen = [ "RSA2k" ] config.import = [ "tests/keys/rsa2k.tsk", ]openpgp-card-rpgp-0.3.0/tests/config/test-cards.toml.example000064400000000000000000000027421046102023000221650ustar 00000000000000# SPDX-FileCopyrightText: 2021 Heiko Schaefer # SPDX-License-Identifier: MIT OR Apache-2.0 # # This configuration specifies which cards the test suite is performed on. # # NOTE: the test suite is **DESTRUCTIVE**. # It will **OVERWRITE** all data on test cards! # # The test suite reads the configuration from 'config/test-cards.toml'. # You should configure your set of test-card identifiers and store your # configuration in that location. # # Usually you will want to test cards via the pcsc backend, which talks # to smartcards via the standard PCSC lite middleware # (https://pcsclite.apdu.fr/). # # An alternative scdc backend is provided. It talks to smartcards via # scdaemon (which is part of the GnuPG system). # The scdc backend is not recommended for use by default, it is offered # for experimental use only. # # (However, note that emulated Gnuk can currently only be accessed via scdc) [card.gnuk_emu] #backend.pcsc = "FFFE:F1420A7A" backend.scdc = "D276000124010200FFFEF1420A7A0000" config.keygen = ["RSA2k", "NIST256", "Curve25519"] config.import = ["data/rsa2k.sec", "data/rsa4k.sec", "data/25519.sec", "data/nist256.sec", "data/nist521.sec"] [card.yubikey5] backend.pcsc = "0006:16019180" #backend.scdc = "D2760001240103040006160191800000" config.keygen = [ "RSA2k", "RSA3k", "RSA4k", "NIST256", "NIST384", "NIST521", "Curve25519" ] config.import = ["data/rsa2k.sec", "data/rsa4k.sec", "data/25519.sec", "data/nist256.sec", "data/nist521.sec"] openpgp-card-rpgp-0.3.0/tests/import.rs000064400000000000000000000056241046102023000162020ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 #![allow(dead_code)] use std::env; use anyhow::Result; use openpgp_card::state::Open; use openpgp_card::Card; use pgp::{Deserializable, SignedPublicKey, SignedSecretKey}; use crate::cards::TestConfig; use crate::util::{ run_test, test_decrypt, test_reset, test_set_login_data, test_set_user_data, test_sign, test_upload_keys, TestError, }; mod cards; mod util; #[ignore] #[test] fn import() -> Result<()> { env_logger::init(); let config = match env::var("TEST_CONFIG") { Ok(path) => TestConfig::load(&path)?, Err(_) => TestConfig::load("config/test-cards.toml")?, // fallback }; let cards = config.into_cardapps(); for card in cards { println!("** Run tests on card '{}' **", card.get_name()); let mut c: Card = card.get_card()?; println!(" -> Card opened"); let mut tx = c.transaction()?; println!(" started transaction"); println!("Reset"); let _ = run_test(&mut tx, test_reset, &[])?; print!("Set user data"); let userdata_out = run_test(&mut tx, test_set_user_data, &[])?; println!(" {userdata_out:x?}"); print!("Set login data"); let login_data_out = run_test(&mut tx, test_set_login_data, &[])?; println!(" {login_data_out:x?}"); let key_files = { let config = card.get_config(); if let Some(import) = &config.import { import.clone() } else { vec![] } }; for key_file in &key_files { // upload keys print!("Upload key '{key_file}'"); let upload_res = run_test(&mut tx, test_upload_keys, &[key_file]); if let Err(TestError::KeyUploadError(_file, err)) = &upload_res { // The card doesn't support this key type, so skip to the // next key - don't try to decrypt/sign for this key. println!(" => Upload failed ({err:?}), skip tests"); continue; } let upload_out = upload_res?; println!(" {upload_out:x?}"); let key = std::fs::read_to_string(key_file).expect("Unable to read ciphertext"); // decrypt print!(" Decrypt"); let (ssk, _) = SignedSecretKey::from_string(&key)?; let spk: SignedPublicKey = ssk.into(); let ciphertext = util::encrypt_to("Hello world!\n", &spk)?; let dec_out = run_test(&mut tx, test_decrypt, &[&key, &ciphertext])?; println!(" {dec_out:x?}"); // sign print!(" Sign"); let sign_out = run_test(&mut tx, test_sign, &[&key])?; println!(" {sign_out:x?}"); } // FIXME: import key with password println!(); } Ok(()) } openpgp-card-rpgp-0.3.0/tests/keygen.rs000064400000000000000000000052071046102023000161470ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 #![allow(dead_code)] use std::env; use anyhow::Result; use openpgp_card::state::Open; use openpgp_card::Card; use pgp::{Deserializable, SignedPublicKey}; use crate::cards::TestConfig; use crate::util::{ run_test, test_decrypt, test_keygen, test_print_algo_info, test_print_caps, test_reset, test_set_user_data, test_sign, TestResult, }; mod cards; mod util; #[ignore] #[test] fn keygen() -> Result<()> { env_logger::init(); let config = match env::var("TEST_CONFIG") { Ok(path) => TestConfig::load(&path)?, Err(_) => TestConfig::load("config/test-cards.toml")?, // fallback }; let cards = config.into_cardapps(); for card in cards { println!("** Run tests on card {} **", card.get_name()); let mut c: Card = card.get_card()?; println!(" -> Card opened"); let mut tx = c.transaction()?; println!(" started transaction"); // println!("Get pubkey"); // let _ = run_test(&mut tx, test_get_pub, &[])?; println!("Caps"); let _ = run_test(&mut tx, test_print_caps, &[])?; // continue; // only print caps println!("Reset"); let _ = run_test(&mut tx, test_reset, &[])?; println!("Algo info"); let _ = run_test(&mut tx, test_print_algo_info, &[])?; // Set user data because keygen expects a name (for the user id) println!("Set user data"); let _ = run_test(&mut tx, test_set_user_data, &[])?; let algos = { let config = card.get_config(); if let Some(keygen) = &config.keygen { keygen.clone() } else { vec![] } }; for algo in algos { println!("Generate key [{algo}]"); let res = run_test(&mut tx, test_keygen, &[&algo])?; if let TestResult::Text(cert_str) = &res[0] { // sign print!(" Sign"); let sign_out = run_test(&mut tx, test_sign, &[cert_str])?; println!(" {sign_out:x?}"); // decrypt let (spk, _) = SignedPublicKey::from_string(cert_str)?; let ciphertext = util::encrypt_to("Hello world!\n", &spk)?; print!(" Decrypt"); let dec_out = run_test(&mut tx, test_decrypt, &[cert_str, &ciphertext])?; println!(" {dec_out:x?}"); } else { panic!("Didn't get back a Cert from test_keygen"); }; } println!(); } Ok(()) } openpgp-card-rpgp-0.3.0/tests/keys/25519.tsk000064400000000000000000000020041046102023000164720ustar 00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- lFgEYPP7+RYJKwYBBAHaRw8BAQdA/jItiRmnvbyr35MBcy0b/OIVIlg64581/+ud YmUaNd4AAPwOtcItL5hHgkr+N2kRuaYVeRxZzgSERB3jwd5m91ePwQ/WtB9DdXJ2 ZSAyNTUxOSA8MjU1MTlAZXhhbXBsZS5vcmc+iJAEExYIADgWIQSU7K5+gu+dm/9F AfJksyIGTiHQPAUCYPP7+QIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRBk syIGTiHQPOrzAQCGefXelCkG70YutfxL32mGL5bNuZqUyHYu7lYWm3/UrAEAwdOJ hgTLA1PoGIrmNxXd7lS+UOpkCmrvbfHRDuRbHg+cXQRg8/v5EgorBgEEAZdVAQUB AQdAOLZLsJ2Ig5vcmNtWz2QO2A02GOWn7y+m6tbd3x08tkIDAQgHAAD/VsqcSP7D 7yh08+TDlzCnAU2mHPd/UK8IaZiVcQKDewAQ64h4BBgWCAAgFiEElOyufoLvnZv/ RQHyZLMiBk4h0DwFAmDz+/kCGwwACgkQZLMiBk4h0DyztwEA0FMbyQ7OKGAoquES hwF+XsXvSqa3FvXl9C0Me0m8uB4A/0FF8K+EkskRaklQuDU/hCQ2GaTctnYKon3F 8JOhSU0JnFgEYPP8BhYJKwYBBAHaRw8BAQdAngGsg0/jlRi3YrztezZJxhkDo5T1 pkgD0FSZb6Rv5N0AAP9bdBzvL3WSvL4Gs6PDOqEN5Nw++wOHpCL62EXYIHTdqxHk iHgEGBYIACAWIQSU7K5+gu+dm/9FAfJksyIGTiHQPAUCYPP8BgIbIAAKCRBksyIG TiHQPCBdAQCXHmhTZN0W0pucHKArum6uetWyQpLrvScQNQ6PdxGhJAD+OHMa1riQ LVYHmKRkgo9+3dUmFet9mhnci6ITV2+V1Qo= =CIG6 -----END PGP PRIVATE KEY BLOCK----- openpgp-card-rpgp-0.3.0/tests/keys/nist256.tsk000064400000000000000000000022021046102023000172170ustar 00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- lHcEYPP/JxMIKoZIzj0DAQcCAwT5a9JIM6BX1zxvFkNr2LMGLyTw72+iXsUZlA8X w3Bn91jVRpSSIITjibHKliS2e2kZlaoHOZvlXmZ3nqOANjV+AAEAzCPG24MzHigZ qyoaNr+7o6u/D8DndXHhsrERqm9cCgcOybQfTklTVCBQMjU2IDxuaXN0MjU2QGV4 YW1wbGUub3JnPoiQBBMTCAA4FiEEzM//qsd8n507stLKPJNRXagTwD8FAmDz/ycC GwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQPJNRXagTwD+bZAD/fu4NjabH GKHB1dIpqX+opDt8E3RFes58P+p4wh8W+xEBAMcPs6HLYvcLLkqtpV06wKYngPY+ Ln/wcpQOagwO+EgfnHsEYPP/JxIIKoZIzj0DAQcCAwTtyP4rOGNlU+Tzpa7UYv5h jR/T9DzMVUntaFhb3Cm0ung7IEGNAOcbgpCx/fdm7BPL+9MJB+qwpsz8bQa4DfnE AwEIBwABALvh9XLpqe1MqwPodYlWKgw4me/tR2FNKmLXPC1gl3g7EAeIeAQYEwgA IBYhBMzP/6rHfJ+dO7LSyjyTUV2oE8A/BQJg8/8nAhsMAAoJEDyTUV2oE8A/SMMA /3DuQU8hb+U9U2nX93bHwpTBQfAONsEn/vUeZ6u4NdX4AP9ABH//08SFfFttiWHm TTAR9e57Rw0DhI/wb6qqWABIyZx3BGDz/zkTCCqGSM49AwEHAgMEJz+bbG6RHQag BoULLuklPRUtQauVTxM9WZZG3PEAnIZuu4LKkHn/JPAN04iSV+K3lBWN+HALVZSV kFweNSOX6gAA/RD5JKvdwS3CofhQY+czewkb8feXGLQIaPS9rIWP7QX4En2IeAQY EwgAIBYhBMzP/6rHfJ+dO7LSyjyTUV2oE8A/BQJg8/85AhsgAAoJEDyTUV2oE8A/ CSkA/2WnUoIwtv4ZBhuCpJY/GIFqRJEPgQ7DW1bXTrYsoTehAQD1wDkG0vD6Jnfu QIPHexNllmYakW7WNqu1gobPuNEQyw== =E2Hb -----END PGP PRIVATE KEY BLOCK----- openpgp-card-rpgp-0.3.0/tests/keys/nist384.tsk000064400000000000000000000026671046102023000172400ustar 00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- lKMEYUsRJRMFK4EEACIDAwQzWJOItbKiZewIEBCm2SRXpogadtYb0Hf5bJ7+PGXP cD/3wFVM+eyT2/MULWpKEOEyMLXLTOgXdSa0ATANOsYjOjVtbKKov7Z72v6qyGb5 CQNP07AQL+adBGdGh7OyfegAAXd55/aXbt5MUMdQw/LcUJ39L/rnUIj5JR9W0wu+ nN2j5ardT/m/iNzB4Q3H6YtIuB3TtB9OSVNUIFAzODQgPG5pc3QzODRAZXhhbXBs ZS5vcmc+iLAEExMJADgWIQQqyDC7WZehBLiridh9Mm8MTF8x5gUCYUsRJQIbAwUL CQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRB9Mm8MTF8x5hEwAYDmB9v7m47Xg6I4 xX/lMCk8EoZV8p+AiPQOnrfJWSKIff7CZ5+sku+WeOEPZSkn7k4BgNqI1AVTXWx/ Ah5Sjj6z8aNRbNPMhHVgPRLokIbBm5I75d41DXw1UvTABWFgpR57oZyoBGFLESUS BSuBBAAiAwMEGUNWJ3ek1YWNzsVM5FuuDzEnteMWo3bbg+QP7T6PHTc7NbixpfDG vkKC4OGwimj3aQc+hjzuqcar72tbUSu3WX8gXhtyCt27fm76TH3lbevFLjvLXKpq SgZfuKuXDGwqAwEJCAABf3EPYC6Ml5lNd6ebX3+KRCl+dxjfvJ8uBMCJmynjYexE p/dmTZeMx/9MJBu+14UDGRcaiJgEGBMJACAWIQQqyDC7WZehBLiridh9Mm8MTF8x 5gUCYUsRJQIbDAAKCRB9Mm8MTF8x5vP9AYDCDFTnbB0KJmNIWpqmzLAZIEra8/LV DWuZGpavAa9IcrVB9wsH5ha57MTINZf1uQEBf0KMYT/cIFXBy1pHwHcmKhp9ltSe PSzaBumoTEsLij5oRj+x/X+r9md83bg3JbTehJykBGFLETYTBSuBBAAiAwMEDQEp 5vG6gA2DtBO44eKFPUS0t5sK/OFUDiSThm/tc2CZmrIYTNnuDxWrZSXZWY1fPHGl PN7OMN8OjQXe2OcL+t/y4bELXXOqfKtkvQT/xQPcrEhw0jBqvUJpK0WV2YQJAAF+ I6YVgkDrlJVwbFDChQv9dhTN1b+j8dn8jsIkh+hROgAKd+MiZOKEvBHGG9qdUDPH GS+ImAQYEwkAIBYhBCrIMLtZl6EEuKuJ2H0ybwxMXzHmBQJhSxE2AhsgAAoJEH0y bwxMXzHmFxIBgJjJGSrttcnT+DFZTQ38fsT6b4nB8QvY+cjC7BbKZHPr8bhof0Kd jYWGHS1O9InQywF7BbB11kY9qt8pNAGUEiASjGIauRZ4HUeMbHae7tvMlmQnRV/o mc3BwfJ8nyrbWGN2 =sdOZ -----END PGP PRIVATE KEY BLOCK----- openpgp-card-rpgp-0.3.0/tests/keys/nist521.tsk000064400000000000000000000034441046102023000172230ustar 00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- lNoEYPP/cRMFK4EEACMEIwQA25xX4uHHO5+6JxY1ob+O/ZA1fGJzTJ1iJ1gkTNmY /g0mmlI2s5KNo8FB7lg7yl9PAfVRTTw+nqY8g1c5O0pf4DAAISyz0c9LTr1fzW21 6yqAc/I56+EysV7NPtbAH2Oeb7cN+52TprBIg7aNDWLwRy5HENWc5BoHKHAIM17o G+Kg46IAAgkB7t68z9emoezXbSzqetUtVl8AnMliV4+UuZuYqr+iw2g7VLUZp/r/ v7zmxnjrpwgTxiOg5lSGtZKQsZr2OJSu+UQmvLQfTklTVCBQNTIxIDxuaXN0NTIx QGV4YW1wbGUub3JnPojUBBMTCgA4FiEEt82fdjceB392HIJlVVQ+bWVtHYAFAmDz /3ECGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQVVQ+bWVtHYDAIQIJARH2 LvMK6YPBsCsAqPTHNLGyeV1YdTmrtGOLpBhhiPH93syCl26hmBHIviM0KbhZ74aR 1GoqxzFZR212qkY1KqwqAgkBGYYC1ddQsQywcSt47IeYyIL6QKlgGuvIWUbLx1+/ OU7/Y44ZHNRPtEGkF4jpwYuuIM79TmgsEUhbBoptYMtsOwSc3gRg8/9xEgUrgQQA IwQjBACf+w3dmb6VmXU1KiABN3lIJkSLWpv0hFeIw2hn+zCSWykzfXkZkuYOHWov Em4m0zI5lsEvRktEUkcGt9rcshzKfADncjbODpXU5aiuBYzaWto/is2fib5m+FIE fMc5mBqtRPIlDe+9KeDxw1DQBFDeoKLxoj/A84wXXV8dLNzS3koRpAMBCgkAAgkB rP5lvQWWhcEXptkBlKV1kvVwI6oaVpoMAVgxnSAXO2Cu7IMJMfudPbPpZ9+7IUIe vHdgeuqnGCvsUoeZtJcHdT4eX4i7BBgTCgAgFiEEt82fdjceB392HIJlVVQ+bWVt HYAFAmDz/3ECGwwACgkQVVQ+bWVtHYCqQwIJAS6+KTBx3DjFFlDw9FPQKtosAjVw Cvi2wSlL0AoXwaU3izcN8R6+QwELXGn9eeZV7+hyVVJcUMAVuOHJO2q4+axlAgjN w8RBWohN7z7yCN6G+1/CXcSh4dpnGVOBzqiDQjQzrxysl1QSZ5EF2sKTd8FNbyVn MDEkalvLfOpj9GKPp8BPwZzaBGDz/4MTBSuBBAAjBCMEAMHegqGU9o2gw3WdxpFV GXNhXx3g4vNknO30DETy/NEAM8iwurWMRAh+iMCYq5ehDMijxJPt4tCW38bB5J/J YanOAHEAlIfPE2+VemNsfMlYyoKyPgu5BSqCq+HiLiw2QtC9EgglL6M7FDp281SM qAg92YA7n5j1EDk68WgUmwaU87LEAAIJAQr4mDB/nxcT7AkDbfa6Fv7jHV5aYzjB vLbDj41KVAtA/1QwQcoLOEh2NIbow6QJ8s1z1q1JuTbujnY0ij+L0coHHz+IuwQY EwoAIBYhBLfNn3Y3Hgd/dhyCZVVUPm1lbR2ABQJg8/+DAhsgAAoJEFVUPm1lbR2A laICCMI9lKpY4KNO4YSZb6zHuVF2yDJ+ej6SkWUICGQJZa88uKRZk55lT924Kc1C EGEztG/94d0FGWUH6f9E+RTZ+RgZAgkBo0UYb9t8AwzG/DWjfsiMI+tmbofR1Td5 vBbYWdPziLF9mmv0dFPLEGQAz4QLzkx2CWGr+mh60uixK0C2B44dD9M= =u4m/ -----END PGP PRIVATE KEY BLOCK----- openpgp-card-rpgp-0.3.0/tests/keys/rsa1k.tsk000064400000000000000000000054201046102023000170330ustar 00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- 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 4homjOXsQgeVG9G2uA== =P7LB -----END PGP PRIVATE KEY BLOCK----- openpgp-card-rpgp-0.3.0/tests/keys/rsa2k.tsk000064400000000000000000000120701046102023000170330ustar 00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- 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 7aYYBzgV/of68E1HMFmY7A== =JojA -----END PGP PRIVATE KEY BLOCK----- openpgp-card-rpgp-0.3.0/tests/keys/rsa3k.tsk000064400000000000000000000165341046102023000170450ustar 00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- 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 TcjdEFcP06EHQOmknwOuZw== =X1NH -----END PGP PRIVATE KEY BLOCK----- openpgp-card-rpgp-0.3.0/tests/keys/rsa4k.tsk000064400000000000000000000232001046102023000170320ustar 00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- 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 zOXoqaL65L6jU3nFiWK3Xg== =Fa1S -----END PGP PRIVATE KEY BLOCK----- openpgp-card-rpgp-0.3.0/tests/loop.sh000075500000000000000000000007241046102023000156260ustar 00000000000000#!/bin/bash # SPDX-FileCopyrightText: Heiko Schaefer # SPDX-License-Identifier: CC0-1.0 # Helper script to manually run an endless loop of one test (currently hardcoded to "import"), # and stop on the first error: # # $ tests/loop.sh tests/ci/virt-opcard-rs.toml TEST=import COUNTER=0 while [ true ]; do TEST_CONFIG=$1 cargo test --release $TEST -- --ignored --nocapture || exit 1 let COUNTER++; echo "=== loop counter: $COUNTER ===" doneopenpgp-card-rpgp-0.3.0/tests/other.rs000064400000000000000000000044071046102023000160070ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 #![allow(dead_code)] use anyhow::Result; use openpgp_card::state::Open; use openpgp_card::Card; use crate::cards::TestConfig; use crate::util::{run_test, test_reset}; mod cards; mod util; #[ignore] #[test] fn other() -> Result<()> { env_logger::init(); let config = TestConfig::load("config/test-cards.toml")?; let cards = config.into_cardapps(); for card in cards { println!("** Run tests on card '{}' **", card.get_name()); let mut c: Card = card.get_card()?; let mut tx = c.transaction()?; // println!("Caps"); // let _ = run_test(&mut card, test_print_caps, &[])?; // continue; // only print caps // println!("Algo info"); // let _ = run_test(&mut card, test_print_algo_info, &[])?; println!("Reset"); let _ = run_test(&mut tx, test_reset, &[])?; // --- // // load private key (change pw on gnuk needs existing keys!) // println!("load key"); // run_test(&mut card, test_upload_keys, &["data/rsa2k.sec"])?; // println!("Change PW"); // let _ = run_test(&mut card, test_change_pw, &[])?; // println!("reset pw1 retry counter"); // let _ = run_test(&mut card, test_reset_retry_counter, &[])?; // --- // println!("Generate key"); // let _ = run_test(&mut card, test_keygen, &[])?; // // panic!(); // println!("Get pubkey"); // let _ = run_test(&mut card, test_get_pub, &[])?; // // panic!(); // --- // print!("Verify"); // let verify_out = run_test(&mut card, test_verify, &[])?; // println!(" {:x?}", verify_out); // print!("PW Status bytes"); // let pw_out = run_test(&mut card, test_pw_status, &[])?; // println!(" {:x?}", pw_out); // print!("Private data"); // let priv_out = run_test(&mut card, test_private_data, &[])?; // println!(" {:x?}", priv_out); // print!("Cardholder Cert"); // let cardh_out = run_test(&mut card, test_cardholder_cert, &[])?; // println!(" {:x?}", cardh_out); // println!(); } Ok(()) } openpgp-card-rpgp-0.3.0/tests/util.rs000064400000000000000000000650451046102023000156500ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 use std::convert::TryFrom; use std::string::FromUtf8Error; use anyhow::{anyhow, Result}; use openpgp_card::ocard::algorithm::AlgoSimple; use openpgp_card::ocard::crypto::CardUploadableKey; use openpgp_card::ocard::data::{KeyGenerationTime, Sex}; use openpgp_card::ocard::StatusBytes; use openpgp_card::state::{Admin, Open, Transaction}; use openpgp_card::{ocard::KeyType, Card, Error}; use openpgp_card_rpgp::{ bind_into_certificate, public_key_material_and_fp_to_key, public_key_material_to_key, CardSlot, UploadableKey, }; use pgp::crypto::sym::SymmetricKeyAlgorithm; use pgp::packet::{LiteralData, PublicSubkey}; use pgp::ser::Serialize; use pgp::types::SecretKeyTrait; use pgp::{ ArmorOptions, Deserializable, Message, PublicOrSecret, SignedPublicKey, SignedSecretKey, StandaloneSignature, }; use rand::thread_rng; #[derive(Debug)] pub enum TestResult { Status(StatusBytes), StatusOk, Text(String), } type TestOutput = Vec; #[derive(thiserror::Error, Debug)] pub enum TestError { #[error("Failed to upload key {0} ({1})")] KeyUploadError(String, anyhow::Error), #[error(transparent)] Opgp(#[from] Error), #[error(transparent)] OCard(#[from] StatusBytes), #[error(transparent)] Other(#[from] anyhow::Error), // source and Display delegate to anyhow::Error #[error(transparent)] Utf8Error(#[from] FromUtf8Error), } impl From for TestError { fn from(value: openpgp_card_rpgp::Error) -> Self { TestError::Other(value.into()) } } /// Run after each "upload keys", if key *was* uploaded (?) pub fn test_decrypt(tx: &mut Card, param: &[&str]) -> Result { assert_eq!( param.len(), 2, "test_decrypt needs filenames for 'cert' and 'encrypted'" ); let msg = param[1].to_string(); let _user = tx.to_user_card(Some("123456".to_string().into()))?; let (msg, _) = pgp::Message::from_armor_single(msg.as_bytes()).expect("parse message"); let cs = CardSlot::init_from_card(tx, KeyType::Decryption, &|| { eprintln!("touch confirmation required") }) .expect("FIXME"); let res = cs.decrypt_message(&msg).expect("FIXME"); if let Message::Literal(lit) = res { let plain = String::from_utf8_lossy(lit.data()); assert_eq!(plain, "Hello world!\n"); Ok(vec![]) } else { panic!("FIXME: got unexpected message") } } /// Run after each "upload keys", if key *was* uploaded (?) pub fn test_sign(tx: &mut Card, param: &[&str]) -> Result { assert_eq!(param.len(), 1, "test_sign needs a filename for 'cert'"); let _sign = tx .to_signing_card(Some("123456".to_string().into())) .unwrap(); let cleartext = "Hello world, I am signed."; let msg = Message::Literal(LiteralData::from_bytes((&[]).into(), cleartext.as_bytes())); let cs = CardSlot::init_from_card(tx, KeyType::Signing, &|| { eprintln!("touch confirmation required") }) .expect("FIXME"); let signed = msg .sign(thread_rng(), &cs, String::default, cs.hash_alg()) .expect("signing on card"); let sig = match signed { Message::Signed { signature, .. } => signature, _ => panic!(), }; let sig = StandaloneSignature::new(sig); // validate sig let (mut parsed, _) = pgp::composed::signed_key::from_armor_many(param[0].as_bytes()).expect("parse key data"); let spk = match parsed.next().expect("no key found") { Ok(PublicOrSecret::Public(p)) => p, Ok(PublicOrSecret::Secret(s)) => SignedPublicKey::from(s), Err(e) => panic!("parse key data {}", e), }; assert!(verify_sig( &spk, cleartext.as_bytes(), &sig.to_bytes().expect("serialize signature") )?); Ok(vec![]) } fn check_key_upload_metadata( admin: &mut Card, meta: &[(String, KeyGenerationTime)], ) -> Result<()> { admin.as_transaction().invalidate_cache()?; // check fingerprints let card_fp = admin.as_transaction().fingerprints()?; let sig = card_fp.signature().expect("signature fingerprint"); assert_eq!(format!("{sig:X}"), meta[0].0.to_ascii_uppercase()); let dec = card_fp.decryption().expect("decryption fingerprint"); assert_eq!(format!("{dec:X}"), meta[1].0.to_ascii_uppercase()); let auth = card_fp .authentication() .expect("authentication fingerprint"); assert_eq!(format!("{auth:X}"), meta[2].0.to_ascii_uppercase()); // get_key_generation_times let card_kg = admin.as_transaction().key_generation_times()?; let sig = card_kg.signature().expect("signature creation time"); assert_eq!(sig, &meta[0].1); let dec = card_kg.decryption().expect("decryption creation time"); assert_eq!(dec, &meta[1].1); let auth = card_kg .authentication() .expect("authentication creation time"); assert_eq!(auth, &meta[2].1); Ok(()) } fn check_key_upload_algo_attrs() -> Result<()> { // get_algorithm_attributes // FIXME Ok(()) } pub fn test_print_caps( tx: &mut Card, _param: &[&str], ) -> Result { let ard = tx.card().application_related_data()?; let aid = ard.application_id()?; println!("aid: {aid:#x?}"); let hist = ard.historical_bytes()?; println!("hist: {hist:#?}"); let ecap = ard.extended_capabilities()?; println!("ecap: {ecap:#?}"); let eli = ard.extended_length_information()?; println!("eli: {eli:#?}"); Ok(vec![]) } pub fn test_print_algo_info( tx: &mut Card, _param: &[&str], ) -> Result { let ard = tx.card().application_related_data()?; let sig = ard.algorithm_attributes(KeyType::Signing)?; println!("Current algorithm for the signing slot: {sig}"); let dec = ard.algorithm_attributes(KeyType::Decryption)?; println!("Current algorithm for the decrypt slot: {dec}"); let aut = ard.algorithm_attributes(KeyType::Authentication)?; println!("Current algorithm for the authentication slot: {aut}"); println!(); let algo = tx.card().algorithm_information(); if let Ok(Some(algo)) = algo { println!("Card algorithm list:\n{algo}"); } Ok(vec![]) } pub fn test_upload_keys( tx: &mut Card, param: &[&str], ) -> Result { assert_eq!( param.len(), 1, "test_upload_keys needs a filename for 'cert'" ); let key = std::fs::read_to_string(param[0]) .unwrap_or_else(|_| panic!("load key from {:?}", param[0])); let (ssk, _) = SignedSecretKey::from_string(&key).expect("parse tsk"); let mut admin = tx.to_admin_card(Some("12345678".to_string().into()))?; let meta = upload_subkeys(&mut admin, &ssk) .map_err(|e| TestError::KeyUploadError(param[0].to_string(), e))?; check_key_upload_metadata(&mut admin, &meta)?; // FIXME: implement check_key_upload_algo_attrs()?; Ok(vec![]) } /// Generate keys for each of the three KeyTypes pub fn test_keygen(tx: &mut Card, param: &[&str]) -> Result { let mut admin = tx .to_admin_card(Some("12345678".to_string().into())) .expect("Couldn't get Admin card"); // Generate all three subkeys on card let algo = param[0]; let alg = AlgoSimple::try_from(algo)?; println!(" Generate subkey for Signing"); admin.set_algorithm(KeyType::Signing, alg)?; let (pkm, ts) = admin.generate_key(openpgp_card_rpgp::public_to_fingerprint, KeyType::Signing)?; let key_sig = public_key_material_to_key(&pkm, KeyType::Signing, &ts, None, None).expect("FIXME"); println!(" Generate subkey for Decryption"); admin.set_algorithm(KeyType::Decryption, alg)?; let (pkm, ts) = admin.generate_key( openpgp_card_rpgp::public_to_fingerprint, KeyType::Decryption, )?; let key_dec = public_key_material_to_key(&pkm, KeyType::Decryption, &ts, None, None).expect("FIXME"); println!(" Generate subkey for Authentication"); admin.set_algorithm(KeyType::Authentication, alg)?; let (pkm, ts) = admin.generate_key( openpgp_card_rpgp::public_to_fingerprint, KeyType::Authentication, )?; let key_aut = public_key_material_to_key(&pkm, KeyType::Authentication, &ts, None, None).expect("FIXME"); tx.invalidate_cache()?; // Generate a Cert for this set of generated keys let cert = bind_into_certificate( tx, key_sig, Some(key_dec), Some(key_aut), &["cardtest@example.org".to_string()], Some("123456".to_string().into()), &|| {}, &|| eprintln!("touch confirmation needed"), )?; let armored = cert .to_armored_string(ArmorOptions::default()) .expect("FIXME"); let res = TestResult::Text(armored); Ok(vec![res]) } /// Construct public key based on data from the card pub fn test_get_pub( transaction: &mut Card, _param: &[&str], ) -> Result { let times = transaction.key_generation_times()?; let fps = transaction.fingerprints()?; // -- let sig = transaction.public_key_material(KeyType::Signing)?; let ts = times.signature().unwrap().get().into(); let key = public_key_material_and_fp_to_key( &sig, KeyType::Signing, &ts, fps.signature().expect("FIXME"), ) .expect("FIXME"); println!(" sig key data from card -> {key:x?}"); // -- let dec = transaction.public_key_material(KeyType::Decryption)?; let ts = times.decryption().unwrap().get().into(); let key = public_key_material_and_fp_to_key( &dec, KeyType::Decryption, &ts, fps.decryption().expect("FIXME"), ) .expect("FIXME"); println!(" dec key data from card -> {key:x?}"); // -- let auth = transaction.public_key_material(KeyType::Authentication)?; let ts = times.authentication().unwrap().get().into(); let key = public_key_material_and_fp_to_key( &auth, KeyType::Authentication, &ts, fps.authentication().expect("FIXME"), ) .expect("FIXME"); println!(" auth key data from card -> {key:x?}"); Ok(vec![]) } pub fn test_reset(tx: &mut Card, _param: &[&str]) -> Result { tx.factory_reset()?; Ok(vec![]) } /// Sets name, lang, sex, url; then reads the fields from the card and /// compares the values with the expected values. /// /// Returns an empty TestOutput, throws errors for unexpected Status codes /// and for unequal field values. pub fn test_set_user_data( tx: &mut Card, _param: &[&str], ) -> Result { let mut admin = tx.to_admin_card(Some("12345678".to_string().into()))?; // name admin.set_cardholder_name("Bar<, _params: &[&str], ) -> std::result::Result { let mut admin = tx.to_admin_card(Some("12345678".to_string().into()))?; let test_login = "someone@somewhere.com"; admin.set_login_data(test_login.as_bytes())?; // Read the previously set login data let read_login_data = tx.login_data()?; assert_eq!(&read_login_data, test_login.as_bytes()); Ok(vec![]) } // pub fn test_private_data(mut card: Card, _param: &[&str]) -> Result { // let mut transaction = card.transaction()?; // // let out = vec![]; // // println!(); // // let d = transaction.private_use_do(1)?; // println!("data 1 {d:?}"); // // transaction.verify_pw1_user("123456")?; // // transaction.set_private_use_do(1, "Foo bar1!".as_bytes().to_vec())?; // transaction.set_private_use_do(3, "Foo bar3!".as_bytes().to_vec())?; // // transaction.verify_pw3("12345678")?; // // transaction.set_private_use_do(2, "Foo bar2!".as_bytes().to_vec())?; // transaction.set_private_use_do(4, "Foo bar4!".as_bytes().to_vec())?; // // let d = transaction.private_use_do(1)?; // println!("data 1 {d:?}"); // let d = transaction.private_use_do(2)?; // println!("data 2 {d:?}"); // let d = transaction.private_use_do(3)?; // println!("data 3 {d:?}"); // let d = transaction.private_use_do(4)?; // println!("data 4 {d:?}"); // // Ok(out) // } // pub fn test_cardholder_cert( // card_tx: &mut CardApp, // _param: &[&str], // ) -> Result { // let mut out = vec![]; // // println!(); // // match card_tx.cardholder_certificate() { // Ok(res) => { // out.push(TestResult::Text(format!("got cert {:x?}", res.data()))) // } // Err(e) => { // out.push(TestResult::Text(format!( // "get_cardholder_certificate failed: {:?}", // e // ))); // return Ok(out); // } // }; // // card_tx.verify_pw3("12345678")?; // // let data = "Foo bar baz!".as_bytes(); // // match card_tx.set_cardholder_certificate(data.to_vec()) { // Ok(_resp) => out.push(TestResult::Text("set cert ok".to_string())), // Err(e) => { // out.push(TestResult::Text(format!( // "set_cardholder_certificate: {:?}", // e // ))); // return Ok(out); // } // } // // let res = card_tx.cardholder_certificate()?; // out.push(TestResult::Text("get cert ok".to_string())); // // if res.data() != data { // out.push(TestResult::Text(format!( // "get after set doesn't match original data: {:x?}", // data // ))); // return Ok(out); // }; // // // try using slot 2 // // match card_tx.select_data(2, &[0x7F, 0x21]) { // Ok(_res) => out.push(TestResult::Text("select_data ok".to_string())), // Err(e) => { // out.push(TestResult::Text(format!("select_data: {:?}", e))); // return Ok(out); // } // } // // Ok(out) // } pub fn test_pw_status(mut card: Card, _param: &[&str]) -> Result { let mut transaction = card.transaction()?; let out = vec![]; let mut pws = transaction.pw_status_bytes()?; println!("pws {pws:?}"); let mut admin = transaction.to_admin_card(Some("12345678".to_string().into()))?; pws.set_pw1_cds_valid_once(false); pws.set_pw1_pin_block(true); admin.set_pw_status_bytes(&pws, false)?; transaction.invalidate_cache()?; let pws = transaction.pw_status_bytes()?; println!("pws {pws:?}"); Ok(out) } /// Outputs: /// - verify pw3 (check) -> Status /// - verify pw1 (check) -> Status pub fn test_verify(mut card: Card, _param: &[&str]) -> Result { let mut transaction = card.transaction()?; // Steps: // // - try to set name without verify, assert result is not ok // - verify pw3 + pin -> Status // - verify pw3 (check) -> Status // - set name -> Status // - get name -> Text(name) // - verify pw1 + pin -> Status // - verify pw1 (check) -> Status // - set name -> Status // - get name -> Text(name) let mut out = vec![]; // try to set name without verify, assert result is not ok! let mut admin = transaction.to_admin_card(None)?; let res = admin.set_cardholder_name("Notverified< { // e.g. yubikey5 returns an error status! out.push(TestResult::Status(s)); } Err(_) => { panic!("unexpected error"); } Ok(_) => out.push(TestResult::StatusOk), } let mut admin = transaction.to_admin_card(None)?; admin.set_cardholder_name("Admin< { // e.g. yubikey5 returns an error status! out.push(TestResult::Status(s)); } Err(_) => { panic!("unexpected error"); } Ok(_) => out.push(TestResult::StatusOk), } let mut admin = transaction.to_admin_card(None)?; admin.set_cardholder_name("There<, _param: &[&str]) -> Result { let mut transaction = card.transaction()?; let out = vec![]; // first do admin-less pw1 on gnuk // (NOTE: Gnuk requires a key to be loaded before allowing pw changes!) println!("change pw1"); transaction.change_user_pin("123456".to_string().into(), "abcdef00".to_string().into())?; // also set admin pw, which means pw1 is now only user-pw again, on gnuk println!("change pw3"); // ca.change_pw3("abcdef00", "abcdefgh")?; // gnuk transaction.change_admin_pin("12345678".to_string().into(), "abcdefgh".to_string().into())?; println!("change pw1"); transaction.change_user_pin("abcdef00".to_string().into(), "abcdef".to_string().into())?; // gnuk // ca.change_pw1("123456", "abcdef")?; println!("verify bad pw1"); match transaction.verify_user_pin("123456ab".to_string().into()) { Err(Error::CardStatus(StatusBytes::SecurityStatusNotSatisfied)) => { // this is expected } Err(_) => { panic!("unexpected error"); } Ok(_) => panic!("this value for pw1 should be considered wrong!"), } println!("verify good pw1"); transaction.verify_user_pin("abcdef".to_string().into())?; println!("verify bad pw3"); match transaction.verify_admin_pin("00000000".to_string().into()) { Err(Error::CardStatus(StatusBytes::SecurityStatusNotSatisfied)) => { // this is expected } Err(_) => { panic!("unexpected error"); } Ok(_) => panic!("this value for pw3 should be considered wrong!"), } println!("verify good pw3"); transaction.verify_admin_pin("abcdefgh".to_string().into())?; println!("change pw3 back to default"); transaction.change_admin_pin("abcdefgh".to_string().into(), "12345678".to_string().into())?; println!("change pw1 back to default"); transaction.change_user_pin("abcdef".to_string().into(), "123456".to_string().into())?; Ok(out) } pub fn test_reset_retry_counter( mut card: Card, _param: &[&str], ) -> Result { let mut transaction = card.transaction()?; let out = vec![]; // set pw3, then pw1 (to bring gnuk into non-admin mode) println!("set pw3"); transaction.change_admin_pin("12345678".to_string().into(), "12345678".to_string().into())?; println!("set pw1"); transaction.change_user_pin("123456".to_string().into(), "123456".to_string().into())?; println!("break pw1"); let _ = transaction.verify_user_pin("wrong0".to_string().into()); let _ = transaction.verify_user_pin("wrong0".to_string().into()); let _ = transaction.verify_user_pin("wrong0".to_string().into()); let res = transaction.verify_user_pin("wrong0".to_string().into()); match res { Err(Error::CardStatus(StatusBytes::AuthenticationMethodBlocked)) => { // this is expected } Err(Error::CardStatus(StatusBytes::IncorrectParametersCommandDataField)) => { println!( "yk says IncorrectParametersCommandDataField when PW \ error count is exceeded" ); } Err(e) => { panic!("unexpected error {:?}", e); } Ok(_) => panic!("use of pw1 should be blocked!"), } println!("verify pw3"); transaction.verify_admin_pin("12345678".to_string().into())?; println!("set resetting code"); let mut admin = transaction.to_admin_card(None)?; admin.set_resetting_code("abcdefgh".to_string().into())?; println!("reset retry counter"); // ca.reset_retry_counter_pw1("abcdef".as_bytes().to_vec(), None)?; let _res = transaction.reset_user_pin("abcdef".to_string().into(), "abcdefgh".to_string().into()); println!("verify good pw1"); transaction.verify_user_pin("abcdef".to_string().into())?; println!("verify bad pw1"); match transaction.verify_user_pin("00000000".to_string().into()) { Err(Error::CardStatus(StatusBytes::SecurityStatusNotSatisfied)) => { // this is expected } Err(_) => { panic!("unexpected error"); } Ok(_) => panic!("this value for pw1 should be considered wrong!"), } Ok(out) } pub fn run_test( card: &mut Card, t: fn(&mut Card, &[&str]) -> Result, param: &[&str], ) -> Result { t(card, param) } pub(crate) fn upload_subkeys( admin: &mut Card, ssk: &SignedSecretKey, ) -> Result> { let mut out = vec![]; for kt in &[ KeyType::Signing, KeyType::Decryption, KeyType::Authentication, ] { if let Some(uk) = subkey_by_type(ssk, *kt) { // store fingerprint as return-value let fp = hex::encode(uk.fingerprint()?.as_bytes()); // store key creation time as return-value let creation = uk.timestamp().get(); out.push((fp, creation.into())); admin.import_key(Box::new(uk), *kt)?; } } Ok(out) } fn subkey_by_type(ssk: &SignedSecretKey, kt: KeyType) -> Option { match kt { KeyType::Signing => signing_sec_key(ssk).ok(), KeyType::Decryption => decryption_sec_key(ssk).ok(), KeyType::Authentication => auth_sec_key(ssk).ok(), KeyType::Attestation => None, } } fn signing_sec_key(ssk: &SignedSecretKey) -> Result { for sk in &ssk.secret_subkeys { let binding = sk .signatures .first() // FIXME: we really want the newest, but for these tests we expect simple keys .expect("expecting a binding signature"); if binding.key_flags().sign() { return Ok(sk.key.clone().into()); } } // look at primary (NOTE: we're not handling direct signatures here!) let binding = ssk .details .users .first() // FIXME: we really want the primary user, but for these tests we expect simple keys .expect("expecting a user") .signatures .first() // FIXME: we really want the newest, but for these tests we expect simple keys .expect("expecting a binding signature"); if binding.key_flags().sign() { return Ok(ssk.primary_key.clone().into()); } Err(anyhow!("no key found")) } fn auth_sec_key(ssk: &SignedSecretKey) -> Result { for sk in &ssk.secret_subkeys { let binding = sk .signatures .first() // FIXME: we really want the newest, but for these tests we expect simple keys .expect("expecting a binding signature"); if binding.key_flags().authentication() { return Ok(sk.key.clone().into()); } } Err(anyhow!("no key found")) } fn decryption_sec_key(ssk: &SignedSecretKey) -> Result { for sk in &ssk.secret_subkeys { let binding = sk .signatures .first() // FIXME: we really want the newest, but for these tests we expect simple keys .expect("expecting a binding signature"); let flags = binding.key_flags(); if flags.encrypt_comms() || flags.encrypt_storage() { return Ok(sk.key.clone().into()); } } Err(anyhow!("no key found")) } /// NOTE: this function ignores key flags and all validity entirely. /// It returns `true` if any component key is found that cryptographically validates the signature. /// This is not ok for production OpenPGP, but sufficient for these tests. pub fn verify_sig(spk: &SignedPublicKey, data: &[u8], sig: &[u8]) -> Result { let sig = StandaloneSignature::from_bytes(sig).expect("signature"); if sig.verify(&spk.primary_key, data).is_ok() { return Ok(true); } for sk in &spk.public_subkeys { if sig.verify(&sk.key, data).is_ok() { return Ok(true); } } Ok(false) } pub fn encrypt_to(plaintext: &str, spk: &SignedPublicKey) -> Result { let lit = LiteralData::from_bytes((&[]).into(), plaintext.as_bytes()); let msg = Message::Literal(lit); let keys = encryption_capable(spk)?; let enc = msg.encrypt_to_keys_seipdv1(&mut thread_rng(), SymmetricKeyAlgorithm::AES256, &keys)?; Ok(enc.to_armored_string(ArmorOptions::default())?) } /// NOTE: we don't support encryption capable primaries here. /// This is a little unfortunate, but for the tests we don't care about that corner case. fn encryption_capable(spk: &SignedPublicKey) -> Result> { let mut v = vec![]; for sk in &spk.public_subkeys { let binding = sk .signatures .first() // FIXME: we really want the newest, but for these tests we expect simple keys .expect("expecting a binding signature"); let flags = binding.key_flags(); if flags.encrypt_comms() || flags.encrypt_storage() { v.push(&sk.key); } } Ok(v) }