Merge pull request #3134 from derekpierre/25519

Use new request keys in `nucypher-core` that use curve 25519 instead of Umbral
pull/3138/head
Derek Pierre 2023-06-07 09:49:40 -04:00 committed by GitHub
commit 0bbd734c64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 927 additions and 791 deletions

View File

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

1350
Pipfile.lock generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"},

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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