Package: wow-srp
wow_srpfor PythonSRP6 library for Python that supports WoW version 1.2 through to 3.3.5.This is just a Python wrapperaround the originalwow_srplibrary.InstallationInstall fromPyPIwith pip:pipinstallwow-srpUsageAuthenticationThe module is split into functionality used by a server implementation and a client implementation.ServerSrpVerifier -> SrpProof -> SrpServerYou will only want to save the username, salt, and password verifier for an account.
Do not save the raw passwords on the server.Construct anSrpVerifierthrough>>>username="A">>>password="A">>>server=SrpVerifier.from_username_and_password(username,password)>>>salt=server.salt()>>>password_verifier=server.password_verifier()Save theusername,salt, andpassword_verifierin your database.When a client connects, retrieve theusername,salt, andpassword_verifierfrom your database and create
anSrpVerifierthrough the constructor and convert it to anSrpProof:>>>server=SrpVerifier(username,password_verifier,salt)>>>server=server.into_proof()Thesalt,server_public_key,generator, andlarge_safe_primecan then be sent to the client:
The internal calculations use the generator and large safe prime from the functions, and these MUST
be the ones sent to the client.>>>salt=server.salt()>>>server_public_key=server.server_public_key()>>>generator=generator()>>>large_safe_prime=large_safe_prime()After receiving theclient_public_keyandclient_proof, the proof can be attempted converted to anSrpServer.>>>client_public_key=[1]*32# Arbitrary data to show usage>>>client_proof=[0]*20# Arbitrary data to show usage>>>try:...# Returns tuple of server, proof, but doctest will fail...server=server.into_server(client_public_key,client_proof)...except:...print("Public key is invalid")>>>ifserverisNone:...print("Password was incorrect")PasswordwasincorrectThe client is now logged in and can be sent the realm list.If the client loses connection it will attempt to reconnect.
This requires a validSrpServerto exist.
In my opinion the reconnect method is insecure since it uses the session key that can easily be deduced
by any third party and it should not be implemented in a production auth server.>>>client_challenge_data=[0]*16# Arbitrary data to show usage>>>client_proof=[0]*20# Arbitrary data to show usage>>># reconnect_valid = server.verify_reconnection_attempt(client_challenge_data, client_proof)ClientSrpClientUser -> SrpClientChallenge -> SrpClient | -> SrpClientReconnectionTheSrpClientReconnectionis just a data struct that contains reconnection values.The client does not have to save any values except for the username and password.>>>username="A">>>password="A">>>client=SrpClientUser(username,password)After getting thegenerator,large_safe_prime,server_public_key, andsaltfrom the server,
theSrpClientUsercan be converted into anSrpClientChallenge.>>>generator=7>>>large_safe_prime=[1]*32>>>server_public_key=[1]*32>>>salt=[0]*32>>>client=client.into_challenge(generator,large_safe_prime,server_public_key,salt)The client can then verify that the server also has the correct password through theserver_proof:
This creates anSrpClient.>>>server_proof=[0]*20>>>client=client.verify_server_proof(server_proof)>>>ifclientisNone:...print("Invalid password")InvalidpasswordTheSrpClientcan attempt to reconnect using theserver_reconnect_data:>>>server_reconnect_data=[0]*16>>># reconnect_data = client.calculate_reconnect_values(server_reconnect_data)And then access the reconnect values fromreconnect_data:>>># challenge_data = reconnect_data.challenge_data()>>># client_proof = reconnect_data.client_proof()Header EncryptionServerFirst, create aProofSeedfrom for the version that you need:>>>server_seed=VanillaProofSeed()>>>server_seed_value=server_seed.seed()Then send the value to the client inSMSG_AUTH_CHALLENGE.After receivingCMSG_AUTH_SESSIONfrom the client, convert the proof to aHeaderCrypto.>>># server_crypto = server_seed.into_server_header_crypto(username, session_key, client_proof, client_seed)You can then encrypt and decrypt message headers with>>># data = server_crypto.encrypt_server_header(size, opcode)>>># size, opcode = server_crypto.decrypt_client_header(data)ClientFirst, create aProofSeedfrom for the version that you need:>>>client_seed=VanillaProofSeed()>>>client_seed_value=client_seed.seed()Then convert the seed to aHeaderCryptousing the seed received fromSMSG_AUTH_CHALLENGE.>>># client_proof, client_crypto = client_seed.into_client_header_crypto(username, session_key, server_seed)Then send theclient_proofandclient_seed_valueto the server throughCMSG_AUTH_SESSION.You can then encrypt and decrypt message headers with>>># data = client_crypto.encrypt_client_header(size, opcode)>>># size, opcode = client_crypto.decrypt_server_header(data)Developmentpipinstallmaturin
curlhttps://pyenv.run|bash# For fishset-UPYENV_ROOT"$HOME/.pyenv"fish_add_path"$PYENV_ROOT/bin"pyenvinit-|sourcepyenvinstall3.10
pyenvvirtualenv3.10pyo3
pyenvactivatepyo3
maturindevelop