mirror of https://github.com/nucypher/nucypher.git
Remove use of threshold decryption request when encrypting data - should not be returned when encrypting - it's use is for decrypting (variant and context)..
parent
ee91cca87b
commit
6fb61bacec
|
@ -19,30 +19,18 @@ before_the_beginning_of_time = {
|
|||
},
|
||||
}
|
||||
|
||||
ciphertext, tdr = enrico.encrypt_for_dkg_and_produce_decryption_request(
|
||||
ciphertext = enrico.encrypt_for_dkg(
|
||||
plaintext=plaintext,
|
||||
conditions=before_the_beginning_of_time,
|
||||
ritual_id=ANYTHING_CAN_BE_PASSED_AS_RITUAL_ID,
|
||||
)
|
||||
|
||||
cleartest_from_tdr = bob.threshold_decrypt(
|
||||
cleartext_from_ciphertext = bob.threshold_decrypt(
|
||||
ciphertext=ciphertext,
|
||||
ritual_id=ANYTHING_CAN_BE_PASSED_AS_RITUAL_ID,
|
||||
conditions=before_the_beginning_of_time,
|
||||
)
|
||||
|
||||
cohort = bob._dkg_insight.fake_ritual.fake_nodes
|
||||
|
||||
cleartext_from_ciphertext = bob.decrypt_using_existing_decryption_request(
|
||||
tdr,
|
||||
participant_public_keys=bob._dkg_insight.fake_ritual.participant_public_keys,
|
||||
cohort=cohort,
|
||||
threshold=1,
|
||||
)
|
||||
|
||||
decoded_cleartext_from_ciphertext = bytes(cleartext_from_ciphertext)
|
||||
decoded_cleartext_from_tdr = bytes(cleartest_from_tdr)
|
||||
|
||||
assert decoded_cleartext_from_ciphertext == plaintext
|
||||
assert plaintext == decoded_cleartext_from_tdr
|
||||
print(f"Decrypted cleartext: {decoded_cleartext_from_ciphertext}")
|
||||
|
|
|
@ -12,10 +12,10 @@ from nucypher_core import (
|
|||
|
||||
from nucypher.characters.lawful import Bob, Enrico
|
||||
from nucypher.cli.types import ChecksumAddress
|
||||
from nucypher.crypto.ferveo import dkg
|
||||
from nucypher.crypto.powers import ThresholdRequestDecryptingPower
|
||||
from nucypher.network.decryption import ThresholdDecryptionClient
|
||||
from nucypher.network.middleware import RestMiddleware
|
||||
from nucypher.utilities.concurrency import Failure
|
||||
|
||||
|
||||
class FakeNode:
|
||||
|
@ -71,7 +71,7 @@ class DKGOmniscient:
|
|||
|
||||
class DKGInsight:
|
||||
# TODO: Make these configurable:
|
||||
tau = 1
|
||||
tau = 1 # ritual id
|
||||
security_threshold = 3
|
||||
shares_num = 4
|
||||
|
||||
|
@ -102,36 +102,29 @@ class DKGOmniscient:
|
|||
# Validators must be sorted by their public key
|
||||
validators.sort(key=attrgetter("address"))
|
||||
|
||||
# Each validator holds their own DKG instance and generates a transcript every
|
||||
# validator, including themselves
|
||||
self.aggregation_messages = []
|
||||
# Each validator generates a transcript which is publicly stored
|
||||
self.transcripts = []
|
||||
for sender in validators:
|
||||
dkg = ferveo.Dkg(
|
||||
tau=self.tau,
|
||||
shares_num=self.shares_num,
|
||||
security_threshold=self.security_threshold,
|
||||
validators=validators,
|
||||
transcript = dkg.generate_transcript(
|
||||
ritual_id=self.tau,
|
||||
me=sender,
|
||||
shares=self.shares_num,
|
||||
threshold=self.security_threshold,
|
||||
nodes=validators,
|
||||
)
|
||||
self.aggregation_messages.append(
|
||||
ferveo.ValidatorMessage(sender, dkg.generate_transcript())
|
||||
)
|
||||
self.transcripts.append((sender, transcript))
|
||||
|
||||
self.dkg = dkg
|
||||
self.validators = validators
|
||||
self.validator_keypairs = validator_keypairs
|
||||
|
||||
self.server_aggregate = dkg.aggregate_transcripts(self.aggregation_messages)
|
||||
assert self.server_aggregate.verify(
|
||||
self.shares_num, self.aggregation_messages
|
||||
)
|
||||
|
||||
# And the client can also aggregate and verify the transcripts
|
||||
self.client_aggregate = ferveo.AggregatedTranscript(
|
||||
self.aggregation_messages
|
||||
)
|
||||
assert self.server_aggregate.verify(
|
||||
self.shares_num, self.aggregation_messages
|
||||
# any validator can generate the same aggregated transcript
|
||||
self.server_aggregate, self.dkg_public_key = dkg.aggregate_transcripts(
|
||||
ritual_id=self.tau,
|
||||
me=validators[0],
|
||||
shares=self.shares_num,
|
||||
threshold=self.security_threshold,
|
||||
transcripts=self.transcripts,
|
||||
)
|
||||
|
||||
_dkg_insight = DKGInsight()
|
||||
|
@ -148,7 +141,7 @@ class NiceGuyEddie(Enrico, DKGOmniscient):
|
|||
del encrypting_key # We take this to match the Enrico public API, but we don't use it, because...
|
||||
|
||||
# ...we're going to use the DKG public key as the encrypting key, and ignore the key passed in.
|
||||
encrypting_key_we_actually_want_to_use = self._dkg_insight.dkg.public_key
|
||||
encrypting_key_we_actually_want_to_use = self._dkg_insight.dkg_public_key
|
||||
super().__init__(
|
||||
# https://imgflip.com/i/7o0po4
|
||||
encrypting_key=encrypting_key_we_actually_want_to_use,
|
||||
|
@ -174,54 +167,54 @@ class DKGOmniscientDecryptionClient(ThresholdDecryptionClient):
|
|||
# We only really need one encrypted tdr.
|
||||
etdr = list(encrypted_requests.values())[0]
|
||||
|
||||
# decrypt request
|
||||
threshold_decryption_request = trdp.decrypt_encrypted_request(etdr)
|
||||
ciphertext = threshold_decryption_request.ciphertext
|
||||
conditions = str(threshold_decryption_request.conditions).encode()
|
||||
ritual_id = threshold_decryption_request.ritual_id
|
||||
variant = threshold_decryption_request.variant
|
||||
|
||||
# We can obtain the transcripts from the side-channel (deserialize) and aggregate them
|
||||
validator_messages = [
|
||||
ferveo.ValidatorMessage(validator, transcript)
|
||||
for validator, transcript in self._learner._dkg_insight.transcripts
|
||||
]
|
||||
aggregate = ferveo.AggregatedTranscript(validator_messages)
|
||||
assert aggregate.verify(
|
||||
self._learner._dkg_insight.shares_num,
|
||||
# TODO this list should have to be passed again (either make `verify` static or use list
|
||||
# provided in constructor
|
||||
validator_messages,
|
||||
)
|
||||
|
||||
for validator, validator_keypair in zip(
|
||||
self._learner._dkg_insight.validators,
|
||||
self._learner._dkg_insight.validator_keypairs,
|
||||
):
|
||||
dkg = ferveo.Dkg(
|
||||
tau=self._learner._dkg_insight.tau,
|
||||
shares_num=self._learner._dkg_insight.shares_num,
|
||||
security_threshold=self._learner._dkg_insight.security_threshold,
|
||||
validators=self._learner._dkg_insight.validators,
|
||||
# get decryption fragments/shares
|
||||
decryption_share = dkg.derive_decryption_share(
|
||||
ritual_id=ritual_id,
|
||||
me=validator,
|
||||
)
|
||||
|
||||
# We can also obtain the aggregated transcript from the side-channel (deserialize)
|
||||
aggregate = ferveo.AggregatedTranscript(
|
||||
self._learner._dkg_insight.aggregation_messages
|
||||
)
|
||||
assert aggregate.verify(
|
||||
self._learner._dkg_insight.shares_num,
|
||||
self._learner._dkg_insight.aggregation_messages,
|
||||
)
|
||||
|
||||
decrypted_encryption_request = trdp.decrypt_encrypted_request(etdr)
|
||||
ciphertext = decrypted_encryption_request.ciphertext
|
||||
conditions_bytes = str(decrypted_encryption_request.conditions).encode()
|
||||
|
||||
decryption_share = aggregate.create_decryption_share_simple(
|
||||
dkg=dkg,
|
||||
shares=self._learner._dkg_insight.shares_num,
|
||||
threshold=self._learner._dkg_insight.security_threshold,
|
||||
nodes=self._learner._dkg_insight.validators,
|
||||
aggregated_transcript=aggregate,
|
||||
keypair=validator_keypair,
|
||||
ciphertext=ciphertext,
|
||||
aad=conditions_bytes,
|
||||
validator_keypair=validator_keypair,
|
||||
aad=conditions,
|
||||
variant=dkg.FerveoVariant(variant),
|
||||
)
|
||||
|
||||
decryption_share_bytes = bytes(decryption_share)
|
||||
|
||||
##### Uncomment for sanity check
|
||||
# ferveo.DecryptionShareSimple.from_bytes(decryption_share_bytes) # No IOError! Let's go!
|
||||
##################
|
||||
|
||||
decryption_response = ThresholdDecryptionResponse(
|
||||
ritual_id=55, # TODO: Abstract this somewhere
|
||||
decryption_share=bytes(decryption_share_bytes),
|
||||
decryption_share=bytes(decryption_share),
|
||||
)
|
||||
|
||||
encrypted_decryptiopn_response = trdp.encrypt_decryption_response(
|
||||
encrypted_decryption_response = trdp.encrypt_decryption_response(
|
||||
decryption_response=decryption_response,
|
||||
requester_public_key=etdr.requester_public_key,
|
||||
)
|
||||
responses[validator.address] = encrypted_decryptiopn_response
|
||||
responses[validator.address] = encrypted_decryption_response
|
||||
|
||||
NO_FAILURES = {}
|
||||
return responses, NO_FAILURES
|
||||
|
|
|
@ -641,23 +641,15 @@ class Bob(Character):
|
|||
)
|
||||
return decryption_request
|
||||
|
||||
def get_decryption_shares_using_existing_decryption_request(
|
||||
def _get_decryption_shares(
|
||||
self,
|
||||
decryption_request: ThresholdDecryptionRequest,
|
||||
participant_public_keys: Dict[ChecksumAddress, SessionStaticKey],
|
||||
cohort: List["Ursula"],
|
||||
threshold: int,
|
||||
variant: FerveoVariant = None,
|
||||
) -> Dict[
|
||||
ChecksumAddress, Union[DecryptionShareSimple, DecryptionSharePrecomputed]
|
||||
]:
|
||||
if variant is None:
|
||||
variant = self.default_dkg_variant
|
||||
if variant == FerveoVariant.PRECOMPUTED:
|
||||
share_type = DecryptionSharePrecomputed
|
||||
elif variant == FerveoVariant.SIMPLE:
|
||||
share_type = DecryptionShareSimple
|
||||
|
||||
# use ephemeral key for request
|
||||
requester_sk = SessionStaticSecret.random()
|
||||
requester_public_key = requester_sk.public_key()
|
||||
|
@ -666,9 +658,7 @@ class Bob(Character):
|
|||
shared_secrets = {}
|
||||
for ursula in cohort:
|
||||
ursula_checksum_address = to_checksum_address(ursula.checksum_address)
|
||||
|
||||
participant_public_key = participant_public_keys[ursula_checksum_address]
|
||||
|
||||
shared_secret = requester_sk.derive_shared_secret(participant_public_key)
|
||||
encrypted_decryption_request = decryption_request.encrypt(
|
||||
shared_secret=shared_secret,
|
||||
|
@ -688,6 +678,11 @@ class Bob(Character):
|
|||
raise Ursula.NotEnoughUrsulas(f"Not enough Ursulas to decrypt: {failures}")
|
||||
self.log.debug("Got enough shares to decrypt.")
|
||||
|
||||
if decryption_request.variant == FerveoVariant.PRECOMPUTED.value:
|
||||
share_type = DecryptionSharePrecomputed
|
||||
elif decryption_request.variant == FerveoVariant.SIMPLE.value:
|
||||
share_type = DecryptionShareSimple
|
||||
|
||||
gathered_shares = {}
|
||||
for provider_address, encrypted_decryption_response in successes.items():
|
||||
shared_secret = shared_secrets[provider_address]
|
||||
|
@ -700,34 +695,6 @@ class Bob(Character):
|
|||
gathered_shares[provider_address] = decryption_share
|
||||
return gathered_shares
|
||||
|
||||
def gather_decryption_shares(
|
||||
self,
|
||||
ritual_id: int,
|
||||
cohort: List["Ursula"],
|
||||
ciphertext: Ciphertext,
|
||||
lingo: Lingo,
|
||||
threshold: int,
|
||||
variant: FerveoVariant,
|
||||
participant_public_keys: Dict[ChecksumAddress, SessionStaticKey],
|
||||
context: Optional[dict] = None,
|
||||
) -> Dict[
|
||||
ChecksumAddress, Union[DecryptionShareSimple, DecryptionSharePrecomputed]
|
||||
]:
|
||||
decryption_request = self.make_decryption_request(
|
||||
ritual_id=ritual_id,
|
||||
ciphertext=ciphertext,
|
||||
lingo=lingo,
|
||||
variant=variant,
|
||||
context=context,
|
||||
)
|
||||
return self.get_decryption_shares_using_existing_decryption_request(
|
||||
decryption_request,
|
||||
participant_public_keys,
|
||||
cohort,
|
||||
threshold,
|
||||
variant=variant,
|
||||
)
|
||||
|
||||
def get_ritual_from_id(self, ritual_id):
|
||||
# blockchain reads: get the DKG parameters and the cohort.
|
||||
if not self.coordinator_agent:
|
||||
|
@ -762,6 +729,7 @@ class Bob(Character):
|
|||
f"{ursula} ({ursula.staking_provider_address}) is not part of the cohort"
|
||||
)
|
||||
self.remember_node(ursula)
|
||||
|
||||
try:
|
||||
variant = FerveoVariant(getattr(FerveoVariant, variant.upper()).value)
|
||||
except AttributeError:
|
||||
|
@ -775,50 +743,25 @@ class Bob(Character):
|
|||
else ritual.shares
|
||||
) # TODO: #3095 get this from the ritual / put it on-chain?
|
||||
|
||||
participant_public_keys = ritual.participant_public_keys
|
||||
decryption_shares = self.gather_decryption_shares(
|
||||
decryption_request = self.make_decryption_request(
|
||||
ritual_id=ritual_id,
|
||||
cohort=ursulas,
|
||||
ciphertext=ciphertext,
|
||||
context=context,
|
||||
lingo=conditions,
|
||||
threshold=threshold,
|
||||
variant=variant,
|
||||
context=context,
|
||||
)
|
||||
participant_public_keys = ritual.participant_public_keys
|
||||
decryption_shares = self._get_decryption_shares(
|
||||
decryption_request=decryption_request,
|
||||
participant_public_keys=participant_public_keys,
|
||||
cohort=ursulas,
|
||||
threshold=threshold,
|
||||
)
|
||||
|
||||
return self._decrypt(
|
||||
list(decryption_shares.values()), ciphertext, conditions, variant
|
||||
)
|
||||
|
||||
def decrypt_using_existing_decryption_request(
|
||||
self,
|
||||
decryption_request,
|
||||
participant_public_keys,
|
||||
cohort,
|
||||
threshold,
|
||||
variant=None,
|
||||
):
|
||||
if variant is None:
|
||||
variant = self.default_dkg_variant
|
||||
|
||||
addresses_and_dfrags = (
|
||||
self.get_decryption_shares_using_existing_decryption_request(
|
||||
decryption_request, participant_public_keys, cohort, threshold
|
||||
)
|
||||
)
|
||||
|
||||
# TODO: 3154
|
||||
conditions_as_json_string = str(decryption_request.conditions)
|
||||
conditions_as_list = json.loads(conditions_as_json_string)
|
||||
|
||||
return self._decrypt(
|
||||
list(addresses_and_dfrags.values()),
|
||||
decryption_request.ciphertext,
|
||||
conditions_as_list,
|
||||
variant,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _decrypt(
|
||||
shares: List[Union[DecryptionShareSimple, DecryptionSharePrecomputed]],
|
||||
|
|
|
@ -35,19 +35,18 @@ def _attempt_decryption(BobClass, plaintext):
|
|||
},
|
||||
}
|
||||
|
||||
ciphertext, tdr = enrico.encrypt_for_dkg_and_produce_decryption_request(
|
||||
ciphertext = enrico.encrypt_for_dkg(
|
||||
plaintext=plaintext,
|
||||
conditions=definitely_false_condition,
|
||||
ritual_id=ANYTHING_CAN_BE_PASSED_AS_RITUAL_DATA,
|
||||
)
|
||||
|
||||
decrypted_cleartext_from_ciphertext_list = bob.threshold_decrypt(
|
||||
ciphertext=ciphertext,
|
||||
decrypted_cleartext = bob.threshold_decrypt(
|
||||
ritual_id=ANYTHING_CAN_BE_PASSED_AS_RITUAL_DATA,
|
||||
ciphertext=ciphertext,
|
||||
conditions=definitely_false_condition,
|
||||
)
|
||||
|
||||
return decrypted_cleartext_from_ciphertext_list
|
||||
return decrypted_cleartext
|
||||
|
||||
|
||||
def test_user_controls_success():
|
||||
|
@ -65,4 +64,4 @@ def test_user_controls_success():
|
|||
def test_user_controls_failure():
|
||||
plaintext = b"ever thus to deadbeats"
|
||||
with pytest.raises(Ursula.NotEnoughUrsulas) as e:
|
||||
result = _attempt_decryption(ThisBobAlwaysFails, plaintext)
|
||||
_ = _attempt_decryption(ThisBobAlwaysFails, plaintext)
|
||||
|
|
Loading…
Reference in New Issue