-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] PrivateKey backed by cryptography #13
Changes from 4 commits
76c579a
d17e425
89d1c23
4a826f3
0abd0b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,14 @@ | ||
from network import Network | ||
from privkey import PrivateKey | ||
from pubkey import PublicKey | ||
from address import Address | ||
from script import Script | ||
from hdprivkey import HDPrivateKey | ||
from network import Network | ||
import network | ||
import ecdsa | ||
# from address import Address | ||
# from script import Script | ||
# from hdprivkey import HDPrivateKey | ||
from unit import Unit | ||
from uri import URI | ||
# from uri import URI | ||
|
||
import network | ||
import privkey | ||
import pubkey | ||
import unit | ||
# import uri |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,68 +1,67 @@ | ||
import utils, binascii, hashlib | ||
from errors import StringError | ||
import binascii | ||
import hashlib | ||
|
||
from bitforge import error | ||
from bitforge.utils import encoding | ||
|
||
class EncodingError(StringError): | ||
"The string {string} is not properly encoded" | ||
|
||
class Error(error.BitforgeError): | ||
pass | ||
|
||
class InvalidBase58h(EncodingError): | ||
"The string {string} is not valid base58/check" | ||
|
||
def b2a_hex(data): | ||
"""Convert a byte buffer to an hexadecimal string.""" | ||
|
||
class InvalidHex(EncodingError): | ||
"The string {string} is not valid hexadecimal" | ||
return binascii.b2a_hex(data) | ||
|
||
|
||
def encode_base58h(bytes): | ||
return utils.encoding.b2a_hashed_base58(bytes) | ||
def a2b_hex(string): | ||
"""Convert an hexadecimal string to a byte buffer.""" | ||
|
||
if len(string) % 2 == 1: | ||
string = '0' + string | ||
|
||
def decode_base58h(string): | ||
try: | ||
return utils.encoding.a2b_hashed_base58(string) | ||
return binascii.a2b_hex(string.encode('ascii')) | ||
except TypeError: | ||
raise Error('Invalid hexadecimal string') | ||
|
||
except utils.encoding.EncodingError: | ||
raise InvalidBase58h(string) | ||
|
||
def b2i_bigendian(data): | ||
"""Convert a big endian byte buffer to an unsigned big integer.""" | ||
|
||
def encode_int(integer, big_endian = True): | ||
bytes = bytearray() | ||
# Encoding and decoding from hexa appears to be way faster than manually | ||
# decoding the buffer in python. | ||
return int(b2a_hex(data), 16) | ||
|
||
while integer > 0: | ||
bytes.append(integer & 0xff) | ||
integer >>= 8 | ||
|
||
if big_endian: | ||
bytes.reverse() | ||
def i2b_bigendian(number, num_bytes = 0): | ||
"""Convert an unsigned big integer to a zero-padded big endian byte buffer. | ||
""" | ||
|
||
return str(bytes) | ||
# Encoding and decoding from hexa appears to be way faster than manually | ||
# decoding the buffer in python. | ||
return a2b_hex('%0*x' % (2 * num_bytes, number)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate number is the right type and raise a bitforge.Error if doesn't. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think we should check whether number is a non-negative integer? This is actually a wider discussion: should we check function argument types as a general strategy, like cryptography does, but contrary with the duck typing philosophy? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, this is a wider discussion. I believe we should embrace a @slezica I would like to hear you input on this regard. |
||
|
||
|
||
def decode_int(bytes, big_endian = True): | ||
if not big_endian: | ||
bytes = reversed(bytes) | ||
# TODO: implement these functions | ||
|
||
integer = 0 | ||
def b2a_base58check(data): | ||
"""Convert a byte buffer to a base58check string.""" | ||
|
||
for char in bytes: | ||
integer <<= 8 | ||
integer += ord(char) | ||
return encoding.b2a_hashed_base58(data) | ||
|
||
return integer | ||
|
||
def a2b_base58check(string): | ||
"""Convert a base58check string to a byte buffer.""" | ||
|
||
def encode_hex(bytes): | ||
return binascii.hexlify(bytes) | ||
|
||
|
||
def decode_hex(string): | ||
try: | ||
return binascii.unhexlify(string) | ||
except: | ||
# unhexlify() throws 2 different exceptions (length, and alphabet) | ||
raise InvalidHex(string) | ||
return encoding.a2b_hashed_base58(string) | ||
except encoding.EncodingError: | ||
raise Error('Invalid base58check string') | ||
|
||
|
||
# TODO: these are not encodings, they shouldn't be here | ||
|
||
def sha256(bytes): | ||
return hashlib.sha256(bytes).digest() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,39 @@ | ||
|
||
class BitforgeError(Exception): | ||
|
||
def __init__(self, *args, **kwargs): | ||
self.cause = kwargs.pop('cause', None) | ||
self.prepare(*args, **kwargs) | ||
self.message = self.__doc__.format(**self.__dict__) | ||
|
||
def prepare(self): | ||
pass | ||
def prepare(self, message=None): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pep8:
|
||
if message is not None: | ||
self.__doc__ = message | ||
|
||
def __str__(self): | ||
return self.message | ||
|
||
|
||
class ObjectError(BitforgeError): | ||
|
||
def prepare(self, object): | ||
self.object = object | ||
|
||
|
||
class StringError(BitforgeError): | ||
|
||
def prepare(self, string): | ||
self.string = repr(string) | ||
self.length = len(string) | ||
|
||
|
||
class NumberError(BitforgeError): | ||
|
||
def prepare(self, number): | ||
self.number = number | ||
|
||
|
||
class KeyValueError(BitforgeError): | ||
|
||
def prepare(self, key, value): | ||
self.key = key | ||
self.value = value |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,7 @@ class InvalidField(Error, error.StringError): | |
'aliases', # All the network aliases | ||
|
||
# Cryptography parameters | ||
# WARNING: only curves over prime fields are currently supported | ||
'curve', # Elliptic curve used for the crypto | ||
'hash_function', # Signature hashing function | ||
|
||
|
@@ -90,6 +91,8 @@ def __new__(cls, **kwargs): | |
if getattr(other, field) == getattr(network, field): | ||
raise InvalidNetwork(field, getattr(network, field)) | ||
|
||
cls._networks.append(network) | ||
|
||
# Enforce uniqueness of the network aliases | ||
for name in [network.name] + network.aliases: | ||
if name in cls._networks_by_name: | ||
|
@@ -108,7 +111,7 @@ def __str__(self): | |
testnet = Network( | ||
name = 'testnet', | ||
aliases = [], | ||
curve = ec.SECP256K1, | ||
curve = ec.SECP256K1(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see the need of having the curve as a Network parameter. I'm most probably missing a use case here, what do you have in mind? |
||
hash_function = hashes.SHA256, | ||
pubkeyhash = 111, | ||
wif_prefix = 239, | ||
|
@@ -129,7 +132,7 @@ def __str__(self): | |
default = livenet = Network( | ||
name = 'livenet', | ||
aliases = ['mainnet', 'default'], | ||
curve = ec.SECP256K1, | ||
curve = ec.SECP256K1(), | ||
hash_function = hashes.SHA256, | ||
pubkeyhash = 0x00, | ||
wif_prefix = 0x80, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may raise an error, capture it and raise a bitforge.Error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What error?