sha256 fulfillment

tests
This commit is contained in:
diminator 2016-03-18 20:01:47 +01:00
parent 821ca9f8e6
commit 9bd1e9ff8b
6 changed files with 186 additions and 13 deletions

View File

@ -51,8 +51,10 @@ class BitmaskRegistry:
}) })
from bigchaindb.crypto.fulfillments.sha256 import Sha256Fulfillment
from bigchaindb.crypto.fulfillments.threshold_sha256 import ThresholdSha256Fulfillment from bigchaindb.crypto.fulfillments.threshold_sha256 import ThresholdSha256Fulfillment
from bigchaindb.crypto.fulfillments.ed25519_sha256 import Ed25519Sha256Fulfillment from bigchaindb.crypto.fulfillments.ed25519_sha256 import Ed25519Sha256Fulfillment
BitmaskRegistry.register_type(Sha256Fulfillment)
BitmaskRegistry.register_type(ThresholdSha256Fulfillment) BitmaskRegistry.register_type(ThresholdSha256Fulfillment)
BitmaskRegistry.register_type(Ed25519Sha256Fulfillment) BitmaskRegistry.register_type(Ed25519Sha256Fulfillment)

View File

@ -5,7 +5,7 @@ from abc import ABCMeta, abstractmethod
from six import string_types from six import string_types
from bigchaindb.crypto.condition import Condition from bigchaindb.crypto.condition import Condition
from bigchaindb.crypto.iostream import Writer, base64_remove_padding, Reader, base64_add_padding from bigchaindb.crypto.iostream import Writer, base64_remove_padding, Reader, base64_add_padding, Predictor
FULFILLMENT_REGEX = r'^cf:1:[1-9a-f][0-9a-f]{0,2}:[a-zA-Z0-9_-]+$' FULFILLMENT_REGEX = r'^cf:1:[1-9a-f][0-9a-f]{0,2}:[a-zA-Z0-9_-]+$'
@ -179,9 +179,7 @@ class Fulfillment(metaclass=ABCMeta):
Return: Return:
{Number} Maximum fulfillment length {Number} Maximum fulfillment length
""" """
# TODO: Predictor predictor = Predictor()
# predictor = Predictor()
predictor = None
self.write_payload(predictor) self.write_payload(predictor)
return predictor.size return predictor.size

View File

@ -164,6 +164,17 @@ class Ed25519Sha256Fulfillment(BaseSha256Fulfillment):
This writes the fulfillment payload to a Writer. This writes the fulfillment payload to a Writer.
COMMON_HEADER =
VARBYTES PUBLIC_KEY
VARBYTES MESSAGE_ID
VARBYTES FIXED_PREFIX
VARUINT DYNAMIC_MESSAGE_LENGTH
FULFILLMENT_PAYLOAD =
COMMON_HEADER
VARBYTES DYNAMIC_MESSAGE
VARBYTES SIGNATURE
Args: Args:
writer (Writer): Subject for writing the fulfillment payload. writer (Writer): Subject for writing the fulfillment payload.
""" """
@ -178,6 +189,16 @@ class Ed25519Sha256Fulfillment(BaseSha256Fulfillment):
Writes the contents of the condition hash to a Hasher. Used internally by `condition`. Writes the contents of the condition hash to a Hasher. Used internally by `condition`.
COMMON_HEADER =
VARBYTES PUBLIC_KEY
VARBYTES MESSAGE_ID
VARBYTES FIXED_PREFIX
VARUINT DYNAMIC_MESSAGE_LENGTH
HASH = SHA256(
COMMON_HEADER
)
Args: Args:
hasher (Hasher): Destination where the hash payload will be written. hasher (Hasher): Destination where the hash payload will be written.
""" """

View File

