Mercurial > libervia-backend
comparison sat/memory/crypto.py @ 3028:ab2696e34d29
Python 3 port:
/!\ this is a huge commit
/!\ starting from this commit, SàT is needs Python 3.6+
/!\ SàT maybe be instable or some feature may not work anymore, this will improve with time
This patch port backend, bridge and frontends to Python 3.
Roughly this has been done this way:
- 2to3 tools has been applied (with python 3.7)
- all references to python2 have been replaced with python3 (notably shebangs)
- fixed files not handled by 2to3 (notably the shell script)
- several manual fixes
- fixed issues reported by Python 3 that where not handled in Python 2
- replaced "async" with "async_" when needed (it's a reserved word from Python 3.7)
- replaced zope's "implements" with @implementer decorator
- temporary hack to handle data pickled in database, as str or bytes may be returned,
to be checked later
- fixed hash comparison for password
- removed some code which is not needed anymore with Python 3
- deactivated some code which needs to be checked (notably certificate validation)
- tested with jp, fixed reported issues until some basic commands worked
- ported Primitivus (after porting dependencies like urwid satext)
- more manual fixes
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 13 Aug 2019 19:08:41 +0200 |
parents | 003b8b4b56a7 |
children | fee60f17ebac |
comparison
equal
deleted
inserted
replaced
3027:ff5bcb12ae60 | 3028:ab2696e34d29 |
---|---|
1 #!/usr/bin/env python2 | 1 #!/usr/bin/env python3 |
2 # -*- coding: utf-8 -*- | 2 # -*- coding: utf-8 -*- |
3 | 3 |
4 # SAT: a jabber client | 4 # SAT: a jabber client |
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) | 5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) |
6 # Copyright (C) 2013-2016 Adrien Cossa (souliane@mailoo.org) | 6 # Copyright (C) 2013-2016 Adrien Cossa (souliane@mailoo.org) |
90 d.addCallback(lambda text: BlockCipher.unpad(text)) | 90 d.addCallback(lambda text: BlockCipher.unpad(text)) |
91 # XXX: cipher.decrypt gives no way to make the distinction between | 91 # XXX: cipher.decrypt gives no way to make the distinction between |
92 # a decrypted empty value and a decryption failure... both return | 92 # a decrypted empty value and a decryption failure... both return |
93 # the empty value. Fortunately, we detect empty passwords beforehand | 93 # the empty value. Fortunately, we detect empty passwords beforehand |
94 # thanks to the "leave_empty" parameter which is used by default. | 94 # thanks to the "leave_empty" parameter which is used by default. |
95 d.addCallback(lambda text: text.decode("utf-8") if text else None) | 95 d.addCallback(lambda text: text if text else None) |
96 return d | 96 return d |
97 | 97 |
98 @classmethod | 98 @classmethod |
99 def getRandomKey(cls, size=None, base64=False): | 99 def getRandomKey(cls, size=None, base64=False): |
100 """Return a random key suitable for block cipher encryption. | 100 """Return a random key suitable for block cipher encryption. |
112 | 112 |
113 @classmethod | 113 @classmethod |
114 def pad(self, s): | 114 def pad(self, s): |
115 """Method from http://stackoverflow.com/a/12525165""" | 115 """Method from http://stackoverflow.com/a/12525165""" |
116 bs = BlockCipher.BLOCK_SIZE | 116 bs = BlockCipher.BLOCK_SIZE |
117 return s + (bs - len(s) % bs) * chr(bs - len(s) % bs) | 117 return s + (bs - len(s) % bs) * (chr(bs - len(s) % bs)).encode('utf-8') |
118 | 118 |
119 @classmethod | 119 @classmethod |
120 def unpad(self, s): | 120 def unpad(self, s): |
121 """Method from http://stackoverflow.com/a/12525165""" | 121 """Method from http://stackoverflow.com/a/12525165""" |
122 s = s.decode('utf-8') | |
122 return s[0 : -ord(s[-1])] | 123 return s[0 : -ord(s[-1])] |
123 | 124 |
124 | 125 |
125 class PasswordHasher(object): | 126 class PasswordHasher(object): |
126 | 127 |
134 @param salt (base-64 encoded str): if not None, use the given salt instead of a random value | 135 @param salt (base-64 encoded str): if not None, use the given salt instead of a random value |
135 @param leave_empty (bool): if True, empty password will be returned "as is" | 136 @param leave_empty (bool): if True, empty password will be returned "as is" |
136 @return: Deferred: base-64 encoded str | 137 @return: Deferred: base-64 encoded str |
137 """ | 138 """ |
138 if leave_empty and password == "": | 139 if leave_empty and password == "": |
139 return succeed(password) | 140 return succeed(b"") |
140 salt = ( | 141 salt = ( |
141 b64decode(salt)[: PasswordHasher.SALT_LEN] | 142 b64decode(salt)[: PasswordHasher.SALT_LEN] |
142 if salt | 143 if salt |
143 else urandom(PasswordHasher.SALT_LEN) | 144 else urandom(PasswordHasher.SALT_LEN) |
144 ) | 145 ) |
145 d = deferToThread(PBKDF2, password, salt) | 146 d = deferToThread(PBKDF2, password, salt) |
146 d.addCallback(lambda hashed: b64encode(salt + hashed)) | 147 d.addCallback(lambda hashed: b64encode(salt + hashed)) |
147 return d | 148 return d |
149 | |
150 @classmethod | |
151 def compare_hash(cls, hashed_attempt, hashed): | |
152 assert isinstance(hashed, bytes) | |
153 return hashed_attempt == hashed | |
148 | 154 |
149 @classmethod | 155 @classmethod |
150 def verify(cls, attempt, hashed): | 156 def verify(cls, attempt, hashed): |
151 """Verify a password attempt. | 157 """Verify a password attempt. |
152 | 158 |
154 @param hashed (str): the hash of the password | 160 @param hashed (str): the hash of the password |
155 @return: Deferred: boolean | 161 @return: Deferred: boolean |
156 """ | 162 """ |
157 leave_empty = hashed == "" | 163 leave_empty = hashed == "" |
158 d = PasswordHasher.hash(attempt, hashed, leave_empty) | 164 d = PasswordHasher.hash(attempt, hashed, leave_empty) |
159 d.addCallback(lambda hashed_attempt: hashed_attempt == hashed) | 165 d.addCallback(cls.compare_hash, hashed=hashed.encode('utf-8')) |
160 return d | 166 return d |