every libs/*.py and hooks/*.py now starts with a one-line module docstring; every bin/* script starts with a `# purpose:` header. discovery-by-`ls`-and-read instead of by index. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
25 lines
857 B
Python
25 lines
857 B
Python
"""postgres: SCRAM-SHA-256 password-hash generation for postgres role provisioning."""
|
|
|
|
from base64 import standard_b64encode
|
|
from hashlib import pbkdf2_hmac, sha256
|
|
import hmac
|
|
|
|
|
|
def b64enc(b: bytes) -> str:
|
|
return standard_b64encode(b).decode('utf8')
|
|
|
|
def generate_scram_sha_256(password, salt):
|
|
if len(salt) != 16:
|
|
raise ValueError(f"Salt '{salt}' is not 16, but {len(salt)} characters long.")
|
|
|
|
digest_len = 32
|
|
iterations = 4096
|
|
|
|
digest_key = pbkdf2_hmac('sha256', password.encode('utf8'), salt, iterations, digest_len)
|
|
client_key = hmac.digest(digest_key, 'Client Key'.encode('utf8'), 'sha256')
|
|
stored_key = sha256(client_key).digest()
|
|
server_key = hmac.digest(digest_key, 'Server Key'.encode('utf8'), 'sha256')
|
|
|
|
return f'SCRAM-SHA-256${iterations}:{b64enc(salt)}${b64enc(stored_key)}:{b64enc(server_key)}'
|
|
|
|
|