def p2sh_address(cls, addr_fmt, witdeem_script): # Multisig and general P2SH support # - witdeem => witness script for segwit, or redeem script otherwise # - redeem script can be generated from witness script if needed. # - this function needs a witdeem script to be provided, not simple to make # - more verification needed to prove it's change/included address (NOT HERE) # - reference: <https://bitcoincore.org/en/segwit_wallet_dev/> # - returns: str(address) assert addr_fmt & AFC_SCRIPT, 'for p2sh only' assert witdeem_script, "need witness/redeem script" if addr_fmt & AFC_SEGWIT: digest = trezorcrypto.sha256(witdeem_script).digest() else: digest = hash160(witdeem_script) if addr_fmt & AFC_BECH32: # bech32 encoded segwit p2sh addr = tcc.codecs.bech32_encode(cls.bech32_hrp, 0, digest) elif addr_fmt == AF_P2WSH_P2SH: # segwit p2wsh encoded as classic P2SH addr = tcc.codecs.b58_encode(cls.b58_script + hash160(b'\x00\x20' + digest)) else: # P2SH classic addr = tcc.codecs.b58_encode(cls.b58_script + digest) return addr
def save_visualization(self, msg, sign_text=False): # write text into spi flash, maybe signing it as we go # - return length and checksum txt_len = msg.seek(0, 2) msg.seek(0) chk = self.chain.hash_message(msg_len=txt_len) if sign_text else None with SFFile(TXN_OUTPUT_OFFSET, max_size=txt_len+300, message="Visualizing...") as fd: await fd.erase() while 1: blk = msg.read(256).encode('ascii') if not blk: break if chk: chk.update(blk) fd.write(blk) if chk: from ubinascii import b2a_base64 # append the signature digest = trezorcrypto.sha256(chk.digest()).digest() sig = sign_message_digest(digest, 'm', None) fd.write(b2a_base64(sig).decode('ascii').strip()) fd.write('\n') return (fd.tell(), fd.checksum.digest())
def hash_message(cls, msg=None, msg_len=0): # Perform sha256 for message-signing purposes (only) # - or get setup for that, if msg == None s = trezorcrypto.sha256() s.update(cls.msg_signing_prefix()) msg_len = msg_len or len(msg) s.update(ser_compact_size(msg_len)) if msg is None: return s s.update(msg) return trezorcrypto.sha256(s.digest()).digest()
def save(self): # Render as JSON, encrypt and write it self.curr_dict['_version'] = self.curr_dict.get('_version', 0) + 1 addr = self.next_addr() print( '===============================================================') print('SAVING SETTINGS! _version={} addr={}'.format( self.curr_dict['_version'], hex(addr))) print( '===============================================================') flash_offset = (addr - SETTINGS_FLASH_START) // BLOCK_SIZE aes = self.get_aes(flash_offset) chk = trezorcrypto.sha256() # Create the JSON string as bytes json_buf = ujson.dumps(self.curr_dict).encode('utf8') # Ensure data is not too big # TODO: Check that null byte at the end is handled properly (no overflow) if len(json_buf) > DATA_SIZE: # TODO: Proper error handling assert false, 'JSON data is larger than'.format(DATA_SIZE) # Create a zero-filled byte buf padded_buf = bytearray(DATA_SIZE) # Copy the json data into the padded buffer for i in range(len(json_buf)): padded_buf[i] = json_buf[i] del json_buf # Add the data and padding to the AES and SHA encrypted_buf = aes.encrypt(padded_buf) chk.update(padded_buf) # Build the final buf for writing to flash save_buf = bytearray(BLOCK_SIZE) for i in range(len(encrypted_buf)): save_buf[i] = encrypted_buf[ i] # TODO: How to do this with slice notation so it doesn't truncate destination? digest = chk.digest() for i in range(32): save_buf[BLOCK_SIZE - 32 + i] = digest[i] # print('addr={}\nbuf={}'.format(hex(addr),b2a_hex(save_buf))) self.flash.write(addr, save_buf) # We don't overwrite the old entry here, even though it's now useless, as that can # cause flash to have ECC errors. self.addr = addr self.is_dirty = 0 print("Settings.save(): wrote @ {}".format(hex(addr)))
def set_key(self, new_secret=None): # System settings (not secrets) are stored in internal flash, encrypted with this # key that is derived from main wallet secret. Call this method when the secret # is first loaded, or changes for some reason. from common import pa from stash import blank_object key = None mine = False if not new_secret: if not pa.is_successful() or pa.is_secret_blank(): # simple fixed key allows us to store a few things when logged out key = b'\0' * 32 else: # read secret and use it. new_secret = pa.fetch() mine = True if new_secret: # hash up the secret... without decoding it or similar assert len(new_secret) >= 32 s = trezorcrypto.sha256(new_secret) for round in range(5): s.update('pad') s = trezorcrypto.sha256(s.digest()) key = s.digest() if mine: blank_object(new_secret) # for restore from backup case, or when changing (created) the seed self.aes_key = key
def create_new_wallet_seed(): # Pick a new random seed, and # await ux_dramatic_pause('Generating...', 4) # TODO: Show screen to indicate delay? # always full 24-word (256 bit) entropy seed = bytearray(32) noise.random_bytes(seed) # hash to mitigate any potential bias in Avalanche RNG seed = trezorcrypto.sha256(seed).digest() print('create_new_wallet(): New seed = {}'.format(b2a_hex(seed))) return seed
def calculate_key(self, password, progress_fcn=None): # do the expected key-derivation # emulate CKeyInfo::CalculateDigest in p7zip_9.38.1/CPP/7zip/Crypto/7zAes.cpp rounds = 1 << self.rounds_pow password = encode_utf_16_le(password) result = sha256() for i in range(rounds): result.update(self.salt) result.update(password) temp = pack('<Q', i) result.update(temp) if i % 1000 == 0 and progress_fcn: progress_fcn(i / rounds) return result.digest()
def __init__(self, start, length=0, max_size=1, message=None, pre_erased=False): if not pre_erased: assert start % blksize == 0 # 'misaligned' self.start = start self.pos = 0 self.length = length # byte-wise length self.message = message if max_size != None: self.max_size = PADOUT(max_size) if not pre_erased else max_size self.readonly = False self.checksum = trezorcrypto.sha256() else: self.readonly = True from common import sf self.sf = sf
def sha256(s): return trezorcrypto.sha256(s).digest()
def load(self): # Search all slots for any we can read, decrypt that, # and pick the newest one (in unlikely case of dups) try: # reset self.curr_dict.clear() self.overrides.clear() self.addr = 0 self.is_dirty = 0 for addr in SLOT_ADDRS: buf = uctypes.bytearray_at(addr, 4) if buf[0] == buf[1] == buf[2] == buf[3] == 0xff: # Save this so we can start at an empty slot when no decodable data # is found (we can't just start at the beginning since it might # not be erased). # print(' Slot is ERASED') # erased (probably) continue # check if first 2 bytes makes sense for JSON flash_offset = (addr - SETTINGS_FLASH_START) // BLOCK_SIZE aes = self.get_aes(flash_offset) chk = aes.decrypt(b'{"') if chk != buf[0:2]: # doesn't look like JSON, so skip it # print(' Slot does not contain JSON') continue # probably good, so prepare to read it aes = self.get_aes(flash_offset) chk = trezorcrypto.sha256() expect = None # Copy the data - our flash is memory mapped, so we read directly by address buf = uctypes.bytearray_at(addr, DATA_SIZE) # Get a bytearray for the SHA256 at the end expected_sha = uctypes.bytearray_at(addr + DATA_SIZE, 32) # Decrypt and check hash b = aes.decrypt(buf) # Add the decrypted result to the SHA chk.update(b) try: # verify hash in last 32 bytes assert expected_sha == chk.digest() # FOUNDATION # loads() can't work from a byte array, and converting to # bytes here would copy it; better to use file emulation. # print('json = {}'.format(b)) d = ujson.load(BytesIO(b)) except: # One in 65k or so chance to come here w/ garbage decoded, so # not an error. # print('ERROR? Unable to decode JSON') continue curr_version = d.get('_version', 0) if curr_version > self.curr_dict.get('_version', -1): # print('Found candidate JSON: {}'.format(d)) # A newer entry was found self.curr_dict = d self.addr = addr # If we loaded settings, then we're done if self.addr: return # Add some che # if self. # If no entries were found, which means this is either the first boot or we have corrupt settings, so raise an exception so we erase and set default # raise ValueError('Flash is either blank or corrupt, so me must reset to recover to avoid a crash!') self.curr_dict = self.default_values() self.overrides.clear() self.addr = 0 except Exception as e: print('Exception in settings.load(): e={}'.format(e)) self.reset() self.is_dirty = True self.write_out()