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."
|
||||||
@ -22,3 +22,4 @@ 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 }
|
||||||
|
14
README.md
14
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.
|
||||||
@ -23,6 +23,7 @@ usage: seedxor [options...]
|
|||||||
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
|
||||||
|
12
src/lib.rs
12
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| {
|
||||||
|
if w.len() == 3 {
|
||||||
|
s.push_str(w);
|
||||||
|
s.push_str(" ");
|
||||||
|
} else {
|
||||||
w.chars().take(4).for_each(|c| s.push(c));
|
w.chars().take(4).for_each(|c| s.push(c));
|
||||||
s.push(' ');
|
s.push(' ');
|
||||||
|
}
|
||||||
s
|
s
|
||||||
});
|
});
|
||||||
|
while ret.chars().last() == Some(' ') {
|
||||||
ret.pop();
|
ret.pop();
|
||||||
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
39
src/main.rs
39
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>,
|
||||||
@ -85,6 +84,7 @@ fn help(success: bool) -> ExitCode {
|
|||||||
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 {
|
} else {
|
||||||
println!("error: need one of -s/-g/-c");
|
println!("{words}");
|
||||||
|
}
|
||||||
|
good += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let bad = total - good as u128;
|
||||||
|
eprintln!("# good: {good} bad: {bad} total: {total}");
|
||||||
|
} else {
|
||||||
|
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