Skip to content

Commit e1c60d4

Browse files
committed
Merge commit 'f5e2c8da41864b47145398995a53e8c7c1d238e8' as 'python/secp256k1proto'
2 parents 7c16422 + f5e2c8d commit e1c60d4

File tree

12 files changed

+613
-0
lines changed

12 files changed

+613
-0
lines changed

python/secp256k1proto/.gitignore

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Python-generated files
2+
__pycache__/
3+
*.py[oc]
4+
build/
5+
dist/
6+
wheels/
7+
*.egg-info
8+
9+
# Virtual environments
10+
.venv

python/secp256k1proto/.python-version

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.11

python/secp256k1proto/README.md

Whitespace-only changes.

python/secp256k1proto/pyproject.toml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[project]
2+
name = "secp256k1proto"
3+
version = "0.1.0"
4+
description = "Add your description here"
5+
readme = "README.md"
6+
authors = [
7+
{ name = "Sebastian Falbesoner", email = "[email protected]" }
8+
]
9+
requires-python = ">=3.11"
10+
dependencies = []
11+
12+
[build-system]
13+
requires = ["hatchling"]
14+
build-backend = "hatchling.build"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2009-2024 The Bitcoin Core developers
4+
Copyright (c) 2009-2024 Bitcoin Developers
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in
14+
all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
THE SOFTWARE.

python/secp256k1proto/src/secp256k1proto/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# The following functions are based on the BIP 340 reference implementation:
2+
# https://github.com/bitcoin/bips/blob/master/bip-0340/reference.py
3+
4+
from .secp256k1 import FE, GE, G
5+
from .util import int_from_bytes, bytes_from_int, xor_bytes, tagged_hash
6+
7+
8+
def pubkey_gen(seckey: bytes) -> bytes:
9+
d0 = int_from_bytes(seckey)
10+
if not (1 <= d0 <= GE.ORDER - 1):
11+
raise ValueError("The secret key must be an integer in the range 1..n-1.")
12+
P = d0 * G
13+
assert not P.infinity
14+
return P.to_bytes_xonly()
15+
16+
17+
def schnorr_sign(
18+
msg: bytes, seckey: bytes, aux_rand: bytes, tag_prefix: str = "BIP0340"
19+
) -> bytes:
20+
d0 = int_from_bytes(seckey)
21+
if not (1 <= d0 <= GE.ORDER - 1):
22+
raise ValueError("The secret key must be an integer in the range 1..n-1.")
23+
if len(aux_rand) != 32:
24+
raise ValueError("aux_rand must be 32 bytes instead of %i." % len(aux_rand))
25+
P = d0 * G
26+
assert not P.infinity
27+
d = d0 if P.has_even_y() else GE.ORDER - d0
28+
t = xor_bytes(bytes_from_int(d), tagged_hash(tag_prefix + "/aux", aux_rand))
29+
k0 = (
30+
int_from_bytes(tagged_hash(tag_prefix + "/nonce", t + P.to_bytes_xonly() + msg))
31+
% GE.ORDER
32+
)
33+
if k0 == 0:
34+
raise RuntimeError("Failure. This happens only with negligible probability.")
35+
R = k0 * G
36+
assert not R.infinity
37+
k = k0 if R.has_even_y() else GE.ORDER - k0
38+
e = (
39+
int_from_bytes(
40+
tagged_hash(
41+
tag_prefix + "/challenge", R.to_bytes_xonly() + P.to_bytes_xonly() + msg
42+
)
43+
)
44+
% GE.ORDER
45+
)
46+
sig = R.to_bytes_xonly() + bytes_from_int((k + e * d) % GE.ORDER)
47+
assert schnorr_verify(msg, P.to_bytes_xonly(), sig, tag_prefix=tag_prefix)
48+
return sig
49+
50+
51+
def schnorr_verify(
52+
msg: bytes, pubkey: bytes, sig: bytes, tag_prefix: str = "BIP0340"
53+
) -> bool:
54+
if len(pubkey) != 32:
55+
raise ValueError("The public key must be a 32-byte array.")
56+
if len(sig) != 64:
57+
raise ValueError("The signature must be a 64-byte array.")
58+
try:
59+
P = GE.from_bytes_xonly(pubkey)
60+
except ValueError:
61+
return False
62+
r = int_from_bytes(sig[0:32])
63+
s = int_from_bytes(sig[32:64])
64+
if (r >= FE.SIZE) or (s >= GE.ORDER):
65+
return False
66+
e = (
67+
int_from_bytes(tagged_hash(tag_prefix + "/challenge", sig[0:32] + pubkey + msg))
68+
% GE.ORDER
69+
)
70+
R = s * G - e * P
71+
if R.infinity or (not R.has_even_y()) or (R.x != r):
72+
return False
73+
return True
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import hashlib
2+
3+
from .secp256k1 import GE, Scalar
4+
5+
6+
def ecdh_compressed_in_raw_out(seckey: bytes, pubkey: bytes) -> GE:
7+
"""TODO"""
8+
shared_secret = Scalar.from_bytes(seckey) * GE.from_bytes_compressed(pubkey)
9+
assert not shared_secret.infinity # prime-order group
10+
return shared_secret
11+
12+
13+
def ecdh_libsecp256k1(seckey: bytes, pubkey: bytes) -> bytes:
14+
"""TODO"""
15+
shared_secret = ecdh_compressed_in_raw_out(seckey, pubkey)
16+
return hashlib.sha256(shared_secret.to_bytes_compressed()).digest()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from .secp256k1 import GE, G
2+
from .util import int_from_bytes
3+
4+
# The following function is based on the BIP 327 reference implementation
5+
# https://github.com/bitcoin/bips/blob/master/bip-0327/reference.py
6+
7+
8+
# Return the plain public key corresponding to a given secret key
9+
def pubkey_gen_plain(seckey: bytes) -> bytes:
10+
d0 = int_from_bytes(seckey)
11+
if not (1 <= d0 <= GE.ORDER - 1):
12+
raise ValueError("The secret key must be an integer in the range 1..n-1.")
13+
P = d0 * G
14+
assert not P.infinity
15+
return P.to_bytes_compressed()

python/secp256k1proto/src/secp256k1proto/py.typed

Whitespace-only changes.

0 commit comments

Comments
 (0)