def test_generate_alice_keystore(temp_dir_path):

    keystore = Keystore.generate(password=INSECURE_DEVELOPMENT_PASSWORD,
                                 keystore_dir=temp_dir_path)

    with pytest.raises(Keystore.Locked):
        _dec_keypair = keystore.derive_crypto_power(DecryptingPower).keypair

    keystore.unlock(password=INSECURE_DEVELOPMENT_PASSWORD)
    assert keystore.derive_crypto_power(DecryptingPower).keypair

    label = b'test'

    delegating_power = keystore.derive_crypto_power(DelegatingPower)
    delegating_pubkey = delegating_power.get_pubkey_from_label(label)

    bob_pubkey = SecretKey.random().public_key()
    signer = Signer(SecretKey.random())
    delegating_pubkey_again, _kfrags = delegating_power.generate_kfrags(
        bob_pubkey, signer, label, threshold=2, shares=3)

    assert delegating_pubkey == delegating_pubkey_again

    another_delegating_power = keystore.derive_crypto_power(DelegatingPower)
    another_delegating_pubkey = another_delegating_power.get_pubkey_from_label(
        label)

    assert delegating_pubkey == another_delegating_pubkey
Example #2
0
def test_verify(testerchain, signature_verifier):
    message = os.urandom(100)

    # Generate Umbral key
    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.sign(message)

    # Get recovery id (v) before using contract
    v = get_signature_recovery_value(message, signature, umbral_pubkey)
    recoverable_signature = bytes(signature) + v

    # Verify signature
    assert signature_verifier.functions.verify(message, recoverable_signature,
                                               umbral_pubkey_bytes,
                                               ALGORITHM_SHA256).call()

    # Verify signature using wrong key
    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,
        ALGORITHM_SHA256).call()
def make_random_bob():
    """Generates a random ephemeral Bob instance."""
    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)
    print(f'Created BOB - {bytes(bob.stamp).hex()}')
    return bob
Example #4
0
def test_create_bob_card_inline(click_runner, alice_verifying_key, alice_nickname):
    command = ('contacts', 'create',
               '--type', 'b',
               '--verifying-key',  bytes(SecretKey.random().public_key()).hex(),
               '--encrypting-key', bytes(SecretKey.random().public_key()).hex(),
               '--nickname', 'hans')

    assert len(list(Card.CARD_DIR.iterdir())) == 3
    result = click_runner.invoke(nucypher_cli, command, catch_exceptions=False)
    assert result.exit_code == 0, result.output
    assert 'Saved new card' in result.output
    assert len(list(Card.CARD_DIR.iterdir())) == 4
Example #5
0
 def metadata(self):
     signer = Signer(SecretKey.random())
     payload = NodeMetadataPayload(
         canonical_address=self.canonical_address,
         domain=':dummy:',
         timestamp_epoch=0,
         decentralized_identity_evidence=b'\x00' *
         LENGTH_ECDSA_SIGNATURE_WITH_RECOVERY,
         verifying_key=signer.verifying_key(),
         encrypting_key=SecretKey.random().public_key(),
         certificate_bytes=b'not a certificate',
         host='127.0.0.1',
         port=1111,
     )
     return NodeMetadata(signer=signer, payload=payload)
Example #6
0
def test_alice_can_learn_about_a_whole_bunch_of_ursulas(highperf_mocked_alice):
    # During the fixture execution, Alice verified one node.
    # TODO: Consider changing this - #1449
    assert VerificationTracker.node_verifications == 1

    _teacher = highperf_mocked_alice.current_teacher_node()

    # Ursulas in the fleet have mocked keys,
    # but we need the teacher to be able to sign the MetadataResponse.
    signer = Signer(SecretKey.random())
    _teacher._stamp = SignatureStamp(verifying_key=signer.verifying_key(),
                                     signer=signer)

    actual_ursula = MOCK_KNOWN_URSULAS_CACHE[_teacher.rest_interface.port]

    # A quick setup so that the bytes casting of Ursulas (on what in the real world will be the remote node)
    # doesn't take up all the time.
    _teacher_known_nodes_bytestring = actual_ursula.bytestring_of_known_nodes()
    actual_ursula.bytestring_of_known_nodes = lambda *args, **kwargs: _teacher_known_nodes_bytestring  # TODO: Formalize this?  #1537

    with mock_cert_storage, mock_cert_loading, mock_verify_node, mock_message_verification, mock_metadata_validation:
        started = time.time()
        highperf_mocked_alice.block_until_number_of_known_nodes_is(
            4000, learn_on_this_thread=True)
        ended = time.time()
        elapsed = ended - started

    # TODO: probably can be brought down a lot when the core is moved to Rust
    assert elapsed < 6  # 6 seconds is still a little long to discover 4000 out of 5000 nodes, but before starting the optimization that went with this test, this operation took about 18 minutes on jMyles' laptop.
    assert VerificationTracker.node_verifications == 1  # We have only verified the first Ursula.
    assert sum(
        isinstance(u, Ursula) for u in highperf_mocked_alice.known_nodes
    ) < 20  # We haven't instantiated many Ursulas.
    VerificationTracker.node_verifications = 0  # Cleanup
