2020# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2121# THE SOFTWARE.
2222
23+ import math
2324from .. import Menu , LETTERS , MENU_CONTINUE , MENU_EXIT
2425from ..login import MnemonicLoader
2526from ...krux_settings import Settings , t
2829from ...key import Key
2930from ...wallet import Wallet
3031
32+ NORMALIZED_THRESHOLD = 30
33+
3134
3235class MnemonicXOR (MnemonicLoader ):
3336 """
@@ -53,13 +56,46 @@ def _xor_bytes(a: bytes, b: bytes) -> bytearray:
5356
5457 return out
5558
59+ @staticmethod
60+ def _shannon_sum (distribution , sample_size ):
61+ """Calculates Shannon's entropy of a given distribution.
62+
63+ Example:
64+
65+ 0x00 *
66+ 0x01 **
67+ Ox02 ********
68+ ... ***
69+ 0xFD **
70+ OxFE ***
71+ 0xFD **
72+ OxFF *
73+ """
74+ # Calculate entropy
75+ unit_entropy = 0
76+ for count in distribution :
77+ probability = count / sample_size
78+ unit_entropy -= probability * (probability and math .log2 (probability ))
79+ return unit_entropy
80+
5681 @staticmethod
5782 def _validate_entropy (entropy : bytes | bytearray ) -> None :
58- """Check for low entropy (all zeros or all ones)"""
59- # TODO: apply a shannon low entropy check for XOR
60- all_zeros = bytes (len (entropy ))
61- all_ones = b"\xff " * len (entropy )
62- if entropy in (all_zeros , all_ones ):
83+ """Check for low entropy against its distribution"""
84+ n = len (entropy )
85+
86+ # Differences lie in [-255, +255], then 511 buckets
87+ derivatives = (entropy [i ] - entropy [i - 1 ] for i in range (1 , n ))
88+ d_counts = [0 ] * 511
89+ total_derivatives = 0
90+ for d in derivatives :
91+ d_counts [d + 255 ] += 1
92+ total_derivatives += 1
93+
94+ d_entropy = MnemonicXOR ._shannon_sum (d_counts , total_derivatives )
95+ max_d_entropy = math .log2 (len (d_counts ))
96+ normalized = (d_entropy / max_d_entropy ) * 100.0
97+
98+ if normalized < NORMALIZED_THRESHOLD :
6399 raise ValueError ("Low entropy mnemonic" )
64100
65101 @staticmethod
0 commit comments