Merge 8425214c2a7d67d24299221991d8b227451187a2 into 55ad60097da46da44a33892d9c4f1f8ce797e774

This commit is contained in:
Dimitri De Jonghe 2016-03-18 15:57:30 +00:00
commit 2f190310b8
16 changed files with 341 additions and 98 deletions

2
.gitignore vendored
View File

@ -65,3 +65,5 @@ target/
# pyenv
.python-version
# IDE related
.idea

View File

@ -1,10 +1,10 @@
import requests
import bigchaindb
from bigchaindb import util
from bigchaindb import config_utils
from bigchaindb import exceptions
from bigchaindb import crypto
from bigchaindb import util
from bigchaindb.crypto import asymmetric
class Client:
@ -92,6 +92,6 @@ def temp_client():
A client initialized with a keypair generated on the fly.
"""
private_key, public_key = crypto.generate_key_pair()
private_key, public_key = asymmetric.generate_key_pair()
return Client(private_key=private_key, public_key=public_key, api_endpoint='http://localhost:5000/api/v1')

View File

@ -1,19 +1,17 @@
"""Command line interface for the `bigchain` command."""
import os
import logging
import argparse
import copy
import logging
import os
import bigchaindb
import bigchaindb.config_utils
from bigchaindb import db
from bigchaindb.exceptions import DatabaseAlreadyExists
from bigchaindb.commands.utils import base_parser, start
from bigchaindb.crypto import asymmetric
from bigchaindb.exceptions import DatabaseAlreadyExists
from bigchaindb.processes import Processes
from bigchaindb import crypto
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@ -52,7 +50,7 @@ def run_configure(args, skip_if_exists=False):
conf = copy.deepcopy(bigchaindb._config)
print('Generating keypair')
conf['keypair']['private'], conf['keypair']['public'] = crypto.generate_key_pair()
conf['keypair']['private'], conf['keypair']['public'] = asymmetric.generate_key_pair()
if not args.yes:
for key in ('host', 'port', 'name'):

View File

@ -1,17 +1,15 @@
import rethinkdb as r
import random
import json
import rapidjson
import rethinkdb as r
import bigchaindb
from bigchaindb import util
from bigchaindb import config_utils
from bigchaindb import exceptions
from bigchaindb import crypto
from bigchaindb import util
from bigchaindb.crypto import asymmetric
from bigchaindb.monitor import Monitor
monitor = Monitor()
@ -98,7 +96,7 @@ class Bigchain(object):
signature = data.pop('signature')
public_key_base58 = signed_transaction['transaction']['current_owner']
public_key = crypto.PublicKey(public_key_base58)
public_key = asymmetric.PublicKey(public_key_base58)
return public_key.verify(util.serialize(data), signature)
@monitor.timer('write_transaction', rate=bigchaindb.config['statsd']['rate'])
@ -331,8 +329,8 @@ class Bigchain(object):
# Calculate the hash of the new block
block_data = util.serialize(block)
block_hash = crypto.hash_data(block_data)
block_signature = crypto.PrivateKey(self.me_private).sign(block_data)
block_hash = asymmetric.hash_data(block_data)
block_signature = asymmetric.PrivateKey(self.me_private).sign(block_data)
block = {
'id': block_hash,
@ -357,7 +355,7 @@ class Bigchain(object):
"""
# 1. Check if current hash is correct
calculated_hash = crypto.hash_data(util.serialize(block['block']))
calculated_hash = asymmetric.hash_data(util.serialize(block['block']))
if calculated_hash != block['id']:
raise exceptions.InvalidHash()
@ -452,7 +450,7 @@ class Bigchain(object):
}
vote_data = util.serialize(vote)
signature = crypto.PrivateKey(self.me_private).sign(vote_data)
signature = asymmetric.PrivateKey(self.me_private).sign(vote_data)
vote_signed = {
'node_pubkey': self.me,

View File

View File

@ -0,0 +1,69 @@
# Separate all crypto code so that we can easily test several implementations
from abc import ABCMeta, abstractmethod
import sha3
class PrivateKey(metaclass=ABCMeta):
"""
PrivateKey instance
"""
@abstractmethod
def sign(self, data):
"""
Sign data with private key
"""
raise NotImplementedError
@staticmethod
@abstractmethod
def encode(private_value):
"""
Encode the decimal number private_value to base58
"""
raise NotImplementedError
@staticmethod
@abstractmethod
def decode(key):
"""
Decode the base58 private_value to decimale
"""
raise NotImplementedError
class PublicKey(metaclass=ABCMeta):
@abstractmethod
def verify(self, data, signature):
raise NotImplementedError
@staticmethod
@abstractmethod
def encode(public_value_x, public_value_y):
"""
Encode the public key represented by the decimal values x and y to base58
"""
raise NotImplementedError
@staticmethod
@abstractmethod
def decode(public_value_compressed_base58):
"""
Decode the base58 public_value to the decimal x and y values
"""
raise NotImplementedError
def hash_data(data):
"""Hash the provided data using SHA3-256"""
return sha3.sha3_256(data.encode()).hexdigest()
from bigchaindb.crypto.ecdsa import ECDSAPrivateKey, ECDSAPublicKey, ecdsa_generate_key_pair
PrivateKey = ECDSAPrivateKey
PublicKey = ECDSAPublicKey
generate_key_pair = ecdsa_generate_key_pair

View File

@ -1,18 +1,18 @@
# Separate all crypto code so that we can easily test several implementations
import binascii
import base58
import sha3
import bitcoin
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives import hashes
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from bigchaindb.crypto.asymmetric import PrivateKey, PublicKey
class PrivateKey(object):
class ECDSAPrivateKey(PrivateKey):
"""
PrivateKey instance
"""
@ -65,7 +65,7 @@ class PrivateKey(object):
Return an instance of cryptography PrivateNumbers from the decimal private_value
"""
public_value_x, public_value_y = self._private_value_to_public_values(private_value)
public_numbers = PublicKey._public_values_to_cryptography_public_numbers(public_value_x, public_value_y)
public_numbers = ECDSAPublicKey._public_values_to_cryptography_public_numbers(public_value_x, public_value_y)
private_numbers = ec.EllipticCurvePrivateNumbers(private_value, public_numbers)
return private_numbers
@ -77,7 +77,7 @@ class PrivateKey(object):
return private_numbers.private_key(default_backend())
class PublicKey(object):
class ECDSAPublicKey(PublicKey):
def __init__(self, key):
"""
@ -85,7 +85,7 @@ class PublicKey(object):
"""
public_value_x, public_value_y = self.decode(key)
public_numbers = self._public_values_to_cryptography_public_numbers(public_value_x, public_value_y)
self.public_key = self._criptography_public_key_from_public_numbers(public_numbers)
self.public_key = self._cryptography_public_key_from_public_numbers(public_numbers)
def verify(self, data, signature):
verifier = self.public_key.verifier(binascii.unhexlify(signature), ec.ECDSA(hashes.SHA256()))
@ -123,33 +123,25 @@ class PublicKey(object):
public_numbers = ec.EllipticCurvePublicNumbers(public_value_x, public_value_y, ec.SECP256K1())
return public_numbers
def _criptography_public_key_from_public_numbers(self, public_numbers):
def _cryptography_public_key_from_public_numbers(self, public_numbers):
"""
Return an instance of cryptography PublicKey from a cryptography instance of PublicNumbers
"""
return public_numbers.public_key(default_backend())
def generate_key_pair():
def ecdsa_generate_key_pair():
"""
Generate a new key pair and return the pair encoded in base58
"""
# Private key
private_key = ec.generate_private_key(ec.SECP256K1, default_backend())
private_value = private_key.private_numbers().private_value
private_value_base58 = PrivateKey.encode(private_value)
private_value_base58 = ECDSAPrivateKey.encode(private_value)
# Public key
public_key = private_key.public_key()
public_value_x, public_value_y = public_key.public_numbers().x, public_key.public_numbers().y
public_value_compressed_base58 = PublicKey.encode(public_value_x, public_value_y)
public_value_compressed_base58 = ECDSAPublicKey.encode(public_value_x, public_value_y)
return (private_value_base58, public_value_compressed_base58)
def hash_data(data):
"""Hash the provided data using SHA3-256"""
return sha3.sha3_256(data.encode()).hexdigest()

View File

@ -0,0 +1,102 @@
# Separate all crypto code so that we can easily test several implementations
import base64
import base58
import ed25519
from bigchaindb.crypto.asymmetric import PrivateKey, PublicKey
class ED25519PrivateKey(PrivateKey):
"""
PrivateKey instance
"""
def __init__(self, key):
"""
Instantiate the private key with the private_value encoded in base58
"""
private_base64 = self.decode(key)
self.private_key = self._private_key_from_private_base64(private_base64)
def sign(self, data):
"""
Sign data with private key
"""
return self.private_key.sign(data.encode('utf-8'), encoding="base64")
@staticmethod
def encode(private_base64):
"""
Encode the base64 number private_base64 to base58
"""
return base58.b58encode(base64.b64decode(private_base64))
@staticmethod
def decode(key):
"""
Decode the base58 private_value to base64
"""
return base64.b64encode(base58.b58decode(key))
@staticmethod
def _private_key_from_private_base64(private_base64):
"""
Return an instance of a ED25519 SignigKey from a base64 key
"""
return ed25519.SigningKey(private_base64, encoding='base64')
class ED25519PublicKey(PublicKey):
def __init__(self, key):
"""
Instantiate the public key with the compressed public value encoded in base58
"""
public_base64 = self.decode(key)
self.public_key = self._public_key_from_public_base64(public_base64)
def verify(self, data, signature, encoding='base64'):
try:
if encoding:
data = data.encode('utf-8')
self.public_key.verify(signature, data, encoding=encoding)
except ed25519.BadSignatureError:
return False
return True
@staticmethod
def encode(public_base64):
"""
Encode the public key represented by base64 to base58
"""
return ED25519PrivateKey.encode(public_base64)
@staticmethod
def decode(public_value_compressed_base58):
"""
Decode the base58 public_value to base64
"""
return ED25519PrivateKey.decode(public_value_compressed_base58)
def _public_key_from_public_base64(self, public_base64):
"""
Return an instance of ED25519 VerifyingKey from a base64
"""
return ed25519.VerifyingKey(public_base64, encoding='base64')
def ed25519_generate_key_pair():
"""
Generate a new key pair and return the pair encoded in base58
"""
sk, vk = ed25519.create_keypair()
# Private key
private_value_base58 = base58.b58encode(sk.to_bytes())
# Public key
public_value_compressed_base58 = base58.b58encode(vk.to_bytes())
return (private_value_base58, public_value_compressed_base58)

View File

@ -1,12 +1,11 @@
import json
import time
import multiprocessing as mp
import time
from datetime import datetime
import bigchaindb
from bigchaindb import exceptions
from bigchaindb.crypto import PrivateKey, PublicKey, hash_data
from bigchaindb.crypto.asymmetric import PrivateKey, PublicKey, hash_data
class ProcessGroup(object):

View File

@ -76,6 +76,7 @@ setup(
'bitcoin==1.1.42',
'flask==0.10.1',
'requests==2.9',
'ed25519==1.4',
],
setup_requires=['pytest-runner'],
tests_require=tests_require,

0
tests/crypto/__init__.py Normal file
View File

121
tests/crypto/test_crypto.py Normal file
View File

@ -0,0 +1,121 @@
import base64
from bigchaindb.crypto.ecdsa import ECDSAPrivateKey, ECDSAPublicKey, ecdsa_generate_key_pair
from bigchaindb.crypto.ed25519 import ED25519PrivateKey, ED25519PublicKey, ed25519_generate_key_pair
class TestBigchainCryptoED25519(object):
PRIVATE_B64 = b'xSrGKGxeJYVlVrk84f29wcPazoTV+y8fzM7P0iFsSdg='
PRIVATE_BYTES = b'\xc5*\xc6(l^%\x85eV\xb9<\xe1\xfd\xbd\xc1\xc3\xda\xce\x84\xd5\xfb/\x1f\xcc\xce\xcf\xd2!lI\xd8v\x08\xfb\x03\x15y/&\xcd^O\xa9\xb8\xd2\x8a\x89\x8d\xf94\x9b\xbe\xb1\xe7\xdb~\x95!o\xde\xa2{\xa5'
PRIVATE_B58 = 'EGf9UJzryLpZaBguyf5f4QAefFnairNbHLkhht8BZ57m'
PUBLIC_B64 = b'dgj7AxV5LybNXk+puNKKiY35NJu+sefbfpUhb96ie6U='
PUBLIC_BYTES = b'v\x08\xfb\x03\x15y/&\xcd^O\xa9\xb8\xd2\x8a\x89\x8d\xf94\x9b\xbe\xb1\xe7\xdb~\x95!o\xde\xa2{\xa5'
PUBLIC_B58 = '8wm3wiqsoujkDJvk8FMZkHijb9eZdUqMuZsnRee4eRz4'
PUBLIC_B64_ILP = 'Lvf3YtnHLMER+VHT0aaeEJF+7WQcvp4iKZAdvMVto7c='
MSG_SHA512_ILP = 'claZQU7qkFz7smkAVtQp9ekUCc5LgoeN9W3RItIzykNEDbGSvzeHvOk9v/vrPpm+XWx5VFjd/sVbM2SLnCpxLw=='
SIG_B64_ILP = 'sd0RahwuJJgeNfg8HvWHtYf4uqNgCOqIbseERacqs8G0kXNQQnhfV6gWAnMb+0RIlY3e0mqbrQiUwbRYJvRBAw=='
def test_private_key_encode(self):
private_value_base58 = ED25519PrivateKey.encode(self.PRIVATE_B64)
assert private_value_base58 == self.PRIVATE_B58
def test_private_key_init(self):
sk = ED25519PrivateKey(self.PRIVATE_B58)
assert sk.private_key.to_ascii(encoding='base64') == self.PRIVATE_B64[:-1]
assert sk.private_key.to_bytes() == self.PRIVATE_BYTES
def test_private_key_decode(self):
private_value = ED25519PrivateKey.decode(self.PRIVATE_B58)
assert private_value == self.PRIVATE_B64
def test_public_key_encode(self):
public_value_base58 = ED25519PublicKey.encode(self.PUBLIC_B64)
assert public_value_base58 == self.PUBLIC_B58
def test_public_key_init(self):
vk = ED25519PublicKey(self.PUBLIC_B58)
assert vk.public_key.to_ascii(encoding='base64') == self.PUBLIC_B64[:-1]
assert vk.public_key.to_bytes() == self.PUBLIC_BYTES
def test_public_key_decode(self):
public_value = ED25519PublicKey.decode(self.PUBLIC_B58)
assert public_value == self.PUBLIC_B64
def test_sign_verify(self):
message = 'Hello World!'
sk = ED25519PrivateKey(self.PRIVATE_B58)
vk = ED25519PublicKey(self.PUBLIC_B58)
assert vk.verify(message, sk.sign(message)) is True
assert vk.verify(message, sk.sign(message + 'dummy')) is False
assert vk.verify(message + 'dummy', sk.sign(message)) is False
vk = ED25519PublicKey(ED25519PublicKey.encode(self.PUBLIC_B64_ILP))
assert vk.verify(message, sk.sign(message)) is False
def test_valid_condition_valid_signature_ilp(self):
vk = ED25519PublicKey(ED25519PublicKey.encode(self.PUBLIC_B64_ILP))
msg = self.MSG_SHA512_ILP
sig = self.SIG_B64_ILP
assert vk.verify(base64.b64decode(msg), base64.b64decode(sig), encoding=None) is True
def test_valid_condition_invalid_signature_ilp(self):
vk = ED25519PublicKey(ED25519PublicKey.encode(self.PUBLIC_B64_ILP))
msg = self.MSG_SHA512_ILP
sig = self.MSG_SHA512_ILP
assert vk.verify(base64.b64decode(msg), base64.b64decode(sig), encoding=None) is False
def test_generate_key_pair(self):
sk, vk = ed25519_generate_key_pair()
assert ED25519PrivateKey.encode(ED25519PrivateKey.decode(sk)) == sk
assert ED25519PublicKey.encode(ED25519PublicKey.decode(vk)) == vk
def test_generate_sign_verify(self):
sk, vk = ed25519_generate_key_pair()
sk = ED25519PrivateKey(sk)
vk = ED25519PublicKey(vk)
message = 'Hello World!'
assert vk.verify(message, sk.sign(message)) is True
assert vk.verify(message, sk.sign(message + 'dummy')) is False
assert vk.verify(message + 'dummy', sk.sign(message)) is False
vk = ED25519PublicKey(ED25519PublicKey.encode(self.PUBLIC_B64_ILP))
assert vk.verify(message, sk.sign(message)) is False
class TestBigchainCryptoECDSA(object):
PRIVATE_VALUE = 64328150571824492670917070117568709277186368319388887463636481841106388379832
PUBLIC_VALUE_X = 48388170575736684074633245566225141536152842355597159440179742847497614196929
PUBLIC_VALUE_Y = 65233479152484407841598798165960909560839872511163322973341535484598825150846
PRIVATE_VALUE_B58 = 'AaAp4xBavbe6VGeQF2mWdSKNM1r6HfR2Z1tAY6aUkwdq'
PUBLIC_VALUE_COMPRESSED_B58 = 'ifEi3UuTDT4CqUUKiS5omgeDodhu2aRFHVp6LoahbEVe'
def test_private_key_encode(self):
private_value_base58 = ECDSAPrivateKey.encode(self.PRIVATE_VALUE)
assert private_value_base58 == self.PRIVATE_VALUE_B58
def test_private_key_decode(self):
private_value = ECDSAPrivateKey.decode(self.PRIVATE_VALUE_B58)
assert private_value == self.PRIVATE_VALUE
def test_public_key_encode(self):
public_value_compressed_base58 = ECDSAPublicKey.encode(self.PUBLIC_VALUE_X, self.PUBLIC_VALUE_Y)
assert public_value_compressed_base58 == self.PUBLIC_VALUE_COMPRESSED_B58
def test_public_key_decode(self):
public_value_x, public_value_y = ECDSAPublicKey.decode(self.PUBLIC_VALUE_COMPRESSED_B58)
assert public_value_x == self.PUBLIC_VALUE_X
assert public_value_y == self.PUBLIC_VALUE_Y
def test_sign_verify(self):
message = 'Hello World!'
public_key = ECDSAPublicKey(self.PUBLIC_VALUE_COMPRESSED_B58)
private_key = ECDSAPrivateKey(self.PRIVATE_VALUE_B58)
assert public_key.verify(message, private_key.sign(message)) is True
def test_generate_key_pair(self):
private_value_base58, public_value_compressed_base58 = ecdsa_generate_key_pair()
assert ECDSAPrivateKey.encode(
ECDSAPrivateKey.decode(private_value_base58)) == private_value_base58
assert ECDSAPublicKey.encode(
*ECDSAPublicKey.decode(public_value_compressed_base58)) == public_value_compressed_base58

View File

@ -6,12 +6,11 @@ import pytest
import rethinkdb as r
import bigchaindb
from bigchaindb import util
from bigchaindb import exceptions
from bigchaindb.crypto import PrivateKey, PublicKey, generate_key_pair, hash_data
from bigchaindb.voter import Voter
from bigchaindb import util
from bigchaindb.block import Block
from bigchaindb.crypto.asymmetric import PrivateKey, PublicKey, generate_key_pair, hash_data
from bigchaindb.voter import Voter
@pytest.mark.skipif(reason='Some tests throw a ResourceWarning that might result in some weird '
@ -408,45 +407,6 @@ class TestBlockValidation(object):
assert b.is_valid_block(block)
class TestBigchainCrypto(object):
PRIVATE_VALUE = 64328150571824492670917070117568709277186368319388887463636481841106388379832
PUBLIC_VALUE_X = 48388170575736684074633245566225141536152842355597159440179742847497614196929
PUBLIC_VALUE_Y = 65233479152484407841598798165960909560839872511163322973341535484598825150846
PRIVATE_VALUE_B58 = 'AaAp4xBavbe6VGeQF2mWdSKNM1r6HfR2Z1tAY6aUkwdq'
PUBLIC_VALUE_COMPRESSED_B58 = 'ifEi3UuTDT4CqUUKiS5omgeDodhu2aRFHVp6LoahbEVe'
def test_private_key_encode(self):
private_value_base58 = PrivateKey.encode(self.PRIVATE_VALUE)
assert private_value_base58 == self.PRIVATE_VALUE_B58
def test_private_key_decode(self):
private_value = PrivateKey.decode(self.PRIVATE_VALUE_B58)
assert private_value == self.PRIVATE_VALUE
def test_public_key_encode(self):
public_value_compressed_base58 = PublicKey.encode(self.PUBLIC_VALUE_X, self.PUBLIC_VALUE_Y)
assert public_value_compressed_base58 == self.PUBLIC_VALUE_COMPRESSED_B58
def test_public_key_decode(self):
public_value_x, public_value_y = PublicKey.decode(self.PUBLIC_VALUE_COMPRESSED_B58)
assert public_value_x == self.PUBLIC_VALUE_X
assert public_value_y == self.PUBLIC_VALUE_Y
def test_sign_verify(self):
message = 'Hello World!'
public_key = PublicKey(self.PUBLIC_VALUE_COMPRESSED_B58)
private_key = PrivateKey(self.PRIVATE_VALUE_B58)
assert public_key.verify(message, private_key.sign(message)) is True
def test_generate_key_pair(self):
private_value_base58, public_value_compressed_base58 = generate_key_pair()
assert PrivateKey.encode(
PrivateKey.decode(private_value_base58)) == private_value_base58
assert PublicKey.encode(
*PublicKey.decode(public_value_compressed_base58)) == public_value_compressed_base58
class TestBigchainVoter(object):
def test_valid_block_voting(self, b):

View File

@ -1,12 +1,12 @@
import pytest
import time
import rethinkdb as r
import multiprocessing as mp
import time
import pytest
import rethinkdb as r
from bigchaindb import util
from bigchaindb.crypto.asymmetric import PublicKey, generate_key_pair
from bigchaindb.voter import Voter, BlockStream
from bigchaindb.crypto import PublicKey, generate_key_pair
class TestBigchainVoter(object):

View File

@ -52,7 +52,7 @@ def mock_rethink_db_drop(monkeypatch):
@pytest.fixture
def mock_generate_key_pair(monkeypatch):
monkeypatch.setattr('bigchaindb.crypto.generate_key_pair', lambda: ('privkey', 'pubkey'))
monkeypatch.setattr('bigchaindb.crypto.asymmetric.generate_key_pair', lambda: ('privkey', 'pubkey'))
@pytest.fixture

View File

@ -1,8 +1,9 @@
import json
import pytest
from bigchaindb import crypto
from bigchaindb import util
from bigchaindb.crypto import asymmetric
TX_ENDPOINT = '/api/v1/transactions/'
@ -17,7 +18,7 @@ def test_get_transaction_endpoint(b, client, user_public_key):
def test_post_create_transaction_endpoint(b, client):
keypair = crypto.generate_key_pair()
keypair = asymmetric.generate_key_pair()
tx = util.create_and_sign_tx(keypair[0], keypair[1], keypair[1], None, 'CREATE')
@ -27,8 +28,8 @@ def test_post_create_transaction_endpoint(b, client):
def test_post_transfer_transaction_endpoint(b, client):
from_keypair = crypto.generate_key_pair()
to_keypair = crypto.generate_key_pair()
from_keypair = asymmetric.generate_key_pair()
to_keypair = asymmetric.generate_key_pair()
tx = util.create_and_sign_tx(from_keypair[0], from_keypair[1], from_keypair[1], None, 'CREATE')
res = client.post(TX_ENDPOINT, data=json.dumps(tx))