Example #7
0
def test_default_character_configuration_preservation(
        configuration_class, testerchain, test_registry_source_manager,
        tmpdir):

    configuration_class.DEFAULT_CONFIG_ROOT = Path('/tmp')
    fake_address = '0xdeadbeef'
    network = TEMPORARY_DOMAIN

    expected_filename = f'{configuration_class.NAME}.{configuration_class._CONFIG_FILE_EXTENSION}'
    generated_filename = configuration_class.generate_filename()
    assert generated_filename == expected_filename
    expected_filepath = Path('/', 'tmp', generated_filename)

    if expected_filepath.exists():
        expected_filepath.unlink()
    assert not expected_filepath.exists()

    if configuration_class == StakeHolderConfiguration:
        # special case for defaults
        character_config = StakeHolderConfiguration(
            provider_uri=testerchain.provider_uri, domain=network)

    elif configuration_class == UrsulaConfiguration:
        # special case for rest_host & dev mode
        # use keystore
        keystore = Keystore.generate(password=INSECURE_DEVELOPMENT_PASSWORD,
                                     keystore_dir=tmpdir)
        keystore.signing_public_key = SecretKey.random().public_key()
        character_config = configuration_class(checksum_address=fake_address,
                                               domain=network,
                                               rest_host=MOCK_IP_ADDRESS,
                                               keystore=keystore)

    else:
        character_config = configuration_class(checksum_address=fake_address,
                                               domain=network)

    generated_filepath = character_config.generate_filepath()
    assert generated_filepath == expected_filepath

    written_filepath = character_config.to_configuration_file()
    assert written_filepath == expected_filepath
    assert written_filepath.exists()

    try:
        # Read
        with open(character_config.filepath, 'r') as f:
            contents = f.read()

        # Restore from JSON file
        restored_configuration = configuration_class.from_configuration_file()
        assert character_config.serialize(
        ) == restored_configuration.serialize()

        # File still exists after reading
        assert written_filepath.exists()

    finally:
        if expected_filepath.exists():
            expected_filepath.unlink()
Example #8
0
def test_key():
    field = Key()

    umbral_pub_key = SecretKey.random().public_key()
    other_umbral_pub_key = SecretKey.random().public_key()

    serialized = field._serialize(value=umbral_pub_key, attr=None, obj=None)
    assert serialized == bytes(umbral_pub_key).hex()
    assert serialized != bytes(other_umbral_pub_key).hex()

    deserialized = field._deserialize(value=serialized, attr=None, data=None)
    assert deserialized == umbral_pub_key
    assert deserialized != other_umbral_pub_key

    with pytest.raises(InvalidInputData):
        field._deserialize(value=b"PublicKey".hex(), attr=None, data=None)
Example #9
0
def fragments():
    delegating_privkey = SecretKey.random()
    delegating_pubkey = delegating_privkey.public_key()
    signing_privkey = SecretKey.random()
    signer = Signer(signing_privkey)
    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,
                             shares=4,
                             sign_delegating_key=False,
                             sign_receiving_key=False)

    capsule = MessageKit(delegating_pubkey, b'unused').capsule
    cfrag = reencrypt(capsule, kfrags[0])
    return capsule, cfrag
Example #10
0
def test_enrico_control_starts(click_runner):
    policy_encrypting_key = bytes(SecretKey.random().public_key()).hex()
    run_args = ('enrico', 'run',
                '--policy-encrypting-key', policy_encrypting_key,
                '--dry-run')

    result = click_runner.invoke(nucypher_cli, run_args, catch_exceptions=False)
    assert result.exit_code == 0
    assert policy_encrypting_key in result.output
    def metadata(self):
        signer = Signer(SecretKey.random())

        # A dummy signature with the recovery byte
        dummy_signature = bytes(signer.sign(b'whatever')) + b'\x00'

        payload = NodeMetadataPayload(
            staking_provider_address=self.canonical_address,
            domain=':dummy:',
            timestamp_epoch=0,
            operator_signature=dummy_signature,
            verifying_key=signer.verifying_key(),
            encrypting_key=SecretKey.random().public_key(),
            certificate_der=b'not a certificate',
            host=MOCK_IP_ADDRESS,
            port=MOCK_PORT,
        )
        return NodeMetadata(signer=signer, payload=payload)
Example #12
0
def test_enrico_encrypt(click_runner):
    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)
    result = click_runner.invoke(nucypher_cli, encrypt_args, catch_exceptions=False)

    assert result.exit_code == 0
    assert policy_encrypting_key in result.output
    assert "message_kit" in result.output
Example #13
0
def mock_ursula(testerchain, account):
    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,
                                                   message=bytes(ursula_stamp))

    ursula = Mock(stamp=ursula_stamp, decentralized_identity_evidence=signed_stamp)
    return ursula
Example #14
0
def test_address(testerchain, signature_verifier):
    # Generate Umbral key and extract "address" from the public key
    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()
    assert signer_address == to_normalized_address(result_address)
Example #15
0
def generate_doctor_keys():
    enc_privkey = SecretKey.random()
    sig_privkey = SecretKey.random()

    doctor_privkeys = {
        'enc': enc_privkey.to_secret_bytes().hex(),
        'sig': sig_privkey.to_secret_bytes().hex(),
    }

    with open(DOCTOR_PRIVATE_JSON, 'w') as f:
        json.dump(doctor_privkeys, f)

    enc_pubkey = enc_privkey.public_key()
    sig_pubkey = sig_privkey.public_key()
    doctor_pubkeys = {
        'enc': bytes(enc_pubkey).hex(),
        'sig': bytes(sig_pubkey).hex()
    }
    with open(DOCTOR_PUBLIC_JSON, 'w') as f:
        json.dump(doctor_pubkeys, f)
Example #16
0
def test_retrieval_kit_field(get_random_checksum_address):
    field = RetrievalKit()

    def run_tests_on_kit(kit: RetrievalKitClass):
        serialized = field._serialize(value=kit, attr=None, obj=None)
        assert serialized == b64encode(bytes(kit)).decode()

        deserialized = field._deserialize(value=serialized,
                                          attr=None,
                                          data=None)
        assert isinstance(deserialized, RetrievalKitClass)
        assert deserialized.capsule == kit.capsule
        assert deserialized.queried_addresses == kit.queried_addresses

    # kit with list of ursulas
    encrypting_key = SecretKey.random().public_key()
    capsule = MessageKit(encrypting_key,
                         b'testing retrieval kit with 2 ursulas').capsule
    ursulas = [get_random_checksum_address(), get_random_checksum_address()]
    run_tests_on_kit(kit=RetrievalKitClass(
        capsule, {to_canonical_address(ursula)
                  for ursula in ursulas}))

    # kit with no ursulas
    encrypting_key = SecretKey.random().public_key()
    capsule = MessageKit(encrypting_key,
                         b'testing retrieval kit with no ursulas').capsule
    run_tests_on_kit(kit=RetrievalKitClass(capsule, set()))

    with pytest.raises(InvalidInputData):
        field._deserialize(value=b"non_base_64_data", attr=None, data=None)

    with pytest.raises(InvalidInputData):
        field._deserialize(
            value=b64encode(b"invalid_retrieval_kit_bytes").decode(),
            attr=None,
            data=None)
