def _pythonify_key(self, key: SecKeyRef): assert isinstance( key, SecKeyRef ), f'Expected key to be of type SecKeyRef, but got: {type(key)}' error, der_key = SecItemExport( key, kSecFormatOpenSSL, # outputFormat - OpenSSL == DER 0, # flags None, # keyParams None, # exportedData ) if error or not der_key: raise KeyringError(f'Error calling SecItemExport: {error}') if self.key_class_type in [ OSXKeyChainKeyClassType.Private.value, OSXKeyChainKeyClassType.Symmetric.value, ]: return serialization.load_der_private_key( der_key, password=None, backend=default_backend() ) return serialization.load_der_public_key(der_key, default_backend())
def get_password(self, service: Optional[str], username: Optional[str]): """ Returns all keyring / enclave keys matching kSecAttrLabel = service and kSecAttrApplicationTag = username, python-wrapped. If a single key is matched, the result is a list of size 1. :param service: If set, will lookup the key with kSecAttrLabel = service :param username: If set, will lookup the key with kSecAttrApplicationTag = username :return: A python-wrapped private / public key of the key. """ query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, None, None) CFDictionarySetValue(query, kSecClass, kSecClassKey) CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll) CFDictionarySetValue(query, kSecReturnRef, 1) if service: CFDictionarySetValue(query, kSecAttrLabel, service) if username: CFDictionarySetValue( query, kSecAttrApplicationTag, username.encode('utf-8') ) if self.access_group: CFDictionarySetValue(query, kSecAttrAccessGroup, self.access_group) error, res = SecItemCopyMatching(query, None) if error: raise KeyringError(f'Error calling SecItemCopyMatching: {error}') return [self._pythonify_key(key) for key in res]
def _get_access_control(self) -> Optional[SecAccessControlRef]: # Do note: Using access control, or the secure enclave, requires the executing binary (i.e. python interpreter) # to be code signed with specific apple entitlements. if not self.use_secure_enclave: return None access_control, error = SecAccessControlCreateWithFlags( kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, kSecAccessControlPrivateKeyUsage, None, ) if error or not access_control: raise KeyringError(f'Failed in SecAccessControlCreateWithFlags: {error}') return access_control
def _get_sec_key_from_password(self, password: Union[str, bytes]): if os.path.isfile(password): with open(password, 'rb') as f: password = f.read() key_params = SecItemImportExportKeyParameters( keyAttributes=(kSecAttrIsExtractable,), passphrase=self.key_password ) error, input_format, item_type, out_items = SecItemImport( password, None, # fileNameOrExtension None, # inputFormat None, # itemType 0, # flags key_params, # keyParameters None, # keychain None, # outItems ) if error or not out_items: raise KeyringError(f'Error calling SecItemImport: {error}') return out_items[-1]
def delete_password(self, servicename, username): raise KeyringError()
def set_password(self, servicename, username, password): raise KeyringError()