wagner


Namewagner JSON
Version 0.0.2 PyPI version JSON
download
home_pagehttps://github.com/conduition/wagner
SummaryPython implementation of Wagner's Algorithm for the Generalized Birthday Problem.
upload_time2023-08-15 17:07:50
maintainerNone
docs_urlNone
authorconduition
requires_pythonNone
licenseNone
keywords birthday musig attack wagner generalized birthday problem k-sum k sum
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # wagner

Python implementation of [Wagner's Algorithm for the Generalized Birthday Problem](https://link.springer.com/content/pdf/10.1007/3-540-45708-9_19.pdf).

This algorithm is used to solve what is known as the generalized birthday problem. Given a modulus $n$, and $k$ lists of random numbers $\\{L_1, L_2, ..., L_k\\}$, how can we find $k$ elements $\\{x_1, x_2, ..., x_k\\} : x_i \in L_i$ from those lists, such that they all sum to some constant $c$ mod $n$?

[Check out my full-length article on the subject](https://conduition.io/cryptography/wagner) for more detailed info. This repository is meant as a demonstration for practically minded and inquisitive readers.

## Usage

The primary export of this library is the `solve` method.

```python
>>> import wagner
>>> wagner.solve(2**16)
[50320, 16960, 11687, 52082, 17220, 47751, 11228, 54896]
>>> sum(_) % (2**16)
0
```

This method solves the generalized birthday problem for a given modulus $n$. The higher $n$ is, the more difficult it is to find a solution and the longer the algorithm will take.

At no cost, the caller can also choose a desired sum other than zero.

```python
n = 2 ** 16
sum(wagner.solve(n, 885)) % n # -> 885
```

To change the number of elements returned by `solve`, specify the height $H$ of the tree used to solve the problem. The number of elements in the solution will be $2^H$.

```python
len(wagner.solve(n, tree_height=2)) # -> 4
len(wagner.solve(n, tree_height=3)) # -> 8
len(wagner.solve(n, tree_height=4)) # -> 16
len(wagner.solve(n, tree_height=5)) # -> 32
```

To specify how the random elements are generated, provide a `generator` callback. By default, `wagner` uses `random.randrange(n)` to generate random values. A common use case for Wagner's Algorithm is to find inputs whose hashes sum to some desired number. To ensure `solve` returns the preimages and not the hash outputs, return `Lineage` instances from your `generator` callback. This class holds is basically an integer with pointers to the element(s) which created it.

```python
import random
import hashlib
import wagner


def hashfunc(r, n, index):
  r_bytes = r.to_bytes((int.bit_length(n) + 7) // 8, 'big')
  preimage = r_bytes + index.to_bytes(16, 'big')
  h = hashlib.sha1(preimage).digest()
  return int.from_bytes(h, 'big') % n


def generator(n, index):
  r = random.randrange(0, n)
  return wagner.Lineage(hashfunc(r, n, index), r)


if __name__ == "__main__":
  n = 2 ** 128
  preimages = wagner.solve(n, generator=generator)
  print(sum(hashfunc(r, n, index) for index, r in enumerate(preimages)) % n) # -> 0
```
            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/conduition/wagner",
    "name": "wagner",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": "birthday,musig,attack,wagner,generalized birthday problem,k-sum,k sum",
    "author": "conduition",
    "author_email": "conduition@proton.me",
    "download_url": "https://files.pythonhosted.org/packages/f7/bb/e14241449ed5b279388298226dcb6951b49409c0e2c77ac48c46009ba33b/wagner-0.0.2.tar.gz",
    "platform": null,
    "description": "# wagner\n\nPython implementation of [Wagner's Algorithm for the Generalized Birthday Problem](https://link.springer.com/content/pdf/10.1007/3-540-45708-9_19.pdf).\n\nThis algorithm is used to solve what is known as the generalized birthday problem. Given a modulus $n$, and $k$ lists of random numbers $\\\\{L_1, L_2, ..., L_k\\\\}$, how can we find $k$ elements $\\\\{x_1, x_2, ..., x_k\\\\} : x_i \\in L_i$ from those lists, such that they all sum to some constant $c$ mod $n$?\n\n[Check out my full-length article on the subject](https://conduition.io/cryptography/wagner) for more detailed info. This repository is meant as a demonstration for practically minded and inquisitive readers.\n\n## Usage\n\nThe primary export of this library is the `solve` method.\n\n```python\n>>> import wagner\n>>> wagner.solve(2**16)\n[50320, 16960, 11687, 52082, 17220, 47751, 11228, 54896]\n>>> sum(_) % (2**16)\n0\n```\n\nThis method solves the generalized birthday problem for a given modulus $n$. The higher $n$ is, the more difficult it is to find a solution and the longer the algorithm will take.\n\nAt no cost, the caller can also choose a desired sum other than zero.\n\n```python\nn = 2 ** 16\nsum(wagner.solve(n, 885)) % n # -> 885\n```\n\nTo change the number of elements returned by `solve`, specify the height $H$ of the tree used to solve the problem. The number of elements in the solution will be $2^H$.\n\n```python\nlen(wagner.solve(n, tree_height=2)) # -> 4\nlen(wagner.solve(n, tree_height=3)) # -> 8\nlen(wagner.solve(n, tree_height=4)) # -> 16\nlen(wagner.solve(n, tree_height=5)) # -> 32\n```\n\nTo specify how the random elements are generated, provide a `generator` callback. By default, `wagner` uses `random.randrange(n)` to generate random values. A common use case for Wagner's Algorithm is to find inputs whose hashes sum to some desired number. To ensure `solve` returns the preimages and not the hash outputs, return `Lineage` instances from your `generator` callback. This class holds is basically an integer with pointers to the element(s) which created it.\n\n```python\nimport random\nimport hashlib\nimport wagner\n\n\ndef hashfunc(r, n, index):\n  r_bytes = r.to_bytes((int.bit_length(n) + 7) // 8, 'big')\n  preimage = r_bytes + index.to_bytes(16, 'big')\n  h = hashlib.sha1(preimage).digest()\n  return int.from_bytes(h, 'big') % n\n\n\ndef generator(n, index):\n  r = random.randrange(0, n)\n  return wagner.Lineage(hashfunc(r, n, index), r)\n\n\nif __name__ == \"__main__\":\n  n = 2 ** 128\n  preimages = wagner.solve(n, generator=generator)\n  print(sum(hashfunc(r, n, index) for index, r in enumerate(preimages)) % n) # -> 0\n```",
    "bugtrack_url": null,
    "license": null,
    "summary": "Python implementation of Wagner's Algorithm for the Generalized Birthday Problem.",
    "version": "0.0.2",
    "project_urls": {
        "Homepage": "https://github.com/conduition/wagner"
    },
    "split_keywords": [
        "birthday",
        "musig",
        "attack",
        "wagner",
        "generalized birthday problem",
        "k-sum",
        "k sum"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "f7bbe14241449ed5b279388298226dcb6951b49409c0e2c77ac48c46009ba33b",
                "md5": "5cc8aebfb781d41b70e3bab7ff7a7f60",
                "sha256": "83d8d934c35988d477bee83e3dd8a73c77d72b2d25148b6b3e758ea0b537df9c"
            },
            "downloads": -1,
            "filename": "wagner-0.0.2.tar.gz",
            "has_sig": false,
            "md5_digest": "5cc8aebfb781d41b70e3bab7ff7a7f60",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 5363,
            "upload_time": "2023-08-15T17:07:50",
            "upload_time_iso_8601": "2023-08-15T17:07:50.514201Z",
            "url": "https://files.pythonhosted.org/packages/f7/bb/e14241449ed5b279388298226dcb6951b49409c0e2c77ac48c46009ba33b/wagner-0.0.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-08-15 17:07:50",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "conduition",
    "github_project": "wagner",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "wagner"
}
        
Elapsed time: 8.33468s