Beispiel #1
0
def fuzz():
    jsonrpc_url = sys.argv[1] if len(
        sys.argv) > 1 else "http://localhost:8000/json/"
    trustee_proxy = JsonRpcProxy(
        url=jsonrpc_url,
        response_error_handler=status_slugs_response_error_handler)

    for i in range(100):
        keychain_uid = generate_uuid0()
        key_cipher_algo = random.choice(SUPPORTED_ASYMMETRIC_KEY_ALGOS)
        print("Fetching key of type %s... " % key_cipher_algo, end="")
        trustee_proxy.fetch_public_key(keychain_uid=keychain_uid,
                                       key_algo=key_cipher_algo)
        print("DONE")
def _get_proxy_for_escrow(escrow):
    if escrow == LOCAL_ESCROW_PLACEHOLDER:
        return LOCAL_ESCROW_API
    elif isinstance(escrow, dict):
        if "url" in escrow:
            return JsonRpcProxy(url=escrow)
        # TODO - Implement escrow lookup in global registry, shared-secret group, etc.
    raise ValueError("Unrecognized escrow identifiers: %s" % str(escrow))
 def _get_proxy_for_escrow(self, escrow):
     if escrow == LOCAL_ESCROW_PLACEHOLDER:
         return self._local_escrow_api
     elif isinstance(escrow, dict):
         if "url" in escrow:
             return JsonRpcProxy(
                 url=escrow["url"],
                 response_error_handler=status_slugs_response_error_handler,
             )
         # TODO - Implement escrow lookup in global registry, shared-secret group, etc.
     raise ValueError("Unrecognized escrow identifiers: %s" % str(escrow))
def test_jsonrpc_set_and_get_public_authenticator(live_server):
    jsonrpc_url = live_server.url + "/gateway/jsonrpc/"

    gateway_proxy = JsonRpcProxy(
        url=jsonrpc_url, response_error_handler=status_slugs_response_error_handler
    )

    parameters = _generate_authenticator_parameter_tree(2)

    with pytest.raises(KeystoreDoesNotExist):
        gateway_proxy.get_public_authenticator(keystore_uid=parameters["keystore_uid"])

    gateway_proxy.set_public_authenticator(keystore_uid=parameters["keystore_uid"],
                                                keystore_owner=parameters["keystore_owner"],
                                                keystore_secret=parameters["keystore_secret"],
                                                public_keys=parameters["public_keys"])

    with pytest.raises(KeystoreAlreadyExists):
        gateway_proxy.set_public_authenticator(keystore_uid=parameters["keystore_uid"],
                                                    keystore_owner=parameters["keystore_owner"],
                                                    keystore_secret="whatever",
                                                    public_keys=parameters["public_keys"])

    # Check handling of secret hash, similar to a password!
    public_authenticator_obj: PublicAuthenticator = PublicAuthenticator.objects.get(keystore_uid=parameters["keystore_uid"])
    _keystore_secret_hash = public_authenticator_obj.keystore_secret_hash
    assert _keystore_secret_hash
    assert _keystore_secret_hash != parameters["keystore_secret"]
    assert _keystore_secret_hash.startswith("pbkdf2_")
    assert public_authenticator_obj.has_usable_keystore_secret()
    assert public_authenticator_obj.check_keystore_secret(parameters["keystore_secret"])
    assert not public_authenticator_obj.check_keystore_secret("whatever")
    public_authenticator_obj.set_unusable_keystore_secret()
    assert not public_authenticator_obj.has_usable_keystore_secret()
    public_authenticator_obj.refresh_from_db()  # Unusable password was NOT saved
    assert public_authenticator_obj.has_usable_keystore_secret()

    public_authenticator = gateway_proxy.get_public_authenticator(keystore_uid=parameters["keystore_uid"])
    del parameters["keystore_secret"]
    assert parameters == public_authenticator
    check_public_authenticator_sanity(public_authenticator)
