1""" 2Functions dealing with encryption 3""" 4import hashlib 5import logging 6import os 7 8import salt.loader 9import salt.utils.files 10from salt.exceptions import SaltInvocationError 11 12log = logging.getLogger(__name__) 13 14 15try: 16 import M2Crypto # pylint: disable=unused-import 17 18 Random = None 19 HAS_M2CRYPTO = True 20except ImportError: 21 HAS_M2CRYPTO = False 22 23if not HAS_M2CRYPTO: 24 try: 25 from Cryptodome import Random 26 27 HAS_CRYPTODOME = True 28 except ImportError: 29 HAS_CRYPTODOME = False 30else: 31 HAS_CRYPTODOME = False 32 33if not HAS_M2CRYPTO and not HAS_CRYPTODOME: 34 try: 35 from Crypto import Random # nosec 36 37 HAS_CRYPTO = True 38 except ImportError: 39 HAS_CRYPTO = False 40else: 41 HAS_CRYPTO = False 42 43 44def decrypt( 45 data, rend, translate_newlines=False, renderers=None, opts=None, valid_rend=None 46): 47 """ 48 .. versionadded:: 2017.7.0 49 50 Decrypt a data structure using the specified renderer. Written originally 51 as a common codebase to handle decryption of encrypted elements within 52 Pillar data, but should be flexible enough for other uses as well. 53 54 Returns the decrypted result, but any decryption renderer should be 55 recursively decrypting mutable types in-place, so any data structure passed 56 should be automagically decrypted using this function. Immutable types 57 obviously won't, so it's a good idea to check if ``data`` is hashable in 58 the calling function, and replace the original value with the decrypted 59 result if that is not the case. For an example of this, see 60 salt.pillar.Pillar.decrypt_pillar(). 61 62 data 63 The data to be decrypted. This can be a string of ciphertext or a data 64 structure. If it is a data structure, the items in the data structure 65 will be recursively decrypted. 66 67 rend 68 The renderer used to decrypt 69 70 translate_newlines : False 71 If True, then the renderer will convert a literal backslash followed by 72 an 'n' into a newline before performing the decryption. 73 74 renderers 75 Optionally pass a loader instance containing loaded renderer functions. 76 If not passed, then the ``opts`` will be required and will be used to 77 invoke the loader to get the available renderers. Where possible, 78 renderers should be passed to avoid the overhead of loading them here. 79 80 opts 81 The master/minion configuration opts. Used only if renderers are not 82 passed. 83 84 valid_rend 85 A list containing valid renderers, used to restrict the renderers which 86 this function will be allowed to use. If not passed, no restriction 87 will be made. 88 """ 89 try: 90 if valid_rend and rend not in valid_rend: 91 raise SaltInvocationError( 92 "'{}' is not a valid decryption renderer. Valid choices are: {}".format( 93 rend, ", ".join(valid_rend) 94 ) 95 ) 96 except TypeError as exc: 97 # SaltInvocationError inherits TypeError, so check for it first and 98 # raise if needed. 99 if isinstance(exc, SaltInvocationError): 100 raise 101 # 'valid' argument is not iterable 102 log.error("Non-iterable value %s passed for valid_rend", valid_rend) 103 104 if renderers is None: 105 if opts is None: 106 raise TypeError("opts are required") 107 renderers = salt.loader.render(opts, {}) 108 109 rend_func = renderers.get(rend) 110 if rend_func is None: 111 raise SaltInvocationError( 112 "Decryption renderer '{}' is not available".format(rend) 113 ) 114 115 return rend_func(data, translate_newlines=translate_newlines) 116 117 118def reinit_crypto(): 119 """ 120 When a fork arises, pycrypto needs to reinit 121 From its doc:: 122 123 Caveat: For the random number generator to work correctly, 124 you must call Random.atfork() in both the parent and 125 child processes after using os.fork() 126 127 """ 128 if HAS_CRYPTODOME or HAS_CRYPTO: 129 Random.atfork() 130 131 132def pem_finger(path=None, key=None, sum_type="sha256"): 133 """ 134 Pass in either a raw pem string, or the path on disk to the location of a 135 pem file, and the type of cryptographic hash to use. The default is SHA256. 136 The fingerprint of the pem will be returned. 137 138 If neither a key nor a path are passed in, a blank string will be returned. 139 """ 140 if not key: 141 if not os.path.isfile(path): 142 return "" 143 144 with salt.utils.files.fopen(path, "rb") as fp_: 145 key = b"".join([x for x in fp_.readlines() if x.strip()][1:-1]) 146 147 pre = getattr(hashlib, sum_type)(key).hexdigest() 148 finger = "" 149 for ind, _ in enumerate(pre): 150 if ind % 2: 151 # Is odd 152 finger += "{}:".format(pre[ind]) 153 else: 154 finger += pre[ind] 155 return finger.rstrip(":") 156