Hide keyboard shortcuts

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. 

6 

7import os 

8from struct import pack 

9 

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 

14 

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

16 

17ENABLE_CRYPTO = envbool("XPRA_ENABLE_CRYPTO", True) 

18ENCRYPT_FIRST_PACKET = envbool("XPRA_ENCRYPT_FIRST_PACKET", False) 

19 

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) 

24 

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() 

42 

43 

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 

64 

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) 

88 

89 

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) 

96 

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)) 

102 

103 

104def get_iv(): 

105 IV = None 

106 #IV = "0000000000000000" 

107 return IV or get_hex_uuid()[:16] 

108 

109def get_iterations() -> int: 

110 return DEFAULT_ITERATIONS 

111 

112 

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 } 

128 

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 

137 

138 

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 

149 

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 

160 

161 

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()) 

171 

172if __name__ == "__main__": 

173 main()