Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/net/digest.py : 74%
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) 2011-2018 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 hmac
8import hashlib
10from xpra.util import csv
11from xpra.log import Logger
12from xpra.os_util import strtobytes, memoryview_to_bytes, hexstr
14log = Logger("network", "crypto")
16BLACKLISTED_HASHES = ("sha1", "md5")
19def get_digests():
20 digests = ["xor"]
21 digests += ["hmac+%s" % x for x in tuple(reversed(sorted(hashlib.algorithms_available)))
22 if not x.startswith("shake_") and x not in BLACKLISTED_HASHES
23 and getattr(hashlib, x, None) is not None]
24 try:
25 from xpra.net import d3des
26 assert d3des
27 digests.append("des")
28 except (ImportError, TypeError): # pragma: no cover
29 pass
30 return digests
32def get_digest_module(digest : str):
33 log("get_digest_module(%s)", digest)
34 if not digest or not digest.startswith("hmac"):
35 return None
36 try:
37 digest_module = digest.split("+")[1] #ie: "hmac+sha512" -> "sha512"
38 except IndexError:
39 return None
40 try:
41 return getattr(hashlib, digest_module)
42 except AttributeError as e:
43 log("no '%s' attribute in hashlib: %s", digest_module, e)
44 return None
46def choose_digest(options) -> str:
47 assert len(options)>0, "no digest options"
48 log("choose_digest(%s)", options)
49 #prefer stronger hashes:
50 for h in ("sha512", "sha384", "sha256", "sha224"):
51 hname = "hmac+%s" % h
52 if hname in options:
53 return hname
54 if "xor" in options:
55 return "xor"
56 if "des" in options:
57 return "des"
58 raise ValueError("no known digest options found in '%s'" % csv(options))
60def gendigest(digest, password, salt):
61 assert password and salt
62 salt = memoryview_to_bytes(salt)
63 password = strtobytes(password)
64 if digest=="des":
65 from xpra.net.d3des import generate_response
66 password = password.ljust(8, b"\x00")[:8]
67 salt = salt.ljust(16, b"\x00")[:16]
68 v = generate_response(password, salt)
69 return hexstr(v)
70 if digest in ("xor", "kerberos", "gss"):
71 #kerberos and gss use xor because we need to use the actual token
72 #at the other end
73 salt = salt.ljust(len(password), b"\x00")[:len(password)]
74 from xpra.buffers.cyxor import xor_str #@UnresolvedImport
75 v = xor_str(password, salt)
76 return memoryview_to_bytes(v)
77 digestmod = get_digest_module(digest)
78 if not digestmod:
79 log("invalid digest module '%s'", digest)
80 return None
81 #warn_server_and_exit(EXIT_UNSUPPORTED, "server requested digest '%s' but it is not supported" % digest, "invalid digest")
82 v = hmac.HMAC(password, salt, digestmod=digestmod).hexdigest()
83 return v
85def verify_digest(digest, password, salt, challenge_response):
86 if not password or not salt or not challenge_response:
87 return False
88 verify = gendigest(digest, password, salt)
89 if not hmac.compare_digest(verify, challenge_response):
90 log("expected '%s' but got '%s'", verify, challenge_response)
91 return False
92 return True
95def get_salt(l=64):
96 #too short: we would not feed enough random data to HMAC
97 assert l>=32, "salt is too short: only %i bytes" % l
98 #too long: limit the amount of random data we request from the system
99 assert l<1024, "salt is too long: %i bytes" % l
100 #all server versions support a client salt,
101 #they also tell us which digest to use:
102 return os.urandom(l)