Merge pull request #278 from jMyles/umbral-compat

Uses Alice's Stamp as a signer for KFrags; Bob and Ursula validate them as such.
pull/276/head^2
Justin Holmes 2018-05-29 18:35:54 -04:00 committed by GitHub
commit 19b0f71555
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 137 additions and 174 deletions

View File

@ -21,7 +21,7 @@ apistar = "*"
mypy = "*" mypy = "*"
pytest-mypy = "*" pytest-mypy = "*"
maya = "*" maya = "*"
pyumbral = {git = "https://github.com/nucypher/pyumbral.git"} pyumbral = {git = "https://github.com/nucypher/pyumbral.git", ref = "kfrag-signing"}
requests = "*" requests = "*"
hendrix = {git = "https://github.com/hendrix/hendrix", ref = "tags/3.0.0rc1"} hendrix = {git = "https://github.com/hendrix/hendrix", ref = "tags/3.0.0rc1"}
constantSorrow = {git = "https://github.com/nucypher/constantSorrow.git", ref = "nucypher-depend"} constantSorrow = {git = "https://github.com/nucypher/constantSorrow.git", ref = "nucypher-depend"}

View File

@ -19,7 +19,7 @@ from nucypher.crypto.api import secure_random, keccak_digest, encrypt_and_sign
from nucypher.crypto.constants import PUBLIC_KEY_LENGTH from nucypher.crypto.constants import PUBLIC_KEY_LENGTH
from nucypher.crypto.kits import UmbralMessageKit from nucypher.crypto.kits import UmbralMessageKit
from nucypher.crypto.powers import CryptoPower, SigningPower, EncryptingPower, DelegatingPower, NoSigningPower from nucypher.crypto.powers import CryptoPower, SigningPower, EncryptingPower, DelegatingPower, NoSigningPower
from nucypher.crypto.signature import Signature, signature_splitter, SignatureStamp, StrangerStamp from nucypher.crypto.signing import Signature, signature_splitter, SignatureStamp, StrangerStamp
from nucypher.network import blockchain_client from nucypher.network import blockchain_client
from nucypher.network.protocols import dht_value_splitter, dht_with_hrac_splitter from nucypher.network.protocols import dht_value_splitter, dht_with_hrac_splitter
from nucypher.network.server import NucypherDHTServer, NucypherSeedOnlyDHTServer, ProxyRESTServer from nucypher.network.server import NucypherDHTServer, NucypherSeedOnlyDHTServer, ProxyRESTServer
@ -36,9 +36,9 @@ class Character(object):
address = "This is a fake address." # TODO: #192 address = "This is a fake address." # TODO: #192
def __init__(self, attach_server=True, crypto_power: CryptoPower=None, def __init__(self, attach_server=True, crypto_power: CryptoPower = None,
crypto_power_ups=None, is_me=True, network_middleware=None, crypto_power_ups=None, is_me=True, network_middleware=None,
config: "NucypherConfig"=None) -> None: config: "NucypherConfig" = None) -> None:
""" """
:param attach_server: Whether to attach a Server when this Character is :param attach_server: Whether to attach a Server when this Character is
born. born.
@ -79,7 +79,8 @@ class Character(object):
if is_me: if is_me:
self.network_middleware = network_middleware or NetworkyStuff() self.network_middleware = network_middleware or NetworkyStuff()
try: try:
self._stamp = SignatureStamp(self._crypto_power.power_ups(SigningPower).keypair) signing_power = self._crypto_power.power_ups(SigningPower)
self._stamp = signing_power.get_signature_stamp()
except NoSigningPower: except NoSigningPower:
self._stamp = constants.NO_SIGNING_POWER self._stamp = constants.NO_SIGNING_POWER
@ -87,8 +88,9 @@ class Character(object):
self.attach_server() self.attach_server()
else: else:
if network_middleware is not None: if network_middleware is not None:
raise TypeError("Can't attach network middleware to a Character who isn't me. What are you even trying to do?") raise TypeError(
self._stamp = StrangerStamp(self._crypto_power.power_ups(SigningPower).keypair) "Can't attach network middleware to a Character who isn't me. What are you even trying to do?")
self._stamp = StrangerStamp(self.public_key(SigningPower))
def __eq__(self, other): def __eq__(self, other):
return bytes(self.stamp) == bytes(other.stamp) return bytes(self.stamp) == bytes(other.stamp)
@ -159,7 +161,7 @@ class Character(object):
def encrypt_for(self, def encrypt_for(self,
recipient: "Character", recipient: "Character",
plaintext: bytes, plaintext: bytes,
sign: bool=True, sign: bool = True,
sign_plaintext=True, sign_plaintext=True,
) -> tuple: ) -> tuple:
""" """
@ -187,8 +189,9 @@ class Character(object):
def verify_from(self, def verify_from(self,
actor_whom_sender_claims_to_be: "Character", actor_whom_sender_claims_to_be: "Character",
message_kit: Union[UmbralMessageKit, bytes], message_kit: Union[UmbralMessageKit, bytes],
signature: Signature=None, signature: Signature = None,
decrypt=False, decrypt=False,
delegator_signing_key: UmbralPublicKey = None,
) -> tuple: ) -> tuple:
""" """
Inverse of encrypt_for. Inverse of encrypt_for.
@ -196,11 +199,14 @@ class Character(object):
:param actor_that_sender_claims_to_be: A Character instance representing :param actor_that_sender_claims_to_be: A Character instance representing
the actor whom the sender claims to be. We check the public key the actor whom the sender claims to be. We check the public key
owned by this Character instance to verify. owned by this Character instance to verify.
:param messages: The messages to be verified. :param message_kit: the message to be (perhaps decrypted and) verified.
:param signature: The signature to check.
:param decrypt: Whether or not to decrypt the messages. :param decrypt: Whether or not to decrypt the messages.
:param signature_is_on_cleartext: True if we expect the signature to be :param delegator_signing_key: A signing key from the original delegator.
on the cleartext. Otherwise, we presume that the ciphertext is what This is used only when decrypting a MessageKit with an activated Capsule
is signed. to check that the KFrag used to create each attached CFrag is the
authentic KFrag initially created by the delegator.
:return: Whether or not the signature is valid, the decrypted plaintext :return: Whether or not the signature is valid, the decrypted plaintext
or NO_DECRYPTION_PERFORMED or NO_DECRYPTION_PERFORMED
""" """
@ -215,7 +221,7 @@ class Character(object):
if decrypt: if decrypt:
# We are decrypting the message; let's do that first and see what the sig header says. # We are decrypting the message; let's do that first and see what the sig header says.
cleartext_with_sig_header = self.decrypt(message_kit) cleartext_with_sig_header = self.decrypt(message_kit, verifying_key=delegator_signing_key)
sig_header, cleartext = default_constant_splitter(cleartext_with_sig_header, return_remainder=True) sig_header, cleartext = default_constant_splitter(cleartext_with_sig_header, return_remainder=True)
if sig_header == constants.SIGNATURE_IS_ON_CIPHERTEXT: if sig_header == constants.SIGNATURE_IS_ON_CIPHERTEXT:
# THe ciphertext is what is signed - note that for later. # THe ciphertext is what is signed - note that for later.
@ -254,8 +260,8 @@ class Character(object):
If they don't have the correct Power, the appropriate PowerUpError is raised. If they don't have the correct Power, the appropriate PowerUpError is raised.
""" """
def decrypt(self, message_kit): def decrypt(self, message_kit, verifying_key: UmbralPublicKey = None):
return self._crypto_power.power_ups(EncryptingPower).decrypt(message_kit) return self._crypto_power.power_ups(EncryptingPower).decrypt(message_kit, verifying_key)
def sign(self, message): def sign(self, message):
return self._crypto_power.power_ups(SigningPower).sign(message) return self._crypto_power.power_ups(SigningPower).sign(message)
@ -302,7 +308,8 @@ class Character(object):
powers_and_keys=({SigningPower: pubkey}) powers_and_keys=({SigningPower: pubkey})
) )
else: else:
message = "Suspicious Activity: Discovered node with bad signature: {}. Propagated by: {}:{}".format(node_meta, address, port) message = "Suspicious Activity: Discovered node with bad signature: {}. Propagated by: {}:{}".format(
node_meta, address, port)
self.log.warn(message) self.log.warn(message)
return new_nodes return new_nodes
@ -335,7 +342,8 @@ class Alice(Character, PolicyAuthor):
:param n: Total number of kfrags to generate :param n: Total number of kfrags to generate
""" """
bob_pubkey_enc = bob.public_key(EncryptingPower) bob_pubkey_enc = bob.public_key(EncryptingPower)
return self._crypto_power.power_ups(DelegatingPower).generate_kfrags(bob_pubkey_enc, label, m, n) delegating_power = self._crypto_power.power_ups(DelegatingPower)
return delegating_power.generate_kfrags(bob_pubkey_enc, self.stamp, label, m, n)
def create_policy(self, bob: "Bob", label: bytes, m: int, n: int): def create_policy(self, bob: "Bob", label: bytes, m: int, n: int):
""" """
@ -654,8 +662,8 @@ class Ursula(Character, ProxyRESTServer):
def interface_information(self): def interface_information(self):
return msgpack.dumps((self.ip_address, return msgpack.dumps((self.ip_address,
self.dht_port, self.dht_port,
self.rest_port)) self.rest_port))
def interface_info_with_metadata(self): def interface_info_with_metadata(self):
interface_info = self.interface_information() interface_info = self.interface_information()

