mirror of https://github.com/nucypher/nucypher.git
Merge pull request #278 from jMyles/umbral-compat
Uses Alice's Stamp as a signer for KFrags; Bob and Ursula validate them as such.pull/276/head^2
commit
19b0f71555
2
Pipfile
2
Pipfile
|
@ -21,7 +21,7 @@ apistar = "*"
|
||||||
mypy = "*"
|
mypy = "*"
|
||||||
pytest-mypy = "*"
|
pytest-mypy = "*"
|
||||||
maya = "*"
|
maya = "*"
|
||||||
pyumbral = {git = "https://github.com/nucypher/pyumbral.git"}
|
pyumbral = {git = "https://github.com/nucypher/pyumbral.git", ref = "kfrag-signing"}
|
||||||
requests = "*"
|
requests = "*"
|
||||||
hendrix = {git = "https://github.com/hendrix/hendrix", ref = "tags/3.0.0rc1"}
|
hendrix = {git = "https://github.com/hendrix/hendrix", ref = "tags/3.0.0rc1"}
|
||||||
constantSorrow = {git = "https://github.com/nucypher/constantSorrow.git", ref = "nucypher-depend"}
|
constantSorrow = {git = "https://github.com/nucypher/constantSorrow.git", ref = "nucypher-depend"}
|
||||||
|
|
|
@ -19,7 +19,7 @@ from nucypher.crypto.api import secure_random, keccak_digest, encrypt_and_sign
|
||||||
from nucypher.crypto.constants import PUBLIC_KEY_LENGTH
|
from nucypher.crypto.constants import PUBLIC_KEY_LENGTH
|
||||||
from nucypher.crypto.kits import UmbralMessageKit
|
from nucypher.crypto.kits import UmbralMessageKit
|
||||||
from nucypher.crypto.powers import CryptoPower, SigningPower, EncryptingPower, DelegatingPower, NoSigningPower
|
from nucypher.crypto.powers import CryptoPower, SigningPower, EncryptingPower, DelegatingPower, NoSigningPower
|
||||||
from nucypher.crypto.signature import Signature, signature_splitter, SignatureStamp, StrangerStamp
|
from nucypher.crypto.signing import Signature, signature_splitter, SignatureStamp, StrangerStamp
|
||||||
from nucypher.network import blockchain_client
|
from nucypher.network import blockchain_client
|
||||||
from nucypher.network.protocols import dht_value_splitter, dht_with_hrac_splitter
|
from nucypher.network.protocols import dht_value_splitter, dht_with_hrac_splitter
|
||||||
from nucypher.network.server import NucypherDHTServer, NucypherSeedOnlyDHTServer, ProxyRESTServer
|
from nucypher.network.server import NucypherDHTServer, NucypherSeedOnlyDHTServer, ProxyRESTServer
|
||||||
|
@ -36,9 +36,9 @@ class Character(object):
|
||||||
|
|
||||||
address = "This is a fake address." # TODO: #192
|
address = "This is a fake address." # TODO: #192
|
||||||
|
|
||||||
def __init__(self, attach_server=True, crypto_power: CryptoPower=None,
|
def __init__(self, attach_server=True, crypto_power: CryptoPower = None,
|
||||||
crypto_power_ups=None, is_me=True, network_middleware=None,
|
crypto_power_ups=None, is_me=True, network_middleware=None,
|
||||||
config: "NucypherConfig"=None) -> None:
|
config: "NucypherConfig" = None) -> None:
|
||||||
"""
|
"""
|
||||||
:param attach_server: Whether to attach a Server when this Character is
|
:param attach_server: Whether to attach a Server when this Character is
|
||||||
born.
|
born.
|
||||||
|
@ -79,7 +79,8 @@ class Character(object):
|
||||||
if is_me:
|
if is_me:
|
||||||
self.network_middleware = network_middleware or NetworkyStuff()
|
self.network_middleware = network_middleware or NetworkyStuff()
|
||||||
try:
|
try:
|
||||||
self._stamp = SignatureStamp(self._crypto_power.power_ups(SigningPower).keypair)
|
signing_power = self._crypto_power.power_ups(SigningPower)
|
||||||
|
self._stamp = signing_power.get_signature_stamp()
|
||||||
except NoSigningPower:
|
except NoSigningPower:
|
||||||
self._stamp = constants.NO_SIGNING_POWER
|
self._stamp = constants.NO_SIGNING_POWER
|
||||||
|
|
||||||
|
@ -87,8 +88,9 @@ class Character(object):
|
||||||
self.attach_server()
|
self.attach_server()
|
||||||
else:
|
else:
|
||||||
if network_middleware is not None:
|
if network_middleware is not None:
|
||||||
raise TypeError("Can't attach network middleware to a Character who isn't me. What are you even trying to do?")
|
raise TypeError(
|
||||||
self._stamp = StrangerStamp(self._crypto_power.power_ups(SigningPower).keypair)
|
"Can't attach network middleware to a Character who isn't me. What are you even trying to do?")
|
||||||
|
self._stamp = StrangerStamp(self.public_key(SigningPower))
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return bytes(self.stamp) == bytes(other.stamp)
|
return bytes(self.stamp) == bytes(other.stamp)
|
||||||
|
@ -159,7 +161,7 @@ class Character(object):
|
||||||
def encrypt_for(self,
|
def encrypt_for(self,
|
||||||
recipient: "Character",
|
recipient: "Character",
|
||||||
plaintext: bytes,
|
plaintext: bytes,
|
||||||
sign: bool=True,
|
sign: bool = True,
|
||||||
sign_plaintext=True,
|
sign_plaintext=True,
|
||||||
) -> tuple:
|
) -> tuple:
|
||||||
"""
|
"""
|
||||||
|
@ -187,8 +189,9 @@ class Character(object):
|
||||||
def verify_from(self,
|
def verify_from(self,
|
||||||
actor_whom_sender_claims_to_be: "Character",
|
actor_whom_sender_claims_to_be: "Character",
|
||||||
message_kit: Union[UmbralMessageKit, bytes],
|
message_kit: Union[UmbralMessageKit, bytes],
|
||||||
signature: Signature=None,
|
signature: Signature = None,
|
||||||
decrypt=False,
|
decrypt=False,
|
||||||
|
delegator_signing_key: UmbralPublicKey = None,
|
||||||
) -> tuple:
|
) -> tuple:
|
||||||
"""
|
"""
|
||||||
Inverse of encrypt_for.
|
Inverse of encrypt_for.
|
||||||
|
@ -196,11 +199,14 @@ class Character(object):
|
||||||
:param actor_that_sender_claims_to_be: A Character instance representing
|
:param actor_that_sender_claims_to_be: A Character instance representing
|
||||||
the actor whom the sender claims to be. We check the public key
|
the actor whom the sender claims to be. We check the public key
|
||||||
owned by this Character instance to verify.
|
owned by this Character instance to verify.
|
||||||
:param messages: The messages to be verified.
|
:param message_kit: the message to be (perhaps decrypted and) verified.
|
||||||
|
:param signature: The signature to check.
|
||||||
: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
|
:param delegator_signing_key: A signing key from the original delegator.
|
||||||
on the cleartext. Otherwise, we presume that the ciphertext is what
|
This is used only when decrypting a MessageKit with an activated Capsule
|
||||||
is signed.
|
to check that the KFrag used to create each attached CFrag is the
|
||||||
|
authentic KFrag initially created by the delegator.
|
||||||
|
|
||||||
:return: Whether or not the signature is valid, the decrypted plaintext
|
:return: Whether or not the signature is valid, the decrypted plaintext
|
||||||
or NO_DECRYPTION_PERFORMED
|
or NO_DECRYPTION_PERFORMED
|
||||||
"""
|
"""
|
||||||
|
@ -215,7 +221,7 @@ class Character(object):
|
||||||
|
|
||||||
if decrypt:
|
if decrypt:
|
||||||
# We are decrypting the message; let's do that first and see what the sig header says.
|
# We are decrypting the message; let's do that first and see what the sig header says.
|
||||||
cleartext_with_sig_header = self.decrypt(message_kit)
|
cleartext_with_sig_header = self.decrypt(message_kit, verifying_key=delegator_signing_key)
|
||||||
sig_header, cleartext = default_constant_splitter(cleartext_with_sig_header, return_remainder=True)
|
sig_header, cleartext = default_constant_splitter(cleartext_with_sig_header, return_remainder=True)
|
||||||
if sig_header == constants.SIGNATURE_IS_ON_CIPHERTEXT:
|
if sig_header == constants.SIGNATURE_IS_ON_CIPHERTEXT:
|
||||||
# THe ciphertext is what is signed - note that for later.
|
# THe ciphertext is what is signed - note that for later.
|
||||||
|
@ -254,8 +260,8 @@ class Character(object):
|
||||||
If they don't have the correct Power, the appropriate PowerUpError is raised.
|
If they don't have the correct Power, the appropriate PowerUpError is raised.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decrypt(self, message_kit):
|
def decrypt(self, message_kit, verifying_key: UmbralPublicKey = None):
|
||||||
return self._crypto_power.power_ups(EncryptingPower).decrypt(message_kit)
|
return self._crypto_power.power_ups(EncryptingPower).decrypt(message_kit, verifying_key)
|
||||||
|
|
||||||
def sign(self, message):
|
def sign(self, message):
|
||||||
return self._crypto_power.power_ups(SigningPower).sign(message)
|
return self._crypto_power.power_ups(SigningPower).sign(message)
|
||||||
|
@ -302,7 +308,8 @@ class Character(object):
|
||||||
powers_and_keys=({SigningPower: pubkey})
|
powers_and_keys=({SigningPower: pubkey})
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
message = "Suspicious Activity: Discovered node with bad signature: {}. Propagated by: {}:{}".format(node_meta, address, port)
|
message = "Suspicious Activity: Discovered node with bad signature: {}. Propagated by: {}:{}".format(
|
||||||
|
node_meta, address, port)
|
||||||
self.log.warn(message)
|
self.log.warn(message)
|
||||||
return new_nodes
|
return new_nodes
|
||||||
|
|
||||||
|
@ -335,7 +342,8 @@ class Alice(Character, PolicyAuthor):
|
||||||
:param n: Total number of kfrags to generate
|
:param n: Total number of kfrags to generate
|
||||||
"""
|
"""
|
||||||
bob_pubkey_enc = bob.public_key(EncryptingPower)
|
bob_pubkey_enc = bob.public_key(EncryptingPower)
|
||||||
return self._crypto_power.power_ups(DelegatingPower).generate_kfrags(bob_pubkey_enc, label, m, n)
|
delegating_power = self._crypto_power.power_ups(DelegatingPower)
|
||||||
|
return delegating_power.generate_kfrags(bob_pubkey_enc, self.stamp, label, m, n)
|
||||||
|
|
||||||
def create_policy(self, bob: "Bob", label: bytes, m: int, n: int):
|
def create_policy(self, bob: "Bob", label: bytes, m: int, n: int):
|
||||||
"""
|
"""
|
||||||
|
@ -654,8 +662,8 @@ class Ursula(Character, ProxyRESTServer):
|
||||||
|
|
||||||
def interface_information(self):
|
def interface_information(self):
|
||||||
return msgpack.dumps((self.ip_address,
|
return msgpack.dumps((self.ip_address,
|
||||||
self.dht_port,
|
self.dht_port,
|
||||||
self.rest_port))
|
self.rest_port))
|
||||||
|
|
||||||
def interface_info_with_metadata(self):
|
def interface_info_with_metadata(self):
|
||||||
interface_info = self.interface_information()
|
interface_info = self.interface_information()
|
||||||
|
|
|
@ -117,7 +117,7 @@ class DerivedKeyBasedPower(CryptoPowerUp):
|
||||||
class SigningPower(KeyPairBasedPower):
|
class SigningPower(KeyPairBasedPower):
|
||||||
_keypair_class = SigningKeypair
|
_keypair_class = SigningKeypair
|
||||||
not_found_error = NoSigningPower
|
not_found_error = NoSigningPower
|
||||||
provides = ("sign", "generate_self_signed_cert")
|
provides = ("sign", "generate_self_signed_cert", "get_signature_stamp")
|
||||||
|
|
||||||
|
|
||||||
class EncryptingPower(KeyPairBasedPower):
|
class EncryptingPower(KeyPairBasedPower):
|
||||||
|
@ -131,7 +131,7 @@ class DelegatingPower(DerivedKeyBasedPower):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.umbral_keying_material = UmbralKeyingMaterial()
|
self.umbral_keying_material = UmbralKeyingMaterial()
|
||||||
|
|
||||||
def generate_kfrags(self, bob_pubkey_enc, label, m, n) -> Union[UmbralPublicKey, List]:
|
def generate_kfrags(self, bob_pubkey_enc, signer, label, m, n) -> Union[UmbralPublicKey, List]:
|
||||||
"""
|
"""
|
||||||
Generates re-encryption key frags ("KFrags") and returns them.
|
Generates re-encryption key frags ("KFrags") and returns them.
|
||||||
|
|
||||||
|
@ -144,5 +144,5 @@ class DelegatingPower(DerivedKeyBasedPower):
|
||||||
# TODO: salt? #265
|
# TODO: salt? #265
|
||||||
|
|
||||||
__private_key = self.umbral_keying_material.derive_privkey_by_label(label)
|
__private_key = self.umbral_keying_material.derive_privkey_by_label(label)
|
||||||
kfrags = pre.split_rekey(__private_key, bob_pubkey_enc, m, n)
|
kfrags = pre.split_rekey(__private_key, signer, bob_pubkey_enc, m, n)
|
||||||
return __private_key.get_pubkey(), kfrags
|
return __private_key.get_pubkey(), kfrags
|
||||||
|
|
|
@ -1,123 +0,0 @@
|
||||||
from nucypher.crypto import api as API
|
|
||||||
from umbral.keys import UmbralPublicKey
|
|
||||||
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature, encode_dss_signature
|
|
||||||
from nucypher.crypto.api import keccak_digest
|
|
||||||
from bytestring_splitter import BytestringSplitter
|
|
||||||
|
|
||||||
|
|
||||||
class Signature(object):
|
|
||||||
"""
|
|
||||||
The Signature object allows signatures to be made and verified.
|
|
||||||
"""
|
|
||||||
_EXPECTED_LENGTH = 64 # With secp256k1
|
|
||||||
|
|
||||||
def __init__(self, r: int, s: int):
|
|
||||||
# TODO: Sanity check for proper r and s.
|
|
||||||
self.r = r
|
|
||||||
self.s = s
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "ECDSA Signature: {}".format(bytes(self).hex()[:15])
|
|
||||||
|
|
||||||
def verify(self, message: bytes, pubkey: UmbralPublicKey) -> bool:
|
|
||||||
"""
|
|
||||||
Verifies that a message's signature was valid.
|
|
||||||
|
|
||||||
:param message: The message to verify
|
|
||||||
:param pubkey: UmbralPublicKey of the signer
|
|
||||||
|
|
||||||
:return: True if valid, False if invalid
|
|
||||||
"""
|
|
||||||
return API.ecdsa_verify(message, self._der_encoded_bytes(), pubkey)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_bytes(cls, signature_as_bytes, der_encoded=False):
|
|
||||||
# TODO: Change the int literals to variables which account for the order of the curve.
|
|
||||||
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):
|
|
||||||
return self.r.to_bytes(32, "big") + self.s.to_bytes(32, "big")
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return len(bytes(self))
|
|
||||||
|
|
||||||
def __add__(self, other):
|
|
||||||
return bytes(self) + other
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
signature_splitter = BytestringSplitter(Signature)
|
|
||||||
|
|
||||||
|
|
||||||
class SignatureStamp(object):
|
|
||||||
"""
|
|
||||||
Can be called to sign something or used to express the signing public
|
|
||||||
key as bytes.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, signing_keypair):
|
|
||||||
self._sign = signing_keypair.sign
|
|
||||||
self._as_bytes = bytes(signing_keypair.pubkey)
|
|
||||||
self._as_umbral_pubkey = signing_keypair.pubkey
|
|
||||||
|
|
||||||
def __bytes__(self):
|
|
||||||
return self._as_bytes
|
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
|
||||||
return self._sign(*args, **kwargs)
|
|
||||||
|
|
||||||
def __hash__(self):
|
|
||||||
return int.from_bytes(self, byteorder="big")
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return other == bytes(self)
|
|
||||||
|
|
||||||
def __add__(self, other):
|
|
||||||
return bytes(self) + other
|
|
||||||
|
|
||||||
def __radd__(self, other):
|
|
||||||
return other + bytes(self)
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return len(bytes(self))
|
|
||||||
|
|
||||||
def __bool__(self):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def as_umbral_pubkey(self):
|
|
||||||
return self._as_umbral_pubkey
|
|
||||||
|
|
||||||
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 StrangerStamp(SignatureStamp):
|
|
||||||
"""
|
|
||||||
SignatureStamp of a stranger (ie, can only be used to glean public key, not to sign)
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
|
||||||
message = "This isn't your SignatureStamp; it belongs to (a Stranger). You can't sign with it."
|
|
||||||
raise TypeError(message)
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
from nucypher.crypto.api import keccak_digest
|
||||||
|
from bytestring_splitter import BytestringSplitter
|
||||||
|
from umbral.signing import Signature, Signer
|
||||||
|
|
||||||
|
signature_splitter = BytestringSplitter(Signature)
|
||||||
|
|
||||||
|
|
||||||
|
class SignatureStamp(object):
|
||||||
|
"""
|
||||||
|
Can be called to sign something or used to express the signing public
|
||||||
|
key as bytes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, signing_key, signer: Signer=None):
|
||||||
|
self.__signer = signer
|
||||||
|
self._as_bytes = bytes(signing_key)
|
||||||
|
self._as_umbral_pubkey = signing_key
|
||||||
|
|
||||||
|
def __bytes__(self):
|
||||||
|
return self._as_bytes
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
return self.__signer(*args, **kwargs)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return int.from_bytes(self, byteorder="big")
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return other == bytes(self)
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
return bytes(self) + other
|
||||||
|
|
||||||
|
def __radd__(self, other):
|
||||||
|
return other + bytes(self)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(bytes(self))
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def as_umbral_pubkey(self):
|
||||||
|
return self._as_umbral_pubkey
|
||||||
|
|
||||||
|
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 StrangerStamp(SignatureStamp):
|
||||||
|
"""
|
||||||
|
SignatureStamp of a stranger (ie, can only be used to glean public key, not to sign)
|
||||||
|
"""
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
message = "This isn't your SignatureStamp; it belongs to (a Stranger). You can't sign with it."
|
||||||
|
raise TypeError(message)
|
|
@ -1,17 +1,20 @@
|
||||||
from nucypher.crypto.api import encrypt_and_sign
|
from nucypher.crypto.api import encrypt_and_sign
|
||||||
from nucypher.crypto.signature import SignatureStamp
|
from nucypher.crypto.powers import SigningPower
|
||||||
|
from nucypher.crypto.signing import SignatureStamp
|
||||||
from nucypher.keystore.keypairs import SigningKeypair
|
from nucypher.keystore.keypairs import SigningKeypair
|
||||||
from constant_sorrow.constants import NO_SIGNING_POWER
|
from constant_sorrow.constants import NO_SIGNING_POWER
|
||||||
from umbral.keys import UmbralPublicKey
|
from umbral.keys import UmbralPublicKey
|
||||||
|
from umbral.signing import Signer
|
||||||
|
|
||||||
|
|
||||||
class DataSource:
|
class DataSource:
|
||||||
|
|
||||||
def __init__(self, policy_pubkey_enc, signer=NO_SIGNING_POWER, label=None):
|
def __init__(self, policy_pubkey_enc, signing_keypair=NO_SIGNING_POWER, label=None):
|
||||||
self.policy_pubkey = policy_pubkey_enc
|
self.policy_pubkey = policy_pubkey_enc
|
||||||
if signer is NO_SIGNING_POWER:
|
if signing_keypair is NO_SIGNING_POWER:
|
||||||
signer = SignatureStamp(SigningKeypair()) # TODO: Generate signing key properly. #241
|
signing_keypair = SigningKeypair() # TODO: Generate signing key properly. #241
|
||||||
self.stamp = signer
|
signing_power = SigningPower(keypair=signing_keypair)
|
||||||
|
self.stamp = signing_power.get_signature_stamp()
|
||||||
self.label = label
|
self.label = label
|
||||||
|
|
||||||
def encapsulate_single_message(self, message):
|
def encapsulate_single_message(self, message):
|
||||||
|
|
|
@ -7,16 +7,18 @@ from umbral.keys import UmbralPrivateKey, UmbralPublicKey
|
||||||
from umbral import pre
|
from umbral import pre
|
||||||
from umbral.config import default_curve
|
from umbral.config import default_curve
|
||||||
from nucypher.crypto.kits import MessageKit
|
from nucypher.crypto.kits import MessageKit
|
||||||
from nucypher.crypto.signature import Signature
|
from nucypher.crypto.signing import SignatureStamp
|
||||||
|
from umbral.signing import Signature, Signer
|
||||||
|
|
||||||
|
|
||||||
class Keypair(object):
|
class Keypair(object):
|
||||||
"""
|
"""
|
||||||
A parent Keypair class for all types of Keypairs.
|
A parent Keypair class for all types of Keypairs.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
umbral_key: Union[UmbralPrivateKey, UmbralPublicKey]=None,
|
umbral_key: Union[UmbralPrivateKey, UmbralPublicKey] = None,
|
||||||
generate_keys_if_needed=True):
|
generate_keys_if_needed=True):
|
||||||
"""
|
"""
|
||||||
Initalizes a Keypair object with an Umbral key object.
|
Initalizes a Keypair object with an Umbral key object.
|
||||||
|
|
||||||
|
@ -34,7 +36,8 @@ class Keypair(object):
|
||||||
self._privkey = UmbralPrivateKey.gen_key()
|
self._privkey = UmbralPrivateKey.gen_key()
|
||||||
self.pubkey = self._privkey.get_pubkey()
|
self.pubkey = self._privkey.get_pubkey()
|
||||||
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.")
|
||||||
|
|
||||||
def serialize_pubkey(self, as_b64=False) -> bytes:
|
def serialize_pubkey(self, as_b64=False) -> bytes:
|
||||||
"""
|
"""
|
||||||
|
@ -61,10 +64,11 @@ class EncryptingKeypair(Keypair):
|
||||||
"""
|
"""
|
||||||
A keypair for Umbral
|
A keypair for Umbral
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def decrypt(self, message_kit: MessageKit) -> bytes:
|
def decrypt(self, message_kit: MessageKit, verifying_key: UmbralPublicKey = None) -> bytes:
|
||||||
"""
|
"""
|
||||||
Decrypt data encrypted with Umbral.
|
Decrypt data encrypted with Umbral.
|
||||||
|
|
||||||
|
@ -72,8 +76,10 @@ class EncryptingKeypair(Keypair):
|
||||||
"""
|
"""
|
||||||
cleartext = pre.decrypt(ciphertext=message_kit.ciphertext,
|
cleartext = pre.decrypt(ciphertext=message_kit.ciphertext,
|
||||||
capsule=message_kit.capsule,
|
capsule=message_kit.capsule,
|
||||||
priv_key=self._privkey,
|
decrypting_key=self._privkey,
|
||||||
alice_pub_key=message_kit.policy_pubkey)
|
delegating_pubkey=message_kit.policy_pubkey,
|
||||||
|
verifying_key=verifying_key,
|
||||||
|
)
|
||||||
|
|
||||||
return cleartext
|
return cleartext
|
||||||
|
|
||||||
|
@ -82,6 +88,7 @@ class SigningKeypair(Keypair):
|
||||||
"""
|
"""
|
||||||
A SigningKeypair that uses ECDSA.
|
A SigningKeypair that uses ECDSA.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -99,3 +106,7 @@ class SigningKeypair(Keypair):
|
||||||
def generate_self_signed_cert(self, common_name):
|
def generate_self_signed_cert(self, common_name):
|
||||||
cryptography_key = self._privkey.to_cryptography_privkey()
|
cryptography_key = self._privkey.to_cryptography_privkey()
|
||||||
return generate_self_signed_certificate(common_name, default_curve(), cryptography_key)
|
return generate_self_signed_certificate(common_name, default_curve(), cryptography_key)
|
||||||
|
|
||||||
|
def get_signature_stamp(self):
|
||||||
|
signer = Signer(self._privkey)
|
||||||
|
return SignatureStamp(signing_key=self.pubkey, signer=signer)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from nucypher.crypto.signature import Signature
|
from nucypher.crypto.signing import Signature
|
||||||
from bytestring_splitter import BytestringSplitter
|
from bytestring_splitter import BytestringSplitter
|
||||||
from nucypher.keystore.db.models import Key, PolicyArrangement, Workorder
|
from nucypher.keystore.db.models import Key, PolicyArrangement, Workorder
|
||||||
from umbral.fragments import KFrag
|
from umbral.fragments import KFrag
|
||||||
|
@ -21,7 +21,7 @@ class KeyStore(object):
|
||||||
"""
|
"""
|
||||||
A storage class of cryptographic keys.
|
A storage class of cryptographic keys.
|
||||||
"""
|
"""
|
||||||
kfrag_splitter = BytestringSplitter(Signature, (KFrag, KFrag.get_size()))
|
kfrag_splitter = BytestringSplitter(Signature, (KFrag, KFrag.expected_bytes_length()))
|
||||||
|
|
||||||
def __init__(self, sqlalchemy_engine=None):
|
def __init__(self, sqlalchemy_engine=None):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -5,7 +5,7 @@ from kademlia.utils import digest
|
||||||
from constant_sorrow import default_constant_splitter, constants
|
from constant_sorrow import default_constant_splitter, constants
|
||||||
from nucypher.crypto.api import keccak_digest
|
from nucypher.crypto.api import keccak_digest
|
||||||
from nucypher.crypto.constants import PUBLIC_KEY_LENGTH, KECCAK_DIGEST_LENGTH
|
from nucypher.crypto.constants import PUBLIC_KEY_LENGTH, KECCAK_DIGEST_LENGTH
|
||||||
from nucypher.crypto.signature import Signature
|
from nucypher.crypto.signing import Signature
|
||||||
from bytestring_splitter import BytestringSplitter
|
from bytestring_splitter import BytestringSplitter
|
||||||
from nucypher.network.node import NucypherNode
|
from nucypher.network.node import NucypherNode
|
||||||
from nucypher.network.routing import NucypherRoutingTable
|
from nucypher.network.routing import NucypherRoutingTable
|
||||||
|
|
|
@ -12,7 +12,7 @@ from nucypher.characters import Bob, Ursula
|
||||||
from nucypher.crypto.api import keccak_digest
|
from nucypher.crypto.api import keccak_digest
|
||||||
from nucypher.crypto.constants import KECCAK_DIGEST_LENGTH
|
from nucypher.crypto.constants import KECCAK_DIGEST_LENGTH
|
||||||
from nucypher.crypto.powers import SigningPower, DelegatingPower
|
from nucypher.crypto.powers import SigningPower, DelegatingPower
|
||||||
from nucypher.crypto.signature import Signature
|
from nucypher.crypto.signing import Signature
|
||||||
from nucypher.crypto.splitters import key_splitter
|
from nucypher.crypto.splitters import key_splitter
|
||||||
from bytestring_splitter import BytestringSplitter
|
from bytestring_splitter import BytestringSplitter
|
||||||
from nucypher.blockchain.eth.policies import BlockchainArrangement
|
from nucypher.blockchain.eth.policies import BlockchainArrangement
|
||||||
|
|
|
@ -7,7 +7,6 @@ from nucypher.crypto.constants import CFRAG_LENGTH_WITHOUT_PROOF
|
||||||
|
|
||||||
|
|
||||||
def test_bob_cannot_follow_the_treasure_map_in_isolation(enacted_policy, bob):
|
def test_bob_cannot_follow_the_treasure_map_in_isolation(enacted_policy, bob):
|
||||||
|
|
||||||
# Assume for the moment that Bob has already received a TreasureMap, perhaps via a side channel.
|
# Assume for the moment that Bob has already received a TreasureMap, perhaps via a side channel.
|
||||||
hrac, treasure_map = enacted_policy.hrac(), enacted_policy.treasure_map
|
hrac, treasure_map = enacted_policy.hrac(), enacted_policy.treasure_map
|
||||||
bob.treasure_maps[hrac] = treasure_map
|
bob.treasure_maps[hrac] = treasure_map
|
||||||
|
@ -121,7 +120,7 @@ def test_bob_can_issue_a_work_order_to_a_specific_ursula(enacted_policy, bob,
|
||||||
|
|
||||||
# OK, so cool - Bob has his cFrag! Let's make sure everything went properly. First, we'll show that it is in fact
|
# OK, so cool - Bob has his cFrag! Let's make sure everything went properly. First, we'll show that it is in fact
|
||||||
# the correct cFrag (ie, that Ursula performed reencryption properly).
|
# the correct cFrag (ie, that Ursula performed reencryption properly).
|
||||||
for u in ursulas: # ...and to do that, we need to address the right ursula.
|
for u in ursulas: # ...and to do that, we need to address the right ursula.
|
||||||
if u.rest_port == work_order.ursula.rest_port:
|
if u.rest_port == work_order.ursula.rest_port:
|
||||||
ursula = u
|
ursula = u
|
||||||
break
|
break
|
||||||
|
@ -134,11 +133,13 @@ def test_bob_can_issue_a_work_order_to_a_specific_ursula(enacted_policy, bob,
|
||||||
the_correct_cfrag = pre.reencrypt(the_kfrag, capsule)
|
the_correct_cfrag = pre.reencrypt(the_kfrag, capsule)
|
||||||
|
|
||||||
# The first CFRAG_LENGTH_WITHOUT_PROOF bytes (ie, the cfrag proper, not the proof material), are the same:
|
# The first CFRAG_LENGTH_WITHOUT_PROOF bytes (ie, the cfrag proper, not the proof material), are the same:
|
||||||
assert bytes(the_cfrag)[:CapsuleFrag.get_size()] == bytes(the_correct_cfrag)[:CapsuleFrag.get_size()] # It's the correct cfrag!
|
assert bytes(the_cfrag)[:CapsuleFrag.expected_bytes_length()] == bytes(the_correct_cfrag)[
|
||||||
|
:CapsuleFrag.expected_bytes_length()] # It's the correct cfrag!
|
||||||
|
|
||||||
assert the_correct_cfrag.verify_correctness(capsule,
|
assert the_correct_cfrag.verify_correctness(capsule,
|
||||||
pubkey_a=enacted_policy.public_key,
|
delegating_pubkey=enacted_policy.public_key,
|
||||||
pubkey_b=bob.public_key(EncryptingPower))
|
signing_pubkey=alice.stamp.as_umbral_pubkey(),
|
||||||
|
encrypting_pubkey=bob.public_key(EncryptingPower))
|
||||||
|
|
||||||
# Now we'll show that Ursula saved the correct WorkOrder.
|
# Now we'll show that Ursula saved the correct WorkOrder.
|
||||||
work_orders_from_bob = ursula.work_orders(bob=bob)
|
work_orders_from_bob = ursula.work_orders(bob=bob)
|
||||||
|
@ -187,7 +188,7 @@ def test_bob_remembers_that_he_has_cfrags_for_a_particular_capsule(enacted_polic
|
||||||
capsule_side_channel[0].capsule.attach_cfrag(new_cfrag)
|
capsule_side_channel[0].capsule.attach_cfrag(new_cfrag)
|
||||||
|
|
||||||
|
|
||||||
def test_bob_gathers_and_combines(enacted_policy, bob, ursulas, capsule_side_channel):
|
def test_bob_gathers_and_combines(enacted_policy, bob, alice, capsule_side_channel):
|
||||||
# The side channel is represented as a single MessageKit, which is all that Bob really needs.
|
# The side channel is represented as a single MessageKit, which is all that Bob really needs.
|
||||||
the_message_kit, the_data_source = capsule_side_channel
|
the_message_kit, the_data_source = capsule_side_channel
|
||||||
|
|
||||||
|
@ -199,7 +200,7 @@ def test_bob_gathers_and_combines(enacted_policy, bob, ursulas, capsule_side_cha
|
||||||
|
|
||||||
# Bob can't decrypt yet with just two CFrags. He needs to gather at least m.
|
# Bob can't decrypt yet with just two CFrags. He needs to gather at least m.
|
||||||
with pytest.raises(pre.GenericUmbralError):
|
with pytest.raises(pre.GenericUmbralError):
|
||||||
bob.decrypt(the_message_kit)
|
bob.decrypt(the_message_kit, verifying_key=alice.stamp.as_umbral_pubkey())
|
||||||
|
|
||||||
number_left_to_collect = enacted_policy.treasure_map.m - len(bob._saved_work_orders)
|
number_left_to_collect = enacted_policy.treasure_map.m - len(bob._saved_work_orders)
|
||||||
|
|
||||||
|
@ -213,6 +214,8 @@ def test_bob_gathers_and_combines(enacted_policy, bob, ursulas, capsule_side_cha
|
||||||
|
|
||||||
# Now.
|
# Now.
|
||||||
# At long last.
|
# At long last.
|
||||||
is_valid, cleartext = bob.verify_from(the_data_source, the_message_kit, decrypt=True)
|
is_valid, cleartext = bob.verify_from(the_data_source, the_message_kit,
|
||||||
|
decrypt=True,
|
||||||
|
delegator_signing_key=alice.stamp.as_umbral_pubkey())
|
||||||
assert cleartext == b'Welcome to the flippering.'
|
assert cleartext == b'Welcome to the flippering.'
|
||||||
assert is_valid
|
assert is_valid
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import pytest
|
import pytest
|
||||||
from nucypher.crypto.api import secure_random
|
from nucypher.crypto.api import secure_random
|
||||||
from nucypher.crypto.signature import Signature
|
from nucypher.crypto.signing import Signature
|
||||||
from bytestring_splitter import BytestringSplitter
|
from bytestring_splitter import BytestringSplitter
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from nucypher.crypto.api import ecdsa_sign
|
from nucypher.crypto.api import ecdsa_sign
|
||||||
from umbral.keys import UmbralPrivateKey
|
from umbral.keys import UmbralPrivateKey
|
||||||
from nucypher.crypto.signature import Signature
|
from nucypher.crypto.signing import Signature
|
||||||
|
|
||||||
|
|
||||||
def test_signature_can_verify():
|
def test_signature_can_verify():
|
||||||
|
|
|
@ -11,7 +11,7 @@ from nucypher.characters import Alice, Bob
|
||||||
from nucypher.keystore import keystore
|
from nucypher.keystore import keystore
|
||||||
from nucypher.keystore.db import Base
|
from nucypher.keystore.db import Base
|
||||||
|
|
||||||
from nucypher.crypto.signature import SignatureStamp
|
from nucypher.crypto.signing import SignatureStamp
|
||||||
from nucypher.data_sources import DataSource
|
from nucypher.data_sources import DataSource
|
||||||
from nucypher.keystore import keystore
|
from nucypher.keystore import keystore
|
||||||
from nucypher.keystore.db import Base
|
from nucypher.keystore.db import Base
|
||||||
|
@ -97,7 +97,7 @@ def test_keystore():
|
||||||
def capsule_side_channel(enacted_policy):
|
def capsule_side_channel(enacted_policy):
|
||||||
signing_keypair = SigningKeypair()
|
signing_keypair = SigningKeypair()
|
||||||
data_source = DataSource(policy_pubkey_enc=enacted_policy.public_key,
|
data_source = DataSource(policy_pubkey_enc=enacted_policy.public_key,
|
||||||
signer=SignatureStamp(signing_keypair))
|
signing_keypair=signing_keypair)
|
||||||
message_kit, _signature = data_source.encapsulate_single_message(b"Welcome to the flippering.")
|
message_kit, _signature = data_source.encapsulate_single_message(b"Welcome to the flippering.")
|
||||||
return message_kit, data_source
|
return message_kit, data_source
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue