Skip to content

Commit

Permalink
#1836 split crypto from digest bits
Browse files Browse the repository at this point in the history
git-svn-id: https://xpra.org/svn/Xpra/trunk@19276 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed May 11, 2018
1 parent 81b128d commit 073c538
Show file tree
Hide file tree
Showing 18 changed files with 131 additions and 114 deletions.
2 changes: 1 addition & 1 deletion src/unittests/unit/server/auth_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import uuid
import hmac
from xpra.os_util import strtobytes, bytestostr, monotonic_time, WIN32, OSX, POSIX
from xpra.net.crypto import get_digests, get_digest_module, gendigest
from xpra.net.digest import get_digests, get_digest_module, gendigest


def temp_filename(prefix=""):
Expand Down
3 changes: 2 additions & 1 deletion src/xpra/client/client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
from xpra.net import compression
from xpra.net.protocol import Protocol, sanity_checks
from xpra.net.net_util import get_network_caps
from xpra.net.crypto import crypto_backend_init, get_iterations, get_iv, get_salt, choose_padding, gendigest, \
from xpra.net.digest import get_salt, gendigest
from xpra.net.crypto import crypto_backend_init, get_iterations, get_iv, choose_padding, \
ENCRYPTION_CIPHERS, ENCRYPT_FIRST_PACKET, DEFAULT_IV, DEFAULT_SALT, DEFAULT_ITERATIONS, INITIAL_PADDING, DEFAULT_PADDING, ALL_PADDING_OPTIONS, PADDING_OPTIONS
from xpra.version_util import get_version_info, XPRA_VERSION
from xpra.platform.info import get_name
Expand Down
99 changes: 4 additions & 95 deletions src/xpra/net/crypto.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
# This file is part of Xpra.
# Copyright (C) 2011-2017 Antoine Martin <[email protected]>
# Copyright (C) 2011-2018 Antoine Martin <[email protected]>
# Copyright (C) 2008, 2009, 2010 Nathaniel Smith <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import os
import hmac
import hashlib
from struct import pack

from xpra.util import envint, envbool, csv, xor
from xpra.util import envint, envbool, xor
from xpra.log import Logger
from xpra.os_util import strtobytes, memoryview_to_bytes, hexstr
from xpra.os_util import hexstr, get_hex_uuid
from xpra.net.digest import get_salt

log = Logger("network", "crypto")

Expand Down Expand Up @@ -92,80 +91,6 @@ def validate_backend(try_backend):
log("validate_backend(%s) passed", try_backend)


def get_digests():
digests = ["hmac", "xor"] + ["hmac+%s" % x for x in tuple(reversed(sorted(hashlib.algorithms_available)))]
try:
from xpra.net import d3des
assert d3des
digests.append("des")
except:
pass
return digests

def get_digest_module(digest):
log("get_digest_module(%s)", digest)
if not digest or not digest.startswith("hmac"):
return None
try:
digest_module = digest.split("+")[1] #ie: "hmac+sha512" -> "sha512"
except:
digest_module = "md5"
try:
return getattr(hashlib, digest_module)
except AttributeError:
return None

def choose_digest(options):
assert len(options)>0, "no digest options"
log("choose_digest(%s)", options)
#prefer stronger hashes:
for h in ("sha512", "sha384", "sha256", "sha224", "sha1", "md5"):
hname = "hmac+%s" % h
if hname in options:
return hname
#legacy name for "hmac+md5":
if "hmac" in options:
return "hmac"
if "xor" in options:
return "xor"
if "des" in options:
return "des"
raise Exception("no known digest options found in '%s'" % csv(options))

