def get_resident_credential( index: int, rp_id_hash: Optional[bytes] = None) -> Optional[Credential]: if not (0 <= index < _MAX_RESIDENT_CREDENTIALS): return None stored_cred_data = common.get(common.APP_WEBAUTHN, index + _RESIDENT_CREDENTIAL_START_KEY) if stored_cred_data is None: return None stored_rp_id_hash = stored_cred_data[:32] stored_cred_id = stored_cred_data[32:] if rp_id_hash is not None and rp_id_hash != stored_rp_id_hash: # Stored credential is not for this RP ID. return None stored_cred = Fido2Credential.from_cred_id(stored_cred_id, stored_rp_id_hash) if stored_cred is None: return None stored_cred.index = index return stored_cred
def get_slip39_remaining_shares(group_index: int) -> Optional[int]: _require_progress() remaining = common.get(_NAMESPACE, _REMAINING) if remaining is None or remaining[group_index] == slip39.MAX_SHARE_COUNT: return None else: return remaining[group_index]
def get_sd_salt_auth_key() -> Optional[bytes]: """ The key used to check the authenticity of the SD card salt. """ auth_key = common.get(_NAMESPACE, _SD_SALT_AUTH_KEY, public=True) if auth_key is not None and len(auth_key) != SD_SALT_AUTH_KEY_LEN_BYTES: raise ValueError return auth_key
def get_passphrase_source() -> int: b = common.get(_NAMESPACE, _PASSPHRASE_SOURCE) if b == b"\x01": return 1 elif b == b"\x02": return 2 else: return 0
def _migrate_from_version_01() -> None: # Make the U2F counter public and writable even when storage is locked. # U2F counter wasn't public, so we are intentionally not using storage.device module. counter = common.get(common.APP_DEVICE, device.U2F_COUNTER) if counter is not None: device.set_u2f_counter(int.from_bytes(counter, "big")) # Delete the old, non-public U2F_COUNTER. common.delete(common.APP_DEVICE, device.U2F_COUNTER) set_current_version()
def set_flags(flags: int) -> None: b = common.get(_NAMESPACE, _FLAGS) if b is None: i = 0 else: i = int.from_bytes(b, "big") flags = (flags | i) & 0xFFFFFFFF if flags != i: common.set(_NAMESPACE, _FLAGS, flags.to_bytes(4, "big"))
def fetch_slip39_remaining_shares() -> Optional[List[int]]: _require_progress() remaining = common.get(_NAMESPACE, _REMAINING) if not remaining: return None group_count = get_slip39_group_count() if not group_count: raise RuntimeError return list(remaining[:group_count])
def fetch_slip39_remaining_shares() -> Optional[List[int]]: remaining = common.get(_NAMESPACE, _REMAINING) if not remaining: return None result = [] for i in range(get_slip39_group_count()): result.append(remaining[i]) return result
def set_slip39_remaining_shares(shares_remaining: int, group_index: int) -> None: """ We store the remaining shares as a bytearray of length group_count. Each byte represents share remaining for group of that group_index. 0x10 (16) was chosen as the default value because it's the max share count for a group. """ _require_progress() remaining = common.get(_NAMESPACE, _REMAINING) group_count = get_slip39_group_count() if not group_count: raise RuntimeError if remaining is None: remaining = bytearray([slip39.MAX_SHARE_COUNT] * group_count) remaining = bytearray(remaining) remaining[group_index] = shares_remaining common.set(_NAMESPACE, _REMAINING, remaining)
def store_resident_credential(cred: Fido2Credential) -> bool: slot = None for i in range(_MAX_RESIDENT_CREDENTIALS): stored_cred_data = common.get(common.APP_WEBAUTHN, i + _RESIDENT_CREDENTIAL_START_KEY) if stored_cred_data is None: if slot is None: slot = i continue stored_rp_id_hash = stored_cred_data[:32] stored_cred_id = stored_cred_data[32:] if cred.rp_id_hash != stored_rp_id_hash: # Stored credential is not for this RP ID. continue stored_cred = Fido2Credential.from_cred_id(stored_cred_id, stored_rp_id_hash) if stored_cred is None: # Stored credential is not for this RP ID. continue # If a credential for the same RP ID and user ID already exists, then overwrite it. if stored_cred.user_id == cred.user_id: slot = i break if slot is None: return False common.set( common.APP_WEBAUTHN, slot + _RESIDENT_CREDENTIAL_START_KEY, cred.rp_id_hash + cred.id, ) return True
def get_label() -> Optional[str]: label = common.get(_NAMESPACE, _LABEL, True) # public if label is None: return None return label.decode()
def get_rotation() -> int: rotation = common.get(_NAMESPACE, _ROTATION, True) # public if not rotation: return 0 return int.from_bytes(rotation, "big")
def get_device_id() -> str: dev_id = common.get(_NAMESPACE, _DEVICE_ID, True) # public if not dev_id: dev_id = _new_device_id().encode() common.set(_NAMESPACE, _DEVICE_ID, dev_id, True) # public return dev_id.decode()
def get_version() -> Optional[bytes]: return common.get(_NAMESPACE, _VERSION)
def is_version_stored() -> bool: return bool(common.get(_NAMESPACE, _VERSION))
def get_autolock_delay_ms() -> int: b = common.get(_NAMESPACE, _AUTOLOCK_DELAY_MS) if b is None: return 10 * 60 * 1000 else: return int.from_bytes(b, "big")
def get_mnemonic_secret() -> Optional[bytes]: return common.get(_NAMESPACE, _MNEMONIC_SECRET)
def get_homescreen() -> Optional[bytes]: return common.get(_NAMESPACE, _HOMESCREEN, True) # public
def get(index: int) -> Optional[str]: m = common.get(common.APP_RECOVERY_SHARES, index) if m: return m.decode() return None
def get_flags() -> int: b = common.get(_NAMESPACE, _FLAGS) if b is None: return 0 else: return int.from_bytes(b, "big")
def get(index: int, group_index: int) -> Optional[str]: m = common.get(common.APP_RECOVERY_SHARES, index + group_index * slip39.MAX_SHARE_COUNT) if m: return m.decode() return None