def test_valid_condition_invalid_signature_ilp(self, vk_ilp, signature): vk = VerifyingKey( VerifyingKey.encode( base64_add_padding(vk_ilp[2]['b64']))) msg = base64.b64decode(signature['msg']) assert vk.verify(msg, signature['msg'], encoding='base64') is False assert vk.verify(msg, binascii.hexlify(base64.b64decode(signature['msg'])), encoding='hex') is False assert vk.verify(msg, base64.b64decode(signature['msg']), encoding=None) is False
def test_sign_verify(self, sk_ilp, vk_ilp): message = b'Hello World!' sk = SigningKey(sk_ilp['b58']) vk = VerifyingKey(vk_ilp['b58']) assert vk.verify(message, sk.sign(message)) is True assert vk.verify(message, sk.sign(message + b'dummy')) is False assert vk.verify(message + b'dummy', sk.sign(message)) is False vk = VerifyingKey(vk_ilp[2]['b64'], encoding='base64') assert vk.verify(message, sk.sign(message)) is False
def test_valid_condition_invalid_signature_ilp(self, vk_ilp, signature): vk = VerifyingKey(vk_ilp[2]['b64'], encoding='base64') msg = base64.b64decode(signature['msg']) assert vk.verify(msg, signature['msg'], encoding='base64') is False assert vk.verify(msg, binascii.hexlify(base64.b64decode(signature['msg'])), encoding='hex') is False assert vk.verify( msg, base64.b64decode(signature['msg']), encoding='bytes') is False
def test_generate_sign_verify(self, vk_ilp): sk_b58, vk_b58 = ed25519_generate_key_pair() sk = SigningKey(sk_b58) vk = VerifyingKey(vk_b58) message = b'Hello World!' assert vk.verify(message, sk.sign(message)) is True assert vk.verify(message, sk.sign(message + b'dummy')) is False assert vk.verify(message + b'dummy', sk.sign(message)) is False vk = VerifyingKey(vk_ilp[2]['b64'], encoding='base64') assert vk.verify(message, sk.sign(message)) is False
def test_sign_verify(self, sk_ilp, vk_ilp): message = 'Hello World!' sk = SigningKey(sk_ilp['b58']) vk = VerifyingKey(vk_ilp['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 = VerifyingKey( VerifyingKey.encode( base64_add_padding(vk_ilp[2]['b64']))) assert vk.verify(message, sk.sign(message)) is False
def test_weak_public_keys(self): """reproduce the problem in https://github.com/bigchaindb/bigchaindb/issues/617 This problem is due to weak keys, specially in this case the key and signature when decoded from base58 correspond to a key and a signature that are zero. In this case its possible to come up with messages that would verify. Libraries like libsodium check for these weak keys and return a BadSignature error if weak keys are being used. More details here: https://github.com/jedisct1/libsodium/issues/112 """ vk_b58 = VerifyingKey('1' * 32) message = b'age=33&name=luo&title=architecture' signature = b'1' * 64 assert vk_b58.verify(message, signature) == False
def test_valid_condition_valid_signature_ilp(self, vk_ilp, signature): vk = VerifyingKey(vk_ilp[2]['b64'], encoding='base64') msg = base64.b64decode(signature['msg']) assert vk.verify(msg, signature['sig'], encoding='base64') is True assert vk.verify(msg, binascii.hexlify(base64.b64decode(signature['sig'])), encoding='hex') is True assert vk.verify(msg, base64.b64decode(signature['sig']), encoding='bytes') is True
class Ed25519Fulfillment(Fulfillment): TYPE_ID = 4 FEATURE_BITMASK = 0x20 PUBKEY_LENGTH = 32 SIGNATURE_LENGTH = 64 FULFILLMENT_LENGTH = PUBKEY_LENGTH + SIGNATURE_LENGTH def __init__(self, public_key=None): """ ED25519: Ed25519 signature condition. This condition implements Ed25519 signatures. ED25519 is assigned the type ID 4. It relies only on the ED25519 feature suite which corresponds to a bitmask of 0x20. Args: public_key (VerifyingKey): Ed25519 publicKey """ if public_key and isinstance(public_key, (str, bytes)): public_key = VerifyingKey(public_key) if public_key and not isinstance(public_key, VerifyingKey): raise TypeError self.public_key = public_key self.signature = None def write_common_header(self, writer): """ Write static header fields. Some fields are common between the hash and the fulfillment payload. This method writes those field to anything implementing the Writer interface. It is used internally when generating the hash of the condition, when generating the fulfillment payload and when calculating the maximum fulfillment size. Args: writer (Writer, Hasher, Predictor): Target for outputting the header. """ writer.write_var_octet_string(self.public_key) def sign(self, message, private_key): """ Sign the message. This method will take the currently configured values for the message prefix and suffix and create a signature using the provided Ed25519 private key. Args: message (string): message to be signed private_key (string) Ed25519 private key """ sk = private_key vk = VerifyingKey(base58.b58encode(sk.get_verifying_key().to_bytes())) self.public_key = vk # This would be the Ed25519ph version (JavaScript ES7): # const message = crypto.createHash('sha512') # .update(Buffer.concat([this.messagePrefix, this.message])) # .digest() self.signature = sk.sign(message, encoding=None) def generate_hash(self): """ Generate the condition hash. Since the public key is the same size as the hash we'd be putting out here, we just return the public key. """ if not self.public_key: raise ValueError('Requires a public publicKey') return self.public_key.to_bytes() def parse_payload(self, reader, *args): """ Parse the payload of an Ed25519 fulfillment. Read a fulfillment payload from a Reader and populate this object with that fulfillment. Args: reader (Reader): Source to read the fulfillment payload from. """ self.public_key = VerifyingKey(base58.b58encode(reader.read_octet_string(Ed25519Fulfillment.PUBKEY_LENGTH))) self.signature = reader.read_octet_string(Ed25519Fulfillment.SIGNATURE_LENGTH) def write_payload(self, writer): """ Generate the fulfillment payload. This writes the fulfillment payload to a Writer. FULFILLMENT_PAYLOAD = VARBYTES PUBLIC_KEY VARBYTES SIGNATURE Args: writer (Writer): Subject for writing the fulfillment payload. """ writer.write_octet_string(self.public_key.to_bytes(), Ed25519Fulfillment.PUBKEY_LENGTH) writer.write_octet_string(self.signature, Ed25519Fulfillment.SIGNATURE_LENGTH) return writer def calculate_max_fulfillment_length(self): return Ed25519Fulfillment.FULFILLMENT_LENGTH def serialize_json(self): """ Generate a JSON object of the fulfillment Returns: """ return json.dumps( { 'type': 'fulfillment', 'type_id': Ed25519Fulfillment.TYPE_ID, 'bitmask': self.bitmask, 'public_key': self.public_key.to_ascii(encoding='base58').decode(), 'signature': base58.b58encode(self.signature) if self.signature else None } ) def parse_json(self, json_data): """ Generate fulfillment payload from a json Args: json_data: json description of the fulfillment Returns: Fulfillment """ self.public_key = VerifyingKey(json_data['public_key']) self.signature = base58.b58decode(json_data['signature']) if json_data['signature'] else None def validate(self, message=None): """ Verify the signature of this Ed25519 fulfillment. The signature of this Ed25519 fulfillment is verified against the provided message and public key. Args: message (str): Message to validate against. Return: boolean: Whether this fulfillment is valid. """ if not (message and self.signature): return False return self.public_key.verify(message, self.signature, encoding=None)
class Ed25519Fulfillment(Fulfillment): """ """ TYPE_ID = 4 FEATURE_BITMASK = 0x20 PUBKEY_LENGTH = 32 SIGNATURE_LENGTH = 64 FULFILLMENT_LENGTH = PUBKEY_LENGTH + SIGNATURE_LENGTH def __init__(self, public_key=None): """ ED25519: Ed25519 signature condition. This condition implements Ed25519 signatures. ED25519 is assigned the type ID 4. It relies only on the ED25519 feature suite which corresponds to a bitmask of 0x20. Args: public_key (VerifyingKey): Ed25519 publicKey """ if public_key and isinstance(public_key, (str, bytes)): public_key = VerifyingKey(public_key) if public_key and not isinstance(public_key, VerifyingKey): raise TypeError self.public_key = public_key self.signature = None def write_common_header(self, writer): """ Write static header fields. Some fields are common between the hash and the fulfillment payload. This method writes those field to anything implementing the Writer interface. It is used internally when generating the hash of the condition, when generating the fulfillment payload and when calculating the maximum fulfillment size. Args: writer (Writer, Hasher, Predictor): Target for outputting the header. """ writer.write_var_octet_string(self.public_key) def sign(self, message, private_key): """ Sign the message. This method will take the currently configured values for the message prefix and suffix and create a signature using the provided Ed25519 private key. Args: message (bytes): message to be signed private_key (:obj:`Ed25519SigningKey`) Ed25519 private key """ sk = private_key vk = sk.get_verifying_key() self.public_key = vk # This would be the Ed25519ph version (JavaScript ES7): # const message = crypto.createHash('sha512') # .update(Buffer.concat([this.messagePrefix, this.message])) # .digest() self.signature = sk.sign(message, encoding='bytes') def generate_hash(self): """ Generate the condition hash. Since the public key is the same size as the hash we'd be putting out here, we just return the public key. """ if not self.public_key: raise ValueError('Requires a public publicKey') return self.public_key.encode(encoding='bytes') def parse_payload(self, reader, *args): """ Parse the payload of an Ed25519 fulfillment. Read a fulfillment payload from a Reader and populate this object with that fulfillment. Args: reader (Reader): Source to read the fulfillment payload from. """ self.public_key = VerifyingKey( base58.b58encode( reader.read_octet_string(Ed25519Fulfillment.PUBKEY_LENGTH))) self.signature = reader.read_octet_string( Ed25519Fulfillment.SIGNATURE_LENGTH) def write_payload(self, writer): """ Generate the fulfillment payload. This writes the fulfillment payload to a Writer. FULFILLMENT_PAYLOAD = VARBYTES PUBLIC_KEY VARBYTES SIGNATURE Args: writer (Writer): Subject for writing the fulfillment payload. """ writer.write_octet_string(self.public_key.encode(encoding='bytes'), Ed25519Fulfillment.PUBKEY_LENGTH) writer.write_octet_string(self.signature, Ed25519Fulfillment.SIGNATURE_LENGTH) return writer def calculate_max_fulfillment_length(self): return Ed25519Fulfillment.FULFILLMENT_LENGTH def to_dict(self): """ Generate a dict of the fulfillment Returns: dict: representing the fulfillment """ return { 'type': 'fulfillment', 'type_id': self.TYPE_ID, 'bitmask': self.bitmask, 'public_key': self.public_key.encode(encoding='base58').decode(), 'signature': base58.b58encode(self.signature) if self.signature else None } def parse_dict(self, data): """ Generate fulfillment payload from a dict Args: data (dict): description of the fulfillment Returns: Fulfillment """ self.public_key = VerifyingKey(data['public_key']) self.signature = base58.b58decode( data['signature']) if data['signature'] else None def validate(self, message=None, **kwargs): """ Verify the signature of this Ed25519 fulfillment. The signature of this Ed25519 fulfillment is verified against the provided message and public key. Args: message (str): Message to validate against. Return: boolean: Whether this fulfillment is valid. """ if not (message and self.signature): return False return self.public_key.verify(message, self.signature, encoding='bytes')