xpra icon
Bug tracker and wiki

Ticket #1789: u2f_auth.py

File u2f_auth.py, 3.1 KB (added by Antoine Martin, 20 months ago)

example of all in one registration + authentication using pyu2f

Line 
1# This file is part of Xpra.
2# Copyright (C) 2018 Antoine Martin <antoine@devloop.org.uk>
3
4import struct
5import binascii
6from hashlib import sha256
7
8from pyu2f import model
9from pyu2f.u2f import GetLocalU2FInterface
10
11iface = GetLocalU2FInterface()
12
13APP_ID = u"Xpra"
14
15def h(s):
16    try:
17        s = s.encode()
18    except:
19        pass
20    return binascii.hexlify(s)
21
22if False:
23    #register app:
24    registered_keys = []
25    challenge= b'01234567890123456789012345678901'
26    rr = iface.Register(APP_ID, challenge, registered_keys)
27    b = rr.registration_data
28    assert b[0]==5
29    pubkey = bytes(b[1:66])
30    khl = b[66]
31    key_handle = bytes(b[67:67 + khl])
32    print("key_handle=%s" % h(key_handle))
33    print("public key=%s" % h(pubkey))
34else:
35    #restore from a previous run:
36    key_handle = binascii.unhexlify("584f1a158d83d965713467a37f3b33b6aca6f4feb9e18899432d4be68fcd46773d2a6fba6d3b8cfe49838abcf1b7ae28adad6fbead538f018519bee023faa2d3")
37    pubkey = binascii.unhexlify("04df0a3c35cf3f4c68120e3d90a1106330b89ea80c8cb37cce25ea563692db0eec9b95792966efa699c40d9cab6017197a59288440a0ab80d818c0db2b110a29c7")
38
39key = model.RegisteredKey(key_handle)
40challenge= b'01234567890123456789012345678901'
41response = iface.Authenticate(APP_ID, challenge, [key])
42sig = response.signature_data
43client_data = response.client_data
44print("signature_data=%s (%s len=%i)" % (h(sig), type(sig), len(sig)))
45print("client_data=%s (%s)" % (client_data, type(client_data),))
46
47touch = bool(sig[0])
48counter = struct.unpack('>I', sig[1:5])[0]
49print("touch=%s, counter=%i" % (touch, counter))
50assert sig[5] == 0x30, "expected sequence got %#x" % sig[5]
51
52def verify(sig):
53    from cryptography.hazmat.primitives import hashes
54    from cryptography.hazmat.primitives.asymmetric import ec
55    from cryptography.hazmat.primitives.serialization import load_der_public_key
56    from cryptography.hazmat.backends import default_backend
57    PUB_KEY_DER_PREFIX = binascii.a2b_hex('3059301306072a8648ce3d020106082a8648ce3d030107034200')
58    der_pubkey = PUB_KEY_DER_PREFIX+pubkey
59    key = load_der_public_key(der_pubkey, default_backend())
60    verifier = key.verifier(sig, ec.ECDSA(hashes.SHA256()))
61    app_param = sha256(APP_ID.encode('utf8')).digest()
62    import json
63    import base64
64    print("challenge=%s" % repr(client_data.raw_server_challenge))
65    print("challenge=%s" % h(client_data.raw_server_challenge))
66    server_challenge_b64 = base64.urlsafe_b64encode(client_data.raw_server_challenge).decode()
67    server_challenge_b64 = server_challenge_b64.rstrip('=')
68    cdata = {
69        "challenge" : server_challenge_b64,
70        "origin"    : "desktop",
71        "typ"       : "navigator.id.getAssertion",
72        }
73    client_param1 = sha256(json.dumps(cdata, sort_keys=True).encode('utf8')).digest()
74    client_param2 = sha256(client_data.GetJson().encode('utf8')).digest()
75    assert client_param1==client_param2
76    verifier.update(app_param+
77                    struct.pack('>B', True) +   #six.int2byte(self.user_presence) +
78                    struct.pack('>I', counter) +
79                    client_param1,
80                    )
81    verifier.verify()
82
83verify(bytes(sig[5:]))
84print("SUCCESS")