|
|
@ -109,14 +109,14 @@ impl Wordlist {
|
|
|
|
/// A BIP-0039 mnemonic with reference to a [`Wordlist`].
|
|
|
|
/// A BIP-0039 mnemonic with reference to a [`Wordlist`].
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct Mnemonic {
|
|
|
|
pub struct Mnemonic {
|
|
|
|
entropy: Vec<u8>,
|
|
|
|
data: Vec<u8>,
|
|
|
|
// words: Vec<usize>,
|
|
|
|
// words: Vec<usize>,
|
|
|
|
wordlist: Arc<Wordlist>,
|
|
|
|
wordlist: Arc<Wordlist>,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl PartialEq for Mnemonic {
|
|
|
|
impl PartialEq for Mnemonic {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
self.entropy.eq(&other.entropy)
|
|
|
|
self.data.eq(&other.data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -124,18 +124,18 @@ impl Eq for Mnemonic {}
|
|
|
|
|
|
|
|
|
|
|
|
impl Display for Mnemonic {
|
|
|
|
impl Display for Mnemonic {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
let bit_count = self.entropy.len() * 8;
|
|
|
|
let bit_count = self.data.len() * 8;
|
|
|
|
let mut bits = vec![false; bit_count + bit_count / 32];
|
|
|
|
let mut bits = vec![false; bit_count + bit_count / 32];
|
|
|
|
|
|
|
|
|
|
|
|
for byte_index in 0..bit_count / 8 {
|
|
|
|
for byte_index in 0..bit_count / 8 {
|
|
|
|
for bit_index in 0..8 {
|
|
|
|
for bit_index in 0..8 {
|
|
|
|
bits[byte_index * 8 + bit_index] =
|
|
|
|
bits[byte_index * 8 + bit_index] =
|
|
|
|
(self.entropy[byte_index] & (1 << (7 - bit_index))) > 0;
|
|
|
|
(self.data[byte_index] & (1 << (7 - bit_index))) > 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let mut hasher = Sha256::new();
|
|
|
|
let mut hasher = Sha256::new();
|
|
|
|
hasher.update(&self.entropy);
|
|
|
|
hasher.update(&self.data);
|
|
|
|
let hash = hasher.finalize().to_vec();
|
|
|
|
let hash = hasher.finalize().to_vec();
|
|
|
|
for check_bit in 0..bit_count / 32 {
|
|
|
|
for check_bit in 0..bit_count / 32 {
|
|
|
|
bits[bit_count + check_bit] = (hash[check_bit / 8] & (1 << (7 - (check_bit % 8)))) > 0;
|
|
|
|
bits[bit_count + check_bit] = (hash[check_bit / 8] & (1 << (7 - (check_bit % 8)))) > 0;
|
|
|
@ -223,7 +223,7 @@ impl FromStr for Mnemonic {
|
|
|
|
bits.truncate(bits.len() * 32 / 33);
|
|
|
|
bits.truncate(bits.len() * 32 / 33);
|
|
|
|
// bits.truncate(bits.len() - bits.len() % 32);
|
|
|
|
// bits.truncate(bits.len() - bits.len() % 32);
|
|
|
|
|
|
|
|
|
|
|
|
let entropy: Vec<u8> = bits
|
|
|
|
let data: Vec<u8> = bits
|
|
|
|
.chunks_exact(8)
|
|
|
|
.chunks_exact(8)
|
|
|
|
.map(|chunk| {
|
|
|
|
.map(|chunk| {
|
|
|
|
let mut num = 0u8;
|
|
|
|
let mut num = 0u8;
|
|
|
@ -235,7 +235,7 @@ impl FromStr for Mnemonic {
|
|
|
|
.collect();
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
|
|
let mut hasher = Sha256::new();
|
|
|
|
let mut hasher = Sha256::new();
|
|
|
|
hasher.update(&entropy);
|
|
|
|
hasher.update(&data);
|
|
|
|
let hash = hasher.finalize().to_vec();
|
|
|
|
let hash = hasher.finalize().to_vec();
|
|
|
|
|
|
|
|
|
|
|
|
for (i, bit) in checksum_bits.iter().enumerate() {
|
|
|
|
for (i, bit) in checksum_bits.iter().enumerate() {
|
|
|
@ -245,7 +245,7 @@ impl FromStr for Mnemonic {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Ok(Mnemonic {
|
|
|
|
Ok(Mnemonic {
|
|
|
|
entropy,
|
|
|
|
data,
|
|
|
|
// words: usize_words,
|
|
|
|
// words: usize_words,
|
|
|
|
wordlist,
|
|
|
|
wordlist,
|
|
|
|
})
|
|
|
|
})
|
|
|
@ -253,18 +253,19 @@ impl FromStr for Mnemonic {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Mnemonic {
|
|
|
|
impl Mnemonic {
|
|
|
|
/// Generate a [`Mnemonic`] from the provided entropy and [`Wordlist`].
|
|
|
|
/// Generate a [`Mnemonic`] from the provided data and [`Wordlist`]. The data is expected to be
|
|
|
|
|
|
|
|
/// of 128, 192, or 256 bits, as per BIP-0039.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// # Errors
|
|
|
|
/// # Errors
|
|
|
|
/// An error may be returned if the entropy is not within the acceptable lengths.
|
|
|
|
/// An error may be returned if the data is not within the expected lengths.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// # Examples
|
|
|
|
/// ```rust
|
|
|
|
/// ```rust
|
|
|
|
/// use keyfork_mnemonic_util::Mnemonic;
|
|
|
|
/// use keyfork_mnemonic_util::Mnemonic;
|
|
|
|
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
|
|
|
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
|
|
|
/// let mnemonic = Mnemonic::from_entropy(data.as_slice(), Default::default()).unwrap();
|
|
|
|
/// let mnemonic = Mnemonic::from_bytes(data.as_slice(), Default::default()).unwrap();
|
|
|
|
/// ```
|
|
|
|
/// ```
|
|
|
|
pub fn from_entropy(
|
|
|
|
pub fn from_bytes(
|
|
|
|
bytes: &[u8],
|
|
|
|
bytes: &[u8],
|
|
|
|
wordlist: Arc<Wordlist>,
|
|
|
|
wordlist: Arc<Wordlist>,
|
|
|
|
) -> Result<Mnemonic, MnemonicGenerationError> {
|
|
|
|
) -> Result<Mnemonic, MnemonicGenerationError> {
|
|
|
@ -278,11 +279,24 @@ impl Mnemonic {
|
|
|
|
return Err(MnemonicGenerationError::InvalidByteLength(bit_count));
|
|
|
|
return Err(MnemonicGenerationError::InvalidByteLength(bit_count));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Ok(unsafe { Self::from_raw_entropy(bytes, wordlist) })
|
|
|
|
Ok(unsafe { Self::from_raw_bytes(bytes, wordlist) })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Create a Mnemonic using an arbitrary length of given entropy. The length does not need to
|
|
|
|
/// Generate a [`Mnemonic`] from the provided data and [`Wordlist`]. The data is expected to be
|
|
|
|
/// conform to BIP-0039 standards.
|
|
|
|
/// of 128, 192, or 256 bits, as per BIP-0039.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// # Errors
|
|
|
|
|
|
|
|
/// An error may be returned if the data is not within the expected lengths.
|
|
|
|
|
|
|
|
#[deprecated = "use Mnemonic::from_bytes"]
|
|
|
|
|
|
|
|
pub fn from_entropy(
|
|
|
|
|
|
|
|
bytes: &[u8],
|
|
|
|
|
|
|
|
wordlist: Arc<Wordlist>,
|
|
|
|
|
|
|
|
) -> Result<Mnemonic, MnemonicGenerationError> {
|
|
|
|
|
|
|
|
Mnemonic::from_bytes(bytes, wordlist)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Create a Mnemonic using an arbitrary length of given data. The length does not need to
|
|
|
|
|
|
|
|
/// conform to BIP-0039 standards, but should be a multiple of 32 bits or 4 bytes.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
///
|
|
|
@ -294,7 +308,7 @@ impl Mnemonic {
|
|
|
|
/// ```rust
|
|
|
|
/// ```rust
|
|
|
|
/// use keyfork_mnemonic_util::Mnemonic;
|
|
|
|
/// use keyfork_mnemonic_util::Mnemonic;
|
|
|
|
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
|
|
|
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
|
|
|
/// let mnemonic = unsafe { Mnemonic::from_raw_entropy(data.as_slice(), Default::default()) };
|
|
|
|
/// let mnemonic = unsafe { Mnemonic::from_raw_bytes(data.as_slice(), Default::default()) };
|
|
|
|
/// let mnemonic_text = mnemonic.to_string();
|
|
|
|
/// let mnemonic_text = mnemonic.to_string();
|
|
|
|
/// ```
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
///
|
|
|
@ -306,37 +320,53 @@ impl Mnemonic {
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// // NOTE: Data is of invalid length, 31
|
|
|
|
/// // NOTE: Data is of invalid length, 31
|
|
|
|
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
|
|
|
/// let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
|
|
|
/// let mnemonic = unsafe { Mnemonic::from_raw_entropy(data.as_slice(), Default::default()) };
|
|
|
|
/// let mnemonic = unsafe { Mnemonic::from_raw_bytes(data.as_slice(), Default::default()) };
|
|
|
|
/// let mnemonic_text = mnemonic.to_string();
|
|
|
|
/// let mnemonic_text = mnemonic.to_string();
|
|
|
|
/// // NOTE: panic happens here
|
|
|
|
/// // NOTE: panic happens here
|
|
|
|
/// let new_mnemonic = Mnemonic::from_str(&mnemonic_text).unwrap();
|
|
|
|
/// let new_mnemonic = Mnemonic::from_str(&mnemonic_text).unwrap();
|
|
|
|
/// ```
|
|
|
|
/// ```
|
|
|
|
pub unsafe fn from_raw_entropy(bytes: &[u8], wordlist: Arc<Wordlist>) -> Mnemonic {
|
|
|
|
pub unsafe fn from_raw_bytes(bytes: &[u8], wordlist: Arc<Wordlist>) -> Mnemonic {
|
|
|
|
Mnemonic {
|
|
|
|
Mnemonic {
|
|
|
|
entropy: bytes.to_vec(),
|
|
|
|
data: bytes.to_vec(),
|
|
|
|
|
|
|
|
wordlist,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Create a Mnemonic using an arbitrary length of given data. The length does not need to
|
|
|
|
|
|
|
|
/// conform to BIP-0039 standards, but should be a multiple of 32 bits or 4 bytes.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// # 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.
|
|
|
|
|
|
|
|
#[deprecated = "use Mnemonic::from_raw_bytes"]
|
|
|
|
|
|
|
|
pub unsafe fn from_raw_entropy(bytes: &[u8], wordlist: Arc<Wordlist>) -> Mnemonic{
|
|
|
|
|
|
|
|
Mnemonic {
|
|
|
|
|
|
|
|
data: bytes.to_vec(),
|
|
|
|
wordlist,
|
|
|
|
wordlist,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// A view to internal representation of the decoded data.
|
|
|
|
/// A view to internal representation of the decoded data.
|
|
|
|
pub fn as_bytes(&self) -> &[u8] {
|
|
|
|
pub fn as_bytes(&self) -> &[u8] {
|
|
|
|
&self.entropy
|
|
|
|
&self.data
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// A clone of the internal representation of the decoded data.
|
|
|
|
/// A clone of the internal representation of the decoded data.
|
|
|
|
pub fn to_bytes(&self) -> Vec<u8> {
|
|
|
|
pub fn to_bytes(&self) -> Vec<u8> {
|
|
|
|
self.entropy.to_vec()
|
|
|
|
self.data.to_vec()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Conver the Mnemonic into the internal representation of the decoded data.
|
|
|
|
/// Conver the Mnemonic into the internal representation of the decoded data.
|
|
|
|
pub fn into_bytes(self) -> Vec<u8> {
|
|
|
|
pub fn into_bytes(self) -> Vec<u8> {
|
|
|
|
self.entropy
|
|
|
|
self.data
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Clone the existing data.
|
|
|
|
/// Clone the existing data.
|
|
|
|
#[deprecated = "Use as_bytes(), to_bytes(), or into_bytes() instead"]
|
|
|
|
#[deprecated = "Use as_bytes(), to_bytes(), or into_bytes() instead"]
|
|
|
|
pub fn entropy(&self) -> Vec<u8> {
|
|
|
|
pub fn entropy(&self) -> Vec<u8> {
|
|
|
|
self.entropy.clone()
|
|
|
|
self.data.clone()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Create a BIP-0032 seed from the provided data and an optional passphrase.
|
|
|
|
/// Create a BIP-0032 seed from the provided data and an optional passphrase.
|
|
|
@ -371,18 +401,18 @@ impl Mnemonic {
|
|
|
|
/// Encode the mnemonic into a list of integers 11 bits in length, matching the length of a
|
|
|
|
/// Encode the mnemonic into a list of integers 11 bits in length, matching the length of a
|
|
|
|
/// BIP-0039 wordlist.
|
|
|
|
/// BIP-0039 wordlist.
|
|
|
|
pub fn words(self) -> (Vec<usize>, Arc<Wordlist>) {
|
|
|
|
pub fn words(self) -> (Vec<usize>, Arc<Wordlist>) {
|
|
|
|
let bit_count = self.entropy.len() * 8;
|
|
|
|
let bit_count = self.data.len() * 8;
|
|
|
|
let mut bits = vec![false; bit_count + bit_count / 32];
|
|
|
|
let mut bits = vec![false; bit_count + bit_count / 32];
|
|
|
|
|
|
|
|
|
|
|
|
for byte_index in 0..bit_count / 8 {
|
|
|
|
for byte_index in 0..bit_count / 8 {
|
|
|
|
for bit_index in 0..8 {
|
|
|
|
for bit_index in 0..8 {
|
|
|
|
bits[byte_index * 8 + bit_index] =
|
|
|
|
bits[byte_index * 8 + bit_index] =
|
|
|
|
(self.entropy[byte_index] & (1 << (7 - bit_index))) > 0;
|
|
|
|
(self.data[byte_index] & (1 << (7 - bit_index))) > 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let mut hasher = Sha256::new();
|
|
|
|
let mut hasher = Sha256::new();
|
|
|
|
hasher.update(&self.entropy);
|
|
|
|
hasher.update(&self.data);
|
|
|
|
let hash = hasher.finalize().to_vec();
|
|
|
|
let hash = hasher.finalize().to_vec();
|
|
|
|
for check_bit in 0..bit_count / 32 {
|
|
|
|
for check_bit in 0..bit_count / 32 {
|
|
|
|
bits[bit_count + check_bit] = (hash[check_bit / 8] & (1 << (7 - (check_bit % 8)))) > 0;
|
|
|
|
bits[bit_count + check_bit] = (hash[check_bit / 8] & (1 << (7 - (check_bit % 8)))) > 0;
|
|
|
@ -421,7 +451,7 @@ mod tests {
|
|
|
|
let entropy = &mut [0u8; 256 / 8];
|
|
|
|
let entropy = &mut [0u8; 256 / 8];
|
|
|
|
random_handle.read_exact(&mut entropy[..]).unwrap();
|
|
|
|
random_handle.read_exact(&mut entropy[..]).unwrap();
|
|
|
|
let wordlist = Wordlist::default().arc();
|
|
|
|
let wordlist = Wordlist::default().arc();
|
|
|
|
let mnemonic = super::Mnemonic::from_entropy(&entropy[..256 / 8], wordlist).unwrap();
|
|
|
|
let mnemonic = super::Mnemonic::from_bytes(&entropy[..256 / 8], wordlist).unwrap();
|
|
|
|
let new_entropy = mnemonic.as_bytes();
|
|
|
|
let new_entropy = mnemonic.as_bytes();
|
|
|
|
assert_eq!(new_entropy, entropy);
|
|
|
|
assert_eq!(new_entropy, entropy);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -438,7 +468,7 @@ mod tests {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
let hex = hex::decode(hex_.as_str().unwrap()).unwrap();
|
|
|
|
let hex = hex::decode(hex_.as_str().unwrap()).unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
let mnemonic = Mnemonic::from_entropy(&hex, wordlist.clone()).unwrap();
|
|
|
|
let mnemonic = Mnemonic::from_bytes(&hex, wordlist.clone()).unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(mnemonic.to_string(), seed.as_str().unwrap());
|
|
|
|
assert_eq!(mnemonic.to_string(), seed.as_str().unwrap());
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -450,7 +480,7 @@ mod tests {
|
|
|
|
let entropy = &mut [0u8; 256 / 8];
|
|
|
|
let entropy = &mut [0u8; 256 / 8];
|
|
|
|
random_handle.read_exact(&mut entropy[..]).unwrap();
|
|
|
|
random_handle.read_exact(&mut entropy[..]).unwrap();
|
|
|
|
let wordlist = Wordlist::default().arc();
|
|
|
|
let wordlist = Wordlist::default().arc();
|
|
|
|
let my_mnemonic = super::Mnemonic::from_entropy(&entropy[..256 / 8], wordlist).unwrap();
|
|
|
|
let my_mnemonic = super::Mnemonic::from_bytes(&entropy[..256 / 8], wordlist).unwrap();
|
|
|
|
let their_mnemonic = bip39::Mnemonic::from_entropy(&entropy[..256 / 8]).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.to_string(), their_mnemonic.to_string());
|
|
|
|
assert_eq!(my_mnemonic.generate_seed(None), their_mnemonic.to_seed(""));
|
|
|
|
assert_eq!(my_mnemonic.generate_seed(None), their_mnemonic.to_seed(""));
|
|
|
@ -475,7 +505,7 @@ mod tests {
|
|
|
|
|
|
|
|
|
|
|
|
for _ in 0..tests {
|
|
|
|
for _ in 0..tests {
|
|
|
|
random.read_exact(&mut entropy[..]).unwrap();
|
|
|
|
random.read_exact(&mut entropy[..]).unwrap();
|
|
|
|
let mnemonic = Mnemonic::from_entropy(&entropy[..256 / 8], wordlist.clone()).unwrap();
|
|
|
|
let mnemonic = Mnemonic::from_bytes(&entropy[..256 / 8], wordlist.clone()).unwrap();
|
|
|
|
let (words, _) = mnemonic.words();
|
|
|
|
let (words, _) = mnemonic.words();
|
|
|
|
hs.clear();
|
|
|
|
hs.clear();
|
|
|
|
hs.extend(words);
|
|
|
|
hs.extend(words);
|
|
|
@ -507,7 +537,7 @@ mod tests {
|
|
|
|
let wordlist = Wordlist::default().arc();
|
|
|
|
let wordlist = Wordlist::default().arc();
|
|
|
|
let mut random = std::fs::File::open("/dev/urandom").unwrap();
|
|
|
|
let mut random = std::fs::File::open("/dev/urandom").unwrap();
|
|
|
|
random.read_exact(&mut entropy[..]).unwrap();
|
|
|
|
random.read_exact(&mut entropy[..]).unwrap();
|
|
|
|
let mnemonic = unsafe { Mnemonic::from_raw_entropy(&entropy[..], wordlist.clone()) };
|
|
|
|
let mnemonic = unsafe { Mnemonic::from_raw_bytes(&entropy[..], wordlist.clone()) };
|
|
|
|
let (words, _) = mnemonic.words();
|
|
|
|
let (words, _) = mnemonic.words();
|
|
|
|
assert!(words.len() == 96);
|
|
|
|
assert!(words.len() == 96);
|
|
|
|
}
|
|
|
|
}
|
|
|
|