scramClientFinal
First introduced in version: 3.00.3
Syntax
scramClientFinal(user, combinedNonce, clientProof)
Details
scramClientFinal is a function used in the second-stage of SCRAM
authentication process. It checks the validity of the combined nonce
(combinedNonce) and the client-provided proof (clientProof).
Parameters
useris a string indicating a user name. It can only contain letters, underscores, or numbers. It cannot start with numbers. The length cannot exceed 30 characters.
combinedNonce is the combination of the client nonce and server nonce, Base64-encoded (i.e., the combined_nonce returned by scramClientFirst).
clientProof is the client-generated proof, Base64-encoded.
Returns
A Base64-encoded server signature, if verification succeeds.
Examples
The following Python script provides a complete example of how to implement SCRAM authentication for a user ("user01") in a Python client. The server-side operations are handled through the DolphinDB Python API.
import dolphindb as ddb
# Establish a session connection with DolphinDB
s = ddb.session("183.134.101.133", 8888)
s.run("print", 1, 2, 3)
##########################
import hashlib
import hmac
import os
import base64
import hashlib
# ------------------ Server Functions ------------------
def server_handle_client_first(username, client_nonce):
"""Handles the first authentication request, returns salt, iteration count, and combined nonce."""
salt, iter_count, nonce = s.run("scramClientFirst", username, client_nonce)
return {
"salt": salt,
"iteration_count": iter_count,
"combined_nonce": nonce
}
def server_handle_client_final(user, combined_nonce, client_proof):
"""Handles the final authentication request, verifies client_proof, and returns server_signature."""
return s.run("scramClientFinal", user, combined_nonce, client_proof)
# ------------------ Client Functions ------------------
def client_initiate_authentication(username):
"""Client initiates authentication by generating a client_nonce."""
client_nonce = base64.b64encode(os.urandom(16)).decode()
return {
"username": username,
"client_nonce": client_nonce
}
def client_generate_proof(user, password, salt, iteration_count, cnonce, combined_nonce):
"""Generates the client_proof."""
# Compute SaltedPassword
salted_password = hashlib.pbkdf2_hmac(
'sha256',
password.encode(),
base64.b64decode(salt),
iteration_count
)
# Generate keys
client_key = hmac.new(salted_password, b"Client Key", hashlib.sha256).digest()
stored_key = hashlib.sha256(client_key).digest()
server_key = hmac.new(salted_password, b"Server Key", hashlib.sha256).digest()
# Construct AuthMessage
auth_message = (
f"n={user},r={cnonce}," # client-first-bare
f"r={combined_nonce},s={salt},i={iteration_count}," # server-first
f"c=biws,r={combined_nonce}" # client-final-without-proof
)
# Compute ClientProof
client_signature = hmac.new(stored_key, auth_message.encode(), hashlib.sha256).digest()
client_proof = bytes([ck ^ cs for ck, cs in zip(client_key, client_signature)])
return {
"client_proof": base64.b64encode(client_proof).decode(),
"server_key": server_key,
"auth_message": auth_message
}
# ------------------ SCRAM Authentication ------------------
if __name__ == "__main__":
# Client initiates authentication
client_data = client_initiate_authentication("user01")
# Server processes the first request
server_response = server_handle_client_first(
client_data["username"],
client_data["client_nonce"]
)
password = "123456"
# Client generates proof
client_proof_data = client_generate_proof(
"user01",
password,
server_response["salt"],
server_response["iteration_count"],
client_data["client_nonce"],
server_response["combined_nonce"]
)
# Server verifies and returns signature
# auth_sessions[server_response["combined_nonce"]]["auth_message"] = client_proof_data["auth_message"]
server_signature = server_handle_client_final(
"user01",
server_response["combined_nonce"],
client_proof_data["client_proof"]
)
# Client verifies the server signature
computed_server_sig = hmac.new(
client_proof_data["server_key"],
client_proof_data["auth_message"].encode(),
hashlib.sha256
).digest()
# If verification passes, authentication succeeds
assert server_signature == base64.b64encode(computed_server_sig).decode()
print("SCRAM authentication succeeded!")
# Check current session and user
print(s.run("getCurrentSessionAndUser()"))
