Merge pull request #2612 from fjarri/new-umbral

Updates for the new Umbral API
pull/2731/head
KPrasch 2021-06-23 12:01:32 -07:00 committed by GitHub
commit 1a997cf129
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 1575 additions and 1611 deletions

View File

@ -8,11 +8,11 @@ python_version = "3"
[packages]
# NuCypher
umbral = "==0.1.3a2"
constant-sorrow = ">=0.1.0a9"
bytestring-splitter = ">=1.3.0"
hendrix = ">=3.4"
lmdb = "*"
umbral = ">=0.2"
# Cryptography
pyopenssl = "*"
cryptography = ">=3.2"
@ -33,6 +33,7 @@ py-evm = "*"
eth-tester = "*"
coincurve = "*"
web3 = "<=5.12.3"
eth-utils = "<1.10"
py-geth = "*"
# CLI / Configuration
appdirs = "*"

1604
Pipfile.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,52 +1,48 @@
-i https://pypi.python.org/simple
apipkg==1.5; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
appdirs==1.4.4
attrs==20.3.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
attrs==21.2.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
bandit==1.7.0
certifi==2020.12.5
cfgv==3.2.0; python_full_version >= '3.6.1'
certifi==2021.5.30
cfgv==3.3.0; python_full_version >= '3.6.1'
chardet==4.0.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
coverage==5.3.1
decorator==4.4.2
distlib==0.3.1
execnet==1.7.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
coverage==5.6b1
decorator==5.0.9; python_version >= '3.5'
distlib==0.3.2
execnet==1.9.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
filelock==3.0.12
gitdb==4.0.5; python_version >= '3.4'
gitpython==3.1.12; python_version >= '3.4'
greenlet==0.4.17
hypothesis==5.49.0
identify==1.5.11; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
gitdb==4.0.7; python_version >= '3.4'
gitpython==3.1.17; python_version >= '3.5'
greenlet==1.1.0; python_version >= '3'
hypothesis==6.14.0
identify==2.2.10; python_full_version >= '3.6.1'
idna==2.10; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
importlib-metadata==3.3.0; python_version < '3.8'
iniconfig==1.1.1
mypy-extensions==0.4.3
mypy==0.790
nodeenv==1.5.0
packaging==20.8; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
pbr==5.5.1; python_version >= '2.6'
pluggy==0.13.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
pre-commit==2.9.3
mypy==0.902
nodeenv==1.6.0
packaging==20.9; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
pbr==5.6.0; python_version >= '2.6'
pluggy==1.0.0.dev0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
pre-commit==2.13.0
py-solc-x==0.10.1
py==1.10.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
pyflakes==2.2.0
pyparsing==2.4.7; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'
pytest-cov==2.10.1
pyflakes==2.3.1
pyparsing==3.0.0b2; python_version >= '3.5'
pytest-cov==2.12.1
pytest-forked==1.3.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
pytest-mock==3.5.0
pytest-mock==3.6.1
pytest-timeout==1.4.2
pytest-twisted==1.13.2
pytest-xdist==2.2.0
pytest==6.2.1
pyyaml==5.3.1
pytest-xdist==2.2.1
pytest==6.2.4
pyyaml==5.4.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'
requests==2.25.1
semantic-version==2.8.5; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
six==1.15.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'
smmap==3.0.4; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
sortedcontainers==2.3.0
six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'
smmap==4.0.0; python_version >= '3.5'
sortedcontainers==2.4.0
stevedore==3.3.0; python_version >= '3.6'
toml==0.10.2; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'
typed-ast==1.4.2
typing-extensions==3.7.4.3; python_version < '3.8' and python_version < '3.8'
urllib3==1.26.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'
virtualenv==20.2.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
zipp==3.4.0; python_version >= '3.6'
typing-extensions==3.10.0.0
urllib3==1.26.5; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'
virtualenv==20.4.7; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'

View File

@ -1,52 +1,49 @@
-i https://pypi.python.org/simple
alabaster==0.7.12
apipkg==1.5; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
attrs==20.3.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
attrs==21.2.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
autosemver==0.5.5
babel==2.9.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
certifi==2020.12.5
babel==2.9.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
certifi==2021.5.30
chardet==4.0.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
click==7.1.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
coverage==5.3.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'
click-default-group==1.2.2
click==8.0.1; python_version >= '3.6'
coverage==5.6b1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
docutils==0.16; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
dulwich==0.19.16
execnet==1.7.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
execnet==1.9.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
git+git://github.com/inspirehep/jsonschema2rst.git@d211d9709046431a6b6a4594c97c22d8713d854b#egg=jsonschema2rst
idna==2.10; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
imagesize==1.2.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
importlib-metadata==3.3.0; python_version < '3.8' and python_version < '3.8'
incremental==17.5.0
incremental==21.3.0
iniconfig==1.1.1
isort==5.7.0; python_version >= '3.6' and python_version < '4'
jinja2==2.11.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
markupsafe==1.1.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
isort==5.8.0; python_version >= '3.6' and python_version < '4'
jinja2==3.0.1; python_version >= '3.6'
markupsafe==2.0.1; python_version >= '3.6'
mock==4.0.3; python_version >= '3.6'
packaging==20.8; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
packaging==20.9; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
pep8==1.7.1
pluggy==0.13.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
pluggy==1.0.0.dev0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
py-solc-x==0.10.1
py==1.10.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
pygments==2.7.3; python_version >= '3.5'
pyparsing==2.4.7; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'
pygments==2.9.0; python_version >= '3.5'
pyparsing==3.0.0b2; python_version >= '3.5'
pytest-cache==1.0
pytest-cov==2.10.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
pytest-cov==2.12.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
pytest-pep8==1.0.6
pytest==6.2.1; python_version >= '3.6'
pytz==2020.5
pyyaml==5.3.1
pytest==6.2.4; python_version >= '3.6'
pytz==2021.1
pyyaml==5.4.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'
requests==2.25.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
semantic-version==2.8.5; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
snowballstemmer==2.0.0
sphinx-rtd-theme==0.5.1
snowballstemmer==2.1.0
sphinx-rtd-theme==0.5.2
sphinx==3.0.1
sphinxcontrib-applehelp==1.0.2; python_version >= '3.5'
sphinxcontrib-devhelp==1.0.2; python_version >= '3.5'
sphinxcontrib-htmlhelp==1.0.3; python_version >= '3.5'
sphinxcontrib-htmlhelp==2.0.0; python_version >= '3.6'
sphinxcontrib-jsmath==1.0.1; python_version >= '3.5'
sphinxcontrib-qthelp==1.0.3; python_version >= '3.5'
sphinxcontrib-serializinghtml==1.1.4; python_version >= '3.5'
sphinxcontrib-serializinghtml==1.1.5; python_version >= '3.5'
toml==0.10.2; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'
towncrier==19.2.0
typing-extensions==3.7.4.3; python_version < '3.8'
urllib3==1.26.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'
zipp==3.4.0; python_version >= '3.6'
towncrier==21.3.0
urllib3==1.26.5; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'

View File

@ -102,7 +102,7 @@ label = label.encode()
policy_pubkey = alicia.get_policy_encrypting_key_from_label(label)
print("The policy public key for "
"label '{}' is {}".format(label.decode("utf-8"), policy_pubkey.to_bytes().hex()))
"label '{}' is {}".format(label.decode("utf-8"), bytes(policy_pubkey).hex()))
# Data Sources can produce encrypted data for access policies
# that **don't exist yet**.
@ -147,7 +147,7 @@ print("Done!")
# For the demo, we need a way to share with Bob some additional info
# about the policy, so we store it in a JSON file
policy_info = {
"policy_pubkey": policy.public_key.to_bytes().hex(),
"policy_pubkey": bytes(policy.public_key).hex(),
"alice_sig_pubkey": bytes(alicia.stamp).hex(),
"label": label.decode("utf-8"),
}

View File

@ -24,13 +24,13 @@ import msgpack
import os
import shutil
import sys
from umbral.keys import UmbralPublicKey
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.kits import UmbralMessageKit
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
@ -86,8 +86,8 @@ print("Doctor = ", doctor)
with open("policy-metadata.json", 'r') as f:
policy_data = json.load(f)
policy_pubkey = UmbralPublicKey.from_bytes(bytes.fromhex(policy_data["policy_pubkey"]))
alices_sig_pubkey = UmbralPublicKey.from_bytes(bytes.fromhex(policy_data["alice_sig_pubkey"]))
policy_pubkey = PublicKey.from_bytes(bytes.fromhex(policy_data["policy_pubkey"]))
alices_sig_pubkey = PublicKey.from_bytes(bytes.fromhex(policy_data["alice_sig_pubkey"]))
label = policy_data["label"].encode()
print("The Doctor joins policy for label '{}'".format(label.decode("utf-8")))

View File

@ -17,29 +17,29 @@
import json
import os
from umbral.keys import UmbralPrivateKey, UmbralPublicKey
from nucypher.crypto.umbral_adapter import SecretKey, PublicKey
DOCTOR_PUBLIC_JSON = 'doctor.public.json'
DOCTOR_PRIVATE_JSON = 'doctor.private.json'
def generate_doctor_keys():
enc_privkey = UmbralPrivateKey.gen_key()
sig_privkey = UmbralPrivateKey.gen_key()
enc_privkey = SecretKey.random()
sig_privkey = SecretKey.random()
doctor_privkeys = {
'enc': enc_privkey.to_bytes().hex(),
'sig': sig_privkey.to_bytes().hex(),
'enc': bytes(enc_privkey).hex(),
'sig': bytes(sig_privkey).hex(),
}
with open(DOCTOR_PRIVATE_JSON, 'w') as f:
json.dump(doctor_privkeys, f)
enc_pubkey = enc_privkey.get_pubkey()
sig_pubkey = sig_privkey.get_pubkey()
enc_pubkey = enc_privkey.public_key()
sig_pubkey = sig_privkey.public_key()
doctor_pubkeys = {
'enc': enc_pubkey.to_bytes().hex(),
'sig': sig_pubkey.to_bytes().hex()
'enc': bytes(enc_pubkey).hex(),
'sig': bytes(sig_pubkey).hex()
}
with open(DOCTOR_PUBLIC_JSON, 'w') as f:
json.dump(doctor_pubkeys, f)
@ -58,8 +58,8 @@ def _get_keys(file, key_class):
def get_doctor_pubkeys():
return _get_keys(DOCTOR_PUBLIC_JSON, UmbralPublicKey)
return _get_keys(DOCTOR_PUBLIC_JSON, PublicKey)
def get_doctor_privkeys():
return _get_keys(DOCTOR_PRIVATE_JSON, UmbralPrivateKey)
return _get_keys(DOCTOR_PRIVATE_JSON, SecretKey)

View File

@ -0,0 +1 @@
Switch to PyUmbral 0.2 and adjust its usage according to the changed API.

View File

