keyfork-mnemonic-generate: split wordlist parsing into its own thing
This commit is contained in:
parent
8ec5dc0dec
commit
8e74c18135
|
@ -91,17 +91,40 @@ fn ensure_offline() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Can a Mnemonic be formatted using a wordlist, without allocating or without storing the
|
struct Wordlist(Vec<String>);
|
||||||
// entire word list?
|
|
||||||
//
|
impl Default for Wordlist {
|
||||||
// NOTE: Yes, use a Language and pass a reference to this.
|
fn default() -> Self {
|
||||||
struct Mnemonic {
|
// TODO: English is the only supported language.
|
||||||
pub words: Vec<usize>,
|
let wordlist_file = include_str!("wordlist.txt");
|
||||||
wordlist: Vec<String>,
|
Wordlist(
|
||||||
|
wordlist_file
|
||||||
|
.lines()
|
||||||
|
.skip(1)
|
||||||
|
.map(|x| x.trim().to_string())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mnemonic {
|
impl Wordlist {
|
||||||
fn from_entropy(bytes: &[u8]) -> Result<Mnemonic> {
|
fn get_word(&self, word: usize) -> Option<String> {
|
||||||
|
self.0.get(word).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn into_inner(self) -> Vec<String> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Mnemonic<'a> {
|
||||||
|
pub words: Vec<usize>,
|
||||||
|
wordlist: &'a Wordlist,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Mnemonic<'a> {
|
||||||
|
fn from_entropy(bytes: &[u8], wordlist: &'a Wordlist) -> Result<Mnemonic<'a>> {
|
||||||
let bit_count = bytes.len() * 8;
|
let bit_count = bytes.len() * 8;
|
||||||
let hash = generate_slice_hash(bytes);
|
let hash = generate_slice_hash(bytes);
|
||||||
|
|
||||||
|
@ -132,16 +155,15 @@ impl Mnemonic {
|
||||||
.map(|chunk| bitslice_to_usize(chunk.try_into().expect("11 bit chunks")))
|
.map(|chunk| bitslice_to_usize(chunk.try_into().expect("11 bit chunks")))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let wordlist = build_wordlist();
|
|
||||||
Ok(Mnemonic { words, wordlist })
|
Ok(Mnemonic { words, wordlist })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Mnemonic {
|
impl<'a> Display for Mnemonic<'a> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let mut iter = self.words.iter().peekable();
|
let mut iter = self.words.iter().peekable();
|
||||||
while let Some(word_index) = iter.next() {
|
while let Some(word_index) = iter.next() {
|
||||||
let word = &self.wordlist[*word_index];
|
let word = self.wordlist.get_word(*word_index).expect("word");
|
||||||
write!(f, "{word}")?;
|
write!(f, "{word}")?;
|
||||||
if iter.peek().is_some() {
|
if iter.peek().is_some() {
|
||||||
write!(f, " ")?;
|
write!(f, " ")?;
|
||||||
|
@ -157,15 +179,6 @@ fn generate_slice_hash(data: &[u8]) -> Vec<u8> {
|
||||||
hasher.finalize().to_vec()
|
hasher.finalize().to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_wordlist() -> Vec<String> {
|
|
||||||
let wordlist_file = include_str!("wordlist.txt");
|
|
||||||
wordlist_file
|
|
||||||
.lines()
|
|
||||||
.skip(1)
|
|
||||||
.map(|x| x.trim().to_string())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bitslice_to_usize(bitslice: [bool; 11]) -> usize {
|
fn bitslice_to_usize(bitslice: [bool; 11]) -> usize {
|
||||||
let mut index = 0usize;
|
let mut index = 0usize;
|
||||||
for i in 0..11 {
|
for i in 0..11 {
|
||||||
|
@ -196,7 +209,8 @@ fn main() -> Result<()> {
|
||||||
let entropy = &mut [0u8; 256 / 8];
|
let entropy = &mut [0u8; 256 / 8];
|
||||||
random_handle.read_exact(&mut entropy[..])?;
|
random_handle.read_exact(&mut entropy[..])?;
|
||||||
|
|
||||||
let mnemonic = Mnemonic::from_entropy(&entropy[..bit_size / 8])?;
|
let wordlist = Wordlist::default();
|
||||||
|
let mnemonic = Mnemonic::from_entropy(&entropy[..bit_size / 8], &wordlist)?;
|
||||||
|
|
||||||
println!("{mnemonic}");
|
println!("{mnemonic}");
|
||||||
|
|
||||||
|
@ -209,7 +223,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn has_good_wordlist() {
|
fn has_good_wordlist() {
|
||||||
let wordlist = build_wordlist();
|
let wordlist = Wordlist::default().into_inner();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
wordlist.len(),
|
wordlist.len(),
|
||||||
2usize.pow(11),
|
2usize.pow(11),
|
||||||
|
@ -228,7 +242,8 @@ 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)?;
|
let wordlist = Wordlist::default();
|
||||||
|
let mnemonic = Mnemonic::from_entropy(&hex, &wordlist)?;
|
||||||
|
|
||||||
assert_eq!(mnemonic.to_string(), seed.as_str().unwrap());
|
assert_eq!(mnemonic.to_string(), seed.as_str().unwrap());
|
||||||
}
|
}
|
||||||
|
@ -240,7 +255,8 @@ mod tests {
|
||||||
let mut random_handle = File::open("/dev/random").unwrap();
|
let mut random_handle = File::open("/dev/random").unwrap();
|
||||||
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 my_mnemonic = super::Mnemonic::from_entropy(&entropy[..256 / 8]).unwrap();
|
let wordlist = Wordlist::default();
|
||||||
|
let my_mnemonic = super::Mnemonic::from_entropy(&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();
|
||||||
/*
|
/*
|
||||||
let my_words = my_mnemonic.words.clone();
|
let my_words = my_mnemonic.words.clone();
|
||||||
|
@ -252,13 +268,14 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn count_to_get_duplicate_words() {
|
fn count_to_get_duplicate_words() {
|
||||||
let mut count = 0.;
|
let mut count = 0.;
|
||||||
let tests = 10_000;
|
let tests = 100_000;
|
||||||
let mut random_handle = File::open("/dev/random").unwrap();
|
let mut random_handle = File::open("/dev/random").unwrap();
|
||||||
let entropy = &mut [0u8; 256 / 8];
|
let entropy = &mut [0u8; 256 / 8];
|
||||||
|
let wordlist = Wordlist::default();
|
||||||
for _ in 0..tests {
|
for _ in 0..tests {
|
||||||
random_handle.read_exact(&mut entropy[..]).unwrap();
|
random_handle.read_exact(&mut entropy[..]).unwrap();
|
||||||
// let bits = generate_slice_hash(entropy);
|
// let bits = generate_slice_hash(entropy);
|
||||||
let mnemonic = Mnemonic::from_entropy(&entropy[..256 / 8]).unwrap();
|
let mnemonic = Mnemonic::from_entropy(&entropy[..256 / 8], &wordlist).unwrap();
|
||||||
let mut words = mnemonic.words.clone();
|
let mut words = mnemonic.words.clone();
|
||||||
words.sort();
|
words.sort();
|
||||||
assert_eq!(words.len(), 24);
|
assert_eq!(words.len(), 24);
|
||||||
|
|
Loading…
Reference in New Issue