rpgpie-sop-0.6.4/.cargo_vcs_info.json0000644000000001500000000000100131240ustar { "git": { "sha1": "a94a56a48512aa137d8c23eba464310dc59d60dd" }, "path_in_vcs": "rpgpie-sop" }rpgpie-sop-0.6.4/Cargo.lock0000644000001243370000000000100111150ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "adler2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aead" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", "generic-array", ] [[package]] name = "aes" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", "cpufeatures", ] [[package]] name = "aes-gcm" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", "cipher", "ctr", "ghash", "subtle", ] [[package]] name = "aes-kw" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69fa2b352dcefb5f7f3a5fb840e02665d311d878955380515e4fd50095dd3d8c" dependencies = [ "aes", ] [[package]] name = "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 = "argon2" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" dependencies = [ "base64ct", "blake2", "cpufeatures", "password-hash", "zeroize", ] [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bitfield" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f798d2d157e547aa99aab0967df39edd0b70307312b6f8bd2848e6abe40896e0" [[package]] name = "blake2" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" dependencies = [ "digest", ] [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "block-padding" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ "generic-array", ] [[package]] name = "blowfish" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" dependencies = [ "byteorder", "cipher", ] [[package]] name = "bstr" version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", "serde", ] [[package]] name = "buffer-redux" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e8acf87c5b9f5897cd3ebb9a327f420e0cae9dd4e5c1d2e36f2c84c571a58f1" dependencies = [ "memchr", ] [[package]] name = "bumpalo" version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "camellia" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3264e2574e9ef2b53ce6f536dea83a69ac0bc600b762d1523ff83fe07230ce30" dependencies = [ "byteorder", "cipher", ] [[package]] name = "cast5" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b07d673db1ccf000e90f54b819db9e75a8348d6eb056e9b8ab53231b7a9911" dependencies = [ "cipher", ] [[package]] name = "cc" version = "1.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" dependencies = [ "shlex", ] [[package]] name = "cfb-mode" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "738b8d467867f80a71351933f70461f5b56f24d5c93e0cf216e59229c968d330" dependencies = [ "cipher", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", "windows-targets", ] [[package]] name = "cipher" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", ] [[package]] name = "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 = "crc24" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd121741cf3eb82c08dd3023eb55bf2665e5f60ec20f89760cf836ae4562e6a0" [[package]] name = "crc32fast" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crypto-bigint" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", "rand_core 0.6.4", "subtle", "zeroize", ] [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "rand_core 0.6.4", "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 0.2.9", "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 = "darling" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", ] [[package]] name = "darling_core" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", "syn", ] [[package]] name = "darling_macro" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", "syn", ] [[package]] name = "dbl" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd2735a791158376708f9347fe8faba9667589d82427ef3aed6794a8981de3d9" dependencies = [ "generic-array", ] [[package]] name = "der" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "pem-rfc7468", "zeroize", ] [[package]] name = "derive_builder" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ "darling", "proc-macro2", "quote", "syn", ] [[package]] name = "derive_builder_macro" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", "syn", ] [[package]] name = "derive_more" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", "syn", "unicode-xid", ] [[package]] name = "des" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" dependencies = [ "cipher", ] [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "const-oid", "crypto-common", "subtle", ] [[package]] name = "dsa" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" dependencies = [ "digest", "num-bigint-dig", "num-traits", "pkcs8", "rfc6979", "sha2", "signature", "zeroize", ] [[package]] name = "eax" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9954fabd903b82b9d7a68f65f97dc96dd9ad368e40ccc907a7c19d53e6bfac28" dependencies = [ "aead", "cipher", "cmac", "ctr", "subtle", ] [[package]] name = "ecdsa" version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", "digest", "elliptic-curve", "rfc6979", "signature", "spki", ] [[package]] name = "ed25519" version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8", "signature", ] [[package]] name = "ed25519-dalek" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", "serde", "sha2", "subtle", "zeroize", ] [[package]] name = "ed448-goldilocks" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87b5fa9e9e3dd5fe1369f380acd3dcdfa766dbd0a1cd5b048fb40e38a6a78e79" dependencies = [ "fiat-crypto 0.1.20", "hex", "subtle", ] [[package]] name = "elliptic-curve" version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", "digest", "ff", "generic-array", "group", "hkdf", "pem-rfc7468", "pkcs8", "rand_core 0.6.4", "sec1", "subtle", "zeroize", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "ff" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ "rand_core 0.6.4", "subtle", ] [[package]] name = "fiat-crypto" version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" [[package]] name = "fiat-crypto" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "flate2" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", "zeroize", ] [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", "wasi", ] [[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 = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", "rand_core 0.6.4", "subtle", ] [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "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 = "idea" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "075557004419d7f2031b8bb7f44bb43e55a83ca7b63076a8fb8fe75753836477" dependencies = [ "cipher", ] [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "inout" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ "generic-array", ] [[package]] name = "iter-read" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071ed4cc1afd86650602c7b11aa2e1ce30762a1c27193201cb5cee9c6ebb1294" [[package]] name = "itoa" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "k256" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", "once_cell", "sha2", "signature", ] [[package]] name = "keccak" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] [[package]] name = "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 = "libm" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "log" version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "md-5" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", "digest", ] [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memsec" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c797b9d6bb23aab2fc369c65f871be49214f5c759af65bde26ffaaa2b646b492" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" dependencies = [ "adler2", ] [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[package]] name = "num-bigint-dig" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" dependencies = [ "byteorder", "lazy_static", "libm", "num-integer", "num-iter", "num-traits", "rand", "serde", "smallvec", "zeroize", ] [[package]] name = "num-integer" version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] [[package]] name = "num-iter" version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", ] [[package]] name = "num_enum" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "syn", ] [[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 = "p256" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ "ecdsa", "elliptic-curve", "primeorder", "sha2", ] [[package]] name = "p384" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" dependencies = [ "ecdsa", "elliptic-curve", "primeorder", "sha2", ] [[package]] name = "p521" version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" dependencies = [ "base16ct", "ecdsa", "elliptic-curve", "primeorder", "rand_core 0.6.4", "sha2", ] [[package]] name = "password-hash" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" dependencies = [ "base64ct", "rand_core 0.6.4", "subtle", ] [[package]] name = "pem-rfc7468" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" dependencies = [ "base64ct", ] [[package]] name = "pgp" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30249ac8a98b356b473b04bc5358c75a260aa96a295d0743ce752fe7b173f235" dependencies = [ "aes", "aes-gcm", "aes-kw", "argon2", "base64", "bitfield", "block-padding", "blowfish", "bstr", "buffer-redux", "byteorder", "camellia", "cast5", "cfb-mode", "chrono", "cipher", "const-oid", "crc24", "curve25519-dalek", "derive_builder", "derive_more", "des", "digest", "dsa", "eax", "ecdsa", "ed25519-dalek", "elliptic-curve", "flate2", "generic-array", "hex", "hkdf", "idea", "iter-read", "k256", "log", "md-5", "nom", "num-bigint-dig", "num-traits", "num_enum", "ocb3", "p256", "p384", "p521", "rand", "ripemd", "rsa", "sha1", "sha1-checked", "sha2", "sha3", "signature", "smallvec", "thiserror 2.0.11", "twofish", "x25519-dalek", "x448", "zeroize", ] [[package]] name = "pkcs1" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ "der", "pkcs8", "spki", ] [[package]] name = "pkcs8" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", "spki", ] [[package]] name = "polyval" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", "opaque-debug", "universal-hash", ] [[package]] name = "ppv-lite86" version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ "zerocopy", ] [[package]] name = "primeorder" version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ "elliptic-curve", ] [[package]] name = "proc-macro-crate" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core 0.6.4", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core 0.6.4", ] [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "rfc6979" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ "hmac", "subtle", ] [[package]] name = "ripemd" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ "digest", ] [[package]] name = "rpgpie" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e362f8e31f5d220605de465458116af58cab01e36c2a551688303db8102692b" dependencies = [ "chrono", "log", "pgp", "rand", "rand_core 0.6.4", "thiserror 2.0.11", "zeroize", ] [[package]] name = "rpgpie-sop" version = "0.6.4" dependencies = [ "chrono", "hex", "log", "pgp", "rand", "rand_core 0.6.4", "rpgpie", "sop", ] [[package]] name = "rsa" version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" dependencies = [ "const-oid", "digest", "num-bigint-dig", "num-integer", "num-traits", "pkcs1", "pkcs8", "rand_core 0.6.4", "signature", "spki", "subtle", "zeroize", ] [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustversion" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", "generic-array", "pkcs8", "subtle", "zeroize", ] [[package]] name = "semver" version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "sha1-checked" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89f599ac0c323ebb1c6082821a54962b839832b03984598375bff3975b804423" dependencies = [ "digest", "sha1", "zeroize", ] [[package]] name = "sha2" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "sha3" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ "digest", "keccak", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signature" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", "rand_core 0.6.4", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "sop" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "178bb001c144c5b83c9420f5ac1c4acc3b6e13b2f40914d150dddf54babf4714" dependencies = [ "memsec", "serde", "serde_json", "thiserror 1.0.69", ] [[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 = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ "thiserror-impl 2.0.11", ] [[package]] name = "thiserror-impl" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "thiserror-impl" version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", "toml_datetime", "winnow", ] [[package]] name = "twofish" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a78e83a30223c757c3947cd144a31014ff04298d8719ae10d03c31c0448c8013" dependencies = [ "cipher", ] [[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "universal-hash" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ "crypto-common", "subtle", ] [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[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 = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" dependencies = [ "memchr", ] [[package]] name = "x25519-dalek" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek", "rand_core 0.6.4", "serde", "zeroize", ] [[package]] name = "x448" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4cd07d4fae29e07089dbcacf7077cd52dce7760125ca9a4dd5a35ca603ffebb" dependencies = [ "ed448-goldilocks", "hex", "rand_core 0.5.1", ] [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", "syn", ] rpgpie-sop-0.6.4/Cargo.toml0000644000000024300000000000100111250ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "rpgpie-sop" version = "0.6.4" authors = ["Heiko Schaefer "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Experimental SOP implementation based on rpgpie" readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://codeberg.org/heiko/rsop" [lib] name = "rpgpie_sop" path = "src/lib.rs" [dependencies.chrono] version = "0.4" [dependencies.hex] version = "0.4" [dependencies.log] version = "0.4.22" [dependencies.pgp] version = "0.15" [dependencies.rand] version = "0.8" [dependencies.rand_core] version = "0.6" [dependencies.rpgpie] version = "0.5.4" [dependencies.sop] version = "0.8.1" [features] unstable-curve448 = [ "pgp/unstable-curve448", "rpgpie/unstable-curve448", ] rpgpie-sop-0.6.4/Cargo.toml.orig000064400000000000000000000011601046102023000146050ustar 00000000000000# SPDX-FileCopyrightText: Heiko Schaefer # SPDX-License-Identifier: CC0-1.0 [package] name = "rpgpie-sop" description = "Experimental SOP implementation based on rpgpie" license = "MIT OR Apache-2.0" version = "0.6.4" authors = ["Heiko Schaefer "] edition = "2021" repository = "https://codeberg.org/heiko/rsop" [lib] [dependencies] chrono = "0.4" hex = "0.4" log = "0.4.22" pgp = "0.15" rand = "0.8" rand_core = "0.6" rpgpie = "0.5.4" sop = "0.8.1" [features] # Enables x448 support in rpgp and rpgpie unstable-curve448 = ["pgp/unstable-curve448", "rpgpie/unstable-curve448"] rpgpie-sop-0.6.4/README.md000064400000000000000000000012111046102023000131720ustar 00000000000000 # rpgpie-sop `rpgpie-sop` is a very thin wrapper on top of [rpgpie](https://crates.io/crates/rpgpie). `rpgpie-sop` implements the excellent ["sop" Rust interface](https://crates.io/crates/sop) and is used to build the [rsop](https://crates.io/crates/rsop) CLI tool. The foundation of `rpgpie-sop` consists of: - [rpgp](https://github.com/rpgp/rpgp/), a production-grade implementation of low-level OpenPGP functionality. - [rpgpie 🦀️🔐🥧](https://crates.io/crates/rpgpie), an experimental higher level OpenPGP library based on rpgp. rpgpie-sop-0.6.4/src/cmd/armor.rs000064400000000000000000000123111046102023000147360ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 use std::io::{BufRead, BufReader, Read, Write}; use std::ops::DerefMut; use std::sync::Mutex; use pgp::armor::BlockType; use pgp::types::Tag; #[derive(Default)] pub(crate) struct Armor { label: sop::ops::ArmorLabel, } impl Armor { pub(crate) fn new() -> Self { Default::default() } } impl<'a> sop::ops::Armor<'a> for Armor { fn label( mut self: Box, label: sop::ops::ArmorLabel, ) -> Box + 'a> { self.label = label; self } fn data<'d>( self: Box, data: &'d mut (dyn Read + Send + Sync), ) -> sop::Result> where 'a: 'd, { Ok(Box::new(ArmorReady { armor: self, data })) } } struct ArmorReady<'a> { armor: Box, data: &'a mut (dyn Read + Send + Sync), } impl sop::ops::Ready for ArmorReady<'_> { fn to_writer(self: Box, mut sink: &mut (dyn Write + Send + Sync)) -> sop::Result<()> { let mut reader = BufReader::new(self.data); let buf = reader.fill_buf()?; if buf.is_empty() { return Ok(()); } if buf[0] & 0x80 == 0 { // the input don't seem to be binary pgp data -> just pass it through std::io::copy(&mut reader, &mut sink).expect("FIXME"); } else { let label = if self.armor.label == sop::ops::ArmorLabel::Auto { // autodetect type encoded_type_id_to_label(buf[0])? } else { self.armor.label }; let typ = blocktype_try_from(label)?; let input = SerializableBinary { data: Box::new(Mutex::new(&mut reader)), }; // TODO: don't write out checksum for v6 artifacts? pgp::armor::write(&input, typ, &mut sink, None, true).expect("FIXME") } Ok(()) } } /// Glue between a raw reader and [pgp::armor::write] struct SerializableBinary<'a> { data: Box>, } impl pgp::ser::Serialize for SerializableBinary<'_> { fn to_writer(&self, w: &mut W) -> pgp::errors::Result<()> { let mut reader = self.data.lock().unwrap(); std::io::copy(reader.deref_mut(), w).map_err(pgp::errors::Error::from)?; Ok(()) } } // Produce the equivalent pgp::armor::reader::BlockType // // NOTE: Panics for sop::ops::ArmorLabel::Auto fn blocktype_try_from(label: sop::ops::ArmorLabel) -> sop::Result { match label { sop::ops::ArmorLabel::Auto => unimplemented!("this should never happen"), sop::ops::ArmorLabel::Cert => Ok(BlockType::PublicKey), sop::ops::ArmorLabel::Key => Ok(BlockType::PrivateKey), sop::ops::ArmorLabel::Message => Ok(BlockType::Message), sop::ops::ArmorLabel::Sig => Ok(BlockType::Signature), } } // autodetect ArmorLabel from "Encoded Packet Type ID" fn encoded_type_id_to_label(byte: u8) -> sop::Result { let tag = from_encoded_type_id(byte)?; match tag { Tag::SecretKey => Ok(sop::ops::ArmorLabel::Key), Tag::PublicKey => Ok(sop::ops::ArmorLabel::Cert), Tag::PublicKeyEncryptedSessionKey | Tag::SymKeyEncryptedSessionKey => { Ok(sop::ops::ArmorLabel::Message) } Tag::OnePassSignature => Ok(sop::ops::ArmorLabel::Message), Tag::Signature => { // TODO: distinguish 'just a bunch of signatures' from 'old-style signed message': // If the packet stream contains only Signature packets, it should be parsed as a // SIGNATURES input (with Armor Header BEGIN PGP SIGNATURE). // If it contains any packet other than a Signature packet, it should be parsed as // an INLINESIGNED input (with Armor Header BEGIN PGP MESSAGE). Ok(sop::ops::ArmorLabel::Sig) } _ => Err(sop::errors::Error::BadData), } } fn from_encoded_type_id(byte: u8) -> sop::Result { if byte & 0x80 == 0 { return Err(sop::errors::Error::BadData); } match byte & 0x40 { 0 => { // Legacy format: // Bit 7 -- always one // Bit 6 -- always zero // Bits 5 to 2 -- Packet Type ID // Bits 1 to 0 -- length-type let tag = byte.checked_shr(2).expect("2 bits") & 0b00001111; Ok(tag.into()) } _ => { // OpenPGP format: // Bit 7 -- always one // Bit 6 -- always one // Bits 5 to 0 -- Packet Type ID let tag = byte & 0b00111111; Ok(tag.into()) } } } #[test] fn test_from_encoded_type_id() { // OpenPGP format assert_eq!( from_encoded_type_id(0b11000001).ok(), Some(Tag::PublicKeyEncryptedSessionKey) ); // Legacy format assert_eq!( from_encoded_type_id(0b10000100).ok(), Some(Tag::PublicKeyEncryptedSessionKey) ); // Bit 7 is zero -> bad data assert!(matches!( from_encoded_type_id(0b00000100), Err(sop::errors::Error::BadData) )); } rpgpie-sop-0.6.4/src/cmd/dearmor.rs000064400000000000000000000024141046102023000152520ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 use std::io::{BufRead, BufReader, Read, Write}; #[derive(Default)] pub(crate) struct Dearmor {} impl Dearmor { pub(crate) fn new() -> Self { Default::default() } } impl<'a> sop::ops::Dearmor<'a> for Dearmor { fn data<'d>( self: Box, data: &'d mut (dyn Read + Send + Sync), ) -> sop::Result> where 'a: 'd, { Ok(Box::new(DearmorReady { data })) } } struct DearmorReady<'a> { data: &'a mut (dyn Read + Send + Sync), } impl sop::ops::Ready for DearmorReady<'_> { fn to_writer(self: Box, mut sink: &mut (dyn Write + Send + Sync)) -> sop::Result<()> { let mut reader = BufReader::new(self.data); let buf = reader.fill_buf()?; if buf.is_empty() { return Ok(()); } if buf[0] & 0x80 != 0 { // the input seems to be binary data -> just pass it through std::io::copy(&mut reader, &mut sink).expect("FIXME"); } else { let mut dearmor = pgp::armor::Dearmor::new(reader); std::io::copy(&mut dearmor, &mut sink).expect("FIXME"); } Ok(()) } } rpgpie-sop-0.6.4/src/cmd/decrypt.rs000064400000000000000000000140621046102023000152750ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 use std::io; use std::time::SystemTime; use pgp::composed::{Deserializable, Message}; use rpgpie::certificate::Certificate; use rpgpie::message::{PkeskDecryptor, SoftkeyPkeskDecryptor}; use rpgpie::tsk::Tsk; use crate::cmd::verify::Verify; use crate::{util, Certs, Keys, RPGSOP}; #[derive(Default)] pub(crate) struct Decrypt { verify: Verify, session_keys: Vec, decryption_keys: Vec, key_passwords: Vec, // Passwords for asymmetric component key material skesk_passwords: Vec, } impl Decrypt { pub(crate) fn new() -> Self { Default::default() } } impl<'a> sop::ops::Decrypt<'a, RPGSOP, Certs, Keys> for Decrypt { fn verify_not_before( self: Box, _t: SystemTime, ) -> Box + 'a> { todo!(); // self.verify.not_before = Some(t); // self } fn verify_not_after( self: Box, _t: SystemTime, ) -> Box + 'a> { todo!() // self.verify.not_after = Some(t); // self } fn verify_with_certs( mut self: Box, certs: &Certs, ) -> sop::Result + 'a>> { self.verify.certs.push(certs.clone()); Ok(self) } fn with_session_key( mut self: Box, session_key: sop::SessionKey, ) -> sop::Result + 'a>> { self.session_keys.push(session_key); Ok(self) } fn with_password( mut self: Box, password: sop::Password, ) -> sop::Result + 'a>> { self.skesk_passwords.push(password); Ok(self) } fn with_keys( mut self: Box, keys: &Keys, ) -> sop::Result + 'a>> { keys.keys .iter() .for_each(|tsk| self.decryption_keys.push(tsk.clone())); Ok(self) } fn with_key_password( mut self: Box, password: sop::Password, ) -> sop::Result + 'a>> { self.key_passwords.push(password); Ok(self) } fn ciphertext<'d>( self: Box, ciphertext: &'d mut (dyn io::Read + Send + Sync), ) -> sop::Result< Box, Vec)> + 'd>, > where 'a: 'd, { Ok(Box::new(DecryptReady { decrypt: *self, ciphertext, })) } } struct DecryptReady<'a> { decrypt: Decrypt, ciphertext: &'a mut (dyn io::Read + Send + Sync), } impl sop::ops::Ready<(Option, Vec)> for DecryptReady<'_> { fn to_writer( self: Box, sink: &mut (dyn io::Write + Send + Sync), ) -> sop::Result<(Option, Vec)> { let (mut iter, _header) = Message::from_reader_many(self.ciphertext).expect("FIXME"); if let Some(Ok(msg)) = iter.next() { // FIXME: use provided session keys, if any let key_passwords: Vec<_> = self .decrypt .key_passwords .iter() .map(sop::plumbing::PasswordsAreHumanReadable::normalized) .collect(); let pkesk_decryptors: Vec<_> = self .decrypt .decryption_keys .iter() .map(|tsk| SoftkeyPkeskDecryptor::new(tsk.clone(), key_passwords.clone())) .map(|spd| Box::new(spd) as Box) .collect(); let skesk_passwords = self .decrypt .skesk_passwords .iter() .map(sop::plumbing::PasswordsAreHumanReadable::normalized) .collect(); let c: Vec = self .decrypt .verify .certs .into_iter() .flat_map(|c| c.certs) .collect(); let mr = { let mut decrypted = None; for sk in self.decrypt.session_keys { if let Ok(mr) = rpgpie::message::unpack_by_session_key( &msg, sk.key(), sk.algorithm().into(), &c, ) { decrypted = Some(mr); break; } } if decrypted.is_none() { if let Ok(mr) = rpgpie::message::unpack( msg, pkesk_decryptors.as_slice(), skesk_passwords, &c, ) { decrypted = Some(mr); } } let Some(mr) = decrypted else { // FIXME: probably the password(s) were wrong, but this is a bit of a guess // // FIXME: SKESK decryption failure should give a different error? return Err(sop::errors::Error::KeyIsProtected); }; mr }; let session_key = mr .session_key .as_ref() .map(|sk| sop::SessionKey::new(sk.0, &(sk.1)).expect("FIXME")); let verifications = util::result_to_verifications(&mr); assert!( iter.next().is_none(), "message must be empty at this point!" ); sink.write_all(mr.cleartext.data()).expect("FIXME"); Ok((session_key, verifications)) } else { panic!("no message found"); } } } rpgpie-sop-0.6.4/src/cmd/detach.rs000064400000000000000000000067631046102023000150640ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 use std::io::{BufRead, Read, Write}; use pgp::{Deserializable, Message, Signature}; use crate::Sigs; #[derive(Default)] pub(crate) struct InlineDetach {} impl InlineDetach { pub(crate) fn new() -> Self { Default::default() } } impl<'a> sop::ops::InlineDetach<'a, Sigs> for InlineDetach { fn message<'d>( self: Box, data: &'d mut (dyn Read + Send + Sync), ) -> sop::Result + 'd>> where 'a: 'd, { Ok(Box::new(InlineDetachReady { data })) } } struct InlineDetachReady<'d> { data: &'d mut (dyn Read + Send + Sync), } impl sop::ops::Ready for InlineDetachReady<'_> { fn to_writer(self: Box, sink: &mut (dyn Write + Send + Sync)) -> sop::Result { // Helper: Get the plaintext and list of signatures for a signed message. // The message may contain compression layers and multiple signatures. // // TODO: upstream to rpgpie / DRY with msg.rs fn unwrap_signed(msg: Message) -> sop::Result<(Vec, Vec)> { unwrap_signed_internal(msg, vec![], 0) } fn unwrap_signed_internal( msg: Message, mut sigs: Vec, depth: usize, ) -> sop::Result<(Vec, Vec)> { if depth > 10 { // FIXME: how to handle excessive message layering? return Err(sop::errors::Error::BadData); }; match msg { Message::Compressed(cd) => { let payload = cd.decompress().expect("FIXME"); let msg = Message::from_bytes(payload).expect("FIXME"); unwrap_signed_internal(msg, sigs, depth + 1) } Message::Signed { message, signature, .. } => { sigs.push(signature); unwrap_signed_internal(*message.expect("FIXME"), sigs, depth + 1) } Message::Literal(lit) => Ok((lit.data().to_vec(), sigs)), Message::Encrypted { .. } => Err(sop::errors::Error::BadData), } } // FIXME: DRY message loading against inline_verify! let mut reader = std::io::BufReader::new(self.data); let buf = reader.fill_buf()?; if buf.is_empty() { panic!("empty input"); } let (payload, sigs) = if buf[0] & 0x80 != 0 { // the input seems to be binary data - presumably an unarmored signed message let msg = Message::from_bytes(reader).expect("FIXME"); unwrap_signed(msg)? } else { let (pgp, _) = pgp::Any::from_armor(reader).expect("FIXME"); match pgp { pgp::Any::Message(msg) => unwrap_signed(msg)?, pgp::Any::Cleartext(csf) => { let payload = csf.signed_text().as_bytes().to_vec(); let sigs = csf .signatures() .iter() .map(|s| s.signature.clone()) .collect(); (payload, sigs) } _ => panic!("unexpected data type"), } }; sink.write_all(&payload).expect("FIXME"); Ok(Sigs { sigs, source_name: None, }) } } rpgpie-sop-0.6.4/src/cmd/encrypt.rs000064400000000000000000000201411046102023000153020ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 use std::io; use chrono::{DateTime, Utc}; use pgp::crypto::aead::AeadAlgorithm; use pgp::crypto::sym::SymmetricKeyAlgorithm; use rpgpie::certificate::{Certificate, Checked}; use rpgpie::message; use rpgpie::policy::Seipd; use rpgpie::ComponentKeyPub; use crate::cmd::sign::Sign; use crate::{Certs, Keys, RPGSOP}; pub(crate) struct Encrypt { armor: bool, profile: &'static str, mode: sop::ops::EncryptAs, symmetric_algorithms: Vec, aead_algorithms: Vec<(SymmetricKeyAlgorithm, AeadAlgorithm)>, seipd: Vec, recipients: Vec, skesk_passwords: Vec, sign: Sign, // Signing infrastructure, including private keys } impl Encrypt { const PROFILE_RFC4880: &'static str = "rfc4880"; const PROFILE_RFC9580: &'static str = "rfc9580"; const PROFILES: &'static [(&'static str, &'static str)] = &[ (Self::PROFILE_RFC4880, "use algorithms from RFC 4880"), (Self::PROFILE_RFC9580, "use algorithms from RFC 9580"), ]; pub(crate) fn new() -> Self { Self { armor: true, profile: Self::PROFILE_RFC4880, mode: Default::default(), symmetric_algorithms: rpgpie::policy::PREFERRED_SYMMETRIC_KEY_ALGORITHMS.into(), aead_algorithms: rpgpie::policy::PREFERRED_AEAD_ALGORITHMS.into(), seipd: rpgpie::policy::PREFERRED_SEIPD_MECHANISMS.into(), recipients: Default::default(), skesk_passwords: Default::default(), sign: Sign::new(), } } fn add_cert(mut self: Box, cert: &Certificate) -> sop::Result> { let ccert: Checked = cert.into(); let now: DateTime = chrono::offset::Utc::now(); // Handle recipient symmetric algorithm preferences, if any // (calculate intersection with our defaults) if let Some(p) = ccert.preferred_symmetric_key_algo(&now) { self.symmetric_algorithms.retain(|a| p.contains(a)); } // Handle recipient aead preferences, if any // (calculate intersection with our defaults) if let Some(p) = ccert.preferred_aead_algo(&now) { self.aead_algorithms.retain(|a| p.contains(a)); } // Handle SEIPD preferences, if any // (calculate intersection with our defaults) if let Some(p) = ccert.features(&now) { fn contains(p: u8, seipd: Seipd) -> bool { match seipd { Seipd::SED => true, Seipd::SEIPD1 => p & 1 != 0, Seipd::SEIPD2 => p & 8 != 0, } } self.seipd.retain(|a| contains(p, *a)); } else { // if there's no features setting, we only do SeipdV1 self.seipd = vec![Seipd::SEIPD1] } let keys = ccert.valid_encryption_capable_component_keys(); match !keys.is_empty() { true => { keys.into_iter().for_each(|key| self.recipients.push(key)); Ok(self) } false => Err(sop::errors::Error::CertCannotEncrypt), } } } impl<'a> sop::ops::Encrypt<'a, RPGSOP, Certs, Keys> for Encrypt { fn no_armor(mut self: Box) -> Box + 'a> { self.armor = false; self } fn list_profiles(&self) -> Vec<(String, String)> { Self::PROFILES .iter() .map(|(p, d)| (p.to_string(), d.to_string())) .collect() } fn profile( mut self: Box, profile: &str, ) -> sop::Result + 'a>> { self.profile = match profile { Self::PROFILE_RFC4880 | "default" => Self::PROFILE_RFC4880, Self::PROFILE_RFC9580 => Self::PROFILE_RFC9580, _ => return Err(sop::errors::Error::UnsupportedProfile), }; Ok(self) } fn mode( mut self: Box, mode: sop::ops::EncryptAs, ) -> Box + 'a> { self.sign.mode = mode.into(); self.mode = mode; self } fn sign_with_keys( mut self: Box, keys: &Keys, ) -> sop::Result + 'a>> { self.sign.add_signing_keys(keys)?; Ok(self) } fn with_key_password( mut self: Box, password: sop::Password, ) -> sop::Result + 'a>> { self.sign.with_key_password.push(password); Ok(self) } fn with_password( mut self: Box, password: sop::Password, ) -> sop::Result + 'a>> { self.skesk_passwords.push(password); Ok(self) } fn with_certs( mut self: Box, certs: &Certs, ) -> sop::Result + 'a>> { for cert in &certs.certs { self = self.add_cert(cert)?; } Ok(self) } fn plaintext<'p>( self: Box, plaintext: &'p mut (dyn io::Read + Send + Sync), ) -> sop::Result> + 'p>> where 'a: 'p, { Ok(Box::new(EncryptReady { encrypt: *self, plaintext, })) } } struct EncryptReady<'a> { encrypt: Encrypt, plaintext: &'a mut (dyn io::Read + Send + Sync), } impl sop::ops::Ready> for EncryptReady<'_> { fn to_writer( self: Box, sink: &mut (dyn io::Write + Send + Sync), ) -> sop::Result> { if self.encrypt.recipients.is_empty() && self.encrypt.skesk_passwords.is_empty() { return Err(sop::errors::Error::MissingArg); } let seipd = if !self.encrypt.recipients.is_empty() { // If we have recipients, we choose the Seipd version purely based on their preferences *self.encrypt.seipd.first().unwrap_or(&Seipd::SEIPD1) } else { // If we have no recipients, choose seipd1 vs. seipd2 based on the profile match self.encrypt.profile { Encrypt::PROFILE_RFC4880 => Seipd::SEIPD1, Encrypt::PROFILE_RFC9580 => Seipd::SEIPD2, _ => unimplemented!(), } }; let mechanism = match seipd { Seipd::SEIPD1 => { let symmetric_algo = *self .encrypt .symmetric_algorithms .first() .unwrap_or(&SymmetricKeyAlgorithm::default()); message::EncryptionMechanism::SeipdV1(symmetric_algo) } Seipd::SEIPD2 => { let aead_algo = *self .encrypt .aead_algorithms .first() .unwrap_or(&(SymmetricKeyAlgorithm::AES128, AeadAlgorithm::Ocb)); message::EncryptionMechanism::SeipdV2(aead_algo.1, aead_algo.0) } Seipd::SED => unimplemented!("SED"), }; let skesk_passwords = self .encrypt .skesk_passwords .iter() .map(sop::plumbing::PasswordsAreHumanReadable::normalized) .collect(); let session_key: Vec = message::encrypt( mechanism, self.encrypt.recipients, skesk_passwords, self.encrypt.sign.signers, self.encrypt.sign.hash_algos.first().copied(), self.plaintext, sink, self.encrypt.armor, ) .expect("FIXME") .to_vec(); let alg_id = u8::from(match mechanism { message::EncryptionMechanism::SeipdV1(sym) | message::EncryptionMechanism::SeipdV2(_, sym) => sym, }); let session_key = sop::SessionKey::new(alg_id, session_key)?; Ok(Some(session_key)) } } rpgpie-sop-0.6.4/src/cmd/extract_cert.rs000064400000000000000000000010541046102023000163070ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{Certs, Keys, RPGSOP}; pub(crate) struct ExtractCert {} impl ExtractCert { pub(crate) fn new() -> Self { Self {} } } impl sop::ops::ExtractCert<'_, RPGSOP, Certs, Keys> for ExtractCert { fn keys(self: Box, keys: &Keys) -> sop::Result { Ok(Certs { certs: keys.keys.iter().map(Into::into).collect(), source_name: keys.source_name.as_ref().cloned(), }) } } rpgpie-sop-0.6.4/src/cmd/generate.rs000064400000000000000000000164321046102023000154200ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 use std::collections::VecDeque; use pgp::crypto::ecc_curve::ECCCurve; use rpgpie::tsk::Tsk; use crate::{Keys, RPGSOP}; const PROFILE_EDDSA: &str = "draft-koch-eddsa-for-openpgp-00"; const PROFILE_RFC9580: &str = "rfc9580"; const PROFILE_RFC4880: &str = "interop-testing-rfc4880"; const PROFILE_NISTP256: &str = "interop-testing-rfc6637-nistp256"; const PROFILE_NISTP384: &str = "interop-testing-rfc6637-nistp384"; const PROFILE_NISTP521: &str = "interop-testing-rfc6637-nistp521"; const PROFILE_RFC9580_NISTP: &str = "interop-testing-rfc9580-nistp"; const PROFILE_RFC9580_RSA: &str = "interop-testing-rfc9580-rsa"; #[cfg(feature = "unstable-curve448")] const PROFILE_RFC9580_CV448: &str = "interop-testing-rfc9580-cv448"; const PROFILES: &[(&str, &str)] = &[ (PROFILE_EDDSA, "use EdDSA & ECDH over Cv25519"), (PROFILE_RFC9580, "use algorithms from RFC 9580"), // // -- the following profiles are for interop testing only -- // ( PROFILE_RFC9580_RSA, "Only for interop-testing: use algorithms from RFC 9580 with RSA", ), ( PROFILE_RFC9580_NISTP, "Only for interop-testing: use algorithms from RFC 9580 with NIST P-256", ), #[cfg(feature = "unstable-curve448")] ( PROFILE_RFC9580_CV448, "Only for interop-testing: use algorithms from RFC 9580 with X448 and Ed25519", ), ]; pub(crate) struct GenerateKey { profile: &'static str, signing_only: bool, key_password: Option, user_ids: VecDeque, } impl GenerateKey { pub(crate) fn new() -> Self { Self { profile: PROFILE_EDDSA, signing_only: false, key_password: Default::default(), user_ids: Default::default(), } } } impl<'a> sop::ops::GenerateKey<'a, RPGSOP, Keys> for GenerateKey { fn list_profiles(&self) -> Vec<(String, String)> { PROFILES .iter() .map(|(p, d)| (p.to_string(), d.to_string())) .collect() } fn profile( mut self: Box, profile: &str, ) -> sop::Result>> { self.profile = match profile { PROFILE_EDDSA | "default" => PROFILE_EDDSA, PROFILE_RFC4880 => PROFILE_RFC4880, PROFILE_NISTP256 => PROFILE_NISTP256, PROFILE_NISTP384 => PROFILE_NISTP384, PROFILE_NISTP521 => PROFILE_NISTP521, PROFILE_RFC9580 => PROFILE_RFC9580, PROFILE_RFC9580_NISTP => PROFILE_RFC9580_NISTP, PROFILE_RFC9580_RSA => PROFILE_RFC9580_RSA, #[cfg(feature = "unstable-curve448")] PROFILE_RFC9580_CV448 => PROFILE_RFC9580_CV448, _ => return Err(sop::errors::Error::UnsupportedProfile), }; Ok(self) } fn signing_only(mut self: Box) -> Box> { self.signing_only = true; self } fn with_key_password( mut self: Box, key_password: sop::Password, ) -> sop::Result>> { self.key_password = Some(key_password); Ok(self) } fn userid( mut self: Box, user_id: &str, ) -> Box> { self.user_ids.push_back(user_id.into()); self } fn generate(mut self: Box) -> sop::Result { let primary_user_id = self.user_ids.pop_front(); let other_user_ids = self.user_ids.into(); let key_password: Option<&[u8]> = self .key_password .as_ref() .map(sop::plumbing::PasswordsAreHumanReadable::normalized); let key_password: Option = key_password.map(String::from_utf8_lossy).map(Into::into); let (key_type_pri, key_type_enc) = match self.profile { // Curve 25519-based keys PROFILE_EDDSA => ( pgp::KeyType::EdDSALegacy, pgp::KeyType::ECDH(ECCCurve::Curve25519), ), // RSA 4096 is compatible with Gnuk v1 (while RSA 3072 is not) PROFILE_RFC4880 => (pgp::KeyType::Rsa(4096), pgp::KeyType::Rsa(4096)), // Nist-P* -based keys PROFILE_NISTP256 => ( pgp::KeyType::ECDSA(ECCCurve::P256), pgp::KeyType::ECDH(ECCCurve::P256), ), PROFILE_NISTP384 => ( pgp::KeyType::ECDSA(ECCCurve::P384), pgp::KeyType::ECDH(ECCCurve::P384), ), PROFILE_NISTP521 => ( pgp::KeyType::ECDSA(ECCCurve::P521), pgp::KeyType::ECDH(ECCCurve::P521), ), PROFILE_RFC9580 => { let tsk = Tsk::generate_v6( pgp::KeyType::Ed25519, pgp::KeyType::X25519, primary_user_id, other_user_ids, key_password.as_deref(), ) .expect("FIXME"); return Ok(Keys { keys: vec![tsk], source_name: None, }); } PROFILE_RFC9580_NISTP => { let tsk = Tsk::generate_v6( pgp::KeyType::ECDSA(ECCCurve::P256), pgp::KeyType::ECDH(ECCCurve::P256), primary_user_id, other_user_ids, key_password.as_deref(), ) .expect("FIXME"); return Ok(Keys { keys: vec![tsk], source_name: None, }); } PROFILE_RFC9580_RSA => { let tsk = Tsk::generate_v6( pgp::KeyType::Rsa(4096), pgp::KeyType::Rsa(4096), primary_user_id, other_user_ids, key_password.as_deref(), ) .expect("FIXME"); return Ok(Keys { keys: vec![tsk], source_name: None, }); } #[cfg(feature = "unstable-curve448")] PROFILE_RFC9580_CV448 => { let tsk = Tsk::generate_v6( pgp::KeyType::Ed25519, // FIXME: use Ed448 when rpgp supports it pgp::KeyType::X448, primary_user_id, other_user_ids, key_password.as_deref(), ) .expect("FIXME"); return Ok(Keys { keys: vec![tsk], source_name: None, }); } _ => return Err(sop::errors::Error::UnsupportedProfile), }; let tsk = Tsk::generate_v4( key_type_pri, if self.signing_only { None } else { Some(key_type_enc) }, primary_user_id, other_user_ids, key_password.as_deref(), ) .map_err(std::io::Error::other)?; Ok(Keys { keys: vec![tsk], source_name: None, }) } } rpgpie-sop-0.6.4/src/cmd/inline_sign.rs000064400000000000000000000201171046102023000161170ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 use std::io; use pgp::cleartext::CleartextSignedMessage; use pgp::packet::{LiteralData, Packet}; use pgp::ser::Serialize; use pgp::types::PublicKeyTrait; use pgp::{ArmorOptions, Deserializable, Message, Signature}; use rpgpie::tsk::DataSigner; use sop::errors::Error; use crate::cmd::sign::Sign; use crate::{Keys, RPGSOP}; pub(crate) struct InlineSign { armor: bool, sign: Sign, mode: sop::ops::InlineSignAs, } impl InlineSign { pub(crate) fn new() -> Self { Self { armor: true, sign: Sign::new(), mode: Default::default(), } } } impl<'a> sop::ops::InlineSign<'a, RPGSOP, Keys> for InlineSign { fn no_armor(mut self: Box) -> Box + 'a> { self.armor = false; self } fn mode( mut self: Box, mode: sop::ops::InlineSignAs, ) -> Box + 'a> { self.mode = mode; self } fn keys( mut self: Box, keys: &Keys, ) -> sop::Result + 'a>> { self.sign.add_signing_keys(keys)?; Ok(self) } fn with_key_password( mut self: Box, password: sop::Password, ) -> sop::Result + 'a>> { self.sign.with_key_password.push(password); Ok(self) } fn data<'d>( self: Box, data: &'d mut (dyn io::Read + Send + Sync), ) -> sop::Result> where 'a: 'd, { if self.sign.signers.is_empty() { return Err(Error::MissingArg); } if !self.armor && matches!(self.mode, sop::ops::InlineSignAs::ClearSigned) { return Err(Error::IncompatibleOptions); } Ok(Box::new(InlineSignReady { inline_sign: *self, data, })) } } struct InlineSignReady<'a> { inline_sign: InlineSign, data: &'a mut (dyn io::Read + Send + Sync), } impl sop::ops::Ready for InlineSignReady<'_> { fn to_writer(self: Box, mut sink: &mut (dyn io::Write + Send + Sync)) -> sop::Result<()> { let mut data = vec![]; self.data.read_to_end(&mut data)?; let hash_algo = self .inline_sign .sign .hash_algos .first() .cloned() .unwrap_or_default(); assert!(!self.inline_sign.sign.signers.is_empty()); // FIXME // Passwords to try let pws: Vec<&[u8]> = if self.inline_sign.sign.with_key_password.is_empty() { vec![&[]] } else { self.inline_sign .sign .with_key_password .iter() .map(sop::plumbing::PasswordsAreHumanReadable::normalized) .collect() }; let mut datasigners: Vec = vec![]; for tsk in self.inline_sign.sign.signers { let mut s: Vec = tsk.signing_capable_component_keys().collect(); if s.is_empty() { panic!( "no signing capable component key found for signer {:02x?}", tsk.key().fingerprint() ); } datasigners.append(&mut s); } let lit = match &self.inline_sign.mode { sop::ops::InlineSignAs::Binary => LiteralData::from_bytes("".into(), &data), sop::ops::InlineSignAs::Text => { LiteralData::from_str("", &String::from_utf8(data).map_err(|_| Error::BadData)?) } sop::ops::InlineSignAs::ClearSigned => { let signers = |text: &[u8]| { let lit = Message::Literal(LiteralData::from_str( [], core::str::from_utf8(text).map_err(|_| { pgp::errors::Error::Message("Data is not UTF-8".to_string()) })?, )); let mut sigs: Vec = vec![]; for ds in datasigners { let mut sig = None; // try all passwords for this DataSigner 'pws: for pw in &pws { let res = ds.sign_msg( lit.clone(), || String::from_utf8_lossy(pw).to_string(), hash_algo, ); if let Ok(Message::Signed { signature, .. }) = res { sig = Some(signature); break 'pws; // we found a working password for ds, stop trying more } } if let Some(s) = sig { sigs.push(s); } else { // We failed to produce a signature, for some reason. // Maybe it's password protection. // TODO: return a more concrete error? return Err(pgp::errors::Error::Message("Couldn't sign".into())); } } Ok(sigs) }; let text = core::str::from_utf8(&data).map_err(|_| Error::BadData)?; let Ok(csf) = CleartextSignedMessage::new_many(text, signers) else { // The signers failed, for some reason. It might be password protection. // TODO: Check error conditions more closely, and return more specific errors. return Err(Error::KeyIsProtected); }; csf.to_armored_writer(&mut sink, ArmorOptions::default()) .expect("writing failed"); return Ok(()); } }; let mut packets = vec![]; packets.push(Packet::from(lit.clone())); let lit_msg = Message::Literal(lit); for ds in datasigners { let sig = pws .iter() .flat_map(|pw| { ds.sign_msg( lit_msg.clone(), || String::from_utf8_lossy(pw).to_string(), hash_algo, ) }) .next(); if let Some(sig) = sig { if let Message::Signed { one_pass_signature, signature, .. } = sig { if let Some(mut ops) = one_pass_signature { if packets.len() > 1 { // only the innermost signature should be marked "last", // so we mark all others as non-last. ops.last = 0; } packets.insert(0, Packet::from(ops)); } packets.push(Packet::from(signature)); } } else { // We failed to produce a signature, for some reason. // Probably it's password protection. // TODO: make sure that password locking was actually the problem. return Err(Error::KeyIsProtected); } } let signed = Message::from_packets(packets.into_iter().map(Ok).peekable()) .next() .unwrap_or(Err(pgp::errors::Error::Message( "parser couldn't construct a message".to_string(), ))) .map_err(|_| Error::UnspecifiedFailure)?; // failed to construct a message match self.inline_sign.armor { true => signed .to_armored_writer(&mut sink, ArmorOptions::default()) .expect("writing failed"), false => signed.to_writer(&mut sink).expect("writing failed"), } Ok(()) } } rpgpie-sop-0.6.4/src/cmd/inline_verify.rs000064400000000000000000000142011046102023000164600ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 use std::default::Default; use std::io; use std::io::BufRead; use pgp::{Deserializable, Message}; use rpgpie::certificate::Certificate; use rpgpie::certificate::Checked; use rpgpie::ComponentKeyPub; use crate::util::to_verification; use crate::{util, Certs, RPGSOP}; #[derive(Default)] pub(crate) struct InlineVerify { _not_before: Option, _not_after: Option, certs: Vec, } impl InlineVerify { pub(crate) fn new() -> Self { Default::default() } } impl<'a> sop::ops::InlineVerify<'a, RPGSOP, Certs> for InlineVerify { fn not_before( self: Box, _t: std::time::SystemTime, ) -> Box + 'a> { todo!() // self.not_before = Some(t); // self } fn not_after( self: Box, _t: std::time::SystemTime, ) -> Box + 'a> { todo!() // self.not_after = Some(t); // self } fn certs( mut self: Box, certs: &Certs, ) -> sop::Result + 'a>> { self.certs.push(certs.clone()); Ok(self) } fn message<'d>( self: Box, data: &'d mut (dyn io::Read + Send + Sync), ) -> sop::Result> + 'd>> where 'a: 'd, { Ok(Box::new(InlineVerifyReady { inline_verify: *self, data, })) } } struct InlineVerifyReady<'a> { inline_verify: InlineVerify, data: &'a mut (dyn io::Read + Send + Sync), } impl sop::ops::Ready> for InlineVerifyReady<'_> { fn to_writer( self: Box, sink: &mut (dyn io::Write + Send + Sync), ) -> sop::Result> { let mut reader = io::BufReader::new(self.data); let buf = reader.fill_buf()?; if buf.is_empty() { panic!("empty input"); } let msg: Message = if buf[0] & 0x80 != 0 { // the input seems to be binary: presumably an unarmored inline signed message Message::from_bytes(reader).expect("read binary") } else { // the input seems to be ascii: could be an armored inline signed message or a CSF message let (pgp, _) = pgp::Any::from_armor(reader).expect("read armored"); match pgp { pgp::Any::Message(msg) => msg, pgp::Any::Cleartext(csf) => { // Handling for CSF messages let validated: Vec<( Certificate, ComponentKeyPub, pgp::Signature, Option, )> = self .inline_verify .certs .iter() .flat_map(|certs| { certs.certs.iter().flat_map(|c| { let cc: Checked = c.into(); let verifiers = cc.valid_signing_capable_component_keys_at( &chrono::offset::Utc::now(), ); let verified: Vec<_> = verifiers .iter() .flat_map(|v| { v.verify_csf(&csf).ok().map(|s| { ( c.clone(), v.as_componentkey().clone(), s.clone().signature, certs.source_name.clone(), ) }) }) .collect(); verified }) }) .collect(); if !validated.is_empty() { let text = csf.signed_text(); sink.write_all(text.as_bytes()).expect("FIXME"); let verifications: Vec<_> = validated .iter() .map(|(cert, key, sig, source)| { util::to_verification(sig, cert, key, source.as_deref()) }) .collect(); return Ok(verifications); } else { return Err(sop::errors::Error::NoSignature); } } _ => panic!("unexpected data type"), } }; // Handling for non-CSF messages let mrs: Vec<_> = self .inline_verify .certs .iter() .map(|certs| { let mr = rpgpie::message::unpack(msg.clone(), &[], vec![], &certs.certs).expect("FIXME"); (mr, certs.source_name.clone()) }) .collect(); // Do we have any mr with a non-empty "validated"? if mrs.iter().any(|(mr, _s)| !mr.validated.is_empty()) { // yes: we got at least one positive validation // the cleartext should be the same in all message results! let text = mrs[0].0.cleartext.data(); let mut vs = Vec::new(); for (mr, source) in &mrs { assert_eq!(mr.cleartext.data(), text); for (cert, ckp, sig) in &mr.validated { let v = to_verification(sig, cert, ckp, source.as_deref()); vs.push(v); } } sink.write_all(text).expect("FIXME"); Ok(vs) } else { Err(sop::errors::Error::NoSignature) } } } rpgpie-sop-0.6.4/src/cmd/mod.rs000064400000000000000000000007141046102023000144010ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 pub(crate) mod armor; pub(crate) mod dearmor; pub(crate) mod decrypt; pub(crate) mod detach; pub(crate) mod encrypt; pub(crate) mod extract_cert; pub(crate) mod generate; pub(crate) mod inline_sign; pub(crate) mod inline_verify; pub(crate) mod password; pub(crate) mod revoke_key; pub(crate) mod sign; pub(crate) mod verify; pub(crate) mod version; rpgpie-sop-0.6.4/src/cmd/password.rs000064400000000000000000000101141046102023000154570ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 use pgp::crypto::aead::AeadAlgorithm; use pgp::crypto::sym::SymmetricKeyAlgorithm; use pgp::types::{KeyVersion, PublicKeyTrait, S2kParams, SecretKeyTrait, StringToKey}; use rand::{thread_rng, Rng}; use rpgpie::tsk::Tsk; use sop::plumbing::PasswordsAreHumanReadable; use sop::Password; use crate::{Keys, RPGSOP}; pub(crate) struct ChangeKeyPassword { pw_old: Option, pw_new: Option, } impl ChangeKeyPassword { pub(crate) fn new() -> Self { Self { pw_old: None, pw_new: None, } } } impl<'a> sop::ops::ChangeKeyPassword<'a, RPGSOP, Keys> for ChangeKeyPassword { fn new_key_password( mut self: Box, password: Password, ) -> sop::Result + 'a>> { self.pw_new = Some(password); Ok(self) } fn old_key_password( mut self: Box, password: Password, ) -> sop::Result + 'a>> { self.pw_old = Some(password); Ok(self) } fn keys(self: Box, keys: &Keys) -> sop::Result { fn s2k(version: KeyVersion) -> S2kParams { match version { KeyVersion::V4 => { let mut rng = thread_rng(); let sym_alg = SymmetricKeyAlgorithm::AES256; let mut iv = vec![0u8; sym_alg.block_size()]; rng.fill(&mut iv[..]); S2kParams::Cfb { sym_alg, s2k: StringToKey::new_default(rng), iv, } } KeyVersion::V6 => { let mut rng = thread_rng(); let sym_alg = SymmetricKeyAlgorithm::AES256; let aead_mode = AeadAlgorithm::Ocb; let mut nonce = vec![0u8; aead_mode.nonce_size()]; rng.fill(&mut nonce[..]); let mut salt = [0u8; 16]; rng.fill(&mut salt[..]); S2kParams::Aead { sym_alg, aead_mode, s2k: StringToKey::Argon2 { salt, t: 1, p: 4, m_enc: 21, // 2 GB }, nonce, } } _ => unimplemented!(), } } let mut res: Vec = vec![]; for key in &keys.keys { let pw_old = self .pw_old .as_ref() .map(|pw| String::from_utf8_lossy(pw.normalized()).to_string()); let pw_new = self .pw_new .as_ref() .map(|pw| String::from_utf8_lossy(pw.normalized()).to_string()); let mut ssk = key.key().clone(); let pri = &mut ssk.primary_key; if let Some(pw_old) = &pw_old { pri.remove_password(|| pw_old.clone()).expect("FIXME"); } if let Some(pw_new) = &pw_new { pri.set_password_with_s2k(|| pw_new.clone(), s2k(pri.public_key().version())) .expect("FIXME"); } for sub in &mut ssk.secret_subkeys { if let Some(pw_old) = &pw_old { sub.key.remove_password(|| pw_old.clone()).expect("FIXME"); } if let Some(pw_new) = &pw_new { sub.key .set_password_with_s2k( || pw_new.clone(), s2k(sub.key.public_key().version()), ) .expect("FIXME"); } } res.push(ssk.into()); } Ok(Keys { keys: res, source_name: None, }) } } rpgpie-sop-0.6.4/src/cmd/revoke_key.rs000064400000000000000000000065341046102023000157730ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 use chrono::SubsecRound; use pgp::packet::{RevocationCode, SignatureConfig, SignatureType, Subpacket, SubpacketData}; use pgp::types::PublicKeyTrait; use pgp::types::{KeyVersion, SecretKeyTrait}; use pgp::{Signature, SignedPublicKey}; use rand::thread_rng; use sop::plumbing::PasswordsAreHumanReadable; use crate::{Certs, Keys, RPGSOP}; pub(crate) struct RevokeKey { key_passwords: Vec, // Passwords for asymmetric component key material } impl RevokeKey { pub(crate) fn new() -> Self { let empty_pw = sop::Password::new_unchecked(vec![]); Self { key_passwords: vec![empty_pw], } } } impl<'a> sop::ops::RevokeKey<'a, RPGSOP, Certs, Keys> for RevokeKey { fn with_key_password( mut self: Box, password: sop::Password, ) -> sop::Result>> { self.key_passwords.push(password); Ok(self) } fn keys(self: Box, keys: &Keys) -> sop::Result { let mut rng = thread_rng(); let mut results = vec![]; for tsk in &keys.keys { let primary = &tsk.key().primary_key; // Make a revocation signature let mut config = match primary.version() { KeyVersion::V4 => SignatureConfig::v4( SignatureType::KeyRevocation, primary.algorithm(), primary.hash_alg(), ), KeyVersion::V6 => SignatureConfig::v6( &mut rng, SignatureType::KeyRevocation, primary.algorithm(), primary.hash_alg(), ) .expect("FIXME"), v => panic!("unsupported key version {:?}", v), }; config.hashed_subpackets = vec![ Subpacket::regular(SubpacketData::SignatureCreationTime( chrono::Utc::now().trunc_subsecs(0), )), Subpacket::regular(SubpacketData::Issuer(primary.key_id())), Subpacket::regular(SubpacketData::RevocationReason( RevocationCode::NoReason, "unspecified".into(), )), Subpacket::regular(SubpacketData::IssuerFingerprint(primary.fingerprint())), ]; let mut rev: Option = None; for pw in &self.key_passwords { let pw = String::from_utf8_lossy(pw.normalized()).to_string(); match config.clone().sign_key(&primary, || pw, &primary) { Ok(sig) => { rev = Some(sig); break; } Err(e) => eprintln!("e: {:?}", e), }; } let Some(rev) = rev else { return Err(sop::errors::Error::KeyCannotSign); }; let mut revoked = tsk.key().clone(); revoked.details.revocation_signatures.push(rev); let spk = SignedPublicKey::from(revoked); results.push(spk.into()); } Ok(Certs { certs: results, source_name: keys.source_name.as_ref().cloned(), }) } } rpgpie-sop-0.6.4/src/cmd/sign.rs000064400000000000000000000115751046102023000145710ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 use std::io; use chrono::{DateTime, Utc}; use pgp::crypto::hash::HashAlgorithm; use pgp::packet::LiteralData; use pgp::Message; use rpgpie::certificate::{Certificate, Checked}; use rpgpie::tsk::Tsk; use crate::{Keys, Sigs, RPGSOP}; pub(crate) struct Sign { pub(crate) mode: sop::ops::SignAs, pub(crate) hash_algos: Vec, pub(crate) with_key_password: Vec, pub(crate) signers: Vec, } impl Sign { pub(crate) fn new() -> Self { Self { mode: Default::default(), hash_algos: rpgpie::policy::PREFERRED_HASH_ALGORITHMS.into(), with_key_password: Default::default(), signers: Default::default(), } } } impl Sign { pub(crate) fn add_signing_keys(&mut self, keys: &Keys) -> sop::Result<()> { for key in &keys.keys { self.add_signing_key(key)?; } Ok(()) } fn add_signing_key(&mut self, tsk: &Tsk) -> sop::Result<()> { let cert = Certificate::from(tsk); let ccert: Checked = (&cert).into(); let now: DateTime = chrono::offset::Utc::now(); // Limit hash algorithms to what the signer prefers if let Some(p) = ccert.preferred_hash_algorithms(&now) { self.hash_algos.retain(|a| p.contains(a)); } self.signers.push(tsk.clone()); Ok(()) } } impl<'a> sop::ops::Sign<'a, RPGSOP, Keys, Sigs> for Sign { fn mode( mut self: Box, mode: sop::ops::SignAs, ) -> Box + 'a> { self.mode = mode; self } fn keys( mut self: Box, keys: &Keys, ) -> sop::Result + 'a>> { self.add_signing_keys(keys)?; Ok(self) } fn with_key_password( mut self: Box, password: sop::Password, ) -> sop::Result + 'a>> { self.with_key_password.push(password); Ok(self) } fn data( self: Box, input: &mut (dyn io::Read + Send + Sync), ) -> sop::Result<(sop::ops::Micalg, Sigs)> { if self.signers.is_empty() { return Err(sop::errors::Error::MissingArg); } let hash_algo = self.hash_algos.first().cloned().unwrap_or_default(); let mut data = vec![]; input.read_to_end(&mut data)?; let lit = match self.mode { sop::ops::SignAs::Binary => LiteralData::from_bytes("".into(), &data), sop::ops::SignAs::Text => { LiteralData::from_str("", &String::from_utf8(data).expect("FIXME")) } }; let msg = Message::Literal(lit); let mut sigs = vec![]; // Passwords to try let pws: Vec<&[u8]> = if self.with_key_password.is_empty() { vec![&[]] } else { self.with_key_password .iter() .map(sop::plumbing::PasswordsAreHumanReadable::normalized) .collect() }; for tsk in self.signers { for signer in tsk.signing_capable_component_keys() { log::info!( "Trying to sign data with signer: {:02x?}", signer.fingerprint() ); let sig = pws .iter() .flat_map(|pw| { let result = signer.sign_msg( msg.clone(), || String::from_utf8_lossy(pw).to_string(), hash_algo, ); if result.is_err() { log::warn!("Signing failed: {result:?}"); } result }) .next(); match sig { Some(Message::Signed { signature, .. }) => sigs.push(signature), Some(_) => panic!("Unexpected message type while signing: {:?}", sig), None => { log::warn!( "Couldn't sign with signer key {:02x?}", signer.fingerprint() ); // signing with this signing key failed but let's continue } }; } } if sigs.is_empty() { // FIXME: probably the password(s) were wrong, but this is a bit of a guess return Err(sop::errors::Error::KeyIsProtected); } let hash_algo_id = u8::from(hash_algo); Ok(( hash_algo_id.into(), Sigs { sigs, source_name: None, }, )) } } rpgpie-sop-0.6.4/src/cmd/verify.rs000064400000000000000000000064331046102023000151320ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 use std::io; use std::time::SystemTime; use rpgpie::certificate::Checked; use crate::util::to_verification; use crate::{Certs, Sigs, RPGSOP}; #[derive(Default)] pub(crate) struct Verify { _not_before: Option, _not_after: Option, pub(crate) certs: Vec, } impl Verify { pub(crate) fn new() -> Self { Default::default() } } impl<'a> sop::ops::Verify<'a, RPGSOP, Certs, Sigs> for Verify { fn not_before( self: Box, _t: SystemTime, ) -> Box + 'a> { todo!() // self.not_before = Some(t); // self } fn not_after( self: Box, _t: SystemTime, ) -> Box + 'a> { todo!() // self.not_after = Some(t); // self } fn certs( mut self: Box, cert: &Certs, ) -> sop::Result + 'a>> { self.certs.push(cert.clone()); Ok(self) } fn signatures<'s>( self: Box, signatures: &'s Sigs, ) -> sop::Result + 's>> where 'a: 's, { Ok(Box::new(VerifySignatures { verify: *self, signatures, })) } } struct VerifySignatures<'s> { verify: Verify, signatures: &'s Sigs, } impl sop::ops::VerifySignatures<'_> for VerifySignatures<'_> { fn data( self: Box, data: &mut (dyn io::Read + Send + Sync), ) -> sop::Result> { if self .verify .certs .iter() .map(|c| c.certs.len()) .all(|len| len == 0) { // no certificates found return Err(sop::errors::Error::MissingArg); } let mut verifications = vec![]; // FIXME: stream input data? let mut payload = vec![]; data.read_to_end(&mut payload)?; for sig in &self.signatures.sigs { for certs in &self.verify.certs { for cert in &certs.certs { let ccert = Checked::from(cert); // Verify at signature creation time. // FIXME: does the signature need to be valid "now", as well? let reference = sig.created().expect("FIXME"); ccert .valid_signing_capable_component_keys_at(reference) .iter() .filter(|c| c.verify(sig, &payload).is_ok()) .map(|v| { to_verification( sig, cert, v.as_componentkey(), certs.source_name.as_deref(), ) }) .for_each(|v| verifications.push(v)); } } } if verifications.is_empty() { Err(sop::errors::Error::NoSignature) } else { Ok(verifications) } } } rpgpie-sop-0.6.4/src/cmd/version.rs000064400000000000000000000020511046102023000153030ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 pub struct Version {} impl Version { pub(crate) fn new() -> Self { Self {} } } impl sop::ops::Version<'_> for Version { fn frontend(&self) -> sop::Result { Ok(sop::ops::VersionInfo { name: env!("CARGO_PKG_NAME").into(), version: env!("CARGO_PKG_VERSION").into(), }) } fn backend(&self) -> sop::Result { Ok(sop::ops::VersionInfo { name: "rpgpie".into(), version: rpgpie::VERSION.into(), }) } fn extended(&self) -> sop::Result { #[cfg(feature = "unstable-curve448")] let c448 = "yes"; #[cfg(not(feature = "unstable-curve448"))] let c448 = "no"; Ok([ format!("rpgpie {}", rpgpie::VERSION), format!("unstable-448: {}", c448), format!("rpgp {}", rpgpie::RPGP_VERSION), ] .join("\n")) } } rpgpie-sop-0.6.4/src/lib.rs000064400000000000000000000153251046102023000136310ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 mod cmd; mod util; use std::io; use pgp::Signature; use rpgpie::certificate::Certificate; use rpgpie::tsk::Tsk; use sop::ops::{CertifyUserID, MergeCerts, UpdateKey, ValidateUserID}; #[derive(Clone, Copy, Default)] pub struct RPGSOP {} // SOP singleton const SOP: RPGSOP = RPGSOP {}; #[derive(Clone, Default)] pub struct Certs { certs: Vec, source_name: Option, } pub struct Keys { keys: Vec, source_name: Option, } pub struct Sigs { sigs: Vec, source_name: Option, } impl sop::SOP<'_> for RPGSOP { type Keys = Keys; type Certs = Certs; type Sigs = Sigs; fn version(&'_ self) -> sop::Result> { Ok(Box::new(cmd::version::Version::new())) } fn sopv_version(&self) -> sop::Result<&'static str> { Ok("1.0") } fn generate_key( &'_ self, ) -> sop::Result + '_>> { Ok(Box::new(cmd::generate::GenerateKey::new())) } fn change_key_password( &'_ self, ) -> sop::Result>> { Ok(Box::new(cmd::password::ChangeKeyPassword::new())) } fn revoke_key( &'_ self, ) -> sop::Result>> { Ok(Box::new(cmd::revoke_key::RevokeKey::new())) } fn extract_cert( &'_ self, ) -> sop::Result + '_>> { Ok(Box::new(cmd::extract_cert::ExtractCert::new())) } fn sign(&'_ self) -> sop::Result + '_>> { Ok(Box::new(cmd::sign::Sign::new())) } fn verify( &'_ self, ) -> sop::Result + '_>> { Ok(Box::new(cmd::verify::Verify::new())) } fn encrypt( &'_ self, ) -> sop::Result + '_>> { Ok(Box::new(cmd::encrypt::Encrypt::new())) } fn decrypt( &'_ self, ) -> sop::Result + '_>> { Ok(Box::new(cmd::decrypt::Decrypt::new())) } fn armor(&'_ self) -> sop::Result> { Ok(Box::new(cmd::armor::Armor::new())) } fn dearmor(&'_ self) -> sop::Result> { Ok(Box::new(cmd::dearmor::Dearmor::new())) } fn inline_detach(&'_ self) -> sop::Result>> { Ok(Box::new(cmd::detach::InlineDetach::new())) } fn inline_verify( &'_ self, ) -> sop::Result + '_>> { Ok(Box::new(cmd::inline_verify::InlineVerify::new())) } fn inline_sign(&'_ self) -> sop::Result + '_>> { Ok(Box::new(cmd::inline_sign::InlineSign::new())) } fn update_key(&'_ self) -> sop::Result + '_>> { todo!() } fn merge_certs(&'_ self) -> sop::Result + '_>> { todo!() } fn certify_userid( &'_ self, ) -> sop::Result + '_>> { todo!() } fn validate_userid(&'_ self) -> sop::Result + '_>> { todo!() } } impl sop::Load<'_, RPGSOP> for Certs { fn from_reader( _sop: &RPGSOP, mut source: &mut (dyn io::Read + Send + Sync), source_name: Option, ) -> sop::Result { let certs = Certificate::load(&mut source).map_err(|e| match e { rpgpie::Error::Io(e) => sop::errors::Error::IoError(e), _ => sop::errors::Error::UnspecifiedFailure, })?; Ok(Certs { certs, source_name }) } fn source_name(&self) -> Option<&str> { self.source_name.as_deref() } } impl sop::Save for Certs { fn to_writer( &self, armored: bool, sink: &mut (dyn io::Write + Send + Sync), ) -> sop::Result<()> { Certificate::save_all(&self.certs, armored, sink).map_err(|e| match e { rpgpie::Error::Io(e) => sop::errors::Error::IoError(e), _ => sop::errors::Error::UnspecifiedFailure, })?; Ok(()) } } impl sop::Load<'_, RPGSOP> for Keys { fn from_reader( _sop: &'_ RPGSOP, mut source: &mut (dyn io::Read + Send + Sync), source_name: Option, ) -> sop::Result { let keys = Tsk::load(&mut source).map_err(|e| match e { rpgpie::Error::Io(e) => sop::errors::Error::IoError(e), _ => sop::errors::Error::UnspecifiedFailure, })?; Ok(Keys { keys, source_name }) } fn source_name(&self) -> Option<&str> { self.source_name.as_deref() } } impl sop::Save for Keys { fn to_writer( &self, armored: bool, sink: &mut (dyn io::Write + Send + Sync), ) -> sop::Result<()> { Tsk::save_all(&self.keys, armored, sink).map_err(|e| match e { rpgpie::Error::Io(e) => sop::errors::Error::IoError(e), _ => sop::errors::Error::UnspecifiedFailure, })?; Ok(()) } } impl sop::Load<'_, RPGSOP> for Sigs { fn from_reader( _sop: &'_ RPGSOP, mut source: &mut (dyn io::Read + Send + Sync), source_name: Option, ) -> sop::Result { let sigs = rpgpie::signature::load(&mut source).map_err(|e| match e { rpgpie::Error::Io(e) => sop::errors::Error::IoError(e), _ => sop::errors::Error::UnspecifiedFailure, })?; Ok(Sigs { sigs, source_name }) } fn source_name(&self) -> Option<&str> { self.source_name.as_deref() } } impl sop::Save for Sigs { fn to_writer( &self, armored: bool, mut sink: &mut (dyn io::Write + Send + Sync), ) -> sop::Result<()> { rpgpie::signature::save(&self.sigs, armored, &mut sink).map_err(|e| match e { rpgpie::Error::Io(e) => sop::errors::Error::IoError(e), _ => sop::errors::Error::UnspecifiedFailure, })?; Ok(()) } } impl<'s> sop::plumbing::SopRef<'s, RPGSOP> for Certs { fn sop(&self) -> &'s RPGSOP { &SOP } } impl<'s> sop::plumbing::SopRef<'s, RPGSOP> for Keys { fn sop(&self) -> &'s RPGSOP { &SOP } } impl<'s> sop::plumbing::SopRef<'s, RPGSOP> for Sigs { fn sop(&self) -> &'s RPGSOP { &SOP } } rpgpie-sop-0.6.4/src/util.rs000064400000000000000000000024721046102023000140370ustar 00000000000000// SPDX-FileCopyrightText: Heiko Schaefer // SPDX-License-Identifier: MIT OR Apache-2.0 use std::time::SystemTime; use pgp::packet::SignatureType; use pgp::Signature; use rpgpie::certificate::Certificate; use rpgpie::message::MessageResult; use rpgpie::ComponentKeyPub; pub(crate) fn to_verification( signature: &Signature, cert: &Certificate, key: &ComponentKeyPub, source: Option<&str>, ) -> sop::ops::Verification { let ct: SystemTime = (*signature.created().expect("FIXME")).into(); let key_fp = hex::encode(key.fingerprint().as_bytes()); let cert_fp = hex::encode(cert.fingerprint().as_bytes()); let mode = match signature.typ() { SignatureType::Binary => sop::ops::SignatureMode::Binary, SignatureType::Text => sop::ops::SignatureMode::Text, _ => panic!("unexpected data signature type"), }; let mut ver = sop::ops::Verification::new(ct, key_fp, cert_fp, mode, None).expect("FIXME"); if let Some(source) = source { ver.add_signer(source); } ver } pub(crate) fn result_to_verifications(mr: &MessageResult) -> Vec { mr.validated .iter() // FIXME: add source information if available .map(|(cert, key, sig)| to_verification(sig, cert, key, None)) .collect() }