async def process_host_command(self, stream, show_fn): """ If command with one of the prefixes is received it will be passed to this method. Should return a tuple: - stream (file, BytesIO etc) - meta object with title and note """ # reads prefix from the stream (until first space) prefix = self.get_prefix(stream) if prefix != b"getrandom": # WTF? It's not our data... raise AppError("Prefix is not valid: %s" % prefix.decode()) # by default we return 32 bytes num_bytes = 32 try: num_bytes = int(stream.read().decode().strip()) except: pass if num_bytes < 0: raise AppError("Seriously? %d bytes? No..." % num_bytes) if num_bytes > 1000: raise AppError("Sorry, 1k bytes max.") obj = {"title": "Here is your entropy", "note": "%d bytes" % num_bytes} return BytesIO(hexlify(get_random_bytes(num_bytes))), obj
def load_enc_secret(self): fpath = self.path + "/enc_secret" if platform.file_exists(fpath): _, secret = self.load_aead(fpath, self.pin_secret) else: # create new key if it doesn't exist secret = get_random_bytes(32) self.save_aead(fpath, plaintext=secret, key=self.pin_secret) self.enc_secret = secret
def create_new_secret(self, path): """Generate new secret and default PIN config""" # generate new and save secret = get_random_bytes(32) # save secret with open(path + "/secret", "wb") as f: f.write(secret) self.secret = secret return secret
def __init__(self, title="Enter your PIN code", note=None, get_word=None, subtitle=None): super().__init__() self.title = add_label(title, scr=self, y=PADDING, style="title") if subtitle is not None: lbl = add_label(subtitle, scr=self, style="hint") lbl.set_recolor(True) lbl.align(self.title, lv.ALIGN.OUT_BOTTOM_MID, 0, 10) if note is not None: lbl = add_label(note, scr=self, style="hint") lbl.align(self.title, lv.ALIGN.OUT_BOTTOM_MID, 0, 180) self.get_word = get_word if get_word is not None: self.words = add_label(get_word(b""), scr=self) self.words.align(self.title, lv.ALIGN.OUT_BOTTOM_MID, 0, 210) btnm = lv.btnm(self) # shuffle numbers to make sure # no constant fingerprints left on screen buttons = ["%d" % i for i in range(0, 10)] btnmap = [] for j in range(3): for i in range(3): v = rng.get_random_bytes(1)[0] % len(buttons) btnmap.append(buttons.pop(v)) btnmap.append("\n") btnmap = btnmap + [lv.SYMBOL.CLOSE, buttons.pop(), lv.SYMBOL.OK, ""] btnm.set_map(btnmap) btnm.set_width(HOR_RES) btnm.set_height(HOR_RES) btnm.align(self, lv.ALIGN.IN_BOTTOM_MID, 0, 0) # increase font size style = lv.style_t() lv.style_copy(style, btnm.get_style(lv.btnm.STYLE.BTN_REL)) style.text.font = lv.font_roboto_28 # remove feedback on press to avoid sidechannels btnm.set_style(lv.btnm.STYLE.BTN_REL, style) btnm.set_style(lv.btnm.STYLE.BTN_PR, style) self.pin = lv.ta(self) self.pin.set_text("") self.pin.set_pwd_mode(True) style = lv.style_t() lv.style_copy(style, styles["theme"].style.ta.oneline) style.text.font = lv.font_roboto_28 style.text.color = styles["theme"].style.scr.text.color style.text.letter_space = 15 self.pin.set_style(lv.label.STYLE.MAIN, style) self.pin.set_width(HOR_RES - 2 * PADDING) self.pin.set_x(PADDING) self.pin.set_y(PADDING + 50) self.pin.set_cursor_type(lv.CURSOR.HIDDEN) self.pin.set_one_line(True) self.pin.set_text_align(lv.label.ALIGN.CENTER) self.pin.set_pwd_show_time(0) self.pin.align(btnm, lv.ALIGN.OUT_TOP_MID, 0, -150) btnm.set_event_cb(feed_rng(self.cb))
def encrypt(plain: bytes, key: bytes) -> bytes: """Encrypt data with bit padding (0x80...)""" iv = rng.get_random_bytes(IV_SIZE) crypto = aes(key, AES_CBC, iv) # encrypted data should be mod 16 (blocksize) # add padding plain += b'\x80' if len(plain) % AES_BLOCK != 0: # fill with zeroes plain += b"\x00" * (AES_BLOCK - (len(plain) % AES_BLOCK)) return iv + crypto.encrypt(plain)
def create_new_secret(self, path): """Generate new secret and default PIN config""" # generate new and save secret = get_random_bytes(32) # save secret with open(path + "/secret", "wb") as f: f.write(secret) # set pin object self.pin = None self._pin_attempts_max = 10 self._pin_attempts_left = 10 self.secret = secret self.save_state() return secret
def set_pin(self, pin): """Saves hmac of the PIN code for verification later""" # set up pin key = tagged_hash("pin", self.secret) self.pin = hmac.new(key=key, msg=pin, digestmod="sha256").digest() self.pin_secret = tagged_hash("pin", self.secret + pin.encode()) self.save_state() # update encryption secret if self.enc_secret is None: self.enc_secret = get_random_bytes(32) self.save_aead(self.path + "/enc_secret", plaintext=self.enc_secret, key=self.pin_secret) # call unlock now self.unlock(pin)
def load_enc_secret(self): data = self.applet.get_secret() # no data yet if len(data) == 0: # create new key if it doesn't exist secret = get_random_bytes(32) # format: magic, 01 len enc_secret, 02 len entropy d = self.serialize_data({"enc": secret}) self.applet.save_secret(d) self._is_key_saved = False else: try: d = self.parse_data(data) secret = d["enc"] if "entropy" in d: self._is_key_saved = True except KeyStoreError as e: # wrong data on the card - not a big deal # just generate a new key self.enc_secret = get_random_bytes(32) self._is_key_saved = False # notify the user about the error raise e self.enc_secret = secret
def save_entropy_encrypted(): try: Key.iv = get_random_bytes(16) entropy_encrypted = entropy_encrypt(entropy) hmac_entropy_encrypted = hmac_sha512(Key.key, entropy_encrypted) obj = { "entropy": hexlify(entropy_encrypted).decode('utf-8'), "iv": hexlify(Key.iv).decode('utf-8'), "hmac": hexlify(hmac_entropy_encrypted).decode('utf-8') } with open(reckless_fname, "w") as f: f.write(json.dumps(obj)) with open(reckless_fname, "r") as f: d = json.loads(f.read()) if "entropy" in d and d["entropy"] == hexlify(entropy_encrypted).decode('utf-8') and \ unhexlify(d["hmac"]) == hmac_entropy_encrypted and entropy == entropy_decrypt(entropy_encrypted): gui.alert("Success!", "Your encrypted key is saved in the memory now") else: gui.error("Something went wrong") except Exception as e: gui.error("Fail: %r" % e)
def gen_mnemonic(num_words: int) -> str: """Generates a mnemonic with num_words""" if num_words < 12 or num_words > 24 or num_words % 3 != 0: raise RuntimeError("Invalid word count") return bip39.mnemonic_from_bytes(rng.get_random_bytes(num_words * 4 // 3))
def sign(self, tx): # good practice to randomize context # it reduces chances of side-channel attacks secp256k1.context_randomize(get_random_bytes(32)) tx.sign_with(self.root) return tx
def open(self, mode=None): """Opens a secure channel. Mode can be "es" - ephemeral-static or "ee" - ephemeral-ephemenral """ # save mode for later - i.e. reestablish secure channel if mode is None: mode = self.mode else: self.mode = mode # check if we know pubkey already if self.card_pubkey is None: self.get_card_pubkey() # generate ephimerial key secret = get_random_bytes(32) host_prv = secret host_pub = secp256k1.ec_pubkey_create(secret) # ee mode - ask card to create ephimerial key and send it to us if mode == "ee": data = secp256k1.ec_pubkey_serialize(host_pub, secp256k1.EC_UNCOMPRESSED) # get ephimerial pubkey from the card res = self.applet.request(self.OPEN_EE + encode(data)) s = BytesIO(res) data = s.read(65) pub = secp256k1.ec_pubkey_parse(data) secp256k1.ec_pubkey_tweak_mul(pub, secret) shared_secret = hashlib.sha256( secp256k1.ec_pubkey_serialize(pub)[1:33]).digest() shared_fingerprint = self.derive_keys(shared_secret) recv_hmac = s.read(MAC_SIZE) h = hmac.new(self.card_mac_key, digestmod="sha256") h.update(data) expected_hmac = h.digest()[:MAC_SIZE] if expected_hmac != recv_hmac: raise SecureChannelError("Wrong HMAC.") data += recv_hmac raw_sig = s.read() sig = secp256k1.ecdsa_signature_parse_der(raw_sig) # in case card doesn't follow low s rule (but it should) sig = secp256k1.ecdsa_signature_normalize(sig) if not secp256k1.ecdsa_verify(sig, hashlib.sha256(data).digest(), self.card_pubkey): raise SecureChannelError("Signature is invalid.") # se mode - use our ephimerial key with card's static key else: data = secp256k1.ec_pubkey_serialize(host_pub, secp256k1.EC_UNCOMPRESSED) # ugly copy pub = secp256k1.ec_pubkey_parse( secp256k1.ec_pubkey_serialize(self.card_pubkey)) secp256k1.ec_pubkey_tweak_mul(pub, secret) shared_secret = secp256k1.ec_pubkey_serialize(pub)[1:33] res = self.applet.request(self.OPEN_SE + encode(data)) s = BytesIO(res) nonce_card = s.read(32) recv_hmac = s.read(MAC_SIZE) secret_with_nonces = hashlib.sha256(shared_secret + nonce_card).digest() shared_fingerprint = self.derive_keys(secret_with_nonces) data = nonce_card h = hmac.new(self.card_mac_key, digestmod="sha256") h.update(data) expected_hmac = h.digest()[:MAC_SIZE] if expected_hmac != recv_hmac: raise SecureChannelError("Wrong HMAC.") data += recv_hmac sig = secp256k1.ecdsa_signature_parse_der(s.read()) # in case card doesn't follow low s rule (but it should) sig = secp256k1.ecdsa_signature_normalize(sig) if not secp256k1.ecdsa_verify(sig, hashlib.sha256(data).digest(), self.card_pubkey): raise SecureChannelError("Signature is invalid") # reset iv self.iv = 0 self.is_open = True
def get_new_mnemonic(words=12): entropy_len = words*4//3 global entropy entropy = get_random_bytes(entropy_len) return bip39.mnemonic_from_bytes(entropy)
def ask_pin(first_time_usage, callback): scr = switch_to_new_screen() first_time_title = "Choose a PIN code" title = "Enter your PIN code" if first_time_usage: title = first_time_title title_lbl = add_label(title, y=PADDING, style="title") btnm = lv.btnm(scr) # shuffle numbers to make sure # no constant fingerprints left on screen buttons = ["%d" % i for i in range(0,10)] btnmap = [] for j in range(3): for i in range(3): v = rng.get_random_bytes(1)[0] % len(buttons) btnmap.append(buttons.pop(v)) btnmap.append("\n") btnmap = btnmap+[lv.SYMBOL.CLOSE, buttons.pop(), lv.SYMBOL.OK, ""] btnm.set_map(btnmap) btnm.set_width(HOR_RES) btnm.set_height(HOR_RES) btnm.align(scr, lv.ALIGN.IN_BOTTOM_MID, 0, 0) # remove feedback on press to avoid sidechannels btnm.set_style(lv.btnm.STYLE.BTN_PR,btnm.get_style(lv.btnm.STYLE.BTN_REL)) pin_lbl = lv.ta(scr) pin_lbl.set_text("") pin_lbl.set_pwd_mode(True) style = lv.style_t() lv.style_copy(style, styles["theme"].style.ta.oneline) style.text.font = lv.font_roboto_28 style.text.color = lv.color_hex(0xffffff) style.text.letter_space = 15 pin_lbl.set_style(lv.label.STYLE.MAIN, style) pin_lbl.set_width(HOR_RES-2*PADDING) pin_lbl.set_x(PADDING) pin_lbl.set_y(PADDING+50) pin_lbl.set_cursor_type(lv.CURSOR.HIDDEN) pin_lbl.set_one_line(True) pin_lbl.set_text_align(lv.label.ALIGN.CENTER) pin_lbl.set_pwd_show_time(0) instruct_txt = "Device tamper check.\nThese words should remain #ffffff the same every time#:" instruct_label = add_label(instruct_txt, 180, style="hint") instruct_label.set_recolor(True) antiphish_label = add_label(antiphishing_word(""), 250) Pin.read_counter() @feed_rng def cb(obj, event): nonlocal first_time_usage if event == lv.EVENT.RELEASED: c = obj.get_active_btn_text() if c is None: return if c == lv.SYMBOL.CLOSE: pin_lbl.set_text("") antiphish_label.set_text(antiphishing_word("")) elif c == lv.SYMBOL.OK: # FIXME: check PIN len Key.generate_key(pin_lbl.get_text()); if first_time_usage: Secret.save_secret(alert); callback() else: Pin.counter -= 1 Pin.save_counter(alert) if Pin.is_pin_valid(): Pin.reset_counter(alert) callback() else: instruct_label.set_text("#f07070 Wrong pin: %d/%d #" % (Pin.counter, Pin.ATTEMPTS_MAX)) if Pin.counter <= 0: Factory_settings.restore(alert) Secret.generate_secret() alert("Security","Device has been factory reset!") first_time_usage = True title_lbl.set_text(first_time_title) instruct_label.set_text(instruct_txt) pin_lbl.set_text("") antiphish_label.set_text(antiphishing_word("")) else: instruct_label.set_text(instruct_txt) pin_lbl.add_text(c) word = antiphishing_word(pin_lbl.get_text()) antiphish_label.set_text(antiphish_label.get_text() + " " + word) btnm.set_event_cb(cb);
def generate_secret(): Secret.secret = get_random_bytes(32)
def roll_dice(sides=6): """ return 1 ... sides """ d = get_random_bytes(1) print (sides) print (d) return 1+int.from_bytes(d, 'big') % sides