View File

@ -117,7 +117,7 @@ class DerivedKeyBasedPower(CryptoPowerUp):
class SigningPower(KeyPairBasedPower): class SigningPower(KeyPairBasedPower):
_keypair_class = SigningKeypair _keypair_class = SigningKeypair
not_found_error = NoSigningPower not_found_error = NoSigningPower
provides = ("sign", "generate_self_signed_cert") provides = ("sign", "generate_self_signed_cert", "get_signature_stamp")
class EncryptingPower(KeyPairBasedPower): class EncryptingPower(KeyPairBasedPower):
@ -131,7 +131,7 @@ class DelegatingPower(DerivedKeyBasedPower):
def __init__(self): def __init__(self):
self.umbral_keying_material = UmbralKeyingMaterial() self.umbral_keying_material = UmbralKeyingMaterial()
def generate_kfrags(self, bob_pubkey_enc, label, m, n) -> Union[UmbralPublicKey, List]: def generate_kfrags(self, bob_pubkey_enc, signer, label, m, n) -> Union[UmbralPublicKey, List]:
""" """
Generates re-encryption key frags ("KFrags") and returns them. Generates re-encryption key frags ("KFrags") and returns them.
@ -144,5 +144,5 @@ class DelegatingPower(DerivedKeyBasedPower):
# TODO: salt? #265 # TODO: salt? #265
__private_key = self.umbral_keying_material.derive_privkey_by_label(label) __private_key = self.umbral_keying_material.derive_privkey_by_label(label)
kfrags = pre.split_rekey(__private_key, bob_pubkey_enc, m, n) kfrags = pre.split_rekey(__private_key, signer, bob_pubkey_enc, m, n)
return __private_key.get_pubkey(), kfrags return __private_key.get_pubkey(), kfrags

View File

@ -1,123 +0,0 @@
from nucypher.crypto import api as API
from umbral.keys import UmbralPublicKey
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature, encode_dss_signature
from nucypher.crypto.api import keccak_digest
from bytestring_splitter import BytestringSplitter
class Signature(object):
"""
The Signature object allows signatures to be made and verified.
"""
_EXPECTED_LENGTH = 64 # With secp256k1
def __init__(self, r: int, s: int):
# TODO: Sanity check for proper r and s.
self.r = r
self.s = s
def __repr__(self):
return "ECDSA Signature: {}".format(bytes(self).hex()[:15])
def verify(self, message: bytes, pubkey: UmbralPublicKey) -> bool:
"""
Verifies that a message's signature was valid.
:param message: The message to verify
:param pubkey: UmbralPublicKey of the signer
:return: True if valid, False if invalid
"""
return API.ecdsa_verify(message, self._der_encoded_bytes(), pubkey)
@classmethod
def from_bytes(cls, signature_as_bytes, der_encoded=False):
# TODO: Change the int literals to variables which account for the order of the curve.
if der_encoded:
r, s = decode_dss_signature(signature_as_bytes)
else:
if not len(signature_as_bytes) == 64:
raise ValueError("Looking for exactly 64 bytes if you call from_bytes with der_encoded=False.")
else:
r = int.from_bytes(signature_as_bytes[:32], "big")
s = int.from_bytes(signature_as_bytes[32:], "big")
return cls(r, s)
def _der_encoded_bytes(self):
return encode_dss_signature(self.r, self.s)
def __bytes__(self):
return self.r.to_bytes(32, "big") + self.s.to_bytes(32, "big")
def __len__(self):
return len(bytes(self))
def __add__(self, other):
return bytes(self) + other
def __radd__(self, other):
return other + bytes(self)
def __eq__(self, other):
# TODO: Consider constant time
return bytes(self) == bytes(other) or self._der_encoded_bytes() == other
signature_splitter = BytestringSplitter(Signature)
class SignatureStamp(object):
"""
Can be called to sign something or used to express the signing public
key as bytes.
"""
def __init__(self, signing_keypair):
self._sign = signing_keypair.sign
self._as_bytes = bytes(signing_keypair.pubkey)
self._as_umbral_pubkey = signing_keypair.pubkey
def __bytes__(self):
return self._as_bytes
def __call__(self, *args, **kwargs):
return self._sign(*args, **kwargs)
def __hash__(self):
return int.from_bytes(self, byteorder="big")
def __eq__(self, other):
return other == bytes(self)
def __add__(self, other):
return bytes(self) + other
def __radd__(self, other):
return other + bytes(self)
def __len__(self):
return len(bytes(self))
def __bool__(self):
return True
def as_umbral_pubkey(self):
return self._as_umbral_pubkey
def fingerprint(self):
"""
Hashes the key using keccak-256 and returns the hexdigest in bytes.
:return: Hexdigest fingerprint of key (keccak-256) in bytes
"""
return keccak_digest(bytes(self)).hex().encode()
class StrangerStamp(SignatureStamp):
"""
SignatureStamp of a stranger (ie, can only be used to glean public key, not to sign)
"""
def __call__(self, *args, **kwargs):
message = "This isn't your SignatureStamp; it belongs to (a Stranger). You can't sign with it."
raise TypeError(message)