Example #17
0
def test_recover(testerchain, signature_verifier):
    message = os.urandom(100)

    # Prepare message hash
    hash_ctx = hashes.Hash(hashes.SHA256(), backend=backend)
    hash_ctx.update(message)
    message_hash = hash_ctx.finalize()

    # Generate Umbral key and extract "address" from the public key
    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.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
    # Only the correct v will match the correct public key
    v = get_signature_recovery_value(message, signature, umbral_pubkey)
    recoverable_signature = bytes(signature) + v

    # Check recovery method in the contract
    assert signer_address == to_normalized_address(
        signature_verifier.functions.recover(message_hash,
                                             recoverable_signature).call())

    # Also numbers 27 and 28 can be used for v
    recoverable_signature = recoverable_signature[:-1] + bytes(
        [recoverable_signature[-1] + 27])
    assert signer_address == to_normalized_address(
        signature_verifier.functions.recover(message_hash,
                                             recoverable_signature).call())

    # Only number 0,1,27,28 are supported for v
    recoverable_signature = bytes(signature) + bytes([2])
    with pytest.raises((TransactionFailed, ValueError)):
        signature_verifier.functions.recover(message_hash,
                                             recoverable_signature).call()

    # Signature must include r, s and v
    recoverable_signature = bytes(signature)
    with pytest.raises((TransactionFailed, ValueError)):
        signature_verifier.functions.recover(message_hash,
                                             recoverable_signature).call()
Example #18
0
def test_verify_eip191(testerchain, signature_verifier):
    message = os.urandom(100)

    # Generate Umbral key
    umbral_privkey = SecretKey.random()
    umbral_pubkey = umbral_privkey.public_key()
    umbral_pubkey_bytes = pubkey_as_uncompressed_bytes(umbral_pubkey)

    #
    # Check EIP191 signatures: Version E
    #

    # Produce EIP191 signature (version E)
    signable_message = encode_defunct(primitive=message)
    signature = Account.sign_message(
        signable_message=signable_message,
        private_key=umbral_privkey.to_secret_bytes())
    signature = bytes(signature.signature)

    # Off-chain verify, just in case
    checksum_address = to_checksum_address(
        canonical_address_from_umbral_key(umbral_pubkey))
    assert verify_eip_191(address=checksum_address,
                          message=message,
                          signature=signature)

    # Verify signature on-chain
    version_E = b'E'
    assert signature_verifier.functions.verifyEIP191(message, signature,
                                                     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, version_0).call()

    # Check that the hash-based method also works independently
    hash = signature_verifier.functions.hashEIP191(message, version_E).call()
    eip191_header = "\x19Ethereum Signed Message:\n" + str(len(message))
    assert hash == keccak_digest(eip191_header.encode() + message)

    address = signature_verifier.functions.recover(hash, signature).call()
    assert address == checksum_address

    #
    # Check EIP191 signatures: Version 0
    #

    # Produce EIP191 signature (version 0)
    validator = to_canonical_address(signature_verifier.address)
    signable_message = SignableMessage(version=HexBytes(version_0),
                                       header=HexBytes(validator),
                                       body=HexBytes(message))
    signature = Account.sign_message(
        signable_message=signable_message,
        private_key=umbral_privkey.to_secret_bytes())
    signature = bytes(signature.signature)

    # Off-chain verify, just in case
    checksum_address = to_checksum_address(
        canonical_address_from_umbral_key(umbral_pubkey))
    assert checksum_address == Account.recover_message(
        signable_message=signable_message, signature=signature)

    # On chain verify signature
    assert signature_verifier.functions.verifyEIP191(message, signature,
                                                     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, version_E).call()

    # Check that the hash-based method also works independently
    hash = signature_verifier.functions.hashEIP191(message, version_0).call()
    eip191_header = b"\x19\x00" + validator
    assert hash == keccak_digest(eip191_header + message)

    address = signature_verifier.functions.recover(hash, signature).call()
    assert address == checksum_address
Example #19
0
def bob_verifying_key():
    return bytes(SecretKey.random().public_key()).hex()
Example #20
0
def bob_encrypting_key():
    return bytes(SecretKey.random().public_key()).hex()
