Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/net/crypto.py : 34%
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-2019 Antoine Martin <antoine@xpra.org>
3# Copyright (C) 2008, 2009, 2010 Nathaniel Smith <njs@pobox.com>
4# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
5# later version. See the file COPYING for details.
7import os
8from struct import pack
10from xpra.util import envint, envbool
11from xpra.log import Logger
12from xpra.os_util import hexstr, get_hex_uuid
13from xpra.net.digest import get_salt
15log = Logger("network", "crypto")
17ENABLE_CRYPTO = envbool("XPRA_ENABLE_CRYPTO", True)
18ENCRYPT_FIRST_PACKET = envbool("XPRA_ENCRYPT_FIRST_PACKET", False)
20DEFAULT_IV = os.environ.get("XPRA_CRYPTO_DEFAULT_IV", "0000000000000000")
21DEFAULT_SALT = os.environ.get("XPRA_CRYPTO_DEFAULT_SALT", "0000000000000000")
22DEFAULT_ITERATIONS = envint("XPRA_CRYPTO_DEFAULT_ITERATIONS", 1000)
23DEFAULT_BLOCKSIZE = envint("XPRA_CRYPTO_BLOCKSIZE", 32)
25#other option "PKCS#7", "legacy"
26PADDING_LEGACY = "legacy"
27PADDING_PKCS7 = "PKCS#7"
28ALL_PADDING_OPTIONS = (PADDING_LEGACY, PADDING_PKCS7)
29INITIAL_PADDING = os.environ.get("XPRA_CRYPTO_INITIAL_PADDING", PADDING_LEGACY)
30DEFAULT_PADDING = PADDING_LEGACY
31PREFERRED_PADDING = os.environ.get("XPRA_CRYPTO_PREFERRED_PADDING", PADDING_PKCS7)
32assert PREFERRED_PADDING in ALL_PADDING_OPTIONS, "invalid preferred padding: %s" % PREFERRED_PADDING
33assert INITIAL_PADDING in ALL_PADDING_OPTIONS, "invalid padding: %s" % INITIAL_PADDING
34#make sure the preferred one is first in the list:
35def get_padding_options():
36 options = [PREFERRED_PADDING]
37 for x in ALL_PADDING_OPTIONS:
38 if x not in options:
39 options.append(x)
40 return options
41PADDING_OPTIONS = get_padding_options()
44ENCRYPTION_CIPHERS = []
45backend = False
46def crypto_backend_init():
47 global backend, ENCRYPTION_CIPHERS
48 log("crypto_backend_init() backend=%s", backend)
49 if backend is not False:
50 return
51 try:
52 from xpra.net import pycryptography_backend
53 #validate it:
54 validate_backend(pycryptography_backend)
55 ENCRYPTION_CIPHERS[:] = pycryptography_backend.ENCRYPTION_CIPHERS[:]
56 backend = pycryptography_backend
57 return
58 except ImportError:
59 log("crypto backend init failure", exc_info=True)
60 log.error("Error: cannot import python-cryptography")
61 except Exception:
62 log.error("Error: cannot initialize python-cryptography", exc_info=True)
63 backend = None
65def validate_backend(try_backend):
66 try_backend.init()
67 message = b"some message1234"
68 password = "this is our secret"
69 key_salt = DEFAULT_SALT
70 iterations = DEFAULT_ITERATIONS
71 block_size = DEFAULT_BLOCKSIZE
72 key = try_backend.get_key(password, key_salt, block_size, iterations)
73 log("validate_backend(%s) key=%s", try_backend, hexstr(key))
74 assert key is not None, "backend %s failed to generate a key" % try_backend
75 enc = try_backend.get_encryptor(key, DEFAULT_IV)
76 log("validate_backend(%s) encryptor=%s", try_backend, enc)
77 assert enc is not None, "backend %s failed to generate an encryptor" % enc
78 dec = try_backend.get_decryptor(key, DEFAULT_IV)
79 log("validate_backend(%s) decryptor=%s", try_backend, dec)
80 assert dec is not None, "backend %s failed to generate a decryptor" % enc
81 ev = enc.encrypt(message)
82 evs = hexstr(ev)
83 log("validate_backend(%s) encrypted(%s)=%s", try_backend, message, evs)
84 dv = dec.decrypt(ev)
85 log("validate_backend(%s) decrypted(%s)=%s", try_backend, evs, dv)
86 assert dv==message
87 log("validate_backend(%s) passed", try_backend)
90def pad(padding, size):
91 if padding==PADDING_LEGACY:
92 return b" "*size
93 if padding==PADDING_PKCS7:
94 return pack("B", size)*size
95 raise Exception("invalid padding: %s" % padding)
97def choose_padding(options):
98 for x in options:
99 if x in PADDING_OPTIONS:
100 return x
101 raise Exception("cannot find a valid padding in %s" % str(options))
104def get_iv():
105 IV = None
106 #IV = "0000000000000000"
107 return IV or get_hex_uuid()[:16]
109def get_iterations() -> int:
110 return DEFAULT_ITERATIONS
113def new_cipher_caps(proto, cipher, encryption_key, padding_options) -> dict:
114 assert backend
115 iv = get_iv()
116 key_salt = get_salt()
117 iterations = get_iterations()
118 padding = choose_padding(padding_options)
119 proto.set_cipher_in(cipher, iv, encryption_key, key_salt, iterations, padding)
120 return {
121 "cipher" : cipher,
122 "cipher.iv" : iv,
123 "cipher.key_salt" : key_salt,
124 "cipher.key_stretch_iterations": iterations,
125 "cipher.padding" : padding,
126 "cipher.padding.options" : PADDING_OPTIONS,
127 }
129def get_crypto_caps() -> dict:
130 if not backend:
131 return {}
132 caps = {
133 "padding" : {"options" : PADDING_OPTIONS},
134 }
135 caps.update(backend.get_info())
136 return caps
139def get_encryptor(ciphername, iv, password, key_salt, iterations):
140 log("get_encryptor(%s, %s, %s, %s, %s)", ciphername, iv, password, hexstr(key_salt), iterations)
141 if not ciphername:
142 return None, 0
143 assert iterations>=100
144 assert ciphername=="AES"
145 assert password and iv
146 block_size = DEFAULT_BLOCKSIZE
147 key = backend.get_key(password, key_salt, block_size, iterations)
148 return backend.get_encryptor(key, iv), block_size
150def get_decryptor(ciphername, iv, password, key_salt, iterations):
151 log("get_decryptor(%s, %s, %s, %s, %s)", ciphername, iv, password, hexstr(key_salt), iterations)
152 if not ciphername:
153 return None, 0
154 assert iterations>=100
155 assert ciphername=="AES"
156 assert password and iv
157 block_size = DEFAULT_BLOCKSIZE
158 key = backend.get_key(password, key_salt, block_size, iterations)
159 return backend.get_decryptor(key, iv), block_size
162def main():
163 from xpra.util import print_nested_dict
164 crypto_backend_init()
165 from xpra.platform import program_context
166 import sys
167 if "-v" in sys.argv or "--verbose" in sys.argv:
168 log.enable_debug()
169 with program_context("Encryption Properties"):
170 print_nested_dict(get_crypto_caps())
172if __name__ == "__main__":
173 main()