Skip to content

Commit 389b3f2

Browse files
committed
add mnemonic extension from 12 to 24 words
1 parent 51fe985 commit 389b3f2

File tree

1 file changed

+54
-8
lines changed

1 file changed

+54
-8
lines changed

src/mnemonic.rs

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
//! TREZOR compatible mnemonic in english
2020
//!
2121
use bitcoin::util::bip158::{BitStreamReader, BitStreamWriter};
22-
use account::Seed;
22+
use account::{Seed, MasterKeyEntropy};
2323
use crypto::{
2424
digest::Digest,
2525
hmac::Hmac,
@@ -28,13 +28,14 @@ use crypto::{
2828
};
2929
use error::Error;
3030
use std::io::Cursor;
31+
use rand::{thread_rng, RngCore};
3132

3233
#[derive(Clone, Eq, PartialEq, Debug)]
33-
pub struct Mnemonic(Vec<&'static str>);
34+
pub struct Mnemonic(Vec<usize>);
3435

3536
impl ToString for Mnemonic {
3637
fn to_string(&self) -> String {
37-
self.0.as_slice().join(" ")
38+
self.0.iter().map(|i| WORDS[*i]).collect::<Vec<_>>().as_slice().join(" ")
3839
}
3940
}
4041

@@ -50,7 +51,7 @@ impl Mnemonic {
5051
}
5152

5253
pub fn iter(&self) -> impl Iterator<Item = &str> {
53-
self.0.iter().map(|s| *s)
54+
self.0.iter().map(|s| WORDS[*s])
5455
}
5556

5657
pub fn from_str(s: &str) -> Result<Mnemonic, Error> {
@@ -65,7 +66,7 @@ impl Mnemonic {
6566
let mut mnemonic = Vec::new();
6667
for word in &words {
6768
if let Ok(idx) = WORDS.binary_search(word) {
68-
mnemonic.push(WORDS[idx]);
69+
mnemonic.push(idx);
6970
writer.write(idx as u64, 11).unwrap();
7071
} else {
7172
return Err(Error::Mnemonic("Mnemonic contains an unknown word"));
@@ -81,6 +82,17 @@ impl Mnemonic {
8182
Ok(Mnemonic(mnemonic))
8283
}
8384

85+
pub fn new_random(entropy: MasterKeyEntropy) -> Result<Mnemonic, Error> {
86+
let len = match entropy {
87+
MasterKeyEntropy::Sufficient => 16,
88+
MasterKeyEntropy::Double => 32,
89+
MasterKeyEntropy::Paranoid => 64
90+
};
91+
let mut random = vec!(0u8; len);
92+
thread_rng().fill_bytes(random.as_mut_slice());
93+
Self::new(random.as_slice())
94+
}
95+
8496
/// create a mnemonic for some data
8597
pub fn new(data: &[u8]) -> Result<Mnemonic, Error> {
8698
if data.len() % 4 != 0 {
@@ -93,11 +105,37 @@ impl Mnemonic {
93105
let mut cursor = Cursor::new(&with_checksum);
94106
let mut reader = BitStreamReader::new(&mut cursor);
95107
let mlen = data.len() * 3 / 4;
96-
let mut memo = Vec::new();
108+
let mut mnemonic = Vec::new();
97109
for _ in 0..mlen {
98-
memo.push(WORDS[reader.read(11).unwrap() as usize]);
110+
mnemonic.push(reader.read(11).unwrap() as usize);
99111
}
100-
Ok(Mnemonic(memo))
112+
Ok(Mnemonic(mnemonic))
113+
}
114+
115+
pub fn extend (&self) -> Result<Mnemonic, Error> {
116+
if self.0.len() != 12 {
117+
return Err(Error::Mnemonic(
118+
"Can only extend mnemonic of 12 words to 24 words",
119+
));
120+
}
121+
let mut data = Vec::new();
122+
let mut writer = BitStreamWriter::new(&mut data);
123+
for idx in &self.0 {
124+
writer.write(*idx as u64, 11).unwrap();
125+
}
126+
for _ in 0..11 {
127+
writer.write(thread_rng().next_u64(), 11).unwrap();
128+
}
129+
writer.write(thread_rng().next_u64(), 3).unwrap();
130+
writer.flush().unwrap();
131+
data.extend_from_slice(Self::checksum(&data).as_slice());
132+
let mut cursor = Cursor::new(&data[..]);
133+
let mut reader = BitStreamReader::new(&mut cursor);
134+
let mut mnemonic = Vec::new();
135+
for _ in 0..24 {
136+
mnemonic.push(reader.read(11).unwrap() as usize);
137+
}
138+
Ok(Mnemonic(mnemonic))
101139
}
102140

103141
fn checksum(data: &[u8]) -> Vec<u8> {
@@ -180,6 +218,14 @@ mod test {
180218
)
181219
.is_err());
182220
}
221+
222+
#[test]
223+
fn test_extend() {
224+
let short = Mnemonic::new_random(MasterKeyEntropy::Sufficient).unwrap();
225+
let extended = short.extend().unwrap();
226+
let check = Mnemonic::from_str(extended.to_string().as_str()).unwrap();
227+
assert!(short.iter().zip(check.iter()).take(12).all(|(a, b)| *a == *b));
228+
}
183229
}
184230

185231
const WORDS: [&str; 2048] = [

0 commit comments

Comments
 (0)