Compare commits
4 Commits
086e56bef0
...
4354be4304
Author | SHA1 | Date |
---|---|---|
Ryan Heywood | 4354be4304 | |
Ryan Heywood | 8108f5e61a | |
Ryan Heywood | 4e2c4487e9 | |
Ryan Heywood | 38b73b670e |
14
Makefile
14
Makefile
|
@ -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)))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
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
|
||||
|
|
|
@ -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)?;
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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> {
|
||||
mod secp256k1 {
|
||||
use super::*;
|
||||
use k256::SecretKey;
|
||||
|
||||
impl AsAlgorithm for SecretKey {
|
||||
fn as_algorithm() -> DerivationAlgorithm {
|
||||
DerivationAlgorithm::Secp256k1
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&DerivationResponse> for ExtendedPrivateKey<SecretKey> {
|
||||
type Error = TryFromDerivationResponseError;
|
||||
|
||||
fn try_from(value: &DerivationResponse) -> std::result::Result<Self, Self::Error> {
|
||||
fn try_from(value: &DerivationResponse) -> Result<Self, Self::Error> {
|
||||
match value.algorithm {
|
||||
DerivationAlgorithm::Secp256k1 => {
|
||||
Self::new_from_parts(&value.data, value.depth, value.chain_code).map_err(From::from)
|
||||
}
|
||||
DerivationAlgorithm::Secp256k1 => Ok(Self::new_from_parts(
|
||||
&value.data,
|
||||
value.depth,
|
||||
value.chain_code,
|
||||
)),
|
||||
_ => Err(Self::Error::Algorithm),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "secp256k1")]
|
||||
impl TryFrom<DerivationResponse> for ExtendedPrivateKey<k256::SecretKey> {
|
||||
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)
|
||||
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> {
|
||||
mod ed25519 {
|
||||
use super::*;
|
||||
use ed25519_dalek::SigningKey;
|
||||
|
||||
impl AsAlgorithm for SigningKey {
|
||||
fn as_algorithm() -> DerivationAlgorithm {
|
||||
DerivationAlgorithm::Ed25519
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&DerivationResponse> for ExtendedPrivateKey<SigningKey> {
|
||||
type Error = TryFromDerivationResponseError;
|
||||
|
||||
fn try_from(value: &DerivationResponse) -> std::result::Result<Self, Self::Error> {
|
||||
fn try_from(value: &DerivationResponse) -> Result<Self, Self::Error> {
|
||||
match value.algorithm {
|
||||
DerivationAlgorithm::Ed25519 => {
|
||||
Self::new_from_parts(&value.data, value.depth, value.chain_code).map_err(From::from)
|
||||
}
|
||||
DerivationAlgorithm::Ed25519 => Ok(Self::new_from_parts(
|
||||
&value.data,
|
||||
value.depth,
|
||||
value.chain_code,
|
||||
)),
|
||||
_ => Err(Self::Error::Algorithm),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ed25519")]
|
||||
impl TryFrom<DerivationResponse> for ExtendedPrivateKey<ed25519_dalek::SigningKey> {
|
||||
impl TryFrom<DerivationResponse> for ExtendedPrivateKey<SigningKey> {
|
||||
type Error = TryFromDerivationResponseError;
|
||||
|
||||
fn try_from(value: DerivationResponse) -> std::result::Result<Self, Self::Error> {
|
||||
ExtendedPrivateKey::<ed25519_dalek::SigningKey>::try_from(&value)
|
||||
fn try_from(value: DerivationResponse) -> Result<Self, Self::Error> {
|
||||
ExtendedPrivateKey::<SigningKey>::try_from(&value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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,
|
||||
)?;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
use super::Keyfork;
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
use keyfork_derive_openpgp::openpgp::{
|
||||
use keyfork_derive_openpgp::{
|
||||
openpgp::{
|
||||
armor::{Kind, Writer},
|
||||
packet::UserID,
|
||||
serialize::Marshal,
|
||||
types::KeyFlags,
|
||||
},
|
||||
XPrvKey,
|
||||
};
|
||||
use keyfork_derive_util::{
|
||||
request::{DerivationAlgorithm, DerivationRequest, DerivationResponse},
|
||||
DerivationIndex, DerivationPath,
|
||||
};
|
||||
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)?;
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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"]
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
{{#include ../../links.md}}
|
||||
|
||||
# keyfork-entropy
|
||||
|
||||
Retrieve system entropy, output in hex format. The machine must be running a
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
{{#include ../../links.md}}
|
||||
|
||||
# keyfork-mnemonic-from-seed
|
||||
|
||||
Generate a mnemonic from a seed passed by input.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
{{#include ../../../links.md}}
|
||||
|
||||
# keyfork-shard-split-openpgp
|
||||
|
||||
<!-- Linked to: keyfork-user-guide/src/bin/keyfork-shard/index.md -->
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
{{#include ../../../links.md}}
|
||||
|
||||
# `keyfork derive`
|
||||
|
||||
Derive keys of various formats.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
{{#include ../../../links.md}}
|
||||
|
||||
# `keyfork mnemonic`
|
||||
|
||||
Utilities for managing mnemonics.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
{{#include links.md}}
|
||||
|
||||
# Configuration File
|
||||
|
||||
The Keyfork configuration file is used to store the integrity of the mnemonic
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
{{#include ../links.md}}
|
||||
|
||||
# Entropy Guide
|
||||
|
||||
Keyfork provides a `keyfork-entropy` crate for generating entropy. The crate
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
{{#include ../links.md}}
|
||||
|
||||
# Handling Data
|
||||
|
||||
In Rust, it is common to name things `as_*`, `to_*`, and `into_*`. These three
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
{{#include ../links.md}}
|
||||
|
||||
# Writing Binaries
|
||||
|
||||
### Binaries - Porcelain and Plumbing
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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].
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue