Merge pull request #8 from jMyles/rm-crypto

Rm crypto
pull/157/head
Tux 2018-02-10 20:58:26 -07:00 committed by GitHub
commit 6922739fd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 117 additions and 80 deletions

View File

@ -15,6 +15,7 @@ from sqlalchemy.exc import IntegrityError
from umbral.fragments import KFrag from umbral.fragments import KFrag
from umbral.keys import UmbralPublicKey from umbral.keys import UmbralPublicKey
import umbral
from nkms.crypto import api as API from nkms.crypto import api as API
from nkms.crypto.api import secure_random, keccak_digest from nkms.crypto.api import secure_random, keccak_digest
@ -238,7 +239,7 @@ class Alice(Character):
_server_class = NuCypherSeedOnlyDHTServer _server_class = NuCypherSeedOnlyDHTServer
_default_crypto_powerups = [SigningPower, EncryptingPower] _default_crypto_powerups = [SigningPower, EncryptingPower]
def generate_rekey_frags(self, alice_privkey, bob, m, n): def generate_kfrags(self, bob, m, n):
""" """
Generates re-encryption key frags and returns the frags and encrypted Generates re-encryption key frags and returns the frags and encrypted
ephemeral key data. ephemeral key data.
@ -250,9 +251,10 @@ class Alice(Character):
:return: Tuple(kfrags, eph_key_data) :return: Tuple(kfrags, eph_key_data)
""" """
kfrags, eph_key_data = API.ecies_ephemeral_split_rekey( # TODO: Is this how we want to access Alice's private key?
alice_privkey, bytes(bob.seal.without_metabytes()), m, n) alice_priv_enc = self._crypto_power._power_ups[EncryptingPower].keypair.privkey
return (kfrags, eph_key_data) k_frags, _v_keys = umbral.umbral.split_rekey(alice_priv_enc, bob.public_key(EncryptingPower), m, n)
return k_frags
def create_policy(self, def create_policy(self,
bob: "Bob", bob: "Bob",
@ -263,17 +265,13 @@ class Alice(Character):
""" """
Alice dictates a new group of policies. Alice dictates a new group of policies.
""" """
kfrags = self.generate_kfrags(bob, m, n)
##### Temporary until we decide on an API for private key access
alice_priv_enc = self._crypto_power._power_ups[EncryptingPower].priv_key
kfrags, pfrag = self.generate_rekey_frags(alice_priv_enc, bob, m, 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,
bob=bob, bob=bob,
kfrags=kfrags, kfrags=kfrags,
pfrag=pfrag,
uri=uri, uri=uri,
) )

View File

@ -8,7 +8,7 @@ from cryptography.exceptions import InvalidSignature
from py_ecc.secp256k1 import ecdsa_raw_recover from py_ecc.secp256k1 import ecdsa_raw_recover
from umbral.keys import UmbralPrivateKey, UmbralPublicKey from umbral.keys import UmbralPrivateKey, UmbralPublicKey
from nkms.crypto.signature import Signature
SYSTEM_RAND = SystemRandom() SYSTEM_RAND = SystemRandom()
@ -75,8 +75,8 @@ def ecdsa_sign(message: bytes, privkey: UmbralPrivateKey) -> bytes:
:return: signature :return: signature
""" """
cryptography_priv_key = privkey.bn_key.to_cryptography_priv_key() cryptography_priv_key = privkey.bn_key.to_cryptography_priv_key()
signature_bytes = cryptography_priv_key.sign(message, ec.ECDSA(hashes.BLAKE2b(64))) signature_der_bytes = cryptography_priv_key.sign(message, ec.ECDSA(hashes.BLAKE2b(64)))
return Signature(signature_bytes) return signature_der_bytes
def ecdsa_verify( def ecdsa_verify(

View File

@ -1,10 +1,10 @@
# TODO: Turn these into classes? BLAKE2B_DIGEST_LENGTH = 64
HASH_DIGEST_LENGTH = 64 KECCAK_DIGEST_LENGTH = 32
NOT_SIGNED = 445 NOT_SIGNED = 445
NO_DECRYPTION_PERFORMED = 455 NO_DECRYPTION_PERFORMED = 455
# These lengths are centric to secp256k1 # These lengths are specific to secp256k1
KFRAG_LENGTH = 194 KFRAG_LENGTH = 194
CFRAG_LENGTH = 131 CFRAG_LENGTH = 131
CAPSULE_LENGTH = 98 CAPSULE_LENGTH = 98

View File

@ -21,15 +21,18 @@ class NoEncryptingPower(PowerUpError):
class CryptoPower(object): class CryptoPower(object):
def __init__(self, power_ups=[], generate_keys_if_needed=False): def __init__(self, power_ups=None, generate_keys_if_needed=False):
self._power_ups = {} self._power_ups = {}
# TODO: The keys here will actually be IDs for looking up in a KeyStore. # TODO: The keys here will actually be IDs for looking up in a KeyStore.
self.public_keys = {} self.public_keys = {}
self.generate_keys = generate_keys_if_needed self.generate_keys = generate_keys_if_needed
if power_ups: if power_ups is not None:
for power_up in power_ups: for power_up in power_ups:
self.consume_power_up(power_up) self.consume_power_up(power_up)
else:
power_ups = [] # default
def consume_power_up(self, power_up): def consume_power_up(self, power_up):
if isinstance(power_up, CryptoPowerUp): if isinstance(power_up, CryptoPowerUp):
@ -47,14 +50,13 @@ class CryptoPower(object):
if power_up.confers_public_key: if power_up.confers_public_key:
# TODO: Make this an ID for later lookup on a KeyStore. # TODO: Make this an ID for later lookup on a KeyStore.
self.public_keys[ self.public_keys[power_up_class] = power_up_instance.public_key()
power_up_class] = power_up_instance.public_key()
def pubkey_sig_bytes(self): def pubkey_sig_bytes(self):
try: try:
# TODO: Turn this into an ID lookup on a KeyStore. # TODO: Turn this into an ID lookup on a KeyStore.
return self._power_ups[ pubkey_sig = self._power_ups[SigningPower].public_key()
SigningPower].pub_key return bytes(pubkey_sig)
except KeyError: except KeyError:
raise NoSigningPower raise NoSigningPower
@ -78,8 +80,7 @@ class CryptoPower(object):
except KeyError as e: except KeyError as e:
raise NoSigningPower(e) raise NoSigningPower(e)
msg_digest = b"".join(API.keccak_digest(m) for m in messages) msg_digest = b"".join(API.keccak_digest(m) for m in messages)
return sig_keypair.sign(msg_digest)
return Signature(sig_keypair.sign(msg_digest))
def decrypt(self, ciphertext): def decrypt(self, ciphertext):
try: try:
@ -118,8 +119,8 @@ class KeyPairBasedPower(CryptoPowerUp):
else: else:
# They didn't pass a keypair; we'll make one with the bytes (if any) # They didn't pass a keypair; we'll make one with the bytes (if any)
# they provided. # they provided.
self.keypair = self._keypair_class.load_key( self.keypair = self._keypair_class(
UmbralPublicKey(pubkey_bytes), UmbralPublicKey.from_bytes(pubkey_bytes),
generate_keys_if_needed=generate_keys_if_needed) generate_keys_if_needed=generate_keys_if_needed)
@ -127,15 +128,11 @@ class SigningPower(KeyPairBasedPower):
confers_public_key = True confers_public_key = True
_keypair_class = SigningKeypair _keypair_class = SigningKeypair
def sign(self, msghash): def sign(self, message):
""" """
Signs a hashed message and returns a signature. Signs a message message and returns a Signature.
:param msghash: The hashed message to sign
:return: Signature in bytes
""" """
return self.keypair.sign(msghash) return self.keypair.sign(message)
def public_key(self): def public_key(self):
return self.keypair.pubkey return self.keypair.pubkey

View File

@ -1,23 +1,20 @@
from nkms.crypto import api as API from nkms.crypto import api as API
from umbral.keys import UmbralPublicKey from umbral.keys import UmbralPublicKey
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature, encode_dss_signature
class Signature(bytes): class Signature(object):
""" """
The Signature object allows signatures to be made and verified. The Signature object allows signatures to be made and verified.
""" """
_EXPECTED_LENGTH = 70 _EXPECTED_LENGTH = 64 # With secp256k1 and BLAKE2b(64).
def __init__(self, sig_as_bytes: bytes): def __init__(self, r: int, s: int):
""" self.r = r
Initializes a Signature object. self.s = s
:param sig_as_bytes: Cryptography.io signature as bytes.
:return: Signature object
"""
self.sig_as_bytes = sig_as_bytes
def __repr__(self): def __repr__(self):
return "ECDSA Signature: {}".format(sig_as_bytes.decode()) return "ECDSA Signature: {}".format(bytes(self).hex()[:15])
def verify(self, message: bytes, pubkey: UmbralPublicKey) -> bool: def verify(self, message: bytes, pubkey: UmbralPublicKey) -> bool:
""" """
@ -28,11 +25,32 @@ class Signature(bytes):
:return: True if valid, False if invalid :return: True if valid, False if invalid
""" """
return API.ecdsa_verify(message, self.sig_as_bytes, pubkey) return API.ecdsa_verify(message, self._der_encoded_bytes(), pubkey)
@classmethod
def from_bytes(cls, signature_as_bytes, der_encoded=False):
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): def __bytes__(self):
""" # A couple quick assertions to be sure this is OK. Remove these at some point.
Implements the __bytes__ call for Signature to transform into a assert self.r.to_bytes(33, "big")[0] == 0, "Is 32 bytes enough?"
transportable mode. assert self.s.to_bytes(33, "big")[0] == 0, "Is 32 bytes enough?"
""" return self.r.to_bytes(32, "big") + self.s.to_bytes(32, "big")
return self.sig_as_bytes
def __len__(self):
return len(bytes(self))
def __add__(self, other):
return bytes(self) + other
__radd__ = __add__

View File

@ -47,7 +47,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) if isinstance(message_type, tuple):
message_meta = message_type
else:
try:
message_meta = message_type, message_type._EXPECTED_LENGTH
except AttributeError:
raise TypeError("No way to know the expected length. Either pass it as the second member of a tuple or set _EXPECTED_LENGTH on the class you're passing.")
return message_meta
class RepeatingBytestringSplitter(BytestringSplitter): class RepeatingBytestringSplitter(BytestringSplitter):

View File

@ -6,6 +6,7 @@ from nkms.crypto import api as API
from umbral.keys import UmbralPrivateKey, UmbralPublicKey from umbral.keys import UmbralPrivateKey, UmbralPublicKey
from umbral import umbral from umbral import umbral
from nkms.crypto.kits import MessageKit from nkms.crypto.kits import MessageKit
from nkms.crypto.signature import Signature
class Keypair(object): class Keypair(object):
@ -22,7 +23,7 @@ class Keypair(object):
:param generate_keys_if_needed: Generate keys or not? :param generate_keys_if_needed: Generate keys or not?
""" """
try: try:
self.pubkey = umbral_key.get_pub_key() self.pubkey = umbral_key.get_pubkey()
self.privkey = umbral_key self.privkey = umbral_key
except NotImplementedError: except NotImplementedError:
self.pubkey = umbral_key self.pubkey = umbral_key
@ -30,7 +31,7 @@ class Keypair(object):
# They didn't pass anything we recognize as a valid key. # They didn't pass anything we recognize as a valid key.
if generate_keys_if_needed: if generate_keys_if_needed:
self.privkey = UmbralPrivateKey.gen_key() self.privkey = UmbralPrivateKey.gen_key()
self.pubkey = self.priv_key.get_pub_key() self.pubkey = self.privkey.get_pub_key()
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.")
@ -79,8 +80,9 @@ class SigningKeypair(Keypair):
""" """
Signs a hashed message and returns a signature. Signs a hashed message and returns a signature.
:param msghash: The hashed message to sign :param message: The message to sign
:return: Signature in bytes :return: Signature in bytes
""" """
return API.ecdsa_sign(message, self.privkey) signature_der_bytes = API.ecdsa_sign(message, self.privkey)
return Signature.from_bytes(signature_der_bytes, der_encoded=True)

View File

@ -2,7 +2,7 @@ from kademlia.node import Node
from kademlia.protocol import KademliaProtocol from kademlia.protocol import KademliaProtocol
from kademlia.utils import digest from kademlia.utils import digest
from nkms.crypto.api import keccak_digest from nkms.crypto.api import keccak_digest
from nkms.crypto.constants import HASH_DIGEST_LENGTH, PUBLIC_KEY_LENGTH from nkms.crypto.constants import PUBLIC_KEY_LENGTH, KECCAK_DIGEST_LENGTH
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.network.constants import NODE_HAS_NO_STORAGE from nkms.network.constants import NODE_HAS_NO_STORAGE
@ -10,7 +10,7 @@ from nkms.network.node import NuCypherNode
from nkms.network.routing import NuCypherRoutingTable from nkms.network.routing import NuCypherRoutingTable
from umbral.keys import UmbralPublicKey from umbral.keys import UmbralPublicKey
dht_value_splitter = BytestringSplitter(Signature, (UmbralPublicKey, PUBLIC_KEY_LENGTH), (bytes, HASH_DIGEST_LENGTH)) dht_value_splitter = BytestringSplitter(Signature, (UmbralPublicKey, PUBLIC_KEY_LENGTH), (bytes, KECCAK_DIGEST_LENGTH))
class NuCypherHashProtocol(KademliaProtocol): class NuCypherHashProtocol(KademliaProtocol):

View File

@ -7,10 +7,12 @@ from npre.constants import UNKNOWN_KFRAG
from nkms.characters import Alice, Bob, Ursula from nkms.characters import Alice, Bob, Ursula
from nkms.crypto.api import keccak_digest from nkms.crypto.api import keccak_digest
from nkms.crypto.constants import NOT_SIGNED, HASH_DIGEST_LENGTH from nkms.crypto.constants import NOT_SIGNED, KECCAK_DIGEST_LENGTH, \
PUBLIC_KEY_LENGTH
from nkms.crypto.powers import SigningPower from nkms.crypto.powers import SigningPower
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 umbral.keys import UmbralPublicKey
class Contract(object): class Contract(object):
@ -19,7 +21,8 @@ class Contract(object):
""" """
_EXPECTED_LENGTH = 124 _EXPECTED_LENGTH = 124
def __init__(self, alice, hrac, expiration, deposit=None, ursula=None, kfrag=UNKNOWN_KFRAG, alices_signature=None): def __init__(self, alice, hrac, expiration, deposit=None, ursula=None,
kfrag=UNKNOWN_KFRAG, alices_signature=None):
""" """
:param deposit: Funds which will pay for the timeframe of this Contract (not the actual re-encryptions); :param deposit: Funds which will pay for the timeframe of this Contract (not the actual re-encryptions);
a portion will be locked for each Ursula that accepts. a portion will be locked for each Ursula that accepts.
@ -40,12 +43,17 @@ class Contract(object):
self.ursula = ursula self.ursula = ursula
def __bytes__(self): def __bytes__(self):
return bytes(self.alice.seal) + bytes(self.hrac) + self.expiration.isoformat().encode() + bytes(self.deposit) return bytes(self.alice.seal) + bytes(
self.hrac) + self.expiration.isoformat().encode() + bytes(
self.deposit)
@classmethod @classmethod
def from_bytes(cls, contract_as_bytes): def from_bytes(cls, contract_as_bytes):
contract_splitter = BytestringSplitter(PublicKey, (bytes, HASH_DIGEST_LENGTH), (bytes, 26)) contract_splitter = BytestringSplitter(
alice_pubkey_sig, hrac, expiration_bytes = contract_splitter(contract_as_bytes) (UmbralPublicKey, PUBLIC_KEY_LENGTH), (bytes, KECCAK_DIGEST_LENGTH),
(bytes, 26))
alice_pubkey_sig, hrac, expiration_bytes, deposit_bytes = contract_splitter(
contract_as_bytes, return_remainder=True)
expiration = maya.parse(expiration_bytes.decode()) expiration = maya.parse(expiration_bytes.decode())
alice = Alice.from_public_keys((SigningPower, alice_pubkey_sig)) alice = Alice.from_public_keys((SigningPower, alice_pubkey_sig))
return cls(alice=alice, hrac=hrac, expiration=expiration) return cls(alice=alice, hrac=hrac, expiration=expiration)
@ -59,7 +67,8 @@ class Contract(object):
""" """
Craft an offer to send to Ursula. Craft an offer to send to Ursula.
""" """
return self.alice.encrypt_for(self.ursula, self.payload())[0] # We don't need the signature separately. # We don't need the signature separately.
return self.alice.encrypt_for(self.ursula, self.payload())[0]
def payload(self): def payload(self):
# TODO: Ship the expiration again? Or some other way of alerting Ursula to recall her previous dialogue regarding this Contract. Update: We'll probably have her store the Contract by hrac. See #127. # TODO: Ship the expiration again? Or some other way of alerting Ursula to recall her previous dialogue regarding this Contract. Update: We'll probably have her store the Contract by hrac. See #127.
@ -82,10 +91,9 @@ class Policy(object):
Once Alice has secured agreement with n Ursulas to enact a Policy, she sends each a KFrag, Once Alice has secured agreement with n Ursulas to enact a Policy, she sends each a KFrag,
and generates a TreasureMap for the Policy, recording which Ursulas got a KFrag. and generates a TreasureMap for the Policy, recording which Ursulas got a KFrag.
""" """
_ursula = None
hashed_part = None
def __init__(self, alice, bob=None, kfrags=(UNKNOWN_KFRAG,), pfrag=None, uri=None, alices_signature=NOT_SIGNED): def __init__(self, alice, bob=None, kfrags=(UNKNOWN_KFRAG,), uri=None,
alices_signature=NOT_SIGNED):
""" """
:param kfrags: A list of KFrags to distribute per this Policy. :param kfrags: A list of KFrags to distribute per this Policy.
:param pfrag: The input ciphertext which Bob will give to Ursula to re-encrypt. :param pfrag: The input ciphertext which Bob will give to Ursula to re-encrypt.
@ -94,7 +102,6 @@ class Policy(object):
self.alice = alice self.alice = alice
self.bob = bob self.bob = bob
self.kfrags = kfrags self.kfrags = kfrags
self.pfrag = pfrag
self.uri = uri self.uri = uri
self.treasure_map = TreasureMap() self.treasure_map = TreasureMap()
self._accepted_contracts = {} self._accepted_contracts = {}
@ -125,13 +132,12 @@ class Policy(object):
@staticmethod @staticmethod
def from_alice(kfrags, def from_alice(kfrags,
pfrag,
alice, alice,
bob, bob,
uri, uri,
): ):
# TODO: What happened to Alice's signature - don't we include it here? # TODO: What happened to Alice's signature - don't we include it here?
policy = Policy(alice, bob, kfrags, pfrag, uri) policy = Policy(alice, bob, kfrags, uri)
return policy return policy
@ -143,7 +149,6 @@ class Policy(object):
@staticmethod @staticmethod
def hrac_for(alice, bob, uri): def hrac_for(alice, bob, uri):
""" """
The "hashed resource authentication code". The "hashed resource authentication code".
@ -172,9 +177,10 @@ class Policy(object):
return self.hash(bytes(self.alice.seal) + self.hrac()) return self.hash(bytes(self.alice.seal) + self.hrac())
def publish_treasure_map(self): def publish_treasure_map(self):
encrypted_treasure_map, signature_for_bob = self.alice.encrypt_for(self.bob, encrypted_treasure_map, signature_for_bob = self.alice.encrypt_for(
self.bob,
self.treasure_map.packed_payload()) self.treasure_map.packed_payload())
signature_for_ursula = self.alice.seal(self.hrac()) # TODO: Great use-case for Ciphertext class signature_for_ursula = self.alice.seal(self.hrac())
# In order to know this is safe to propagate, Ursula needs to see a signature, our public key, # In order to know this is safe to propagate, Ursula needs to see a signature, our public key,
# and, reasons explained in treasure_map_dht_key above, the uri_hash. # and, reasons explained in treasure_map_dht_key above, the uri_hash.
@ -188,7 +194,6 @@ class Policy(object):
return encrypted_treasure_map, dht_value, signature_for_bob, signature_for_ursula return encrypted_treasure_map, dht_value, signature_for_bob, signature_for_ursula
def enact(self, networky_stuff): def enact(self, networky_stuff):
for contract in self._accepted_contracts.values(): for contract in self._accepted_contracts.values():
policy_payload = contract.encrypt_payload_for_ursula() policy_payload = contract.encrypt_payload_for_ursula()
full_payload = self.alice.seal + msgpack.dumps(policy_payload) full_payload = self.alice.seal + msgpack.dumps(policy_payload)
@ -200,9 +205,11 @@ class Policy(object):
self.treasure_map.add_ursula(contract.ursula) self.treasure_map.add_ursula(contract.ursula)
def draw_up_contract(self, deposit, expiration): def draw_up_contract(self, deposit, expiration):
return Contract(self.alice, self.hrac(), expiration=expiration, deposit=deposit) return Contract(self.alice, self.hrac(), expiration=expiration,
deposit=deposit)
def find_ursulas(self, networky_stuff, deposit, expiration, num_ursulas=None): def find_ursulas(self, networky_stuff, deposit, expiration,
num_ursulas=None):
# TODO: This is a number mismatch - we need not one contract, but n contracts. # TODO: This is a number mismatch - we need not one contract, but n contracts.
""" """
:param networky_stuff: A compliant interface (maybe a Client instance) to be used to engage the DHT swarm. :param networky_stuff: A compliant interface (maybe a Client instance) to be used to engage the DHT swarm.
@ -258,7 +265,8 @@ class TreasureMap(object):
class WorkOrder(object): class WorkOrder(object):
def __init__(self, bob, kfrag_hrac, pfrags, receipt_bytes, receipt_signature, ursula_id=None): def __init__(self, bob, kfrag_hrac, pfrags, receipt_bytes,
receipt_signature, ursula_id=None):
self.bob = bob self.bob = bob
self.kfrag_hrac = kfrag_hrac self.kfrag_hrac = kfrag_hrac
self.pfrags = pfrags self.pfrags = pfrags
@ -267,12 +275,14 @@ class WorkOrder(object):
self.ursula_id = ursula_id # TODO: We may still need a more elegant system for ID'ing Ursula. See #136. self.ursula_id = ursula_id # TODO: We may still need a more elegant system for ID'ing Ursula. See #136.
def __repr__(self): def __repr__(self):
return "WorkOrder (pfrags: {}) {} for {}".format([binascii.hexlify(bytes(p))[:6] for p in self.pfrags], return "WorkOrder (pfrags: {}) {} for {}".format(
[binascii.hexlify(bytes(p))[:6] for p in self.pfrags],
binascii.hexlify(self.receipt_bytes)[:6], binascii.hexlify(self.receipt_bytes)[:6],
binascii.hexlify(self.ursula_id)[:6]) binascii.hexlify(self.ursula_id)[:6])
def __eq__(self, other): def __eq__(self, other):
return (self.receipt_bytes, self.receipt_signature) == (other.receipt_bytes, other.receipt_signature) return (self.receipt_bytes, self.receipt_signature) == (
other.receipt_bytes, other.receipt_signature)
def __len__(self): def __len__(self):
return len(self.pfrags) return len(self.pfrags)

View File

@ -2,11 +2,14 @@ import datetime
from nkms.characters import Ursula from nkms.characters import Ursula
from nkms.crypto.api import keccak_digest from nkms.crypto.api import keccak_digest
from nkms.crypto.constants import PUBLIC_KEY_LENGTH
from nkms.crypto.powers import SigningPower, EncryptingPower from nkms.crypto.powers import SigningPower, EncryptingPower
from nkms.crypto.utils import BytestringSplitter from nkms.crypto.utils import BytestringSplitter
from tests.utilities import MockNetworkyStuff from tests.utilities import MockNetworkyStuff
from apistar.test import TestClient from apistar.test import TestClient
from umbral.keys import UmbralPublicKey
def test_grant(alice, bob, ursulas): def test_grant(alice, bob, ursulas):
networky_stuff = MockNetworkyStuff(ursulas) networky_stuff = MockNetworkyStuff(ursulas)
@ -30,6 +33,7 @@ def test_grant(alice, bob, ursulas):
def test_alice_can_get_ursulas_keys_via_rest(alice, ursulas): def test_alice_can_get_ursulas_keys_via_rest(alice, ursulas):
mock_client = TestClient(ursulas[0].rest_app) mock_client = TestClient(ursulas[0].rest_app)
response = mock_client.get('http://localhost/public_keys') response = mock_client.get('http://localhost/public_keys')
signing_key_bytes, encrypting_key_bytes = BytestringSplitter(PublicKey)(response.content, return_remainder=True) splitter = BytestringSplitter((UmbralPublicKey, PUBLIC_KEY_LENGTH))
signing_key_bytes, encrypting_key_bytes = splitter(response.content, return_remainder=True)
stranger_ursula_from_public_keys = Ursula.from_public_keys((SigningPower, signing_key_bytes), (EncryptingPower, encrypting_key_bytes)) stranger_ursula_from_public_keys = Ursula.from_public_keys((SigningPower, signing_key_bytes), (EncryptingPower, encrypting_key_bytes))
assert stranger_ursula_from_public_keys == ursulas[0] assert stranger_ursula_from_public_keys == ursulas[0]