mirror of https://github.com/nucypher/nucypher.git
PEP8 changes to characters
parent
7f7c1207ef
commit
5b73db801e
|
@ -22,7 +22,6 @@ from nkms.crypto.constants import NOT_SIGNED, NO_DECRYPTION_PERFORMED, KFRAG_LEN
|
||||||
from nkms.crypto.powers import CryptoPower, SigningPower, EncryptingPower
|
from nkms.crypto.powers import CryptoPower, SigningPower, 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.network import blockchain_client
|
from nkms.network import blockchain_client
|
||||||
from nkms.network.protocols import dht_value_splitter
|
from nkms.network.protocols import dht_value_splitter
|
||||||
from nkms.network.server import NuCypherDHTServer, NuCypherSeedOnlyDHTServer
|
from nkms.network.server import NuCypherDHTServer, NuCypherSeedOnlyDHTServer
|
||||||
|
@ -41,19 +40,25 @@ class Character(object):
|
||||||
def __init__(self, attach_server=True, crypto_power: CryptoPower = None,
|
def __init__(self, attach_server=True, crypto_power: CryptoPower = None,
|
||||||
crypto_power_ups=[], is_me=True) -> None:
|
crypto_power_ups=[], is_me=True) -> None:
|
||||||
"""
|
"""
|
||||||
:param attach_server: Whether to attach a Server when this Character is born.
|
:param attach_server: Whether to attach a Server when this Character is
|
||||||
:param crypto_power: A CryptoPower object; if provided, this will be the character's CryptoPower.
|
born.
|
||||||
:param crypto_power_ups: If crypto_power is not provided, a new CryptoPower will be made and
|
:param crypto_power: A CryptoPower object; if provided, this will be the
|
||||||
will consume all of the CryptoPowerUps in this list.
|
character's CryptoPower.
|
||||||
|
:param crypto_power_ups: If crypto_power is not provided, a new
|
||||||
|
CryptoPower will be made and will consume all of the CryptoPowerUps
|
||||||
|
in this list.
|
||||||
|
|
||||||
If neither crypto_power nor crypto_power_ups are provided, we give this Character all CryptoPowerUps
|
If neither crypto_power nor crypto_power_ups are provided, we give this
|
||||||
listed in their _default_crypto_powerups attribute.
|
Character all CryptoPowerUps listed in their _default_crypto_powerups
|
||||||
|
attribute.
|
||||||
|
|
||||||
:param is_me: Set this to True when you want this Character to represent the owner of the configuration under
|
:param is_me: Set this to True when you want this Character to represent
|
||||||
which the program is being run. A Character who is_me can do things that other Characters can't, like run
|
the owner of the configuration under which the program is being run.
|
||||||
servers, sign messages, and decrypt messages which are encrypted for them. Typically this will be True
|
A Character who is_me can do things that other Characters can't,
|
||||||
for exactly one Character, but there are scenarios in which its imaginable to be represented by zero Characters
|
like run servers, sign messages, and decrypt messages which are
|
||||||
or by more than one Character.
|
encrypted for them. Typically this will be True for exactly one
|
||||||
|
Character, but there are scenarios in which its imaginable to be
|
||||||
|
represented by zero Characters or by more than one Character.
|
||||||
"""
|
"""
|
||||||
self.log = getLogger("characters")
|
self.log = getLogger("characters")
|
||||||
if crypto_power and crypto_power_ups:
|
if crypto_power and crypto_power_ups:
|
||||||
|
@ -72,9 +77,11 @@ class Character(object):
|
||||||
if crypto_power:
|
if crypto_power:
|
||||||
self._crypto_power = crypto_power
|
self._crypto_power = crypto_power
|
||||||
elif crypto_power_ups:
|
elif crypto_power_ups:
|
||||||
self._crypto_power = CryptoPower(power_ups=crypto_power_ups, generate_keys_if_needed=is_me)
|
self._crypto_power = CryptoPower(power_ups=crypto_power_ups,
|
||||||
|
generate_keys_if_needed=is_me)
|
||||||
else:
|
else:
|
||||||
self._crypto_power = CryptoPower(self._default_crypto_powerups, generate_keys_if_needed=is_me)
|
self._crypto_power = CryptoPower(self._default_crypto_powerups,
|
||||||
|
generate_keys_if_needed=is_me)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return bytes(self.seal) == bytes(other.seal)
|
return bytes(self.seal) == bytes(other.seal)
|
||||||
|
@ -83,7 +90,8 @@ class Character(object):
|
||||||
return int.from_bytes(self.seal, byteorder="big")
|
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."""
|
||||||
|
|
||||||
class SuspiciousActivity(RuntimeError):
|
class SuspiciousActivity(RuntimeError):
|
||||||
"""raised when an action appears to amount to malicious conduct."""
|
"""raised when an action appears to amount to malicious conduct."""
|
||||||
|
@ -91,12 +99,14 @@ class Character(object):
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_public_keys(cls, *powers_and_key_bytes):
|
def from_public_keys(cls, *powers_and_key_bytes):
|
||||||
"""
|
"""
|
||||||
Sometimes we discover a Character and, at the same moment, learn one or more of their public keys.
|
Sometimes we discover a Character and, at the same moment, learn one or
|
||||||
Here, we take a collection of tuples (powers_and_key_bytes) in the following format:
|
more of their public keys. Here, we take a collection of tuples
|
||||||
|
(powers_and_key_bytes) in the following format:
|
||||||
(CryptoPowerUp class, public_key_bytes)
|
(CryptoPowerUp class, public_key_bytes)
|
||||||
|
|
||||||
Each item in the collection will have the CryptoPowerUp instantiated with the public_key_bytes, and the resulting
|
Each item in the collection will have the CryptoPowerUp instantiated
|
||||||
CryptoPowerUp instance consumed by the Character.
|
with the public_key_bytes, and the resulting CryptoPowerUp instance
|
||||||
|
consumed by the Character.
|
||||||
"""
|
"""
|
||||||
crypto_power = CryptoPower()
|
crypto_power = CryptoPower()
|
||||||
|
|
||||||
|
@ -105,9 +115,10 @@ class Character(object):
|
||||||
|
|
||||||
return cls(is_me=False, crypto_power=crypto_power)
|
return cls(is_me=False, crypto_power=crypto_power)
|
||||||
|
|
||||||
def attach_server(self, ksize=20, alpha=3, id=None, storage=None,
|
def attach_server(self, ksize=20, alpha=3, id=None,
|
||||||
*args, **kwargs) -> None:
|
storage=None, *args, **kwargs) -> None:
|
||||||
self._server = self._server_class(ksize, alpha, id, storage, *args, **kwargs)
|
self._server = self._server_class(
|
||||||
|
ksize, alpha, id, storage, *args, **kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def seal(self):
|
def seal(self):
|
||||||
|
@ -133,60 +144,73 @@ class Character(object):
|
||||||
def learn_about_actor(self, actor):
|
def learn_about_actor(self, actor):
|
||||||
self._actor_mapping[actor.id()] = actor
|
self._actor_mapping[actor.id()] = actor
|
||||||
|
|
||||||
def encrypt_for(self, recipient: "Character", cleartext: bytes, sign: bool = True,
|
def encrypt_for(self, recipient: "Character", cleartext: bytes,
|
||||||
sign_cleartext=True) -> tuple:
|
sign: bool=True, sign_cleartext=True) -> tuple:
|
||||||
"""
|
"""
|
||||||
Looks up recipient actor, finds that actor's pubkey_enc on our keyring, and encrypts for them.
|
Looks up recipient actor, finds that actor's pubkey_enc on our keyring,
|
||||||
Optionally signs the message as well.
|
and encrypts for them. Optionally signs the message as well.
|
||||||
|
|
||||||
:param recipient: The character whose public key will be used to encrypt cleartext.
|
:param recipient: The character whose public key will be used to encrypt
|
||||||
:param cleartext: The secret to be encrypted.
|
cleartext.
|
||||||
|
:param cleartext: The secret to be encrypted.
|
||||||
:param sign: Whether or not to sign the message.
|
:param sign: Whether or not to sign the message.
|
||||||
:param sign_cleartext: When signing, the cleartext is signed if this is True, Otherwise, the resulting ciphertext is signed.
|
:param sign_cleartext: When signing, the cleartext is signed if this is
|
||||||
:return: A tuple, (ciphertext, signature). If sign==False, then signature will be NOT_SIGNED.
|
True, Otherwise, the resulting ciphertext is signed.
|
||||||
|
|
||||||
|
:return: A tuple, (ciphertext, signature). If sign==False,
|
||||||
|
then signature will be NOT_SIGNED.
|
||||||
"""
|
"""
|
||||||
actor = self._lookup_actor(recipient)
|
actor = self._lookup_actor(recipient)
|
||||||
|
|
||||||
if sign:
|
if sign:
|
||||||
if sign_cleartext:
|
if sign_cleartext:
|
||||||
signature = self.seal(cleartext)
|
signature = self.seal(cleartext)
|
||||||
ciphertext = self._crypto_power.encrypt_for(actor.public_key(EncryptingPower),
|
ciphertext = self._crypto_power.encrypt_for(
|
||||||
signature + cleartext)
|
actor.public_key(EncryptingPower), signature + cleartext)
|
||||||
else:
|
else:
|
||||||
ciphertext = self._crypto_power.encrypt_for(actor.public_key(EncryptingPower),
|
ciphertext = self._crypto_power.encrypt_for(
|
||||||
cleartext)
|
actor.public_key(EncryptingPower), cleartext)
|
||||||
signature = self.seal(ciphertext)
|
signature = self.seal(ciphertext)
|
||||||
else:
|
else:
|
||||||
signature = NOT_SIGNED
|
signature = NOT_SIGNED
|
||||||
ciphertext = self._crypto_power.encrypt_for(actor.public_key(EncryptingPower),
|
ciphertext = self._crypto_power.encrypt_for(
|
||||||
cleartext)
|
actor.public_key(EncryptingPower), cleartext)
|
||||||
|
|
||||||
return ciphertext, signature
|
return ciphertext, signature
|
||||||
|
|
||||||
def verify_from(self, actor_whom_sender_claims_to_be: "Character", message: bytes, signature: Signature = None,
|
def verify_from(self,
|
||||||
decrypt=False,
|
actor_whom_sender_claims_to_be: "Character", message: bytes,
|
||||||
signature_is_on_cleartext=False) -> tuple:
|
signature: Signature=None, decrypt=False,
|
||||||
|
signature_is_on_cleartext=False) -> tuple:
|
||||||
"""
|
"""
|
||||||
Inverse of encrypt_for.
|
Inverse of encrypt_for.
|
||||||
|
|
||||||
:param actor_that_sender_claims_to_be: A Character instance representing the actor whom the sender claims to be. We check the public key owned by this Character instance to verify.
|
:param actor_that_sender_claims_to_be: A Character instance representing
|
||||||
|
the actor whom the sender claims to be. We check the public key
|
||||||
|
owned by this Character instance to verify.
|
||||||
:param messages: The messages to be verified.
|
:param messages: The messages to be verified.
|
||||||
: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 on the cleartext. Otherwise, we presume that the ciphertext is what is signed.
|
:param signature_is_on_cleartext: True if we expect the signature to be
|
||||||
:return: (Whether or not the signature is valid, the decrypted plaintext or NO_DECRYPTION_PERFORMED)
|
on the cleartext. Otherwise, we presume that the ciphertext is what
|
||||||
|
is signed.
|
||||||
|
:return: Whether or not the signature is valid, the decrypted plaintext
|
||||||
|
or NO_DECRYPTION_PERFORMED
|
||||||
"""
|
"""
|
||||||
if not signature and not signature_is_on_cleartext:
|
if not signature and not signature_is_on_cleartext:
|
||||||
raise ValueError("You need to either provide the Signature or decrypt and find it on the cleartext.")
|
raise ValueError("You need to either provide the Signature or \
|
||||||
|
decrypt and find it on the cleartext.")
|
||||||
|
|
||||||
cleartext = NO_DECRYPTION_PERFORMED
|
cleartext = NO_DECRYPTION_PERFORMED
|
||||||
|
|
||||||
if signature_is_on_cleartext:
|
if signature_is_on_cleartext:
|
||||||
if decrypt:
|
if decrypt:
|
||||||
cleartext = self._crypto_power.decrypt(message)
|
cleartext = self._crypto_power.decrypt(message)
|
||||||
signature, message = BytestringSplitter(Signature)(cleartext, return_remainder=True)
|
signature, message = BytestringSplitter(Signature)(cleartext,
|
||||||
|
return_remainder=True)
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Can't look for a signature on the cleartext if we're not decrypting.")
|
"Can't look for a signature on the cleartext if we're not \
|
||||||
|
decrypting.")
|
||||||
|
|
||||||
actor = self._lookup_actor(actor_whom_sender_claims_to_be)
|
actor = self._lookup_actor(actor_whom_sender_claims_to_be)
|
||||||
|
|
||||||
|
@ -196,16 +220,18 @@ class Character(object):
|
||||||
try:
|
try:
|
||||||
return self._actor_mapping[actor.id()]
|
return self._actor_mapping[actor.id()]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise self.NotFound("We haven't learned of an actor with ID {}".format(actor.id()))
|
raise self.NotFound(
|
||||||
|
"We haven't learned of an actor with ID {}".format(actor.id()))
|
||||||
|
|
||||||
def id(self):
|
def id(self):
|
||||||
return hexlify(bytes(self.seal))
|
return hexlify(bytes(self.seal))
|
||||||
|
|
||||||
def public_key(self, key_class):
|
def public_key(self, key_class):
|
||||||
|
# TODO: Does it make sense to have a specialized exception here? Probably.
|
||||||
try:
|
try:
|
||||||
return self._crypto_power.public_keys[key_class]
|
return self._crypto_power.public_keys[key_class]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise # TODO: Does it make sense to have a specialized exception here? Probably.
|
raise
|
||||||
|
|
||||||
|
|
||||||
class Alice(Character):
|
class Alice(Character):
|
||||||
|
@ -240,8 +266,8 @@ class Alice(Character):
|
||||||
|
|
||||||
##### Temporary until we decide on an API for private key access
|
##### Temporary until we decide on an API for private key access
|
||||||
alice_priv_enc = self._crypto_power._power_ups[EncryptingPower].priv_key
|
alice_priv_enc = self._crypto_power._power_ups[EncryptingPower].priv_key
|
||||||
kfrags, pfrag = self.generate_rekey_frags(alice_priv_enc, bob, m,
|
kfrags, pfrag = self.generate_rekey_frags(alice_priv_enc, bob, m, n)
|
||||||
n) # TODO: Access Alice's private key inside this method.
|
# TODO: Access Alice's private key inside this method.
|
||||||
from nkms.policy.models import Policy
|
from nkms.policy.models import Policy
|
||||||
policy = Policy.from_alice(
|
policy = Policy.from_alice(
|
||||||
alice=self,
|
alice=self,
|
||||||
|
@ -253,7 +279,8 @@ class Alice(Character):
|
||||||
|
|
||||||
return policy
|
return policy
|
||||||
|
|
||||||
def grant(self, bob, uri, networky_stuff, m=None, n=None, expiration=None, deposit=None):
|
def grant(self, bob, uri, networky_stuff,
|
||||||
|
m=None, n=None, expiration=None, deposit=None):
|
||||||
if not m:
|
if not m:
|
||||||
# TODO: get m from config
|
# TODO: get m from config
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
@ -272,12 +299,15 @@ class Alice(Character):
|
||||||
|
|
||||||
policy = self.create_policy(bob, uri, m, n)
|
policy = self.create_policy(bob, uri, m, n)
|
||||||
|
|
||||||
# We'll find n Ursulas by default. It's possible to "play the field" by trying differet
|
# We'll find n Ursulas by default. It's possible to "play the field"
|
||||||
|
# by trying differet
|
||||||
# deposits and expirations on a limited number of Ursulas.
|
# deposits and expirations on a limited number of Ursulas.
|
||||||
# Users may decide to inject some market strategies here.
|
# Users may decide to inject some market strategies here.
|
||||||
found_ursulas = policy.find_ursulas(networky_stuff, deposit, expiration, num_ursulas=n)
|
found_ursulas = policy.find_ursulas(networky_stuff, deposit,
|
||||||
|
expiration, num_ursulas=n)
|
||||||
policy.match_kfrags_to_found_ursulas(found_ursulas)
|
policy.match_kfrags_to_found_ursulas(found_ursulas)
|
||||||
policy.enact(networky_stuff) # REST call happens here, as does population of TreasureMap.
|
# REST call happens here, as does population of TreasureMap.
|
||||||
|
policy.enact(networky_stuff)
|
||||||
|
|
||||||
return policy
|
return policy
|
||||||
|
|
||||||
|
@ -312,12 +342,18 @@ class Bob(Character):
|
||||||
getter = self.server.get(ursula_interface_id)
|
getter = self.server.get(ursula_interface_id)
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
value = loop.run_until_complete(getter)
|
value = loop.run_until_complete(getter)
|
||||||
signature, ursula_pubkey_sig, hrac, (port, interface, ttl) = dht_value_splitter(value.lstrip(b"uaddr"),
|
|
||||||
msgpack_remainder=True)
|
# TODO: Make this much prettier
|
||||||
|
signature, ursula_pubkey_sig, hrac, (port, interface, ttl) =\
|
||||||
|
dht_value_splitter(value.lstrip(b"uaddr"), msgpack_remainder=True)
|
||||||
|
|
||||||
# TODO: If we're going to implement TTL, it will be here.
|
# TODO: If we're going to implement TTL, it will be here.
|
||||||
self._ursulas[ursula_interface_id] = Ursula.as_discovered_on_network(dht_port=port, dht_interface=interface,
|
self._ursulas[ursula_interface_id] =\
|
||||||
pubkey_sig_bytes=ursula_pubkey_sig)
|
Ursula.as_discovered_on_network(
|
||||||
|
dht_port=port,
|
||||||
|
dht_interface=interface,
|
||||||
|
pubkey_sig_bytes=ursula_pubkey_sig
|
||||||
|
)
|
||||||
|
|
||||||
def get_treasure_map(self, policy_group):
|
def get_treasure_map(self, policy_group):
|
||||||
|
|
||||||
|
@ -326,16 +362,26 @@ class Bob(Character):
|
||||||
ursula_coro = self.server.get(dht_key)
|
ursula_coro = self.server.get(dht_key)
|
||||||
event_loop = asyncio.get_event_loop()
|
event_loop = asyncio.get_event_loop()
|
||||||
packed_encrypted_treasure_map = event_loop.run_until_complete(ursula_coro)
|
packed_encrypted_treasure_map = event_loop.run_until_complete(ursula_coro)
|
||||||
_signature_for_ursula, pubkey_sig_alice, hrac, encrypted_treasure_map = dht_value_splitter(
|
|
||||||
packed_encrypted_treasure_map[5::], msgpack_remainder=True)
|
# TODO: Make this prettier
|
||||||
verified, cleartext = self.verify_from(self.alice, encrypted_treasure_map,
|
_signature_for_ursula, pubkey_sig_alice, hrac, encrypted_treasure_map =\
|
||||||
signature_is_on_cleartext=True, decrypt=True)
|
dht_value_splitter(
|
||||||
alices_signature, packed_node_list = BytestringSplitter(Signature)(cleartext, return_remainder=True)
|
packed_encrypted_treasure_map[5::], msgpack_remainder=True
|
||||||
|
)
|
||||||
|
verified, cleartext = self.verify_from(
|
||||||
|
self.alice, encrypted_treasure_map,
|
||||||
|
signature_is_on_cleartext=True, decrypt=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
|
||||||
else:
|
else:
|
||||||
from nkms.policy.models import TreasureMap
|
from nkms.policy.models import TreasureMap
|
||||||
self.treasure_maps[policy_group.hrac] = TreasureMap(msgpack.loads(packed_node_list))
|
self.treasure_maps[policy_group.hrac] = TreasureMap(
|
||||||
|
msgpack.loads(packed_node_list)
|
||||||
|
)
|
||||||
return self.treasure_maps[policy_group.hrac]
|
return self.treasure_maps[policy_group.hrac]
|
||||||
|
|
||||||
def generate_work_orders(self, kfrag_hrac, *pfrags, num_ursulas=None):
|
def generate_work_orders(self, kfrag_hrac, *pfrags, num_ursulas=None):
|
||||||
|
@ -344,7 +390,8 @@ class Bob(Character):
|
||||||
try:
|
try:
|
||||||
treasure_map_to_use = self.treasure_maps[kfrag_hrac]
|
treasure_map_to_use = self.treasure_maps[kfrag_hrac]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise KeyError("Bob doesn't have a TreasureMap matching the hrac {}".format(kfrag_hrac))
|
raise KeyError(
|
||||||
|
"Bob doesn't have a TreasureMap matching the hrac {}".format(kfrag_hrac))
|
||||||
|
|
||||||
generated_work_orders = {}
|
generated_work_orders = {}
|
||||||
|
|
||||||
|
@ -354,7 +401,8 @@ class Bob(Character):
|
||||||
for ursula_dht_key in treasure_map_to_use:
|
for ursula_dht_key in treasure_map_to_use:
|
||||||
ursula = self._ursulas[ursula_dht_key]
|
ursula = self._ursulas[ursula_dht_key]
|
||||||
|
|
||||||
completed_work_orders_for_this_ursula = self._saved_work_orders.setdefault(ursula_dht_key, [])
|
completed_work_orders_for_this_ursula =\
|
||||||
|
self._saved_work_orders.setdefault(ursula_dht_key, [])
|
||||||
|
|
||||||
pfrags_to_include = []
|
pfrags_to_include = []
|
||||||
for pfrag in pfrags:
|
for pfrag in pfrags:
|
||||||
|
@ -363,7 +411,8 @@ class Bob(Character):
|
||||||
pfrags_to_include.append(pfrag)
|
pfrags_to_include.append(pfrag)
|
||||||
|
|
||||||
if pfrags_to_include:
|
if pfrags_to_include:
|
||||||
work_order = WorkOrder.constructed_by_bob(kfrag_hrac, pfrags_to_include, ursula_dht_key, self)
|
work_order = WorkOrder.constructed_by_bob(
|
||||||
|
kfrag_hrac, pfrags_to_include, ursula_dht_key, self)
|
||||||
generated_work_orders[ursula_dht_key] = work_order
|
generated_work_orders[ursula_dht_key] = work_order
|
||||||
|
|
||||||
if num_ursulas is not None:
|
if num_ursulas is not None:
|
||||||
|
@ -401,7 +450,8 @@ class Ursula(Character):
|
||||||
|
|
||||||
self._rest_app = None
|
self._rest_app = None
|
||||||
self._work_orders = []
|
self._work_orders = []
|
||||||
self._contracts = {} # TODO: This needs to actually be a persistent data store. See #127.
|
# TODO: This needs to actually be a persistent data store. See #127.
|
||||||
|
self._contracts = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def rest_app(self):
|
def rest_app(self):
|
||||||
|
@ -412,7 +462,8 @@ class Ursula(Character):
|
||||||
return self._rest_app
|
return self._rest_app
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def as_discovered_on_network(cls, dht_port, dht_interface, pubkey_sig_bytes, rest_address=None, rest_port=None):
|
def as_discovered_on_network(cls, dht_port, dht_interface, pubkey_sig_bytes,
|
||||||
|
rest_address=None, rest_port=None):
|
||||||
# TODO: We also need the encrypting public key here.
|
# TODO: We also need the encrypting public key here.
|
||||||
ursula = cls.from_public_keys((SigningPower, pubkey_sig_bytes))
|
ursula = cls.from_public_keys((SigningPower, pubkey_sig_bytes))
|
||||||
ursula.dht_port = dht_port
|
ursula.dht_port = dht_port
|
||||||
|
@ -426,25 +477,35 @@ class Ursula(Character):
|
||||||
response = requests.get(url)
|
response = requests.get(url)
|
||||||
if not response.status_code == 200:
|
if not response.status_code == 200:
|
||||||
raise RuntimeError("Got a bad response: {}".format(response))
|
raise RuntimeError("Got a bad response: {}".format(response))
|
||||||
signing_key_bytes, encrypting_key_bytes = BytestringSplitter(PublicKey)(response.content, return_remainder=True)
|
signing_key_bytes, encrypting_key_bytes =\
|
||||||
stranger_ursula_from_public_keys = cls.from_public_keys(signing=signing_key_bytes,
|
BytestringSplitter(PublicKey)(response.content,
|
||||||
encrypting=encrypting_key_bytes)
|
return_remainder=True)
|
||||||
|
stranger_ursula_from_public_keys = cls.from_public_keys(
|
||||||
|
signing=signing_key_bytes, encrypting=encrypting_key_bytes)
|
||||||
return stranger_ursula_from_public_keys
|
return stranger_ursula_from_public_keys
|
||||||
|
|
||||||
def attach_server(self, ksize=20, alpha=3, id=None, storage=None,
|
def attach_server(self, ksize=20, alpha=3, id=None,
|
||||||
*args, **kwargs):
|
storage=None, *args, **kwargs):
|
||||||
|
|
||||||
|
# TODO: Network-wide deterministic ID generation (ie, auction or
|
||||||
|
# whatever) See #136.
|
||||||
if not id:
|
if not id:
|
||||||
id = digest(
|
id = digest(secure_random(32))
|
||||||
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}',
|
||||||
Route('/kFrag/{hrac_as_hex}/reencrypt', 'POST', self.reencrypt_via_rest),
|
'POST',
|
||||||
Route('/public_keys', 'GET', self.get_signing_and_encrypting_public_keys),
|
self.set_policy),
|
||||||
Route('/consider_contract', 'POST', self.consider_contract),
|
Route('/kFrag/{hrac_as_hex}/reencrypt',
|
||||||
|
'POST',
|
||||||
|
self.reencrypt_via_rest),
|
||||||
|
Route('/public_keys', 'GET',
|
||||||
|
self.get_signing_and_encrypting_public_keys),
|
||||||
|
Route('/consider_contract',
|
||||||
|
'POST',
|
||||||
|
self.consider_contract),
|
||||||
]
|
]
|
||||||
|
|
||||||
self._rest_app = App(routes=routes)
|
self._rest_app = App(routes=routes)
|
||||||
|
@ -462,7 +523,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.dht_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.dht_interface_info()))
|
return self.hash(msgpack.dumps(self.dht_interface_info()))
|
||||||
|
@ -482,31 +546,40 @@ class Ursula(Character):
|
||||||
"""
|
"""
|
||||||
REST endpoint for getting both signing and encrypting public keys.
|
REST endpoint for getting both signing and encrypting public keys.
|
||||||
"""
|
"""
|
||||||
return Response(content=bytes(self.seal) + bytes(self.public_key(EncryptingPower)),
|
return Response(
|
||||||
content_type="application/octet-stream")
|
content=bytes(self.seal) + bytes(self.public_key(EncryptingPower)),
|
||||||
|
content_type="application/octet-stream")
|
||||||
|
|
||||||
def consider_contract(self, hrac_as_hex, request: http.Request):
|
def consider_contract(self, hrac_as_hex, request: http.Request):
|
||||||
# TODO: This actually needs to be a REST endpoint, with the payload carrying the kfrag hash separately.
|
# TODO: This actually needs to be a REST endpoint, with the payload
|
||||||
|
# carrying the kfrag hash separately.
|
||||||
from nkms.policy.models import Contract
|
from nkms.policy.models import Contract
|
||||||
contract, deposit_as_bytes = BytestringSplitter(Contract)(request.body, return_remainder=True)
|
contract, deposit_as_bytes =\
|
||||||
|
BytestringSplitter(Contract)(request.body, return_remainder=True)
|
||||||
contract.deposit = deposit_as_bytes
|
contract.deposit = deposit_as_bytes
|
||||||
|
|
||||||
contract_to_store = { # TODO: This needs to be a datastore - see #127.
|
contract_to_store = { # TODO: This needs to be a datastore - see #127.
|
||||||
"alice_pubkey_sig": bytes(contract.alice.seal),
|
"alice_pubkey_sig": bytes(contract.alice.seal),
|
||||||
"deposit": contract.deposit,
|
"deposit": contract.deposit,
|
||||||
# TODO: Whatever type "deposit" ends up being, we'll need to serialize it here. See #148.
|
# TODO: Whatever type "deposit" ends up being, we'll need to
|
||||||
|
# serialize it here. See #148.
|
||||||
"expiration": contract.expiration,
|
"expiration": contract.expiration,
|
||||||
}
|
}
|
||||||
self._contracts[contract.hrac] = contract_to_store
|
self._contracts[contract.hrac] = contract_to_store
|
||||||
|
|
||||||
# TODO: Make the rest of this logic actually work - do something here to decide if this Contract is worth accepting.
|
# TODO: Make the rest of this logic actually work - do something here
|
||||||
return Response(b"This will eventually be an actual acceptance of the contract.", content_type="application/octet-stream")
|
# to decide if this Contract is worth accepting.
|
||||||
|
return Response(
|
||||||
|
b"This will eventually be an actual acceptance of the contract.",
|
||||||
|
content_type="application/octet-stream")
|
||||||
|
|
||||||
def set_policy(self, hrac_as_hex, request: http.Request):
|
def set_policy(self, hrac_as_hex, request: http.Request):
|
||||||
"""
|
"""
|
||||||
REST endpoint for setting a kFrag.
|
REST endpoint for setting a kFrag.
|
||||||
TODO: Instead of taking a Request, use the apistar typing system to type a payload and validate / split it.
|
TODO: Instead of taking a Request, use the apistar typing system to type
|
||||||
TODO: Validate that the kfrag being saved is pursuant to an approved Policy (see #121).
|
a payload and validate / split it.
|
||||||
|
TODO: Validate that the kfrag being saved is pursuant to an approved
|
||||||
|
Policy (see #121).
|
||||||
"""
|
"""
|
||||||
from nkms.policy.models import Contract # Avoid circular import
|
from nkms.policy.models import Contract # Avoid circular import
|
||||||
hrac = binascii.unhexlify(hrac_as_hex)
|
hrac = binascii.unhexlify(hrac_as_hex)
|
||||||
|
@ -514,20 +587,25 @@ class Ursula(Character):
|
||||||
group_payload_splitter = BytestringSplitter(PublicKey)
|
group_payload_splitter = BytestringSplitter(PublicKey)
|
||||||
policy_payload_splitter = BytestringSplitter((KFrag, KFRAG_LENGTH))
|
policy_payload_splitter = BytestringSplitter((KFrag, KFRAG_LENGTH))
|
||||||
|
|
||||||
alice_pubkey_sig, payload_encrypted_for_ursula = group_payload_splitter(request.body, msgpack_remainder=True)
|
alice_pubkey_sig, payload_encrypted_for_ursula =\
|
||||||
|
group_payload_splitter(request.body, msgpack_remainder=True)
|
||||||
alice = Alice.from_public_keys((SigningPower, alice_pubkey_sig))
|
alice = Alice.from_public_keys((SigningPower, alice_pubkey_sig))
|
||||||
self.learn_about_actor(alice)
|
self.learn_about_actor(alice)
|
||||||
|
|
||||||
verified, cleartext = self.verify_from(alice, payload_encrypted_for_ursula,
|
verified, cleartext = self.verify_from(
|
||||||
decrypt=True, signature_is_on_cleartext=True)
|
alice, payload_encrypted_for_ursula,
|
||||||
|
decrypt=True, signature_is_on_cleartext=True)
|
||||||
|
|
||||||
if not verified:
|
if not verified:
|
||||||
# TODO: What do we do if the Policy isn't signed properly?
|
# TODO: What do we do if the Policy isn't signed properly?
|
||||||
pass
|
pass
|
||||||
|
|
||||||
alices_signature, policy_payload = BytestringSplitter(Signature)(cleartext, return_remainder=True)
|
alices_signature, policy_payload =\
|
||||||
|
BytestringSplitter(Signature)(cleartext, return_remainder=True)
|
||||||
|
|
||||||
kfrag = policy_payload_splitter(policy_payload)[0] # TODO: If we're not adding anything else in the payload, stop using the splitter here.
|
# TODO: If we're not adding anything else in the payload, stop using the
|
||||||
|
# splitter here.
|
||||||
|
kfrag = policy_payload_splitter(policy_payload)[0]
|
||||||
|
|
||||||
# TODO: Query stored Contract and reconstitute
|
# TODO: Query stored Contract and reconstitute
|
||||||
contract_details = self._contracts[hrac]
|
contract_details = self._contracts[hrac]
|
||||||
|
@ -536,7 +614,8 @@ class Ursula(Character):
|
||||||
if stored_alice_pubkey_sig != alice_pubkey_sig:
|
if stored_alice_pubkey_sig != alice_pubkey_sig:
|
||||||
raise Alice.SuspiciousActivity
|
raise Alice.SuspiciousActivity
|
||||||
|
|
||||||
contract = Contract(alice=alice, hrac=hrac, kfrag=kfrag, **contract_details)
|
contract = Contract(alice=alice, hrac=hrac,
|
||||||
|
kfrag=kfrag, **contract_details)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.keystore.add_kfrag(hrac, contract.kfrag, alices_signature)
|
self.keystore.add_kfrag(hrac, contract.kfrag, alices_signature)
|
||||||
|
@ -557,9 +636,11 @@ class Ursula(Character):
|
||||||
# TODO: Sign the result of this. See #141.
|
# TODO: Sign the result of this. See #141.
|
||||||
cfrag_byte_stream += API.ecies_reencrypt(kfrag, pfrag.encrypted_key)
|
cfrag_byte_stream += API.ecies_reencrypt(kfrag, pfrag.encrypted_key)
|
||||||
|
|
||||||
self._work_orders.append(work_order) # TODO: Put this in Ursula's datastore
|
# TODO: Put this in Ursula's datastore
|
||||||
|
self._work_orders.append(work_order)
|
||||||
|
|
||||||
return Response(content=cfrag_byte_stream, content_type="application/octet-stream")
|
return Response(content=cfrag_byte_stream,
|
||||||
|
content_type="application/octet-stream")
|
||||||
|
|
||||||
def work_orders(self, bob=None):
|
def work_orders(self, bob=None):
|
||||||
"""
|
"""
|
||||||
|
@ -577,7 +658,8 @@ class Ursula(Character):
|
||||||
|
|
||||||
class Seal(object):
|
class Seal(object):
|
||||||
"""
|
"""
|
||||||
Can be called to sign something or used to express the signing public key as bytes.
|
Can be called to sign something or used to express the signing public
|
||||||
|
key as bytes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, character):
|
def __init__(self, character):
|
||||||
|
|
Loading…
Reference in New Issue