Password based key derivation function 2 (PKCS #5 v2.0) This Python implementations based on the hmac module about as fast as OpenSSL's PKCS5_PBKDF2_HMAC for short passwords and much faster for long passwords.
(hash_name, password, salt, iterations, dklen=None)
| 186 | _trans_36 = bytes((x ^ 0x36) for x in range(256)) |
| 187 | |
| 188 | def pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None): |
| 189 | """Password based key derivation function 2 (PKCS #5 v2.0) |
| 190 | |
| 191 | This Python implementations based on the hmac module about as fast |
| 192 | as OpenSSL's PKCS5_PBKDF2_HMAC for short passwords and much faster |
| 193 | for long passwords. |
| 194 | """ |
| 195 | _warn( |
| 196 | "Python implementation of pbkdf2_hmac() is deprecated.", |
| 197 | category=DeprecationWarning, |
| 198 | stacklevel=2 |
| 199 | ) |
| 200 | if not isinstance(hash_name, str): |
| 201 | raise TypeError(hash_name) |
| 202 | |
| 203 | if not isinstance(password, (bytes, bytearray)): |
| 204 | password = bytes(memoryview(password)) |
| 205 | if not isinstance(salt, (bytes, bytearray)): |
| 206 | salt = bytes(memoryview(salt)) |
| 207 | |
| 208 | # Fast inline HMAC implementation |
| 209 | inner = new(hash_name) |
| 210 | outer = new(hash_name) |
| 211 | blocksize = getattr(inner, 'block_size', 64) |
| 212 | if len(password) > blocksize: |
| 213 | password = new(hash_name, password).digest() |
| 214 | password = password + b'\x00' * (blocksize - len(password)) |
| 215 | inner.update(password.translate(_trans_36)) |
| 216 | outer.update(password.translate(_trans_5C)) |
| 217 | |
| 218 | def prf(msg, inner=inner, outer=outer): |
| 219 | # PBKDF2_HMAC uses the password as key. We can re-use the same |
| 220 | # digest objects and just update copies to skip initialization. |
| 221 | icpy = inner.copy() |
| 222 | ocpy = outer.copy() |
| 223 | icpy.update(msg) |
| 224 | ocpy.update(icpy.digest()) |
| 225 | return ocpy.digest() |
| 226 | |
| 227 | if iterations < 1: |
| 228 | raise ValueError(iterations) |
| 229 | if dklen is None: |
| 230 | dklen = outer.digest_size |
| 231 | if dklen < 1: |
| 232 | raise ValueError(dklen) |
| 233 | |
| 234 | dkey = b'' |
| 235 | loop = 1 |
| 236 | from_bytes = int.from_bytes |
| 237 | while len(dkey) < dklen: |
| 238 | prev = prf(salt + loop.to_bytes(4)) |
| 239 | # endianness doesn't matter here as long to / from use the same |
| 240 | rkey = from_bytes(prev) |
| 241 | for i in range(iterations - 1): |
| 242 | prev = prf(prev) |
| 243 | # rkey = rkey ^ prev |
| 244 | rkey ^= from_bytes(prev) |
| 245 | loop += 1 |