Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/client/auth/u2f_handler.py : 28%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of Xpra.
2# Copyright (C) 2019 Antoine Martin <antoine@xpra.org>
3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
4# later version. See the file COPYING for details.
6import os
7import logging
8import binascii
10from xpra.os_util import bytestostr, load_binary_file, osexpand
11from xpra.log import Logger, is_debug_enabled
13log = Logger("auth")
16class Handler:
18 def __init__(self, client, **_kwargs):
19 self.client = client
21 def __repr__(self):
22 return "u2f"
24 def get_digest(self) -> str:
25 return "u2f"
27 def handle(self, packet) -> bool:
28 digest = bytestostr(packet[3])
29 if not digest.startswith("u2f:"):
30 log("%s is not a u2f challenge", digest)
31 return False
32 try:
33 from pyu2f import model #@UnresolvedImport
34 from pyu2f.u2f import GetLocalU2FInterface #@UnresolvedImport
35 except ImportError as e:
36 log.warn("Warning: cannot use u2f authentication handler")
37 log.warn(" %s", e)
38 return False
39 if not is_debug_enabled("auth"):
40 logging.getLogger("pyu2f.hardware").setLevel(logging.INFO)
41 logging.getLogger("pyu2f.hidtransport").setLevel(logging.INFO)
42 dev = GetLocalU2FInterface()
43 APP_ID = os.environ.get("XPRA_U2F_APP_ID", "Xpra")
44 key_handle = self.get_key_handle()
45 if not key_handle:
46 return False
47 key = model.RegisteredKey(key_handle)
48 #use server salt as challenge directly
49 challenge = packet[1]
50 log.info("activate your U2F device for authentication")
51 response = dev.Authenticate(APP_ID, challenge, [key])
52 sig = response.signature_data
53 client_data = response.client_data
54 log("process_challenge_u2f client data=%s, signature=%s", client_data, binascii.hexlify(sig))
55 self.client.do_send_challenge_reply(bytes(sig), client_data.origin)
56 return True
58 def get_key_handle(self) -> bytes:
59 key_handle_str = os.environ.get("XPRA_U2F_KEY_HANDLE")
60 log("process_challenge_u2f XPRA_U2F_KEY_HANDLE=%s", key_handle_str)
61 if not key_handle_str:
62 #try to load the key handle from the user conf dir(s):
63 from xpra.platform.paths import get_user_conf_dirs
64 info = self.client._protocol.get_info(False)
65 key_handle_filenames = []
66 for hostinfo in ("-%s" % info.get("host", ""), ""):
67 for d in get_user_conf_dirs():
68 key_handle_filenames.append(os.path.join(d, "u2f-keyhandle%s.hex" % hostinfo))
69 for filename in key_handle_filenames:
70 p = osexpand(filename)
71 key_handle_str = load_binary_file(p)
72 log("key_handle_str(%s)=%s", p, key_handle_str)
73 if key_handle_str:
74 key_handle_str = key_handle_str.rstrip(b" \n\r")
75 break
76 if not key_handle_str:
77 log.warn("Warning: no U2F key handle found")
78 return None
79 log("process_challenge_u2f key_handle=%s", key_handle_str)
80 return binascii.unhexlify(key_handle_str)