Ensure node discovery verification and identity_evidence remian compadible with federated nodes

pull/1040/head
Kieran R. Prasch 2019-06-01 15:34:37 -07:00 committed by Kieran Prasch
parent 775d314f6f
commit 2ab9c303d4
No known key found for this signature in database
GPG Key ID: 199AB839D4125A62
10 changed files with 57 additions and 44 deletions

View File

@ -880,7 +880,7 @@ class Ursula(Teacher, Character, Miner):
version = self.TEACHER_VERSION.to_bytes(2, "big")
interface_info = VariableLengthBytestring(bytes(self.rest_information()[0]))
identity_evidence = VariableLengthBytestring(self._evidence_of_decentralized_identity)
identity_evidence = VariableLengthBytestring(self._identity_evidence)
certificate = self.rest_server_certificate()
cert_vbytes = VariableLengthBytestring(certificate.public_bytes(Encoding.PEM))

View File

@ -21,7 +21,6 @@ import collections
import os
import click
import requests
from constant_sorrow.constants import NO_PASSWORD, NO_BLOCKCHAIN_CONNECTION
from nacl.exceptions import CryptoError
from twisted.logger import Logger

View File

@ -18,10 +18,11 @@ import base64
import contextlib
import json
import os
import shutil
import stat
from json import JSONDecodeError
from typing import ClassVar, Tuple, Callable, Union, Dict, List
from constant_sorrow.constants import KEYRING_LOCKED
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.backends.openssl.ec import _EllipticCurvePrivateKey
@ -37,19 +38,20 @@ from eth_utils import to_checksum_address, is_checksum_address
from nacl.exceptions import CryptoError
from nacl.secret import SecretBox
from twisted.logger import Logger
from typing import ClassVar, Tuple, Callable, Union, Dict, List
from umbral.keys import UmbralPrivateKey, UmbralPublicKey, UmbralKeyingMaterial, derive_key_from_password
from constant_sorrow.constants import KEYRING_LOCKED
from nucypher.blockchain.eth.chains import Blockchain
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
from nucypher.crypto.api import generate_self_signed_certificate
from nucypher.crypto.constants import BLAKE2B
from nucypher.crypto.powers import SigningPower, DecryptingPower, KeyPairBasedPower, DerivedKeyBasedPower, \
from nucypher.crypto.powers import (
SigningPower,
DecryptingPower,
KeyPairBasedPower,
DerivedKeyBasedPower,
BlockchainPower
)
from nucypher.network.server import TLSHostingPower
from nucypher.blockchain.eth.chains import Blockchain
FILE_ENCODING = 'utf-8'

View File

