diff --git a/tools/pico-fido-tool.py b/tools/pico-fido-tool.py index 4740eba..119803c 100644 --- a/tools/pico-fido-tool.py +++ b/tools/pico-fido-tool.py @@ -21,6 +21,7 @@ import sys import argparse +import platform try: from fido2.ctap2.config import Config @@ -44,6 +45,16 @@ except: from enum import IntEnum from binascii import hexlify +if (platform.system() == 'Windows'): + from secure_key import windows as skey +elif (platform.system() == 'Linux'): + from secure_key import linux as skey +elif (platform.system() == 'Darwin'): + from secure_key import macos as skey +else: + print('ERROR: platform not supported') + sys.exit(-1) + class VendorConfig(Config): class PARAM(IntEnum): @@ -63,7 +74,7 @@ class VendorConfig(Config): super().__init__(ctap, pin_uv_protocol, pin_uv_token) def _get_key_device(self): - return b"\x69"*32 + return skey.get_secure_key() def _get_shared_key(self): ret = self._call( diff --git a/tools/secure_key/macos.py b/tools/secure_key/macos.py new file mode 100644 index 0000000..cf365dc --- /dev/null +++ b/tools/secure_key/macos.py @@ -0,0 +1,45 @@ +import sys +import keyring + +DOMAIN = "PicoKeys.com" +USERNAME = "Pico-Fido" + +try: + import keyring + from keyrings.osx_keychain_keys.backend import OSXKeychainKeysBackend, OSXKeychainKeyType, OSXKeyChainKeyClassType +except: + print('ERROR: keyring module not found! Install keyring package.\nTry with `pip install keyrings.osx-keychain-keys`') + sys.exit(-1) + + +def get_backend(use_secure_enclave=False): + backend = OSXKeychainKeysBackend( + key_type=OSXKeychainKeyType.EC, # Key type, e.g. RSA, RC, DSA, ... + key_class_type=OSXKeyChainKeyClassType.Private, # Private key, Public key, Symmetric-key + key_size_in_bits=256, + is_permanent=True, # If set, saves the key in keychain; else, returns a transient key + use_secure_enclave=use_secure_enclave, # Saves the key in the T2 (TPM) chip, requires a code-signed interpreter + access_group=None, # Limits key management and retrieval to set group, requires a code-signed interpreter + is_extractable=True # If set, private key is extractable; else, it can't be retrieved, but only operated against + ) + return backend + +def generate_secure_key(use_secure_enclave=False): + backend = get_backend(use_secure_enclave) + backend.set_password(DOMAIN, USERNAME, password=None) + return backend.get_password(DOMAIN, USERNAME) + +def get_d(key): + return key.private_numbers().private_value.to_bytes(32, 'big') + +def get_secure_key(): + key = None + try: + backend = get_backend(False) + key = backend.get_password(DOMAIN, USERNAME)[0] + except keyring.errors.KeyringError: + try: + key = generate_secure_key(False)[0] # It should be True, but secure enclave causes python segfault + except keyring.errors.PasswordSetError: + key = generate_secure_key(False)[0] + return get_d(key)