@ -24,10 +24,3 @@ from nucypher.__about__ import (
__all__ = [
"__title__", "__summary__", "__version__", "__author__", "__license__", "__copyright__", "__email__", "__url__"
]
# Set Default Curve #
#####################
from umbral.config import set_default_curve
set_default_curve()

View File

@ -17,10 +17,6 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
from nucypher.blockchain.eth.sol.compile.types import SourceBundle
from nucypher.exceptions import DevelopmentInstallationRequired
try:
import tests
except ImportError:
raise DevelopmentInstallationRequired(importable_name='tests')
from nucypher.blockchain.eth.sol.compile.constants import IGNORE_CONTRACT_PREFIXES, SOLC_LOGGER
import os
from pathlib import Path

View File

@ -34,8 +34,6 @@ from cryptography.exceptions import InvalidSignature
from eth_keys import KeyAPI as EthKeyAPI
from eth_utils import to_canonical_address, to_checksum_address
from typing import ClassVar, Dict, List, Optional, Union
from umbral.keys import UmbralPublicKey
from umbral.signing import Signature
from nucypher.acumen.nicknames import Nickname
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
@ -56,8 +54,9 @@ from nucypher.crypto.powers import (
from nucypher.crypto.signing import (
SignatureStamp,
StrangerStamp,
signature_splitter
)
from nucypher.crypto.splitters import signature_splitter
from nucypher.crypto.umbral_adapter import PublicKey, Signature
from nucypher.network.middleware import RestMiddleware
from nucypher.network.nodes import Learner
@ -305,14 +304,14 @@ class Character(Learner):
@classmethod
def from_public_keys(cls,
powers_and_material: Dict = None,
verifying_key: Union[bytes, UmbralPublicKey] = None,
encrypting_key: Union[bytes, UmbralPublicKey] = None,
verifying_key: Union[bytes, PublicKey] = None,
encrypting_key: Union[bytes, PublicKey] = None,
*args, **kwargs) -> 'Character':
"""
Sometimes we discover a Character and, at the same moment,
learn the public parts of more of their powers. Here, we take a Dict
(powers_and_material) in the format {CryptoPowerUp class: material},
where material can be bytes or UmbralPublicKey.
where material can be bytes or umbral.PublicKey.
Each item in the collection will have the CryptoPowerUp instantiated
with the given material, and the resulting CryptoPowerUp instance
@ -333,7 +332,7 @@ class Character(Learner):
for power_up, public_key in powers_and_material.items():
try:
umbral_key = UmbralPublicKey.from_bytes(public_key)
umbral_key = PublicKey.from_bytes(public_key)
except TypeError:
umbral_key = public_key
@ -461,7 +460,7 @@ class Character(Learner):
signature_to_use = signature or signature_from_kit
if signature_to_use:
is_valid = signature_to_use.verify(message, sender_verifying_key) # FIXME: Message is undefined here
is_valid = signature_to_use.verify(sender_verifying_key, message) # FIXME: Message is undefined here
if not is_valid:
try:
node_on_the_other_end = self.known_node_class.from_seednode_metadata(stranger.seed_node_metadata(),
@ -505,9 +504,7 @@ class Character(Learner):
def derive_federated_address(self):
if self.federated_only:
verifying_key = self.public_keys(SigningPower)
uncompressed_bytes = verifying_key.to_bytes(is_compressed=False)
without_prefix = uncompressed_bytes[1:]
verifying_key_as_eth_key = EthKeyAPI.PublicKey(without_prefix)
verifying_key_as_eth_key = EthKeyAPI.PublicKey.from_compressed_bytes(bytes(verifying_key))
federated_address = verifying_key_as_eth_key.to_checksum_address()
else:
raise RuntimeError('Federated address can only be derived for federated characters.')

View File

@ -18,11 +18,11 @@
import functools
import maya
from typing import Union
from umbral.keys import UmbralPublicKey
from nucypher.characters.control.specifications import alice, bob, enrico
from nucypher.crypto.kits import UmbralMessageKit
from nucypher.crypto.powers import DecryptingPower, SigningPower
from nucypher.crypto.umbral_adapter import PublicKey
from nucypher.crypto.utils import construct_policy_id
from nucypher.network.middleware import RestMiddleware
@ -209,8 +209,8 @@ class BobInterface(CharacterPublicInterface):
"""
from nucypher.characters.lawful import Enrico
policy_encrypting_key = UmbralPublicKey.from_bytes(policy_encrypting_key)
alice_verifying_key = UmbralPublicKey.from_bytes(alice_verifying_key)
policy_encrypting_key = PublicKey.from_bytes(policy_encrypting_key)
alice_verifying_key = PublicKey.from_bytes(alice_verifying_key)
message_kit = UmbralMessageKit.from_bytes(
message_kit) # TODO #846: May raise UnknownOpenSSLError and InvalidTag.

View File

@ -16,10 +16,10 @@
"""
from marshmallow import fields
from umbral.keys import UmbralPublicKey
from nucypher.characters.control.specifications.exceptions import InvalidInputData, InvalidNativeDataTypes
from nucypher.characters.control.specifications.fields.base import BaseField
from nucypher.crypto.umbral_adapter import PublicKey
class Key(BaseField, fields.Field):
@ -37,6 +37,6 @@ class Key(BaseField, fields.Field):
def _validate(self, value):
try:
UmbralPublicKey.from_bytes(value)
PublicKey.from_bytes(value)
except InvalidNativeDataTypes as e:
raise InvalidInputData(f"Could not convert input for {self.name} to an Umbral Key: {e}")

View File

@ -18,10 +18,10 @@
from base64 import b64decode, b64encode
from marshmallow import fields
from umbral.signing import Signature
from nucypher.characters.control.specifications.exceptions import InvalidInputData, InvalidNativeDataTypes
from nucypher.characters.control.specifications.fields.base import BaseField
from nucypher.crypto.umbral_adapter import Signature
class UmbralSignature(BaseField, fields.Field):

View File

@ -25,6 +25,7 @@ from nucypher.characters.control.specifications.fields.base import BaseField
from nucypher.crypto.constants import HRAC_LENGTH
from nucypher.crypto.kits import UmbralMessageKit
from nucypher.crypto.signing import Signature
from nucypher.crypto.splitters import signature_splitter
class TreasureMap(BaseField, fields.Field):
@ -40,7 +41,7 @@ class TreasureMap(BaseField, fields.Field):
def _validate(self, value):
splitter = BytestringSplitter(Signature,
splitter = BytestringSplitter(signature_splitter,
(bytes, HRAC_LENGTH), # hrac
(UmbralMessageKit, VariableLengthBytestring)
) # TODO: USe the one from TMap

View File

@ -56,10 +56,6 @@ from twisted.internet.defer import Deferred
from twisted.internet.task import LoopingCall
from twisted.logger import Logger
from typing import Dict, Iterable, List, NamedTuple, Tuple, Union, Optional, Sequence, Set, Any
from umbral import pre
from umbral.keys import UmbralPublicKey
from umbral.kfrags import KFrag
from umbral.signing import Signature
import nucypher
from nucypher.acumen.nicknames import Nickname
@ -79,7 +75,7 @@ from nucypher.cli.processes import UrsulaCommandProtocol
from nucypher.config.constants import END_OF_POLICIES_PROBATIONARY_PERIOD
from nucypher.config.storages import ForgetfulNodeStorage, NodeStorage
from nucypher.crypto.api import encrypt_and_sign, keccak_digest
from nucypher.crypto.constants import HRAC_LENGTH, PUBLIC_KEY_LENGTH
from nucypher.crypto.constants import HRAC_LENGTH
from nucypher.crypto.keypairs import HostingKeypair
from nucypher.crypto.kits import UmbralMessageKit
from nucypher.crypto.powers import (
@ -90,6 +86,8 @@ from nucypher.crypto.powers import (
TransactingPower
)
from nucypher.crypto.signing import InvalidSignature
from nucypher.crypto.splitters import key_splitter, signature_splitter
from nucypher.crypto.umbral_adapter import Capsule, PublicKey, VerifiedKeyFrag, Signature, VerificationError, reencrypt
from nucypher.datastore.datastore import DatastoreTransactionError, RecordNotFound
from nucypher.datastore.queries import find_expired_policies, find_expired_treasure_maps
from nucypher.network.exceptions import NodeSeemsToBeDown
@ -223,7 +221,7 @@ class Alice(Character, BlockchainPolicyAuthor):
bob_encrypting_key = bob.public_keys(DecryptingPower)
delegating_power = self._crypto_power.power_ups(DelegatingPower)
return delegating_power.generate_kfrags(bob_pubkey_enc=bob_encrypting_key,
signer=self.stamp,
signer=self.stamp.as_umbral_signer(),
label=label,
m=m or self.m,
n=n or self.n)
@ -362,7 +360,7 @@ class Alice(Character, BlockchainPolicyAuthor):
enacted_policy.treasure_map_publisher.block_until_success_is_reasonably_likely()
return enacted_policy
def get_policy_encrypting_key_from_label(self, label: bytes) -> UmbralPublicKey:
def get_policy_encrypting_key_from_label(self, label: bytes) -> PublicKey:
alice_delegating_power = self._crypto_power.power_ups(DelegatingPower)
policy_pubkey = alice_delegating_power.get_pubkey_from_label(label)
return policy_pubkey
@ -626,7 +624,7 @@ class Bob(Character):
def make_compass_for_alice(self, alice):
return partial(self.verify_from, alice, decrypt=True)
def construct_policy_hrac(self, verifying_key: Union[bytes, UmbralPublicKey], label: bytes) -> bytes:
def construct_policy_hrac(self, verifying_key: Union[bytes, PublicKey], label: bytes) -> bytes:
_hrac = keccak_digest(bytes(verifying_key) + self.stamp + label)[:HRAC_LENGTH]
return _hrac
@ -685,7 +683,7 @@ class Bob(Character):
def work_orders_for_capsules(self,
*capsules,
alice_verifying_key: UmbralPublicKey,
alice_verifying_key: PublicKey,
treasure_map: 'TreasureMap' = None,
num_ursulas: int = None,
) -> Tuple[Dict[ChecksumAddress, 'WorkOrder'], Dict['Capsule', 'WorkOrder']]:
@ -751,34 +749,22 @@ class Bob(Character):
def _filter_work_orders_and_capsules(self,
work_orders: Dict[ChecksumAddress, 'WorkOrder'],
capsules: Sequence['Capsule'],
message_kits: Sequence['UmbralMessageKit'],
m: int,
) -> Tuple[List['WorkOrder'], Set['Capsule']]:
remaining_work_orders = []
remaining_capsules = set(capsule for capsule in capsules if len(capsule) < m)
remaining_capsules = {mk.capsule for mk in message_kits if len(mk) < m}
for work_order in work_orders.values():
for capsule in work_order.tasks:
work_order_is_useful = False
if len(capsule) >= m:
remaining_capsules.discard(capsule)
else:
work_order_is_useful = True
break
# If all the capsules are now activated, we can stop here.
if not remaining_capsules:
break
if not work_order_is_useful:
# None of the Capsules for this particular WorkOrder need to be activated. Move on to the next one.
continue
remaining_work_orders.append(work_order)
if not work_order.tasks.keys().isdisjoint(remaining_capsules):
remaining_work_orders.append(work_order)
return remaining_work_orders, remaining_capsules
def _reencrypt(self,
work_order: 'WorkOrder',
# TODO (#2028): it's kind of awkward to pass this mapping here,
# there should be a better way to handle this.
message_kits_map: Dict[Capsule, UmbralMessageKit],
retain_cfrags: bool = False
) -> Tuple[bool, Union[List['Ursula'], List['CapsuleFrag']]]:
@ -808,27 +794,37 @@ class Bob(Character):
self._completed_work_orders.save_work_order(work_order, as_replete=retain_cfrags)
the_airing_of_grievances = []
verified_cfrags = []
for capsule, pre_task in work_order.tasks.items():
if not pre_task.cfrag.verify_correctness(capsule):
try:
keys = message_kits_map[capsule].get_correctness_keys()
verified_cfrag = pre_task.cfrag.verify(capsule,
verifying_pk=keys['verifying'],
delegating_pk=keys['delegating'],
receiving_pk=keys['receiving'])
except VerificationError:
# TODO: WARNING - This block is untested.
# I got a lot of problems with you people ...
the_airing_of_grievances.append(work_order.ursula)
else:
verified_cfrags.append(verified_cfrag) # FIXME: this is unused
pre_task.cfrag = verified_cfrag # FIXME: massive yikes, needs to be done properly
if the_airing_of_grievances:
return False, the_airing_of_grievances
else:
return True, cfrags
return True, verified_cfrags
def retrieve(self,
# Policy
*message_kits: UmbralMessageKit,
alice_verifying_key: Union[UmbralPublicKey, bytes],
alice_verifying_key: Union[PublicKey, bytes],
label: bytes,
# Source Authentication
enrico: "Enrico" = None,
policy_encrypting_key: UmbralPublicKey = None,
policy_encrypting_key: PublicKey = None,
# Retrieval Behaviour
retain_cfrags: bool = False,
@ -838,8 +834,8 @@ class Bob(Character):
) -> List[bytes]:
# Try our best to get an UmbralPublicKey from input
alice_verifying_key = UmbralPublicKey.from_bytes(bytes(alice_verifying_key))
# Try our best to get an umbral.PublicKey from input
alice_verifying_key = PublicKey.from_bytes(bytes(alice_verifying_key))
if treasure_map is not None:
@ -870,14 +866,15 @@ class Bob(Character):
_unknown_ursulas, _known_ursulas, m = self.follow_treasure_map(treasure_map=treasure_map, block=True)
# Part I: Assembling the WorkOrders.
capsules_to_activate = set(mk.capsule for mk in message_kits)
message_kits_map = {mk.capsule: mk for mk in message_kits}
# Normalization
for message in message_kits:
message.ensure_correct_sender(enrico=enrico, policy_encrypting_key=policy_encrypting_key)
for message_kit in message_kits:
message_kit.ensure_correct_sender(enrico=enrico, policy_encrypting_key=policy_encrypting_key)
# Sanity check: If we're not using attached cfrags, we don't want a Capsule which has them.
if not use_attached_cfrags and any(len(message.capsule) > 0 for message in message_kits):
if not use_attached_cfrags and any(len(message_kit) > 0 for message_kit in message_kits):
raise TypeError(
"Not using cached retrievals, but the MessageKit's capsule has attached CFrags. "
"In order to retrieve this message, you must set cache=True. "
@ -887,26 +884,24 @@ class Bob(Character):
# We'll start by following the treasure map, setting the correctness keys, and attaching cfrags from
# WorkOrders that we have already completed in the past.
for message in message_kits:
capsule = message.capsule
capsule.set_correctness_keys(receiving=self.public_keys(DecryptingPower))
capsule.set_correctness_keys(verifying=alice_verifying_key)
for message_kit in message_kits:
message_kit.set_correctness_keys(receiving=self.public_keys(DecryptingPower))
message_kit.set_correctness_keys(verifying=alice_verifying_key)
new_work_orders, complete_work_orders = self.work_orders_for_capsules(
treasure_map=treasure_map,
alice_verifying_key=alice_verifying_key,
*capsules_to_activate)
*[mk.capsule for mk in message_kits])
self.log.info(f"Found {len(complete_work_orders)} complete work orders "
f"for Capsules ({capsules_to_activate}).")
f"for message kits ({message_kits}).")
if complete_work_orders:
if use_precedent_work_orders:
for capsule, work_orders in complete_work_orders.items():
for work_order in work_orders:
cfrag_in_question = work_order.tasks[capsule].cfrag
capsule.attach_cfrag(cfrag_in_question)
message_kits_map[capsule].attach_cfrag(cfrag_in_question)
else:
self.log.warn(
"Found existing complete WorkOrders, but use_precedent_work_orders is set to False. To use Bob in 'KMS mode', set retain_cfrags=False as well.")
@ -918,30 +913,31 @@ class Bob(Character):
# TODO Optimization: Block here (or maybe even later) until map is done being followed (instead of blocking above). #1114
the_airing_of_grievances = []
remaining_work_orders, capsules_to_activate = self._filter_work_orders_and_capsules(
new_work_orders, capsules_to_activate, m)
remaining_work_orders, remaining_capsules = self._filter_work_orders_and_capsules(
new_work_orders, message_kits, m)
# If all the capsules are now activated, we can stop here.
if capsules_to_activate and remaining_work_orders:
if remaining_capsules and remaining_work_orders:
# OK, so we're going to need to do some network activity for this retrieval. Let's make sure we've seeded.
if not self.done_seeding:
self.learn_from_teacher_node()
for work_order in remaining_work_orders:
success, result = self._reencrypt(work_order, retain_cfrags)
success, result = self._reencrypt(work_order, message_kits_map, retain_cfrags)
if not success:
the_airing_of_grievances.extend(result)
continue
for capsule, pre_task in work_order.tasks.items():
capsule.attach_cfrag(pre_task.cfrag) # already verified, will not fail
if len(capsule) >= m:
capsules_to_activate.discard(capsule)
message_kit = message_kits_map[capsule]
message_kit.attach_cfrag(pre_task.cfrag)
if len(message_kit) >= m:
remaining_capsules.discard(capsule)
# If all the capsules are now activated, we can stop here.
if not capsules_to_activate:
if not remaining_capsules:
break
else:
raise Ursula.NotEnoughUrsulas(
@ -954,13 +950,13 @@ class Bob(Character):
# - There maybe enough cfrags to still open the capsule
# - This line is unreachable when NotEnoughUrsulas
for message in message_kits:
delivered_cleartext = self.verify_from(message.sender, message, decrypt=True)
for message_kit in message_kits:
delivered_cleartext = self.verify_from(message_kit.sender, message_kit, decrypt=True)
cleartexts.append(delivered_cleartext)
finally:
if not retain_cfrags:
for message in message_kits:
message.capsule.clear_cfrags()
for message_kit in message_kits:
message_kit.clear_cfrags()
for work_order in new_work_orders.values():
work_order.sanitize()
@ -983,7 +979,7 @@ class Bob(Character):
search_boundary = 2
target_nodes = []
target_hex_match = self.public_keys(DecryptingPower).hex()[1]
target_hex_match = bytes(self.public_keys(DecryptingPower)).hex()[1]
while len(target_nodes) < no_less_than:
target_nodes = []
search_boundary += 2
@ -1568,13 +1564,13 @@ class Ursula(Teacher, Character, Worker):
public_address=ETH_ADDRESS_BYTE_LENGTH,
domain=VariableLengthBytestring,
timestamp=(int, 4, {'byteorder': 'big'}),
interface_signature=Signature,
interface_signature=signature_splitter,
# FIXME: Fixed length doesn't work with federated. It was LENGTH_ECDSA_SIGNATURE_WITH_RECOVERY,
decentralized_identity_evidence=VariableLengthBytestring,
verifying_key=(UmbralPublicKey, PUBLIC_KEY_LENGTH),
encrypting_key=(UmbralPublicKey, PUBLIC_KEY_LENGTH),
verifying_key=key_splitter,
encrypting_key=key_splitter,
certificate=(load_pem_x509_certificate, VariableLengthBytestring, {"backend": default_backend()}),
rest_interface=InterfaceInfo,
)
@ -1727,26 +1723,21 @@ class Ursula(Teacher, Character, Worker):
# Re-Encryption
#
def _reencrypt(self, kfrag: KFrag, work_order: 'WorkOrder', alice_verifying_key: UmbralPublicKey):
def _reencrypt(self, kfrag: VerifiedKeyFrag, work_order: 'WorkOrder', alice_verifying_key: PublicKey):
# Prepare a bytestring for concatenating re-encrypted
# capsule data for each work order task.
cfrag_byte_stream = bytes()
for capsule, task in work_order.tasks.items():
# Ursula signs on top of Bob's signature of each task.
# Now both are committed to the same task. See #259.
reencryption_metadata = bytes(self.stamp(bytes(task.signature)))
# Ursula sets Alice's verifying key for capsule correctness verification.
capsule.set_correctness_keys(verifying=alice_verifying_key)
# See #259 for the discussion leading to the current protocol.
# Then re-encrypts the fragment.
cfrag = pre.reencrypt(kfrag, capsule, metadata=reencryption_metadata) # <--- pyUmbral
cfrag = reencrypt(capsule, kfrag) # <--- pyUmbral
self.log.info(f"Re-encrypted capsule {capsule} -> made {cfrag}.")
# Next, Ursula signs to commit to her results.
reencryption_signature = self.stamp(bytes(cfrag))
cfrag_byte_stream += VariableLengthBytestring(cfrag) + reencryption_signature
cfrag_byte_stream += bytes(cfrag) + bytes(reencryption_signature)
# ... and finally returns all the re-encrypted bytes
return cfrag_byte_stream
@ -1839,7 +1830,7 @@ class Enrico(Character):
def __init__(self,
is_me: bool = True,
policy_encrypting_key: Optional[UmbralPublicKey] = None,
policy_encrypting_key: Optional[PublicKey] = None,
controller: bool = True,
*args, **kwargs):

View File

@ -47,8 +47,8 @@ def collect_keys_from_card(emitter: StdoutEmitter, card_identifier: str, force:
if not force:
click.confirm('Is this the correct grantee (Bob)?', abort=True)
bob_encrypting_key = card.encrypting_key.hex()
bob_verifying_key = card.verifying_key.hex()
bob_encrypting_key = bytes(card.encrypting_key).hex()
bob_verifying_key = bytes(card.verifying_key).hex()
public_keys = PublicKeys(encrypting_key=bob_encrypting_key, verifying_key=bob_verifying_key)
return public_keys

View File

@ -379,9 +379,9 @@ def retrieve(general_config,
if card.character is not Alice:
emitter.error('Grantee card is not an Alice.')
raise click.Abort
alice_verifying_key = card.verifying_key.hex()
alice_verifying_key = bytes(card.verifying_key).hex()
emitter.message(f'{card.nickname or ("Alice #"+card.id.hex())}\n'
f'Verifying Key | {card.verifying_key.hex()}',
f'Verifying Key | {bytes(card.verifying_key).hex()}',
color='green')
if not force:
click.confirm('Is this the correct Granter (Alice)?', abort=True)

View File

@ -17,7 +17,6 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
import click
from umbral.keys import UmbralPublicKey
from nucypher.characters.control.interfaces import EnricoInterface
from nucypher.characters.lawful import Enrico
@ -25,6 +24,7 @@ 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()
@ -99,7 +99,7 @@ def encrypt(general_config, policy_encrypting_key, message, file, ipfs):
def _create_enrico(emitter, policy_encrypting_key) -> Enrico:
policy_encrypting_key = UmbralPublicKey.from_bytes(bytes.fromhex(policy_encrypting_key))
policy_encrypting_key = PublicKey.from_bytes(bytes.fromhex(policy_encrypting_key))
ENRICO = Enrico(policy_encrypting_key=policy_encrypting_key)
ENRICO.controller.emitter = emitter
return ENRICO

View File

@ -26,9 +26,9 @@ from nucypher.policy.identity import Card
def paint_single_card(emitter, card: Card, qrcode: bool = False) -> None:
emitter.echo('*'*90, color='cyan')
emitter.message(f'{(card.nickname or str(card.character.__name__)).capitalize()}\'s Card (ID {card.id.hex()})', bold=True)
emitter.echo(f'Verifying Key - {card.verifying_key.hex()}')
emitter.echo(f'Verifying Key - {bytes(card.verifying_key).hex()}')
if card.character is Bob:
emitter.echo(f'Encrypting Key - {card.encrypting_key.hex()}')
emitter.echo(f'Encrypting Key - {bytes(card.encrypting_key).hex()}')
if qrcode:
card.to_qr_code()
emitter.echo('*'*90, color='cyan')

View File

@ -20,12 +20,12 @@ from cryptography.exceptions import InternalError
from decimal import Decimal, DecimalException
from eth_utils import to_checksum_address
from ipaddress import ip_address
from umbral.keys import UmbralPublicKey
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
@ -127,7 +127,7 @@ class UmbralPublicKeyHex(click.ParamType):
def convert(self, value, param, ctx):
if self.validate:
try:
_key = UmbralPublicKey.from_hex(value)
_key = PublicKey.from_bytes(bytes.fromhex(value))
except (InternalError, ValueError):
self.fail(f"'{value}' is not a valid nucypher public key.")
return value

View File

@ -34,7 +34,6 @@ from constant_sorrow.constants import (
LIVE_CONFIGURATION
)
from eth_utils.address import is_checksum_address
from umbral.signing import Signature
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
from nucypher.blockchain.eth.networks import NetworksInventory
@ -53,6 +52,7 @@ from nucypher.config.storages import (
NodeStorage
)
from nucypher.crypto.powers import CryptoPower, CryptoPowerUp
from nucypher.crypto.umbral_adapter import Signature
from nucypher.network.middleware import RestMiddleware
from nucypher.utilities.logging import Logger

View File

@ -101,7 +101,7 @@ class UrsulaConfiguration(CharacterConfiguration):
return base_filepaths
def generate_filepath(self, modifier: str = None, *args, **kwargs) -> str:
filepath = super().generate_filepath(modifier=modifier or self.keyring.signing_public_key.hex()[:8], *args, **kwargs)
filepath = super().generate_filepath(modifier=modifier or bytes(self.keyring.signing_public_key).hex()[:8], *args, **kwargs)
return filepath
def static_payload(self) -> dict:

View File

@ -33,21 +33,24 @@ from cryptography.hazmat.backends.openssl.ec import _EllipticCurvePrivateKey
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurve
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives.serialization import Encoding, load_pem_private_key
from cryptography.x509 import Certificate
from eth_account import Account
from eth_keys import KeyAPI as EthKeyAPI
from eth_utils import to_checksum_address
from nacl.exceptions import CryptoError
from nacl.secret import SecretBox
from umbral.keys import UmbralKeyingMaterial, UmbralPrivateKey, UmbralPublicKey, derive_key_from_password
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
from nucypher.crypto.api import generate_teacher_certificate, _TLS_CURVE
from nucypher.crypto.constants import BLAKE2B
from nucypher.crypto.keypairs import HostingKeypair
from nucypher.crypto.passwords import (
secret_box_encrypt,
secret_box_decrypt,
SecretBoxAuthenticationError,
derive_key_material_from_password,
)
from nucypher.crypto.powers import (DecryptingPower, DerivedKeyBasedPower, KeyPairBasedPower, SigningPower)
from nucypher.crypto.umbral_adapter import SecretKey, PublicKey, SecretKeyFactory
from nucypher.network.server import TLSHostingPower
from nucypher.utilities.logging import Logger
@ -64,11 +67,6 @@ __PRIVATE_MODE = stat.S_IRUSR | stat.S_IWUSR # 0o600
__PUBLIC_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL # Write, Create, Non-Existing
__PUBLIC_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH # 0o644
# Keyring
__WRAPPING_KEY_LENGTH = 32
__WRAPPING_KEY_INFO = b'NuCypher-KeyWrap'
__HKDF_HASH_ALGORITHM = BLAKE2B
PrivateKeyData = Union[
Dict[str, bytes],
bytes,
@ -207,43 +205,23 @@ def _read_tls_public_certificate(filepath: str) -> Certificate:
raise FileNotFoundError("No SSL certificate found at {}".format(filepath))
#
# Key wrapping
#
def _derive_wrapping_key_from_key_material(salt: bytes,
key_material: bytes,
) -> bytes:
"""
Uses HKDF to derive a 32 byte wrapping key to encrypt key material with.
"""
wrapping_key = HKDF(
algorithm=__HKDF_HASH_ALGORITHM,
length=__WRAPPING_KEY_LENGTH,
salt=salt,
info=__WRAPPING_KEY_INFO,
backend=default_backend()
).derive(key_material)
return wrapping_key
#
# Keypair Generation
#
def _generate_encryption_keys() -> Tuple[UmbralPrivateKey, UmbralPublicKey]:
def _generate_encryption_keys() -> Tuple[SecretKey, PublicKey]:
"""Use pyUmbral keys to generate a new encrypting key pair"""
privkey = UmbralPrivateKey.gen_key()
pubkey = privkey.get_pubkey()
privkey = SecretKey.random()
pubkey = privkey.public_key()
return privkey, pubkey
def _generate_signing_keys() -> Tuple[UmbralPrivateKey, UmbralPublicKey]:
def _generate_signing_keys() -> Tuple[SecretKey, PublicKey]:
"""
"""
privkey = UmbralPrivateKey.gen_key()
pubkey = privkey.get_pubkey()
privkey = SecretKey.random()
pubkey = privkey.public_key()
return privkey, pubkey
@ -390,13 +368,13 @@ class NucypherKeyring:
@property
def signing_public_key(self):
signature_pubkey_bytes = _read_keyfile(keypath=self.__signing_pub_keypath, deserializer=None)
signature_pubkey = UmbralPublicKey.from_bytes(signature_pubkey_bytes)
signature_pubkey = PublicKey.from_bytes(signature_pubkey_bytes)
return signature_pubkey
@property
def encrypting_public_key(self):
encrypting_pubkey_bytes = _read_keyfile(keypath=self.__root_pub_keypath, deserializer=None)
encrypting_pubkey = UmbralPublicKey.from_bytes(encrypting_pubkey_bytes)
encrypting_pubkey = PublicKey.from_bytes(encrypting_pubkey_bytes)
return encrypting_pubkey
@property
@ -433,15 +411,19 @@ class NucypherKeyring:
return __key_filepaths
@unlock_required
def __decrypt_keyfile(self, key_path: str) -> UmbralPrivateKey:
def __decrypt_keyfile(self, key_path: str) -> SecretKey:
"""Returns plaintext version of decrypting key."""
key_data = _read_keyfile(key_path, deserializer=_deserialize_private_key)
wrap_key = _derive_wrapping_key_from_key_material(salt=key_data['wrap_salt'],
key_material=self.__derived_key_material)
try:
plain_umbral_key = UmbralPrivateKey.from_bytes(key_bytes=key_data['key'], wrapping_key=wrap_key)
except CryptoError:
raise self.AuthenticationFailed('Invalid or incorrect nucypher keyring password.')
key_bytes = secret_box_decrypt(key_material=self.__derived_key_material,
salt=key_data['wrap_salt'],
ciphertext=key_data['key'])
except SecretBoxAuthenticationError as e:
raise self.AuthenticationFailed('Invalid or incorrect nucypher keyring password.') from e
plain_umbral_key = SecretKey.from_bytes(key_bytes)
return plain_umbral_key
#
@ -465,14 +447,9 @@ class NucypherKeyring:
return self.is_unlocked
key_data = _read_keyfile(keypath=self.__root_keypath, deserializer=_deserialize_private_key)
self.log.info("Unlocking keyring.")
try:
derived_key = derive_key_from_password(password=password.encode(), salt=key_data['master_salt'])
except CryptoError:
self.log.info("Keyring unlock failed.")
raise self.AuthenticationFailed
else:
self.__derived_key_material = derived_key
self.log.info("Finished unlocking.")
derived_key = derive_key_material_from_password(password=password.encode(), salt=key_data['master_salt'])
self.__derived_key_material = derived_key
self.log.info("Finished unlocking.")
return self.is_unlocked
@unlock_required
@ -515,8 +492,12 @@ class NucypherKeyring:
# Derived
elif issubclass(power_class, DerivedKeyBasedPower):
key_data = _read_keyfile(self.__delegating_keypath, deserializer=_deserialize_private_key)
wrap_key = _derive_wrapping_key_from_key_material(salt=key_data['wrap_salt'], key_material=self.__derived_key_material)
keying_material = SecretBox(wrap_key).decrypt(key_data['key'])
try:
keying_material = secret_box_decrypt(key_material=self.__derived_key_material,
salt=key_data['wrap_salt'],
ciphertext=key_data['key'])
except SecretBoxAuthenticationError as e:
raise self.AuthenticationFailed('Invalid or incorrect nucypher keyring password.') from e
new_cryptopower = power_class(keying_material=keying_material)
else:
@ -576,9 +557,7 @@ class NucypherKeyring:
signing_private_key, signing_public_key = _generate_signing_keys()
if checksum_address is FEDERATED_ADDRESS:
uncompressed_bytes = signing_public_key.to_bytes(is_compressed=False)
without_prefix = uncompressed_bytes[1:]
verifying_key_as_eth_key = EthKeyAPI.PublicKey(without_prefix)
verifying_key_as_eth_key = EthKeyAPI.PublicKey.from_compressed_bytes(bytes(signing_public_key))
checksum_address = verifying_key_as_eth_key.to_checksum_address()
else:
@ -594,21 +573,24 @@ class NucypherKeyring:
public_key_dir=_public_key_dir)
if encrypting is True:
encrypting_private_key, encrypting_public_key = _generate_encryption_keys()
delegating_keying_material = UmbralKeyingMaterial().to_bytes()
delegating_keying_material = bytes(SecretKeyFactory.random())
# Derive Wrapping Keys
password_salt, encrypting_salt, signing_salt, delegating_salt = (os.urandom(32) for _ in range(4))
cls.log.info("About to derive key from password.")
derived_key_material = derive_key_from_password(salt=password_salt, password=password.encode())
encrypting_wrap_key = _derive_wrapping_key_from_key_material(salt=encrypting_salt, key_material=derived_key_material)
signature_wrap_key = _derive_wrapping_key_from_key_material(salt=signing_salt, key_material=derived_key_material)
delegating_wrap_key = _derive_wrapping_key_from_key_material(salt=delegating_salt, key_material=derived_key_material)
derived_key_material = derive_key_material_from_password(salt=password_salt, password=password.encode())
# Encapsulate Private Keys
encrypting_key_data = encrypting_private_key.to_bytes(wrapping_key=encrypting_wrap_key)
signing_key_data = signing_private_key.to_bytes(wrapping_key=signature_wrap_key)
delegating_key_data = bytes(SecretBox(delegating_wrap_key).encrypt(delegating_keying_material))
encrypting_key_data = secret_box_encrypt(key_material=derived_key_material,
salt=encrypting_salt,
plaintext=bytes(encrypting_private_key))
signing_key_data = secret_box_encrypt(key_material=derived_key_material,
salt=signing_salt,
plaintext=bytes(signing_private_key))
delegating_key_data = secret_box_encrypt(key_material=derived_key_material,
salt=delegating_salt,
plaintext=delegating_keying_material)
# Assemble Private Keys
encrypting_key_metadata = _assemble_key_data(key_data=encrypting_key_data,
@ -646,8 +628,8 @@ class NucypherKeyring:
serializer=_serialize_private_key)
# Write Public Keys
root_keypath = _write_public_keyfile(__key_filepaths['root_pub'], encrypting_public_key.to_bytes())
signing_keypath = _write_public_keyfile(__key_filepaths['signing_pub'], signing_public_key.to_bytes())
root_keypath = _write_public_keyfile(__key_filepaths['root_pub'], bytes(encrypting_public_key))
signing_keypath = _write_public_keyfile(__key_filepaths['signing_pub'], bytes(signing_public_key))
except (PrivateKeyExistsError, FileExistsError):
if not force:
raise ExistingKeyringError(f"There is an existing keyring for address '{checksum_address}'")

View File

@ -35,12 +35,11 @@ from eth_utils import is_checksum_address, to_checksum_address
from ipaddress import IPv4Address
from random import SystemRandom
from typing import Tuple
from umbral import pre
from umbral.keys import UmbralPrivateKey, UmbralPublicKey
from umbral.signing import Signature
from nucypher.crypto.constants import SHA256
from nucypher.crypto.kits import UmbralMessageKit
import nucypher.crypto.umbral_adapter as umbral
from nucypher.crypto.umbral_adapter import SecretKey, PublicKey, Signature
SYSTEM_RAND = SystemRandom()
_TLS_CURVE = ec.SECP384R1
@ -115,22 +114,6 @@ def sha256_digest(*messages: bytes) -> bytes:
return digest
def ecdsa_sign(message: bytes,
private_key: UmbralPrivateKey
) -> bytes:
"""
Accepts a hashed message and signs it with the private key given.
:param message: Message to hash and sign
:param private_key: Private key to sign with
:return: signature
"""
signing_key = private_key.to_cryptography_privkey()
signature_der_bytes = signing_key.sign(message, ec.ECDSA(SHA256))
return signature_der_bytes
def recover_address_eip_191(message: bytes, signature: bytes) -> str:
"""
Recover checksum address from EIP-191 signature
@ -150,33 +133,6 @@ def verify_eip_191(address: str, message: bytes, signature: bytes) -> bool:
return signature_is_valid
def verify_ecdsa(message: bytes,
signature: bytes,
public_key: UmbralPublicKey
) -> bool:
"""
Accepts a message and signature and verifies it with the
provided public key.
:param message: Message to verify
:param signature: Signature to verify
:param public_key: UmbralPublicKey to verify signature with
:return: True if valid, False if invalid.
"""
cryptography_pub_key = public_key.to_cryptography_pubkey()
try:
cryptography_pub_key.verify(
signature,
message,
ec.ECDSA(SHA256)
)
except InvalidSignature:
return False
return True
def __generate_self_signed_certificate(host: str,
curve: EllipticCurve = _TLS_CURVE,
private_key: _EllipticCurvePrivateKey = None,
@ -234,7 +190,7 @@ def read_certificate_pseudonym(certificate: Certificate):
return checksum_address
def encrypt_and_sign(recipient_pubkey_enc: UmbralPublicKey,
def encrypt_and_sign(recipient_pubkey_enc: PublicKey,
plaintext: bytes,
signer: 'SignatureStamp',
sign_plaintext: bool = True
@ -245,11 +201,11 @@ def encrypt_and_sign(recipient_pubkey_enc: UmbralPublicKey,
# Sign first, encrypt second.
sig_header = constants.SIGNATURE_TO_FOLLOW
signature = signer(plaintext)
ciphertext, capsule = pre.encrypt(recipient_pubkey_enc, sig_header + signature + plaintext)
capsule, ciphertext = umbral.encrypt(recipient_pubkey_enc, sig_header + bytes(signature) + plaintext)
else:
# Encrypt first, sign second.
sig_header = constants.SIGNATURE_IS_ON_CIPHERTEXT
ciphertext, capsule = pre.encrypt(recipient_pubkey_enc, sig_header + plaintext)
capsule, ciphertext = umbral.encrypt(recipient_pubkey_enc, sig_header + plaintext)
signature = signer(ciphertext)
message_kit = UmbralMessageKit(ciphertext=ciphertext, capsule=capsule,
sender_verifying_key=signer.as_umbral_pubkey(),
@ -257,7 +213,7 @@ def encrypt_and_sign(recipient_pubkey_enc: UmbralPublicKey,
else:
# Don't sign.
signature = sig_header = constants.NOT_SIGNED
ciphertext, capsule = pre.encrypt(recipient_pubkey_enc, sig_header + plaintext)
capsule, ciphertext = umbral.encrypt(recipient_pubkey_enc, sig_header + plaintext)
message_kit = UmbralMessageKit(ciphertext=ciphertext, capsule=capsule)
return message_kit, signature

View File

@ -27,7 +27,3 @@ BLAKE2B_DIGEST_LENGTH = 64
# Hashes
SHA256 = hashes.SHA256()
BLAKE2B = hashes.BLAKE2b(64)
# SECP256K1
CAPSULE_LENGTH = 98
PUBLIC_KEY_LENGTH = 33

View File

@ -24,15 +24,13 @@ from constant_sorrow import constants
from cryptography.hazmat.primitives.asymmetric import ec
from hendrix.deploy.tls import HendrixDeployTLS
from hendrix.facilities.services import ExistingKeyTLSContextFactory
from umbral import pre
from umbral.keys import UmbralPrivateKey, UmbralPublicKey
from umbral.signing import Signature, Signer
from nucypher.config.constants import MAX_UPLOAD_CONTENT_LENGTH
from nucypher.crypto import api as API
from nucypher.crypto.api import generate_teacher_certificate, _TLS_CURVE
from nucypher.crypto.kits import MessageKit
from nucypher.crypto.signing import SignatureStamp, StrangerStamp
from nucypher.crypto.umbral_adapter import SecretKey, PublicKey, Signature, Signer, decrypt_original, decrypt_reencrypted
from nucypher.network.resources import get_static_resources
@ -41,8 +39,8 @@ class Keypair(object):
A parent Keypair class for all types of Keypairs.
"""
_private_key_source = UmbralPrivateKey.gen_key
_public_key_method = "get_pubkey"
_private_key_source = SecretKey.random
_public_key_method = "public_key"
def __init__(self,
private_key=None,
@ -67,17 +65,6 @@ class Keypair(object):
raise ValueError(
"Either pass a valid key or, if you want to generate keys, set generate_keys_if_needed to True.")
def serialize_pubkey(self, as_b64=False) -> bytes:
"""
Serializes the pubkey for storage/transport in either urlsafe base64
or as a bytestring.
:param as_b64: Return the pubkey as urlsafe base64 byte string
:return: The serialized pubkey in bytes
"""
encoder = base64.urlsafe_b64encode if as_b64 else None
return self.pubkey.to_bytes(encoder=encoder)
def fingerprint(self):
"""
Hashes the key using keccak-256 and returns the hexdigest in bytes.
@ -101,10 +88,16 @@ class DecryptingKeypair(Keypair):
:return: bytes
"""
cleartext = pre.decrypt(ciphertext=message_kit.ciphertext,
capsule=message_kit.capsule,
decrypting_key=self._privkey,
)
if len(message_kit) > 0:
cleartext = decrypt_reencrypted(self._privkey,
message_kit._delegating_key,
message_kit.capsule,
list(message_kit._cfrags),
message_kit.ciphertext)
else:
cleartext = decrypt_original(self._privkey,
message_kit.capsule,
message_kit.ciphertext)
return cleartext
@ -117,19 +110,18 @@ class SigningKeypair(Keypair):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
def sign(self, message: bytes) -> bytes:
def sign(self, message: bytes) -> Signature:
"""
Signs a hashed message and returns a signature.
Signs the given message and returns a signature.
:param message: The message to sign
:return: Signature in bytes
:return: Signature
"""
signature_der_bytes = API.ecdsa_sign(message, self._privkey)
return Signature.from_bytes(signature_der_bytes, der_encoded=True)
return Signer(self._privkey).sign(message)
def get_signature_stamp(self):
if self._privkey == constants.PUBLIC_ONLY:
if self._privkey is constants.PUBLIC_ONLY:
return StrangerStamp(verifying_key=self.pubkey)
else:
signer = Signer(self._privkey)
@ -146,7 +138,7 @@ class HostingKeypair(Keypair):
def __init__(self,
host: str,
checksum_address: str = None,
private_key: Union[UmbralPrivateKey, UmbralPublicKey] = None,
private_key: Union[SecretKey, PublicKey] = None,
certificate=None,
certificate_filepath: str = None,
generate_certificate=False,

View File

@ -15,13 +15,13 @@ 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 typing import Optional
from typing import Optional, Dict
from bytestring_splitter import BytestringKwargifier, VariableLengthBytestring
from constant_sorrow.constants import NOT_SIGNED, UNKNOWN_SENDER
from umbral.keys import UmbralPublicKey
from nucypher.crypto.splitters import capsule_splitter, key_splitter
from nucypher.crypto.umbral_adapter import PublicKey, VerifiedCapsuleFrag, Capsule
class CryptoKit:
@ -35,7 +35,6 @@ class CryptoKit:
def split_bytes(cls, some_bytes):
if not cls.splitter:
raise TypeError("This kit doesn't have a splitter defined.")
splitter = cls.splitter()
return splitter(some_bytes)
@ -50,9 +49,9 @@ class MessageKit(CryptoKit):
"""
def __init__(self,
capsule,
sender_verifying_key=None,
ciphertext=None,
capsule: Capsule,
sender_verifying_key: Optional[PublicKey] = None,
ciphertext: bytes = None,
signature=NOT_SIGNED) -> None:
self.ciphertext = ciphertext
@ -60,6 +59,52 @@ class MessageKit(CryptoKit):
self.sender_verifying_key = sender_verifying_key
self._signature = signature
self._cfrags = set()
self._delegating_key = None
self._receiving_key = None
self._verifying_key = None
def attach_cfrag(self, cfrag: VerifiedCapsuleFrag):
# TODO: check that the cfrag belongs to this capsule?
self._cfrags.add(cfrag)
def clear_cfrags(self):
self._cfrags = set()
def set_correctness_keys(self,
delegating: Optional[PublicKey] = None,
receiving: Optional[PublicKey] = None,
verifying: Optional[PublicKey] = None):
# TODO (#2028): remove the need in sanity checks
if self._delegating_key is None:
self._delegating_key = delegating
elif delegating is not None and delegating != self._delegating_key:
raise Exception("Replacing an existing delegating key")
if self._receiving_key is None:
self._receiving_key = receiving
elif receiving is not None and receiving != self._receiving_key:
raise Exception("Replacing an existing receiving key")
if self._verifying_key is None:
self._verifying_key = verifying
elif verifying is not None and verifying != self._verifying_key:
raise Exception("Replacing an existing verifying key")
def get_correctness_keys(self) -> Dict[str, PublicKey]:
return dict(delegating=self._delegating_key,
receiving=self._receiving_key,
verifying=self._verifying_key)
def __len__(self):
# TODO: may be better to just have a "has enough cfrags" method
return len(self._cfrags)
def __str__(self):
return f"{self.__class__.__name__}({self.capsule}, {len(self)} cfrags)"
def to_bytes(self, include_alice_pubkey=True):
# We include the capsule first.
as_bytes = bytes(self.capsule)
@ -102,7 +147,7 @@ class PolicyMessageKit(MessageKit):
@sender.setter
def sender(self, enrico):
# Here we set the delegating correctness key to the policy public key (which happens to be composed on enrico, but for which of course he doesn't have the corresponding private key).
self.capsule.set_correctness_keys(delegating=enrico.policy_pubkey)
self.set_correctness_keys(delegating=enrico.policy_pubkey)
self._sender = enrico
def __bytes__(self):
@ -110,7 +155,7 @@ class PolicyMessageKit(MessageKit):
def ensure_correct_sender(self,
enrico: Optional["Enrico"] = None,
policy_encrypting_key: Optional[UmbralPublicKey] = None):
policy_encrypting_key: Optional[PublicKey] = None):
"""
Make sure that the sender of the message kit is set and corresponds to
the given ``enrico``, or create it from the given ``policy_encrypting_key``.

View File

@ -0,0 +1,111 @@
"""
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 typing import Optional
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from nacl.secret import SecretBox
from nacl.utils import random as nacl_random
from nacl.exceptions import CryptoError
from nucypher.crypto.constants import BLAKE2B
__MASTER_KEY_LENGTH = 32 # This will be passed to HKDF, but it is not picky about the length
__WRAPPING_KEY_LENGTH = SecretBox.KEY_SIZE
__WRAPPING_KEY_INFO = b'NuCypher-KeyWrap'
__HKDF_HASH_ALGORITHM = BLAKE2B
def derive_key_material_from_password(password: bytes, salt: bytes) -> bytes:
"""
Derives a symmetric encryption key seed from a pair of password and salt.
This is secure, but takes a long time.
So only call it once, and use the resulting key material as a seed for specific keys
(e.g by passing it to `derive_wrapping_key_from_key_material`, `secret_box_encrypt`
or `secret_box_decrypt`)
:param password: byte-encoded password used to derive a symmetric key
:param salt: cryptographic salt added during key derivation
:return:
"""
# WARNING: RFC7914 recommends that you use a 2^20 cost value for sensitive
# files. It is NOT recommended to change the `_scrypt_cost` value unless
# you know what you are doing.
_scrypt_cost = 20
try:
derived_key = Scrypt(
salt=salt,
length=__MASTER_KEY_LENGTH,
n=2 ** _scrypt_cost,
r=8,
p=1,
backend=default_backend()
).derive(password)
except InternalError as e:
required_memory = 128 * 2**_scrypt_cost * 8 // (10**6)
if e.err_code[0].reason == 65:
raise MemoryError(
"Scrypt key derivation requires at least {} MB of memory. "
"Please free up some memory and try again.".format(required_memory)
)
else:
raise e
else:
return derived_key
def derive_wrapping_key_from_key_material(key_material: bytes, salt: bytes) -> bytes:
"""
Uses HKDF to derive a 32 byte wrapping key to encrypt key material with.
"""
wrapping_key = HKDF(
algorithm=__HKDF_HASH_ALGORITHM,
length=__WRAPPING_KEY_LENGTH,
salt=salt,
info=__WRAPPING_KEY_INFO,
backend=default_backend()
).derive(key_material)
return wrapping_key
class SecretBoxAuthenticationError(Exception):
pass
def secret_box_encrypt(key_material: bytes, salt: bytes, plaintext: bytes) -> bytes:
wrapping_key = derive_wrapping_key_from_key_material(key_material, salt)
secret_box = SecretBox(wrapping_key)
ciphertext = secret_box.encrypt(plaintext)
return ciphertext
def secret_box_decrypt(key_material: bytes, salt: bytes, ciphertext: bytes) -> bytes:
wrapping_key = derive_wrapping_key_from_key_material(key_material, salt)
secret_box = SecretBox(wrapping_key)
try:
plaintext = secret_box.decrypt(ciphertext)
except CryptoError as e:
raise SecretBoxAuthenticationError from e
return plaintext

View File

@ -20,13 +20,12 @@ import inspect
from eth_typing.evm import ChecksumAddress
from hexbytes import HexBytes
from typing import List, Optional, Tuple
from umbral import pre
from umbral.keys import UmbralKeyingMaterial, UmbralPrivateKey, UmbralPublicKey
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
from nucypher.crypto.umbral_adapter import generate_kfrags, SecretKeyFactory, SecretKey, PublicKey
class PowerUpError(TypeError):
@ -187,22 +186,22 @@ class TransactingPower(CryptoPowerUp):
class KeyPairBasedPower(CryptoPowerUp):
confers_public_key = True
_keypair_class = keypairs.Keypair
_default_private_key_class = UmbralPrivateKey
_default_private_key_class = SecretKey
def __init__(self, public_key: UmbralPublicKey = None, keypair: keypairs.Keypair = None):
def __init__(self, public_key: PublicKey = None, keypair: keypairs.Keypair = None):
if keypair and public_key:
raise ValueError("Pass keypair or pubkey_bytes (or neither), but not both.")
elif keypair:
self.keypair = keypair
else:
# They didn't pass a keypair; we'll make one with the bytes or
# UmbralPublicKey if they provided such a thing.
# Umbral PublicKey if they provided such a thing.
if public_key:
try:
public_key = public_key.as_umbral_pubkey()
except AttributeError:
try:
public_key = UmbralPublicKey.from_bytes(public_key)
public_key = PublicKey.from_bytes(public_key)
except TypeError:
public_key = public_key
self.keypair = self._keypair_class(
@ -221,7 +220,7 @@ class KeyPairBasedPower(CryptoPowerUp):
else:
raise PowerUpError("This {} doesn't provide {}.".format(self.__class__, item))
def public_key(self) -> 'UmbralPublicKey':
def public_key(self) -> 'PublicKey':
return self.keypair.pubkey
@ -246,20 +245,17 @@ class DerivedKeyBasedPower(CryptoPowerUp):
class DelegatingPower(DerivedKeyBasedPower):
def __init__(self,
keying_material: Optional[bytes] = None,
password: Optional[bytes] = None) -> None:
def __init__(self, keying_material: Optional[bytes] = None):
if keying_material is None:
self.__umbral_keying_material = UmbralKeyingMaterial()
self.__umbral_keying_material = SecretKeyFactory.random()
else:
self.__umbral_keying_material = UmbralKeyingMaterial.from_bytes(key_bytes=keying_material,
password=password)
self.__umbral_keying_material = SecretKeyFactory.from_bytes(keying_material)
def _get_privkey_from_label(self, label):
return self.__umbral_keying_material.derive_privkey_by_label(label)
return self.__umbral_keying_material.secret_key_by_label(label)
def get_pubkey_from_label(self, label):
return self._get_privkey_from_label(label).get_pubkey()
return self._get_privkey_from_label(label).public_key()
def generate_kfrags(self,
bob_pubkey_enc,
@ -267,7 +263,7 @@ class DelegatingPower(DerivedKeyBasedPower):
label: bytes,
m: int,
n: int
) -> Tuple[UmbralPublicKey, List]:
) -> Tuple[PublicKey, List]:
"""
Generates re-encryption key frags ("KFrags") and returns them.
@ -279,15 +275,15 @@ class DelegatingPower(DerivedKeyBasedPower):
"""
__private_key = self._get_privkey_from_label(label)
kfrags = pre.generate_kfrags(delegating_privkey=__private_key,
receiving_pubkey=bob_pubkey_enc,
threshold=m,
N=n,
signer=signer,
sign_delegating_key=False,
sign_receiving_key=False,
)
return __private_key.get_pubkey(), kfrags
kfrags = generate_kfrags(delegating_sk=__private_key,
receiving_pk=bob_pubkey_enc,
threshold=m,
num_kfrags=n,
signer=signer,
sign_delegating_key=False,
sign_receiving_key=False,
)
return __private_key.public_key(), kfrags
def get_decrypting_power_from_label(self, label):
label_privkey = self._get_privkey_from_label(label)

View File

@ -17,11 +17,9 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
from bytestring_splitter import BytestringSplitter
from umbral.signing import Signature, Signer
from nucypher.crypto.api import keccak_digest
signature_splitter = BytestringSplitter(Signature)
from nucypher.crypto.umbral_adapter import Signature, Signer
class SignatureStamp(object):
@ -39,7 +37,7 @@ class SignatureStamp(object):
return self._as_bytes
def __call__(self, *args, **kwargs):
return self.__signer(*args, **kwargs)
return self.__signer.sign(*args, **kwargs)
def __hash__(self):
return int.from_bytes(self, byteorder="big")
@ -59,6 +57,9 @@ class SignatureStamp(object):
def __bool__(self):
return True
def as_umbral_signer(self):
return self.__signer
def as_umbral_pubkey(self):
return self._as_umbral_pubkey

View File

@ -17,13 +17,9 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
from bytestring_splitter import BytestringSplitter, VariableLengthBytestring
from umbral.cfrags import CapsuleFrag
from umbral.config import default_params
from umbral.keys import UmbralPublicKey
from umbral.pre import Capsule
from nucypher.crypto.umbral_adapter import CapsuleFrag, PublicKey, Capsule, Signature
from nucypher.crypto.constants import CAPSULE_LENGTH, PUBLIC_KEY_LENGTH
key_splitter = BytestringSplitter((UmbralPublicKey, PUBLIC_KEY_LENGTH))
capsule_splitter = BytestringSplitter((Capsule, CAPSULE_LENGTH, {"params": default_params()}))
cfrag_splitter = BytestringSplitter((CapsuleFrag, VariableLengthBytestring))
key_splitter = BytestringSplitter((PublicKey, PublicKey.serialized_size()))
capsule_splitter = BytestringSplitter((Capsule, Capsule.serialized_size()))
cfrag_splitter = BytestringSplitter((CapsuleFrag, CapsuleFrag.serialized_size()))
signature_splitter = BytestringSplitter((Signature, Signature.serialized_size()))

View File

@ -0,0 +1,21 @@
"""
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 *

View File

@ -18,10 +18,10 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
from coincurve import PublicKey
from eth_keys import KeyAPI as EthKeyAPI
from typing import Any, Union
from umbral.keys import UmbralPublicKey
from nucypher.crypto.api import keccak_digest
from nucypher.crypto.signing import SignatureStamp
from nucypher.crypto.umbral_adapter import PublicKey
def fingerprint_from_key(public_key: Any):
@ -40,10 +40,10 @@ def construct_policy_id(label: bytes, stamp: bytes) -> bytes:
return keccak_digest(label + stamp)
def canonical_address_from_umbral_key(public_key: Union[UmbralPublicKey, SignatureStamp]) -> bytes:
def canonical_address_from_umbral_key(public_key: Union[PublicKey, SignatureStamp]) -> bytes:
if isinstance(public_key, SignatureStamp):
public_key = public_key.as_umbral_pubkey()
pubkey_compressed_bytes = public_key.to_bytes(is_compressed=True)
pubkey_compressed_bytes = bytes(public_key)
eth_pubkey = EthKeyAPI.PublicKey.from_compressed_bytes(pubkey_compressed_bytes)
canonical_address = eth_pubkey.to_canonical_address()
return canonical_address

View File

@ -15,10 +15,9 @@ 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 maya import MayaDT
from umbral.keys import UmbralPublicKey
from umbral.kfrags import KFrag
from nucypher.crypto.signing import Signature
from nucypher.crypto.umbral_adapter import PublicKey, VerifiedKeyFrag
from nucypher.datastore.base import DatastoreRecord, RecordField
@ -29,21 +28,21 @@ class PolicyArrangement(DatastoreRecord):
encode=lambda maya_date: maya_date.iso8601().encode(),
decode=lambda maya_bytes: MayaDT.from_iso8601(maya_bytes.decode()))
_kfrag = RecordField(
KFrag,
encode=lambda kfrag: kfrag.to_bytes(),
decode=KFrag.from_bytes)
_alice_verifying_key = RecordField(
UmbralPublicKey,
VerifiedKeyFrag,
encode=bytes,
decode=UmbralPublicKey.from_bytes)
decode=VerifiedKeyFrag.from_verified_bytes)
_alice_verifying_key = RecordField(
PublicKey,
encode=bytes,
decode=PublicKey.from_bytes)
class Workorder(DatastoreRecord):
_arrangement_id = RecordField(bytes)
_bob_verifying_key = RecordField(
UmbralPublicKey,
PublicKey,
encode=bytes,
decode=UmbralPublicKey.from_bytes)
decode=PublicKey.from_bytes)
_bob_signature = RecordField(
Signature,
encode=bytes,

View File

@ -26,8 +26,7 @@ from cryptography import x509
from cryptography.hazmat.backends import default_backend
from nucypher.blockchain.eth.networks import NetworksInventory
from nucypher.crypto.signing import signature_splitter
from nucypher.crypto.splitters import cfrag_splitter
from nucypher.crypto.splitters import cfrag_splitter, signature_splitter
from nucypher.utilities.logging import Logger
EXEMPT_FROM_VERIFICATION.bool_value(False)

View File

@ -44,7 +44,6 @@ from eth_utils import to_checksum_address
from requests.exceptions import SSLError
from twisted.internet import reactor, task
from twisted.internet.defer import Deferred
from umbral.signing import Signature
from nucypher.acumen.nicknames import Nickname
from nucypher.acumen.perception import FleetSensor
@ -58,7 +57,8 @@ from nucypher.config.storages import ForgetfulNodeStorage
from nucypher.crypto.api import InvalidNodeCertificate, recover_address_eip_191, verify_eip_191
from nucypher.crypto.kits import UmbralMessageKit
from nucypher.crypto.powers import DecryptingPower, NoSigningPower, SigningPower
from nucypher.crypto.signing import signature_splitter
from nucypher.crypto.splitters import signature_splitter
from nucypher.crypto.umbral_adapter import Signature
from nucypher.network import LEARNING_LOOP_VERSION
from nucypher.network.exceptions import NodeSeemsToBeDown
from nucypher.network.middleware import RestMiddleware
@ -758,7 +758,7 @@ class Learner:
#
if signature:
is_valid = signature.verify(message, sender_verifying_key)
is_valid = signature.verify(sender_verifying_key, message)
if not is_valid:
raise self.InvalidSignature("Signature for message isn't valid: {}".format(signature))
else:
@ -1258,7 +1258,7 @@ class Teacher:
"""
interface_info_message = self._signable_interface_info_message() # Contains canonical address.
message = self.timestamp_bytes() + interface_info_message
interface_is_valid = self._interface_signature.verify(message, self.public_keys(SigningPower))
interface_is_valid = self._interface_signature.verify(self.public_keys(SigningPower), message)
self.verified_interface = interface_is_valid
if interface_is_valid:
return True

View File

@ -34,7 +34,6 @@ from mako import exceptions as mako_exceptions
from mako.template import Template
from maya import MayaDT
from typing import Tuple
from umbral.kfrags import KFrag
from web3.exceptions import TimeExhausted
import nucypher
@ -44,6 +43,7 @@ from nucypher.crypto.keypairs import HostingKeypair
from nucypher.crypto.kits import UmbralMessageKit
from nucypher.crypto.powers import KeyPairBasedPower, PowerUpError
from nucypher.crypto.signing import InvalidSignature
from nucypher.crypto.umbral_adapter import KeyFrag, VerificationError
from nucypher.crypto.utils import canonical_address_from_umbral_key
from nucypher.datastore.datastore import Datastore, RecordNotFound, DatastoreTransactionError
from nucypher.datastore.models import PolicyArrangement, TreasureMap, Workorder
@ -261,15 +261,17 @@ def _make_rest_app(datastore: Datastore, this_node, domain: str, log: Logger) ->
else:
_tx = NO_BLOCKCHAIN_CONNECTION
kfrag_bytes = cleartext
kfrag = KFrag.from_bytes(kfrag_bytes)
kfrag = KeyFrag.from_bytes(kfrag_bytes)
if not kfrag.verify(signing_pubkey=alices_verifying_key):
try:
verified_kfrag = kfrag.verify(verifying_pk=alices_verifying_key)
except VerificationError:
return Response(f"Signature on {kfrag} is invalid", status=403)
with datastore.describe(PolicyArrangement, id_as_hex, writeable=True) as policy_arrangement:
if not policy_arrangement.alice_verifying_key == alice.stamp.as_umbral_pubkey():
return Response("Policy arrangement's signing key does not match sender's", status=403)
policy_arrangement.kfrag = kfrag
policy_arrangement.kfrag = verified_kfrag
# TODO: Sign the arrangement here. #495
return "" # TODO: Return A 200, with whatever policy metadata.
@ -277,7 +279,7 @@ def _make_rest_app(datastore: Datastore, this_node, domain: str, log: Logger) ->
@rest_app.route('/kFrag/<id_as_hex>', methods=["DELETE"])
def revoke_arrangement(id_as_hex):
"""
REST endpoint for revoking/deleting a KFrag from a node.
REST endpoint for revoking/deleting a KeyFrag from a node.
"""
from nucypher.policy.collections import Revocation
@ -295,10 +297,10 @@ def _make_rest_app(datastore: Datastore, this_node, domain: str, log: Logger) ->
policy_arrangement.delete()
except (DatastoreTransactionError, InvalidSignature) as e:
log.debug("Exception attempting to revoke: {}".format(e))
return Response(response='KFrag not found or revocation signature is invalid.', status=404)
return Response(response='KeyFrag not found or revocation signature is invalid.', status=404)
else:
log.info("KFrag successfully removed.")
return Response(response='KFrag deleted!', status=200)
log.info("KeyFrag successfully removed.")
return Response(response='KeyFrag deleted!', status=200)
@rest_app.route('/kFrag/<id_as_hex>/reencrypt', methods=["POST"])
def reencrypt_via_rest(id_as_hex):
@ -309,7 +311,7 @@ def _make_rest_app(datastore: Datastore, this_node, domain: str, log: Logger) ->
except (binascii.Error, TypeError):
return Response(response=b'Invalid arrangement ID', status=405)
try:
# Get KFrag
# Get KeyFrag
# TODO: Yeah, well, what if this arrangement hasn't been enacted? 1702
with datastore.describe(PolicyArrangement, id_as_hex) as policy_arrangement:
kfrag = policy_arrangement.kfrag

View File

@ -30,9 +30,6 @@ from cryptography.hazmat.backends.openssl import backend
from cryptography.hazmat.primitives import hashes
from eth_utils import to_canonical_address, to_checksum_address
from typing import Optional, Tuple
from umbral.config import default_params
from umbral.keys import UmbralPublicKey
from umbral.pre import Capsule
from nucypher.blockchain.eth.constants import ETH_ADDRESS_BYTE_LENGTH, ETH_HASH_BYTE_LENGTH
from nucypher.characters.lawful import Bob, Character
@ -40,9 +37,9 @@ from nucypher.crypto.api import encrypt_and_sign, keccak_digest
from nucypher.crypto.api import verify_eip_191
from nucypher.crypto.constants import HRAC_LENGTH
from nucypher.crypto.kits import UmbralMessageKit
from nucypher.crypto.signing import InvalidSignature, Signature, signature_splitter, SignatureStamp
from nucypher.crypto.splitters import capsule_splitter, key_splitter
from nucypher.crypto.splitters import cfrag_splitter
from nucypher.crypto.signing import InvalidSignature, Signature, SignatureStamp
from nucypher.crypto.splitters import capsule_splitter, cfrag_splitter, key_splitter, signature_splitter
from nucypher.crypto.umbral_adapter import PublicKey, Capsule
from nucypher.crypto.utils import (
canonical_address_from_umbral_key,
)
@ -101,7 +98,7 @@ class TreasureMap:
@classmethod
def splitter(cls):
return BytestringKwargifier(cls,
public_signature=Signature,
public_signature=signature_splitter,
hrac=(bytes, HRAC_LENGTH),
message_kit=(UmbralMessageKit, VariableLengthBytestring)
)
@ -139,7 +136,7 @@ class TreasureMap:
self._id = keccak_digest(bytes(self._verifying_key) + bytes(self._hrac)).hex()
def _set_payload(self):
self._payload = self._public_signature + self._hrac + bytes(
self._payload = bytes(self._public_signature) + self._hrac + bytes(
VariableLengthBytestring(self.message_kit.to_bytes()))
def __bytes__(self):
@ -198,7 +195,7 @@ class TreasureMap:
def public_verify(self):
message = bytes(self._verifying_key) + self._hrac
verified = self._public_signature.verify(message, self._verifying_key)
verified = self._public_signature.verify(self._verifying_key, message)
if verified:
return True
@ -254,7 +251,7 @@ class SignedTreasureMap(TreasureMap):
def splitter(cls):
return BytestringKwargifier(cls,
blockchain_signature=65,
public_signature=Signature,
public_signature=signature_splitter,
hrac=(bytes, HRAC_LENGTH),
message_kit=(UmbralMessageKit, VariableLengthBytestring)
)
@ -293,7 +290,6 @@ class WorkOrder:
blockhash = bytes(blockhash)
expected_lengths = (
(ursula_pubkey, 'ursula_pubkey', UmbralPublicKey.expected_bytes_length()),
(alice_address, 'alice_address', ETH_ADDRESS_BYTE_LENGTH),
(blockhash, 'blockhash', ETH_HASH_BYTE_LENGTH),
# TODO: Why does ursula_identity_evidence has a default value of b''? for federated, perhaps?
@ -313,7 +309,7 @@ class WorkOrder:
def __bytes__(self):
data = bytes(self.capsule) + bytes(self.signature)
if self.cfrag and self.cfrag_signature:
data += VariableLengthBytestring(self.cfrag) + bytes(self.cfrag_signature)
data += bytes(self.cfrag) + bytes(self.cfrag_signature)
return data
@classmethod
@ -394,7 +390,7 @@ class WorkOrder:
@classmethod
def from_rest_payload(cls, arrangement_id, rest_payload, ursula, alice_address):
payload_splitter = BytestringSplitter(Signature) + key_splitter + BytestringSplitter(ETH_HASH_BYTE_LENGTH)
payload_splitter = signature_splitter + key_splitter + BytestringSplitter(ETH_HASH_BYTE_LENGTH)
signature, bob_verifying_key, blockhash, remainder = payload_splitter(rest_payload, return_remainder=True)
tasks = {capsule: cls.PRETask(capsule, sig) for capsule, sig in cls.PRETask.input_splitter.repeat(remainder)}
@ -411,13 +407,13 @@ class WorkOrder:
blockhash,
ursula_identity_evidence)
if not task.signature.verify(specification, bob_verifying_key):
if not task.signature.verify(bob_verifying_key, specification):
raise InvalidSignature()
# Check receipt
capsules = b''.join(map(bytes, tasks.keys()))
receipt_bytes = cls.HEADER + bytes(ursula.stamp) + capsules
if not signature.verify(receipt_bytes, bob_verifying_key):
if not signature.verify(bob_verifying_key, receipt_bytes):
raise InvalidSignature()
bob = Bob.from_public_keys(verifying_key=bob_verifying_key)
@ -444,15 +440,9 @@ class WorkOrder:
ursula_verifying_key = self.ursula.stamp.as_umbral_pubkey()
for task, (cfrag, cfrag_signature) in zip(self.tasks.values(), cfrags_and_signatures):
# Validate re-encryption metadata
metadata_input = bytes(task.signature)
metadata_as_signature = Signature.from_bytes(cfrag.proof.metadata)
if not metadata_as_signature.verify(metadata_input, ursula_verifying_key):
raise InvalidSignature(f"Invalid metadata for {cfrag}.")
# TODO: Instead of raising, we should do something (#957)
# Validate re-encryption signatures
if cfrag_signature.verify(bytes(cfrag), ursula_verifying_key):
if cfrag_signature.verify(ursula_verifying_key, bytes(cfrag)):
good_cfrags.append(cfrag)
else:
raise InvalidSignature(f"{cfrag} is not properly signed by Ursula.")
@ -525,7 +515,7 @@ class Revocation:
REVOKE-<arrangement id to revoke><signature of the previous string>
This is sent as a payload in a DELETE method to the /KFrag/ endpoint.
"""
revocation_splitter = BytestringSplitter((bytes, 7), (bytes, 32), Signature)
revocation_splitter = BytestringSplitter((bytes, 7), (bytes, 32), signature_splitter)
def __init__(self, arrangement_id: bytes,
signer: 'SignatureStamp' = None,
@ -558,11 +548,11 @@ class Revocation:
_, arrangement_id, signature = cls.revocation_splitter(revocation_bytes)
return cls(arrangement_id, signature=signature)
def verify_signature(self, alice_pubkey: 'UmbralPublicKey'):
def verify_signature(self, alice_pubkey: 'PublicKey'):
"""
Verifies the revocation was from the provided pubkey.
"""
if not self.signature.verify(self.prefix + self.arrangement_id, alice_pubkey):
if not self.signature.verify(alice_pubkey, self.prefix + self.arrangement_id):
raise InvalidSignature(
"Revocation has an invalid signature: {}".format(self.signature))
return True

View File

@ -27,12 +27,12 @@ import os
from bytestring_splitter import VariableLengthBytestring, BytestringKwargifier
from constant_sorrow.constants import ALICE, BOB, NO_SIGNATURE
from hexbytes.main import HexBytes
from umbral.keys import UmbralPublicKey
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 SigningPower, DecryptingPower
from nucypher.crypto.umbral_adapter import PublicKey
class Card:
@ -42,14 +42,14 @@ class Card:
_alice_specification = dict(
character_flag=(bytes, 8),
verifying_key=(UmbralPublicKey, 33),
verifying_key=(PublicKey, PublicKey.serialized_size()),
nickname=(bytes, VariableLengthBytestring),
)
_bob_specification = dict(
character_flag=(bytes, 8),
verifying_key=(UmbralPublicKey, 33),
encrypting_key=(UmbralPublicKey, 33),
verifying_key=(PublicKey, PublicKey.serialized_size()),
encrypting_key=(PublicKey, PublicKey.serialized_size()),
nickname=(bytes, VariableLengthBytestring),
)
@ -80,8 +80,8 @@ class Card:
def __init__(self,
character_flag: Union[ALICE, BOB],
verifying_key: Union[UmbralPublicKey, bytes],
encrypting_key: Optional[Union[UmbralPublicKey, bytes]] = None,
verifying_key: Union[PublicKey, bytes],
encrypting_key: Optional[Union[PublicKey, bytes]] = None,
nickname: Optional[Union[bytes, str]] = None):
try:
@ -91,11 +91,11 @@ class Card:
self.__character_flag = character_flag
if isinstance(verifying_key, bytes):
verifying_key = UmbralPublicKey.from_bytes(verifying_key)
verifying_key = PublicKey.from_bytes(verifying_key)
self.__verifying_key = verifying_key # signing public key
if isinstance(encrypting_key, bytes):
encrypting_key = UmbralPublicKey.from_bytes(encrypting_key)
encrypting_key = PublicKey.from_bytes(encrypting_key)
self.__encrypting_key = encrypting_key # public key
if isinstance(nickname, str):
@ -251,11 +251,11 @@ class Card:
@property
def verifying_key(self) -> UmbralPublicKey:
def verifying_key(self) -> PublicKey:
return self.__verifying_key
@property
def encrypting_key(self) -> UmbralPublicKey:
def encrypting_key(self) -> PublicKey:
return self.__encrypting_key
@property

View File

@ -34,16 +34,16 @@ from twisted._threads import AlreadyQuit
from twisted.internet import reactor
from twisted.internet.defer import ensureDeferred, Deferred
from twisted.python.threadpool import ThreadPool
from umbral.keys import UmbralPublicKey
from umbral.kfrags import KFrag
from nucypher.blockchain.eth.actors import BlockchainPolicyAuthor
from nucypher.blockchain.eth.agents import PolicyManagerAgent, StakersReservoir, StakingEscrowAgent
from nucypher.characters.lawful import Alice, Ursula
from nucypher.crypto.api import keccak_digest, secure_random
from nucypher.crypto.constants import HRAC_LENGTH, PUBLIC_KEY_LENGTH
from nucypher.crypto.constants import HRAC_LENGTH
from nucypher.crypto.kits import RevocationKit
from nucypher.crypto.powers import DecryptingPower, SigningPower, TransactingPower
from nucypher.crypto.splitters import key_splitter
from nucypher.crypto.umbral_adapter import PublicKey, KeyFrag
from nucypher.crypto.utils import construct_policy_id
from nucypher.network.exceptions import NodeSeemsToBeDown
from nucypher.network.middleware import RestMiddleware
@ -57,7 +57,7 @@ class Arrangement:
"""
ID_LENGTH = 32
splitter = BytestringSplitter((UmbralPublicKey, PUBLIC_KEY_LENGTH), # alive_verifying_key
splitter = BytestringSplitter(key_splitter, # alive_verifying_key
(bytes, ID_LENGTH), # arrangement_id
(bytes, VariableLengthBytestring)) # expiration
@ -68,7 +68,7 @@ class Arrangement:
return cls(alice_verifying_key, expiration, arrangement_id)
def __init__(self,
alice_verifying_key: UmbralPublicKey,
alice_verifying_key: PublicKey,
expiration: maya.MayaDT,
arrangement_id: bytes,
) -> None:
@ -190,7 +190,7 @@ class Policy(ABC):
class NotEnoughUrsulas(Exception):
"""
Raised when a Policy has been used to generate Arrangements with Ursulas insufficient number
such that we don't have enough KFrags to give to each Ursula.
such that we don't have enough KeyFrags to give to each Ursula.
"""
class EnactmentError(Exception):
@ -203,13 +203,13 @@ class Policy(ABC):
label: bytes,
expiration: maya.MayaDT,
bob: 'Bob',
kfrags: Sequence[KFrag],
public_key: UmbralPublicKey,
kfrags: Sequence[KeyFrag],
public_key: PublicKey,
m: int,
):
"""
:param kfrags: A list of KFrags to distribute per this Policy.
:param kfrags: A list of KeyFrags to distribute per this Policy.
:param label: The identity of the resource to which Bob is granted access.
"""
@ -500,7 +500,7 @@ class Policy(ABC):
raise NotImplementedError
@abstractmethod
def _make_enactment_payload(self, publication_transaction: Optional[HexBytes], kfrag: KFrag) -> bytes:
def _make_enactment_payload(self, publication_transaction: Optional[HexBytes], kfrag: KeyFrag) -> bytes:
"""
Serializes a given kfrag and policy publication transaction to send to Ursula.
"""
@ -656,11 +656,11 @@ class EnactedPolicy:
id: bytes,
hrac: bytes,
label: bytes,
public_key: UmbralPublicKey,
public_key: PublicKey,
treasure_map: 'TreasureMap',
treasure_map_publisher: TreasureMapPublisher,
revocation_kit: RevocationKit,
alice_verifying_key: UmbralPublicKey,
alice_verifying_key: PublicKey,
):
self.id = id # TODO: is it even used anywhere?

View File

@ -1,56 +1,56 @@
-i https://pypi.python.org/simple
appdirs==1.4.4
asn1crypto==1.4.0
attrs==20.3.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
autobahn==20.12.3; python_version >= '3.6'
attrs==21.2.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
autobahn==21.3.1; python_version >= '3.7'
automat==20.2.0
base58==2.0.1; python_version >= '3.5'
base58==2.1.0; python_version >= '3.5'
bitarray==1.2.2
blake2b-py==0.1.3
bytestring-splitter==2.2.0
blake2b-py==0.1.4
bytestring-splitter==2.3.2
cached-property==1.5.2
certifi==2020.12.5
cffi==1.14.4
certifi==2021.5.30
cffi==1.14.5
chardet==4.0.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
click==7.1.2
coincurve==13.0.0
coincurve==15.0.0
colorama==0.4.4
constant-sorrow==0.1.0a9
constantly==15.1.0
construct==2.10.56; python_version >= '3.6'
cryptography==3.3.1
construct==2.10.67; python_version >= '3.6'
cryptography==3.4.7
cytoolz==0.11.0; implementation_name == 'cpython'
dateparser==1.0.0; python_version >= '3.5'
ecdsa==0.16.1; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'
ecdsa==0.17.0; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'
eth-abi==2.1.1; python_version >= '3.6' and python_version < '4'
eth-account==0.5.4; python_version >= '3.6' and python_version < '4'
eth-bloom==1.0.3; python_version >= '3.5' and python_full_version != '3.5.2' and python_version < '4'
eth-hash[pycryptodome]==0.2.0
eth-bloom==1.0.4; python_version >= '3.6' and python_version < '4'
eth-hash[pycryptodome]==0.3.1; python_version >= '3.5' and python_version < '4'
eth-keyfile==0.5.1
eth-keys==0.3.3
eth-rlp==0.2.1; python_version >= '3.6' and python_version < '4'
eth-tester==0.5.0b3
eth-tester==0.5.0b4
eth-typing==2.2.2; python_version >= '3.5' and python_version < '4'
eth-utils==1.9.5; python_version >= '3.5' and python_full_version != '3.5.2' and python_version < '4'
flask-sqlalchemy==2.4.4
flask==1.1.2
eth-utils==1.9.5
flask-sqlalchemy==2.5.1
flask==2.0.1
greenlet==1.1.0; python_version >= '3'
hendrix==3.4.0
hexbytes==0.2.1; python_version >= '3.6' and python_version < '4'
humanize==3.2.0; python_version >= '3.6'
hyperlink==20.0.1
humanize==3.8.0; python_version >= '3.6'
hyperlink==21.0.0
idna==2.10; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
importlib-metadata==3.3.0; python_version < '3.8'
incremental==17.5.0
incremental==21.3.0
ipfshttpclient==0.7.0a1; python_full_version >= '3.5.4' and python_full_version not in '3.6.0, 3.6.1, 3.7.0, 3.7.1'
itsdangerous==1.1.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
jinja2==2.11.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
itsdangerous==2.0.1; python_version >= '3.6'
jinja2==3.0.1; python_version >= '3.6'
jsonschema==3.2.0
libusb1==1.9.1
lmdb==1.0.0
lru-dict==1.1.6
mako==1.1.3
markupsafe==1.1.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
marshmallow==3.10.0
libusb1==1.9.2
lmdb==1.2.1
lru-dict==1.1.7
mako==1.1.4
markupsafe==2.0.1; python_version >= '3.6'
marshmallow==3.12.1
maya==0.6.1
mnemonic==0.19
msgpack-python==0.5.6
@ -60,49 +60,47 @@ mypy-extensions==0.4.3
netaddr==0.8.0
parsimonious==0.8.1
pendulum==2.1.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
pillow==8.1.0
protobuf==3.14.0
pillow==8.2.0
protobuf==3.17.3
py-ecc==4.1.0; python_version >= '3.5' and python_version < '4'
py-evm==0.3.0a20
py-geth==2.4.0
py-evm==0.4.0a4
py-geth==3.1.0
pyasn1-modules==0.2.8
pyasn1==0.4.8
pychalk==2.0.1
pycparser==2.20; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
pycryptodome==3.9.9
pycryptodome==3.10.1
pyethash==0.1.27
pyhamcrest==2.0.2; python_version >= '3.5'
pynacl==1.4.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
pyopenssl==20.0.1
pyrsistent==0.17.3; python_version >= '3.5'
pysha3==1.0.2
python-dateutil==2.8.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'
pytz==2020.5
pytz==2021.1
pytzdata==2020.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
qrcode[pil]==6.1
regex==2020.11.13
regex==2021.4.4
requests==2.25.1
rlp==2.0.1
semantic-version==2.8.5; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
service-identity==18.1.0
six==1.15.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'
service-identity==21.1.0
six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'
snaptime==0.2.4
sortedcontainers==2.3.0
sqlalchemy==1.3.22
tabulate==0.8.7
sortedcontainers==2.4.0
sqlalchemy==1.4.18
tabulate==0.8.9
toolz==0.11.1; python_version >= '3.5'
trezor==0.12.2
trie==2.0.0a5; python_version >= '3.6' and python_version < '4'
twisted==20.3.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
txaio==20.12.1; python_version >= '3.6'
typing-extensions==3.7.4.3; python_version < '3.8' and python_version < '3.8'
twisted==21.2.0; python_full_version >= '3.5.4'
txaio==21.2.1; python_version >= '3.6'
typing-extensions==3.10.0.0
tzlocal==2.1
umbral==0.1.3a2
urllib3==1.26.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'
umbral==0.2.0
urllib3==1.26.5; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'
varint==1.0.2
watchdog==1.0.2; python_version >= '3.6'
watchdog==2.1.2; python_version >= '3.6'
web3==5.12.3
websockets==8.1; python_full_version >= '3.6.1'
werkzeug==1.0.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
zipp==3.4.0; python_version >= '3.6'
zope.interface==5.2.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
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'

View File

@ -249,7 +249,7 @@ def test_enrico_web_character_control_encrypt_message(enrico_web_controller_test
assert 'signature' in response_data['result']
# Check that it serializes correctly.
assert UmbralMessageKit.from_bytes(b64decode(response_data['result']['message_kit']))
message_kit = UmbralMessageKit.from_bytes(b64decode(response_data['result']['message_kit']))
# Send bad data to assert error return
response = enrico_web_controller_test_client.post('/encrypt_message', data=json.dumps({'bad': 'input'}))

View File

@ -20,9 +20,9 @@ import os
import pytest
import tempfile
from pathlib import Path
from umbral.keys import UmbralPrivateKey
from nucypher.cli.main import nucypher_cli
from nucypher.crypto.umbral_adapter import SecretKey
from nucypher.policy.identity import Card
@ -39,7 +39,7 @@ def patch_card_directory(session_mocker):
@pytest.fixture(scope='module')
def alice_verifying_key():
return UmbralPrivateKey.gen_key().get_pubkey().hex()
return bytes(SecretKey.random().public_key()).hex()
@pytest.fixture(scope='module')
@ -54,12 +54,12 @@ def alice_nickname():
@pytest.fixture(scope='module')
def bob_verifying_key():
return UmbralPrivateKey.gen_key().get_pubkey().hex()
return bytes(SecretKey.random().public_key()).hex()
@pytest.fixture(scope='module')
def bob_encrypting_key():
return UmbralPrivateKey.gen_key().get_pubkey().hex()
return bytes(SecretKey.random().public_key()).hex()
def test_card_directory_autocreation(click_runner, mocker):
@ -107,7 +107,7 @@ def test_create_alice_card_interactive(click_runner, alice_verifying_key, alice_
def test_create_alice_card_inline(click_runner, alice_verifying_key, alice_nickname):
command = ('contacts', 'create',
'--type', 'a',
'--verifying-key', UmbralPrivateKey.gen_key().get_pubkey().hex(),
'--verifying-key', bytes(SecretKey.random().public_key()).hex(),
'--nickname', 'philippa')
assert len(os.listdir(Card.CARD_DIR)) == 1
@ -139,8 +139,8 @@ def test_create_bob_card_interactive(click_runner, bob_nickname, bob_encrypting_
def test_create_bob_card_inline(click_runner, alice_verifying_key, alice_nickname):
command = ('contacts', 'create',
'--type', 'b',
'--verifying-key', UmbralPrivateKey.gen_key().get_pubkey().hex(),
'--encrypting-key', UmbralPrivateKey.gen_key().get_pubkey().hex(),
'--verifying-key', bytes(SecretKey.random().public_key()).hex(),
'--encrypting-key', bytes(SecretKey.random().public_key()).hex(),
'--nickname', 'hans')
assert len(os.listdir(Card.CARD_DIR)) == 3

View File

@ -15,13 +15,12 @@
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
from umbral.keys import UmbralPrivateKey
from nucypher.cli.main import nucypher_cli
from nucypher.crypto.umbral_adapter import SecretKey
def test_enrico_encrypt(click_runner):
policy_encrypting_key = UmbralPrivateKey.gen_key().get_pubkey().to_bytes().hex()
policy_encrypting_key = bytes(SecretKey.random().public_key()).hex()
encrypt_args = ('enrico', 'encrypt',
'--message', 'to be or not to be',
'--policy-encrypting-key', policy_encrypting_key)
@ -34,7 +33,7 @@ def test_enrico_encrypt(click_runner):
def test_enrico_control_starts(click_runner):
policy_encrypting_key = UmbralPrivateKey.gen_key().get_pubkey().to_bytes().hex()
policy_encrypting_key = bytes(SecretKey.random().public_key()).hex()
run_args = ('enrico', 'run',
'--policy-encrypting-key', policy_encrypting_key,
'--dry-run')

View File

@ -22,14 +22,13 @@ import pytest
import shutil
from pathlib import Path
from umbral.keys import UmbralPrivateKey
from nucypher.blockchain.eth.actors import Worker
from nucypher.cli.main import nucypher_cli
from nucypher.config.characters import AliceConfiguration, FelixConfiguration, UrsulaConfiguration
from nucypher.config.constants import NUCYPHER_ENVVAR_KEYRING_PASSWORD, TEMPORARY_DOMAIN, \
NUCYPHER_ENVVAR_ALICE_ETH_PASSWORD, NUCYPHER_ENVVAR_BOB_ETH_PASSWORD
from nucypher.config.keyring import NucypherKeyring
from nucypher.crypto.umbral_adapter import SecretKey
from nucypher.network.nodes import Teacher
from tests.constants import (
INSECURE_DEVELOPMENT_PASSWORD,
@ -162,7 +161,7 @@ def test_coexisting_configurations(click_runner,
assert os.path.isfile(ursula_file_location)
# keyring signing key
signing_public_key = UmbralPrivateKey.gen_key().get_pubkey()
signing_public_key = SecretKey.random().public_key()
with patch('nucypher.config.keyring.NucypherKeyring.signing_public_key',
PropertyMock(return_value=signing_public_key)):
# Use the same local filepath to init another persistent Ursula
@ -178,7 +177,7 @@ def test_coexisting_configurations(click_runner,
assert result.exit_code == 0
another_ursula_configuration_file_location = custom_filepath / UrsulaConfiguration.generate_filename(
modifier=signing_public_key.hex()[:8])
modifier=bytes(signing_public_key).hex()[:8])
# All configuration files still exist.
assert os.path.isfile(felix_file_location)

View File

@ -19,14 +19,13 @@ import os
import pytest
from eth_tester.exceptions import TransactionFailed
from eth_utils import to_canonical_address, to_wei
from umbral.keys import UmbralPrivateKey
from umbral.signing import Signer
from web3.contract import Contract
from nucypher.blockchain.economics import BaseEconomics
from nucypher.blockchain.eth.constants import NULL_ADDRESS
from nucypher.crypto.api 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
@ -194,8 +193,8 @@ def escrow(testerchain, escrow_bare, escrow_dispatcher):
def mock_ursula(testerchain, account, mocker):
ursula_privkey = UmbralPrivateKey.gen_key()
ursula_stamp = SignatureStamp(verifying_key=ursula_privkey.pubkey,
ursula_privkey = SecretKey.random()
ursula_stamp = SignatureStamp(verifying_key=ursula_privkey.public_key(),
signer=Signer(ursula_privkey))
signed_stamp = testerchain.client.sign_message(account=account,

View File

@ -18,17 +18,17 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
import os
import pytest
from coincurve import PublicKey
import coincurve
from cryptography.hazmat.backends.openssl import backend
from cryptography.hazmat.primitives import hashes
from eth_account.account import Account
from eth_account.messages import HexBytes, SignableMessage, encode_defunct
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 umbral.keys import UmbralPrivateKey, UmbralPublicKey
from umbral.signing import Signer, Signature
from nucypher.crypto.api import keccak_digest, verify_eip_191
from nucypher.crypto.umbral_adapter import SecretKey, PublicKey, Signer, Signature
from nucypher.crypto.utils import canonical_address_from_umbral_key
ALGORITHM_KECCAK256 = 0
@ -38,7 +38,7 @@ ALGORITHM_RIPEMD160 = 2
def get_signature_recovery_value(message: bytes,
signature: Signature,
public_key: UmbralPublicKey
public_key: PublicKey
) -> bytes:
"""
Obtains the recovery value of a standard ECDSA signature.
@ -51,14 +51,14 @@ def get_signature_recovery_value(message: bytes,
"""
signature = bytes(signature)
ecdsa_signature_size = Signature.expected_bytes_length()
ecdsa_signature_size = 64 # two curve scalars
if len(signature) != ecdsa_signature_size:
raise ValueError(f"The signature size should be {ecdsa_signature_size} B.")
for v in (0, 1):
v_byte = bytes([v])
recovered_pubkey = PublicKey.from_signature_and_message(serialized_sig=signature + v_byte,
message=message)
recovered_pubkey = coincurve.PublicKey.from_signature_and_message(signature=signature + v_byte,
message=message)
if bytes(public_key) == recovered_pubkey.format(compressed=True):
return v_byte
else:
@ -66,6 +66,20 @@ def get_signature_recovery_value(message: bytes,
"Either the message, the signature or the public key is not correct")
def pubkey_as_address(umbral_pubkey):
"""
Returns the public key as b'0x' + keccak(uncompressed_bytes)[-20:]
"""
return to_normalized_address(canonical_address_from_umbral_key(umbral_pubkey).hex())
def pubkey_as_uncompressed_bytes(umbral_pubkey):
"""
Returns the public key as uncompressed bytes (without the prefix, so 64 bytes long)
"""
return EthKeyAPI.PublicKey.from_compressed_bytes(bytes(umbral_pubkey)).to_bytes()
@pytest.fixture()
def signature_verifier(testerchain, deploy_contract):
contract, _ = deploy_contract('SignatureVerifierMock')
@ -81,15 +95,13 @@ def test_recover(testerchain, signature_verifier):
message_hash = hash_ctx.finalize()
# Generate Umbral key and extract "address" from the public key
umbral_privkey = UmbralPrivateKey.gen_key()
umbral_pubkey = umbral_privkey.get_pubkey()
umbral_pubkey_bytes = umbral_privkey.get_pubkey().to_bytes(is_compressed=False)
signer_address = keccak_digest(umbral_pubkey_bytes[1:])
signer_address = to_normalized_address(signer_address[12:])
umbral_privkey = SecretKey.random()
umbral_pubkey = umbral_privkey.public_key()
signer_address = pubkey_as_address(umbral_pubkey)
# Sign message
signer = Signer(umbral_privkey)
signature = signer(message)
signature = signer.sign(message)
# Get recovery id (v) before using contract
# If we don't have recovery id while signing then we should try to recover public key with different v
@ -119,11 +131,10 @@ def test_recover(testerchain, signature_verifier):
def test_address(testerchain, signature_verifier):
# Generate Umbral key and extract "address" from the public key
umbral_privkey = UmbralPrivateKey.gen_key()
umbral_pubkey = umbral_privkey.get_pubkey()
umbral_pubkey_bytes = umbral_pubkey.to_bytes(is_compressed=False)[1:]
signer_address = keccak_digest(umbral_pubkey_bytes)
signer_address = to_normalized_address(signer_address[12:])
umbral_privkey = SecretKey.random()
umbral_pubkey = umbral_privkey.public_key()
signer_address = pubkey_as_address(umbral_pubkey)
umbral_pubkey_bytes = pubkey_as_uncompressed_bytes(umbral_pubkey)
# Check extracting address in library
result_address = signature_verifier.functions.toAddress(umbral_pubkey_bytes).call()
@ -146,13 +157,13 @@ def test_verify(testerchain, signature_verifier):
message = os.urandom(100)
# Generate Umbral key
umbral_privkey = UmbralPrivateKey.gen_key()
umbral_pubkey = umbral_privkey.get_pubkey()
umbral_pubkey_bytes = umbral_pubkey.to_bytes(is_compressed=False)
umbral_privkey = SecretKey.random()
umbral_pubkey = umbral_privkey.public_key()
umbral_pubkey_bytes = pubkey_as_uncompressed_bytes(umbral_pubkey)
# Sign message using SHA-256 hash
signer = Signer(umbral_privkey)
signature = signer(message)
signature = signer.sign(message)
# Get recovery id (v) before using contract
v = get_signature_recovery_value(message, signature, umbral_pubkey)
@ -161,15 +172,15 @@ def test_verify(testerchain, signature_verifier):
# Verify signature
assert signature_verifier.functions.verify(message,
recoverable_signature,
umbral_pubkey_bytes[1:],
umbral_pubkey_bytes,
ALGORITHM_SHA256).call()
# Verify signature using wrong key
umbral_privkey = UmbralPrivateKey.gen_key()
umbral_pubkey_bytes = umbral_privkey.get_pubkey().to_bytes(is_compressed=False)
umbral_privkey = SecretKey.random()
umbral_pubkey_bytes = pubkey_as_uncompressed_bytes(umbral_privkey.public_key())
assert not signature_verifier.functions.verify(message,
recoverable_signature,
umbral_pubkey_bytes[1:],
umbral_pubkey_bytes,
ALGORITHM_SHA256).call()
@ -177,9 +188,9 @@ def test_verify_eip191(testerchain, signature_verifier):
message = os.urandom(100)
# Generate Umbral key
umbral_privkey = UmbralPrivateKey.gen_key()
umbral_pubkey = umbral_privkey.get_pubkey()
umbral_pubkey_bytes = umbral_pubkey.to_bytes(is_compressed=False)
umbral_privkey = SecretKey.random()
umbral_pubkey = umbral_privkey.public_key()
umbral_pubkey_bytes = pubkey_as_uncompressed_bytes(umbral_pubkey)
#
# Check EIP191 signatures: Version E
@ -188,7 +199,7 @@ def test_verify_eip191(testerchain, signature_verifier):
# Produce EIP191 signature (version E)
signable_message = encode_defunct(primitive=message)
signature = Account.sign_message(signable_message=signable_message,
private_key=umbral_privkey.to_bytes())
private_key=bytes(umbral_privkey))
signature = bytes(signature.signature)
# Off-chain verify, just in case
@ -201,14 +212,14 @@ def test_verify_eip191(testerchain, signature_verifier):
version_E = b'E'
assert signature_verifier.functions.verifyEIP191(message,
signature,
umbral_pubkey_bytes[1:],
umbral_pubkey_bytes,
version_E).call()
# Of course, it'll fail if we try using version 0
version_0 = b'\x00'
assert not signature_verifier.functions.verifyEIP191(message,
signature,
umbral_pubkey_bytes[1:],
umbral_pubkey_bytes,
version_0).call()
# Check that the hash-based method also works independently
@ -229,7 +240,7 @@ def test_verify_eip191(testerchain, signature_verifier):
header=HexBytes(validator),
body=HexBytes(message))
signature = Account.sign_message(signable_message=signable_message,
private_key=umbral_privkey.to_bytes())
private_key=bytes(umbral_privkey))
signature = bytes(signature.signature)
# Off-chain verify, just in case
@ -240,13 +251,13 @@ def test_verify_eip191(testerchain, signature_verifier):
# On chain verify signature
assert signature_verifier.functions.verifyEIP191(message,
signature,
umbral_pubkey_bytes[1:],
umbral_pubkey_bytes,
version_0).call()
# Of course, now it fails if we try with version E
assert not signature_verifier.functions.verifyEIP191(message,
signature,
umbral_pubkey_bytes[1:],
umbral_pubkey_bytes,
version_E).call()
# Check that the hash-based method also works independently

View File

@ -19,8 +19,7 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
import os
import pytest
from eth_tester.exceptions import TransactionFailed
from umbral import keys, pre
from umbral.signing import Signer
from nucypher.crypto.umbral_adapter import Signer, SecretKey, generate_kfrags, encrypt, reencrypt
@pytest.fixture()
@ -31,27 +30,22 @@ def deserializer(testerchain, deploy_contract):
@pytest.fixture(scope="module")
def fragments():
delegating_privkey = keys.UmbralPrivateKey.gen_key()
delegating_pubkey = delegating_privkey.get_pubkey()
signing_privkey = keys.UmbralPrivateKey.gen_key()
delegating_privkey = SecretKey.random()
delegating_pubkey = delegating_privkey.public_key()
signing_privkey = SecretKey.random()
signer = Signer(signing_privkey)
priv_key_bob = keys.UmbralPrivateKey.gen_key()
pub_key_bob = priv_key_bob.get_pubkey()
kfrags = pre.generate_kfrags(delegating_privkey=delegating_privkey,
signer=signer,
receiving_pubkey=pub_key_bob,
threshold=2,
N=4,
sign_delegating_key=False,
sign_receiving_key=False)
# TODO: Use nucypher re-encryption metadata
metadata = b"This is an example of metadata for re-encryption request"
priv_key_bob = SecretKey.random()
pub_key_bob = priv_key_bob.public_key()
kfrags = generate_kfrags(delegating_sk=delegating_privkey,
signer=signer,
receiving_pk=pub_key_bob,
threshold=2,
num_kfrags=4,
sign_delegating_key=False,
sign_receiving_key=False)
_symmetric_key, capsule = pre._encapsulate(delegating_pubkey)
capsule.set_correctness_keys(delegating=delegating_pubkey,
receiving=pub_key_bob,
verifying=signing_privkey.get_pubkey())
cfrag = pre.reencrypt(kfrags[0], capsule, metadata=metadata)
capsule, _ciphertext = encrypt(delegating_pubkey, b'unused')
cfrag = reencrypt(capsule, kfrags[0])
return capsule, cfrag
@ -69,42 +63,9 @@ def test_capsule(testerchain, deserializer, fragments):
# Check real capsule
capsule, _cfrag = fragments
capsule_bytes = capsule.to_bytes()
capsule_bytes = bytes(capsule)
result = deserializer.functions.toCapsule(capsule_bytes).call()
assert bytes(capsule.point_e) == result[0] + result[1]
assert bytes(capsule.point_v) == result[2] + result[3]
assert capsule.bn_sig.to_bytes() == bytes(result[4])
def test_proof(testerchain, deserializer, fragments):
# Wrong number of bytes to deserialize proof
with pytest.raises((TransactionFailed, ValueError)):
deserializer.functions.toCorrectnessProof(os.urandom(227)).call()
# Check random proof bytes without metadata
proof_bytes = os.urandom(228)
result = deserializer.functions.toCorrectnessProof(proof_bytes).call()
assert proof_bytes == bytes().join(result)
# Check random proof bytes with metadata
proof_bytes = os.urandom(270)
result = deserializer.functions.toCorrectnessProof(proof_bytes).call()
assert proof_bytes == bytes().join(result)
# Get real cfrag and proof
_capsule, cfrag = fragments
proof = cfrag.proof
proof_bytes = proof.to_bytes()
# Check real proof
result = deserializer.functions.toCorrectnessProof(proof_bytes).call()
assert bytes(proof.point_e2) == result[0] + result[1]
assert bytes(proof.point_v2) == result[2] + result[3]
assert bytes(proof.point_kfrag_commitment) == result[4] + result[5]
assert bytes(proof.point_kfrag_pok) == result[6] + result[7]
assert proof.bn_sig.to_bytes() == result[8]
assert bytes(proof.kfrag_signature) == result[9]
assert bytes(proof.metadata) == result[10]
assert b''.join(result) == capsule_bytes
def test_cfrag(testerchain, deserializer, fragments):
@ -123,20 +84,10 @@ def test_cfrag(testerchain, deserializer, fragments):
# Check real cfrag
_capsule, cfrag = fragments
proof = cfrag.proof
cfrag_bytes = cfrag.to_bytes()
result = deserializer.functions.toCapsuleFrag(cfrag_bytes).call()
assert bytes(cfrag.point_e1) == result[0] + result[1]
assert bytes(cfrag.point_v1) == result[2] + result[3]
assert bytes(cfrag.kfrag_id) == result[4]
assert bytes(cfrag.point_precursor) == result[5] + result[6]
result = deserializer.functions.toCorrectnessProofFromCapsuleFrag(cfrag_bytes).call()
assert bytes(proof.point_e2) == result[0] + result[1]
assert bytes(proof.point_v2) == result[2] + result[3]
assert bytes(proof.point_kfrag_commitment) == result[4] + result[5]
assert bytes(proof.point_kfrag_pok) == result[6] + result[7]
assert proof.bn_sig.to_bytes() == result[8]
assert bytes(proof.kfrag_signature) == result[9]
assert bytes(proof.metadata) == result[10]
cfrag_bytes = bytes(cfrag)
result_frag = deserializer.functions.toCapsuleFrag(cfrag_bytes).call()
result_proof = deserializer.functions.toCorrectnessProofFromCapsuleFrag(cfrag_bytes).call()
assert cfrag_bytes == b''.join(result_frag) + b''.join(result_proof)
# TODO: Missing test for precomputed_data

View File

@ -861,7 +861,7 @@ def test_verifying_correctness(testerchain, token_economics, escrow, deploy_cont
# Set gas only for one check
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state)\
.transact({'gas': gas_to_save_state + 30000, 'gas_price': 0})
.transact({'gas': gas_to_save_state + 35000, 'gas_price': 0})
testerchain.wait_for_receipt(tx)
assert worklock.functions.nextBidderToCheck().call() == 1
@ -1051,7 +1051,7 @@ def test_force_refund(testerchain, token_economics, deploy_contract, worklock_fa
# But can verify only one of them
assert worklock.functions.nextBidderToCheck().call() == 0
tx = worklock.functions.verifyBiddingCorrectness(gas_to_save_state)\
.transact({'gas': gas_to_save_state + 30000, 'gas_price': 0})
.transact({'gas': gas_to_save_state + 35000, 'gas_price': 0})
testerchain.wait_for_receipt(tx)
assert worklock.functions.nextBidderToCheck().call() == 1

View File

@ -244,7 +244,7 @@ def test_enrico_web_character_control_encrypt_message(enrico_web_controller_test
assert 'signature' in response_data['result']
# Check that it serializes correctly.
assert UmbralMessageKit.from_bytes(b64decode(response_data['result']['message_kit']))
UmbralMessageKit.from_bytes(b64decode(response_data['result']['message_kit']))
# Send bad data to assert error return
response = enrico_web_controller_test_client.post('/encrypt_message', data=json.dumps({'bad': 'input'}))

View File

@ -18,9 +18,6 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
import pytest
import pytest_twisted
from twisted.internet import threads
from umbral import pre
from umbral.cfrags import CapsuleFrag
from umbral.kfrags import KFrag
from nucypher.crypto.kits import PolicyMessageKit
from nucypher.crypto.powers import DecryptingPower
@ -130,7 +127,7 @@ def test_bob_can_issue_a_work_order_to_a_specific_ursula(enacted_federated_polic
saves it and responds by re-encrypting and giving Bob a cFrag.
This is a multipart test; it shows proper relations between the Characters Ursula and Bob and also proper
interchange between a KFrag, Capsule, and CFrag object in the context of REST-driven proxy re-encryption.
interchange between a KeyFrag, Capsule, and CFrag object in the context of REST-driven proxy re-encryption.
"""
# We pick up our story with Bob already having followed the treasure map above, ie:
@ -146,12 +143,12 @@ def test_bob_can_issue_a_work_order_to_a_specific_ursula(enacted_federated_polic
# We'll test against just a single Ursula - here, we make a WorkOrder for just one.
# We can pass any number of capsules as args; here we pass just one.
capsule = capsule_side_channel().capsule
capsule.set_correctness_keys(delegating=enacted_federated_policy.public_key,
receiving=federated_bob.public_keys(DecryptingPower),
verifying=federated_alice.stamp.as_umbral_pubkey())
message_kit = capsule_side_channel()
message_kit.set_correctness_keys(delegating=enacted_federated_policy.public_key,
receiving=federated_bob.public_keys(DecryptingPower),
verifying=federated_alice.stamp.as_umbral_pubkey())
work_orders, _ = federated_bob.work_orders_for_capsules(
capsule,
message_kit.capsule,
treasure_map=treasure_map,
alice_verifying_key=federated_alice.stamp.as_umbral_pubkey(),
num_ursulas=1)
@ -164,7 +161,7 @@ def test_bob_can_issue_a_work_order_to_a_specific_ursula(enacted_federated_polic
# This time, we'll tell Bob to cache it.
retained_work_orders, _ = federated_bob.work_orders_for_capsules(
capsule,
message_kit.capsule,
treasure_map=treasure_map,
alice_verifying_key=federated_alice.stamp.as_umbral_pubkey(),
num_ursulas=1)
@ -174,7 +171,7 @@ def test_bob_can_issue_a_work_order_to_a_specific_ursula(enacted_federated_polic
assert work_order.completed is False
# **** RE-ENCRYPTION HAPPENS HERE! ****
_success, cfrags = federated_bob._reencrypt(work_order, retain_cfrags=True)
_success, cfrags = federated_bob._reencrypt(work_order, {message_kit.capsule: message_kit}, retain_cfrags=True)
# We only gave one Capsule, so we only got one cFrag.
assert len(cfrags) == 1
@ -184,7 +181,7 @@ def test_bob_can_issue_a_work_order_to_a_specific_ursula(enacted_federated_polic
assert work_order.completed
# Attach the CFrag to the Capsule.
capsule.attach_cfrag(the_cfrag)
message_kit.attach_cfrag(the_cfrag)
# Having received the cFrag, Bob also saved the WorkOrder as complete.
assert len(federated_bob._completed_work_orders.by_ursula[address]) == 1
@ -200,13 +197,6 @@ def test_bob_can_issue_a_work_order_to_a_specific_ursula(enacted_federated_polic
with ursula.datastore.describe(PolicyArrangement, work_order.arrangement_id.hex()) as policy_arrangement:
the_kfrag = policy_arrangement.kfrag
the_correct_cfrag = pre.reencrypt(the_kfrag, capsule)
# The first CFRAG_LENGTH_WITHOUT_PROOF bytes (ie, the cfrag proper, not the proof material), are the same:
assert bytes(the_cfrag)[:CapsuleFrag.expected_bytes_length()] == bytes(
the_correct_cfrag)[:CapsuleFrag.expected_bytes_length()] # It's the correct cfrag!
assert the_correct_cfrag.verify_correctness(capsule)
# Now we'll show that Ursula saved the correct WorkOrder.
with ursula.datastore.query_by(Workorder, filter_field='bob_verifying_key',
@ -225,7 +215,8 @@ def test_bob_can_use_cfrag_attached_to_completed_workorder(enacted_federated_pol
assert len(work_orders) == 1
# ...and it matched the last capsule that came through the side channel.
last_capsule_on_side_channel = capsule_side_channel.messages[-1][0].capsule
last_message_kit_on_side_channel = capsule_side_channel.messages[-1][0]
last_capsule_on_side_channel = last_message_kit_on_side_channel.capsule
old_work_order = work_orders[0][last_capsule_on_side_channel]
incomplete_work_orders, complete_work_orders = federated_bob.work_orders_for_capsules(
@ -244,8 +235,9 @@ def test_bob_can_use_cfrag_attached_to_completed_workorder(enacted_federated_pol
assert old_work_order.tasks[last_capsule_on_side_channel].cfrag
# As such, we will get TypeError if we try to get CFrags again.
message_kit_dict = {last_message_kit_on_side_channel.capsule: last_message_kit_on_side_channel}
with pytest.raises(TypeError):
federated_bob._reencrypt(new_work_order)
federated_bob._reencrypt(new_work_order, message_kit_dict)
def test_bob_remembers_that_he_has_cfrags_for_a_particular_capsule(enacted_federated_policy, federated_alice,
@ -256,7 +248,8 @@ def test_bob_remembers_that_he_has_cfrags_for_a_particular_capsule(enacted_feder
assert len(work_orders) == 1
# ...and it matched the last capsule that came through the side channel.
last_capsule_on_side_channel = capsule_side_channel.messages[-1][0].capsule
last_message_kit_on_side_channel = capsule_side_channel.messages[-1][0]
last_capsule_on_side_channel = last_message_kit_on_side_channel.capsule
first_and_only_work_order = work_orders[0]
list_of_one_capsule = list(first_and_only_work_order.keys())
capsule_as_saved = list_of_one_capsule[0]
@ -265,7 +258,7 @@ def test_bob_remembers_that_he_has_cfrags_for_a_particular_capsule(enacted_feder
assert len(federated_bob._completed_work_orders.by_capsule(last_capsule_on_side_channel)) == 1
# ...and he used it to obtain a CFrag from Ursula.
assert len(capsule_as_saved) == 1
assert len(last_message_kit_on_side_channel) == 1
# He can also get a dict of {Ursula:WorkOrder} by looking them up from the capsule.
work_orders_by_capsule = federated_bob._completed_work_orders.by_capsule(capsule_as_saved)
@ -294,14 +287,15 @@ def test_bob_remembers_that_he_has_cfrags_for_a_particular_capsule(enacted_feder
assert new_work_order != saved_work_order
# This WorkOrder has never been completed
_success, cfrags = federated_bob._reencrypt(new_work_order, retain_cfrags=True)
message_kit_dict = {last_capsule_on_side_channel: last_message_kit_on_side_channel}
_success, cfrags = federated_bob._reencrypt(new_work_order, message_kit_dict, retain_cfrags=True)
# Again: one Capsule, one cFrag.
assert len(cfrags) == 1
new_cfrag = cfrags[0]
# Attach the CFrag to the Capsule.
last_capsule_on_side_channel.attach_cfrag(new_cfrag)
last_message_kit_on_side_channel.attach_cfrag(new_cfrag)
def test_bob_gathers_and_combines(enacted_federated_policy, federated_bob, federated_alice, capsule_side_channel):
@ -317,12 +311,12 @@ def test_bob_gathers_and_combines(enacted_federated_policy, federated_bob, feder
assert len(federated_bob._completed_work_orders) < enacted_federated_policy.treasure_map.m
# Bob can't decrypt yet with just two CFrags. He needs to gather at least m.
with pytest.raises(pre.GenericUmbralError):
with pytest.raises(ValueError):
federated_bob.decrypt(the_message_kit)
number_left_to_collect = enacted_federated_policy.treasure_map.m - len(federated_bob._completed_work_orders)
the_message_kit.capsule.set_correctness_keys(
the_message_kit.set_correctness_keys(
delegating=the_data_source.policy_pubkey,
receiving=federated_bob.public_keys(DecryptingPower),
verifying=federated_alice.stamp.as_umbral_pubkey())
@ -334,11 +328,10 @@ def test_bob_gathers_and_combines(enacted_federated_policy, federated_bob, feder
num_ursulas=number_left_to_collect)
_id_of_yet_another_ursula, new_work_order = list(new_incomplete_work_orders.items())[0]
_success, cfrags = federated_bob._reencrypt(new_work_order)
_success, cfrags = federated_bob._reencrypt(new_work_order, {the_message_kit.capsule: the_message_kit})
cfrag = cfrags[0]
assert cfrag not in the_message_kit.capsule._attached_cfrags
the_message_kit.capsule.attach_cfrag(cfrags[0])
the_message_kit.attach_cfrag(cfrags[0])
# Now.
# At long last.
@ -528,7 +521,7 @@ def test_federated_bob_cannot_resume_retrieval_without_caching(federated_bob,
label=enacted_federated_policy.label)
# Since we weren't caching, there are no attached Cfrags.
assert len(the_message_kit.capsule) == 0
assert len(the_message_kit) == 0
# Now the remaining two Ursulas go down.
ursula9 = list(federated_ursulas)[8]
@ -587,7 +580,7 @@ def test_federated_retrieves_partially_then_finishes(federated_bob,
retain_cfrags=True)
# Since we were caching, there are now 2 attached cfrags.
assert len(the_message_kit.capsule) == 2
assert len(the_message_kit) == 2
# Now the remaining two Ursulas go down.
ursula9 = list(federated_ursulas)[8]
@ -631,7 +624,7 @@ def test_federated_retrieves_partially_then_finishes(federated_bob,
assert b"Welcome to flippering number 1." == delivered_cleartexts[0]
# Heck, even if we delete the attached CFrags, as might happen if we were loading the Capsule again from disk...
the_message_kit.capsule.clear_cfrags()
the_message_kit.clear_cfrags()
# ...we can still get the message with the network being down because Bob has the properly completed WorkOrders cached in state.
delivered_cleartexts = federated_bob.retrieve(the_message_kit,

View File

@ -161,7 +161,7 @@ def test_bob_joins_policy_and_retrieves(federated_alice,
assert _cleartexts == delivered_cleartexts # TODO: 892
# OK, but we imagine that the message_kit is fresh here.
message_kit.capsule.clear_cfrags()
message_kit.clear_cfrags()
with pytest.raises(Ursula.NotEnoughUrsulas):
_cleartexts = bob.retrieve(message_kit,
@ -216,7 +216,7 @@ def test_bob_retrieves_with_treasure_map(
label=enacted_federated_policy.label,
treasure_map=treasure_map)
message_kit.capsule.clear_cfrags() # Return back to a non re-encrypted state
message_kit.clear_cfrags() # Return back to a non re-encrypted state
# Serialized treasure map
text2 = federated_bob.retrieve(
message_kit,

View File

@ -18,7 +18,6 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
import datetime
import maya
import pytest
from umbral.kfrags import KFrag
from nucypher.characters.lawful import Enrico
from nucypher.crypto.api import keccak_digest

View File

@ -145,7 +145,8 @@ def test_key_validation(federated_bob):
with pytest.raises(SpecificationError) as e:
# lets just take a couple bytes off
BobKeyInputRequirer().load({'bobkey': "02f0cb3f3a33f16255d9b2586e6c56570aa07bbeb1157e169f1fb114ffb40037"})
assert "Unknown OpenSSL error." in str(e)
assert "Could not convert input for bobkey to an Umbral Key" in str(e)
assert "xpected 33 bytes, got 32" in str(e)
result = BobKeyInputRequirer().load(dict(bobkey=bytes(federated_bob.public_keys(DecryptingPower)).hex()))
assert isinstance(result['bobkey'], bytes)

View File

@ -21,7 +21,6 @@ from unittest.mock import Mock
import pytest
import tempfile
from constant_sorrow.constants import CERTIFICATE_NOT_SAVED, NO_KEYRING_ATTACHED
from umbral.keys import UmbralPrivateKey
from tests.constants import MOCK_IP_ADDRESS
from nucypher.blockchain.eth.actors import StakeHolder
@ -40,6 +39,8 @@ from nucypher.config.constants import TEMPORARY_DOMAIN
from nucypher.config.keyring import NucypherKeyring
from nucypher.config.base import CharacterConfiguration
from nucypher.config.storages import ForgetfulNodeStorage
from nucypher.crypto.umbral_adapter import SecretKey
# Main Cast
configurations = (AliceConfiguration, BobConfiguration, UrsulaConfiguration)
@ -128,7 +129,7 @@ def test_default_character_configuration_preservation(configuration_class, teste
# special case for rest_host & dev mode
# use keyring
keyring = Mock(spec=NucypherKeyring)
keyring.signing_public_key = UmbralPrivateKey.gen_key().get_pubkey()
keyring.signing_public_key = SecretKey.random().public_key()
character_config = configuration_class(checksum_address=fake_address,
domain=network,
rest_host=MOCK_IP_ADDRESS,

View File

@ -21,15 +21,14 @@ from unittest.mock import ANY
import pytest
from constant_sorrow.constants import FEDERATED_ADDRESS
from cryptography.hazmat.primitives.serialization.base import Encoding
from cryptography.hazmat.primitives.serialization import Encoding
from flask import Flask
from umbral.keys import UmbralPrivateKey
from umbral.signing import Signer
from nucypher.characters.lawful import Alice, Bob, Ursula
from nucypher.config.constants import TEMPORARY_DOMAIN
from nucypher.config.keyring import NucypherKeyring
from nucypher.crypto.powers import DecryptingPower, DelegatingPower
from nucypher.crypto.umbral_adapter import SecretKey, Signer
from nucypher.datastore.datastore import Datastore
from nucypher.network.server import TLSHostingPower, ProxyRESTServer
from nucypher.utilities.networking import LOOPBACK_ADDRESS
@ -63,8 +62,8 @@ def test_generate_alice_keyring(tmpdir):
delegating_power = keyring.derive_crypto_power(DelegatingPower)
delegating_pubkey = delegating_power.get_pubkey_from_label(label)
bob_pubkey = UmbralPrivateKey.gen_key().get_pubkey()
signer = Signer(UmbralPrivateKey.gen_key())
bob_pubkey = SecretKey.random().public_key()
signer = Signer(SecretKey.random())
delegating_pubkey_again, _kfrags = delegating_power.generate_kfrags(
bob_pubkey, signer, label, m=2, n=3
)

View File

@ -19,10 +19,10 @@ import pytest
import time
from datetime import datetime
from flask import Response
from umbral.keys import UmbralPublicKey
from unittest.mock import patch
from nucypher.characters.lawful import Ursula
from nucypher.crypto.umbral_adapter import PublicKey
from nucypher.datastore.base import RecordField
from nucypher.network.nodes import Teacher
from tests.markers import skip_on_circleci
@ -101,7 +101,7 @@ def test_alice_verifies_ursula_just_in_time(fleet_of_highperf_mocked_ursulas,
not_public_key_record_field = RecordField(NotAPublicKey, encode=bytes,
decode=NotAPublicKey.from_bytes)
_umbral_pubkey_from_bytes = UmbralPublicKey.from_bytes
_umbral_pubkey_from_bytes = PublicKey.from_bytes
def actual_random_key_instead(*args, **kwargs):
_previous_bytes = args[0]
@ -117,8 +117,8 @@ def test_alice_verifies_ursula_just_in_time(fleet_of_highperf_mocked_ursulas,
with NotARestApp.replace_route("receive_treasure_map", mock_receive_treasure_map):
with NotARestApp.replace_route("set_policy", mock_set_policy):
with patch('umbral.keys.UmbralPublicKey.__eq__', lambda *args, **kwargs: True):
with patch('umbral.keys.UmbralPublicKey.from_bytes',
with patch('umbral.PublicKey.__eq__', lambda *args, **kwargs: True):
with patch('umbral.PublicKey.from_bytes',
new=actual_random_key_instead):
with patch("nucypher.datastore.models.PolicyArrangement._alice_verifying_key",
new=not_public_key_record_field):
@ -170,7 +170,7 @@ def test_mass_treasure_map_placement(fleet_of_highperf_mocked_ursulas,
policy = _POLICY_PRESERVER.pop()
with patch('umbral.keys.UmbralPublicKey.__eq__', lambda *args, **kwargs: True), mock_metadata_validation:
with patch('umbral.PublicKey.__eq__', lambda *args, **kwargs: True), mock_metadata_validation:
started = datetime.now()

View File

@ -33,8 +33,7 @@ from web3.contract import Contract
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
from nucypher.blockchain.eth.signers.software import Web3Signer
from nucypher.crypto.powers import TransactingPower
from umbral.keys import UmbralPrivateKey
from umbral.signing import Signer
from unittest.mock import Mock
from zope.interface import provider
@ -47,6 +46,7 @@ from nucypher.blockchain.eth.agents import (
)
from nucypher.blockchain.eth.constants import NUCYPHER_CONTRACT_NAMES, NULL_ADDRESS
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
@ -130,8 +130,8 @@ class AnalyzeGas:
def mock_ursula(testerchain, account):
ursula_privkey = UmbralPrivateKey.gen_key()
ursula_stamp = SignatureStamp(verifying_key=ursula_privkey.pubkey,
ursula_privkey = SecretKey.random()
ursula_stamp = SignatureStamp(verifying_key=ursula_privkey.public_key(),
signer=Signer(ursula_privkey))
signed_stamp = testerchain.client.sign_message(account=account,

View File

@ -35,13 +35,13 @@ import shutil
import time
from eth_typing.evm import ChecksumAddress
from typing import Set, Optional, List, Tuple
from umbral.keys import UmbralPrivateKey
from web3.main import Web3
from web3.types import Wei
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
@ -94,10 +94,10 @@ HANDPICKED_URSULA_URIS: List[str] = [
def make_random_bob():
"""Generates a random ephemeral Bob instance."""
bob_verifying_secret = UmbralPrivateKey.gen_key()
bob_verifying_key = bob_verifying_secret.pubkey
decrypting_secret = UmbralPrivateKey.gen_key()
decrypting_key = decrypting_secret.pubkey
bob_verifying_secret = SecretKey.random()
bob_verifying_key = bob_verifying_secret.public_key()
decrypting_secret = SecretKey.random()
decrypting_key = decrypting_secret.public_key()
bob = Bob.from_public_keys(verifying_key=bob_verifying_key,
encrypting_key=decrypting_key,
federated_only=False)
@ -137,8 +137,8 @@ def collect(alice: Alice,
print(f'GRANT FAIL\n{e}')
else:
success += 1
policies[policy.public_key.hex()] = policy # track
print(f"PEK:{policy.public_key.hex()} | HRAC {policy.hrac.hex()}")
policies[bytes(policy.public_key).hex()] = policy # track
print(f"PEK:{bytes(policy.public_key).hex()} | HRAC {policy.hrac.hex()}")
# timeit
end = maya.now()

View File

@ -19,11 +19,9 @@ import tempfile
from contextlib import contextmanager
from unittest.mock import patch
from nucypher.crypto.umbral_adapter import PublicKey, Signature
from nucypher.network.server import make_rest_app
from tests.mock.serials import good_serials
from umbral.config import default_params
from umbral.keys import UmbralPublicKey
from umbral.signing import Signature
mock_cert_storage = patch("nucypher.config.storages.ForgetfulNodeStorage.store_node_certificate",
new=lambda *args, **kwargs: "this_might_normally_be_a_filepath")
@ -54,7 +52,7 @@ class NotAPublicKey:
_serial_bytes_length = 5
_serial = 10000
_umbral_pubkey_from_bytes = UmbralPublicKey.from_bytes
_umbral_pubkey_from_bytes = PublicKey.from_bytes
def _tick():
for serial in good_serials:
@ -91,22 +89,11 @@ class NotAPublicKey:
self.__dict__ = _umbral_pubkey.__dict__
self.__class__ = _umbral_pubkey.__class__
def to_cryptography_pubkey(self):
self.i_want_to_be_a_real_boy()
return self.to_cryptography_pubkey()
@property
def params(self):
# Holy heck, metamock hacking.
self.i_want_to_be_a_real_boy()
return self.params
def __eq__(self, other):
return bytes(self) == bytes(other)
class NotAPrivateKey:
params = default_params()
fake_signature = Signature.from_bytes(
b'@\xbfS&\x97\xb3\x9e\x9e\xd3\\j\x9f\x0e\x8fY\x0c\xbeS\x08d\x0b%s\xf6\x17\xe2\xb6\xcd\x95u\xaapON\xd9E\xb3\x10M\xe1\xf4u\x0bL\x99q\xd6\r\x8e_\xe5I\x1e\xe5\xa2\xcf\xe5\x8be_\x077Gz'
@ -115,12 +102,6 @@ class NotAPrivateKey:
def public_key(self):
return NotAPublicKey()
def get_pubkey(self, *args, **kwargs):
return self.public_key()
def to_cryptography_privkey(self, *args, **kwargs):
return self
def sign(self, *args, **kwargs):
return b'0D\x02 @\xbfS&\x97\xb3\x9e\x9e\xd3\\j\x9f\x0e\x8fY\x0c\xbeS\x08d\x0b%s\xf6\x17\xe2\xb6\xcd\x95u\xaap\x02 ON\xd9E\xb3\x10M\xe1\xf4u\x0bL\x99q\xd6\r\x8e_\xe5I\x1e\xe5\xa2\xcf\xe5\x8be_\x077Gz'
@ -242,13 +223,13 @@ def mock_secret_source(*args, **kwargs):
@contextmanager
def mock_pubkey_from_bytes(*args, **kwargs):
with patch('umbral.keys.UmbralPublicKey.from_bytes', NotAPublicKey.from_bytes):
with patch('umbral.PublicKey.from_bytes', NotAPublicKey.from_bytes):
yield
NotAPublicKey.reset()
mock_stamp_call = patch('nucypher.crypto.signing.SignatureStamp.__call__', new=NotAPrivateKey.stamp)
mock_signature_bytes = patch('umbral.signing.Signature.__bytes__', new=NotAPrivateKey.signature_bytes)
mock_signature_bytes = patch('umbral.Signature.__bytes__', new=NotAPrivateKey.signature_bytes)
def _determine_good_serials(start, end):

View File

@ -62,9 +62,8 @@ def test_actor_with_signing_power_can_sign():
signature = stamp_of_the_signer(message)
# ...or to get the signer's public key for verification purposes.
# (note: we use the private _der_encoded_bytes here to test directly against the API, instead of Character)
verification = api.verify_ecdsa(message, signature._der_encoded_bytes(),
stamp_of_the_signer.as_umbral_pubkey())
# (note: we verify directly using Umbral API, skipping Character)
verification = signature.verify(stamp_of_the_signer.as_umbral_pubkey(), message)
assert verification is True

View File

@ -19,7 +19,7 @@ from pathlib import Path
import pytest
from constant_sorrow.constants import FEDERATED_ADDRESS
from cryptography.hazmat.primitives.serialization.base import Encoding
from cryptography.hazmat.primitives.serialization import Encoding
from nucypher.config.keyring import (
_assemble_key_data,
@ -71,8 +71,8 @@ def test_keyring_restoration(tmpdir):
account = keyring.account
checksum_address = keyring.checksum_address
certificate_filepath = keyring.certificate_filepath
encrypting_public_key_hex = keyring.encrypting_public_key.hex()
signing_public_key_hex = keyring.signing_public_key.hex()
encrypting_public_key_hex = bytes(keyring.encrypting_public_key).hex()
signing_public_key_hex = bytes(keyring.signing_public_key).hex()
# tls power
tls_hosting_power = keyring.derive_crypto_power(power_class=TLSHostingPower, host=LOOPBACK_ADDRESS)
@ -83,12 +83,12 @@ def test_keyring_restoration(tmpdir):
# decrypting power
decrypting_power = keyring.derive_crypto_power(power_class=DecryptingPower)
decrypting_power_public_key_hex = decrypting_power.public_key().hex()
decrypting_power_public_key_hex = bytes(decrypting_power.public_key()).hex()
decrypting_power_fingerprint = decrypting_power.keypair.fingerprint()
# signing power
signing_power = keyring.derive_crypto_power(power_class=SigningPower)
signing_power_public_key_hex = signing_power.public_key().hex()
signing_power_public_key_hex = bytes(signing_power.public_key()).hex()
signing_power_fingerprint = signing_power.keypair.fingerprint()
# get rid of object, but not persistent data
@ -100,8 +100,8 @@ def test_keyring_restoration(tmpdir):
assert restored_keyring.account == account
assert restored_keyring.checksum_address == checksum_address
assert restored_keyring.certificate_filepath == certificate_filepath
assert restored_keyring.encrypting_public_key.hex() == encrypting_public_key_hex
assert restored_keyring.signing_public_key.hex() == signing_public_key_hex
assert bytes(restored_keyring.encrypting_public_key).hex() == encrypting_public_key_hex
assert bytes(restored_keyring.signing_public_key).hex() == signing_public_key_hex
# tls power
restored_tls_hosting_power = restored_keyring.derive_crypto_power(power_class=TLSHostingPower,
@ -113,12 +113,12 @@ def test_keyring_restoration(tmpdir):
# decrypting power
restored_decrypting_power = restored_keyring.derive_crypto_power(power_class=DecryptingPower)
assert restored_decrypting_power.public_key().hex() == decrypting_power_public_key_hex
assert bytes(restored_decrypting_power.public_key()).hex() == decrypting_power_public_key_hex
assert restored_decrypting_power.keypair.fingerprint() == decrypting_power_fingerprint
# signing power
restored_signing_power = restored_keyring.derive_crypto_power(power_class=SigningPower)
assert restored_signing_power.public_key().hex() == signing_power_public_key_hex
assert bytes(restored_signing_power.public_key()).hex() == signing_power_public_key_hex
assert restored_signing_power.keypair.fingerprint() == signing_power_fingerprint

View File

@ -18,49 +18,46 @@ import base64
import sha3
from constant_sorrow.constants import PUBLIC_ONLY
from umbral.keys import UmbralPrivateKey
from nucypher.crypto import keypairs
from nucypher.crypto.umbral_adapter import SecretKey
def test_gen_keypair_if_needed():
new_dec_keypair = keypairs.DecryptingKeypair()
assert new_dec_keypair._privkey is not None
assert new_dec_keypair.pubkey is not None
assert new_dec_keypair.pubkey == new_dec_keypair._privkey.get_pubkey()
assert new_dec_keypair.pubkey == new_dec_keypair._privkey.public_key()
new_sig_keypair = keypairs.SigningKeypair()
assert new_sig_keypair._privkey is not None
assert new_sig_keypair.pubkey is not None
assert new_sig_keypair.pubkey == new_sig_keypair._privkey.get_pubkey()
assert new_sig_keypair.pubkey == new_sig_keypair._privkey.public_key()
def test_keypair_with_umbral_keys():
umbral_privkey = UmbralPrivateKey.gen_key()
umbral_pubkey = umbral_privkey.get_pubkey()
umbral_privkey = SecretKey.random()
umbral_pubkey = umbral_privkey.public_key()
new_keypair_from_priv = keypairs.Keypair(umbral_privkey)
assert new_keypair_from_priv._privkey.bn_key.to_bytes() == umbral_privkey.bn_key.to_bytes()
assert new_keypair_from_priv.pubkey.to_bytes() == umbral_pubkey.to_bytes()
assert new_keypair_from_priv._privkey == umbral_privkey
assert bytes(new_keypair_from_priv.pubkey) == bytes(umbral_pubkey)
new_keypair_from_pub = keypairs.Keypair(public_key=umbral_pubkey)
assert new_keypair_from_pub.pubkey.to_bytes() == umbral_pubkey.to_bytes()
assert bytes(new_keypair_from_pub.pubkey) == bytes(umbral_pubkey)
assert new_keypair_from_pub._privkey == PUBLIC_ONLY
def test_keypair_serialization():
umbral_pubkey = UmbralPrivateKey.gen_key().get_pubkey()
umbral_pubkey = SecretKey.random().public_key()
new_keypair = keypairs.Keypair(public_key=umbral_pubkey)
pubkey_bytes = new_keypair.serialize_pubkey()
pubkey_bytes = bytes(new_keypair.pubkey)
assert pubkey_bytes == bytes(umbral_pubkey)
pubkey_b64 = new_keypair.serialize_pubkey(as_b64=True)
assert pubkey_b64 == base64.urlsafe_b64encode(umbral_pubkey.to_bytes())
def test_keypair_fingerprint():
umbral_pubkey = UmbralPrivateKey.gen_key().get_pubkey()
umbral_pubkey = SecretKey.random().public_key()
new_keypair = keypairs.Keypair(public_key=umbral_pubkey)
fingerprint = new_keypair.fingerprint()
@ -71,15 +68,15 @@ def test_keypair_fingerprint():
def test_signing():
umbral_privkey = UmbralPrivateKey.gen_key()
umbral_privkey = SecretKey.random()
sig_keypair = keypairs.SigningKeypair(umbral_privkey)
msg = b'peace at dawn'
signature = sig_keypair.sign(msg)
assert signature.verify(msg, sig_keypair.pubkey)
assert signature.verify(sig_keypair.pubkey, msg)
bad_msg = b'bad message'
assert not signature.verify(bad_msg, sig_keypair.pubkey)
assert not signature.verify(sig_keypair.pubkey, bad_msg)
# TODO: Add test for DecryptingKeypair.decrypt

View File

@ -21,38 +21,7 @@ from nucypher.characters.lawful import Enrico
from nucypher.crypto.api import secure_random
from nucypher.crypto.kits import UmbralMessageKit
from nucypher.crypto.signing import Signature
def test_split_two_signatures():
"""
We make two random Signatures and concat them. Then split them and show that we got the proper result.
"""
sig1 = Signature.from_bytes(secure_random(64))
sig2 = Signature.from_bytes(secure_random(64))
sigs_concatted = sig1 + sig2
two_signature_splitter = BytestringSplitter(Signature, Signature)
rebuilt_sig1, rebuilt_sig2 = two_signature_splitter(sigs_concatted)
assert (sig1, sig2) == (rebuilt_sig1, rebuilt_sig2)
def test_split_signature_from_arbitrary_bytes():
how_many_bytes = 10
signature = Signature.from_bytes(secure_random(64))
some_bytes = secure_random(how_many_bytes)
splitter = BytestringSplitter(Signature, (bytes, how_many_bytes))
rebuilt_signature, rebuilt_bytes = splitter(signature + some_bytes)
def test_trying_to_extract_too_many_bytes_raises_typeerror():
how_many_bytes = 10
too_many_bytes = 11
signature = Signature.from_bytes(secure_random(64))
some_bytes = secure_random(how_many_bytes)
splitter = BytestringSplitter(Signature, (bytes, too_many_bytes))
with pytest.raises(BytestringSplittingError):
rebuilt_signature, rebuilt_bytes = splitter(signature + some_bytes, return_remainder=True)
from nucypher.crypto.splitters import signature_splitter
def test_message_kit_serialization_via_enrico(enacted_federated_policy, federated_alice):

View File

@ -19,11 +19,10 @@ import os
import pytest
from bytestring_splitter import VariableLengthBytestring
from eth_utils import to_canonical_address
from umbral.keys import UmbralPrivateKey
from umbral.signing import Signer
from nucypher.blockchain.eth.constants import ETH_HASH_BYTE_LENGTH, LENGTH_ECDSA_SIGNATURE_WITH_RECOVERY
from nucypher.crypto.signing import SignatureStamp, InvalidSignature
from nucypher.crypto.umbral_adapter import SecretKey, Signer
from nucypher.crypto.utils import canonical_address_from_umbral_key
from nucypher.policy.collections import WorkOrder
from nucypher.policy.policies import Arrangement
@ -32,8 +31,8 @@ from nucypher.policy.policies import Arrangement
@pytest.fixture(scope="function")
def ursula(mocker):
identity_evidence = os.urandom(LENGTH_ECDSA_SIGNATURE_WITH_RECOVERY)
ursula_privkey = UmbralPrivateKey.gen_key()
ursula_stamp = SignatureStamp(verifying_key=ursula_privkey.pubkey,
ursula_privkey = SecretKey.random()
ursula_stamp = SignatureStamp(verifying_key=ursula_privkey.public_key(),
signer=Signer(ursula_privkey))
ursula = mocker.Mock(stamp=ursula_stamp, decentralized_identity_evidence=identity_evidence)
ursula.mature = lambda: True
@ -46,7 +45,7 @@ def test_pre_task(mock_ursula_reencrypts, ursula, get_random_checksum_address):
task = mock_ursula_reencrypts(ursula)
cfrag = task.cfrag
capsule = task.capsule
capsule_bytes = capsule.to_bytes()
capsule_bytes = bytes(capsule)
signature = ursula.stamp(capsule_bytes)
@ -62,7 +61,7 @@ def test_pre_task(mock_ursula_reencrypts, ursula, get_random_checksum_address):
assert signature == deserialized_task.signature
# Attaching cfrags to the task
cfrag_bytes = bytes(VariableLengthBytestring(cfrag.to_bytes()))
cfrag_bytes = bytes(cfrag)
cfrag_signature = ursula.stamp(cfrag_bytes)
task.attach_work_result(cfrag, cfrag_signature)
@ -128,8 +127,8 @@ def test_work_order_with_multiple_capsules(mock_ursula_reencrypts,
assert work_order.tasks[capsule].capsule == capsule
task = WorkOrder.PRETask(capsule, signature=None)
specification = task.get_specification(ursula.stamp, alice_address, blockhash, identity_evidence)
assert work_order.tasks[capsule].signature.verify(specification, bob_verifying_pubkey)
assert work_order.receipt_signature.verify(receipt_input, bob_verifying_pubkey)
assert work_order.tasks[capsule].signature.verify(bob_verifying_pubkey, specification)
assert work_order.receipt_signature.verify(bob_verifying_pubkey, receipt_input)
assert work_order.ursula == ursula
assert work_order.blockhash == blockhash
assert not work_order.completed
@ -169,11 +168,6 @@ def test_work_order_with_multiple_capsules(mock_ursula_reencrypts,
# Testing WorkOrder.complete()
# Trying to complete this work order fails because the current task signatures are different from the ones created
# when the re-encryption fixture ran. This is an expected effect of using that fixture, which makes the test simpler
with pytest.raises(InvalidSignature, match="Invalid metadata"):
work_order.complete(list(zip(cfrags, cfrag_signatures)))
# Let's use the original task signatures in our WorkOrder, instead
for capsule, task_signature in zip(capsules, signatures):
work_order.tasks[capsule].signature = task_signature

View File

@ -20,15 +20,13 @@ import contextlib
import socket
from cryptography.x509 import Certificate
from typing import Iterable, List, Optional, Set
from umbral import pre
from umbral.keys import UmbralPrivateKey
from umbral.signing import Signer
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 nucypher.crypto.utils import canonical_address_from_umbral_key
from nucypher.policy.collections import WorkOrder
from tests.constants import NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK
@ -165,21 +163,20 @@ MOCK_URSULA_STARTING_PORT = 51000 # select_test_port()
def _mock_ursula_reencrypts(ursula):
delegating_privkey = UmbralPrivateKey.gen_key()
_symmetric_key, capsule = pre._encapsulate(delegating_privkey.get_pubkey())
signing_privkey = UmbralPrivateKey.gen_key()
signing_pubkey = signing_privkey.get_pubkey()
delegating_privkey = SecretKey.random()
capsule, _ciphertext = encrypt(delegating_privkey.public_key(), b'unused')
signing_privkey = SecretKey.random()
signing_pubkey = signing_privkey.public_key()
signer = Signer(signing_privkey)
priv_key_bob = UmbralPrivateKey.gen_key()
pub_key_bob = priv_key_bob.get_pubkey()
kfrags = pre.generate_kfrags(delegating_privkey=delegating_privkey,
signer=signer,
receiving_pubkey=pub_key_bob,
threshold=2,
N=4,
sign_delegating_key=False,
sign_receiving_key=False)
capsule.set_correctness_keys(delegating_privkey.get_pubkey(), pub_key_bob, signing_pubkey)
priv_key_bob = SecretKey.random()
pub_key_bob = priv_key_bob.public_key()
kfrags = generate_kfrags(delegating_sk=delegating_privkey,
signer=signer,
receiving_pk=pub_key_bob,
threshold=2,
num_kfrags=4,
sign_delegating_key=False,
sign_receiving_key=False)
ursula_pubkey = ursula.stamp.as_umbral_pubkey()
@ -193,11 +190,9 @@ def _mock_ursula_reencrypts(ursula):
blockhash))
bobs_signer = Signer(priv_key_bob)
task_signature = bytes(bobs_signer(specification))
task_signature = bytes(bobs_signer.sign(specification))
metadata = bytes(ursula.stamp(task_signature))
cfrag = pre.reencrypt(kfrags[0], capsule, metadata=metadata)
cfrag = reencrypt(capsule, kfrags[0])
cfrag_signature = ursula.stamp(bytes(cfrag))
bob = Bob.from_public_keys(verifying_key=pub_key_bob)