rust: move bitbox02::keystore::bip39_mnemonic_from_seed() to bip39 module
What changed, and why it matters
This commit is a simple internal code reorganization: a function that converts a seed into a BIP39 recovery phrase is moved from one Rust module to another, with all callers updated. The actual behavior of the function is unchanged, and no security bug is introduced or fixed.
No security action required. Treat as routine refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change relocates bip39_mnemonic_from_seed() from bitbox02::keystore to the bip39 module in bitbox02-rust. The implementation remains identical (bip39::Mnemonic::from_entropy(seed) wrapped in zeroize::Zeroizing). Call sites in show_mnemonic.rs, keystore.rs, and tests are updated to use the new path. No functional or security-relevant modifications are present.
Changed components
src/rust/bitbox02-rust/src/bip39.rssrc/rust/bitbox02-rust/src/hww/api/show_mnemonic.rssrc/rust/bitbox02-rust/src/keystore.rssrc/rust/bitbox02/src/keystore.rsInspect captured patch +36 / −37
diff --git a/src/rust/bitbox02-rust/src/bip39.rs b/src/rust/bitbox02-rust/src/bip39.rs
index 5916c5e..ab952e8 100644
--- a/src/rust/bitbox02-rust/src/bip39.rs
+++ b/src/rust/bitbox02-rust/src/bip39.rs
@@ -26,6 +26,12 @@ pub fn get_word(idx: u16) -> Result<zeroize::Zeroizing<String>, ()> {
))
}
+/// Encode a seed as a BIP39 mnemonic.
+pub fn mnemonic_from_seed(seed: &[u8]) -> Result<zeroize::Zeroizing<String>, ()> {
+ let mnemonic = bip39::Mnemonic::from_entropy(seed).map_err(|_| ())?;
+ Ok(zeroize::Zeroizing::new(mnemonic.to_string()))
+}
+
/// Decode a BIP39 mnemonic.
pub fn mnemonic_to_seed(mnemonic: &str) -> Result<zeroize::Zeroizing<Vec<u8>>, ()> {
let mnemonic =
@@ -103,6 +109,33 @@ mod tests {
assert_eq!(get_word(563).unwrap().as_ref() as &str, "edit");
}
+ #[test]
+ fn test_mnemonic_from_seed() {
+ // 12 words
+ let seed = b"\xae\x6a\x40\x26\x1f\x0a\xcc\x16\x57\x04\x9c\xb2\x1a\xf5\xfb\xf7";
+ assert_eq!(
+ mnemonic_from_seed(seed).unwrap().as_str(),
+ "purpose faith another dignity proud arctic foster near rare stumble leave urge",
+ );
+
+ // 18 words
+ let seed = b"\x2a\x3e\x07\xa9\xe7\x5e\xd7\x3a\xa6\xb2\xe1\xaf\x90\x3d\x50\x17\xde\x80\x4f\xdf\x2b\x45\xc2\x4b";
+ assert_eq!(
+ mnemonic_from_seed(seed).unwrap().as_str(),
+ "clay usual tuna solid uniform outer onion found question limit favorite cook trend child lake hamster seat foot",
+ );
+
+ // 24 words
+ let seed = b"\x24\x1d\x5b\x78\x35\x90\xc2\x1f\x79\x69\x8e\x7c\xe8\x92\xdd\x03\xfb\x2c\x8f\xad\xc2\x44\x0e\xc2\x3a\xa5\xde\x9e\x2d\x23\x81\xb0";
+ assert_eq!(
+ mnemonic_from_seed(seed).unwrap().as_str(),
+ "catch turn task hen around autumn toss crack language duty resemble among ready elephant require embrace attract balcony practice rule tissue mushroom almost athlete",
+ );
+
+ // Invalid seed side
+ assert!(mnemonic_from_seed(b"foo").is_err());
+ }
+
#[test]
fn test_mnemonic_to_seed() {
assert!(mnemonic_to_seed("invalid").is_err());
diff --git a/src/rust/bitbox02-rust/src/hww/api/show_mnemonic.rs b/src/rust/bitbox02-rust/src/hww/api/show_mnemonic.rs
index 4850730..b9654d9 100644
--- a/src/rust/bitbox02-rust/src/hww/api/show_mnemonic.rs
+++ b/src/rust/bitbox02-rust/src/hww/api/show_mnemonic.rs
@@ -34,7 +34,7 @@ pub async fn process(hal: &mut impl crate::hal::Hal) -> Result<Response, Error>
crate::keystore::copy_seed()?
};
- bitbox02::keystore::bip39_mnemonic_from_seed(&seed)?
+ crate::bip39::mnemonic_from_seed(&seed)?
};
hal.ui()
diff --git a/src/rust/bitbox02-rust/src/keystore.rs b/src/rust/bitbox02-rust/src/keystore.rs
index ec992f0..078d3bd 100644
--- a/src/rust/bitbox02-rust/src/keystore.rs
+++ b/src/rust/bitbox02-rust/src/keystore.rs
@@ -84,7 +84,7 @@ pub fn create_and_store_seed(password: &str, host_entropy: &[u8]) -> Result<(),
/// Returns the keystore's seed encoded as a BIP-39 mnemonic.
pub fn get_bip39_mnemonic() -> Result<zeroize::Zeroizing<String>, ()> {
- keystore::bip39_mnemonic_from_seed(©_seed()?)
+ crate::bip39::mnemonic_from_seed(©_seed()?)
}
fn get_xprv(keypath: &[u32]) -> Result<bip32::Xprv, ()> {
@@ -276,7 +276,7 @@ pub fn bip85_bip39(words: u32, index: u32) -> Result<zeroize::Zeroizing<String>,
];
let entropy = bip85_entropy(&keypath)?;
- keystore::bip39_mnemonic_from_seed(&entropy[..seed_size])
+ crate::bip39::mnemonic_from_seed(&entropy[..seed_size])
}
/// Computes a 16 byte deterministic seed specifically for Lightning hot wallets according to BIP-85.
diff --git a/src/rust/bitbox02/src/keystore.rs b/src/rust/bitbox02/src/keystore.rs
index 98ada42..5064586 100644
--- a/src/rust/bitbox02/src/keystore.rs
+++ b/src/rust/bitbox02/src/keystore.rs
@@ -14,7 +14,6 @@
extern crate alloc;
-use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
@@ -231,11 +230,6 @@ pub fn _copy_bip39_seed() -> Result<zeroize::Zeroizing<Vec<u8>>, ()> {
}
}
-pub fn bip39_mnemonic_from_seed(seed: &[u8]) -> Result<zeroize::Zeroizing<String>, ()> {
- let mnemonic = bip39::Mnemonic::from_entropy(seed).map_err(|_| ())?;
- Ok(zeroize::Zeroizing::new(mnemonic.to_string()))
-}
-
pub struct SignResult {
pub signature: [u8; 64],
pub recid: u8,
@@ -315,36 +309,8 @@ pub fn mock_unlocked(seed: &[u8]) {
mod tests {
use super::*;
use bitcoin::secp256k1;
-
use util::bb02_async::block_on;
- #[test]
- fn test_bip39_mnemonic_from_seed() {
- // 12 words
- let seed = b"\xae\x6a\x40\x26\x1f\x0a\xcc\x16\x57\x04\x9c\xb2\x1a\xf5\xfb\xf7";
- assert_eq!(
- bip39_mnemonic_from_seed(seed).unwrap().as_str(),
- "purpose faith another dignity proud arctic foster near rare stumble leave urge",
- );
-
- // 18 words
- let seed = b"\x2a\x3e\x07\xa9\xe7\x5e\xd7\x3a\xa6\xb2\xe1\xaf\x90\x3d\x50\x17\xde\x80\x4f\xdf\x2b\x45\xc2\x4b";
- assert_eq!(
- bip39_mnemonic_from_seed(seed).unwrap().as_str(),
- "clay usual tuna solid uniform outer onion found question limit favorite cook trend child lake hamster seat foot",
- );
-
- // 24 words
- let seed = b"\x24\x1d\x5b\x78\x35\x90\xc2\x1f\x79\x69\x8e\x7c\xe8\x92\xdd\x03\xfb\x2c\x8f\xad\xc2\x44\x0e\xc2\x3a\xa5\xde\x9e\x2d\x23\x81\xb0";
- assert_eq!(
- bip39_mnemonic_from_seed(seed).unwrap().as_str(),
- "catch turn task hen around autumn toss crack language duty resemble among ready elephant require embrace attract balcony practice rule tissue mushroom almost athlete",
- );
-
- // Invalid seed side
- assert!(bip39_mnemonic_from_seed(b"foo").is_err());
- }
-
#[test]
fn test_derive_bip39_seed() {
struct Test {
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.