commit
05f2f09043
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,4 +3,5 @@ build/
|
||||
dist/
|
||||
.github/
|
||||
pybtc.egg-info/
|
||||
*.pyc
|
||||
|
||||
|
||||
33
README.md
33
README.md
@ -1,8 +1,6 @@
|
||||
## Python bitcoin library modified for FLO
|
||||
|
||||
|
||||
### Installation
|
||||
|
||||
To install pyflo,
|
||||
|
||||
$ git clone https://github.com/ranchimall/pyflo
|
||||
@ -20,10 +18,10 @@ To install pyflo,
|
||||
Every message sent to the Blockchain is in hash format, and not in plain string. So we convert the message we are signing into a SHA256 hash before.
|
||||
|
||||
```
|
||||
>>> import pybtc
|
||||
>>> import pyflo
|
||||
|
||||
# ADDRESS GENERATION
|
||||
>>> a = pybtc.Address(address_type="P2PKH")
|
||||
>>> a = pyflo.Address(address_type="P2PKH")
|
||||
>>> a.address
|
||||
'FTP7LL7QjhgKfqYX1pis18bCqEpZaGSRzZ'
|
||||
>>> a.private_key.wif
|
||||
@ -34,13 +32,13 @@ Every message sent to the Blockchain is in hash format, and not in plain string.
|
||||
'033c30b269e2d5df229f3f0ce294b19c4f0a3a8d12280415ce41e7bd3784a619c4'
|
||||
|
||||
# CONVERT MESSAGE INTO SHA-256 HASH
|
||||
>>> pybtc.sha256(b'vivek'.hex())
|
||||
>>> pyflo.sha256(b'vivek'.hex())
|
||||
b'\xa3\xdas\x97e\x01\x81,\xd7\xb8!\xa2\x0b\xfb\t\xaf\nj\x89\x1eA\x9c\xdf\xb7a\xfb\x19\xa9,\x91BB'
|
||||
>>> pybtc.sha256(b'vivek'.hex()).hex()
|
||||
>>> pyflo.sha256(b'vivek'.hex()).hex()
|
||||
'a3da73976501812cd7b821a20bfb09af0a6a891e419cdfb761fb19a92c914242'
|
||||
>>> msg = b'vivek'.hex()
|
||||
>>> msg_hash_hex = sha256(msg).hex()
|
||||
>>> msg_hash_bytes = sha256(msg)
|
||||
>>> msg_hash_hex = pyflo.sha256(msg).hex()
|
||||
>>> msg_hash_bytes = pyflo.sha256(msg)
|
||||
>>> msg
|
||||
'766976656b'
|
||||
>>> msg_hash_hex
|
||||
@ -49,10 +47,19 @@ b'\xa3\xdas\x97e\x01\x81,\xd7\xb8!\xa2\x0b\xfb\t\xaf\nj\x89\x1eA\x9c\xdf\xb7a\xf
|
||||
b'\xa3\xdas\x97e\x01\x81,\xd7\xb8!\xa2\x0b\xfb\t\xaf\nj\x89\x1eA\x9c\xdf\xb7a\xfb\x19\xa9,\x91BB'
|
||||
|
||||
# SIGN AND VERIFY THE MESSAGE
|
||||
>>> sig_msg_hex = pybtc.sign_message(msg_hash_hex, a.private_key.wif)
|
||||
>>> pybtc.verify_signature(sig_msg_hex, a.public_key.hex, msg_hash_hex)
|
||||
>>> sig_msg_hex = pyflo.sign_message(msg_hash_hex, a.private_key.wif)
|
||||
>>> pyflo.verify_signature(sig_msg_hex, a.public_key.hex, msg_hash_hex)
|
||||
True
|
||||
|
||||
# SIGN AND VERIFY MESSAGE IN STANDARD OPERATION
|
||||
>> pyflo.sign_message_standard_ops('vivek', a.private_key.wif)
|
||||
'3045022039747449a6fbac008d04d763a3a62f2261d1c5a35ee6a21a8354d8757d27593802210085a4d4b9886de6d06c3563c97160c8d70f492ce56f9e00dbcd7276004369402e'
|
||||
>> sig_msg_hex = pyflo.sign_message_standard_ops('vivek', a.private_key.wif)
|
||||
|
||||
# To verify the above signature, run the following in the console of any Standard Ops app
|
||||
>> sign_msg_hex = '3045022039747449a6fbac008d04d763a3a62f2261d1c5a35ee6a21a8354d8757d27593802210085a4d4b9886de6d06c3563c97160c8d70f492ce56f9e00dbcd7276004369402e'
|
||||
>> a.public_key.hex = '033c30b269e2d5df229f3f0ce294b19c4f0a3a8d12280415ce41e7bd3784a619c4'
|
||||
>> floCrypto.verifySign('vivek', sig_msg_hex, a.public_key.hex)
|
||||
true
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
from pybtc.constants import *
|
||||
from pybtc.opcodes import *
|
||||
from pybtc.consensus import *
|
||||
from pybtc.functions import *
|
||||
|
||||
|
||||
from .transaction import *
|
||||
from .block import *
|
||||
from .address import *
|
||||
from .wallet import *
|
||||
10
pyflo/__init__.py
Normal file
10
pyflo/__init__.py
Normal file
@ -0,0 +1,10 @@
|
||||
from pyflo.constants import *
|
||||
from pyflo.opcodes import *
|
||||
from pyflo.consensus import *
|
||||
from pyflo.functions import *
|
||||
|
||||
|
||||
from .transaction import *
|
||||
from .block import *
|
||||
from .address import *
|
||||
from .wallet import *
|
||||
BIN
pyflo/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
pyflo/__pycache__/__init__.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/__pycache__/address.cpython-38.pyc
Normal file
BIN
pyflo/__pycache__/address.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/__pycache__/block.cpython-38.pyc
Normal file
BIN
pyflo/__pycache__/block.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/__pycache__/consensus.cpython-38.pyc
Normal file
BIN
pyflo/__pycache__/consensus.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/__pycache__/constants.cpython-38.pyc
Normal file
BIN
pyflo/__pycache__/constants.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/__pycache__/opcodes.cpython-38.pyc
Normal file
BIN
pyflo/__pycache__/opcodes.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/__pycache__/transaction.cpython-38.pyc
Normal file
BIN
pyflo/__pycache__/transaction.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/__pycache__/wallet.cpython-38.pyc
Normal file
BIN
pyflo/__pycache__/wallet.cpython-38.pyc
Normal file
Binary file not shown.
@ -1,10 +1,10 @@
|
||||
from pybtc.constants import *
|
||||
from pybtc.opcodes import *
|
||||
from pybtc.functions.tools import bytes_from_hex, int_to_var_int
|
||||
from pybtc.functions.script import op_push_data, decode_script
|
||||
from pybtc.functions.hash import hash160, sha256
|
||||
from pybtc.functions.address import hash_to_address, public_key_to_p2sh_p2wpkh_script
|
||||
from pybtc.functions.key import (create_private_key,
|
||||
from pyflo.constants import *
|
||||
from pyflo.opcodes import *
|
||||
from pyflo.functions.tools import bytes_from_hex, int_to_var_int
|
||||
from pyflo.functions.script import op_push_data, decode_script
|
||||
from pyflo.functions.hash import hash160, sha256
|
||||
from pyflo.functions.address import hash_to_address, public_key_to_p2sh_p2wpkh_script
|
||||
from pyflo.functions.key import (create_private_key,
|
||||
private_key_to_wif,
|
||||
is_wif_valid,
|
||||
wif_to_private_key,
|
||||
@ -1,9 +1,9 @@
|
||||
from struct import unpack, pack
|
||||
from io import BytesIO
|
||||
from pybtc.functions.block import bits_to_target, target_to_difficulty
|
||||
from pybtc.functions.hash import double_sha256
|
||||
from pybtc.functions.tools import var_int_to_int, read_var_int, var_int_len, rh2s
|
||||
from pybtc.transaction import Transaction
|
||||
from pyflo.functions.block import bits_to_target, target_to_difficulty
|
||||
from pyflo.functions.hash import double_sha256
|
||||
from pyflo.functions.tools import var_int_to_int, read_var_int, var_int_len, rh2s
|
||||
from pyflo.transaction import Transaction
|
||||
|
||||
|
||||
class Block(dict):
|
||||
6
pyflo/ellipticcurve/__init__.py
Normal file
6
pyflo/ellipticcurve/__init__.py
Normal file
@ -0,0 +1,6 @@
|
||||
from .utils.compatibility import *
|
||||
from .privateKey import PrivateKey
|
||||
from .publicKey import PublicKey
|
||||
from .signature import Signature
|
||||
from .utils.file import File
|
||||
from .ecdsa import Ecdsa
|
||||
BIN
pyflo/ellipticcurve/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
pyflo/ellipticcurve/__pycache__/__init__.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/ellipticcurve/__pycache__/curve.cpython-38.pyc
Normal file
BIN
pyflo/ellipticcurve/__pycache__/curve.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/ellipticcurve/__pycache__/ecdsa.cpython-38.pyc
Normal file
BIN
pyflo/ellipticcurve/__pycache__/ecdsa.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/ellipticcurve/__pycache__/math.cpython-38.pyc
Normal file
BIN
pyflo/ellipticcurve/__pycache__/math.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/ellipticcurve/__pycache__/point.cpython-38.pyc
Normal file
BIN
pyflo/ellipticcurve/__pycache__/point.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/ellipticcurve/__pycache__/privateKey.cpython-38.pyc
Normal file
BIN
pyflo/ellipticcurve/__pycache__/privateKey.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/ellipticcurve/__pycache__/publicKey.cpython-38.pyc
Normal file
BIN
pyflo/ellipticcurve/__pycache__/publicKey.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/ellipticcurve/__pycache__/signature.cpython-38.pyc
Normal file
BIN
pyflo/ellipticcurve/__pycache__/signature.cpython-38.pyc
Normal file
Binary file not shown.
79
pyflo/ellipticcurve/curve.py
Normal file
79
pyflo/ellipticcurve/curve.py
Normal file
@ -0,0 +1,79 @@
|
||||
#
|
||||
# Elliptic Curve Equation
|
||||
#
|
||||
# y^2 = x^3 + A*x + B (mod P)
|
||||
#
|
||||
from .point import Point
|
||||
|
||||
|
||||
class CurveFp:
|
||||
|
||||
def __init__(self, A, B, P, N, Gx, Gy, name, oid, nistName=None):
|
||||
self.A = A
|
||||
self.B = B
|
||||
self.P = P
|
||||
self.N = N
|
||||
self.G = Point(Gx, Gy)
|
||||
self.name = name
|
||||
self.nistName = nistName
|
||||
self.oid = oid # ASN.1 Object Identifier
|
||||
|
||||
def contains(self, p):
|
||||
"""
|
||||
Verify if the point `p` is on the curve
|
||||
|
||||
:param p: Point p = Point(x, y)
|
||||
:return: boolean
|
||||
"""
|
||||
if not 0 <= p.x <= self.P - 1:
|
||||
return False
|
||||
if not 0 <= p.y <= self.P - 1:
|
||||
return False
|
||||
if (p.y**2 - (p.x**3 + self.A * p.x + self.B)) % self.P != 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
def length(self):
|
||||
return (1 + len("%x" % self.N)) // 2
|
||||
|
||||
|
||||
secp256k1 = CurveFp(
|
||||
name="secp256k1",
|
||||
A=0x0000000000000000000000000000000000000000000000000000000000000000,
|
||||
B=0x0000000000000000000000000000000000000000000000000000000000000007,
|
||||
P=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
|
||||
N=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141,
|
||||
Gx=0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
|
||||
Gy=0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8,
|
||||
oid=[1, 3, 132, 0, 10]
|
||||
)
|
||||
|
||||
prime256v1 = CurveFp(
|
||||
name="prime256v1",
|
||||
nistName="P-256",
|
||||
A=0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc,
|
||||
B=0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b,
|
||||
P=0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff,
|
||||
N=0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551,
|
||||
Gx=0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296,
|
||||
Gy=0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5,
|
||||
oid=[1, 2, 840, 10045, 3, 1, 7],
|
||||
)
|
||||
|
||||
p256 = prime256v1
|
||||
|
||||
supportedCurves = [
|
||||
secp256k1,
|
||||
prime256v1,
|
||||
]
|
||||
|
||||
_curvesByOid = {tuple(curve.oid): curve for curve in supportedCurves}
|
||||
|
||||
|
||||
def getCurveByOid(oid):
|
||||
if oid not in _curvesByOid:
|
||||
raise Exception("Unknown curve with oid {oid}; The following are registered: {names}".format(
|
||||
oid=".".join([str(number) for number in oid]),
|
||||
names=", ".join([curve.name for curve in supportedCurves]),
|
||||
))
|
||||
return _curvesByOid[oid]
|
||||
46
pyflo/ellipticcurve/ecdsa.py
Normal file
46
pyflo/ellipticcurve/ecdsa.py
Normal file
@ -0,0 +1,46 @@
|
||||
from hashlib import sha256
|
||||
from .signature import Signature
|
||||
from .math import Math
|
||||
from .utils.integer import RandomInteger
|
||||
from .utils.binary import numberFromByteString
|
||||
from .utils.compatibility import *
|
||||
|
||||
|
||||
class Ecdsa:
|
||||
|
||||
@classmethod
|
||||
def sign(cls, message, privateKey, hashfunc=sha256):
|
||||
byteMessage = hashfunc(toBytes(message)).digest()
|
||||
numberMessage = numberFromByteString(byteMessage)
|
||||
curve = privateKey.curve
|
||||
|
||||
r, s, randSignPoint = 0, 0, None
|
||||
while r == 0 or s == 0:
|
||||
randNum = RandomInteger.between(1, curve.N - 1)
|
||||
randSignPoint = Math.multiply(curve.G, n=randNum, A=curve.A, P=curve.P, N=curve.N)
|
||||
r = randSignPoint.x % curve.N
|
||||
s = ((numberMessage + r * privateKey.secret) * (Math.inv(randNum, curve.N))) % curve.N
|
||||
recoveryId = randSignPoint.y & 1
|
||||
if randSignPoint.y > curve.N:
|
||||
recoveryId += 2
|
||||
|
||||
return Signature(r=r, s=s, recoveryId=recoveryId)
|
||||
|
||||
@classmethod
|
||||
def verify(cls, message, signature, publicKey, hashfunc=sha256):
|
||||
byteMessage = hashfunc(toBytes(message)).digest()
|
||||
numberMessage = numberFromByteString(byteMessage)
|
||||
curve = publicKey.curve
|
||||
r = signature.r
|
||||
s = signature.s
|
||||
if not 1 <= r <= curve.N - 1:
|
||||
return False
|
||||
if not 1 <= s <= curve.N - 1:
|
||||
return False
|
||||
inv = Math.inv(s, curve.N)
|
||||
u1 = Math.multiply(curve.G, n=(numberMessage * inv) % curve.N, N=curve.N, A=curve.A, P=curve.P)
|
||||
u2 = Math.multiply(publicKey.point, n=(r * inv) % curve.N, N=curve.N, A=curve.A, P=curve.P)
|
||||
v = Math.add(u1, u2, A=curve.A, P=curve.P)
|
||||
if v.isAtInfinity():
|
||||
return False
|
||||
return v.x % curve.N == r
|
||||
177
pyflo/ellipticcurve/math.py
Normal file
177
pyflo/ellipticcurve/math.py
Normal file
@ -0,0 +1,177 @@
|
||||
from .point import Point
|
||||
|
||||
|
||||
class Math:
|
||||
|
||||
@classmethod
|
||||
def multiply(cls, p, n, N, A, P):
|
||||
"""
|
||||
Fast way to multily point and scalar in elliptic curves
|
||||
|
||||
:param p: First Point to mutiply
|
||||
:param n: Scalar to mutiply
|
||||
:param N: Order of the elliptic curve
|
||||
:param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
|
||||
:param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
|
||||
:return: Point that represents the sum of First and Second Point
|
||||
"""
|
||||
return cls._fromJacobian(
|
||||
cls._jacobianMultiply(cls._toJacobian(p), n, N, A, P), P
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def add(cls, p, q, A, P):
|
||||
"""
|
||||
Fast way to add two points in elliptic curves
|
||||
|
||||
:param p: First Point you want to add
|
||||
:param q: Second Point you want to add
|
||||
:param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
|
||||
:param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
|
||||
:return: Point that represents the sum of First and Second Point
|
||||
"""
|
||||
return cls._fromJacobian(
|
||||
cls._jacobianAdd(cls._toJacobian(p), cls._toJacobian(q), A, P), P,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def inv(cls, x, n):
|
||||
"""
|
||||
Extended Euclidean Algorithm. It's the 'division' in elliptic curves
|
||||
|
||||
:param x: Divisor
|
||||
:param n: Mod for division
|
||||
:return: Value representing the division
|
||||
"""
|
||||
if x == 0:
|
||||
return 0
|
||||
|
||||
lm = 1
|
||||
hm = 0
|
||||
low = x % n
|
||||
high = n
|
||||
|
||||
while low > 1:
|
||||
r = high // low
|
||||
nm = hm - lm * r
|
||||
nw = high - low * r
|
||||
high = low
|
||||
hm = lm
|
||||
low = nw
|
||||
lm = nm
|
||||
|
||||
return lm % n
|
||||
|
||||
@classmethod
|
||||
def _toJacobian(cls, p):
|
||||
"""
|
||||
Convert point to Jacobian coordinates
|
||||
|
||||
:param p: First Point you want to add
|
||||
:return: Point in Jacobian coordinates
|
||||
"""
|
||||
return Point(p.x, p.y, 1)
|
||||
|
||||
@classmethod
|
||||
def _fromJacobian(cls, p, P):
|
||||
"""
|
||||
Convert point back from Jacobian coordinates
|
||||
|
||||
:param p: First Point you want to add
|
||||
:param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
|
||||
:return: Point in default coordinates
|
||||
"""
|
||||
z = cls.inv(p.z, P)
|
||||
x = (p.x * z ** 2) % P
|
||||
y = (p.y * z ** 3) % P
|
||||
|
||||
return Point(x, y, 0)
|
||||
|
||||
@classmethod
|
||||
def _jacobianDouble(cls, p, A, P):
|
||||
"""
|
||||
Double a point in elliptic curves
|
||||
|
||||
:param p: Point you want to double
|
||||
:param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
|
||||
:param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
|
||||
:return: Point that represents the sum of First and Second Point
|
||||
"""
|
||||
if p.y == 0:
|
||||
return Point(0, 0, 0)
|
||||
|
||||
ysq = (p.y ** 2) % P
|
||||
S = (4 * p.x * ysq) % P
|
||||
M = (3 * p.x ** 2 + A * p.z ** 4) % P
|
||||
nx = (M**2 - 2 * S) % P
|
||||
ny = (M * (S - nx) - 8 * ysq ** 2) % P
|
||||
nz = (2 * p.y * p.z) % P
|
||||
|
||||
return Point(nx, ny, nz)
|
||||
|
||||
@classmethod
|
||||
def _jacobianAdd(cls, p, q, A, P):
|
||||
"""
|
||||
Add two points in elliptic curves
|
||||
|
||||
:param p: First Point you want to add
|
||||
:param q: Second Point you want to add
|
||||
:param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
|
||||
:param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
|
||||
:return: Point that represents the sum of First and Second Point
|
||||
"""
|
||||
if p.y == 0:
|
||||
return q
|
||||
if q.y == 0:
|
||||
return p
|
||||
|
||||
U1 = (p.x * q.z ** 2) % P
|
||||
U2 = (q.x * p.z ** 2) % P
|
||||
S1 = (p.y * q.z ** 3) % P
|
||||
S2 = (q.y * p.z ** 3) % P
|
||||
|
||||
if U1 == U2:
|
||||
if S1 != S2:
|
||||
return Point(0, 0, 1)
|
||||
return cls._jacobianDouble(p, A, P)
|
||||
|
||||
H = U2 - U1
|
||||
R = S2 - S1
|
||||
H2 = (H * H) % P
|
||||
H3 = (H * H2) % P
|
||||
U1H2 = (U1 * H2) % P
|
||||
nx = (R ** 2 - H3 - 2 * U1H2) % P
|
||||
ny = (R * (U1H2 - nx) - S1 * H3) % P
|
||||
nz = (H * p.z * q.z) % P
|
||||
|
||||
return Point(nx, ny, nz)
|
||||
|
||||
@classmethod
|
||||
def _jacobianMultiply(cls, p, n, N, A, P):
|
||||
"""
|
||||
Multily point and scalar in elliptic curves
|
||||
|
||||
:param p: First Point to mutiply
|
||||
:param n: Scalar to mutiply
|
||||
:param N: Order of the elliptic curve
|
||||
:param P: Prime number in the module of the equation Y^2 = X^3 + A*X + B (mod p)
|
||||
:param A: Coefficient of the first-order term of the equation Y^2 = X^3 + A*X + B (mod p)
|
||||
:return: Point that represents the sum of First and Second Point
|
||||
"""
|
||||
if p.y == 0 or n == 0:
|
||||
return Point(0, 0, 1)
|
||||
|
||||
if n == 1:
|
||||
return p
|
||||
|
||||
if n < 0 or n >= N:
|
||||
return cls._jacobianMultiply(p, n % N, N, A, P)
|
||||
|
||||
if (n % 2) == 0:
|
||||
return cls._jacobianDouble(
|
||||
cls._jacobianMultiply(p, n // 2, N, A, P), A, P
|
||||
)
|
||||
|
||||
return cls._jacobianAdd(
|
||||
cls._jacobianDouble(cls._jacobianMultiply(p, n // 2, N, A, P), A, P), p, A, P
|
||||
)
|
||||
14
pyflo/ellipticcurve/point.py
Normal file
14
pyflo/ellipticcurve/point.py
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
class Point:
|
||||
|
||||
def __init__(self, x=0, y=0, z=0):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.z = z
|
||||
|
||||
def __str__(self):
|
||||
return "({x}, {y}, {z})".format(x=self.x, y=self.y, z=self.z)
|
||||
|
||||
def isAtInfinity(self):
|
||||
return self.y == 0
|
||||
72
pyflo/ellipticcurve/privateKey.py
Normal file
72
pyflo/ellipticcurve/privateKey.py
Normal file
@ -0,0 +1,72 @@
|
||||
from .math import Math
|
||||
from .utils.integer import RandomInteger
|
||||
from .utils.pem import getPemContent, createPem
|
||||
from .utils.binary import hexFromByteString, byteStringFromHex, intFromHex, base64FromByteString, byteStringFromBase64
|
||||
from .utils.der import hexFromInt, parse, encodeConstructed, DerFieldType, encodePrimitive
|
||||
from .curve import secp256k1, getCurveByOid
|
||||
from .publicKey import PublicKey
|
||||
|
||||
|
||||
class PrivateKey:
|
||||
|
||||
def __init__(self, curve=secp256k1, secret=None):
|
||||
self.curve = curve
|
||||
self.secret = secret or RandomInteger.between(1, curve.N - 1)
|
||||
|
||||
def publicKey(self):
|
||||
curve = self.curve
|
||||
publicPoint = Math.multiply(
|
||||
p=curve.G,
|
||||
n=self.secret,
|
||||
N=curve.N,
|
||||
A=curve.A,
|
||||
P=curve.P,
|
||||
)
|
||||
return PublicKey(point=publicPoint, curve=curve)
|
||||
|
||||
def toString(self):
|
||||
return hexFromInt(self.secret)
|
||||
|
||||
def toDer(self):
|
||||
publicKeyString = self.publicKey().toString(encoded=True)
|
||||
hexadecimal = encodeConstructed(
|
||||
encodePrimitive(DerFieldType.integer, 1),
|
||||
encodePrimitive(DerFieldType.octetString, hexFromInt(self.secret)),
|
||||
encodePrimitive(DerFieldType.oidContainer, encodePrimitive(DerFieldType.object, self.curve.oid)),
|
||||
encodePrimitive(DerFieldType.publicKeyPointContainer, encodePrimitive(DerFieldType.bitString, publicKeyString))
|
||||
)
|
||||
return byteStringFromHex(hexadecimal)
|
||||
|
||||
def toPem(self):
|
||||
der = self.toDer()
|
||||
return createPem(content=base64FromByteString(der), template=_pemTemplate)
|
||||
|
||||
@classmethod
|
||||
def fromPem(cls, string):
|
||||
privateKeyPem = getPemContent(pem=string, template=_pemTemplate)
|
||||
return cls.fromDer(byteStringFromBase64(privateKeyPem))
|
||||
|
||||
@classmethod
|
||||
def fromDer(cls, string):
|
||||
hexadecimal = hexFromByteString(string)
|
||||
privateKeyFlag, secretHex, curveData, publicKeyString = parse(hexadecimal)[0]
|
||||
if privateKeyFlag != 1:
|
||||
raise Exception("Private keys should start with a '1' flag, but a '{flag}' was found instead".format(
|
||||
flag=privateKeyFlag
|
||||
))
|
||||
curve = getCurveByOid(curveData[0])
|
||||
privateKey = cls.fromString(string=secretHex, curve=curve)
|
||||
if privateKey.publicKey().toString(encoded=True) != publicKeyString[0]:
|
||||
raise Exception("The public key described inside the private key file doesn't match the actual public key of the pair")
|
||||
return privateKey
|
||||
|
||||
@classmethod
|
||||
def fromString(cls, string, curve=secp256k1):
|
||||
return PrivateKey(secret=intFromHex(string), curve=curve)
|
||||
|
||||
|
||||
_pemTemplate = """
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
{content}
|
||||
-----END EC PRIVATE KEY-----
|
||||
"""
|
||||
88
pyflo/ellipticcurve/publicKey.py
Normal file
88
pyflo/ellipticcurve/publicKey.py
Normal file
@ -0,0 +1,88 @@
|
||||
from .math import Math
|
||||
from .point import Point
|
||||
from .curve import secp256k1, getCurveByOid
|
||||
from .utils.pem import getPemContent, createPem
|
||||
from .utils.der import hexFromInt, parse, DerFieldType, encodeConstructed, encodePrimitive
|
||||
from .utils.binary import hexFromByteString, byteStringFromHex, intFromHex, base64FromByteString, byteStringFromBase64
|
||||
|
||||
|
||||
class PublicKey:
|
||||
|
||||
def __init__(self, point, curve):
|
||||
self.point = point
|
||||
self.curve = curve
|
||||
|
||||
def toString(self, encoded=False):
|
||||
baseLength = 2 * self.curve.length()
|
||||
xHex = hexFromInt(self.point.x).zfill(baseLength)
|
||||
yHex = hexFromInt(self.point.y).zfill(baseLength)
|
||||
string = xHex + yHex
|
||||
if encoded:
|
||||
return "0004" + string
|
||||
return string
|
||||
|
||||
def toDer(self):
|
||||
hexadecimal = encodeConstructed(
|
||||
encodeConstructed(
|
||||
encodePrimitive(DerFieldType.object, _ecdsaPublicKeyOid),
|
||||
encodePrimitive(DerFieldType.object, self.curve.oid),
|
||||
),
|
||||
encodePrimitive(DerFieldType.bitString, self.toString(encoded=True)),
|
||||
)
|
||||
return byteStringFromHex(hexadecimal)
|
||||
|
||||
def toPem(self):
|
||||
der = self.toDer()
|
||||
return createPem(content=base64FromByteString(der), template=_pemTemplate)
|
||||
|
||||
@classmethod
|
||||
def fromPem(cls, string):
|
||||
publicKeyPem = getPemContent(pem=string, template=_pemTemplate)
|
||||
return cls.fromDer(byteStringFromBase64(publicKeyPem))
|
||||
|
||||
@classmethod
|
||||
def fromDer(cls, string):
|
||||
hexadecimal = hexFromByteString(string)
|
||||
curveData, pointString = parse(hexadecimal)[0]
|
||||
publicKeyOid, curveOid = curveData
|
||||
if publicKeyOid != _ecdsaPublicKeyOid:
|
||||
raise Exception("The Public Key Object Identifier (OID) should be {ecdsaPublicKeyOid}, but {actualOid} was found instead".format(
|
||||
ecdsaPublicKeyOid=_ecdsaPublicKeyOid,
|
||||
actualOid=publicKeyOid,
|
||||
))
|
||||
curve = getCurveByOid(curveOid)
|
||||
return cls.fromString(string=pointString, curve=curve)
|
||||
|
||||
@classmethod
|
||||
def fromString(cls, string, curve=secp256k1, validatePoint=True):
|
||||
baseLength = 2 * curve.length()
|
||||
if len(string) > 2 * baseLength and string[:4] == "0004":
|
||||
string = string[4:]
|
||||
|
||||
xs = string[:baseLength]
|
||||
ys = string[baseLength:]
|
||||
|
||||
p = Point(
|
||||
x=intFromHex(xs),
|
||||
y=intFromHex(ys),
|
||||
)
|
||||
publicKey = PublicKey(point=p, curve=curve)
|
||||
if not validatePoint:
|
||||
return publicKey
|
||||
if p.isAtInfinity():
|
||||
raise Exception("Public Key point is at infinity")
|
||||
if not curve.contains(p):
|
||||
raise Exception("Point ({x},{y}) is not valid for curve {name}".format(x=p.x, y=p.y, name=curve.name))
|
||||
if not Math.multiply(p=p, n=curve.N, N=curve.N, A=curve.A, P=curve.P).isAtInfinity():
|
||||
raise Exception("Point ({x},{y}) * {name}.N is not at infinity".format(x=p.x, y=p.y, name=curve.name))
|
||||
return publicKey
|
||||
|
||||
|
||||
_ecdsaPublicKeyOid = (1, 2, 840, 10045, 2, 1)
|
||||
|
||||
|
||||
_pemTemplate = """
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
{content}
|
||||
-----END PUBLIC KEY-----
|
||||
"""
|
||||
48
pyflo/ellipticcurve/signature.py
Normal file
48
pyflo/ellipticcurve/signature.py
Normal file
@ -0,0 +1,48 @@
|
||||
from .utils.compatibility import *
|
||||
from .utils.der import parse, encodeConstructed, encodePrimitive, DerFieldType
|
||||
from .utils.binary import hexFromByteString, byteStringFromHex, base64FromByteString, byteStringFromBase64
|
||||
|
||||
|
||||
class Signature:
|
||||
|
||||
def __init__(self, r, s, recoveryId=None):
|
||||
self.r = r
|
||||
self.s = s
|
||||
self.recoveryId = recoveryId
|
||||
|
||||
def toDer(self, withRecoveryId=False):
|
||||
hexadecimal = self._toString()
|
||||
encodedSequence = byteStringFromHex(hexadecimal)
|
||||
if not withRecoveryId:
|
||||
return encodedSequence
|
||||
return toBytes(chr(27 + self.recoveryId)) + encodedSequence
|
||||
|
||||
def toBase64(self, withRecoveryId=False):
|
||||
return base64FromByteString(self.toDer(withRecoveryId))
|
||||
|
||||
@classmethod
|
||||
def fromDer(cls, string, recoveryByte=False):
|
||||
recoveryId = None
|
||||
if recoveryByte:
|
||||
recoveryId = string[0] if isinstance(string[0], intTypes) else ord(string[0])
|
||||
recoveryId -= 27
|
||||
string = string[1:]
|
||||
|
||||
hexadecimal = hexFromByteString(string)
|
||||
return cls._fromString(string=hexadecimal, recoveryId=recoveryId)
|
||||
|
||||
@classmethod
|
||||
def fromBase64(cls, string, recoveryByte=False):
|
||||
der = byteStringFromBase64(string)
|
||||
return cls.fromDer(der, recoveryByte)
|
||||
|
||||
def _toString(self):
|
||||
return encodeConstructed(
|
||||
encodePrimitive(DerFieldType.integer, self.r),
|
||||
encodePrimitive(DerFieldType.integer, self.s),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _fromString(cls, string, recoveryId=None):
|
||||
r, s = parse(string)[0]
|
||||
return Signature(r=r, s=s, recoveryId=recoveryId)
|
||||
0
pyflo/ellipticcurve/utils/__init__.py
Normal file
0
pyflo/ellipticcurve/utils/__init__.py
Normal file
BIN
pyflo/ellipticcurve/utils/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
pyflo/ellipticcurve/utils/__pycache__/__init__.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/ellipticcurve/utils/__pycache__/binary.cpython-38.pyc
Normal file
BIN
pyflo/ellipticcurve/utils/__pycache__/binary.cpython-38.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
pyflo/ellipticcurve/utils/__pycache__/der.cpython-38.pyc
Normal file
BIN
pyflo/ellipticcurve/utils/__pycache__/der.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/ellipticcurve/utils/__pycache__/file.cpython-38.pyc
Normal file
BIN
pyflo/ellipticcurve/utils/__pycache__/file.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/ellipticcurve/utils/__pycache__/integer.cpython-38.pyc
Normal file
BIN
pyflo/ellipticcurve/utils/__pycache__/integer.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/ellipticcurve/utils/__pycache__/oid.cpython-38.pyc
Normal file
BIN
pyflo/ellipticcurve/utils/__pycache__/oid.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/ellipticcurve/utils/__pycache__/pem.cpython-38.pyc
Normal file
BIN
pyflo/ellipticcurve/utils/__pycache__/pem.cpython-38.pyc
Normal file
Binary file not shown.
37
pyflo/ellipticcurve/utils/binary.py
Normal file
37
pyflo/ellipticcurve/utils/binary.py
Normal file
@ -0,0 +1,37 @@
|
||||
from base64 import b64encode, b64decode
|
||||
from .compatibility import safeHexFromBinary, safeBinaryFromHex, toString
|
||||
|
||||
|
||||
def hexFromInt(number):
|
||||
hexadecimal = "{0:x}".format(number)
|
||||
if len(hexadecimal) % 2 == 1:
|
||||
hexadecimal = "0" + hexadecimal
|
||||
return hexadecimal
|
||||
|
||||
|
||||
def intFromHex(hexadecimal):
|
||||
return int(hexadecimal, 16)
|
||||
|
||||
|
||||
def hexFromByteString(byteString):
|
||||
return safeHexFromBinary(byteString)
|
||||
|
||||
|
||||
def byteStringFromHex(hexadecimal):
|
||||
return safeBinaryFromHex(hexadecimal)
|
||||
|
||||
|
||||
def numberFromByteString(byteString):
|
||||
return intFromHex(hexFromByteString(byteString))
|
||||
|
||||
|
||||
def base64FromByteString(byteString):
|
||||
return toString(b64encode(byteString))
|
||||
|
||||
|
||||
def byteStringFromBase64(base64String):
|
||||
return b64decode(base64String)
|
||||
|
||||
|
||||
def bitsFromHex(hexadecimal):
|
||||
return format(intFromHex(hexadecimal), 'b').zfill(4 * len(hexadecimal))
|
||||
40
pyflo/ellipticcurve/utils/compatibility.py
Normal file
40
pyflo/ellipticcurve/utils/compatibility.py
Normal file
@ -0,0 +1,40 @@
|
||||
from sys import version_info as pyVersion
|
||||
from binascii import hexlify, unhexlify
|
||||
|
||||
|
||||
if pyVersion.major == 3:
|
||||
# py3 constants and conversion functions
|
||||
|
||||
stringTypes = (str,)
|
||||
intTypes = (int, float)
|
||||
|
||||
def toString(string, encoding="utf-8"):
|
||||
return string.decode(encoding)
|
||||
|
||||
def toBytes(string, encoding="utf-8"):
|
||||
return string.encode(encoding)
|
||||
|
||||
def safeBinaryFromHex(hexadecimal):
|
||||
if len(hexadecimal) % 2 == 1:
|
||||
hexadecimal = "0" + hexadecimal
|
||||
return unhexlify(hexadecimal)
|
||||
|
||||
def safeHexFromBinary(byteString):
|
||||
return toString(hexlify(byteString))
|
||||
else:
|
||||
# py2 constants and conversion functions
|
||||
|
||||
stringTypes = (str, unicode)
|
||||
intTypes = (int, float, long)
|
||||
|
||||
def toString(string, encoding="utf-8"):
|
||||
return string
|
||||
|
||||
def toBytes(string, encoding="utf-8"):
|
||||
return string
|
||||
|
||||
def safeBinaryFromHex(hexadecimal):
|
||||
return unhexlify(hexadecimal)
|
||||
|
||||
def safeHexFromBinary(byteString):
|
||||
return hexlify(byteString)
|
||||
159
pyflo/ellipticcurve/utils/der.py
Normal file
159
pyflo/ellipticcurve/utils/der.py
Normal file
@ -0,0 +1,159 @@
|
||||
from datetime import datetime
|
||||
from .oid import oidToHex, oidFromHex
|
||||
from .binary import hexFromInt, intFromHex, byteStringFromHex, bitsFromHex
|
||||
|
||||
|
||||
class DerFieldType:
|
||||
|
||||
integer = "integer"
|
||||
bitString = "bitString"
|
||||
octetString = "octetString"
|
||||
null = "null"
|
||||
object = "object"
|
||||
printableString = "printableString"
|
||||
utcTime = "utcTime"
|
||||
sequence = "sequence"
|
||||
set = "set"
|
||||
oidContainer = "oidContainer"
|
||||
publicKeyPointContainer = "publicKeyPointContainer"
|
||||
|
||||
|
||||
_hexTagToType = {
|
||||
"02": DerFieldType.integer,
|
||||
"03": DerFieldType.bitString,
|
||||
"04": DerFieldType.octetString,
|
||||
"05": DerFieldType.null,
|
||||
"06": DerFieldType.object,
|
||||
"13": DerFieldType.printableString,
|
||||
"17": DerFieldType.utcTime,
|
||||
"30": DerFieldType.sequence,
|
||||
"31": DerFieldType.set,
|
||||
"a0": DerFieldType.oidContainer,
|
||||
"a1": DerFieldType.publicKeyPointContainer,
|
||||
}
|
||||
_typeToHexTag = {v: k for k, v in _hexTagToType.items()}
|
||||
|
||||
|
||||
def encodeConstructed(*encodedValues):
|
||||
return encodePrimitive(DerFieldType.sequence, "".join(encodedValues))
|
||||
|
||||
|
||||
def encodePrimitive(tagType, value):
|
||||
if tagType == DerFieldType.integer:
|
||||
value = _encodeInteger(value)
|
||||
if tagType == DerFieldType.object:
|
||||
value = oidToHex(value)
|
||||
return "{tag}{size}{value}".format(tag=_typeToHexTag[tagType], size=_generateLengthBytes(value), value=value)
|
||||
|
||||
|
||||
def parse(hexadecimal):
|
||||
if not hexadecimal:
|
||||
return []
|
||||
typeByte, hexadecimal = hexadecimal[:2], hexadecimal[2:]
|
||||
length, lengthBytes = _readLengthBytes(hexadecimal)
|
||||
content, hexadecimal = hexadecimal[lengthBytes: lengthBytes + length], hexadecimal[lengthBytes + length:]
|
||||
if len(content) < length:
|
||||
raise Exception("missing bytes in DER parse")
|
||||
|
||||
tagData = _getTagData(typeByte)
|
||||
if tagData["isConstructed"]:
|
||||
content = parse(content)
|
||||
|
||||
valueParser = {
|
||||
DerFieldType.null: _parseNull,
|
||||
DerFieldType.object: _parseOid,
|
||||
DerFieldType.utcTime: _parseTime,
|
||||
DerFieldType.integer: _parseInteger,
|
||||
DerFieldType.printableString: _parseString,
|
||||
}.get(tagData["type"], _parseAny)
|
||||
return [valueParser(content)] + parse(hexadecimal)
|
||||
|
||||
|
||||
def _parseAny(hexadecimal):
|
||||
return hexadecimal
|
||||
|
||||
|
||||
def _parseOid(hexadecimal):
|
||||
return tuple(oidFromHex(hexadecimal))
|
||||
|
||||
|
||||
def _parseTime(hexadecimal):
|
||||
string = _parseString(hexadecimal)
|
||||
return datetime.strptime(string, "%y%m%d%H%M%SZ")
|
||||
|
||||
|
||||
def _parseString(hexadecimal):
|
||||
return byteStringFromHex(hexadecimal).decode()
|
||||
|
||||
|
||||
def _parseNull(_content):
|
||||
return None
|
||||
|
||||
|
||||
def _parseInteger(hexadecimal):
|
||||
integer = intFromHex(hexadecimal)
|
||||
bits = bitsFromHex(hexadecimal[0])
|
||||
if bits[0] == "0": # negative numbers are encoded using two's complement
|
||||
return integer
|
||||
bitCount = 4 * len(hexadecimal)
|
||||
return integer - (2 ** bitCount)
|
||||
|
||||
|
||||
def _encodeInteger(number):
|
||||
hexadecimal = hexFromInt(abs(number))
|
||||
if number < 0:
|
||||
bitCount = 4 * len(hexadecimal)
|
||||
twosComplement = (2 ** bitCount) + number
|
||||
return hexFromInt(twosComplement)
|
||||
bits = bitsFromHex(hexadecimal[0])
|
||||
if bits[0] == "1": # if first bit was left as 1, number would be parsed as a negative integer with two's complement
|
||||
hexadecimal = "00" + hexadecimal
|
||||
return hexadecimal
|
||||
|
||||
|
||||
def _readLengthBytes(hexadecimal):
|
||||
lengthBytes = 2
|
||||
lengthIndicator = intFromHex(hexadecimal[0:lengthBytes])
|
||||
isShortForm = lengthIndicator < 128 # checks if first bit of byte is 1 (a.k.a. short-form)
|
||||
if isShortForm:
|
||||
length = lengthIndicator * 2
|
||||
return length, lengthBytes
|
||||
|
||||
lengthLength = lengthIndicator - 128 # nullifies first bit of byte (only used as long-form flag)
|
||||
if lengthLength == 0:
|
||||
raise Exception("indefinite length encoding located in DER")
|
||||
lengthBytes += 2 * lengthLength
|
||||
length = intFromHex(hexadecimal[2:lengthBytes]) * 2
|
||||
return length, lengthBytes
|
||||
|
||||
|
||||
def _generateLengthBytes(hexadecimal):
|
||||
size = len(hexadecimal) // 2
|
||||
length = hexFromInt(size)
|
||||
if size < 128: # checks if first bit of byte should be 0 (a.k.a. short-form flag)
|
||||
return length.zfill(2)
|
||||
lengthLength = 128 + len(length) // 2 # +128 sets the first bit of the byte as 1 (a.k.a. long-form flag)
|
||||
return hexFromInt(lengthLength) + length
|
||||
|
||||
|
||||
def _getTagData(tag):
|
||||
bits = bitsFromHex(tag)
|
||||
bit8, bit7, bit6 = bits[:3]
|
||||
|
||||
tagClass = {
|
||||
"0": {
|
||||
"0": "universal",
|
||||
"1": "application",
|
||||
},
|
||||
"1": {
|
||||
"0": "context-specific",
|
||||
"1": "private",
|
||||
},
|
||||
}[bit8][bit7]
|
||||
isConstructed = bit6 == "1"
|
||||
|
||||
return {
|
||||
"class": tagClass,
|
||||
"isConstructed": isConstructed,
|
||||
"type": _hexTagToType.get(tag),
|
||||
}
|
||||
9
pyflo/ellipticcurve/utils/file.py
Normal file
9
pyflo/ellipticcurve/utils/file.py
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
class File:
|
||||
|
||||
@classmethod
|
||||
def read(cls, path, mode="r"):
|
||||
with open(path, mode) as blob:
|
||||
content = blob.read()
|
||||
return content
|
||||
16
pyflo/ellipticcurve/utils/integer.py
Normal file
16
pyflo/ellipticcurve/utils/integer.py
Normal file
@ -0,0 +1,16 @@
|
||||
from random import SystemRandom
|
||||
|
||||
|
||||
class RandomInteger:
|
||||
|
||||
@classmethod
|
||||
def between(cls, min, max):
|
||||
"""
|
||||
Return integer x in the range: min <= x <= max
|
||||
|
||||
:param min: minimum value of the integer
|
||||
:param max: maximum value of the integer
|
||||
:return:
|
||||
"""
|
||||
|
||||
return SystemRandom().randrange(min, max + 1)
|
||||
35
pyflo/ellipticcurve/utils/oid.py
Normal file
35
pyflo/ellipticcurve/utils/oid.py
Normal file
@ -0,0 +1,35 @@
|
||||
from .binary import intFromHex, hexFromInt
|
||||
|
||||
|
||||
def oidFromHex(hexadecimal):
|
||||
firstByte, remainingBytes = hexadecimal[:2], hexadecimal[2:]
|
||||
firstByteInt = intFromHex(firstByte)
|
||||
oid = [firstByteInt // 40, firstByteInt % 40]
|
||||
oidInt = 0
|
||||
while len(remainingBytes) > 0:
|
||||
byte, remainingBytes = remainingBytes[0:2], remainingBytes[2:]
|
||||
byteInt = intFromHex(byte)
|
||||
if byteInt >= 128:
|
||||
oidInt = (128 * oidInt) + (byteInt - 128)
|
||||
continue
|
||||
oidInt = (128 * oidInt) + byteInt
|
||||
oid.append(oidInt)
|
||||
oidInt = 0
|
||||
return oid
|
||||
|
||||
|
||||
def oidToHex(oid):
|
||||
hexadecimal = hexFromInt(40 * oid[0] + oid[1])
|
||||
for number in oid[2:]:
|
||||
hexadecimal += _oidNumberToHex(number)
|
||||
return hexadecimal
|
||||
|
||||
|
||||
def _oidNumberToHex(number):
|
||||
hexadecimal = ""
|
||||
endDelta = 0
|
||||
while number > 0:
|
||||
hexadecimal = hexFromInt((number % 128) + endDelta) + hexadecimal
|
||||
number //= 128
|
||||
endDelta = 128
|
||||
return hexadecimal or "00"
|
||||
14
pyflo/ellipticcurve/utils/pem.py
Normal file
14
pyflo/ellipticcurve/utils/pem.py
Normal file
@ -0,0 +1,14 @@
|
||||
from re import search
|
||||
|
||||
|
||||
def getPemContent(pem, template):
|
||||
pattern = template.format(content="(.*)")
|
||||
return search("".join(pattern.splitlines()), "".join(pem.splitlines())).group(1)
|
||||
|
||||
|
||||
def createPem(content, template):
|
||||
lines = [
|
||||
content[start:start + 64]
|
||||
for start in range(0, len(content), 64)
|
||||
]
|
||||
return template.format(content="\n".join(lines))
|
||||
BIN
pyflo/functions/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
pyflo/functions/__pycache__/__init__.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/functions/__pycache__/address.cpython-38.pyc
Normal file
BIN
pyflo/functions/__pycache__/address.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/functions/__pycache__/bip32.cpython-38.pyc
Normal file
BIN
pyflo/functions/__pycache__/bip32.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/functions/__pycache__/bip39_mnemonic.cpython-38.pyc
Normal file
BIN
pyflo/functions/__pycache__/bip39_mnemonic.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/functions/__pycache__/block.cpython-38.pyc
Normal file
BIN
pyflo/functions/__pycache__/block.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/functions/__pycache__/encode.cpython-38.pyc
Normal file
BIN
pyflo/functions/__pycache__/encode.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/functions/__pycache__/hash.cpython-38.pyc
Normal file
BIN
pyflo/functions/__pycache__/hash.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/functions/__pycache__/key.cpython-38.pyc
Normal file
BIN
pyflo/functions/__pycache__/key.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/functions/__pycache__/script.cpython-38.pyc
Normal file
BIN
pyflo/functions/__pycache__/script.cpython-38.pyc
Normal file
Binary file not shown.
BIN
pyflo/functions/__pycache__/tools.cpython-38.pyc
Normal file
BIN
pyflo/functions/__pycache__/tools.cpython-38.pyc
Normal file
Binary file not shown.
@ -1,9 +1,9 @@
|
||||
from pybtc.opcodes import *
|
||||
from pybtc.constants import *
|
||||
from pyflo.opcodes import *
|
||||
from pyflo.constants import *
|
||||
|
||||
from pybtc.functions.tools import bytes_from_hex
|
||||
from pybtc.functions.hash import double_sha256, hash160
|
||||
from pybtc.functions.encode import (encode_base58,
|
||||
from pyflo.functions.tools import bytes_from_hex
|
||||
from pyflo.functions.hash import double_sha256, hash160
|
||||
from pyflo.functions.encode import (encode_base58,
|
||||
rebase_8_to_5,
|
||||
bech32_polymod,
|
||||
rebase_5_to_32,
|
||||
@ -14,7 +14,7 @@ from pybtc.functions.encode import (encode_base58,
|
||||
base32charset_upcase)
|
||||
|
||||
|
||||
def hash_to_address(address_hash, testnet=False, script_hash=False, witness_version=None):
|
||||
def hash_to_address(address_hash, testnet=False, script_hash=False, witness_version=0):
|
||||
"""
|
||||
Get address from public key/script hash. In case PUBKEY, P2PKH, P2PKH public key/script hash is SHA256+RIPEMD160,
|
||||
P2WSH script hash is SHA256.
|
||||
@ -70,7 +70,7 @@ def hash_to_address(address_hash, testnet=False, script_hash=False, witness_vers
|
||||
return "%s1%s" % (hrp, rebase_5_to_32(address_hash + checksum).decode())
|
||||
|
||||
|
||||
def public_key_to_address(pubkey, testnet=False, p2sh_p2wpkh=False, witness_version=None):
|
||||
def public_key_to_address(pubkey, testnet=False, p2sh_p2wpkh=False, witness_version=0):
|
||||
"""
|
||||
Get address from public key/script hash. In case PUBKEY, P2PKH, P2PKH public key/script hash is SHA256+RIPEMD160,
|
||||
P2WSH script hash is SHA256.
|
||||
@ -1,11 +1,11 @@
|
||||
from struct import pack
|
||||
from secp256k1 import ffi, lib
|
||||
from pybtc.functions.key import private_to_public_key, private_key_to_wif
|
||||
from pybtc.functions.hash import hmac_sha512, double_sha256, hash160
|
||||
from pybtc.functions.encode import (encode_base58,
|
||||
from pyflo.functions.key import private_to_public_key, private_key_to_wif
|
||||
from pyflo.functions.hash import hmac_sha512, double_sha256, hash160
|
||||
from pyflo.functions.encode import (encode_base58,
|
||||
decode_base58_with_checksum,
|
||||
encode_base58_with_checksum)
|
||||
from pybtc.constants import *
|
||||
from pyflo.constants import *
|
||||
|
||||
|
||||
def create_master_xprivate_key(seed, testnet=False, base58=True, hex=False):
|
||||
@ -1,8 +1,8 @@
|
||||
from pybtc.constants import *
|
||||
from pyflo.constants import *
|
||||
import time
|
||||
import hashlib
|
||||
from pybtc.functions.hash import sha256
|
||||
from pybtc.functions.tools import int_from_bytes
|
||||
from pyflo.functions.hash import sha256
|
||||
from pyflo.functions.tools import int_from_bytes
|
||||
|
||||
def generate_entropy(strength=256, hex=True):
|
||||
"""
|
||||
@ -1,5 +1,5 @@
|
||||
from pybtc.functions.tools import s2rh, bytes_from_hex, int_from_bytes
|
||||
from pybtc.functions.hash import double_sha256
|
||||
from pyflo.functions.tools import s2rh, bytes_from_hex, int_from_bytes
|
||||
from pyflo.functions.hash import double_sha256
|
||||
|
||||
def merkle_root(tx_hash_list, hex=True):
|
||||
"""
|
||||
@ -1,5 +1,5 @@
|
||||
from pybtc.functions.hash import double_sha256
|
||||
from pybtc.functions.tools import bytes_from_hex
|
||||
from pyflo.functions.hash import double_sha256
|
||||
from pyflo.functions.tools import bytes_from_hex
|
||||
|
||||
b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
base32charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
|
||||
@ -2,9 +2,9 @@ from secp256k1 import ffi, lib
|
||||
secp256k1_ec_pubkey_create = lib.secp256k1_ec_pubkey_create
|
||||
secp256k1_ec_pubkey_serialize = lib.secp256k1_ec_pubkey_serialize
|
||||
|
||||
from pybtc.constants import *
|
||||
from pybtc.functions.encode import encode_base58, decode_base58
|
||||
from pybtc.functions.hash import double_sha256
|
||||
from pyflo.constants import *
|
||||
from pyflo.functions.encode import encode_base58, decode_base58
|
||||
from pyflo.functions.hash import double_sha256
|
||||
from .bip39_mnemonic import generate_entropy
|
||||
|
||||
bytes_from_hex = bytes.fromhex
|
||||
@ -1,5 +1,9 @@
|
||||
from struct import unpack
|
||||
|
||||
import hashlib
|
||||
from pyflo.ellipticcurve.privateKey import PrivateKey
|
||||
from pyflo.ellipticcurve.signature import Signature
|
||||
from pyflo.ellipticcurve.math import Math
|
||||
from pyflo.ellipticcurve.utils.integer import RandomInteger
|
||||
from secp256k1 import ffi, lib
|
||||
secp256k1_ecdsa_signature_parse_der = lib.secp256k1_ecdsa_signature_parse_der
|
||||
secp256k1_ec_pubkey_parse = lib.secp256k1_ec_pubkey_parse
|
||||
@ -11,13 +15,13 @@ secp256k1_ecdsa_recoverable_signature_parse_compact = lib.secp256k1_ecdsa_recove
|
||||
secp256k1_ecdsa_recover = lib.secp256k1_ecdsa_recover
|
||||
secp256k1_ec_pubkey_serialize = lib.secp256k1_ec_pubkey_serialize
|
||||
|
||||
from pybtc.opcodes import *
|
||||
from pybtc.constants import *
|
||||
from pyflo.opcodes import *
|
||||
from pyflo.constants import *
|
||||
|
||||
from pybtc.functions.tools import bytes_from_hex, int_to_bytes, get_stream
|
||||
from pybtc.functions.hash import hash160, sha256
|
||||
from pybtc.functions.address import hash_to_address
|
||||
from pybtc.functions.key import is_wif_valid, wif_to_private_key
|
||||
from pyflo.functions.tools import bytes_from_hex, int_to_bytes, get_stream
|
||||
from pyflo.functions.hash import hash160, sha256
|
||||
from pyflo.functions.address import hash_to_address
|
||||
from pyflo.functions.key import is_wif_valid, wif_to_private_key
|
||||
|
||||
|
||||
def public_key_to_pubkey_script(key, hex=True):
|
||||
@ -400,11 +404,50 @@ def verify_signature(sig, pub_key, msg):
|
||||
result = secp256k1_ecdsa_verify(ECDSA_CONTEXT_VERIFY, raw_sig, msg, raw_pubkey)
|
||||
return True if result else False
|
||||
|
||||
def to_base(n, base):
|
||||
if base == 10:
|
||||
return n
|
||||
result = 0
|
||||
counter = 0
|
||||
while n:
|
||||
r = n % base
|
||||
n //= base
|
||||
result += r * 10 ** counter
|
||||
counter += 1
|
||||
return result
|
||||
|
||||
# https://raw.githubusercontent.com/starkbank/ecdsa-python/master/ellipticcurve/ecdsa.py
|
||||
def modSign(message, privateKey, hashfunc=sha256):
|
||||
# byteMessage = toBytes(message).digest()
|
||||
# byteMessage = hashfunc(toBytes(message)).digest()
|
||||
|
||||
byteMessage = hashlib.sha256(message.encode("utf-8")).hexdigest()
|
||||
# byteMessage = '0x' + byteMessage
|
||||
|
||||
# byteMessage = script_to_hash("686579", True)
|
||||
numberMessage = int(byteMessage, base=16)
|
||||
numberMessage = to_base(numberMessage, 16)
|
||||
curve = privateKey.curve
|
||||
r, s, randSignPoint = 0, 0, None
|
||||
while r == 0 or s == 0:
|
||||
randNum = RandomInteger.between(1, curve.N - 1)
|
||||
randSignPoint = Math.multiply(
|
||||
curve.G, n=randNum, A=curve.A, P=curve.P, N=curve.N
|
||||
)
|
||||
r = randSignPoint.x % curve.N
|
||||
s = (
|
||||
(numberMessage + r * privateKey.secret) * (Math.inv(randNum, curve.N))
|
||||
) % curve.N
|
||||
recoveryId = randSignPoint.y & 1
|
||||
if randSignPoint.y > curve.N:
|
||||
recoveryId += 2
|
||||
|
||||
return Signature(r=r, s=s, recoveryId=recoveryId)
|
||||
|
||||
|
||||
def sign_message(msg, private_key, hex=True):
|
||||
"""
|
||||
Sign message
|
||||
|
||||
:param msg: message to sign bytes or HEX encoded string.
|
||||
:param private_key: private key (bytes, hex encoded string or WIF format)
|
||||
:param hex: (optional) If set to True return key in HEX format, by default is True.
|
||||
@ -447,6 +490,25 @@ def sign_message(msg, private_key, hex=True):
|
||||
return signature.hex() if hex else signature
|
||||
|
||||
|
||||
def sign_message_standard_ops(msg, private_key):
|
||||
"""
|
||||
Sign message
|
||||
|
||||
:param msg: message to sign in string.
|
||||
:param private_key: private key (WIF format)
|
||||
:param hex: (optional) If set to True return key in HEX format, by default is True.
|
||||
:return: DER encoded signature in bytes or HEX encoded string.
|
||||
"""
|
||||
if(is_wif_valid(private_key)):
|
||||
hexKey = wif_to_private_key(private_key)
|
||||
else:
|
||||
raise TypeError("Invalid Private_Key, must be in WIF format")
|
||||
pk = PrivateKey.fromString(hexKey)
|
||||
signature = modSign(msg, pk)
|
||||
toDer = int.from_bytes(signature.toDer(), 'big')
|
||||
return hex(toDer)[2:]
|
||||
|
||||
|
||||
def public_key_recovery(signature, messsage, rec_id, compressed=True, hex=True):
|
||||
if isinstance(signature, str):
|
||||
signature = bytes_from_hex(signature)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user