wow-srp


Namewow-srp JSON
Version 0.4.0 PyPI version JSON
download
home_pageNone
SummaryCryptography library for authenticating with World of Warcraft 1.2-3.3.5 clients.
upload_time2024-11-12 14:21:20
maintainerNone
docs_urlNone
authorGtker <github@gtker.com>
requires_python>=3.7
licenseMIT 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"
}
        
Elapsed time: 2.54817s