keyfork-shard: begin work on OpenPGP card support
This commit is contained in:
parent
8afcae5447
commit
adad3e5b6b
|
@ -32,6 +32,21 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.5.0"
|
||||
|
@ -234,6 +249,12 @@ version = "3.14.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.4.0"
|
||||
|
@ -261,6 +282,27 @@ dependencies = [
|
|||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "card-backend"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd3ee3a298842065dc489180c34a4fe4bbbb8643bb422009d79558a099fb42e5"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "card-backend-pcsc"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68bb0b707b1b6b058ed93abd70ef65703ed6fd4150d32a0d735b78cfa61cbb35"
|
||||
dependencies = [
|
||||
"card-backend",
|
||||
"iso7816-tlv",
|
||||
"log",
|
||||
"pcsc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
|
@ -291,9 +333,12 @@ version = "0.4.31"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -372,6 +417,12 @@ version = "0.9.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.9"
|
||||
|
@ -446,6 +497,17 @@ dependencies = [
|
|||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"pem-rfc7468",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.8"
|
||||
|
@ -478,6 +540,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"const-oid",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
@ -515,10 +578,10 @@ version = "0.16.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4"
|
||||
dependencies = [
|
||||
"der",
|
||||
"der 0.7.8",
|
||||
"elliptic-curve",
|
||||
"signature 2.1.0",
|
||||
"spki",
|
||||
"spki 0.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -536,7 +599,7 @@ version = "2.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d"
|
||||
dependencies = [
|
||||
"pkcs8",
|
||||
"pkcs8 0.10.2",
|
||||
"signature 2.1.0",
|
||||
]
|
||||
|
||||
|
@ -571,7 +634,7 @@ dependencies = [
|
|||
"ff",
|
||||
"generic-array",
|
||||
"group",
|
||||
"pkcs8",
|
||||
"pkcs8 0.10.2",
|
||||
"rand_core 0.6.4",
|
||||
"sec1",
|
||||
"subtle",
|
||||
|
@ -787,6 +850,12 @@ version = "0.4.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
|
||||
|
||||
[[package]]
|
||||
name = "hex-slice"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5491a308e0214554f07a81d8944abe45f552871c12e3c3c6e7e5d354039a6c4c"
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
|
@ -796,6 +865,29 @@ dependencies = [
|
|||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
|
||||
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 = "idna"
|
||||
version = "0.3.0"
|
||||
|
@ -851,6 +943,15 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iso7816-tlv"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d47365efc3b4c252f8a3384445c0f7e8a4e0ae5c22bf3bedd2dd16f9bb45016a"
|
||||
dependencies = [
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
|
@ -982,7 +1083,9 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"card-backend-pcsc",
|
||||
"keyfork-derive-openpgp",
|
||||
"openpgp-card-sequoia",
|
||||
"sequoia-openpgp",
|
||||
"serde",
|
||||
"sharks",
|
||||
|
@ -1063,6 +1166,9 @@ name = "lazy_static"
|
|||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
|
@ -1086,6 +1192,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
|
@ -1220,6 +1332,44 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[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 0.8.5",
|
||||
"smallvec",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.16"
|
||||
|
@ -1227,6 +1377,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1254,6 +1405,36 @@ version = "1.18.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "openpgp-card"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ba6b39b46a9deba985be9cc960709e284806b550d7e1aff915f8be4b06c3640"
|
||||
dependencies = [
|
||||
"card-backend",
|
||||
"chrono",
|
||||
"hex-slice",
|
||||
"log",
|
||||
"nom",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openpgp-card-sequoia"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7637080b15379df16fef0f81fd2664d403366b7514c721f2231c8974778017c3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"card-backend",
|
||||
"chrono",
|
||||
"log",
|
||||
"openpgp-card",
|
||||
"rsa",
|
||||
"sequoia-openpgp",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
|
@ -1293,12 +1474,40 @@ dependencies = [
|
|||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pcsc"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37cab0be9d04e808a8d8059fa54befcd71dc8b168f9f0c04bdb7e59832abbab4"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"pcsc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pcsc-sys"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1b7bfecba2c0f1b5efb0e7caf7533ab1c295024165bcbb066231f60d33e23ea"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.4"
|
||||
|
@ -1350,14 +1559,36 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs1"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eff33bdbdfc54cc98a2eca766ebdec3e1b8fb7387523d5c9c9a2891da856f719"
|
||||
dependencies = [
|
||||
"der 0.6.1",
|
||||
"pkcs8 0.9.0",
|
||||
"spki 0.6.0",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba"
|
||||
dependencies = [
|
||||
"der 0.6.1",
|
||||
"spki 0.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
"der 0.7.8",
|
||||
"spki 0.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1601,6 +1832,26 @@ dependencies = [
|
|||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55a77d189da1fee555ad95b7e50e7457d91c0e089ec68ca69ad2989413bbdab4"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"digest 0.10.7",
|
||||
"num-bigint-dig",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-traits",
|
||||
"pkcs1",
|
||||
"pkcs8 0.9.0",
|
||||
"rand_core 0.6.4",
|
||||
"signature 2.1.0",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
|
@ -1674,9 +1925,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"der",
|
||||
"der 0.7.8",
|
||||
"generic-array",
|
||||
"pkcs8",
|
||||
"pkcs8 0.10.2",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
@ -1817,6 +2068,7 @@ version = "2.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500"
|
||||
dependencies = [
|
||||
"digest 0.10.7",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
|
@ -1852,6 +2104,22 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der 0.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.2"
|
||||
|
@ -1859,7 +2127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
"der 0.7.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2171,6 +2439,12 @@ version = "0.2.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
|
@ -2283,6 +2557,15 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.51.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
|
|
|
@ -6,13 +6,16 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
default = ["openpgp"]
|
||||
default = ["openpgp", "openpgp-card"]
|
||||
openpgp = ["sequoia-openpgp"]
|
||||
openpgp-card = ["openpgp-card-sequoia", "card-backend-pcsc"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
bincode = "1.3.3"
|
||||
card-backend-pcsc = { version = "0.5.0", optional = true }
|
||||
keyfork-derive-openpgp = { version = "0.1.0", path = "../keyfork-derive-openpgp" }
|
||||
openpgp-card-sequoia = { version = "0.2.0", optional = true }
|
||||
sequoia-openpgp = { version = "1.16.1", optional = true }
|
||||
serde = "1.0.188"
|
||||
sharks = "0.5.0"
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
io::{stdin, stdout},
|
||||
path::PathBuf,
|
||||
process::ExitCode,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use keyfork_shard::openpgp::{combine, discover_certs, parse_messages, openpgp::Cert};
|
||||
use keyfork_shard::openpgp::{combine, discover_certs, openpgp::Cert, parse_messages};
|
||||
|
||||
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
||||
|
||||
fn validate(threshold: &str, key_discovery: &str) -> Result<(u8, Vec<Cert>)> {
|
||||
fn validate(
|
||||
threshold: &str,
|
||||
key_discovery: &str,
|
||||
recovery_file: &str,
|
||||
) -> Result<(u8, Vec<Cert>, PathBuf)> {
|
||||
let threshold = u8::from_str(threshold)?;
|
||||
let key_discovery = PathBuf::from(key_discovery);
|
||||
|
||||
|
@ -20,19 +25,33 @@ fn validate(threshold: &str, key_discovery: &str) -> Result<(u8, Vec<Cert>)> {
|
|||
// Load certs from path
|
||||
let certs = discover_certs(key_discovery)?;
|
||||
|
||||
Ok((threshold, certs))
|
||||
let recovery_file = PathBuf::from(if recovery_file == "=" {
|
||||
eprintln!("loading certs from stdin; note that prompting smartcard devices will not work");
|
||||
"/dev/stdin"
|
||||
} else {
|
||||
recovery_file
|
||||
});
|
||||
|
||||
std::fs::metadata(&recovery_file)?;
|
||||
|
||||
Ok((threshold, certs, recovery_file))
|
||||
}
|
||||
|
||||
fn run() -> Result<()> {
|
||||
let mut args = env::args();
|
||||
let program_name = args.next().expect("program name");
|
||||
let args = args.collect::<Vec<_>>();
|
||||
let (threshold, cert_list) = match args.as_slice() {
|
||||
[threshold, key_discovery] => validate(threshold, key_discovery)?,
|
||||
_ => panic!("Usage: {program_name} threshold key_discovery"),
|
||||
let (threshold, cert_list, recovery_file) = match args.as_slice() {
|
||||
[threshold, key_discovery, recovery_file] => {
|
||||
validate(threshold, key_discovery, recovery_file)?
|
||||
}
|
||||
[threshold, key_discovery] => {
|
||||
validate(threshold, key_discovery, "-")?
|
||||
}
|
||||
_ => panic!("Usage: {program_name} threshold key_discovery recovery_file"),
|
||||
};
|
||||
|
||||
let mut encrypted_messages = parse_messages(stdin())?;
|
||||
let mut encrypted_messages = parse_messages(File::open(recovery_file)?)?;
|
||||
|
||||
let encrypted_metadata = encrypted_messages
|
||||
.pop_front()
|
||||
|
|
|
@ -13,7 +13,10 @@ use openpgp::{
|
|||
armor::{Kind, Writer},
|
||||
cert::{Cert, CertParser, ValidCert},
|
||||
packet::{Packet, Tag, UserID, PKESK, SEIP},
|
||||
parse::{stream::DecryptorBuilder, Parse},
|
||||
parse::{
|
||||
stream::{DecryptionHelper, DecryptorBuilder, VerificationHelper},
|
||||
Parse,
|
||||
},
|
||||
policy::{NullPolicy, Policy, StandardPolicy},
|
||||
serialize::{
|
||||
stream::{ArbitraryWriter, Encryptor, LiteralWriter, Message, Recipient, Signer},
|
||||
|
@ -28,6 +31,9 @@ use sharks::{Share, Sharks};
|
|||
mod keyring;
|
||||
use keyring::Keyring;
|
||||
|
||||
mod smartcard;
|
||||
use smartcard::SmartcardManager;
|
||||
|
||||
// TODO: better error handling
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -57,7 +63,10 @@ impl EncryptedMessage {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn decrypt_with(&self, policy: &'_ dyn Policy, keyring: &mut Keyring) -> Result<Vec<u8>> {
|
||||
pub fn decrypt_with<H>(&self, policy: &'_ dyn Policy, decryptor: H) -> Result<Vec<u8>>
|
||||
where
|
||||
H: VerificationHelper + DecryptionHelper,
|
||||
{
|
||||
let mut packets = vec![];
|
||||
|
||||
for pkesk in &self.pkesks {
|
||||
|
@ -76,7 +85,7 @@ impl EncryptedMessage {
|
|||
message.finalize()?;
|
||||
|
||||
let mut decryptor =
|
||||
DecryptorBuilder::from_bytes(&packets)?.with_policy(policy, None, keyring)?;
|
||||
DecryptorBuilder::from_bytes(&packets)?.with_policy(policy, None, decryptor)?;
|
||||
|
||||
let mut content = vec![];
|
||||
decryptor.read_to_end(&mut content)?;
|
||||
|
@ -222,9 +231,42 @@ pub fn combine(
|
|||
let left_from_threshold = threshold as usize - decrypted_messages.len();
|
||||
if left_from_threshold > 0 {
|
||||
eprintln!("remaining keys: {left_from_threshold}, prompting yubikeys");
|
||||
// TODO: allow decrypt metadata with Yubikey, avoid require stage 1
|
||||
let mut manager =
|
||||
SmartcardManager::new(keyring.root_cert().expect("stage 1 decrypt").clone());
|
||||
let mut remaining_usable_certs = certs
|
||||
.iter()
|
||||
.filter(|cert| messages.contains_key(&cert.keyid()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
while threshold as usize - decrypted_messages.len() > 0 {
|
||||
remaining_usable_certs.retain(|cert| messages.contains_key(&cert.keyid()));
|
||||
let mut fingerprints = HashMap::new();
|
||||
for valid_cert in remaining_usable_certs
|
||||
.iter()
|
||||
.map(|cert| cert.with_policy(&policy, None))
|
||||
{
|
||||
let valid_cert = valid_cert?;
|
||||
fingerprints.insert(
|
||||
valid_cert.keyid(),
|
||||
valid_cert
|
||||
.keys()
|
||||
.for_storage_encryption()
|
||||
.map(|k| k.fingerprint())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
for (cert_id, fingerprints) in fingerprints {
|
||||
if manager.load_any_fingerprint(fingerprints)?.is_some() {
|
||||
// manager is loaded with a Card<Open>, utilize in tx
|
||||
let message = messages.remove(&cert_id);
|
||||
if let Some(message) = message {
|
||||
let message = message.decrypt_with(&policy, &mut manager)?;
|
||||
decrypted_messages.insert(cert_id, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _ in 0..left_from_threshold {
|
||||
todo!("prompt for Yubikeys")
|
||||
}
|
||||
|
||||
let shares = decrypted_messages
|
||||
|
|
|
@ -110,7 +110,7 @@ impl DecryptionHelper for &mut Keyring {
|
|||
where
|
||||
D: FnMut(openpgp::types::SymmetricAlgorithm, &openpgp::crypto::SessionKey) -> bool,
|
||||
{
|
||||
// optimized route: use all locally stored certs
|
||||
// unoptimized route: use all locally stored certs
|
||||
for pkesk in pkesks {
|
||||
for cert in self.get_certs_for_pkesk(pkesk) {
|
||||
for key in cert.keys().secret() {
|
||||
|
@ -129,8 +129,6 @@ impl DecryptionHelper for &mut Keyring {
|
|||
}
|
||||
}
|
||||
|
||||
// smartcard route: plug in smartcard, attempt decrypt, fail and bail
|
||||
|
||||
Err(KeyringFailure::SecretKeyNotFound.into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use super::openpgp::{
|
||||
self,
|
||||
cert::Cert,
|
||||
packet::{PKESK, SKESK},
|
||||
parse::stream::{DecryptionHelper, MessageLayer, MessageStructure, VerificationHelper},
|
||||
Fingerprint,
|
||||
};
|
||||
|
||||
use card_backend_pcsc::PcscBackend;
|
||||
use openpgp_card_sequoia::{state::Open, Card};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SmartcardFailure {
|
||||
#[allow(dead_code)]
|
||||
SmartCardPromptFailed,
|
||||
|
||||
SmartCardNotFound,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SmartcardFailure {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::SmartCardPromptFailed => f.write_str("Attempt to prompt for smart card failed"),
|
||||
Self::SmartCardNotFound => f.write_str("No smart card backend was stored"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for SmartcardFailure {}
|
||||
|
||||
pub struct SmartcardManager {
|
||||
current_card: Option<Card<Open>>,
|
||||
root: Cert,
|
||||
}
|
||||
|
||||
impl SmartcardManager {
|
||||
pub fn new(root: Cert) -> Self {
|
||||
Self {
|
||||
current_card: None,
|
||||
root,
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility function to prompt for a newline from standard input.
|
||||
pub fn prompt(&self, prompt: impl std::fmt::Display) -> std::io::Result<()> {
|
||||
eprint!("{prompt}: ");
|
||||
std::io::stdin().read_line(&mut String::new()).map(|_| ())
|
||||
}
|
||||
|
||||
/// Utility function to obtain a prompt from the command line.
|
||||
pub fn prompt_pin(&self, prompt: impl std::fmt::Display) -> std::io::Result<String> {
|
||||
eprint!("{prompt}: ");
|
||||
let mut output = String::new();
|
||||
std::io::stdin().read_line(&mut output)?;
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Return all [`Fingerprint`] for the currently accessible backends.
|
||||
///
|
||||
/// NOTE: Only implemented for decryption keys.
|
||||
pub fn iter_fingerprints() -> impl Iterator<Item = Fingerprint> {
|
||||
PcscBackend::cards(None).into_iter().flat_map(|iter| {
|
||||
iter.filter_map(|backend| {
|
||||
let backend = backend.ok()?;
|
||||
let mut card = Card::<Open>::new(backend).ok()?;
|
||||
let transaction = card.transaction().ok()?;
|
||||
transaction
|
||||
.fingerprints()
|
||||
.ok()?
|
||||
.decryption()
|
||||
.map(|fp| Fingerprint::from_bytes(fp.as_bytes()))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Load a backend if any [`Fingerprint`] has been matched by a currently active card.
|
||||
///
|
||||
/// NOTE: Only implemented for decryption keys.
|
||||
pub fn load_any_fingerprint(
|
||||
&mut self,
|
||||
fingerprints: impl IntoIterator<Item = Fingerprint>,
|
||||
) -> Result<Option<Fingerprint>, Box<dyn std::error::Error>> {
|
||||
// NOTE: This can't be HashSet::from_iter() because from_iter() requires a passed-in state
|
||||
// I do not want to provide.
|
||||
let mut requested_fingerprints = HashSet::new();
|
||||
requested_fingerprints.extend(fingerprints);
|
||||
|
||||
let mut had_any_backend = false;
|
||||
|
||||
while !had_any_backend {
|
||||
// Load all backends, confirm if any have any fingerprints
|
||||
for backend in PcscBackend::cards(None)? {
|
||||
had_any_backend = true;
|
||||
let backend = backend?;
|
||||
let mut card = Card::<Open>::new(backend)?;
|
||||
let transaction = card.transaction()?;
|
||||
let mut fingerprint = None;
|
||||
if let Some(fp) = transaction
|
||||
.fingerprints()?
|
||||
.decryption()
|
||||
.map(|fp| Fingerprint::from_bytes(fp.as_bytes()))
|
||||
{
|
||||
if requested_fingerprints.contains(&fp) {
|
||||
fingerprint.replace(fp);
|
||||
}
|
||||
}
|
||||
drop(transaction);
|
||||
if fingerprint.is_some() {
|
||||
self.current_card.replace(card);
|
||||
return Ok(fingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!("No matching smartcard detected.");
|
||||
self.prompt("Please plug in a smart card and press enter")?;
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl VerificationHelper for &mut SmartcardManager {
|
||||
fn get_certs(&mut self, ids: &[openpgp::KeyHandle]) -> openpgp::Result<Vec<Cert>> {
|
||||
Ok(ids
|
||||
.iter()
|
||||
.flat_map(|kh| {
|
||||
if &self.root.key_handle() == kh {
|
||||
Some(self.root.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn check(&mut self, structure: MessageStructure) -> openpgp::Result<()> {
|
||||
for layer in structure.into_iter() {
|
||||
#[allow(unused_variables)]
|
||||
match layer {
|
||||
MessageLayer::Compression { algo } => {}
|
||||
MessageLayer::Encryption {
|
||||
sym_algo,
|
||||
aead_algo,
|
||||
} => {}
|
||||
MessageLayer::SignatureGroup { results } => {
|
||||
for result in results {
|
||||
if let Err(e) = result {
|
||||
// FIXME: anyhow leak
|
||||
return Err(anyhow::anyhow!(e.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DecryptionHelper for &mut SmartcardManager {
|
||||
fn decrypt<D>(
|
||||
&mut self,
|
||||
pkesks: &[PKESK],
|
||||
_skesks: &[SKESK],
|
||||
sym_algo: Option<openpgp::types::SymmetricAlgorithm>,
|
||||
mut decrypt: D,
|
||||
) -> openpgp::Result<Option<Fingerprint>>
|
||||
where
|
||||
D: FnMut(openpgp::types::SymmetricAlgorithm, &openpgp::crypto::SessionKey) -> bool,
|
||||
{
|
||||
let mut card = self.current_card.take();
|
||||
let Some(card) = card.as_mut() else {
|
||||
return Err(SmartcardFailure::SmartCardNotFound.into());
|
||||
};
|
||||
|
||||
let mut transaction = card.transaction()?;
|
||||
let fp = transaction
|
||||
.fingerprints()?
|
||||
.decryption()
|
||||
.map(|fp| Fingerprint::from_bytes(fp.as_bytes()));
|
||||
let pin = self.prompt_pin("Please enter PIN to unlock card")?;
|
||||
let mut user = transaction.to_user_card(pin.as_str())?;
|
||||
let mut decryptor =
|
||||
user.decryptor(&|| println!("Touch confirmation needed for decryption"))?;
|
||||
for pkesk in pkesks {
|
||||
if pkesk
|
||||
.decrypt(&mut decryptor, sym_algo)
|
||||
.map(|(algo, sk)| decrypt(algo, &sk))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return Ok(fp);
|
||||
}
|
||||
}
|
||||
|
||||
Err(SmartcardFailure::SmartCardNotFound.into())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue