mirror of https://github.com/nucypher/nucypher.git
commit
1a997cf129
3
Pipfile
3
Pipfile
|
@ -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 = "*"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
|
|
|
@ -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")))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Switch to PyUmbral 0.2 and adjust its usage according to the changed API.
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.')
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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}")
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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}'")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -27,7 +27,3 @@ BLAKE2B_DIGEST_LENGTH = 64
|
|||
# Hashes
|
||||
SHA256 = hashes.SHA256()
|
||||
BLAKE2B = hashes.BLAKE2b(64)
|
||||
|
||||
# SECP256K1
|
||||
CAPSULE_LENGTH = 98
|
||||
PUBLIC_KEY_LENGTH = 33
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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``.
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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 *
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
|
|
100
requirements.txt
100
requirements.txt
|
@ -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'
|
||||
|
|
|
@ -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'}))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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'}))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue