mirror of https://github.com/nucypher/nucypher.git
Merge pull request #133 from jMyles/apistar
REST relationship for Bob and Ursula, with complete PRE using all three Fragment classes.pull/145/head
commit
761553b26b
|
@ -10,3 +10,4 @@ __pycache__
|
||||||
/.idea
|
/.idea
|
||||||
.coverage
|
.coverage
|
||||||
_temp_test_datastore
|
_temp_test_datastore
|
||||||
|
.mypy_cache
|
|
@ -4,11 +4,12 @@ from binascii import hexlify
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
|
||||||
import msgpack
|
import msgpack
|
||||||
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
from apistar import http
|
from apistar import http
|
||||||
from apistar.core import Route
|
from apistar.core import Route
|
||||||
from apistar.frameworks.wsgi import WSGIApp as App
|
from apistar.frameworks.wsgi import WSGIApp as App
|
||||||
from sqlalchemy.exc import IntegrityError
|
from apistar.http import Response
|
||||||
|
|
||||||
from kademlia.network import Server
|
from kademlia.network import Server
|
||||||
from kademlia.utils import digest
|
from kademlia.utils import digest
|
||||||
from nkms.crypto import api as API
|
from nkms.crypto import api as API
|
||||||
|
@ -71,6 +72,12 @@ class Character(object):
|
||||||
else:
|
else:
|
||||||
self._seal = StrangerSeal(self)
|
self._seal = StrangerSeal(self)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return bytes(self.seal) == bytes(other.seal)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return int.from_bytes(self.seal, byteorder="big")
|
||||||
|
|
||||||
class NotFound(KeyError):
|
class NotFound(KeyError):
|
||||||
"""raised when we try to interact with an actor of whom we haven't learned yet."""
|
"""raised when we try to interact with an actor of whom we haven't learned yet."""
|
||||||
|
|
||||||
|
@ -224,11 +231,12 @@ class Bob(Character):
|
||||||
_server_class = NuCypherSeedOnlyDHTServer
|
_server_class = NuCypherSeedOnlyDHTServer
|
||||||
_default_crypto_powerups = [SigningPower, EncryptingPower]
|
_default_crypto_powerups = [SigningPower, EncryptingPower]
|
||||||
|
|
||||||
def __init__(self, alice=None):
|
def __init__(self, alice=None, *args, **kwargs):
|
||||||
super().__init__()
|
super().__init__(*args, **kwargs)
|
||||||
self._ursulas = {}
|
self._ursulas = {}
|
||||||
if alice:
|
if alice:
|
||||||
self.alice = alice
|
self.alice = alice
|
||||||
|
self._work_orders = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def alice(self):
|
def alice(self):
|
||||||
|
@ -265,7 +273,7 @@ class Bob(Character):
|
||||||
_signature_for_ursula, pubkey_sig_alice, hrac, encrypted_treasure_map = dht_value_splitter(
|
_signature_for_ursula, pubkey_sig_alice, hrac, encrypted_treasure_map = dht_value_splitter(
|
||||||
packed_encrypted_treasure_map[5::], msgpack_remainder=True)
|
packed_encrypted_treasure_map[5::], msgpack_remainder=True)
|
||||||
verified, cleartext = self.verify_from(self.alice, encrypted_treasure_map,
|
verified, cleartext = self.verify_from(self.alice, encrypted_treasure_map,
|
||||||
signature_is_on_cleartext=True, decrypt=True)
|
signature_is_on_cleartext=True, decrypt=True)
|
||||||
alices_signature, packed_node_list = BytestringSplitter(Signature)(cleartext, return_remainder=True)
|
alices_signature, packed_node_list = BytestringSplitter(Signature)(cleartext, return_remainder=True)
|
||||||
if not verified:
|
if not verified:
|
||||||
return NOT_FROM_ALICE
|
return NOT_FROM_ALICE
|
||||||
|
@ -273,6 +281,34 @@ class Bob(Character):
|
||||||
from nkms.policy.models import TreasureMap
|
from nkms.policy.models import TreasureMap
|
||||||
return TreasureMap(msgpack.loads(packed_node_list))
|
return TreasureMap(msgpack.loads(packed_node_list))
|
||||||
|
|
||||||
|
def generate_work_orders(self, policy_group, *pfrags, num_ursulas=None):
|
||||||
|
# TODO: Perhaps instead of taking a policy_group, it makes more sense for Bob to reconstruct one with the TreasureMap.
|
||||||
|
from nkms.policy.models import WorkOrder # Prevent circular import
|
||||||
|
|
||||||
|
# existing_work_orders = self._work_orders.get(pfrags, {}) # TODO: lookup whether we've done this reencryption before - see #137.
|
||||||
|
existing_work_orders = {}
|
||||||
|
generated_work_orders = {}
|
||||||
|
|
||||||
|
for ursula_dht_key, ursula in self._ursulas.items():
|
||||||
|
if ursula_dht_key in existing_work_orders:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
work_order = WorkOrder.constructed_by_bob(policy_group.hrac(), pfrags, ursula_dht_key, self)
|
||||||
|
existing_work_orders[ursula_dht_key] = generated_work_orders[ursula_dht_key] = work_order
|
||||||
|
|
||||||
|
if num_ursulas is not None:
|
||||||
|
if num_ursulas == len(generated_work_orders):
|
||||||
|
break
|
||||||
|
|
||||||
|
return generated_work_orders
|
||||||
|
|
||||||
|
def get_reencrypted_c_frag(self, networky_stuff, work_order):
|
||||||
|
cfrags = networky_stuff.reencrypt(work_order)
|
||||||
|
return cfrags
|
||||||
|
|
||||||
|
def get_ursula(self, ursula_id):
|
||||||
|
return self._ursulas[ursula_id]
|
||||||
|
|
||||||
|
|
||||||
class Ursula(Character):
|
class Ursula(Character):
|
||||||
_server_class = NuCypherDHTServer
|
_server_class = NuCypherDHTServer
|
||||||
|
@ -287,6 +323,7 @@ class Ursula(Character):
|
||||||
self.keystore = urulsas_keystore
|
self.keystore = urulsas_keystore
|
||||||
|
|
||||||
self._rest_app = None
|
self._rest_app = None
|
||||||
|
self._work_orders = []
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def rest_app(self):
|
def rest_app(self):
|
||||||
|
@ -307,12 +344,13 @@ class Ursula(Character):
|
||||||
*args, **kwargs):
|
*args, **kwargs):
|
||||||
|
|
||||||
if not id:
|
if not id:
|
||||||
id = digest(secure_random(32)) # TODO: Network-wide deterministic ID generation (ie, auction or whatever)
|
id = digest(secure_random(32)) # TODO: Network-wide deterministic ID generation (ie, auction or whatever) #136.
|
||||||
|
|
||||||
super().attach_server(ksize, alpha, id, storage)
|
super().attach_server(ksize, alpha, id, storage)
|
||||||
|
|
||||||
routes = [
|
routes = [
|
||||||
Route('/kFrag/{hrac_as_hex}', 'POST', self.set_policy),
|
Route('/kFrag/{hrac_as_hex}', 'POST', self.set_policy),
|
||||||
|
Route('/kFrag/{hrac_as_hex}/reencrypt', 'POST', self.reencrypt_via_rest),
|
||||||
]
|
]
|
||||||
|
|
||||||
self._rest_app = App(routes=routes)
|
self._rest_app = App(routes=routes)
|
||||||
|
@ -322,7 +360,7 @@ class Ursula(Character):
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
return self.server.listen(port, interface)
|
return self.server.listen(port, interface)
|
||||||
|
|
||||||
def interface_info(self):
|
def dht_interface_info(self):
|
||||||
return self.port, self.interface, self.interface_ttl
|
return self.port, self.interface, self.interface_ttl
|
||||||
|
|
||||||
def interface_dht_key(self):
|
def interface_dht_key(self):
|
||||||
|
@ -330,10 +368,10 @@ class Ursula(Character):
|
||||||
|
|
||||||
def interface_dht_value(self):
|
def interface_dht_value(self):
|
||||||
signature = self.seal(self.interface_hrac())
|
signature = self.seal(self.interface_hrac())
|
||||||
return b"uaddr" + signature + self.seal + self.interface_hrac() + msgpack.dumps(self.interface_info())
|
return b"uaddr" + signature + self.seal + self.interface_hrac() + msgpack.dumps(self.dht_interface_info())
|
||||||
|
|
||||||
def interface_hrac(self):
|
def interface_hrac(self):
|
||||||
return self.hash(msgpack.dumps(self.interface_info()))
|
return self.hash(msgpack.dumps(self.dht_interface_info()))
|
||||||
|
|
||||||
def publish_interface_information(self):
|
def publish_interface_information(self):
|
||||||
if not self.port and self.interface:
|
if not self.port and self.interface:
|
||||||
|
@ -362,7 +400,34 @@ class Ursula(Character):
|
||||||
raise
|
raise
|
||||||
# Do something appropriately RESTful (ie, 4xx).
|
# Do something appropriately RESTful (ie, 4xx).
|
||||||
|
|
||||||
return # A 200, which whatever policy metadata.
|
return # A 200, with whatever policy metadata.
|
||||||
|
|
||||||
|
def reencrypt_via_rest(self, hrac_as_hex, request: http.Request):
|
||||||
|
from nkms.policy.models import WorkOrder # Avoid circular import
|
||||||
|
hrac = binascii.unhexlify(hrac_as_hex)
|
||||||
|
work_order = WorkOrder.from_rest_payload(hrac, request.body)
|
||||||
|
kfrag = self.keystore.get_kfrag(hrac) # Careful! :-)
|
||||||
|
cfrag_byte_stream = b""
|
||||||
|
|
||||||
|
for pfrag in work_order.pfrags:
|
||||||
|
cfrag_byte_stream += API.ecies_reencrypt(kfrag, pfrag.encrypted_key)
|
||||||
|
|
||||||
|
self._work_orders.append(work_order) # TODO: Put this in Ursula's datastore
|
||||||
|
|
||||||
|
return Response(content=cfrag_byte_stream, content_type="application/octet-stream")
|
||||||
|
|
||||||
|
def work_orders(self, bob=None):
|
||||||
|
"""
|
||||||
|
TODO: This is better written as a model method for Ursula's datastore.
|
||||||
|
"""
|
||||||
|
if not bob:
|
||||||
|
return self._work_orders
|
||||||
|
else:
|
||||||
|
work_orders_from_bob = []
|
||||||
|
for work_order in self._work_orders:
|
||||||
|
if work_order.bob == bob:
|
||||||
|
work_orders_from_bob.append(work_order)
|
||||||
|
return work_orders_from_bob
|
||||||
|
|
||||||
|
|
||||||
class Seal(object):
|
class Seal(object):
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
from random import SystemRandom
|
|
||||||
from typing import Tuple, Union, List
|
from typing import Tuple, Union, List
|
||||||
|
|
||||||
import sha3
|
import sha3
|
||||||
from nacl.secret import SecretBox
|
from nacl.secret import SecretBox
|
||||||
from py_ecc.secp256k1 import N, privtopub, ecdsa_raw_recover, ecdsa_raw_sign
|
from py_ecc.secp256k1 import N, privtopub, ecdsa_raw_recover, ecdsa_raw_sign
|
||||||
|
from random import SystemRandom
|
||||||
|
|
||||||
from nkms.crypto import _internal
|
from nkms.crypto import _internal
|
||||||
|
from nkms.crypto.fragments import KFrag, PFrag, CFrag
|
||||||
from nkms.keystore.constants import SIG_KEYPAIR_BYTE, PUB_KEY_BYTE
|
from nkms.keystore.constants import SIG_KEYPAIR_BYTE, PUB_KEY_BYTE
|
||||||
from npre import elliptic_curve
|
from npre import elliptic_curve
|
||||||
from npre import umbral
|
from npre import umbral
|
||||||
|
@ -378,8 +379,9 @@ def ecies_split_rekey(
|
||||||
privkey_a = priv_bytes2ec(privkey_a)
|
privkey_a = priv_bytes2ec(privkey_a)
|
||||||
if type(privkey_b) == bytes:
|
if type(privkey_b) == bytes:
|
||||||
privkey_b = priv_bytes2ec(privkey_b)
|
privkey_b = priv_bytes2ec(privkey_b)
|
||||||
return PRE.split_rekey(privkey_a, privkey_b,
|
umbral_rekeys = PRE.split_rekey(privkey_a, privkey_b,
|
||||||
min_shares, total_shares)
|
min_shares, total_shares)
|
||||||
|
return [KFrag(umbral_kfrag=u) for u in umbral_rekeys]
|
||||||
|
|
||||||
|
|
||||||
def ecies_ephemeral_split_rekey(
|
def ecies_ephemeral_split_rekey(
|
||||||
|
@ -402,14 +404,15 @@ def ecies_ephemeral_split_rekey(
|
||||||
:return: A tuple containing a list of rekey frags, and a tuple of the
|
:return: A tuple containing a list of rekey frags, and a tuple of the
|
||||||
encrypted ephemeral key data (enc_symm_key, enc_eph_privkey)
|
encrypted ephemeral key data (enc_symm_key, enc_eph_privkey)
|
||||||
"""
|
"""
|
||||||
eph_privkey, enc_eph_data = _internal._ecies_gen_ephemeral_key(pubkey_b)
|
eph_privkey, (encrypted_key, encrypted_message) = _internal._ecies_gen_ephemeral_key(pubkey_b)
|
||||||
frags = ecies_split_rekey(privkey_a, eph_privkey, min_shares, total_shares)
|
kfrags = ecies_split_rekey(privkey_a, eph_privkey, min_shares, total_shares)
|
||||||
|
pfrag = PFrag(ephemeral_data_as_bytes=None, encrypted_key=encrypted_key, encrypted_message=encrypted_message)
|
||||||
|
|
||||||
return (frags, enc_eph_data)
|
return (kfrags, pfrag)
|
||||||
|
|
||||||
|
|
||||||
def ecies_combine(
|
def ecies_combine(
|
||||||
encrypted_keys: List[umbral.EncryptedKey]
|
cfrags: List[CFrag]
|
||||||
) -> umbral.EncryptedKey:
|
) -> umbral.EncryptedKey:
|
||||||
"""
|
"""
|
||||||
Combines the encrypted keys together to form a rekey from split_rekey.
|
Combines the encrypted keys together to form a rekey from split_rekey.
|
||||||
|
@ -418,7 +421,7 @@ def ecies_combine(
|
||||||
|
|
||||||
:return: The combined EncryptedKey of the rekey
|
:return: The combined EncryptedKey of the rekey
|
||||||
"""
|
"""
|
||||||
return PRE.combine(encrypted_keys)
|
return PRE.combine([cfrag.encrypted_key for cfrag in cfrags])
|
||||||
|
|
||||||
|
|
||||||
def ecies_reencrypt(
|
def ecies_reencrypt(
|
||||||
|
@ -437,4 +440,5 @@ def ecies_reencrypt(
|
||||||
rekey = umbral.RekeyFrag(None, priv_bytes2ec(rekey))
|
rekey = umbral.RekeyFrag(None, priv_bytes2ec(rekey))
|
||||||
if type(enc_key) == bytes:
|
if type(enc_key) == bytes:
|
||||||
enc_key = umbral.EncryptedKey(priv_bytes2ec(enc_key), None)
|
enc_key = umbral.EncryptedKey(priv_bytes2ec(enc_key), None)
|
||||||
return PRE.reencrypt(rekey, enc_key)
|
reencrypted_data = PRE.reencrypt(rekey, enc_key)
|
||||||
|
return CFrag(reencrypted_data=reencrypted_data)
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
from nkms.crypto.utils import BytestringSplitter
|
||||||
|
from npre.constants import UNKNOWN_KFRAG
|
||||||
|
from npre.umbral import RekeyFrag, EncryptedKey
|
||||||
|
|
||||||
|
|
||||||
|
class PFrag(object):
|
||||||
|
_key_length = 34
|
||||||
|
_message_length = 72
|
||||||
|
_EXPECTED_LENGTH = _key_length + _message_length
|
||||||
|
|
||||||
|
splitter = BytestringSplitter((bytes, _key_length), (bytes, _message_length))
|
||||||
|
|
||||||
|
def __init__(self, ephemeral_data_as_bytes=None, encrypted_key=None, encrypted_message=None):
|
||||||
|
from nkms.crypto.api import PRE # Avoid circular import
|
||||||
|
if ephemeral_data_as_bytes and encrypted_key:
|
||||||
|
raise ValueError("Pass either the ephemeral data as bytes or the encrypted key and message. Not both.")
|
||||||
|
elif ephemeral_data_as_bytes:
|
||||||
|
encrypted_key, self.encrypted_message = self.splitter(ephemeral_data_as_bytes)
|
||||||
|
self.encrypted_key = EncryptedKey(ekey=PRE.load_key(encrypted_key), re_id=None)
|
||||||
|
elif encrypted_key and encrypted_message:
|
||||||
|
self.encrypted_key = encrypted_key
|
||||||
|
self.encrypted_message = encrypted_message
|
||||||
|
else:
|
||||||
|
assert False # What do we do if all the values were None? Perhaps have an "UNKNOWN_PFRAG" concept?
|
||||||
|
|
||||||
|
def __bytes__(self):
|
||||||
|
from nkms.crypto.api import PRE # Avoid circular import
|
||||||
|
encrypted_key_bytes = PRE.save_key(self.encrypted_key.ekey)
|
||||||
|
return encrypted_key_bytes + self.encrypted_message
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(bytes(self))
|
||||||
|
|
||||||
|
def deserialized(self):
|
||||||
|
return self.encrypted_key, self.encrypted_message
|
||||||
|
|
||||||
|
|
||||||
|
class KFrag(object):
|
||||||
|
|
||||||
|
_EXPECTED_LENGTH = 66
|
||||||
|
_is_unknown_kfrag = False
|
||||||
|
|
||||||
|
def __init__(self, id_plus_key_as_bytes=None, umbral_kfrag=None):
|
||||||
|
if all((id_plus_key_as_bytes, umbral_kfrag)):
|
||||||
|
raise ValueError("Pass either the id/key or an umbral_kfrag (or neither for UNKNOWN_KFRAG). Not both.")
|
||||||
|
elif id_plus_key_as_bytes:
|
||||||
|
self._umbral_kfrag = RekeyFrag.from_bytes(id_plus_key_as_bytes)
|
||||||
|
elif umbral_kfrag:
|
||||||
|
self._umbral_kfrag = umbral_kfrag
|
||||||
|
else:
|
||||||
|
self._is_unknown_kfrag = True
|
||||||
|
|
||||||
|
def __bytes__(self):
|
||||||
|
return bytes(self._umbral_kfrag)
|
||||||
|
|
||||||
|
def __eq__(self, other_kfrag):
|
||||||
|
if other_kfrag is UNKNOWN_KFRAG:
|
||||||
|
return bool(self._is_unknown_kfrag)
|
||||||
|
else:
|
||||||
|
return bytes(self) == bytes(other_kfrag)
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
return bytes(self) + other
|
||||||
|
|
||||||
|
def __radd__(self, other):
|
||||||
|
return other + bytes(self)
|
||||||
|
|
||||||
|
def __getitem__(self, slice):
|
||||||
|
return bytes(self)[slice]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def key(self):
|
||||||
|
return self._umbral_kfrag.key
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
return self._umbral_kfrag.id
|
||||||
|
|
||||||
|
|
||||||
|
class CFrag(object):
|
||||||
|
_EXPECTED_LENGTH = 67
|
||||||
|
_key_element_length = 34
|
||||||
|
_re_id_length = 33
|
||||||
|
|
||||||
|
def __init__(self, encrypted_key_as_bytes=None, reencrypted_data=None):
|
||||||
|
from nkms.crypto.api import PRE # Avoid circular import
|
||||||
|
if encrypted_key_as_bytes and reencrypted_data:
|
||||||
|
raise ValueError("Pass the bytes or the EncryptedKey, not both.")
|
||||||
|
elif encrypted_key_as_bytes:
|
||||||
|
if not len(encrypted_key_as_bytes) == self._EXPECTED_LENGTH:
|
||||||
|
raise ValueError("Got {} bytes; need {} for a proper cFrag.".format(len(encrypted_key_as_bytes)),
|
||||||
|
self._EXPECTED_LENGTH)
|
||||||
|
key_element = PRE.load_key(encrypted_key_as_bytes[:self._key_element_length])
|
||||||
|
re_id = PRE.load_key(encrypted_key_as_bytes[self._key_element_length:])
|
||||||
|
self.encrypted_key = EncryptedKey(ekey=key_element, re_id=re_id)
|
||||||
|
elif reencrypted_data:
|
||||||
|
self.encrypted_key = reencrypted_data
|
||||||
|
else:
|
||||||
|
assert False # Again, do we want a concept of an "empty" CFrag?
|
||||||
|
|
||||||
|
def __bytes__(self):
|
||||||
|
from nkms.crypto.api import PRE # Avoid circular import
|
||||||
|
as_bytes = PRE.save_key(self.encrypted_key.ekey) + PRE.save_key(self.encrypted_key.re_id)
|
||||||
|
if len(as_bytes) != self._EXPECTED_LENGTH:
|
||||||
|
raise TypeError("Something went crazy wrong here. This CFrag serialized to {} bytes.".format(len(as_bytes)))
|
||||||
|
else:
|
||||||
|
return as_bytes
|
||||||
|
|
||||||
|
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_cfrag):
|
||||||
|
return bytes(self) == bytes(other_cfrag)
|
|
@ -45,3 +45,15 @@ class BytestringSplitter(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_message_meta(message_type):
|
def get_message_meta(message_type):
|
||||||
return message_type if isinstance(message_type, tuple) else (message_type, message_type._EXPECTED_LENGTH)
|
return message_type if isinstance(message_type, tuple) else (message_type, message_type._EXPECTED_LENGTH)
|
||||||
|
|
||||||
|
|
||||||
|
class RepeatingBytestringSplitter(BytestringSplitter):
|
||||||
|
|
||||||
|
def __call__(self, splittable):
|
||||||
|
remainder = True
|
||||||
|
messages = []
|
||||||
|
while remainder:
|
||||||
|
message, remainder = super().__call__(splittable, return_remainder=True)
|
||||||
|
messages.append(message)
|
||||||
|
splittable = remainder
|
||||||
|
return messages
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import sha3
|
import sha3
|
||||||
|
|
||||||
|
from nkms.crypto.fragments import KFrag
|
||||||
from nkms.keystore import keypairs, constants
|
from nkms.keystore import keypairs, constants
|
||||||
from nkms.keystore.db.models import Key, KeyFrag
|
from nkms.keystore.db.models import Key, KeyFrag
|
||||||
from nkms.crypto.utils import BytestringSplitter
|
from nkms.crypto.utils import BytestringSplitter
|
||||||
|
@ -20,11 +22,7 @@ class KeyStore(object):
|
||||||
A storage class of cryptographic keys.
|
A storage class of cryptographic keys.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
kFrag_splitter = BytestringSplitter(
|
kfrag_splitter = BytestringSplitter(Signature, KFrag)
|
||||||
Signature,
|
|
||||||
(bytes, constants.REKEY_FRAG_ID_LEN),
|
|
||||||
(bytes, constants.REKEY_FRAG_KEY_LEN)
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, sqlalchemy_engine=None):
|
def __init__(self, sqlalchemy_engine=None):
|
||||||
"""
|
"""
|
||||||
|
@ -105,12 +103,11 @@ class KeyStore(object):
|
||||||
.format(hrac)
|
.format(hrac)
|
||||||
)
|
)
|
||||||
# TODO: Make this use a class
|
# TODO: Make this use a class
|
||||||
sig, id, key = self.kFrag_splitter(kfrag.key_frag)
|
sig, kfrag = self.kfrag_splitter(kfrag.key_frag)
|
||||||
|
|
||||||
kFrag = RekeyFrag(id=id, key=key)
|
|
||||||
if get_sig:
|
if get_sig:
|
||||||
return (kFrag, sig)
|
return (kfrag, sig)
|
||||||
return kFrag
|
return kfrag
|
||||||
|
|
||||||
def add_key(self,
|
def add_key(self,
|
||||||
keypair: Union[keypairs.EncryptingKeypair,
|
keypair: Union[keypairs.EncryptingKeypair,
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
|
import binascii
|
||||||
|
|
||||||
import msgpack
|
import msgpack
|
||||||
|
|
||||||
from nkms.characters import Alice, Bob, Ursula
|
from nkms.characters import Alice, Bob, Ursula
|
||||||
from nkms.crypto import api
|
from nkms.crypto import api
|
||||||
from nkms.crypto.api import keccak_digest
|
from nkms.crypto.api import keccak_digest
|
||||||
from nkms.crypto.constants import HASH_DIGEST_LENGTH, NOT_SIGNED
|
from nkms.crypto.constants import NOT_SIGNED
|
||||||
|
from nkms.crypto.fragments import KFrag, PFrag
|
||||||
from nkms.crypto.powers import EncryptingPower
|
from nkms.crypto.powers import EncryptingPower
|
||||||
from nkms.crypto.signature import Signature
|
from nkms.crypto.signature import Signature
|
||||||
from nkms.crypto.utils import BytestringSplitter
|
from nkms.crypto.utils import BytestringSplitter
|
||||||
from nkms.keystore.keypairs import PublicKey
|
from nkms.keystore.keypairs import PublicKey
|
||||||
from npre.constants import UNKNOWN_KFRAG
|
from npre.constants import UNKNOWN_KFRAG
|
||||||
from npre.umbral import RekeyFrag
|
|
||||||
|
|
||||||
group_payload_splitter = BytestringSplitter(PublicKey)
|
group_payload_splitter = BytestringSplitter(PublicKey)
|
||||||
policy_payload_splitter = BytestringSplitter((bytes, 66)) # TODO: I wish ReKeyFrag worked with this interface.
|
policy_payload_splitter = BytestringSplitter(KFrag)
|
||||||
|
|
||||||
|
|
||||||
class PolicyOffer(object):
|
class PolicyOffer(object):
|
||||||
|
@ -59,15 +61,15 @@ class PolicyManagerForAlice(PolicyManager):
|
||||||
re_enc_keys, encrypted_key = self.owner.generate_rekey_frags(alice_priv_enc, bob, m,
|
re_enc_keys, encrypted_key = self.owner.generate_rekey_frags(alice_priv_enc, bob, m,
|
||||||
n) # TODO: Access Alice's private key inside this method.
|
n) # TODO: Access Alice's private key inside this method.
|
||||||
policies = []
|
policies = []
|
||||||
for kfrag_id, rekey in enumerate(re_enc_keys):
|
for kfrag_id, kfrag in enumerate(re_enc_keys):
|
||||||
policy = Policy.from_alice(
|
policy = Policy.from_alice(
|
||||||
alice=self.owner,
|
alice=self.owner,
|
||||||
bob=bob,
|
bob=bob,
|
||||||
kfrag=rekey,
|
kfrag=kfrag,
|
||||||
)
|
)
|
||||||
policies.append(policy)
|
policies.append(policy)
|
||||||
|
|
||||||
return PolicyGroup(uri, self.owner, bob, policies)
|
return PolicyGroup(uri, self.owner, bob, encrypted_key, policies)
|
||||||
|
|
||||||
|
|
||||||
class PolicyGroup(object):
|
class PolicyGroup(object):
|
||||||
|
@ -77,10 +79,11 @@ class PolicyGroup(object):
|
||||||
|
|
||||||
_id = None
|
_id = None
|
||||||
|
|
||||||
def __init__(self, uri: bytes, alice: Alice, bob: Bob, policies=None) -> None:
|
def __init__(self, uri: bytes, alice: Alice, bob: Bob, pfrag, policies=None) -> None:
|
||||||
self.policies = policies or []
|
self.policies = policies or []
|
||||||
self.alice = alice
|
self.alice = alice
|
||||||
self.bob = bob
|
self.bob = bob
|
||||||
|
self.pfrag = pfrag
|
||||||
self.uri = uri
|
self.uri = uri
|
||||||
self.treasure_map = TreasureMap()
|
self.treasure_map = TreasureMap()
|
||||||
|
|
||||||
|
@ -137,7 +140,6 @@ class PolicyGroup(object):
|
||||||
self.hrac(),
|
self.hrac(),
|
||||||
full_payload) # TODO: Parse response for confirmation.
|
full_payload) # TODO: Parse response for confirmation.
|
||||||
|
|
||||||
|
|
||||||
# Assuming response is what we hope for
|
# Assuming response is what we hope for
|
||||||
self.treasure_map.add_ursula(policy.ursula)
|
self.treasure_map.add_ursula(policy.ursula)
|
||||||
|
|
||||||
|
@ -219,7 +221,7 @@ class Policy(object):
|
||||||
alice = Alice.from_pubkey_sig_bytes(alice_pubkey_sig)
|
alice = Alice.from_pubkey_sig_bytes(alice_pubkey_sig)
|
||||||
ursula.learn_about_actor(alice)
|
ursula.learn_about_actor(alice)
|
||||||
verified, cleartext = ursula.verify_from(alice, payload_encrypted_for_ursula,
|
verified, cleartext = ursula.verify_from(alice, payload_encrypted_for_ursula,
|
||||||
decrypt=True, signature_is_on_cleartext=True)
|
decrypt=True, signature_is_on_cleartext=True)
|
||||||
|
|
||||||
if not verified:
|
if not verified:
|
||||||
# TODO: What do we do if it's not signed properly?
|
# TODO: What do we do if it's not signed properly?
|
||||||
|
@ -227,8 +229,7 @@ class Policy(object):
|
||||||
|
|
||||||
alices_signature, policy_payload = BytestringSplitter(Signature)(cleartext, return_remainder=True)
|
alices_signature, policy_payload = BytestringSplitter(Signature)(cleartext, return_remainder=True)
|
||||||
|
|
||||||
kfrag_bytes, encrypted_challenge_pack = policy_payload_splitter(policy_payload, return_remainder=True)
|
kfrag, encrypted_challenge_pack = policy_payload_splitter(policy_payload, return_remainder=True)
|
||||||
kfrag = RekeyFrag.from_bytes(kfrag_bytes)
|
|
||||||
policy = Policy(alice=alice, alices_signature=alices_signature, kfrag=kfrag,
|
policy = Policy(alice=alice, alices_signature=alices_signature, kfrag=kfrag,
|
||||||
encrypted_challenge_pack=encrypted_challenge_pack)
|
encrypted_challenge_pack=encrypted_challenge_pack)
|
||||||
|
|
||||||
|
@ -288,3 +289,44 @@ class TreasureMap(object):
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self.ids)
|
return iter(self.ids)
|
||||||
|
|
||||||
|
|
||||||
|
class WorkOrder(object):
|
||||||
|
def __init__(self, bob, kfrag_hrac, pfrags, receipt_bytes, receipt_signature, ursula_id=None):
|
||||||
|
self.bob = bob
|
||||||
|
self.kfrag_hrac = kfrag_hrac
|
||||||
|
self.pfrags = pfrags
|
||||||
|
self.receipt_bytes = receipt_bytes
|
||||||
|
self.receipt_signature = receipt_signature
|
||||||
|
self.ursula_id = ursula_id # TODO: We may still need a more elegant system for ID'ing Ursula. See #136.
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "WorkOrder (pfrags: {}) {} for {}".format([binascii.hexlify(bytes(p))[:6] for p in self.pfrags],
|
||||||
|
binascii.hexlify(self.receipt_bytes)[:6],
|
||||||
|
binascii.hexlify(self.ursula_id)[:6])
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return (self.receipt_bytes, self.receipt_signature) == (other.receipt_bytes, other.receipt_signature)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def constructed_by_bob(cls, kfrag_hrac, pfrags, ursula_dht_key, bob):
|
||||||
|
receipt_bytes = b"wo:" + ursula_dht_key # TODO: represent the pfrags as bytes and hash them as part of the receipt, ie + keccak_digest(b"".join(pfrags)) - See #137
|
||||||
|
receipt_signature = bob.seal(receipt_bytes)
|
||||||
|
return cls(bob, kfrag_hrac, pfrags, receipt_bytes, receipt_signature, ursula_dht_key)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_rest_payload(cls, kfrag_hrac, rest_payload):
|
||||||
|
payload_splitter = BytestringSplitter(Signature, PublicKey)
|
||||||
|
signature, bob_pubkey_sig, (receipt_bytes, packed_pfrags) = payload_splitter(rest_payload,
|
||||||
|
msgpack_remainder=True)
|
||||||
|
pfrags = [PFrag(p) for p in msgpack.loads(packed_pfrags)]
|
||||||
|
verified = signature.verify(receipt_bytes, bob_pubkey_sig)
|
||||||
|
if not verified:
|
||||||
|
raise ValueError("This doesn't appear to be from Bob.")
|
||||||
|
bob = Bob.from_pubkey_sig_bytes(bob_pubkey_sig)
|
||||||
|
return cls(bob, kfrag_hrac, pfrags, receipt_bytes, signature)
|
||||||
|
|
||||||
|
def payload(self):
|
||||||
|
pfrags_as_bytes = [bytes(p) for p in self.pfrags]
|
||||||
|
packed_receipt_and_pfrags = msgpack.dumps((self.receipt_bytes, msgpack.dumps(pfrags_as_bytes)))
|
||||||
|
return bytes(self.receipt_signature) + self.bob.seal + packed_receipt_and_pfrags
|
||||||
|
|
|
@ -5,6 +5,7 @@ import sha3
|
||||||
from nacl.utils import EncryptedMessage
|
from nacl.utils import EncryptedMessage
|
||||||
|
|
||||||
from nkms.crypto import api
|
from nkms.crypto import api
|
||||||
|
from nkms.crypto.fragments import PFrag
|
||||||
from nkms.keystore.keypairs import PublicKey
|
from nkms.keystore.keypairs import PublicKey
|
||||||
from npre import elliptic_curve as ec
|
from npre import elliptic_curve as ec
|
||||||
from npre import umbral
|
from npre import umbral
|
||||||
|
@ -297,10 +298,9 @@ class TestCrypto(unittest.TestCase):
|
||||||
self.assertEqual(list, type(frags))
|
self.assertEqual(list, type(frags))
|
||||||
self.assertEqual(4, len(frags))
|
self.assertEqual(4, len(frags))
|
||||||
|
|
||||||
self.assertEqual(tuple, type(enc_eph_data))
|
self.assertEqual(PFrag._EXPECTED_LENGTH, len(enc_eph_data))
|
||||||
self.assertEqual(2, len(enc_eph_data))
|
self.assertEqual(umbral.EncryptedKey, type(enc_eph_data.deserialized()[0]))
|
||||||
self.assertEqual(umbral.EncryptedKey, type(enc_eph_data[0]))
|
self.assertEqual(EncryptedMessage, type(enc_eph_data.deserialized()[1]))
|
||||||
self.assertEqual(EncryptedMessage, type(enc_eph_data[1]))
|
|
||||||
|
|
||||||
def test_ecies_combine(self):
|
def test_ecies_combine(self):
|
||||||
eph_priv = self.pre.gen_priv()
|
eph_priv = self.pre.gen_priv()
|
||||||
|
@ -320,7 +320,6 @@ class TestCrypto(unittest.TestCase):
|
||||||
shares = [api.ecies_reencrypt(rk_frag, enc_key) for rk_frag in rk_selected]
|
shares = [api.ecies_reencrypt(rk_frag, enc_key) for rk_frag in rk_selected]
|
||||||
self.assertEqual(list, type(shares))
|
self.assertEqual(list, type(shares))
|
||||||
self.assertEqual(6, len(shares))
|
self.assertEqual(6, len(shares))
|
||||||
[self.assertEqual(umbral.EncryptedKey, type(share)) for share in shares]
|
|
||||||
|
|
||||||
e_b = api.ecies_combine(shares)
|
e_b = api.ecies_combine(shares)
|
||||||
self.assertEqual(umbral.EncryptedKey, type(e_b))
|
self.assertEqual(umbral.EncryptedKey, type(e_b))
|
||||||
|
@ -345,6 +344,6 @@ class TestCrypto(unittest.TestCase):
|
||||||
self.assertEqual(umbral.RekeyFrag, type(rk_eb))
|
self.assertEqual(umbral.RekeyFrag, type(rk_eb))
|
||||||
self.assertEqual(ec.ec_element, type(rk_eb.key))
|
self.assertEqual(ec.ec_element, type(rk_eb.key))
|
||||||
|
|
||||||
reenc_key = api.ecies_reencrypt(rk_eb, enc_key)
|
cfrag = api.ecies_reencrypt(rk_eb, enc_key)
|
||||||
dec_key = api.ecies_decapsulate(self.privkey_b, reenc_key)
|
dec_key = api.ecies_decapsulate(self.privkey_b, cfrag.encrypted_key)
|
||||||
self.assertEqual(plain_key, dec_key)
|
self.assertEqual(plain_key, dec_key)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from nkms.crypto import api
|
||||||
from nkms.crypto.api import secure_random
|
from nkms.crypto.api import secure_random
|
||||||
|
from nkms.crypto.fragments import KFrag
|
||||||
from nkms.crypto.signature import Signature
|
from nkms.crypto.signature import Signature
|
||||||
from nkms.crypto.utils import BytestringSplitter
|
from nkms.crypto.utils import BytestringSplitter
|
||||||
|
|
||||||
|
@ -22,10 +24,22 @@ def test_split_signature_from_arbitrary_bytes():
|
||||||
some_bytes = secure_random(how_many_bytes)
|
some_bytes = secure_random(how_many_bytes)
|
||||||
splitter = BytestringSplitter(Signature, (bytes, how_many_bytes))
|
splitter = BytestringSplitter(Signature, (bytes, how_many_bytes))
|
||||||
|
|
||||||
|
|
||||||
rebuilt_signature, rebuilt_bytes = splitter(signature + some_bytes)
|
rebuilt_signature, rebuilt_bytes = splitter(signature + some_bytes)
|
||||||
|
|
||||||
|
|
||||||
|
def test_split_kfrag_from_arbitrary_bytes():
|
||||||
|
rand_id = b'\x00' + api.secure_random(32)
|
||||||
|
rand_key = b'\x00' + api.secure_random(32)
|
||||||
|
kfrag = KFrag(rand_id + rand_key)
|
||||||
|
|
||||||
|
how_many_bytes = 10
|
||||||
|
some_bytes = secure_random(how_many_bytes)
|
||||||
|
|
||||||
|
splitter = BytestringSplitter(KFrag, (bytes, how_many_bytes))
|
||||||
|
rebuilt_kfrag, rebuilt_bytes = splitter(kfrag + some_bytes)
|
||||||
|
assert kfrag == rebuilt_kfrag
|
||||||
|
|
||||||
|
|
||||||
def test_trying_to_extract_too_many_bytes_raises_typeerror():
|
def test_trying_to_extract_too_many_bytes_raises_typeerror():
|
||||||
how_many_bytes = 10
|
how_many_bytes = 10
|
||||||
too_many_bytes = 11
|
too_many_bytes = 11
|
||||||
|
|
|
@ -36,7 +36,7 @@ def enacted_policy_group(alices_policy_group, ursulas):
|
||||||
|
|
||||||
networky_stuff = MockNetworkyStuff(ursulas)
|
networky_stuff = MockNetworkyStuff(ursulas)
|
||||||
alices_policy_group.find_n_ursulas(networky_stuff, offer)
|
alices_policy_group.find_n_ursulas(networky_stuff, offer)
|
||||||
alices_policy_group.enact_policies(networky_stuff)
|
alices_policy_group.enact_policies(networky_stuff) # REST call happens here.
|
||||||
|
|
||||||
return alices_policy_group
|
return alices_policy_group
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import unittest
|
import unittest
|
||||||
import sha3
|
import sha3
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
|
|
||||||
|
from nkms.crypto.fragments import KFrag
|
||||||
from nkms.keystore.db import Base
|
from nkms.keystore.db import Base
|
||||||
from nkms.keystore import keystore, keypairs
|
from nkms.keystore import keystore, keypairs
|
||||||
from npre.umbral import RekeyFrag
|
from npre.umbral import RekeyFrag
|
||||||
|
@ -72,19 +74,24 @@ class TestKeyStore(unittest.TestCase):
|
||||||
key = self.ks.get_key(fingerprint_priv)
|
key = self.ks.get_key(fingerprint_priv)
|
||||||
|
|
||||||
def test_keyfrag_sqlite(self):
|
def test_keyfrag_sqlite(self):
|
||||||
|
kfrag_component_length = 32
|
||||||
rand_sig = API.secure_random(65)
|
rand_sig = API.secure_random(65)
|
||||||
rand_id = b'\x00' + API.secure_random(32)
|
rand_id = b'\x00' + API.secure_random(kfrag_component_length)
|
||||||
rand_key = b'\x00' + API.secure_random(32)
|
rand_key = b'\x00' + API.secure_random(kfrag_component_length)
|
||||||
rand_hrac = API.secure_random(32)
|
rand_hrac = API.secure_random(32)
|
||||||
|
|
||||||
kfrag = RekeyFrag.from_bytes(rand_id+rand_key)
|
kfrag = KFrag(rand_id+rand_key)
|
||||||
self.ks.add_kfrag(rand_hrac, kfrag, sig=rand_sig)
|
self.ks.add_kfrag(rand_hrac, kfrag, sig=rand_sig)
|
||||||
|
|
||||||
# Check that kfrag was added
|
# Check that kfrag was added
|
||||||
kfrag, signature = self.ks.get_kfrag(rand_hrac, get_sig=True)
|
kfrag_from_datastore, signature = self.ks.get_kfrag(rand_hrac, get_sig=True)
|
||||||
self.assertEqual(rand_sig, signature)
|
self.assertEqual(rand_sig, signature)
|
||||||
self.assertEqual(kfrag.id, rand_id)
|
|
||||||
self.assertEqual(kfrag.key, rand_key)
|
# De/serialization happens here, by dint of the slicing interface, which casts the kfrag to bytes.
|
||||||
|
# The +1 is to account for the metabyte.
|
||||||
|
self.assertEqual(kfrag_from_datastore[:kfrag_component_length + 1], rand_id)
|
||||||
|
self.assertEqual(kfrag_from_datastore[kfrag_component_length + 1:], rand_key)
|
||||||
|
self.assertEqual(kfrag_from_datastore, kfrag)
|
||||||
|
|
||||||
# Check that kfrag gets deleted
|
# Check that kfrag gets deleted
|
||||||
self.ks.del_kfrag(rand_hrac)
|
self.ks.del_kfrag(rand_hrac)
|
||||||
|
|
|
@ -107,16 +107,6 @@ def test_alice_creates_policy_group_with_correct_hrac(alices_policy_group):
|
||||||
bytes(alice.seal) + bytes(bob.seal) + alice.__resource_id)
|
bytes(alice.seal) + bytes(bob.seal) + alice.__resource_id)
|
||||||
|
|
||||||
|
|
||||||
def test_alice_enacts_policies_in_policy_group_via_rest(enacted_policy_group):
|
|
||||||
"""
|
|
||||||
Now that Alice has made a PolicyGroup, she can enact its policies, using Ursula's Public Key to encrypt each offer
|
|
||||||
and transmitting them via REST.
|
|
||||||
"""
|
|
||||||
ursula = enacted_policy_group.policies[0].ursula
|
|
||||||
kfrag_that_was_set = ursula.keystore.get_kfrag(enacted_policy_group.hrac())
|
|
||||||
assert bool(kfrag_that_was_set) # TODO: This can be a more poignant assertion.
|
|
||||||
|
|
||||||
|
|
||||||
def test_alice_sets_treasure_map_on_network(enacted_policy_group, ursulas):
|
def test_alice_sets_treasure_map_on_network(enacted_policy_group, ursulas):
|
||||||
"""
|
"""
|
||||||
Having enacted all the policies of a PolicyGroup, Alice creates a TreasureMap and sends it to Ursula via the DHT.
|
Having enacted all the policies of a PolicyGroup, Alice creates a TreasureMap and sends it to Ursula via the DHT.
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
from tests.utilities import EVENT_LOOP
|
from nkms.crypto import api
|
||||||
|
from tests.utilities import EVENT_LOOP, MockNetworkyStuff
|
||||||
|
|
||||||
|
|
||||||
def test_bob_can_follow_treasure_map(enacted_policy_group, ursulas):
|
def test_alice_enacts_policies_in_policy_group_via_rest(enacted_policy_group):
|
||||||
|
"""
|
||||||
|
Now that Alice has made a PolicyGroup, she can enact its policies, using Ursula's Public Key to encrypt each offer
|
||||||
|
and transmitting them via REST.
|
||||||
|
"""
|
||||||
|
ursula = enacted_policy_group.policies[0].ursula
|
||||||
|
kfrag_that_was_set = ursula.keystore.get_kfrag(enacted_policy_group.hrac())
|
||||||
|
assert bool(kfrag_that_was_set) # TODO: This can be a more poignant assertion.
|
||||||
|
|
||||||
|
|
||||||
|
def test_bob_can_follow_treasure_map(enacted_policy_group, ursulas, alice, bob):
|
||||||
"""
|
"""
|
||||||
Upon receiving a TreasureMap, Bob populates his list of Ursulas with the correct number.
|
Upon receiving a TreasureMap, Bob populates his list of Ursulas with the correct number.
|
||||||
"""
|
"""
|
||||||
alice = enacted_policy_group.alice
|
|
||||||
bob = enacted_policy_group.bob
|
|
||||||
assert len(bob._ursulas) == 0
|
assert len(bob._ursulas) == 0
|
||||||
|
|
||||||
setter, encrypted_treasure_map, packed_encrypted_treasure_map, signature_for_bob, signature_for_ursula = alice.publish_treasure_map(
|
setter, encrypted_treasure_map, packed_encrypted_treasure_map, signature_for_bob, signature_for_ursula = alice.publish_treasure_map(
|
||||||
|
@ -15,3 +24,41 @@ def test_bob_can_follow_treasure_map(enacted_policy_group, ursulas):
|
||||||
|
|
||||||
bob.follow_treasure_map(enacted_policy_group.treasure_map)
|
bob.follow_treasure_map(enacted_policy_group.treasure_map)
|
||||||
assert len(bob._ursulas) == len(ursulas)
|
assert len(bob._ursulas) == len(ursulas)
|
||||||
|
|
||||||
|
|
||||||
|
def test_bob_can_issue_a_work_order_to_a_specific_ursula(enacted_policy_group, alice, bob, ursulas):
|
||||||
|
"""
|
||||||
|
Now that Bob has his list of Ursulas, he can issue a WorkOrder to one. Upon receiving the WorkOrder, Ursula
|
||||||
|
saves it and responds by re-encrypting and giving Bob a cFrag.
|
||||||
|
|
||||||
|
This is a multipart test; it shows proper relations between the Characters Ursula and Bob and also proper
|
||||||
|
interchange between a KFrag, PFrag, and CFrag object in the context of REST-driven proxy re-encryption.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# We pick up our story with Bob already having followed the treasure map above, ie:
|
||||||
|
assert len(bob._ursulas) == len(ursulas)
|
||||||
|
|
||||||
|
the_pfrag = enacted_policy_group.pfrag
|
||||||
|
|
||||||
|
# We'll test against just a single Ursula - here, we made a WorkOrder for just one.
|
||||||
|
work_orders = bob.generate_work_orders(enacted_policy_group, the_pfrag, num_ursulas=1)
|
||||||
|
assert len(work_orders) == 1
|
||||||
|
|
||||||
|
networky_stuff = MockNetworkyStuff(ursulas)
|
||||||
|
|
||||||
|
ursula_dht_key, work_order = list(work_orders.items())[0]
|
||||||
|
cfrags = bob.get_reencrypted_c_frag(networky_stuff, work_order)
|
||||||
|
|
||||||
|
the_cfrag = cfrags[0] # We only gave one pFrag, so we only got one cFrag.
|
||||||
|
|
||||||
|
# Wow, 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).
|
||||||
|
ursula = networky_stuff.get_ursula_by_id(work_order.ursula_id)
|
||||||
|
the_kfrag = ursula.keystore.get_kfrag(work_order.kfrag_hrac)
|
||||||
|
the_correct_cfrag = api.ecies_reencrypt(the_kfrag, the_pfrag.encrypted_key)
|
||||||
|
assert the_cfrag == the_correct_cfrag # It's the correct cfrag!
|
||||||
|
|
||||||
|
# Now we'll show that Ursula saved the correct WorkOrder.
|
||||||
|
work_orders_from_bob = ursula.work_orders(bob=bob)
|
||||||
|
assert len(work_orders_from_bob) == 1
|
||||||
|
assert work_orders_from_bob[0] == work_order
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from apistar.test import TestClient
|
import pytest
|
||||||
from sqlalchemy.engine import create_engine
|
from sqlalchemy.engine import create_engine
|
||||||
|
|
||||||
from nkms.characters import Ursula, Alice, Bob
|
from apistar.test import TestClient
|
||||||
|
from nkms.characters import Ursula
|
||||||
|
from nkms.crypto.fragments import CFrag
|
||||||
|
from nkms.crypto.utils import RepeatingBytestringSplitter
|
||||||
from nkms.keystore import keystore
|
from nkms.keystore import keystore
|
||||||
from nkms.keystore.db import Base
|
from nkms.keystore.db import Base
|
||||||
from nkms.network.node import NetworkyStuff
|
from nkms.network.node import NetworkyStuff
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
NUMBER_OF_URSULAS_IN_NETWORK = 6
|
NUMBER_OF_URSULAS_IN_NETWORK = 6
|
||||||
|
|
||||||
EVENT_LOOP = asyncio.get_event_loop()
|
EVENT_LOOP = asyncio.get_event_loop()
|
||||||
|
@ -52,6 +53,7 @@ class MockPolicyOfferResponse(object):
|
||||||
|
|
||||||
class MockNetworkyStuff(NetworkyStuff):
|
class MockNetworkyStuff(NetworkyStuff):
|
||||||
def __init__(self, ursulas):
|
def __init__(self, ursulas):
|
||||||
|
self._ursulas = {u.interface_dht_key(): u for u in ursulas}
|
||||||
self.ursulas = iter(ursulas)
|
self.ursulas = iter(ursulas)
|
||||||
|
|
||||||
def go_live_with_policy(self, ursula, policy_offer):
|
def go_live_with_policy(self, ursula, policy_offer):
|
||||||
|
@ -70,3 +72,20 @@ class MockNetworkyStuff(NetworkyStuff):
|
||||||
mock_client = TestClient(ursula.rest_app)
|
mock_client = TestClient(ursula.rest_app)
|
||||||
response = mock_client.post('http://localhost/kFrag/{}'.format(hrac.hex()), payload)
|
response = mock_client.post('http://localhost/kFrag/{}'.format(hrac.hex()), payload)
|
||||||
return True, ursula.interface_dht_key()
|
return True, ursula.interface_dht_key()
|
||||||
|
|
||||||
|
def get_ursula_by_id(self, ursula_id):
|
||||||
|
print(self._ursulas)
|
||||||
|
try:
|
||||||
|
ursula = self._ursulas[ursula_id]
|
||||||
|
except KeyError:
|
||||||
|
pytest.fail("No Ursula with ID {}".format(ursula_id))
|
||||||
|
return ursula
|
||||||
|
|
||||||
|
def reencrypt(self, work_order):
|
||||||
|
print(work_order)
|
||||||
|
ursula = self.get_ursula_by_id(work_order.ursula_id)
|
||||||
|
mock_client = TestClient(ursula.rest_app)
|
||||||
|
payload = work_order.payload()
|
||||||
|
response = mock_client.post('http://localhost/kFrag/{}/reencrypt'.format(work_order.kfrag_hrac.hex()), payload)
|
||||||
|
cfrags = RepeatingBytestringSplitter(CFrag)(response.content)
|
||||||
|
return cfrags
|
||||||
|
|
Loading…
Reference in New Issue