View File

@ -0,0 +1,61 @@
from nucypher.crypto.api import keccak_digest
from bytestring_splitter import BytestringSplitter
from umbral.signing import Signature, Signer
signature_splitter = BytestringSplitter(Signature)
class SignatureStamp(object):
"""
Can be called to sign something or used to express the signing public
key as bytes.
"""
def __init__(self, signing_key, signer: Signer=None):
self.__signer = signer
self._as_bytes = bytes(signing_key)
self._as_umbral_pubkey = signing_key
def __bytes__(self):
return self._as_bytes
def __call__(self, *args, **kwargs):
return self.__signer(*args, **kwargs)
def __hash__(self):
return int.from_bytes(self, byteorder="big")
def __eq__(self, other):
return other == bytes(self)
def __add__(self, other):
return bytes(self) + other
def __radd__(self, other):
return other + bytes(self)
def __len__(self):
return len(bytes(self))
def __bool__(self):
return True
def as_umbral_pubkey(self):
return self._as_umbral_pubkey
def fingerprint(self):
"""
Hashes the key using keccak-256 and returns the hexdigest in bytes.
:return: Hexdigest fingerprint of key (keccak-256) in bytes
"""
return keccak_digest(bytes(self)).hex().encode()
class StrangerStamp(SignatureStamp):
"""
SignatureStamp of a stranger (ie, can only be used to glean public key, not to sign)
"""
def __call__(self, *args, **kwargs):
message = "This isn't your SignatureStamp; it belongs to (a Stranger). You can't sign with it."
raise TypeError(message)