def test_jsonrpc_trustee_signature(live_server):
    jsonrpc_url = _get_trustee_jsonrpc_url(live_server)

    trustee_proxy = JsonRpcProxy(
        url=jsonrpc_url,
        response_error_handler=status_slugs_response_error_handler)

    keychain_uid = generate_uuid0()
    payload_signature_algo = "DSA_DSS"
    secret = get_random_bytes(101)
    secret_too_big = get_random_bytes(150)

    public_key_signature_pem = trustee_proxy.fetch_public_key(
        keychain_uid=keychain_uid, key_algo=payload_signature_algo)
    public_key_signature = load_asymmetric_key_from_pem_bytestring(
        key_pem=public_key_signature_pem, key_algo=payload_signature_algo)

    signature = trustee_proxy.get_message_signature(
        keychain_uid=keychain_uid,
        message=secret,
        signature_algo=payload_signature_algo)

    with pytest.raises(ValidationError, match="too big"):
        trustee_proxy.get_message_signature(
            keychain_uid=keychain_uid,
            message=secret_too_big,
            signature_algo=payload_signature_algo,
        )

    verify_message_signature(
        message=secret,
        signature=signature,
        key=public_key_signature,
        signature_algo=payload_signature_algo,
    )

    signature["signature_value"] += b"xyz"
    with pytest.raises(SignatureVerificationError,
                       match="not authentic|Incorrect signature"):
        verify_message_signature(
            message=secret,
            signature=signature,
            key=public_key_signature,
            signature_algo=payload_signature_algo,
        )
Beispiel #6
0
def test_waserver_abnormal_error_masking(live_server):
    jsonrpc_url = live_server.url + "/gateway/jsonrpc/"

    gateway_proxy = JsonRpcProxy(
        url=jsonrpc_url,
        response_error_handler=status_slugs_response_error_handler)

    keystore_uid = UUID("cac682a8-809f-4de5-bbfd-72b533a37a21")

    with pytest.raises(KeystoreDoesNotExist):  # Error well translated
        gateway_proxy.get_public_authenticator(keystore_uid=keystore_uid)

    # Patch the IMPORTED callable of view.py!
    with patch("waserver.apps.wagateway.views.get_public_authenticator",
               side_effect=KeyError("wrong key ABC")):

        with pytest.raises(TransportError):  # Server error NOT translated!
            gateway_proxy.get_public_authenticator(keystore_uid=keystore_uid)
def test_jsonrpc_extended_json_calls():

    uid = uuid.UUID("450fc293-b702-42d3-ae65-e9cc58e5a62a")

    server = JsonRpcProxy("http://mock/xmlrpc")

    # rpc call with positional args
    def callback1(request):
        request_message = json.loads(request.body)
        assert request_message["params"] == [
            {
                "$numberInt": "42"
            },
            {
                "$binary": {
                    "base64": b64encode(b"xyz").decode("ascii"),
                    "subType": "00"
                }
            },
            {
                "$binary": {
                    "base64": "RQ/Ck7cCQtOuZenMWOWmKg==",
                    "subType": "03"
                }
            },
        ]
        return 200, {}, u'{"jsonrpc": "2.0", "result": {"$binary": {"base64": "RQ/Ck7cCQtOuZenMWOWmKg==", "subType": "03"}}, "id": 1}'

    responses.add_callback(
        responses.POST,
        "http://mock/xmlrpc",
        content_type="application/json",
        callback=callback1,
    )
    assert server.foobar(42, b"xyz", uid) == uid
    responses.reset()

    # rpc call with named parameters
    def callback2(request):
        request_message = json.loads(request.body)
        assert request_message["params"] == {
            "x": {
                "$numberInt": "42"
            },
            "y": {
                "$binary": {
                    "base64": "eHl6",
                    "subType": "00"
                }
            },
            "z": {
                "$binary": {
                    "base64": "RQ/Ck7cCQtOuZenMWOWmKg==",
                    "subType": "03"
                }
            },
        }
        return 200, {}, u'{"jsonrpc": "2.0", "result": {"$binary": {"base64": "eHl6", "subType": "00"}}, "id": 1}'

    responses.add_callback(
        responses.POST,
        "http://mock/xmlrpc",
        content_type="application/json",
        callback=callback2,
    )
    assert server.foobar(x=42, y=b"xyz", z=uid) == b"xyz"
    responses.reset()

    # rpc call with a mapping type -> we disabled auto unpacking of arguments!!
    def callback3(request):
        request_message = json.loads(request.body)
        assert request_message["params"] == [{
            "foo": "bar"
        }]  # remains a LIST of 1 positional parameter!
        return 200, {}, u'{"jsonrpc": "2.0", "result": null}'

    responses.add_callback(
        responses.POST,
        "http://mock/xmlrpc",
        content_type="application/json",
        callback=callback3,
    )
    assert server.foobar({"foo": "bar"}) is None
    responses.reset()

    with pytest.raises(
            ProtocolError,
            match="spec forbids mixing arguments and keyword arguments"):
        server.foobar(33, a=22)
