sqlmap/lib/utils/bcrypt.py
2026-07-09 09:56:51 +02:00

146 lines
4.5 KiB
Python

#!/usr/bin/env python
"""
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
See the file 'LICENSE' for copying permission
"""
import struct
from lib.core.compat import xrange
from lib.core.convert import getBytes
# bcrypt (Provos-Mazieres EksBlowfish); the Blowfish P/S init constants are the fractional hex digits of pi
BCRYPT_ITOA64 = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
_bcryptState = None
def _bcryptInitState():
global _bcryptState
if _bcryptState is None:
count = 18 + 4 * 256
ndigits = count * 8
prec = ndigits + 16
one = 1 << (4 * prec)
def _arctan(inv):
total = term = one // inv
square = inv * inv
i = 1
while term:
term //= square
total += (term // (2 * i + 1)) * (-1 if i % 2 else 1)
i += 1
return total
frac = (16 * _arctan(5) - 4 * _arctan(239) - 3 * one) >> (4 * (prec - ndigits))
hexstr = "%0*x" % (ndigits, frac)
words = [int(hexstr[i * 8:(i + 1) * 8], 16) for i in xrange(count)]
_bcryptState = (words[:18], [words[18 + i * 256:18 + (i + 1) * 256] for i in xrange(4)])
return _bcryptState
def _bcryptEncipher(P, S, L, R):
for i in xrange(16):
L ^= P[i]
R ^= (((S[0][(L >> 24) & 0xff] + S[1][(L >> 16) & 0xff]) & 0xffffffff) ^ S[2][(L >> 8) & 0xff]) + S[3][L & 0xff] & 0xffffffff
L, R = R, L
L, R = R, L
return (L ^ P[17]) & 0xffffffff, (R ^ P[16]) & 0xffffffff
def _bcryptStream(data, offset):
word = 0
for _ in xrange(4):
word = ((word << 8) | data[offset[0]]) & 0xffffffff
offset[0] = (offset[0] + 1) % len(data)
return word
def _bcryptExpand(P, S, data, key):
koffset = [0]
for i in xrange(18):
P[i] ^= _bcryptStream(key, koffset)
doffset = [0]
L = R = 0
for i in xrange(0, 18, 2):
if data:
L ^= _bcryptStream(data, doffset)
R ^= _bcryptStream(data, doffset)
L, R = _bcryptEncipher(P, S, L, R)
P[i], P[i + 1] = L, R
for b in xrange(4):
for k in xrange(0, 256, 2):
if data:
L ^= _bcryptStream(data, doffset)
R ^= _bcryptStream(data, doffset)
L, R = _bcryptEncipher(P, S, L, R)
S[b][k], S[b][k + 1] = L, R
def _bcryptBase64(data):
retVal = ""
i = 0
while i < len(data):
c = data[i]; i += 1
retVal += BCRYPT_ITOA64[(c >> 2) & 0x3f]
c = (c & 3) << 4
if i >= len(data):
retVal += BCRYPT_ITOA64[c & 0x3f]; break
d = data[i]; i += 1
retVal += BCRYPT_ITOA64[(c | (d >> 4) & 0x0f) & 0x3f]
c = (d & 0x0f) << 2
if i >= len(data):
retVal += BCRYPT_ITOA64[c & 0x3f]; break
e = data[i]; i += 1
retVal += BCRYPT_ITOA64[(c | (e >> 6) & 3) & 0x3f]
retVal += BCRYPT_ITOA64[e & 0x3f]
return retVal
def _bcryptUnbase64(value, length):
retVal = bytearray()
positions = [BCRYPT_ITOA64.index(_) for _ in value]
i = 0
while i < len(positions) and len(retVal) < length:
c1 = positions[i]
c2 = positions[i + 1] if i + 1 < len(positions) else 0
retVal.append(((c1 << 2) | (c2 >> 4)) & 0xff)
if len(retVal) >= length:
break
c3 = positions[i + 2] if i + 2 < len(positions) else 0
retVal.append((((c2 & 0x0f) << 4) | (c3 >> 2)) & 0xff)
if len(retVal) >= length:
break
c4 = positions[i + 3] if i + 3 < len(positions) else 0
retVal.append((((c3 & 3) << 6) | c4) & 0xff)
i += 4
return retVal[:length]
def bcryptHash(password, salt, cost):
"""
Provos-Mazieres EksBlowfish digest, base64-encoded (the openwall bcrypt hash tail)
>>> bcryptHash("U*U", "CCCCCCCCCCCCCCCCCCCCC.", 5)
'E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW'
"""
P0, S0 = _bcryptInitState()
P, S = list(P0), [list(_) for _ in S0]
key = bytearray(getBytes(password) + b"\0")
saltbytes = _bcryptUnbase64(salt, 16)
_bcryptExpand(P, S, saltbytes, key)
for _ in xrange(1 << cost):
_bcryptExpand(P, S, b"", key)
_bcryptExpand(P, S, b"", saltbytes)
ctext = list(struct.unpack(">6I", b"OrpheanBeholderScryDoubt"))
for _ in xrange(64):
for j in xrange(0, 6, 2):
ctext[j], ctext[j + 1] = _bcryptEncipher(P, S, ctext[j], ctext[j + 1])
digest = bytearray(struct.pack(">6I", *ctext))[:23]
return _bcryptBase64(digest)