Functional Weave
Code in Rust

auth.password-hash@1.0.0

impl/python/verify_password.py

1,782 bytes · the Python implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

from typing import List, Tuple

from .crypto_constant_time_equal import constant_time_equal  ← from crypto.constant-time-equal ^1.0.0 · built alongside by fune
from .crypto_pbkdf2_sha256 import pbkdf2_sha256  ← from crypto.pbkdf2-sha256 ^1.0.0 · built alongside by fune
from .encoding_base64_base64_decode import base64_decode
from .encoding_utf8_utf8_encode import utf8_encode

MALFORMED = "stored password hash is malformed"


def _decode_field(text: str) -> List[int]:
    try:
        data = base64_decode(text)
    except ValueError:
        raise ValueError(MALFORMED) from None
    if len(data) == 0:
        raise ValueError(MALFORMED)
    return data


def parse_stored_hash(stored: str) -> Tuple[int, List[int], List[int]]:
    """(iterations, salt, hash) of a stored pbkdf2_sha256 string. A string this
    code did not write is a data problem to surface, not a wrong password."""
    if not isinstance(stored, str):
        raise TypeError("stored password hash must be a string")
    parts = stored.split("$")
    if parts[0] != "pbkdf2_sha256":
        raise ValueError("stored password hash is not a pbkdf2_sha256 hash")
    if len(parts) != 4:
        raise ValueError(MALFORMED)
    count = parts[1]
    if len(count) == 0 or len(count) > 10 or count[0] == "0" or any(not ("0" <= ch <= "9") for ch in count):
        raise ValueError(MALFORMED)
    return int(count), _decode_field(parts[2]), _decode_field(parts[3])


def verify_password(password: str, stored: str) -> bool:
    """Does the password match the stored hash? Re-derived with the stored
    salt and iteration count, compared in constant time."""
    if not isinstance(password, str):
        raise TypeError("password must be a string")
    iterations, salt, expected = parse_stored_hash(stored)
    derived = pbkdf2_sha256(utf8_encode(password), salt, iterations, len(expected))
    return constant_time_equal(derived, expected)