URI: 
       trevealer.py - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
       trevealer.py (3555B)
       ---
            1 import random
            2 import os
            3 from hashlib import sha256
            4 from typing import NamedTuple, Optional, Dict, Tuple
            5 
            6 from electrum.plugin import BasePlugin
            7 from electrum.util import to_bytes, bh2u, bfh
            8 
            9 from .hmac_drbg import DRBG
           10 
           11 
           12 class VersionedSeed(NamedTuple):
           13     version: str
           14     seed: str
           15     checksum: str
           16 
           17     def get_ui_string_version_plus_seed(self):
           18         version, seed = self.version, self.seed
           19         assert isinstance(version, str) and len(version) == 1, version
           20         assert isinstance(seed, str) and len(seed) >= 32
           21         ret = version + seed
           22         ret = ret.upper()
           23         return ' '.join(ret[i : i+4] for i in range(0, len(ret), 4))
           24 
           25 
           26 class RevealerPlugin(BasePlugin):
           27 
           28     LATEST_VERSION = '1'
           29     KNOWN_VERSIONS = ('0', '1')
           30     assert LATEST_VERSION in KNOWN_VERSIONS
           31 
           32     SIZE = (159, 97)
           33 
           34     def __init__(self, parent, config, name):
           35         BasePlugin.__init__(self, parent, config, name)
           36 
           37     @classmethod
           38     def code_hashid(cls, txt: str) -> str:
           39         txt = txt.lower()
           40         x = to_bytes(txt, 'utf8')
           41         hash = sha256(x).hexdigest()
           42         return hash[-3:].upper()
           43 
           44     @classmethod
           45     def get_versioned_seed_from_user_input(cls, txt: str) -> Optional[VersionedSeed]:
           46         if len(txt) < 34:
           47             return None
           48         try:
           49             int(txt, 16)
           50         except:
           51             return None
           52         version = txt[0]
           53         if version not in cls.KNOWN_VERSIONS:
           54             return None
           55         checksum = cls.code_hashid(txt[:-3])
           56         if txt[-3:].upper() != checksum.upper():
           57             return None
           58         return VersionedSeed(version=version.upper(),
           59                              seed=txt[1:-3].upper(),
           60                              checksum=checksum.upper())
           61 
           62     @classmethod
           63     def get_noise_map(cls, versioned_seed: VersionedSeed) -> Dict[Tuple[int, int], int]:
           64         """Returns a map from (x,y) coordinate to pixel value 0/1, to be used as rawnoise."""
           65         w, h = cls.SIZE
           66         version  = versioned_seed.version
           67         hex_seed = versioned_seed.seed
           68         checksum = versioned_seed.checksum
           69         noise_map = {}
           70         if version == '0':
           71             random.seed(int(hex_seed, 16))
           72             for x in range(w):
           73                 for y in range(h):
           74                     noise_map[(x, y)] = random.randint(0, 1)
           75         elif version == '1':
           76             prng_seed = bfh(hex_seed + version + checksum)
           77             drbg = DRBG(prng_seed)
           78             num_noise_bytes = 1929  # ~ w*h
           79             noise_array = bin(int.from_bytes(drbg.generate(num_noise_bytes), 'big'))[2:]
           80             # there's an approx 1/1024 chance that the generated number is 'too small'
           81             # and we would get IndexError below. easiest backwards compat fix:
           82             noise_array += '0' * (w * h - len(noise_array))
           83             i = 0
           84             for x in range(w):
           85                 for y in range(h):
           86                     noise_map[(x, y)] = int(noise_array[i])
           87                     i += 1
           88         else:
           89             raise Exception(f"unexpected revealer version: {version}")
           90         return noise_map
           91 
           92     @classmethod
           93     def gen_random_versioned_seed(cls):
           94         version = cls.LATEST_VERSION
           95         hex_seed = bh2u(os.urandom(16))
           96         checksum = cls.code_hashid(version + hex_seed)
           97         return VersionedSeed(version=version.upper(),
           98                              seed=hex_seed.upper(),
           99                              checksum=checksum.upper())
          100 
          101 
          102 if __name__ == '__main__':
          103     for i in range(10**4):
          104         vs = RevealerPlugin.gen_random_versioned_seed()
          105         nm = RevealerPlugin.get_noise_map(vs)