Merge pull request #10 from jMyles/rm-crypto

Rm crypto
pull/161/head
Tux 2018-02-12 13:39:17 -08:00 committed by GitHub
commit 9c7d469270
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 155 additions and 79 deletions

View File

@ -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):
"""

View File

@ -1,6 +1,7 @@
BLAKE2B_DIGEST_LENGTH = 64
KECCAK_DIGEST_LENGTH = 32
UNKNOWN_KFRAG = 404
NOT_SIGNED = 445
NO_DECRYPTION_PERFORMED = 455

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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.
"""

View File

@ -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

View File

@ -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):

View File

@ -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())