View File

@ -1,17 +1,20 @@
from nucypher.crypto.api import encrypt_and_sign from nucypher.crypto.api import encrypt_and_sign
from nucypher.crypto.signature import SignatureStamp from nucypher.crypto.powers import SigningPower
from nucypher.crypto.signing import SignatureStamp
from nucypher.keystore.keypairs import SigningKeypair from nucypher.keystore.keypairs import SigningKeypair
from constant_sorrow.constants import NO_SIGNING_POWER from constant_sorrow.constants import NO_SIGNING_POWER
from umbral.keys import UmbralPublicKey from umbral.keys import UmbralPublicKey
from umbral.signing import Signer
class DataSource: class DataSource:
def __init__(self, policy_pubkey_enc, signer=NO_SIGNING_POWER, label=None): def __init__(self, policy_pubkey_enc, signing_keypair=NO_SIGNING_POWER, label=None):
self.policy_pubkey = policy_pubkey_enc self.policy_pubkey = policy_pubkey_enc
if signer is NO_SIGNING_POWER: if signing_keypair is NO_SIGNING_POWER:
signer = SignatureStamp(SigningKeypair()) # TODO: Generate signing key properly. #241 signing_keypair = SigningKeypair() # TODO: Generate signing key properly. #241
self.stamp = signer signing_power = SigningPower(keypair=signing_keypair)
self.stamp = signing_power.get_signature_stamp()
self.label = label self.label = label
def encapsulate_single_message(self, message): def encapsulate_single_message(self, message):

View File

