Compare commits

...

17 Commits

Author SHA1 Message Date
Vivek Teega
ee50ef60b9 Renamed packge to "pyflo-lib" so we can upload to PyPI 2023-02-20 21:56:27 +05:30
Vivek Teega
64119f8baa Changes for PyPI 2023-02-20 21:10:39 +05:30
Vivek Teega
1f7cbcfa13 Changes to integrate FLO Multisig
Co-Authored-By: Sai Raj <39055732+sairajzero@users.noreply.github.com>
2023-02-20 17:01:29 +05:30
Vivek Teega
c4f1c8b5af Change verify_signature_standard_ops : Totally depends on external API 2022-07-13 16:50:12 +05:30
Vivek Teega
3618687377 Bug fix 2022-07-12 11:37:41 +00:00
Vivek Teega
498d0064cd Update verify_signature_standard_ops to check if the floid is same as public key 2022-07-12 11:20:24 +00:00
Vivek Teega
c9a58a7305 Fix bug in standard ops verification 2022-07-10 18:52:07 +00:00
Vivek Teega
a386de5197 Updated verify_signature_standard_ops to only return signature verification 2022-07-10 13:29:31 +00:00
Vivek Teega
0a3bf90767 Updated script.py to use external API for verifying Standard Ops signatures 2022-07-10 06:57:01 +00:00
042f3da96e
Update README.md 2022-07-09 16:23:45 +05:30
Vivek Teega
282edba6a0 Update Readme 2022-07-09 10:29:14 +00:00
Vivek Teega
09c24b370f
Update README.md 2022-07-09 14:55:13 +05:30
Vivek Teega
05f2f09043
Merge pull request #3 from ranchimall/pr/2
Pr/2
2022-07-09 14:50:27 +05:30
Vivek Teega
ea9816f198 Merge branch 'master' into pr/2 2022-07-09 14:41:26 +05:30
Vivek Teega
11c5ff96a3 Update Readme and Gitignore 2022-07-09 08:55:14 +00:00
Vivek Teega
34198f7313 Added support for old and new sign_message 2022-07-09 13:56:17 +05:30
Tanishk Goyal
c8db052b42 Signing Complete 2022-02-16 15:46:16 +05:30
107 changed files with 1134 additions and 92 deletions

4
.gitignore vendored
View File

@ -3,4 +3,6 @@ build/
dist/ dist/
.github/ .github/
pybtc.egg-info/ pybtc.egg-info/
pyflo.egg-info/
pyflo_lib.egg-info/
*.pyc

128
README.md
View File

