scramp: SCRAM Protocol Implementation

raw JSON →
1.4.8 verified Tue May 12 auth: no python install: verified quickstart: stale

scramp is a pure-Python implementation of the SCRAM (Salted Challenge Response Authentication Mechanism) authentication protocol. It supports various SCRAM mechanisms including SCRAM-SHA-1, SCRAM-SHA-256, SCRAM-SHA-512, SCRAM-SHA3-512, and their channel-binding ('-PLUS') variants. The library is currently at version 1.4.8 (as of January 6, 2026) and maintains an active development status with moderate release cadence.

pip install scramp
error ModuleNotFoundError: No module named 'scramp'
cause The 'scramp' library is not installed in the current Python environment.
fix
pip install scramp
error TypeError: a bytes-like object is required, not 'str'
cause A Python string (`str`) was provided to a 'scramp' function or method that expects a byte string (`bytes`), often for sensitive data like passwords or salts.
fix
Encode the string to bytes, typically using .encode('utf-8'). Example: client.set_password('mysecret'.encode('utf-8'))
error scramp.exceptions.ScrampError: Authentication failed
cause The SCRAM authentication exchange failed due to incorrect credentials (username, password), invalid proofs, or a violation of the authentication protocol steps.
fix
Verify the provided username, password, salt, and iteration count (N). Ensure both client and server are correctly following the SCRAM message exchange sequence and data integrity.
error ValueError: Auth mechanism 'SCRAM-SHA-128' not supported by server
cause The client attempted to negotiate an authentication mechanism (e.g., 'SCRAM-SHA-128') that the 'scramp' server instance was not configured to support or did not advertise as available.
fix
Ensure that the client requests a mechanism that is present in the server's list of supported mechanisms, or configure the server to include the desired mechanism in its mechanisms parameter during instantiation.
breaking Version 1.2.0 introduced backward-incompatible changes to the server-side API. This update modified how authentication information is handled to enable storing derived user data (e.g., in a database) and integrate with third-party hashing libraries like `passlib`.
fix Review server-side implementations (e.g., `make_auth_info`, `build_server_first_message`) and adapt to the updated API, especially if custom authentication databases or hashing functions are used.
gotcha `ScramClient` selects the most secure mechanism from a provided list. To use channel-binding (`-PLUS`) variants, the `channel_binding` parameter (a tuple of name and data) must be explicitly provided during `ScramClient` initialization. If `channel_binding` is `None`, `-PLUS` mechanisms will be filtered out.
fix When initializing `ScramClient`, pass `channel_binding=('tls-server-end-point', your_tls_data)` or similar if channel binding is desired for stronger security.
gotcha The package name `scramp` is easily confused with other Python libraries or projects (e.g., `scamp` for music, `SCAMP`/`pyscamp` for matrix profiles, `SCaMP` for metagenomics, `SCRAP` for RNA-seq, Scamp5d vision system). Ensure you are installing and importing `scramp` specifically for SCRAM protocol implementation.
fix Always verify the PyPI project description and source URL to confirm you are using `scramp` for SCRAM authentication, not a similarly named library with different functionality.
python os / libc status wheel install import disk
3.10 alpine (musl) wheel - 0.14s 18.8M
3.10 alpine (musl) - - 0.16s 18.8M
3.10 slim (glibc) wheel 1.7s 0.11s 19M
3.10 slim (glibc) - - 0.11s 19M
3.11 alpine (musl) wheel - 0.22s 20.9M
3.11 alpine (musl) - - 0.26s 20.9M
3.11 slim (glibc) wheel 1.7s 0.19s 21M
3.11 slim (glibc) - - 0.19s 21M
3.12 alpine (musl) wheel - 0.19s 12.7M
3.12 alpine (musl) - - 0.19s 12.7M
3.12 slim (glibc) wheel 1.8s 0.19s 13M
3.12 slim (glibc) - - 0.19s 13M
3.13 alpine (musl) wheel - 0.18s 12.4M
3.13 alpine (musl) - - 0.19s 12.3M
3.13 slim (glibc) wheel 1.6s 0.18s 13M
3.13 slim (glibc) - - 0.19s 13M
3.9 alpine (musl) wheel - 0.14s 18.3M
3.9 alpine (musl) - - 0.15s 18.3M
3.9 slim (glibc) wheel 1.8s 0.12s 19M
3.9 slim (glibc) - - 0.12s 19M

This quickstart demonstrates a basic SCRAM authentication flow between a client and a server. The server initializes a `ScramMechanism` with the user's password to generate and store authentication information. The client then initiates the exchange using `ScramClient`, sending messages back and forth with the server until authentication is complete. The password for the server-side setup is retrieved from an environment variable `SCRAMP_TEST_PASSWORD` for security best practices.

import os
from scramp import ScramClient, ScramMechanism

# --- Server Side Setup ---
username = "user@example.com"
password = os.environ.get('SCRAMP_TEST_PASSWORD', 'test_password') # In a real app, load from secure config

# Server mechanism (stores user's SCRAM data derived from password)
# In a real application, auth_info would be retrieved from a database for the given username.
server_mechanism = ScramMechanism(password=password)
server_auth_info = server_mechanism.make_auth_info(password) # Derived data to store/retrieve for user

print("Server: Initialized SCRAM mechanism and derived auth info.")

# --- Client Side Exchange ---
client = ScramClient(
    mechanisms=['SCRAM-SHA-256'], # Client offers preferred mechanisms
    username=username,
    password=password
)
print(f"Client: Initialized SCRAM client for user '{username}'.")

# 1. Client sends initial message
client_first_message = client.build_client_first_message()
print(f"Client: Sending client-first-message: {client_first_message}")

# 2. Server receives client-first-message and builds server-first-message
# In a real server, 'server_auth_info' would be loaded from a DB based on 'username'
server_first_message = server_mechanism.build_server_first_message(
    client_first_message, server_auth_info
)
print(f"Server: Sending server-first-message: {server_first_message}")

# 3. Client receives server-first-message and builds client-final-message
client_final_message = client.build_client_final_message(server_first_message)
print(f"Client: Sending client-final-message: {client_final_message}")

# 4. Server receives client-final-message and authenticates
server_final_message = server_mechanism.build_server_final_message(
    client_final_message, server_auth_info
)

if server_mechanism.authenticated:
    print("Server: Client authenticated successfully!")
    print(f"Server: Sending server-final-message: {server_final_message}")
else:
    print("Server: Authentication failed.")

# 5. Client receives server-final-message (for verification and channel binding)
try:
    client.verify_server_final_message(server_final_message)
    print("Client: Server final message verified (authentication successful from client perspective).")
except ValueError as e:
    print(f"Client: Server final message verification failed: {e}")