def gendigest(digest, password, salt):
assert digest and password and salt
salt = memoryview_to_bytes(salt)
password = strtobytes(password)
if digest=="des":
from xpra.net.d3des import generate_response
password = password.ljust(8, b"\x00")[:8]
salt = salt.ljust(16, b"\x00")[:16]
v = generate_response(password, salt)
return hexstr(v)
elif digest in ("xor", "kerberos", "gss"):
#kerberos and gss use xor because we need to use the actual token
#at the other end
salt = salt.ljust(len(password), b"\x00")[:len(password)]
v = xor(password, salt)
return memoryview_to_bytes(v)
digestmod = get_digest_module(digest)
if not digestmod:
log("invalid digest module '%s': %s", digest)
return None
#warn_server_and_exit(EXIT_UNSUPPORTED, "server requested digest '%s' but it is not supported" % digest, "invalid digest")
v = hmac.HMAC(strtobytes(password), strtobytes(salt), digestmod=digestmod).hexdigest()
return v

def verify_digest(digest, password, salt, challenge_response):
if not password or not salt or not challenge_response:
return False
verify = gendigest(digest, password, salt)
if not hmac.compare_digest(verify, challenge_response):
log("expected '%s' but got '%s'", verify, challenge_response)
return False
return True


def pad(padding, size):
if padding==PADDING_LEGACY:
return b" "*size
Expand All @@ -181,27 +106,11 @@ def choose_padding(options):
raise Exception("cannot find a valid padding in %s" % str(options))


def get_hex_uuid():
from xpra.os_util import get_hex_uuid as ghu
return ghu()

def get_iv():
IV = None
#IV = "0000000000000000"
return IV or get_hex_uuid()[:16]

def get_rand_str(l):
#too short: we would not feed enough random data to HMAC
assert l>=32, "salt is too short: only %i bytes" % l
#too long: limit the amount of random data we request from the system
assert l<1024, "salt is too long: %i bytes" % l
#all server versions support a client salt,
#they also tell us which digest to use:
return os.urandom(l)

def get_salt(l=64):
return get_rand_str(l)

def get_iterations():
return DEFAULT_ITERATIONS

Expand Down
105 changes: 105 additions & 0 deletions src/xpra/net/digest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# This file is part of Xpra.
# Copyright (C) 2011-2018 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import os
import hmac
import hashlib

from xpra.util import csv, xor
from xpra.log import Logger
from xpra.os_util import strtobytes, memoryview_to_bytes, hexstr

log = Logger("network", "crypto")


try:
from xpra.codecs.xor.cyxor import xor_str #@UnresolvedImport
xor = xor_str
except Exception:
log("no accelerated xor", exc_info=True)


def get_digests():
digests = ["hmac", "xor"] + ["hmac+%s" % x for x in tuple(reversed(sorted(hashlib.algorithms_available)))]
try:
from xpra.net import d3des
assert d3des
digests.append("des")
except:
pass
return digests

def get_digest_module(digest):
log("get_digest_module(%s)", digest)
if not digest or not digest.startswith("hmac"):
return None
try:
digest_module = digest.split("+")[1] #ie: "hmac+sha512" -> "sha512"
except:
digest_module = "md5"
try:
return getattr(hashlib, digest_module)
except AttributeError:
return None

def choose_digest(options):
assert len(options)>0, "no digest options"
log("choose_digest(%s)", options)
#prefer stronger hashes:
for h in ("sha512", "sha384", "sha256", "sha224", "sha1", "md5"):
hname = "hmac+%s" % h
if hname in options:
return hname
#legacy name for "hmac+md5":
if "hmac" in options:
return "hmac"
if "xor" in options:
return "xor"
if "des" in options:
return "des"
raise Exception("no known digest options found in '%s'" % csv(options))

def gendigest(digest, password, salt):
assert digest and password and salt
salt = memoryview_to_bytes(salt)
password = strtobytes(password)
if digest=="des":
from xpra.net.d3des import generate_response
password = password.ljust(8, b"\x00")[:8]
salt = salt.ljust(16, b"\x00")[:16]
v = generate_response(password, salt)
return hexstr(v)
elif digest in ("xor", "kerberos", "gss"):
#kerberos and gss use xor because we need to use the actual token
#at the other end
salt = salt.ljust(len(password), b"\x00")[:len(password)]
v = xor(password, salt)
return memoryview_to_bytes(v)
digestmod = get_digest_module(digest)
if not digestmod:
log("invalid digest module '%s': %s", digest)
return None
#warn_server_and_exit(EXIT_UNSUPPORTED, "server requested digest '%s' but it is not supported" % digest, "invalid digest")
v = hmac.HMAC(strtobytes(password), strtobytes(salt), digestmod=digestmod).hexdigest()
return v

