sequoia-policy-config-0.8.0/.cargo_vcs_info.json0000644000000001360000000000100152470ustar { "git": { "sha1": "21d0426f7e8420d1b95adaf1cf1c4362cd473b30" }, "path_in_vcs": "" }sequoia-policy-config-0.8.0/.ci/all_commits.sh000075500000000000000000000022711046102023000173540ustar 00000000000000#!/usr/bin/env bash # Test all commits on this branch but the last one. # # Used in the all_commits ci job to ensure all commits build # and tests pass at least for the sequoia-openpgp crate. # NOTE: under gitlab's Settings, "CI/CD", General Pipelines ensure # that the "git shallow clone" setting is set to 0. Otherwise other # branch are not fetched. set -e set -x # Use dummy identity to make git rebase happy. git config user.name "C.I. McTestface" git config user.email "ci.mctestface@example.com" # Make sure the gitlab project is configured. if ! git describe --all origin/main then echo "origin/main is not present. Configure the gitlab project (see .ci/all_commits.sh)." exit 1 fi # If the previous commit already is on main we're done. git merge-base --is-ancestor HEAD~ origin/main && echo "All commits tested already" && exit 0 # Leave out the last commit - it has already been checked. git checkout HEAD~ git status git rebase origin/main \ --exec 'echo ===; echo ===; echo ===; git log -n 1;' \ --exec 'cargo test --all' && echo "All commits passed tests" && exit 0 # The rebase failed - probably because a test failed. git rebase --abort; exit 1 sequoia-policy-config-0.8.0/.codespellrc000064400000000000000000000004031046102023000163340ustar 00000000000000[codespell] skip = *.bin,*.gpg,*.pgp,./.git,data,highlight.js,*/target,Makefile,*.html,*/cargo,*.xml,*.xmlv2, ignore-words-list = crate,ede,iff,mut,nd,te,uint,KeyServer,keyserver,Keyserver,keyservers,Keyservers,keypair,keypairs,KeyPair,fpr,dedup,ba,captable, sequoia-policy-config-0.8.0/.gitignore000064400000000000000000000000101046102023000160160ustar 00000000000000/target sequoia-policy-config-0.8.0/.gitlab-ci.yml000064400000000000000000000001731046102023000164740ustar 00000000000000stages: - pre-check - build - test include: - component: "gitlab.com/sequoia-pgp/common-ci/sequoia-pipeline@main" sequoia-policy-config-0.8.0/Cargo.lock0000644000001402310000000000100132230ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aead" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", "generic-array", ] [[package]] name = "aes" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", "cpufeatures", ] [[package]] name = "aes-gcm" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", "cipher", "ctr", "ghash", "subtle", ] [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "android-tzdata" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "anstyle" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anyhow" version = "1.0.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", ] [[package]] name = "ascii-canvas" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" dependencies = [ "term", ] [[package]] name = "assert_cmd" version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" dependencies = [ "anstyle", "bstr", "doc-comment", "libc", "predicates", "predicates-core", "predicates-tree", "wait-timeout", ] [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bindgen" version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ "bitflags", "cexpr", "clang-sys", "itertools 0.13.0", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", "syn", ] [[package]] name = "bit-set" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" version = "2.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", ] [[package]] name = "block-padding" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ "generic-array", ] [[package]] name = "bstr" version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", "regex-automata", "serde", ] [[package]] name = "buffered-reader" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db26bf1f092fd5e05b5ab3be2f290915aeb6f3f20c4e9f86ce0f07f336c2412f" dependencies = [ "libc", ] [[package]] name = "bumpalo" version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" version = "1.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" 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 = "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", "zeroize", ] [[package]] name = "clang-sys" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", "libloading", ] [[package]] name = "cmac" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8543454e3c3f5126effff9cd44d562af4e31fb8ce1cc0d3dcd8f084515dbc1aa" dependencies = [ "cipher", "dbl", "digest", ] [[package]] name = "const-oid" version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] [[package]] name = "crunchy" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "rand_core", "typenum", ] [[package]] name = "ctr" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ "cipher", ] [[package]] name = "curve25519-dalek" version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest", "fiat-crypto", "rustc_version", "subtle", "zeroize", ] [[package]] name = "curve25519-dalek-derive" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "dbl" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd2735a791158376708f9347fe8faba9667589d82427ef3aed6794a8981de3d9" dependencies = [ "generic-array", ] [[package]] name = "der" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "zeroize", ] [[package]] name = "difflib" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "const-oid", "crypto-common", "subtle", ] [[package]] name = "dirs-next" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ "cfg-if", "dirs-sys-next", ] [[package]] name = "dirs-sys-next" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", "redox_users", "winapi", ] [[package]] name = "displaydoc" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "doc-comment" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "dyn-clone" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "feeef44e73baff3a26d371801df019877a9866a8c493d315ab00177843314f35" [[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 = "ed25519" version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8", "signature", ] [[package]] name = "ed25519-dalek" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", "rand_core", "serde", "sha2", "subtle", "zeroize", ] [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "ena" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" dependencies = [ "log", ] [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", "windows-sys", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "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 = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", ] [[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 = "glob" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "hkdf" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ "hmac", ] [[package]] name = "hmac" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ "digest", ] [[package]] name = "iana-time-zone" version = "0.1.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", ] [[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 = [ "block-padding", "generic-array", ] [[package]] name = "itertools" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ "either", ] [[package]] name = "itertools" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] [[package]] name = "js-sys" version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "lalrpop" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" dependencies = [ "ascii-canvas", "bit-set", "ena", "itertools 0.11.0", "lalrpop-util", "petgraph", "regex", "regex-syntax", "string_cache", "term", "tiny-keccak", "unicode-xid", "walkdir", ] [[package]] name = "lalrpop-util" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ "regex-automata", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ "spin", ] [[package]] name = "libc" version = "0.2.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 = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memsec" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c797b9d6bb23aab2fc369c65f871be49214f5c759af65bde26ffaaa2b646b492" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nettle" version = "7.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44e6ff4a94e5d34a1fd5abbd39418074646e2fa51b257198701330f22fcd6936" dependencies = [ "getrandom 0.2.15", "libc", "nettle-sys", "thiserror", "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", "smallvec", ] [[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", ] [[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.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "opaque-debug" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[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 = "petgraph" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", "indexmap", ] [[package]] name = "phf_shared" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ "siphasher", ] [[package]] name = "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 = "precomputed-hash" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "predicates" version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" dependencies = [ "anstyle", "difflib", "predicates-core", ] [[package]] name = "predicates-core" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" [[package]] name = "predicates-tree" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" dependencies = [ "predicates-core", "termtree", ] [[package]] name = "proc-macro2" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quickcheck" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" dependencies = [ "rand", ] [[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 = [ "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", ] [[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 = "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", ] [[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 = "semver" version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "sequoia-openpgp" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "015e5fc3d023418b9db98ca9a7f3e90b305872eeafe5ca45c5c32b5eb335c1e8" dependencies = [ "aes-gcm", "anyhow", "argon2", "base64", "buffered-reader", "chrono", "cipher", "dyn-clone", "eax", "ed25519", "ed25519-dalek", "getrandom 0.2.15", "hkdf", "idna", "lalrpop", "lalrpop-util", "libc", "memsec", "nettle", "num-bigint-dig", "ocb3", "rand_core", "regex", "regex-syntax", "sha1collisiondetection", "sha2", "thiserror", "win-crypto-ng", "winapi", "xxhash-rust", ] [[package]] name = "sequoia-policy-config" version = "0.8.0" dependencies = [ "anyhow", "assert_cmd", "chrono", "quickcheck", "sequoia-openpgp", "serde", "thiserror", "toml", ] [[package]] name = "serde" version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "sha1collisiondetection" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f606421e4a6012877e893c399822a4ed4b089164c5969424e1b9d1e66e6964b" dependencies = [ "digest", "generic-array", ] [[package]] name = "sha2" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "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 = [ "rand_core", ] [[package]] name = "siphasher" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "smallvec" version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "spki" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", "der", ] [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "string_cache" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "938d512196766101d333398efde81bc1f37b00cb42c2f8350e5df639f040bbbe" dependencies = [ "new_debug_unreachable", "parking_lot", "phf_shared", "precomputed-hash", ] [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "synstructure" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tempfile" version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a40f762a77d2afa88c2d919489e390a12bdd261ed568e60cfa7e48d4e20f0d33" dependencies = [ "cfg-if", "fastrand", "getrandom 0.3.1", "once_cell", "rustix", "windows-sys", ] [[package]] name = "term" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" dependencies = [ "dirs-next", "rustversion", "winapi", ] [[package]] name = "termtree" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tiny-keccak" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" dependencies = [ "crunchy", ] [[package]] name = "tinystr" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", ] [[package]] name = "toml" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] [[package]] name = "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 = "utf16_iter" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wait-timeout" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" dependencies = [ "libc", ] [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" version = "0.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", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ "unicode-ident", ] [[package]] name = "win-crypto-ng" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99abfb435a71e54ab2971d8d8c32f1a7e006cdbf527f71743b1d45b93517bb92" dependencies = [ "cipher", "doc-comment", "rand_core", "winapi", "zeroize", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 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 = "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 = "xxhash-rust" version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" [[package]] name = "yoke" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", "yoke-derive", "zerofrom", ] [[package]] name = "yoke-derive" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "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", "synstructure", ] [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[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", ] sequoia-policy-config-0.8.0/Cargo.toml0000644000000035430000000000100132520ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.70" name = "sequoia-policy-config" version = "0.8.0" authors = ["Neal H. Walfield "] build = "build.rs" description = "Configure Sequoia using a configuration file." homepage = "https://sequoia-pgp.org/" readme = "README.md" keywords = [ "cryptography", "openpgp", ] license = "LGPL-2.0-or-later" repository = "https://gitlab.com/sequoia-pgp/sequoia-policy-config" [package.metadata.docs.rs] features = ["sequoia-openpgp/default"] [lib] name = "sequoia_policy_config" path = "src/lib.rs" [[bin]] name = "sequoia-policy-config-check" path = "src/check.rs" [dependencies.anyhow] version = "1.0.18" [dependencies.chrono] version = "0.4" [dependencies.sequoia-openpgp] version = "2" default-features = false [dependencies.serde] version = "1.0" [dependencies.thiserror] version = "1.0.2" [dependencies.toml] version = "0.5" [dev-dependencies.assert_cmd] version = "2" [dev-dependencies.quickcheck] version = "1" default-features = false [target."cfg(not(windows))".dev-dependencies.sequoia-openpgp] version = "2" features = [ "crypto-nettle", "__implicit-crypto-backend-for-tests", ] default-features = false [target."cfg(windows)".dev-dependencies.sequoia-openpgp] version = "2" features = [ "crypto-cng", "__implicit-crypto-backend-for-tests", ] default-features = false [badges.maintenance] status = "actively-developed" sequoia-policy-config-0.8.0/Cargo.toml.orig000064400000000000000000000026141046102023000167310ustar 00000000000000[package] name = "sequoia-policy-config" authors = ["Neal H. Walfield "] homepage = "https://sequoia-pgp.org/" repository = "https://gitlab.com/sequoia-pgp/sequoia-policy-config" readme = "README.md" keywords = ["cryptography", "openpgp" ] description = "Configure Sequoia using a configuration file." license = "LGPL-2.0-or-later" version = "0.8.0" edition = "2021" rust-version = "1.70" build = "build.rs" [badges] maintenance = { status = "actively-developed" } [lib] name = "sequoia_policy_config" path = "src/lib.rs" [[bin]] name = "sequoia-policy-config-check" path = "src/check.rs" [dependencies] anyhow = "1.0.18" chrono = "0.4" sequoia-openpgp = { version = "2", default-features = false } serde = "1.0" thiserror = "1.0.2" toml = "0.5" [dev-dependencies] assert_cmd = "2" quickcheck = { version = "1", default-features = false } [target.'cfg(not(windows))'.dev-dependencies] # Enables a crypto backend for the tests: sequoia-openpgp = { version = "2", default-features = false, features = ["crypto-nettle", "__implicit-crypto-backend-for-tests"] } [target.'cfg(windows)'.dev-dependencies] # Enables a crypto backend for the tests: sequoia-openpgp = { version = "2", default-features = false, features = ["crypto-cng", "__implicit-crypto-backend-for-tests"] } [package.metadata.docs.rs] # Enables a crypto backend for the docs.rs generation: features = ["sequoia-openpgp/default"] sequoia-policy-config-0.8.0/LICENSE.txt000064400000000000000000000627341046102023000156760ustar 00000000000000Sequoia PGP is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Sequoia PGP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. --- GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! sequoia-policy-config-0.8.0/README.md000064400000000000000000000044521046102023000153230ustar 00000000000000A library for reading the configuration of [Sequoia]'s `StandardPolicy` from a configuration file. [Sequoia]: https://sequoia-pgp.org [`StandardPolicy`]: https://docs.rs/sequoia-openpgp/1.11.0/sequoia_openpgp/policy/struct.StandardPolicy.html Sequoia's [`StandardPolicy`] can be configured using Rust. As with most things, Sequoia's low-level library avoids imposing a policy on users of the library, like where a configuration file should be or even what format it should have. When necessary, it is up to the application to provide an interface, and to configure the policy appropriately. This library provides a high-level interface that parses a configuration file, and returns a configured `StandardPolicy`. See the crate's documentation for a description of the file format. ## Quick Start To add `sequoia-policy-config` to your crate add the following your crate: ```toml [dependencies] sequoia-openpgp = { version = "1" } sequoia-policy-config = { version = "0.6" } ``` This will use `sequoia-openpgp`'s default cryptographic backend, which is currently Nettle. To select a different cryptographic backend, such as OpenSSL, you can then do: ```shell cargo build --release --no-default-features --features sequoia-openpgp/crypto-openssl ``` To use `sequoia-policy-config` in your crate, it is usually enough to replace the use of `StandardPolicy::new` with the following:: ```rust use sequoia_policy_config::ConfiguredStandardPolicy; fn main() -> openpgp::Result<()> { let mut p = ConfiguredStandardPolicy::new(); p.from_bytes(b"[hash_algorithms] sha1.collision_resistance = \"never\"")?; let p = &p.build(); // ... Ok(()) } ``` ## Building This crate is purely a library, so it is not usually built directly. If you do build it (e.g., because you are modifying it), you'll need to select a cryptographic backend. See [`sequoia-openpgp`'s README] for details. [`sequoia-openpgp`'s README]: https://gitlab.com/sequoia-pgp/sequoia#features The short version is: ``` # Use the Nettle backend: $ cargo build --release --features sequoia-openpgp/crypto-nettle $ cargo test --release --features sequoia-openpgp/crypto-nettle # Use the OpenSSL backend: $ cargo build --release --features sequoia-openpgp/crypto-openssl $ cargo test --release --features sequoia-openpgp/crypto-openssl ``` sequoia-policy-config-0.8.0/build.rs000064400000000000000000000023171046102023000155070ustar 00000000000000use std::env; use std::fs; use std::io; use std::io::Write; use std::path::PathBuf; /// Builds the index of the test data for use with the `::testdata` /// module. fn include_test_data() -> io::Result<()> { let cwd = env::current_dir()?; let mut sink = fs::File::create( PathBuf::from(env::var_os("OUT_DIR").unwrap()) .join("tests.index.rs.inc")).unwrap(); writeln!(&mut sink, "{{")?; let mut dirs = vec![PathBuf::from("tests/data")]; while let Some(dir) = dirs.pop() { println!("rerun-if-changed={}", dir.to_str().unwrap()); for entry in fs::read_dir(dir).unwrap() { let entry = entry?; let path = entry.path(); if path.is_file() { writeln!( &mut sink, " add!({:?}, {:?});", path.components().skip(2) .map(|c| c.as_os_str().to_str().expect("valid UTF-8")) .collect::>().join("/"), cwd.join(path))?; } else if path.is_dir() { dirs.push(path.clone()); } } } writeln!(&mut sink, "}}")?; Ok(()) } fn main() { include_test_data().unwrap(); } sequoia-policy-config-0.8.0/doc/release-checklist.md000064400000000000000000000031571046102023000205230ustar 00000000000000This is a checklist for doing releases. 1. Start from `origin/main`, create a branch `staging`. 1. Switch to the branch. 1. Bump the version in `Cargo.toml` to `XXX`. 1. Bump the version in `README.md` to `XXX`. 1. Run `cargo check` (this implicitly updates `Cargo.lock`). 1. Update dependencies and run tests. - Use the exact Rust toolchain version of the current Sequoia MSRV (refer to `Cargo.toml`): `rustup default 1.xx` - Run `cargo update` to update the dependencies. If some dependency is updated and breaks due to our MSRV, find a good version of that dependency and select it using e.g. `cargo update -p backtrace --precise -3.46`. - Run `cargo build && cargo check` 1. Commit changes to `Cargo.toml` and `Cargo.lock`. 1. Make a commit with the message `Release XXX.`. - Push to gitlab, and create a merge request. Don't auto merge!!! 1. Make sure `cargo publish` works: - `mkdir -p /tmp/sequoia-staging` - `cd /tmp/sequoia-staging` - `git clone git@gitlab.com:sequoia-pgp/sequoia-policy-config.git` - `cd sequoia-policy-config` - `git checkout origin/staging` - `cargo publish --features sequoia-openpgp/crypto-nettle --dry-run` 1. Wait until CI and `cargo publish ... --dry-run` are successful. In case of errors, correct them, and restart. 1. Merge the merge request. 1. Run `cargo publish --features sequoia-openpgp/crypto-nettle`. 1. Make a tag `vXXX` with the message `Release XXX.` signed with an offline-key, which has been certified by our `openpgp-ca@sequoia-pgp.org` key. 1. Push the signed tag `vXXX`. sequoia-policy-config-0.8.0/openpgp-policy.toml000064400000000000000000002464301046102023000177120ustar 00000000000000version = 0 commit_goodlist = [] [authorization."Justus Winter (Code Signing Key) "] sign_commit = true keyring = """ -----BEGIN PGP PUBLIC KEY BLOCK----- Comment: D2F2 C5D4 5BE9 FDE6 A4EE 0AAF 3185 5247 6038 31FD Comment: Justus Winter (Code Signing Key) "] sign_commit = true sign_tag = true sign_archive = true add_user = true retire_user = true audit = true keyring = """ -----BEGIN PGP PUBLIC KEY BLOCK----- Comment: CBCD 8F03 0588 653E EDD7 E265 9B7D D433 F254 904A Comment: Comment: Justus Winter Comment: Justus Winter Comment: Justus Winter Comment: Justus Winter Comment: Justus Winter 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 =BwBn -----END PGP PUBLIC KEY BLOCK----- """ [authorization."Neal H. Walfield "] sign_commit = true keyring = """ -----BEGIN PGP PUBLIC KEY BLOCK----- Comment: F717 3B3C 7C68 5CD9 ECC4 191B 74E4 45BA 0E15 C957 Comment: Neal H. Walfield (Code Signing Key) "] sign_commit = true sign_tag = true sign_archive = true add_user = true retire_user = true audit = true keyring = """ -----BEGIN PGP PUBLIC KEY BLOCK----- Comment: 8F17 7771 18A3 3DDA 9BA4 8E62 AACB 3243 6300 52D9 Comment: Neal H. Walfield Comment: Neal H. Walfield Comment: Neal H. Walfield Comment: Neal H. Walfield Comment: Neal H. Walfield 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 XuwkEDh4MBNicSOdhoItEg89lEbzxQ== =+VvR -----END PGP PUBLIC KEY BLOCK----- """ sequoia-policy-config-0.8.0/src/check.rs000064400000000000000000000024011046102023000162460ustar 00000000000000use std::io::Read; use std::fs::File; use sequoia_openpgp as openpgp; use openpgp::Result; use sequoia_policy_config::{ConfiguredStandardPolicy, DumpDefault}; fn usage() -> ! { let binary = std::env::args().next() .unwrap_or_else(|| String::from("sequoia-policy-config-check")); eprintln!("Usage: {} [--dump] [config.toml]", binary); eprintln!(""); eprintln!("If no configuration is supplied, the default configuration \ is checked."); std::process::exit(1); } fn main() -> Result<()> { let args = std::env::args().collect::>(); if args.len() > 3 { eprintln!("Expected at most two arguments."); usage(); } let dump = args.get(1).map(|a| a == "--dump").unwrap_or(false); if ! dump && args.len() == 3 { usage(); } let filename_offset = if dump { 2 } else { 1 }; let mut p = ConfiguredStandardPolicy::new(); if let Some(filename) = args.get(filename_offset) { let mut file = File::open(filename)?; let mut bytes = Vec::new(); file.read_to_end(&mut bytes)?; p.parse_bytes(bytes)?; } else { p.parse_default_config()?; } if dump { p.dump(&mut std::io::stdout(), DumpDefault::Template)?; } Ok(()) } sequoia-policy-config-0.8.0/src/dump.rs000064400000000000000000000314171046102023000161470ustar 00000000000000//! Creates a TOML template containing the current configuration. use std::{ collections::BTreeMap, io, time::{SystemTime, UNIX_EPOCH}, }; use sequoia_openpgp::{ packet::Tag, policy::{ AsymmetricAlgorithm, HashAlgoSecurity, StandardPolicy, }, types::{ AEADAlgorithm, HashAlgorithm, SymmetricAlgorithm, }, }; use crate::{ AEAD_ALGO_MAP, ASYMM_ALGO_MAP, ConfiguredStandardPolicy, HASH_ALGO_MAP, PACKET_MAP, Result, SYMM_ALGO_MAP, algo_to_key, known_packet_version, versioned_packet, }; /// Controls how [`ConfiguredStandardPolicy::dump`] emits default /// values. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum DumpDefault { /// Default values are not emitted. /// /// The advantage is that changes to the `StandardPolicy`'s /// defaults are picked up by applications using a policy /// configuration emitted by /// [`ConfiguredStandardPolicy::dump`]. /// /// The disadvantage is that the defaults are somewhat opaque, /// i.e. hard to discover and adjust for the user. Or, it may be /// that the intent of writing out the policy configuration is to /// freeze the policy, i.e. prevent updates to the /// `StandardPolicy`'s defaults to become effective. Implicit, /// Defaults are emitted, but commented out. /// /// Represents a middle ground between [`DumpDefault::Implicit`] /// and [`DumpDefault::Explicit`]: it allows the /// `StandardPolicy`'s defaults to evolve, while allowing the user /// to discover and tweak the results if they wish. Template, /// Default values are made explicit. /// /// The advantage is that default values are made explicit, so /// that users can discover and tweak them. Further, the current /// policy is frozen, i.e. it prevents updates to the /// `StandardPolicy`'s defaults from becoming effective. /// /// The downside is that the policy is frozen, so users are at /// risk of using a stale policy configuration. Explicit, } impl DumpDefault { /// Emits the key according to the variant. fn emit(&self, sink: &mut dyn io::Write, cutoff: Option, cutoff_ref: Option, key: &str) -> Result<()> { self.emit_cond(sink, cutoff, cutoff == cutoff_ref, key) } /// Emits the key according to the variant depending on `omit`. fn emit_cond(&self, sink: &mut dyn io::Write, cutoff: Option, omit: bool, key: &str) -> Result<()> { if omit { match self { DumpDefault::Implicit => return Ok(()), DumpDefault::Template => write!(sink, "#")?, DumpDefault::Explicit => (), } } writeln!(sink, "{} = {}", key, format_value(cutoff))?; Ok(()) } } impl ConfiguredStandardPolicy<'_> { /// Writes the configuration to `sink`. /// /// Writes the configuration to the given writer, so that when the /// policy configuration is parsed again, it results in a policy /// that behaves the same way as this one. /// /// The handling of default cutoff times can be tweaked using the /// [`DumpDefault`] parameter: they can be omitted, emitted but /// commented out, or emitted. /// /// Note that if this policy has been read from a file, this does /// not necessarily reproduce that file exactly: as the parser /// ignores any unknown values, they are not represented in the /// parsed policy. Further, any default disposition settings are /// lost, as they only affect the parsing, and are not represented /// in the resulting policy. pub fn dump(&self, sink: &mut dyn io::Write, w: DumpDefault) -> Result<()> { const REF: StandardPolicy = StandardPolicy::new(); writeln!(sink, "[hash_algorithms]")?; for a in HashAlgorithm::variants() { let spir = self.policy.hash_cutoff( a, HashAlgoSecurity::SecondPreImageResistance); let spir_ref = REF.hash_cutoff( a, HashAlgoSecurity::SecondPreImageResistance); let cr = self.policy.hash_cutoff( a, HashAlgoSecurity::CollisionResistance); let cr_ref = REF.hash_cutoff( a, HashAlgoSecurity::CollisionResistance); if spir == cr { w.emit(sink, spir, spir_ref, algo_to_key(&HASH_ALGO_MAP, a)?)?; } else { w.emit(sink, spir, spir_ref, &format!("{}.second_preimage_resistance", algo_to_key(&HASH_ALGO_MAP, a)?))?; w.emit(sink, cr, cr_ref, &format!("{}.collision_resistance", algo_to_key(&HASH_ALGO_MAP, a)?))?; } } writeln!(sink)?; writeln!(sink, "[symmetric_algorithms]")?; for a in SymmetricAlgorithm::variants() { let cutoff = self.policy.symmetric_algo_cutoff(a); let cutoff_ref = REF.symmetric_algo_cutoff(a); w.emit(sink, cutoff, cutoff_ref, algo_to_key(&SYMM_ALGO_MAP, a)?)?; } writeln!(sink)?; writeln!(sink, "[asymmetric_algorithms]")?; for a in AsymmetricAlgorithm::variants() { let cutoff = self.policy.asymmetric_algo_cutoff(a); let cutoff_ref = REF.asymmetric_algo_cutoff(a); w.emit(sink, cutoff, cutoff_ref, algo_to_key(&ASYMM_ALGO_MAP, a)?)?; } writeln!(sink)?; writeln!(sink, "[aead_algorithms]")?; for a in AEADAlgorithm::variants() { let cutoff = self.policy.aead_algo_cutoff(a); let cutoff_ref = REF.aead_algo_cutoff(a); w.emit(sink, cutoff, cutoff_ref, algo_to_key(&AEAD_ALGO_MAP, a)?)?; } writeln!(sink)?; writeln!(sink, "[packets]")?; for t in Tag::variants() { if versioned_packet(t) { // We need to be clever here. First, compute a // histogram of packet version cutoffs. let mut histo: BTreeMap, Vec> = Default::default(); // Constant cutoff time in the reference? let mut ref_constant = true; let ref_0 = REF.packet_tag_version_cutoff(t, 0); for v in 0..=255 { let cutoff = self.policy.packet_tag_version_cutoff(t, v); histo.entry(cutoff).or_default().push(v); ref_constant &= REF.packet_tag_version_cutoff(t, v) == ref_0; } if histo.len() == 1 { // All versions have the same cutoff. Collapse // into unversioned cutoff. let cutoff = *histo.keys().next().unwrap(); let cutoff_ref = REF.packet_tag_version_cutoff(t, 0); w.emit_cond(sink, cutoff, cutoff == cutoff_ref && ref_constant, algo_to_key(&PACKET_MAP, t)?)?; continue; } // Collapse the largest group into the default // disposition. let mut histo = histo.into_iter().collect::>(); histo.sort_by_key(|(_, versions)| versions.len()); let (default_disposition, default_group) = histo.pop().unwrap(); let default_ref = REF.packet_tag_version_cutoff(t, default_group[0]); w.emit_cond(sink, default_disposition, default_disposition == default_ref && ref_constant, &format!("{}.default_disposition", algo_to_key(&PACKET_MAP, t)?))?; let mut unknown_versions = Vec::new(); for (cutoff, versions) in histo { for v in versions { let cutoff_ref = REF.packet_tag_version_cutoff(t, v); w.emit_cond( sink, cutoff, cutoff == cutoff_ref && cutoff == default_disposition, &format!("{}.v{}", algo_to_key(&PACKET_MAP, t)?, v))?; if ! known_packet_version(t, v) { unknown_versions.push(format!("v{}", v)); } } } if ! unknown_versions.is_empty() { writeln!(sink, "{}.ignore_invalid = {:?}", algo_to_key(&PACKET_MAP, t)?, &unknown_versions)?; } } else { let cutoff = self.policy.packet_tag_version_cutoff(t, 0); let cutoff_ref = REF.packet_tag_version_cutoff(t, 0); w.emit(sink, cutoff, cutoff_ref, algo_to_key(&PACKET_MAP, t)?)?; } } Ok(()) } } /// Formats the given cutoff value. fn format_value(v: Option) -> String { match v { None => "\"always\"".into(), Some(cutoff) => if cutoff == UNIX_EPOCH { "\"never\"".into() } else { use chrono::{DateTime, NaiveTime, Utc}; let d: DateTime = cutoff.into(); if d.time().signed_duration_since( NaiveTime::from_hms_opt(0, 0, 0).unwrap()).is_zero() { d.format("%Y-%m-%d").to_string() } else { d.format("%Y-%m-%dT%H:%M:%SZ").to_string() } } } } #[cfg(test)] mod tests { use super::*; #[test] fn roundtrip_default() -> Result<()> { test_roundtrip(Default::default()) } quickcheck::quickcheck! { fn roundtrip(p: ConfiguredStandardPolicy<'static>) -> bool { test_roundtrip(p).unwrap(); true } } fn test_roundtrip(p: ConfiguredStandardPolicy) -> Result<()> { for v in [DumpDefault::Explicit, DumpDefault::Template, DumpDefault::Implicit] { let mut sink = Vec::new(); p.dump(&mut sink, v)?; eprintln!("{:?}:", v); eprintln!("{}", std::str::from_utf8(&sink)?); let mut q = ConfiguredStandardPolicy::new(); q.parse_bytes(&sink)?; assert_effectively_eq(&p.policy, &q.policy); } Ok(()) } fn assert_effectively_eq(a: &StandardPolicy, b:&StandardPolicy) { // XXX: The parser ignores unknown algorithms, even though the // standard policy could represent them. Therefore, we only // compare known algorithms. for alg in HashAlgorithm::variants() { assert_eq!( a.hash_cutoff(alg, HashAlgoSecurity::SecondPreImageResistance), b.hash_cutoff(alg, HashAlgoSecurity::SecondPreImageResistance), "differ on second preimage resistance cutoff for {}", alg); assert_eq!( a.hash_cutoff(alg, HashAlgoSecurity::CollisionResistance), b.hash_cutoff(alg, HashAlgoSecurity::CollisionResistance), "differ on collision resistance cutoff for {}", alg); } for alg in SymmetricAlgorithm::variants() { assert_eq!( a.symmetric_algo_cutoff(alg), b.symmetric_algo_cutoff(alg), "differ on symmetric algo cutoff for {}", alg); } for alg in AEADAlgorithm::variants() { assert_eq!( a.aead_algo_cutoff(alg), b.aead_algo_cutoff(alg), "differ on aead algo cutoff for {}", alg); } for alg in AsymmetricAlgorithm::variants() { assert_eq!( a.asymmetric_algo_cutoff(alg), b.asymmetric_algo_cutoff(alg), "differ on asymmetric algo cutoff for {}", alg); } for t in Tag::variants() { for v in 0..=255 { // XXX: The parser flat out ignores unknown packet // versions, even though the standard policy can // represent unknown values. Therefore, we only // compare known versions. if ! known_packet_version(t, v) { continue; } assert_eq!( a.packet_tag_version_cutoff(t, v), b.packet_tag_version_cutoff(t, v), "differ on {} version {} cutoff", t, v); } } } } sequoia-policy-config-0.8.0/src/lib.rs000064400000000000000000002110011046102023000157350ustar 00000000000000//! Configures a `StandardPolicy` using a configuration file. //! //! Sequoia's [`StandardPolicy`] can be configured using Rust. As //! with most things, Sequoia's low-level library avoids imposing a //! policy on users of the library, like where a configuration file //! should be or even what format it should have. When necessary, it //! is up to the application to provide an interface, and to configure //! the policy appropriately. //! //! [`StandardPolicy`]: https://docs.rs/sequoia-openpgp/1.*/sequoia_openpgp/policy/struct.StandardPolicy.html //! //! This library provides a high-level interface that parses a //! configuration file, and returns a configured `StandardPolicy`. //! //! # Format //! //! The file format is based on [TOML]. It contains several sections, //! one for hash algorithms, one for asymmetric algorithms, etc. //! //! [TOML]: https://toml.io/ //! //! ## Forward Compatibility //! //! This parser is strict, but we want the configuration format to be //! forwards compatible so that the same configuration file can be //! used with different versions of the parser. //! //! ### Dealing with New Keys //! //! Unknown sections and unknown keys cause a parse error. To allow //! configuration files a degree of backwards compatibility, it is //! possible to set the per-map `ignore_invalid` key to the key or the //! list of keys that should be ignored if they are not recognized. //! For instance, if Sequoia adds support for `SHA4`, then you could //! do the following to unconditionally reject `SHA4` while ensuring //! that the configuration is still readable by older versions of //! Sequoia that don't know about `SHA4`: //! //! ```toml //! [hash_algorithms] //! ignore_invalid = [ "sha4" ] //! sha4 = never //! ``` //! //! (The same principle applies to sections.) //! //! ### Dealing with Type Changes //! //! Most keys are string types. In the future, we may want to make a //! given algorithm or data structure's configuration more nuanced. A //! logical way to do this is to change the key from taking a string //! type to taking a map type. //! //! To support this type of change, all keys that take a string are //! also recognized as maps with a single key, `default_disposition`. //! Thus, if `key` is ever extended in this way, //! `key.default_disposition` can be used to control the configuration //! of older versions, and new versions can use the configuration //! parameters. //! //! For instance, imagine that AES128 is found to be vulnerable to an //! attack called `foo` in certain, detectable situations. We could //! extend AES128 with a new key (`foo`) that is respected when those //! conditions are detected. This can be expressed in the following, //! backwards compatible manner: //! //! ```toml //! [symmetric_algorithms] //! aes128.default_disposition = "always" //! aes128.foo = "2023-01-01" //! aes128.ignore_invalid = "foo" //! ``` //! //! ## Cutoff Times //! //! Most settings take a so-called cutoff time. The cutoff time is //! the time at which an algorithm (e.g., the broken [SHA-1] hash //! algorithm) or a data structure (e.g. the obsolete [SED packet]) //! should no longer be considered valid. Using a cutoff time //! provides more nuance than simply marking an algorithm as invalid. //! In particular, it allows sun setting algorithms that have been //! weakened, but are not yet completely broken, and using data that //! has been saved to a trusted medium before its security was broken. //! //! Cutoff times are expressed using TOML's `datetime` datatype, which //! is an [RFC 3339] formatted timestamp. The following variants are //! valid: //! //! [SHA-1]: https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-07#section-14.1 //! [SED packet]: https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-07#section-5.7 //! //! Offset datetime: //! //! ```toml //! [hash_algorithms] //! sha1 = 2010-01-01T00:00:00Z //! ``` //! //! Local datetime: //! //! ```toml //! [hash_algorithms] //! sha1 = 2010-01-01T00:00:00+00:00 //! ``` //! //! Local date (interpreted as midnight in UTC): //! //! ```toml //! [hash_algorithms] //! sha1 = 2010-01-01 //! ``` //! //! The local time format is not supported as it doesn't make sense in //! this context. //! //! [RFC 3339]: https://www.rfc-editor.org/rfc/rfc3339 //! //! Two special values are also supported: `always` means that the //! algorithm or data structure should always be considered valid, and //! `never` means that the algorithm or data structure should never be //! considered valid. These values are checked for in a //! case-sensitive manner. //! //! ```toml //! [hash_algorithms] //! sha1 = "never" //! ``` //! //! ## Default Disposition //! //! In some situations, it is desirable to only allow a fixed set of //! algorithms. Using the cutoff mechanism, it is possible to accept //! or reject each of the known algorithms, but unknown algorithms, //! i.e., those that Sequoia will add in the future, and will likely //! enable by default, can't be rejected in this way, because their //! name is---by definition---not yet known. //! //! To accommodate this usage, it is possible to set an algorithm //! class or data structure class's default disposition using the //! `default_disposition` key. Currently, only one value is supported //! for this key: `never`. If this key is present in a section, then //! that key is processed first, and all algorithms are set to be //! rejected. The rest of the keys are then processed as usual. //! //! The following example shows how to only allow the SHA256 and //! SHA512 hash functions. Even if a theoretical SHA4 hash function //! is added to Sequoia, it will be rejected by this configuration. //! //! ```toml //! [hash_algorithms] //! sha256.collision_resistance = "always" //! sha256.second_preimage_resistance = "always" //! sha512.collision_resistance = "always" //! sha512.second_preimage_resistance = "always" //! default_disposition = "never" //! ``` //! //! ## Sections //! //! ### Hash Functions //! //! Hash algorithms are used to ensure multiple properties. Of //! particular relevance in the context of OpenPGP are collision //! resistance, and second preimage resistance. In some contexts like //! self signatures, only second preimage resistance is required. In //! other contexts, both properties are required. As collision //! resistance is much easier to attack, these two properties can be //! set separately. //! //! You configure just the second preimage resistance cutoff by //! setting the `ALGO.second_preimage_resistance` key. You configure //! just the collision resistance cutoff by setting the //! `ALGO.collision_resistance` key. Setting the `ALGO` key is //! shorthand for setting both. //! //! A hash algorithm's key is the lower-case version of the value //! returned by the [`Display` name][hash-display]. For instance, //! SHA1 is `sha1`. //! //! [hash-display]: https://docs.rs/sequoia-openpgp/1.10.0/sequoia_openpgp/types/enum.HashAlgorithm.html#impl-Display //! //! ```toml //! [hash_algorithms] //! md5 = "never" //! sha1.second_preimage_resistance = 2030-01-01 //! sha1.collision_resistance = 2022-01-01 //! sha512 = "always" //! ``` //! //! ### Symmetric Algorithms //! //! Like hash algorithms, [symmetric algorithms] can be rejected //! outright or have a cutoff date. They don't have any subkeys like //! `collision_resistance`, so there is only one way to set the //! cutoff: using the `algo` key. //! //! [symmetric algorithms]: https://docs.rs/sequoia-openpgp/1.10.0/sequoia_openpgp/types/enum.SymmetricAlgorithm.html#impl-Display //! //! The unencrypted variant, the unknown variants, and the private //! variants cannot currently be set. //! //! ```toml //! [symmetric_algorithms] //! cast5 = "never" //! aes128 = "always" //! ``` //! //! ### Asymmetric Algorithms //! //! Like symmetric algorithms, [asymmetric algorithms] can be rejected //! outright or have a cutoff date. //! //! [asymmetric algorithms]: https://docs.sequoia-pgp.org/sequoia_openpgp/policy/enum.AsymmetricAlgorithm.html#impl-Display //! //! The unknown variants, and the private variants cannot currently be //! set. //! //! ```toml //! [asymmetric_algorithms] //! rsa1024 = "never" //! rsa2048 = 2028-01-01 //! ``` //! //! ### AEAD Algorithms //! //! Like symmetric algorithms, [AEAD algorithms] can be rejected //! outright or have a cutoff date. //! //! [AEAD algorithms]: https://docs.rs/sequoia-openpgp/1.10.0/sequoia_openpgp/types/enum.AEADAlgorithm.html#impl-Display //! //! The unknown variants, and the private variants cannot currently be //! set. //! //! ```toml //! [aead_algorithms] //! eax = "never" //! ocb = "always" //! ``` //! //! ### Packets //! //! Packets can be rejected outright or have a cutoff date. The [SED //! packet] is, for instance, considered broken, and messages that use //! it should generally be rejected unless they are known to not be //! from an attacker, e.g., because they were stored on a trusted //! medium before the attack was feasible. //! //! It is also possible to reject particular versions of a packet. In //! this case, the packet is a map and the fields `vX` where `X` is a //! `u8` can be used to set the cutoff for version `X` of the packet. //! This mechanism is only supported for packets that actually are //! versioned, and only for known versions. (Unknown versions can //! still be set in a forwards compatible way using the //! `ignore_invalid` key.) //! //! The packets are named after the [names of the Packet variants]. //! //! [names of the Packet variants]: https://docs.rs/sequoia-openpgp/1.10.0/sequoia_openpgp/packet/enum.Tag.html#variants //! //! The reserved packet, the unknown variants, and the private //! variants cannot currently be set. //! //! ```toml //! [packets] //! sed = "never" //! seip = 2028-01-01 //! //! signature.v3 = 2017-01-01 //! signature.v4 = 2030-01-01 //! signature.v6 = "always" //! # v6 signatures are coming, but not yet recognized. //! signature.ignore_invalid = "v6" //! ``` //! //! ## Examples //! //! The following example shows how to use a configuration file to //! configure a `StandardPolicy`: //! //! ```rust //! use sequoia_openpgp as openpgp; //! use openpgp::policy::HashAlgoSecurity; //! use openpgp::types::HashAlgorithm; //! //! use sequoia_policy_config::ConfiguredStandardPolicy; //! //! # fn main() -> openpgp::Result<()> { //! let mut p = ConfiguredStandardPolicy::new(); //! p.parse_bytes(b"[hash_algorithms] //! sha1.collision_resistance = \"never\"")?; //! let p = &p.build(); //! //! assert_eq!(p.hash_cutoff(HashAlgorithm::SHA1, //! HashAlgoSecurity::CollisionResistance), //! Some(std::time::UNIX_EPOCH)); //! # Ok(()) } //! ``` #![allow(clippy::type_complexity)] use std::collections::HashSet; use std::env; use std::io; use std::path::Path; use std::path::PathBuf; use std::string::String; use std::time::SystemTime; use std::time::UNIX_EPOCH; use sequoia_openpgp as openpgp; use openpgp::packet::Tag; use openpgp::policy::AsymmetricAlgorithm; use openpgp::policy::StandardPolicy; use openpgp::policy::HashAlgoSecurity; use openpgp::types::AEADAlgorithm; use openpgp::types::HashAlgorithm; use openpgp::types::SymmetricAlgorithm; use anyhow::Context as _; use chrono::DateTime; use chrono::NaiveDate; use chrono::Utc; use toml::value; use toml::Value; mod dump; pub use dump::DumpDefault; #[cfg(test)] mod testdata; #[derive(Clone, Debug)] pub struct ConfiguredStandardPolicy<'a> { policy: StandardPolicy<'a>, } type Result = std::result::Result; /// Errors used in this crate. /// /// Note: This enum cannot be exhaustively matched to allow future /// extensions. #[non_exhaustive] #[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] pub enum Error { /// Parse error #[error("Parse error: {0}")] ParseError(String), /// A Relative Path was provided where an absolute path was expected. #[error("Relative path not allowed: {0}")] RelativePathError(PathBuf), /// An algorithm is not known to this crate. #[error("Unknown algorithm: {0}")] UnknownAlgorithm(String), } /// A timestamp. /// /// None is always. type Timestamp = Option; /// Always accept. const ALWAYS: Timestamp = None; /// Never accept. /// /// By setting the acceptance time to the unix epoch, we never accept /// anything. const NEVER: Timestamp = Some(UNIX_EPOCH); // Parses a timestamp stored in a toml value. // // Recognizes the special values "always" and "never". fn parse_time(t: &Value) -> Result { let t = match t { Value::String(t) => t.clone(), Value::Datetime(t) => t.to_string(), _ => Err(Error::ParseError( format!("timestamp is not a string ({:?})", t)))?, }; Ok(match &t[..] { "always" => ALWAYS, "never" => NEVER, t => { let t = if let Ok(p) = DateTime::parse_from_rfc3339(t) { p.into() } else { let t = NaiveDate::parse_from_str(t, "%Y-%m-%d") .context(format!("Parsing timestamp {}", t))? .and_hms_opt(0, 0, 0).expect("valid time"); let t: DateTime = DateTime::from_utc(t, Utc); t.into() }; Some(t) } }) } // Returns an error if a key is unknown. // // known_keys better be lowercase. fn check_sections(path: Option<&str>, section: &value::Map, known_keys: &[&str]) -> Result<()> { // known_keys better be lowercase. known_keys.iter().for_each( |&s| debug_assert_eq!(&s.to_lowercase()[..], s)); let prefix = || if let Some(path) = path { format!("{}.", path) } else { "".to_string() }; let keys: HashSet<&str> = section .keys() .map(|s| s.as_str()) .collect(); // The set of allowed keys are the known keys, plus // "ignore_invalid", and the value of "ignore_invalid". let mut allowed_keys: Vec<&str> = known_keys.to_vec(); if let Some(ignore) = section.get("ignore_invalid") { allowed_keys.push("ignore_invalid"); match ignore { Value::String(k) => allowed_keys.push(k.as_str()), Value::Array(ks) => { for k in ks { if let Value::String(k) = k { allowed_keys.push(k.as_str()); } else { Err(Error::ParseError(format!( "'{}ignore_invalid' takes a string \ or an array of strings", prefix())))? } } } _ => { return Err(Error::ParseError(format!( "Invalid value for '{}ignore_invalid': {}, \ expected a string or an array of strings", prefix(), ignore)).into()); } } } // Now check if there are any unknown sections. let unknown_keys = keys .difference(&allowed_keys.into_iter().collect()) .map(|s| *s) .collect::>(); if ! unknown_keys.is_empty() { return Err(Error::ParseError(format!( "{} has unknown keys: {}, valid keys are: {}", if let Some(path) = path { path } else { "top-level section" }, unknown_keys.join(", "), // We don't include the keys listed in ignore_invalid. known_keys.join(", "))).into()); } Ok(()) } const HASH_ALGO_PROPS: &[(&str, HashAlgoSecurity)] = &[ ("second_preimage_resistance", HashAlgoSecurity::SecondPreImageResistance), ("collision_resistance", HashAlgoSecurity::CollisionResistance), ]; const HASH_ALGO_MAP: [(&str, HashAlgorithm, &[(&str, HashAlgoSecurity)]); 9] = [ ("md5", HashAlgorithm::MD5, HASH_ALGO_PROPS), ("sha1", HashAlgorithm::SHA1, HASH_ALGO_PROPS), ("ripemd160", HashAlgorithm::RipeMD, HASH_ALGO_PROPS), ("sha256", HashAlgorithm::SHA256, HASH_ALGO_PROPS), ("sha384", HashAlgorithm::SHA384, HASH_ALGO_PROPS), ("sha512", HashAlgorithm::SHA512, HASH_ALGO_PROPS), ("sha224", HashAlgorithm::SHA224, HASH_ALGO_PROPS), ("sha3-256", HashAlgorithm::SHA3_256, HASH_ALGO_PROPS), ("sha3-512", HashAlgorithm::SHA3_512, HASH_ALGO_PROPS), ]; const ASYMM_ALGO_MAP: [(&str, AsymmetricAlgorithm, &[(&str, ())]); 23] = [ ("rsa1024", AsymmetricAlgorithm::RSA1024, &[]), ("rsa2048", AsymmetricAlgorithm::RSA2048, &[]), ("rsa3072", AsymmetricAlgorithm::RSA3072, &[]), ("rsa4096", AsymmetricAlgorithm::RSA4096, &[]), ("elgamal1024", AsymmetricAlgorithm::ElGamal1024, &[]), ("elgamal2048", AsymmetricAlgorithm::ElGamal2048, &[]), ("elgamal3072", AsymmetricAlgorithm::ElGamal3072, &[]), ("elgamal4096", AsymmetricAlgorithm::ElGamal4096, &[]), ("dsa1024", AsymmetricAlgorithm::DSA1024, &[]), ("dsa2048", AsymmetricAlgorithm::DSA2048, &[]), ("dsa3072", AsymmetricAlgorithm::DSA3072, &[]), ("dsa4096", AsymmetricAlgorithm::DSA4096, &[]), ("nistp256", AsymmetricAlgorithm::NistP256, &[]), ("nistp384", AsymmetricAlgorithm::NistP384, &[]), ("nistp521", AsymmetricAlgorithm::NistP521, &[]), ("brainpoolp256", AsymmetricAlgorithm::BrainpoolP256, &[]), ("brainpoolp384", AsymmetricAlgorithm::BrainpoolP384, &[]), ("brainpoolp512", AsymmetricAlgorithm::BrainpoolP512, &[]), ("cv25519", AsymmetricAlgorithm::Cv25519, &[]), ("x25519", AsymmetricAlgorithm::X25519, &[]), ("ed25519", AsymmetricAlgorithm::Ed25519, &[]), ("x448", AsymmetricAlgorithm::X448, &[]), ("ed448", AsymmetricAlgorithm::Ed448, &[]), ]; #[allow(deprecated)] const SYMM_ALGO_MAP: [(&str, SymmetricAlgorithm, &[(&str, ())]); 11] = [ ("idea", SymmetricAlgorithm::IDEA, &[]), ("tripledes", SymmetricAlgorithm::TripleDES, &[]), ("cast5", SymmetricAlgorithm::CAST5, &[]), ("blowfish", SymmetricAlgorithm::Blowfish, &[]), ("aes128", SymmetricAlgorithm::AES128, &[]), ("aes192", SymmetricAlgorithm::AES192, &[]), ("aes256", SymmetricAlgorithm::AES256, &[]), ("twofish", SymmetricAlgorithm::Twofish, &[]), ("camellia128", SymmetricAlgorithm::Camellia128, &[]), ("camellia192", SymmetricAlgorithm::Camellia192, &[]), ("camellia256", SymmetricAlgorithm::Camellia256, &[]), ]; const AEAD_ALGO_MAP: [(&str, AEADAlgorithm, &[(&str, ())]); 3] = [ ("eax", AEADAlgorithm::EAX, &[]), ("ocb", AEADAlgorithm::OCB, &[]), ("gcm", AEADAlgorithm::GCM, &[]), ]; const PACKET_MAP: [(&str, Tag, &[(&str, u8)]); 19] = [ ("pkesk", Tag::PKESK, &[("v3", 3), ("v5", 5), ("v6", 6)]), ("signature", Tag::Signature, &[("v3", 3), ("v4", 4), ("v5", 5), ("v6", 6)]), ("skesk", Tag::SKESK, &[("v4", 4), ("v5", 5), ("v6", 6)]), ("onepasssig", Tag::OnePassSig, &[("v3", 3), ("v6", 6)]), ("secretkey", Tag::SecretKey, &[("v4", 4), ("v5", 5), ("v6", 6)]), ("publickey", Tag::PublicKey, &[("v4", 4), ("v5", 5), ("v6", 6)]), ("secretsubkey", Tag::SecretSubkey, &[("v4", 4), ("v5", 5), ("v6", 6)]), ("compresseddata", Tag::CompressedData, &[]), ("sed", Tag::SED, &[]), ("marker", Tag::Marker, &[]), ("literal", Tag::Literal, &[]), ("trust", Tag::Trust, &[]), ("userid", Tag::UserID, &[]), ("publicsubkey", Tag::PublicSubkey, &[("v4", 4), ("v5", 5), ("v6", 6)]), ("userattribute", Tag::UserAttribute, &[]), ("seip", Tag::SEIP, &[("v1", 1), ("v2", 2)]), ("mdc", Tag::MDC, &[]), ("aed", Tag::AED, &[("v1", 1)]), ("padding", Tag::Padding, &[]), ]; /// Given a map and an algorithm (or packet tag), returns the key /// usable in the configuration file. fn algo_to_key(map: &[(&'static str, A, &[V])], a: A) -> std::result::Result<&'static str, Error> where A: Copy + PartialEq + ToString, { map.iter().find_map(|e| (e.1 == a).then_some(e.0)) .ok_or_else(|| Error::UnknownAlgorithm(a.to_string())) } /// Given a packet tag, returns whether `v` is a known version. fn known_packet_version(t: Tag, v: u8) -> bool { PACKET_MAP.iter().find(|e| e.1 == t) .map(|(_, _, versions)| versions.iter().any(|(_, known)| *known == v)) .unwrap_or(false) } /// Returns whether the given packet type is versioned. fn versioned_packet(t: Tag) -> bool { matches!(t, | Tag::PKESK | Tag::Signature | Tag::SKESK | Tag::OnePassSig | Tag::SecretKey | Tag::PublicKey | Tag::SecretSubkey | Tag::PublicSubkey | Tag::SEIP | Tag::AED) } impl Default for ConfiguredStandardPolicy<'_> { fn default() -> Self { Self::new() } } impl<'a> ConfiguredStandardPolicy<'a> { /// The default environment variable. pub const ENV_VAR: &'static str = "SEQUOIA_CRYPTO_POLICY"; /// The default configuration file. pub const CONFIG_FILE: &'static str = "/etc/crypto-policies/back-ends/sequoia.config"; /// Returns a new `ConfiguredStandardPolicy` with a default /// `StandardPolicy`. /// /// Normally you'll want to follow this up with a call to /// [`ConfiguredStandardPolicy::parse_bytes`] or /// [`ConfiguredStandardPolicy::parse_default_config`]. pub fn new() -> Self { Self::from_policy(StandardPolicy::new()) } /// Returns a new `ConfiguredStandardPolicy`. /// /// The `StandardPolicy` is created using [`StandardPolicy::at`]. /// /// [`StandardPolicy::at`]: https://docs.rs/sequoia-openpgp/1.10.0/sequoia_openpgp/policy/struct.StandardPolicy.html#method.at /// /// Normally you'll want to follow this up with a call to /// [`ConfiguredStandardPolicy::parse_bytes`] or /// [`ConfiguredStandardPolicy::parse_default_config`]. pub fn at(t: T) -> Self where T: Into { Self::from_policy(StandardPolicy::at(t)) } /// Returns a new `ConfiguredStandardPolicy` using the provided /// `StandardPolicy`. /// /// Normally you'll want to follow this up with a call to /// [`ConfiguredStandardPolicy::parse_bytes`] or /// [`ConfiguredStandardPolicy::parse_default_config`]. pub fn from_policy(policy: StandardPolicy<'a>) -> Self { ConfiguredStandardPolicy { policy } } /// Parses the configuration file specified by the environment /// variable. /// /// To use the default environment variable, specify /// [`ConfiguredStandardPolicy::ENV_VAR`]. /// /// This function returns: `Ok(true)` if the policy was /// configured; `Ok(false)` if the policy was not configured; or, /// an error if there was a problem opening, reading, or parsing /// the configuration file. /// /// Specifically: /// /// - If the specified environment variable is not set, this /// function returns `Ok(false)` to indicate that the policy /// was not configured. /// /// - If the specified environment variable is set to the empty /// string, no configuration file is read, but the policy is /// considered to be configured (that is, the empty string /// means to use the policy as is), and the function returns /// `Ok(true)`. /// /// - If the environment variable is set to a relative path, /// this function returns an error. /// /// - If the environment variable is set to an absolute path, /// the specified file is parsed. If an error occurs while /// opening (including that the file does not exist), reading, /// or parsing the configuration file, the error is returned. /// If the configuration file is successfully parsed, the /// function returns `Ok(true)` to indicate that the policy is /// configured. pub fn parse_env_config(&mut self, env_var: &str) -> Result { let config_file = match env::var(env_var) { // Environment variable wasn't set. Err(_err) => return Ok(false), Ok(filename) => filename, }; if config_file.is_empty() { // We're configured: the empty string means to just use // the policy as is. return Ok(true); } let config_file = PathBuf::from(config_file); if config_file.is_relative() { // A relative path is an error. let err = anyhow::Error::from( Error::RelativePathError(config_file)); return Err(err) .context(format!("Invalid value for {}", env_var)); } // We've got a configuration file. Parse it. let config = match std::fs::read(&config_file) { Err(err) => { let err = anyhow::Error::from(err); return Err(err).with_context(|| { format!("Reading {:?}", config_file) }); } Ok(config) => config, }; self.parse_bytes(config) .with_context(|| { format!("Parsing {:?}", config_file) })?; // We're configured. Ok(true) } /// Configures the policy using the specified configuration file. /// /// If `config_file` does not exist, returns `Ok(false)` to /// indicate that the policy was not configured. /// /// If an error occurs while opening, reading, or parsing the /// configuration file, the error is returned. /// /// Otherwise, `Ok(true)` is returned to indicate that the /// policy was configured. pub fn parse_config_file

