keyfork-user-guide: more additions

This commit is contained in:
Ryan Heywood 2023-10-19 08:53:59 -05:00
parent 8809db6f7f
commit 5d5d5181b3
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
10 changed files with 157 additions and 93 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
target target
testing_data

View File

@ -1,4 +1,4 @@
use std::{env, str::FromStr}; use std::{env, process::ExitCode, str::FromStr};
use keyfork_derive_util::{ use keyfork_derive_util::{
request::{DerivationAlgorithm, DerivationError, DerivationRequest}, request::{DerivationAlgorithm, DerivationError, DerivationRequest},
@ -27,7 +27,7 @@ fn validate(algo: &str, path: &str) -> Result<(DerivationAlgorithm, DerivationPa
Ok((algo, path)) Ok((algo, path))
} }
fn main() -> Result<(), Box<dyn std::error::Error>> { fn run() -> Result<(), Box<dyn std::error::Error>> {
let mut args = env::args(); let mut args = env::args();
let program_name = args.next().expect("program name"); let program_name = args.next().expect("program name");
let args = args.collect::<Vec<_>>(); let args = args.collect::<Vec<_>>();
@ -42,3 +42,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("{}", smex::encode(&response.data)); println!("{}", smex::encode(&response.data));
Ok(()) Ok(())
} }
fn main() -> ExitCode {
if let Err(e) = run() {
eprintln!("Error: {e}");
ExitCode::FAILURE
} else {
ExitCode::SUCCESS
}
}

View File

@ -1,5 +1,4 @@
use std::{ use std::{
collections::VecDeque,
env, env,
io::{stdin, stdout}, io::{stdin, stdout},
path::PathBuf, path::PathBuf,
@ -7,30 +6,7 @@ use std::{
str::FromStr, str::FromStr,
}; };
use keyfork_shard::{combine, discover_certs, openpgp::Cert, EncryptedMessage}; use keyfork_shard::{combine, discover_certs, parse_messages, openpgp::Cert};
use openpgp::{
packet::Packet,
parse::Parse,
PacketPile,
};
use sequoia_openpgp as openpgp;
#[derive(Clone, Debug)]
enum Error {
Usage(String),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Usage(program_name) => {
write!(f, "Usage: {program_name} threshold key_discovery")
}
}
}
}
impl std::error::Error for Error {}
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>; type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
@ -53,25 +29,10 @@ fn run() -> Result<()> {
let args = args.collect::<Vec<_>>(); let args = args.collect::<Vec<_>>();
let (threshold, cert_list) = match args.as_slice() { let (threshold, cert_list) = match args.as_slice() {
[threshold, key_discovery] => validate(threshold, key_discovery)?, [threshold, key_discovery] => validate(threshold, key_discovery)?,
_ => return Err(Error::Usage(program_name).into()), _ => panic!("Usage: {program_name} threshold key_discovery"),
}; };
let stdin = stdin(); let mut encrypted_messages = parse_messages(stdin())?;
let mut pkesks = Vec::new();
let mut encrypted_messages = VecDeque::new();
for packet in PacketPile::from_reader(stdin)?.into_children() {
match packet {
Packet::PKESK(p) => pkesks.push(p),
Packet::SEIP(s) => {
encrypted_messages.push_back(EncryptedMessage::with_swap(&mut pkesks, s));
}
s => {
panic!("Invalid variant found: {}", s.tag());
}
}
}
let encrypted_metadata = encrypted_messages let encrypted_metadata = encrypted_messages
.pop_front() .pop_front()

View File

@ -1,5 +1,5 @@
use std::{ use std::{
collections::HashMap, collections::{HashMap, VecDeque},
io::{Read, Write}, io::{Read, Write},
path::Path, path::Path,
str::FromStr, str::FromStr,
@ -12,7 +12,7 @@ use keyfork_derive_openpgp::derive_util::{
use openpgp::{ use openpgp::{
armor::{Kind, Writer}, armor::{Kind, Writer},
cert::{Cert, CertParser, ValidCert}, cert::{Cert, CertParser, ValidCert},
packet::{Tag, UserID, PKESK, SEIP}, packet::{Packet, Tag, UserID, PKESK, SEIP},
parse::{stream::DecryptorBuilder, Parse}, parse::{stream::DecryptorBuilder, Parse},
policy::{NullPolicy, Policy, StandardPolicy}, policy::{NullPolicy, Policy, StandardPolicy},
serialize::{ serialize::{
@ -20,7 +20,7 @@ use openpgp::{
Marshal, Marshal,
}, },
types::KeyFlags, types::KeyFlags,
KeyID, KeyID, PacketPile
}; };
pub use sequoia_openpgp as openpgp; pub use sequoia_openpgp as openpgp;
use sharks::{Share, Sharks}; use sharks::{Share, Sharks};
@ -75,24 +75,6 @@ impl EncryptedMessage {
message.write_all(&packet)?; message.write_all(&packet)?;
message.finalize()?; message.finalize()?;
/*
// TODO: only serialize the message and use provided PKESK vec
let mut pkesks = vec![];
let ppr = PacketParser::from_reader(&packets[..])?;
while let PacketParserResult::Some(mut pp) = ppr {
match pp.packet {
openpgp::Packet::PKESK(pkesk) => pkesks.push(pkesk),
openpgp::Packet::SEIP(_) => {
keyring.decrypt(&pkesks, &[], None, |algo, sk| {
// pushes packet to stack
pp.decrypt(algo, sk).is_ok()
});
},
p => panic!("Unexpected packet type: {}", p.tag()),
}
}
*/
let mut decryptor = let mut decryptor =
DecryptorBuilder::from_bytes(&packets)?.with_policy(policy, None, keyring)?; DecryptorBuilder::from_bytes(&packets)?.with_policy(policy, None, keyring)?;
@ -124,6 +106,25 @@ pub fn discover_certs(path: impl AsRef<Path>) -> Result<Vec<Cert>> {
} }
} }
pub fn parse_messages(reader: impl Read + Send + Sync) -> Result<VecDeque<EncryptedMessage> >{
let mut pkesks = Vec::new();
let mut encrypted_messages = VecDeque::new();
for packet in PacketPile::from_reader(reader)?.into_children() {
match packet {
Packet::PKESK(p) => pkesks.push(p),
Packet::SEIP(s) => {
encrypted_messages.push_back(EncryptedMessage::with_swap(&mut pkesks, s));
}
s => {
panic!("Invalid variant found: {}", s.tag());
}
}
}
Ok(encrypted_messages)
}
fn get_encryption_keys<'a>( fn get_encryption_keys<'a>(
cert: &'a ValidCert, cert: &'a ValidCert,
) -> openpgp::cert::prelude::ValidKeyAmalgamationIter< ) -> openpgp::cert::prelude::ValidKeyAmalgamationIter<

View File

@ -1,6 +1,8 @@
<!-- vim:set et sts=0 sw=2 ts=2: --> <!-- vim:set et sts=0 sw=2 ts=2: -->
# Summary # Summary
# User Guide
- [Installing Keyfork](./INSTALL.md) - [Installing Keyfork](./INSTALL.md)
- [Common Usage](./usage.md) - [Common Usage](./usage.md)
- [Binaries](./bin/index.md) - [Binaries](./bin/index.md)
@ -11,4 +13,8 @@
- [keyfork-mnemonic-from-seed](./bin/keyfork-plumbing/mnemonic-from-seed.md) - [keyfork-mnemonic-from-seed](./bin/keyfork-plumbing/mnemonic-from-seed.md)
- [keyfork-derive-key](./bin/keyfork-derive-key.md) - [keyfork-derive-key](./bin/keyfork-derive-key.md)
- [keyfork-derive-openpgp](./bin/keyfork-derive-openpgp.md) - [keyfork-derive-openpgp](./bin/keyfork-derive-openpgp.md)
- [Development Guide](./dev-guide/index.md)
# Developers Guide
- [Writing Binaries](./dev-guide/index.md)
- [Auditing](./dev-guide/auditing.md)

View File

@ -18,3 +18,5 @@ the shell silently ignoring the single quotes in the derivation path.
Hex-encoded private key. Note that this is not the _extended_ private key, and Hex-encoded private key. Note that this is not the _extended_ private key, and
can't be used to derive further data. can't be used to derive further data.
[`keyforkd`]: ./bin/keyforkd.md

View File

@ -28,3 +28,5 @@ the shell silently ignoring the single quotes in the derivation path.
## Output ## Output
OpenPGP ASCII armored key, signed to be valid for 24 hours. OpenPGP ASCII armored key, signed to be valid for 24 hours.
[`keyforkd`]: ./bin/keyforkd.md

View File

@ -12,11 +12,16 @@ requires a system be both offline and running an up-to-date kernel before
generating a mnemonic. However, the command may be run with the variable generating a mnemonic. However, the command may be run with the variable
`INSECURE_HARDWARE_ALLOWED=1` to override system validation. `INSECURE_HARDWARE_ALLOWED=1` to override system validation.
## Usage ## Arguments
``` `keyfork mnemonic generate --source=system --size=256`
% keyfork mnemonic generate
ceiling talent smooth jealous dust render hello resource ripple crucial pepper tribe noble gate shield glad slide document pulse negative spider this fancy seven * `--source`: The source from where a seed is created. Can be:
% keyfork mnemonic generate --size 128 * `system`
sustain glory control silk gym argue jaguar citizen remember doctor depth senior
``` Choosing any option besides `system` will prompt the user for input:
* `playing`
* `tarot`
* `dice`
* `--size`: The size in bits for the memonic, either `128` or `256`.

View File

@ -0,0 +1,22 @@
# Auditing Dependencies
Dependencies must be reviewed before being added to the repository, and must
not be added for pure convenience. There are few exceptions, such as `clap` and
`thiserror`, which provide derivation macros that are used heavily throughout
`keyfork` and the codebase as a whole. Any dependency added must be reviewed at
least on a surface level to ensure no malicious actions are performed with the
data the library will be responsible for handling. For example, any use of
`std::process` in a crate providing cryptographic functions should be heavily
scrutinized, and any crate that loads arbitrary code or performs networking
requests should have an incredibly important reason for doing so.
Dependencies should be restricted such that the least amount of dead code is
enabled. For instance, a crate such as `keyfork_derive_openpgp` can only make
use of the `ed25519` algorithm, so it exports its own `derive_util` that only
includes the crates required for that library. This can then be used by
programs such as `keyfork-shard`'s OpenPGP mode or `keyfork provision openpgp`
to ensure only the required dependencies are enabled. This reduces the burden
of auditors, but it does mean we can't use projects such as [`hakari`] to
optimize full-project builds.
[`hakari`]: https://docs.rs/cargo-hakari/latest/cargo_hakari/index.html

View File

@ -1,4 +1,4 @@
# Development Guide # Writing Binaries
### Binaries - Porcelain and Plumbing ### Binaries - Porcelain and Plumbing
@ -13,25 +13,80 @@ Usually, only cryptographic functionality (such as `sequoia-openpgp` or
`dalek-ed25519`) or hardware integration libraries (such as `openpgp-card`) are `dalek-ed25519`) or hardware integration libraries (such as `openpgp-card`) are
included. included.
### Auditing Dependencies ### Writing Binaries
Dependencies must be reviewed before being added to the repository, and must Crates can be either a library, or a library and binary, but should never be
not be added for pure convenience. There are few exceptions, such as `clap` and just a binary. When creating a crate with a binary, the `main.rs` file should
`thiserror`, which provide derivation macros that are used heavily throughout be designed to validate arguments, load any necessary system resources, and
`keyfork` and the codebase as a whole. Any dependency added must be reviewed at call a separate exposed function to do the heavy lifting. The following example
least on a surface level to ensure no malicious actions are performed with the was taken from `keyfork-shard` to demonstrate how a program can validate
data the library will be responsible for handling. For example, any use of arguments, parse input, and stream an output.
`std::process` in a crate providing cryptographic functions should be heavily
scrutinized, and any crate that loads arbitrary code or performs networking
requests should have an incredibly important reason for doing so.
Dependencies should be restricted such that the least amount of dead code is ```rust
enabled. For instance, a crate such as `keyfork_derive_openpgp` can only make use std::{
use of the `ed25519` algorithm, so it exports its own `derive_util` that only collections::VecDeque,
includes the crates required for that library. This can then be used by env,
programs such as `keyfork-shard`'s OpenPGP mode or `keyfork provision openpgp` io::{stdin, stdout},
to ensure only the required dependencies are enabled. This reduces the burden path::PathBuf,
of auditors, but it does mean we can't use projects such as [`hakari`] to process::ExitCode,
optimize full-project builds. str::FromStr,
};
[`hakari`]: https://docs.rs/cargo-hakari/latest/cargo_hakari/index.html use keyfork_shard::openpgp::{combine, discover_certs, openpgp::Cert};
use sequoia_openpgp as openpgp;
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>)> {
let threshold = u8::from_str(threshold)?;
let key_discovery = PathBuf::from(key_discovery);
// Verify path exists
std::fs::metadata(&key_discovery)?;
// Load certs from path
let certs = discover_certs(key_discovery)?;
Ok((threshold, certs))
}
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 encrypted_messages = parse_messages(stdin())?;
let encrypted_metadata = encrypted_messages
.pop_front()
.expect("any pgp encrypted message");
combine(
threshold,
cert_list,
encrypted_metadata,
encrypted_messages.into(),
stdout(),
)?;
Ok(())
}
fn main() -> ExitCode {
let result = run();
if let Err(e) = result {
eprintln!("Error: {e}");
return ExitCode::FAILURE;
}
ExitCode::SUCCESS
}
```
Designing binaries with this format makes it easier to load them to the Keyfork
porcelain binary, since the porcelain can call `combine()` with arguments that
it has parsed using its own configuration systems, using a `String` as a `mut
Write` as necessary.