Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
15885cc00b |
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -40,12 +40,19 @@ version = "0.2.148"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
|
checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "permutohedron"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b687ff7b5da449d39e418ad391e5e08da53ec334903ddbb921db208908fc372c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "seedxor"
|
name = "seedxor"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bip39",
|
"bip39",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
|
"permutohedron",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "seedxor"
|
name = "seedxor"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["moparisthebest <admin@moparisthebest.com>", "KaiWitt <kaiwitt@protonmail.com>"]
|
authors = ["moparisthebest <admin@moparisthebest.com>", "KaiWitt <kaiwitt@protonmail.com>"]
|
||||||
description = "XOR bip39 mnemonics."
|
description = "XOR bip39 mnemonics."
|
||||||
@ -20,5 +20,6 @@ include = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bip39 = { version = "2.0", default-features = false }
|
bip39 = { version = "2.0", default-features = false }
|
||||||
getrandom = { version = "0.2", default-features = false }
|
getrandom = { version = "0.2", default-features = false }
|
||||||
|
permutohedron = { version = "0.2.4", default-features = false }
|
||||||
|
36
README.md
36
README.md
@ -3,7 +3,7 @@
|
|||||||
seedxor builds on top of [rust-bip39](https://github.com/rust-bitcoin/rust-bip39/) and is a fork of [seed-xor](https://github.com/kaiwolfram/seed-xor)
|
seedxor builds on top of [rust-bip39](https://github.com/rust-bitcoin/rust-bip39/) and is a fork of [seed-xor](https://github.com/kaiwolfram/seed-xor)
|
||||||
and lets you XOR bip39 mnemonics as described in [Coldcards docs](https://github.com/Coldcard/firmware/blob/master/docs/seed-xor.md).
|
and lets you XOR bip39 mnemonics as described in [Coldcards docs](https://github.com/Coldcard/firmware/blob/master/docs/seed-xor.md).
|
||||||
|
|
||||||
It also lets you split existing mnemonics into as many seeds as you wish
|
It also lets you split existing mnemonics into as many seeds as you wish, and unscramble parts of a seed in random order into valid seeds.
|
||||||
|
|
||||||
It is also possible to XOR mnemonics with differing numbers of words.
|
It is also possible to XOR mnemonics with differing numbers of words.
|
||||||
For this the xored value takes on the entropy surplus of the longer seed.
|
For this the xored value takes on the entropy surplus of the longer seed.
|
||||||
@ -12,17 +12,18 @@ For this the xored value takes on the entropy surplus of the longer seed.
|
|||||||
|
|
||||||
```
|
```
|
||||||
usage: seedxor [options...]
|
usage: seedxor [options...]
|
||||||
-h, --help Display this help
|
-h, --help Display this help
|
||||||
-s, --split <seed> Split seed into num-seeds
|
-s, --split <seed> Split seed into num-seeds
|
||||||
-n, --num-seeds <num> Number of seeds to split into or generate
|
-n, --num-seeds <num> Number of seeds to split into or generate
|
||||||
default 2
|
default 2
|
||||||
-y, --no-validate Do not validate a split can be successfully recombined, useful for
|
-y, --no-validate Do not validate a split can be successfully recombined, useful for
|
||||||
non-bip39 seeds, like ethereum
|
non-bip39 seeds, like ethereum
|
||||||
-g, --generate Generate num-seeds
|
-g, --generate Generate num-seeds
|
||||||
-w, --word-count <num> Number of words to generate in the seed
|
-w, --word-count <num> Number of words to generate in the seed
|
||||||
default 24
|
default 24
|
||||||
-c, --combine <seeds...> Combine seeds into one seed
|
-c, --combine <seeds...> Combine seeds into one seed
|
||||||
-r, --short Display only first 4 letters of seed words
|
-r, --short Display only first 4 letters of seed words
|
||||||
|
-u, --unscramble <seed-parts...> Unscramble seed words in random order to valid seeds
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -45,6 +46,17 @@ spell system smoke army frame vacant trick jacket anchor gasp acoustic supply de
|
|||||||
solar lab option erosion unit example convince viable soft smart smile spoon range card gentle miracle latin they verify want reject side cheese panther
|
solar lab option erosion unit example convince viable soft smart smile spoon range card gentle miracle latin they verify want reject side cheese panther
|
||||||
$ seedxor -c 'spell system smoke army frame vacant trick jacket anchor gasp acoustic supply deputy portion butter similar trend scorpion cause fish outer armor process faint' 'solar lab option erosion unit example convince viable soft smart smile spoon range card gentle miracle latin they verify want reject side cheese panther'
|
$ seedxor -c 'spell system smoke army frame vacant trick jacket anchor gasp acoustic supply deputy portion butter similar trend scorpion cause fish outer armor process faint' 'solar lab option erosion unit example convince viable soft smart smile spoon range card gentle miracle latin they verify want reject side cheese panther'
|
||||||
butter patch first doll raise safe side lounge shiver protect solid area melody member lazy easily nice canvas stomach pattern claim slot million stomach
|
butter patch first doll raise safe side lounge shiver protect solid area melody member lazy easily nice canvas stomach pattern claim slot million stomach
|
||||||
|
|
||||||
|
$ seedxor -u 'affair mutual spare' 'smooth mushroom scale' 'include neck grab' 'fly maze obtain'
|
||||||
|
# total permutations: 24
|
||||||
|
include neck grab smooth mushroom scale fly maze obtain affair mutual spare
|
||||||
|
# good: 1 bad: 23 total: 24
|
||||||
|
|
||||||
|
$ seedxor -u 'squirrel tray cheese' 'seek enhance oval' 'expect sense fish' 'total salad page'
|
||||||
|
# total permutations: 24
|
||||||
|
squirrel tray cheese seek enhance oval expect sense fish total salad page
|
||||||
|
expect sense fish squirrel tray cheese seek enhance oval total salad page
|
||||||
|
# good: 2 bad: 22 total: 24
|
||||||
```
|
```
|
||||||
|
|
||||||
## Library Example
|
## Library Example
|
||||||
|
24
src/lib.rs
24
src/lib.rs
@ -37,11 +37,10 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
pub use bip39::{Error, Language};
|
pub use bip39::{Error, Language};
|
||||||
use std::fmt::Display;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
ops::{BitXor, BitXorAssign},
|
fmt::Display,
|
||||||
|
ops::{BitXor, BitXorAssign, Deref, DerefMut},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -149,11 +148,18 @@ impl Mnemonic {
|
|||||||
|
|
||||||
pub fn to_short_string(&self) -> String {
|
pub fn to_short_string(&self) -> String {
|
||||||
let mut ret = self.word_iter().fold(String::new(), |mut s, w| {
|
let mut ret = self.word_iter().fold(String::new(), |mut s, w| {
|
||||||
w.chars().take(4).for_each(|c| s.push(c));
|
if w.len() == 3 {
|
||||||
s.push(' ');
|
s.push_str(w);
|
||||||
|
s.push_str(" ");
|
||||||
|
} else {
|
||||||
|
w.chars().take(4).for_each(|c| s.push(c));
|
||||||
|
s.push(' ');
|
||||||
|
}
|
||||||
s
|
s
|
||||||
});
|
});
|
||||||
ret.pop();
|
while ret.chars().last() == Some(' ') {
|
||||||
|
ret.pop();
|
||||||
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,7 +407,7 @@ mod tests {
|
|||||||
|
|
||||||
let short_string = seed.to_short_string();
|
let short_string = seed.to_short_string();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"sile toe meat poss chai blos wait occu this wort opti boy",
|
"sile toe meat poss chai blos wait occu this wort opti boy",
|
||||||
short_string
|
short_string
|
||||||
);
|
);
|
||||||
//assert_eq!(Language::English, bip39::Mnemonic::language_of(&short_string).unwrap());
|
//assert_eq!(Language::English, bip39::Mnemonic::language_of(&short_string).unwrap());
|
||||||
@ -414,7 +420,7 @@ mod tests {
|
|||||||
|
|
||||||
let short_string = seed.to_short_string();
|
let short_string = seed.to_short_string();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"song vani mist nigh drin add modi lens aver cool evil ches",
|
"song vani mist nigh drin add modi lens aver cool evil ches",
|
||||||
short_string
|
short_string
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -429,7 +435,7 @@ mod tests {
|
|||||||
|
|
||||||
let short_string = seed.to_short_string();
|
let short_string = seed.to_short_string();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"ramp exot reso icon sun addi equi sand leis spar swin toas",
|
"ramp exot reso icon sun addi equi sand leis spar swin toas",
|
||||||
short_string
|
short_string
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
61
src/main.rs
61
src/main.rs
@ -1,6 +1,5 @@
|
|||||||
use seedxor::{Language, Mnemonic, SeedXor};
|
use seedxor::{expand_words, Language, Mnemonic, SeedXor};
|
||||||
use std::process::ExitCode;
|
use std::{process::ExitCode, str::FromStr};
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
@ -74,17 +73,18 @@ const WORD_COUNT: usize = 24;
|
|||||||
fn help(success: bool) -> ExitCode {
|
fn help(success: bool) -> ExitCode {
|
||||||
println!(
|
println!(
|
||||||
r###"usage: seedxor [options...]
|
r###"usage: seedxor [options...]
|
||||||
-h, --help Display this help
|
-h, --help Display this help
|
||||||
-s, --split <seed> Split seed into num-seeds
|
-s, --split <seed> Split seed into num-seeds
|
||||||
-n, --num-seeds <num> Number of seeds to split into or generate
|
-n, --num-seeds <num> Number of seeds to split into or generate
|
||||||
default {NUM_SEEDS}
|
default {NUM_SEEDS}
|
||||||
-y, --no-validate Do not validate a split can be successfully recombined, useful for
|
-y, --no-validate Do not validate a split can be successfully recombined, useful for
|
||||||
non-bip39 seeds, like ethereum
|
non-bip39 seeds, like ethereum
|
||||||
-g, --generate Generate num-seeds
|
-g, --generate Generate num-seeds
|
||||||
-w, --word-count <num> Number of words to generate in the seed
|
-w, --word-count <num> Number of words to generate in the seed
|
||||||
default {WORD_COUNT}
|
default {WORD_COUNT}
|
||||||
-c, --combine <seeds...> Combine seeds into one seed
|
-c, --combine <seeds...> Combine seeds into one seed
|
||||||
-r, --short Display only first 4 letters of seed words
|
-r, --short Display only first 4 letters of seed words
|
||||||
|
-u, --unscramble <seed-parts...> Unscramble seed words in random order to valid seeds
|
||||||
"###
|
"###
|
||||||
);
|
);
|
||||||
if success {
|
if success {
|
||||||
@ -157,8 +157,39 @@ fn main() -> ExitCode {
|
|||||||
.collect();
|
.collect();
|
||||||
let seed = Mnemonic::xor_all(&parts).unwrap();
|
let seed = Mnemonic::xor_all(&parts).unwrap();
|
||||||
println!("{}", seed.to_display_string(short));
|
println!("{}", seed.to_display_string(short));
|
||||||
|
} else if args.flags(&["-u", "--unscramble"]) {
|
||||||
|
let remaining = args.remaining();
|
||||||
|
if remaining.is_empty() {
|
||||||
|
println!("error: --unscramble needs > 0 arguments");
|
||||||
|
return help(false);
|
||||||
|
}
|
||||||
|
let mut parts: Vec<String> = remaining
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| expand_words(&s).expect("invalid bip39 seed words"))
|
||||||
|
.collect();
|
||||||
|
let total: u128 = (1..=parts.len() as u128).product();
|
||||||
|
eprintln!("# total permutations: {total}");
|
||||||
|
if total > u64::MAX as u128 {
|
||||||
|
println!("total too large, will never finish, aborting");
|
||||||
|
return ExitCode::FAILURE;
|
||||||
|
}
|
||||||
|
let mut heap = permutohedron::Heap::new(&mut parts);
|
||||||
|
let mut good = 0u64;
|
||||||
|
while let Some(words) = heap.next_permutation() {
|
||||||
|
let words = words.join(" ");
|
||||||
|
if let Ok(mnemonic) = Mnemonic::from_str(&words) {
|
||||||
|
if short {
|
||||||
|
println!("{}", mnemonic.to_short_string());
|
||||||
|
} else {
|
||||||
|
println!("{words}");
|
||||||
|
}
|
||||||
|
good += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let bad = total - good as u128;
|
||||||
|
eprintln!("# good: {good} bad: {bad} total: {total}");
|
||||||
} else {
|
} else {
|
||||||
println!("error: need one of -s/-g/-c");
|
println!("error: need one of -s/-g/-c/-u");
|
||||||
return help(false);
|
return help(false);
|
||||||
}
|
}
|
||||||
ExitCode::SUCCESS
|
ExitCode::SUCCESS
|
||||||
|
Loading…
Reference in New Issue
Block a user