mirror of https://github.com/nucypher/nucypher.git
Merge pull request #3134 from derekpierre/25519
Use new request keys in `nucypher-core` that use curve 25519 instead of Umbralpull/3138/head
commit
0bbd734c64
4
Pipfile
4
Pipfile
|
@ -11,10 +11,9 @@ python_version = "3"
|
|||
constant-sorrow = ">=0.1.0a9"
|
||||
bytestring-splitter = ">=2.4.0"
|
||||
hendrix = ">=4.0"
|
||||
nucypher-core = {git="https://github.com/nucypher/nucypher-core.git", ref="main", subdirectory="nucypher-core-python"}
|
||||
nucypher-core = ">=0.9.0"
|
||||
# Cryptography
|
||||
cryptography = ">=3.2"
|
||||
ferveo = ">=0.1.11"
|
||||
mnemonic = "*"
|
||||
pynacl= ">=1.4.0"
|
||||
pyopenssl = "*"
|
||||
|
@ -54,7 +53,6 @@ pytest-mock = "*"
|
|||
pytest-timeout = "*"
|
||||
# Tools
|
||||
eth-ape = "*"
|
||||
# TODO eventually change to official release, issue #3131, once fix is available
|
||||
ape-solidity = ">=0.6.5"
|
||||
hypothesis = "*"
|
||||
pre-commit = "2.12.1"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,7 +8,7 @@ async-timeout==4.0.2 ; python_version >= '3.6'
|
|||
attrs==23.1.0 ; python_version >= '3.7'
|
||||
backcall==0.2.0
|
||||
base58==1.0.3
|
||||
bitarray==2.7.3
|
||||
bitarray==2.7.4
|
||||
cached-property==1.5.2
|
||||
certifi==2023.5.7 ; python_version >= '3.6'
|
||||
cffi==1.15.1
|
||||
|
@ -17,16 +17,16 @@ charset-normalizer==2.1.1 ; python_full_version >= '3.6.0'
|
|||
click==8.1.3
|
||||
commonmark==0.9.1
|
||||
coverage==6.5.0
|
||||
cryptography==40.0.2
|
||||
cryptography==41.0.1
|
||||
cytoolz==0.12.1 ; implementation_name == 'cpython'
|
||||
dataclassy==0.11.1 ; python_version >= '3.6'
|
||||
decorator==5.1.1 ; python_version >= '3.5'
|
||||
deprecated==1.2.13 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||
deprecated==1.2.14 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||
distlib==0.3.6
|
||||
eip712==0.2.1 ; python_version >= '3.8' and python_version < '4'
|
||||
eth-abi==4.0.0 ; python_version >= '3.7' and python_version < '4'
|
||||
eth-account==0.8.0 ; python_version >= '3.6' and python_version < '4'
|
||||
eth-ape==0.6.9
|
||||
eth-ape==0.6.10
|
||||
eth-bloom==2.0.0 ; python_version >= '3.7' and python_version < '4'
|
||||
eth-hash==0.5.1 ; python_version >= '3.7' and python_version < '4'
|
||||
eth-keyfile==0.6.1
|
||||
|
@ -35,7 +35,7 @@ eth-rlp==0.3.0 ; python_version >= '3.7' and python_version < '4'
|
|||
eth-tester==0.9.0b1
|
||||
eth-typing==3.3.0 ; python_full_version >= '3.7.2' and python_version < '4'
|
||||
eth-utils==2.1.0
|
||||
ethpm-types==0.5.1 ; python_version >= '3.8' and python_version < '4'
|
||||
ethpm-types==0.5.2 ; python_version >= '3.8' and python_version < '4'
|
||||
evm-trace==0.1.0a20 ; python_version >= '3.8' and python_version < '4'
|
||||
exceptiongroup==1.1.1 ; python_version < '3.11'
|
||||
executing==1.2.0
|
||||
|
@ -43,7 +43,7 @@ filelock==3.12.0 ; python_version >= '3.7'
|
|||
frozenlist==1.3.3 ; python_version >= '3.7'
|
||||
greenlet==2.0.2 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
||||
hexbytes==0.3.0 ; python_version >= '3.7' and python_version < '4'
|
||||
hypothesis==6.75.3
|
||||
hypothesis==6.76.0
|
||||
identify==2.5.24 ; python_version >= '3.7'
|
||||
idna==3.4 ; python_version >= '3.5'
|
||||
ijson==3.2.0.post0
|
||||
|
@ -52,10 +52,10 @@ importlib-resources==5.12.0 ; python_version < '3.9'
|
|||
iniconfig==2.0.0 ; python_version >= '3.7'
|
||||
ipython==8.12.2 ; python_version >= '3.8'
|
||||
jedi==0.18.2 ; python_version >= '3.6'
|
||||
jsonschema==4.18.0a7 ; python_version >= '3.8'
|
||||
jsonschema-specifications==2023.5.1 ; python_version >= '3.8'
|
||||
jsonschema==4.18.0a9 ; python_version >= '3.8'
|
||||
jsonschema-specifications==2023.5.2 ; python_version >= '3.8'
|
||||
lazyasd==0.1.4
|
||||
lru-dict==1.1.8
|
||||
lru-dict==1.2.0
|
||||
matplotlib-inline==0.1.6 ; python_version >= '3.5'
|
||||
morphys==1.0
|
||||
msgspec==0.15.1 ; python_version >= '3.8'
|
||||
|
@ -74,7 +74,7 @@ platformdirs==3.5.1 ; python_version >= '3.7'
|
|||
pluggy==1.0.0 ; python_version >= '3.6'
|
||||
pre-commit==3.3.2
|
||||
prompt-toolkit==3.0.38 ; python_full_version >= '3.7.0'
|
||||
protobuf==4.23.1 ; python_version >= '3.7'
|
||||
protobuf==4.23.2 ; python_version >= '3.7'
|
||||
ptyprocess==0.7.0
|
||||
pure-eval==0.2.2
|
||||
py==1.11.0 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
||||
|
@ -96,7 +96,7 @@ pyjwt[crypto]==2.7.0 ; python_version >= '3.7'
|
|||
pynacl==1.5.0
|
||||
pysha3==1.0.2
|
||||
pytest==6.2.5
|
||||
pytest-cov==4.0.0
|
||||
pytest-cov==4.1.0
|
||||
pytest-mock==3.10.0
|
||||
pytest-timeout==2.1.0
|
||||
pytest-twisted==1.14.0
|
||||
|
@ -104,8 +104,8 @@ python-baseconv==1.2.2
|
|||
python-dateutil==2.8.2 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'
|
||||
pytz==2023.3
|
||||
pyyaml==6.0 ; python_version >= '3.6'
|
||||
referencing==0.28.3 ; python_version >= '3.8'
|
||||
regex==2023.5.5 ; python_version >= '3.6'
|
||||
referencing==0.29.0 ; python_version >= '3.8'
|
||||
regex==2023.6.3 ; python_version >= '3.6'
|
||||
requests==2.31.0
|
||||
rich==12.6.0 ; python_full_version >= '3.6.3' and python_full_version < '4.0.0'
|
||||
rlp==3.0.0
|
||||
|
@ -122,8 +122,8 @@ toolz==0.12.0 ; python_version >= '3.5'
|
|||
tqdm==4.65.0 ; python_version >= '3.7'
|
||||
traitlets==5.9.0 ; python_version >= '3.7'
|
||||
trie==2.1.0 ; python_version >= '3.7' and python_version < '4'
|
||||
typing-extensions==4.6.0 ; python_version >= '3.7'
|
||||
urllib3==2.0.2 ; python_version >= '3.7'
|
||||
typing-extensions==4.6.3 ; python_version >= '3.7'
|
||||
urllib3==2.0.3 ; python_version >= '3.7'
|
||||
varint==1.0.2
|
||||
virtualenv==20.23.0 ; python_version >= '3.7'
|
||||
watchdog==2.3.1
|
||||
|
|
|
@ -8,9 +8,10 @@ click-default-group==1.2.2
|
|||
docutils==0.17.1
|
||||
idna==3.4 ; python_version >= '3.5'
|
||||
imagesize==1.4.1 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||
importlib-resources==5.12.0 ; python_version < '3.10'
|
||||
incremental==22.10.0
|
||||
jinja2==3.0.3
|
||||
markupsafe==2.1.2 ; python_version >= '3.7'
|
||||
markupsafe==2.1.3 ; python_version >= '3.7'
|
||||
packaging==23.1 ; python_version >= '3.7'
|
||||
pygments==2.15.1 ; python_version >= '3.7'
|
||||
pytz==2023.3 ; python_version < '3.9'
|
||||
|
@ -27,5 +28,6 @@ sphinxcontrib-jsmath==1.0.1 ; python_version >= '3.5'
|
|||
sphinxcontrib-qthelp==1.0.3 ; python_version >= '3.5'
|
||||
sphinxcontrib-serializinghtml==1.1.5 ; python_version >= '3.5'
|
||||
tomli==2.0.1 ; python_version < '3.11'
|
||||
towncrier==22.12.0
|
||||
urllib3==2.0.2 ; python_version >= '3.7'
|
||||
towncrier==23.6.0rc1
|
||||
urllib3==2.0.3 ; python_version >= '3.7'
|
||||
zipp==3.15.0 ; python_version < '3.10'
|
||||
|
|
|
@ -7,7 +7,10 @@ from eth_typing import ChecksumAddress
|
|||
from hexbytes import HexBytes
|
||||
from nucypher_core import (
|
||||
EncryptedThresholdDecryptionRequest,
|
||||
EncryptedThresholdDecryptionResponse,
|
||||
SessionStaticKey,
|
||||
ThresholdDecryptionRequest,
|
||||
ThresholdDecryptionResponse,
|
||||
)
|
||||
from nucypher_core.ferveo import (
|
||||
AggregatedTranscript,
|
||||
|
@ -15,7 +18,6 @@ from nucypher_core.ferveo import (
|
|||
FerveoPublicKey,
|
||||
Validator,
|
||||
)
|
||||
from nucypher_core.umbral import PublicKey
|
||||
from web3 import Web3
|
||||
from web3.types import TxReceipt
|
||||
|
||||
|
@ -399,14 +401,14 @@ class Ritualist(BaseActor):
|
|||
) -> TxReceipt:
|
||||
"""Publish an aggregated transcript to publicly available storage."""
|
||||
# look up the node index for this node on the blockchain
|
||||
request_encrypting_key = self.threshold_request_power.get_pubkey_from_ritual_id(
|
||||
participant_public_key = self.threshold_request_power.get_pubkey_from_ritual_id(
|
||||
ritual_id
|
||||
)
|
||||
receipt = self.coordinator_agent.post_aggregation(
|
||||
ritual_id=ritual_id,
|
||||
aggregated_transcript=aggregated_transcript,
|
||||
public_key=public_key,
|
||||
request_encrypting_key=request_encrypting_key,
|
||||
participant_public_key=participant_public_key,
|
||||
transacting_power=self.transacting_power
|
||||
)
|
||||
return receipt
|
||||
|
@ -599,11 +601,21 @@ class Ritualist(BaseActor):
|
|||
|
||||
def decrypt_threshold_decryption_request(
|
||||
self, encrypted_request: EncryptedThresholdDecryptionRequest
|
||||
) -> Tuple[ThresholdDecryptionRequest, PublicKey]:
|
||||
) -> ThresholdDecryptionRequest:
|
||||
return self.threshold_request_power.decrypt_encrypted_request(
|
||||
encrypted_request=encrypted_request
|
||||
)
|
||||
|
||||
def encrypt_threshold_decryption_response(
|
||||
self,
|
||||
decryption_response: ThresholdDecryptionResponse,
|
||||
requester_public_key: SessionStaticKey,
|
||||
) -> EncryptedThresholdDecryptionResponse:
|
||||
return self.threshold_request_power.encrypt_decryption_response(
|
||||
decryption_response=decryption_response,
|
||||
requester_public_key=requester_public_key,
|
||||
)
|
||||
|
||||
|
||||
class PolicyAuthor(NucypherTokenActor):
|
||||
"""Alice base class for blockchain operations, mocking up new policies!"""
|
||||
|
|
|
@ -10,8 +10,8 @@ from constant_sorrow.constants import CONTRACT_ATTRIBUTE # type: ignore
|
|||
from constant_sorrow.constants import CONTRACT_CALL, TRANSACTION
|
||||
from eth_typing.evm import ChecksumAddress
|
||||
from eth_utils.address import to_checksum_address
|
||||
from nucypher_core import SessionStaticKey
|
||||
from nucypher_core.ferveo import AggregatedTranscript, DkgPublicKey, Transcript
|
||||
from nucypher_core.umbral import PublicKey
|
||||
from web3.contract.contract import Contract, ContractFunction
|
||||
from web3.types import Timestamp, TxParams, TxReceipt, Wei
|
||||
|
||||
|
@ -560,7 +560,7 @@ class CoordinatorAgent(EthereumContractAgent):
|
|||
provider: ChecksumAddress
|
||||
aggregated: bool = False
|
||||
transcript: bytes = bytes()
|
||||
requestEncryptingKey: bytes = bytes()
|
||||
decryption_request_static_key: bytes = bytes()
|
||||
|
||||
class G1Point(NamedTuple):
|
||||
"""Coordinator contract representation of DkgPublicKey."""
|
||||
|
@ -617,14 +617,14 @@ class CoordinatorAgent(EthereumContractAgent):
|
|||
return len(self.providers)
|
||||
|
||||
@property
|
||||
def request_encrypting_keys(self):
|
||||
request_encrypting_keys = {}
|
||||
def participant_public_keys(self) -> Dict[ChecksumAddress, SessionStaticKey]:
|
||||
participant_public_keys = {}
|
||||
for p in self.participants:
|
||||
request_encrypting_keys[p.provider] = PublicKey.from_compressed_bytes(
|
||||
p.requestEncryptingKey
|
||||
participant_public_keys[p.provider] = SessionStaticKey.from_bytes(
|
||||
p.decryption_request_static_key
|
||||
)
|
||||
|
||||
return request_encrypting_keys
|
||||
return participant_public_keys
|
||||
|
||||
@contract_api(CONTRACT_CALL)
|
||||
def get_timeout(self) -> int:
|
||||
|
@ -667,7 +667,7 @@ class CoordinatorAgent(EthereumContractAgent):
|
|||
provider=ChecksumAddress(r[0]),
|
||||
aggregated=r[1],
|
||||
transcript=bytes(r[2]),
|
||||
requestEncryptingKey=bytes(r[3]),
|
||||
decryption_request_static_key=bytes(r[3]),
|
||||
)
|
||||
participants.append(participant)
|
||||
return participants
|
||||
|
@ -688,7 +688,7 @@ class CoordinatorAgent(EthereumContractAgent):
|
|||
provider=ChecksumAddress(result[0]),
|
||||
aggregated=result[1],
|
||||
transcript=bytes(result[2]),
|
||||
requestEncryptingKey=bytes(result[3]),
|
||||
decryption_request_static_key=bytes(result[3]),
|
||||
)
|
||||
return participant
|
||||
|
||||
|
@ -724,14 +724,14 @@ class CoordinatorAgent(EthereumContractAgent):
|
|||
ritual_id: int,
|
||||
aggregated_transcript: AggregatedTranscript,
|
||||
public_key: DkgPublicKey,
|
||||
request_encrypting_key: PublicKey,
|
||||
participant_public_key: SessionStaticKey,
|
||||
transacting_power: TransactingPower,
|
||||
) -> TxReceipt:
|
||||
contract_function: ContractFunction = self.contract.functions.postAggregation(
|
||||
ritualId=ritual_id,
|
||||
aggregatedTranscript=bytes(aggregated_transcript),
|
||||
publicKey=self.Ritual.G1Point.from_dkg_public_key(public_key),
|
||||
requestEncryptingKey=request_encrypting_key.to_compressed_bytes(),
|
||||
decryptionRequestStaticKey=bytes(participant_public_key),
|
||||
)
|
||||
receipt = self.blockchain.send_transaction(
|
||||
contract_function=contract_function,
|
||||
|
|
|
@ -40,6 +40,8 @@ from nucypher_core import (
|
|||
NodeMetadata,
|
||||
NodeMetadataPayload,
|
||||
ReencryptionResponse,
|
||||
SessionStaticKey,
|
||||
SessionStaticSecret,
|
||||
ThresholdDecryptionRequest,
|
||||
TreasureMap,
|
||||
)
|
||||
|
@ -59,7 +61,6 @@ from nucypher_core.ferveo import (
|
|||
from nucypher_core.umbral import (
|
||||
PublicKey,
|
||||
RecoverableSignature,
|
||||
SecretKey,
|
||||
VerifiedKeyFrag,
|
||||
reencrypt,
|
||||
)
|
||||
|
@ -587,7 +588,7 @@ class Bob(Character):
|
|||
def get_decryption_shares_using_existing_decryption_request(
|
||||
self,
|
||||
decryption_request: ThresholdDecryptionRequest,
|
||||
request_encrypting_keys: Dict[ChecksumAddress, PublicKey],
|
||||
participant_public_keys: Dict[ChecksumAddress, SessionStaticKey],
|
||||
variant: FerveoVariant,
|
||||
cohort: List["Ursula"],
|
||||
threshold: int,
|
||||
|
@ -600,18 +601,20 @@ class Bob(Character):
|
|||
share_type = DecryptionShareSimple
|
||||
|
||||
# use ephemeral key for request
|
||||
# TODO don't use Umbral in the long-run
|
||||
response_sk = SecretKey.random()
|
||||
response_encrypting_key = response_sk.public_key()
|
||||
requester_sk = SessionStaticSecret.random()
|
||||
requester_public_key = requester_sk.public_key()
|
||||
|
||||
decryption_request_mapping = {}
|
||||
shared_secrets = {}
|
||||
for ursula in cohort:
|
||||
ursula_checksum_address = to_checksum_address(ursula.checksum_address)
|
||||
request_encrypting_key = request_encrypting_keys[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(
|
||||
request_encrypting_key=request_encrypting_key,
|
||||
response_encrypting_key=response_encrypting_key,
|
||||
shared_secret=shared_secret,
|
||||
requester_public_key=requester_public_key,
|
||||
)
|
||||
shared_secrets[ursula_checksum_address] = shared_secret
|
||||
decryption_request_mapping[
|
||||
ursula_checksum_address
|
||||
] = encrypted_decryption_request
|
||||
|
@ -627,7 +630,10 @@ class Bob(Character):
|
|||
|
||||
gathered_shares = {}
|
||||
for provider_address, encrypted_decryption_response in successes.items():
|
||||
decryption_response = encrypted_decryption_response.decrypt(sk=response_sk)
|
||||
shared_secret = shared_secrets[provider_address]
|
||||
decryption_response = encrypted_decryption_response.decrypt(
|
||||
shared_secret=shared_secret
|
||||
)
|
||||
decryption_share = share_type.from_bytes(
|
||||
decryption_response.decryption_share
|
||||
)
|
||||
|
@ -642,7 +648,7 @@ class Bob(Character):
|
|||
lingo: LingoList,
|
||||
threshold: int,
|
||||
variant: FerveoVariant,
|
||||
request_encrypting_keys: Dict[ChecksumAddress, PublicKey],
|
||||
participant_public_keys: Dict[ChecksumAddress, SessionStaticKey],
|
||||
context: Optional[dict] = None,
|
||||
) -> Dict[
|
||||
ChecksumAddress, Union[DecryptionShareSimple, DecryptionSharePrecomputed]
|
||||
|
@ -655,7 +661,7 @@ class Bob(Character):
|
|||
context=context,
|
||||
)
|
||||
return self.get_decryption_shares_using_existing_decryption_request(
|
||||
decryption_request, request_encrypting_keys, variant, cohort, threshold
|
||||
decryption_request, participant_public_keys, variant, cohort, threshold
|
||||
)
|
||||
|
||||
def threshold_decrypt(
|
||||
|
@ -698,7 +704,7 @@ class Bob(Character):
|
|||
else ritual.shares
|
||||
) # TODO: #3095 get this from the ritual / put it on-chain?
|
||||
|
||||
request_encrypting_keys = ritual.request_encrypting_keys
|
||||
participant_public_keys = ritual.participant_public_keys
|
||||
decryption_shares = self.gather_decryption_shares(
|
||||
ritual_id=ritual_id,
|
||||
cohort=ursulas,
|
||||
|
@ -707,7 +713,7 @@ class Bob(Character):
|
|||
lingo=conditions,
|
||||
threshold=threshold,
|
||||
variant=variant,
|
||||
request_encrypting_keys=request_encrypting_keys,
|
||||
participant_public_keys=participant_public_keys,
|
||||
)
|
||||
|
||||
if not params:
|
||||
|
|
|
@ -12,6 +12,7 @@ from typing import Callable, ClassVar, Dict, List, Optional, Tuple, Union
|
|||
import click
|
||||
from constant_sorrow.constants import KEYSTORE_LOCKED
|
||||
from mnemonic.mnemonic import Mnemonic
|
||||
from nucypher_core import SessionSecretFactory
|
||||
from nucypher_core.ferveo import Keypair
|
||||
from nucypher_core.umbral import SecretKeyFactory
|
||||
|
||||
|
@ -43,7 +44,7 @@ _SIGNING_INFO = __INFO_BASE + b"signing"
|
|||
_DECRYPTING_INFO = __INFO_BASE + b"decrypting"
|
||||
_DELEGATING_INFO = __INFO_BASE + b"delegating"
|
||||
_RITUALISTIC_INFO = __INFO_BASE + b"ritualistic"
|
||||
_THRESHOLD_REQUEST_DECRYPTING_INFO = __INFO_BASE + b"threshoold_request_decrypting"
|
||||
_THRESHOLD_REQUEST_DECRYPTING_INFO = __INFO_BASE + b"threshold_request_decrypting"
|
||||
_TLS_INFO = __INFO_BASE + b"tls"
|
||||
|
||||
# Wrapping key
|
||||
|
@ -434,6 +435,18 @@ class Keystore:
|
|||
keypair = power_class._keypair_class(__skf.make_key(info))
|
||||
power = power_class(keypair=keypair, *power_args, **power_kwargs)
|
||||
|
||||
elif issubclass(power_class, ThresholdRequestDecryptingPower):
|
||||
# TODO is this really how we want
|
||||
# to derive the session factory (similar to RitualisticPower)
|
||||
size = SessionSecretFactory.seed_size()
|
||||
secret = __skf.make_secret(info)[:size]
|
||||
session_secret_factory = SessionSecretFactory.from_secure_randomness(secret)
|
||||
power = power_class(
|
||||
session_secret_factory=session_secret_factory,
|
||||
*power_args,
|
||||
**power_kwargs,
|
||||
)
|
||||
|
||||
elif issubclass(power_class, DerivedKeyBasedPower):
|
||||
parent_skf = SecretKeyFactory.from_secure_randomness(self.__secret)
|
||||
child_skf = parent_skf.make_factory(_DELEGATING_INFO)
|
||||
|
|
|
@ -6,7 +6,12 @@ from eth_typing.evm import ChecksumAddress
|
|||
from hexbytes import HexBytes
|
||||
from nucypher_core import (
|
||||
EncryptedThresholdDecryptionRequest,
|
||||
EncryptedThresholdDecryptionResponse,
|
||||
SessionSecretFactory,
|
||||
SessionStaticKey,
|
||||
SessionStaticSecret,
|
||||
ThresholdDecryptionRequest,
|
||||
ThresholdDecryptionResponse,
|
||||
ferveo,
|
||||
)
|
||||
from nucypher_core.ferveo import (
|
||||
|
@ -332,30 +337,49 @@ class ThresholdRequestDecryptingPower(DerivedKeyBasedPower):
|
|||
class ThresholdRequestDecryptionFailed(Exception):
|
||||
"""Raised when decryption of the request fails."""
|
||||
|
||||
def __init__(self, secret_key_factory: Optional[SecretKeyFactory] = None):
|
||||
if not secret_key_factory:
|
||||
secret_key_factory = SecretKeyFactory.random()
|
||||
self.__secret_key_factory = secret_key_factory
|
||||
class ThresholdResponseEncryptionFailed(Exception):
|
||||
"""Raised when encryption of response to request fails."""
|
||||
|
||||
def _get_privkey_from_ritual_id(self, ritual_id: int):
|
||||
return self.__secret_key_factory.make_key(bytes(ritual_id))
|
||||
def __init__(self, session_secret_factory: Optional[SessionSecretFactory] = None):
|
||||
if not session_secret_factory:
|
||||
session_secret_factory = SessionSecretFactory.random()
|
||||
self.__request_key_factory = session_secret_factory
|
||||
|
||||
def get_pubkey_from_ritual_id(self, ritual_id: int) -> PublicKey:
|
||||
return self._get_privkey_from_ritual_id(ritual_id).public_key()
|
||||
def _get_static_secret_from_ritual_id(self, ritual_id: int) -> SessionStaticSecret:
|
||||
return self.__request_key_factory.make_key(bytes(ritual_id))
|
||||
|
||||
def get_pubkey_from_ritual_id(self, ritual_id: int) -> SessionStaticKey:
|
||||
return self._get_static_secret_from_ritual_id(ritual_id).public_key()
|
||||
|
||||
def decrypt_encrypted_request(
|
||||
self, encrypted_request: EncryptedThresholdDecryptionRequest
|
||||
) -> Tuple[ThresholdDecryptionRequest, PublicKey]:
|
||||
) -> ThresholdDecryptionRequest:
|
||||
try:
|
||||
priv_key = self._get_privkey_from_ritual_id(encrypted_request.ritual_id)
|
||||
e2e_request = encrypted_request.decrypt(sk=priv_key)
|
||||
return (
|
||||
e2e_request.decryption_request,
|
||||
e2e_request.response_encrypting_key,
|
||||
static_secret = self._get_static_secret_from_ritual_id(
|
||||
encrypted_request.ritual_id
|
||||
)
|
||||
requester_public_key = encrypted_request.requester_public_key
|
||||
shared_secret = static_secret.derive_shared_secret(requester_public_key)
|
||||
decrypted_request = encrypted_request.decrypt(shared_secret)
|
||||
return decrypted_request
|
||||
except Exception as e:
|
||||
raise self.ThresholdRequestDecryptionFailed from e
|
||||
|
||||
def encrypt_decryption_response(
|
||||
self,
|
||||
decryption_response: ThresholdDecryptionResponse,
|
||||
requester_public_key: SessionStaticKey,
|
||||
) -> EncryptedThresholdDecryptionResponse:
|
||||
try:
|
||||
static_secret = self._get_static_secret_from_ritual_id(
|
||||
decryption_response.ritual_id
|
||||
)
|
||||
shared_secret = static_secret.derive_shared_secret(requester_public_key)
|
||||
encrypted_decryption_response = decryption_response.encrypt(shared_secret)
|
||||
return encrypted_decryption_response
|
||||
except Exception as e:
|
||||
raise self.ThresholdResponseEncryptionFailed from e
|
||||
|
||||
|
||||
class DelegatingPower(DerivedKeyBasedPower):
|
||||
|
||||
|
|
|
@ -148,10 +148,8 @@ def _make_rest_app(this_node, log: Logger) -> Flask:
|
|||
encrypted_decryption_request = EncryptedThresholdDecryptionRequest.from_bytes(
|
||||
request.data
|
||||
)
|
||||
(
|
||||
decryption_request,
|
||||
response_encrypting_key,
|
||||
) = this_node.decrypt_threshold_decryption_request(
|
||||
|
||||
decryption_request = this_node.decrypt_threshold_decryption_request(
|
||||
encrypted_request=encrypted_decryption_request
|
||||
)
|
||||
|
||||
|
@ -200,10 +198,15 @@ def _make_rest_app(this_node, log: Logger) -> Flask:
|
|||
)
|
||||
|
||||
# return the decryption share
|
||||
# TODO: #3079 #3081 encrypt the response with the requester's public key
|
||||
# TODO: #3098 nucypher-core#49 Use DecryptionShare type
|
||||
response = ThresholdDecryptionResponse(decryption_share=bytes(decryption_share))
|
||||
encrypted_response = response.encrypt(encrypting_key=response_encrypting_key)
|
||||
decryption_response = ThresholdDecryptionResponse(
|
||||
ritual_id=decryption_request.ritual_id,
|
||||
decryption_share=bytes(decryption_share),
|
||||
)
|
||||
encrypted_response = this_node.encrypt_threshold_decryption_response(
|
||||
decryption_response=decryption_response,
|
||||
requester_public_key=encrypted_decryption_request.requester_public_key,
|
||||
)
|
||||
return Response(
|
||||
bytes(encrypted_response),
|
||||
headers={"Content-Type": "application/octet-stream"},
|
||||
|
|
|
@ -7,7 +7,7 @@ attrs==23.1.0 ; python_version >= '3.7'
|
|||
autobahn==23.1.2 ; python_version >= '3.7'
|
||||
automat==22.10.0
|
||||
backports.zoneinfo==0.2.1 ; python_version >= '3.7' and python_version < '3.9'
|
||||
bitarray==2.7.3
|
||||
bitarray==2.7.4
|
||||
bytestring-splitter==2.4.1
|
||||
cached-property==1.5.2
|
||||
certifi==2023.5.7 ; python_version >= '3.6'
|
||||
|
@ -17,7 +17,7 @@ click==8.1.3
|
|||
colorama==0.4.6
|
||||
constant-sorrow==0.1.0a9
|
||||
constantly==15.1.0
|
||||
cryptography==40.0.2
|
||||
cryptography==41.0.1
|
||||
cytoolz==0.12.1 ; implementation_name == 'cpython'
|
||||
dateparser==1.1.8 ; python_version >= '3.7'
|
||||
eip712-structs==1.1.0
|
||||
|
@ -31,7 +31,6 @@ eth-rlp==0.3.0 ; python_version >= '3.7' and python_version < '4'
|
|||
eth-tester==0.9.0b1
|
||||
eth-typing==3.3.0 ; python_version < '4' and python_full_version >= '3.7.2'
|
||||
eth-utils==2.1.0
|
||||
ferveo==0.1.13
|
||||
flask==2.2.5
|
||||
frozenlist==1.3.3 ; python_version >= '3.7'
|
||||
hendrix==4.0.0
|
||||
|
@ -44,11 +43,11 @@ importlib-resources==5.12.0 ; python_version < '3.9'
|
|||
incremental==22.10.0
|
||||
itsdangerous==2.1.2 ; python_version >= '3.7'
|
||||
jinja2==3.0.3
|
||||
jsonschema==4.18.0a7 ; python_version >= '3.8'
|
||||
jsonschema-specifications==2023.5.1 ; python_version >= '3.8'
|
||||
lru-dict==1.1.8
|
||||
jsonschema==4.18.0a9 ; python_version >= '3.8'
|
||||
jsonschema-specifications==2023.5.2 ; python_version >= '3.8'
|
||||
lru-dict==1.2.0
|
||||
mako==1.2.4
|
||||
markupsafe==2.1.2 ; python_version >= '3.7'
|
||||
markupsafe==2.1.3 ; python_version >= '3.7'
|
||||
marshmallow==3.19.0
|
||||
maya==0.6.1
|
||||
mnemonic==0.20
|
||||
|
@ -56,12 +55,12 @@ msgpack==1.0.5
|
|||
msgpack-python==0.5.6
|
||||
multidict==5.2.0 ; python_version >= '3.6'
|
||||
mypy-extensions==0.4.4 ; python_version >= '2.7'
|
||||
nucypher-core @ git+https://github.com/nucypher/nucypher-core.git@a037b066a7469bccea3b91aee3f092f8de161929#subdirectory=nucypher-core-python
|
||||
nucypher-core==0.9.0
|
||||
packaging==23.1 ; python_version >= '3.7'
|
||||
parsimonious==0.9.0
|
||||
pendulum==3.0.0a1 ; python_version >= '3.7' and python_version < '4.0'
|
||||
pkgutil-resolve-name==1.3.10 ; python_version < '3.9'
|
||||
protobuf==4.23.1 ; python_version >= '3.7'
|
||||
protobuf==4.23.2 ; python_version >= '3.7'
|
||||
py-ecc==6.0.0 ; python_version >= '3.6' and python_version < '4'
|
||||
py-evm==0.7.0a2
|
||||
pyasn1==0.5.0 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'
|
||||
|
@ -71,12 +70,12 @@ pycparser==2.21
|
|||
pycryptodome==3.18.0
|
||||
pyethash==0.1.27
|
||||
pynacl==1.5.0
|
||||
pyopenssl==23.1.1
|
||||
pyopenssl==23.2.0
|
||||
pysha3==1.0.2
|
||||
python-dateutil==2.8.2 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||
pytz==2023.3
|
||||
referencing==0.28.3 ; python_version >= '3.8'
|
||||
regex==2023.5.5 ; python_version >= '3.6'
|
||||
referencing==0.29.0 ; python_version >= '3.8'
|
||||
regex==2023.6.3 ; python_version >= '3.6'
|
||||
requests==2.31.0
|
||||
rlp==3.0.0
|
||||
rpds-py==0.7.1 ; python_version >= '3.8'
|
||||
|
@ -92,10 +91,10 @@ toolz==0.12.0 ; python_version >= '3.5'
|
|||
trie==2.1.0 ; python_version >= '3.7' and python_version < '4'
|
||||
twisted==22.10.0 ; python_full_version >= '3.7.1'
|
||||
txaio==23.1.1 ; python_version >= '3.7'
|
||||
typing-extensions==4.6.0 ; python_version >= '3.7'
|
||||
typing-extensions==4.6.3 ; python_version >= '3.7'
|
||||
tzdata==2023.3 ; python_version >= '2'
|
||||
tzlocal==5.0.1 ; python_version >= '3.7'
|
||||
urllib3==2.0.2 ; python_version >= '3.7'
|
||||
urllib3==2.0.3 ; python_version >= '3.7'
|
||||
watchdog==2.3.1
|
||||
web3==6.4.0
|
||||
websockets==11.0.3 ; python_version >= '3.7'
|
||||
|
|
|
@ -2,7 +2,7 @@ import os
|
|||
|
||||
import pytest
|
||||
from eth_utils import keccak
|
||||
from nucypher_core.umbral import SecretKey
|
||||
from nucypher_core import SessionStaticSecret
|
||||
|
||||
from nucypher.blockchain.eth.agents import (
|
||||
ContractAgency,
|
||||
|
@ -125,17 +125,17 @@ def test_post_aggregation(
|
|||
agent, aggregated_transcript, dkg_public_key, transacting_powers, cohort
|
||||
):
|
||||
ritual_id = agent.number_of_rituals() - 1
|
||||
request_encrypting_keys = {}
|
||||
participant_public_keys = {}
|
||||
for i, transacting_power in enumerate(transacting_powers):
|
||||
request_encrypting_key = SecretKey.random().public_key()
|
||||
participant_public_key = SessionStaticSecret.random().public_key()
|
||||
receipt = agent.post_aggregation(
|
||||
ritual_id=ritual_id,
|
||||
aggregated_transcript=aggregated_transcript,
|
||||
public_key=dkg_public_key,
|
||||
request_encrypting_key=request_encrypting_key,
|
||||
participant_public_key=participant_public_key,
|
||||
transacting_power=transacting_power,
|
||||
)
|
||||
request_encrypting_keys[cohort[i]] = request_encrypting_key
|
||||
participant_public_keys[cohort[i]] = participant_public_key
|
||||
assert receipt["status"] == 1
|
||||
|
||||
post_aggregation_events = (
|
||||
|
@ -151,13 +151,12 @@ def test_post_aggregation(
|
|||
participants = agent.get_participants(ritual_id)
|
||||
for p in participants:
|
||||
assert p.aggregated
|
||||
assert (
|
||||
p.requestEncryptingKey
|
||||
== request_encrypting_keys[p.provider].to_compressed_bytes()
|
||||
assert p.decryption_request_static_key == bytes(
|
||||
participant_public_keys[p.provider]
|
||||
)
|
||||
|
||||
ritual = agent.get_ritual(ritual_id)
|
||||
assert ritual.request_encrypting_keys == request_encrypting_keys
|
||||
assert ritual.participant_public_keys == participant_public_keys
|
||||
|
||||
assert agent.get_ritual_status(ritual_id=ritual_id) == agent.Ritual.Status.FINALIZED
|
||||
|
||||
|
|
|
@ -4,7 +4,12 @@ from unittest.mock import ANY
|
|||
import pytest
|
||||
from cryptography.hazmat.primitives.serialization import Encoding
|
||||
from flask import Flask
|
||||
from nucypher_core import Conditions, ThresholdDecryptionRequest
|
||||
from nucypher_core import (
|
||||
Conditions,
|
||||
SessionStaticSecret,
|
||||
ThresholdDecryptionRequest,
|
||||
ThresholdDecryptionResponse,
|
||||
)
|
||||
from nucypher_core.umbral import SecretKey, Signer
|
||||
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
|
@ -175,31 +180,34 @@ def test_ritualist(temp_dir_path, testerchain, dkg_public_key):
|
|||
conditions=Conditions(json.dumps(CONDITIONS)),
|
||||
)
|
||||
|
||||
request_encrypting_key = ursula.threshold_request_power.get_pubkey_from_ritual_id(
|
||||
ritual_id=ritual_id
|
||||
)
|
||||
response_encrypting_key = SecretKey.random().public_key()
|
||||
encrypted_decryption_request = decryption_request.encrypt(
|
||||
request_encrypting_key=request_encrypting_key,
|
||||
response_encrypting_key=response_encrypting_key,
|
||||
)
|
||||
# successful decryption
|
||||
(
|
||||
decrypted_decryption_request,
|
||||
decrypted_response_encrypting_key,
|
||||
) = ursula.threshold_request_power.decrypt_encrypted_request(
|
||||
encrypted_decryption_request
|
||||
)
|
||||
assert bytes(decrypted_decryption_request) == bytes(decryption_request)
|
||||
assert (
|
||||
decrypted_response_encrypting_key.to_compressed_bytes()
|
||||
== response_encrypting_key.to_compressed_bytes()
|
||||
#
|
||||
# test requester sends encrypted decryption request
|
||||
#
|
||||
ursula_request_public_key = (
|
||||
ursula.threshold_request_power.get_pubkey_from_ritual_id(ritual_id=ritual_id)
|
||||
)
|
||||
|
||||
# failed decryption - incorrect encrypting key used
|
||||
requester_sk = SessionStaticSecret.random()
|
||||
requester_public_key = requester_sk.public_key()
|
||||
shared_secret = requester_sk.derive_shared_secret(ursula_request_public_key)
|
||||
encrypted_decryption_request = decryption_request.encrypt(
|
||||
shared_secret=shared_secret,
|
||||
requester_public_key=requester_public_key,
|
||||
)
|
||||
# successful decryption
|
||||
decrypted_decryption_request = (
|
||||
ursula.threshold_request_power.decrypt_encrypted_request(
|
||||
encrypted_decryption_request
|
||||
)
|
||||
)
|
||||
assert bytes(decrypted_decryption_request) == bytes(decryption_request)
|
||||
|
||||
# failed encryption - incorrect encrypting key used
|
||||
invalid_encrypted_decryption_request = decryption_request.encrypt(
|
||||
request_encrypting_key=SecretKey.random().public_key(),
|
||||
response_encrypting_key=response_encrypting_key,
|
||||
shared_secret=SessionStaticSecret.random().derive_shared_secret(
|
||||
ursula_request_public_key
|
||||
),
|
||||
requester_public_key=requester_public_key,
|
||||
)
|
||||
with pytest.raises(
|
||||
ThresholdRequestDecryptingPower.ThresholdRequestDecryptionFailed
|
||||
|
@ -207,3 +215,28 @@ def test_ritualist(temp_dir_path, testerchain, dkg_public_key):
|
|||
ursula.threshold_request_power.decrypt_encrypted_request(
|
||||
invalid_encrypted_decryption_request
|
||||
)
|
||||
#
|
||||
# test ursula sends encrypted response based on request
|
||||
#
|
||||
decryption_response = ThresholdDecryptionResponse(
|
||||
ritual_id=ritual_id, decryption_share=b"decryption_share"
|
||||
)
|
||||
encrypted_decryption_response = (
|
||||
ursula.threshold_request_power.encrypt_decryption_response(
|
||||
decryption_response=decryption_response,
|
||||
requester_public_key=requester_public_key,
|
||||
)
|
||||
)
|
||||
# successful decryption
|
||||
decrypted_decryption_response = encrypted_decryption_response.decrypt(shared_secret)
|
||||
assert bytes(decrypted_decryption_response) == bytes(decryption_response)
|
||||
|
||||
# failed encryption - incorrect decrypting key used
|
||||
with pytest.raises(
|
||||
ThresholdRequestDecryptingPower.ThresholdResponseEncryptionFailed
|
||||
):
|
||||
ursula.threshold_request_power.encrypt_decryption_response(
|
||||
decryption_response=decryption_response,
|
||||
# incorrect use of Umbral key here
|
||||
requester_public_key=SecretKey.random().public_key(),
|
||||
)
|
||||
|
|
|
@ -4,8 +4,8 @@ from typing import Dict, List
|
|||
|
||||
from eth_typing import ChecksumAddress
|
||||
from eth_utils import keccak
|
||||
from nucypher_core import SessionStaticKey
|
||||
from nucypher_core.ferveo import AggregatedTranscript, DkgPublicKey, Transcript
|
||||
from nucypher_core.umbral import PublicKey
|
||||
from web3.types import TxReceipt
|
||||
|
||||
from nucypher.blockchain.eth.agents import CoordinatorAgent
|
||||
|
@ -112,7 +112,7 @@ class MockCoordinatorAgent(MockContractAgent):
|
|||
ritual_id: int,
|
||||
aggregated_transcript: AggregatedTranscript,
|
||||
public_key: DkgPublicKey,
|
||||
request_encrypting_key: PublicKey,
|
||||
participant_public_key: SessionStaticKey,
|
||||
transacting_power: TransactingPower,
|
||||
) -> TxReceipt:
|
||||
ritual = self.rituals[ritual_id]
|
||||
|
@ -124,7 +124,7 @@ class MockCoordinatorAgent(MockContractAgent):
|
|||
)
|
||||
participant = self.get_participant_from_provider(ritual_id, provider)
|
||||
participant.aggregated = True
|
||||
participant.requestEncryptingKey = request_encrypting_key.to_compressed_bytes()
|
||||
participant.decryption_request_static_key = bytes(participant_public_key)
|
||||
|
||||
g1_point = self.Ritual.G1Point.from_dkg_public_key(public_key)
|
||||
if len(ritual.aggregated_transcript) == 0:
|
||||
|
|
|
@ -323,25 +323,15 @@ def test_derive_threshold_request_decrypting_power(tmpdir):
|
|||
)
|
||||
|
||||
ritual_id = 23
|
||||
request_encrypting_key = (
|
||||
threshold_request_decrypting_power.get_pubkey_from_ritual_id(
|
||||
ritual_id=ritual_id
|
||||
)
|
||||
public_key = threshold_request_decrypting_power.get_pubkey_from_ritual_id(
|
||||
ritual_id=ritual_id
|
||||
)
|
||||
other_request_encrypting_key = (
|
||||
threshold_request_decrypting_power.get_pubkey_from_ritual_id(
|
||||
ritual_id=ritual_id
|
||||
)
|
||||
)
|
||||
assert (
|
||||
request_encrypting_key.to_compressed_bytes()
|
||||
== other_request_encrypting_key.to_compressed_bytes()
|
||||
other_public_key = threshold_request_decrypting_power.get_pubkey_from_ritual_id(
|
||||
ritual_id=ritual_id
|
||||
)
|
||||
assert bytes(public_key) == bytes(other_public_key)
|
||||
|
||||
different_ritual_request_encrypting_key = (
|
||||
different_ritual_public_key = (
|
||||
threshold_request_decrypting_power.get_pubkey_from_ritual_id(ritual_id=0)
|
||||
)
|
||||
assert (
|
||||
request_encrypting_key.to_compressed_bytes
|
||||
!= different_ritual_request_encrypting_key.to_compressed_bytes()
|
||||
)
|
||||
assert bytes(public_key) != bytes(different_ritual_public_key)
|
||||
|
|
|
@ -3,7 +3,7 @@ from unittest.mock import Mock
|
|||
|
||||
import pytest
|
||||
from eth_account import Account
|
||||
from nucypher_core.umbral import SecretKey
|
||||
from nucypher_core import SessionStaticSecret
|
||||
|
||||
from tests.mock.coordinator import MockCoordinatorAgent
|
||||
from tests.mock.interfaces import MockBlockchain
|
||||
|
@ -107,17 +107,17 @@ def test_mock_coordinator_round_2(
|
|||
for p in ritual.participants:
|
||||
assert p.transcript == bytes(random_transcript)
|
||||
|
||||
request_encrypting_keys = []
|
||||
participant_public_keys = []
|
||||
for index, node_address in enumerate(nodes_transacting_powers):
|
||||
request_encrypting_key = SecretKey.random().public_key()
|
||||
participant_public_key = SessionStaticSecret.random().public_key()
|
||||
coordinator.post_aggregation(
|
||||
ritual_id=0,
|
||||
aggregated_transcript=aggregated_transcript,
|
||||
public_key=dkg_public_key,
|
||||
request_encrypting_key=request_encrypting_key,
|
||||
participant_public_key=participant_public_key,
|
||||
transacting_power=nodes_transacting_powers[node_address]
|
||||
)
|
||||
request_encrypting_keys.append(request_encrypting_key)
|
||||
participant_public_keys.append(participant_public_key)
|
||||
if index == len(nodes_transacting_powers) - 1:
|
||||
assert len(coordinator.EVENTS) == 2
|
||||
|
||||
|
@ -128,10 +128,7 @@ def test_mock_coordinator_round_2(
|
|||
# unchanged
|
||||
assert p.transcript == bytes(random_transcript)
|
||||
assert p.transcript != bytes(aggregated_transcript)
|
||||
assert (
|
||||
p.requestEncryptingKey
|
||||
== request_encrypting_keys[index].to_compressed_bytes()
|
||||
)
|
||||
assert p.decryption_request_static_key == bytes(participant_public_keys[index])
|
||||
|
||||
assert len(coordinator.EVENTS) == 2 # no additional event emitted here?
|
||||
assert (
|
||||
|
|
Loading…
Reference in New Issue