comparison sat/memory/crypto.py @ 2624:56f94936df1e

code style reformatting using black
author Goffi <goffi@goffi.org>
date Wed, 27 Jun 2018 20:14:46 +0200
parents 26edcf3a30eb
children 003b8b4b56a7
comparison
equal deleted inserted replaced
2623:49533de4540b 2624:56f94936df1e
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: Deferred: base-64 encoded str
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")
54 key = key[:BlockCipher.MAX_KEY_SIZE] if len(key) >= BlockCipher.MAX_KEY_SIZE else BlockCipher.pad(key) 54 key = (
55 key[: BlockCipher.MAX_KEY_SIZE]
56 if len(key) >= BlockCipher.MAX_KEY_SIZE
57 else BlockCipher.pad(key)
58 )
55 cipher = AES.new(key, AES.MODE_CFB, iv) 59 cipher = AES.new(key, AES.MODE_CFB, iv)
56 d = deferToThread(cipher.encrypt, BlockCipher.pad(text.encode('utf-8'))) 60 d = deferToThread(cipher.encrypt, BlockCipher.pad(text.encode("utf-8")))
57 d.addCallback(lambda ciphertext: b64encode(iv + ciphertext)) 61 d.addCallback(lambda ciphertext: b64encode(iv + ciphertext))
58 return d 62 return d
59 63
60 @classmethod 64 @classmethod
61 def decrypt(cls, key, ciphertext, leave_empty=True): 65 def decrypt(cls, key, ciphertext, leave_empty=True):
66 @param key (unicode): the decryption key 70 @param key (unicode): the decryption key
67 @param ciphertext (base-64 encoded str): the text to decrypt 71 @param ciphertext (base-64 encoded str): the text to decrypt
68 @param leave_empty (bool): if True, empty ciphertext will be returned "as is" 72 @param leave_empty (bool): if True, empty ciphertext will be returned "as is"
69 @return: Deferred: str or None if the password could not be decrypted 73 @return: Deferred: str or None if the password could not be decrypted
70 """ 74 """
71 if leave_empty and ciphertext == '': 75 if leave_empty and ciphertext == "":
72 return succeed('') 76 return succeed("")
73 ciphertext = b64decode(ciphertext) 77 ciphertext = b64decode(ciphertext)
74 iv, ciphertext = ciphertext[:BlockCipher.IV_SIZE], ciphertext[BlockCipher.IV_SIZE:] 78 iv, ciphertext = (
75 key = key.encode('utf-8') 79 ciphertext[: BlockCipher.IV_SIZE],
76 key = key[:BlockCipher.MAX_KEY_SIZE] if len(key) >= BlockCipher.MAX_KEY_SIZE else BlockCipher.pad(key) 80 ciphertext[BlockCipher.IV_SIZE :],
81 )
82 key = key.encode("utf-8")
83 key = (
84 key[: BlockCipher.MAX_KEY_SIZE]
85 if len(key) >= BlockCipher.MAX_KEY_SIZE
86 else BlockCipher.pad(key)
87 )
77 cipher = AES.new(key, AES.MODE_CFB, iv) 88 cipher = AES.new(key, AES.MODE_CFB, iv)
78 d = deferToThread(cipher.decrypt, ciphertext) 89 d = deferToThread(cipher.decrypt, ciphertext)
79 d.addCallback(lambda text: BlockCipher.unpad(text)) 90 d.addCallback(lambda text: BlockCipher.unpad(text))
80 # XXX: cipher.decrypt gives no way to make the distinction between 91 # XXX: cipher.decrypt gives no way to make the distinction between
81 # a decrypted empty value and a decryption failure... both return 92 # a decrypted empty value and a decryption failure... both return
82 # the empty value. Fortunately, we detect empty passwords beforehand 93 # the empty value. Fortunately, we detect empty passwords beforehand
83 # thanks to the "leave_empty" parameter which is used by default. 94 # thanks to the "leave_empty" parameter which is used by default.
84 d.addCallback(lambda text: text.decode('utf-8') if text else None) 95 d.addCallback(lambda text: text.decode("utf-8") if text else None)
85 return d 96 return d
86 97
87 @classmethod 98 @classmethod
88 def getRandomKey(cls, size=None, base64=False): 99 def getRandomKey(cls, size=None, base64=False):
89 """Return a random key suitable for block cipher encryption. 100 """Return a random key suitable for block cipher encryption.
106 return s + (bs - len(s) % bs) * chr(bs - len(s) % bs) 117 return s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
107 118
108 @classmethod 119 @classmethod
109 def unpad(self, s): 120 def unpad(self, s):
110 """Method from http://stackoverflow.com/a/12525165""" 121 """Method from http://stackoverflow.com/a/12525165"""
111 return s[0:-ord(s[-1])] 122 return s[0 : -ord(s[-1])]
112 123
113 124
114 class PasswordHasher(object): 125 class PasswordHasher(object):
115 126
116 SALT_LEN = 16 # 128 bits 127 SALT_LEN = 16 # 128 bits
122 @param password (str): the password to hash 133 @param password (str): the password to hash
123 @param salt (base-64 encoded str): if not None, use the given salt instead of a random value 134 @param salt (base-64 encoded str): if not None, use the given salt instead of a random value
124 @param leave_empty (bool): if True, empty password will be returned "as is" 135 @param leave_empty (bool): if True, empty password will be returned "as is"
125 @return: Deferred: base-64 encoded str 136 @return: Deferred: base-64 encoded str
126 """ 137 """
127 if leave_empty and password == '': 138 if leave_empty and password == "":
128 return succeed(password) 139 return succeed(password)
129 salt = b64decode(salt)[:PasswordHasher.SALT_LEN] if salt else urandom(PasswordHasher.SALT_LEN) 140 salt = (
141 b64decode(salt)[: PasswordHasher.SALT_LEN]
142 if salt
143 else urandom(PasswordHasher.SALT_LEN)
144 )
130 d = deferToThread(PBKDF2, password, salt) 145 d = deferToThread(PBKDF2, password, salt)
131 d.addCallback(lambda hashed: b64encode(salt + hashed)) 146 d.addCallback(lambda hashed: b64encode(salt + hashed))
132 return d 147 return d
133 148
134 @classmethod 149 @classmethod
137 152
138 @param attempt (str): the attempt to check 153 @param attempt (str): the attempt to check
139 @param hashed (str): the hash of the password 154 @param hashed (str): the hash of the password
140 @return: Deferred: boolean 155 @return: Deferred: boolean
141 """ 156 """
142 leave_empty = hashed == '' 157 leave_empty = hashed == ""
143 d = PasswordHasher.hash(attempt, hashed, leave_empty) 158 d = PasswordHasher.hash(attempt, hashed, leave_empty)
144 d.addCallback(lambda hashed_attempt: hashed_attempt == hashed) 159 d.addCallback(lambda hashed_attempt: hashed_attempt == hashed)
145 return d 160 return d