diff --git a/nucypher/acumen/perception.py b/nucypher/acumen/perception.py
index 6b14bf7da..84555e21e 100644
--- a/nucypher/acumen/perception.py
+++ b/nucypher/acumen/perception.py
@@ -22,14 +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 nucypher_core import FleetStateChecksum, NodeMetadata
-from ..crypto.utils import keccak_digest
from nucypher.utilities.logging import Logger
from .nicknames import Nickname
diff --git a/nucypher/characters/control/specifications/fields/__init__.py b/nucypher/characters/control/specifications/fields/__init__.py
index 17912d924..ae5e73bab 100644
--- a/nucypher/characters/control/specifications/fields/__init__.py
+++ b/nucypher/characters/control/specifications/fields/__init__.py
@@ -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 *
diff --git a/nucypher/characters/control/specifications/fields/signature.py b/nucypher/characters/control/specifications/fields/signature.py
deleted file mode 100644
index cda78a710..000000000
--- a/nucypher/characters/control/specifications/fields/signature.py
+++ /dev/null
@@ -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 .
-"""
-
-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}")
diff --git a/nucypher/characters/lawful.py b/nucypher/characters/lawful.py
index f978bd0bb..f8b98d3cf 100644
--- a/nucypher/characters/lawful.py
+++ b/nucypher/characters/lawful.py
@@ -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
@@ -39,11 +38,9 @@ from constant_sorrow.constants import (
from cryptography.hazmat.primitives.serialization import Encoding
from cryptography.x509 import Certificate, NameOID
from eth_typing.evm import ChecksumAddress
-from eth_utils import to_canonical_address, to_checksum_address
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
@@ -88,7 +85,6 @@ from nucypher.crypto.umbral_adapter import (
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
@@ -360,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]]]:
@@ -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,
diff --git a/nucypher/core.py b/nucypher/core.py
deleted file mode 100644
index 36ddf3b8b..000000000
--- a/nucypher/core.py
+++ /dev/null
@@ -1,1025 +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 .
-"""
-
-from typing import Optional, Sequence, Dict, Tuple, List, Iterable, Mapping, NamedTuple, Callable
-
-from bytestring_splitter import (
- BytestringSplitter,
- VariableLengthBytestring,
- BytestringKwargifier,
- BytestringSplittingError,
-)
-from eth_typing.evm import ChecksumAddress
-from eth_utils.address import to_checksum_address, to_canonical_address
-
-from nucypher.utilities.versioning import Versioned
-
-from nucypher.blockchain.eth.constants import LENGTH_ECDSA_SIGNATURE_WITH_RECOVERY
-from nucypher.crypto.utils import keccak_digest
-from nucypher.crypto.signing import InvalidSignature
-import nucypher.crypto.umbral_adapter as umbral # need it to mock `umbral.encrypt`
-from nucypher.crypto.umbral_adapter import (
- SecretKey,
- PublicKey,
- Signer,
- Capsule,
- Signature,
- CapsuleFrag,
- VerifiedCapsuleFrag,
- KeyFrag,
- VerifiedKeyFrag,
- VerificationError,
- decrypt_original,
- decrypt_reencrypted,
- )
-
-
-ETH_ADDRESS_BYTE_LENGTH = 20
-
-key_splitter = BytestringSplitter((PublicKey, PublicKey.serialized_size()))
-signature_splitter = BytestringSplitter((Signature, Signature.serialized_size()))
-capsule_splitter = BytestringSplitter((Capsule, Capsule.serialized_size()))
-cfrag_splitter = BytestringSplitter((CapsuleFrag, CapsuleFrag.serialized_size()))
-kfrag_splitter = BytestringSplitter((KeyFrag, KeyFrag.serialized_size()))
-checksum_address_splitter = BytestringSplitter((to_checksum_address, ETH_ADDRESS_BYTE_LENGTH)) # TODO: is there a pre-defined constant?
-variable_length_splitter = BytestringSplitter(VariableLengthBytestring)
-
-
-_OPTIONAL_NONE = b'\x00'
-_OPTIONAL_SOME = b'\x01'
-
-
-def serialize_optional(value: Optional) -> bytes:
- if value is None:
- return _OPTIONAL_NONE
- else:
- return _OPTIONAL_SOME + bytes(value)
-
-
-def take_optional(take_obj: Callable[[bytes], Tuple[object, bytes]], data: bytes):
- optional_flag = data[0:1]
- remainder = data[1:]
-
- if optional_flag == _OPTIONAL_NONE:
- return None, remainder
- elif optional_flag == _OPTIONAL_SOME:
- obj, remainder = take_obj(remainder)
- return obj, remainder
- else:
- raise ValueError(f"Incorrect optional flag: {optional_flag}")
-
-
-def take_decentralized_identity_evidence(data):
- expected_length = LENGTH_ECDSA_SIGNATURE_WITH_RECOVERY
- if len(data) < LENGTH_ECDSA_SIGNATURE_WITH_RECOVERY:
- raise ValueError(f"Not enough bytes to fit a decentralized_identity_evidence ({len(data)})")
- return data[:expected_length], data[expected_length:]
-
-
-class MessageKit(Versioned):
- """
- A message encrypted for re-encryption
- """
-
- @classmethod
- def author(cls, policy_encrypting_key: PublicKey, plaintext: bytes) -> 'MessageKit':
- capsule, ciphertext = umbral.encrypt(policy_encrypting_key, plaintext)
- return cls(capsule=capsule, ciphertext=ciphertext)
-
- def __init__(self, capsule: Capsule, ciphertext: bytes):
- self.ciphertext = ciphertext
- self.capsule = capsule
-
- def __eq__(self, other):
- return (self.ciphertext == other.ciphertext and
- self.capsule == other.capsule)
-
- def decrypt(self, sk: SecretKey) -> bytes:
- return decrypt_original(sk, self.capsule, self.ciphertext)
-
- def decrypt_reencrypted(self,
- sk: SecretKey,
- policy_encrypting_key: PublicKey,
- cfrags: Sequence[VerifiedCapsuleFrag],
- ) -> bytes:
- return decrypt_reencrypted(sk, policy_encrypting_key, self.capsule, cfrags, self.ciphertext)
-
- def __str__(self):
- return f"{self.__class__.__name__}({self.capsule})"
-
- def _payload(self) -> bytes:
- return bytes(self.capsule) + VariableLengthBytestring(self.ciphertext)
-
- @classmethod
- def _brand(cls) -> bytes:
- return b'MKit'
-
- @classmethod
- def _version(cls) -> Tuple[int, int]:
- return 1, 0
-
- @classmethod
- def _old_version_handlers(cls) -> Dict:
- return {}
-
- @classmethod
- def _from_bytes_current(cls, data):
- splitter = BytestringSplitter(capsule_splitter, VariableLengthBytestring)
- capsule, ciphertext, remainder = splitter(data, return_remainder=True)
- return cls(capsule, ciphertext), remainder
-
-
-class HRAC:
- """
- "hashed resource access code".
-
- A hash of:
- * Publisher's verifying key
- * Bob's verifying key
- * the label
-
- Publisher and Bob have all the information they need to construct this.
- Ursula does not, so we share it with her.
-
- This way, Bob can generate it and use it to find the TreasureMap.
- """
-
- # Note: this corresponds to the hardcoded size in the contracts
- # (which use `byte16` for this variable).
- SIZE = 16
-
- @classmethod
- def derive(cls, publisher_verifying_key: PublicKey, bob_verifying_key: PublicKey, label: bytes) -> 'HRAC':
- return cls(keccak_digest(bytes(publisher_verifying_key) + bytes(bob_verifying_key) + label)[:cls.SIZE])
-
- def __init__(self, hrac_bytes: bytes):
- self._hrac_bytes = hrac_bytes
-
- def __bytes__(self):
- return self._hrac_bytes
-
- @classmethod
- def from_bytes(cls, data: bytes) -> 'HRAC':
- if len(data) != cls.SIZE:
- raise ValueError(f"Incorrect HRAC size: expected {cls.SIZE}, got {len(data)}")
- return cls(data)
-
- def __eq__(self, other):
- return self._hrac_bytes == other._hrac_bytes
-
- def __hash__(self):
- return hash(self._hrac_bytes)
-
- def __str__(self):
- return f"HRAC({self._hrac_bytes.hex()})"
-
-
-hrac_splitter = BytestringSplitter((HRAC, HRAC.SIZE))
-
-
-class AuthorizedKeyFrag(Versioned):
-
- def __init__(self, signature: Signature, kfrag: KeyFrag):
- self.signature = signature
- self.kfrag = kfrag
-
- @classmethod
- def construct_by_publisher(cls,
- signer: Signer,
- hrac: HRAC,
- verified_kfrag: VerifiedKeyFrag,
- ) -> 'AuthorizedKeyFrag':
-
- # "un-verify" kfrag to keep further logic streamlined
- kfrag = KeyFrag.from_bytes(bytes(verified_kfrag))
-
- # Publisher makes plain to Ursula that, upon decrypting this message,
- # this particular KFrag is authorized for use in the policy identified by this HRAC.
- signature = signer.sign(bytes(hrac) + bytes(kfrag))
-
- return cls(signature, kfrag)
-
- def _payload(self) -> bytes:
- """Returns the unversioned bytes serialized representation of this instance."""
- return bytes(self.signature) + bytes(self.kfrag)
-
- @classmethod
- def _brand(cls) -> bytes:
- return b'AKFr'
-
- @classmethod
- def _version(cls) -> Tuple[int, int]:
- return 1, 0
-
- @classmethod
- def _old_version_handlers(cls) -> Dict:
- return {}
-
- @classmethod
- def _from_bytes_current(cls, data):
- splitter = BytestringSplitter(signature_splitter, kfrag_splitter)
- signature, kfrag, remainder = splitter(data, return_remainder=True)
- return cls(signature, kfrag), remainder
-
- def verify(self,
- hrac: HRAC,
- publisher_verifying_key: PublicKey,
- ) -> VerifiedKeyFrag:
-
- signed_message = bytes(hrac) + bytes(self.kfrag)
-
- if not self.signature.verify(message=signed_message, verifying_pk=publisher_verifying_key):
- raise InvalidSignature("HRAC + KeyFrag are not signed by the provided publisher")
-
- # Ursula has no side channel to get the KeyFrag author's key,
- # so verifying the keyfrag is useless.
- # TODO: assuming here that VerifiedKeyFrag and KeyFrag have the same byte representation;
- # would it be more clear if `kfrag` had some method like `force_verify()`?
- verified_kfrag = VerifiedKeyFrag.from_verified_bytes(bytes(self.kfrag))
-
- return verified_kfrag
-
-
-class EncryptedKeyFrag:
-
- _splitter = BytestringSplitter(capsule_splitter, VariableLengthBytestring)
-
- @classmethod
- def author(cls, recipient_key: PublicKey, authorized_kfrag: AuthorizedKeyFrag):
- # TODO: using Umbral for encryption to avoid introducing more crypto primitives.
- # Most probably it is an overkill, unless it can be used somehow
- # for Ursula-to-Ursula "baton passing".
- capsule, ciphertext = umbral.encrypt(recipient_key, bytes(authorized_kfrag))
- return cls(capsule, ciphertext)
-
- def __init__(self, capsule: Capsule, ciphertext: bytes):
- self.capsule = capsule
- self.ciphertext = ciphertext
-
- def decrypt(self, sk: SecretKey) -> AuthorizedKeyFrag:
- cleartext = decrypt_original(sk, self.capsule, self.ciphertext)
- return AuthorizedKeyFrag.from_bytes(cleartext)
-
- def __bytes__(self):
- return bytes(self.capsule) + bytes(VariableLengthBytestring(self.ciphertext))
-
- # Ideally we would define a splitter here that would deserialize into an EKF,
- # but due to BSS limitations it cannot be nested (since it doesn't have a definite size).
- # So we have to define this helper method and use that instead.
- @classmethod
- def take(cls, data):
- capsule, ciphertext, remainder = cls._splitter(data, return_remainder=True)
- return cls(capsule, ciphertext), remainder
-
- def __eq__(self, other):
- return self.capsule == other.capsule and self.ciphertext == other.ciphertext
-
-
-class TreasureMap(Versioned):
-
- def __init__(self,
- threshold: int,
- hrac: HRAC,
- policy_encrypting_key: PublicKey,
- publisher_verifying_key: PublicKey,
- destinations: Dict[ChecksumAddress, EncryptedKeyFrag]):
- self.threshold = threshold
- self.destinations = destinations
- self.hrac = hrac
- self.policy_encrypting_key = policy_encrypting_key
- self.publisher_verifying_key = publisher_verifying_key
-
- def __iter__(self):
- return iter(self.destinations.items())
-
- def __len__(self):
- return len(self.destinations)
-
- def __eq__(self, other):
- if not isinstance(other, TreasureMap):
- return False
-
- return (self.threshold == other.threshold and
- self.hrac == other.hrac and
- self.destinations == other.destinations)
-
- @classmethod
- def construct_by_publisher(cls,
- signer: Signer,
- hrac: HRAC,
- policy_encrypting_key: PublicKey,
- assigned_kfrags: Mapping[ChecksumAddress, Tuple[PublicKey, VerifiedKeyFrag]],
- threshold: int,
- ) -> 'TreasureMap':
- """Create a new treasure map for a collection of ursulas and kfrags."""
-
- if threshold < 1 or threshold > 255:
- raise ValueError("The threshold must be between 1 and 255.")
-
- if len(assigned_kfrags) < threshold:
- raise ValueError(
- f"The number of destinations ({len(assigned_kfrags)}) "
- f"must be equal or greater than the threshold ({threshold})")
-
- # Encrypt each kfrag for an Ursula.
- destinations = {}
- for ursula_address, key_and_kfrag in assigned_kfrags.items():
- ursula_key, verified_kfrag = key_and_kfrag
- authorized_kfrag = AuthorizedKeyFrag.construct_by_publisher(signer=signer,
- hrac=hrac,
- verified_kfrag=verified_kfrag,
- )
- encrypted_kfrag = EncryptedKeyFrag.author(recipient_key=ursula_key,
- authorized_kfrag=authorized_kfrag)
-
- destinations[ursula_address] = encrypted_kfrag
-
- return cls(threshold=threshold,
- hrac=hrac,
- policy_encrypting_key=policy_encrypting_key,
- publisher_verifying_key=signer.verifying_key(),
- destinations=destinations)
-
- @classmethod
- def _brand(cls) -> bytes:
- return b'TMap'
-
- @classmethod
- def _version(cls) -> Tuple[int, int]:
- return 1, 0
-
- @classmethod
- def _old_version_handlers(cls) -> Dict:
- return {}
-
- def _payload(self) -> bytes:
- """Returns the unversioned bytes serialized representation of this instance."""
- assigned_kfrags = b''.join(
- to_canonical_address(ursula_address) + bytes(encrypted_kfrag)
- for ursula_address, encrypted_kfrag in self.destinations.items()
- )
-
- return (self.threshold.to_bytes(1, "big") +
- bytes(self.hrac) +
- bytes(self.policy_encrypting_key) +
- bytes(self.publisher_verifying_key) +
- bytes(VariableLengthBytestring(assigned_kfrags)))
-
- @classmethod
- def _from_bytes_current(cls, data):
-
- main_splitter = BytestringSplitter(
- (int, 1, {'byteorder': 'big'}),
- hrac_splitter,
- key_splitter,
- key_splitter,
- VariableLengthBytestring,
- )
-
- threshold, hrac, policy_encrypting_key, publisher_verifying_key, assigned_kfrags_bytes, remainder = main_splitter(data, return_remainder=True)
-
- destinations = {}
- while assigned_kfrags_bytes:
- ursula_address, assigned_kfrags_bytes = checksum_address_splitter(assigned_kfrags_bytes, return_remainder=True)
- ekf, assigned_kfrags_bytes = EncryptedKeyFrag.take(assigned_kfrags_bytes)
- destinations[ursula_address] = ekf
-
- return cls(threshold, hrac, policy_encrypting_key, publisher_verifying_key, destinations), remainder
-
- def encrypt(self,
- signer: Signer,
- recipient_key: PublicKey,
- ) -> 'EncryptedTreasureMap':
- return EncryptedTreasureMap.construct_by_publisher(signer=signer,
- recipient_key=recipient_key,
- treasure_map=self)
-
-
-class AuthorizedTreasureMap(Versioned):
-
- @classmethod
- def construct_by_publisher(cls,
- signer: Signer,
- recipient_key: PublicKey,
- treasure_map: TreasureMap
- ) -> 'AuthorizedTreasureMap':
- payload = bytes(recipient_key) + bytes(treasure_map)
- signature = signer.sign(payload)
- return cls(signature, treasure_map)
-
- def __init__(self, signature: Signature, treasure_map: TreasureMap):
- self.signature = signature
- self.treasure_map = treasure_map
-
- @classmethod
- def _brand(cls) -> bytes:
- return b'AMap'
-
- @classmethod
- def _version(cls) -> Tuple[int, int]:
- return 1, 0
-
- @classmethod
- def _old_version_handlers(cls) -> Dict:
- return {}
-
- def _payload(self) -> bytes:
- """Returns the unversioned bytes serialized representation of this instance."""
- return (bytes(self.signature) +
- bytes(self.treasure_map))
-
- @classmethod
- def _from_bytes_current(cls, data):
- signature, remainder = signature_splitter(data, return_remainder=True)
- treasure_map, remainder = TreasureMap.take(remainder)
- return cls(signature, treasure_map), remainder
-
- def verify(self, recipient_key: PublicKey, publisher_verifying_key: PublicKey) -> TreasureMap:
- payload = bytes(recipient_key) + bytes(self.treasure_map)
- if not self.signature.verify(message=payload, verifying_pk=publisher_verifying_key):
- raise InvalidSignature("This TreasureMap does not contain the correct signature "
- "from the publisher.")
- return self.treasure_map
-
-
-class EncryptedTreasureMap(Versioned):
-
- def __init__(self, capsule: Capsule, ciphertext: bytes):
- self.capsule = capsule
- self.ciphertext = ciphertext
-
- @classmethod
- def construct_by_publisher(cls,
- signer: Signer,
- recipient_key: PublicKey,
- treasure_map: TreasureMap,
- ) -> 'EncryptedTreasureMap':
-
- # TODO: using Umbral for encryption to avoid introducing more crypto primitives.
- # Most probably it is an overkill, unless it can be used somehow
- # for Ursula-to-Ursula "baton passing".
-
- # TODO: `signer` here can be different from the one in TreasureMap, it seems.
- # Do we ever cross-check them? Do we want to enforce them to be the same?
- payload = AuthorizedTreasureMap.construct_by_publisher(signer=signer,
- recipient_key=recipient_key,
- treasure_map=treasure_map)
-
- capsule, ciphertext = umbral.encrypt(recipient_key, bytes(payload))
- return cls(capsule, ciphertext)
-
- def decrypt(self, sk: SecretKey) -> AuthorizedTreasureMap:
- payload_bytes = decrypt_original(sk, self.capsule, self.ciphertext)
- return AuthorizedTreasureMap.from_bytes(payload_bytes)
-
- def _payload(self) -> bytes:
- return bytes(self.capsule) + bytes(VariableLengthBytestring(self.ciphertext))
-
- @classmethod
- def _brand(cls) -> bytes:
- return b'EMap'
-
- @classmethod
- def _version(cls) -> Tuple[int, int]:
- return 1, 0
-
- @classmethod
- def _old_version_handlers(cls) -> Dict:
- return {}
-
- @classmethod
- def _from_bytes_current(cls, data):
- splitter = BytestringSplitter(capsule_splitter, VariableLengthBytestring)
- capsule, ciphertext, remainder = splitter(data, return_remainder=True)
- return cls(capsule, ciphertext), remainder
-
- def __eq__(self, other):
- return bytes(self) == bytes(other)
-
- def __hash__(self):
- return hash((self.__class__, bytes(self)))
-
-
-class ReencryptionRequest(Versioned):
- """
- A request for an Ursula to reencrypt for several capsules.
- """
-
- @classmethod
- def from_treasure_map(cls,
- ursula_address: ChecksumAddress,
- capsules: Sequence[Capsule],
- treasure_map: TreasureMap,
- bob_verifying_key: PublicKey,
- ) -> 'ReencryptionRequest':
- return cls(hrac=treasure_map.hrac,
- publisher_verifying_key=treasure_map.publisher_verifying_key,
- bob_verifying_key=bob_verifying_key,
- encrypted_kfrag=treasure_map.destinations[ursula_address],
- capsules=capsules,
- )
-
- def __init__(self,
- hrac: HRAC,
- publisher_verifying_key: PublicKey,
- bob_verifying_key: PublicKey,
- encrypted_kfrag: EncryptedKeyFrag,
- capsules: List[Capsule]):
-
- self.hrac = hrac
- self.publisher_verifying_key = publisher_verifying_key
- self.bob_verifying_key = bob_verifying_key
- self.encrypted_kfrag = encrypted_kfrag
- self.capsules = capsules
-
- def _payload(self) -> bytes:
- return (bytes(self.hrac) +
- bytes(self.publisher_verifying_key) +
- bytes(self.bob_verifying_key) +
- bytes(self.encrypted_kfrag) +
- bytes(VariableLengthBytestring(b''.join(bytes(capsule) for capsule in self.capsules)))
- )
-
- @classmethod
- def _brand(cls) -> bytes:
- return b'ReRq'
-
- @classmethod
- def _version(cls) -> Tuple[int, int]:
- return 1, 0
-
- @classmethod
- def _old_version_handlers(cls) -> Dict:
- return {}
-
- @classmethod
- def _from_bytes_current(cls, data):
- splitter = (hrac_splitter +
- key_splitter +
- key_splitter)
-
- hrac, publisher_vk, bob_vk, remainder = splitter(data, return_remainder=True)
- ekfrag, remainder = EncryptedKeyFrag.take(remainder)
- capsule_bytes, remainder = variable_length_splitter(remainder, return_remainder=True)
- capsules = capsule_splitter.repeat(capsule_bytes)
- return cls(hrac, publisher_vk, bob_vk, ekfrag, capsules), remainder
-
-
-class ReencryptionResponse(Versioned):
- """
- A response from Ursula with reencrypted capsule frags.
- """
-
- @classmethod
- def construct_by_ursula(cls,
- signer: Signer,
- capsules: List[Capsule],
- cfrags: List[VerifiedCapsuleFrag],
- ) -> 'ReencryptionResponse':
-
- # un-verify
- cfrags = [CapsuleFrag.from_bytes(bytes(cfrag)) for cfrag in cfrags]
-
- capsules_bytes = b''.join(bytes(capsule) for capsule in capsules)
- cfrags_bytes = b''.join(bytes(cfrag) for cfrag in cfrags)
- signature = signer.sign(capsules_bytes + cfrags_bytes)
- return cls(cfrags, signature)
-
- def __init__(self, cfrags: List[CapsuleFrag], signature: Signature):
- self.cfrags = cfrags
- self.signature = signature
-
- def _payload(self) -> bytes:
- """Returns the unversioned bytes serialized representation of this instance."""
- return bytes(self.signature) + bytes(VariableLengthBytestring(b''.join(bytes(cfrag) for cfrag in self.cfrags)))
-
- @classmethod
- def _brand(cls) -> bytes:
- return b'ReRs'
-
- @classmethod
- def _version(cls) -> Tuple[int, int]:
- return 1, 0
-
- @classmethod
- def _old_version_handlers(cls) -> Dict:
- return {}
-
- @classmethod
- def _from_bytes_current(cls, data):
- splitter = BytestringSplitter(signature_splitter, VariableLengthBytestring)
- signature, cfrags_bytes, remainder = splitter(data, return_remainder=True)
-
- # We would never send a request with no capsules, so there should be cfrags.
- # The splitter would fail anyway, this just makes the error message more clear.
- if not cfrags_bytes:
- raise ValueError(f"{cls.__name__} contains no cfrags")
-
- cfrags = cfrag_splitter.repeat(cfrags_bytes)
- return cls(cfrags, signature), remainder
-
- def verify(self,
- capsules: Sequence[Capsule],
- alice_verifying_key: PublicKey,
- ursula_verifying_key: PublicKey,
- policy_encrypting_key: PublicKey,
- bob_encrypting_key: PublicKey,
- ) -> List[VerifiedCapsuleFrag]:
-
- if len(capsules) != len(self.cfrags):
- raise ValueError("Mismatched number of capsules and cfrags")
-
- capsules_bytes = b''.join(bytes(capsule) for capsule in capsules)
- cfrags_bytes = b''.join(bytes(cfrag) for cfrag in self.cfrags)
-
- # Validate re-encryption signature
- if not self.signature.verify(ursula_verifying_key, capsules_bytes + cfrags_bytes):
- message = (f"{capsules} and {self.cfrags} "
- "are not properly signed by Ursula.")
- raise InvalidSignature(message)
-
- verified_cfrags = {}
- for capsule, cfrag in zip(capsules, self.cfrags):
- verified_cfrags[capsule] = cfrag.verify(capsule,
- verifying_pk=alice_verifying_key,
- delegating_pk=policy_encrypting_key,
- receiving_pk=bob_encrypting_key)
-
- return verified_cfrags
-
-
-class RetrievalKit(Versioned):
- """
- An object encapsulating the information necessary for retrieval of cfrags from Ursulas.
- Contains the capsule and the checksum addresses of Ursulas from which the requester
- already received cfrags.
- """
-
- @classmethod
- def from_message_kit(cls, message_kit: MessageKit) -> 'RetrievalKit':
- return cls(message_kit.capsule, set())
-
- def __init__(self, capsule: Capsule, queried_addresses: Iterable[ChecksumAddress]):
- self.capsule = capsule
- # Can store cfrags too, if we're worried about Ursulas supplying duplicate ones.
- self.queried_addresses = set(queried_addresses)
-
- def _payload(self) -> bytes:
- return (bytes(self.capsule) +
- bytes(VariableLengthBytestring(b''.join(to_canonical_address(address) for address in self.queried_addresses))))
-
- @classmethod
- def _brand(cls) -> bytes:
- return b'RKit'
-
- @classmethod
- def _version(cls) -> Tuple[int, int]:
- return 1, 0
-
- @classmethod
- def _old_version_handlers(cls) -> Dict:
- return {}
-
- @classmethod
- def _from_bytes_current(cls, data):
- splitter = BytestringSplitter(capsule_splitter, VariableLengthBytestring)
- capsule, addresses_bytes, remainder = splitter(data, return_remainder=True)
- if addresses_bytes:
- addresses = checksum_address_splitter.repeat(addresses_bytes)
- else:
- addresses = ()
- return cls(capsule, addresses), remainder
-
-
-class RevocationOrder(Versioned):
- """
- Represents a string used by characters to perform a revocation on a specific Ursula.
- """
-
- @classmethod
- def author(cls,
- signer: Signer,
- ursula_address: ChecksumAddress,
- encrypted_kfrag: EncryptedKeyFrag,
- ) -> 'RevocationOrder':
- return cls(ursula_address=ursula_address,
- encrypted_kfrag=encrypted_kfrag,
- signature=signer.sign(cls._signed_payload(ursula_address, encrypted_kfrag)))
-
- def __init__(self, ursula_address: ChecksumAddress, encrypted_kfrag: EncryptedKeyFrag, signature: Signature):
- self.ursula_address = ursula_address
- self.encrypted_kfrag = encrypted_kfrag
- self.signature = signature
-
- def __repr__(self):
- return bytes(self)
-
- def __len__(self):
- return len(bytes(self))
-
- def __eq__(self, other):
- return bytes(self) == bytes(other)
-
- @staticmethod
- def _signed_payload(ursula_address, encrypted_kfrag):
- return to_canonical_address(ursula_address) + bytes(encrypted_kfrag)
-
- def verify_signature(self, alice_verifying_key: PublicKey) -> bool:
- """
- Verifies the revocation was from the provided pubkey.
- """
- # TODO: raise an exception instead of returning `bool`?
- payload = self._signed_payload(self.ursula_address, self.encrypted_kfrag)
- if not self.signature.verify(payload, alice_verifying_key):
- raise InvalidSignature(f"Revocation has an invalid signature: {self.signature}")
- return True
-
- @classmethod
- def _brand(cls) -> bytes:
- return b'Revo'
-
- @classmethod
- def _version(cls) -> Tuple[int, int]:
- return 1, 0
-
- @classmethod
- def _old_version_handlers(cls) -> Dict:
- return {}
-
- def _payload(self) -> bytes:
- return bytes(self.signature) + self._signed_payload(self.ursula_address, self.encrypted_kfrag)
-
- @classmethod
- def _from_bytes_current(cls, data):
- splitter = BytestringSplitter(
- signature_splitter,
- checksum_address_splitter, # ursula canonical address
- )
- signature, ursula_address, remainder = splitter(data, return_remainder=True)
- ekfrag, remainder = EncryptedKeyFrag.take(remainder)
- obj = cls(ursula_address=ursula_address,
- encrypted_kfrag=ekfrag,
- signature=signature)
- return obj, remainder
-
-
-class NodeMetadataPayload(NamedTuple):
-
- public_address: bytes
- domain: str
- timestamp_epoch: int
- verifying_key: PublicKey
- encrypting_key: PublicKey
- certificate_bytes: bytes # serialized `cryptography.x509.Certificate`
- host: str
- port: int
- decentralized_identity_evidence: Optional[bytes] # TODO: make its own type?
-
- _splitter = BytestringSplitter(
- (bytes, ETH_ADDRESS_BYTE_LENGTH), # public_address
- VariableLengthBytestring, # domain_bytes
- (int, 4, {'byteorder': 'big'}), # timestamp_epoch
- key_splitter, # verifying_key
- key_splitter, # encrypting_key
- VariableLengthBytestring, # certificate_bytes
- VariableLengthBytestring, # host_bytes
- (int, 2, {'byteorder': 'big'}), # port
- )
-
- def __bytes__(self):
- as_bytes = bytes().join((self.public_address,
- bytes(VariableLengthBytestring(self.domain.encode('utf-8'))),
- self.timestamp_epoch.to_bytes(4, 'big'),
- bytes(self.verifying_key),
- bytes(self.encrypting_key),
- bytes(VariableLengthBytestring(self.certificate_bytes)),
- bytes(VariableLengthBytestring(self.host.encode('utf-8'))),
- self.port.to_bytes(2, 'big'),
- serialize_optional(self.decentralized_identity_evidence),
- ))
- return as_bytes
-
- @classmethod
- def take(cls, data):
- *fields, remainder = cls._splitter(data, return_remainder=True)
-
- (public_address,
- domain,
- timestamp_epoch,
- verifying_key,
- encrypting_key,
- certificate_bytes,
- host,
- port,
- ) = fields
-
- decentralized_identity_evidence, remainder = take_optional(take_decentralized_identity_evidence, remainder)
-
- obj = cls(public_address=public_address,
- domain=domain.decode('utf-8'),
- timestamp_epoch=timestamp_epoch,
- verifying_key=verifying_key,
- encrypting_key=encrypting_key,
- certificate_bytes=certificate_bytes,
- host=host.decode('utf-8'),
- port=port,
- decentralized_identity_evidence=decentralized_identity_evidence,
- )
-
- return obj, remainder
-
- @classmethod
- def from_bytes(cls, data):
- obj, remainder = cls.take(data)
- if remainder:
- raise ValueError(f"{len(remainder)} bytes remaining after deserializing {cls}")
- return obj
-
-
-class NodeMetadata(Versioned):
-
- @classmethod
- def author(cls, signer: Signer, **kwds):
- payload = NodeMetadataPayload(**kwds)
- signature = signer.sign(bytes(payload))
- # TODO: we can cache payload bytes here, for later use in serialization/verification
- return cls(signature=signature, payload=payload)
-
- def __init__(self, signature: Signature, payload: NodeMetadataPayload):
- self.signature = signature
- self._metadata_payload = payload
- for name, value in payload._asdict().items():
- setattr(self, name, value)
-
- def verify(self) -> bool:
- # Note: in order for this to make sense, `verifying_key` must be checked independently.
- # Currently it is done in `validate_worker()` (using `decentralized_identity_evidence`)
- # TODO: do this on deserialization?
- return self.signature.verify(message=bytes(self._metadata_payload), verifying_pk=self.verifying_key)
-
- @classmethod
- def _brand(cls) -> bytes:
- return b'NdMd'
-
- @classmethod
- def _version(cls) -> Tuple[int, int]:
- return 1, 0
-
- @classmethod
- def _old_version_handlers(cls) -> Dict:
- return {}
-
- def _payload(self):
- return bytes(self.signature) + bytes(self._metadata_payload)
-
- @classmethod
- def _from_bytes_current(cls, data: bytes):
- signature, remainder = signature_splitter(data, return_remainder=True)
- payload, remainder = NodeMetadataPayload.take(remainder)
- return cls(signature=signature, payload=payload), remainder
-
- @classmethod
- def _batch_from_bytes(cls, data: bytes):
- nodes = []
- while data:
- node, data = cls.take(data)
- nodes.append(node)
- return nodes
-
-
-class MetadataRequest(Versioned):
-
- _fleet_state_checksum_splitter = BytestringSplitter((bytes, 32))
-
- def __init__(self,
- fleet_state_checksum: str,
- announce_nodes: Optional[Iterable[NodeMetadata]] = None,
- ):
-
- self.fleet_state_checksum = fleet_state_checksum
- self.announce_nodes = announce_nodes
-
- @classmethod
- def _brand(cls) -> bytes:
- return b'MdRq'
-
- @classmethod
- def _version(cls) -> Tuple[int, int]:
- return 1, 0
-
- @classmethod
- def _old_version_handlers(cls) -> Dict:
- return {}
-
- def _payload(self):
- if self.announce_nodes:
- nodes_bytes = b''.join(bytes(n) for n in self.announce_nodes)
- else:
- nodes_bytes = b''
- return bytes.fromhex(self.fleet_state_checksum) + bytes(VariableLengthBytestring(nodes_bytes))
-
- @classmethod
- def _from_bytes_current(cls, data):
- splitter = BytestringSplitter(
- (bytes, 32), # fleet state checksum
- VariableLengthBytestring,
- )
- fleet_state_checksum_bytes, nodes_bytes, remainder = splitter(data, return_remainder=True)
- if nodes_bytes:
- nodes = NodeMetadata._batch_from_bytes(nodes_bytes)
- else:
- nodes = None
- obj = cls(fleet_state_checksum=fleet_state_checksum_bytes.hex(),
- announce_nodes=nodes)
- return obj, remainder
-
-
-class MetadataResponse(Versioned):
-
- @classmethod
- def author(cls,
- signer: Signer,
- timestamp_epoch: int,
- this_node: Optional[NodeMetadata] = None,
- other_nodes: Optional[Iterable[NodeMetadata]] = None,
- ):
- payload = cls._signed_payload(timestamp_epoch, this_node, other_nodes)
- signature = signer.sign(payload)
- return cls(signature=signature,
- timestamp_epoch=timestamp_epoch,
- this_node=this_node,
- other_nodes=other_nodes)
-
- @staticmethod
- def _signed_payload(timestamp_epoch, this_node, other_nodes):
- timestamp = timestamp_epoch.to_bytes(4, byteorder="big")
- nodes_payload = b''.join(bytes(node) for node in other_nodes) if other_nodes else b''
- return (
- timestamp +
- serialize_optional(this_node) +
- bytes(VariableLengthBytestring(nodes_payload))
- )
-
- def __init__(self,
- signature: Signature,
- timestamp_epoch: int,
- this_node: Optional[NodeMetadata] = None,
- other_nodes: Optional[List[NodeMetadata]] = None,
- ):
- self.signature = signature
- self.timestamp_epoch = timestamp_epoch
- self.this_node = this_node
- self.other_nodes = other_nodes
-
- def verify(self, verifying_pk: PublicKey):
- payload = self._signed_payload(self.timestamp_epoch, self.this_node, self.other_nodes)
- if not self.signature.verify(verifying_pk=verifying_pk, message=payload):
- raise InvalidSignature("Incorrect payload signature for MetadataResponse")
-
- @classmethod
- def _brand(cls) -> bytes:
- return b'MdRs'
-
- @classmethod
- def _version(cls) -> Tuple[int, int]:
- return 1, 0
-
- @classmethod
- def _old_version_handlers(cls) -> Dict:
- return {}
-
- def _payload(self):
- payload = self._signed_payload(self.timestamp_epoch, self.this_node, self.other_nodes)
- return bytes(self.signature) + payload
-
- @classmethod
- def _from_bytes_current(cls, data: bytes):
-
- splitter = BytestringSplitter(
- signature_splitter,
- (int, 4, {'byteorder': 'big'}),
- )
-
- signature, timestamp_epoch, remainder = splitter(data, return_remainder=True)
- this_node, remainder = take_optional(NodeMetadata.take, remainder)
- maybe_other_nodes, remainder = variable_length_splitter(remainder, return_remainder=True)
- other_nodes = NodeMetadata._batch_from_bytes(maybe_other_nodes) if maybe_other_nodes else None
- obj = cls(signature=signature,
- timestamp_epoch=timestamp_epoch,
- this_node=this_node,
- other_nodes=other_nodes)
- return obj, remainder
diff --git a/nucypher/crypto/umbral_adapter.py b/nucypher/crypto/umbral_adapter.py
index c69244dca..cdd639222 100644
--- a/nucypher/crypto/umbral_adapter.py
+++ b/nucypher/crypto/umbral_adapter.py
@@ -26,14 +26,10 @@ from nucypher_core.umbral import (
Signature,
Signer,
Capsule,
- KeyFrag,
VerifiedKeyFrag,
CapsuleFrag,
VerifiedCapsuleFrag,
VerificationError,
- encrypt,
- decrypt_original,
generate_kfrags,
reencrypt,
- decrypt_reencrypted,
)
diff --git a/nucypher/network/retrieval.py b/nucypher/network/retrieval.py
index 9508dcd89..2f5f1f8f5 100644
--- a/nucypher/network/retrieval.py
+++ b/nucypher/network/retrieval.py
@@ -20,7 +20,7 @@ import random
from typing import Dict, Sequence, List
from eth_typing.evm import ChecksumAddress
-from eth_utils import to_checksum_address, to_canonical_address
+from eth_utils import to_checksum_address
from twisted.logger import Logger
from nucypher_core import (
diff --git a/nucypher/network/server.py b/nucypher/network/server.py
index 794003366..638c3c9dc 100644
--- a/nucypher/network/server.py
+++ b/nucypher/network/server.py
@@ -23,7 +23,7 @@ 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
@@ -31,13 +31,11 @@ from mako.template import Template
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
@@ -82,7 +80,6 @@ class ProxyRESTServer:
def make_rest_app(
db_filepath: Path,
this_node,
- domain,
log: Logger = Logger("http-application-layer")
) -> Tuple[Flask, Datastore]:
"""
@@ -99,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
diff --git a/nucypher/utilities/versioning.py b/nucypher/utilities/versioning.py
deleted file mode 100644
index af65beaad..000000000
--- a/nucypher/utilities/versioning.py
+++ /dev/null
@@ -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 .
-"""
-
-
-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__()}
diff --git a/tests/contracts/lib/test_umbral_deserializer.py b/tests/contracts/lib/test_umbral_deserializer.py
index b6a797abc..53f4b65a0 100644
--- a/tests/contracts/lib/test_umbral_deserializer.py
+++ b/tests/contracts/lib/test_umbral_deserializer.py
@@ -17,9 +17,13 @@ along with nucypher. If not, see .
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.crypto.umbral_adapter import Signer, SecretKey, generate_kfrags, reencrypt
@pytest.fixture()
@@ -44,7 +48,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
diff --git a/tests/integration/learning/test_discovery_phases.py b/tests/integration/learning/test_discovery_phases.py
index 76ea8597a..66b9ea109 100644
--- a/tests/integration/learning/test_discovery_phases.py
+++ b/tests/integration/learning/test_discovery_phases.py
@@ -27,7 +27,7 @@ from flask import Response
from nucypher.characters.lawful import Ursula
from nucypher.crypto.signing import SignatureStamp
-from nucypher.crypto.umbral_adapter import SecretKey, Signer, PublicKey, encrypt
+from nucypher.crypto.umbral_adapter import SecretKey, Signer, PublicKey
from nucypher.datastore.base import RecordField
from nucypher.network.nodes import Teacher
from tests.markers import skip_on_circleci
@@ -107,11 +107,6 @@ 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 = (
mock_pubkey_from_bytes(),
mock_secret_source(),
diff --git a/tests/mock/performance_mocks.py b/tests/mock/performance_mocks.py
index 23fdd2a5c..14b02d68d 100644
--- a/tests/mock/performance_mocks.py
+++ b/tests/mock/performance_mocks.py
@@ -173,8 +173,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(
diff --git a/tests/unit/characters/control/test_character_fields.py b/tests/unit/characters/control/test_character_fields.py
index d22b3a1ec..183e89c49 100644
--- a/tests/unit/characters/control/test_character_fields.py
+++ b/tests/unit/characters/control/test_character_fields.py
@@ -30,7 +30,6 @@ from nucypher.characters.control.specifications.fields import (
FileField,
Key,
MessageKit,
- UmbralSignature,
EncryptedTreasureMap
)
from nucypher.characters.lawful import Enrico
@@ -135,30 +134,6 @@ def test_message_kit(enacted_federated_policy, federated_alice):
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
diff --git a/tests/unit/test_porter.py b/tests/unit/test_porter.py
index fbba4de4b..e8b60cd62 100644
--- a/tests/unit/test_porter.py
+++ b/tests/unit/test_porter.py
@@ -21,11 +21,11 @@ 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.control.specifications.exceptions import InvalidInputData
from nucypher.control.specifications.fields import StringList
-from nucypher.crypto.umbral_adapter import SecretKey, encrypt
+from nucypher.crypto.umbral_adapter import SecretKey
from nucypher.utilities.porter.control.specifications.fields import UrsulaChecksumAddress
from nucypher.utilities.porter.control.specifications.fields.retrieve import RetrievalKit
@@ -104,13 +104,13 @@ 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, {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')
+ capsule = MessageKit(encrypting_key, b'testing retrieval kit with no ursulas').capsule
run_tests_on_kit(kit=RetrievalKitClass(capsule, set()))
with pytest.raises(InvalidInputData):
diff --git a/tests/unit/test_versioning.py b/tests/unit/test_versioning.py
deleted file mode 100644
index 354ec6092..000000000
--- a/tests/unit/test_versioning.py
+++ /dev/null
@@ -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 .
-"""
-
-
-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
diff --git a/tests/utils/ursula.py b/tests/utils/ursula.py
index 07b265df6..7247db3b6 100644
--- a/tests/utils/ursula.py
+++ b/tests/utils/ursula.py
@@ -26,7 +26,7 @@ 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 nucypher.crypto.umbral_adapter import SecretKey, Signer, generate_kfrags
from tests.constants import NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK
from tests.mock.datastore import MOCK_DB