@ -1,8 +1,6 @@
## Python bitcoin library modified for FLO ## Python bitcoin library modified for FLO
### Installation ### Installation
To install pyflo, To install pyflo,
$ git clone https://github.com/ranchimall/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. 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 # ADDRESS GENERATION
>>> a = pybtc.Address(address_type="P2PKH") >>> a = pyflo.Address(address_type="P2PKH")
>>> a.address >>> a.address
'FTP7LL7QjhgKfqYX1pis18bCqEpZaGSRzZ' 'FTP7LL7QjhgKfqYX1pis18bCqEpZaGSRzZ'
>>> a.private_key.wif >>> a.private_key.wif
@ -34,13 +32,13 @@ Every message sent to the Blockchain is in hash format, and not in plain string.
'033c30b269e2d5df229f3f0ce294b19c4f0a3a8d12280415ce41e7bd3784a619c4' '033c30b269e2d5df229f3f0ce294b19c4f0a3a8d12280415ce41e7bd3784a619c4'
# CONVERT MESSAGE INTO SHA-256 HASH # 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' 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' 'a3da73976501812cd7b821a20bfb09af0a6a891e419cdfb761fb19a92c914242'
>>> msg = b'vivek'.hex() >>> msg = b'vivek'.hex()
>>> msg_hash_hex = sha256(msg).hex() >>> msg_hash_hex = pyflo.sha256(msg).hex()
>>> msg_hash_bytes = sha256(msg) >>> msg_hash_bytes = pyflo.sha256(msg)
>>> msg >>> msg
'766976656b' '766976656b'
>>> msg_hash_hex >>> msg_hash_hex
@ -49,10 +47,120 @@ 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' 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 # SIGN AND VERIFY THE MESSAGE
>>> sig_msg_hex = pybtc.sign_message(msg_hash_hex, a.private_key.wif) >>> sig_msg_hex = pyflo.sign_message(msg_hash_hex, a.private_key.wif)
>>> pybtc.verify_signature(sig_msg_hex, a.public_key.hex, msg_hash_hex) >>> pyflo.verify_signature(sig_msg_hex, a.public_key.hex, msg_hash_hex)
True 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
``` ```
The **sign_message_standard_ops** function verifies with [RanchiMall standard operations](https://github.com/ranchimall/Standard_Operations).
Things to note:
The function now takes 2 parameters -
1. Message in string ( not hex-encoded string)
2. Private key in wif (not bytes etc.)
The hex parameter has been removed as we are always returning hex in standard ops
New libraries used - hashlib
The elliptical curve folder, holds the code taken from starkbank ecdsa (MIT License)
no other additional dependencies
The signature generated with **sign_message_standard_ops** cannot be verified using purely Pyflo. Users can use the following API to verify signatures
** Python **
```
import requests
url = 'https://flo-sign-validator.duckdns.org'
myobj = {'floID': floID,
'pubKey': pubKey,
'message': message,
'sign': sign}
x = requests.post(url, json = myobj)
print(x.text)
```
** JavaScript **
```
fetch("https://flo-sign-validator.duckdns.org", {
method: "POST",
body: JSON.stringify({
floID: floID,
pubKey: pubKey,
message: message,
sign: sign
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
},
})
.then(function (response) {
return response.json();
})
.then(function (data) {
console.log(data);
})
.catch((error) => console.error("Error:", error));
```
** PHP **
```
function callAPI($method, $url, $data){
$curl = curl_init();
switch ($method){
case "POST":
curl_setopt($curl, CURLOPT_POST, 1);
if ($data)
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
break;
case "PUT":
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT");
if ($data)
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
break;
default:
if ($data)
$url = sprintf("%s?%s", $url, http_build_query($data));
}
// OPTIONS:
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'APIKEY: 111111111111111111111',
'Content-Type: application/json',
));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
// EXECUTE:
$result = curl_exec($curl);
curl_close($curl);
return $result;
}
$floID = $_POST['floID'];
$pubKey = $_POST['floPubKey'];
$message = $_POST['message'];
$signDataWithFlo = $_POST['signDataWithFlo'];
$data_array = array( "floID" => $floID, "pubKey" => $pubKey, "message" => $message, "sign" => $signDataWithFlo );
$make_call = callAPI('POST', 'https://flo-sign-validator.duckdns.org', json_encode($data_array));
$response = json_decode($make_call, true);
print_r($response);
```

View File

@ -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
View 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 *

View File

@ -1,10 +1,10 @@
from pybtc.constants import * from pyflo.constants import *
from pybtc.opcodes import * from pyflo.opcodes import *
from pybtc.functions.tools import bytes_from_hex, int_to_var_int from pyflo.functions.tools import bytes_from_hex, int_to_var_int
from pybtc.functions.script import op_push_data, decode_script from pyflo.functions.script import op_push_data, decode_script
from pybtc.functions.hash import hash160, sha256 from pyflo.functions.hash import hash160, sha256
from pybtc.functions.address import hash_to_address, public_key_to_p2sh_p2wpkh_script from pyflo.functions.address import hash_to_address, public_key_to_p2sh_p2wpkh_script
from pybtc.functions.key import (create_private_key, from pyflo.functions.key import (create_private_key,
private_key_to_wif, private_key_to_wif,
is_wif_valid, is_wif_valid,
wif_to_private_key, wif_to_private_key,

View File

@ -1,9 +1,9 @@
from struct import unpack, pack from struct import unpack, pack
from io import BytesIO from io import BytesIO
from pybtc.functions.block import bits_to_target, target_to_difficulty from pyflo.functions.block import bits_to_target, target_to_difficulty
from pybtc.functions.hash import double_sha256 from pyflo.functions.hash import double_sha256
from pybtc.functions.tools import var_int_to_int, read_var_int, var_int_len, rh2s from pyflo.functions.tools import var_int_to_int, read_var_int, var_int_len, rh2s
from pybtc.transaction import Transaction from pyflo.transaction import Transaction
class Block(dict): class Block(dict):

View File

@ -14,7 +14,7 @@ ECDSA_SEC256K1_ORDER = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8
MAINNET_ADDRESS_BYTE_PREFIX = b'\x23' MAINNET_ADDRESS_BYTE_PREFIX = b'\x23'
TESTNET_ADDRESS_BYTE_PREFIX = b'\x73' TESTNET_ADDRESS_BYTE_PREFIX = b'\x73'
MAINNET_SCRIPT_ADDRESS_BYTE_PREFIX = b'\x05' MAINNET_SCRIPT_ADDRESS_BYTE_PREFIX = b'\x5e'
TESTNET_SCRIPT_ADDRESS_BYTE_PREFIX = b'\xc4' TESTNET_SCRIPT_ADDRESS_BYTE_PREFIX = b'\xc4'
MAINNET_SEGWIT_ADDRESS_BYTE_PREFIX = b'\x03\x03\x00\x02\x03' MAINNET_SEGWIT_ADDRESS_BYTE_PREFIX = b'\x03\x03\x00\x02\x03'
TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX = b'\x03\x03\x00\x14\x02' TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX = b'\x03\x03\x00\x14\x02'
@ -22,7 +22,8 @@ TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX = b'\x03\x03\x00\x14\x02'
MAINNET_ADDRESS_PREFIX = 'F' MAINNET_ADDRESS_PREFIX = 'F'
TESTNET_ADDRESS_PREFIX = 'o' TESTNET_ADDRESS_PREFIX = 'o'
TESTNET_ADDRESS_PREFIX_2 = 'o' TESTNET_ADDRESS_PREFIX_2 = 'o'
MAINNET_SCRIPT_ADDRESS_PREFIX = '3' MAINNET_SCRIPT_ADDRESS_PREFIX = 'e'
MAINNET_SCRIPT_ADDRESS_PREFIX_2 = 'f'
TESTNET_SCRIPT_ADDRESS_PREFIX = '2' TESTNET_SCRIPT_ADDRESS_PREFIX = '2'
MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX = '5' MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX = '5'
@ -35,6 +36,7 @@ ADDRESS_PREFIX_LIST = (MAINNET_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX, TESTNET_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX_2, TESTNET_ADDRESS_PREFIX_2,
MAINNET_SCRIPT_ADDRESS_PREFIX, MAINNET_SCRIPT_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX_2,
TESTNET_SCRIPT_ADDRESS_PREFIX) TESTNET_SCRIPT_ADDRESS_PREFIX)
PRIVATE_KEY_PREFIX_LIST = (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX, PRIVATE_KEY_PREFIX_LIST = (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX,

View 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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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]

View 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
View 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
)

View 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

View 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-----
"""

View 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-----
"""

View 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)

View File

View 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))