def verify_digest(digest, password, salt, challenge_response):
if not password or not salt or not challenge_response:
return False
verify = gendigest(digest, password, salt)
if not hmac.compare_digest(verify, challenge_response):
log("expected '%s' but got '%s'", verify, challenge_response)
return False
return True


def get_salt(l=64):
#too short: we would not feed enough random data to HMAC
assert l>=32, "salt is too short: only %i bytes" % l
#too long: limit the amount of random data we request from the system
assert l<1024, "salt is too long: %i bytes" % l
#all server versions support a client salt,
#they also tell us which digest to use:
return os.urandom(l)
2 changes: 1 addition & 1 deletion src/xpra/net/mmap_pipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def validate_size(size):
if WIN32:
validate_size(mmap_size)
if not filename:
from xpra.net.crypto import get_hex_uuid
from xpra.os_util import get_hex_uuid
filename = "xpra-%s" % get_hex_uuid()
mmap_filename = filename
mmap_area = mmap.mmap(0, mmap_size, filename)
Expand Down
3 changes: 2 additions & 1 deletion src/xpra/net/net_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,8 @@ def get_network_caps():
from xpra.platform.features import MMAP_SUPPORTED
except:
MMAP_SUPPORTED = False
from xpra.net.crypto import get_digests, get_crypto_caps
from xpra.net.digest import get_digests
from xpra.net.crypto import get_crypto_caps
from xpra.net.compression import get_enabled_compressors, get_compression_caps
from xpra.net.packet_encoding import get_enabled_encoders, get_packet_encoding_caps
digests = get_digests()
Expand Down
4 changes: 2 additions & 2 deletions src/xpra/server/auth/file_auth.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# This file is part of Xpra.
# Copyright (C) 2013-2017 Antoine Martin <[email protected]>
# Copyright (C) 2013-2018 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

#authentication from a file containing just the password

from xpra.net.crypto import verify_digest
from xpra.net.digest import verify_digest
from xpra.server.auth.file_auth_base import FileAuthenticatorBase, init, log
from xpra.util import obsc

Expand Down
4 changes: 2 additions & 2 deletions src/xpra/server/auth/file_auth_base.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# This file is part of Xpra.
# Copyright (C) 2013-2017 Antoine Martin <[email protected]>
# Copyright (C) 2013-2018 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

#authentication from a file containing just the password

import os.path

from xpra.net.crypto import get_salt, choose_digest
from xpra.net.digest import get_salt, choose_digest
from xpra.os_util import strtobytes
from xpra.server.auth.sys_auth_base import SysAuthenticator
from xpra.log import Logger
Expand Down
2 changes: 1 addition & 1 deletion src/xpra/server/auth/gss_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import sys

from xpra.server.auth.sys_auth_base import SysAuthenticatorBase, init, log, parse_uid, parse_gid
from xpra.net.crypto import get_salt, get_digests, gendigest
from xpra.net.digest import get_salt, get_digests, gendigest
from xpra.util import xor
assert init and log #tests will disable logging from here

Expand Down
2 changes: 1 addition & 1 deletion src/xpra/server/auth/kerberos_password_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import sys

from xpra.server.auth.sys_auth_base import SysAuthenticatorBase, init, log, parse_uid, parse_gid
from xpra.net.crypto import get_salt, get_digests, gendigest
from xpra.net.digest import get_salt, get_digests, gendigest
from xpra.util import xor
from xpra.os_util import WIN32
assert init and log #tests will disable logging from here
Expand Down
2 changes: 1 addition & 1 deletion src/xpra/server/auth/kerberos_token_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import sys

