mirror of https://github.com/nucypher/nucypher.git
commit
9c7d469270
|
@ -178,6 +178,7 @@ class Character(object):
|
|||
message_kit = self._crypto_power.encrypt_for(
|
||||
actor.public_key(EncryptingPower), plaintext)
|
||||
signature = self.seal(message_kit)
|
||||
message_kit.alice_pubkey = self.public_key(SigningPower)
|
||||
else:
|
||||
signature = NOT_SIGNED
|
||||
message_kit = self._crypto_power.encrypt_for(
|
||||
|
@ -214,6 +215,7 @@ class Character(object):
|
|||
cleartext_with_sig = self._crypto_power.decrypt(message_kit)
|
||||
signature, cleartext = BytestringSplitter(Signature)(cleartext_with_sig,
|
||||
return_remainder=True)
|
||||
message_kit.signature = signature # TODO: Obviously this is the wrong way to do this. Let's make signature a property.
|
||||
else:
|
||||
raise ValueError(
|
||||
"Can't look for a signature on the cleartext if we're not \
|
||||
|
@ -235,10 +237,7 @@ class Character(object):
|
|||
|
||||
def public_key(self, key_class):
|
||||
# TODO: Does it make sense to have a specialized exception here? Probably.
|
||||
try:
|
||||
return self._crypto_power.public_keys[key_class]
|
||||
except KeyError:
|
||||
raise
|
||||
return self._crypto_power.public_keys[key_class]
|
||||
|
||||
|
||||
class Alice(Character):
|
||||
|
@ -601,26 +600,31 @@ class Ursula(Character):
|
|||
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)
|
||||
#
|
||||
# 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 = policy_payload_splitter(policy_payload)[0]
|
||||
kfrag = KFrag.from_bytes(cleartext)
|
||||
|
||||
# TODO: Query stored Contract and reconstitute
|
||||
contract_details = self._contracts[hrac]
|
||||
stored_alice_pubkey_sig = contract_details.pop("alice_pubkey_sig")
|
||||
|
||||
if stored_alice_pubkey_sig != alice_pubkey_sig:
|
||||
if stored_alice_pubkey_sig != alice.seal:
|
||||
raise Alice.SuspiciousActivity
|
||||
|
||||
contract = Contract(alice=alice, hrac=hrac,
|
||||
kfrag=kfrag, **contract_details)
|
||||
|
||||
try:
|
||||
self.keystore.add_kfrag(hrac, contract.kfrag, alices_signature)
|
||||
self.keystore.add_policy_contract(
|
||||
expiration=contract_details['expiration'].datetime(),
|
||||
deposit=contract_details['deposit'], hrac=hrac, kfrag=kfrag,
|
||||
alice_pubkey_sig=alice.seal,
|
||||
alice_signature=policy_message_kit.signature)
|
||||
except IntegrityError:
|
||||
raise
|
||||
# Do something appropriately RESTful (ie, 4xx).
|
||||
|
@ -670,9 +674,6 @@ class Seal(object):
|
|||
def __call__(self, *args, **kwargs):
|
||||
return self.character._crypto_power.sign(*args, **kwargs)
|
||||
|
||||
def _as_tuple(self):
|
||||
return self.character._crypto_power.pubkey_sig_tuple()
|
||||
|
||||
def __iter__(seal):
|
||||
yield from seal._as_tuple()
|
||||
|
||||
|
@ -680,7 +681,7 @@ class Seal(object):
|
|||
return self.character._crypto_power.pubkey_sig_bytes()
|
||||
|
||||
def __eq__(self, other):
|
||||
return other == self._as_tuple() or other == bytes(self)
|
||||
return other == bytes(self)
|
||||
|
||||
def __add__(self, other):
|
||||
return bytes(self) + other
|
||||
|
@ -694,6 +695,14 @@ class Seal(object):
|
|||
def without_metabytes(self):
|
||||
return self.character._crypto_power.pubkey_sig_bytes().without_metabytes()
|
||||
|
||||
def fingerprint(self):
|
||||
"""
|
||||
Hashes the key using keccak-256 and returns the hexdigest in bytes.
|
||||
|
||||
:return: Hexdigest fingerprint of key (keccak-256) in bytes
|
||||
"""
|
||||
return keccak_digest(bytes(self)).hex().encode()
|
||||
|
||||
|
||||
class StrangerSeal(Seal):
|
||||
"""
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
BLAKE2B_DIGEST_LENGTH = 64
|
||||
KECCAK_DIGEST_LENGTH = 32
|
||||
|
||||
UNKNOWN_KFRAG = 404
|
||||
NOT_SIGNED = 445
|
||||
NO_DECRYPTION_PERFORMED = 455
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class CryptoKit:
|
|||
class MessageKit(CryptoKit):
|
||||
splitter = capsule_splitter + key_splitter
|
||||
|
||||
def __init__(self, capsule, alice_pubkey, ciphertext):
|
||||
def __init__(self, capsule, alice_pubkey=None, ciphertext=None):
|
||||
self.ciphertext = ciphertext
|
||||
self.capsule = capsule
|
||||
self.alice_pubkey = alice_pubkey
|
||||
|
|
|
@ -89,10 +89,9 @@ class CryptoPower(object):
|
|||
except KeyError:
|
||||
raise NoEncryptingPower
|
||||
|
||||
def encrypt_for(self, pubkey, plaintext):
|
||||
ciphertext, capsule = umbral.umbral.encrypt(pubkey, plaintext)
|
||||
return MessageKit(ciphertext=ciphertext, capsule=capsule,
|
||||
alice_pubkey=pubkey)
|
||||
def encrypt_for(self, enc_pubkey, plaintext):
|
||||
ciphertext, capsule = umbral.umbral.encrypt(enc_pubkey, plaintext)
|
||||
return MessageKit(ciphertext=ciphertext, capsule=capsule)
|
||||
|
||||
|
||||
class CryptoPowerUp(object):
|
||||
|
|
|
@ -56,3 +56,7 @@ class Signature(object):
|
|||
|
||||
def __radd__(self, other):
|
||||
return other + bytes(self)
|
||||
|
||||
def __eq__(self, other):
|
||||
# TODO: Consider constant time
|
||||
return bytes(self) == bytes(other) or self._der_encoded_bytes() == other
|
||||
|
|
|
@ -31,24 +31,24 @@ class PolicyContract(Base):
|
|||
expiration = Column(DateTime)
|
||||
deposit = Column(LargeBinary)
|
||||
hrac = Column(LargeBinary, unique=True)
|
||||
key_frag = Column(LargeBinary, unique=True)
|
||||
k_frag = Column(LargeBinary, unique=True)
|
||||
alice_pubkey_sig_id = Column(Integer, ForeignKey('keys.id'))
|
||||
alice_pubkey_enc_id = Column(Integer, ForeignKey('keys.id'))
|
||||
bob_pubkey_sig_id = Column(Integer, ForeignKey('keys.id'))
|
||||
# alice_pubkey_enc_id = Column(Integer, ForeignKey('keys.id'))
|
||||
# bob_pubkey_sig_id = Column(Integer, ForeignKey('keys.id'))
|
||||
alice_signature = Column(LargeBinary, unique=True)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
def __init__(self, expiration, deposit, hrac,
|
||||
key_frag, alice_pubkey_sig_id,
|
||||
alice_pubkey_enc_id, bob_pubkey_sig_id,
|
||||
alice_signature, policy):
|
||||
k_frag, alice_pubkey_sig_id,
|
||||
# alice_pubkey_enc_id, bob_pubkey_sig_id,
|
||||
alice_signature):
|
||||
self.expiration = expiration
|
||||
self.deposit = deposit
|
||||
self.hrac = hrac
|
||||
self.key_frag = key_frag
|
||||
self.k_frag = k_frag
|
||||
self.alice_pubkey_sig_id = alice_pubkey_sig_id
|
||||
self.alice_pubkey_enc_id = alice_pubkey_enc_id
|
||||
self.bob_pubkey_sig_id = bob_pubkey_sig_id
|
||||
# self.alice_pubkey_enc_id = alice_pubkey_enc_id
|
||||
# self.bob_pubkey_sig_id = bob_pubkey_sig_id
|
||||
self.alice_signature = alice_signature
|
||||
|
||||
|
||||
|
|
|
@ -32,18 +32,15 @@ class KeyStore(object):
|
|||
"""
|
||||
self.session = sessionmaker(bind=sqlalchemy_engine)()
|
||||
|
||||
def add_key(self,
|
||||
keypair: Union[keypairs.EncryptingKeypair,
|
||||
keypairs.SigningKeypair]) -> Key:
|
||||
def add_key(self, key, is_signing=True) -> Key:
|
||||
|
||||
"""
|
||||
:param keypair: Keypair object to store in the keystore.
|
||||
|
||||
:return: The newly added key object.
|
||||
"""
|
||||
fingerprint = keypair.get_fingerprint()
|
||||
key_data = keypair.serialize_pubkey(as_b64=True)
|
||||
is_signing = isinstance(keypair, keypairs.SigningKeypair)
|
||||
fingerprint = key.fingerprint()
|
||||
key_data = bytes(key)
|
||||
|
||||
new_key = Key(fingerprint, key_data, is_signing)
|
||||
|
||||
|
@ -52,57 +49,86 @@ class KeyStore(object):
|
|||
return new_key
|
||||
|
||||
|
||||
def get_key(self, fingerprint: bytes) -> Union[keypairs.EncryptingKeypair,
|
||||
keypairs.SigningKeypair]:
|
||||
"""
|
||||
Returns a key from the KeyStore.
|
||||
def get_key(self, fingerprint: bytes) -> Union[keypairs.EncryptingKeypair,
|
||||
keypairs.SigningKeypair]:
|
||||
"""
|
||||
Returns a key from the KeyStore.
|
||||
|
||||
:param fingerprint: Fingerprint, in bytes, of key to return
|
||||
:param fingerprint: Fingerprint, in bytes, of key to return
|
||||
|
||||
:return: Keypair of the returned key.
|
||||
"""
|
||||
key = self.session.query(Key).filter_by(fingerprint=fingerprint).first()
|
||||
if not key:
|
||||
raise NotFound(
|
||||
"No key with fingerprint {} found.".format(fingerprint))
|
||||
if key.is_signing:
|
||||
pubkey = UmbralPublicKey(key.key_data, as_b64=True)
|
||||
return keypairs.SigningKeypair(pubkey)
|
||||
:return: Keypair of the returned key.
|
||||
"""
|
||||
key = self.session.query(Key).filter_by(fingerprint=fingerprint).first()
|
||||
if not key:
|
||||
raise NotFound(
|
||||
"No key with fingerprint {} found.".format(fingerprint))
|
||||
if key.is_signing:
|
||||
pubkey = UmbralPublicKey(key.key_data, as_b64=True)
|
||||
return keypairs.SigningKeypair(pubkey)
|
||||
|
||||
|
||||
def del_key(self, fingerprint: bytes):
|
||||
"""
|
||||
Deletes a key from the KeyStore.
|
||||
def del_key(self, fingerprint: bytes):
|
||||
"""
|
||||
Deletes a key from the KeyStore.
|
||||
|
||||
:param fingerprint: Fingerprint of key to delete
|
||||
"""
|
||||
self.session.query(Key).filter_by(fingerprint=fingerprint).delete()
|
||||
self.session.commit()
|
||||
:param fingerprint: Fingerprint of key to delete
|
||||
"""
|
||||
self.session.query(Key).filter_by(fingerprint=fingerprint).delete()
|
||||
self.session.commit()
|
||||
|
||||
def add_policy_contract(self, expiration, deposit, hrac, kfrag,
|
||||
alice_pubkey_sig, # alice_pubkey_enc,
|
||||
alice_signature) -> PolicyContract:
|
||||
"""
|
||||
Creates a PolicyContract to the Keystore.
|
||||
|
||||
def add_policy_contract(self, expiration, deposit, hrac,
|
||||
alice_pubkey_sig, alice_pubkey_enc,
|
||||
bob_pubkey_sig, alice_signature) -> PolicyContract:
|
||||
"""
|
||||
Creates a PolicyContract to the Keystore.
|
||||
:return: The newly added PolicyContract object
|
||||
"""
|
||||
# TODO: This can be optimized to one commit/write.
|
||||
alice_pubkey_sig = self.add_key(alice_pubkey_sig, is_signing=True)
|
||||
# alice_pubkey_enc = self.add_key(alice_pubkey_enc)
|
||||
# bob_pubkey_sig = self.add_key(bob_pubkey_sig)
|
||||
|
||||
:return: The newly added PolicyContract object
|
||||
"""
|
||||
# TODO: This can be optimized to one commit/write.
|
||||
alice_pubkey_sig = self.add_key(alice_pubkey_sig)
|
||||
alice_pubkey_enc = self.add_key(alice_pubkey_enc)
|
||||
bob_pubkey_sig = self.add_key(bob_pubkey_sig)
|
||||
new_policy_contract = PolicyContract(
|
||||
expiration, deposit, hrac, bytes(kfrag), alice_pubkey_sig.id,
|
||||
bytes(alice_signature), # bob_pubkey_sig.id
|
||||
)
|
||||
|
||||
new_policy_contract = PolicyContract(
|
||||
expiration, deposit, hrac, alice_pubkey_sig.id,
|
||||
alice_pubkey_enc.id, bob_pubkey_sig.id, alice_signature
|
||||
)
|
||||
self.session.add(new_policy_contract)
|
||||
self.session.commit()
|
||||
|
||||
self.session.add(new_policy_contract)
|
||||
return new_policy_contract
|
||||
|
||||
return new_policy_contract
|
||||
def get_policy_contract(self, hrac: bytes) -> PolicyContract:
|
||||
"""
|
||||
Returns the PolicyContract by its HRAC.
|
||||
|
||||
def get_workorders(self, hrac: bytes) -> Workorder:
|
||||
:return: The PolicyContract object
|
||||
"""
|
||||
policy_contract = self.session.query(PolicyContract).filter_by(hrac=hrac).first()
|
||||
if not policy_contract:
|
||||
raise NotFound("No PolicyContract with {} HRAC found.".format(hrac))
|
||||
return policy_contract
|
||||
|
||||
def del_policy_contract(self, hrac: bytes):
|
||||
"""
|
||||
Deletes a PolicyContract from the Keystore.
|
||||
"""
|
||||
self.session.query(PolicyContract).filter_by(hrac=hrac).delete()
|
||||
self.session.commit()
|
||||
|
||||
def add_workorder(self, bob_pubkey_sig, bob_signature, hrac) -> Workorder:
|
||||
"""
|
||||
Adds a Workorder to the keystore.
|
||||
"""
|
||||
bob_pubkey_sig = self.add_key(bob_pubkey_sig)
|
||||
new_workorder = Workorder(bob_pubkey_sig.id, bob_signature, hrac)
|
||||
|
||||
self.session.add(new_workorder)
|
||||
self.session.commit()
|
||||
return new_workorder
|
||||
|
||||
def get_workorder(self, hrac: bytes) -> Workorder:
|
||||
"""
|
||||
Returns a list of Workorders by HRAC.
|
||||
"""
|
||||
|
|
|
@ -7,7 +7,7 @@ import msgpack
|
|||
from nkms.characters import Alice, Bob, Ursula
|
||||
from nkms.crypto.api import keccak_digest
|
||||
from nkms.crypto.constants import NOT_SIGNED, KECCAK_DIGEST_LENGTH, \
|
||||
PUBLIC_KEY_LENGTH
|
||||
PUBLIC_KEY_LENGTH, UNKNOWN_KFRAG
|
||||
from nkms.crypto.powers import SigningPower
|
||||
from nkms.crypto.signature import Signature
|
||||
from nkms.crypto.splitters import key_splitter
|
||||
|
@ -194,10 +194,10 @@ class Policy(object):
|
|||
|
||||
def enact(self, networky_stuff):
|
||||
for contract in self._accepted_contracts.values():
|
||||
full_payload = contract.encrypt_payload_for_ursula()
|
||||
policy_message_kit = contract.encrypt_payload_for_ursula()
|
||||
response = networky_stuff.enact_policy(contract.ursula,
|
||||
self.hrac(),
|
||||
bytes(full_payload))
|
||||
bytes(policy_message_kit))
|
||||
# TODO: Parse response for confirmation.
|
||||
response
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ from nkms.crypto.constants import PUBLIC_KEY_LENGTH
|
|||
from nkms.crypto.powers import SigningPower, EncryptingPower
|
||||
from nkms.crypto.utils import BytestringSplitter
|
||||
from tests.utilities import MockNetworkyStuff
|
||||
from umbral.fragments import KFrag
|
||||
from umbral.keys import UmbralPublicKey
|
||||
|
||||
|
||||
|
@ -25,10 +26,19 @@ def test_grant(alice, bob, ursulas):
|
|||
# Let's look at the first Ursula.
|
||||
ursula = list(policy._accepted_contracts.values())[0].ursula
|
||||
|
||||
# Get the kfrag, based in the hrac.
|
||||
# Get the Policy from Ursula's datastore, looking up by hrac.
|
||||
proper_hrac = keccak_digest(bytes(alice.seal) + bytes(bob.seal) + uri)
|
||||
kfrag_that_was_set = ursula.keystore.get_kfrag(proper_hrac)
|
||||
assert kfrag_that_was_set
|
||||
retrieved_policy = ursula.keystore.get_policy_contract(proper_hrac)
|
||||
|
||||
# TODO: Make this a legit KFrag, not bytes.
|
||||
retrieved_k_frag = KFrag.from_bytes(retrieved_policy.k_frag)
|
||||
|
||||
# TODO: Implement KFrag.__eq__
|
||||
found = False
|
||||
for k_frag in policy.kfrags:
|
||||
if bytes(k_frag) == bytes(retrieved_k_frag):
|
||||
found = True
|
||||
assert found
|
||||
|
||||
|
||||
def test_alice_can_get_ursulas_keys_via_rest(alice, ursulas):
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
from nkms.crypto.api import ecdsa_sign
|
||||
from umbral.keys import UmbralPrivateKey
|
||||
from nkms.crypto.signature import Signature
|
||||
|
||||
|
||||
def test_signature_can_verify():
|
||||
privkey = UmbralPrivateKey.gen_key()
|
||||
message = b"attack at dawn"
|
||||
der_sig_bytes = ecdsa_sign(message, privkey)
|
||||
signature = Signature.from_bytes(der_sig_bytes, der_encoded=True)
|
||||
assert signature.verify(message, privkey.get_pubkey())
|
||||
|
||||
|
||||
def test_signature_rs_serialization():
|
||||
privkey = UmbralPrivateKey.gen_key()
|
||||
message = b"attack at dawn"
|
||||
der_sig_bytes = ecdsa_sign(message, privkey)
|
||||
|
||||
signature_from_der = Signature.from_bytes(der_sig_bytes, der_encoded=True)
|
||||
rs_sig_bytes = bytes(signature_from_der)
|
||||
assert len(rs_sig_bytes) == 64
|
||||
|
||||
signature_from_rs = Signature.from_bytes(rs_sig_bytes, der_encoded=False)
|
||||
|
||||
assert signature_from_rs == signature_from_der
|
||||
assert signature_from_rs == der_sig_bytes
|
||||
assert signature_from_rs.verify(message, privkey.get_pubkey())
|
Loading…
Reference in New Issue