Name | wow-srp JSON |
Version |
0.4.0
JSON |
| download |
home_page | None |
Summary | Cryptography library for authenticating with World of Warcraft 1.2-3.3.5 clients. |
upload_time | 2024-11-12 14:21:20 |
maintainer | None |
docs_url | None |
author | Gtker <github@gtker.com> |
requires_python | >=3.7 |
license | MIT OR Apache-2.0 |
keywords |
wow
srp
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# `wow_srp` for Python
SRP6 library for Python that supports WoW version 1.2 through to 3.3.5.
This is just a Python wrapper [around the original `wow_srp` library](https://github.com/gtker/wow_srp).
# Installation
Install from [PyPI](https://pypi.org/project/wow-srp/) with pip:
```bash
pip install wow-srp
```
# Usage
## Authentication
The module is split into functionality used by a server implementation and a client implementation.
### Server
```text
SrpVerifier -> SrpProof -> SrpServer
```
You will only want to save the username, salt, and password verifier for an account.
Do not save the raw passwords on the server.
Construct an `SrpVerifier` through
```python
>>> username = "A"
>>> password = "A"
>>> server = SrpVerifier.from_username_and_password(username, password)
>>> salt = server.salt()
>>> password_verifier = server.password_verifier()
```
Save the `username`, `salt`, and `password_verifier` in your database.
When a client connects, retrieve the `username`, `salt`, and `password_verifier` from your database and create
an `SrpVerifier` through the constructor and convert it to an `SrpProof`:
```python
>>> server = SrpVerifier(username, password_verifier, salt)
>>> server = server.into_proof()
```
The `salt`, `server_public_key`, `generator`, and `large_safe_prime` can 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.
```python
>>> salt = server.salt()
>>> server_public_key = server.server_public_key()
>>> generator = generator()
>>> large_safe_prime = large_safe_prime()
```
After receiving the `client_public_key` and `client_proof`, the proof can be attempted converted to an `SrpServer`.
```python
>>> 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")
>>> if server is None:
... print("Password was incorrect")
Password was incorrect
```
The 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 valid `SrpServer` to 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.
```python
>>> 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)
```
### Client
```text
SrpClientUser -> SrpClientChallenge -> SrpClient | -> SrpClientReconnection
```
The `SrpClientReconnection` is just a data struct that contains reconnection values.
The client does not have to save any values except for the username and password.
```python
>>> username = "A"
>>> password = "A"
>>> client = SrpClientUser(username, password)
```
After getting the `generator`, `large_safe_prime`, `server_public_key`, and `salt` from the server,
the `SrpClientUser` can be converted into an `SrpClientChallenge`.
```python
>>> 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 the `server_proof`:
This creates an `SrpClient`.
```python
>>> server_proof = [0] * 20
>>> client = client.verify_server_proof(server_proof)
>>> if client is None:
... print("Invalid password")
Invalid password
```
The `SrpClient` can attempt to reconnect using the `server_reconnect_data`:
```python
>>> server_reconnect_data = [0] * 16
>>> # reconnect_data = client.calculate_reconnect_values(server_reconnect_data)
```
And then access the reconnect values from `reconnect_data`:
```python
>>> # challenge_data = reconnect_data.challenge_data()
>>> # client_proof = reconnect_data.client_proof()
```
## Header Encryption
### Server
First, create a `ProofSeed` from for the version that you need:
```python
>>> server_seed = VanillaProofSeed()
>>> server_seed_value = server_seed.seed()
```
Then send the value to the client in
[SMSG_AUTH_CHALLENGE](https://gtker.com/wow_messages/docs/smsg_auth_challenge.html).
After receiving [CMSG_AUTH_SESSION](https://gtker.com/wow_messages/docs/cmsg_auth_session.html)
from the client, convert the proof to a `HeaderCrypto`.
```python
>>> # server_crypto = server_seed.into_server_header_crypto(username, session_key, client_proof, client_seed)
```
You can then encrypt and decrypt message headers with
```python
>>> # data = server_crypto.encrypt_server_header(size, opcode)
>>> # size, opcode = server_crypto.decrypt_client_header(data)
```
### Client
First, create a `ProofSeed` from for the version that you need:
```python
>>> client_seed = VanillaProofSeed()
>>> client_seed_value = client_seed.seed()
```
Then convert the seed to a `HeaderCrypto` using the seed received from
[SMSG_AUTH_CHALLENGE](https://gtker.com/wow_messages/docs/smsg_auth_challenge.html).
```python
>>> # client_proof, client_crypto = client_seed.into_client_header_crypto(username, session_key, server_seed)
```
Then send the `client_proof` and `client_seed_value` to the server through
[CMSG_AUTH_SESSION](https://gtker.com/wow_messages/docs/cmsg_auth_session.html).
You can then encrypt and decrypt message headers with
```python
>>> # data = client_crypto.encrypt_client_header(size, opcode)
>>> # size, opcode = client_crypto.decrypt_server_header(data)
```
# Development
```bash
pip install maturin
curl https://pyenv.run | bash
# For fish
set -U PYENV_ROOT "$HOME/.pyenv"
fish_add_path "$PYENV_ROOT/bin"
pyenv init - | source
pyenv install 3.10
pyenv virtualenv 3.10 pyo3
pyenv activate pyo3
maturin develop
```
Raw data
{
"_id": null,
"home_page": null,
"name": "wow-srp",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": null,
"keywords": "wow, srp",
"author": "Gtker <github@gtker.com>",
"author_email": "Gtker <pip@gtker.com>",
"download_url": "https://files.pythonhosted.org/packages/70/dd/c267a7e1901e25ac18ec36d17077ebcd4e1d0472a3034d108e30113db691/wow_srp-0.4.0.tar.gz",
"platform": null,
"description": "# `wow_srp` for Python\n\nSRP6 library for Python that supports WoW version 1.2 through to 3.3.5.\n\nThis is just a Python wrapper [around the original `wow_srp` library](https://github.com/gtker/wow_srp).\n\n# Installation\n\nInstall from [PyPI](https://pypi.org/project/wow-srp/) with pip:\n\n```bash\npip install wow-srp\n```\n\n# Usage\n\n## Authentication\n\nThe module is split into functionality used by a server implementation and a client implementation.\n\n### Server\n\n```text\nSrpVerifier -> SrpProof -> SrpServer\n```\n\nYou will only want to save the username, salt, and password verifier for an account.\nDo not save the raw passwords on the server.\n\nConstruct an `SrpVerifier` through\n\n```python\n>>> username = \"A\"\n>>> password = \"A\"\n>>> server = SrpVerifier.from_username_and_password(username, password)\n>>> salt = server.salt()\n>>> password_verifier = server.password_verifier()\n```\n\nSave the `username`, `salt`, and `password_verifier` in your database.\n\nWhen a client connects, retrieve the `username`, `salt`, and `password_verifier` from your database and create\nan `SrpVerifier` through the constructor and convert it to an `SrpProof`:\n\n```python\n>>> server = SrpVerifier(username, password_verifier, salt)\n>>> server = server.into_proof()\n```\n\nThe `salt`, `server_public_key`, `generator`, and `large_safe_prime` can then be sent to the client:\nThe internal calculations use the generator and large safe prime from the functions, and these MUST\nbe the ones sent to the client.\n\n```python\n>>> salt = server.salt()\n>>> server_public_key = server.server_public_key()\n>>> generator = generator()\n>>> large_safe_prime = large_safe_prime()\n```\n\nAfter receiving the `client_public_key` and `client_proof`, the proof can be attempted converted to an `SrpServer`.\n\n```python\n>>> client_public_key = [1] * 32 # Arbitrary data to show usage\n>>> client_proof = [0] * 20 # Arbitrary data to show usage\n>>> try:\n... # Returns tuple of server, proof, but doctest will fail\n... server = server.into_server(client_public_key, client_proof)\n... except:\n... print(\"Public key is invalid\")\n>>> if server is None:\n... print(\"Password was incorrect\")\nPassword was incorrect\n```\n\nThe client is now logged in and can be sent the realm list.\n\nIf the client loses connection it will attempt to reconnect.\nThis requires a valid `SrpServer` to exist.\nIn my opinion the reconnect method is insecure since it uses the session key that can easily be deduced\nby any third party and it should not be implemented in a production auth server.\n\n```python\n>>> client_challenge_data = [0] * 16 # Arbitrary data to show usage\n>>> client_proof = [0] * 20 # Arbitrary data to show usage\n>>> # reconnect_valid = server.verify_reconnection_attempt(client_challenge_data, client_proof)\n```\n\n### Client\n\n```text\nSrpClientUser -> SrpClientChallenge -> SrpClient | -> SrpClientReconnection\n```\nThe `SrpClientReconnection` is just a data struct that contains reconnection values.\n\nThe client does not have to save any values except for the username and password.\n\n```python\n>>> username = \"A\"\n>>> password = \"A\"\n>>> client = SrpClientUser(username, password)\n```\n\nAfter getting the `generator`, `large_safe_prime`, `server_public_key`, and `salt` from the server,\nthe `SrpClientUser` can be converted into an `SrpClientChallenge`.\n\n```python\n>>> generator = 7\n>>> large_safe_prime = [1] * 32\n>>> server_public_key = [1] * 32\n>>> salt = [0] * 32\n>>> client = client.into_challenge(generator, large_safe_prime, server_public_key, salt)\n```\n\nThe client can then verify that the server also has the correct password through the `server_proof`:\nThis creates an `SrpClient`.\n\n```python\n>>> server_proof = [0] * 20\n>>> client = client.verify_server_proof(server_proof)\n>>> if client is None:\n... print(\"Invalid password\")\nInvalid password\n```\n\nThe `SrpClient` can attempt to reconnect using the `server_reconnect_data`:\n\n```python\n>>> server_reconnect_data = [0] * 16\n>>> # reconnect_data = client.calculate_reconnect_values(server_reconnect_data)\n```\n\nAnd then access the reconnect values from `reconnect_data`:\n\n```python\n>>> # challenge_data = reconnect_data.challenge_data()\n>>> # client_proof = reconnect_data.client_proof()\n```\n\n## Header Encryption\n\n### Server\n\nFirst, create a `ProofSeed` from for the version that you need:\n\n```python\n>>> server_seed = VanillaProofSeed()\n>>> server_seed_value = server_seed.seed()\n```\n\nThen send the value to the client in\n[SMSG_AUTH_CHALLENGE](https://gtker.com/wow_messages/docs/smsg_auth_challenge.html).\n\nAfter receiving [CMSG_AUTH_SESSION](https://gtker.com/wow_messages/docs/cmsg_auth_session.html)\nfrom the client, convert the proof to a `HeaderCrypto`.\n\n```python\n>>> # server_crypto = server_seed.into_server_header_crypto(username, session_key, client_proof, client_seed)\n```\n\nYou can then encrypt and decrypt message headers with\n\n```python\n>>> # data = server_crypto.encrypt_server_header(size, opcode)\n>>> # size, opcode = server_crypto.decrypt_client_header(data)\n```\n\n### Client\n\nFirst, create a `ProofSeed` from for the version that you need:\n\n```python\n>>> client_seed = VanillaProofSeed()\n>>> client_seed_value = client_seed.seed()\n```\n\nThen convert the seed to a `HeaderCrypto` using the seed received from\n[SMSG_AUTH_CHALLENGE](https://gtker.com/wow_messages/docs/smsg_auth_challenge.html).\n\n```python\n>>> # client_proof, client_crypto = client_seed.into_client_header_crypto(username, session_key, server_seed)\n```\n\nThen send the `client_proof` and `client_seed_value` to the server through\n[CMSG_AUTH_SESSION](https://gtker.com/wow_messages/docs/cmsg_auth_session.html).\n\nYou can then encrypt and decrypt message headers with\n\n```python\n>>> # data = client_crypto.encrypt_client_header(size, opcode)\n>>> # size, opcode = client_crypto.decrypt_server_header(data)\n```\n\n# Development\n\n```bash\npip install maturin\ncurl https://pyenv.run | bash\n\n# For fish\nset -U PYENV_ROOT \"$HOME/.pyenv\"\nfish_add_path \"$PYENV_ROOT/bin\"\n\npyenv init - | source\npyenv install 3.10\npyenv virtualenv 3.10 pyo3\npyenv activate pyo3\nmaturin develop\n```\n\n",
"bugtrack_url": null,
"license": "MIT OR Apache-2.0",
"summary": "Cryptography library for authenticating with World of Warcraft 1.2-3.3.5 clients.",
"version": "0.4.0",
"project_urls": {
"Source Code": "https://www.github.com/gtker/wow_srp_python"
},
"split_keywords": [
"wow",
" srp"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "70ddc267a7e1901e25ac18ec36d17077ebcd4e1d0472a3034d108e30113db691",
"md5": "a870f054fcd5ed16fe4ca01fb08f9a52",
"sha256": "d568cbbb195885803bb25514f0b87935eb37e02cec5af9652ba475eb8273ae84"
},
"downloads": -1,
"filename": "wow_srp-0.4.0.tar.gz",
"has_sig": false,
"md5_digest": "a870f054fcd5ed16fe4ca01fb08f9a52",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 18141,
"upload_time": "2024-11-12T14:21:20",
"upload_time_iso_8601": "2024-11-12T14:21:20.449718Z",
"url": "https://files.pythonhosted.org/packages/70/dd/c267a7e1901e25ac18ec36d17077ebcd4e1d0472a3034d108e30113db691/wow_srp-0.4.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-11-12 14:21:20",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "gtker",
"github_project": "wow_srp_python",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "wow-srp"
}