Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
What's new
7
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Switch to GitLab Next
Sign in / Register
Toggle navigation
Menu
Open sidebar
Bitcoin Cash Node
Bitcoin Cash Node
Commits
cd7dbbc0
Commit
cd7dbbc0
authored
Jul 13, 2020
by
Calin Culianu
Browse files
Merge branch 'backport/D5965' into 'master'
[backport] Pure python EC See merge request
!423
parents
16bab221
7025e91e
Pipeline
#166151450
passed with stages
in 49 minutes and 10 seconds
Changes
10
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
test/functional/abc-mempool-accept-txn.py
View file @
cd7dbbc0
...
...
@@ -16,7 +16,7 @@ from test_framework.blocktools import (
create_tx_with_script
,
)
from
test_framework.cdefs
import
MAX_STANDARD_TX_SIGOPS
from
test_framework.key
import
C
ECKey
from
test_framework.key
import
ECKey
from
test_framework.messages
import
(
COutPoint
,
CTransaction
,
...
...
@@ -65,9 +65,10 @@ class FullBlockTest(BitcoinTestFramework):
self
.
num_nodes
=
1
self
.
setup_clean_chain
=
True
self
.
block_heights
=
{}
self
.
coinbase_key
=
CECKey
()
self
.
coinbase_key
.
set_secretbytes
(
b
"horsebattery"
)
self
.
coinbase_pubkey
=
self
.
coinbase_key
.
get_pubkey
()
self
.
coinbase_key
=
ECKey
()
# The test expects uncompressed keys
self
.
coinbase_key
.
generate
(
compressed
=
False
)
self
.
coinbase_pubkey
=
self
.
coinbase_key
.
get_pubkey
().
get_bytes
()
self
.
tip
=
None
self
.
blocks
=
{}
self
.
extra_args
=
[
...
...
@@ -101,7 +102,7 @@ class FullBlockTest(BitcoinTestFramework):
sighash
=
SignatureHashForkId
(
spend_tx
.
vout
[
n
].
scriptPubKey
,
tx
,
0
,
SIGHASH_ALL
|
SIGHASH_FORKID
,
spend_tx
.
vout
[
n
].
nValue
)
tx
.
vin
[
0
].
scriptSig
=
CScript
(
[
self
.
coinbase_key
.
sign
(
sighash
)
+
bytes
(
bytearray
([
SIGHASH_ALL
|
SIGHASH_FORKID
]))])
[
self
.
coinbase_key
.
sign
_ecdsa
(
sighash
)
+
bytes
(
bytearray
([
SIGHASH_ALL
|
SIGHASH_FORKID
]))])
def
create_and_sign_transaction
(
self
,
spend_tx
,
n
,
value
,
script
=
CScript
([
OP_TRUE
])):
...
...
@@ -223,7 +224,7 @@ class FullBlockTest(BitcoinTestFramework):
# Sign the transaction using the redeem script
sighash
=
SignatureHashForkId
(
redeem_script
,
spent_p2sh_tx
,
0
,
SIGHASH_ALL
|
SIGHASH_FORKID
,
p2sh_tx_to_spend
.
vout
[
0
].
nValue
)
sig
=
self
.
coinbase_key
.
sign
(
sig
=
self
.
coinbase_key
.
sign
_ecdsa
(
sighash
)
+
bytes
(
bytearray
([
SIGHASH_ALL
|
SIGHASH_FORKID
]))
spent_p2sh_tx
.
vin
[
0
].
scriptSig
=
CScript
([
sig
,
redeem_script
])
assert
len
(
...
...
test/functional/abc-mempool-coherence-on-activations.py
View file @
cd7dbbc0
...
...
@@ -25,7 +25,7 @@ from test_framework.blocktools import (
create_tx_with_script
,
make_conform_to_ctor
,
)
from
test_framework.key
import
C
ECKey
from
test_framework.key
import
ECKey
from
test_framework.messages
import
(
COIN
,
COutPoint
,
...
...
@@ -72,9 +72,9 @@ def create_fund_and_activation_specific_spending_tx(spend, pre_fork_only):
# create transactions that are only valid before or after the fork.
# Generate a key pair to test
private_key
=
C
ECKey
()
private_key
.
set_secretbytes
(
b
"replayprotection"
)
public_key
=
private_key
.
get_pubkey
()
private_key
=
ECKey
()
private_key
.
generate
(
)
public_key
=
private_key
.
get_pubkey
()
.
get_bytes
()
# Fund transaction
script
=
CScript
([
public_key
,
OP_CHECKSIG
])
...
...
@@ -94,7 +94,7 @@ def create_fund_and_activation_specific_spending_tx(spend, pre_fork_only):
sighashtype
=
(
forkvalue
<<
8
)
|
SIGHASH_ALL
|
SIGHASH_FORKID
sighash
=
SignatureHashForkId
(
script
,
txspend
,
0
,
sighashtype
,
50
*
COIN
)
sig
=
private_key
.
sign
(
sighash
)
+
\
sig
=
private_key
.
sign
_ecdsa
(
sighash
)
+
\
bytes
(
bytearray
([
SIGHASH_ALL
|
SIGHASH_FORKID
]))
txspend
.
vin
[
0
].
scriptSig
=
CScript
([
sig
])
txspend
.
rehash
()
...
...
test/functional/abc-p2p-fullblocktest-sigops.py
View file @
cd7dbbc0
...
...
@@ -26,7 +26,7 @@ from test_framework.cdefs import (
MAX_TX_SIGOPS_COUNT
,
ONE_MEGABYTE
,
)
from
test_framework.key
import
C
ECKey
from
test_framework.key
import
ECKey
from
test_framework.messages
import
(
COutPoint
,
CTransaction
,
...
...
@@ -346,9 +346,9 @@ class FullBlockTest(BitcoinTestFramework):
tip
(
26
)
# Generate a key pair to test P2SH sigops count
private_key
=
C
ECKey
()
private_key
.
set_secretbytes
(
b
"fatstacks"
)
public_key
=
private_key
.
get_pubkey
()
private_key
=
ECKey
()
private_key
.
generate
(
)
public_key
=
private_key
.
get_pubkey
()
.
get_bytes
()
# P2SH
# Build the redeem script, hash it, use hash to create the p2sh script
...
...
@@ -375,7 +375,7 @@ class FullBlockTest(BitcoinTestFramework):
# Sign the transaction using the redeem script
sighash
=
SignatureHashForkId
(
redeem_script
,
spent_p2sh_tx
,
0
,
SIGHASH_ALL
|
SIGHASH_FORKID
,
p2sh_tx
.
vout
[
0
].
nValue
)
sig
=
private_key
.
sign
(
sighash
)
+
\
sig
=
private_key
.
sign
_ecdsa
(
sighash
)
+
\
bytes
(
bytearray
([
SIGHASH_ALL
|
SIGHASH_FORKID
]))
spent_p2sh_tx
.
vin
[
0
].
scriptSig
=
CScript
([
sig
,
redeem_script
])
spent_p2sh_tx
.
rehash
()
...
...
test/functional/abc-replay-protection.py
View file @
cd7dbbc0
...
...
@@ -17,7 +17,7 @@ from test_framework.blocktools import (
create_tx_with_script
,
make_conform_to_ctor
,
)
from
test_framework.key
import
C
ECKey
from
test_framework.key
import
ECKey
from
test_framework.messages
import
(
COIN
,
COutPoint
,
...
...
@@ -146,9 +146,9 @@ class ReplayProtectionTest(BitcoinTestFramework):
out
.
append
(
get_spendable_output
())
# Generate a key pair to test P2SH sigops count
private_key
=
C
ECKey
()
private_key
.
set_secretbytes
(
b
"replayprotection"
)
public_key
=
private_key
.
get_pubkey
()
private_key
=
ECKey
()
private_key
.
generate
(
)
public_key
=
private_key
.
get_pubkey
()
.
get_bytes
()
# This is a little handier to use than the version in blocktools.py
def
create_fund_and_spend_tx
(
spend
,
forkvalue
=
0
):
...
...
@@ -167,7 +167,7 @@ class ReplayProtectionTest(BitcoinTestFramework):
sighashtype
=
(
forkvalue
<<
8
)
|
SIGHASH_ALL
|
SIGHASH_FORKID
sighash
=
SignatureHashForkId
(
script
,
txspend
,
0
,
sighashtype
,
50
*
COIN
-
1000
)
sig
=
private_key
.
sign
(
sighash
)
+
\
sig
=
private_key
.
sign
_ecdsa
(
sighash
)
+
\
bytes
(
bytearray
([
SIGHASH_ALL
|
SIGHASH_FORKID
]))
txspend
.
vin
[
0
].
scriptSig
=
CScript
([
sig
])
txspend
.
rehash
()
...
...
test/functional/abc-schnorr.py
View file @
cd7dbbc0
...
...
@@ -16,7 +16,7 @@ from test_framework.blocktools import (
create_tx_with_script
,
make_conform_to_ctor
,
)
from
test_framework.key
import
C
ECKey
from
test_framework.key
import
ECKey
from
test_framework.messages
import
(
CBlock
,
COutPoint
,
...
...
@@ -153,10 +153,10 @@ class SchnorrTest(BitcoinTestFramework):
# Generate a key pair
privkeybytes
=
b
"Schnorr!"
*
4
private_key
=
C
ECKey
()
private_key
.
set
_secretbytes
(
privkeybytes
)
private_key
=
ECKey
()
private_key
.
set
(
privkeybytes
,
True
)
# get uncompressed public key serialization
public_key
=
private_key
.
get_pubkey
()
public_key
=
private_key
.
get_pubkey
()
.
get_bytes
()
def
create_fund_and_spend_tx
(
multi
=
False
,
sig
=
'schnorr'
):
spendfrom
=
spendable_outputs
.
pop
()
...
...
@@ -188,7 +188,7 @@ class SchnorrTest(BitcoinTestFramework):
if
sig
==
'schnorr'
:
txsig
=
schnorr
.
sign
(
privkeybytes
,
sighash
)
+
hashbyte
elif
sig
==
'ecdsa'
:
txsig
=
private_key
.
sign
(
sighash
)
+
hashbyte
txsig
=
private_key
.
sign
_ecdsa
(
sighash
)
+
hashbyte
elif
isinstance
(
sig
,
bytes
):
txsig
=
sig
+
hashbyte
if
multi
:
...
...
test/functional/abc-schnorrmultisig.py
View file @
cd7dbbc0
...
...
@@ -19,7 +19,7 @@ from test_framework.blocktools import (
create_tx_with_script
,
make_conform_to_ctor
,
)
from
test_framework.key
import
C
ECKey
from
test_framework.key
import
ECKey
from
test_framework.messages
import
(
CBlock
,
COutPoint
,
...
...
@@ -156,10 +156,10 @@ class SchnorrMultisigTest(BitcoinTestFramework):
# Generate a key pair
privkeybytes
=
b
"Schnorr!"
*
4
private_key
=
C
ECKey
()
private_key
.
set
_secretbytes
(
privkeybytes
)
private_key
=
ECKey
()
private_key
.
set
(
privkeybytes
,
True
)
# get uncompressed public key serialization
public_key
=
private_key
.
get_pubkey
()
public_key
=
private_key
.
get_pubkey
()
.
get_bytes
()
def
create_fund_and_spend_tx
(
dummy
=
OP_0
,
sigtype
=
'ecdsa'
):
spendfrom
=
spendable_outputs
.
pop
()
...
...
@@ -188,7 +188,7 @@ class SchnorrMultisigTest(BitcoinTestFramework):
if
sigtype
==
'schnorr'
:
txsig
=
schnorr
.
sign
(
privkeybytes
,
sighash
)
+
hashbyte
elif
sigtype
==
'ecdsa'
:
txsig
=
private_key
.
sign
(
sighash
)
+
hashbyte
txsig
=
private_key
.
sign
_ecdsa
(
sighash
)
+
hashbyte
txspend
.
vin
[
0
].
scriptSig
=
CScript
([
dummy
,
txsig
])
txspend
.
rehash
()
...
...
test/functional/feature_assumevalid.py
View file @
cd7dbbc0
...
...
@@ -32,7 +32,7 @@ Start three nodes:
import
time
from
test_framework.blocktools
import
(
create_block
,
create_coinbase
)
from
test_framework.key
import
C
ECKey
from
test_framework.key
import
ECKey
from
test_framework.messages
import
(
CBlockHeader
,
COutPoint
,
...
...
@@ -111,9 +111,9 @@ class AssumeValidTest(BitcoinTestFramework):
self
.
blocks
=
[]
# Get a pubkey for the coinbase TXO
coinbase_key
=
C
ECKey
()
coinbase_key
.
set_secretbytes
(
b
"horsebattery"
)
coinbase_pubkey
=
coinbase_key
.
get_pubkey
()
coinbase_key
=
ECKey
()
coinbase_key
.
generate
(
)
coinbase_pubkey
=
coinbase_key
.
get_pubkey
()
.
get_bytes
()
# Create the first block with a coinbase output to our key
height
=
1
...
...
test/functional/feature_block.py
View file @
cd7dbbc0
...
...
@@ -15,7 +15,7 @@ from test_framework.blocktools import (
make_conform_to_ctor
,
)
from
test_framework.cdefs
import
LEGACY_MAX_BLOCK_SIZE
from
test_framework.key
import
C
ECKey
from
test_framework.key
import
ECKey
from
test_framework.messages
import
(
CBlock
,
COIN
,
...
...
@@ -80,9 +80,9 @@ class FullBlockTest(BitcoinTestFramework):
self
.
bootstrap_p2p
()
# Add one p2p connection to the node
self
.
block_heights
=
{}
self
.
coinbase_key
=
C
ECKey
()
self
.
coinbase_key
.
set_secretbytes
(
b
"horsebattery"
)
self
.
coinbase_pubkey
=
self
.
coinbase_key
.
get_pubkey
()
self
.
coinbase_key
=
ECKey
()
self
.
coinbase_key
.
generate
(
)
self
.
coinbase_pubkey
=
self
.
coinbase_key
.
get_pubkey
()
.
get_bytes
()
self
.
tip
=
None
self
.
blocks
=
{}
self
.
genesis_hash
=
int
(
self
.
nodes
[
0
].
getbestblockhash
(),
16
)
...
...
@@ -1053,7 +1053,7 @@ class FullBlockTest(BitcoinTestFramework):
sighash
=
SignatureHashForkId
(
spend_tx
.
vout
[
0
].
scriptPubKey
,
tx
,
0
,
SIGHASH_ALL
|
SIGHASH_FORKID
,
spend_tx
.
vout
[
0
].
nValue
)
tx
.
vin
[
0
].
scriptSig
=
CScript
(
[
self
.
coinbase_key
.
sign
(
sighash
)
+
bytes
(
bytearray
([
SIGHASH_ALL
|
SIGHASH_FORKID
]))])
[
self
.
coinbase_key
.
sign
_ecdsa
(
sighash
)
+
bytes
(
bytearray
([
SIGHASH_ALL
|
SIGHASH_FORKID
]))])
def
create_and_sign_transaction
(
self
,
spend_tx
,
value
,
script
=
CScript
([
OP_TRUE
])):
...
...
test/functional/feature_block_sigops.py
View file @
cd7dbbc0
...
...
@@ -18,7 +18,7 @@ from test_framework.blocktools import (
make_conform_to_ctor
,
)
from
test_framework.cdefs
import
LEGACY_MAX_BLOCK_SIZE
,
MAX_BLOCK_SIGOPS_PER_MB
from
test_framework.key
import
C
ECKey
from
test_framework.key
import
ECKey
from
test_framework.messages
import
(
COutPoint
,
CTransaction
,
...
...
@@ -61,9 +61,9 @@ class FullBlockSigOpsTest(BitcoinTestFramework):
self
.
bootstrap_p2p
()
# Add one p2p connection to the node
self
.
block_heights
=
{}
self
.
coinbase_key
=
C
ECKey
()
self
.
coinbase_key
.
set_secretbytes
(
b
"horsebattery"
)
self
.
coinbase_pubkey
=
self
.
coinbase_key
.
get_pubkey
()
self
.
coinbase_key
=
ECKey
()
self
.
coinbase_key
.
generate
(
)
self
.
coinbase_pubkey
=
self
.
coinbase_key
.
get_pubkey
()
.
get_bytes
()
self
.
tip
=
None
self
.
blocks
=
{}
self
.
genesis_hash
=
int
(
self
.
nodes
[
0
].
getbestblockhash
(),
16
)
...
...
@@ -263,7 +263,7 @@ class FullBlockSigOpsTest(BitcoinTestFramework):
sighash
=
SignatureHashForkId
(
redeem_script
,
tx
,
1
,
SIGHASH_ALL
|
SIGHASH_FORKID
,
lastAmount
)
sig
=
self
.
coinbase_key
.
sign
(
sig
=
self
.
coinbase_key
.
sign
_ecdsa
(
sighash
)
+
bytes
(
bytearray
([
SIGHASH_ALL
|
SIGHASH_FORKID
]))
scriptSig
=
CScript
([
sig
,
redeem_script
])
...
...
@@ -420,7 +420,7 @@ class FullBlockSigOpsTest(BitcoinTestFramework):
sighash
=
SignatureHashForkId
(
spend_tx
.
vout
[
0
].
scriptPubKey
,
tx
,
0
,
SIGHASH_ALL
|
SIGHASH_FORKID
,
spend_tx
.
vout
[
0
].
nValue
)
tx
.
vin
[
0
].
scriptSig
=
CScript
(
[
self
.
coinbase_key
.
sign
(
sighash
)
+
bytes
(
bytearray
([
SIGHASH_ALL
|
SIGHASH_FORKID
]))])
[
self
.
coinbase_key
.
sign
_ecdsa
(
sighash
)
+
bytes
(
bytearray
([
SIGHASH_ALL
|
SIGHASH_FORKID
]))])
def
create_and_sign_transaction
(
self
,
spend_tx
,
value
,
script
=
CScript
([
OP_TRUE
])):
...
...
test/functional/test_framework/key.py
View file @
cd7dbbc0
#!/usr/bin/env python3
# Copyright (c) 2011 Sam Rushing
"""ECC secp256k1 OpenSSL wrapper.
# Copyright (c) 2019 Pieter Wuille
WARNING: This module does not mlock() secrets; your private keys may end up on
disk in swap! Use with caution!
"""Test-only secp256k1 elliptic curve implementation
This file is modified from python-bitcoinlib.
WARNING: This code is slow, uses bad randomness, does not properly protect
keys, and is trivially vulnerable to side channel attacks. Do not use for
anything but tests.
"""
import
ctypes
import
ctypes.util
import
hashlib
import
random
ssl
=
ctypes
.
cdll
.
LoadLibrary
(
ctypes
.
util
.
find_library
(
'ssl'
)
or
'libeay32'
)
ssl
.
BN_new
.
restype
=
ctypes
.
c_void_p
ssl
.
BN_new
.
argtypes
=
[]
ssl
.
BN_free
.
restype
=
None
ssl
.
BN_free
.
argtypes
=
[
ctypes
.
c_void_p
]
ssl
.
BN_bin2bn
.
restype
=
ctypes
.
c_void_p
ssl
.
BN_bin2bn
.
argtypes
=
[
ctypes
.
c_char_p
,
ctypes
.
c_int
,
ctypes
.
c_void_p
]
ssl
.
BN_CTX_free
.
restype
=
None
ssl
.
BN_CTX_free
.
argtypes
=
[
ctypes
.
c_void_p
]
ssl
.
BN_CTX_new
.
restype
=
ctypes
.
c_void_p
ssl
.
BN_CTX_new
.
argtypes
=
[]
ssl
.
ECDH_compute_key
.
restype
=
ctypes
.
c_int
ssl
.
ECDH_compute_key
.
argtypes
=
[
ctypes
.
c_void_p
,
ctypes
.
c_int
,
ctypes
.
c_void_p
,
ctypes
.
c_void_p
]
ssl
.
ECDSA_sign
.
restype
=
ctypes
.
c_int
ssl
.
ECDSA_sign
.
argtypes
=
[
ctypes
.
c_int
,
ctypes
.
c_void_p
,
ctypes
.
c_int
,
ctypes
.
c_void_p
,
ctypes
.
c_void_p
,
ctypes
.
c_void_p
]
ssl
.
ECDSA_verify
.
restype
=
ctypes
.
c_int
ssl
.
ECDSA_verify
.
argtypes
=
[
ctypes
.
c_int
,
ctypes
.
c_void_p
,
ctypes
.
c_int
,
ctypes
.
c_void_p
,
ctypes
.
c_int
,
ctypes
.
c_void_p
]
ssl
.
EC_KEY_free
.
restype
=
None
ssl
.
EC_KEY_free
.
argtypes
=
[
ctypes
.
c_void_p
]
ssl
.
EC_KEY_new_by_curve_name
.
restype
=
ctypes
.
c_void_p
ssl
.
EC_KEY_new_by_curve_name
.
argtypes
=
[
ctypes
.
c_int
]
ssl
.
EC_KEY_get0_group
.
restype
=
ctypes
.
c_void_p
ssl
.
EC_KEY_get0_group
.
argtypes
=
[
ctypes
.
c_void_p
]
ssl
.
EC_KEY_get0_public_key
.
restype
=
ctypes
.
c_void_p
ssl
.
EC_KEY_get0_public_key
.
argtypes
=
[
ctypes
.
c_void_p
]
ssl
.
EC_KEY_set_private_key
.
restype
=
ctypes
.
c_int
ssl
.
EC_KEY_set_private_key
.
argtypes
=
[
ctypes
.
c_void_p
,
ctypes
.
c_void_p
]
ssl
.
EC_KEY_set_conv_form
.
restype
=
None
ssl
.
EC_KEY_set_conv_form
.
argtypes
=
[
ctypes
.
c_void_p
,
ctypes
.
c_int
]
ssl
.
EC_KEY_set_public_key
.
restype
=
ctypes
.
c_int
ssl
.
EC_KEY_set_public_key
.
argtypes
=
[
ctypes
.
c_void_p
,
ctypes
.
c_void_p
]
ssl
.
i2o_ECPublicKey
.
restype
=
ctypes
.
c_void_p
ssl
.
i2o_ECPublicKey
.
argtypes
=
[
ctypes
.
c_void_p
,
ctypes
.
c_void_p
]
ssl
.
EC_POINT_new
.
restype
=
ctypes
.
c_void_p
ssl
.
EC_POINT_new
.
argtypes
=
[
ctypes
.
c_void_p
]
ssl
.
EC_POINT_free
.
restype
=
None
ssl
.
EC_POINT_free
.
argtypes
=
[
ctypes
.
c_void_p
]
ssl
.
EC_POINT_mul
.
restype
=
ctypes
.
c_int
ssl
.
EC_POINT_mul
.
argtypes
=
[
ctypes
.
c_void_p
,
ctypes
.
c_void_p
,
ctypes
.
c_void_p
,
ctypes
.
c_void_p
,
ctypes
.
c_void_p
,
ctypes
.
c_void_p
]
# this specifies the curve used with ECDSA.
NID_secp256k1
=
714
# from openssl/obj_mac.h
def
modinv
(
a
,
n
):
"""Compute the modular inverse of a modulo n
See https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers
"""
t1
,
t2
=
0
,
1
r1
,
r2
=
n
,
a
while
r2
!=
0
:
q
=
r1
//
r2
t1
,
t2
=
t2
,
t1
-
q
*
t2
r1
,
r2
=
r2
,
r1
-
q
*
r2
if
r1
>
1
:
return
None
if
t1
<
0
:
t1
+=
n
return
t1
def
jacobi_symbol
(
n
,
k
):
"""Compute the Jacobi symbol of n modulo k
See http://en.wikipedia.org/wiki/Jacobi_symbol
"""
assert
k
>
0
and
k
&
1
n
%=
k
t
=
0
while
n
!=
0
:
while
n
&
1
==
0
:
n
>>=
1
r
=
k
&
7
t
^=
(
r
==
3
or
r
==
5
)
n
,
k
=
k
,
n
t
^=
(
n
&
k
&
3
==
3
)
n
=
n
%
k
if
k
==
1
:
return
-
1
if
t
else
1
return
0
def
modsqrt
(
a
,
p
):
"""Compute the square root of a modulo p
For p = 3 mod 4, if a square root exists, it is equal to a**((p+1)/4) mod p.
"""
assert
(
p
%
4
==
3
)
# Only p = 3 mod 4 is implemented
sqrt
=
pow
(
a
,
(
p
+
1
)
//
4
,
p
)
if
pow
(
sqrt
,
2
,
p
)
==
a
%
p
:
return
sqrt
return
None
class
EllipticCurve
:
def
__init__
(
self
,
p
,
a
,
b
):
"""Initialize elliptic curve y^2 = x^3 + a*x + b over GF(p)."""
self
.
p
=
p
self
.
a
=
a
%
p
self
.
b
=
b
%
p
def
affine
(
self
,
p1
):
"""Convert a Jacobian point tuple p1 to affine form, or None if at infinity."""
x1
,
y1
,
z1
=
p1
if
z1
==
0
:
return
None
inv
=
modinv
(
z1
,
self
.
p
)
inv_2
=
(
inv
**
2
)
%
self
.
p
inv_3
=
(
inv_2
*
inv
)
%
self
.
p
return
((
inv_2
*
x1
)
%
self
.
p
,
(
inv_3
*
y1
)
%
self
.
p
,
1
)
def
negate
(
self
,
p1
):
"""Negate a Jacobian point tuple p1."""
x1
,
y1
,
z1
=
p1
return
(
x1
,
(
self
.
p
-
y1
)
%
self
.
p
,
z1
)
def
on_curve
(
self
,
p1
):
"""Determine whether a Jacobian tuple p is on the curve (and not infinity)"""
x1
,
y1
,
z1
=
p1
z2
=
pow
(
z1
,
2
,
self
.
p
)
z4
=
pow
(
z2
,
2
,
self
.
p
)
return
z1
!=
0
and
(
pow
(
x1
,
3
,
self
.
p
)
+
self
.
a
*
x1
*
z4
+
self
.
b
*
z2
*
z4
-
pow
(
y1
,
2
,
self
.
p
))
%
self
.
p
==
0
def
is_x_coord
(
self
,
x
):
"""Test whether x is a valid X coordinate on the curve."""
x_3
=
pow
(
x
,
3
,
self
.
p
)
return
jacobi_symbol
(
x_3
+
self
.
a
*
x
+
self
.
b
,
self
.
p
)
!=
-
1
def
lift_x
(
self
,
x
):
"""Given an X coordinate on the curve, return a corresponding affine point."""
x_3
=
pow
(
x
,
3
,
self
.
p
)
v
=
x_3
+
self
.
a
*
x
+
self
.
b
y
=
modsqrt
(
v
,
self
.
p
)
if
y
is
None
:
return
None
return
(
x
,
y
,
1
)
def
double
(
self
,
p1
):
"""Double a Jacobian tuple p1"""
x1
,
y1
,
z1
=
p1
if
z1
==
0
:
return
(
0
,
1
,
0
)
y1_2
=
(
y1
**
2
)
%
self
.
p
y1_4
=
(
y1_2
**
2
)
%
self
.
p
x1_2
=
(
x1
**
2
)
%
self
.
p
s
=
(
4
*
x1
*
y1_2
)
%
self
.
p
m
=
3
*
x1_2
if
self
.
a
:
m
+=
self
.
a
*
pow
(
z1
,
4
,
self
.
p
)
m
=
m
%
self
.
p
x2
=
(
m
**
2
-
2
*
s
)
%
self
.
p
y2
=
(
m
*
(
s
-
x2
)
-
8
*
y1_4
)
%
self
.
p
z2
=
(
2
*
y1
*
z1
)
%
self
.
p
return
(
x2
,
y2
,
z2
)
def
add_mixed
(
self
,
p1
,
p2
):
"""Add a Jacobian tuple p1 and an affine tuple p2"""
x1
,
y1
,
z1
=
p1
x2
,
y2
,
z2
=
p2
assert
(
z2
==
1
)
if
z1
==
0
:
return
p2
z1_2
=
(
z1
**
2
)
%
self
.
p
z1_3
=
(
z1_2
*
z1
)
%
self
.
p
u2
=
(
x2
*
z1_2
)
%
self
.
p
s2
=
(
y2
*
z1_3
)
%
self
.
p
if
x1
==
u2
:
if
(
y1
!=
s2
):
return
(
0
,
1
,
0
)
return
self
.
double
(
p1
)
h
=
u2
-
x1
r
=
s2
-
y1
h_2
=
(
h
**
2
)
%
self
.
p
h_3
=
(
h_2
*
h
)
%
self
.
p
u1_h_2
=
(
x1
*
h_2
)
%
self
.
p
x3
=
(
r
**
2
-
h_3
-
2
*
u1_h_2
)
%
self
.
p
y3
=
(
r
*
(
u1_h_2
-
x3
)
-
y1
*
h_3
)
%
self
.
p
z3
=
(
h
*
z1
)
%
self
.
p
return
(
x3
,
y3
,
z3
)
def
add
(
self
,
p1
,
p2
):
"""Add two Jacobian tuples p1 and p2"""
x1
,
y1
,
z1
=
p1
x2
,
y2
,
z2
=
p2
if
z1
==
0
:
return
p2
if
z2
==
0
:
return
p1
if
z1
==
1
:
return
self
.
add_mixed
(
p2
,
p1
)
if
z2
==
1
:
return
self
.
add_mixed
(
p1
,
p2
)
z1_2
=
(
z1
**
2
)
%
self
.
p
z1_3
=
(
z1_2
*
z1
)
%
self
.
p
z2_2
=
(
z2
**
2
)
%
self
.
p
z2_3
=
(
z2_2
*
z2
)
%
self
.
p
u1
=
(
x1
*
z2_2
)
%
self
.
p
u2
=
(
x2
*
z1_2
)
%
self
.
p
s1
=
(
y1
*
z2_3
)
%
self
.
p
s2
=
(
y2
*
z1_3
)
%
self
.
p
if
u1
==
u2
:
if
(
s1
!=
s2
):
return
(
0
,
1
,
0
)
return
self
.
double
(
p1
)
h
=
u2
-
u1
r
=
s2
-
s1
h_2
=
(
h
**
2
)
%
self
.
p
h_3
=
(
h_2
*
h
)
%
self
.
p
u1_h_2
=
(
u1
*
h_2
)
%
self
.
p
x3
=
(
r
**
2
-
h_3
-
2
*
u1_h_2
)
%
self
.
p
y3
=
(
r
*
(
u1_h_2
-
x3
)
-
s1
*
h_3
)
%
self
.
p
z3
=
(
h
*
z1
*
z2
)
%
self
.
p
return
(
x3
,
y3
,
z3
)
def
mul
(
self
,
ps
):
"""Compute a (multi) point multiplication
ps is a list of (Jacobian tuple, scalar) pairs.
"""
r
=
(
0
,
1
,
0
)
for
i
in
range
(
255
,
-
1
,
-
1
):
r
=
self
.
double
(
r
)
for
(
p
,
n
)
in
ps
:
if
((
n
>>
i
)
&
1
):
r
=
self
.
add
(
r
,
p
)
return
r
SECP256K1
=
EllipticCurve
(
2
**
256
-
2
**
32
-
977
,
0
,
7
)
SECP256K1_G
=
(
0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
,
0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
,
1
)
SECP256K1_ORDER
=
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
SECP256K1_ORDER_HALF
=
SECP256K1_ORDER
//
2
# Thx to Sam Devlin for the ctypes magic 64-bit fix.
def
_check_result
(
val
,
func
,
args
):
if
val
==
0
:
raise
ValueError
else
:
return
ctypes
.
c_void_p
(
val
)
ssl
.
EC_KEY_new_by_curve_name
.
restype
=
ctypes
.
c_void_p
ssl
.
EC_KEY_new_by_curve_name
.
errcheck
=
_check_result