def test_jsonrpc_extended_json_calls():

    uid = uuid.UUID("450fc293-b702-42d3-ae65-e9cc58e5a62a")

    server = JsonRpcProxy("http://mock/xmlrpc", response_error_handler=None)

    # rpc call with positional args
    def callback1(request):
        request_message = json.loads(request.body)
        assert request_message["params"] == [
            {
                "$numberInt": "42"
            },
            {
                "$binary": {
                    "base64": b64encode(b"xyz").decode("ascii"),
                    "subType": "00"
                }
            },
            {
                "$binary": {
                    "base64": "RQ/Ck7cCQtOuZenMWOWmKg==",
                    "subType": "03"
                }
            },
        ]
        return (
            200,
            {},
            u'{"jsonrpc": "2.0", "result": {"$binary": {"base64": "RQ/Ck7cCQtOuZenMWOWmKg==", "subType": "03"}}, "id": 1}',
        )

    responses.add_callback(
        responses.POST,
        "http://mock/xmlrpc",
        content_type="application/json",
        callback=callback1,
    )
    assert server.foobar(42, b"xyz", uid) == uid
    responses.reset()

    # rpc call with named parameters
    def callback2(request):
        request_message = json.loads(request.body)
        assert request_message["params"] == {
            "x": {
                "$numberInt": "42"
            },
            "y": {
                "$binary": {
                    "base64": "eHl6",
                    "subType": "00"
                }
            },
            "z": {
                "$binary": {
                    "base64": "RQ/Ck7cCQtOuZenMWOWmKg==",
                    "subType": "03"
                }
            },
        }
        return (
            200,
            {},
            u'{"jsonrpc": "2.0", "result": {"$binary": {"base64": "eHl6", "subType": "00"}}, "id": 1}',
        )

    responses.add_callback(
        responses.POST,
        "http://mock/xmlrpc",
        content_type="application/json",
        callback=callback2,
    )
    assert server.foobar(x=42, y=b"xyz", z=uid) == b"xyz"
    responses.reset()

    # rpc call with a mapping type -> we disabled auto unpacking of arguments!!
    def callback3(request):
        request_message = json.loads(request.body)
        assert request_message["params"] == [{
            "foo": "bar"
        }]  # remains a LIST of 1 positional parameter!
        return 200, {}, u'{"jsonrpc": "2.0", "result": null}'

    responses.add_callback(
        responses.POST,
        "http://mock/xmlrpc",
        content_type="application/json",
        callback=callback3,
    )
    assert server.foobar({"foo": "bar"}) is None
    responses.reset()

    with pytest.raises(
            ProtocolError,
            match="spec forbids mixing arguments and keyword arguments"):
        server.foobar(33, a=22)

    def callback_protocol_error(request):
        return (
            200,
            {},
            u'{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}',
        )

    # Test exception handling

    responses.add_callback(
        responses.POST,
        "http://mock/xmlrpc",
        content_type="application/json",
        callback=callback_protocol_error,
    )
    with pytest.raises(ProtocolError, match="Error: -32700 Parse error"):
        server.foobar({"foo": "bar"})

    must_raise = True

    def _response_error_handler(exc_to_handle):
        nonlocal must_raise
        if not must_raise:
            return "some error occurred"
        raise RuntimeError(str(exc_to_handle))

    server = JsonRpcProxy("http://mock/xmlrpc",
                          response_error_handler=_response_error_handler)

    with pytest.raises(RuntimeError, match="Error: -32700 Parse error"):
        server.foobar({"foo": "bar"})

    must_raise = False

    assert server.foobar({"foo": "bar"}) == "some error occurred"

    responses.reset()
def test_jsonrpc_trustee_request_decryption_authorization_for_free_keys(
        live_server):
    jsonrpc_url = jsonrpc_url = _get_trustee_jsonrpc_url(live_server)

    trustee_proxy = JsonRpcProxy(
        url=jsonrpc_url,
        response_error_handler=status_slugs_response_error_handler)

    keychain_uid_free = generate_uuid0()
    free_key_algo1 = "RSA_OAEP"
    free_key_algo2 = "ECC_DSS"
    free_key_algo3 = "DSA_DSS"

    all_requested_keypair_identifiers = [
        dict(keychain_uid=keychain_uid_free, key_algo=free_key_algo1),
        dict(keychain_uid=keychain_uid_free, key_algo=free_key_algo2),
    ]

    sql_keystore = SqlKeystore()

    with freeze_time(
    ) as frozen_datetime:  # TEST RELATION WITH FREE KEYS ATTACHMENT

        for i in range(3):  # Generate 1 free keypair per type
            has_generated = generate_free_keypair_for_least_provisioned_key_algo(
                keystore=sql_keystore,
                max_free_keys_per_algo=1,
                key_algos=[free_key_algo1, free_key_algo2, free_key_algo3])
            assert has_generated

        keys_generated_before_datetime = timezone.now()

        public_key_pem1 = trustee_proxy.fetch_public_key(
            keychain_uid=keychain_uid_free, key_algo=free_key_algo1)
        assert public_key_pem1

        # This key will not have early-enough request for authorization
        public_key_pem3 = trustee_proxy.fetch_public_key(
            keychain_uid=keychain_uid_free, key_algo=free_key_algo3)
        assert public_key_pem3

        result = trustee_proxy.request_decryption_authorization(
            keypair_identifiers=all_requested_keypair_identifiers,
            request_message="I want early decryption!",
        )
        assert result["success_count"] == 1
        assert result["too_old_count"] == 0
        assert result[
            "not_found_count"] == 1  # free_key_algo2 is not attached yet

        frozen_datetime.tick(delta=timedelta(minutes=6))

        public_key_pem2 = trustee_proxy.fetch_public_key(
            keychain_uid=keychain_uid_free, key_algo=free_key_algo2)
        assert public_key_pem2

        result = trustee_proxy.request_decryption_authorization(
            keypair_identifiers=all_requested_keypair_identifiers,
            request_message="I want later decryption!",
        )
        assert result[
            "success_count"] == 1  # It's attachment time which counts!
        assert result["too_old_count"] == 1  # First key is too old now
        assert result["not_found_count"] == 0

        keypair_obj = TrusteeKeypair.objects.get(
            keychain_uid=keychain_uid_free, key_algo=free_key_algo1)
        assert keypair_obj.created_at <= keys_generated_before_datetime
        assert keypair_obj.attached_at
        assert keypair_obj.decryption_authorized_at
        first_authorized_at = keypair_obj.decryption_authorized_at

        keypair_obj = TrusteeKeypair.objects.get(
            keychain_uid=keychain_uid_free, key_algo=free_key_algo2)
        assert keypair_obj.created_at <= keys_generated_before_datetime
        assert keypair_obj.attached_at
        assert keypair_obj.decryption_authorized_at
        assert keypair_obj.decryption_authorized_at >= first_authorized_at + timedelta(
            minutes=5)

        keypair_obj = TrusteeKeypair.objects.get(
            keychain_uid=keychain_uid_free, key_algo=free_key_algo3)
        assert keypair_obj.created_at <= keys_generated_before_datetime
        assert keypair_obj.attached_at
        assert not keypair_obj.decryption_authorized_at  # Never requested
