From 6366f3ac0d46dba9ca84afe142e6de277e3eb38b Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Thu, 8 Aug 2019 16:26:54 +0200 Subject: [PATCH] core/storage: Implement storage of FIDO2 resident credentials. --- core/src/apps/common/storage/common.py | 1 + core/src/apps/common/storage/webauthn.py | 78 ++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 core/src/apps/common/storage/webauthn.py diff --git a/core/src/apps/common/storage/common.py b/core/src/apps/common/storage/common.py index f80963c11..a410a6c4a 100644 --- a/core/src/apps/common/storage/common.py +++ b/core/src/apps/common/storage/common.py @@ -9,6 +9,7 @@ if False: _APP_DEVICE = 0x01 _APP_RECOVERY = 0x02 _APP_RECOVERY_SHARES = 0x03 +_APP_FIDO2 = 0x04 # fmt: on _FALSE_BYTE = b"\x00" diff --git a/core/src/apps/common/storage/webauthn.py b/core/src/apps/common/storage/webauthn.py new file mode 100644 index 000000000..5c25f533f --- /dev/null +++ b/core/src/apps/common/storage/webauthn.py @@ -0,0 +1,78 @@ +from micropython import const + +from apps.common.storage import common +from apps.webauthn.credential import Credential, Fido2Credential + +if False: + from typing import List, Optional + +_RESIDENT_CREDENTIAL_START_KEY = const(1) +_MAX_RESIDENT_CREDENTIALS = const(16) + + +def get_resident_credentials(rp_id_hash: Optional[bytes]) -> List[Credential]: + creds = [] # type: List[Credential] + for i in range( + _RESIDENT_CREDENTIAL_START_KEY, + _RESIDENT_CREDENTIAL_START_KEY + _MAX_RESIDENT_CREDENTIALS, + ): + stored_cred_data = common._get(common._APP_FIDO2, i) + if stored_cred_data is None: + continue + + 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. + continue + + stored_cred = Fido2Credential.from_cred_id(stored_cred_id, stored_rp_id_hash) + if stored_cred is not None: + creds.append(stored_cred) + + return creds + + +def store_resident_credential(cred: Fido2Credential) -> bool: + slot = None + for i in range( + _RESIDENT_CREDENTIAL_START_KEY, + _RESIDENT_CREDENTIAL_START_KEY + _MAX_RESIDENT_CREDENTIALS, + ): + stored_cred_data = common._get(common._APP_FIDO2, i) + 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_FIDO2, slot, cred.rp_id_hash + cred.id) + return True + + +def erase_resident_credentials() -> None: + for i in range( + _RESIDENT_CREDENTIAL_START_KEY, + _RESIDENT_CREDENTIAL_START_KEY + _MAX_RESIDENT_CREDENTIALS, + ): + common._delete(common._APP_FIDO2, i)