@@ -606,6 +606,35 @@ impl Mnemonic {
606
606
let ( arr, len) = self . to_entropy_array ( ) ;
607
607
arr[ 0 ..len] . to_vec ( )
608
608
}
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
+ }
609
638
}
610
639
611
640
impl fmt:: Display for Mnemonic {
@@ -879,6 +908,51 @@ mod tests {
879
908
}
880
909
}
881
910
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
+
882
956
#[ test]
883
957
fn test_invalid_engish ( ) {
884
958
// correct phrase:
0 commit comments