def test_jsonrpc_trustee_request_decryption_authorization_for_normal_keys(
        live_server):
    jsonrpc_url = jsonrpc_url = _get_trustee_jsonrpc_url(live_server)

    trustee_proxy = JsonRpcProxy(
        url=jsonrpc_url,
        response_error_handler=status_slugs_response_error_handler)

    key_cipher_algo = "RSA_OAEP"

    with freeze_time(
    ) as frozen_datetime:  # TEST AUTHORIZATION REQUEST HANDLING

        keychain_uid1 = generate_uuid0()
        keychain_uid2 = generate_uuid0()
        keychain_uid3 = generate_uuid0()
        keychain_uid4 = generate_uuid0()
        keychain_uid_unexisting = generate_uuid0()

        all_keypair_identifiers = [
            dict(keychain_uid=keychain_uid1, key_algo=key_cipher_algo),
            dict(keychain_uid=keychain_uid2, key_algo=key_cipher_algo),
            dict(keychain_uid=keychain_uid3, key_algo=key_cipher_algo),
            dict(keychain_uid=keychain_uid4, key_algo=key_cipher_algo),
            dict(keychain_uid=keychain_uid_unexisting,
                 key_algo=key_cipher_algo),
        ]

        public_key_pem = trustee_proxy.fetch_public_key(
            keychain_uid=keychain_uid1, key_algo=key_cipher_algo)
        assert public_key_pem
        assert not _fetch_key_object_or_raise(
            keychain_uid=keychain_uid1,
            key_algo=key_cipher_algo).decryption_authorized_at

        # Non-pregenerated keys don't have that field set!
        assert not _fetch_key_object_or_raise(
            keychain_uid=keychain_uid1, key_algo=key_cipher_algo).attached_at

        result = trustee_proxy.request_decryption_authorization(
            keypair_identifiers=[], request_message="I want decryption!")
        assert result["success_count"] == 0
        assert result["too_old_count"] == 0
        assert result["not_found_count"] == 0

        frozen_datetime.tick(delta=timedelta(minutes=2))

        result = trustee_proxy.request_decryption_authorization(
            keypair_identifiers=all_keypair_identifiers,
            request_message="I want decryption!",
        )
        assert result["success_count"] == 1
        assert result["too_old_count"] == 0
        assert (result["not_found_count"] == 4
                )  # keychain_uid2 and keychain_uid3 not created yet

        old_decryption_authorized_at = _fetch_key_object_or_raise(
            keychain_uid=keychain_uid1,
            key_algo=key_cipher_algo).decryption_authorized_at
        assert old_decryption_authorized_at

        public_key_pem = trustee_proxy.fetch_public_key(
            keychain_uid=keychain_uid2, key_algo=key_cipher_algo)
        assert public_key_pem
        public_key_pem = trustee_proxy.fetch_public_key(
            keychain_uid=keychain_uid3, key_algo=key_cipher_algo)
        assert public_key_pem

        frozen_datetime.tick(delta=timedelta(minutes=4))

        result = trustee_proxy.request_decryption_authorization(
            keypair_identifiers=all_keypair_identifiers,
            request_message="I want decryption!",
        )
        assert result["success_count"] == 2
        assert result["too_old_count"] == 1
        assert result["not_found_count"] == 2

        assert (_fetch_key_object_or_raise(
            keychain_uid=keychain_uid1,
            key_algo=key_cipher_algo).decryption_authorized_at ==
                old_decryption_authorized_at)  # Unchanged
        assert _fetch_key_object_or_raise(
            keychain_uid=keychain_uid2,
            key_algo=key_cipher_algo).decryption_authorized_at
        assert _fetch_key_object_or_raise(
            keychain_uid=keychain_uid3,
            key_algo=key_cipher_algo).decryption_authorized_at

        with pytest.raises(KeyDoesNotExist, match="not found"):
            _fetch_key_object_or_raise(keychain_uid=keychain_uid_unexisting,
                                       key_algo=key_cipher_algo)

        public_key_pem = trustee_proxy.fetch_public_key(
            keychain_uid=keychain_uid4, key_algo=key_cipher_algo)
        assert public_key_pem

        frozen_datetime.tick(delta=timedelta(minutes=6))

        result = trustee_proxy.request_decryption_authorization(
            keypair_identifiers=all_keypair_identifiers,
            request_message="I want decryption!",
        )
        assert result["success_count"] == 0
        assert result["too_old_count"] == 4
        assert result["not_found_count"] == 1

        assert (_fetch_key_object_or_raise(
            keychain_uid=keychain_uid1,
            key_algo=key_cipher_algo).decryption_authorized_at ==
                old_decryption_authorized_at)  # Unchanged

    del all_keypair_identifiers
