Compare commits

...

4 Commits

39 changed files with 447 additions and 236 deletions

View File

@ -10,6 +10,20 @@ define clone-repo
test `git -C $(1) rev-parse HEAD` = $(3)
endef
docs/book: docs/src/links.md $(shell find docs/src -type f -name '*.md')
mdbook build docs
mkdir -p docs/book/rustdoc
cargo doc --no-deps
cp -r ${CARGO_TARGET_DIR}/doc/* docs/book/rustdoc/
docs/src/links.md: docs/src/links.md.template
echo "<!-- DO NOT EDIT THIS FILE MANUALLY, edit links.md.template -->" > $@
envsubst < $< >> $@
.PHONY: touch
touch:
touch docs/src/links.md.template
.PHONY: review
review:
$(eval BASE_REF_PARSED := $(shell git rev-parse $(BASE_REF)))

View File

@ -2,8 +2,13 @@
use std::{collections::HashMap, os::unix::net::UnixStream, path::PathBuf};
use keyfork_derive_util::{
request::{AsAlgorithm, DerivationRequest},
DerivationPath, ExtendedPrivateKey, PrivateKey,
};
use keyfork_frame::{try_decode_from, try_encode_to, DecodeError, EncodeError};
use keyforkd_models::{Request, Response, Error as KeyforkdError};
use keyforkd_models::{Error as KeyforkdError, Request, Response};
#[cfg(test)]
mod tests;
@ -11,6 +16,10 @@ mod tests;
/// An error occurred while interacting with Keyforkd.
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// The response from the server did not match the request.
#[error("The response from the server did not match the request")]
InvalidResponse,
/// The environment variables used for determining a Keyforkd socket path were not set.
#[error("Neither KEYFORK_SOCKET_PATH nor XDG_RUNTIME_DIR were set")]
EnvVarsNotFound,
@ -37,7 +46,7 @@ pub enum Error {
/// An error encountered in Keyforkd.
#[error("Error in Keyforkd: {0}")]
Keyforkd(#[from] KeyforkdError)
Keyforkd(#[from] KeyforkdError),
}
#[allow(missing_docs)]
@ -95,6 +104,38 @@ impl Client {
get_socket().map(|socket| Self { socket })
}
/// Request an [`ExtendedPrivateKey`] for a given [`DerivationPath`].
///
/// # Errors
/// An error may be returned if:
/// * Reading or writing from or to the socket encountered an error.
/// * Bincode could not serialize the request or deserialize the response.
/// * An error occurred in Keyforkd.
/// * Keyforkd returned invalid data.
pub fn request_xprv<K>(&mut self, path: &DerivationPath) -> Result<ExtendedPrivateKey<K>>
where
K: PrivateKey + Clone + AsAlgorithm,
{
let algo = K::as_algorithm();
let request = Request::Derivation(DerivationRequest::new(algo.clone(), path));
let response = self.request(&request)?;
match response {
Response::Derivation(d) => {
if d.algorithm != algo {
return Err(Error::InvalidResponse);
}
let depth = path.len() as u8;
Ok(ExtendedPrivateKey::new_from_parts(
&d.data,
depth,
d.chain_code,
))
}
_ => Err(Error::InvalidResponse),
}
}
/// Serialize and send a [`Request`] to the server, awaiting a [`Result<Response>`].
///
/// # Errors

View File

@ -2,13 +2,10 @@
use std::time::{Duration, SystemTime, SystemTimeError};
use derive_util::{
request::{DerivationResponse, TryFromDerivationResponseError},
DerivationIndex, ExtendedPrivateKey, PrivateKey,
IndexError,
};
use derive_util::{DerivationIndex, ExtendedPrivateKey, IndexError, PrivateKey};
use ed25519_dalek::SigningKey;
pub use keyfork_derive_util as derive_util;
pub use sequoia_openpgp as openpgp;
use sequoia_openpgp::{
packet::{
key::{Key4, PrimaryRole, SubordinateRole},
@ -18,7 +15,9 @@ use sequoia_openpgp::{
types::{KeyFlags, SignatureType},
Cert, Packet,
};
pub use sequoia_openpgp as openpgp;
pub type XPrvKey = SigningKey;
pub type XPrv = ExtendedPrivateKey<SigningKey>;
/// An error occurred while creating an OpenPGP key.
#[derive(Debug, thiserror::Error)]
@ -32,10 +31,6 @@ pub enum Error {
#[error("Key configured with both encryption and non-encryption key flags: {0:?}")]
InvalidKeyFlags(KeyFlags),
/// The derivation response contained incorrect data.
#[error("Incorrect derived data: {0}")]
IncorrectDerivedData(#[from] TryFromDerivationResponseError),
/// A derivation index could not be created from the given index.
#[error("Could not create derivation index: {0}")]
Index(#[from] IndexError),
@ -66,7 +61,7 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;
///
/// # Errors
/// The function may error for any condition mentioned in [`Error`].
pub fn derive(data: DerivationResponse, keys: &[KeyFlags], userid: &UserID) -> Result<Cert> {
pub fn derive(xprv: XPrv, keys: &[KeyFlags], userid: &UserID) -> Result<Cert> {
let primary_key_flags = match keys.get(0) {
Some(kf) if kf.for_certification() => kf,
_ => return Err(Error::NotCert),
@ -76,7 +71,6 @@ pub fn derive(data: DerivationResponse, keys: &[KeyFlags], userid: &UserID) -> R
let one_day = SystemTime::now() + Duration::from_secs(60 * 60 * 24);
// Create certificate with initial key and signature
let xprv = ExtendedPrivateKey::<SigningKey>::try_from(data)?;
let derived_primary_key = xprv.derive_child(&DerivationIndex::new(0, true)?)?;
let primary_key = Key::from(Key4::<_, PrimaryRole>::import_secret_ed25519(
&PrivateKey::to_bytes(derived_primary_key.private_key()),
@ -118,21 +112,14 @@ pub fn derive(data: DerivationResponse, keys: &[KeyFlags], userid: &UserID) -> R
bytes[0] &= 0b1111_1000;
bytes[31] &= !0b1000_0000;
bytes[31] |= 0b0100_0000;
Key::from(
Key4::<_, SubordinateRole>::import_secret_cv25519(
&bytes,
None,
None,
epoch,
)?
)
Key::from(Key4::<_, SubordinateRole>::import_secret_cv25519(
&bytes, None, None, epoch,
)?)
} else {
Key::from(
Key4::<_, SubordinateRole>::import_secret_ed25519(
&PrivateKey::to_bytes(derived_key.private_key()),
epoch,
)?
)
Key::from(Key4::<_, SubordinateRole>::import_secret_ed25519(
&PrivateKey::to_bytes(derived_key.private_key()),
epoch,
)?)
};
// As per OpenPGP spec, signing keys must backsig the primary key

View File

@ -2,12 +2,16 @@
use std::{env, process::ExitCode, str::FromStr};
use keyfork_derive_util::{
request::{DerivationAlgorithm, DerivationRequest, DerivationResponse},
DerivationIndex, DerivationPath,
};
use keyfork_derive_util::{DerivationIndex, DerivationPath};
use keyforkd_client::Client;
use sequoia_openpgp::{packet::UserID, types::KeyFlags, armor::{Kind, Writer}, serialize::Marshal};
use ed25519_dalek::SigningKey;
use sequoia_openpgp::{
armor::{Kind, Writer},
packet::UserID,
serialize::Marshal,
types::KeyFlags,
};
#[derive(Debug, thiserror::Error)]
enum Error {
@ -108,16 +112,13 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
_ => panic!("Usage: {program_name} path subkey_format default_userid"),
};
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
let derived_data: DerivationResponse = Client::discover_socket()?
.request(&request.into())?
.try_into()?;
let derived_xprv = Client::discover_socket()?.request_xprv::<SigningKey>(&path)?;
let subkeys = subkey_format
.iter()
.map(|kt| kt.inner().clone())
.collect::<Vec<_>>();
let cert = keyfork_derive_openpgp::derive(derived_data, subkeys.as_slice(), &default_userid)?;
let cert = keyfork_derive_openpgp::derive(derived_xprv, subkeys.as_slice(), &default_userid)?;
let mut w = Writer::new(std::io::stdout(), Kind::SecretKey)?;

View File

@ -10,18 +10,10 @@ const KEY_SIZE: usize = 256;
/// Errors associated with creating or deriving Extended Private Keys.
#[derive(Error, Clone, Debug)]
pub enum Error {
/// The seed has an unsuitable length; supported lengths are 16 bytes, 32 bytes, or 64 bytes.
#[error("Seed had an unsuitable length: {0}")]
BadSeedLength(usize),
/// The maximum depth for key derivation has been reached. The supported maximum depth is 255.
#[error("Reached maximum depth for key derivation")]
Depth,
/// This should never happen. HMAC keys should be able to take any size input.
#[error("Invalid length for HMAC key while generating master key (report me!)")]
HmacInvalidLength(#[from] hmac::digest::InvalidLength),
/// An unknown error occurred while deriving a child key.
#[error("Unknown error while deriving child key")]
Derivation,
@ -45,11 +37,36 @@ type HmacSha512 = Hmac<Sha512>;
#[derive(Clone, Serialize, Deserialize)]
pub struct ExtendedPrivateKey<K: PrivateKey + Clone> {
/// The internal private key data.
#[serde(with = "serde_with")]
private_key: K,
depth: u8,
chain_code: ChainCode,
}
mod serde_with {
use super::*;
pub(crate) fn serialize<S, K>(value: &K, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
K: PrivateKey + Clone,
{
serializer.serialize_bytes(&value.to_bytes())
}
pub(crate) fn deserialize<'de, D, K>(deserializer: D) -> Result<K, D::Error>
where
D: serde::Deserializer<'de>,
K: PrivateKey + Clone,
{
let variable_len_bytes = <&[u8]>::deserialize(deserializer)?;
let bytes: [u8; 32] = variable_len_bytes
.try_into()
.expect("unable to parse serialized private key; no support for static len");
Ok(K::from_bytes(&bytes))
}
}
impl<K: PrivateKey + Clone> std::fmt::Debug for ExtendedPrivateKey<K> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ExtendedPrivateKey")
@ -73,7 +90,7 @@ where
/// # Errors
/// An error may be returned if:
/// * The given seed had an incorrect length.
/// * A `HmacSha512` can't be constructed - this should be impossible.
/// * A `HmacSha512` can't be constructed.
///
/// # Examples
/// ```rust
@ -82,31 +99,26 @@ where
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
/// # Ok(())
/// # }
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed);
/// ```
pub fn new(seed: impl AsRef<[u8]>) -> Result<Self> {
pub fn new(seed: impl AsRef<[u8]>) -> Self {
Self::new_internal(seed.as_ref())
}
fn new_internal(seed: &[u8]) -> Result<Self> {
let len = seed.len();
if ![16, 32, 64].contains(&len) {
return Err(Error::BadSeedLength(len));
}
let hash = HmacSha512::new_from_slice(&K::key().bytes().collect::<Vec<_>>())?
fn new_internal(seed: &[u8]) -> Self {
let hash = HmacSha512::new_from_slice(&K::key().bytes().collect::<Vec<_>>())
.expect("HmacSha512 InvalidLength should be infallible")
.chain_update(seed)
.finalize()
.into_bytes();
let (private_key, chain_code) = hash.split_at(KEY_SIZE / 8);
Self::new_from_parts(
private_key,
private_key
.try_into()
.expect("KEY_SIZE / 8 did not give a 32 byte slice"),
0,
// Checked: chain_code is always the same length, hash is static size
chain_code.try_into().expect("Invalid chain code length"),
@ -125,21 +137,18 @@ where
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let key: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let chain_code: &[u8; 32] = //
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
/// # Ok(())
/// # }
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code);
/// ```
pub fn new_from_parts(seed: &[u8], depth: u8, chain_code: [u8; 32]) -> Result<Self> {
Ok(Self {
private_key: K::from_bytes(seed.try_into()?),
pub fn new_from_parts(key: &[u8; 32], depth: u8, chain_code: [u8; 32]) -> Self {
Self {
private_key: K::from_bytes(key),
depth,
chain_code,
})
}
}
/// Returns a reference to the [`PrivateKey`].
@ -152,15 +161,12 @@ where
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let key: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let chain_code: &[u8; 32] = //
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code);
/// assert_eq!(xprv.private_key(), &PrivateKey::from_bytes(key));
/// # Ok(())
/// # }
/// ```
pub fn private_key(&self) -> &K {
&self.private_key
@ -185,7 +191,7 @@ where
/// # 102, 201, 210, 159, 219, 222, 42, 201, 44, 196, 27,
/// # 90, 221, 80, 85, 135, 79, 39, 253, 223, 35, 251
/// # ];
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed);
/// let xpub = xprv.extended_public_key();
/// assert_eq!(known_key, xpub.public_key().to_bytes());
/// # Ok(())
@ -209,7 +215,7 @@ where
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed);
/// let pubkey = xprv.public_key();
/// # Ok(())
/// # }
@ -227,15 +233,12 @@ where
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let key: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let chain_code: &[u8; 32] = //
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code);
/// assert_eq!(xprv.depth(), 4);
/// # Ok(())
/// # }
/// ```
pub fn depth(&self) -> u8 {
self.depth
@ -250,15 +253,12 @@ where
/// # public_key::TestPublicKey as PublicKey,
/// # private_key::TestPrivateKey as PrivateKey,
/// # };
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let key: &[u8; 32] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let chain_code: &[u8; 32] = //
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code);
/// assert_eq!(chain_code, &xprv.chain_code());
/// # Ok(())
/// # }
/// ```
pub fn chain_code(&self) -> [u8; 32] {
self.chain_code
@ -280,7 +280,7 @@ where
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed);
/// let path = DerivationPath::default()
/// .chain_push(DerivationIndex::new(44, true)?)
/// .chain_push(DerivationIndex::new(0, true)?)
@ -326,7 +326,7 @@ where
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let seed: &[u8; 64] = //
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed);
/// let bip44_wallet = DerivationPath::default()
/// .chain_push(DerivationIndex::new(44, true)?)
/// .chain_push(DerivationIndex::new(0, true)?)
@ -342,8 +342,8 @@ where
pub fn derive_child(&self, index: &DerivationIndex) -> Result<Self> {
let depth = self.depth.checked_add(1).ok_or(Error::Depth)?;
let mut hmac =
HmacSha512::new_from_slice(&self.chain_code).map_err(Error::HmacInvalidLength)?;
let mut hmac = HmacSha512::new_from_slice(&self.chain_code)
.expect("HmacSha512 InvalidLength should be infallible");
if index.is_hardened() {
hmac.update(&[0]);
hmac.update(&self.private_key.to_bytes());

View File

@ -68,7 +68,7 @@ impl DerivationAlgorithm {
match self {
#[cfg(feature = "ed25519")]
Self::Ed25519 => {
let key = ExtendedPrivateKey::<ed25519_dalek::SigningKey>::new(seed)?;
let key = ExtendedPrivateKey::<ed25519_dalek::SigningKey>::new(seed);
let derived_key = key.derive_path(path)?;
Ok(DerivationResponse::with_algo_and_xprv(
self.clone(),
@ -77,7 +77,7 @@ impl DerivationAlgorithm {
}
#[cfg(feature = "secp256k1")]
Self::Secp256k1 => {
let key = ExtendedPrivateKey::<k256::SecretKey>::new(seed)?;
let key = ExtendedPrivateKey::<k256::SecretKey>::new(seed);
let derived_key = key.derive_path(path)?;
Ok(DerivationResponse::with_algo_and_xprv(
self.clone(),
@ -85,7 +85,7 @@ impl DerivationAlgorithm {
))
}
Self::Internal => {
let key = ExtendedPrivateKey::<TestPrivateKey>::new(seed)?;
let key = ExtendedPrivateKey::<TestPrivateKey>::new(seed);
let derived_key = key.derive_path(path)?;
Ok(DerivationResponse::with_algo_and_xprv(
self.clone(),
@ -110,6 +110,18 @@ impl std::str::FromStr for DerivationAlgorithm {
}
}
/// Acquire the associated [`DerivationAlgorithm`] for a [`PrivateKey`].
pub trait AsAlgorithm: PrivateKey {
/// Return the appropriate [`DerivationAlgorithm`].
fn as_algorithm() -> DerivationAlgorithm;
}
impl AsAlgorithm for TestPrivateKey {
fn as_algorithm() -> DerivationAlgorithm {
DerivationAlgorithm::Internal
}
}
/// A derivation request.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct DerivationRequest {
@ -234,7 +246,7 @@ pub struct DerivationResponse {
pub algorithm: DerivationAlgorithm,
/// The derived private key.
pub data: Vec<u8>,
pub data: [u8; 32],
/// The chain code, used for further derivation.
pub chain_code: [u8; 32],
@ -251,7 +263,7 @@ impl DerivationResponse {
) -> Self {
Self {
algorithm,
data: PrivateKey::to_bytes(xprv.private_key()).to_vec(),
data: PrivateKey::to_bytes(xprv.private_key()),
chain_code: xprv.chain_code(),
depth: xprv.depth(),
}
@ -272,47 +284,71 @@ pub enum TryFromDerivationResponseError {
}
#[cfg(feature = "secp256k1")]
impl TryFrom<&DerivationResponse> for ExtendedPrivateKey<k256::SecretKey> {
type Error = TryFromDerivationResponseError;
mod secp256k1 {
use super::*;
use k256::SecretKey;
fn try_from(value: &DerivationResponse) -> std::result::Result<Self, Self::Error> {
match value.algorithm {
DerivationAlgorithm::Secp256k1 => {
Self::new_from_parts(&value.data, value.depth, value.chain_code).map_err(From::from)
}
_ => Err(Self::Error::Algorithm),
impl AsAlgorithm for SecretKey {
fn as_algorithm() -> DerivationAlgorithm {
DerivationAlgorithm::Secp256k1
}
}
}
#[cfg(feature = "secp256k1")]
impl TryFrom<DerivationResponse> for ExtendedPrivateKey<k256::SecretKey> {
type Error = TryFromDerivationResponseError;
impl TryFrom<&DerivationResponse> for ExtendedPrivateKey<SecretKey> {
type Error = TryFromDerivationResponseError;
fn try_from(value: DerivationResponse) -> std::result::Result<Self, Self::Error> {
ExtendedPrivateKey::<k256::SecretKey>::try_from(&value)
}
}
#[cfg(feature = "ed25519")]
impl TryFrom<&DerivationResponse> for ExtendedPrivateKey<ed25519_dalek::SigningKey> {
type Error = TryFromDerivationResponseError;
fn try_from(value: &DerivationResponse) -> std::result::Result<Self, Self::Error> {
match value.algorithm {
DerivationAlgorithm::Ed25519 => {
Self::new_from_parts(&value.data, value.depth, value.chain_code).map_err(From::from)
fn try_from(value: &DerivationResponse) -> Result<Self, Self::Error> {
match value.algorithm {
DerivationAlgorithm::Secp256k1 => Ok(Self::new_from_parts(
&value.data,
value.depth,
value.chain_code,
)),
_ => Err(Self::Error::Algorithm),
}
_ => Err(Self::Error::Algorithm),
}
}
impl TryFrom<DerivationResponse> for ExtendedPrivateKey<SecretKey> {
type Error = TryFromDerivationResponseError;
fn try_from(value: DerivationResponse) -> Result<Self, Self::Error> {
ExtendedPrivateKey::<SecretKey>::try_from(&value)
}
}
}
#[cfg(feature = "ed25519")]
impl TryFrom<DerivationResponse> for ExtendedPrivateKey<ed25519_dalek::SigningKey> {
type Error = TryFromDerivationResponseError;
mod ed25519 {
use super::*;
use ed25519_dalek::SigningKey;
fn try_from(value: DerivationResponse) -> std::result::Result<Self, Self::Error> {
ExtendedPrivateKey::<ed25519_dalek::SigningKey>::try_from(&value)
impl AsAlgorithm for SigningKey {
fn as_algorithm() -> DerivationAlgorithm {
DerivationAlgorithm::Ed25519
}
}
impl TryFrom<&DerivationResponse> for ExtendedPrivateKey<SigningKey> {
type Error = TryFromDerivationResponseError;
fn try_from(value: &DerivationResponse) -> Result<Self, Self::Error> {
match value.algorithm {
DerivationAlgorithm::Ed25519 => Ok(Self::new_from_parts(
&value.data,
value.depth,
value.chain_code,
)),
_ => Err(Self::Error::Algorithm),
}
}
}
impl TryFrom<DerivationResponse> for ExtendedPrivateKey<SigningKey> {
type Error = TryFromDerivationResponseError;
fn try_from(value: DerivationResponse) -> Result<Self, Self::Error> {
ExtendedPrivateKey::<SigningKey>::try_from(&value)
}
}
}

View File

@ -30,7 +30,7 @@ fn secp256k1() {
} = test;
// Tests for ExtendedPrivateKey
let xkey = ExtendedPrivateKey::<SecretKey>::new(seed).unwrap();
let xkey = ExtendedPrivateKey::<SecretKey>::new(seed);
let derived_key = xkey.derive_path(&chain).unwrap();
assert_eq!(
derived_key.chain_code().as_slice(),
@ -51,7 +51,7 @@ fn secp256k1() {
// Tests for DerivationRequest
let request = DerivationRequest::new(DerivationAlgorithm::Secp256k1, &chain);
let response = request.derive_with_master_seed(seed.clone()).unwrap();
assert_eq!(&response.data, private_key, "test: {chain}");
assert_eq!(&response.data, private_key.as_slice(), "test: {chain}");
}
}
}
@ -75,7 +75,7 @@ fn ed25519() {
} = test;
// Tests for ExtendedPrivateKey
let xkey = ExtendedPrivateKey::<SigningKey>::new(seed).unwrap();
let xkey = ExtendedPrivateKey::<SigningKey>::new(seed);
let derived_key = xkey.derive_path(&chain).unwrap();
assert_eq!(
derived_key.chain_code().as_slice(),
@ -96,7 +96,7 @@ fn ed25519() {
// Tests for DerivationRequest
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &chain);
let response = request.derive_with_master_seed(seed.to_vec()).unwrap();
assert_eq!(&response.data, private_key, "test: {chain}");
assert_eq!(&response.data, private_key.as_slice(), "test: {chain}");
}
}
}
@ -108,7 +108,7 @@ fn panics_with_unhardened_derivation() {
use ed25519_dalek::SigningKey;
let seed = hex!("000102030405060708090a0b0c0d0e0f");
let xkey = ExtendedPrivateKey::<SigningKey>::new(seed).unwrap();
let xkey = ExtendedPrivateKey::<SigningKey>::new(seed);
xkey.derive_path(&DerivationPath::from_str("m/0").unwrap())
.unwrap();
}
@ -120,7 +120,7 @@ fn panics_at_depth() {
use ed25519_dalek::SigningKey;
let seed = hex!("000102030405060708090a0b0c0d0e0f");
let mut xkey = ExtendedPrivateKey::<SigningKey>::new(seed).unwrap();
let mut xkey = ExtendedPrivateKey::<SigningKey>::new(seed);
for i in 0..=u32::from(u8::MAX) {
xkey = xkey
.derive_child(&DerivationIndex::new(i, true).unwrap())

View File

@ -13,9 +13,9 @@ use aes_gcm::{
Aes256Gcm, Error as AesError, KeyInit, Nonce,
};
use hkdf::{Hkdf, InvalidLength as HkdfInvalidLength};
use keyfork_derive_openpgp::derive_util::{
request::{DerivationAlgorithm, DerivationRequest},
DerivationPath, PathError,
use keyfork_derive_openpgp::{
derive_util::{DerivationPath, PathError},
XPrv,
};
use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError, MnemonicGenerationError, Wordlist};
use keyfork_prompt::{
@ -123,6 +123,10 @@ pub enum Error {
#[error("IO error: {0}")]
Io(#[source] std::io::Error),
/// An error occurred while deriving data.
#[error("Derivation: {0}")]
Derivation(#[from] keyfork_derive_openpgp::derive_util::extended_key::private_key::Error),
/// An error occurred while parsing a derivation path.
#[error("Derivation path: {0}")]
DerivationPath(#[from] PathError),
@ -643,13 +647,10 @@ pub fn combine(
// TODO: extract as function
let userid = UserID::from("keyfork-sss");
let kdr = DerivationRequest::new(
DerivationAlgorithm::Ed25519,
&DerivationPath::from_str("m/7366512'/0'")?,
)
.derive_with_master_seed(secret.clone())?;
let path = DerivationPath::from_str("m/7366512'/0'")?;
let xprv = XPrv::new(&secret).derive_path(&path)?;
let derived_cert = keyfork_derive_openpgp::derive(
kdr,
xprv,
&[KeyFlags::empty().set_certification().set_signing()],
&userid,
)?;
@ -680,13 +681,10 @@ pub fn combine(
pub fn split(threshold: u8, certs: Vec<Cert>, secret: &[u8], output: impl Write) -> Result<()> {
// build cert to sign encrypted shares
let userid = UserID::from("keyfork-sss");
let kdr = DerivationRequest::new(
DerivationAlgorithm::Ed25519,
&DerivationPath::from_str("m/7366512'/0'")?,
)
.derive_with_master_seed(secret.to_vec())?;
let path = DerivationPath::from_str("m/7366512'/0'")?;
let xprv = XPrv::new(&secret).derive_path(&path)?;
let derived_cert = keyfork_derive_openpgp::derive(
kdr,
xprv,
&[KeyFlags::empty().set_certification().set_signing()],
&userid,
)?;

View File

@ -1,16 +1,16 @@
use super::Keyfork;
use clap::{Parser, Subcommand};
use keyfork_derive_openpgp::openpgp::{
armor::{Kind, Writer},
packet::UserID,
serialize::Marshal,
types::KeyFlags,
};
use keyfork_derive_util::{
request::{DerivationAlgorithm, DerivationRequest, DerivationResponse},
DerivationIndex, DerivationPath,
use keyfork_derive_openpgp::{
openpgp::{
armor::{Kind, Writer},
packet::UserID,
serialize::Marshal,
types::KeyFlags,
},
XPrvKey,
};
use keyfork_derive_util::{DerivationIndex, DerivationPath};
use keyforkd_client::Client;
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
@ -48,12 +48,9 @@ impl DeriveSubcommands {
.set_storage_encryption(),
KeyFlags::empty().set_authentication(),
];
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
let derived_data: DerivationResponse = Client::discover_socket()?
.request(&request.into())?
.try_into()?;
let xprv = Client::discover_socket()?.request_xprv::<XPrvKey>(&path)?;
let default_userid = UserID::from(user_id.as_str());
let cert = keyfork_derive_openpgp::derive(derived_data, &subkeys, &default_userid)?;
let cert = keyfork_derive_openpgp::derive(xprv, &subkeys, &default_userid)?;
let mut w = Writer::new(std::io::stdout(), Kind::SecretKey)?;

View File

@ -5,11 +5,11 @@ use std::{collections::HashSet, fs::File, io::IsTerminal, path::PathBuf};
use card_backend_pcsc::PcscBackend;
use openpgp_card_sequoia::{state::Open, types::KeyType, Card};
use keyfork_derive_openpgp::openpgp::{self, packet::UserID, types::KeyFlags, Cert};
use keyfork_derive_util::{
request::{DerivationAlgorithm, DerivationRequest},
DerivationIndex, DerivationPath,
use keyfork_derive_openpgp::{
openpgp::{self, packet::UserID, types::KeyFlags, Cert},
XPrv,
};
use keyfork_derive_util::{DerivationIndex, DerivationPath};
use keyfork_prompt::{
validators::{PinValidator, Validator},
Message, PromptHandler, Terminal,
@ -42,10 +42,9 @@ fn derive_key(seed: &[u8], index: u8) -> Result<Cert> {
.chain_push(chain)
.chain_push(account)
.chain_push(subkey);
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
let response = request.derive_with_master_seed(seed.to_vec())?;
let xprv = XPrv::new(seed).derive_path(&path)?;
let userid = UserID::from(format!("Keyfork Shard {index}"));
let cert = keyfork_derive_openpgp::derive(response, &subkeys, &userid)?;
let cert = keyfork_derive_openpgp::derive(xprv, &subkeys, &userid)?;
Ok(cert)
}

View File

@ -9,7 +9,7 @@ license = "MIT"
[features]
default = []
bin = ["decode-backend-zbar"]
bin = ["decode-backend-rqrr"]
decode-backend-rqrr = ["dep:rqrr"]
decode-backend-zbar = ["dep:keyfork-zbar"]

View File

@ -22,9 +22,9 @@ impl Image {
///
/// A FourCC code can be given in the format:
///
/// ```no_run
/// ```rust,ignore
/// self.set_format(b"Y800")
/// ````
/// ```
pub(crate) fn set_format(&mut self, fourcc: &[u8; 4]) {
let fourcc: u64 = fourcc[0] as u64
| ((fourcc[1] as u64) << 8)

View File

@ -1,3 +1,5 @@
{{#include links.md}}
## Dependencies
Keyfork has different dependencies depending on the feature set used for
@ -66,5 +68,3 @@ cargo install --index https://git.distrust.co/public/_cargo-index keyfork-entrop
# Confirmed to work as of 2024-01-17.
cargo install --locked --path crates/util/keyfork-entropy --bin keyfork-entropy --features bin
```
[SBOM]: https://en.wikipedia.org/wiki/SBOM

View File

@ -1,8 +1,10 @@
<!-- vim:set et sts=0 sw=2 ts=2: -->
{{ #include links.md }}
# Summary
# User Guide
- [Introduction to Keyfork](./introduction.md)
- [Installing Keyfork](./INSTALL.md)
- [Security Considerations](./security.md)
- [Shard Commands](./shard.md)

View File

@ -1,3 +1,5 @@
{{#include ../links.md}}
# keyfork-derive-key
Derive a key from a given derivation path.
@ -18,5 +20,3 @@ 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
can't be used to derive further data.
[`keyforkd`]: ./bin/keyforkd.md

View File

@ -1,3 +1,5 @@
{{#include ../links.md}}
# keyfork-derive-openpgp
Derive a key from a given derivation path.
@ -28,5 +30,3 @@ the shell silently ignoring the single quotes in the derivation path.
## Output
OpenPGP ASCII armored key, signed to be valid for 24 hours.
[`keyforkd`]: ./bin/keyforkd.md

View File

@ -1,3 +1,5 @@
{{#include ../../links.md}}
# keyfork-entropy
Retrieve system entropy, output in hex format. The machine must be running a

View File

@ -1,3 +1,5 @@
{{#include ../../links.md}}
# keyfork-mnemonic-from-seed
Generate a mnemonic from a seed passed by input.

View File

@ -1,3 +1,5 @@
{{#include ../../links.md}}
# keyfork-shard
<!-- Linked to: keyfork-user-guide/src/bin/keyfork/shard/index.md -->
@ -7,13 +9,9 @@ data. All binaries use Shamir's Secret Sharing through the [`sharks`] crate.
## OpenPGP
Keyfork provides OpenPGP compatible [`split`][openpgp-split] and
[`combine`][openpgp-combine] versions of Shard binaries. These binaries use
Keyfork provides OpenPGP compatible [`split`][kshard-opgp-split] and
[`combine`][kshard-opgp-combine] versions of Shard binaries. These binaries use
Sequoia OpenPGP and while they require all the necessary certificates for the
splitting stage, the certificates are included in the payload, and once Keyfork
supports decrypting using OpenPGP smartcards, certificates will not be required
to decrypt the shares.
[`sharks`]: https://docs.rs/sharks/latest/sharks/
[openpgp-split]: ./openpgp/split.md
[openpgp-combine]: ./openpgp/combine.md

View File

@ -1,6 +1,8 @@
{{#include ../../../links.md}}
# keyfork-shard-combine-openpgp
Combine `threshold` shares into a previously [`split`] secret.
Combine shares into a previously [`split`][kshard-opgp-split] secret.
## Arguments
@ -31,5 +33,3 @@ keyfork-shard-combine-openpgp shard.pgp
# Decrypt using on-disk private keys
keyfork-shard-combine-openpgp key_discovery.pgp shard.pgp
```
[`split`]: ./split.md

View File

@ -1,3 +1,5 @@
{{#include ../../../links.md}}
# keyfork-shard-split-openpgp
<!-- Linked to: keyfork-user-guide/src/bin/keyfork-shard/index.md -->

View File

@ -1,3 +1,5 @@
{{#include ../../../links.md}}
# `keyfork derive`
Derive keys of various formats.

View File

@ -1,3 +1,5 @@
{{#include ../../links.md}}
# keyfork
The primary interface for interacting with Keyfork utilities.
@ -33,5 +35,3 @@ been recovered, the Keyfork server starts, and derivation requests can begin.
Utilities to automatically manage the setup of Keyfork. This includes
generating a seed, splitting it into a Shard file, and provisioning smart cards
with the capability to decrypt the shards.
[BIP-0044]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki

View File

@ -1,3 +1,5 @@
{{#include ../../../links.md}}
# `keyfork mnemonic`
Utilities for managing mnemonics.

View File

@ -1,3 +1,5 @@
{{#include ../../../links.md}}
# `keyfork recover`
Recover a seed to memory from a mnemonic, shard, or other format, then launch
@ -38,5 +40,3 @@ shardholders.
For every shardholder, the recovery command will prompt 33 words to be sent to
the shardholder, followed by an input prompt of 72 words to be received from
the shardholder.
[`keyfork shard transport`]: ../shard/index.md#keyfork-shard-transport

View File

@ -1,3 +1,5 @@
{{#include ../../../links.md}}
# `keyfork shard`
<!-- Linked to: keyfork-user-guide/src/bin/keyfork-shard/index.md -->
@ -128,5 +130,3 @@ keyfork shard transport shard.pgp
# Transport using on-disk private keys
keyfork shard transport key_discovery.pgp shard.pgp
```
[`keyfork recover remote-shard`]: ../recover/index.md#keyfork-recover-remote-shard

View File

@ -1,3 +1,5 @@
{{#include ../../../links.md}}
# `keyfork wizard`
Set up Keyfork using a guided setup process.
@ -63,5 +65,3 @@ shardholder.
An OpenPGP-encrypted Shard file, if not previously configured to be written to
a file using `--output`.
[BIP-0032]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki

View File

@ -1,3 +1,5 @@
{{#include ../links.md}}
# keyforkd
Keyforkd is the backend for deriving data using Keyfork. A mnemonic is loaded
@ -13,7 +15,7 @@ are not leaked. In the future, `keyforkd` could implement GUI or TTY approval
for users to approve the path requested by the client, such as `m/44'/0'` being
"Bitcoin", or `m/7366512'` being "OpenPGP".
The protocol for the UNIX socket is a framed, [bincode] format. While it is
The protocol for the UNIX socket is a framed, [`bincode`] format. While it is
custom to Keyfork, it is easy to implement. The crate `keyfork-frame` provides
a sync (`Read`, `Write`) and Tokio-compatible async (`AsyncRead`, `AsyncWrite`)
pair of methods for encoding and decoding frames.
@ -27,6 +29,3 @@ For encoding the data, the process is reversed. A SHA-256 hash is created, and
the length of the hash and the data is encoded to big-endian and written to the
stream. Then, the hash is written to the stream. Lastly, the data itself is
written as-is to the stream.
[bincode]: https://docs.rs/bincode/latest/bincode/
[BIP-0044]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki

View File

@ -1,3 +1,5 @@
{{#include links.md}}
# Configuration File
The Keyfork configuration file is used to store the integrity of the mnemonic

View File

@ -1,3 +1,5 @@
{{#include ../links.md}}
# Auditing Dependencies
Dependencies must be reviewed before being added to the repository, and must
@ -35,7 +37,7 @@ These dependencies will show up often:
A command line interface for generating, deriving from, and managing secrets.
* [`card-backend-pcsc`]: Interacting with smartcards using PCSC. Used as a card
backend for `openpgp-card`.
backend for [`openpgp-card`].
* [`clap`]: Command line argument parsing, helps building an intuitive command
line interface.
* [`clap_complete`]: Shell autocompletion file generator. Helps the user
@ -221,40 +223,6 @@ Test data for SLIP10/BIP-0032 derivation.
Zero-dependency hex encoding and decoding.
[`aes-gcm`]: https://github.com/RustCrypto/AEADs/tree/master/aes-gcm
[`anyhow`]: https://github.com/dtolnay/anyhow
[`bincode`]: https://github.com/bincode-org/bincode
[`card-backend`]: https://gitlab.com/openpgp-card/openpgp-card/-/tree/main/card-backend
[`card-backend-pcsc`]: https://gitlab.com/openpgp-card/openpgp-card/-/tree/main/pcsc
[`clap`]: https://github.com/clap-rs/clap/
[`clap_complete`]: https://github.com/clap-rs/clap/tree/master/clap_complete
[`digest`]: https://github.com/RustCrypto/traits/tree/master/digest
[`ed25519-dalek`]: https://github.com/dalek-cryptography/curve25519-dalek/tree/main/ed25519-dalek
[`hakari`]: https://docs.rs/cargo-hakari/latest/cargo_hakari/index.html
[`hkdf`]: https://github.com/RustCrypto/KDFs/tree/master/hkdf
[`hmac`]: https://github.com/RustCrypto/MACs/tree/master/hmac
[`image`]: https://github.com/image-rs/image
[`k256`]: https://github.com/RustCrypto/elliptic-curves/tree/master/k256
[`openpgp-card`]: https://gitlab.com/openpgp-card/openpgp-card/-/tree/main
[`openpgp-card-sequoia`]: https://gitlab.com/openpgp-card/openpgp-card/-/tree/main/openpgp-card-sequoia
[`pbkdf2`]: https://github.com/RustCrypto/password-hashes/tree/master/pbkdf2
[`ripemd`]: https://github.com/RustCrypto/hashes/tree/master/ripemd
[`rqrr`]: https://github.com/WanzenBug/rqrr/
[`sequoia-openpgp`]: https://gitlab.com/sequoia-pgp/sequoia
[`serde`]: https://github.com/dtolnay/serde
[`sha2`]: https://github.com/RustCrypto/hashes/tree/master/sha2
[`thiserror`]: https://github.com/dtolnay/thiserror
[`tokio`]: https://github.com/tokio-rs/tokio
[`tower`]: https://github.com/tower-rs/tower
[`tracing`]: https://github.com/tokio-rs/tracing
[`tracing-error`]: https://github.com/tokio-rs/tracing/tree/master/tracing-error
[`tracing-subscriber`]: https://github.com/tokio-rs/tracing/tree/master/tracing-subscriber
[`v4l`]: https://github.com/raymanfx/libv4l-rs/
[`zbar`]: https://github.com/mchehab/zbar
[`bindgen`]: https://github.com/rust-lang/rust-bindgen
[`pkg-config`]: https://github.com/rust-lang/pkg-config-rs
[`keyfork-crossterm`]: #keyfork-crossterm
[`keyfork-derive-openpgp`]: #keyfork-derive-openpgp
[`keyfork-derive-path-data`]: #keyfork-derive-path-data

View File

@ -1,3 +1,5 @@
{{#include ../links.md}}
# Entropy Guide
Keyfork provides a `keyfork-entropy` crate for generating entropy. The crate

View File

@ -1,3 +1,5 @@
{{#include ../links.md}}
# Handling Data
In Rust, it is common to name things `as_*`, `to_*`, and `into_*`. These three

View File

@ -1,3 +1,5 @@
{{#include ../links.md}}
# Writing Binaries
### Binaries - Porcelain and Plumbing

View File

@ -1,3 +1,5 @@
{{#include ../links.md}}
# Developing Provisioners
**Note:** This document makes heavy use of references to OpenPGP and assumes
@ -75,6 +77,3 @@ device. The porcelain provisioner code should make a best-effort attempt to
derive unique keys for each use, such as OpenPGP capabilities or PIV slots.
Additionally, when provisioning to a key, the configuration for that
provisioner should be stored to the configuration file.
[application identifier]: https://docs.rs/openpgp-card-sequoia/latest/openpgp_card_sequoia/struct.Card.html#method.application_identifier
[cardholder name]: https://docs.rs/openpgp-card-sequoia/latest/openpgp_card_sequoia/struct.Card.html#method.cardholder_name

14
docs/src/introduction.md Normal file
View File

@ -0,0 +1,14 @@
{{#include links.md}}
# Introduction
Keyfork is a tool to help manage the creation and derivation of binary data
using [BIP-0039] mnemonics. A mnemonic is, in simple terms, a way of encoding a
large number between 128 and 256 bits, as a list of 12 to 24 words that can be
easily stored or memorized. Once a user has a mnemonic, Keyfork utilizes
[BIP-0032] to derive cryptographic keys, which can be utilized by a variety of
applications.
## Rust documentation
Documentation is [automatically built][keyfork-rustdoc].

71
docs/src/links.md Normal file
View File

@ -0,0 +1,71 @@
<!-- DO NOT EDIT THIS FILE MANUALLY, edit links.md.template -->
<!-- vim:set et sw=4 ts=4 tw=79 ft=markdown: -->
[comments]: <> (
Please keep all links contained in this file, so they can be reused if
necessary across multiple pages.
)
[comments]: <> (
External links
)
[application identifier]: https://docs.rs/openpgp-card-sequoia/latest/openpgp_card_sequoia/struct.Card.html#method.application_identifier
[cardholder name]: https://docs.rs/openpgp-card-sequoia/latest/openpgp_card_sequoia/struct.Card.html#method.cardholder_name
[BIP-0032]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
[BIP-0039]: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
[BIP-0044]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
[SBOM]: https://en.wikipedia.org/wiki/SBOM
[Sequoia]: https://sequoia-pgp.org
[comments]: <> (
Crate source links
)
[`aes-gcm`]: https://github.com/RustCrypto/AEADs/tree/master/aes-gcm
[`anyhow`]: https://github.com/dtolnay/anyhow
[`bincode`]: https://github.com/bincode-org/bincode
[`card-backend`]: https://gitlab.com/openpgp-card/openpgp-card/-/tree/main/card-backend
[`card-backend-pcsc`]: https://gitlab.com/openpgp-card/openpgp-card/-/tree/main/pcsc
[`clap`]: https://github.com/clap-rs/clap/
[`clap_complete`]: https://github.com/clap-rs/clap/tree/master/clap_complete
[`digest`]: https://github.com/RustCrypto/traits/tree/master/digest
[`ed25519-dalek`]: https://github.com/dalek-cryptography/curve25519-dalek/tree/main/ed25519-dalek
[`hakari`]: https://docs.rs/cargo-hakari/latest/cargo_hakari/index.html
[`hkdf`]: https://github.com/RustCrypto/KDFs/tree/master/hkdf
[`hmac`]: https://github.com/RustCrypto/MACs/tree/master/hmac
[`image`]: https://github.com/image-rs/image
[`k256`]: https://github.com/RustCrypto/elliptic-curves/tree/master/k256
[`openpgp-card`]: https://gitlab.com/openpgp-card/openpgp-card/-/tree/main
[`openpgp-card-sequoia`]: https://gitlab.com/openpgp-card/openpgp-card/-/tree/main/openpgp-card-sequoia
[`pbkdf2`]: https://github.com/RustCrypto/password-hashes/tree/master/pbkdf2
[`ripemd`]: https://github.com/RustCrypto/hashes/tree/master/ripemd
[`rqrr`]: https://github.com/WanzenBug/rqrr/
[`sequoia-openpgp`]: https://gitlab.com/sequoia-pgp/sequoia
[`serde`]: https://github.com/dtolnay/serde
[`sha2`]: https://github.com/RustCrypto/hashes/tree/master/sha2
[`sharks`]: https://github.com/c0dearm/sharks
[`thiserror`]: https://github.com/dtolnay/thiserror
[`tokio`]: https://github.com/tokio-rs/tokio
[`tower`]: https://github.com/tower-rs/tower
[`tracing`]: https://github.com/tokio-rs/tracing
[`tracing-error`]: https://github.com/tokio-rs/tracing/tree/master/tracing-error
[`tracing-subscriber`]: https://github.com/tokio-rs/tracing/tree/master/tracing-subscriber
[`v4l`]: https://github.com/raymanfx/libv4l-rs/
[`zbar`]: https://github.com/mchehab/zbar
[`bindgen`]: https://github.com/rust-lang/rust-bindgen
[`pkg-config`]: https://github.com/rust-lang/pkg-config-rs
[comments]: <> (
Internal links, based on root path
)
[`keyforkd`]: /bin/keyforkd.md
[`keyfork shard transport`]: /bin/keyfork/shard/index.md#keyfork-shard-transport
[`keyfork recover remote-shard`]: /bin/keyfork/recover/index.md#keyfork-recover-remote-shard
[kshard-opgp-split]: /bin/keyfork-shard/openpgp/split.md
[kshard-opgp-combine]: /bin/keyfork-shard/openpgp/combine.md
[keyfork-rustdoc]: ./rustdoc/keyfork/index.html

View File

@ -0,0 +1,70 @@
<!-- vim:set et sw=4 ts=4 tw=79 ft=markdown: -->
[comments]: <> (
Please keep all links contained in this file, so they can be reused if
necessary across multiple pages.
)
[comments]: <> (
External links
)
[application identifier]: https://docs.rs/openpgp-card-sequoia/latest/openpgp_card_sequoia/struct.Card.html#method.application_identifier
[cardholder name]: https://docs.rs/openpgp-card-sequoia/latest/openpgp_card_sequoia/struct.Card.html#method.cardholder_name
[BIP-0032]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
[BIP-0039]: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
[BIP-0044]: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
[SBOM]: https://en.wikipedia.org/wiki/SBOM
[Sequoia]: https://sequoia-pgp.org
[comments]: <> (
Crate source links
)
[`aes-gcm`]: https://github.com/RustCrypto/AEADs/tree/master/aes-gcm
[`anyhow`]: https://github.com/dtolnay/anyhow
[`bincode`]: https://github.com/bincode-org/bincode
[`card-backend`]: https://gitlab.com/openpgp-card/openpgp-card/-/tree/main/card-backend
[`card-backend-pcsc`]: https://gitlab.com/openpgp-card/openpgp-card/-/tree/main/pcsc
[`clap`]: https://github.com/clap-rs/clap/
[`clap_complete`]: https://github.com/clap-rs/clap/tree/master/clap_complete
[`digest`]: https://github.com/RustCrypto/traits/tree/master/digest
[`ed25519-dalek`]: https://github.com/dalek-cryptography/curve25519-dalek/tree/main/ed25519-dalek
[`hakari`]: https://docs.rs/cargo-hakari/latest/cargo_hakari/index.html
[`hkdf`]: https://github.com/RustCrypto/KDFs/tree/master/hkdf
[`hmac`]: https://github.com/RustCrypto/MACs/tree/master/hmac
[`image`]: https://github.com/image-rs/image
[`k256`]: https://github.com/RustCrypto/elliptic-curves/tree/master/k256
[`openpgp-card`]: https://gitlab.com/openpgp-card/openpgp-card/-/tree/main
[`openpgp-card-sequoia`]: https://gitlab.com/openpgp-card/openpgp-card/-/tree/main/openpgp-card-sequoia
[`pbkdf2`]: https://github.com/RustCrypto/password-hashes/tree/master/pbkdf2
[`ripemd`]: https://github.com/RustCrypto/hashes/tree/master/ripemd
[`rqrr`]: https://github.com/WanzenBug/rqrr/
[`sequoia-openpgp`]: https://gitlab.com/sequoia-pgp/sequoia
[`serde`]: https://github.com/dtolnay/serde
[`sha2`]: https://github.com/RustCrypto/hashes/tree/master/sha2
[`sharks`]: https://github.com/c0dearm/sharks
[`thiserror`]: https://github.com/dtolnay/thiserror
[`tokio`]: https://github.com/tokio-rs/tokio
[`tower`]: https://github.com/tower-rs/tower
[`tracing`]: https://github.com/tokio-rs/tracing
[`tracing-error`]: https://github.com/tokio-rs/tracing/tree/master/tracing-error
[`tracing-subscriber`]: https://github.com/tokio-rs/tracing/tree/master/tracing-subscriber
[`v4l`]: https://github.com/raymanfx/libv4l-rs/
[`zbar`]: https://github.com/mchehab/zbar
[`bindgen`]: https://github.com/rust-lang/rust-bindgen
[`pkg-config`]: https://github.com/rust-lang/pkg-config-rs
[comments]: <> (
Internal links, based on root path
)
[`keyforkd`]: ${ROOT_PATH}/bin/keyforkd.md
[`keyfork shard transport`]: ${ROOT_PATH}/bin/keyfork/shard/index.md#keyfork-shard-transport
[`keyfork recover remote-shard`]: ${ROOT_PATH}/bin/keyfork/recover/index.md#keyfork-recover-remote-shard
[kshard-opgp-split]: ${ROOT_PATH}/bin/keyfork-shard/openpgp/split.md
[kshard-opgp-combine]: ${ROOT_PATH}/bin/keyfork-shard/openpgp/combine.md
[keyfork-rustdoc]: ./rustdoc/keyfork/index.html

View File

@ -1,3 +1,5 @@
{{#include links.md}}
# Keyfork Shard Commands
Sharding a seed allows "M-of-N" recovery of the seed, which is useful for
@ -77,5 +79,3 @@ The key, including the secret portions, can be retrieved by running the command
without the `sq` portion, but should not be run on a system with a persistent
filesystem, to avoid keeping the key on written memory for longer than
necessary.
[Sequoia]: https://sequoia-pgp.org

View File

@ -1,3 +1,5 @@
{{#include links.md}}
# Common Usage
Keyfork is a tool to help manage the creation and derivation of binary data
@ -74,6 +76,3 @@ the following command for an OpenPGP certificate with one of each subkey:
```sh
keyfork derive openpgp "John Doe <jdoe@example.com>"
```
[BIP-0039]: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
[BIP-0032]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki