# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['ecies']

package_data = \
{'': ['*']}

install_requires = \
['coincurve>=13.0,<14.0',
 'eth-keys>=0.3.3,<0.4.0',
 'pycryptodome>=3.9.9,<4.0.0']

entry_points = \
{'console_scripts': ['eciespy = ecies.__main__:main']}

setup_kwargs = {
    'name': 'eciespy',
    'version': '0.3.9',
    'description': 'Elliptic Curve Integrated Encryption Scheme for secp256k1 in Python',
    'long_description': '# eciespy\n\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/2a11aeb9939244019d2c64bce3ff3c4e)](https://www.codacy.com/app/ecies/py)\n[![CI](https://img.shields.io/github/workflow/status/ecies/py/Build)](https://github.com/ecies/py/actions)\n[![Codecov](https://img.shields.io/codecov/c/github/ecies/py.svg)](https://codecov.io/gh/ecies/py)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/eciespy.svg)](https://pypi.org/project/eciespy/)\n[![PyPI](https://img.shields.io/pypi/v/eciespy.svg)](https://pypi.org/project/eciespy/)\n[![License](https://img.shields.io/github/license/ecies/py.svg)](https://github.com/ecies/py)\n\nElliptic Curve Integrated Encryption Scheme for secp256k1 in Python.\n\nOther language versions:\n\n- [Rust](https://github.com/ecies/rs)\n- [TypeScript](https://github.com/ecies/js)\n- [Golang](https://github.com/ecies/go)\n- [WASM](https://github.com/ecies/rs-wasm)\n\nYou can also check a FastAPI web backend demo [here](https://github.com/kigawas/eciespy-demo).\n\n## Install\n\nInstall with `pip install eciespy` under Python 3.6+.\n\n## Quick Start\n\n```python\n>>> from ecies.utils import generate_eth_key, generate_key\n>>> from ecies import encrypt, decrypt\n>>> eth_k = generate_eth_key()\n>>> sk_hex = eth_k.to_hex()  # hex string\n>>> pk_hex = eth_k.public_key.to_hex()  # hex string\n>>> data = b\'this is a test\'\n>>> decrypt(sk_hex, encrypt(pk_hex, data))\nb\'this is a test\'\n>>> secp_k = generate_key()\n>>> sk_bytes = secp_k.secret  # bytes\n>>> pk_bytes = secp_k.public_key.format(True)  # bytes\n>>> decrypt(sk_bytes, encrypt(pk_bytes, data))\nb\'this is a test\'\n```\n\nOr just use a builtin command `eciespy` in your favorite [command line](#command-line-interface).\n\n## API\n\n### `ecies.encrypt(receiver_pk: Union[str, bytes], msg: bytes) -> bytes`\n\nParameters:\n\n- **receiver_pk** - Receiver\'s public key (hex str or bytes)\n- **msg** - Data to encrypt\n\nReturns: **bytes**\n\n### `ecies.decrypt(receiver_sk: Union[str, bytes], msg: bytes) -> bytes`\n\nParameters:\n\n- **receiver_sk** - Receiver\'s private key (hex str or bytes)\n- **msg** - Data to decrypt\n\nReturns: **bytes**\n\n## Command Line Interface\n\n### Show help\n\n```console\n$ eciespy -h\nusage: eciespy [-h] [-e] [-d] [-g] [-k KEY] [-D [DATA]] [-O [OUT]]\n\nElliptic Curve Integrated Encryption Scheme for secp256k1 in Python\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -e, --encrypt         encrypt with public key, exclusive with -d\n  -d, --decrypt         decrypt with private key, exclusive with -e\n  -g, --generate        generate ethereum key pair\n  -k KEY, --key KEY     public or private key file\n  -D [DATA], --data [DATA]\n                        file to encrypt or decrypt, if not specified, it will\n                        read from stdin\n  -O [OUT], --out [OUT]\n                        encrypted or decrypted file, if not specified, it will\n                        write to stdout\n```\n\n### Generate eth key\n\n```console\n$ eciespy -g\nPrivate: 0x95d3c5e483e9b1d4f5fc8e79b2deaf51362980de62dbb082a9a4257eef653d7d\nPublic: 0x98afe4f150642cd05cc9d2fa36458ce0a58567daeaf5fde7333ba9b403011140a4e28911fcf83ab1f457a30b4959efc4b9306f514a4c3711a16a80e3b47eb58b\nAddress: 0x47e801184B3a8ea8E6A4A7A4CFEfEcC76809Da72\n```\n\n### Encrypt with public key and decrypt with private key\n\n```console\n$ echo \'0x95d3c5e483e9b1d4f5fc8e79b2deaf51362980de62dbb082a9a4257eef653d7d\' > prv\n$ echo \'0x98afe4f150642cd05cc9d2fa36458ce0a58567daeaf5fde7333ba9b403011140a4e28911fcf83ab1f457a30b4959efc4b9306f514a4c3711a16a80e3b47eb58b\' > pub\n$ echo \'helloworld\' | eciespy -e -k pub | eciespy -d -k prv\nhelloworld\n$ echo \'data to encrypt\' > data\n$ eciespy -e -k pub -D data -O enc_data\n$ eciespy -d -k prv -D enc_data\ndata to encrypt\n$ rm prv pub data enc_data\n```\n\n## Mechanism and implementation details\n\nThis library combines `secp256k1` and `AES-256-GCM` (powered by [`coincurve`](https://github.com/ofek/coincurve) and [`pycryptodome`](https://github.com/Legrandin/pycryptodome)) to provide an API of encrypting with `secp256k1` public key and decrypting with `secp256k1`\'s private key. It has two parts generally:\n\n1. Use [ECDH](https://en.wikipedia.org/wiki/Elliptic-curve_Diffie–Hellman) to exchange an AES session key;\n\n   > Notice that the sender public key is generated every time when `ecies.encrypt` is invoked, thus, the AES session key varies.\n   >\n   > We are using HKDF-SHA256 instead of SHA256 to derive the AES keys.\n\n2. Use this AES session key to encrypt/decrypt the data under `AES-256-GCM`.\n\nBasically the encrypted data will be like this:\n\n```plaintext\n+-------------------------------+----------+----------+-----------------+\n| 65 Bytes                      | 16 Bytes | 16 Bytes | == data size    |\n+-------------------------------+----------+----------+-----------------+\n| Sender Public Key (ephemeral) | Nonce/IV | Tag/MAC  | Encrypted data  |\n+-------------------------------+----------+----------+-----------------+\n| sender_pk                     | nonce    | tag      | encrypted_data  |\n+-------------------------------+----------+----------+-----------------+\n|           Secp256k1           |              AES-256-GCM              |\n+-------------------------------+---------------------------------------+\n```\n\n### Secp256k1\n\n#### Glance at ECDH\n\nSo, **how** do we calculate the ECDH key under `secp256k1`? If you use a library like [`coincurve`](https://github.com/ofek/coincurve), you might just simply call `k1.ecdh(k2.public_key.format())`, then uh-huh, you got it! Let\'s see how to do it in simple Python snippets:\n\n```python\n>>> from coincurve import PrivateKey\n>>> k1 = PrivateKey.from_int(3)\n>>> k2 = PrivateKey.from_int(2)\n>>> k1.public_key.format(False).hex() # 65 bytes, False means uncompressed key\n\'04f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672\'\n>>> k2.public_key.format(False).hex() # 65 bytes\n\'04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a\'\n>>> k1.ecdh(k2.public_key.format()).hex()\n\'c7d9ba2fa1496c81be20038e5c608f2fd5d0246d8643783730df6c2bbb855cb2\'\n>>> k2.ecdh(k1.public_key.format()).hex()\n\'c7d9ba2fa1496c81be20038e5c608f2fd5d0246d8643783730df6c2bbb855cb2\'\n```\n\n#### Calculate your ecdh key manually\n\nHowever, as a hacker like you with strong desire to learn something, you must be curious about the magic under the ground.\n\nIn one sentence, the `secp256k1`\'s ECDH key of `k1` and `k2` is nothing but `sha256(k2.public_key.multiply(k1))`.\n\n```python\n>>> k1.to_int()\n3\n>>> shared_pub = k2.public_key.multiply(k1.secret)\n>>> shared_pub.point()\n(115780575977492633039504758427830329241728645270042306223540962614150928364886,\n 78735063515800386211891312544505775871260717697865196436804966483607426560663)\n>>> import hashlib\n>>> h = hashlib.sha256()\n>>> h.update(shared_pub.format())\n>>> h.hexdigest()  # here you got the ecdh key same as above!\n\'c7d9ba2fa1496c81be20038e5c608f2fd5d0246d8643783730df6c2bbb855cb2\'\n```\n\n> Warning: **NEVER** use small integers as private keys on any production systems or storing any valuable assets.\n>\n> Warning: **ALWAYS** use safe methods like [`os.urandom`](https://docs.python.org/3/library/os.html#os.urandom) to generate private keys.\n\n#### Math on ecdh\n\nLet\'s discuss in details. The word _multiply_ here means multiplying a **point** of a public key on elliptic curve (like `(x, y)`) with a **scalar** (like `k`). Here `k` is the integer format of a private key, for instance, it can be `3` for `k1` here, and `(x, y)` here is an extremely large number pair like `(115780575977492633039504758427830329241728645270042306223540962614150928364886, 78735063515800386211891312544505775871260717697865196436804966483607426560663)`.\n\n> Warning: 1 \\* (x, y) == (x, y) is always true, since 1 is the **identity element** for multiplication. If you take integer 1 as a private key, the public key will be the [base point](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm#Signature_generation_algorithm).\n\nMathematically, the elliptic curve cryptography is based on the fact that you can easily multiply point `A` (aka base point, or public key in ECDH) and scalar `k` (aka private key) to get another point `B` (aka public key), but it\'s almost impossible to calculate `A` from `B` reversely (which means it\'s a "one-way function").\n\n#### Compressed and uncompressed keys\n\nA point multiplying a scalar can be regarded that this point adds itself multiple times, and the point `B` can be converted to a readable public key in a compressed or uncompressed format.\n\n- Compressed format (`x` coordinate only)\n\n```python\n>>> point = (89565891926547004231252920425935692360644145829622209833684329913297188986597, 12158399299693830322967808612713398636155367887041628176798871954788371653930)\n>>> point == k2.public_key.point()\nTrue\n>>> prefix = \'02\' if point[1] % 2 == 0 else \'03\'\n>>> compressed_key_hex = prefix + hex(point[0])[2:]\n>>> compressed_key = bytes.fromhex(compressed_key_hex)\n>>> compressed_key.hex()\n\'02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5\'\n```\n\n- Uncompressed format (`(x, y)` coordinate)\n\n```python\n>>> uncompressed_key_hex = \'04\' + hex(point[0])[2:] + hex(point[1])[2:]\n>>> uncompressed_key = bytes.fromhex(uncompressed_key_hex)\n>>> uncompressed_key.hex()\n\'04c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a\'\n```\n\nThe format is depicted by the image below from the [bitcoin book](https://github.com/bitcoinbook/bitcoinbook).\n\n![EC public key format](https://raw.githubusercontent.com/bitcoinbook/bitcoinbook/develop/images/mbc2_0407.png)\n\n> If you want to convert the compressed format to uncompressed, basically, you need to calculate `y` from `x` by solving the equation using [Cipolla\'s Algorithm](https://en.wikipedia.org/wiki/Cipolla\'s_algorithm):\n>\n> ![y^2=(x^3 + 7) mod p, where p=2^{256}-2^{32}-2^{9}-2^{8}-2^{7}-2^{6}-2^{4}-1](<https://tex.s2cms.ru/svg/%20y%5E2%3D(x%5E3%20%2B%207)%20%5Cbmod%20p%2C%5C%20where%5C%20p%3D2%5E%7B256%7D-2%5E%7B32%7D-2%5E%7B9%7D-2%5E%7B8%7D-2%5E%7B7%7D-2%5E%7B6%7D-2%5E%7B4%7D-1%20>)\n>\n> You can check the [bitcoin wiki](https://en.bitcoin.it/wiki/Secp256k1) and this thread on [bitcointalk.org](https://bitcointalk.org/index.php?topic=644919.msg7205689#msg7205689) for more details.\n\nThen, the shared key between `k1` and `k2` is the `sha256` hash of the **compressed** ECDH public key. It\'s better to use the compressed format, since you can always get `x` from `x` or `(x, y)` without any calculation.\n\nYou may want to ask, what if we don\'t hash it? Briefly, hash can:\n\n1. Make the shared key\'s length fixed;\n2. Make it safer since hash functions can remove "weak bits" in the original computed key. Check the introduction section of this [paper](http://cacr.uwaterloo.ca/techreports/1998/corr98-05.pdf) for more details.\n\n> Warning: According to some recent research, although widely used, the `sha256` key derivation function is [not secure enough](https://github.com/ecies/py/issues/82).\n\n### AES\n\nNow we have the shared key, and we can use the `nonce` and `tag` to decrypt. This is quite straight, and the example derives from `pycryptodome`\'s [documentation](https://pycryptodome.readthedocs.io/en/latest/src/examples.html#encrypt-data-with-aes).\n\n```python\n>>> from Crypto.Cipher import AES\n>>> key = b\'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\'\n>>> iv = b\'\\xf3\\xe1\\xba\\x81\\r,\\x89\\x00\\xb1\\x13\\x12\\xb7\\xc7%V_\'\n>>> tag = b\'\\xec;q\\xe1|\\x11\\xdb\\xe3\\x14\\x84\\xda\\x94P\\xed\\xcfl\'\n>>> data = b\'\\x02\\xd2\\xff\\xed\\x93\\xb8V\\xf1H\\xb9\'\n>>> decipher = AES.new(key, AES.MODE_GCM, nonce=iv)\n>>> decipher.decrypt_and_verify(data, tag)\nb\'helloworld\'\n```\n\n> Strictly speaking, `nonce` != `iv`, but this is a little bit off topic, if you are curious, you can check [the comment in `utils.py`](https://github.com/ecies/py/blob/master/ecies/utils.py#L213).\n\n## Release Notes\n\n### 0.3.1 ~ 0.3.9\n\n- Support Python 3.8, 3.9 and phase out 3.5\n- Bump dependencies\n- Update documentation\n\n### 0.3.0\n\n- API change: use `HKDF-sha256` to derive shared keys instead of `sha256`\n\n### 0.2.0\n\n- API change: `ecies.encrypt` and `ecies.decrypt` now can take both hex str and raw bytes\n- Bump dependencies\n- Update documentation\n\n### 0.1.1 ~ 0.1.9\n\n- Bump dependencies\n- Update documentation\n- Switch to Circle CI\n- Change license to MIT\n\n### 0.1.0\n\n- First beta version release\n',
    'author': 'Weiliang Li',
    'author_email': 'to.be.impressive@gmail.com',
    'maintainer': 'Weiliang Li',
    'maintainer_email': 'to.be.impressive@gmail.com',
    'url': 'https://github.com/ecies/py',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'entry_points': entry_points,
    'python_requires': '>=3.6,<4.0',
}


setup(**setup_kwargs)