View 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)

View 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),
}

View 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

View 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)

View 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"

View 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))

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,9 +1,9 @@
from pybtc.opcodes import * from pyflo.opcodes import *
from pybtc.constants import * from pyflo.constants import *
from pybtc.functions.tools import bytes_from_hex from pyflo.functions.tools import bytes_from_hex
from pybtc.functions.hash import double_sha256, hash160 from pyflo.functions.hash import double_sha256, hash160
from pybtc.functions.encode import (encode_base58, from pyflo.functions.encode import (encode_base58,
rebase_8_to_5, rebase_8_to_5,
bech32_polymod, bech32_polymod,
rebase_5_to_32, rebase_5_to_32,
@ -14,7 +14,7 @@ from pybtc.functions.encode import (encode_base58,
base32charset_upcase) 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, Get address from public key/script hash. In case PUBKEY, P2PKH, P2PKH public key/script hash is SHA256+RIPEMD160,
P2WSH script hash is SHA256. 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()) 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, Get address from public key/script hash. In case PUBKEY, P2PKH, P2PKH public key/script hash is SHA256+RIPEMD160,
P2WSH script hash is SHA256. P2WSH script hash is SHA256.
@ -129,7 +129,8 @@ def address_type(address, num=False):
:return: address type in string or numeric format. :return: address type in string or numeric format.
""" """
if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX, if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX): MAINNET_SCRIPT_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX_2):
t = 'P2SH' t = 'P2SH'
elif address[0] in (MAINNET_ADDRESS_PREFIX, elif address[0] in (MAINNET_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX, TESTNET_ADDRESS_PREFIX,
@ -155,7 +156,7 @@ def address_net_type(address):
:param address: address in base58 or bech32 format. :param address: address in base58 or bech32 format.
:return: address network type in string format or None. :return: address network type in string format or None.
""" """
if address[0] in (MAINNET_SCRIPT_ADDRESS_PREFIX, if address[0] in (MAINNET_SCRIPT_ADDRESS_PREFIX, MAINNET_SCRIPT_ADDRESS_PREFIX_2,
MAINNET_ADDRESS_PREFIX): MAINNET_ADDRESS_PREFIX):
return "mainnet" return "mainnet"
elif address[:2] == MAINNET_SEGWIT_ADDRESS_PREFIX: elif address[:2] == MAINNET_SEGWIT_ADDRESS_PREFIX:
@ -178,7 +179,8 @@ def address_to_script(address, hex=False):
:return: public key script in HEX or bytes string. :return: public key script in HEX or bytes string.
""" """
if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX, if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX): MAINNET_SCRIPT_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX_2):
s = [OP_HASH160, s = [OP_HASH160,
b'\x14', b'\x14',
address_to_hash(address, hex=False), address_to_hash(address, hex=False),
@ -222,6 +224,7 @@ def is_address_valid(address, testnet=False):
return False return False
if address[0] in (MAINNET_ADDRESS_PREFIX, if address[0] in (MAINNET_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX, MAINNET_SCRIPT_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX_2,
TESTNET_ADDRESS_PREFIX, TESTNET_ADDRESS_PREFIX,
TESTNET_ADDRESS_PREFIX_2, TESTNET_ADDRESS_PREFIX_2,
TESTNET_SCRIPT_ADDRESS_PREFIX): TESTNET_SCRIPT_ADDRESS_PREFIX):
@ -232,7 +235,8 @@ def is_address_valid(address, testnet=False):
return False return False
else: else:
if address[0] not in (MAINNET_ADDRESS_PREFIX, if address[0] not in (MAINNET_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX): MAINNET_SCRIPT_ADDRESS_PREFIX,
MAINNET_SCRIPT_ADDRESS_PREFIX_2):
return False return False
h = decode_base58(address) h = decode_base58(address)
if len(h) != 25: if len(h) != 25:
@ -275,11 +279,11 @@ def is_address_valid(address, testnet=False):
if checksum != checksum2: if checksum != checksum2:
return False return False
return True return True
else:
return False
def get_witness_version(address): def get_witness_version(address):
address = address.split("1")[1] address = address.split("1")[1]
h = rebase_32_to_5(address) h = rebase_32_to_5(address)
return h[0] return h[0]

View File

@ -1,11 +1,11 @@
from struct import pack from struct import pack
from secp256k1 import ffi, lib from secp256k1 import ffi, lib
from pybtc.functions.key import private_to_public_key, private_key_to_wif from pyflo.functions.key import private_to_public_key, private_key_to_wif
from pybtc.functions.hash import hmac_sha512, double_sha256, hash160 from pyflo.functions.hash import hmac_sha512, double_sha256, hash160
from pybtc.functions.encode import (encode_base58, from pyflo.functions.encode import (encode_base58,
decode_base58_with_checksum, decode_base58_with_checksum,
encode_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): def create_master_xprivate_key(seed, testnet=False, base58=True, hex=False):

View File

@ -1,8 +1,8 @@
from pybtc.constants import * from pyflo.constants import *
import time import time
import hashlib import hashlib
from pybtc.functions.hash import sha256 from pyflo.functions.hash import sha256
from pybtc.functions.tools import int_from_bytes from pyflo.functions.tools import int_from_bytes
def generate_entropy(strength=256, hex=True): def generate_entropy(strength=256, hex=True):
""" """

View File

@ -1,5 +1,5 @@
from pybtc.functions.tools import s2rh, bytes_from_hex, int_from_bytes from pyflo.functions.tools import s2rh, bytes_from_hex, int_from_bytes
from pybtc.functions.hash import double_sha256 from pyflo.functions.hash import double_sha256
def merkle_root(tx_hash_list, hex=True): def merkle_root(tx_hash_list, hex=True):
""" """

View File

@ -1,5 +1,5 @@
from pybtc.functions.hash import double_sha256 from pyflo.functions.hash import double_sha256
from pybtc.functions.tools import bytes_from_hex from pyflo.functions.tools import bytes_from_hex
b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
base32charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" base32charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"

View File

@ -2,9 +2,9 @@ from secp256k1 import ffi, lib
secp256k1_ec_pubkey_create = lib.secp256k1_ec_pubkey_create secp256k1_ec_pubkey_create = lib.secp256k1_ec_pubkey_create
secp256k1_ec_pubkey_serialize = lib.secp256k1_ec_pubkey_serialize secp256k1_ec_pubkey_serialize = lib.secp256k1_ec_pubkey_serialize
from pybtc.constants import * from pyflo.constants import *
from pybtc.functions.encode import encode_base58, decode_base58 from pyflo.functions.encode import encode_base58, decode_base58
from pybtc.functions.hash import double_sha256 from pyflo.functions.hash import double_sha256
from .bip39_mnemonic import generate_entropy from .bip39_mnemonic import generate_entropy
bytes_from_hex = bytes.fromhex bytes_from_hex = bytes.fromhex

View File

@ -1,5 +1,9 @@
from struct import unpack 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 from secp256k1 import ffi, lib
secp256k1_ecdsa_signature_parse_der = lib.secp256k1_ecdsa_signature_parse_der secp256k1_ecdsa_signature_parse_der = lib.secp256k1_ecdsa_signature_parse_der
secp256k1_ec_pubkey_parse = lib.secp256k1_ec_pubkey_parse secp256k1_ec_pubkey_parse = lib.secp256k1_ec_pubkey_parse
@ -11,13 +15,14 @@ secp256k1_ecdsa_recoverable_signature_parse_compact = lib.secp256k1_ecdsa_recove
secp256k1_ecdsa_recover = lib.secp256k1_ecdsa_recover secp256k1_ecdsa_recover = lib.secp256k1_ecdsa_recover
secp256k1_ec_pubkey_serialize = lib.secp256k1_ec_pubkey_serialize secp256k1_ec_pubkey_serialize = lib.secp256k1_ec_pubkey_serialize
from pybtc.opcodes import * from pyflo.opcodes import *
from pybtc.constants import * from pyflo.constants import *
from pybtc.functions.tools import bytes_from_hex, int_to_bytes, get_stream from pyflo.functions.tools import bytes_from_hex, int_to_bytes, get_stream
from pybtc.functions.hash import hash160, sha256 from pyflo.functions.hash import hash160, sha256
from pybtc.functions.address import hash_to_address from pyflo.functions.address import hash_to_address
from pybtc.functions.key import is_wif_valid, wif_to_private_key from pyflo.functions.key import is_wif_valid, wif_to_private_key
import requests, json
def public_key_to_pubkey_script(key, hex=True): def public_key_to_pubkey_script(key, hex=True):
@ -161,9 +166,6 @@ def script_to_address(script, testnet=False):
return None return None
def decode_script(script, asm=False): def decode_script(script, asm=False):
""" """
Decode script to ASM format or to human readable OPCODES string. Decode script to ASM format or to human readable OPCODES string.
@ -401,10 +403,76 @@ def verify_signature(sig, pub_key, msg):
return True if result else False return True if result else False
def verify_signature_standard_ops(sig, pub_key, msg, floID):
"""
Verify signature for message and given public key
:param sig: signature in bytes or HEX encoded string.
:param pub_key: public key in bytes or HEX encoded string.
:param msg: message in bytes, string or HEX encoded string.
:flo_id: FLO ID in HEX encoded string.
:return: boolean.
"""
url = 'https://flo-sign-validator.duckdns.org'
post_data = {
'floID': floID,
'pubKey': pub_key,
'message': msg,
'sign': sig
}
signature_verification = requests.post(url, json = post_data)
signature_verification = json.loads(signature_verification.text)
if signature_verification['success']:
return True
else:
return 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): def sign_message(msg, private_key, hex=True):
""" """
Sign message Sign message
:param msg: message to sign bytes or HEX encoded string. :param msg: message to sign bytes or HEX encoded string.
:param private_key: private key (bytes, hex encoded string or WIF format) :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. :param hex: (optional) If set to True return key in HEX format, by default is True.
@ -447,6 +515,25 @@ def sign_message(msg, private_key, hex=True):
return signature.hex() if hex else signature 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): def public_key_recovery(signature, messsage, rec_id, compressed=True, hex=True):
if isinstance(signature, str): if isinstance(signature, str):
signature = bytes_from_hex(signature) signature = bytes_from_hex(signature)
@ -551,5 +638,4 @@ def is_valid_signature_encoding(sig):
# interpreted as a negative number. # interpreted as a negative number.
if (len_s > 1) and (sig[len_r + 6] == 0x00) and (not sig[len_r + 7] & 0x80): if (len_s > 1) and (sig[len_r + 6] == 0x00) and (not sig[len_r + 7] & 0x80):
return False return False
return True return True

Some files were not shown because too many files have changed in this diff Show More