@ -0,0 +1,94 @@
from bigchaindb.crypto.fulfillments.base_sha256 import BaseSha256Fulfillment
from bigchaindb.crypto.iostream import Hasher, Reader, Writer, Predictor
class Sha256Fulfillment(BaseSha256Fulfillment):
_bitmask = 0x01
def __init__(self):
self._preimage = None
@property
def preimage(self):
return self._preimage
@preimage.setter
def preimage(self, value):
"""
Provide a preimage.
The preimage is the only input to a SHA256 hashlock condition.
Note that the preimage should contain enough (pseudo-random) data in order
to be difficult to guess. A sufficiently large secret seed and a
cryptographically secure pseudo-random number generator (CSPRNG) can be
used to avoid having to store each individual preimage.
Args:
value: Secret data that will be hashed to form the condition.
"""
# TODO: Verify preimage
self._preimage = value
def write_hash_payload(self, hasher):
"""
Generate the contents of the condition hash.
Writes the contents of the condition hash to a Hasher. Used internally by `getCondition`.
HASH = SHA256(PREIMAGE)
Args:
hasher (Hasher): Destination where the hash payload will be written.
"""
if not isinstance(hasher, Hasher):
raise TypeError('hasher must be a Hasher instance')
if self.preimage is None:
raise ValueError('Could not calculate hash, no preimage provided')
hasher.write(self.preimage)
def parse_payload(self, reader):
"""
Parse the payload of a SHA256 hashlock fulfillment.
Read a fulfillment payload from a Reader and populate this object with that fulfillment.
FULFILLMENT_PAYLOAD =
VARBYTES PREIMAGE
Args:
reader (Reader): Source to read the fulfillment payload from.
"""
if not isinstance(reader, Reader):
raise TypeError('reader must be a Reader instance')
self.preimage = reader.read_var_bytes()
def write_payload(self, writer):
"""
Generate the fulfillment payload.
This writes the fulfillment payload to a Writer.
Args:
writer (Writer): Subject for writing the fulfillment payload.
"""
if not isinstance(writer, (Writer, Predictor)):
raise TypeError('writer must be a Writer instance')
if self.preimage is None:
raise ValueError('Preimage must be specified')
writer.write_var_bytes(self.preimage)
return writer
def validate(self):
"""
Validate this fulfillment.
For a SHA256 hashlock fulfillment, successful parsing implies that the
fulfillment is valid, so this method is a no-op.
Returns:
boolean: Validation result
"""
return True

View File

@ -102,6 +102,14 @@ class ThresholdSha256Fulfillment(BaseSha256Fulfillment):
This function is called internally by the `getCondition` method. This function is called internally by the `getCondition` method.
HASH = SHA256(
VARUINT TYPE_BIT
VARUINT THRESHOLD
VARARRAY
VARUINT WEIGHT
CONDITION
)
Args: Args:
hasher (Hasher): Hash generator hasher (Hasher): Hash generator
""" """
@ -182,6 +190,15 @@ class ThresholdSha256Fulfillment(BaseSha256Fulfillment):
This writes the fulfillment payload to a Writer. This writes the fulfillment payload to a Writer.
FULFILLMENT_PAYLOAD =
VARUINT THRESHOLD
VARARRAY
VARUINT WEIGHT
FULFILLMENT
VARARRAY
VARUINT WEIGHT
CONDITION
Args: Args:
writer (Writer): Subject for writing the fulfillment payload. writer (Writer): Subject for writing the fulfillment payload.
""" """
@ -195,6 +212,7 @@ class ThresholdSha256Fulfillment(BaseSha256Fulfillment):
# Prefer shorter fulfillments # Prefer shorter fulfillments
fulfillments.sort(key=lambda f: len(f['binary'])) fulfillments.sort(key=lambda f: len(f['binary']))
# Cut off unnecessary fulfillments
if len(fulfillments) < self.threshold: if len(fulfillments) < self.threshold:
raise ValueError('Not enough subfulfillments') raise ValueError('Not enough subfulfillments')

View File