(&mut self, config_file: P) -> Result where P: AsRef { let config_file = config_file.as_ref(); let config = match std::fs::read(config_file) { Err(err) => { if err.kind() == io::ErrorKind::NotFound { // A missing configuration file is not an error. return Ok(false); } else { let err = anyhow::Error::from(err); return Err(err).with_context(|| { format!("Reading {:?}", config_file) }); } } Ok(config) => config, }; self.parse_bytes(config) .with_context(|| { format!("Parsing {:?}", config_file) })?; // We're configured. Ok(true) } /// Parses the specified configuration. /// /// This function first tries to configure the policy using the /// configuration file specified in the environment variable /// `env_var` using /// [`ConfiguredStandardPolicy::parse_env_config`]. If that /// returns `Ok(false)`, then it tries to parse `config_file` /// using [`ConfiguredStandardPolicy::parse_config_file`]. pub fn parse_config

(&mut self, env_var: &str, config_file: P) -> Result where P: AsRef { let config_file = config_file.as_ref(); match self.parse_env_config(env_var) { Ok(false) => { // No error and we didn't configure the policy. Fallback // to the configuration file. self.parse_config_file(config_file) } otherwise => otherwise, } } /// Parses the default configuration. /// /// This first tries to parse the configuration file specified in /// the environment variable `SEQUOIA_CRYPTO_POLICY`. See /// [`ConfiguredStandardPolicy::parse_env_config`] for the /// semantics. /// /// If `ConfiguredStandardPolicy::parse_env_config` doesn't /// configure the policy (i.e., it returns `Ok(false)`), this /// function tries to parse /// [`ConfiguredStandardPolicy::CONFIG_FILE`] using /// [`ConfiguredStandardPolicy::parse_config_file`]. pub fn parse_default_config(&mut self) -> Result { self.parse_config(Self::ENV_VAR, Self::CONFIG_FILE) } /// Configures the policy according to the configuration data. pub fn parse_bytes(&mut self, config: B) -> Result<()> where B: AsRef<[u8]> { let config = config.as_ref(); let config = std::str::from_utf8(config)?.parse::()?; let config = if let Value::Table(config) = config { config } else { return Err(Error::ParseError( "Expected toml sections".into()).into()); }; check_sections( None, &config, &[ "hash_algorithms", "asymmetric_algorithms", "symmetric_algorithms", "aead_algorithms", "packets", ])?; macro_rules! doit { (// The name of the section (&str). $section_name:literal, // HASH_ALGO_MAP, etc. $algo_map:ident, // A callback that consumes the values: // // fn set(default: Option, // properties: Vec<(AlgorithmId, Timestamp)>) $set:expr, // An iterator over all of the variants. If the // default_disposition key is set, $set is called once // for each variant. $variants:expr) => { if let Some(section) = config.get($section_name) { let section = if let Value::Table(section) = section { section } else { return Err(Error::ParseError( format!("{} is not a map", $section_name)).into()); }; let mut keys = $algo_map.into_iter() .map(|(k, _, _)| k) .collect::>(); keys.push("default_disposition"); check_sections( Some($section_name), §ion, &keys[..])?; // Handle default_disposition first. It is the // default; other settings override it. if let Some(disposition) = section.iter().find_map(|(k, d)| { if k == "default_disposition" { Some(d) } else { None } }) { match disposition.as_str() { // Reject everything by default. Some("never") => { // We assume that all types are a u8. // This is currently the case. for algo in $variants { $set(algo.into(), (Some(NEVER), vec![])); } } _ => { return Err(Error::ParseError(format!( "{}.default_disposition: \ invalid value ({:?}), expected never", $section_name, disposition)).into()); } } } // Iterate over the keys/value pairs. for (k, v) in section { // We already handled "default_disposition" above. if k == "default_disposition" { continue; } // Is the key known? let metadata = $algo_map .into_iter() .find_map(|(key, algo, props)| { if k == key { Some((algo, props)) } else { None } }); let (algo, props) = if let Some((algo, props)) = metadata { (algo, props) } else { // It's unknown, but in "ignore_invalid" // (otherwise check_sections would have // returned an error) so silently skip it. continue; }; // Parse the value. let t: (Option, Vec<(_, Timestamp)>) = match v { Value::Datetime(_) => { let t = Some(parse_time(v)?); (t, vec![]) } Value::String(_) => { let t = Some(parse_time(v)?); (t, vec![]) } Value::Table(m) => { // We got a table. If this key has // properties, then process them. // Otherwise, only look for the // default property. let mut names = props .into_iter() .map(|(name, _id)| name) .cloned() .collect::>(); let ids = props .into_iter() .map(|(_name, id)| id) .cloned() .collect::>(); names.push(&"default_disposition"); check_sections( Some(&format!("{}.{}", $section_name, k)), &m, &names[..])?; let default_disposition = m.get("default_disposition") .map(parse_time).transpose()?; let props: Vec<(_, Timestamp)> = names .into_iter() .zip(ids.into_iter()) .filter_map(|(name, id)| { match m.get(name).map(parse_time) { Some(Ok(t)) => Some(Ok((id, t))), // Parse error. Some(Err(err)) => Some(Err(err)), // property not present. None => None, } }) .collect::>>()?; (default_disposition, props) } v => { return Err(Error::ParseError(format!( "{}.{}: invalid value ({:?}), expected \ a valid timestamp, always, or never", $section_name, k, v)).into()); } }; $set(algo, t); } } } } doit!("hash_algorithms", HASH_ALGO_MAP, |algo: HashAlgorithm, props: (Option, Vec<(HashAlgoSecurity, Timestamp)>)| { let (default_disposition, props) = props; if let Some(default_disposition) = default_disposition { self.policy.reject_hash_property_at( algo, HashAlgoSecurity::SecondPreImageResistance, default_disposition); self.policy.reject_hash_property_at( algo, HashAlgoSecurity::CollisionResistance, default_disposition); } for (id, value) in props { self.policy.reject_hash_property_at( algo, id, value); } }, HashAlgorithm::variants()); doit!("asymmetric_algorithms", ASYMM_ALGO_MAP, |algo: AsymmetricAlgorithm, props: (Option, Vec<((), Timestamp)>)| { // No algorithm has any properties beyond the // default property. assert!(props.1.is_empty()); if let Some(t) = props.0 { self.policy.reject_asymmetric_algo_at( algo, t); } }, AsymmetricAlgorithm::variants()); doit!("symmetric_algorithms", SYMM_ALGO_MAP, |algo: SymmetricAlgorithm, props: (Option, Vec<((), Timestamp)>)| { // No algorithm has any properties beyond the // default property. assert!(props.1.is_empty()); if let Some(t) = props.0 { self.policy.reject_symmetric_algo_at( algo, t); } }, SymmetricAlgorithm::variants()); doit!("aead_algorithms", AEAD_ALGO_MAP, |algo: AEADAlgorithm, props: (Option, Vec<((), Timestamp)>)| { // No algorithm has any properties beyond the // default property. assert!(props.1.is_empty()); if let Some(t) = props.0 { self.policy.reject_aead_algo_at( algo, t); } }, AEADAlgorithm::variants()); doit!("packets", PACKET_MAP, |algo: Tag, props: (Option, Vec<(u8, Timestamp)>)| { if let Some(default_disposition) = props.0 { self.policy.reject_packet_tag_at( algo, default_disposition); } for (version, t) in props.1 { self.policy.reject_packet_tag_version_at( algo, version, t); } }, Tag::variants()); Ok(()) } /// Returns the configured policy. pub fn build(self) -> StandardPolicy<'a> { self.policy } } #[cfg(test)] mod tests { use super::*; use std::time::Duration; use openpgp::Cert; use openpgp::parse::Parse; use quickcheck::{Arbitrary, Gen}; fn arbitrary_cutoff(g: &mut Gen) -> Option { match g.choose(&[0, 1, 2]).unwrap() { 0 => None, 1 => Some(UNIX_EPOCH), 2 => Some(SystemTime::arbitrary(g)), _ => unreachable!(), } } impl Arbitrary for ConfiguredStandardPolicy<'static> { fn arbitrary(g: &mut Gen) -> Self { let mut p = ConfiguredStandardPolicy::default(); for _ in 0..u8::arbitrary(g) { let cutoff = arbitrary_cutoff(g); match g.choose(&[0, 1, 2, 3, 4]).unwrap() { // Hash algorithms. 0 => { let variants = HashAlgorithm::variants().collect::>(); let a = g.choose(&variants).unwrap(); if let Some(sec) = g.choose(&[ Some(HashAlgoSecurity::SecondPreImageResistance), Some(HashAlgoSecurity::CollisionResistance), None, ]).unwrap() { p.policy.reject_hash_property_at(*a, *sec, cutoff); } else { p.policy.reject_hash_at(*a, cutoff); } }, // Symmetric algorithms. 1 => { let variants = SymmetricAlgorithm::variants().collect::>(); let a = g.choose(&variants).unwrap(); p.policy.reject_symmetric_algo_at(*a, cutoff); }, // Asymmetric algorithms. 2 => { let variants = AsymmetricAlgorithm::variants().collect::>(); let a = g.choose(&variants).unwrap(); p.policy.reject_asymmetric_algo_at(*a, cutoff); }, // AEAD algorithms. 3 => { let variants = AEADAlgorithm::variants().collect::>(); let a = g.choose(&variants).unwrap(); p.policy.reject_aead_algo_at(*a, cutoff); }, // Packets. 4 => { let variants = Tag::variants().collect::>(); let t = g.choose(&variants).unwrap(); if versioned_packet(*t) && bool::arbitrary(g) { let versions = PACKET_MAP.iter().find(|e| e.1 == *t).unwrap() .2.iter().map(|(_, v)| *v) .collect::>(); let v = g.choose(&versions).unwrap(); p.policy.reject_packet_tag_version_at( *t, *v, cutoff); } else { p.policy.reject_packet_tag_at(*t, cutoff); } }, _ => unreachable!(), } } p } } // Check that invalid sections cause an error, unless they are // explicitly ignored. #[test] fn invalid_section() -> Result<()> { let mut p = ConfiguredStandardPolicy::new(); assert!(p.parse_bytes(b"x=1").is_err()); // A known section. p.parse_bytes(b"[hash_algorithms] sha1 = \"always\" ").expect("valid"); // A known section and an unknown section. assert!(p.parse_bytes(b"[hash_algorithms] sha1 = \"always\" [x] blah = 1 ").is_err()); // A known section, and an unknown section that we should // ignore. p.parse_bytes(b"ignore_invalid = \"x\" [hash_algorithms] sha1 = \"always\" [x] blah = 1 ").expect("valid"); p.parse_bytes(b"ignore_invalid = [ \"x\", \"y\" ] [hash_algorithms] sha1 = \"always\" [x] blah = 1 [y] blah = 1 ").expect("valid"); // Section names are case sensitive. assert!(p.parse_bytes(b"[HASH_ALGORITHMS] sha1 = \"never\" ").is_err()); assert!(p.parse_bytes(b"[Hash_Algorithms] sha1 = \"never\" ").is_err()); // Underscores can't be replaced by spaces. assert!(p.parse_bytes(b"[HASH_ALGORITHMS] sha1 = \"never\" ").is_err()); Ok(()) } // Make sure invalid keys cause an error, unless they are ignored. #[test] fn invalid_keys() -> Result<()> { let mut p = ConfiguredStandardPolicy::new(); // A known section with known keys. p.parse_bytes(b"[hash_algorithms] sha1 = \"always\" sha224 = \"never\" ").expect("valid"); // A known section with unknown keys. assert!(p.parse_bytes(b"[hash_algorithms] sha1 = \"always\" sha99 = \"never\" ").is_err()); // A known section with unknown keys, the ignore_invalid // directive is in the wrong place. assert!(p.parse_bytes(b" ignore_invalid = \"SHA99\" [hash_algorithms] sha1 = \"always\" SHA99 = \"never\" ").is_err()); // A known section with unknown, but ignored keys. p.parse_bytes(b"[hash_algorithms] ignore_invalid = \"SHA99\" sha1 = \"always\" SHA99 = \"never\" ").expect("valid"); // A known section with unknown keys, which are incorrectly // ignore (wrong case). assert!(p.parse_bytes(b" [hash_algorithms] ignore_invalid = \"SHA99\" sha1 = \"always\" sha99 = \"never\" ").is_err()); Ok(()) } /// Set one property at a time via a config file. Check that is /// has been set. Where appropriate, check that related /// properties have not been changed relative to the standard /// policy. #[test] fn get_set_one() -> Result<()> { let sp = StandardPolicy::new(); let times: [(&str, Timestamp); 5] = [ ("never", NEVER), ("always", ALWAYS), // $ date -u +%s --date='20200101' // 1577836800 ("2020-01-01T00:00:00Z", Some(UNIX_EPOCH + Duration::new(1577836800, 0))), // $ date -u +%s --date='20500101' // 2524608000 ("2050-01-01T00:00:00+00:00", Some(UNIX_EPOCH + Duration::new(2524608000, 0))), // $ date -u +%s --date='21050101' // 4260211200 ("2105-01-01", Some(UNIX_EPOCH + Duration::new(4260211200, 0))), ]; macro_rules! check_hash { ($id:expr, $algo:expr, $props:expr, $time:expr, $config:expr) => { let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes($config).expect("valid"); let p = p.build(); let all = [ HashAlgoSecurity::CollisionResistance, HashAlgoSecurity::SecondPreImageResistance, ]; for prop in all.iter().cloned() { if $props.contains(&prop) { assert_eq!(p.hash_cutoff(*$algo, prop), $time.1, "algo: {}, t: {}", $id, $time.0); } else { // Check that the other security properties // didn't change. assert_eq!( sp.hash_cutoff(*$algo, prop), p.hash_cutoff(*$algo, prop), "algo: {}, t: {}", $id, $time.0); } } } } for time in times.iter() { for (id, algo, _props) in HASH_ALGO_MAP.iter() { // Second preimage resistance and collision resistance. check_hash!(id, algo, [ HashAlgoSecurity::SecondPreImageResistance, HashAlgoSecurity::CollisionResistance, ], time, format!( "[hash_algorithms] {} = \"{}\" ", id, time.0)); // Second preimage resistance. check_hash!(id, algo, [ HashAlgoSecurity::SecondPreImageResistance ], time, format!( "[hash_algorithms] {}.second_preimage_resistance = \"{}\" ", id, time.0)); // Collision resistance. check_hash!(id, algo, [ HashAlgoSecurity::CollisionResistance ], time, format!( "[hash_algorithms] {}.collision_resistance = \"{}\" ", id, time.0)); // Different ways of naming the key. check_hash!(id, algo, [ HashAlgoSecurity::CollisionResistance ], time, format!( "[hash_algorithms.{}] collision_resistance = \"{}\" ", id, time.0)); check_hash!(id, algo, [ HashAlgoSecurity::CollisionResistance ], time, format!( "hash_algorithms.{}.collision_resistance = \"{}\" ", id, time.0)); } } macro_rules! check { ($id:expr, $algo:expr, $time:expr, $config:expr, $get:ident) => { let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes($config).expect("valid"); let p = p.build(); assert_eq!(p.$get($algo.clone()), $time.1, "algo: {}, t: {}", $id, $time.0); } } for time in times.iter() { for (id, algo, _props) in ASYMM_ALGO_MAP.iter() { check!(id, algo, time, format!( "[asymmetric_algorithms] {} = \"{}\" ", id, time.0), asymmetric_algo_cutoff); } } for time in times.iter() { for (id, algo, _props) in SYMM_ALGO_MAP.iter() { check!(id, algo, time, format!( "[symmetric_algorithms] {} = \"{}\" ", id, time.0), symmetric_algo_cutoff); } } for time in times.iter() { for (id, algo, _props) in AEAD_ALGO_MAP.iter() { check!(id, algo, time, format!( "[aead_algorithms] {} = \"{}\" ", id, time.0), aead_algo_cutoff); } } for time in times.iter() { for (id, algo, props) in PACKET_MAP.into_iter() { // Unversioned. let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes(format!( "[packets] {} = \"{}\" ", id, time.0)).expect("valid"); let p = p.build(); let cutoff = p.packet_tag_version_cutoff(algo, 1); assert_eq!( cutoff, time.1, "algo: {}, t: {}", id, time.0); // Setting the default means it should apply to all // versions, including "unknown" versions. for version in props.into_iter().map(|(_id, v)| v) .chain(std::iter::once(&99)) { assert_eq!( p.packet_tag_version_cutoff(algo.clone(), *version), time.1, "algo: {}, version: {}, t: {}", id, version, time.0); } // Versioned. for (prop, version) in props { let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes(format!( "[packets] {}.{} = \"{}\" ", id, prop, time.0)).expect("valid"); let p = p.build(); assert_eq!( p.packet_tag_version_cutoff( algo.clone(), *version), time.1, "algo: {}, version: {}, t: {}", id, *version, time.0); } } } Ok(()) } // Convenience function for using a timestamp. fn ts(t: &str) -> Option { parse_time(&Value::String(t.into())).expect("valid timestamp") } /// Check a simple config file. #[test] fn simple_config() -> Result<()> { // Something simple. let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes(b"[hash_algorithms] sha1 = \"always\" sha224 = 2017-03-04T13:25:35Z sha512.second_preimage_resistance = \"always\" sha512.collision_resistance = 2050-01-01 [symmetric_algorithms] cast5 = \"always\" aes128 = \"2040-01-01\" aes256 = \"2050-01-01\" ").expect("valid config"); let p = p.build(); assert_eq!( p.hash_cutoff(HashAlgorithm::SHA1, HashAlgoSecurity::SecondPreImageResistance), ts("always")); assert_eq!( p.hash_cutoff(HashAlgorithm::SHA224, HashAlgoSecurity::SecondPreImageResistance), ts("2017-03-04T13:25:35Z")); assert_eq!( p.hash_cutoff(HashAlgorithm::SHA512, HashAlgoSecurity::CollisionResistance), ts("2050-01-01")); assert_eq!( p.hash_cutoff(HashAlgorithm::SHA512, HashAlgoSecurity::SecondPreImageResistance), ts("always")); assert_eq!( p.symmetric_algo_cutoff(SymmetricAlgorithm::CAST5), ts("always")); assert_eq!( p.symmetric_algo_cutoff(SymmetricAlgorithm::AES128), ts("2040-01-01")); assert_eq!( p.symmetric_algo_cutoff(SymmetricAlgorithm::AES256), ts("2050-01-01")); Ok(()) } // Configure everything, then read it out again. #[test] fn all_config() -> Result<()> { let epoch = NaiveDate::from_ymd_opt(2020, 1, 1).expect("valid"); let mut d = epoch; let mut date = || -> String { let s = d.format("%Y-%m-%d"); d = d.succ_opt().expect("valid"); s.to_string() }; let mut config = String::new(); config.push_str("[hash_algorithms]\n"); for (id, algo, _props) in HASH_ALGO_MAP { config.push_str( &format!("{} = {} # {}\n", id, date(), algo)); } config.push_str("\n[asymmetric_algorithms]\n"); for (id, algo, _props) in ASYMM_ALGO_MAP { config.push_str( &format!("{} = {} # {}\n", id, date(), algo)); } config.push_str("\n[symmetric_algorithms]\n"); for (id, algo, _props) in SYMM_ALGO_MAP { config.push_str( &format!("{} = {} # {}\n", id, date(), algo)); } config.push_str("\n[aead_algorithms]\n"); for (id, algo, _props) in AEAD_ALGO_MAP { config.push_str( &format!("{} = {} # {}\n", id, date(), algo)); } config.push_str("\n[packets]\n"); for (id, algo, _props) in PACKET_MAP { config.push_str( &format!("{} = {} # {}\n", id, date(), algo)); } let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes(config.clone()) .expect(&format!("valid config: {}", config)); let p = p.build(); // Reset the date to the epoch. let mut d = epoch; let mut date = || -> String { let s = d.format("%Y-%m-%d"); d = d.succ_opt().expect("valid"); s.to_string() }; // Make sure everything we set was really set. for (_, algo, _) in HASH_ALGO_MAP { assert_eq!( p.hash_cutoff(algo, HashAlgoSecurity::SecondPreImageResistance), ts(&date())); } for (_, algo, _) in ASYMM_ALGO_MAP { assert_eq!(p.asymmetric_algo_cutoff(algo), ts(&date())); } for (_, algo, _) in SYMM_ALGO_MAP { assert_eq!(p.symmetric_algo_cutoff(algo), ts(&date())); } for (_, algo, _) in AEAD_ALGO_MAP { assert_eq!(p.aead_algo_cutoff(algo), ts(&date())); } for (_, algo, props) in PACKET_MAP { let d = ts(&date()); let cutoff = p.packet_tag_version_cutoff(algo, 1); assert_eq!(cutoff, d); // Setting the default means it should apply to all // versions, including "unknown" versions. for version in props.into_iter().map(|(_id, v)| v) .chain(std::iter::once(&99)) { assert_eq!( p.packet_tag_version_cutoff(algo, *version), d, "algo: {}, version: {}, t: {:?}", algo, version, d); } } Ok(()) } #[test] fn check_sig() -> Result<()> { let alice = testdata::file("alice-secret.asc"); let cert = Cert::from_bytes(alice).expect("valid cert"); let now = ts("2022-10-30").unwrap(); // Should be valid according to the standard policy. let sp = &StandardPolicy::at(now); let _cert = cert.with_policy(sp, None).expect("valid under standard policy"); // If SHA512 has no collision resistance, the cert is still valid. let mut p = ConfiguredStandardPolicy::at(now); p.parse_bytes(b"[hash_algorithms] sha512.collision_resistance = \"never\"") .expect(&format!("valid config")); let p = p.build(); let _cert = cert.with_policy(&p, None) .expect("valid under: SHA512 has no collision resistance"); // If SHA512 has no 2nd preimage resistance, the cert is not valid. let mut p = ConfiguredStandardPolicy::at(now); p.parse_bytes(b"[hash_algorithms] sha512.second_preimage_resistance = \"never\"") .expect(&format!("valid config")); let p = p.build(); let _cert = assert!( cert.with_policy(&p, None).is_err(), "invalid under: SHA512 has no 2nd preimage resistance"); // If SHA512's 2nd preimage resistance is cutoff, it should be // invalid. let mut p = ConfiguredStandardPolicy::at(now); p.parse_bytes(&format!("[hash_algorithms] sha512.second_preimage_resistance = 2022-10-24")) .expect(&format!("valid config")); let p = p.build(); let _cert = assert!( cert.with_policy(&p, None).is_err(), "should be invalid under: SHA512 2nd preimage resistance cut off"); // If SHA512's 2nd preimage resistance will be cutoff, it // should be invalid. let mut p = ConfiguredStandardPolicy::at(ts("2022-10-26").unwrap()); p.parse_bytes(&format!("[hash_algorithms] sha512.second_preimage_resistance = 2022-10-27")) .expect(&format!("valid config")); let p = p.build(); let _cert = cert.with_policy(&p, ts("2022-10-26")).expect( "valid under: SHA512 2nd preimage resistance not yet cut off"); let mut p = ConfiguredStandardPolicy::at(ts("2022-10-29").unwrap()); p.parse_bytes(&format!("[hash_algorithms] sha512.second_preimage_resistance = 2022-10-27")) .expect(&format!("valid config")); let p = p.build(); let _cert = assert!( cert.with_policy(&p, ts("2022-10-28")).is_err(), "invalid under: SHA512 2nd preimage resistance cut off"); Ok(()) } #[test] fn default_disposition() -> Result<()> { // Reject everything. let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes(b"[hash_algorithms] default_disposition = \"never\" [asymmetric_algorithms] default_disposition = \"never\" [symmetric_algorithms] default_disposition = \"never\" [aead_algorithms] default_disposition = \"never\" [packets] default_disposition = \"never\" ").expect("valid config"); let p = p.build(); for algo in 0..=u8::MAX { assert_eq!( p.hash_cutoff(algo.into(), HashAlgoSecurity::CollisionResistance), Some(UNIX_EPOCH)); assert_eq!( p.hash_cutoff(algo.into(), HashAlgoSecurity::SecondPreImageResistance), Some(UNIX_EPOCH)); } for (_, algo, _) in ASYMM_ALGO_MAP { assert_eq!(p.asymmetric_algo_cutoff(algo), Some(UNIX_EPOCH)); } for algo in 0..=u8::MAX { assert_eq!(p.symmetric_algo_cutoff(algo.into()), Some(UNIX_EPOCH)); } for algo in 0..=u8::MAX { assert_eq!(p.aead_algo_cutoff(algo.into()), Some(UNIX_EPOCH)); } for algo in 0..=u8::MAX { let cutoff = p.packet_tag_version_cutoff(algo.into(), 1); assert_eq!(cutoff, Some(UNIX_EPOCH)); } for (_, algo, props) in PACKET_MAP { // Setting the default means it should apply to all // versions, including "unknown" versions. for version in props.into_iter().map(|(_id, v)| v) .chain(std::iter::once(&99)) { assert_eq!( p.packet_tag_version_cutoff(algo, *version), Some(UNIX_EPOCH), "algo: {}, version: {}", algo, version); } } let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes(b"[hash_algorithms] sha512.collision_resistance = 2050-01-01 default_disposition = \"never\" [asymmetric_algorithms] rsa1024 = \"always\" default_disposition = \"never\" [symmetric_algorithms] aes128 = \"2040-01-01\" aes256 = \"2050-01-01\" default_disposition = \"never\" [aead_algorithms] ocb = \"always\" default_disposition = \"never\" [packets] seip = \"always\" default_disposition = \"never\" ").expect("valid config"); let p = p.build(); for algo in 0..=u8::MAX { let algo: HashAlgorithm = algo.into(); if algo == HashAlgorithm::SHA512 { assert_eq!( p.hash_cutoff(algo.into(), HashAlgoSecurity::CollisionResistance), ts("2050-01-01")); } else { assert_eq!( p.hash_cutoff(algo, HashAlgoSecurity::CollisionResistance), NEVER); } assert_eq!( p.hash_cutoff(algo.into(), HashAlgoSecurity::SecondPreImageResistance), NEVER); } for (_, algo, _) in ASYMM_ALGO_MAP { match algo { AsymmetricAlgorithm::RSA1024 => assert_eq!(p.asymmetric_algo_cutoff(algo), ALWAYS), algo => assert_eq!(p.asymmetric_algo_cutoff(algo), NEVER), } } for algo in 0..=u8::MAX { let algo = SymmetricAlgorithm::from(algo); match algo { SymmetricAlgorithm::AES128 => assert_eq!(p.symmetric_algo_cutoff(algo), ts("2040-01-01")), SymmetricAlgorithm::AES256 => assert_eq!(p.symmetric_algo_cutoff(algo), ts("2050-01-01")), algo => assert_eq!(p.symmetric_algo_cutoff(algo), NEVER), } } for algo in 0..=u8::MAX { let algo = AEADAlgorithm::from(algo); if algo == AEADAlgorithm::OCB { assert_eq!(p.aead_algo_cutoff(algo), ALWAYS); } else { assert_eq!(p.aead_algo_cutoff(algo), NEVER); } } for algo in 0..=u8::MAX { let algo = Tag::from(algo); let cutoff = p.packet_tag_version_cutoff(algo.into(), 1); if algo == Tag::SEIP { assert_eq!(cutoff, ALWAYS); } else { assert_eq!(cutoff, NEVER); } } for (_, algo, props) in PACKET_MAP { // Setting the default means it should apply to all // versions, including "unknown" versions. for version in props.into_iter().map(|(_id, v)| v) .chain(std::iter::once(&99)) { let cutoff = p.packet_tag_version_cutoff(algo, *version); if algo == Tag::SEIP { assert_eq!(cutoff, ALWAYS); } else { assert_eq!(cutoff, NEVER); } } } Ok(()) } // A string type can also be written as `key.default_disposition`. #[test] fn default_key() -> Result<()> { // Make sure `key` is the same as `key.default_disposition`: let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes(b"[hash_algorithms] sha512.default_disposition = 2050-01-01 [symmetric_algorithms] aes128.default_disposition = \"2040-01-01\" [aead_algorithms] ocb.default_disposition = \"always\" default_disposition = \"never\" ").expect("valid config"); let p = p.build(); assert_eq!( p.hash_cutoff(HashAlgorithm::SHA512, HashAlgoSecurity::CollisionResistance), ts("2050-01-01")); assert_eq!( p.hash_cutoff(HashAlgorithm::SHA512, HashAlgoSecurity::SecondPreImageResistance), ts("2050-01-01")); assert_eq!( p.symmetric_algo_cutoff(SymmetricAlgorithm::AES128), ts("2040-01-01")); assert_eq!( p.aead_algo_cutoff(AEADAlgorithm::OCB), ts("always")); assert_eq!( p.aead_algo_cutoff(AEADAlgorithm::EAX), ts("never")); // `key` is a string type, which is used as a map with an // invalid key. let mut p = ConfiguredStandardPolicy::new(); assert!(p.parse_bytes(b"[symmetric_algorithms] aes128.foo = \"2040-01-01\"").is_err()); // `key` is a string type, which is used as a map with an // invalid key, which is in ignore_invalid. let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes(b"[symmetric_algorithms] aes256.foo = \"2040-01-01\" aes256.ignore_invalid = \"foo\" ").expect("valid config"); let p = p.build(); let sp = &StandardPolicy::new(); assert_eq!( p.symmetric_algo_cutoff(SymmetricAlgorithm::AES256), sp.symmetric_algo_cutoff(SymmetricAlgorithm::AES256)); // `key` is a string type, which is used as a map with an // invalid key, which is in ignore_invalid, and there is a // default_disposition key. let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes(b"[symmetric_algorithms] aes256.foo = \"2040-01-01\" aes256.default_disposition = \"2050-01-01\" aes256.ignore_invalid = \"foo\" ").expect("valid config"); let p = p.build(); assert_eq!( p.symmetric_algo_cutoff(SymmetricAlgorithm::AES256), ts("2050-01-01")); Ok(()) } #[test] fn unversioned_packets() -> Result<()> { let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes(b"[packets] signature = 2030-01-01 ").expect("valid config"); let p = p.build(); assert_eq!( p.packet_tag_version_cutoff(Tag::Signature, 3), ts("2030-01-01")); assert_eq!( p.packet_tag_version_cutoff(Tag::Signature, 4), ts("2030-01-01")); Ok(()) } #[test] fn versioned_packets() -> Result<()> { let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes(b"[packets] signature.v3 = 2010-01-01 signature.v4 = 2030-01-01 ").expect("valid config"); let p = p.build(); assert_eq!( p.packet_tag_version_cutoff(Tag::Signature, 3), ts("2010-01-01")); assert_eq!( p.packet_tag_version_cutoff(Tag::Signature, 4), ts("2030-01-01")); let mut p = ConfiguredStandardPolicy::new(); assert!(p.parse_bytes(b"[packets] signature.v9 = 2010-01-01 signature.v4 = 2030-01-01 ").is_err()); let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes(b"[packets] signature.v3 = 2010-01-01 signature.v9 = 2010-01-01 signature.ignore_invalid = \"v9\" signature.v4 = 2030-01-01 ").expect("valid config"); let p = p.build(); assert_eq!( p.packet_tag_version_cutoff(Tag::Signature, 3), ts("2010-01-01")); assert_eq!( p.packet_tag_version_cutoff(Tag::Signature, 4), ts("2030-01-01")); // Use a default and override it for one version where the // override is less than the default. let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes(b"[packets] signature.default_disposition = 2030-01-01 signature.v3 = 2010-01-01 ").expect("valid config"); let p = p.build(); assert_eq!( p.packet_tag_version_cutoff(Tag::Signature, 3), ts("2010-01-01")); assert_eq!( p.packet_tag_version_cutoff(Tag::Signature, 4), ts("2030-01-01")); // Use a default and override it for one version where the // override is greater than the default. let mut p = ConfiguredStandardPolicy::new(); p.parse_bytes(b"[packets] signature.default_disposition = 2030-01-01 signature.v3 = 2040-01-01 ").expect("valid config"); let p = p.build(); assert_eq!( p.packet_tag_version_cutoff(Tag::Signature, 3), ts("2040-01-01")); assert_eq!( p.packet_tag_version_cutoff(Tag::Signature, 4), ts("2030-01-01")); Ok(()) } } sequoia-policy-config-0.8.0/src/testdata.rs000064400000000000000000000023271046102023000170110ustar 00000000000000//! Test data. //! //! This module includes the test data from `tests/data` in a //! structured way. use std::fmt; use std::collections::BTreeMap; use std::path::PathBuf; use std::sync::OnceLock; pub struct Test { path: &'static str, pub bytes: &'static [u8], } impl fmt::Display for Test { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "tests/data/{}", self.path) } } #[allow(unused)] pub fn dir() -> PathBuf { PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/data")) } /// Returns the content of the given file below `keystore/tests/data`. pub fn file(name: &str) -> &'static [u8] { static FILES: OnceLock> = OnceLock::new(); FILES.get_or_init(|| { let mut m: BTreeMap<&'static str, &'static [u8]> = Default::default(); macro_rules! add { ( $key: expr, $path: expr ) => { m.insert($key, include_bytes!($path)) } } include!(concat!(env!("OUT_DIR"), "/tests.index.rs.inc")); // Sanity checks. assert!(m.contains_key("alice-secret.asc")); m }).get(name).unwrap_or_else(|| panic!("No such file {:?}", name)) } sequoia-policy-config-0.8.0/tests/check.rs000064400000000000000000000044751046102023000166360ustar 00000000000000use std::env::current_dir; use sequoia_openpgp as openpgp; use openpgp::Result; use assert_cmd::Command; // use predicates::prelude::*; #[test] fn bad_config() -> Result<()> { let cwd = current_dir()?; println!("The current directory is {}", cwd.display()); let mut cmd = Command::cargo_bin("sequoia-policy-config-check")?; let assert = cmd.arg("tests/config/bad.toml") .assert(); assert.failure(); Ok(()) } #[test] fn good_config() -> Result<()> { let cwd = current_dir()?; println!("The current directory is {}", cwd.display()); let mut cmd = Command::cargo_bin("sequoia-policy-config-check")?; let assert = cmd.arg("tests/config/good.toml") .assert(); assert.success(); Ok(()) } #[test] fn bad_config_env() -> Result<()> { let cwd = current_dir()?; println!("The current directory is {}", cwd.display()); let mut cmd = Command::cargo_bin("sequoia-policy-config-check")?; let cmd = cmd.env( "SEQUOIA_CRYPTO_POLICY", format!("{}/{}", cwd.display(), "tests/config/bad.toml")); let assert = cmd.assert(); assert.failure(); Ok(()) } #[test] fn good_config_env() -> Result<()> { let cwd = current_dir()?; println!("The current directory is {}", cwd.display()); let mut cmd = Command::cargo_bin("sequoia-policy-config-check")?; let cmd = cmd.env( "SEQUOIA_CRYPTO_POLICY", format!("{}/{}", cwd.display(), "tests/config/good.toml")); let assert = cmd.assert(); assert.success(); Ok(()) } // If no configuration is supplied, this will just read the default // configuration. #[test] fn no_args() -> Result<()> { let mut cmd = Command::cargo_bin("sequoia-policy-config-check")?; // Make the default config a no-op in case the system // configuration is bad. cmd.env("SEQUOIA_CRYPTO_POLICY", ""); let assert = cmd.assert(); assert.success(); Ok(()) } // Using a relative path in "SEQUOIA_CRYPTO_POLICY" is not allowed. #[test] fn relative_path_env() -> Result<()> { let cwd = current_dir()?; println!("The current directory is {}", cwd.display()); let mut cmd = Command::cargo_bin("sequoia-policy-config-check")?; let cmd = cmd.env( "SEQUOIA_CRYPTO_POLICY", "tests/config/good.toml"); let assert = cmd.assert(); assert.failure(); Ok(()) } sequoia-policy-config-0.8.0/tests/config/bad.toml000064400000000000000000000000621046102023000200670ustar 00000000000000[hash_algorithms] sha1 = "always" sha42 = "never" sequoia-policy-config-0.8.0/tests/config/good.toml000064400000000000000000000000421046102023000202670ustar 00000000000000[hash_algorithms] sha1 = "always" sequoia-policy-config-0.8.0/tests/data/alice-secret.asc000064400000000000000000000016041046102023000211430ustar 00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- Comment: 7B2D 9926 16FD 0088 6A83 1154 2F92 5915 9C83 38C1 Comment: Alice xVgEY1aAWBYJKwYBBAHaRw8BAQdA9Tg2NuCoA8QYEvjBuGLzxXPNzd7PyKg2vG5b JJkBlGMAAQDYOh3Is4tC0g7iMWkC7Gf0y2h94VB9UmgBRwJqC1Qc2Q49wsALBB8W CgB9BYJjVoBYAwsJBwkQL5JZFZyDOMFHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMu c2VxdW9pYS1wZ3Aub3JnxH3auv+f9ubvVPzyAH+B8afVL+Rf4pARSJGVpAZaQowD FQoIApsBAh4BFiEEey2ZJhb9AIhqgxFUL5JZFZyDOMEAAAP3AQDwX256ji+JmFmw K0gJqYk5Su6QsHGjaHniplTjiEsaBQEA8n9FB7Sj465lwJ91WuToXAKkedpNoP86 K1PYxWmNswPNGUFsaWNlIDxhbGljZUBleGFtcGxlLm9yZz7CwA4EExYKAIAFgmNW gFgDCwkHCRAvklkVnIM4wUcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lh LXBncC5vcmc6yIpZHwZmJ/qczU/BFHlentZHUM9rdHUUO+4aQ/uC4AMVCggCmQEC mwECHgEWIQR7LZkmFv0AiGqDEVQvklkVnIM4wQAA0RUA/08n5I8nU6lLHPZU7QNO 0Cx0dhnPXliCVmNvorXPYd8BAP9cMGitLMXgT4CIKDSfhh3h/Z4TgOt0rEvtU8JG Fb3RCw== =loDe -----END PGP PRIVATE KEY BLOCK-----