@ -16,6 +16,7 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
import binascii
import json
import os
import secrets
@ -25,7 +26,6 @@ from json import JSONDecodeError
from tempfile import TemporaryDirectory
from typing import List, Set
import binascii
import eth_utils
from constant_sorrow.constants import (
UNINITIALIZED_CONFIGURATION,
@ -43,7 +43,6 @@ from umbral.signing import Signature
from nucypher.blockchain.eth.agents import PolicyAgent, MinerAgent, NucypherTokenAgent
from nucypher.blockchain.eth.chains import Blockchain
from nucypher.blockchain.eth.clients import NuCypherGethDevnetProcess
from nucypher.blockchain.eth.registry import EthereumContractRegistry
from nucypher.config.constants import DEFAULT_CONFIG_ROOT, BASE_DIR
from nucypher.config.keyring import NucypherKeyring

View File

@ -22,7 +22,7 @@ from typing import Tuple
import sha3
from constant_sorrow import constants
from cryptography import x509
from cryptography.exceptions import InvalidSignature # TODO: Use nucypher exceptions
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.backends.openssl.ec import _EllipticCurvePrivateKey
from cryptography.hazmat.primitives import hashes

View File

@ -14,6 +14,8 @@ 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 bytestring_splitter import BytestringSplitter
from umbral.signing import Signature, Signer

View File

@ -267,7 +267,7 @@ class Learner:
version_splitter = BytestringSplitter((int, 2, {"byteorder": "big"}))
tracker_class = FleetStateTracker
invalid_metadata_message = "{} has invalid metadata. Maybe its stake is over? Or maybe it is transitioning to a new interface. Ignoring."
invalid_metadata_message = "{} has invalid metadata. The node's stake may have ended, or it is transitioning to a new interface. Ignoring."
unknown_version_message = "{} purported to be of version {}, but we're only version {}. Is there a new version of NuCypher?"
really_unknown_version_message = "Unable to glean address from node that perhaps purported to be version {}. We're only version {}."
fleet_state_icon = ""
@ -389,8 +389,8 @@ class Learner:
self.log.warn("No seednodes were available after {} attempts".format(retry_attempts))
# TODO: Need some actual logic here for situation with no seed nodes (ie, maybe try again much later)
def read_nodes_from_storage(self) -> set:
stored_nodes = self.node_storage.all(federated_only=self.federated_only) # TODO: 466
def read_nodes_from_storage(self) -> None:
stored_nodes = self.node_storage.all(federated_only=self.federated_only) # TODO: #466
for node in stored_nodes:
self.remember_node(node)
@ -815,9 +815,8 @@ class Learner:
self.log.warn(node.invalid_metadata_message.format(node))
except node.SuspiciousActivity:
# FIXME: NameError
message = f"Suspicious Activity: Discovered node with bad signature: {current_teacher.checksum_public_address}." \
f"Propagated by: {teacher_uri}"
message = f"Suspicious Activity: Discovered node with bad signature: {node}." \
f"Propagated by: {current_teacher}"
self.log.warn(message)
else:
@ -865,10 +864,10 @@ class Teacher:
self.certificate_filepath = certificate_filepath
self._interface_signature_object = interface_signature
self._timestamp = timestamp
self.last_seen = NEVER_SEEN("Haven't connected to this node yet.")
self.last_seen = NEVER_SEEN("No Connection to Node")
self.fleet_state_checksum = None
self.fleet_state_updated = None
self._evidence_of_decentralized_identity = constant_or_bytes(identity_evidence)
self._identity_evidence = constant_or_bytes(identity_evidence)
if substantiate_immediately:
self.substantiate_stamp(password=password) # TODO: Derive from keyring
@ -926,7 +925,7 @@ class Teacher:
def _stamp_has_valid_wallet_signature(self):
"""Offline Signature Verification"""
signature_bytes = self._evidence_of_decentralized_identity
signature_bytes = self._identity_evidence
if signature_bytes is NOT_SIGNED:
return False
signature_is_valid = verify_eip_191(message=bytes(self.stamp),
@ -945,20 +944,37 @@ class Teacher:
if not locked_tokens:
raise self.InvalidNode(f"{self.checksum_public_address} has no active stakes.")
def stamp_is_valid(self) -> bool:
def stamp_is_valid(self, verify_staking: bool = True) -> bool:
"""Offline-only check of valid stamp signature"""
signature = self._evidence_of_decentralized_identity
if self._stamp_has_valid_wallet_signature():
self.verified_stamp = True
return True
elif self.federated_only and signature is NOT_SIGNED:
message = "This node can't be verified in this manner, " \
"but is OK to use in federated mode if you" \
" have reason to believe it is trustworthy."
raise self.WrongMode(message)
#
# Federated
#
if self.federated_only:
if self._identity_evidence is not NOT_SIGNED:
message = "This node cannot be verified in this manner, " \
"but is OK to use in federated mode if you " \
"have reason to believe it is trustworthy."
raise self.WrongMode(message)
#
# Decentralized
#
else:
raise self.InvalidNode
if self._identity_evidence is NOT_SIGNED:
raise self.InvalidNode
# Off-chain signature verification
if self._stamp_has_valid_wallet_signature():
self.verified_stamp = True
return True
# On-chain staking check
if verify_staking:
self._is_valid_worker()
def verify_id(self, ursula_id, digest_factory=bytes):
self.verify()
@ -979,15 +995,11 @@ class Teacher:
# Offline check of valid stamp signature by worker
try:
self.stamp_is_valid()
self.stamp_is_valid(verify_staking=verify_staking)
except self.WrongMode:
if not accept_federated_only:
raise
# Check for on-chain staking
if verify_staking and not self.federated_only:
self._is_valid_worker()
def verify_node(self,
network_middleware,
certificate_filepath: str = None,
@ -1014,7 +1026,7 @@ class Teacher:
return True
# This is both the stamp's client signature and interface metadata check.
self.validate_metadata(accept_federated_only)
self.validate_metadata(accept_federated_only=accept_federated_only)
if not certificate_filepath:
@ -1036,7 +1048,7 @@ class Teacher:
verifying_keys_match = node_details['verifying_key'] == self.public_keys(SigningPower)
encrypting_keys_match = node_details['encrypting_key'] == self.public_keys(DecryptingPower)
addresses_match = node_details['public_address'] == self.canonical_public_address
evidence_matches = node_details['identity_evidence'] == self._evidence_of_decentralized_identity
evidence_matches = node_details['identity_evidence'] == self._identity_evidence
if not all((encrypting_keys_match, verifying_keys_match, addresses_match, evidence_matches)):
# TODO: #355 - Optional reporting.
@ -1052,7 +1064,7 @@ class Teacher:
blockchain_power = self._crypto_power.power_ups(BlockchainPower)
blockchain_power.unlock_account(password=password) # TODO: #349
signature = blockchain_power.sign_message(bytes(self.stamp))
self._evidence_of_decentralized_identity = signature
self._identity_evidence = signature
#
# Interface

View File

@ -53,7 +53,7 @@ def test_new_federated_ursula_announces_herself(ursula_federated_test_config):
def test_blockchain_ursula_substantiates_stamp(blockchain_ursulas):
first_ursula = list(blockchain_ursulas)[0]
signature_as_bytes = first_ursula._evidence_of_decentralized_identity
signature_as_bytes = first_ursula._identity_evidence
signature_as_bytes = to_standard_signature_bytes(signature_as_bytes)
assert verify_eip_191(address=first_ursula.checksum_public_address,
message=bytes(first_ursula.stamp),

View File

@ -21,7 +21,6 @@ from nucypher.utilities.sandbox.constants import (
@pytest_twisted.inlineCallbacks
def test_run_felix(click_runner,
testerchain,
federated_ursulas, # TODO: Make these blockchain Ursulas
deploy_user_input,
mock_primary_registry_filepath):

View File

@ -13,9 +13,9 @@ from nucypher.utilities.sandbox.ursula import make_federated_ursulas
def test_blockchain_ursula_is_not_valid_with_unsigned_identity_evidence(blockchain_ursulas, caplog):
lonely_blockchain_learner, blockchain_teacher, unsigned = list(blockchain_ursulas)[0:3]
unsigned._evidence_of_decentralized_identity = NOT_SIGNED
unsigned._identity_evidence = NOT_SIGNED
# Wipe known nodes.
# Wipe known nodes .
lonely_blockchain_learner._Learner__known_nodes = FleetStateTracker()
lonely_blockchain_learner._current_teacher_node = blockchain_teacher