10 Commits
Author SHA1 Message Date
nelsbrock 20c0a919c5 refactor: replace with_context calls with context calls 2026-05-10 18:13:47 +02:00
nelsbrock 3ea0de1cdb refactor: remove ref_self variable 2026-05-10 18:08:14 +02:00
nelsbrock 9f992caef9 refactor: fix some pedantic clippy lints 2026-05-10 18:02:54 +02:00
nelsbrock c8bedabe1f fix: determine workers count before prompting for password 2026-05-10 17:51:43 +02:00
nelsbrock e8b83d41cb make small improvements to CLI
- set meaningful value names for options
- add backticks around CLI options in user-facing messages
2026-05-10 17:34:56 +02:00
nelsbrock 6af66fd292 add --workers option 2026-05-10 17:28:01 +02:00
nelsbrock bc07cba585 remove unnecessary clone() 2026-05-10 16:21:11 +02:00
nelsbrock c6b5020795 release version 0.3.0 2026-05-09 16:30:04 +02:00
nelsbrock c74dcd0f01 add --userid option 2026-05-09 16:27:59 +02:00
nelsbrock 06e528ac09 upgrade and update dependencies 2026-05-09 16:27:32 +02:00
5 changed files with 217 additions and 158 deletions
Generated
+111 -113
View File
@@ -156,9 +156,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "2.11.0"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
[[package]]
name = "blake2"
@@ -234,9 +234,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.59"
version = "1.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283"
checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -289,9 +289,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.6.0"
version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351"
checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
dependencies = [
"clap_builder",
"clap_derive",
@@ -311,9 +311,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.6.0"
version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a"
checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
dependencies = [
"heck",
"proc-macro2",
@@ -446,14 +446,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.61.2",
]
[[package]]
name = "fancy-regex"
version = "0.17.0"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8"
checksum = "e1e1dacd0d2082dfcf1351c4bdd566bbe89a2b263235a2b50058f1e130a47277"
dependencies = [
"bit-set",
"regex-automata",
@@ -462,9 +462,9 @@ dependencies = [
[[package]]
name = "fastrand"
version = "2.3.0"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
[[package]]
name = "find-msvc-tools"
@@ -474,14 +474,13 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "fingerprunk"
version = "0.2.2"
version = "0.3.0"
dependencies = [
"anyhow",
"clap",
"ctrlc",
"fancy-regex",
"num-integer",
"num_cpus",
"rpassword",
"sequoia-openpgp",
]
@@ -508,6 +507,30 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "futures-core"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-task"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-util"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.7"
@@ -520,9 +543,9 @@ dependencies = [
[[package]]
name = "generic-array"
version = "1.3.5"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542"
checksum = "dab9e9188e97a93276e1fe7b56401b851e2b45a46d045ca658100c1303ada649"
dependencies = [
"rustversion",
"typenum",
@@ -571,9 +594,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.16.1"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "heck"
@@ -581,12 +604,6 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "iana-time-zone"
version = "0.1.65"
@@ -712,9 +729,9 @@ dependencies = [
[[package]]
name = "idna_adapter"
version = "1.2.1"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714"
dependencies = [
"icu_normalizer",
"icu_properties",
@@ -722,12 +739,12 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.13.1"
version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
"hashbrown 0.17.1",
"serde",
"serde_core",
]
@@ -764,10 +781,12 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "js-sys"
version = "0.3.94"
version = "0.3.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9"
checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08"
dependencies = [
"cfg-if",
"futures-util",
"once_cell",
"wasm-bindgen",
]
@@ -820,15 +839,15 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libbz2-rs-sys"
version = "0.2.2"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
checksum = "b3a6a8c165077efc8f3a971534c50ea6a1a18b329ef4a66e897a7e3a1494565f"
[[package]]
name = "libc"
version = "0.2.184"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "libloading"
@@ -897,14 +916,14 @@ dependencies = [
[[package]]
name = "nettle"
version = "7.4.0"
version = "7.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44e6ff4a94e5d34a1fd5abbd39418074646e2fa51b257198701330f22fcd6936"
checksum = "2578a3627c28fefb60f1680e20e85f38bd8c8bf98c288b370489a573b0640907"
dependencies = [
"getrandom 0.2.17",
"getrandom 0.4.2",
"libc",
"nettle-sys",
"thiserror 1.0.69",
"thiserror",
"typenum",
]
@@ -968,16 +987,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "objc2"
version = "0.6.4"
@@ -1059,10 +1068,16 @@ dependencies = [
]
[[package]]
name = "pkg-config"
version = "0.3.32"
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "pkg-config"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
[[package]]
name = "potential_utf"
@@ -1159,23 +1174,23 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "rpassword"
version = "7.4.0"
version = "7.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d4c8b64f049c6721ec8ccec37ddfc3d641c4a7fca57e8f2a89de509c73df39"
checksum = "5ac5b223d9738ef56e0b98305410be40fa0941bf6036c56f1506751e43552d64"
dependencies = [
"libc",
"rtoolbox",
"windows-sys 0.59.0",
"windows-sys 0.61.2",
]
[[package]]
name = "rtoolbox"
version = "0.0.3"
version = "0.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7cc970b249fbe527d6e02e0a227762c9108b2f49d81094fe357ffc6d14d7f6f"
checksum = "50a0e551c1e27e1731aba276dbeaeac73f53c7cd34d1bda485d02bd1e0f36844"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -1194,7 +1209,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
"windows-sys 0.61.2",
]
[[package]]
@@ -1248,7 +1263,7 @@ dependencies = [
"regex",
"regex-syntax",
"sha1collisiondetection",
"thiserror 2.0.18",
"thiserror",
"xxhash-rust",
]
@@ -1301,14 +1316,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f606421e4a6012877e893c399822a4ed4b089164c5969424e1b9d1e66e6964b"
dependencies = [
"digest",
"generic-array 1.3.5",
"generic-array 1.4.1",
]
[[package]]
name = "sha3"
version = "0.10.8"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874"
dependencies = [
"digest",
"keccak",
@@ -1328,9 +1343,15 @@ checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
[[package]]
name = "siphasher"
version = "1.0.2"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "smallvec"
@@ -1400,7 +1421,7 @@ dependencies = [
"getrandom 0.4.2",
"once_cell",
"rustix",
"windows-sys 0.52.0",
"windows-sys 0.61.2",
]
[[package]]
@@ -1409,16 +1430,7 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1"
dependencies = [
"windows-sys 0.59.0",
]
[[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",
"windows-sys 0.61.2",
]
[[package]]
@@ -1427,18 +1439,7 @@ version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl 2.0.18",
]
[[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",
"thiserror-impl",
]
[[package]]
@@ -1464,9 +1465,9 @@ dependencies = [
[[package]]
name = "typenum"
version = "1.19.0"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
[[package]]
name = "unicode-ident"
@@ -1522,11 +1523,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.2+wasi-0.2.9"
version = "1.0.3+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
dependencies = [
"wit-bindgen",
"wit-bindgen 0.57.1",
]
[[package]]
@@ -1535,14 +1536,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen",
"wit-bindgen 0.51.0",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.117"
version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0"
checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790"
dependencies = [
"cfg-if",
"once_cell",
@@ -1553,9 +1554,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.117"
version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be"
checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1563,9 +1564,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.117"
version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2"
checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -1576,9 +1577,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.117"
version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b"
checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441"
dependencies = [
"unicode-ident",
]
@@ -1623,7 +1624,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.52.0",
"windows-sys 0.61.2",
]
[[package]]
@@ -1685,15 +1686,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
@@ -1785,6 +1777,12 @@ dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
+4 -5
View File
@@ -1,6 +1,6 @@
[package]
name = "fingerprunk"
version = "0.2.2"
version = "0.3.0"
authors = ["Niklas Elsbrock <mail@nelsbrock.de>"]
edition = "2024"
description = "CLI tool for brute-forcing OpenPGP keys with cool fingerprints"
@@ -11,10 +11,9 @@ categories = ["command-line-utilities"]
[dependencies]
anyhow = "1.0.102"
clap = { version = "4.6.0", features = ["derive"] }
clap = { version = "4.6.1", features = ["derive"] }
ctrlc = "3.5.2"
fancy-regex = "0.17.0"
fancy-regex = "0.18.0"
num-integer = "0.1.46"
num_cpus = "1.17.0"
rpassword = "7.4.0"
rpassword = "7.5.2"
sequoia-openpgp = "2.2.0"
+2 -2
View File
@@ -18,11 +18,11 @@ at `secret.asc`. The regex for this is `^C0FFEE`. Now, simply use the following
the search:
```sh
fingerprunk -r '^C0FFEE' >> secret.asc
fingerprunk -r '^C0FFEE' -u "Your Name <your.email@example.org>" >> secret.asc
```
Fingerprunk will now generate many keys and write out all keys with matching fingerprints to
standard output (here: `secret.asc`).
standard output (here: `secret.asc`), adding the provided user ID.
If you want Fingerprunk to output password-encrypted keys use the `-p` flag and you will be prompted
for a password.
+55 -28
View File
@@ -3,13 +3,13 @@
use std::{
fmt::{self, Write},
io,
num::NonZeroU64,
num::NonZero,
sync::{
atomic::{AtomicBool, AtomicU64, Ordering},
mpsc,
},
thread,
time::{Duration, Instant},
time::{Duration, Instant, SystemTime},
};
use fancy_regex::Regex;
@@ -18,7 +18,7 @@ use sequoia_openpgp::{
Cert, Packet, armor,
crypto::Password,
packet::{
Key,
Key, UserID,
key::{Key4, PrimaryRole, SecretParts},
prelude::SignatureBuilder,
},
@@ -38,8 +38,10 @@ enum Message {
pub struct Config {
pub regex: Regex,
pub status_enabled: bool,
pub stop_after: Option<NonZeroU64>,
pub stop_after: Option<NonZero<u64>>,
pub password: Option<Password>,
pub userids: Vec<UserID>,
pub workers: NonZero<usize>,
}
#[derive(Debug)]
@@ -82,24 +84,20 @@ impl Fingerprunk {
}
thread::scope(|scope| {
let ref_self = &self;
let status_displayer = if self.config.status_enabled {
Some(
thread::Builder::new()
.name("status_displayer".to_string())
.spawn_scoped(scope, move || ref_self.status_displayer_thread())?,
.spawn_scoped(scope, || self.status_displayer_thread())?,
)
} else {
None
};
for num in 0..num_cpus::get() {
let sender = sender.clone();
for num in 0..self.config.workers.get() {
thread::Builder::new()
.name(format!("worker-{num:03}"))
.spawn_scoped(scope, move || ref_self.worker_thread(sender))?;
.spawn_scoped(scope, || self.worker_thread(&sender))?;
}
let mut stdout = io::stdout().lock();
@@ -108,8 +106,8 @@ impl Fingerprunk {
for message in receiver {
match message {
Message::Key(key) => {
let cert = self.key_to_cert(&key)?;
self.serialize_cert(cert, &mut stdout)?;
let cert = self.key_to_cert(key)?;
self.serialize_cert(&cert, &mut stdout)?;
// Increase "found" counter and stop if enough matches have been found
let prev = self.counter_found.fetch_add(1, Ordering::Relaxed);
@@ -133,7 +131,7 @@ impl Fingerprunk {
})
}
fn worker_thread(&self, sender: mpsc::SyncSender<Message>) {
fn worker_thread(&self, sender: &mpsc::SyncSender<Message>) {
let mut fingerprint_hex = String::with_capacity(20 * 2);
while !self.stop.load(Ordering::Relaxed) {
@@ -159,35 +157,50 @@ impl Fingerprunk {
.expect("should check regex without error")
}
fn key_to_cert(&self, key: &SecretKey) -> anyhow::Result<Cert> {
let sig = SignatureBuilder::new(SignatureType::DirectKey)
.set_hash_algo(HashAlgorithm::SHA512)
.set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512, HashAlgorithm::SHA256])?
.set_preferred_symmetric_algorithms(vec![
SymmetricAlgorithm::AES256,
SymmetricAlgorithm::AES128,
])?;
fn key_to_cert(&self, mut key: SecretKey) -> anyhow::Result<Cert> {
let creation_time = SystemTime::now();
let mut signer = key
.clone()
.into_keypair()
.expect("key should have a secret");
let sig = sig.sign_direct_key(&mut signer, key.parts_as_public())?;
let secret_key_packet = Packet::SecretKey({
let mut key = key.clone();
// Sign keypair
let key_sig = create_sig_builder(SignatureType::DirectKey, creation_time)?
.sign_direct_key(&mut signer, key.parts_as_public())?;
// Create certificate
let mut cert = Cert::try_from(Packet::SecretKey({
if let Some(ref password) = self.config.password {
let (k, mut secret) = key.take_secret();
secret.encrypt_in_place(&k, password)?;
key = k.add_secret(secret).0;
}
key
});
}))?;
Cert::try_from(vec![secret_key_packet, Packet::from(sig)])
let mut packets = vec![Packet::from(key_sig)];
// Sign user IDs
let mut next_is_primary = true;
for user_id in self.config.userids.iter().cloned() {
let mut sig_builder =
create_sig_builder(SignatureType::PositiveCertification, creation_time)?;
if next_is_primary {
sig_builder = sig_builder.set_primary_userid(true)?;
next_is_primary = false;
}
let sig = user_id.bind(&mut signer, &cert, sig_builder)?;
packets.push(user_id.into());
packets.push(sig.into());
}
fn serialize_cert(&self, cert: Cert, to: impl io::Write) -> anyhow::Result<()> {
cert = cert.insert_packets(packets)?.0;
Ok(cert)
}
fn serialize_cert(&self, cert: &Cert, to: impl io::Write) -> anyhow::Result<()> {
let mut comments = cert.armor_headers();
comments.push(format!(
"Generated with Fingerprunk. Regex: {}",
@@ -256,3 +269,17 @@ impl Fingerprunk {
);
}
}
fn create_sig_builder(
typ: SignatureType,
creation_time: SystemTime,
) -> Result<SignatureBuilder, anyhow::Error> {
SignatureBuilder::new(typ)
.set_signature_creation_time(creation_time)?
.set_hash_algo(HashAlgorithm::SHA512)
.set_preferred_hash_algorithms(vec![HashAlgorithm::SHA512, HashAlgorithm::SHA256])?
.set_preferred_symmetric_algorithms(vec![
SymmetricAlgorithm::AES256,
SymmetricAlgorithm::AES128,
])
}
+45 -10
View File
@@ -1,12 +1,13 @@
use std::{
io::{self, IsTerminal},
num::NonZeroU64,
num::NonZero,
};
use anyhow::{Context as AnyhowContext, anyhow};
use anyhow::{Context, anyhow};
use clap::{ArgAction, Parser, ValueEnum};
use fancy_regex::Regex;
use fingerprunk::Fingerprunk;
use sequoia_openpgp::packet::UserID;
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
@@ -30,15 +31,32 @@ struct Args {
status: StatusEnabled,
/// Stop once the specified number of matching keys has been found.
#[arg(long)]
stop_after: Option<NonZeroU64>,
#[arg(long, value_name = "NUM")]
stop_after: Option<NonZero<u64>>,
/// Prompt for a password and use it to encrypt found keys.
/// Prompt for a password and use it to encrypt matching keys.
///
/// By default, found keys are printed to stdout unencrypted. Use this if you actually plan to
/// use generated keys.
#[arg(short, long, action = ArgAction::SetTrue)]
password: bool,
/// Add the given user ID to matching keys.
#[arg(short, long = "userid")]
userid: Vec<UserID>,
/// Explicitly do not add user IDs to matching keys.
///
/// Disables the warning about importing keys without user IDs into GnuPG.
#[arg(long, conflicts_with = "userid", action = ArgAction::SetTrue)]
no_userid: bool,
/// Use the specified amount of worker threads.
///
/// If not specified, the amount of worker threads will be set to the amount of the machine's
/// available parallelism.
#[arg(long, value_name = "NUM")]
workers: Option<NonZero<usize>>,
}
#[derive(ValueEnum, Clone, Copy, Debug, Default)]
@@ -62,16 +80,33 @@ impl StatusEnabled {
fn main() -> anyhow::Result<()> {
let args = Args::parse();
let workers = match args.workers {
Some(workers) => workers,
None => std::thread::available_parallelism().context(
"unable to determine available parallelism, \
use `--workers <NUM>` to specify amount of worker threads",
)?,
};
if !args.no_userid && args.userid.is_empty() {
eprintln!(
"WARNING: No user ID was provided.\n\
You may experience problems importing generated keys into GnuPG.\n\
Use `--userid <USERID>` to add a user ID.\n"
);
}
let password = if args.password {
let password = rpassword::prompt_password(
"Enter password for encrypting found keys (leave empty for no encryption): ",
)
.with_context(|| "Failed to prompt password")?;
.context("Failed to prompt password")?;
if password.is_empty() {
None
} else {
let password_retype = rpassword::prompt_password("Retype password: ")
.with_context(|| "Failed to prompt password retype")?;
.context("Failed to prompt password retype")?;
if password_retype == password {
Some(password.into())
} else {
@@ -87,9 +122,9 @@ fn main() -> anyhow::Result<()> {
status_enabled: args.status.evaluate(),
stop_after: args.stop_after,
password,
userids: args.userid,
workers,
};
Fingerprunk::new_from_config(config).run()?;
Ok(())
Fingerprunk::new_from_config(config).run()
}