@ -7,16 +7,18 @@ from umbral.keys import UmbralPrivateKey, UmbralPublicKey
from umbral import pre from umbral import pre
from umbral.config import default_curve from umbral.config import default_curve
from nucypher.crypto.kits import MessageKit from nucypher.crypto.kits import MessageKit
from nucypher.crypto.signature import Signature from nucypher.crypto.signing import SignatureStamp
from umbral.signing import Signature, Signer
class Keypair(object): class Keypair(object):
""" """
A parent Keypair class for all types of Keypairs. A parent Keypair class for all types of Keypairs.
""" """
def __init__(self, def __init__(self,
umbral_key: Union[UmbralPrivateKey, UmbralPublicKey]=None, umbral_key: Union[UmbralPrivateKey, UmbralPublicKey] = None,
generate_keys_if_needed=True): generate_keys_if_needed=True):
""" """
Initalizes a Keypair object with an Umbral key object. Initalizes a Keypair object with an Umbral key object.
@ -34,7 +36,8 @@ class Keypair(object):
self._privkey = UmbralPrivateKey.gen_key() self._privkey = UmbralPrivateKey.gen_key()
self.pubkey = self._privkey.get_pubkey() self.pubkey = self._privkey.get_pubkey()
else: else:
raise ValueError("Either pass a valid key as umbral_key or, if you want to generate keys, set generate_keys_if_needed to True.") raise ValueError(
"Either pass a valid key as umbral_key or, if you want to generate keys, set generate_keys_if_needed to True.")
def serialize_pubkey(self, as_b64=False) -> bytes: def serialize_pubkey(self, as_b64=False) -> bytes:
""" """
@ -61,10 +64,11 @@ class EncryptingKeypair(Keypair):
""" """
A keypair for Umbral A keypair for Umbral
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def decrypt(self, message_kit: MessageKit) -> bytes: def decrypt(self, message_kit: MessageKit, verifying_key: UmbralPublicKey = None) -> bytes:
""" """
Decrypt data encrypted with Umbral. Decrypt data encrypted with Umbral.
@ -72,8 +76,10 @@ class EncryptingKeypair(Keypair):
""" """
cleartext = pre.decrypt(ciphertext=message_kit.ciphertext, cleartext = pre.decrypt(ciphertext=message_kit.ciphertext,
capsule=message_kit.capsule, capsule=message_kit.capsule,
priv_key=self._privkey, decrypting_key=self._privkey,
alice_pub_key=message_kit.policy_pubkey) delegating_pubkey=message_kit.policy_pubkey,
verifying_key=verifying_key,
)
return cleartext return cleartext
@ -82,6 +88,7 @@ class SigningKeypair(Keypair):
""" """
A SigningKeypair that uses ECDSA. A SigningKeypair that uses ECDSA.
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -99,3 +106,7 @@ class SigningKeypair(Keypair):
def generate_self_signed_cert(self, common_name): def generate_self_signed_cert(self, common_name):
cryptography_key = self._privkey.to_cryptography_privkey() cryptography_key = self._privkey.to_cryptography_privkey()
return generate_self_signed_certificate(common_name, default_curve(), cryptography_key) return generate_self_signed_certificate(common_name, default_curve(), cryptography_key)
def get_signature_stamp(self):
signer = Signer(self._privkey)
return SignatureStamp(signing_key=self.pubkey, signer=signer)

View File

@ -1,6 +1,6 @@
from typing import Union from typing import Union
from nucypher.crypto.signature import Signature from nucypher.crypto.signing import Signature
from bytestring_splitter import BytestringSplitter from bytestring_splitter import BytestringSplitter
from nucypher.keystore.db.models import Key, PolicyArrangement, Workorder from nucypher.keystore.db.models import Key, PolicyArrangement, Workorder
from umbral.fragments import KFrag from umbral.fragments import KFrag
@ -21,7 +21,7 @@ class KeyStore(object):
""" """
A storage class of cryptographic keys. A storage class of cryptographic keys.
""" """
kfrag_splitter = BytestringSplitter(Signature, (KFrag, KFrag.get_size())) kfrag_splitter = BytestringSplitter(Signature, (KFrag, KFrag.expected_bytes_length()))
def __init__(self, sqlalchemy_engine=None): def __init__(self, sqlalchemy_engine=None):
""" """

View File

@ -5,7 +5,7 @@ from kademlia.utils import digest
from constant_sorrow import default_constant_splitter, constants from constant_sorrow import default_constant_splitter, constants
from nucypher.crypto.api import keccak_digest from nucypher.crypto.api import keccak_digest
from nucypher.crypto.constants import PUBLIC_KEY_LENGTH, KECCAK_DIGEST_LENGTH from nucypher.crypto.constants import PUBLIC_KEY_LENGTH, KECCAK_DIGEST_LENGTH
from nucypher.crypto.signature import Signature from nucypher.crypto.signing import Signature
from bytestring_splitter import BytestringSplitter from bytestring_splitter import BytestringSplitter
from nucypher.network.node import NucypherNode from nucypher.network.node import NucypherNode
from nucypher.network.routing import NucypherRoutingTable from nucypher.network.routing import NucypherRoutingTable

View File

