Skip to content

Commit ff70d3f

Browse files
committed
Refactor utils
1 parent bb4a780 commit ff70d3f

17 files changed

Lines changed: 217 additions & 159 deletions

.cspell.jsonc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"words": [
33
"bitcointalk",
4+
"chacha",
45
"Cipolla",
56
"Codacy",
67
"Codecov",
@@ -15,11 +16,15 @@
1516
"hexdigest",
1617
"hkdf",
1718
"pycryptodome",
19+
"pytest",
1820
"readablize",
1921
"secp",
2022
"urandom",
2123
"xcfl",
2224
"xchacha"
2325
],
24-
"ignorePaths": [".cspell.jsonc", "LICENSE"]
26+
"ignorePaths": [
27+
".cspell.jsonc",
28+
"LICENSE"
29+
]
2530
}

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
# Release Notes
22

3+
## 0.4.4
4+
5+
- Refactor `utils`
6+
- Bump dependencies
7+
- Make `eth-keys` optional. Install with `pip install ecies[eth]` for the first time
8+
- Revamp doc
9+
310
## 0.4.1 ~ 0.4.3
411

512
- Bump dependencies
613
- Support Python 3.12, 3.13
14+
- Drop Python 3.8
715

816
## 0.4.0
917

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2018-2024 Weiliang Li
3+
Copyright (c) 2018-2025 Weiliang Li
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
11
# eciespy
22

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

1011
Elliptic Curve Integrated Encryption Scheme for secp256k1 in Python.
1112

1213
Other language versions:
1314