def test_jsonrpc_trustee_decryption_authorization_flags(live_server):
    jsonrpc_url = _get_trustee_jsonrpc_url(live_server)

    trustee_proxy = JsonRpcProxy(
        url=jsonrpc_url,
        response_error_handler=status_slugs_response_error_handler)

    keychain_uid = generate_uuid0()
    keychain_uid_bad = generate_uuid0()
    key_cipher_algo = "RSA_OAEP"
    secret = get_random_bytes(101)

    public_encryption_key_pem = trustee_proxy.fetch_public_key(
        keychain_uid=keychain_uid, key_algo=key_cipher_algo)
    public_encryption_key = load_asymmetric_key_from_pem_bytestring(
        key_pem=public_encryption_key_pem, key_algo=key_cipher_algo)

    cipherdict = _encrypt_via_rsa_oaep(
        plaintext=secret, key_dict=dict(key=public_encryption_key))

    def _attempt_decryption():
        return trustee_proxy.decrypt_with_private_key(
            keychain_uid=keychain_uid,
            cipher_algo=key_cipher_algo,
            cipherdict=cipherdict,
        )

    with freeze_time() as frozen_datetime:
        with pytest.raises(AuthorizationError,
                           match="Decryption not authorized"):
            _attempt_decryption()

        keypair_obj = TrusteeKeypair.objects.get(keychain_uid=keychain_uid,
                                                 key_algo=key_cipher_algo)
        keypair_obj.decryption_authorized_at = timezone.now() + timedelta(
            hours=2)
        keypair_obj.save()

        with pytest.raises(
                AuthorizationError,
                match="Decryption authorization is only valid from"):
            _attempt_decryption()  # Too early

        frozen_datetime.tick(delta=timedelta(hours=3))

        decrypted = _attempt_decryption()
        assert decrypted == secret  # It works!

        with pytest.raises(KeyDoesNotExist, match="not found"):
            trustee_proxy.decrypt_with_private_key(
                keychain_uid=keychain_uid_bad,
                cipher_algo=key_cipher_algo,
                cipherdict=cipherdict,
            )

        cipherdict["digest_list"].append(b"aaabbbccc")
        with pytest.raises(DecryptionError,
                           match="Ciphertext with incorrect length"):
            trustee_proxy.decrypt_with_private_key(
                keychain_uid=keychain_uid,
                cipher_algo=key_cipher_algo,
                cipherdict=cipherdict,
            )

        frozen_datetime.tick(delta=timedelta(
            hours=24))  # We hardcode DECRYPTION_AUTHORIZATION_LIFESPAN_H here

        with pytest.raises(
                AuthorizationError,
                match="Decryption authorization is only valid from"):
            _attempt_decryption(
            )  # Too late, cipherdict is not even used so no ValueError

        keypair_obj.decryption_authorized_at = None
        keypair_obj.save()

        with pytest.raises(AuthorizationError,
                           match="Decryption not authorized"):
            _attempt_decryption()  # No more authorization at all