@ -12,7 +12,7 @@ from nucypher.characters import Bob, Ursula
from nucypher.crypto.api import keccak_digest from nucypher.crypto.api import keccak_digest
from nucypher.crypto.constants import KECCAK_DIGEST_LENGTH from nucypher.crypto.constants import KECCAK_DIGEST_LENGTH
from nucypher.crypto.powers import SigningPower, DelegatingPower from nucypher.crypto.powers import SigningPower, DelegatingPower
from nucypher.crypto.signature import Signature from nucypher.crypto.signing import Signature
from nucypher.crypto.splitters import key_splitter from nucypher.crypto.splitters import key_splitter
from bytestring_splitter import BytestringSplitter from bytestring_splitter import BytestringSplitter
from nucypher.blockchain.eth.policies import BlockchainArrangement from nucypher.blockchain.eth.policies import BlockchainArrangement

View File

@ -7,7 +7,6 @@ from nucypher.crypto.constants import CFRAG_LENGTH_WITHOUT_PROOF
def test_bob_cannot_follow_the_treasure_map_in_isolation(enacted_policy, bob): def test_bob_cannot_follow_the_treasure_map_in_isolation(enacted_policy, bob):
# Assume for the moment that Bob has already received a TreasureMap, perhaps via a side channel. # Assume for the moment that Bob has already received a TreasureMap, perhaps via a side channel.
hrac, treasure_map = enacted_policy.hrac(), enacted_policy.treasure_map hrac, treasure_map = enacted_policy.hrac(), enacted_policy.treasure_map
bob.treasure_maps[hrac] = treasure_map bob.treasure_maps[hrac] = treasure_map
@ -121,7 +120,7 @@ def test_bob_can_issue_a_work_order_to_a_specific_ursula(enacted_policy, bob,
# OK, so cool - Bob has his cFrag! Let's make sure everything went properly. First, we'll show that it is in fact # OK, so cool - Bob has his cFrag! Let's make sure everything went properly. First, we'll show that it is in fact
# the correct cFrag (ie, that Ursula performed reencryption properly). # the correct cFrag (ie, that Ursula performed reencryption properly).
for u in ursulas: # ...and to do that, we need to address the right ursula. for u in ursulas: # ...and to do that, we need to address the right ursula.
if u.rest_port == work_order.ursula.rest_port: if u.rest_port == work_order.ursula.rest_port:
ursula = u ursula = u
break break
@ -134,11 +133,13 @@ def test_bob_can_issue_a_work_order_to_a_specific_ursula(enacted_policy, bob,
the_correct_cfrag = pre.reencrypt(the_kfrag, capsule) the_correct_cfrag = pre.reencrypt(the_kfrag, capsule)
# The first CFRAG_LENGTH_WITHOUT_PROOF bytes (ie, the cfrag proper, not the proof material), are the same: # The first CFRAG_LENGTH_WITHOUT_PROOF bytes (ie, the cfrag proper, not the proof material), are the same:
assert bytes(the_cfrag)[:CapsuleFrag.get_size()] == bytes(the_correct_cfrag)[:CapsuleFrag.get_size()] # It's the correct cfrag! assert bytes(the_cfrag)[:CapsuleFrag.expected_bytes_length()] == bytes(the_correct_cfrag)[
:CapsuleFrag.expected_bytes_length()] # It's the correct cfrag!
assert the_correct_cfrag.verify_correctness(capsule, assert the_correct_cfrag.verify_correctness(capsule,
pubkey_a=enacted_policy.public_key, delegating_pubkey=enacted_policy.public_key,
pubkey_b=bob.public_key(EncryptingPower)) signing_pubkey=alice.stamp.as_umbral_pubkey(),
encrypting_pubkey=bob.public_key(EncryptingPower))
# Now we'll show that Ursula saved the correct WorkOrder. # Now we'll show that Ursula saved the correct WorkOrder.
work_orders_from_bob = ursula.work_orders(bob=bob) work_orders_from_bob = ursula.work_orders(bob=bob)
@ -187,7 +188,7 @@ def test_bob_remembers_that_he_has_cfrags_for_a_particular_capsule(enacted_polic
capsule_side_channel[0].capsule.attach_cfrag(new_cfrag) capsule_side_channel[0].capsule.attach_cfrag(new_cfrag)
def test_bob_gathers_and_combines(enacted_policy, bob, ursulas, capsule_side_channel): def test_bob_gathers_and_combines(enacted_policy, bob, alice, capsule_side_channel):
# The side channel is represented as a single MessageKit, which is all that Bob really needs. # The side channel is represented as a single MessageKit, which is all that Bob really needs.
the_message_kit, the_data_source = capsule_side_channel the_message_kit, the_data_source = capsule_side_channel
@ -199,7 +200,7 @@ def test_bob_gathers_and_combines(enacted_policy, bob, ursulas, capsule_side_cha
# Bob can't decrypt yet with just two CFrags. He needs to gather at least m. # Bob can't decrypt yet with just two CFrags. He needs to gather at least m.
with pytest.raises(pre.GenericUmbralError): with pytest.raises(pre.GenericUmbralError):
bob.decrypt(the_message_kit) bob.decrypt(the_message_kit, verifying_key=alice.stamp.as_umbral_pubkey())
number_left_to_collect = enacted_policy.treasure_map.m - len(bob._saved_work_orders) number_left_to_collect = enacted_policy.treasure_map.m - len(bob._saved_work_orders)
@ -213,6 +214,8 @@ def test_bob_gathers_and_combines(enacted_policy, bob, ursulas, capsule_side_cha
# Now. # Now.
# At long last. # At long last.
is_valid, cleartext = bob.verify_from(the_data_source, the_message_kit, decrypt=True) is_valid, cleartext = bob.verify_from(the_data_source, the_message_kit,
decrypt=True,
delegator_signing_key=alice.stamp.as_umbral_pubkey())
assert cleartext == b'Welcome to the flippering.' assert cleartext == b'Welcome to the flippering.'
assert is_valid assert is_valid

View File

@ -1,6 +1,6 @@
import pytest import pytest
from nucypher.crypto.api import secure_random from nucypher.crypto.api import secure_random
from nucypher.crypto.signature import Signature from nucypher.crypto.signing import Signature
from bytestring_splitter import BytestringSplitter from bytestring_splitter import BytestringSplitter

View File

@ -1,6 +1,6 @@
from nucypher.crypto.api import ecdsa_sign from nucypher.crypto.api import ecdsa_sign
from umbral.keys import UmbralPrivateKey from umbral.keys import UmbralPrivateKey
from nucypher.crypto.signature import Signature from nucypher.crypto.signing import Signature
def test_signature_can_verify(): def test_signature_can_verify():

View File

@ -11,7 +11,7 @@ from nucypher.characters import Alice, Bob
from nucypher.keystore import keystore from nucypher.keystore import keystore
from nucypher.keystore.db import Base from nucypher.keystore.db import Base
from nucypher.crypto.signature import SignatureStamp from nucypher.crypto.signing import SignatureStamp
from nucypher.data_sources import DataSource from nucypher.data_sources import DataSource
from nucypher.keystore import keystore from nucypher.keystore import keystore
from nucypher.keystore.db import Base from nucypher.keystore.db import Base
@ -97,7 +97,7 @@ def test_keystore():
def capsule_side_channel(enacted_policy): def capsule_side_channel(enacted_policy):
signing_keypair = SigningKeypair() signing_keypair = SigningKeypair()
data_source = DataSource(policy_pubkey_enc=enacted_policy.public_key, data_source = DataSource(policy_pubkey_enc=enacted_policy.public_key,
signer=SignatureStamp(signing_keypair)) signing_keypair=signing_keypair)
message_kit, _signature = data_source.encapsulate_single_message(b"Welcome to the flippering.") message_kit, _signature = data_source.encapsulate_single_message(b"Welcome to the flippering.")
return message_kit, data_source return message_kit, data_source