14-
- [Rust](https://github.com/ecies/rs)
1515
- [TypeScript](https://github.com/ecies/js)
16+
- [Rust](https://github.com/ecies/rs)
1617
- [Golang](https://github.com/ecies/go)
1718
- [WASM](https://github.com/ecies/rs-wasm)
19+
- [Java](https://github.com/ecies/java)
20+
- [Dart](https://github.com/ecies/dart)
1821

19-
You can also check a FastAPI web backend demo [here](https://github.com/ecies/py-demo).
22+
You can also check a web backend demo [here](https://github.com/ecies/py-demo).
2023

2124
## Install
2225

23-
`pip install eciespy`
26+
`pip install eciespy[eth]`
2427

2528
## Quick Start
2629

@@ -44,21 +47,23 @@ Or just use a builtin command `eciespy` in your favorite [command line](#command
4447

4548
## API
4649

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

4952
Parameters:
5053

5154
- **receiver_pk** - Receiver's public key (hex str or bytes)
52-
- **msg** - Data to encrypt
55+
- **data** - Data to encrypt
56+
- **config** - Optional configuration object
5357

5458
Returns: **bytes**
5559

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

5862
Parameters:
5963

6064
- **receiver_sk** - Receiver's private key (hex str or bytes)
61-
- **msg** - Data to decrypt
65+
- **data** - Data to decrypt
66+
- **config** - Optional configuration object
6267

6368
Returns: **bytes**
6469

ecies/__init__.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919

2020
def encrypt(
21-
receiver_pk: Union[str, bytes], msg: bytes, config: Config = ECIES_CONFIG
21+
receiver_pk: Union[str, bytes], data: bytes, config: Config = ECIES_CONFIG
2222
) -> bytes:
2323
"""
2424
Encrypt with receiver's secp256k1 public key
@@ -27,8 +27,10 @@ def encrypt(
2727
----------
2828
receiver_pk: Union[str, bytes]
2929
Receiver's public key (hex str or bytes)
30-
msg: bytes
30+
data: bytes
3131
Data to encrypt
32+
config: Config
33+
Optional configuration object
3234
3335
Returns
3436
-------
@@ -45,13 +47,15 @@ def encrypt(
4547
ephemeral_sk = generate_key()
4648
ephemeral_pk = ephemeral_sk.public_key.format(config.is_ephemeral_key_compressed)
4749

48-
sym_key = encapsulate(ephemeral_sk, pk, config)
49-
encrypted = sym_encrypt(sym_key, msg, config)
50+
sym_key = encapsulate(ephemeral_sk, pk, config.is_hkdf_key_compressed)
51+
encrypted = sym_encrypt(
52+
sym_key, data, config.symmetric_algorithm, config.symmetric_nonce_length
53+
)
5054
return ephemeral_pk + encrypted
5155

5256

5357
def decrypt(
54-
receiver_sk: Union[str, bytes], msg: bytes, config: Config = ECIES_CONFIG
58+
receiver_sk: Union[str, bytes], data: bytes, config: Config = ECIES_CONFIG
5559
) -> bytes:
5660
"""
5761
Decrypt with receiver's secp256k1 private key
@@ -60,8 +64,10 @@ def decrypt(
6064
----------
6165
receiver_sk: Union[str, bytes]
6266
Receiver's private key (hex str or bytes)
63-
msg: bytes
67+
data: bytes
6468
Data to decrypt
69+
config: Config
70+
Optional configuration object
6571
6672
Returns
6773
-------
@@ -76,7 +82,9 @@ def decrypt(
7682
raise TypeError("Invalid secret key type")
7783

7884
key_size = config.ephemeral_key_size
79-
ephemeral_pk, encrypted = PublicKey(msg[0:key_size]), msg[key_size:]
85+
ephemeral_pk, encrypted = PublicKey(data[0:key_size]), data[key_size:]
8086

81-
sym_key = decapsulate(ephemeral_pk, sk, config)
82-
return sym_decrypt(sym_key, encrypted, config)
87+
sym_key = decapsulate(ephemeral_pk, sk, config.is_hkdf_key_compressed)
88+
return sym_decrypt(
89+
sym_key, encrypted, config.symmetric_algorithm, config.symmetric_nonce_length
90+
)

ecies/utils/__init__.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@
77
hex2pk,
88
hex2sk,
99
)
10-
from .hex import decode_hex, sha256
10+
from .hash import derive_key, sha256
11+
from .hex import decode_hex
1112
from .symmetric import sym_decrypt, sym_encrypt
1213

1314
__all__ = [
14-
"sha256",
15-
"decode_hex",
1615
"sym_encrypt",
1716
"sym_decrypt",
1817
"generate_key",
@@ -22,4 +21,9 @@
2221
"decapsulate",
2322
"encapsulate",
2423
"compat_eth_public_key",
24+
# hex
25+
"decode_hex",
26+
# hash
27+
"sha256",
28+
"derive_key",
2529
]

ecies/utils/elliptic.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
from coincurve import PrivateKey, PublicKey
22
from coincurve.utils import get_valid_secret
33

4-
from ..config import ECIES_CONFIG, Config
4+
from .hash import derive_key
55
from .hex import decode_hex
6-
from .symmetric import derive_key
76

87

98
def generate_key() -> PrivateKey:
@@ -22,12 +21,14 @@ def generate_key() -> PrivateKey:
2221

2322
def generate_eth_key():
2423
"""
24+
Note: `eth-keys` needs to be installed in advance.
25+
2526
Generate a random `eth_keys.keys.PrivateKey`
2627
2728
Returns
2829
-------
2930
eth_keys.keys.PrivateKey
30-
An ethereum key
31+
An ethereum flavored secp256k1 key
3132
3233
"""
3334
from eth_keys import keys
@@ -55,12 +56,6 @@ def hex2pk(pk_hex: str) -> PublicKey:
5556
return PublicKey(compat_eth_public_key(decode_hex(pk_hex)))
5657

5758

58-
def compat_eth_public_key(data: bytes):
59-
if len(data) == 64: # eth public key format
60-
data = b"\x04" + data
61-
return data
62-
63-
6459
def hex2sk(sk_hex: str) -> PrivateKey:
6560
"""
6661
Convert ethereum hex to `coincurve.PrivateKey`
@@ -79,11 +74,30 @@ def hex2sk(sk_hex: str) -> PrivateKey:
7974
return PrivateKey(decode_hex(sk_hex))
8075

8176

77+
def compat_eth_public_key(data: bytes):
78+
"""
79+
Convert ethereum public key format to uncompressed format
80+
81+
Parameters
82+
----------
83+
data: bytes
84+
Ethereum public key (64 bytes)
85+
86+
Returns
87+
-------
88+
bytes
89+
Uncompressed public key (65 bytes)
90+
91+
"""
92+
if len(data) == 64:
93+
data = b"\x04" + data
94+
return data
95+
96+
8297
# private below
8398
def encapsulate(
84-
private_key: PrivateKey, peer_public_key: PublicKey, config: Config = ECIES_CONFIG
99+
private_key: PrivateKey, peer_public_key: PublicKey, is_compressed: bool = False
85100
) -> bytes:
86-
is_compressed = config.is_hkdf_key_compressed
87101
shared_point = peer_public_key.multiply(private_key.secret)
88102
master = private_key.public_key.format(is_compressed) + shared_point.format(
89103
is_compressed
@@ -92,9 +106,8 @@ def encapsulate(
92106

93107

94108
def decapsulate(
95-
public_key: PublicKey, peer_private_key: PrivateKey, config: Config = ECIES_CONFIG
109+
public_key: PublicKey, peer_private_key: PrivateKey, is_compressed: bool = False
96110
) -> bytes:
97-
is_compressed = config.is_hkdf_key_compressed
98111
shared_point = public_key.multiply(peer_private_key.secret)
99112
master = public_key.format(is_compressed) + shared_point.format(is_compressed)
100113
return derive_key(master)

ecies/utils/hash.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import hashlib
2+
3+
from Crypto.Hash import SHA256
4+
from Crypto.Protocol.KDF import HKDF
5+
6+
7+
def sha256(data: bytes) -> bytes:
8+
"""
9+
Calculate sha256 hash.
10+
11+
Parameters
12+
----------
13+
data: bytes
14+
data to hash
15+
16+
Returns
17+
-------
18+
bytes
19+
sha256 hash in bytes
20+
21+
>>> sha256(b'0'*16).hex()[:8] == 'fcdb4b42'
22+
True
23+
"""
24+
return hashlib.sha256(data).digest()
25+
26+
27+
def derive_key(master: bytes) -> bytes:
28+
derived = HKDF(master, 32, b"", SHA256)
29+
return derived # type: ignore

ecies/utils/hex.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,4 @@
11
import codecs
2-
import hashlib
3-
4-
5-
def sha256(msg: bytes) -> bytes:
6-
"""
7-
Calculate sha256 hash.
8-
9-
Parameters
10-
----------
11-
msg: bytes
12-
message to hash
13-
14-
Returns
15-
-------
16-
bytes
17-
sha256 hash in bytes
18-
19-
>>> sha256(b'0'*16).hex()[:8] == 'fcdb4b42'
20-
True
21-
"""
22-
return hashlib.sha256(msg).digest()
232

243

254
def decode_hex(s: str) -> bytes:

0 commit comments

Comments
 (0)