eciespy 0.4.6
Elliptic Curve Integrated Encryption Scheme for secp256k1/curve25519 in Python
License: MIT
Requires Python: <4.0,>=3.9
Keywords: secp256k1, crypto, elliptic curves, ecies, bitcoin, ethereum, cryptocurrency

# eciespy

[![Codacy Badge](https://api.codacy.com/project/badge/Grade/2a11aeb9939244019d2c64bce3ff3c4e)](https://app.codacy.com/gh/ecies/py/dashboard)
[![License](https://img.shields.io/github/license/ecies/py.svg)](https://github.com/ecies/py)
[![PyPI](https://img.shields.io/pypi/v/eciespy.svg)](https://pypi.org/project/eciespy/)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/eciespy)](https://pypistats.org/packages/eciespy)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/eciespy.svg)](https://pypi.org/project/eciespy/)
[![CI](https://img.shields.io/github/actions/workflow/status/ecies/py/ci.yml?branch=master)](https://github.com/ecies/py/actions)
[![Codecov](https://img.shields.io/codecov/c/github/ecies/py.svg)](https://codecov.io/gh/ecies/py)

Elliptic Curve Integrated Encryption Scheme for secp256k1/curve25519 in Python.

Other language versions:

- [TypeScript](https://github.com/ecies/js)
- [Rust](https://github.com/ecies/rs)
- [Golang](https://github.com/ecies/go)
- [WASM](https://github.com/ecies/rs-wasm)
- [Java](https://github.com/ecies/java)
- [Dart](https://github.com/ecies/dart)

You can also check a [web backend demo](https://github.com/ecies/py-demo).

## Install

`pip install eciespy`

Or `pip install 'eciespy[eth]'` to install `eth-keys` as well.

## Quick Start

### Secp256k1

```python
>>> from ecies.keys import PrivateKey
>>> from ecies import encrypt, decrypt
>>> data = 'hello world🌍'.encode()
>>> sk = PrivateKey('secp256k1')
>>> sk_bytes = sk.secret  # bytes
>>> pk_bytes = sk.public_key.to_bytes(True)  # bytes
>>> decrypt(sk_bytes, encrypt(pk_bytes, data)).decode()
'hello world🌍'
>>> sk_hex = sk.to_hex() # hex str
>>> pk_hex = sk.public_key.to_hex(True) # hex str
>>> decrypt(sk_hex, encrypt(pk_hex, data)).decode()
'hello world🌍'
```

### X25519/Ed25519

```python
>>> from ecies.keys import PrivateKey
>>> from ecies import encrypt, decrypt
>>> from ecies.config import ECIES_CONFIG
>>> ECIES_CONFIG.elliptic_curve = 'x25519' # or 'ed25519'
>>> data = 'hello world🌍'.encode()
>>> sk = PrivateKey('x25519') # or 'ed25519'
>>> decrypt(sk.secret, encrypt(sk.public_key.to_bytes(), data)).decode()
'hello world🌍'
```

Or just use a builtin command `eciespy` in your favorite [command line](#command-line-interface).

## API

### `ecies.encrypt(receiver_pk: Union[str, bytes], data: bytes, config: Config = ECIES_CONFIG) -> bytes`

Parameters:

- `receiver_pk` - Receiver's public key (hex `str` or `bytes`)
- `data` - Data to encrypt
- `config` - Optional configuration object

Returns: `bytes`

### `ecies.decrypt(receiver_sk: Union[str, bytes], data: bytes, config: Config = ECIES_CONFIG) -> bytes`

Parameters:

- `receiver_sk` - Receiver's private key (hex `str` or `bytes`)
- `data` - Data to decrypt
- `config` - Optional configuration object

Returns: `bytes`

## Command Line Interface

### Show help

```console
$ eciespy -h
usage: eciespy [-h] [-e] [-d] [-g] [-k KEY] [-c {secp256k1,x25519,ed25519}] [-D [DATA]] [-O [OUT]]

Elliptic Curve Integrated Encryption Scheme for secp256k1/curve25519 in Python

options:
  -h, --help            show this help message and exit
  -e, --encrypt         encrypt with public key, exclusive with -d
  -d, --decrypt         decrypt with private key, exclusive with -e
  -g, --generate        generate key pair, for secp256k1, ethereum public key and address will be printed
  -k, --key KEY         public or private key file
  -c, --curve {secp256k1,x25519,ed25519}
                        elliptic curve, default: secp256k1
  -D, --data [DATA]     file to encrypt or decrypt, if not specified, it will read from stdin
  -O, --out [OUT]       encrypted or decrypted file, if not specified, it will write to stdout
```

### Generate eth key

```console
$ eciespy -g
Private: 0x95d3c5e483e9b1d4f5fc8e79b2deaf51362980de62dbb082a9a4257eef653d7d
Public: 0x98afe4f150642cd05cc9d2fa36458ce0a58567daeaf5fde7333ba9b403011140a4e28911fcf83ab1f457a30b4959efc4b9306f514a4c3711a16a80e3b47eb58b
Address: 0x47e801184B3a8ea8E6A4A7A4CFEfEcC76809Da72
```

### Encrypt with public key and decrypt with private key

```console
$ echo '0x95d3c5e483e9b1d4f5fc8e79b2deaf51362980de62dbb082a9a4257eef653d7d' > sk
$ echo '0x98afe4f150642cd05cc9d2fa36458ce0a58567daeaf5fde7333ba9b403011140a4e28911fcf83ab1f457a30b4959efc4b9306f514a4c3711a16a80e3b47eb58b' > pk
$ echo 'hello ecies' | eciespy -e -k pk | eciespy -d -k sk
hello ecies
$ echo 'data to encrypt' > data
$ eciespy -e -k pk -D data -O enc_data
$ eciespy -d -k sk -D enc_data
data to encrypt
$ rm sk pk data enc_data
```

## Configuration

Following configurations are available.

- Elliptic curve: secp256k1 or curve25519 (x25519/ed25519)
- Ephemeral key format in the payload: compressed or uncompressed (only for secp256k1)
- Shared elliptic curve key format in the key derivation: compressed or uncompressed (only for secp256k1)
- Symmetric cipher algorithm: AES-256-GCM or XChaCha20-Poly1305
- Symmetric nonce length: 12 or 16 bytes (only for AES-256-GCM)

For compatibility, make sure different applications share the same configuration.

```py
EllipticCurve = Literal["secp256k1", "x25519", "ed25519"]
SymmetricAlgorithm = Literal["aes-256-gcm", "xchacha20"]
NonceLength = Literal[12, 16]  # only for aes-256-gcm, xchacha20 will always be 24


@dataclass()
class Config:
    elliptic_curve: EllipticCurve = "secp256k1"
    is_ephemeral_key_compressed: bool = False
    is_hkdf_key_compressed: bool = False
    symmetric_algorithm: SymmetricAlgorithm = "aes-256-gcm"
    symmetric_nonce_length: NonceLength = 16

    @property
    def ephemeral_key_size(self):
        if self.elliptic_curve == "secp256k1":
            return (
                COMPRESSED_PUBLIC_KEY_SIZE
                if self.is_ephemeral_key_compressed
                else UNCOMPRESSED_PUBLIC_KEY_SIZE
            )
        elif self.elliptic_curve in ("x25519", "ed25519"):
            return CURVE25519_PUBLIC_KEY_SIZE
        else:
            raise NotImplementedError


ECIES_CONFIG = Config()
```

On `ECIES_CONFIG.elliptic_curve = "x25519"` or `"ed25519"`, x25519 (key exchange function on curve25519) or ed25519 (signature algorithm on curve25519) will be used for key exchange instead of secp256k1.

In this case, the payload would always be: `32 Bytes + Ciphered`.

> If you don't know how to choose between x25519 and ed25519, just use the dedicated key exchange function x25519 for efficiency.
>
> Because any 32-byte data is a valid curve25519 public key, the payload would seem random. This property is excellent for circumventing censorship by adversaries.

### Secp256k1-specific configuration

On `is_ephemeral_key_compressed = True`, the payload would be like: `33 Bytes + Ciphered` instead of `65 Bytes + Ciphered`.

On `is_hkdf_key_compressed = True`, the hkdf key would be derived from `ephemeral public key (compressed) + shared public key (compressed)` instead of `ephemeral public key (uncompressed) + shared public key (uncompressed)`.

### Symmetric cipher configuration

On `symmetric_algorithm = "xchacha20"`, plaintext data would be encrypted with XChaCha20-Poly1305.

On `symmetric_nonce_length = 12`, then the nonce of AES-256-GCM would be 12 bytes. XChaCha20-Poly1305's nonce is always 24 bytes.

### Which configuration should I choose?

For compatibility with other [ecies libraries](https://github.com/orgs/ecies/repositories), start with the default (secp256k1 with AES-256-GCM).

For speed and security, pick x25519 with XChaCha20-Poly1305.

## Technical details

See [DETAILS.md](./DETAILS.md).

## Changelog

See [CHANGELOG.md](./CHANGELOG.md).