Skip to content

Commit b11619d

Browse files
authored
Merge pull request #62 from pezcore/feature/checksum
Add interface to expose the mnemonic checksum
2 parents 54358b9 + 778527c commit b11619d

File tree

1 file changed

+74
-0
lines changed

1 file changed

+74
-0
lines changed

src/lib.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,35 @@ impl Mnemonic {
606606
let (arr, len) = self.to_entropy_array();
607607
arr[0..len].to_vec()
608608
}
609+
610+
/// Return checksum value for the Mnemonic.
611+
///
612+
/// The checksum value is the numerical value of the first `self.word_count() / 3` bits of the
613+
/// [SHA256](https://en.wikipedia.org/wiki/SHA-2) digest of the Mnemonic's entropy, and is
614+
/// encoded by the last word of the mnemonic sentence.
615+
///
616+
/// This is useful for validating the integrity of a mnemonic: For a valid mnemonic `m`, the
617+
/// following assertion should hold:
618+
///
619+
/// ```rust
620+
/// # use bip39::Mnemonic;
621+
/// # use bitcoin_hashes::{Hash, sha256, hex::FromHex};
622+
/// # let ent = Vec::from_hex("98FE3D0FF6E955A484B0A1D0C9CE10F6").unwrap();
623+
/// # let m = Mnemonic::from_entropy(&ent).unwrap();
624+
/// let checksum_width = m.word_count() / 3;
625+
/// let shift_width = 8 - checksum_width;
626+
/// assert_eq!(sha256::Hash::hash(&m.to_entropy())[0] >> shift_width, m.checksum());
627+
/// ```
628+
///
629+
/// Note that since this library constrains initialization of `Mnemonic` instances through an
630+
/// API that guarantees validity, all `Mnemonic` instances should be valid and the above
631+
/// condition should hold.
632+
pub fn checksum(&self) -> u8 {
633+
let word_count = self.word_count();
634+
let last_word = self.words[word_count - 1];
635+
let mask = 0xFF >> (8 - word_count / 3);
636+
last_word as u8 & mask
637+
}
609638
}
610639

611640
impl fmt::Display for Mnemonic {
@@ -879,6 +908,51 @@ mod tests {
879908
}
880909
}
881910

911+
#[test]
912+
fn checksum() {
913+
let vectors = [
914+
"00000000000000000000000000000000",
915+
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
916+
"80808080808080808080808080808080",
917+
"ffffffffffffffffffffffffffffffff",
918+
"000000000000000000000000000000000000000000000000",
919+
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
920+
"808080808080808080808080808080808080808080808080",
921+
"ffffffffffffffffffffffffffffffffffffffffffffffff",
922+
"0000000000000000000000000000000000000000000000000000000000000000",
923+
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
924+
"8080808080808080808080808080808080808080808080808080808080808080",
925+
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
926+
"9e885d952ad362caeb4efe34a8e91bd2",
927+
"6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",
928+
"68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",
929+
"c0ba5a8e914111210f2bd131f3d5e08d",
930+
"6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",
931+
"9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",
932+
"23db8160a31d3e0dca3688ed941adbf3",
933+
"8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",
934+
"066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",
935+
"f30f8c1da665478f49b001d94c5fc452",
936+
"c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",
937+
"f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",
938+
"ed3b83f0d7913a19667a1cfd7298cd57",
939+
"70639a4e81b151277b345476d169a3743ff3c141",
940+
"ba2520298b92063a7a0ee1d453ba92513af81d4f86e1d336",
941+
"9447d2cf44349cd88a58f5b4ff6f83b9a2d54c42f033e12b8e4d00cc",
942+
"38711e550dc6557df8082b2a87f7860ebbe47ea5867a7068f5f0f5b85db68be8",
943+
];
944+
945+
for entropy_hex in &vectors {
946+
let ent = Vec::from_hex(entropy_hex).unwrap();
947+
let m = Mnemonic::from_entropy(&ent).unwrap();
948+
let word_count = m.word_count();
949+
let cs = m.checksum();
950+
let digest = sha256::Hash::hash(&ent);
951+
dbg!(digest);
952+
assert_eq!(digest[0] >> (8 - word_count / 3), cs);
953+
}
954+
}
955+
882956
#[test]
883957
fn test_invalid_engish() {
884958
// correct phrase:

0 commit comments

Comments
 (0)