{"id":23532610,"url":"https://github.com/conduition/wagner","last_synced_at":"2025-04-22T22:40:49.711Z","repository":{"id":187996150,"uuid":"677846836","full_name":"conduition/wagner","owner":"conduition","description":"Python implementation of Wagner's Algorithm for the Generalized Birthday Problem.","archived":false,"fork":false,"pushed_at":"2023-08-15T17:07:38.000Z","size":13,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-27T20:47:38.322Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/conduition.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-08-12T20:49:15.000Z","updated_at":"2024-11-08T14:50:26.000Z","dependencies_parsed_at":"2024-12-25T23:12:04.964Z","dependency_job_id":"1da25c23-c653-4da1-b482-a58477b6df27","html_url":"https://github.com/conduition/wagner","commit_stats":null,"previous_names":["conduition/wagner"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/conduition%2Fwagner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/conduition%2Fwagner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/conduition%2Fwagner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/conduition%2Fwagner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/conduition","download_url":"https://codeload.github.com/conduition/wagner/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250337101,"owners_count":21414065,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-25T23:11:54.290Z","updated_at":"2025-04-22T22:40:49.692Z","avatar_url":"https://github.com/conduition.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 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\u003e\u003e\u003e import wagner\n\u003e\u003e\u003e wagner.solve(2**16)\n[50320, 16960, 11687, 52082, 17220, 47751, 11228, 54896]\n\u003e\u003e\u003e 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 # -\u003e 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)) # -\u003e 4\nlen(wagner.solve(n, tree_height=3)) # -\u003e 8\nlen(wagner.solve(n, tree_height=4)) # -\u003e 16\nlen(wagner.solve(n, tree_height=5)) # -\u003e 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) # -\u003e 0\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconduition%2Fwagner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fconduition%2Fwagner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconduition%2Fwagner/lists"}