Compare commits

..

No commits in common. "b8c1fc1a93f4023757ad63757a3847b64876c9dd" and "dd3ffe74b3c027f6f683a1beaeeb9ef0e23249d1" have entirely different histories.

6 changed files with 131 additions and 83 deletions

92
Cargo.lock generated
View File

@ -324,9 +324,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base64"
version = "0.21.6"
version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c79fed4cdb43e993fcdadc7e58a09fd0e3e649c4436fa11da71c9f1f3ee7feb9"
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
[[package]]
name = "base64ct"
@ -568,9 +568,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.4.14"
version = "4.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33e92c5c1a78c62968ec57dbc2440366a2d6e5a23faf829970ff1585dc6b18e2"
checksum = "52bdc885e4cacc7f7c9eedc1ef6da641603180c783c41a15c264944deeaab642"
dependencies = [
"clap_builder",
"clap_derive",
@ -578,9 +578,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.4.14"
version = "4.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4323769dc8a61e2c39ad7dc26f6f2800524691a44d74fe3d1071a5c24db6370"
checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9"
dependencies = [
"anstream",
"anstyle",
@ -624,14 +624,14 @@ dependencies = [
[[package]]
name = "console"
version = "0.15.8"
version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"windows-sys 0.52.0",
"windows-sys 0.45.0",
]
[[package]]
@ -1143,9 +1143,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.12"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
"cfg-if",
"js-sys",
@ -1393,9 +1393,9 @@ dependencies = [
[[package]]
name = "k256"
version = "0.13.3"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b"
checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b"
dependencies = [
"cfg-if",
"ecdsa",
@ -3177,6 +3177,15 @@ dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
@ -3195,6 +3204,21 @@ dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
@ -3225,6 +3249,12 @@ dependencies = [
"windows_x86_64_msvc 0.52.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
@ -3237,6 +3267,12 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
@ -3249,6 +3285,12 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
@ -3261,6 +3303,12 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
@ -3273,6 +3321,12 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
@ -3285,6 +3339,12 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
@ -3297,6 +3357,12 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"

View File

@ -1,10 +1,7 @@
[package]
name = "keyfork-prompt"
version = "0.1.0"
description = "Prompt management utilities for Keyfork"
repository = "https://git.distrust.co/public/keyfork"
edition = "2021"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,7 +1,7 @@
use std::{
env,
fs::File,
path::{Path, PathBuf},
io::stdin,
path::PathBuf,
process::ExitCode,
};
@ -9,11 +9,10 @@ use keyfork_shard::openpgp::{combine, discover_certs, openpgp::Cert, parse_messa
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
fn validate(
shard: impl AsRef<Path>,
key_discovery: Option<&str>,
) -> Result<(File, Vec<Cert>)> {
let key_discovery = key_discovery.map(PathBuf::from);
fn validate<'a>(
key_discovery: impl Into<Option<&'a str>>,
) -> Result<Vec<Cert>> {
let key_discovery = key_discovery.into().map(PathBuf::from);
key_discovery.as_ref().map(std::fs::metadata).transpose()?;
// Load certs from path
@ -22,20 +21,20 @@ fn validate(
.transpose()?
.unwrap_or(vec![]);
Ok((File::open(shard)?, certs))
Ok(certs)
}
fn run() -> Result<()> {
let mut args = env::args();
let program_name = args.next().expect("program name");
let args = args.collect::<Vec<_>>();
let (messages_file, cert_list) = match args.as_slice() {
[shard, key_discovery] => validate(shard, Some(key_discovery))?,
[shard] => validate(shard, None)?,
_ => panic!("Usage: {program_name} <shard> [key_discovery]"),
let cert_list = match args.as_slice() {
[key_discovery] => validate(key_discovery.as_str())?,
[] => validate(None)?,
_ => panic!("Usage: {program_name} threshold [key_discovery]"),
};
let mut encrypted_messages = parse_messages(messages_file)?;
let mut encrypted_messages = parse_messages(stdin())?;
let encrypted_metadata = encrypted_messages
.pop_front()

View File

@ -4,9 +4,8 @@ Combine `threshold` shares into a previously [`split`] secret.
## Arguments
`keyfork-shard-combine-openpgp <shard> [key_discovery]`
`keyfork-shard-combine-openpgp [key_discovery]`
* `shard`: The shard file to read from.
* `key_discovery`: A file or directory containing OpenPGP keys.
If the number of keys found is less than `threshold`, an OpenPGP Card
fallback will be used to decrypt the rest of the messages.
@ -18,6 +17,10 @@ The terminal may be overridden if the default pinentry command is
used if an OpenPGP key file has an encrypted secret key or to prompt for the
PIN for an OpenPGP smart card.
## Input
OpenPGP messages from [`split`].
## Output
Hex-encoded secret.
@ -26,10 +29,10 @@ Hex-encoded secret.
```sh
# Decrypt using only smartcards
keyfork-shard-combine-openpgp shard.pgp
keyfork-shard-combine-openpgp < shard.pgp
# Decrypt using on-disk private keys
keyfork-shard-combine-openpgp key_discovery.pgp shard.pgp
keyfork-shard-combine-openpgp key_discovery.pgp < shard.pgp
```
[`split`]: ./split.md

View File

@ -68,9 +68,8 @@ Combine `threshold` shares into a secret.
### Arguments
`keyfork shard combine <shard> [key_discovery]`
`keyfork shard combine [key_discovery]`
* `shard`: A file containing the encrypted shards.
* `key_discovery`: Either a file or a directory containing public keys.
If a file, load all private keys from a file.
If a directory, for every file in the directory (non-recursively), load
@ -78,6 +77,11 @@ Combine `threshold` shares into a secret.
If the amount of keys found is less than `threshold`, it is up to the format
to determine how to discover the keys.
### Input
The input of the command is dependent on the format, but should be the exact
same as the output from the `split` command previously used.
### Output
Hex-encoded secret.
@ -102,9 +106,8 @@ by a remote recovery operator.
### Arguments
`keyfork shard transport <shard> [key_discovery]`
`keyfork shard transport [key_discovery]`
* `shard`: A file containing encrypted shards.
* `key_discovery`: Either a file or a directory containing public keys.
If a file, load all private keys from a file.
If a directory, for every file in the directory (non-recursively), load
@ -112,6 +115,11 @@ by a remote recovery operator.
If the amount of keys found is less than `threshold`, it is up to the format
to determine how to discover the keys.
### Input
The input of the command is dependent on the format, but should be the exact
same as the output from the `split` command previously used.
### Prompts
The command will prompt for 33 words from the remote shard recovery operator,
@ -123,10 +131,10 @@ operator.
```sh
# Transport using a smart card
keyfork shard transport shard.pgp
keyfork shard transport < shard.pgp
# Transport using on-disk private keys
keyfork shard transport key_discovery.pgp shard.pgp
keyfork shard transport key_discovery.pgp < shard.pgp
```
[`keyfork recover remote-shard`]: ../recover/index.md#keyfork-recover-remote-shard

View File

@ -164,9 +164,6 @@ pub enum ShardSubcommands {
/// Decrypt a single share and re-encrypt it to an ephemeral symmetric key using mnemonic-based
/// prompts. The mnemonics can be sent over insecure channels.
Transport {
/// The path to load the shard from.
shard: PathBuf,
/// The path to discover private keys from.
key_discovery: Option<PathBuf>,
},
@ -178,9 +175,6 @@ pub enum ShardSubcommands {
/// hardware metadata discovery, any hardware key used to split may be used to decrypt metadata
/// used to combine.
Combine {
/// The path to load the shards from.
shard: PathBuf,
/// The path to discover private keys from.
key_discovery: Option<PathBuf>,
},
@ -192,16 +186,21 @@ impl ShardSubcommands {
shard: &Shard,
_keyfork: &Keyfork,
) -> Result<(), Box<dyn std::error::Error>> {
let stdin = stdin();
let mut stdin = stdin();
let mut stdout = stdout();
let mut input = String::new();
stdin.read_to_string(&mut input)?;
let mut format = shard.format.clone();
// bang sandwich macro fun
if input.contains("BEGIN PGP MESSAGE") && !matches!(self, ShardSubcommands::Split { .. }) {
let _ = format.insert(Format::OpenPGP(OpenPGP));
}
match self {
ShardSubcommands::Split {
threshold,
max,
key_discovery,
} => {
let input = std::io::read_to_string(stdin)?;
assert!(threshold <= max, "threshold {threshold} <= max {max}");
let secret = smex::decode(input.trim())?;
match format {
@ -214,44 +213,20 @@ impl ShardSubcommands {
None => panic!("{COULD_NOT_DETERMINE_FORMAT}"),
}
}
ShardSubcommands::Transport {
shard,
key_discovery,
} => {
let shard_content = std::fs::read_to_string(shard)?;
if shard_content.contains("BEGIN PGP MESSAGE") {
let _ = format.insert(Format::OpenPGP(OpenPGP));
}
match format {
Some(Format::OpenPGP(o)) => {
o.decrypt(key_discovery.as_ref(), shard_content.as_bytes())
}
ShardSubcommands::Transport { key_discovery } => match format {
Some(Format::OpenPGP(o)) => o.decrypt(key_discovery.as_ref(), input.as_bytes()),
Some(Format::P256(_p)) => todo!(),
None => panic!("{COULD_NOT_DETERMINE_FORMAT}"),
},
ShardSubcommands::Combine { key_discovery } => match format {
Some(Format::OpenPGP(o)) => {
o.combine(key_discovery.as_ref(), input.as_bytes(), &mut stdout)
}
}
ShardSubcommands::Combine {
shard,
key_discovery,
} => {
let shard_content = std::fs::read_to_string(shard)?;
if shard_content.contains("BEGIN PGP MESSAGE") {
let _ = format.insert(Format::OpenPGP(OpenPGP));
}
match format {
Some(Format::OpenPGP(o)) => o.combine(
key_discovery.as_ref(),
shard_content.as_bytes(),
&mut stdout,
),
Some(Format::P256(_p)) => {
todo!()
}
None => panic!("{COULD_NOT_DETERMINE_FORMAT}"),
}
}
},
}
}
}