@ -8,6 +8,7 @@ from bigchaindb.crypto.condition import Condition
from bigchaindb.crypto.ed25519 import ED25519PrivateKey, ED25519PublicKey from bigchaindb.crypto.ed25519 import ED25519PrivateKey, ED25519PublicKey
from bigchaindb.crypto.fulfillment import Fulfillment from bigchaindb.crypto.fulfillment import Fulfillment
from bigchaindb.crypto.fulfillments.ed25519_sha256 import Ed25519Sha256Fulfillment from bigchaindb.crypto.fulfillments.ed25519_sha256 import Ed25519Sha256Fulfillment
from bigchaindb.crypto.fulfillments.sha256 import Sha256Fulfillment
from bigchaindb.crypto.fulfillments.threshold_sha256 import ThresholdSha256Fulfillment from bigchaindb.crypto.fulfillments.threshold_sha256 import ThresholdSha256Fulfillment
@ -20,6 +21,38 @@ class TestBigchainILPSha256Condition:
assert condition.serialize_uri() == self.CONDITION_SHA256_ILP assert condition.serialize_uri() == self.CONDITION_SHA256_ILP
class TestBigchainILPSha256Fulfillment:
CONDITION_SHA256_ILP = 'cc:1:1:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU:1'
FULFILLMENT_SHA256_ILP = 'cf:1:1:AA'
def test_deserialize_and_validate_fulfillment(self):
fulfillment = Fulfillment.from_uri(self.FULFILLMENT_SHA256_ILP)
assert fulfillment.serialize_uri() == self.FULFILLMENT_SHA256_ILP
assert fulfillment.condition.serialize_uri() == self.CONDITION_SHA256_ILP
assert fulfillment.validate()
def test_deserialize_condition_and_validate_fulfillment(self):
condition = Condition.from_uri(self.CONDITION_SHA256_ILP)
fulfillment = Sha256Fulfillment()
fulfillment.preimage = ''
assert fulfillment.serialize_uri() == self.FULFILLMENT_SHA256_ILP
assert fulfillment.condition.serialize_uri() == condition.serialize_uri()
assert fulfillment.validate()
def test_condition_from_fulfillment(self):
fulfillment = Sha256Fulfillment()
with pytest.raises(ValueError):
fulfillment.condition
fulfillment.preimage = 'Hello World!'
condition = fulfillment.condition
verify_fulfillment = Sha256Fulfillment()
verify_fulfillment.preimage = 'Hello World!'
assert verify_fulfillment.condition.serialize_uri() == condition.serialize_uri()
assert verify_fulfillment.validate()
class TestBigchainILPEd25519Sha256Fulfillment: class TestBigchainILPEd25519Sha256Fulfillment:
PUBLIC_HEX_ILP = b'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf' PUBLIC_HEX_ILP = b'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf'
PUBLIC_B64_ILP = b'7Bcrk61eVjv0kyxw4SRQNMNUZ+8u/U1k6/gZaDRn4r8' PUBLIC_B64_ILP = b'7Bcrk61eVjv0kyxw4SRQNMNUZ+8u/U1k6/gZaDRn4r8'
@ -133,12 +166,14 @@ class TestBigchainILPThresholdSha256Fulfillment:
'mUhQNmD2Cvk7e3EFOo-arA2TKYTP-474Z4okhbYmKij6XxObIbRsDScjXILAJ6mV5hP7Xyqkg5fcSsZbfRYypzlsAM' 'mUhQNmD2Cvk7e3EFOo-arA2TKYTP-474Z4okhbYmKij6XxObIbRsDScjXILAJ6mV5hP7Xyqkg5fcSsZbfRYypzlsAM'
HASH_ED25519_HEX_ILP = b'a9020d5b6ba6e7d0b80c1f494955c7d6282a026698186aabca59475200a97cf5' HASH_ED25519_HEX_ILP = b'a9020d5b6ba6e7d0b80c1f494955c7d6282a026698186aabca59475200a97cf5'
CONDITION_THRESHOLD_ED25519_ILP_2 = 'cc:1:c:IZgoTeE1Weg6tfGMLWGe2JmS-waBN-CUrlbhtI9GBcQ:230' CONDITION_SHA256_ILP = 'cc:1:1:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU:1'
FULFILLMENT_SHA256_ILP = 'cf:1:1:AA'
CONDITION_THRESHOLD_ED25519_ILP_2 = 'cc:1:d:fDM51fekeLlbeF9yj9W1KT76jtqa7u0vMlJAbM4EyiE:230'
FULFILLMENT_THRESHOLD_ED25519_ILP_2 = \ FULFILLMENT_THRESHOLD_ED25519_ILP_2 = \
'cf:1:4:AgIBCCDsFyuTrV5WO_STLHDhJFA0w1Rn7y79TWTr-BloNGfivwxIZWxsbyB3b3JsZCEgFSBDb25kaXRpb25zIGFyZSBoZXJlIUBDW' \ 'cf:1:4:AgIBAQABCCDsFyuTrV5WO_STLHDhJFA0w1Rn7y79TWTr-BloNGfivwxIZWxsbyB3b3JsZCEgFSBDb25kaXRpb25zIGFyZSBoZXJlI' \
'6ped9T2wiZUVLyoz-epNFyiTDqyBqNheurnrk7UZ2KyQdrdmbbXX1zOIMw__O3h9Z2U6buK05AMfNYUnacCAQgg7Bcrk61eVjv0kyxw4SRQN' \ 'UBDW6ped9T2wiZUVLyoz-epNFyiTDqyBqNheurnrk7UZ2KyQdrdmbbXX1zOIMw__O3h9Z2U6buK05AMfNYUnacCAQEIIP1s06x7xZ7-CPh9H' \
'MNUZ-8u_U1k6_gZaDRn4r8MSGVsbG8gd29ybGQhIBUgQ29uZGl0aW9ucyBhcmUgaGVyZSFAQ1uqXnfU9sImVFS8qM_nqTRcokw6sgajYXrq5' \ 'AClmekN4N2NA7t2ltNZyLGgLIqPdA'
'65O1GdiskHa3Zm2119cziDMP_zt4fWdlOm7itOQDHzWFJ2nAgEBCCD9bNOse8We_gj4fRwApZnpDeDdjQO7dpbTWcixoCyKj3Q'
def create_fulfillment_ed25519sha256(self): def create_fulfillment_ed25519sha256(self):
sk = ED25519PrivateKey(self.PRIVATE_B58_ILP) sk = ED25519PrivateKey(self.PRIVATE_B58_ILP)
@ -153,8 +188,9 @@ class TestBigchainILPThresholdSha256Fulfillment:
return fulfillment return fulfillment
def test_serialize_condition_and_validate_fulfillment(self): def test_serialize_condition_and_validate_fulfillment(self):
ilp_fulfillment = Fulfillment.from_uri(self.FULFILLMENT_ED25519_ILP) ilp_fulfillment = Fulfillment.from_uri(self.FULFILLMENT_ED25519_ILP_2)
ilp_fulfillment_2 = Fulfillment.from_uri(self.FULFILLMENT_ED25519_ILP_2) ilp_fulfillment_2 = Fulfillment.from_uri(self.FULFILLMENT_ED25519_ILP)
ilp_fulfillment_3 = Fulfillment.from_uri(self.FULFILLMENT_SHA256_ILP)
assert ilp_fulfillment.validate() == True assert ilp_fulfillment.validate() == True
assert ilp_fulfillment_2.validate() == True assert ilp_fulfillment_2.validate() == True
@ -163,9 +199,9 @@ class TestBigchainILPThresholdSha256Fulfillment:
# Create a threshold condition # Create a threshold condition
fulfillment = ThresholdSha256Fulfillment() fulfillment = ThresholdSha256Fulfillment()
fulfillment.add_subfulfillment(ilp_fulfillment)
fulfillment.add_subfulfillment(ilp_fulfillment_2) fulfillment.add_subfulfillment(ilp_fulfillment_2)
fulfillment.add_subfulfillment(ilp_fulfillment) fulfillment.add_subfulfillment(ilp_fulfillment_3)
fulfillment.add_subfulfillment(ilp_fulfillment)
fulfillment.threshold = THRESHOLD # defaults to subconditions.length fulfillment.threshold = THRESHOLD # defaults to subconditions.length
assert fulfillment.condition.serialize_uri() == self.CONDITION_THRESHOLD_ED25519_ILP_2 assert fulfillment.condition.serialize_uri() == self.CONDITION_THRESHOLD_ED25519_ILP_2
@ -185,6 +221,10 @@ class TestBigchainILPThresholdSha256Fulfillment:
assert len(fulfillment.get_all_subconditions()) == NUM_FULFILLMENTS assert len(fulfillment.get_all_subconditions()) == NUM_FULFILLMENTS
assert fulfillment.serialize_uri() == self.FULFILLMENT_THRESHOLD_ED25519_ILP_2 assert fulfillment.serialize_uri() == self.FULFILLMENT_THRESHOLD_ED25519_ILP_2
assert fulfillment.validate() assert fulfillment.validate()
assert isinstance(fulfillment.subfulfillments[0], Sha256Fulfillment)
assert isinstance(fulfillment.subfulfillments[1], Ed25519Sha256Fulfillment)
assert fulfillment.subfulfillments[0].condition.serialize_uri() == self.CONDITION_SHA256_ILP
assert fulfillment.subfulfillments[1].condition.serialize_uri() == self.CONDITION_ED25519_ILP
def test_serialize_deserialize_fulfillment(self): def test_serialize_deserialize_fulfillment(self):
ilp_fulfillment = Fulfillment.from_uri(self.FULFILLMENT_ED25519_ILP) ilp_fulfillment = Fulfillment.from_uri(self.FULFILLMENT_ED25519_ILP)