mirror of https://github.com/nucypher/nucypher.git
Moved Ursula's REST bits to a new class.
parent
e1b4f242ab
commit
f9833f7d0d
|
@ -1,18 +1,13 @@
|
|||
import asyncio
|
||||
import binascii
|
||||
from binascii import hexlify
|
||||
from logging import getLogger
|
||||
from typing import Union, List
|
||||
|
||||
import msgpack
|
||||
import requests
|
||||
from apistar import http
|
||||
from apistar.core import Route
|
||||
from apistar.frameworks.wsgi import WSGIApp as App
|
||||
from apistar.http import Response
|
||||
from kademlia.network import Server
|
||||
from kademlia.utils import digest
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from typing import Union, List
|
||||
|
||||
from nkms.crypto.api import secure_random, keccak_digest
|
||||
from nkms.crypto.constants import NOT_SIGNED, NO_DECRYPTION_PERFORMED
|
||||
|
@ -23,10 +18,9 @@ from nkms.crypto.utils import BytestringSplitter
|
|||
from nkms.network import blockchain_client
|
||||
from nkms.network.constants import BYTESTRING_IS_URSULA_IFACE_INFO, BYTESTRING_IS_TREASURE_MAP
|
||||
from nkms.network.protocols import dht_value_splitter
|
||||
from nkms.network.server import NuCypherDHTServer, NuCypherSeedOnlyDHTServer
|
||||
from nkms.network.server import NuCypherDHTServer, NuCypherSeedOnlyDHTServer, ProxyRESTServer
|
||||
from nkms.policy.constants import NOT_FROM_ALICE, NON_PAYMENT
|
||||
from umbral import pre
|
||||
from umbral.fragments import KFrag
|
||||
from umbral.keys import UmbralPublicKey
|
||||
|
||||
|
||||
|
@ -177,7 +171,6 @@ class Character(object):
|
|||
signature = NOT_SIGNED
|
||||
ciphertext, capsule = pre.encrypt(recipient_pubkey_enc, plaintext)
|
||||
|
||||
|
||||
message_kit = MessageKit(ciphertext=ciphertext, capsule=capsule)
|
||||
message_kit.alice_pubkey = self.public_key(SigningPower)
|
||||
return message_kit, signature
|
||||
|
@ -459,8 +452,9 @@ class Bob(Character):
|
|||
return self._ursulas[ursula_id]
|
||||
|
||||
|
||||
class Ursula(Character):
|
||||
class Ursula(Character, ProxyRESTServer):
|
||||
_server_class = NuCypherDHTServer
|
||||
_alice_class = Alice
|
||||
_default_crypto_powerups = [SigningPower, EncryptingPower]
|
||||
|
||||
dht_port = None
|
||||
|
@ -584,111 +578,6 @@ class Ursula(Character):
|
|||
loop = asyncio.get_event_loop()
|
||||
loop.run_until_complete(setter)
|
||||
|
||||
def get_signing_and_encrypting_public_keys(self):
|
||||
"""
|
||||
REST endpoint for getting both signing and encrypting public keys.
|
||||
"""
|
||||
return Response(
|
||||
content=bytes(self.stamp) + bytes(self.public_key(EncryptingPower)),
|
||||
content_type="application/octet-stream")
|
||||
|
||||
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.
|
||||
from nkms.policy.models import Contract
|
||||
contract, deposit_as_bytes = \
|
||||
BytestringSplitter(Contract)(request.body, return_remainder=True)
|
||||
contract.deposit = deposit_as_bytes
|
||||
|
||||
# contract_to_store = { # TODO: This needs to be a datastore - see #127.
|
||||
# "alice_pubkey_sig":
|
||||
# "deposit": contract.deposit,
|
||||
# # TODO: Whatever type "deposit" ends up being, we'll need to
|
||||
# # serialize it here. See #148.
|
||||
# "expiration": contract.expiration,
|
||||
# }
|
||||
self.keystore.add_policy_contract(contract.expiration.datetime(),
|
||||
contract.deposit,
|
||||
hrac=contract.hrac.hex().encode(),
|
||||
alice_pubkey_sig=contract.alice.stamp
|
||||
)
|
||||
# TODO: Make the rest of this logic actually work - do something here
|
||||
# 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):
|
||||
"""
|
||||
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: Validate that the kfrag being saved is pursuant to an approved
|
||||
Policy (see #121).
|
||||
"""
|
||||
hrac = binascii.unhexlify(hrac_as_hex)
|
||||
policy_message_kit = MessageKit.from_bytes(request.body)
|
||||
# group_payload_splitter = BytestringSplitter(PublicKey)
|
||||
# policy_payload_splitter = BytestringSplitter((KFrag, KFRAG_LENGTH))
|
||||
|
||||
alice = Alice.from_public_keys((SigningPower, policy_message_kit.alice_pubkey))
|
||||
|
||||
verified, cleartext = self.verify_from(
|
||||
alice, policy_message_kit,
|
||||
decrypt=True, signature_is_on_cleartext=True)
|
||||
|
||||
if not verified:
|
||||
# TODO: What do we do if the Policy isn't signed properly?
|
||||
pass
|
||||
#
|
||||
# alices_signature, policy_payload =\
|
||||
# BytestringSplitter(Signature)(cleartext, return_remainder=True)
|
||||
|
||||
# TODO: If we're not adding anything else in the payload, stop using the
|
||||
# splitter here.
|
||||
# kfrag = policy_payload_splitter(policy_payload)[0]
|
||||
kfrag = KFrag.from_bytes(cleartext)
|
||||
|
||||
# TODO: Query stored Contract and reconstitute
|
||||
policy_contract = self.keystore.get_policy_contract(hrac_as_hex.encode())
|
||||
# contract_details = self._contracts[hrac.hex()]
|
||||
|
||||
if policy_contract.alice_pubkey_sig.key_data != alice.stamp:
|
||||
raise Alice.SuspiciousActivity
|
||||
|
||||
# contract = Contract(alice=alice, hrac=hrac,
|
||||
# kfrag=kfrag, expiration=policy_contract.expiration)
|
||||
|
||||
try:
|
||||
# TODO: Obviously we do this lower-level.
|
||||
policy_contract.k_frag = bytes(kfrag)
|
||||
self.keystore.session.commit()
|
||||
|
||||
except IntegrityError:
|
||||
raise
|
||||
# Do something appropriately RESTful (ie, 4xx).
|
||||
|
||||
return # TODO: 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_bytes = self.keystore.get_policy_contract(hrac.hex().encode()).k_frag # Careful! :-)
|
||||
# TODO: Push this to a lower level.
|
||||
kfrag = KFrag.from_bytes(kfrag_bytes)
|
||||
cfrag_byte_stream = b""
|
||||
|
||||
for capsule in work_order.capsules:
|
||||
# TODO: Sign the result of this. See #141.
|
||||
cfrag_byte_stream += bytes(pre.reencrypt(kfrag, capsule))
|
||||
|
||||
# TODO: Put this in Ursula's datastore
|
||||
self._work_orders.append(work_order)
|
||||
|
||||
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.
|
||||
|
|
|
@ -1,13 +1,23 @@
|
|||
import asyncio
|
||||
import binascii
|
||||
import random
|
||||
|
||||
from apistar import http
|
||||
from apistar.http import Response
|
||||
from kademlia.crawling import NodeSpiderCrawl
|
||||
from kademlia.network import Server
|
||||
from kademlia.utils import digest
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
||||
from nkms.crypto.kits import MessageKit
|
||||
from nkms.crypto.powers import EncryptingPower, SigningPower
|
||||
from nkms.crypto.utils import BytestringSplitter
|
||||
from nkms.network.capabilities import SeedOnly, ServerCapability
|
||||
from nkms.network.node import NuCypherNode
|
||||
from nkms.network.protocols import NuCypherSeedOnlyProtocol, NuCypherHashProtocol
|
||||
from nkms.network.storage import SeedOnlyStorage
|
||||
from umbral import pre
|
||||
from umbral.fragments import KFrag
|
||||
|
||||
|
||||
class NuCypherDHTServer(Server):
|
||||
|
@ -80,3 +90,111 @@ class NuCypherSeedOnlyDHTServer(NuCypherDHTServer):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.storage = SeedOnlyStorage()
|
||||
|
||||
|
||||
class ProxyRESTServer(object):
|
||||
|
||||
def get_signing_and_encrypting_public_keys(self):
|
||||
"""
|
||||
REST endpoint for getting both signing and encrypting public keys.
|
||||
"""
|
||||
return Response(
|
||||
content=bytes(self.stamp) + bytes(self.public_key(EncryptingPower)),
|
||||
content_type="application/octet-stream")
|
||||
|
||||
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.
|
||||
from nkms.policy.models import Contract
|
||||
contract, deposit_as_bytes = \
|
||||
BytestringSplitter(Contract)(request.body, return_remainder=True)
|
||||
contract.deposit = deposit_as_bytes
|
||||
|
||||
# contract_to_store = { # TODO: This needs to be a datastore - see #127.
|
||||
# "alice_pubkey_sig":
|
||||
# "deposit": contract.deposit,
|
||||
# # TODO: Whatever type "deposit" ends up being, we'll need to
|
||||
# # serialize it here. See #148.
|
||||
# "expiration": contract.expiration,
|
||||
# }
|
||||
self.keystore.add_policy_contract(contract.expiration.datetime(),
|
||||
contract.deposit,
|
||||
hrac=contract.hrac.hex().encode(),
|
||||
alice_pubkey_sig=contract.alice.stamp
|
||||
)
|
||||
# TODO: Make the rest of this logic actually work - do something here
|
||||
# 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):
|
||||
"""
|
||||
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: Validate that the kfrag being saved is pursuant to an approved
|
||||
Policy (see #121).
|
||||
"""
|
||||
hrac = binascii.unhexlify(hrac_as_hex)
|
||||
policy_message_kit = MessageKit.from_bytes(request.body)
|
||||
# group_payload_splitter = BytestringSplitter(PublicKey)
|
||||
# policy_payload_splitter = BytestringSplitter((KFrag, KFRAG_LENGTH))
|
||||
|
||||
alice = self._alice_class.from_public_keys((SigningPower, policy_message_kit.alice_pubkey))
|
||||
|
||||
verified, cleartext = self.verify_from(
|
||||
alice, policy_message_kit,
|
||||
decrypt=True, signature_is_on_cleartext=True)
|
||||
|
||||
if not verified:
|
||||
# TODO: What do we do if the Policy isn't signed properly?
|
||||
pass
|
||||
#
|
||||
# alices_signature, policy_payload =\
|
||||
# BytestringSplitter(Signature)(cleartext, return_remainder=True)
|
||||
|
||||
# TODO: If we're not adding anything else in the payload, stop using the
|
||||
# splitter here.
|
||||
# kfrag = policy_payload_splitter(policy_payload)[0]
|
||||
kfrag = KFrag.from_bytes(cleartext)
|
||||
|
||||
# TODO: Query stored Contract and reconstitute
|
||||
policy_contract = self.keystore.get_policy_contract(hrac_as_hex.encode())
|
||||
# contract_details = self._contracts[hrac.hex()]
|
||||
|
||||
if policy_contract.alice_pubkey_sig.key_data != alice.stamp:
|
||||
raise self._alice_class.SuspiciousActivity
|
||||
|
||||
# contract = Contract(alice=alice, hrac=hrac,
|
||||
# kfrag=kfrag, expiration=policy_contract.expiration)
|
||||
|
||||
try:
|
||||
# TODO: Obviously we do this lower-level.
|
||||
policy_contract.k_frag = bytes(kfrag)
|
||||
self.keystore.session.commit()
|
||||
|
||||
except IntegrityError:
|
||||
raise
|
||||
# Do something appropriately RESTful (ie, 4xx).
|
||||
|
||||
return # TODO: 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_bytes = self.keystore.get_policy_contract(hrac.hex().encode()).k_frag # Careful! :-)
|
||||
# TODO: Push this to a lower level.
|
||||
kfrag = KFrag.from_bytes(kfrag_bytes)
|
||||
cfrag_byte_stream = b""
|
||||
|
||||
for capsule in work_order.capsules:
|
||||
# TODO: Sign the result of this. See #141.
|
||||
cfrag_byte_stream += bytes(pre.reencrypt(kfrag, capsule))
|
||||
|
||||
# TODO: Put this in Ursula's datastore
|
||||
self._work_orders.append(work_order)
|
||||
|
||||
return Response(content=cfrag_byte_stream,
|
||||
content_type="application/octet-stream")
|
||||
|
|
Loading…
Reference in New Issue