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)
|
test `git -C $(1) rev-parse HEAD` = $(3)
|
||||||
endef
|
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
|
.PHONY: review
|
||||||
review:
|
review:
|
||||||
$(eval BASE_REF_PARSED := $(shell git rev-parse $(BASE_REF)))
|
$(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 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 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)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
@ -11,6 +16,10 @@ mod tests;
|
||||||
/// An error occurred while interacting with Keyforkd.
|
/// An error occurred while interacting with Keyforkd.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum 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.
|
/// The environment variables used for determining a Keyforkd socket path were not set.
|
||||||
#[error("Neither KEYFORK_SOCKET_PATH nor XDG_RUNTIME_DIR were set")]
|
#[error("Neither KEYFORK_SOCKET_PATH nor XDG_RUNTIME_DIR were set")]
|
||||||
EnvVarsNotFound,
|
EnvVarsNotFound,
|
||||||
|
@ -37,7 +46,7 @@ pub enum Error {
|
||||||
|
|
||||||
/// An error encountered in Keyforkd.
|
/// An error encountered in Keyforkd.
|
||||||
#[error("Error in Keyforkd: {0}")]
|
#[error("Error in Keyforkd: {0}")]
|
||||||
Keyforkd(#[from] KeyforkdError)
|
Keyforkd(#[from] KeyforkdError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
@ -95,6 +104,38 @@ impl Client {
|
||||||
get_socket().map(|socket| Self { socket })
|
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>`].
|
/// Serialize and send a [`Request`] to the server, awaiting a [`Result<Response>`].
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|
|
@ -2,13 +2,10 @@
|
||||||
|
|
||||||
use std::time::{Duration, SystemTime, SystemTimeError};
|
use std::time::{Duration, SystemTime, SystemTimeError};
|
||||||
|
|
||||||
use derive_util::{
|
use derive_util::{DerivationIndex, ExtendedPrivateKey, IndexError, PrivateKey};
|
||||||
request::{DerivationResponse, TryFromDerivationResponseError},
|
|
||||||
DerivationIndex, ExtendedPrivateKey, PrivateKey,
|
|
||||||
IndexError,
|
|
||||||
};
|
|
||||||
use ed25519_dalek::SigningKey;
|
use ed25519_dalek::SigningKey;
|
||||||
pub use keyfork_derive_util as derive_util;
|
pub use keyfork_derive_util as derive_util;
|
||||||
|
pub use sequoia_openpgp as openpgp;
|
||||||
use sequoia_openpgp::{
|
use sequoia_openpgp::{
|
||||||
packet::{
|
packet::{
|
||||||
key::{Key4, PrimaryRole, SubordinateRole},
|
key::{Key4, PrimaryRole, SubordinateRole},
|
||||||
|
@ -18,7 +15,9 @@ use sequoia_openpgp::{
|
||||||
types::{KeyFlags, SignatureType},
|
types::{KeyFlags, SignatureType},
|
||||||
Cert, Packet,
|
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.
|
/// An error occurred while creating an OpenPGP key.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
@ -32,10 +31,6 @@ pub enum Error {
|
||||||
#[error("Key configured with both encryption and non-encryption key flags: {0:?}")]
|
#[error("Key configured with both encryption and non-encryption key flags: {0:?}")]
|
||||||
InvalidKeyFlags(KeyFlags),
|
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.
|
/// A derivation index could not be created from the given index.
|
||||||
#[error("Could not create derivation index: {0}")]
|
#[error("Could not create derivation index: {0}")]
|
||||||
Index(#[from] IndexError),
|
Index(#[from] IndexError),
|
||||||
|
@ -66,7 +61,7 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// The function may error for any condition mentioned in [`Error`].
|
/// 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) {
|
let primary_key_flags = match keys.get(0) {
|
||||||
Some(kf) if kf.for_certification() => kf,
|
Some(kf) if kf.for_certification() => kf,
|
||||||
_ => return Err(Error::NotCert),
|
_ => 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);
|
let one_day = SystemTime::now() + Duration::from_secs(60 * 60 * 24);
|
||||||
|
|
||||||
// Create certificate with initial key and signature
|
// 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 derived_primary_key = xprv.derive_child(&DerivationIndex::new(0, true)?)?;
|
||||||
let primary_key = Key::from(Key4::<_, PrimaryRole>::import_secret_ed25519(
|
let primary_key = Key::from(Key4::<_, PrimaryRole>::import_secret_ed25519(
|
||||||
&PrivateKey::to_bytes(derived_primary_key.private_key()),
|
&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[0] &= 0b1111_1000;
|
||||||
bytes[31] &= !0b1000_0000;
|
bytes[31] &= !0b1000_0000;
|
||||||
bytes[31] |= 0b0100_0000;
|
bytes[31] |= 0b0100_0000;
|
||||||
Key::from(
|
Key::from(Key4::<_, SubordinateRole>::import_secret_cv25519(
|
||||||
Key4::<_, SubordinateRole>::import_secret_cv25519(
|
&bytes, None, None, epoch,
|
||||||
&bytes,
|
)?)
|
||||||
None,
|
|
||||||
None,
|
|
||||||
epoch,
|
|
||||||
)?
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
Key::from(
|
Key::from(Key4::<_, SubordinateRole>::import_secret_ed25519(
|
||||||
Key4::<_, SubordinateRole>::import_secret_ed25519(
|
&PrivateKey::to_bytes(derived_key.private_key()),
|
||||||
&PrivateKey::to_bytes(derived_key.private_key()),
|
epoch,
|
||||||
epoch,
|
)?)
|
||||||
)?
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// As per OpenPGP spec, signing keys must backsig the primary key
|
// As per OpenPGP spec, signing keys must backsig the primary key
|
||||||
|
|
|
@ -2,12 +2,16 @@
|
||||||
|
|
||||||
use std::{env, process::ExitCode, str::FromStr};
|
use std::{env, process::ExitCode, str::FromStr};
|
||||||
|
|
||||||
use keyfork_derive_util::{
|
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
||||||
request::{DerivationAlgorithm, DerivationRequest, DerivationResponse},
|
|
||||||
DerivationIndex, DerivationPath,
|
|
||||||
};
|
|
||||||
use keyforkd_client::Client;
|
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)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
enum Error {
|
enum Error {
|
||||||
|
@ -108,16 +112,13 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
_ => panic!("Usage: {program_name} path subkey_format default_userid"),
|
_ => panic!("Usage: {program_name} path subkey_format default_userid"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
|
let derived_xprv = Client::discover_socket()?.request_xprv::<SigningKey>(&path)?;
|
||||||
let derived_data: DerivationResponse = Client::discover_socket()?
|
|
||||||
.request(&request.into())?
|
|
||||||
.try_into()?;
|
|
||||||
let subkeys = subkey_format
|
let subkeys = subkey_format
|
||||||
.iter()
|
.iter()
|
||||||
.map(|kt| kt.inner().clone())
|
.map(|kt| kt.inner().clone())
|
||||||
.collect::<Vec<_>>();
|
.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)?;
|
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.
|
/// Errors associated with creating or deriving Extended Private Keys.
|
||||||
#[derive(Error, Clone, Debug)]
|
#[derive(Error, Clone, Debug)]
|
||||||
pub enum Error {
|
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.
|
/// The maximum depth for key derivation has been reached. The supported maximum depth is 255.
|
||||||
#[error("Reached maximum depth for key derivation")]
|
#[error("Reached maximum depth for key derivation")]
|
||||||
Depth,
|
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.
|
/// An unknown error occurred while deriving a child key.
|
||||||
#[error("Unknown error while deriving child key")]
|
#[error("Unknown error while deriving child key")]
|
||||||
Derivation,
|
Derivation,
|
||||||
|
@ -45,11 +37,36 @@ type HmacSha512 = Hmac<Sha512>;
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct ExtendedPrivateKey<K: PrivateKey + Clone> {
|
pub struct ExtendedPrivateKey<K: PrivateKey + Clone> {
|
||||||
/// The internal private key data.
|
/// The internal private key data.
|
||||||
|
#[serde(with = "serde_with")]
|
||||||
private_key: K,
|
private_key: K,
|
||||||
depth: u8,
|
depth: u8,
|
||||||
chain_code: ChainCode,
|
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> {
|
impl<K: PrivateKey + Clone> std::fmt::Debug for ExtendedPrivateKey<K> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("ExtendedPrivateKey")
|
f.debug_struct("ExtendedPrivateKey")
|
||||||
|
@ -73,7 +90,7 @@ where
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// An error may be returned if:
|
/// An error may be returned if:
|
||||||
/// * The given seed had an incorrect length.
|
/// * The given seed had an incorrect length.
|
||||||
/// * A `HmacSha512` can't be constructed - this should be impossible.
|
/// * A `HmacSha512` can't be constructed.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```rust
|
/// ```rust
|
||||||
|
@ -82,31 +99,26 @@ where
|
||||||
/// # public_key::TestPublicKey as PublicKey,
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
/// # private_key::TestPrivateKey as PrivateKey,
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
/// # };
|
/// # };
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
/// let seed: &[u8; 64] = //
|
/// let seed: &[u8; 64] = //
|
||||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
|
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed);
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new(seed: impl AsRef<[u8]>) -> Result<Self> {
|
pub fn new(seed: impl AsRef<[u8]>) -> Self {
|
||||||
Self::new_internal(seed.as_ref())
|
Self::new_internal(seed.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_internal(seed: &[u8]) -> Result<Self> {
|
fn new_internal(seed: &[u8]) -> Self {
|
||||||
let len = seed.len();
|
let hash = HmacSha512::new_from_slice(&K::key().bytes().collect::<Vec<_>>())
|
||||||
if ![16, 32, 64].contains(&len) {
|
.expect("HmacSha512 InvalidLength should be infallible")
|
||||||
return Err(Error::BadSeedLength(len));
|
|
||||||
}
|
|
||||||
|
|
||||||
let hash = HmacSha512::new_from_slice(&K::key().bytes().collect::<Vec<_>>())?
|
|
||||||
.chain_update(seed)
|
.chain_update(seed)
|
||||||
.finalize()
|
.finalize()
|
||||||
.into_bytes();
|
.into_bytes();
|
||||||
let (private_key, chain_code) = hash.split_at(KEY_SIZE / 8);
|
let (private_key, chain_code) = hash.split_at(KEY_SIZE / 8);
|
||||||
|
|
||||||
Self::new_from_parts(
|
Self::new_from_parts(
|
||||||
private_key,
|
private_key
|
||||||
|
.try_into()
|
||||||
|
.expect("KEY_SIZE / 8 did not give a 32 byte slice"),
|
||||||
0,
|
0,
|
||||||
// Checked: chain_code is always the same length, hash is static size
|
// Checked: chain_code is always the same length, hash is static size
|
||||||
chain_code.try_into().expect("Invalid chain code length"),
|
chain_code.try_into().expect("Invalid chain code length"),
|
||||||
|
@ -125,21 +137,18 @@ where
|
||||||
/// # public_key::TestPublicKey as PublicKey,
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
/// # private_key::TestPrivateKey as PrivateKey,
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
/// # };
|
/// # };
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
/// let key: &[u8; 32] = //
|
/// let key: &[u8; 32] = //
|
||||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
/// let chain_code: &[u8; 32] = //
|
/// let chain_code: &[u8; 32] = //
|
||||||
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||||
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
|
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code);
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new_from_parts(seed: &[u8], depth: u8, chain_code: [u8; 32]) -> Result<Self> {
|
pub fn new_from_parts(key: &[u8; 32], depth: u8, chain_code: [u8; 32]) -> Self {
|
||||||
Ok(Self {
|
Self {
|
||||||
private_key: K::from_bytes(seed.try_into()?),
|
private_key: K::from_bytes(key),
|
||||||
depth,
|
depth,
|
||||||
chain_code,
|
chain_code,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the [`PrivateKey`].
|
/// Returns a reference to the [`PrivateKey`].
|
||||||
|
@ -152,15 +161,12 @@ where
|
||||||
/// # public_key::TestPublicKey as PublicKey,
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
/// # private_key::TestPrivateKey as PrivateKey,
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
/// # };
|
/// # };
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
/// let key: &[u8; 32] = //
|
/// let key: &[u8; 32] = //
|
||||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
/// let chain_code: &[u8; 32] = //
|
/// let chain_code: &[u8; 32] = //
|
||||||
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
/// # 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));
|
/// assert_eq!(xprv.private_key(), &PrivateKey::from_bytes(key));
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
pub fn private_key(&self) -> &K {
|
pub fn private_key(&self) -> &K {
|
||||||
&self.private_key
|
&self.private_key
|
||||||
|
@ -185,7 +191,7 @@ where
|
||||||
/// # 102, 201, 210, 159, 219, 222, 42, 201, 44, 196, 27,
|
/// # 102, 201, 210, 159, 219, 222, 42, 201, 44, 196, 27,
|
||||||
/// # 90, 221, 80, 85, 135, 79, 39, 253, 223, 35, 251
|
/// # 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();
|
/// let xpub = xprv.extended_public_key();
|
||||||
/// assert_eq!(known_key, xpub.public_key().to_bytes());
|
/// assert_eq!(known_key, xpub.public_key().to_bytes());
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
|
@ -209,7 +215,7 @@ where
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let seed: &[u8; 64] = //
|
/// let seed: &[u8; 64] = //
|
||||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
|
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed);
|
||||||
/// let pubkey = xprv.public_key();
|
/// let pubkey = xprv.public_key();
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
|
@ -227,15 +233,12 @@ where
|
||||||
/// # public_key::TestPublicKey as PublicKey,
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
/// # private_key::TestPrivateKey as PrivateKey,
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
/// # };
|
/// # };
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
/// let key: &[u8; 32] = //
|
/// let key: &[u8; 32] = //
|
||||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
/// let chain_code: &[u8; 32] = //
|
/// let chain_code: &[u8; 32] = //
|
||||||
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
/// # 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);
|
/// assert_eq!(xprv.depth(), 4);
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
pub fn depth(&self) -> u8 {
|
pub fn depth(&self) -> u8 {
|
||||||
self.depth
|
self.depth
|
||||||
|
@ -250,15 +253,12 @@ where
|
||||||
/// # public_key::TestPublicKey as PublicKey,
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
/// # private_key::TestPrivateKey as PrivateKey,
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
/// # };
|
/// # };
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
/// let key: &[u8; 32] = //
|
/// let key: &[u8; 32] = //
|
||||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
/// let chain_code: &[u8; 32] = //
|
/// let chain_code: &[u8; 32] = //
|
||||||
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
/// # 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());
|
/// assert_eq!(chain_code, &xprv.chain_code());
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
pub fn chain_code(&self) -> [u8; 32] {
|
pub fn chain_code(&self) -> [u8; 32] {
|
||||||
self.chain_code
|
self.chain_code
|
||||||
|
@ -280,7 +280,7 @@ where
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let seed: &[u8; 64] = //
|
/// let seed: &[u8; 64] = //
|
||||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
|
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed);
|
||||||
/// let path = DerivationPath::default()
|
/// let path = DerivationPath::default()
|
||||||
/// .chain_push(DerivationIndex::new(44, true)?)
|
/// .chain_push(DerivationIndex::new(44, true)?)
|
||||||
/// .chain_push(DerivationIndex::new(0, true)?)
|
/// .chain_push(DerivationIndex::new(0, true)?)
|
||||||
|
@ -326,7 +326,7 @@ where
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// let seed: &[u8; 64] = //
|
/// let seed: &[u8; 64] = //
|
||||||
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
|
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed);
|
||||||
/// let bip44_wallet = DerivationPath::default()
|
/// let bip44_wallet = DerivationPath::default()
|
||||||
/// .chain_push(DerivationIndex::new(44, true)?)
|
/// .chain_push(DerivationIndex::new(44, true)?)
|
||||||
/// .chain_push(DerivationIndex::new(0, true)?)
|
/// .chain_push(DerivationIndex::new(0, true)?)
|
||||||
|
@ -342,8 +342,8 @@ where
|
||||||
pub fn derive_child(&self, index: &DerivationIndex) -> Result<Self> {
|
pub fn derive_child(&self, index: &DerivationIndex) -> Result<Self> {
|
||||||
let depth = self.depth.checked_add(1).ok_or(Error::Depth)?;
|
let depth = self.depth.checked_add(1).ok_or(Error::Depth)?;
|
||||||
|
|
||||||
let mut hmac =
|
let mut hmac = HmacSha512::new_from_slice(&self.chain_code)
|
||||||
HmacSha512::new_from_slice(&self.chain_code).map_err(Error::HmacInvalidLength)?;
|
.expect("HmacSha512 InvalidLength should be infallible");
|
||||||
if index.is_hardened() {
|
if index.is_hardened() {
|
||||||
hmac.update(&[0]);
|
hmac.update(&[0]);
|
||||||
hmac.update(&self.private_key.to_bytes());
|
hmac.update(&self.private_key.to_bytes());
|
||||||
|
|
|
@ -68,7 +68,7 @@ impl DerivationAlgorithm {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
Self::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)?;
|
let derived_key = key.derive_path(path)?;
|
||||||
Ok(DerivationResponse::with_algo_and_xprv(
|
Ok(DerivationResponse::with_algo_and_xprv(
|
||||||
self.clone(),
|
self.clone(),
|
||||||
|
@ -77,7 +77,7 @@ impl DerivationAlgorithm {
|
||||||
}
|
}
|
||||||
#[cfg(feature = "secp256k1")]
|
#[cfg(feature = "secp256k1")]
|
||||||
Self::Secp256k1 => {
|
Self::Secp256k1 => {
|
||||||
let key = ExtendedPrivateKey::<k256::SecretKey>::new(seed)?;
|
let key = ExtendedPrivateKey::<k256::SecretKey>::new(seed);
|
||||||
let derived_key = key.derive_path(path)?;
|
let derived_key = key.derive_path(path)?;
|
||||||
Ok(DerivationResponse::with_algo_and_xprv(
|
Ok(DerivationResponse::with_algo_and_xprv(
|
||||||
self.clone(),
|
self.clone(),
|
||||||
|
@ -85,7 +85,7 @@ impl DerivationAlgorithm {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
Self::Internal => {
|
Self::Internal => {
|
||||||
let key = ExtendedPrivateKey::<TestPrivateKey>::new(seed)?;
|
let key = ExtendedPrivateKey::<TestPrivateKey>::new(seed);
|
||||||
let derived_key = key.derive_path(path)?;
|
let derived_key = key.derive_path(path)?;
|
||||||
Ok(DerivationResponse::with_algo_and_xprv(
|
Ok(DerivationResponse::with_algo_and_xprv(
|
||||||
self.clone(),
|
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.
|
/// A derivation request.
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct DerivationRequest {
|
pub struct DerivationRequest {
|
||||||
|
@ -234,7 +246,7 @@ pub struct DerivationResponse {
|
||||||
pub algorithm: DerivationAlgorithm,
|
pub algorithm: DerivationAlgorithm,
|
||||||
|
|
||||||
/// The derived private key.
|
/// The derived private key.
|
||||||
pub data: Vec<u8>,
|
pub data: [u8; 32],
|
||||||
|
|
||||||
/// The chain code, used for further derivation.
|
/// The chain code, used for further derivation.
|
||||||
pub chain_code: [u8; 32],
|
pub chain_code: [u8; 32],
|
||||||
|
@ -251,7 +263,7 @@ impl DerivationResponse {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
algorithm,
|
algorithm,
|
||||||
data: PrivateKey::to_bytes(xprv.private_key()).to_vec(),
|
data: PrivateKey::to_bytes(xprv.private_key()),
|
||||||
chain_code: xprv.chain_code(),
|
chain_code: xprv.chain_code(),
|
||||||
depth: xprv.depth(),
|
depth: xprv.depth(),
|
||||||
}
|
}
|
||||||
|
@ -272,47 +284,71 @@ pub enum TryFromDerivationResponseError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "secp256k1")]
|
#[cfg(feature = "secp256k1")]
|
||||||
impl TryFrom<&DerivationResponse> for ExtendedPrivateKey<k256::SecretKey> {
|
mod secp256k1 {
|
||||||
type Error = TryFromDerivationResponseError;
|
use super::*;
|
||||||
|
use k256::SecretKey;
|
||||||
|
|
||||||
fn try_from(value: &DerivationResponse) -> std::result::Result<Self, Self::Error> {
|
impl AsAlgorithm for SecretKey {
|
||||||
match value.algorithm {
|
fn as_algorithm() -> DerivationAlgorithm {
|
||||||
DerivationAlgorithm::Secp256k1 => {
|
DerivationAlgorithm::Secp256k1
|
||||||
Self::new_from_parts(&value.data, value.depth, value.chain_code).map_err(From::from)
|
|
||||||
}
|
|
||||||
_ => Err(Self::Error::Algorithm),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "secp256k1")]
|
impl TryFrom<&DerivationResponse> for ExtendedPrivateKey<SecretKey> {
|
||||||
impl TryFrom<DerivationResponse> for ExtendedPrivateKey<k256::SecretKey> {
|
type Error = TryFromDerivationResponseError;
|
||||||
type Error = TryFromDerivationResponseError;
|
|
||||||
|
|
||||||
fn try_from(value: DerivationResponse) -> std::result::Result<Self, Self::Error> {
|
fn try_from(value: &DerivationResponse) -> Result<Self, Self::Error> {
|
||||||
ExtendedPrivateKey::<k256::SecretKey>::try_from(&value)
|
match value.algorithm {
|
||||||
}
|
DerivationAlgorithm::Secp256k1 => Ok(Self::new_from_parts(
|
||||||
}
|
&value.data,
|
||||||
|
value.depth,
|
||||||
#[cfg(feature = "ed25519")]
|
value.chain_code,
|
||||||
impl TryFrom<&DerivationResponse> for ExtendedPrivateKey<ed25519_dalek::SigningKey> {
|
)),
|
||||||
type Error = TryFromDerivationResponseError;
|
_ => Err(Self::Error::Algorithm),
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
_ => 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")]
|
#[cfg(feature = "ed25519")]
|
||||||
impl TryFrom<DerivationResponse> for ExtendedPrivateKey<ed25519_dalek::SigningKey> {
|
mod ed25519 {
|
||||||
type Error = TryFromDerivationResponseError;
|
use super::*;
|
||||||
|
use ed25519_dalek::SigningKey;
|
||||||
|
|
||||||
fn try_from(value: DerivationResponse) -> std::result::Result<Self, Self::Error> {
|
impl AsAlgorithm for SigningKey {
|
||||||
ExtendedPrivateKey::<ed25519_dalek::SigningKey>::try_from(&value)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ fn secp256k1() {
|
||||||
} = test;
|
} = test;
|
||||||
|
|
||||||
// Tests for ExtendedPrivateKey
|
// Tests for ExtendedPrivateKey
|
||||||
let xkey = ExtendedPrivateKey::<SecretKey>::new(seed).unwrap();
|
let xkey = ExtendedPrivateKey::<SecretKey>::new(seed);
|
||||||
let derived_key = xkey.derive_path(&chain).unwrap();
|
let derived_key = xkey.derive_path(&chain).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
derived_key.chain_code().as_slice(),
|
derived_key.chain_code().as_slice(),
|
||||||
|
@ -51,7 +51,7 @@ fn secp256k1() {
|
||||||
// Tests for DerivationRequest
|
// Tests for DerivationRequest
|
||||||
let request = DerivationRequest::new(DerivationAlgorithm::Secp256k1, &chain);
|
let request = DerivationRequest::new(DerivationAlgorithm::Secp256k1, &chain);
|
||||||
let response = request.derive_with_master_seed(seed.clone()).unwrap();
|
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;
|
} = test;
|
||||||
|
|
||||||
// Tests for ExtendedPrivateKey
|
// Tests for ExtendedPrivateKey
|
||||||
let xkey = ExtendedPrivateKey::<SigningKey>::new(seed).unwrap();
|
let xkey = ExtendedPrivateKey::<SigningKey>::new(seed);
|
||||||
let derived_key = xkey.derive_path(&chain).unwrap();
|
let derived_key = xkey.derive_path(&chain).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
derived_key.chain_code().as_slice(),
|
derived_key.chain_code().as_slice(),
|
||||||
|
@ -96,7 +96,7 @@ fn ed25519() {
|
||||||
// Tests for DerivationRequest
|
// Tests for DerivationRequest
|
||||||
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &chain);
|
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &chain);
|
||||||
let response = request.derive_with_master_seed(seed.to_vec()).unwrap();
|
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;
|
use ed25519_dalek::SigningKey;
|
||||||
|
|
||||||
let seed = hex!("000102030405060708090a0b0c0d0e0f");
|
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())
|
xkey.derive_path(&DerivationPath::from_str("m/0").unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ fn panics_at_depth() {
|
||||||
use ed25519_dalek::SigningKey;
|
use ed25519_dalek::SigningKey;
|
||||||
|
|
||||||
let seed = hex!("000102030405060708090a0b0c0d0e0f");
|
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) {
|
for i in 0..=u32::from(u8::MAX) {
|
||||||
xkey = xkey
|
xkey = xkey
|
||||||
.derive_child(&DerivationIndex::new(i, true).unwrap())
|
.derive_child(&DerivationIndex::new(i, true).unwrap())
|
||||||
|
|
|
@ -13,9 +13,9 @@ use aes_gcm::{
|
||||||
Aes256Gcm, Error as AesError, KeyInit, Nonce,
|
Aes256Gcm, Error as AesError, KeyInit, Nonce,
|
||||||
};
|
};
|
||||||
use hkdf::{Hkdf, InvalidLength as HkdfInvalidLength};
|
use hkdf::{Hkdf, InvalidLength as HkdfInvalidLength};
|
||||||
use keyfork_derive_openpgp::derive_util::{
|
use keyfork_derive_openpgp::{
|
||||||
request::{DerivationAlgorithm, DerivationRequest},
|
derive_util::{DerivationPath, PathError},
|
||||||
DerivationPath, PathError,
|
XPrv,
|
||||||
};
|
};
|
||||||
use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError, MnemonicGenerationError, Wordlist};
|
use keyfork_mnemonic_util::{Mnemonic, MnemonicFromStrError, MnemonicGenerationError, Wordlist};
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
|
@ -123,6 +123,10 @@ pub enum Error {
|
||||||
#[error("IO error: {0}")]
|
#[error("IO error: {0}")]
|
||||||
Io(#[source] std::io::Error),
|
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.
|
/// An error occurred while parsing a derivation path.
|
||||||
#[error("Derivation path: {0}")]
|
#[error("Derivation path: {0}")]
|
||||||
DerivationPath(#[from] PathError),
|
DerivationPath(#[from] PathError),
|
||||||
|
@ -643,13 +647,10 @@ pub fn combine(
|
||||||
|
|
||||||
// TODO: extract as function
|
// TODO: extract as function
|
||||||
let userid = UserID::from("keyfork-sss");
|
let userid = UserID::from("keyfork-sss");
|
||||||
let kdr = DerivationRequest::new(
|
let path = DerivationPath::from_str("m/7366512'/0'")?;
|
||||||
DerivationAlgorithm::Ed25519,
|
let xprv = XPrv::new(&secret).derive_path(&path)?;
|
||||||
&DerivationPath::from_str("m/7366512'/0'")?,
|
|
||||||
)
|
|
||||||
.derive_with_master_seed(secret.clone())?;
|
|
||||||
let derived_cert = keyfork_derive_openpgp::derive(
|
let derived_cert = keyfork_derive_openpgp::derive(
|
||||||
kdr,
|
xprv,
|
||||||
&[KeyFlags::empty().set_certification().set_signing()],
|
&[KeyFlags::empty().set_certification().set_signing()],
|
||||||
&userid,
|
&userid,
|
||||||
)?;
|
)?;
|
||||||
|
@ -680,13 +681,10 @@ pub fn combine(
|
||||||
pub fn split(threshold: u8, certs: Vec<Cert>, secret: &[u8], output: impl Write) -> Result<()> {
|
pub fn split(threshold: u8, certs: Vec<Cert>, secret: &[u8], output: impl Write) -> Result<()> {
|
||||||
// build cert to sign encrypted shares
|
// build cert to sign encrypted shares
|
||||||
let userid = UserID::from("keyfork-sss");
|
let userid = UserID::from("keyfork-sss");
|
||||||
let kdr = DerivationRequest::new(
|
let path = DerivationPath::from_str("m/7366512'/0'")?;
|
||||||
DerivationAlgorithm::Ed25519,
|
let xprv = XPrv::new(&secret).derive_path(&path)?;
|
||||||
&DerivationPath::from_str("m/7366512'/0'")?,
|
|
||||||
)
|
|
||||||
.derive_with_master_seed(secret.to_vec())?;
|
|
||||||
let derived_cert = keyfork_derive_openpgp::derive(
|
let derived_cert = keyfork_derive_openpgp::derive(
|
||||||
kdr,
|
xprv,
|
||||||
&[KeyFlags::empty().set_certification().set_signing()],
|
&[KeyFlags::empty().set_certification().set_signing()],
|
||||||
&userid,
|
&userid,
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
use super::Keyfork;
|
use super::Keyfork;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
use keyfork_derive_openpgp::openpgp::{
|
use keyfork_derive_openpgp::{
|
||||||
armor::{Kind, Writer},
|
openpgp::{
|
||||||
packet::UserID,
|
armor::{Kind, Writer},
|
||||||
serialize::Marshal,
|
packet::UserID,
|
||||||
types::KeyFlags,
|
serialize::Marshal,
|
||||||
};
|
types::KeyFlags,
|
||||||
use keyfork_derive_util::{
|
},
|
||||||
request::{DerivationAlgorithm, DerivationRequest, DerivationResponse},
|
XPrvKey,
|
||||||
DerivationIndex, DerivationPath,
|
|
||||||
};
|
};
|
||||||
|
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
||||||
use keyforkd_client::Client;
|
use keyforkd_client::Client;
|
||||||
|
|
||||||
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
||||||
|
@ -48,12 +48,9 @@ impl DeriveSubcommands {
|
||||||
.set_storage_encryption(),
|
.set_storage_encryption(),
|
||||||
KeyFlags::empty().set_authentication(),
|
KeyFlags::empty().set_authentication(),
|
||||||
];
|
];
|
||||||
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
|
let xprv = Client::discover_socket()?.request_xprv::<XPrvKey>(&path)?;
|
||||||
let derived_data: DerivationResponse = Client::discover_socket()?
|
|
||||||
.request(&request.into())?
|
|
||||||
.try_into()?;
|
|
||||||
let default_userid = UserID::from(user_id.as_str());
|
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)?;
|
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 card_backend_pcsc::PcscBackend;
|
||||||
use openpgp_card_sequoia::{state::Open, types::KeyType, Card};
|
use openpgp_card_sequoia::{state::Open, types::KeyType, Card};
|
||||||
|
|
||||||
use keyfork_derive_openpgp::openpgp::{self, packet::UserID, types::KeyFlags, Cert};
|
use keyfork_derive_openpgp::{
|
||||||
use keyfork_derive_util::{
|
openpgp::{self, packet::UserID, types::KeyFlags, Cert},
|
||||||
request::{DerivationAlgorithm, DerivationRequest},
|
XPrv,
|
||||||
DerivationIndex, DerivationPath,
|
|
||||||
};
|
};
|
||||||
|
use keyfork_derive_util::{DerivationIndex, DerivationPath};
|
||||||
use keyfork_prompt::{
|
use keyfork_prompt::{
|
||||||
validators::{PinValidator, Validator},
|
validators::{PinValidator, Validator},
|
||||||
Message, PromptHandler, Terminal,
|
Message, PromptHandler, Terminal,
|
||||||
|
@ -42,10 +42,9 @@ fn derive_key(seed: &[u8], index: u8) -> Result<Cert> {
|
||||||
.chain_push(chain)
|
.chain_push(chain)
|
||||||
.chain_push(account)
|
.chain_push(account)
|
||||||
.chain_push(subkey);
|
.chain_push(subkey);
|
||||||
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &path);
|
let xprv = XPrv::new(seed).derive_path(&path)?;
|
||||||
let response = request.derive_with_master_seed(seed.to_vec())?;
|
|
||||||
let userid = UserID::from(format!("Keyfork Shard {index}"));
|
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)
|
Ok(cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ license = "MIT"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
bin = ["decode-backend-zbar"]
|
bin = ["decode-backend-rqrr"]
|
||||||
decode-backend-rqrr = ["dep:rqrr"]
|
decode-backend-rqrr = ["dep:rqrr"]
|
||||||
decode-backend-zbar = ["dep:keyfork-zbar"]
|
decode-backend-zbar = ["dep:keyfork-zbar"]
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,9 @@ impl Image {
|
||||||
///
|
///
|
||||||
/// A FourCC code can be given in the format:
|
/// A FourCC code can be given in the format:
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```rust,ignore
|
||||||
/// self.set_format(b"Y800")
|
/// self.set_format(b"Y800")
|
||||||
/// ````
|
/// ```
|
||||||
pub(crate) fn set_format(&mut self, fourcc: &[u8; 4]) {
|
pub(crate) fn set_format(&mut self, fourcc: &[u8; 4]) {
|
||||||
let fourcc: u64 = fourcc[0] as u64
|
let fourcc: u64 = fourcc[0] as u64
|
||||||
| ((fourcc[1] as u64) << 8)
|
| ((fourcc[1] as u64) << 8)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include links.md}}
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
Keyfork has different dependencies depending on the feature set used for
|
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.
|
# Confirmed to work as of 2024-01-17.
|
||||||
cargo install --locked --path crates/util/keyfork-entropy --bin keyfork-entropy --features bin
|
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: -->
|
<!-- vim:set et sts=0 sw=2 ts=2: -->
|
||||||
|
{{ #include links.md }}
|
||||||
# Summary
|
# Summary
|
||||||
|
|
||||||
# User Guide
|
# User Guide
|
||||||
|
|
||||||
|
- [Introduction to Keyfork](./introduction.md)
|
||||||
- [Installing Keyfork](./INSTALL.md)
|
- [Installing Keyfork](./INSTALL.md)
|
||||||
- [Security Considerations](./security.md)
|
- [Security Considerations](./security.md)
|
||||||
- [Shard Commands](./shard.md)
|
- [Shard Commands](./shard.md)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../links.md}}
|
||||||
|
|
||||||
# keyfork-derive-key
|
# keyfork-derive-key
|
||||||
|
|
||||||
Derive a key from a given derivation path.
|
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
|
Hex-encoded private key. Note that this is not the _extended_ private key, and
|
||||||
can't be used to derive further data.
|
can't be used to derive further data.
|
||||||
|
|
||||||
[`keyforkd`]: ./bin/keyforkd.md
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../links.md}}
|
||||||
|
|
||||||
# keyfork-derive-openpgp
|
# keyfork-derive-openpgp
|
||||||
|
|
||||||
Derive a key from a given derivation path.
|
Derive a key from a given derivation path.
|
||||||
|
@ -28,5 +30,3 @@ the shell silently ignoring the single quotes in the derivation path.
|
||||||
## Output
|
## Output
|
||||||
|
|
||||||
OpenPGP ASCII armored key, signed to be valid for 24 hours.
|
OpenPGP ASCII armored key, signed to be valid for 24 hours.
|
||||||
|
|
||||||
[`keyforkd`]: ./bin/keyforkd.md
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../../links.md}}
|
||||||
|
|
||||||
# keyfork-entropy
|
# keyfork-entropy
|
||||||
|
|
||||||
Retrieve system entropy, output in hex format. The machine must be running a
|
Retrieve system entropy, output in hex format. The machine must be running a
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../../links.md}}
|
||||||
|
|
||||||
# keyfork-mnemonic-from-seed
|
# keyfork-mnemonic-from-seed
|
||||||
|
|
||||||
Generate a mnemonic from a seed passed by input.
|
Generate a mnemonic from a seed passed by input.
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../../links.md}}
|
||||||
|
|
||||||
# keyfork-shard
|
# keyfork-shard
|
||||||
|
|
||||||
<!-- Linked to: keyfork-user-guide/src/bin/keyfork/shard/index.md -->
|
<!-- 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
|
## OpenPGP
|
||||||
|
|
||||||
Keyfork provides OpenPGP compatible [`split`][openpgp-split] and
|
Keyfork provides OpenPGP compatible [`split`][kshard-opgp-split] and
|
||||||
[`combine`][openpgp-combine] versions of Shard binaries. These binaries use
|
[`combine`][kshard-opgp-combine] versions of Shard binaries. These binaries use
|
||||||
Sequoia OpenPGP and while they require all the necessary certificates for the
|
Sequoia OpenPGP and while they require all the necessary certificates for the
|
||||||
splitting stage, the certificates are included in the payload, and once Keyfork
|
splitting stage, the certificates are included in the payload, and once Keyfork
|
||||||
supports decrypting using OpenPGP smartcards, certificates will not be required
|
supports decrypting using OpenPGP smartcards, certificates will not be required
|
||||||
to decrypt the shares.
|
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
|
# keyfork-shard-combine-openpgp
|
||||||
|
|
||||||
Combine `threshold` shares into a previously [`split`] secret.
|
Combine shares into a previously [`split`][kshard-opgp-split] secret.
|
||||||
|
|
||||||
## Arguments
|
## Arguments
|
||||||
|
|
||||||
|
@ -31,5 +33,3 @@ keyfork-shard-combine-openpgp shard.pgp
|
||||||
# Decrypt using on-disk private keys
|
# Decrypt using on-disk private keys
|
||||||
keyfork-shard-combine-openpgp key_discovery.pgp shard.pgp
|
keyfork-shard-combine-openpgp key_discovery.pgp shard.pgp
|
||||||
```
|
```
|
||||||
|
|
||||||
[`split`]: ./split.md
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../../../links.md}}
|
||||||
|
|
||||||
# keyfork-shard-split-openpgp
|
# keyfork-shard-split-openpgp
|
||||||
|
|
||||||
<!-- Linked to: keyfork-user-guide/src/bin/keyfork-shard/index.md -->
|
<!-- Linked to: keyfork-user-guide/src/bin/keyfork-shard/index.md -->
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../../../links.md}}
|
||||||
|
|
||||||
# `keyfork derive`
|
# `keyfork derive`
|
||||||
|
|
||||||
Derive keys of various formats.
|
Derive keys of various formats.
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../../links.md}}
|
||||||
|
|
||||||
# keyfork
|
# keyfork
|
||||||
|
|
||||||
The primary interface for interacting with Keyfork utilities.
|
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
|
Utilities to automatically manage the setup of Keyfork. This includes
|
||||||
generating a seed, splitting it into a Shard file, and provisioning smart cards
|
generating a seed, splitting it into a Shard file, and provisioning smart cards
|
||||||
with the capability to decrypt the shards.
|
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`
|
# `keyfork mnemonic`
|
||||||
|
|
||||||
Utilities for managing mnemonics.
|
Utilities for managing mnemonics.
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../../../links.md}}
|
||||||
|
|
||||||
# `keyfork recover`
|
# `keyfork recover`
|
||||||
|
|
||||||
Recover a seed to memory from a mnemonic, shard, or other format, then launch
|
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
|
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, followed by an input prompt of 72 words to be received from
|
||||||
the shardholder.
|
the shardholder.
|
||||||
|
|
||||||
[`keyfork shard transport`]: ../shard/index.md#keyfork-shard-transport
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../../../links.md}}
|
||||||
|
|
||||||
# `keyfork shard`
|
# `keyfork shard`
|
||||||
|
|
||||||
<!-- Linked to: keyfork-user-guide/src/bin/keyfork-shard/index.md -->
|
<!-- 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
|
# Transport using on-disk private keys
|
||||||
keyfork shard transport key_discovery.pgp shard.pgp
|
keyfork shard transport key_discovery.pgp shard.pgp
|
||||||
```
|
```
|
||||||
|
|
||||||
[`keyfork recover remote-shard`]: ../recover/index.md#keyfork-recover-remote-shard
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../../../links.md}}
|
||||||
|
|
||||||
# `keyfork wizard`
|
# `keyfork wizard`
|
||||||
|
|
||||||
Set up Keyfork using a guided setup process.
|
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
|
An OpenPGP-encrypted Shard file, if not previously configured to be written to
|
||||||
a file using `--output`.
|
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
|
||||||
|
|
||||||
Keyforkd is the backend for deriving data using Keyfork. A mnemonic is loaded
|
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
|
for users to approve the path requested by the client, such as `m/44'/0'` being
|
||||||
"Bitcoin", or `m/7366512'` being "OpenPGP".
|
"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
|
custom to Keyfork, it is easy to implement. The crate `keyfork-frame` provides
|
||||||
a sync (`Read`, `Write`) and Tokio-compatible async (`AsyncRead`, `AsyncWrite`)
|
a sync (`Read`, `Write`) and Tokio-compatible async (`AsyncRead`, `AsyncWrite`)
|
||||||
pair of methods for encoding and decoding frames.
|
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
|
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
|
stream. Then, the hash is written to the stream. Lastly, the data itself is
|
||||||
written as-is to the stream.
|
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
|
# Configuration File
|
||||||
|
|
||||||
The Keyfork configuration file is used to store the integrity of the mnemonic
|
The Keyfork configuration file is used to store the integrity of the mnemonic
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../links.md}}
|
||||||
|
|
||||||
# Auditing Dependencies
|
# Auditing Dependencies
|
||||||
|
|
||||||
Dependencies must be reviewed before being added to the repository, and must
|
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.
|
A command line interface for generating, deriving from, and managing secrets.
|
||||||
|
|
||||||
* [`card-backend-pcsc`]: Interacting with smartcards using PCSC. Used as a card
|
* [`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
|
* [`clap`]: Command line argument parsing, helps building an intuitive command
|
||||||
line interface.
|
line interface.
|
||||||
* [`clap_complete`]: Shell autocompletion file generator. Helps the user
|
* [`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.
|
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-crossterm`]: #keyfork-crossterm
|
||||||
[`keyfork-derive-openpgp`]: #keyfork-derive-openpgp
|
[`keyfork-derive-openpgp`]: #keyfork-derive-openpgp
|
||||||
[`keyfork-derive-path-data`]: #keyfork-derive-path-data
|
[`keyfork-derive-path-data`]: #keyfork-derive-path-data
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../links.md}}
|
||||||
|
|
||||||
# Entropy Guide
|
# Entropy Guide
|
||||||
|
|
||||||
Keyfork provides a `keyfork-entropy` crate for generating entropy. The crate
|
Keyfork provides a `keyfork-entropy` crate for generating entropy. The crate
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../links.md}}
|
||||||
|
|
||||||
# Handling Data
|
# Handling Data
|
||||||
|
|
||||||
In Rust, it is common to name things `as_*`, `to_*`, and `into_*`. These three
|
In Rust, it is common to name things `as_*`, `to_*`, and `into_*`. These three
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../links.md}}
|
||||||
|
|
||||||
# Writing Binaries
|
# Writing Binaries
|
||||||
|
|
||||||
### Binaries - Porcelain and Plumbing
|
### Binaries - Porcelain and Plumbing
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include ../links.md}}
|
||||||
|
|
||||||
# Developing Provisioners
|
# Developing Provisioners
|
||||||
|
|
||||||
**Note:** This document makes heavy use of references to OpenPGP and assumes
|
**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.
|
derive unique keys for each use, such as OpenPGP capabilities or PIV slots.
|
||||||
Additionally, when provisioning to a key, the configuration for that
|
Additionally, when provisioning to a key, the configuration for that
|
||||||
provisioner should be stored to the configuration file.
|
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
|
# Keyfork Shard Commands
|
||||||
|
|
||||||
Sharding a seed allows "M-of-N" recovery of the seed, which is useful for
|
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
|
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
|
filesystem, to avoid keeping the key on written memory for longer than
|
||||||
necessary.
|
necessary.
|
||||||
|
|
||||||
[Sequoia]: https://sequoia-pgp.org
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{{#include links.md}}
|
||||||
|
|
||||||
# Common Usage
|
# Common Usage
|
||||||
|
|
||||||
Keyfork is a tool to help manage the creation and derivation of binary data
|
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
|
```sh
|
||||||
keyfork derive openpgp "John Doe <jdoe@example.com>"
|
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