Mercurial > libervia-backend
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 |