def test_alice_get_ursulas_schema(get_random_checksum_address):
    #
    # Input i.e. load
    #

    # no args
    with pytest.raises(InvalidInputData):
        AliceGetUrsulas().load({})

    quantity = 10
    required_data = {
        'quantity': quantity,
        'duration_periods': 4,
    }

    # required args
    AliceGetUrsulas().load(required_data)

    # missing required args
    updated_data = {k: v for k, v in required_data.items() if k != 'quantity'}
    with pytest.raises(InvalidInputData):
        AliceGetUrsulas().load(updated_data)

    updated_data = {
        k: v
        for k, v in required_data.items() if k != 'duration_periods'
    }
    with pytest.raises(InvalidInputData):
        AliceGetUrsulas().load(updated_data)

    # optional components

    # only exclude
    updated_data = dict(required_data)
    exclude_ursulas = []
    for i in range(2):
        exclude_ursulas.append(get_random_checksum_address())
    updated_data['exclude_ursulas'] = exclude_ursulas
    AliceGetUrsulas().load(updated_data)

    # only include
    updated_data = dict(required_data)
    include_ursulas = []
    for i in range(3):
        include_ursulas.append(get_random_checksum_address())
    updated_data['include_ursulas'] = include_ursulas
    AliceGetUrsulas().load(updated_data)

    # both exclude and include
    updated_data = dict(required_data)
    updated_data['exclude_ursulas'] = exclude_ursulas
    updated_data['include_ursulas'] = include_ursulas
    AliceGetUrsulas().load(updated_data)

    # list input formatted as ',' separated strings
    updated_data = dict(required_data)
    updated_data['exclude_ursulas'] = ','.join(exclude_ursulas)
    updated_data['include_ursulas'] = ','.join(include_ursulas)
    data = AliceGetUrsulas().load(updated_data)
    assert data['exclude_ursulas'] == exclude_ursulas
    assert data['include_ursulas'] == include_ursulas

    # single value as string cast to list
    updated_data = dict(required_data)
    updated_data['exclude_ursulas'] = exclude_ursulas[0]
    updated_data['include_ursulas'] = include_ursulas[0]
    data = AliceGetUrsulas().load(updated_data)
    assert data['exclude_ursulas'] == [exclude_ursulas[0]]
    assert data['include_ursulas'] == [include_ursulas[0]]

    # invalid include entry
    updated_data = dict(required_data)
    updated_data['exclude_ursulas'] = exclude_ursulas
    updated_data['include_ursulas'] = list(
        include_ursulas)  # make copy to modify
    updated_data['include_ursulas'].append("0xdeadbeef")
    with pytest.raises(InvalidInputData):
        AliceGetUrsulas().load(updated_data)

    # invalid exclude entry
    updated_data = dict(required_data)
    updated_data['exclude_ursulas'] = list(
        exclude_ursulas)  # make copy to modify
    updated_data['exclude_ursulas'].append("0xdeadbeef")
    updated_data['include_ursulas'] = include_ursulas
    with pytest.raises(InvalidInputData):
        AliceGetUrsulas().load(updated_data)

    # too many ursulas to include
    updated_data = dict(required_data)
    too_many_ursulas_to_include = []
    while len(too_many_ursulas_to_include) <= quantity:
        too_many_ursulas_to_include.append(get_random_checksum_address())
    updated_data['include_ursulas'] = too_many_ursulas_to_include
    with pytest.raises(InvalidArgumentCombo):
        # number of ursulas to include exceeds quantity to sample
        AliceGetUrsulas().load(updated_data)

    # include and exclude addresses are not mutually exclusive - include has common entry
    updated_data = dict(required_data)
    updated_data['exclude_ursulas'] = exclude_ursulas
    updated_data['include_ursulas'] = list(
        include_ursulas)  # make copy to modify
    updated_data['include_ursulas'].append(
        exclude_ursulas[0])  # one address that overlaps
    with pytest.raises(InvalidArgumentCombo):
        # 1 address in both include and exclude lists
        AliceGetUrsulas().load(updated_data)

    # include and exclude addresses are not mutually exclusive - exclude has common entry
    updated_data = dict(required_data)
    updated_data['exclude_ursulas'] = list(
        exclude_ursulas)  # make copy to modify
    updated_data['exclude_ursulas'].append(
        include_ursulas[0])  # on address that overlaps
    updated_data['include_ursulas'] = include_ursulas
    with pytest.raises(InvalidArgumentCombo):
        # 1 address in both include and exclude lists
        AliceGetUrsulas().load(updated_data)

    #
    # Output i.e. dump
    #
    ursulas_info = []
    expected_ursulas_info = []
    port = 11500
    for i in range(3):
        ursula_info = Porter.UrsulaInfo(get_random_checksum_address(),
                                        f"https://127.0.0.1:{port+i}",
                                        SecretKey.random().public_key())
        ursulas_info.append(ursula_info)

        # use schema to determine expected output (encrypting key gets changed to hex)
        expected_ursulas_info.append(UrsulaInfoSchema().dump(ursula_info))

    output = AliceGetUrsulas().dump(obj={'ursulas': ursulas_info})
    assert output == {"ursulas": expected_ursulas_info}