comparison sat/memory/crypto.py @ 3040:fee60f17ebac

jp: jp asyncio port: /!\ this commit is huge. Jp is temporarily not working with `dbus` bridge /!\ This patch implements the port of jp to asyncio, so it is now correctly using the bridge asynchronously, and it can be used with bridges like `pb`. This also simplify the code, notably for things which were previously implemented with many callbacks (like pagination with RSM). During the process, some behaviours have been modified/fixed, in jp and backends, check diff for details.
author Goffi <goffi@goffi.org>
date Wed, 25 Sep 2019 08:56:41 +0200
parents ab2696e34d29
children 9d0df638c8b4
comparison
equal deleted inserted replaced
3039:a1bc34f90fa5 3040:fee60f17ebac
43 Based on http://stackoverflow.com/a/12525165 43 Based on http://stackoverflow.com/a/12525165
44 44
45 @param key (unicode): the encryption key 45 @param key (unicode): the encryption key
46 @param text (unicode): the text to encrypt 46 @param text (unicode): the text to encrypt
47 @param leave_empty (bool): if True, empty text will be returned "as is" 47 @param leave_empty (bool): if True, empty text will be returned "as is"
48 @return: Deferred: base-64 encoded str 48 @return (D(str)): base-64 encoded encrypted message
49 """ 49 """
50 if leave_empty and text == "": 50 if leave_empty and text == "":
51 return succeed(text) 51 return succeed(text)
52 iv = BlockCipher.getRandomKey() 52 iv = BlockCipher.getRandomKey()
53 key = key.encode("utf-8") 53 key = key.encode("utf-8")
57 else BlockCipher.pad(key) 57 else BlockCipher.pad(key)
58 ) 58 )
59 cipher = AES.new(key, AES.MODE_CFB, iv) 59 cipher = AES.new(key, AES.MODE_CFB, iv)
60 d = deferToThread(cipher.encrypt, BlockCipher.pad(text.encode("utf-8"))) 60 d = deferToThread(cipher.encrypt, BlockCipher.pad(text.encode("utf-8")))
61 d.addCallback(lambda ciphertext: b64encode(iv + ciphertext)) 61 d.addCallback(lambda ciphertext: b64encode(iv + ciphertext))
62 d.addCallback(lambda bytes_cypher: bytes_cypher.decode('utf-8'))
62 return d 63 return d
63 64
64 @classmethod 65 @classmethod
65 def decrypt(cls, key, ciphertext, leave_empty=True): 66 def decrypt(cls, key, ciphertext, leave_empty=True):
66 """Decrypt a message. 67 """Decrypt a message.
135 @param salt (base-64 encoded str): if not None, use the given salt instead of a random value 136 @param salt (base-64 encoded str): if not None, use the given salt instead of a random value
136 @param leave_empty (bool): if True, empty password will be returned "as is" 137 @param leave_empty (bool): if True, empty password will be returned "as is"
137 @return: Deferred: base-64 encoded str 138 @return: Deferred: base-64 encoded str
138 """ 139 """
139 if leave_empty and password == "": 140 if leave_empty and password == "":
140 return succeed(b"") 141 return succeed("")
141 salt = ( 142 salt = (
142 b64decode(salt)[: PasswordHasher.SALT_LEN] 143 b64decode(salt)[: PasswordHasher.SALT_LEN]
143 if salt 144 if salt
144 else urandom(PasswordHasher.SALT_LEN) 145 else urandom(PasswordHasher.SALT_LEN)
145 ) 146 )
146 d = deferToThread(PBKDF2, password, salt) 147 d = deferToThread(PBKDF2, password, salt)
147 d.addCallback(lambda hashed: b64encode(salt + hashed)) 148 d.addCallback(lambda hashed: b64encode(salt + hashed))
149 d.addCallback(lambda hashed_bytes: hashed_bytes.decode('utf-8'))
148 return d 150 return d
149 151
150 @classmethod 152 @classmethod
151 def compare_hash(cls, hashed_attempt, hashed): 153 def compare_hash(cls, hashed_attempt, hashed):
152 assert isinstance(hashed, bytes) 154 assert isinstance(hashed, str)
153 return hashed_attempt == hashed 155 return hashed_attempt == hashed
154 156
155 @classmethod 157 @classmethod
156 def verify(cls, attempt, hashed): 158 def verify(cls, attempt, hashed):
157 """Verify a password attempt. 159 """Verify a password attempt.
158 160
159 @param attempt (str): the attempt to check 161 @param attempt (str): the attempt to check
160 @param hashed (str): the hash of the password 162 @param hashed (str): the hash of the password
161 @return: Deferred: boolean 163 @return: Deferred: boolean
162 """ 164 """
165 assert isinstance(attempt, str)
166 assert isinstance(hashed, str)
163 leave_empty = hashed == "" 167 leave_empty = hashed == ""
164 d = PasswordHasher.hash(attempt, hashed, leave_empty) 168 d = PasswordHasher.hash(attempt, hashed, leave_empty)
165 d.addCallback(cls.compare_hash, hashed=hashed.encode('utf-8')) 169 d.addCallback(cls.compare_hash, hashed=hashed)
166 return d 170 return d