Compare commits
No commits in common. "31e51f65a5789a205747a8526020051cfdf9540d" and "33405ee4fc7e48813481a202bf704f2bb5ff5ef4" have entirely different histories.
31e51f65a5
...
33405ee4fc
|
@ -40,25 +40,6 @@
|
|||
//! # keyforkd::test_util::Infallible::Ok(())
|
||||
//! # }).unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! In tests, the Keyforkd test_util module and TestPrivateKeys can be used.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use std::str::FromStr;
|
||||
//!
|
||||
//! use keyforkd_client::Client;
|
||||
//! use keyfork_derive_util::DerivationPath;
|
||||
//! use keyfork_derive_util::private_key::TestPrivateKey as PrivateKey;
|
||||
//!
|
||||
//! let seed = b"funky accordion noises";
|
||||
//! keyforkd::test_util::run_test(seed, |socket_path| {
|
||||
//! std::env::set_var("KEYFORKD_SOCKET_PATH", socket_path);
|
||||
//! let derivation_path = DerivationPath::from_str("m/44'/0'").unwrap();
|
||||
//! let mut client = Client::discover_socket().unwrap();
|
||||
//! let xprv = client.request_xprv::<PrivateKey>(&derivation_path).unwrap();
|
||||
//! keyforkd::test_util::Infallible::Ok(())
|
||||
//! }).unwrap();
|
||||
//! ```
|
||||
|
||||
pub use std::os::unix::net::UnixStream;
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
|
|
@ -18,7 +18,7 @@ fn secp256k1_test_suite() {
|
|||
let seed = seed_test.seed;
|
||||
run_test(&seed, move |socket_path| -> Result<(), Box<dyn std::error::Error + Send>> {
|
||||
for test in seed_test.tests {
|
||||
let socket = UnixStream::connect(socket_path).unwrap();
|
||||
let socket = UnixStream::connect(&socket_path).unwrap();
|
||||
let mut client = Client::new(socket);
|
||||
let chain = DerivationPath::from_str(test.chain).unwrap();
|
||||
let chain_len = chain.len();
|
||||
|
@ -29,7 +29,7 @@ fn secp256k1_test_suite() {
|
|||
// key using an XPrv, for all but the last XPrv, which is verified after this
|
||||
for i in 2..chain_len {
|
||||
// FIXME: Keyfork will only allow one request per session
|
||||
let socket = UnixStream::connect(socket_path).unwrap();
|
||||
let socket = UnixStream::connect(&socket_path).unwrap();
|
||||
let mut client = Client::new(socket);
|
||||
let path = DerivationPath::from_str(test.chain).unwrap();
|
||||
let left_path = path.inner()[..i]
|
||||
|
@ -40,7 +40,7 @@ fn secp256k1_test_suite() {
|
|||
.fold(DerivationPath::default(), |p, i| p.chain_push(i.clone()));
|
||||
let xprv = dbg!(client.request_xprv::<SecretKey>(&left_path)).unwrap();
|
||||
let derived_xprv = xprv.derive_path(&right_path).unwrap();
|
||||
let socket = UnixStream::connect(socket_path).unwrap();
|
||||
let socket = UnixStream::connect(&socket_path).unwrap();
|
||||
let mut client = Client::new(socket);
|
||||
let keyforkd_xprv = client.request_xprv::<SecretKey>(&path).unwrap();
|
||||
assert_eq!(
|
||||
|
@ -73,7 +73,7 @@ fn ed25519_test_suite() {
|
|||
let seed = seed_test.seed;
|
||||
run_test(&seed, move |socket_path| {
|
||||
for test in seed_test.tests {
|
||||
let socket = UnixStream::connect(socket_path).unwrap();
|
||||
let socket = UnixStream::connect(&socket_path).unwrap();
|
||||
let mut client = Client::new(socket);
|
||||
let chain = DerivationPath::from_str(test.chain).unwrap();
|
||||
let chain_len = chain.len();
|
||||
|
|
|
@ -34,15 +34,3 @@ request, as well as its best-effort guess on what path is being derived (using
|
|||
the `keyfork-derive-path-data` crate), to inform the user of what keys are
|
||||
requested. Once the server sends the client the new extended private key, the
|
||||
client can then choose to use the key as-is, or derive further keys.
|
||||
|
||||
## Testing
|
||||
|
||||
A Keyfork server can be automatically started by using [`test_util::run_test`].
|
||||
The function accepts a closure, starting the server before the closure is run,
|
||||
and closing the server after the closure has completed. This may be useful for
|
||||
people writing software that interacts with the Keyfork server, such as a
|
||||
deriver or a provisioner. A test seed must be provided, but can be any content.
|
||||
The closure accepts one argument, the path of the UNIX socket from which the
|
||||
server can be accessed.
|
||||
|
||||
Examples of the test utility can be seen in the `keyforkd-client` crate.
|
||||
|
|
|
@ -57,7 +57,7 @@ pub async fn start_and_run_server_on(
|
|||
let service = ServiceBuilder::new()
|
||||
.layer(middleware::BincodeLayer::new())
|
||||
// TODO: passphrase support and/or store passphrase with mnemonic
|
||||
.service(Keyforkd::new(mnemonic.generate_seed(None)));
|
||||
.service(Keyforkd::new(mnemonic.seed(None)?));
|
||||
|
||||
let mut server = match UnixServer::bind(socket_path) {
|
||||
Ok(s) => s,
|
||||
|
|
|
@ -25,25 +25,14 @@ pub struct InfallibleError {
|
|||
/// ```
|
||||
pub type Infallible<T> = std::result::Result<T, InfallibleError>;
|
||||
|
||||
/// Run a test making use of a Keyforkd server. The test may use a seed (the first argument) from a
|
||||
/// test suite, or (as shown in the example below) a simple seed may be used solely to ensure
|
||||
/// the server is capable of being interacted with. The test is in the form of a closure, expected
|
||||
/// to return a [`Result`] where success is a unit type (test passed) and the error is any error
|
||||
/// that happened during the test (alternatively, a panic may be used, and will be returned as an
|
||||
/// error).
|
||||
/// Run a test making use of a Keyforkd server. The path to the socket of the Keyforkd server is
|
||||
/// provided as the only argument to the closure. The closure is expected to return a Result; the
|
||||
/// Error field of the Result may be an error returned by a test.
|
||||
///
|
||||
/// # Panics
|
||||
/// The function may panic if any errors arise while configuring and using the Tokio multithreaded
|
||||
/// runtime.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// use std::os::unix::net::UnixStream;
|
||||
/// let seed = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
/// keyforkd::test_util::run_test(seed.as_slice(), |path| {
|
||||
/// UnixStream::connect(&path).map(|_| ())
|
||||
/// }).unwrap();
|
||||
/// ```
|
||||
/// The function is not expected to run in production; therefore, the function plays "fast and
|
||||
/// loose" wih the usage of [`Result::expect`]. In normal usage, these should never be an issue.
|
||||
#[allow(clippy::missing_errors_doc)]
|
||||
pub fn run_test<F, E>(seed: &[u8], closure: F) -> Result<(), E>
|
||||
where
|
||||
|
|
|
@ -46,7 +46,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let mut client = Client::discover_socket()?;
|
||||
let request = DerivationRequest::new(algo, &path);
|
||||
let response = client.request(&request.into())?;
|
||||
println!("{}", smex::encode(DerivationResponse::try_from(response)?.data));
|
||||
println!("{}", smex::encode(&DerivationResponse::try_from(response)?.data));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! Creation of OpenPGP Transferable Secret Keys from BIP-0032 derived data.
|
||||
//! Creation of OpenPGP certificates from BIP-0032 derived data.
|
||||
|
||||
use std::{
|
||||
str::FromStr,
|
||||
|
@ -74,7 +74,7 @@ pub fn derive(xprv: XPrv, keys: &[KeyFlags], userid: &UserID) -> Result<Cert> {
|
|||
let expiration_date = match std::env::var("KEYFORK_OPENPGP_EXPIRE").as_mut() {
|
||||
Ok(var) => {
|
||||
let ch = var.pop();
|
||||
match (ch, u64::from_str(var)) {
|
||||
match (ch, u64::from_str(&var)) {
|
||||
(Some(ch @ ('d' | 'm' | 'y')), Ok(expire)) => {
|
||||
let multiplier = match ch {
|
||||
'd' => 1,
|
||||
|
|
|
@ -209,7 +209,7 @@ impl DerivationRequest {
|
|||
/// # }
|
||||
pub fn derive_with_mnemonic(&self, mnemonic: &Mnemonic) -> Result<DerivationResponse> {
|
||||
// TODO: passphrase support and/or store passphrase within mnemonic
|
||||
self.derive_with_master_seed(&mnemonic.generate_seed(None))
|
||||
self.derive_with_master_seed(&mnemonic.seed(None)?)
|
||||
}
|
||||
|
||||
/// Derive an [`ExtendedPrivateKey`] using the given seed.
|
||||
|
|
|
@ -30,7 +30,7 @@ fn secp256k1() {
|
|||
} = test;
|
||||
|
||||
// Tests for ExtendedPrivateKey
|
||||
let varlen_seed = VariableLengthSeed::new(seed);
|
||||
let varlen_seed = VariableLengthSeed::new(&seed);
|
||||
let xkey = ExtendedPrivateKey::<SecretKey>::new(varlen_seed);
|
||||
let derived_key = xkey.derive_path(&chain).unwrap();
|
||||
assert_eq!(
|
||||
|
@ -51,7 +51,7 @@ fn secp256k1() {
|
|||
|
||||
// Tests for DerivationRequest
|
||||
let request = DerivationRequest::new(DerivationAlgorithm::Secp256k1, &chain);
|
||||
let response = request.derive_with_master_seed(seed).unwrap();
|
||||
let response = request.derive_with_master_seed(&seed).unwrap();
|
||||
assert_eq!(&response.data, private_key.as_slice(), "test: {chain}");
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ fn ed25519() {
|
|||
} = test;
|
||||
|
||||
// Tests for ExtendedPrivateKey
|
||||
let varlen_seed = VariableLengthSeed::new(seed);
|
||||
let varlen_seed = VariableLengthSeed::new(&seed);
|
||||
let xkey = ExtendedPrivateKey::<SigningKey>::new(varlen_seed);
|
||||
let derived_key = xkey.derive_path(&chain).unwrap();
|
||||
assert_eq!(
|
||||
|
@ -97,7 +97,7 @@ fn ed25519() {
|
|||
|
||||
// Tests for DerivationRequest
|
||||
let request = DerivationRequest::new(DerivationAlgorithm::Ed25519, &chain);
|
||||
let response = request.derive_with_master_seed(seed).unwrap();
|
||||
let response = request.derive_with_master_seed(&seed).unwrap();
|
||||
assert_eq!(&response.data, private_key.as_slice(), "test: {chain}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ fn run() -> Result<()> {
|
|||
let Some(line) = stdin().lines().next() else {
|
||||
return Err(Error::Input.into());
|
||||
};
|
||||
smex::decode(line?)?
|
||||
smex::decode(&line?)?
|
||||
};
|
||||
|
||||
split(threshold, cert_list, &input, std::io::stdout())?;
|
||||
|
|
|
@ -57,21 +57,10 @@ fn ensure_offline() {
|
|||
/// # std::env::set_var("SHOOT_SELF_IN_FOOT", "1");
|
||||
/// keyfork_entropy::ensure_safe();
|
||||
/// ```
|
||||
///
|
||||
/// When running on a system that's online, or running an outdated kernel:
|
||||
///
|
||||
/// ```rust,should_panic
|
||||
/// # // NOTE: sometimes, the environment variable is set, for testing purposes. I'm not sure how
|
||||
/// # // to un-set it. Set it to a sentinel value.
|
||||
/// # std::env::set_var("SHOOT_SELF_IN_FOOT", "test-must-fail");
|
||||
/// # std::env::set_var("INSECURE_HARDWARE_ALLOWED", "test-must-fail");
|
||||
/// keyfork_entropy::ensure_safe();
|
||||
/// ```
|
||||
pub fn ensure_safe() {
|
||||
if !std::env::vars().any(|(name, value)| {
|
||||
(name == "SHOOT_SELF_IN_FOOT" || name == "INSECURE_HARDWARE_ALLOWED")
|
||||
&& value != "test-must-fail"
|
||||
}) {
|
||||
if !std::env::vars()
|
||||
.any(|(name, _)| name == "SHOOT_SELF_IN_FOOT" || name == "INSECURE_HARDWARE_ALLOWED")
|
||||
{
|
||||
ensure_safe_kernel_version();
|
||||
ensure_offline();
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
);
|
||||
|
||||
let entropy = keyfork_entropy::generate_entropy_of_size(bit_size / 8)?;
|
||||
println!("{}", smex::encode(entropy));
|
||||
println!("{}", smex::encode(&entropy));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -66,11 +66,6 @@ pub(crate) fn hash(data: &[u8]) -> Vec<u8> {
|
|||
/// # Errors
|
||||
/// An error may be returned if the given `data` is more than [`u32::MAX`] bytes. This is a
|
||||
/// constraint on a protocol level.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// let data = keyfork_frame::try_encode(b"hello world!".as_slice()).unwrap();
|
||||
/// ```
|
||||
pub fn try_encode(data: &[u8]) -> Result<Vec<u8>, EncodeError> {
|
||||
let mut output = vec![];
|
||||
try_encode_to(data, &mut output)?;
|
||||
|
@ -82,12 +77,6 @@ pub fn try_encode(data: &[u8]) -> Result<Vec<u8>, EncodeError> {
|
|||
/// # Errors
|
||||
/// An error may be returned if the givenu `data` is more than [`u32::MAX`] bytes, or if the writer
|
||||
/// is unable to write data.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// let mut output = vec![];
|
||||
/// keyfork_frame::try_encode_to(b"hello world!".as_slice(), &mut output).unwrap();
|
||||
/// ```
|
||||
pub fn try_encode_to(data: &[u8], writable: &mut impl Write) -> Result<(), EncodeError> {
|
||||
let hash = hash(data);
|
||||
let len = hash.len() + data.len();
|
||||
|
@ -118,40 +107,18 @@ pub(crate) fn verify_checksum(data: &[u8]) -> Result<&[u8], DecodeError> {
|
|||
/// * The given `data` does not contain enough data to parse a length,
|
||||
/// * The given `data` does not contain the given length's worth of data,
|
||||
/// * The given `data` has a checksum that does not match what we build locally.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// let input = b"hello world!";
|
||||
/// let encoded = keyfork_frame::try_encode(input.as_slice()).unwrap();
|
||||
/// let decoded = keyfork_frame::try_decode(&encoded).unwrap();
|
||||
/// assert_eq!(input.as_slice(), decoded.as_slice());
|
||||
/// ```
|
||||
pub fn try_decode(data: &[u8]) -> Result<Vec<u8>, DecodeError> {
|
||||
try_decode_from(&mut &data[..])
|
||||
}
|
||||
|
||||
/// Read and decode a framed message into a `Vec<u8>`.
|
||||
///
|
||||
/// Note that unlike [`try_encode_to`], this method does not allow writing to an object
|
||||
/// implementing Write. This is because the data must be stored entirely in memory to allow
|
||||
/// verifying the data. The data is then returned using the same in-memory representation as is
|
||||
/// used in memory, and a caller may then choose to use `writable.write_all()`.
|
||||
///
|
||||
/// # Errors
|
||||
/// An error may be returned if:
|
||||
/// * The given `data` does not contain enough data to parse a length,
|
||||
/// * The given `data` does not contain the given length's worth of data,
|
||||
/// * The given `data` has a checksum that does not match what we build locally.
|
||||
/// * The source for the data returned an error.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// let input = b"hello world!";
|
||||
/// let mut encoded = vec![];
|
||||
/// keyfork_frame::try_encode_to(input.as_slice(), &mut encoded).unwrap();
|
||||
/// let decoded = keyfork_frame::try_decode_from(&mut &encoded[..]).unwrap();
|
||||
/// assert_eq!(input.as_slice(), decoded.as_slice());
|
||||
/// ```
|
||||
pub fn try_decode_from(readable: &mut impl Read) -> Result<Vec<u8>, DecodeError> {
|
||||
let mut bytes = 0u32.to_be_bytes();
|
||||
readable.read_exact(&mut bytes)?;
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
//! Zero-dependency Mnemonic encoding and decoding.
|
||||
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt::Display,
|
||||
str::FromStr,
|
||||
sync::{Arc, OnceLock},
|
||||
};
|
||||
use std::{error::Error, fmt::Display, str::FromStr, sync::Arc};
|
||||
|
||||
use hmac::Hmac;
|
||||
use pbkdf2::pbkdf2;
|
||||
|
@ -48,26 +43,19 @@ impl Error for MnemonicGenerationError {}
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Wordlist(Vec<String>);
|
||||
|
||||
static ENGLISH: OnceLock<Wordlist> = OnceLock::new();
|
||||
|
||||
impl Default for Wordlist {
|
||||
/// Returns the English wordlist in the Bitcoin BIP-0039 specification.
|
||||
fn default() -> Self {
|
||||
// TODO: English is the only supported language.
|
||||
ENGLISH
|
||||
.get_or_init(|| {
|
||||
let wordlist_file = include_str!("data/wordlist.txt");
|
||||
Wordlist(
|
||||
wordlist_file
|
||||
.lines()
|
||||
// skip 1: comment at top of file to point to BIP-0039 source.
|
||||
.skip(1)
|
||||
.map(|x| x.trim().to_string())
|
||||
.collect(),
|
||||
)
|
||||
.shrank()
|
||||
})
|
||||
.clone()
|
||||
let wordlist_file = include_str!("data/wordlist.txt");
|
||||
Wordlist(
|
||||
wordlist_file
|
||||
.lines()
|
||||
// skip 1: comment at top of file to point to BIP-0039 source.
|
||||
.skip(1)
|
||||
.map(|x| x.trim().to_string())
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,12 +66,6 @@ impl Wordlist {
|
|||
Arc::new(self)
|
||||
}
|
||||
|
||||
/// Return a shrank version of the Wordlist
|
||||
pub fn shrank(mut self) -> Self {
|
||||
self.0.shrink_to_fit();
|
||||
self
|
||||
}
|
||||
|
||||
/// Determine whether the Wordlist contains a given word.
|
||||
pub fn contains(&self, word: &str) -> bool {
|
||||
self.0.iter().any(|w| w.as_str() == word)
|
||||
|
@ -114,14 +96,6 @@ pub struct Mnemonic {
|
|||
wordlist: Arc<Wordlist>,
|
||||
}
|
||||
|
||||
impl PartialEq for Mnemonic {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.entropy.eq(&other.entropy)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Mnemonic {}
|
||||
|
||||
impl Display for Mnemonic {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let bit_count = self.entropy.len() * 8;
|
||||
|
@ -257,13 +231,6 @@ impl Mnemonic {
|
|||
///
|
||||
/// # Errors
|
||||
/// An error may be returned if the entropy is not within the acceptable lengths.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// use keyfork_mnemonic_util::Mnemonic;
|
||||
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
/// let mnemonic = Mnemonic::from_entropy(data.as_slice(), Default::default()).unwrap();
|
||||
/// ```
|
||||
pub fn from_entropy(
|
||||
bytes: &[u8],
|
||||
wordlist: Arc<Wordlist>,
|
||||
|
@ -281,36 +248,11 @@ impl Mnemonic {
|
|||
Ok(unsafe { Self::from_raw_entropy(bytes, wordlist) })
|
||||
}
|
||||
|
||||
/// Create a Mnemonic using an arbitrary length of given entropy. The length does not need to
|
||||
/// conform to BIP-0039 standards.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function can potentially produce mnemonics that are not BIP-0039 compliant or can't
|
||||
/// properly be encoded as a mnemonic. It is assumed the caller asserts the byte count is `% 4
|
||||
/// == 0`. If the assumption is incorrect, code may panic.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// use keyfork_mnemonic_util::Mnemonic;
|
||||
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
/// let mnemonic = unsafe { Mnemonic::from_raw_entropy(data.as_slice(), Default::default()) };
|
||||
/// let mnemonic_text = mnemonic.to_string();
|
||||
/// ```
|
||||
///
|
||||
/// If given an invalid length, undefined behavior may follow, or code may panic.
|
||||
///
|
||||
/// ```rust,should_panic
|
||||
/// use keyfork_mnemonic_util::Mnemonic;
|
||||
/// use std::str::FromStr;
|
||||
///
|
||||
/// // NOTE: Data is of invalid length, 31
|
||||
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||
/// let mnemonic = unsafe { Mnemonic::from_raw_entropy(data.as_slice(), Default::default()) };
|
||||
/// let mnemonic_text = mnemonic.to_string();
|
||||
/// // NOTE: panic happens here
|
||||
/// let new_mnemonic = Mnemonic::from_str(&mnemonic_text).unwrap();
|
||||
/// ```
|
||||
/// == 0`.
|
||||
pub unsafe fn from_raw_entropy(bytes: &[u8], wordlist: Arc<Wordlist>) -> Mnemonic {
|
||||
Mnemonic {
|
||||
entropy: bytes.to_vec(),
|
||||
|
@ -318,22 +260,22 @@ impl Mnemonic {
|
|||
}
|
||||
}
|
||||
|
||||
/// A view to internal representation of the decoded data.
|
||||
/// The internal representation of the decoded data.
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.entropy
|
||||
}
|
||||
|
||||
/// A clone of the internal representation of the decoded data.
|
||||
/// The internal representation of the decoded data, as a [`Vec<u8>`].
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.entropy.to_vec()
|
||||
}
|
||||
|
||||
/// Conver the Mnemonic into the internal representation of the decoded data.
|
||||
/// Drop self, returning the decoded data.
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
self.entropy
|
||||
}
|
||||
|
||||
/// Clone the existing data.
|
||||
/// Clone the existing entropy.
|
||||
#[deprecated = "Use as_bytes(), to_bytes(), or into_bytes() instead"]
|
||||
pub fn entropy(&self) -> Vec<u8> {
|
||||
self.entropy.clone()
|
||||
|
@ -342,34 +284,23 @@ impl Mnemonic {
|
|||
/// Create a BIP-0032 seed from the provided data and an optional passphrase.
|
||||
///
|
||||
/// # Errors
|
||||
/// The method should not return an error.
|
||||
#[deprecated = "Use generate_seed() instead"]
|
||||
/// The method may return an error if the pbkdf2 function returns an invalid length, but this
|
||||
/// case should not be reached.
|
||||
pub fn seed<'a>(
|
||||
&self,
|
||||
passphrase: impl Into<Option<&'a str>>,
|
||||
) -> Result<Vec<u8>, MnemonicGenerationError> {
|
||||
Ok(self.generate_seed(passphrase))
|
||||
}
|
||||
|
||||
/// Create a BIP-0032 seed from the provided data and an optional passphrase.
|
||||
///
|
||||
/// # Panics
|
||||
/// The function may panic if the HmacSha512 function returns an error. The only error the
|
||||
/// HmacSha512 function should return is an invalid length, which should not be possible.
|
||||
///
|
||||
pub fn generate_seed<'a>(&self, passphrase: impl Into<Option<&'a str>>) -> Vec<u8> {
|
||||
let passphrase = passphrase.into();
|
||||
|
||||
let mut seed = [0u8; 64];
|
||||
let mnemonic = self.to_string();
|
||||
let salt = ["mnemonic", passphrase.unwrap_or("")].join("");
|
||||
pbkdf2::<Hmac<Sha512>>(mnemonic.as_bytes(), salt.as_bytes(), 2048, &mut seed)
|
||||
.expect("HmacSha512 InvalidLength should be infallible");
|
||||
seed.to_vec()
|
||||
.map_err(|_| MnemonicGenerationError::InvalidPbkdf2Length)?;
|
||||
Ok(seed.to_vec())
|
||||
}
|
||||
|
||||
/// Encode the mnemonic into a list of integers 11 bits in length, matching the length of a
|
||||
/// BIP-0039 wordlist.
|
||||
/// Encode the mnemonic into a list of wordlist indexes.
|
||||
pub fn words(self) -> (Vec<usize>, Arc<Wordlist>) {
|
||||
let bit_count = self.entropy.len() * 8;
|
||||
let mut bits = vec![false; bit_count + bit_count / 32];
|
||||
|
@ -453,13 +384,13 @@ mod tests {
|
|||
let my_mnemonic = super::Mnemonic::from_entropy(&entropy[..256 / 8], wordlist).unwrap();
|
||||
let their_mnemonic = bip39::Mnemonic::from_entropy(&entropy[..256 / 8]).unwrap();
|
||||
assert_eq!(my_mnemonic.to_string(), their_mnemonic.to_string());
|
||||
assert_eq!(my_mnemonic.generate_seed(None), their_mnemonic.to_seed(""));
|
||||
assert_eq!(my_mnemonic.seed(None).unwrap(), their_mnemonic.to_seed(""));
|
||||
assert_eq!(
|
||||
my_mnemonic.generate_seed("testing"),
|
||||
my_mnemonic.seed("testing").unwrap(),
|
||||
their_mnemonic.to_seed("testing")
|
||||
);
|
||||
assert_ne!(
|
||||
my_mnemonic.generate_seed("test1"),
|
||||
my_mnemonic.seed("test1").unwrap(),
|
||||
their_mnemonic.to_seed("test2")
|
||||
);
|
||||
}
|
||||
|
|
|
@ -28,15 +28,7 @@ impl std::fmt::Display for DecodeError {
|
|||
impl std::error::Error for DecodeError {}
|
||||
|
||||
/// Encode a given input as a hex string.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// let data = b"hello world!";
|
||||
/// let result = smex::encode(&data);
|
||||
/// assert_eq!(result, "68656c6c6f20776f726c6421");
|
||||
/// ```
|
||||
pub fn encode(input: impl AsRef<[u8]>) -> String {
|
||||
let input = input.as_ref();
|
||||
pub fn encode(input: &[u8]) -> String {
|
||||
let mut s = String::new();
|
||||
for byte in input {
|
||||
write!(s, "{byte:02x}").unwrap();
|
||||
|
@ -58,26 +50,7 @@ fn val(c: u8) -> Result<u8, DecodeError> {
|
|||
/// # Errors
|
||||
/// The function may error if a non-hex character is encountered or if the character count is not
|
||||
/// evenly divisible by two.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// let data = b"hello world!";
|
||||
/// let encoded = smex::encode(&data);
|
||||
/// let decoded = smex::decode(&encoded).unwrap();
|
||||
/// assert_eq!(data.as_slice(), decoded.as_slice());
|
||||
/// ```
|
||||
///
|
||||
/// The function may return an error if the given input is not valid hex.
|
||||
///
|
||||
/// ```rust,should_panic
|
||||
/// let data = b"hello world!";
|
||||
/// let mut encoded = smex::encode(&data);
|
||||
/// encoded.push('G');
|
||||
/// let decoded = smex::decode(&encoded).unwrap();
|
||||
/// assert_eq!(data.as_slice(), decoded.as_slice());
|
||||
/// ```
|
||||
pub fn decode(input: impl AsRef<str>) -> Result<Vec<u8>, DecodeError> {
|
||||
let input = input.as_ref();
|
||||
pub fn decode(input: &str) -> Result<Vec<u8>, DecodeError> {
|
||||
let len = input.len();
|
||||
if len % 2 != 0 {
|
||||
return Err(DecodeError::InvalidCharacterCount(len));
|
||||
|
|
Loading…
Reference in New Issue