from xpra.server.auth.sys_auth_base import SysAuthenticatorBase, init, log, parse_uid, parse_gid
from xpra.net.crypto import get_salt, get_digests, gendigest
from xpra.net.digest import get_salt, get_digests, gendigest
from xpra.util import xor
from xpra.os_util import WIN32
assert init and log #tests will disable logging from here
Expand Down
2 changes: 1 addition & 1 deletion src/xpra/server/auth/ldap3_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def check(self, password):

def main(argv):
from xpra.util import xor
from xpra.net.crypto import get_salt, get_digests, gendigest
from xpra.net.digest import get_salt, get_digests, gendigest
from xpra.platform import program_context
with program_context("LDAP3-Password-Auth", "LDAP3-Password-Authentication"):
for x in list(argv):
Expand Down
2 changes: 1 addition & 1 deletion src/xpra/server/auth/ldap_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def emsg(e):

def main(argv):
from xpra.util import xor
from xpra.net.crypto import get_salt, get_digests, gendigest
from xpra.net.digest import get_salt, get_digests, gendigest
from xpra.platform import program_context
with program_context("LDAP-Password-Auth", "LDAP-Password-Authentication"):
for x in list(argv):
Expand Down
2 changes: 1 addition & 1 deletion src/xpra/server/auth/multifile_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from xpra.server.auth.file_auth_base import log, FileAuthenticatorBase, init as file_init
from xpra.os_util import strtobytes, hexstr
from xpra.util import parse_simple_dict
from xpra.net.crypto import verify_digest
from xpra.net.digest import verify_digest


def init(opts):
Expand Down
4 changes: 2 additions & 2 deletions src/xpra/server/auth/reject_auth.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# This file is part of Xpra.
# Copyright (C) 2014-2017 Antoine Martin <[email protected]>
# Copyright (C) 2014-2018 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.


from xpra.net.crypto import get_salt, choose_digest
from xpra.net.digest import get_salt, choose_digest

def init(opts):
pass
Expand Down
2 changes: 1 addition & 1 deletion src/xpra/server/auth/sys_auth_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from xpra.platform.dotxpra import DotXpra
from xpra.util import envint
from collections import deque
from xpra.net.crypto import get_salt, choose_digest, verify_digest, gendigest
from xpra.net.digest import get_salt, choose_digest, verify_digest, gendigest
from xpra.os_util import hexstr, POSIX
from xpra.log import Logger
log = Logger("auth")
Expand Down
2 changes: 1 addition & 1 deletion src/xpra/server/proxy/proxy_instance_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,7 @@ def process_server_packet(self, proto, packet):
if not password:
self.stop("authentication requested by the server, but no password available for this session")
return
from xpra.net.crypto import get_salt, gendigest
from xpra.net.digest import get_salt, gendigest
#client may have already responded to the challenge,
#so we have to handle authentication from this end
server_salt = bytestostr(packet[1])
Expand Down
3 changes: 2 additions & 1 deletion src/xpra/server/server_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
from xpra.os_util import load_binary_file, get_machine_id, get_user_uuid, platform_name, strtobytes, bytestostr, get_hex_uuid, monotonic_time, get_peercred, hexstr, SIGNAMES, WIN32, POSIX, PYTHON3, BITS
from xpra.version_util import version_compat_check, get_version_info_full, get_platform_info, get_host_info
from xpra.net.protocol import Protocol, sanity_checks
from xpra.net.crypto import crypto_backend_init, new_cipher_caps, get_salt, choose_digest, \
from xpra.net.digest import get_salt, choose_digest
from xpra.net.crypto import crypto_backend_init, new_cipher_caps, \
ENCRYPTION_CIPHERS, ENCRYPT_FIRST_PACKET, DEFAULT_IV, DEFAULT_SALT, DEFAULT_ITERATIONS, INITIAL_PADDING, DEFAULT_PADDING, ALL_PADDING_OPTIONS
from xpra.server.background_worker import stop_worker, get_worker
from xpra.make_thread import start_thread
Expand Down

0 comments on commit 073c538

Please sign in to comment.