mirror of https://github.com/nucypher/nucypher.git
commit
8885cc94bc
|
@ -483,7 +483,7 @@ commands:
|
|||
steps:
|
||||
- chown_user_paths
|
||||
- save_cache:
|
||||
key: pip-v1-{{ .Branch }}-{{ checksum "Pipfile.lock" }}
|
||||
key: pip-v3-{{ .Branch }}-{{ checksum "Pipfile.lock" }}
|
||||
paths:
|
||||
- "~/.local/bin"
|
||||
- "~/.local/lib/python3.7/site-packages"
|
||||
|
@ -496,7 +496,7 @@ commands:
|
|||
description: "Restore cached python installation files"
|
||||
steps:
|
||||
- restore_cache: # ensure this step occurs *before* installing dependencies
|
||||
key: pip-v1-{{ .Branch }}-{{ checksum "Pipfile.lock" }}
|
||||
key: pip-v3-{{ .Branch }}-{{ checksum "Pipfile.lock" }}
|
||||
- restore_cache:
|
||||
key: solc-v2-{{ checksum "nucypher/blockchain/eth/sol/__conf__.py" }}
|
||||
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
FROM python:3.8.7-slim
|
||||
|
||||
# Update
|
||||
RUN apt update -y && apt upgrade -y
|
||||
RUN apt install patch gcc libffi-dev wget -y
|
||||
FROM kprasch/rust-python:3.8.9
|
||||
|
||||
WORKDIR /code
|
||||
COPY . /code
|
||||
|
|
|
@ -2,7 +2,7 @@ FROM python:3.8.7-slim
|
|||
|
||||
# Update
|
||||
RUN apt update -y && apt upgrade -y
|
||||
RUN apt install patch gcc libffi-dev wget -y
|
||||
RUN apt install patch gcc libffi-dev wget git -y
|
||||
|
||||
WORKDIR /code
|
||||
COPY . /code
|
||||
|
|
|
@ -24,13 +24,13 @@ import msgpack
|
|||
import shutil
|
||||
import sys
|
||||
|
||||
from nucypher.core import MessageKit, EncryptedTreasureMap
|
||||
from nucypher_core import MessageKit, EncryptedTreasureMap
|
||||
from nucypher_core.umbral import PublicKey
|
||||
|
||||
from nucypher.characters.lawful import Bob, Enrico, Ursula
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN
|
||||
from nucypher.crypto.keypairs import DecryptingKeypair, SigningKeypair
|
||||
from nucypher.crypto.powers import DecryptingPower, SigningPower
|
||||
from nucypher.crypto.umbral_adapter import PublicKey
|
||||
from nucypher.network.middleware import RestMiddleware
|
||||
from nucypher.utilities.logging import GlobalLoggerSettings
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from nucypher.crypto.umbral_adapter import SecretKey, PublicKey
|
||||
from nucypher_core.umbral import SecretKey, PublicKey
|
||||
|
||||
DOCTOR_PUBLIC_JSON = Path('doctor.public.json')
|
||||
DOCTOR_PRIVATE_JSON = Path('doctor.private.json')
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Switched to Rust implementation of the protocol types (``nucypher-core``). Correspondingly, API has been simplified, and type requirements have been made more strict.
|
|
@ -22,12 +22,11 @@ from collections import deque
|
|||
from collections.abc import KeysView
|
||||
from typing import Optional, Dict, Iterable, List, Tuple, NamedTuple, Union, Any
|
||||
|
||||
import binascii
|
||||
import itertools
|
||||
import maya
|
||||
from eth_typing import ChecksumAddress
|
||||
|
||||
from ..crypto.utils import keccak_digest
|
||||
from nucypher_core import FleetStateChecksum, NodeMetadata
|
||||
|
||||
from nucypher.utilities.logging import Logger
|
||||
from .nicknames import Nickname
|
||||
|
||||
|
@ -40,7 +39,7 @@ class ArchivedFleetState(NamedTuple):
|
|||
population: int
|
||||
|
||||
def to_json(self):
|
||||
return dict(checksum=self.checksum,
|
||||
return dict(checksum=bytes(self.checksum).hex(),
|
||||
nickname=self.nickname.to_json(),
|
||||
timestamp=self.timestamp.rfc2822(),
|
||||
population=self.population)
|
||||
|
@ -74,22 +73,21 @@ class FleetState:
|
|||
|
||||
@classmethod
|
||||
def new(cls, this_node: Optional['Ursula'] = None) -> 'FleetState':
|
||||
this_node_ref = weakref.ref(this_node) if this_node is not None else None
|
||||
# Using empty checksum so that JSON library is not confused.
|
||||
# Plus, we do need some checksum anyway. It's a legitimate state after all.
|
||||
return cls(checksum=keccak_digest(b"").hex(),
|
||||
nodes={},
|
||||
this_node_ref = weakref.ref(this_node) if this_node else None
|
||||
# `this_node` might not have its metadata available yet.
|
||||
this_node_metadata = None
|
||||
|
||||
return cls(nodes={},
|
||||
this_node_ref=this_node_ref,
|
||||
this_node_metadata=None)
|
||||
this_node_metadata=this_node_metadata)
|
||||
|
||||
def __init__(self,
|
||||
checksum: str,
|
||||
nodes: Dict[ChecksumAddress, 'Ursula'],
|
||||
this_node_ref: Optional[weakref.ReferenceType],
|
||||
this_node_metadata: Optional[bytes]):
|
||||
|
||||
self.checksum = checksum
|
||||
self.nickname = Nickname.from_seed(checksum, length=1)
|
||||
this_node_metadata: Optional[NodeMetadata]):
|
||||
self.checksum = FleetStateChecksum(this_node=this_node_metadata,
|
||||
other_nodes=[node.metadata() for node in nodes.values()])
|
||||
self.nickname = Nickname.from_seed(bytes(self.checksum), length=1)
|
||||
self._nodes = nodes
|
||||
self.timestamp = maya.now()
|
||||
self._this_node_ref = this_node_ref
|
||||
|
@ -132,7 +130,7 @@ class FleetState:
|
|||
|
||||
if self._this_node_ref is not None and not skip_this_node:
|
||||
this_node = self._this_node_ref()
|
||||
this_node_metadata = bytes(this_node.metadata())
|
||||
this_node_metadata = this_node.metadata()
|
||||
this_node_updated = self._this_node_metadata != this_node_metadata
|
||||
this_node_list = [this_node]
|
||||
else:
|
||||
|
@ -153,17 +151,10 @@ class FleetState:
|
|||
nodes[checksum_address] = new_node
|
||||
for checksum_address in diff.nodes_removed:
|
||||
del nodes[checksum_address]
|
||||
|
||||
all_nodes_sorted = sorted(itertools.chain(this_node_list, nodes.values()),
|
||||
key=lambda node: node.checksum_address)
|
||||
joined_metadata = b"".join(bytes(node.metadata()) for node in all_nodes_sorted)
|
||||
checksum = keccak_digest(joined_metadata).hex()
|
||||
else:
|
||||
nodes = self._nodes
|
||||
checksum = self.checksum
|
||||
|
||||
new_state = FleetState(checksum=checksum,
|
||||
nodes=nodes,
|
||||
new_state = FleetState(nodes=nodes,
|
||||
this_node_ref=self._this_node_ref,
|
||||
this_node_metadata=this_node_metadata)
|
||||
|
||||
|
@ -217,7 +208,7 @@ class FleetState:
|
|||
def __str__(self):
|
||||
return '{checksum} ⇀{nickname}↽ {icon} '.format(icon=self.nickname.icon,
|
||||
nickname=self.nickname,
|
||||
checksum=self.checksum[:7])
|
||||
checksum=bytes(self.checksum).hex()[:7])
|
||||
|
||||
def __repr__(self):
|
||||
return f"FleetState({self.checksum}, {self._nodes}, {self._this_node_ref}, {self._this_node_metadata})"
|
||||
|
@ -362,14 +353,14 @@ class FleetSensor:
|
|||
|
||||
def record_remote_fleet_state(self,
|
||||
checksum_address: ChecksumAddress,
|
||||
state_checksum: str,
|
||||
state_checksum: FleetStateChecksum,
|
||||
timestamp: maya.MayaDT,
|
||||
population: int):
|
||||
|
||||
if checksum_address not in self._current_state:
|
||||
raise KeyError(f"A node {checksum_address} is not present in the current fleet state")
|
||||
|
||||
nickname = Nickname.from_seed(state_checksum, length=1)
|
||||
nickname = Nickname.from_seed(bytes(state_checksum), length=1)
|
||||
state = ArchivedFleetState(checksum=state_checksum,
|
||||
nickname=nickname,
|
||||
timestamp=timestamp,
|
||||
|
|
|
@ -34,7 +34,7 @@ from web3 import Web3
|
|||
from web3.exceptions import ValidationError
|
||||
from web3.types import TxReceipt
|
||||
|
||||
from nucypher.core import HRAC
|
||||
from nucypher_core import HRAC
|
||||
|
||||
from nucypher.acumen.nicknames import Nickname
|
||||
from nucypher.blockchain.economics import (
|
||||
|
|
|
@ -28,9 +28,10 @@ from constant_sorrow.constants import (
|
|||
STRANGER
|
||||
)
|
||||
from eth_keys import KeyAPI as EthKeyAPI
|
||||
from eth_utils import to_canonical_address, to_checksum_address
|
||||
from eth_utils import to_canonical_address
|
||||
|
||||
from nucypher.core import MessageKit
|
||||
from nucypher_core import MessageKit
|
||||
from nucypher_core.umbral import PublicKey
|
||||
|
||||
from nucypher.acumen.nicknames import Nickname
|
||||
from nucypher.blockchain.eth.registry import BaseContractRegistry, InMemoryContractRegistry
|
||||
|
@ -49,7 +50,6 @@ from nucypher.crypto.signing import (
|
|||
SignatureStamp,
|
||||
StrangerStamp,
|
||||
)
|
||||
from nucypher.crypto.umbral_adapter import PublicKey
|
||||
from nucypher.network.middleware import RestMiddleware
|
||||
from nucypher.network.nodes import Learner
|
||||
|
||||
|
@ -276,14 +276,10 @@ class Character(Learner):
|
|||
return self._stamp
|
||||
|
||||
@property
|
||||
def canonical_public_address(self):
|
||||
def canonical_address(self):
|
||||
# TODO: This is wasteful. #1995
|
||||
return to_canonical_address(self.checksum_address)
|
||||
|
||||
@canonical_public_address.setter
|
||||
def canonical_public_address(self, address_bytes):
|
||||
self._checksum_address = to_checksum_address(address_bytes)
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, config, **overrides) -> 'Character':
|
||||
return config.produce(**overrides)
|
||||
|
@ -364,8 +360,8 @@ class Character(Learner):
|
|||
|
||||
# TODO: who even uses this method except for tests?
|
||||
|
||||
message_kit = MessageKit.author(policy_encrypting_key=recipient.public_keys(DecryptingPower),
|
||||
plaintext=plaintext)
|
||||
message_kit = MessageKit(policy_encrypting_key=recipient.public_keys(DecryptingPower),
|
||||
plaintext=plaintext)
|
||||
return message_kit
|
||||
|
||||
def public_keys(self, power_up_class: ClassVar):
|
||||
|
|
|
@ -19,13 +19,13 @@ from typing import Union, List
|
|||
|
||||
import maya
|
||||
|
||||
from nucypher.core import MessageKit, HRAC, EncryptedTreasureMap
|
||||
from nucypher_core import MessageKit, HRAC, EncryptedTreasureMap
|
||||
from nucypher_core.umbral import PublicKey
|
||||
|
||||
from nucypher.characters.base import Character
|
||||
from nucypher.characters.control.specifications import alice, bob, enrico
|
||||
from nucypher.control.interfaces import attach_schema, ControlInterface
|
||||
from nucypher.crypto.powers import DecryptingPower, SigningPower
|
||||
from nucypher.crypto.umbral_adapter import PublicKey
|
||||
from nucypher.network.middleware import RestMiddleware
|
||||
|
||||
|
||||
|
@ -105,7 +105,7 @@ class AliceInterface(CharacterPublicInterface):
|
|||
def revoke(self, label: bytes, bob_verifying_key: PublicKey) -> dict:
|
||||
|
||||
# TODO: Move deeper into characters
|
||||
policy_hrac = HRAC.derive(self.implementer.stamp.as_umbral_pubkey(), bob_verifying_key, label)
|
||||
policy_hrac = HRAC(self.implementer.stamp.as_umbral_pubkey(), bob_verifying_key, label)
|
||||
policy = self.implementer.active_policies[policy_hrac]
|
||||
|
||||
receipt, failed_revocations = self.implementer.revoke(policy)
|
||||
|
|
|
@ -23,4 +23,3 @@ from nucypher.characters.control.specifications.fields.label import *
|
|||
from nucypher.characters.control.specifications.fields.cleartext import *
|
||||
from nucypher.characters.control.specifications.fields.misc import *
|
||||
from nucypher.characters.control.specifications.fields.file import *
|
||||
from nucypher.characters.control.specifications.fields.signature import *
|
||||
|
|
|
@ -17,9 +17,10 @@
|
|||
|
||||
from marshmallow import fields
|
||||
|
||||
from nucypher_core.umbral import PublicKey
|
||||
|
||||
from nucypher.control.specifications.exceptions import InvalidInputData, InvalidNativeDataTypes
|
||||
from nucypher.control.specifications.fields.base import BaseField
|
||||
from nucypher.crypto.umbral_adapter import PublicKey
|
||||
|
||||
|
||||
class Key(BaseField, fields.Field):
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from nucypher.core import MessageKit as MessageKitClass
|
||||
from nucypher_core import MessageKit as MessageKitClass
|
||||
|
||||
from nucypher.control.specifications.exceptions import InvalidInputData
|
||||
from nucypher.control.specifications.fields.base import Base64BytesRepresentation
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
"""
|
||||
This file is part of nucypher.
|
||||
|
||||
nucypher is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
nucypher is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from base64 import b64decode, b64encode
|
||||
|
||||
from marshmallow import fields
|
||||
|
||||
from nucypher.control.specifications.exceptions import InvalidInputData, InvalidNativeDataTypes
|
||||
from nucypher.control.specifications.fields.base import BaseField
|
||||
from nucypher.crypto.umbral_adapter import Signature
|
||||
|
||||
|
||||
class UmbralSignature(BaseField, fields.Field):
|
||||
|
||||
def _serialize(self, value: Signature, attr, obj, **kwargs):
|
||||
return b64encode(bytes(value)).decode()
|
||||
|
||||
def _deserialize(self, value, attr, data, **kwargs):
|
||||
if isinstance(value, bytes):
|
||||
return value
|
||||
try:
|
||||
return Signature.from_bytes(b64decode(value))
|
||||
except InvalidNativeDataTypes as e:
|
||||
raise InvalidInputData(f"Could not parse {self.name}: {e}")
|
||||
|
||||
def _validate(self, value):
|
||||
try:
|
||||
Signature.from_bytes(value)
|
||||
except InvalidNativeDataTypes as e:
|
||||
raise InvalidInputData(f"Could not parse {self.name}: {e}")
|
|
@ -15,7 +15,7 @@
|
|||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from nucypher.core import EncryptedTreasureMap as EncryptedTreasureMapClass, TreasureMap as TreasureMapClass
|
||||
from nucypher_core import EncryptedTreasureMap as EncryptedTreasureMapClass, TreasureMap as TreasureMapClass
|
||||
|
||||
from nucypher.control.specifications.exceptions import InvalidInputData
|
||||
from nucypher.control.specifications.fields.base import Base64BytesRepresentation
|
||||
|
|
|
@ -21,7 +21,6 @@ from http import HTTPStatus
|
|||
import json
|
||||
import time
|
||||
from base64 import b64encode
|
||||
from datetime import datetime
|
||||
from json.decoder import JSONDecodeError
|
||||
from pathlib import Path
|
||||
from queue import Queue
|
||||
|
@ -42,19 +41,24 @@ from eth_typing.evm import ChecksumAddress
|
|||
from flask import Response, request
|
||||
from twisted.internet import reactor, stdio
|
||||
from twisted.internet.defer import Deferred
|
||||
from twisted.internet.task import LoopingCall
|
||||
from twisted.logger import Logger
|
||||
from web3.types import TxReceipt
|
||||
|
||||
from nucypher.core import (
|
||||
from nucypher_core import (
|
||||
MessageKit,
|
||||
AuthorizedKeyFrag,
|
||||
EncryptedKeyFrag,
|
||||
TreasureMap,
|
||||
EncryptedTreasureMap,
|
||||
ReencryptionResponse,
|
||||
NodeMetadata
|
||||
NodeMetadata,
|
||||
NodeMetadataPayload,
|
||||
HRAC,
|
||||
)
|
||||
from nucypher_core.umbral import (
|
||||
PublicKey,
|
||||
reencrypt,
|
||||
VerifiedKeyFrag,
|
||||
)
|
||||
|
||||
import nucypher
|
||||
from nucypher.acumen.nicknames import Nickname
|
||||
|
@ -81,12 +85,6 @@ from nucypher.crypto.powers import (
|
|||
TransactingPower,
|
||||
TLSHostingPower,
|
||||
)
|
||||
from nucypher.crypto.umbral_adapter import (
|
||||
PublicKey,
|
||||
reencrypt,
|
||||
VerifiedKeyFrag,
|
||||
)
|
||||
from nucypher.datastore.datastore import DatastoreTransactionError, RecordNotFound
|
||||
from nucypher.network.exceptions import NodeSeemsToBeDown
|
||||
from nucypher.network.middleware import RestMiddleware
|
||||
from nucypher.network.nodes import NodeSprout, TEACHER_NODES, Teacher
|
||||
|
@ -358,7 +356,7 @@ class Alice(Character, BlockchainPolicyAuthor):
|
|||
return policy_pubkey
|
||||
|
||||
def revoke(self,
|
||||
policy: 'Policy',
|
||||
policy: Policy,
|
||||
onchain: bool = True, # forced to False for federated mode
|
||||
offchain: bool = True
|
||||
) -> Tuple[TxReceipt, Dict[ChecksumAddress, Tuple['Revocation', Exception]]]:
|
||||
|
@ -413,7 +411,7 @@ class Alice(Character, BlockchainPolicyAuthor):
|
|||
|
||||
delegating_power = self._crypto_power.power_ups(DelegatingPower)
|
||||
decrypting_power = delegating_power.get_decrypting_power_from_label(label)
|
||||
cleartext = decrypting_power.decrypt(message_kit)
|
||||
cleartext = decrypting_power.decrypt_message_kit(message_kit)
|
||||
|
||||
# TODO: why does it return a list of cleartexts but takes a single message kit?
|
||||
# Shouldn't it be able to take a list of them too?
|
||||
|
@ -531,10 +529,8 @@ class Bob(Character):
|
|||
publisher_verifying_key: PublicKey
|
||||
) -> TreasureMap:
|
||||
decrypting_power = self._crypto_power.power_ups(DecryptingPower)
|
||||
auth_tmap = decrypting_power.decrypt(encrypted_treasure_map)
|
||||
treasure_map = auth_tmap.verify(recipient_key=decrypting_power.keypair.pubkey,
|
||||
publisher_verifying_key=publisher_verifying_key)
|
||||
return treasure_map
|
||||
return decrypting_power.decrypt_treasure_map(encrypted_treasure_map,
|
||||
publisher_verifying_key=publisher_verifying_key)
|
||||
|
||||
def retrieve(
|
||||
self,
|
||||
|
@ -561,7 +557,7 @@ class Bob(Character):
|
|||
publisher_verifying_key = alice_verifying_key
|
||||
|
||||
# A small optimization to avoid multiple treasure map decryptions.
|
||||
map_hash = hash(encrypted_treasure_map)
|
||||
map_hash = hash(bytes(encrypted_treasure_map))
|
||||
if map_hash in self._treasure_maps:
|
||||
treasure_map = self._treasure_maps[map_hash]
|
||||
else:
|
||||
|
@ -613,7 +609,7 @@ class Bob(Character):
|
|||
cleartexts = []
|
||||
decrypting_power = self._crypto_power.power_ups(DecryptingPower)
|
||||
for message_kit in message_kits:
|
||||
cleartext = decrypting_power.decrypt(message_kit)
|
||||
cleartext = decrypting_power.decrypt_message_kit(message_kit)
|
||||
cleartexts.append(cleartext)
|
||||
|
||||
return cleartexts
|
||||
|
@ -766,8 +762,7 @@ class Ursula(Teacher, Character, Worker):
|
|||
|
||||
self.rest_server = self._make_local_server(host=rest_host,
|
||||
port=rest_port,
|
||||
db_filepath=db_filepath,
|
||||
domain=domain)
|
||||
db_filepath=db_filepath)
|
||||
|
||||
# Self-signed TLS certificate of self for Teacher.__init__
|
||||
certificate_filepath = self._crypto_power.power_ups(TLSHostingPower).keypair.certificate_filepath
|
||||
|
@ -809,11 +804,10 @@ class Ursula(Teacher, Character, Worker):
|
|||
self._crypto_power.consume_power_up(tls_hosting_power) # Consume!
|
||||
return tls_hosting_power
|
||||
|
||||
def _make_local_server(self, host, port, domain, db_filepath) -> ProxyRESTServer:
|
||||
def _make_local_server(self, host, port, db_filepath) -> ProxyRESTServer:
|
||||
rest_app, datastore = make_rest_app(
|
||||
this_node=self,
|
||||
db_filepath=db_filepath,
|
||||
domain=domain,
|
||||
)
|
||||
rest_server = ProxyRESTServer(rest_host=host,
|
||||
rest_port=port,
|
||||
|
@ -991,17 +985,18 @@ class Ursula(Teacher, Character, Worker):
|
|||
decentralized_identity_evidence = None
|
||||
else:
|
||||
decentralized_identity_evidence = self.decentralized_identity_evidence
|
||||
return NodeMetadata.author(signer=self.stamp.as_umbral_signer(),
|
||||
public_address=self.canonical_public_address,
|
||||
domain=self.domain,
|
||||
timestamp_epoch=timestamp.epoch,
|
||||
decentralized_identity_evidence=decentralized_identity_evidence,
|
||||
verifying_key=self.public_keys(SigningPower),
|
||||
encrypting_key=self.public_keys(DecryptingPower),
|
||||
certificate_bytes=self.certificate.public_bytes(Encoding.PEM),
|
||||
host=self.rest_interface.host,
|
||||
port=self.rest_interface.port,
|
||||
)
|
||||
payload = NodeMetadataPayload(canonical_address=self.canonical_address,
|
||||
domain=self.domain,
|
||||
timestamp_epoch=timestamp.epoch,
|
||||
decentralized_identity_evidence=decentralized_identity_evidence,
|
||||
verifying_key=self.public_keys(SigningPower),
|
||||
encrypting_key=self.public_keys(DecryptingPower),
|
||||
certificate_bytes=self.certificate.public_bytes(Encoding.PEM),
|
||||
host=self.rest_interface.host,
|
||||
port=self.rest_interface.port,
|
||||
)
|
||||
return NodeMetadata(signer=self.stamp.as_umbral_signer(),
|
||||
payload=payload)
|
||||
|
||||
def metadata(self):
|
||||
if not self._metadata:
|
||||
|
@ -1010,7 +1005,7 @@ class Ursula(Teacher, Character, Worker):
|
|||
|
||||
@property
|
||||
def timestamp(self):
|
||||
return maya.MayaDT(self.metadata().timestamp_epoch)
|
||||
return maya.MayaDT(self.metadata().payload.timestamp_epoch)
|
||||
|
||||
#
|
||||
# Alternate Constructors
|
||||
|
@ -1187,9 +1182,9 @@ class Ursula(Teacher, Character, Worker):
|
|||
# Re-Encryption
|
||||
#
|
||||
|
||||
def _decrypt_kfrag(self, encrypted_kfrag: EncryptedKeyFrag) -> AuthorizedKeyFrag:
|
||||
def _decrypt_kfrag(self, encrypted_kfrag: EncryptedKeyFrag, hrac: HRAC, publisher_verifying_key: PublicKey) -> VerifiedKeyFrag:
|
||||
decrypting_power = self._crypto_power.power_ups(DecryptingPower)
|
||||
return decrypting_power.decrypt(encrypted_kfrag)
|
||||
return decrypting_power.decrypt_kfrag(encrypted_kfrag, hrac, publisher_verifying_key)
|
||||
|
||||
def _reencrypt(self, kfrag: VerifiedKeyFrag, capsules) -> ReencryptionResponse:
|
||||
cfrags = []
|
||||
|
@ -1198,9 +1193,9 @@ class Ursula(Teacher, Character, Worker):
|
|||
cfrags.append(cfrag)
|
||||
self.log.info(f"Re-encrypted capsule {capsule} -> made {cfrag}.")
|
||||
|
||||
return ReencryptionResponse.construct_by_ursula(signer=self.stamp.as_umbral_signer(),
|
||||
capsules=capsules,
|
||||
cfrags=cfrags)
|
||||
return ReencryptionResponse(signer=self.stamp.as_umbral_signer(),
|
||||
capsules=capsules,
|
||||
vcfrags=cfrags)
|
||||
|
||||
def status_info(self, omit_known_nodes: bool = False) -> 'LocalUrsulaStatus':
|
||||
|
||||
|
@ -1310,8 +1305,8 @@ class Enrico(Character):
|
|||
|
||||
def encrypt_message(self, plaintext: bytes) -> MessageKit:
|
||||
# TODO: #2107 Rename to "encrypt"
|
||||
message_kit = MessageKit.author(policy_encrypting_key=self.policy_pubkey,
|
||||
plaintext=plaintext)
|
||||
message_kit = MessageKit(policy_encrypting_key=self.policy_pubkey,
|
||||
plaintext=plaintext)
|
||||
return message_kit
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -22,7 +22,7 @@ from unittest.mock import patch
|
|||
|
||||
from eth_tester.exceptions import ValidationError
|
||||
|
||||
from nucypher.core import NodeMetadata
|
||||
from nucypher_core import NodeMetadata
|
||||
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.characters.lawful import Alice, Ursula
|
||||
|
@ -83,26 +83,32 @@ class Vladimir(Ursula):
|
|||
)
|
||||
|
||||
# Let's use the target's public info, and try to make some changes.
|
||||
# We are going to mutate it, so make a copy (it is cached in the Ursula).
|
||||
metadata = NodeMetadata.from_bytes(bytes(target_ursula.metadata()))
|
||||
metadata_payload = metadata._metadata_payload
|
||||
|
||||
metadata = target_ursula.metadata()
|
||||
metadata_bytes = bytes(metadata)
|
||||
|
||||
# Since it is an object from a Rust extension, we cannot directly modify it,
|
||||
# so we have to replace stuff in the byte representation and then deserialize.
|
||||
# We are replacinig objects with constant size,
|
||||
# so it should work regardless of the binary format.
|
||||
|
||||
# Our basic replacement. We want to impersonate the target Ursula.
|
||||
metadata_payload = metadata_payload._replace(public_address=vladimir.canonical_public_address)
|
||||
metadata_bytes = metadata_bytes.replace(metadata.payload.canonical_address,
|
||||
vladimir.canonical_address)
|
||||
|
||||
# Use our own verifying key
|
||||
if substitute_verifying_key:
|
||||
metadata_payload = metadata_payload._replace(
|
||||
verifying_key=vladimir.stamp.as_umbral_pubkey())
|
||||
metadata_bytes = metadata_bytes.replace(bytes(metadata.payload.verifying_key),
|
||||
bytes(vladimir.stamp.as_umbral_pubkey()))
|
||||
|
||||
fake_metadata = NodeMetadata.from_bytes(metadata_bytes)
|
||||
|
||||
# Re-generate metadata signature using our signing key
|
||||
if sign_metadata:
|
||||
signature = vladimir.stamp(bytes(metadata_payload))
|
||||
else:
|
||||
signature = metadata.signature
|
||||
fake_metadata = NodeMetadata(vladimir.stamp.as_umbral_signer(), fake_metadata.payload)
|
||||
|
||||
# Put metadata back
|
||||
vladimir._metadata = NodeMetadata(signature, metadata_payload)
|
||||
vladimir._metadata = fake_metadata
|
||||
|
||||
return vladimir
|
||||
|
||||
|
|
|
@ -18,13 +18,14 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
import click
|
||||
|
||||
from nucypher_core.umbral import PublicKey
|
||||
|
||||
from nucypher.characters.control.interfaces import EnricoInterface
|
||||
from nucypher.characters.lawful import Enrico
|
||||
from nucypher.cli.utils import setup_emitter
|
||||
from nucypher.cli.config import group_general_config
|
||||
from nucypher.cli.options import option_dry_run, option_policy_encrypting_key
|
||||
from nucypher.cli.types import NETWORK_PORT
|
||||
from nucypher.crypto.umbral_adapter import PublicKey
|
||||
|
||||
|
||||
@click.group()
|
||||
|
|
|
@ -22,11 +22,12 @@ from decimal import Decimal, DecimalException
|
|||
from eth_utils import to_checksum_address
|
||||
from ipaddress import ip_address
|
||||
|
||||
from nucypher_core.umbral import PublicKey
|
||||
|
||||
from nucypher.blockchain.economics import StandardTokenEconomics
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterface
|
||||
from nucypher.blockchain.eth.networks import NetworksInventory
|
||||
from nucypher.blockchain.eth.token import NU
|
||||
from nucypher.crypto.umbral_adapter import PublicKey
|
||||
from nucypher.utilities.networking import validate_worker_ip, InvalidWorkerIP
|
||||
|
||||
|
||||
|
|
1025
nucypher/core.py
1025
nucypher/core.py
File diff suppressed because it is too large
Load Diff
|
@ -25,17 +25,24 @@ from cryptography.hazmat.primitives.asymmetric import ec
|
|||
from hendrix.deploy.tls import HendrixDeployTLS
|
||||
from hendrix.facilities.services import ExistingKeyTLSContextFactory
|
||||
|
||||
from nucypher.core import MessageKit, EncryptedTreasureMap, EncryptedKeyFrag
|
||||
|
||||
from nucypher.config.constants import MAX_UPLOAD_CONTENT_LENGTH
|
||||
from nucypher.crypto.signing import SignatureStamp, StrangerStamp
|
||||
from nucypher.crypto.tls import _read_tls_certificate, _TLS_CURVE, generate_self_signed_certificate
|
||||
from nucypher.crypto.umbral_adapter import (
|
||||
from nucypher_core import (
|
||||
MessageKit,
|
||||
EncryptedTreasureMap,
|
||||
EncryptedKeyFrag,
|
||||
HRAC,
|
||||
TreasureMap,
|
||||
)
|
||||
from nucypher_core.umbral import (
|
||||
SecretKey,
|
||||
PublicKey,
|
||||
Signature,
|
||||
Signer,
|
||||
VerifiedKeyFrag,
|
||||
)
|
||||
|
||||
from nucypher.config.constants import MAX_UPLOAD_CONTENT_LENGTH
|
||||
from nucypher.crypto.signing import SignatureStamp, StrangerStamp
|
||||
from nucypher.crypto.tls import _read_tls_certificate, _TLS_CURVE, generate_self_signed_certificate
|
||||
from nucypher.network.resources import get_static_resources
|
||||
|
||||
|
||||
|
@ -90,7 +97,7 @@ class DecryptingKeypair(Keypair):
|
|||
def __init__(self, *args, **kwargs) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def decrypt(self, message_kit: Union[MessageKit, EncryptedKeyFrag, EncryptedTreasureMap]) -> bytes:
|
||||
def decrypt_message_kit(self, message_kit: MessageKit) -> bytes:
|
||||
"""
|
||||
Decrypt data encrypted with Umbral.
|
||||
|
||||
|
@ -101,6 +108,12 @@ class DecryptingKeypair(Keypair):
|
|||
except ValueError as e:
|
||||
raise self.DecryptionFailed() from e
|
||||
|
||||
def decrypt_kfrag(self, ekfrag: EncryptedKeyFrag, hrac: HRAC, publisher_verifying_key: PublicKey) -> VerifiedKeyFrag:
|
||||
return ekfrag.decrypt(self._privkey, hrac, publisher_verifying_key)
|
||||
|
||||
def decrypt_treasure_map(self, etmap: EncryptedTreasureMap, publisher_verifying_key: PublicKey) -> TreasureMap:
|
||||
return etmap.decrypt(self._privkey, publisher_verifying_key)
|
||||
|
||||
|
||||
class SigningKeypair(Keypair):
|
||||
"""
|
||||
|
|
|
@ -31,6 +31,8 @@ import click
|
|||
from constant_sorrow.constants import KEYSTORE_LOCKED
|
||||
from mnemonic.mnemonic import Mnemonic
|
||||
|
||||
from nucypher_core.umbral import SecretKey, SecretKeyFactory
|
||||
|
||||
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
|
||||
from nucypher.control.emitters import StdoutEmitter
|
||||
from nucypher.crypto.keypairs import HostingKeypair
|
||||
|
@ -50,7 +52,6 @@ from nucypher.crypto.powers import (
|
|||
TLSHostingPower,
|
||||
)
|
||||
from nucypher.crypto.tls import generate_self_signed_certificate
|
||||
from nucypher.crypto.umbral_adapter import SecretKey, SecretKeyFactory
|
||||
|
||||
# HKDF
|
||||
__INFO_BASE = b'NuCypher/'
|
||||
|
|
|
@ -22,11 +22,12 @@ from typing import List, Optional, Tuple
|
|||
from eth_typing.evm import ChecksumAddress
|
||||
from hexbytes import HexBytes
|
||||
|
||||
from nucypher_core.umbral import generate_kfrags, SecretKeyFactory, SecretKey, PublicKey
|
||||
|
||||
from nucypher.blockchain.eth.decorators import validate_checksum_address
|
||||
from nucypher.blockchain.eth.signers.base import Signer
|
||||
from nucypher.crypto import keypairs
|
||||
from nucypher.crypto.keypairs import DecryptingKeypair, SigningKeypair, HostingKeypair
|
||||
from nucypher.crypto.umbral_adapter import generate_kfrags, SecretKeyFactory, SecretKey, PublicKey
|
||||
|
||||
|
||||
class PowerUpError(TypeError):
|
||||
|
@ -234,7 +235,7 @@ class SigningPower(KeyPairBasedPower):
|
|||
class DecryptingPower(KeyPairBasedPower):
|
||||
_keypair_class = DecryptingKeypair
|
||||
not_found_error = NoDecryptingPower
|
||||
provides = ("decrypt",)
|
||||
provides = ("decrypt_message_kit", "decrypt_kfrag", "decrypt_treasure_map")
|
||||
|
||||
|
||||
class DerivedKeyBasedPower(CryptoPowerUp):
|
||||
|
|
|
@ -15,7 +15,7 @@ You should have received a copy of the GNU Affero General Public License
|
|||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from nucypher.crypto.umbral_adapter import Signer
|
||||
from nucypher_core.umbral import Signer
|
||||
|
||||
|
||||
class SignatureStamp(object):
|
||||
|
|
|
@ -30,7 +30,7 @@ from cryptography.hazmat.primitives.serialization import Encoding
|
|||
from cryptography.x509 import Certificate
|
||||
from cryptography.x509.oid import NameOID
|
||||
|
||||
from nucypher.crypto.umbral_adapter import SecretKey
|
||||
from nucypher_core.umbral import SecretKey
|
||||
|
||||
_TLS_CERTIFICATE_ENCODING = Encoding.PEM
|
||||
_TLS_CURVE = ec.SECP384R1
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
"""
|
||||
This file is part of nucypher.
|
||||
|
||||
nucypher is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
nucypher is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
# This module is used to have a single point where the Umbral implementation is chosen.
|
||||
# Do not import Umbral directly, use re-exports from this module.
|
||||
|
||||
|
||||
from umbral import (
|
||||
SecretKey,
|
||||
PublicKey,
|
||||
SecretKeyFactory,
|
||||
Signature,
|
||||
Signer,
|
||||
Capsule,
|
||||
KeyFrag,
|
||||
VerifiedKeyFrag,
|
||||
CapsuleFrag,
|
||||
VerifiedCapsuleFrag,
|
||||
VerificationError,
|
||||
encrypt,
|
||||
decrypt_original,
|
||||
generate_kfrags,
|
||||
reencrypt,
|
||||
decrypt_reencrypted,
|
||||
)
|
|
@ -26,8 +26,9 @@ from eth_account.messages import encode_defunct
|
|||
from eth_keys import KeyAPI as EthKeyAPI
|
||||
from eth_utils.address import to_checksum_address
|
||||
|
||||
from nucypher_core.umbral import PublicKey
|
||||
|
||||
from nucypher.crypto.signing import SignatureStamp
|
||||
from nucypher.crypto.umbral_adapter import PublicKey
|
||||
|
||||
SYSTEM_RAND = SystemRandom()
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@ You should have received a copy of the GNU Affero General Public License
|
|||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from nucypher.crypto.umbral_adapter import PublicKey
|
||||
from nucypher_core.umbral import PublicKey
|
||||
|
||||
from nucypher.datastore.base import DatastoreRecord, RecordField
|
||||
|
||||
|
||||
|
|
|
@ -20,9 +20,10 @@ from http import HTTPStatus
|
|||
import socket
|
||||
import ssl
|
||||
import time
|
||||
from typing import Sequence
|
||||
import requests
|
||||
|
||||
from nucypher.core import MetadataRequest
|
||||
from nucypher_core import MetadataRequest, FleetStateChecksum, NodeMetadata
|
||||
|
||||
from constant_sorrow.constants import CERTIFICATE_NOT_SAVED, EXEMPT_FROM_VERIFICATION
|
||||
from cryptography import x509
|
||||
|
@ -235,8 +236,8 @@ class RestMiddleware:
|
|||
|
||||
def get_nodes_via_rest(self,
|
||||
node,
|
||||
fleet_state_checksum: str,
|
||||
announce_nodes=None):
|
||||
fleet_state_checksum: FleetStateChecksum,
|
||||
announce_nodes: Sequence[NodeMetadata]):
|
||||
|
||||
request = MetadataRequest(fleet_state_checksum=fleet_state_checksum,
|
||||
announce_nodes=announce_nodes)
|
||||
|
|
|
@ -39,7 +39,8 @@ from requests.exceptions import SSLError
|
|||
from twisted.internet import reactor, task
|
||||
from twisted.internet.defer import Deferred
|
||||
|
||||
from nucypher.core import NodeMetadata, MetadataResponse
|
||||
from nucypher_core import NodeMetadata, MetadataResponse, MetadataResponsePayload
|
||||
from nucypher_core.umbral import Signature
|
||||
|
||||
from nucypher.acumen.nicknames import Nickname
|
||||
from nucypher.acumen.perception import FleetSensor
|
||||
|
@ -57,7 +58,6 @@ from nucypher.crypto.powers import (
|
|||
SigningPower,
|
||||
)
|
||||
from nucypher.crypto.signing import SignatureStamp, InvalidSignature
|
||||
from nucypher.crypto.umbral_adapter import Signature
|
||||
from nucypher.crypto.utils import recover_address_eip_191, verify_eip_191
|
||||
from nucypher.network.exceptions import NodeSeemsToBeDown
|
||||
from nucypher.network.middleware import RestMiddleware
|
||||
|
@ -81,8 +81,9 @@ class NodeSprout:
|
|||
"""
|
||||
verified_node = False
|
||||
|
||||
def __init__(self, node_metadata):
|
||||
self._metadata = node_metadata
|
||||
def __init__(self, metadata: NodeMetadata):
|
||||
self._metadata = metadata
|
||||
self._metadata_payload = metadata.payload
|
||||
|
||||
# cached properties
|
||||
self._checksum_address = None
|
||||
|
@ -114,9 +115,13 @@ class NodeSprout:
|
|||
@property
|
||||
def checksum_address(self):
|
||||
if not self._checksum_address:
|
||||
self._checksum_address = to_checksum_address(self._metadata.public_address)
|
||||
self._checksum_address = to_checksum_address(self.canonical_address)
|
||||
return self._checksum_address
|
||||
|
||||
@property
|
||||
def canonical_address(self):
|
||||
return self._metadata_payload.canonical_address
|
||||
|
||||
@property
|
||||
def nickname(self):
|
||||
if not self._nickname:
|
||||
|
@ -126,7 +131,7 @@ class NodeSprout:
|
|||
@property
|
||||
def rest_interface(self):
|
||||
if not self._rest_interface:
|
||||
self._rest_interface = InterfaceInfo(self._metadata.host, self._metadata.port)
|
||||
self._rest_interface = InterfaceInfo(self._metadata_payload.host, self._metadata_payload.port)
|
||||
return self._rest_interface
|
||||
|
||||
def rest_url(self):
|
||||
|
@ -137,48 +142,44 @@ class NodeSprout:
|
|||
|
||||
@property
|
||||
def verifying_key(self):
|
||||
return self._metadata.verifying_key
|
||||
return self._metadata_payload.verifying_key
|
||||
|
||||
@property
|
||||
def encrypting_key(self):
|
||||
return self._metadata.encrypting_key
|
||||
return self._metadata_payload.encrypting_key
|
||||
|
||||
@property
|
||||
def decentralized_identity_evidence(self):
|
||||
return self._metadata.decentralized_identity_evidence or NOT_SIGNED
|
||||
|
||||
@property
|
||||
def public_address(self):
|
||||
return self._metadata.public_address
|
||||
return self._metadata_payload.decentralized_identity_evidence or NOT_SIGNED
|
||||
|
||||
@property
|
||||
def timestamp(self):
|
||||
return maya.MayaDT(self._metadata.timestamp_epoch)
|
||||
return maya.MayaDT(self._metadata_payload.timestamp_epoch)
|
||||
|
||||
@property
|
||||
def stamp(self) -> SignatureStamp:
|
||||
return SignatureStamp(self._metadata.verifying_key)
|
||||
return SignatureStamp(self._metadata_payload.verifying_key)
|
||||
|
||||
@property
|
||||
def domain(self) -> str:
|
||||
return self._metadata.domain
|
||||
return self._metadata_payload.domain
|
||||
|
||||
def finish(self):
|
||||
from nucypher.characters.lawful import Ursula
|
||||
|
||||
crypto_power = CryptoPower()
|
||||
crypto_power.consume_power_up(SigningPower(public_key=self._metadata.verifying_key))
|
||||
crypto_power.consume_power_up(DecryptingPower(public_key=self._metadata.encrypting_key))
|
||||
crypto_power.consume_power_up(SigningPower(public_key=self._metadata_payload.verifying_key))
|
||||
crypto_power.consume_power_up(DecryptingPower(public_key=self._metadata_payload.encrypting_key))
|
||||
|
||||
return Ursula(is_me=False,
|
||||
crypto_power=crypto_power,
|
||||
rest_host=self._metadata.host,
|
||||
rest_port=self._metadata.port,
|
||||
rest_host=self._metadata_payload.host,
|
||||
rest_port=self._metadata_payload.port,
|
||||
checksum_address=self.checksum_address,
|
||||
domain=self._metadata.domain,
|
||||
domain=self._metadata_payload.domain,
|
||||
timestamp=self.timestamp,
|
||||
decentralized_identity_evidence=self.decentralized_identity_evidence,
|
||||
certificate=load_pem_x509_certificate(self._metadata.certificate_bytes, backend=default_backend()),
|
||||
certificate=load_pem_x509_certificate(self._metadata_payload.certificate_bytes, backend=default_backend()),
|
||||
metadata=self._metadata
|
||||
)
|
||||
|
||||
|
@ -787,7 +788,7 @@ class Learner:
|
|||
if isinstance(self, Teacher):
|
||||
announce_nodes = [self.metadata()]
|
||||
else:
|
||||
announce_nodes = None
|
||||
announce_nodes = []
|
||||
|
||||
unresponsive_nodes = set()
|
||||
|
||||
|
@ -855,18 +856,18 @@ class Learner:
|
|||
return
|
||||
|
||||
try:
|
||||
metadata.verify(current_teacher.stamp.as_umbral_pubkey())
|
||||
except InvalidSignature:
|
||||
metadata_payload = metadata.verify(current_teacher.stamp.as_umbral_pubkey())
|
||||
except Exception as e:
|
||||
# TODO (#567): bucket the node as suspicious
|
||||
self.log.warn(
|
||||
f"Invalid signature received from teacher {current_teacher} for MetadataResponse {response.content}")
|
||||
f"Failed to verify MetadataResponse from Teacher {current_teacher} ({e}): {response.content}")
|
||||
return
|
||||
|
||||
# End edge case handling.
|
||||
|
||||
fleet_state_updated = maya.MayaDT(metadata.timestamp_epoch)
|
||||
fleet_state_updated = maya.MayaDT(metadata_payload.timestamp_epoch)
|
||||
|
||||
if not metadata.this_node and not metadata.other_nodes:
|
||||
if not metadata_payload.announce_nodes:
|
||||
# The teacher had the same fleet state
|
||||
self.known_nodes.record_remote_fleet_state(
|
||||
current_teacher.checksum_address,
|
||||
|
@ -876,15 +877,7 @@ class Learner:
|
|||
|
||||
return FLEET_STATES_MATCH
|
||||
|
||||
# Note: There was previously a version check here, but that required iterating through node bytestrings twice,
|
||||
# so it has been removed. When we create a new Ursula bytestring version, let's put the check
|
||||
# somewhere more performant, like mature() or verify_node().
|
||||
|
||||
nodes = (
|
||||
([metadata.this_node] if metadata.this_node else []) +
|
||||
(metadata.other_nodes if metadata.other_nodes else [])
|
||||
)
|
||||
sprouts = [NodeSprout(node) for node in nodes]
|
||||
sprouts = [NodeSprout(node) for node in metadata_payload.announce_nodes]
|
||||
|
||||
for sprout in sprouts:
|
||||
try:
|
||||
|
@ -1033,11 +1026,10 @@ class Teacher:
|
|||
def bytestring_of_known_nodes(self):
|
||||
# TODO (#1537): FleetSensor does metadata-to-byte conversion as well,
|
||||
# we may be able to cache the results there.
|
||||
response = MetadataResponse.author(signer=self.stamp.as_umbral_signer(),
|
||||
timestamp_epoch=self.known_nodes.timestamp.epoch,
|
||||
this_node=self.metadata(),
|
||||
other_nodes=[node.metadata() for node in self.known_nodes],
|
||||
)
|
||||
announce_nodes = [self.metadata()] + [node.metadata() for node in self.known_nodes]
|
||||
response_payload = MetadataResponsePayload(timestamp_epoch=self.known_nodes.timestamp.epoch,
|
||||
announce_nodes=announce_nodes)
|
||||
response = MetadataResponse(self.stamp.as_umbral_signer(), response_payload)
|
||||
return bytes(response)
|
||||
|
||||
#
|
||||
|
@ -1128,7 +1120,7 @@ class Teacher:
|
|||
"""
|
||||
Checks that the interface info is valid for this node's canonical address.
|
||||
"""
|
||||
metadata_is_valid = self._metadata.verify()
|
||||
metadata_is_valid = self.metadata().verify()
|
||||
self.verified_metadata = metadata_is_valid
|
||||
if metadata_is_valid:
|
||||
return True
|
||||
|
@ -1198,11 +1190,14 @@ class Teacher:
|
|||
port=self.rest_interface.port,
|
||||
certificate_filepath=certificate_filepath)
|
||||
|
||||
sprout = NodeSprout(NodeMetadata.from_bytes(response_data))
|
||||
try:
|
||||
sprout = self.from_metadata_bytes(response_data)
|
||||
except Exception as e:
|
||||
raise self.InvalidNode(str(e))
|
||||
|
||||
verifying_keys_match = sprout.verifying_key == self.public_keys(SigningPower)
|
||||
encrypting_keys_match = sprout.encrypting_key == self.public_keys(DecryptingPower)
|
||||
addresses_match = sprout.public_address == self.canonical_public_address
|
||||
addresses_match = sprout.checksum_address == self.checksum_address
|
||||
evidence_matches = sprout.decentralized_identity_evidence == self.__decentralized_identity_evidence
|
||||
|
||||
if not all((encrypting_keys_match, verifying_keys_match, addresses_match, evidence_matches)):
|
||||
|
|
|
@ -20,22 +20,23 @@ import random
|
|||
from typing import Dict, Sequence, List
|
||||
|
||||
from eth_typing.evm import ChecksumAddress
|
||||
from eth_utils import to_checksum_address
|
||||
from twisted.logger import Logger
|
||||
|
||||
from nucypher.core import (
|
||||
from nucypher_core import (
|
||||
TreasureMap,
|
||||
ReencryptionResponse,
|
||||
ReencryptionRequest,
|
||||
RetrievalKit,
|
||||
)
|
||||
|
||||
from nucypher.crypto.signing import InvalidSignature
|
||||
from nucypher.crypto.umbral_adapter import (
|
||||
from nucypher_core.umbral import (
|
||||
Capsule,
|
||||
PublicKey,
|
||||
VerifiedCapsuleFrag,
|
||||
VerificationError,
|
||||
)
|
||||
|
||||
from nucypher.crypto.signing import InvalidSignature
|
||||
from nucypher.network.exceptions import NodeSeemsToBeDown
|
||||
from nucypher.network.nodes import Learner
|
||||
from nucypher.policy.kits import RetrievalResult
|
||||
|
@ -121,7 +122,10 @@ class RetrievalPlan:
|
|||
)
|
||||
|
||||
def results(self) -> List['RetrievalResult']:
|
||||
return [RetrievalResult(self._results[capsule]) for capsule in self._capsules]
|
||||
# TODO (#1995): when that issue is fixed, conversion is no longer needed
|
||||
return [RetrievalResult({to_checksum_address(address): cfrag
|
||||
for address, cfrag in self._results[capsule].items()})
|
||||
for capsule in self._capsules]
|
||||
|
||||
|
||||
class RetrievalWorkOrder:
|
||||
|
@ -155,6 +159,10 @@ class RetrievalClient:
|
|||
self._learner.learn_from_teacher_node()
|
||||
|
||||
ursulas_in_map = treasure_map.destinations.keys()
|
||||
|
||||
# TODO (#1995): when that issue is fixed, conversion is no longer needed
|
||||
ursulas_in_map = [to_checksum_address(address) for address in ursulas_in_map]
|
||||
|
||||
all_known_ursulas = self._learner.known_nodes.addresses()
|
||||
|
||||
# Push all unknown Ursulas from the map in the queue for learning
|
||||
|
@ -236,7 +244,8 @@ class RetrievalClient:
|
|||
self.log.warn(message)
|
||||
raise RuntimeError(message)
|
||||
|
||||
return verified_cfrags
|
||||
return {capsule: vcfrag for capsule, vcfrag
|
||||
in zip(reencryption_request.capsules, verified_cfrags)}
|
||||
|
||||
def retrieve_cfrags(
|
||||
self,
|
||||
|
@ -257,15 +266,19 @@ class RetrievalClient:
|
|||
|
||||
work_order = retrieval_plan.get_work_order()
|
||||
|
||||
if work_order.ursula_address not in self._learner.known_nodes:
|
||||
# TODO (#1995): when that issue is fixed, conversion is no longer needed
|
||||
ursula_checksum_address = to_checksum_address(work_order.ursula_address)
|
||||
|
||||
if ursula_checksum_address not in self._learner.known_nodes:
|
||||
continue
|
||||
|
||||
ursula = self._learner.known_nodes[work_order.ursula_address]
|
||||
reencryption_request = ReencryptionRequest.from_treasure_map(
|
||||
ursula_address=work_order.ursula_address,
|
||||
ursula = self._learner.known_nodes[ursula_checksum_address]
|
||||
reencryption_request = ReencryptionRequest(
|
||||
hrac=treasure_map.hrac,
|
||||
capsules=work_order.capsules,
|
||||
treasure_map=treasure_map,
|
||||
bob_verifying_key=bob_verifying_key)
|
||||
encrypted_kfrag=treasure_map.destinations[work_order.ursula_address],
|
||||
bob_verifying_key=bob_verifying_key,
|
||||
publisher_verifying_key=treasure_map.publisher_verifying_key)
|
||||
|
||||
try:
|
||||
cfrags = self._request_reencryption(ursula=ursula,
|
||||
|
|
|
@ -23,20 +23,19 @@ from pathlib import Path
|
|||
from typing import Tuple
|
||||
|
||||
from constant_sorrow import constants
|
||||
from constant_sorrow.constants import RELAX, NOT_STAKING
|
||||
from constant_sorrow.constants import RELAX
|
||||
from flask import Flask, Response, jsonify, request
|
||||
from mako import exceptions as mako_exceptions
|
||||
from mako.template import Template
|
||||
|
||||
from nucypher.core import (
|
||||
from nucypher_core import (
|
||||
ReencryptionRequest,
|
||||
RevocationOrder,
|
||||
NodeMetadata,
|
||||
MetadataRequest,
|
||||
MetadataResponse,
|
||||
MetadataResponsePayload,
|
||||
)
|
||||
|
||||
from nucypher.blockchain.eth.utils import period_to_epoch
|
||||
from nucypher.config.constants import MAX_UPLOAD_CONTENT_LENGTH
|
||||
from nucypher.crypto.keypairs import DecryptingKeypair
|
||||
from nucypher.crypto.signing import InvalidSignature
|
||||
|
@ -81,7 +80,6 @@ class ProxyRESTServer:
|
|||
def make_rest_app(
|
||||
db_filepath: Path,
|
||||
this_node,
|
||||
domain,
|
||||
log: Logger = Logger("http-application-layer")
|
||||
) -> Tuple[Flask, Datastore]:
|
||||
"""
|
||||
|
@ -98,12 +96,12 @@ def make_rest_app(
|
|||
|
||||
log.info("Starting datastore {}".format(db_filepath))
|
||||
datastore = Datastore(db_filepath)
|
||||
rest_app = _make_rest_app(weakref.proxy(datastore), weakref.proxy(this_node), domain, log)
|
||||
rest_app = _make_rest_app(weakref.proxy(datastore), weakref.proxy(this_node), log)
|
||||
|
||||
return rest_app, datastore
|
||||
|
||||
|
||||
def _make_rest_app(datastore: Datastore, this_node, domain: str, log: Logger) -> Flask:
|
||||
def _make_rest_app(datastore: Datastore, this_node, log: Logger) -> Flask:
|
||||
|
||||
# TODO: Avoid circular imports :-(
|
||||
from nucypher.characters.lawful import Alice, Bob, Ursula
|
||||
|
@ -145,13 +143,21 @@ def _make_rest_app(datastore: Datastore, this_node, domain: str, log: Logger) ->
|
|||
# log.debug("Learner already knew fleet state {}; doing nothing.".format(learner_fleet_state)) # 1712
|
||||
headers = {'Content-Type': 'application/octet-stream'}
|
||||
# No nodes in the response: same fleet state
|
||||
response = MetadataResponse.author(signer=this_node.stamp.as_umbral_signer(),
|
||||
timestamp_epoch=this_node.known_nodes.timestamp.epoch)
|
||||
response_payload = MetadataResponsePayload(timestamp_epoch=this_node.known_nodes.timestamp.epoch,
|
||||
announce_nodes=[])
|
||||
response = MetadataResponse(this_node.stamp.as_umbral_signer(),
|
||||
response_payload)
|
||||
return Response(bytes(response), headers=headers)
|
||||
|
||||
if metadata_request.announce_nodes:
|
||||
for node in metadata_request.announce_nodes:
|
||||
this_node.remember_node(NodeSprout(node))
|
||||
for metadata in metadata_request.announce_nodes:
|
||||
try:
|
||||
metadata.verify()
|
||||
except Exception:
|
||||
# inconsistent metadata
|
||||
pass
|
||||
else:
|
||||
this_node.remember_node(NodeSprout(metadata))
|
||||
|
||||
# TODO: generate a new fleet state here?
|
||||
|
||||
|
@ -183,28 +189,19 @@ def _make_rest_app(datastore: Datastore, this_node, domain: str, log: Logger) ->
|
|||
|
||||
# Verify & Decrypt KFrag Payload
|
||||
try:
|
||||
authorized_kfrag = this_node._decrypt_kfrag(reenc_request.encrypted_kfrag)
|
||||
verified_kfrag = this_node._decrypt_kfrag(reenc_request.encrypted_kfrag, hrac, publisher_verifying_key)
|
||||
except DecryptingKeypair.DecryptionFailed:
|
||||
# TODO: don't we want to record suspicious activities here too?
|
||||
return Response(response="EncryptedKeyFrag decryption failed.", status=HTTPStatus.FORBIDDEN)
|
||||
except Exception as e:
|
||||
message = f'{bob_identity_message} Invalid EncryptedKeyFrag: {e}.'
|
||||
log.info(message)
|
||||
# TODO (#567): bucket the node as suspicious
|
||||
return Response(message, status=HTTPStatus.BAD_REQUEST)
|
||||
|
||||
# Verify KFrag Authorization (offchain)
|
||||
try:
|
||||
verified_kfrag = authorized_kfrag.verify(hrac=hrac,
|
||||
publisher_verifying_key=publisher_verifying_key)
|
||||
except InvalidSignature as e:
|
||||
message = f'{bob_identity_message} Invalid signature for KeyFrag: {e}.'
|
||||
log.info(message)
|
||||
# TODO (#567): bucket the node as suspicious
|
||||
return Response(message, status=HTTPStatus.UNAUTHORIZED) # 401 - Unauthorized
|
||||
except Exception as e:
|
||||
message = f'{bob_identity_message} Invalid KeyFrag: {e}.'
|
||||
message = f'{bob_identity_message} Invalid EncryptedKeyFrag: {e}.'
|
||||
log.info(message)
|
||||
# TODO (#567): bucket the node as suspicious
|
||||
return Response(message, status=HTTPStatus.BAD_REQUEST)
|
||||
|
||||
if not this_node.federated_only:
|
||||
|
|
|
@ -35,7 +35,7 @@ def character_span(character):
|
|||
<td>
|
||||
<span>${state.population} nodes</span>
|
||||
<br/>
|
||||
<span class="checksum">${state.checksum[0:8]}</span>
|
||||
<span class="checksum">${bytes(state.checksum)[0:8]}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -27,11 +27,12 @@ from bytestring_splitter import BytestringKwargifier, VariableLengthBytestring
|
|||
from constant_sorrow.constants import ALICE, BOB, NO_SIGNATURE
|
||||
from hexbytes.main import HexBytes
|
||||
|
||||
from nucypher_core.umbral import PublicKey
|
||||
|
||||
from nucypher.characters.base import Character
|
||||
from nucypher.characters.lawful import Alice, Bob
|
||||
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
|
||||
from nucypher.crypto.powers import DecryptingPower, SigningPower
|
||||
from nucypher.crypto.umbral_adapter import PublicKey
|
||||
|
||||
|
||||
class Card:
|
||||
|
|
|
@ -18,11 +18,11 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
from typing import Dict, Set, Union
|
||||
|
||||
from eth_utils import to_canonical_address
|
||||
from eth_typing import ChecksumAddress
|
||||
|
||||
from nucypher.core import MessageKit, RetrievalKit
|
||||
|
||||
from nucypher.crypto.umbral_adapter import PublicKey, VerifiedCapsuleFrag, SecretKey
|
||||
from nucypher_core import MessageKit, RetrievalKit
|
||||
from nucypher_core.umbral import PublicKey, VerifiedCapsuleFrag, SecretKey
|
||||
|
||||
|
||||
class PolicyMessageKit:
|
||||
|
@ -47,10 +47,12 @@ class PolicyMessageKit:
|
|||
self._result = result
|
||||
|
||||
def as_retrieval_kit(self) -> RetrievalKit:
|
||||
return RetrievalKit(self.message_kit.capsule, self._result.addresses())
|
||||
return RetrievalKit(self.message_kit.capsule, self._result.canonical_addresses())
|
||||
|
||||
def decrypt(self, sk: SecretKey) -> bytes:
|
||||
return self.message_kit.decrypt_reencrypted(sk, self.policy_encrypting_key, self._result.cfrags.values())
|
||||
return self.message_kit.decrypt_reencrypted(sk,
|
||||
self.policy_encrypting_key,
|
||||
list(self._result.cfrags.values()))
|
||||
|
||||
def is_decryptable_by_receiver(self) -> bool:
|
||||
return len(self._result.cfrags) >= self.threshold
|
||||
|
@ -75,8 +77,9 @@ class RetrievalResult:
|
|||
def __init__(self, cfrags: Dict[ChecksumAddress, VerifiedCapsuleFrag]):
|
||||
self.cfrags = cfrags
|
||||
|
||||
def addresses(self) -> Set[ChecksumAddress]:
|
||||
return set(self.cfrags)
|
||||
def canonical_addresses(self) -> Set[bytes]:
|
||||
# TODO (#1995): propagate this to use canonical addresses everywhere
|
||||
return set([to_canonical_address(address) for address in self.cfrags])
|
||||
|
||||
def with_result(self, result: 'RetrievalResult') -> 'RetrievalResult':
|
||||
"""
|
||||
|
|
|
@ -22,9 +22,10 @@ from typing import Sequence, Optional, Iterable, List
|
|||
import maya
|
||||
from eth_typing.evm import ChecksumAddress
|
||||
|
||||
from nucypher.core import HRAC, TreasureMap
|
||||
from nucypher_core.umbral import PublicKey, VerifiedKeyFrag
|
||||
|
||||
from nucypher_core import HRAC, TreasureMap
|
||||
from nucypher.crypto.powers import DecryptingPower
|
||||
from nucypher.crypto.umbral_adapter import PublicKey, VerifiedKeyFrag
|
||||
from nucypher.network.middleware import RestMiddleware
|
||||
from nucypher.policy.reservoir import (
|
||||
make_federated_staker_reservoir,
|
||||
|
@ -97,9 +98,9 @@ class Policy(ABC):
|
|||
self.kfrags = kfrags
|
||||
self.public_key = public_key
|
||||
self.expiration = expiration
|
||||
self.hrac = HRAC.derive(publisher_verifying_key=self.publisher.stamp.as_umbral_pubkey(),
|
||||
bob_verifying_key=self.bob.stamp.as_umbral_pubkey(),
|
||||
label=self.label)
|
||||
self.hrac = HRAC(publisher_verifying_key=self.publisher.stamp.as_umbral_pubkey(),
|
||||
bob_verifying_key=self.bob.stamp.as_umbral_pubkey(),
|
||||
label=self.label)
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.__class__.__name__}:{bytes(self.hrac).hex()[:6]}"
|
||||
|
@ -181,15 +182,15 @@ class Policy(ABC):
|
|||
self._publish(ursulas=ursulas)
|
||||
|
||||
assigned_kfrags = {
|
||||
ursula.checksum_address: (ursula.public_keys(DecryptingPower), vkfrag)
|
||||
ursula.canonical_address: (ursula.public_keys(DecryptingPower), vkfrag)
|
||||
for ursula, vkfrag in zip(ursulas, self.kfrags)
|
||||
}
|
||||
|
||||
treasure_map = TreasureMap.construct_by_publisher(signer=self.publisher.stamp.as_umbral_signer(),
|
||||
hrac=self.hrac,
|
||||
policy_encrypting_key=self.public_key,
|
||||
assigned_kfrags=assigned_kfrags,
|
||||
threshold=self.threshold)
|
||||
treasure_map = TreasureMap(signer=self.publisher.stamp.as_umbral_signer(),
|
||||
hrac=self.hrac,
|
||||
policy_encrypting_key=self.public_key,
|
||||
assigned_kfrags=assigned_kfrags,
|
||||
threshold=self.threshold)
|
||||
|
||||
enc_treasure_map = treasure_map.encrypt(signer=self.publisher.stamp.as_umbral_signer(),
|
||||
recipient_key=self.bob.public_keys(DecryptingPower))
|
||||
|
|
|
@ -15,8 +15,10 @@ You should have received a copy of the GNU Affero General Public License
|
|||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from eth_typing.evm import ChecksumAddress
|
||||
from eth_utils import to_checksum_address, to_canonical_address
|
||||
|
||||
from nucypher.core import RevocationOrder
|
||||
from nucypher_core import RevocationOrder
|
||||
from nucypher.crypto.signing import SignatureStamp
|
||||
|
||||
|
||||
|
@ -25,16 +27,17 @@ class RevocationKit:
|
|||
def __init__(self, treasure_map, signer: SignatureStamp):
|
||||
# TODO: move to core and make a method of TreasureMap?
|
||||
self.revocations = dict()
|
||||
for node_id, encrypted_kfrag in treasure_map:
|
||||
self.revocations[node_id] = RevocationOrder.author(signer=signer.as_umbral_signer(),
|
||||
ursula_address=node_id,
|
||||
for ursula_address, encrypted_kfrag in treasure_map.destinations.items():
|
||||
self.revocations[ursula_address] = RevocationOrder(signer=signer.as_umbral_signer(),
|
||||
ursula_address=ursula_address,
|
||||
encrypted_kfrag=encrypted_kfrag)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.revocations.values())
|
||||
|
||||
def __getitem__(self, node_id):
|
||||
return self.revocations[node_id]
|
||||
def __getitem__(self, ursula_address: ChecksumAddress):
|
||||
# TODO (#1995): when that issue is fixed, conversion is no longer needed
|
||||
return self.revocations[to_canonical_address(ursula_address)]
|
||||
|
||||
def __len__(self):
|
||||
return len(self.revocations)
|
||||
|
@ -45,8 +48,9 @@ class RevocationKit:
|
|||
@property
|
||||
def revokable_addresses(self):
|
||||
"""Returns a Set of revokable addresses in the checksum address formatting"""
|
||||
return set(self.revocations.keys())
|
||||
# TODO (#1995): when that issue is fixed, conversion is no longer needed
|
||||
return set([to_checksum_address(address) for address in self.revocations.keys()])
|
||||
|
||||
def add_confirmation(self, node_id, signed_receipt):
|
||||
def add_confirmation(self, ursula_address, signed_receipt):
|
||||
"""Adds a signed confirmation of Ursula's ability to revoke the node."""
|
||||
raise NotImplementedError
|
||||
|
|
|
@ -18,10 +18,10 @@ from typing import List, Optional
|
|||
|
||||
from eth_typing import ChecksumAddress
|
||||
|
||||
from nucypher.core import TreasureMap, RetrievalKit
|
||||
from nucypher_core import TreasureMap, RetrievalKit
|
||||
from nucypher_core.umbral import PublicKey
|
||||
|
||||
from nucypher.control.interfaces import ControlInterface, attach_schema
|
||||
from nucypher.crypto.umbral_adapter import PublicKey
|
||||
from nucypher.utilities.porter.control.specifications import porter_schema
|
||||
|
||||
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
"""
|
||||
from marshmallow import fields
|
||||
|
||||
from nucypher.core import RetrievalKit as RetrievalKitClass
|
||||
from nucypher_core import RetrievalKit as RetrievalKitClass
|
||||
from nucypher_core.umbral import CapsuleFrag as CapsuleFragClass
|
||||
|
||||
from nucypher.control.specifications.base import BaseSchema
|
||||
from nucypher.control.specifications.exceptions import InvalidInputData
|
||||
from nucypher.control.specifications.fields import Base64BytesRepresentation
|
||||
from nucypher.crypto.umbral_adapter import CapsuleFrag as CapsuleFragClass
|
||||
from nucypher.utilities.porter.control.specifications.fields import UrsulaChecksumAddress
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,8 @@ from constant_sorrow.constants import NO_BLOCKCHAIN_CONNECTION, NO_CONTROL_PROTO
|
|||
from eth_typing import ChecksumAddress
|
||||
from flask import request, Response
|
||||
|
||||
from nucypher.core import TreasureMap, RetrievalKit
|
||||
from nucypher_core import TreasureMap, RetrievalKit
|
||||
from nucypher_core.umbral import PublicKey
|
||||
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, StakingEscrowAgent
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
|
||||
|
@ -29,7 +30,6 @@ from nucypher.blockchain.eth.registry import BaseContractRegistry, InMemoryContr
|
|||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.control.controllers import JSONRPCController, WebController
|
||||
from nucypher.crypto.powers import DecryptingPower
|
||||
from nucypher.crypto.umbral_adapter import PublicKey
|
||||
from nucypher.network.nodes import Learner
|
||||
from nucypher.network.retrieval import RetrievalClient
|
||||
from nucypher.policy.kits import RetrievalResult
|
||||
|
|
|
@ -1,179 +0,0 @@
|
|||
"""
|
||||
This file is part of nucypher.
|
||||
|
||||
nucypher is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
nucypher is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
from abc import abstractmethod, ABC
|
||||
import re
|
||||
from typing import Dict, Tuple, Callable
|
||||
|
||||
|
||||
class Versioned(ABC):
|
||||
"""Base class for serializable entities"""
|
||||
|
||||
_VERSION_PARTS = 2
|
||||
_VERSION_PART_SIZE = 2 # bytes
|
||||
_BRAND_SIZE = 4
|
||||
_VERSION_SIZE = _VERSION_PART_SIZE * _VERSION_PARTS
|
||||
_HEADER_SIZE = _BRAND_SIZE + _VERSION_SIZE
|
||||
|
||||
class InvalidHeader(ValueError):
|
||||
"""Raised when an unexpected or invalid bytes header is encountered."""
|
||||
|
||||
class IncompatibleVersion(ValueError):
|
||||
"""Raised when attempting to deserialize incompatible bytes"""
|
||||
|
||||
class Empty(ValueError):
|
||||
"""Raised when 0 bytes are remaining after parsing the header."""
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def _brand(cls) -> bytes:
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def _version(cls) -> Tuple[int, int]:
|
||||
"""tuple(major, minor)"""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def version_string(cls) -> str:
|
||||
major, minor = cls._version()
|
||||
return f'{major}.{minor}'
|
||||
|
||||
#
|
||||
# Serialize
|
||||
#
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
return self._header() + self._payload()
|
||||
|
||||
@classmethod
|
||||
def _header(cls) -> bytes:
|
||||
"""The entire bytes header to prepend to the instance payload."""
|
||||
major, minor = cls._version()
|
||||
major_bytes = major.to_bytes(cls._VERSION_PART_SIZE, 'big')
|
||||
minor_bytes = minor.to_bytes(cls._VERSION_PART_SIZE, 'big')
|
||||
header = cls._brand() + major_bytes + minor_bytes
|
||||
return header
|
||||
|
||||
@abstractmethod
|
||||
def _payload(self) -> bytes:
|
||||
"""The unbranded and unversioned bytes-serialized representation of this instance."""
|
||||
raise NotImplementedError
|
||||
|
||||
#
|
||||
# Deserialize
|
||||
#
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def _from_bytes_current(cls, data):
|
||||
"""The current deserializer"""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def _old_version_handlers(cls) -> Dict[Tuple[int, int], Callable]:
|
||||
"""Old deserializer callables keyed by version."""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def take(cls, data: bytes):
|
||||
"""
|
||||
Deserializes the object from the given bytestring
|
||||
and returns the object and the remainder of the bytestring.
|
||||
"""
|
||||
brand, version, payload = cls._parse_header(data)
|
||||
version = cls._resolve_version(version=version)
|
||||
handlers = cls._deserializers()
|
||||
obj, remainder = handlers[version](payload)
|
||||
return obj, remainder
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, data: bytes):
|
||||
""""Public deserialization API"""
|
||||
obj, remainder = cls.take(data)
|
||||
if remainder:
|
||||
raise ValueError(f"{len(remainder)} bytes remaining after deserializing {cls}")
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def _resolve_version(cls, version: Tuple[int, int]) -> Tuple[int, int]:
|
||||
|
||||
# Unpack version metadata
|
||||
bytrestring_major, bytrestring_minor = version
|
||||
latest_major_version, latest_minor_version = cls._version()
|
||||
|
||||
# Enforce major version compatibility
|
||||
if not bytrestring_major == latest_major_version:
|
||||
message = f'Incompatible versioned bytes for {cls.__name__}. ' \
|
||||
f'Compatible version is {latest_major_version}.x, ' \
|
||||
f'Got {bytrestring_major}.{bytrestring_minor}.'
|
||||
raise cls.IncompatibleVersion(message)
|
||||
|
||||
# Enforce minor version compatibility.
|
||||
# Pass future minor versions to the latest minor handler.
|
||||
if bytrestring_minor >= latest_minor_version:
|
||||
version = cls._version()
|
||||
|
||||
return version
|
||||
|
||||
@classmethod
|
||||
def _parse_header(cls, data: bytes) -> Tuple[bytes, Tuple[int, int], bytes]:
|
||||
if len(data) < cls._HEADER_SIZE:
|
||||
# handles edge case when input is too short.
|
||||
raise ValueError(f'Invalid bytes for {cls.__name__}.')
|
||||
brand = cls._parse_brand(data)
|
||||
version = cls._parse_version(data)
|
||||
payload = cls._parse_payload(data)
|
||||
return brand, version, payload
|
||||
|
||||
@classmethod
|
||||
def _parse_brand(cls, data: bytes) -> bytes:
|
||||
brand = data[:cls._BRAND_SIZE]
|
||||
if brand != cls._brand():
|
||||
error = f"Incorrect brand. Expected {cls._brand()}, Got {brand}."
|
||||
if not re.fullmatch(rb'\w+', brand):
|
||||
# unversioned entities for older versions will most likely land here.
|
||||
error = f"Incompatible bytes for {cls.__name__}."
|
||||
raise cls.InvalidHeader(error)
|
||||
return brand
|
||||
|
||||
@classmethod
|
||||
def _parse_version(cls, data: bytes) -> Tuple[int, int]:
|
||||
version_data = data[cls._BRAND_SIZE:cls._HEADER_SIZE]
|
||||
major, minor = version_data[:cls._VERSION_PART_SIZE], version_data[cls._VERSION_PART_SIZE:]
|
||||
major, minor = int.from_bytes(major, 'big'), int.from_bytes(minor, 'big')
|
||||
version = major, minor
|
||||
return version
|
||||
|
||||
@classmethod
|
||||
def _parse_payload(cls, data: bytes) -> bytes:
|
||||
payload = data[cls._HEADER_SIZE:]
|
||||
if len(payload) == 0:
|
||||
raise ValueError(f'No content to deserialize {cls.__name__}.')
|
||||
return payload
|
||||
|
||||
@classmethod
|
||||
def _deserializers(cls) -> Dict[Tuple[int, int], Callable]:
|
||||
"""Return a dict of all known deserialization handlers for this class keyed by version"""
|
||||
return {cls._version(): cls._from_bytes_current, **cls._old_version_handlers()}
|
||||
|
||||
|
||||
# Collects the brands of every serializable entity, potentially useful for documentation.
|
||||
# SERIALIZABLE_ENTITIES = {v.__class__.__name__: v._brand() for v in Versioned.__subclasses__()}
|
|
@ -104,3 +104,4 @@ web3==5.12.3
|
|||
websockets==8.1; python_full_version >= '3.6.1'
|
||||
werkzeug==2.0.1; python_version >= '3.6'
|
||||
zope.interface==5.4.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
||||
nucypher-core==0.0.1; python_version >= '3.7'
|
||||
|
|
|
@ -18,6 +18,8 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
import pytest
|
||||
|
||||
from nucypher_core.umbral import SecretKeyFactory, Signer
|
||||
|
||||
from nucypher.blockchain.eth.actors import Investigator, Staker
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, NucypherTokenAgent, StakingEscrowAgent
|
||||
from nucypher.blockchain.eth.constants import NULL_ADDRESS
|
||||
|
@ -26,7 +28,6 @@ from nucypher.blockchain.eth.token import NU
|
|||
from nucypher.config.constants import TEMPORARY_DOMAIN
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
from nucypher.crypto.signing import SignatureStamp
|
||||
from nucypher.crypto.umbral_adapter import SecretKeyFactory, Signer
|
||||
|
||||
|
||||
def mock_ursula(testerchain, account, mocker):
|
||||
|
|
|
@ -18,6 +18,8 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
import pytest
|
||||
|
||||
from nucypher_core.umbral import SecretKeyFactory, Signer
|
||||
|
||||
from nucypher.blockchain.eth.actors import NucypherTokenActor, Staker
|
||||
from nucypher.blockchain.eth.agents import (
|
||||
AdjudicatorAgent,
|
||||
|
@ -31,7 +33,6 @@ from nucypher.blockchain.eth.token import NU
|
|||
from nucypher.config.constants import TEMPORARY_DOMAIN
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
from nucypher.crypto.signing import SignatureStamp
|
||||
from nucypher.crypto.umbral_adapter import SecretKeyFactory, Signer
|
||||
|
||||
|
||||
def mock_ursula(testerchain, account, mocker):
|
||||
|
|
|
@ -30,7 +30,7 @@ def test_treasure_map(enacted_blockchain_policy):
|
|||
assert serialized == b64encode(bytes(treasure_map)).decode()
|
||||
|
||||
deserialized = field._deserialize(value=serialized, attr=None, data=None)
|
||||
assert deserialized == treasure_map
|
||||
assert bytes(deserialized) == bytes(treasure_map)
|
||||
|
||||
with pytest.raises(InvalidInputData):
|
||||
field._deserialize(value=b64encode(b"TreasureMap").decode(), attr=None, data=None)
|
||||
|
|
|
@ -23,7 +23,7 @@ import maya
|
|||
import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
from nucypher.core import MessageKit, EncryptedTreasureMap
|
||||
from nucypher_core import MessageKit, EncryptedTreasureMap
|
||||
|
||||
import nucypher
|
||||
from nucypher.crypto.powers import DecryptingPower
|
||||
|
|
|
@ -21,7 +21,7 @@ import datetime
|
|||
import maya
|
||||
import pytest
|
||||
|
||||
from nucypher.core import EncryptedKeyFrag
|
||||
from nucypher_core import EncryptedKeyFrag
|
||||
|
||||
from nucypher.crypto.utils import keccak_digest
|
||||
|
||||
|
@ -48,8 +48,8 @@ def test_decentralized_grant(blockchain_alice, blockchain_bob, blockchain_ursula
|
|||
|
||||
# Let's look at the destinations.
|
||||
for ursula in blockchain_ursulas:
|
||||
if ursula.checksum_address in treasure_map.destinations:
|
||||
kfrag_kit = treasure_map.destinations[ursula.checksum_address]
|
||||
if ursula.canonical_address in treasure_map.destinations:
|
||||
kfrag_kit = treasure_map.destinations[ursula.canonical_address]
|
||||
|
||||
# TODO: try to decrypt?
|
||||
assert isinstance(kfrag_kit, EncryptedKeyFrag)
|
||||
|
|
|
@ -83,7 +83,7 @@ def test_vladimir_cannot_verify_interface_with_ursulas_signing_key(blockchain_ur
|
|||
vladimir = remote_vladimir(target_ursula=his_target)
|
||||
|
||||
# Now, even though his public signing key matches Ursulas...
|
||||
assert vladimir.metadata().verifying_key == his_target.stamp.as_umbral_pubkey()
|
||||
assert vladimir.metadata().payload.verifying_key == his_target.stamp.as_umbral_pubkey()
|
||||
|
||||
# ...he is unable to pretend that his interface is valid
|
||||
# because the validity check contains the canonical public address as part of its message.
|
||||
|
|
|
@ -29,7 +29,7 @@ import pytest
|
|||
from twisted.internet import threads
|
||||
from web3 import Web3
|
||||
|
||||
from nucypher.core import MessageKit, EncryptedTreasureMap
|
||||
from nucypher_core import MessageKit, EncryptedTreasureMap
|
||||
|
||||
from nucypher.cli.main import nucypher_cli
|
||||
from nucypher.config.characters import AliceConfiguration, BobConfiguration
|
||||
|
|
|
@ -21,8 +21,9 @@ import pytest
|
|||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from nucypher_core.umbral import SecretKey
|
||||
|
||||
from nucypher.cli.main import nucypher_cli
|
||||
from nucypher.crypto.umbral_adapter import SecretKey
|
||||
from nucypher.policy.identity import Card
|
||||
|
||||
|
||||
|
|
|
@ -15,8 +15,9 @@
|
|||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from nucypher_core.umbral import SecretKey
|
||||
|
||||
from nucypher.cli.main import nucypher_cli
|
||||
from nucypher.crypto.umbral_adapter import SecretKey
|
||||
|
||||
|
||||
def test_enrico_encrypt(click_runner):
|
||||
|
|
|
@ -622,7 +622,7 @@ def test_collect_rewards_integration(click_runner,
|
|||
# Ensure that the handpicked Ursula was selected for the policy
|
||||
treasure_map = blockchain_bob._decrypt_treasure_map(blockchain_policy.treasure_map,
|
||||
blockchain_policy.publisher_verifying_key)
|
||||
assert ursula.checksum_address in treasure_map.destinations
|
||||
assert ursula.canonical_address in treasure_map.destinations
|
||||
|
||||
# Bob learns about the new staker and joins the policy
|
||||
blockchain_bob.start_learning_loop()
|
||||
|
|
|
@ -19,7 +19,7 @@ import pytest
|
|||
from twisted.logger import LogLevel, globalLogPublisher
|
||||
from constant_sorrow.constants import NOT_SIGNED
|
||||
|
||||
from nucypher.core import MetadataResponse
|
||||
from nucypher_core import MetadataResponse, MetadataResponsePayload
|
||||
|
||||
from nucypher.acumen.perception import FleetSensor
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN
|
||||
|
@ -74,8 +74,10 @@ def test_blockchain_ursula_stamp_verification_tolerance(blockchain_ursulas, mock
|
|||
|
||||
def bad_bytestring_of_known_nodes():
|
||||
# Signing with the learner's signer instead of the teacher's signer
|
||||
response = MetadataResponse.author(signer=lonely_blockchain_learner.stamp.as_umbral_signer(),
|
||||
timestamp_epoch=blockchain_teacher.known_nodes.timestamp.epoch)
|
||||
response_payload = MetadataResponsePayload(timestamp_epoch=blockchain_teacher.known_nodes.timestamp.epoch,
|
||||
announce_nodes=[])
|
||||
response = MetadataResponse(signer=lonely_blockchain_learner.stamp.as_umbral_signer(),
|
||||
payload=response_payload)
|
||||
return bytes(response)
|
||||
|
||||
mocker.patch.object(blockchain_teacher, 'bytestring_of_known_nodes', bad_bytestring_of_known_nodes)
|
||||
|
@ -87,7 +89,7 @@ def test_blockchain_ursula_stamp_verification_tolerance(blockchain_ursulas, mock
|
|||
assert len(warnings) == 2
|
||||
warning = warnings[1]['log_format']
|
||||
assert str(blockchain_teacher) in warning
|
||||
assert "Invalid signature received from teacher" in warning # TODO: Cleanup logging templates
|
||||
assert "Failed to verify MetadataResponse from Teacher" in warning # TODO: Cleanup logging templates
|
||||
|
||||
|
||||
@pytest.mark.skip("See Issue #1075") # TODO: Issue #1075
|
||||
|
|
|
@ -20,7 +20,7 @@ import os
|
|||
from base64 import b64encode
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from nucypher.core import RetrievalKit
|
||||
from nucypher_core import RetrievalKit
|
||||
|
||||
from nucypher.characters.lawful import Enrico
|
||||
from nucypher.crypto.powers import DecryptingPower
|
||||
|
@ -147,7 +147,7 @@ def test_retrieve_cfrags(blockchain_porter,
|
|||
|
||||
assert policy_message_kit.is_decryptable_by_receiver()
|
||||
|
||||
cleartext = blockchain_bob._crypto_power.power_ups(DecryptingPower).keypair.decrypt(policy_message_kit)
|
||||
cleartext = blockchain_bob._crypto_power.power_ups(DecryptingPower).keypair.decrypt_message_kit(policy_message_kit)
|
||||
assert cleartext == original_message
|
||||
|
||||
#
|
||||
|
|
|
@ -21,11 +21,12 @@ from eth_tester.exceptions import TransactionFailed
|
|||
from eth_utils import to_canonical_address, to_wei
|
||||
from web3.contract import Contract
|
||||
|
||||
from nucypher_core.umbral import SecretKey, Signer
|
||||
|
||||
from nucypher.blockchain.economics import BaseEconomics
|
||||
from nucypher.blockchain.eth.constants import NULL_ADDRESS, POLICY_ID_LENGTH
|
||||
from nucypher.crypto.utils import sha256_digest
|
||||
from nucypher.crypto.signing import SignatureStamp
|
||||
from nucypher.crypto.umbral_adapter import SecretKey, Signer
|
||||
from nucypher.utilities.ethereum import to_32byte_hex
|
||||
|
||||
|
||||
|
|
|
@ -28,7 +28,8 @@ from eth_keys import KeyAPI as EthKeyAPI
|
|||
from eth_tester.exceptions import TransactionFailed
|
||||
from eth_utils import to_canonical_address, to_checksum_address, to_normalized_address
|
||||
|
||||
from nucypher.crypto.umbral_adapter import SecretKey, PublicKey, Signer, Signature
|
||||
from nucypher_core.umbral import SecretKey, PublicKey, Signer, Signature
|
||||
|
||||
from nucypher.crypto.utils import (
|
||||
canonical_address_from_umbral_key,
|
||||
keccak_digest,
|
||||
|
|
|
@ -17,9 +17,12 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from eth_tester.exceptions import TransactionFailed
|
||||
from nucypher.crypto.umbral_adapter import Signer, SecretKey, generate_kfrags, encrypt, reencrypt
|
||||
import pytest
|
||||
|
||||
from nucypher_core import MessageKit
|
||||
from nucypher_core.umbral import Signer, SecretKey, generate_kfrags, reencrypt
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
|
@ -44,7 +47,7 @@ def fragments():
|
|||
sign_delegating_key=False,
|
||||
sign_receiving_key=False)
|
||||
|
||||
capsule, _ciphertext = encrypt(delegating_pubkey, b'unused')
|
||||
capsule = MessageKit(delegating_pubkey, b'unused').capsule
|
||||
cfrag = reencrypt(capsule, kfrags[0])
|
||||
return capsule, cfrag
|
||||
|
||||
|
|
|
@ -93,7 +93,6 @@ from tests.mock.performance_mocks import (
|
|||
mock_record_fleet_state,
|
||||
mock_remember_node,
|
||||
mock_rest_app_creation,
|
||||
mock_secret_source,
|
||||
mock_verify_node
|
||||
)
|
||||
from tests.utils.blockchain import TesterBlockchain, token_airdrop
|
||||
|
@ -940,7 +939,6 @@ def get_random_checksum_address():
|
|||
def fleet_of_highperf_mocked_ursulas(ursula_federated_test_config, request):
|
||||
|
||||
mocks = (
|
||||
mock_secret_source(),
|
||||
mock_cert_storage,
|
||||
mock_cert_loading,
|
||||
mock_rest_app_creation,
|
||||
|
|
|
@ -24,7 +24,7 @@ import maya
|
|||
import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
from nucypher.core import MessageKit, EncryptedTreasureMap
|
||||
from nucypher_core import MessageKit, EncryptedTreasureMap
|
||||
|
||||
import nucypher
|
||||
from nucypher.crypto.powers import DecryptingPower
|
||||
|
|
|
@ -19,7 +19,7 @@ import pytest
|
|||
import pytest_twisted
|
||||
from twisted.internet import threads
|
||||
|
||||
from nucypher.core import RetrievalKit
|
||||
from nucypher_core import RetrievalKit
|
||||
|
||||
from nucypher.characters.lawful import Enrico, Bob
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN
|
||||
|
@ -52,7 +52,7 @@ def test_retrieval_kit(enacted_federated_policy, federated_ursulas):
|
|||
messages, message_kits = _make_message_kits(enacted_federated_policy.public_key)
|
||||
|
||||
capsule = message_kits[0].capsule
|
||||
addresses = [ursula.checksum_address for ursula in list(federated_ursulas)[:2]]
|
||||
addresses = {ursula.canonical_address for ursula in list(federated_ursulas)[:2]}
|
||||
|
||||
retrieval_kit = RetrievalKit(capsule, addresses)
|
||||
serialized = bytes(retrieval_kit)
|
||||
|
|
|
@ -21,7 +21,7 @@ import datetime
|
|||
import maya
|
||||
import pytest
|
||||
|
||||
from nucypher.core import EncryptedKeyFrag, RevocationOrder
|
||||
from nucypher_core import EncryptedKeyFrag, RevocationOrder
|
||||
|
||||
from nucypher.characters.lawful import Enrico
|
||||
from nucypher.crypto.utils import keccak_digest
|
||||
|
@ -48,8 +48,8 @@ def test_federated_grant(federated_alice, federated_bob, federated_ursulas):
|
|||
|
||||
# Let's look at the destinations.
|
||||
for ursula in federated_ursulas:
|
||||
if ursula.checksum_address in treasure_map.destinations:
|
||||
kfrag_kit = treasure_map.destinations[ursula.checksum_address]
|
||||
if ursula.canonical_address in treasure_map.destinations:
|
||||
kfrag_kit = treasure_map.destinations[ursula.canonical_address]
|
||||
|
||||
# TODO: Deeper testing here: try to decrypt?
|
||||
# TODO: Use a new type for EncryptedKFrags?
|
||||
|
|
|
@ -22,11 +22,12 @@ import datetime
|
|||
import maya
|
||||
import pytest
|
||||
|
||||
from nucypher.core import (
|
||||
from nucypher_core import (
|
||||
MessageKit,
|
||||
EncryptedTreasureMap as EncryptedTreasureMapClass,
|
||||
TreasureMap as TreasureMapClass,
|
||||
)
|
||||
from nucypher_core.umbral import PublicKey
|
||||
|
||||
from nucypher.characters.control.specifications import fields
|
||||
from nucypher.characters.control.specifications.alice import GrantPolicy
|
||||
|
@ -34,7 +35,15 @@ from nucypher.characters.control.specifications.fields.treasuremap import Encryp
|
|||
from nucypher.control.specifications.base import BaseSchema
|
||||
from nucypher.control.specifications.exceptions import SpecificationError, InvalidInputData, InvalidArgumentCombo
|
||||
from nucypher.crypto.powers import DecryptingPower
|
||||
from nucypher.crypto.umbral_adapter import PublicKey
|
||||
|
||||
|
||||
def make_header(brand: bytes, major: int, minor: int) -> bytes:
|
||||
# Hardcoding this since it's too much trouble to expose it all the way from Rust
|
||||
assert len(brand) == 4
|
||||
major_bytes = major.to_bytes(2, 'big')
|
||||
minor_bytes = minor.to_bytes(2, 'big')
|
||||
header = brand + major_bytes + minor_bytes
|
||||
return header
|
||||
|
||||
|
||||
def test_various_field_validations_by_way_of_alice_grant(federated_bob):
|
||||
|
@ -94,14 +103,14 @@ def test_treasure_map_validation(enacted_federated_policy,
|
|||
assert "Invalid base64-encoded string" in str(e)
|
||||
|
||||
# valid base64 but invalid treasuremap
|
||||
bad_map = EncryptedTreasureMapClass._header() + b"your face looks like a treasure map"
|
||||
bad_map = make_header(b'EMap', 1, 0) + b"your face looks like a treasure map"
|
||||
bad_map_b64 = base64.b64encode(bad_map).decode()
|
||||
|
||||
with pytest.raises(InvalidInputData) as e:
|
||||
EncryptedTreasureMapsOnly().load({'tmap': bad_map_b64})
|
||||
|
||||
assert "Could not convert input for tmap to an EncryptedTreasureMap" in str(e)
|
||||
assert "Can't split a message with more bytes than the original splittable." in str(e)
|
||||
assert "Failed to deserialize" in str(e)
|
||||
|
||||
# a valid treasuremap for once...
|
||||
tmap_bytes = bytes(enacted_federated_policy.treasure_map)
|
||||
|
@ -124,14 +133,14 @@ def test_treasure_map_validation(enacted_federated_policy,
|
|||
assert "Invalid base64-encoded string" in str(e)
|
||||
|
||||
# valid base64 but invalid treasuremap
|
||||
bad_map = TreasureMapClass._header() + b"your face looks like a treasure map"
|
||||
bad_map = make_header(b'TMap', 1, 0) + b"your face looks like a treasure map"
|
||||
bad_map_b64 = base64.b64encode(bad_map).decode()
|
||||
|
||||
with pytest.raises(InvalidInputData) as e:
|
||||
UnenncryptedTreasureMapsOnly().load({'tmap': bad_map_b64})
|
||||
|
||||
assert "Could not convert input for tmap to a TreasureMap" in str(e)
|
||||
assert "Can't split a message with more bytes than the original splittable." in str(e)
|
||||
assert "Failed to deserialize" in str(e)
|
||||
|
||||
# a valid treasuremap
|
||||
decrypted_treasure_map = federated_bob._decrypt_treasure_map(enacted_federated_policy.treasure_map,
|
||||
|
@ -158,14 +167,14 @@ def test_messagekit_validation(capsule_side_channel):
|
|||
assert "Incorrect padding" in str(e)
|
||||
|
||||
# valid base64 but invalid messagekit
|
||||
bad_kit = MessageKit._header() + b"I got a message for you"
|
||||
bad_kit = make_header(b'MKit', 1, 0) + b"I got a message for you"
|
||||
bad_kit_b64 = base64.b64encode(bad_kit).decode()
|
||||
|
||||
with pytest.raises(SpecificationError) as e:
|
||||
MessageKitsOnly().load({'mkit': bad_kit_b64})
|
||||
|
||||
assert "Could not parse mkit" in str(e)
|
||||
assert "Can't split a message with more bytes than the original splittable." in str(e)
|
||||
assert "Failed to deserialize" in str(e)
|
||||
|
||||
# test a valid messagekit
|
||||
valid_kit = capsule_side_channel.messages[0][0]
|
||||
|
|
|
@ -22,6 +22,7 @@ import pytest
|
|||
import tempfile
|
||||
|
||||
from constant_sorrow.constants import CERTIFICATE_NOT_SAVED, NO_KEYSTORE_ATTACHED
|
||||
from nucypher_core.umbral import SecretKey
|
||||
|
||||
from nucypher.blockchain.eth.actors import StakeHolder
|
||||
from nucypher.characters.chaotic import Felix
|
||||
|
@ -39,7 +40,6 @@ from nucypher.config.characters import (
|
|||
from nucypher.config.constants import TEMPORARY_DOMAIN
|
||||
from nucypher.config.storages import ForgetfulNodeStorage
|
||||
from nucypher.crypto.keystore import Keystore
|
||||
from nucypher.crypto.umbral_adapter import SecretKey
|
||||
from tests.constants import INSECURE_DEVELOPMENT_PASSWORD
|
||||
from tests.constants import MOCK_IP_ADDRESS
|
||||
|
||||
|
|
|
@ -24,11 +24,12 @@ from constant_sorrow.constants import FEDERATED_ADDRESS
|
|||
from cryptography.hazmat.primitives.serialization import Encoding
|
||||
from flask import Flask
|
||||
|
||||
from nucypher_core.umbral import SecretKey, Signer
|
||||
|
||||
from nucypher.characters.lawful import Alice, Bob, Ursula
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN
|
||||
from nucypher.crypto.keystore import Keystore
|
||||
from nucypher.crypto.powers import DecryptingPower, DelegatingPower, TLSHostingPower
|
||||
from nucypher.crypto.umbral_adapter import SecretKey, Signer
|
||||
from nucypher.datastore.datastore import Datastore
|
||||
from nucypher.network.server import ProxyRESTServer
|
||||
from nucypher.utilities.networking import LOOPBACK_ADDRESS
|
||||
|
|
|
@ -19,7 +19,7 @@ import tempfile
|
|||
|
||||
import pytest
|
||||
|
||||
from nucypher.core import NodeMetadata
|
||||
from nucypher_core import NodeMetadata
|
||||
|
||||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN
|
||||
|
@ -32,6 +32,15 @@ ADDITIONAL_NODES_TO_LEARN_ABOUT = 10
|
|||
MOCK_URSULA_DB_FILEPATH = tempfile.mkdtemp()
|
||||
|
||||
|
||||
def make_header(brand: bytes, major: int, minor: int) -> bytes:
|
||||
# Hardcoding this since it's too much trouble to expose it all the way from Rust
|
||||
assert len(brand) == 4
|
||||
major_bytes = major.to_bytes(2, 'big')
|
||||
minor_bytes = minor.to_bytes(2, 'big')
|
||||
header = brand + major_bytes + minor_bytes
|
||||
return header
|
||||
|
||||
|
||||
class BaseTestNodeStorageBackends:
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
|
@ -112,7 +121,7 @@ class TestTemporaryFileBasedNodeStorage(BaseTestNodeStorageBackends):
|
|||
# Let's break the metadata (but not the version)
|
||||
metadata_path = self.storage_backend.metadata_dir / some_node
|
||||
with open(metadata_path, 'wb') as file:
|
||||
file.write(NodeMetadata._header() + b'invalid')
|
||||
file.write(make_header(b'NdMd', 1, 0) + b'invalid')
|
||||
|
||||
with pytest.raises(TemporaryFileBasedNodeStorage.InvalidNodeMetadata):
|
||||
self.storage_backend.get(stamp=some_node.name[:-5],
|
||||
|
@ -122,7 +131,7 @@ class TestTemporaryFileBasedNodeStorage(BaseTestNodeStorageBackends):
|
|||
# Let's break the metadata, by putting a completely wrong version
|
||||
metadata_path = self.storage_backend.metadata_dir / another_node
|
||||
with open(metadata_path, 'wb') as file:
|
||||
full_header = NodeMetadata._header()
|
||||
full_header = make_header(b'NdMd', 1, 0)
|
||||
file.write(full_header[:-1]) # Not even a valid header
|
||||
|
||||
with pytest.raises(TemporaryFileBasedNodeStorage.InvalidNodeMetadata):
|
||||
|
|
|
@ -25,9 +25,10 @@ import maya
|
|||
import pytest
|
||||
from flask import Response
|
||||
|
||||
from nucypher_core.umbral import SecretKey, Signer, PublicKey
|
||||
|
||||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.crypto.signing import SignatureStamp
|
||||
from nucypher.crypto.umbral_adapter import SecretKey, Signer, PublicKey, encrypt
|
||||
from nucypher.datastore.base import RecordField
|
||||
from nucypher.network.nodes import Teacher
|
||||
from tests.markers import skip_on_circleci
|
||||
|
@ -39,7 +40,6 @@ from tests.mock.performance_mocks import (
|
|||
mock_cert_storage,
|
||||
mock_message_verification,
|
||||
mock_metadata_validation,
|
||||
mock_pubkey_from_bytes,
|
||||
mock_secret_source,
|
||||
mock_verify_node
|
||||
)
|
||||
|
@ -65,7 +65,7 @@ performance bottlenecks.
|
|||
"""
|
||||
|
||||
|
||||
@skip_on_circleci # TODO: #2552 Taking 6-10 seconds on CircleCI, passing locally.
|
||||
#@skip_on_circleci # TODO: #2552 Taking 6-10 seconds on CircleCI, passing locally.
|
||||
def test_alice_can_learn_about_a_whole_bunch_of_ursulas(highperf_mocked_alice):
|
||||
# During the fixture execution, Alice verified one node.
|
||||
# TODO: Consider changing this - #1449
|
||||
|
@ -102,24 +102,16 @@ def test_alice_can_learn_about_a_whole_bunch_of_ursulas(highperf_mocked_alice):
|
|||
_POLICY_PRESERVER = []
|
||||
|
||||
|
||||
@skip_on_circleci # TODO: #2552 Taking 6-10 seconds on CircleCI, passing locally.
|
||||
#@skip_on_circleci # TODO: #2552 Taking 6-10 seconds on CircleCI, passing locally.
|
||||
def test_alice_verifies_ursula_just_in_time(fleet_of_highperf_mocked_ursulas,
|
||||
highperf_mocked_alice,
|
||||
highperf_mocked_bob):
|
||||
|
||||
def mock_encrypt(public_key, plaintext):
|
||||
if not isinstance(public_key, PublicKey):
|
||||
public_key = public_key.i_want_to_be_a_real_boy()
|
||||
return encrypt(public_key, plaintext)
|
||||
|
||||
mocks = (
|
||||
patch('nucypher.crypto.umbral_adapter.PublicKey.__eq__', lambda *args, **kwargs: True),
|
||||
mock_pubkey_from_bytes(),
|
||||
mock_secret_source(),
|
||||
mock_cert_loading,
|
||||
mock_metadata_validation,
|
||||
mock_message_verification,
|
||||
patch('nucypher.crypto.umbral_adapter.encrypt', new=mock_encrypt),
|
||||
)
|
||||
|
||||
with contextlib.ExitStack() as stack:
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import pytest
|
||||
|
||||
from nucypher.core import HRAC
|
||||
from nucypher_core import HRAC
|
||||
|
||||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.crypto.utils import keccak_digest
|
||||
|
@ -29,9 +29,9 @@ def test_alice_creates_policy_with_correct_hrac(federated_alice, federated_bob,
|
|||
Alice creates a Policy. It has the proper HRAC, unique per her, Bob, and the label
|
||||
"""
|
||||
# TODO: what are we actually testing here?
|
||||
assert idle_federated_policy.hrac == HRAC.derive(federated_alice.stamp.as_umbral_pubkey(),
|
||||
federated_bob.stamp.as_umbral_pubkey(),
|
||||
idle_federated_policy.label)
|
||||
assert idle_federated_policy.hrac == HRAC(federated_alice.stamp.as_umbral_pubkey(),
|
||||
federated_bob.stamp.as_umbral_pubkey(),
|
||||
idle_federated_policy.label)
|
||||
|
||||
|
||||
def test_alice_does_not_update_with_old_ursula_info(federated_alice, federated_ursulas):
|
||||
|
|
|
@ -18,7 +18,7 @@ from base64 import b64decode
|
|||
|
||||
import pytest
|
||||
|
||||
from nucypher.core import HRAC, TreasureMap
|
||||
from nucypher_core import HRAC, TreasureMap
|
||||
|
||||
from nucypher.crypto.powers import DecryptingPower
|
||||
|
||||
|
@ -30,19 +30,19 @@ def random_federated_treasure_map_data(federated_alice, federated_bob, federated
|
|||
threshold = 2
|
||||
shares = threshold + 1
|
||||
policy_key, kfrags = federated_alice.generate_kfrags(bob=federated_bob, label=label, threshold=threshold, shares=shares)
|
||||
hrac = HRAC.derive(publisher_verifying_key=federated_alice.stamp.as_umbral_pubkey(),
|
||||
bob_verifying_key=federated_bob.stamp.as_umbral_pubkey(),
|
||||
label=label)
|
||||
hrac = HRAC(publisher_verifying_key=federated_alice.stamp.as_umbral_pubkey(),
|
||||
bob_verifying_key=federated_bob.stamp.as_umbral_pubkey(),
|
||||
label=label)
|
||||
|
||||
assigned_kfrags = {
|
||||
ursula.checksum_address: (ursula.public_keys(DecryptingPower), vkfrag)
|
||||
ursula.canonical_address: (ursula.public_keys(DecryptingPower), vkfrag)
|
||||
for ursula, vkfrag in zip(list(federated_ursulas)[:shares], kfrags)}
|
||||
|
||||
random_treasure_map = TreasureMap.construct_by_publisher(signer=federated_alice.stamp.as_umbral_signer(),
|
||||
hrac=hrac,
|
||||
policy_encrypting_key=policy_key,
|
||||
assigned_kfrags=assigned_kfrags,
|
||||
threshold=threshold)
|
||||
random_treasure_map = TreasureMap(signer=federated_alice.stamp.as_umbral_signer(),
|
||||
hrac=hrac,
|
||||
policy_encrypting_key=policy_key,
|
||||
assigned_kfrags=assigned_kfrags,
|
||||
threshold=threshold)
|
||||
|
||||
bob_key = federated_bob.public_keys(DecryptingPower)
|
||||
enc_treasure_map = random_treasure_map.encrypt(signer=federated_alice.stamp.as_umbral_signer(),
|
||||
|
|
|
@ -20,7 +20,7 @@ import json
|
|||
from base64 import b64encode
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from nucypher.core import RetrievalKit
|
||||
from nucypher_core import RetrievalKit
|
||||
|
||||
from nucypher.characters.lawful import Enrico
|
||||
from nucypher.crypto.powers import DecryptingPower
|
||||
|
@ -144,7 +144,7 @@ def test_retrieve_cfrags(federated_porter,
|
|||
|
||||
assert policy_message_kit.is_decryptable_by_receiver()
|
||||
|
||||
cleartext = federated_bob._crypto_power.power_ups(DecryptingPower).keypair.decrypt(policy_message_kit)
|
||||
cleartext = federated_bob._crypto_power.power_ups(DecryptingPower).keypair.decrypt_message_kit(policy_message_kit)
|
||||
assert cleartext == original_message
|
||||
|
||||
#
|
||||
|
|
|
@ -18,9 +18,10 @@ import random
|
|||
|
||||
import pytest
|
||||
|
||||
from nucypher_core.umbral import SecretKey
|
||||
|
||||
from nucypher.characters.control.specifications.fields import Key
|
||||
from nucypher.control.specifications.exceptions import InvalidArgumentCombo, InvalidInputData
|
||||
from nucypher.crypto.umbral_adapter import SecretKey
|
||||
from nucypher.utilities.porter.control.specifications.fields import UrsulaInfoSchema, RetrievalResultSchema
|
||||
from nucypher.utilities.porter.control.specifications.porter_schema import (
|
||||
AliceGetUrsulas,
|
||||
|
@ -199,6 +200,7 @@ def test_bob_retrieve_cfrags(federated_porter,
|
|||
retrieval_results = federated_porter.retrieve_cfrags(**non_encoded_retrieval_args)
|
||||
expected_retrieval_results_json = []
|
||||
retrieval_result_schema = RetrievalResultSchema()
|
||||
|
||||
for result in retrieval_results:
|
||||
data = retrieval_result_schema.dump(result)
|
||||
expected_retrieval_results_json.append(data)
|
||||
|
|
|
@ -20,24 +20,23 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
|
||||
import json
|
||||
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from unittest.mock import Mock
|
||||
import tabulate
|
||||
import time
|
||||
|
||||
from twisted.logger import ILogObserver, globalLogPublisher, jsonFileLogObserver
|
||||
from web3.contract import Contract
|
||||
from zope.interface import provider
|
||||
|
||||
from nucypher_core.umbral import SecretKey, Signer
|
||||
|
||||
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
|
||||
from nucypher.blockchain.eth.signers.software import Web3Signer
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
|
||||
from unittest.mock import Mock
|
||||
from zope.interface import provider
|
||||
|
||||
from nucypher.blockchain.economics import StandardTokenEconomics
|
||||
from nucypher.blockchain.eth.agents import (
|
||||
AdjudicatorAgent,
|
||||
|
@ -47,7 +46,6 @@ from nucypher.blockchain.eth.agents import (
|
|||
)
|
||||
from nucypher.blockchain.eth.constants import NUCYPHER_CONTRACT_NAMES, NULL_ADDRESS, POLICY_ID_LENGTH
|
||||
from nucypher.crypto.signing import SignatureStamp
|
||||
from nucypher.crypto.umbral_adapter import SecretKey, Signer
|
||||
from nucypher.exceptions import DevelopmentInstallationRequired
|
||||
from nucypher.policy.policies import Policy
|
||||
from nucypher.utilities.logging import Logger
|
||||
|
|
|
@ -38,10 +38,11 @@ from typing import Set, Optional, List, Tuple
|
|||
from web3.main import Web3
|
||||
from web3.types import Wei
|
||||
|
||||
from nucypher_core.umbral import SecretKey
|
||||
|
||||
from nucypher.network.middleware import RestMiddleware
|
||||
from nucypher.characters.lawful import Bob, Ursula, Alice
|
||||
from nucypher.config.characters import AliceConfiguration
|
||||
from nucypher.crypto.umbral_adapter import SecretKey
|
||||
from nucypher.policy.policies import Policy
|
||||
from nucypher.utilities.logging import GlobalLoggerSettings
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
from contextlib import contextmanager
|
||||
from unittest.mock import patch
|
||||
|
||||
from nucypher.crypto.umbral_adapter import PublicKey, Signature
|
||||
from nucypher_core.umbral import PublicKey, Signature
|
||||
|
||||
from nucypher.network.server import make_rest_app
|
||||
from tests.mock.serials import good_serials
|
||||
|
||||
|
@ -173,8 +174,7 @@ class NotARestApp:
|
|||
def actual_rest_app(self):
|
||||
if self._actual_rest_app is None:
|
||||
self._actual_rest_app, self._datastore = make_rest_app(db_filepath=self.db_filepath,
|
||||
this_node=self.this_node,
|
||||
domain=None)
|
||||
this_node=self.this_node)
|
||||
_new_view_functions = self._ViewFunctions(self._actual_rest_app.view_functions)
|
||||
self._actual_rest_app.view_functions = _new_view_functions
|
||||
self._actual_rest_apps.append(
|
||||
|
@ -218,13 +218,6 @@ def mock_secret_source(*args, **kwargs):
|
|||
NotAPublicKey.reset()
|
||||
|
||||
|
||||
@contextmanager
|
||||
def mock_pubkey_from_bytes(*args, **kwargs):
|
||||
with patch('nucypher.crypto.umbral_adapter.PublicKey.from_bytes', NotAPublicKey.from_bytes):
|
||||
yield
|
||||
NotAPublicKey.reset()
|
||||
|
||||
|
||||
def _determine_good_serials(start, end):
|
||||
'''
|
||||
Figure out which serials are good to use in mocks because they won't result in non-viable public keys.
|
||||
|
|
|
@ -20,15 +20,16 @@ from base64 import b64encode, b64decode
|
|||
import maya
|
||||
import pytest
|
||||
|
||||
from nucypher.core import MessageKit as MessageKitClass
|
||||
from nucypher_core import (
|
||||
MessageKit as MessageKitClass,
|
||||
EncryptedTreasureMap as EncryptedTreasureMapClass)
|
||||
from nucypher_core.umbral import SecretKey, Signer
|
||||
|
||||
from nucypher.crypto.umbral_adapter import SecretKey, Signer
|
||||
from nucypher.characters.control.specifications.fields import (
|
||||
DateTime,
|
||||
FileField,
|
||||
Key,
|
||||
MessageKit,
|
||||
UmbralSignature,
|
||||
EncryptedTreasureMap
|
||||
)
|
||||
from nucypher.characters.lawful import Enrico
|
||||
|
@ -126,36 +127,13 @@ def test_message_kit(enacted_federated_policy, federated_alice):
|
|||
assert serialized == b64encode(bytes(message_kit)).decode()
|
||||
|
||||
deserialized = field._deserialize(value=serialized, attr=None, data=None)
|
||||
assert deserialized == message_kit
|
||||
deserialized_plaintext = federated_alice.decrypt_message_kit(enacted_federated_policy.label, deserialized)[0]
|
||||
assert deserialized_plaintext == plaintext_bytes
|
||||
|
||||
with pytest.raises(InvalidInputData):
|
||||
field._deserialize(value=b"MessageKit", attr=None, data=None)
|
||||
|
||||
|
||||
def test_umbral_signature():
|
||||
umbral_priv_key = SecretKey.random()
|
||||
signer = Signer(umbral_priv_key)
|
||||
|
||||
message = b'this is a message'
|
||||
signature = signer.sign(message)
|
||||
other_signature = signer.sign(b'this is a different message')
|
||||
|
||||
field = UmbralSignature()
|
||||
serialized = field._serialize(value=signature, attr=None, obj=None)
|
||||
assert serialized == b64encode(bytes(signature)).decode()
|
||||
assert serialized != b64encode(bytes(other_signature)).decode()
|
||||
|
||||
deserialized = field._deserialize(value=serialized, attr=None, data=None)
|
||||
assert deserialized == signature
|
||||
assert deserialized != other_signature
|
||||
|
||||
field._validate(value=bytes(signature))
|
||||
field._validate(value=bytes(other_signature))
|
||||
|
||||
with pytest.raises(InvalidInputData):
|
||||
field._validate(value=b"UmbralSignature")
|
||||
|
||||
|
||||
def test_treasure_map(enacted_federated_policy):
|
||||
treasure_map = enacted_federated_policy.treasure_map
|
||||
|
||||
|
@ -164,7 +142,8 @@ def test_treasure_map(enacted_federated_policy):
|
|||
assert serialized == b64encode(bytes(treasure_map)).decode()
|
||||
|
||||
deserialized = field._deserialize(value=serialized, attr=None, data=None)
|
||||
assert deserialized == treasure_map
|
||||
assert isinstance(deserialized, EncryptedTreasureMapClass)
|
||||
assert bytes(deserialized) == bytes(treasure_map)
|
||||
|
||||
with pytest.raises(InvalidInputData):
|
||||
field._deserialize(value=b64encode(b"TreasureMap").decode(), attr=None, data=None)
|
||||
|
|
|
@ -18,8 +18,9 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
import sha3
|
||||
from constant_sorrow.constants import PUBLIC_ONLY
|
||||
|
||||
from nucypher_core.umbral import SecretKey
|
||||
|
||||
from nucypher.crypto import keypairs
|
||||
from nucypher.crypto.umbral_adapter import SecretKey
|
||||
|
||||
|
||||
def test_gen_keypair_if_needed():
|
||||
|
|
|
@ -26,6 +26,8 @@ from constant_sorrow.constants import KEYSTORE_LOCKED
|
|||
from cryptography.hazmat.primitives._serialization import Encoding
|
||||
from mnemonic.mnemonic import Mnemonic
|
||||
|
||||
from nucypher_core.umbral import SecretKey, SecretKeyFactory
|
||||
|
||||
from nucypher.crypto.keystore import (
|
||||
Keystore,
|
||||
InvalidPassword,
|
||||
|
@ -41,7 +43,6 @@ from nucypher.crypto.keystore import (
|
|||
_read_keystore
|
||||
)
|
||||
from nucypher.crypto.powers import DecryptingPower, SigningPower, DelegatingPower, TLSHostingPower
|
||||
from nucypher.crypto.umbral_adapter import SecretKey, SecretKeyFactory
|
||||
from nucypher.utilities.networking import LOOPBACK_ADDRESS
|
||||
from tests.constants import INSECURE_DEVELOPMENT_PASSWORD
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ You should have received a copy of the GNU Affero General Public License
|
|||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from nucypher.core import MessageKit
|
||||
from nucypher_core import MessageKit
|
||||
|
||||
from nucypher.characters.lawful import Enrico
|
||||
|
||||
|
|
|
@ -15,9 +15,14 @@
|
|||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from eth_utils import to_checksum_address
|
||||
import pytest
|
||||
|
||||
from nucypher_core import NodeMetadata, NodeMetadataPayload
|
||||
from nucypher_core.umbral import SecretKey, Signer
|
||||
|
||||
from nucypher.acumen.perception import FleetSensor
|
||||
from nucypher.blockchain.eth.constants import LENGTH_ECDSA_SIGNATURE_WITH_RECOVERY
|
||||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.network.exceptions import NodeSeemsToBeDown
|
||||
from nucypher.network.middleware import NucypherMiddlewareClient
|
||||
|
@ -37,8 +42,9 @@ MOCK_NETWORK = 'holodeck'
|
|||
|
||||
class Dummy: # Teacher
|
||||
|
||||
def __init__(self, checksum_address):
|
||||
self.checksum_address = checksum_address
|
||||
def __init__(self, canonical_address):
|
||||
self.canonical_address = canonical_address
|
||||
self.checksum_address = to_checksum_address(canonical_address)
|
||||
self.certificate_filepath = None
|
||||
self.domain = MOCK_NETWORK
|
||||
|
||||
|
@ -61,7 +67,19 @@ class Dummy: # Teacher
|
|||
return MOCK_IP_ADDRESS
|
||||
|
||||
def metadata(self):
|
||||
return self.checksum_address.encode()
|
||||
signer = Signer(SecretKey.random())
|
||||
payload = NodeMetadataPayload(canonical_address=self.canonical_address,
|
||||
domain=':dummy:',
|
||||
timestamp_epoch=0,
|
||||
decentralized_identity_evidence=b'\x00' * LENGTH_ECDSA_SIGNATURE_WITH_RECOVERY,
|
||||
verifying_key=signer.verifying_key(),
|
||||
encrypting_key=SecretKey.random().public_key(),
|
||||
certificate_bytes=b'not a certificate',
|
||||
host='127.0.0.1',
|
||||
port=1111,
|
||||
)
|
||||
return NodeMetadata(signer=signer,
|
||||
payload=payload)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
@ -97,7 +115,7 @@ def test_get_external_ip_from_empty_known_nodes(mock_requests):
|
|||
|
||||
def test_get_external_ip_from_known_nodes_with_one_known_node(mock_requests):
|
||||
sensor = FleetSensor(domain=MOCK_NETWORK)
|
||||
sensor.record_node(Dummy('0xdeadbeef'))
|
||||
sensor.record_node(Dummy(b'deadbeefdeadbeefdead'))
|
||||
sensor.record_fleet_state()
|
||||
assert len(sensor) == 1
|
||||
get_external_ip_from_known_nodes(known_nodes=sensor)
|
||||
|
@ -110,9 +128,9 @@ def test_get_external_ip_from_known_nodes(mock_client):
|
|||
# Setup FleetSensor
|
||||
sensor = FleetSensor(domain=MOCK_NETWORK)
|
||||
sample_size = 3
|
||||
sensor.record_node(Dummy('0xdeadbeef'))
|
||||
sensor.record_node(Dummy('0xdeadllama'))
|
||||
sensor.record_node(Dummy('0xdeadmouse'))
|
||||
sensor.record_node(Dummy(b'deadbeefdeadbeefdead'))
|
||||
sensor.record_node(Dummy(b'deadllamadeadllamade'))
|
||||
sensor.record_node(Dummy(b'deadmousedeadmousede'))
|
||||
sensor.record_fleet_state()
|
||||
assert len(sensor) == sample_size
|
||||
|
||||
|
@ -132,14 +150,14 @@ def test_get_external_ip_from_known_nodes_client(mocker, mock_client):
|
|||
# Setup FleetSensor
|
||||
sensor = FleetSensor(domain=MOCK_NETWORK)
|
||||
sample_size = 3
|
||||
sensor.record_node(Dummy('0xdeadbeef'))
|
||||
sensor.record_node(Dummy('0xdeadllama'))
|
||||
sensor.record_node(Dummy('0xdeadmouse'))
|
||||
sensor.record_node(Dummy(b'deadbeefdeadbeefdead'))
|
||||
sensor.record_node(Dummy(b'deadllamadeadllamade'))
|
||||
sensor.record_node(Dummy(b'deadmousedeadmousede'))
|
||||
sensor.record_fleet_state()
|
||||
assert len(sensor) == sample_size
|
||||
|
||||
# Setup HTTP Client
|
||||
mocker.patch.object(Ursula, 'from_teacher_uri', return_value=Dummy('0xdeadpork'))
|
||||
mocker.patch.object(Ursula, 'from_teacher_uri', return_value=Dummy(b'deadporkdeadporkdead'))
|
||||
teacher_uri = TEACHER_NODES[MOCK_NETWORK][0]
|
||||
|
||||
get_external_ip_from_known_nodes(known_nodes=sensor, sample_size=sample_size)
|
||||
|
@ -162,7 +180,7 @@ def test_get_external_ip_from_default_teacher(mocker, mock_client, mock_requests
|
|||
|
||||
mock_client.return_value = Dummy.GoodResponse
|
||||
teacher_uri = TEACHER_NODES[MOCK_NETWORK][0]
|
||||
mocker.patch.object(Ursula, 'from_teacher_uri', return_value=Dummy('0xdeadbeef'))
|
||||
mocker.patch.object(Ursula, 'from_teacher_uri', return_value=Dummy(b'deadbeefdeadbeefdead'))
|
||||
|
||||
# "Success"
|
||||
ip = get_external_ip_from_default_teacher(network=MOCK_NETWORK)
|
||||
|
@ -195,7 +213,7 @@ def test_get_external_ip_cascade_failure(mocker, mock_requests):
|
|||
third = mocker.patch('nucypher.utilities.networking.get_external_ip_from_centralized_source', return_value=None)
|
||||
|
||||
sensor = FleetSensor(domain=MOCK_NETWORK)
|
||||
sensor.record_node(Dummy('0xdeadbeef'))
|
||||
sensor.record_node(Dummy(b'deadbeefdeadbeefdead'))
|
||||
sensor.record_fleet_state()
|
||||
|
||||
with pytest.raises(UnknownIPAddress, match='External IP address detection failed'):
|
||||
|
|
|
@ -17,14 +17,15 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
|
||||
from base64 import b64encode
|
||||
from eth_utils import to_canonical_address
|
||||
|
||||
import pytest
|
||||
|
||||
from nucypher.core import RetrievalKit as RetrievalKitClass
|
||||
from nucypher_core import RetrievalKit as RetrievalKitClass, MessageKit
|
||||
from nucypher_core.umbral import SecretKey
|
||||
|
||||
from nucypher.control.specifications.exceptions import InvalidInputData
|
||||
from nucypher.control.specifications.fields import StringList
|
||||
from nucypher.crypto.umbral_adapter import SecretKey, encrypt
|
||||
from nucypher.utilities.porter.control.specifications.fields import UrsulaChecksumAddress
|
||||
from nucypher.utilities.porter.control.specifications.fields.retrieve import RetrievalKit
|
||||
|
||||
|
@ -103,14 +104,14 @@ def test_retrieval_kit_field(get_random_checksum_address):
|
|||
|
||||
# kit with list of ursulas
|
||||
encrypting_key = SecretKey.random().public_key()
|
||||
capsule, _ = encrypt(encrypting_key, b'testing retrieval kit with 2 ursulas')
|
||||
capsule = MessageKit(encrypting_key, b'testing retrieval kit with 2 ursulas').capsule
|
||||
ursulas = [get_random_checksum_address(), get_random_checksum_address()]
|
||||
run_tests_on_kit(kit=RetrievalKitClass(capsule, ursulas))
|
||||
run_tests_on_kit(kit=RetrievalKitClass(capsule, {to_canonical_address(ursula) for ursula in ursulas}))
|
||||
|
||||
# kit with no ursulas
|
||||
encrypting_key = SecretKey.random().public_key()
|
||||
capsule, _ = encrypt(encrypting_key, b'testing retrieval kit with no ursulas')
|
||||
run_tests_on_kit(kit=RetrievalKitClass(capsule, []))
|
||||
capsule = MessageKit(encrypting_key, b'testing retrieval kit with no ursulas').capsule
|
||||
run_tests_on_kit(kit=RetrievalKitClass(capsule, set()))
|
||||
|
||||
with pytest.raises(InvalidInputData):
|
||||
field._deserialize(value=b"non_base_64_data", attr=None, data=None)
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
"""
|
||||
This file is part of nucypher.
|
||||
|
||||
nucypher is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
nucypher is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from nucypher.core import HRAC, TreasureMap, EncryptedTreasureMap
|
||||
|
||||
from nucypher.crypto.powers import DecryptingPower
|
||||
from nucypher.crypto.umbral_adapter import KeyFrag
|
||||
|
||||
|
||||
def test_complete_treasure_map_journey(federated_alice, federated_bob, federated_ursulas, idle_federated_policy, mocker):
|
||||
|
||||
label = "chili con carne 🔥".encode('utf-8')
|
||||
kfrags = idle_federated_policy.kfrags
|
||||
ursulas = list(federated_ursulas)[:len(kfrags)]
|
||||
|
||||
hrac = HRAC.derive(publisher_verifying_key=federated_alice.stamp.as_umbral_pubkey(),
|
||||
bob_verifying_key=federated_bob.stamp.as_umbral_pubkey(),
|
||||
label=label)
|
||||
|
||||
assigned_kfrags = {
|
||||
ursula.checksum_address: (ursula.public_keys(DecryptingPower), vkfrag)
|
||||
for ursula, vkfrag in zip(ursulas, kfrags)}
|
||||
|
||||
treasure_map = TreasureMap.construct_by_publisher(signer=federated_alice.stamp.as_umbral_signer(),
|
||||
hrac=hrac,
|
||||
policy_encrypting_key=idle_federated_policy.public_key,
|
||||
assigned_kfrags=assigned_kfrags,
|
||||
threshold=1)
|
||||
|
||||
ursula_rolodex = {u.checksum_address: u for u in ursulas}
|
||||
for ursula_address, encrypted_kfrag in treasure_map.destinations.items():
|
||||
assert ursula_address in ursula_rolodex
|
||||
ursula = ursula_rolodex[ursula_address]
|
||||
auth_kfrag = ursula._decrypt_kfrag(encrypted_kfrag)
|
||||
auth_kfrag.verify(hrac=treasure_map.hrac,
|
||||
publisher_verifying_key=federated_alice.stamp.as_umbral_pubkey())
|
||||
|
||||
serialized_map = bytes(treasure_map)
|
||||
# ...
|
||||
deserialized_map = TreasureMap.from_bytes(serialized_map)
|
||||
|
||||
assert treasure_map.destinations == deserialized_map.destinations
|
||||
assert treasure_map.hrac == deserialized_map.hrac
|
||||
|
||||
|
||||
enc_treasure_map = treasure_map.encrypt(signer=federated_alice.stamp.as_umbral_signer(),
|
||||
recipient_key=federated_bob.public_keys(DecryptingPower))
|
||||
|
||||
enc_serialized_map = bytes(enc_treasure_map)
|
||||
# ...
|
||||
enc_deserialized_map = EncryptedTreasureMap.from_bytes(enc_serialized_map)
|
||||
|
||||
decrypted_map = federated_bob._decrypt_treasure_map(enc_deserialized_map,
|
||||
federated_alice.stamp.as_umbral_pubkey())
|
||||
|
||||
assert treasure_map.threshold == decrypted_map.threshold == 1
|
||||
assert treasure_map.destinations == decrypted_map.destinations
|
||||
assert treasure_map.hrac == decrypted_map.hrac
|
||||
|
||||
|
||||
@pytest.mark.skip(reason='Backwards-incompatible with umbral 0.2+')
|
||||
def test_treasure_map_versioning(mocker, federated_alice, federated_bob, federated_ursulas, idle_federated_policy):
|
||||
# Produced using f04d564a1
|
||||
map_from_previous_version = b'\x87T\x19\xceV_1\x8e\xb0\x87\xf6\xd9\x9d\x80\xba\xaf\xc4\x84\xa1\xd9|P=\x02\x13\xa0r1\x9eB\xf4\xfc\xc6w\xdf\xd1\x88\xc4\x83\x8f \x1c|\xec\xfcnW~k\x95f8\x19\r\xb1\xad\xe9\xa8\xc9\x06\x93j\xaf\xc50&[\xe5Cy\x9cr_R\xcd\xb1\xb1F\xed\x01\x00\x00\x02\xf4\x02][\xb8VP\xfa%D\xc3\xeb\xd4\x8b\xd2SW\x0f\xfe\xe5\x0f\xaa\xe6\x83\x9a\xa1\x91\xf6\x8e\xca\x00\x95\xf9\x90\x02-\x7f\xca\xe8$L\xcd0\x1d\xa1D\x80\xafjY\xea2\xbc\x04\x94\x1c\xd6E\xa4l\x8fu\xdf\x8a#\x04\xe1\x8eKN\xc9Y\xfbB7I\x9b\xa153\xcef\xfd\xb2/9[\x1b^\xe3\xcf\x08/\xf4%k\x06\xf4\xa5\x03\xfa\xf1\xdc\xec\xe1\t\xeb%\x0c\x11{\xbb\xc7Z\xb2^\x1d.\x18\xeaJ\xaa\xa6f\xd8\xb0\x92U\x84;\xbe6\x00\x00\x02m\x89\x97?\xcavL\xa7q\x13\x01\x1e\x1f6\x05)\xc2?\xcd\x96\xafhH/>6\x8d\x1a\xf8\xfd\xd5\x8a\xf9e\xb0\xc5\xa8\xbd(\x86\x9f\xb9L\xb9n=\xcb\xa0\xd2\t\x94\x90l\xc0\xb7\x85\x90N\xe0\xc9M{\x08\xc4\xf5\x80\xb7\xd1\x10\x18P\x8bl\x0f\x87fS\x836\xa6q\'\xabr\xd1l\x1e\xe2\xe7\xce\xccZ1[\x0b\xe7\xaa\x9c\x92Qh"2F\x1f\x9f-7HylC\xad\x03\x8ek_\xb6M\x19\xb2\xef\xde~\xa6\x10F<\xac\x94\xa6e\xc3\xb5\x132\x94\x96\xc4\xd9\'\xf9h\x1c\xe8\xb8Zm\x86M\xed\x00\x86\xc3\xf4\x93\x03/J\x1d6$\x1a\xe5+\xad\xf93\x17n\xc3\x19sQ2C\xaf\x9d\x89p\xb9557O\x9a\xc3O\xf0\x1f\xb3M.\xa9\x89\xeb\xb9\xf6\xe8\xcc@\xb0\\)\x9d\xdb\'\xfc\xc4_\xfd\xe1\xef\x01\xe3\xe7va\xac\xd7y\xb2\xcfm\xda\x85\x06(\x92H\xe2p\xf1\x9aw\xaf\x83\x1c\xd3@a\xaa\xf6\xee\xfc\xae&;\xdd*\x94I\'r1JG\xca\xdb\x9e\xef\x18Z\x9f\x15\x81\xe3\x1c\xcfJ\xd6;2H\xe8\xed\xfc\x98\x8e\xc6\x94\x1f\x1d\x95A\xa5\x8e\xe5\xc6f\x85\xbb\xc3\xd0\x9d\x83\xd3\xdf\x91]\x16\xe6)\xfa\xc0\xf3\xba\x7fAb\x81\xe0\x8f\x1bu0\x0b\x82^\xe9\x16\xf0\xfc\xc3p\xd4\x9f\'\xa6\xe5\xb4\xf7\xe1\x99\xa5\xfe\x12\x0e{L\xb0\xd6\xa1\x049\xcf\xe0\xca\x06\xe3\xd6u\x9e\xb3P\xb7\x1a\xc5X\xb7\xb2\xfa\x1dJ\xe1\xa9Gb\xf6l~DG\x8e5X\xc2^\x87\xac\x89W(\xaf\xd3\x15o\xde\xf7\xe4\x18\xd9\x98\xc3\tcL\xd3\x9dF\x8e3\xe5u\x03\x0b\xe7\tj\xdb\xd3B\xa1\x85\x9d \x9c\xa4{n\x01"\xab\xe1509\xdaoL\xc9\x8d\xc9\xfd"\xad\xd8\xfd\xf5\x14\xa2\xa8N\xf5\xa0\xf4\x04Y\x85i\xe0zj34\xc9\xbd\xac\xb9gn\x19J]\x0eL\x81C\xb9\x95\x86Q,\x81\xdf\xcbh\x13\xae8\xe8\x06y\xd1\xcd\x867\x1a\x1c\xe1\x05\xba\xfaL\x1a\x1f\x9f~\x18O1p@\xee\xee\xc4\xed\x84%\xb4\xb4\x12\xb6\x81\x0c\xcamf.\x9c\xe1\xfe\xc4\x87I\'\xc7e\xc1\x7f\xeb\x9c\xe1\xca\xa5\r.\x15\xa8r\xa8\x82Q\x13\x99K\x12X3\x04\xbc\x99\x96\xf8\xc3\x1es\x0c\x85\x8d\xd3\xee\x1b^\xc8\xf5\x1d^\x1a&6#\xbc\xa8~wp}]8\xb5\xe6v\xa4D\xfe:\xb8<q\xd9\x02\xfa\x7f\xcfWA\xad\xd1#\xac\x8b\xd7\xff\xca\xf7[dm\x9b\x06\xcc\x03\x1b\xfa\xd1\xf6:\xad\x1c\xb6\xb8'
|
||||
|
||||
kfrags = idle_federated_policy.kfrags[:3]
|
||||
|
||||
hrac = HRAC.derive(publisher_verifying_key=federated_alice.stamp.as_umbral_pubkey(),
|
||||
bob_verifying_key=federated_bob.stamp.as_umbral_pubkey(),
|
||||
label=label)
|
||||
|
||||
assigned_kfrags = {
|
||||
ursula.checksum_address: (ursula.public_keys(DecryptingPower), vkfrag)
|
||||
for ursula, vkfrag in zip(list(federated_ursulas)[:len(kfrags)], kfrags)}
|
||||
|
||||
treasure_map = TreasureMap.construct_by_publisher(signer=federated_alice.stamp.as_umbral_signer(),
|
||||
hrac=hrac,
|
||||
policy_encrypting_key=idle_federated_policy.public_key,
|
||||
assigned_kfrags=assigned_kfrags,
|
||||
threshold=2)
|
||||
|
||||
# Good version (baseline)
|
||||
serialized_map = bytes(treasure_map)
|
||||
deserialized_map = TreasureMap.from_bytes(serialized_map)
|
||||
assert treasure_map == deserialized_map
|
||||
|
||||
map_from_f04d564a1 = TreasureMap.from_bytes(map_from_previous_version)
|
||||
assert map_from_f04d564a1.public_verify()
|
|
@ -1,236 +0,0 @@
|
|||
"""
|
||||
This file is part of nucypher.
|
||||
|
||||
nucypher is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
nucypher is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
import re
|
||||
from typing import Tuple, Any, Type
|
||||
|
||||
import pytest
|
||||
|
||||
from nucypher.utilities.versioning import Versioned
|
||||
|
||||
|
||||
def _check_valid_version_tuple(version: Any, cls: Type):
|
||||
if not isinstance(version, tuple):
|
||||
pytest.fail(f"Old version handlers keys for {cls.__name__} must be a tuple")
|
||||
if not len(version) == Versioned._VERSION_PARTS:
|
||||
pytest.fail(f"Old version handlers keys for {cls.__name__} must be a {str(Versioned._VERSION_PARTS)}-tuple")
|
||||
if not all(isinstance(part, int) for part in version):
|
||||
pytest.fail(f"Old version handlers version parts {cls.__name__} must be integers")
|
||||
|
||||
|
||||
class A(Versioned):
|
||||
|
||||
def __init__(self, x: int):
|
||||
self.x = x
|
||||
|
||||
@classmethod
|
||||
def _brand(cls):
|
||||
return b"ABCD"
|
||||
|
||||
@classmethod
|
||||
def _version(cls) -> Tuple[int, int]:
|
||||
return 2, 1
|
||||
|
||||
def _payload(self) -> bytes:
|
||||
return self.x.to_bytes(1, 'big')
|
||||
|
||||
@classmethod
|
||||
def _old_version_handlers(cls):
|
||||
return {
|
||||
(2, 0): cls._from_bytes_v2_0,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _from_bytes_v2_0(cls, data):
|
||||
# v2.0 saved a 4 byte integer in hex format
|
||||
int_hex, remainder = data[:2], data[2:]
|
||||
int_bytes = bytes.fromhex(int_hex.decode())
|
||||
return cls(int.from_bytes(int_bytes, 'big')), remainder
|
||||
|
||||
@classmethod
|
||||
def _from_bytes_current(cls, data):
|
||||
# v2.1 saves a 4 byte integer as 4 bytes
|
||||
int_bytes, remainder = data[:1], data[1:]
|
||||
return cls(int.from_bytes(int_bytes, 'big')), remainder
|
||||
|
||||
|
||||
def test_unique_branding():
|
||||
brands = tuple(v._brand() for v in Versioned.__subclasses__())
|
||||
brands_set = set(brands)
|
||||
if len(brands) != len(brands_set):
|
||||
duplicate_brands = list(brands)
|
||||
for brand in brands_set:
|
||||
duplicate_brands.remove(brand)
|
||||
pytest.fail(f"Duplicated brand(s) {duplicate_brands}.")
|
||||
|
||||
|
||||
def test_valid_branding():
|
||||
for cls in Versioned.__subclasses__():
|
||||
if len(cls._brand()) != cls._BRAND_SIZE:
|
||||
pytest.fail(f"Brand must be exactly {str(Versioned._BRAND_SIZE)} bytes.")
|
||||
if not re.fullmatch(rb'\w+', cls._brand()):
|
||||
pytest.fail(f"Brand must be alphanumeric; Got {cls._brand()}")
|
||||
|
||||
def test_valid_version_implementation():
|
||||
for cls in Versioned.__subclasses__():
|
||||
_check_valid_version_tuple(version=cls._version(), cls=cls)
|
||||
|
||||
|
||||
def test_valid_old_handlers_index():
|
||||
for cls in Versioned.__subclasses__():
|
||||
for version in cls._deserializers():
|
||||
_check_valid_version_tuple(version=version, cls=cls)
|
||||
|
||||
|
||||
def test_version_metadata():
|
||||
major, minor = A._version()
|
||||
assert A.version_string() == f'{major}.{minor}'
|
||||
|
||||
|
||||
def test_versioning_header_prepend():
|
||||
a = A(1) # stake sauce
|
||||
assert a.x == 1
|
||||
|
||||
serialized = bytes(a)
|
||||
assert len(serialized) > Versioned._HEADER_SIZE
|
||||
|
||||
header = serialized[:Versioned._HEADER_SIZE]
|
||||
brand = header[:Versioned._BRAND_SIZE]
|
||||
assert brand == A._brand()
|
||||
|
||||
version = header[Versioned._BRAND_SIZE:]
|
||||
major, minor = version[:Versioned._VERSION_PART_SIZE], version[Versioned._VERSION_PART_SIZE:]
|
||||
major_number = int.from_bytes(major, 'big')
|
||||
minor_number = int.from_bytes(minor, 'big')
|
||||
assert (major_number, minor_number) == A._version()
|
||||
|
||||
|
||||
def test_versioning_input_too_short():
|
||||
empty = b'ABCD\x00\x01'
|
||||
with pytest.raises(ValueError, match='Invalid bytes for A.'):
|
||||
A.from_bytes(empty)
|
||||
|
||||
|
||||
def test_versioning_empty_payload():
|
||||
empty = b'ABCD\x00\x02\x00\x01'
|
||||
with pytest.raises(ValueError, match='No content to deserialize A.'):
|
||||
A.from_bytes(empty)
|
||||
|
||||
|
||||
def test_versioning_invalid_brand():
|
||||
invalid = b'\x01\x02\x00\x03\x00\x0112'
|
||||
with pytest.raises(Versioned.InvalidHeader, match="Incompatible bytes for A."):
|
||||
A.from_bytes(invalid)
|
||||
|
||||
# A partially invalid brand, to check that the regexp validates
|
||||
# the whole brand and not just the beginning of it.
|
||||
invalid = b'ABC \x00\x02\x00\x0112'
|
||||
with pytest.raises(Versioned.InvalidHeader, match="Incompatible bytes for A."):
|
||||
A.from_bytes(invalid)
|
||||
|
||||
|
||||
def test_versioning_incorrect_brand():
|
||||
incorrect = b'ABAB\x00\x0112'
|
||||
with pytest.raises(Versioned.InvalidHeader, match="Incorrect brand. Expected b'ABCD', Got b'ABAB'."):
|
||||
A.from_bytes(incorrect)
|
||||
|
||||
|
||||
def test_unknown_future_major_version():
|
||||
empty = b'ABCD\x00\x03\x00\x0212'
|
||||
message = 'Incompatible versioned bytes for A. Compatible version is 2.x, Got 3.2.'
|
||||
with pytest.raises(ValueError, match=message):
|
||||
A.from_bytes(empty)
|
||||
|
||||
|
||||
def test_incompatible_old_major_version(mocker):
|
||||
current_spy = mocker.spy(A, "_from_bytes_current")
|
||||
v1_data = b'ABCD\x00\x01\x00\x0012'
|
||||
message = 'Incompatible versioned bytes for A. Compatible version is 2.x, Got 1.0.'
|
||||
with pytest.raises(Versioned.IncompatibleVersion, match=message):
|
||||
A.from_bytes(v1_data)
|
||||
assert not current_spy.call_count
|
||||
|
||||
|
||||
def test_incompatible_future_major_version(mocker):
|
||||
current_spy = mocker.spy(A, "_from_bytes_current")
|
||||
v1_data = b'ABCD\x00\x03\x00\x0012'
|
||||
message = 'Incompatible versioned bytes for A. Compatible version is 2.x, Got 3.0.'
|
||||
with pytest.raises(Versioned.IncompatibleVersion, match=message):
|
||||
A.from_bytes(v1_data)
|
||||
assert not current_spy.call_count
|
||||
|
||||
|
||||
def test_resolve_version():
|
||||
# past
|
||||
v2_0 = 2, 0
|
||||
resolved_version = A._resolve_version(version=v2_0)
|
||||
assert resolved_version == v2_0
|
||||
|
||||
# present
|
||||
v2_1 = 2, 1
|
||||
resolved_version = A._resolve_version(version=v2_1)
|
||||
assert resolved_version == v2_1
|
||||
|
||||
# future minor version resolves to the latest minor version.
|
||||
v2_2 = 2, 2
|
||||
resolved_version = A._resolve_version(version=v2_2)
|
||||
assert resolved_version == v2_1
|
||||
|
||||
|
||||
def test_old_minor_version_handler_routing(mocker):
|
||||
current_spy = mocker.spy(A, "_from_bytes_current")
|
||||
v2_0_spy = mocker.spy(A, "_from_bytes_v2_0")
|
||||
|
||||
# Old minor version
|
||||
v2_0_data = b'ABCD\x00\x02\x00\x0012'
|
||||
a = A.from_bytes(v2_0_data)
|
||||
assert a.x == 18
|
||||
|
||||
# Old minor version was correctly routed to the v2.0 handler.
|
||||
assert v2_0_spy.call_count == 1
|
||||
v2_0_spy.assert_called_with(b'12')
|
||||
assert not current_spy.call_count
|
||||
|
||||
|
||||
def test_current_minor_version_handler_routing(mocker):
|
||||
current_spy = mocker.spy(A, "_from_bytes_current")
|
||||
v2_0_spy = mocker.spy(A, "_from_bytes_v2_0")
|
||||
|
||||
v2_1_data = b'ABCD\x00\x02\x00\x01\x12'
|
||||
a = A.from_bytes(v2_1_data)
|
||||
assert a.x == 18
|
||||
|
||||
# Current version was correctly routed to the v2.1 handler.
|
||||
assert current_spy.call_count == 1
|
||||
current_spy.assert_called_with(b'\x12')
|
||||
assert not v2_0_spy.call_count
|
||||
|
||||
|
||||
def test_future_minor_version_handler_routing(mocker):
|
||||
current_spy = mocker.spy(A, "_from_bytes_current")
|
||||
v2_0_spy = mocker.spy(A, "_from_bytes_v2_0")
|
||||
|
||||
v2_2_data = b'ABCD\x00\x02\x02\x01\x12'
|
||||
a = A.from_bytes(v2_2_data)
|
||||
assert a.x == 18
|
||||
|
||||
# Future minor version was correctly routed to
|
||||
# the current minor version handler.
|
||||
assert current_spy.call_count == 1
|
||||
current_spy.assert_called_with(b'\x12')
|
||||
assert not v2_0_spy.call_count
|
|
@ -23,7 +23,7 @@ import socket
|
|||
from constant_sorrow.constants import CERTIFICATE_NOT_SAVED
|
||||
from flask import Response
|
||||
|
||||
from nucypher.core import MetadataRequest
|
||||
from nucypher_core import MetadataRequest, FleetStateChecksum
|
||||
|
||||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.config.constants import TEMPORARY_DOMAIN
|
||||
|
@ -186,7 +186,7 @@ class EvilMiddleWare(MockRestMiddleware):
|
|||
"""
|
||||
Try to get Ursula to propagate a malicious (or otherwise shitty) interface ID.
|
||||
"""
|
||||
fleet_state_checksum = os.urandom(32).hex()
|
||||
fleet_state_checksum = FleetStateChecksum(this_node=None, other_nodes=[])
|
||||
request = MetadataRequest(fleet_state_checksum=fleet_state_checksum, announce_nodes=[shitty_metadata])
|
||||
response = self.client.post(node_or_sprout=ursula,
|
||||
path="node_metadata",
|
||||
|
|
|
@ -20,7 +20,7 @@ import random
|
|||
import string
|
||||
from typing import Dict, Tuple
|
||||
|
||||
from nucypher.core import MessageKit, RetrievalKit
|
||||
from nucypher_core import MessageKit, RetrievalKit
|
||||
|
||||
from nucypher.characters.control.specifications.fields import Key, TreasureMap
|
||||
from nucypher.characters.lawful import Enrico
|
||||
|
|
|
@ -21,12 +21,13 @@ from typing import Iterable, List, Optional, Set
|
|||
|
||||
from cryptography.x509 import Certificate
|
||||
|
||||
from nucypher_core.umbral import SecretKey, Signer, generate_kfrags
|
||||
|
||||
from nucypher.blockchain.eth.actors import Staker
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterface
|
||||
from nucypher.characters.lawful import Bob
|
||||
from nucypher.characters.lawful import Ursula
|
||||
from nucypher.config.characters import UrsulaConfiguration
|
||||
from nucypher.crypto.umbral_adapter import SecretKey, Signer, encrypt, generate_kfrags, reencrypt
|
||||
from tests.constants import NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK
|
||||
from tests.mock.datastore import MOCK_DB
|
||||
|
||||
|
|
Loading…
Reference in New Issue