Example #1
0
    def invitation_success(self, sigkeypayload, cid, _debug_when_done):
        if not sigkeypayload.startswith("i0:"):
            raise ValueError("expected i0:, got '%r'" % sigkeypayload[:20])
        their_verfkey = VerifyKey(sigkeypayload[3:3+32])
        encoded_payload = their_verfkey.verify(sigkeypayload[3+32:])
        payload = json.loads(encoded_payload.decode("utf-8"))

        self.db.update(
            "UPDATE addressbook SET"
            " invitation_state=?, wormhole=?, wormhole_payload=?,"
            " when_accepted=?,"
            " acked=1,"  # TODO: faked. add a roundtrip?
            " latest_offered_mailbox_json=?,"
            " their_channel_record_json=?,"
            " they_used_new_channel_key=?, their_verfkey=?"
            " WHERE id=?",
            (invitation.INVITE_COMPLETE, None, None,
             time.time(),
             json.dumps(payload.get("mailbox")),
             json.dumps(payload["channel"]),
             0, their_verfkey.encode().encode("hex"),
             cid), "addressbook", cid)
        self.maybe_accept_mailbox(cid)
        self.db.commit()
        log.msg("addressbook entry added for petname=%r" %
                self.petname_for_cid(cid))
        if _debug_when_done:
            eventually(_debug_when_done.callback, cid)
Example #2
0
    def post(self):
        """ key fetching method.

            Args:
                device_verify_key    (str): NaCl verification key for the device the user
                is sending the query as.
                destination_username (str): base64 encoded, signed destination username.

            Returns:
                HTTP 422: If the device_verify_key provided by the user does not exist.

                HTTP 400: If the provided destination_username is not signed by the
                correct device_verify_key provided during device registration.

                device_public_keys (dict): A dictionary containing the list of all
                device_public_key entries that corresponded to the requested user.

        """

        parser = reqparse.RequestParser()
        parser.add_argument('device_verify_key',
                            type=str,
                            required=True,
                            help="device_verify_key is either blank or incorrect type.")
        parser.add_argument('destination_username',
                            type=str,
                            required=True,
                            help="destination_username is either blank or incorrect type.")
        args = parser.parse_args()

        #check if user exists already
        stored_key = query_db('''
                              SELECT device_verify_key
                              FROM devices
                              WHERE device_verify_key = ?;''',
                              [args['device_verify_key']],
                              one=True)
        if stored_key is None:
            abort(422, message="Device does not exist.")

        destination_username = reconstruct_signed_message(args['destination_username'])

        device_verify_key = VerifyKey(stored_key['device_verify_key'], encoder=HexEncoder)

        try:
            device_verify_key.verify(destination_username)
        except BadSignatureError:
            abort(400,
                  message="Signature for provided username is corrupt or invalid.")

        device_public_keys = []
        for row in query_db('''
                            SELECT device_public_key
                            FROM devices
                            WHERE username=?;''',
                            [destination_username.message]):
            device_public_keys.append(row['device_public_key'])

        return {'device_public_keys': device_public_keys}
Example #3
0
    def test_valid_signed_message(
            self, public_key, signed, message, signature):
        key = VerifyKey(
            public_key,
            encoder=HexEncoder,
        )

        assert binascii.hexlify(
            key.verify(signed, encoder=HexEncoder),
        ) == message
        assert binascii.hexlify(
            key.verify(message, signature, encoder=HexEncoder),
        ) == message
Example #4
0
def test_delete_all(omq, random_sn, sk, exclude):
    swarm = ss.get_swarm(omq, random_sn, sk)
    sns = ss.random_swarm_members(swarm, 2, exclude)
    conns = [omq.connect_remote(sn_address(sn)) for sn in sns]

    msgs = ss.store_n(omq, conns[0], sk, b"omg123", 5)

    my_ss_id = '05' + sk.verify_key.encode().hex()

    ts = int(time.time() * 1000)
    to_sign = "delete_all{}".format(ts).encode()
    sig = sk.sign(to_sign, encoder=Base64Encoder).signature.decode()
    params = json.dumps({
        "pubkey": my_ss_id,
        "timestamp": ts,
        "signature": sig
    }).encode()

    resp = omq.request_future(conns[1], 'storage.delete_all', [params]).get()

    assert len(resp) == 1
    r = json.loads(resp[0])

    assert set(
        r['swarm'].keys()) == {x['pubkey_ed25519']
                               for x in swarm['snodes']}

    msg_hashes = sorted(m['hash'] for m in msgs)

    # signature of ( PUBKEY_HEX || TIMESTAMP || DELETEDHASH[0] || ... || DELETEDHASH[N] )
    expected_signed = "".join((my_ss_id, str(ts), *msg_hashes)).encode()
    for k, v in r['swarm'].items():
        assert v['deleted'] == msg_hashes
        edpk = VerifyKey(k, encoder=HexEncoder)
        edpk.verify(expected_signed, base64.b64decode(v['signature']))

    r = omq.request_future(conns[0], 'storage.retrieve', [
        json.dumps({
            "pubkey":
            my_ss_id,
            "timestamp":
            ts,
            "signature":
            sk.sign(f"retrieve{ts}".encode(),
                    encoder=Base64Encoder).signature.decode()
        }).encode()
    ]).get()
    assert len(r) == 1
    r = json.loads(r[0])
    assert not r['messages']
Example #5
0
async def verifySignature(signature, publicKey, data):
    data = data.copy()
    data.pop("signature")
    data = json.dumps(data).encode()
    publicKey = publicKey[4:56] + "===="
    publicKey = base64.b32decode(publicKey.upper().encode())
    verifier = VerifyKey(publicKey)
    signature = (int(signature, 16)).to_bytes(64, byteorder="little")
    try:
        verifier.verify(data, signature)
        return True

    except BadSignatureError:
        return False
class TwexitRequestValidator(object):
    def __init__(self, token):
        self.token = token.encode("utf-8")
        if VerifyKey is None:
            raise RuntimeError(
                "missing required PyNaCl library to verify Twexit signatures. "
                "`pip install pynacl` to install.")
        self._verify_key = VerifyKey(self.token, encoder=Base64Encoder)

    def _compute_signed_bytes(self, uri, params):
        s = uri
        if params:
            for k, v in sorted(params.items()):
                s += k + v

        # compute signature and compare signatures
        return s.encode("utf-8")

    def validate(self, uri, params, signature):
        """Compute the signature for a given request

        :param uri: full URI that Twilio requested on your server
        :param params: post vars that Twilio sent with the request
        :param utf: whether return should be bytestring or unicode (python3)

        :returns: Boolean indicating if the signature was valid or not
        """
        if params is None:
            params = {}

        parsed_uri = urlparse(uri)
        uri_with_port = add_port(parsed_uri)
        uri_without_port = remove_port(parsed_uri)

        signature_bytes = base64.b64decode(signature)

        # compute signature with Ed25519 algorithm
        #  check signature of uri with and without port,
        #  since sig generation on back end is inconsistent
        for uri in [uri_without_port, uri_with_port]:
            try:
                self._verify_key.verify(self._compute_signed_bytes(
                    uri, params),
                                        signature=signature_bytes)
            except BadSignatureError:
                continue
            else:
                return True

        return False
Example #7
0
    def test_valid_signed_message(
            self, _seed, public_key, message, signature, signed):
        key = VerifyKey(
            public_key,
            encoder=HexEncoder,
        )

        assert binascii.hexlify(
            key.verify(signed, encoder=HexEncoder),
        ) == message
        assert binascii.hexlify(
            key.verify(message, HexEncoder.decode(signature),
                       encoder=HexEncoder),
        ) == message
Example #8
0
def verify_signature(signed, public):
    vk = VerifyKey(public)
    vk.verify(signed)
    child = Child(
        signed[0:64],
        signed[64:128],
        int.from_bytes(signed[128:144], 'little'),
        int.from_bytes(signed[144:160], 'little'),
        signed[160:192],
        signed[192:],
    )
    if child.public != public:
        raise ValueError('embedded pubkey mismatch: {!r} != {!r}'.format(
            child.public, public))
    return child
Example #9
0
    def handle_request(self, stream: IngressStream):
        # New connection, see what they want
        command = stream.read(4)
        print(command)

        if (command == Protocol.REQUEST_TRUST_SET):
            # Trust set requested, get the public key
            public_key = VerifyKey(stream.read(32))

            # Certificate requested, do we have it?
            if (self.store.has_trust_set(public_key)):
                # Yes, get ready to reply
                def established(egress: EgressStream):
                    # Get the trust set
                    trust_set = self.store.get_trust_set(public_key)

                    # Send the trust set
                    self.send_response(trust_set.serialise(), egress)

                # Reply to the request
                self.instance.establish_stream(
                    stream.origin,
                    in_reply_to=stream.id).subscribe(established)

        elif (command == Protocol.REQUEST_SIGNATURE):
            # Signature requested, get the public key
            public_key = VerifyKey(stream.read(32))

            # Get the message hash
            digest = stream.read(64)

            # Do we have the private key for this public key?
            if (self.store.has_signing_key(public_key)):
                # Yes, get ready to reply
                def established(egress: EgressStream):
                    # Get the signing key
                    signing_key = self.store.get_signing_key(public_key)

                    # Create the time signature
                    signature = TimeSignature.sign(digest, signing_key)

                    # Send the signature
                    self.send_response(signature.serialise(), egress)

                # Reply to the request
                self.instance.establish_stream(
                    stream.origin,
                    in_reply_to=stream.id).subscribe(established)
Example #10
0
def get_all_users_msg(
    msg: GetUsersMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> GetUsersResponse:
    # Get Payload Content
    try:
        _current_user_id = msg.content.get("current_user", None)
    except Exception:
        _current_user_id = None

    users = node.users

    if not _current_user_id:
        _current_user_id = users.first(verify_key=verify_key.encode(
            encoder=HexEncoder).decode("utf-8")).id

    users = users.all()
    _msg = []
    for user in users:
        _user_json = model_to_json(user)
        _msg.append(_user_json)

    return GetUsersResponse(
        address=msg.reply_to,
        status_code=200,
        content=_msg,
    )
Example #11
0
def del_role_msg(
    msg: DeleteRoleMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> DeleteRoleResponse:
    _role_id = msg.content.get("role_id", None)
    _current_user_id = msg.content.get("current_user", None)

    users = node.users

    if not _current_user_id:
        _current_user_id = users.first(verify_key=verify_key.encode(
            encoder=HexEncoder).decode("utf-8")).id

    if not _role_id:
        raise MissingRequestKeyError

    _allowed = node.users.can_edit_roles(user_id=_current_user_id)

    if _allowed:
        node.roles.delete(id=_role_id)
    else:
        raise AuthorizationError("You're not authorized to delete this role!")

    return DeleteRoleResponse(
        address=msg.reply_to,
        status_code=200,
        content={"msg": "Role has been deleted!"},
    )
Example #12
0
def get_all_roles_msg(
    msg: GetRolesMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> GetRolesResponse:
    try:
        _current_user_id = msg.content.get("current_user", None)
    except Exception:
        _current_user_id = None

    users = node.users

    if not _current_user_id:
        _current_user_id = users.first(verify_key=verify_key.encode(
            encoder=HexEncoder).decode("utf-8")).id

    _allowed = users.can_triage_requests(user_id=_current_user_id)

    if _allowed:
        roles = node.roles.all()
        _msg = [model_to_json(role) for role in roles]
    else:
        raise AuthorizationError("You're not allowed to get Role information!")

    return GetRolesResponse(address=msg.reply_to,
                            status_code=200,
                            content=_msg)
Example #13
0
 def parse(cls, file_path):
     amap = toml.load(file_path)
     author_to_public_keys = {}
     for k, v in amap.items():
         address = bytes.fromhex(k)
         author_to_public_keys[address] = VerifyKey(bytes.fromhex(v['c']))
     return ValidatorVerifier(author_to_public_keys)
Example #14
0
 def verifykey_obj(self):
     if not self._verifykey_obj:
         assert self.pubkey
         verifykey = binascii.unhexlify(self.pubkey)
         assert len(verifykey) == 32
         self._verifykey_obj = VerifyKey(verifykey)
     return self._verifykey_obj
Example #15
0
def search_users_msg(
    msg: SearchUsersMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> SearchUsersResponse:
    # Get Payload Content
    _current_user_id = msg.content.get("current_user", None)
    users = node.users
    users = node.users
    if not _current_user_id:
        _current_user_id = users.first(
            verify_key=verify_key.encode(encoder=HexEncoder).decode("utf-8")
        ).id

    user_parameters = {
        "email": msg.content.get("email", None),
        "role": msg.content.get("role", None),
    }

    filter_parameters = lambda key: user_parameters[key]
    filtered_parameters = filter(filter_parameters, user_parameters.keys())
    user_parameters = {key: user_parameters[key] for key in filtered_parameters}

    try:
        users = node.users.query(**user_parameters)
        _msg = [model_to_json(user) for user in users]
    except UserNotFoundError:
        _msg = {}

    return SearchUsersResponse(
        address=msg.reply_to,
        status_code=200,
        content=_msg,
    )
Example #16
0
def update_dataset_msg(
    msg: UpdateDatasetMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> UpdateDatasetResponse:
    # Get Payload Content
    _current_user_id = msg.content.get("current_user", None)
    _dataset_id = msg.content.get("dataset_id", None)
    _tags = msg.content.get("tags", [])
    _description = msg.content.get("description", "")
    _manifest = msg.content.get("manifest", "")

    users = node.users
    if not _current_user_id:
        _current_user_id = users.first(verify_key=verify_key.encode(
            encoder=HexEncoder).decode("utf-8")).id

    _allowed = users.can_upload_data(user_id=_current_user_id)

    _msg = {}
    if _allowed:
        storage = node.disk_store
        _msg = update_dataset(_dataset_id, _tags, _manifest, _description)
    else:
        raise AuthorizationError("You're not allowed to upload data!")

    return UpdateDatasetResponse(
        address=msg.reply_to,
        status_code=204,
        content={"message": "Dataset updated successfully!"},
    )
Example #17
0
def update_group_msg(
    msg: UpdateGroupMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> UpdateGroupResponse:
    _current_user_id = msg.content.get("current_user", None)
    _group_id = msg.content.get("group_id", None)
    _group_name = msg.content.get("name", None)
    _users = msg.content.get("users", None)

    users = node.users

    if not _current_user_id:
        _current_user_id = users.first(verify_key=verify_key.encode(
            encoder=HexEncoder).decode("utf-8")).id

    # Checks
    _is_allowed = node.users.can_create_groups(user_id=_current_user_id)

    if not node.groups.contain(id=_group_id):
        raise GroupNotFoundError("Group ID not found!")
    elif _is_allowed:
        node.groups.update(group_id=_group_id,
                           group_name=_group_name,
                           users=_users)
    else:
        raise AuthorizationError("You're not allowed to get this group!")

    return UpdateGroupResponse(
        address=msg.reply_to,
        status_code=200,
        content={"msg": "Group updated successfully!"},
    )
Example #18
0
def get_worker_msg(msg: GetWorkerMessage, node: AbstractNode,
                   verify_key: VerifyKey) -> GetWorkerResponse:
    try:

        worker_id = msg.content.get("worker_id", None)
        _current_user_id = msg.content.get("current_user", None)

        users = node.users

        if not _current_user_id:
            _current_user_id = users.first(verify_key=verify_key.encode(
                encoder=HexEncoder).decode("utf-8")).id

        env_ids = [
            env.id for env in node.environments.get_environments(
                user=_current_user_id)
        ]
        is_admin = users.can_manage_infrastructure(user_id=_current_user_id)

        if (int(worker_id) in env_ids) or is_admin:

            _msg = model_to_json(node.environments.first(id=int(worker_id)))
        else:
            _msg = {}

        return GetWorkerResponse(address=msg.reply_to,
                                 status_code=200,
                                 content=_msg)
    except Exception as e:
        return GetWorkerResponse(address=msg.reply_to,
                                 status_code=500,
                                 content={"error": str(e)})
Example #19
0
def get_all_datasets_metadata_msg(
    msg: GetDatasetsMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> GetDatasetsResponse:
    # Get Payload Content
    _current_user_id = msg.content.get("current_user", None)
    users = node.users
    users = node.users
    if not _current_user_id:
        _current_user_id = users.first(verify_key=verify_key.encode(
            encoder=HexEncoder).decode("utf-8")).id

    _msg = {}

    storage = node.disk_store
    datasets = []
    for dataset in get_all_datasets():
        ds = model_to_json(dataset)
        objs = get_all_relations(dataset.id)
        ds["data"] = [{
            "name": obj.name,
            "id": obj.obj,
            "dtype": obj.dtype,
            "shape": obj.shape,
        } for obj in objs]
        datasets.append(ds)

    return GetDatasetsResponse(
        address=msg.reply_to,
        status_code=200,
        content=datasets,
    )
Example #20
0
def get_workers_msg(msg: GetWorkersMessage, node: AbstractNode,
                    verify_key: VerifyKey) -> GetWorkersResponse:
    try:
        _current_user_id = msg.content.get("current_user", None)
        include_all = msg.content.get("include_all", False)
        include_failed = msg.content.get("include_failed", False)
        include_destroyed = msg.content.get("include_destroyed", False)

        if not _current_user_id:
            _current_user_id = node.users.first(verify_key=verify_key.encode(
                encoder=HexEncoder).decode("utf-8")).id

        envs = node.environments.get_environments(user=_current_user_id)

        workers = []
        for env in envs:
            _env = node.environments.first(id=env.id)
            if (include_all or (_env.state == states["success"])
                    or (include_failed and _env.state == states["failed"]) or
                (include_destroyed and _env.state == states["destroyed"])):
                workers.append(model_to_json(_env))

        _msg = {"workers": workers}

        return GetWorkersResponse(address=msg.reply_to,
                                  status_code=200,
                                  content=_msg)
    except Exception as e:
        return GetWorkersResponse(address=msg.reply_to,
                                  status_code=False,
                                  content={"error": str(e)})
Example #21
0
def get_user_msg(
    msg: GetUserMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> GetUserResponse:
    # Get Payload Content
    _user_id = msg.content.get("user_id", None)
    _current_user_id = msg.content.get("current_user", None)

    users = node.users
    if not _current_user_id:
        _current_user_id = users.first(verify_key=verify_key.encode(
            encoder=HexEncoder).decode("utf-8")).id

    _allowed = users.can_triage_requests(user_id=_current_user_id)

    if _allowed:
        user = users.first(id=_user_id)
        _msg = model_to_json(user)
        _msg["groups"] = [
            node.groups.first(id=group).name
            for group in node.groups.get_groups(user_id=_user_id)
        ]
    else:
        raise AuthorizationError("You're not allowed to get User information!")

    return GetUserResponse(
        address=msg.reply_to,
        status_code=200,
        content=_msg,
    )
Example #22
0
def del_request_msg(
    msg: DeleteRequestMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> DeleteRequestResponse:

    # Get Payload Content
    request_id = msg.content.get("request_id", None)
    current_user_id = msg.content.get("current_user", None)

    users = node.users

    if not current_user_id:
        current_user_id = users.first(
            verify_key=verify_key.encode(encoder=HexEncoder).decode("utf-8")
        ).id

    requests = node.data_requests
    request = requests.first(id=request_id)

    # Only the creator of a request may delete their request.
    if request.user_id == current_user_id:
        requests.delete(id=request_id)
    else:
        raise AuthorizationError("You're not allowed to delete this Request!")

    return DeleteRequestResponse(
        address=msg.reply_to,
        status_code=200,
        content={"msg": "Request deleted!"},
    )
Example #23
0
 def verify(self, message: bytes, signature: bytes) -> bool:
     # Indicates if a message and a signature match.
     try:
         VerifyKey(self.get_key()).verify(message, signature)
         return True
     except BadSignatureError:
         return False
Example #24
0
                def on_authenticate_ok(principal):
                    self.log.info(
                        '{klass}.hello(realm="{realm}", details={details}) -> on_authenticate_ok(principal={principal})',
                        klass=self.__class__.__name__,
                        realm=realm,
                        details=details,
                        principal=principal)
                    error = self._assign_principal(principal)
                    if error:
                        return error

                    self._verify_key = VerifyKey(
                        principal['pubkey'], encoder=nacl.encoding.HexEncoder)

                    extra = self._compute_challenge(channel_binding)
                    return types.Challenge(self._authmethod, extra)
Example #25
0
def get_all_request_msg(
    msg: GetRequestsMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> GetRequestsResponse:

    # Get Payload Content
    current_user_id = msg.content.get("current_user", None)

    users = node.users

    if not current_user_id:
        current_user_id = users.first(
            verify_key=verify_key.encode(encoder=HexEncoder).decode("utf-8")
        ).id

    allowed = users.can_triage_requests(user_id=current_user_id)

    if allowed:
        requests = node.data_requests
        requests = requests.all()
        requests_json = [model_to_json(requests) for requests in requests]
    else:
        raise AuthorizationError("You're not allowed to get Request information!")

    return GetRequestsResponse(
        address=msg.reply_to,
        status_code=200,
        content=requests_json,
    )
Example #26
0
def delete_dataset_msg(
    msg: UpdateDatasetMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> DeleteDatasetResponse:
    # Get Payload Content
    _current_user_id = msg.content.get("current_user", None)
    _dataset_id = msg.content.get("dataset_id", None)

    users = node.users
    if not _current_user_id:
        _current_user_id = users.first(verify_key=verify_key.encode(
            encoder=HexEncoder).decode("utf-8")).id

    _allowed = users.can_upload_data(user_id=_current_user_id)

    if _allowed:
        storage = node.disk_store
        delete_dataset(_dataset_id)
    else:
        raise AuthorizationError("You're not allowed to upload data!")

    return DeleteDatasetResponse(
        address=msg.reply_to,
        status_code=204,
        content={"message": "Dataset deleted successfully!"},
    )
Example #27
0
def del_user_msg(
    msg: DeleteUserMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> DeleteUserResponse:
    # Get Payload Content
    _user_id = msg.content.get("user_id", None)
    _current_user_id = msg.content.get("current_user", None)

    users = node.users
    if not _current_user_id:
        _current_user_id = users.first(verify_key=verify_key.encode(
            encoder=HexEncoder).decode("utf-8")).id

    _allowed = (users.can_create_users(user_id=_current_user_id)
                and users.first(id=_user_id)
                and users.role(user_id=_user_id).name != "Owner")
    if _allowed:
        node.users.delete(id=_user_id)
    else:
        raise AuthorizationError(
            "You're not allowed to delete this user information!")

    return DeleteUserResponse(
        address=msg.reply_to,
        status_code=200,
        content={"message": "User deleted successfully!"},
    )
Example #28
0
    def _proto2object(proto: SignedMessage_PB) -> SignedMessageT:
        # TODO: horrible temp hack, need to rethink address on SignedMessage
        sub_message = validate_type(
            _deserialize(blob=proto.message, from_bytes=True), SyftMessage)

        address = sub_message.address

        # proto.obj_type is final subclass callee for example ReprMessage
        # but we want the associated signed_type which is
        # ReprMessage -> ImmediateSyftMessageWithoutReply.signed_type
        # == SignedImmediateSyftMessageWithoutReply
        module_parts = proto.obj_type.split(".")
        klass = module_parts.pop()
        obj_type = getattr(sys.modules[".".join(module_parts)], klass)
        obj = obj_type.signed_type(
            msg_id=_deserialize(blob=proto.msg_id),
            address=address,
            obj_type=proto.obj_type,
            signature=proto.signature,
            verify_key=VerifyKey(proto.verify_key),
            message=proto.message,
        )

        icon = "🤷🏾‍♀️"
        if hasattr(obj, "icon"):
            icon = obj.icon
        debug(f"> {icon} <- 🔢 Proto")

        if type(obj) != obj_type.signed_type:
            traceback_and_raise(
                TypeError(
                    "Deserializing SignedMessage. " +
                    f"Expected type {obj_type.signed_type}. Got {type(obj)}"))

        return obj
Example #29
0
def get_all_groups_msg(
    msg: GetGroupsMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> GetGroupsResponse:

    try:
        _current_user_id = msg.content.get("current_user", None)
    except Exception:
        _current_user_id = None

    users = node.users

    if not _current_user_id:
        _current_user_id = users.first(verify_key=verify_key.encode(
            encoder=HexEncoder).decode("utf-8")).id

    # Checks
    _is_allowed = node.users.can_create_groups(user_id=_current_user_id)
    if _is_allowed:
        _groups = node.groups.all()
    else:
        raise AuthorizationError("You're not allowed to get the groups!")

    _groups = [model_to_json(group) for group in _groups]
    for group in _groups:
        group["users"] = node.groups.get_users(group_id=group["id"])

    return GetGroupsResponse(
        address=msg.reply_to,
        status_code=200,
        content=_groups,
    )
Example #30
0
def create_group_msg(
    msg: CreateGroupMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> CreateGroupResponse:
    _current_user_id = msg.content.get("current_user", None)
    _group_name = msg.content.get("name", None)
    _users = msg.content.get("users", None)

    users = node.users

    if not _current_user_id:
        _current_user_id = users.first(verify_key=verify_key.encode(
            encoder=HexEncoder).decode("utf-8")).id

    # Checks
    _is_allowed = node.users.can_create_groups(user_id=_current_user_id)

    if not _group_name:
        raise MissingRequestKeyError("Invalid group name!")
    elif _is_allowed:
        node.groups.create(group_name=_group_name, users=_users)
    else:
        raise AuthorizationError("You're not allowed to create groups!")

    return CreateGroupResponse(
        address=msg.reply_to,
        status_code=200,
        content={"msg": "Group created successfully!"},
    )
Example #31
0
def get_group_msg(
    msg: GetGroupMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> GetGroupResponse:
    _current_user_id = msg.content.get("current_user", None)
    _group_id = msg.content.get("group_id", None)

    users = node.users

    if not _current_user_id:
        _current_user_id = users.first(verify_key=verify_key.encode(
            encoder=HexEncoder).decode("utf-8")).id

    # Checks
    _is_allowed = node.users.can_create_groups(user_id=_current_user_id)

    if not node.groups.contain(id=_group_id):
        raise GroupNotFoundError("Group ID not found!")
    elif _is_allowed:
        _group = node.groups.first(id=_group_id)
    else:
        raise AuthorizationError("You're not allowed to get this group!")

    _msg = model_to_json(_group)
    _msg["users"] = node.groups.get_users(group_id=_group_id)

    return GetGroupResponse(
        address=msg.reply_to,
        status_code=200,
        content=_msg,
    )
Example #32
0
def get_request_msg(
    msg: GetRequestMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> GetRequestResponse:

    # Get Payload Content
    request_id = msg.content.get("request_id", None)
    current_user_id = msg.content.get("current_user", None)

    users = node.users

    if not current_user_id:
        current_user_id = users.first(verify_key=verify_key.encode(
            encoder=HexEncoder).decode("utf-8")).id

    requests = node.data_requests
    request = requests.first(id=request_id)

    # A user can get a request if he's the owner of that request
    # or has the can_triage_requests permission
    allowed = request.user_id == current_user_id or users.can_triage_requests(
        user_id=current_user_id)

    if allowed:
        request_json = model_to_json(request)
    else:
        raise AuthorizationError(
            "You're not allowed to get Request information!")

    return GetRequestResponse(
        address=msg.reply_to,
        status_code=200,
        content=request_json,
    )
Example #33
0
def get_dataset_metadata_msg(
    msg: GetDatasetMessage,
    node: AbstractNode,
    verify_key: VerifyKey,
) -> GetDatasetResponse:
    # Get Payload Content
    _dataset_id = msg.content.get("dataset_id", None)
    _current_user_id = msg.content.get("current_user", None)
    users = node.users
    if not _current_user_id:
        _current_user_id = users.first(verify_key=verify_key.encode(
            encoder=HexEncoder).decode("utf-8")).id

    _msg = {}

    storage = node.disk_store
    ds, objs = get_specific_dataset_and_relations(_dataset_id)
    if not ds:
        raise DatasetNotFoundError
    dataset_json = model_to_json(ds)
    dataset_json["data"] = [{
        "name": obj.name,
        "id": obj.obj,
        "dtype": obj.dtype,
        "shape": obj.shape
    } for obj in objs]

    return GetDatasetResponse(
        address=msg.reply_to,
        status_code=200,
        content=dataset_json,
    )
Example #34
0
            def on_authenticate_ok(principal):
                error = self._assign_principal(principal)
                if error:
                    return error

                self._verify_key = VerifyKey(principal[u"pubkey"], encoder=nacl.encoding.HexEncoder)

                extra = self._compute_challenge(channel_binding)
                return types.Challenge(self._authmethod, extra)
Example #35
0
    def post(self):
        """ device registration method.

            Args:
                username          (str): Username the device will be registered against.
                device_verify_key (str): NaCl verification key for the device.
                device_public_key (str): NaCl public key for the device.

            Returns:
                HTTP 422: If the username the user has requested to register the device
                under does not exist.

                HTTP 400: If either device_public_key or device_verify_key is not a valid
                NaCl key, or if any of the provided keys are not signed by the master
                verification key provided during user registration.

                device_verify_key, HTTP 201: If the device registration was successful.
        """

        parser = reqparse.RequestParser()
        parser.add_argument('username',
                            type=str,
                            required=True,
                            help="username is either blank or incorrect type.")
        parser.add_argument('device_verify_key',
                            type=str,
                            required=True,
                            help="device_verify_key is either blank or incorrect type.")
        parser.add_argument('device_public_key',
                            type=str,
                            required=True,
                            help="device_public_key is either blank or incorrect type.")
        args = parser.parse_args()

        #check if user exists already
        stored_key = query_db('''
                              SELECT master_verify_key
                              FROM users
                              WHERE username = ?;''',
                              [args['username']],
                              one=True)
        if stored_key is None:
            abort(422, message="Username does not exist.")

        #check if input is valid
        device_verify_key = reconstruct_signed_message(args['device_verify_key'])
        try:
            VerifyKey(device_verify_key.message, encoder=HexEncoder)
        except TypeError:
            abort(400,
                  message="The provided device_verify_key is not valid.")

        device_public_key = reconstruct_signed_message(args['device_public_key'])
        try:
            PublicKey(device_public_key.message, encoder=HexEncoder)
        except TypeError:
            abort(400,
                  message="The provided device_public_key is not valid.")

        #check to ensure keys are signed with master key
        master_verify_key = VerifyKey(stored_key['master_verify_key'], encoder=HexEncoder)

        try:
            master_verify_key.verify(device_verify_key)
        except BadSignatureError:
            abort(400,
                  message="Signature for device_verify_key is corrupt or invalid.")
        try:
            master_verify_key.verify(device_public_key)
        except BadSignatureError:
            abort(400,
                  message="Signature for device_public_key is corrupt or invalid.")

        #otherwise, add device
        query_db('''
                 INSERT INTO devices
                 VALUES(?, ?, ?);''',
                 [device_verify_key.message,
                  args['username'],
                  device_public_key.message])
        get_db().commit()

        return device_verify_key.message, 201
Example #36
0
    def hello(self, realm, details):
        # the channel binding requested by the client authenticating
        channel_binding = details.authextra.get(u"channel_binding", None)
        if channel_binding is not None and channel_binding not in [u"tls-unique"]:
            return types.Deny(message=u'invalid channel binding type "{}" requested'.format(channel_binding))
        else:
            self.log.info("WAMP-cryptosign CHANNEL BINDING requested: {binding}", binding=channel_binding)

        # remember the realm the client requested to join (if any)
        self._realm = realm

        # remember the authid the client wants to identify as (if any)
        self._authid = details.authid

        # use static principal database from configuration
        if self._config["type"] == "static":

            self._authprovider = u"static"

            # get client's pubkey, if it was provided in authextra
            pubkey = None
            if details.authextra and u"pubkey" in details.authextra:
                pubkey = details.authextra[u"pubkey"]

            # if the client provides it's public key, that's enough to identify,
            # and we can infer the authid from that. BUT: that requires that
            # there is a 1:1 relation between authid's and pubkey's !! see below (*)
            if self._authid is None:
                if pubkey:
                    # we do a naive search, but that is ok, since "static mode" is from
                    # node configuration, and won't contain a lot principals anyway
                    for _authid, _principal in self._config.get(u"principals", {}).items():
                        if pubkey in _principal[u"authorized_keys"]:
                            # (*): this is necessary to detect multiple authid's having the same pubkey
                            # in which case we couldn't reliably map the authid from the pubkey
                            if self._authid is None:
                                self._authid = _authid
                            else:
                                return types.Deny(
                                    message=u"cannot infer client identity from pubkey: multiple authids in principal database have this pubkey"
                                )
                    if self._authid is None:
                        return types.Deny(
                            message=u"cannot identify client: no authid requested and no principal found for provided extra.pubkey"
                        )
                else:
                    return types.Deny(
                        message=u"cannot identify client: no authid requested and no extra.pubkey provided"
                    )

            if self._authid in self._config.get(u"principals", {}):

                principal = self._config[u"principals"][self._authid]

                if pubkey and (pubkey not in principal[u"authorized_keys"]):
                    return types.Deny(
                        message=u"extra.pubkey provided does not match any one of authorized_keys for the principal"
                    )

                error = self._assign_principal(principal)
                if error:
                    return error

                self._verify_key = VerifyKey(pubkey, encoder=nacl.encoding.HexEncoder)

                extra = self._compute_challenge(channel_binding)
                return types.Challenge(self._authmethod, extra)

            else:
                return types.Deny(message=u'no principal with authid "{}" exists'.format(details.authid))

        elif self._config[u"type"] == u"dynamic":

            self._authprovider = u"dynamic"

            error = self._init_dynamic_authenticator()
            if error:
                return error

            self._session_details[u"authmethod"] = self._authmethod  # from AUTHMETHOD, via base
            self._session_details[u"authextra"] = details.authextra

            d = self._authenticator_session.call(self._authenticator, realm, details.authid, self._session_details)

            def on_authenticate_ok(principal):
                error = self._assign_principal(principal)
                if error:
                    return error

                self._verify_key = VerifyKey(principal[u"pubkey"], encoder=nacl.encoding.HexEncoder)

                extra = self._compute_challenge(channel_binding)
                return types.Challenge(self._authmethod, extra)

            def on_authenticate_error(err):
                return self._marshal_dynamic_authenticator_error(err)

            d.addCallbacks(on_authenticate_ok, on_authenticate_error)
            return d

        else:
            # should not arrive here, as config errors should be caught earlier
            return types.Deny(
                message=u'invalid authentication configuration (authentication type "{}" is unknown)'.format(
                    self._config["type"]
                )
            )
Example #37
0
import os, sys
from nacl.signing import VerifyKey

for channelID in os.listdir(sys.argv[1]):
    print "channel %s..:" % channelID[:5]
    vk = VerifyKey(channelID.decode("hex"))
    for msgid in os.listdir(os.path.join(sys.argv[1], channelID)):
        fn = os.path.join(sys.argv[1], channelID, msgid)
        sm = open(fn, "rb").read()
        assert sm.startswith("r0:")
        m = vk.verify(sm[len("r0:"):].decode("hex"))
        print " msg %s..: %s..." % (msgid[:10], repr(m[:12]))
Example #38
0
class PendingAuthCryptosign(PendingAuth):
    """
    Pending Cryptosign authentication.
    """

    log = make_logger()

    AUTHMETHOD = u"cryptosign"

    def __init__(self, session, config):
        PendingAuth.__init__(self, session, config)
        self._verify_key = None

        # https://tools.ietf.org/html/rfc5056
        # https://tools.ietf.org/html/rfc5929
        # https://www.ietf.org/proceedings/90/slides/slides-90-uta-0.pdf
        channel_id_hex = session._transport._transport_info.get(u"channel_id", None)
        if channel_id_hex:
            self._channel_id = binascii.a2b_hex(channel_id_hex)
        else:
            self._channel_id = None

        self._challenge = None
        self._expected_signed_message = None

        # create a map: pubkey -> authid
        # this is to allow clients to authenticate without specifying an authid
        if config["type"] == "static":
            self._pubkey_to_authid = {}
            for authid, principal in self._config.get(u"principals", {}).items():
                for pubkey in principal[u"authorized_keys"]:
                    self._pubkey_to_authid[pubkey] = authid

    def _compute_challenge(self, channel_binding):
        self._challenge = os.urandom(32)

        if self._channel_id:
            self._expected_signed_message = util.xor(self._challenge, self._channel_id)
        else:
            self._expected_signed_message = self._challenge

        extra = {u"challenge": binascii.b2a_hex(self._challenge)}
        return extra

    def hello(self, realm, details):
        # the channel binding requested by the client authenticating
        channel_binding = details.authextra.get(u"channel_binding", None)
        if channel_binding is not None and channel_binding not in [u"tls-unique"]:
            return types.Deny(message=u'invalid channel binding type "{}" requested'.format(channel_binding))
        else:
            self.log.info("WAMP-cryptosign CHANNEL BINDING requested: {binding}", binding=channel_binding)

        # remember the realm the client requested to join (if any)
        self._realm = realm

        # remember the authid the client wants to identify as (if any)
        self._authid = details.authid

        # use static principal database from configuration
        if self._config["type"] == "static":

            self._authprovider = u"static"

            # get client's pubkey, if it was provided in authextra
            pubkey = None
            if details.authextra and u"pubkey" in details.authextra:
                pubkey = details.authextra[u"pubkey"]

            # if the client provides it's public key, that's enough to identify,
            # and we can infer the authid from that. BUT: that requires that
            # there is a 1:1 relation between authid's and pubkey's !! see below (*)
            if self._authid is None:
                if pubkey:
                    # we do a naive search, but that is ok, since "static mode" is from
                    # node configuration, and won't contain a lot principals anyway
                    for _authid, _principal in self._config.get(u"principals", {}).items():
                        if pubkey in _principal[u"authorized_keys"]:
                            # (*): this is necessary to detect multiple authid's having the same pubkey
                            # in which case we couldn't reliably map the authid from the pubkey
                            if self._authid is None:
                                self._authid = _authid
                            else:
                                return types.Deny(
                                    message=u"cannot infer client identity from pubkey: multiple authids in principal database have this pubkey"
                                )
                    if self._authid is None:
                        return types.Deny(
                            message=u"cannot identify client: no authid requested and no principal found for provided extra.pubkey"
                        )
                else:
                    return types.Deny(
                        message=u"cannot identify client: no authid requested and no extra.pubkey provided"
                    )

            if self._authid in self._config.get(u"principals", {}):

                principal = self._config[u"principals"][self._authid]

                if pubkey and (pubkey not in principal[u"authorized_keys"]):
                    return types.Deny(
                        message=u"extra.pubkey provided does not match any one of authorized_keys for the principal"
                    )

                error = self._assign_principal(principal)
                if error:
                    return error

                self._verify_key = VerifyKey(pubkey, encoder=nacl.encoding.HexEncoder)

                extra = self._compute_challenge(channel_binding)
                return types.Challenge(self._authmethod, extra)

            else:
                return types.Deny(message=u'no principal with authid "{}" exists'.format(details.authid))

        elif self._config[u"type"] == u"dynamic":

            self._authprovider = u"dynamic"

            error = self._init_dynamic_authenticator()
            if error:
                return error

            self._session_details[u"authmethod"] = self._authmethod  # from AUTHMETHOD, via base
            self._session_details[u"authextra"] = details.authextra

            d = self._authenticator_session.call(self._authenticator, realm, details.authid, self._session_details)

            def on_authenticate_ok(principal):
                error = self._assign_principal(principal)
                if error:
                    return error

                self._verify_key = VerifyKey(principal[u"pubkey"], encoder=nacl.encoding.HexEncoder)

                extra = self._compute_challenge(channel_binding)
                return types.Challenge(self._authmethod, extra)

            def on_authenticate_error(err):
                return self._marshal_dynamic_authenticator_error(err)

            d.addCallbacks(on_authenticate_ok, on_authenticate_error)
            return d

        else:
            # should not arrive here, as config errors should be caught earlier
            return types.Deny(
                message=u'invalid authentication configuration (authentication type "{}" is unknown)'.format(
                    self._config["type"]
                )
            )

    def authenticate(self, signed_message):
        """
        Verify the signed message sent by the client. With WAMP-cryptosign, this must be 96 bytes (as a string
        in HEX encoding): the concatenation of the Ed25519 signature (64 bytes) and the 32 bytes we sent
        as a challenge previously, XORed with the 32 bytes transport channel ID (if available).
        """
        try:
            if type(signed_message) != six.text_type:
                return types.Deny(message=u"invalid type {} for signed message".format(type(signed_message)))

            try:
                signed_message = binascii.a2b_hex(signed_message)
            except TypeError:
                return types.Deny(message=u"signed message is invalid (not a HEX encoded string)")

            if len(signed_message) != 96:
                return types.Deny(
                    message=u"signed message has invalid length (was {}, but should have been 96)".format(
                        len(signed_message)
                    )
                )

            # now verify the signed message versus the client public key ..
            try:
                message = self._verify_key.verify(signed_message)
            except BadSignatureError:
                return types.Deny(message=u"signed message has invalid signature")

            # .. and check that the message signed by the client is really what we expect
            if message != self._expected_signed_message:
                return types.Deny(message=u"message signed is bogus")

            # signature was valid _and_ the message that was signed is equal to
            # what we expected => accept the client
            return self._accept()

        except Exception as e:

            # should not arrive here .. but who knows
            return types.Deny(message=u"internal error: {}".format(e))
Example #39
0
    def hello(self, realm, details):

        # remember the realm the client requested to join (if any)
        self._realm = realm

        # remember the authid the client wants to identify as (if any)
        self._authid = details.authid

        # use static principal database from configuration
        if self._config['type'] == 'static':

            self._authprovider = u'static'

            # get client's pubkey, if it was provided in authextra
            pubkey = None
            if details.authextra and u'pubkey' in details.authextra:
                pubkey = details.authextra[u'pubkey']

            # if the client provides it's public key, that's enough to identify,
            # and we can infer the authid from that. BUT: that requires that
            # there is a 1:1 relation between authid's and pubkey's !! see below (*)
            if self._authid is None:
                if pubkey:
                    # we do a naive search, but that is ok, since "static mode" is from
                    # node configuration, and won't contain a lot principals anyway
                    for _authid, _principal in self._config.get(u'principals', {}).items():
                        if _principal[u'pubkey'] == pubkey:
                            # (*): this is necessary to detect multiple authid's having the same pubkey
                            # in which case we couldn't reliably map the authid from the pubkey
                            if self._authid is None:
                                self._authid = _authid
                            else:
                                return types.Deny(message=u'cannot infer client identity from pubkey: multiple authids in principal database have this pubkey')
                    if self._authid is None:
                        return types.Deny(message=u'cannot identify client: no authid requested and no principal found for provided extra.pubkey')
                else:
                    return types.Deny(message=u'cannot identify client: no authid requested and no extra.pubkey provided')

            if self._authid in self._config.get(u'principals', {}):

                principal = self._config[u'principals'][self._authid]

                if pubkey and (principal[u'pubkey'] != pubkey):
                    return types.Deny(message=u'extra.pubkey provided does not match the one in principal database')

                error = self._assign_principal(principal)
                if error:
                    return error

                self._verify_key = VerifyKey(principal[u'pubkey'], encoder=nacl.encoding.HexEncoder)

                extra, self._challenge = self._compute_challenge()
                return types.Challenge(self._authmethod, extra)
            else:
                return types.Deny(message=u'no principal with authid "{}" exists'.format(details.authid))

        elif self._config[u'type'] == u'dynamic':

            self._authprovider = u'dynamic'

            error = self._init_dynamic_authenticator()
            if error:
                return error

            d = self._authenticator_session.call(self._authenticator, realm, details.authid, self._session_details)

            def on_authenticate_ok(principal):
                error = self._assign_principal(principal)
                if error:
                    return error

                self._verify_key = VerifyKey(principal[u'pubkey'], encoder=nacl.encoding.HexEncoder)

                extra, self._challenge = self._compute_challenge()
                return types.Challenge(self._authmethod, extra)

            def on_authenticate_error(err):
                return self._marshal_dynamic_authenticator_error(err)

            d.addCallbacks(on_authenticate_ok, on_authenticate_error)
            return d

        else:
            # should not arrive here, as config errors should be caught earlier
            return types.Deny(message=u'invalid authentication configuration (authentication type "{}" is unknown)'.format(self._config['type']))
Example #40
0
    def post(self):
        """ message delivery method.

            Args:
                device_verify_key        (str): NaCl verification for the device the user
                is sending the query as.
                signed_device_verify_key (str): base64 encoded, signed device_verify_key to
                ensure that the user is only fetching messages for devices which they posess
                the full device verification keypair for.

            Returns:
                HTTP 422: If the device_verify_key provided by the user does not exist.

                HTTP 400: If the provided signed_device_verify_key is not signed by the
                correct device_verify_key provided during device registration.

                messages (dict): A dictionary containing all messages to be delivered to
                the requested device.
        """

        parser = reqparse.RequestParser()
        parser.add_argument('device_verify_key',
                            type=str,
                            required=True,
                            help="device_verify_key is either blank or incorrect type.")
        parser.add_argument('signed_device_verify_key',
                            type=str,
                            required=True,
                            help="signed_device_verify_key is either blank or incorrect type.")
        args = parser.parse_args()

        #check if user exists already
        stored_key = query_db('''
                              SELECT device_verify_key
                              FROM devices
                              WHERE device_verify_key = ?;''',
                              [args['device_verify_key']],
                              one=True)
        if stored_key is None:
            abort(422, message="Device does not exist.")

        signed_device_verify_key = reconstruct_signed_message(args['signed_device_verify_key'])

        device_verify_key = VerifyKey(stored_key['device_verify_key'], encoder=HexEncoder)

        try:
            device_verify_key.verify(signed_device_verify_key)
        except nacl.exceptions.BadSignatureError:
            abort(400,
                  message="Signature for provided username is corrupt or invalid.")

        messages = {}
        for row in query_db('''
                            SELECT message_public_key, reply_to, message_contents
                            FROM messages
                            JOIN message_recipients
                            ON messages.message_id = message_recipients.message_id
                            WHERE device_verify_key=?;''',
                            [signed_device_verify_key.message]):
            if row is not None:
                messages[row[0]] = json.dumps({'reply_to': row[1], 'message_manifest': row[2]})
                query_db('''
                         DELETE FROM message_recipients
                         WHERE device_verify_key=?;''',
                         [signed_device_verify_key.message])
                query_db('''
                         DELETE FROM messages
                         WHERE message_id
                         NOT IN (
                                 SELECT message_id
                                 FROM message_recipients);''')
                get_db().commit()

        return {'messages': messages}
Example #41
0
    def post(self):
        """ message sending method.

            Args:
                device_verify_key     (str): NaCl verification key for the device the user
                is sending the query as.
                destination_usernames (str): base64 encoded, signed, JSON encapsulated
                list of destination usernames.
                message_public_key    (str): base64 encoded, signed, ephemeral public
                key that was used to encrypt the message.
                message_contents      (str): base64 encoded, signed message contents.

            Returns:
                HTTP 422: If the device_verify_key provided by the user does not exist.

                HTTP 400: If the provided destination_usernames, message_public_key or
                message_contents is not signed by the correct device_verify_key provided
                during device registration, or if the provided message_public_key is not
                a valid NaCl public key.

                device_verify_key, HTTP 201: If the message was sent successfully.

        """

        parser = reqparse.RequestParser()
        parser.add_argument('device_verify_key',
                            type=str,
                            required=True,
                            help="device_verify_key is either blank or incorrect type.")
        parser.add_argument('destination_usernames',
                            type=str,
                            required=True,
                            help="destination_usernames is either blank or incorrect type.")
        parser.add_argument('message_public_key',
                            type=str,
                            required=True,
                            help="message_public_key is either blank or incorrect type.")
        parser.add_argument('message_contents',
                            type=str,
                            required=True,
                            help="message_contents is either blank or incorrect type.")
        args = parser.parse_args()

        #check if user exists already
        device_record = query_db('''
                                 SELECT username, device_verify_key
                                 FROM devices
                                 WHERE device_verify_key = ?;''',
                                 [args['device_verify_key']],
                                 one=True)
        if device_record is None:
            abort(422, message="Device does not exist.")
        else:
            stored_key = device_record['device_verify_key']
            username = device_record['username']

        destination_usernames = reconstruct_signed_message(args['destination_usernames'])

        message_contents = reconstruct_signed_message(args['message_contents'])

        message_public_key = reconstruct_signed_message(args['message_public_key'])
        try:
            PublicKey(message_public_key.message, encoder=HexEncoder)
        except TypeError:
            abort(400, message='Provided message_public_key is not a valid public key.')

        device_verify_key = VerifyKey(stored_key, encoder=HexEncoder)

        try:
            device_verify_key.verify(destination_usernames)
        except BadSignatureError:
            abort(400, message="Signature for provided username is corrupt or invalid.")
        try:
            device_verify_key.verify(message_contents)
        except BadSignatureError:
            abort(400, message="Signature for provided message_contents is corrupt or invalid.")
        try:
            device_verify_key.verify(message_public_key)
        except BadSignatureError:
            abort(400, message="Signature for provided message_public_key is corrupt or invalid.")

        message_id = b64encode(message_contents.signature)
        query_db('''
                 INSERT INTO messages
                 VALUES(?, ?, ?, ?);''',
                 [message_id,
                  username,
                  b64encode(message_contents.message),
                  b64encode(message_public_key.message)])
        get_db().commit()

        for dest_user in json.loads(destination_usernames.message)['destination_usernames']:

            for row in query_db('''
                                SELECT device_verify_key
                                FROM devices
                                WHERE username=?;''',
                                [dest_user]):
                query_db('''
                         INSERT INTO message_recipients
                         VALUES(?, ?);''',
                         [row['device_verify_key'],
                          message_id])
                get_db().commit()


        return args['device_verify_key'], 201
Example #42
0
class PendingAuthCryptosign(PendingAuth):
    """
    Pending Cryptosign authentication.
    """

    AUTHMETHOD = u'cryptosign'

    def __init__(self, session, config):
        PendingAuth.__init__(self, session, config)
        self._verify_key = None
        if config['type'] == 'static':
            self._pubkey_to_authid = {}
            for authid, principal in self._config.get(u'principals', {}).items():
                self._pubkey_to_authid[principal[u'pubkey']] = authid

    def _compute_challenge(self):
        challenge = binascii.b2a_hex(os.urandom(32))
        extra = {
            u'challenge': challenge
        }
        return extra, challenge

    def hello(self, realm, details):

        # remember the realm the client requested to join (if any)
        self._realm = realm

        # remember the authid the client wants to identify as (if any)
        self._authid = details.authid

        # use static principal database from configuration
        if self._config['type'] == 'static':

            self._authprovider = u'static'

            # get client's pubkey, if it was provided in authextra
            pubkey = None
            if details.authextra and u'pubkey' in details.authextra:
                pubkey = details.authextra[u'pubkey']

            # if the client provides it's public key, that's enough to identify,
            # and we can infer the authid from that. BUT: that requires that
            # there is a 1:1 relation between authid's and pubkey's !! see below (*)
            if self._authid is None:
                if pubkey:
                    # we do a naive search, but that is ok, since "static mode" is from
                    # node configuration, and won't contain a lot principals anyway
                    for _authid, _principal in self._config.get(u'principals', {}).items():
                        if _principal[u'pubkey'] == pubkey:
                            # (*): this is necessary to detect multiple authid's having the same pubkey
                            # in which case we couldn't reliably map the authid from the pubkey
                            if self._authid is None:
                                self._authid = _authid
                            else:
                                return types.Deny(message=u'cannot infer client identity from pubkey: multiple authids in principal database have this pubkey')
                    if self._authid is None:
                        return types.Deny(message=u'cannot identify client: no authid requested and no principal found for provided extra.pubkey')
                else:
                    return types.Deny(message=u'cannot identify client: no authid requested and no extra.pubkey provided')

            if self._authid in self._config.get(u'principals', {}):

                principal = self._config[u'principals'][self._authid]

                if pubkey and (principal[u'pubkey'] != pubkey):
                    return types.Deny(message=u'extra.pubkey provided does not match the one in principal database')

                error = self._assign_principal(principal)
                if error:
                    return error

                self._verify_key = VerifyKey(principal[u'pubkey'], encoder=nacl.encoding.HexEncoder)

                extra, self._challenge = self._compute_challenge()
                return types.Challenge(self._authmethod, extra)
            else:
                return types.Deny(message=u'no principal with authid "{}" exists'.format(details.authid))

        elif self._config[u'type'] == u'dynamic':

            self._authprovider = u'dynamic'

            error = self._init_dynamic_authenticator()
            if error:
                return error

            d = self._authenticator_session.call(self._authenticator, realm, details.authid, self._session_details)

            def on_authenticate_ok(principal):
                error = self._assign_principal(principal)
                if error:
                    return error

                self._verify_key = VerifyKey(principal[u'pubkey'], encoder=nacl.encoding.HexEncoder)

                extra, self._challenge = self._compute_challenge()
                return types.Challenge(self._authmethod, extra)

            def on_authenticate_error(err):
                return self._marshal_dynamic_authenticator_error(err)

            d.addCallbacks(on_authenticate_ok, on_authenticate_error)
            return d

        else:
            # should not arrive here, as config errors should be caught earlier
            return types.Deny(message=u'invalid authentication configuration (authentication type "{}" is unknown)'.format(self._config['type']))

    def authenticate(self, signature):
        # signatures in WAMP are strings, hence we roundtrip Hex
        signature = binascii.a2b_hex(signature)

        signed = SignedMessage(signature)
        try:
            # now verify the signed message versus the client public key
            self._verify_key.verify(signed)

            # signature was valid: accept the client
            return self._accept()

        except BadSignatureError:

            # signature was invalid: deny the client
            return types.Deny(message=u"invalid signature")

        except Exception as e:

            # should not arrive here .. but who knows
            return types.Deny(message=u"internal error: {}".format(e))
Example #43
0
    def processM2(self, msg):
        #print "processM2", repr(msg[:10]), "...", self.petname
        assert self.theirTempPubkey
        nonce_and_ciphertext = msg
        my_privkey = self.getMyTempPrivkey()
        b = Box(my_privkey, self.theirTempPubkey)
        #nonce = msg[:Box.NONCE_SIZE]
        #ciphertext = msg[Box.NONCE_SIZE:]
        #print "DECRYPTING n+ct", len(msg), msg.encode("hex")
        body = b.decrypt(nonce_and_ciphertext)
        if not body.startswith("i0:m2a:"):
            raise ValueError("expected i0:m2a:, got '%r'" % body[:20])
        verfkey_and_signedBody = body[len("i0:m2a:"):]
        theirVerfkey = VerifyKey(verfkey_and_signedBody[:32])
        signedBody = verfkey_and_signedBody[32:]
        body = theirVerfkey.verify(signedBody)
        check_myTempPubkey = body[:32]
        check_theirTempPubkey = body[32:64]
        their_channel_record_json = body[64:].decode("utf-8")
        #print " binding checks:"
        #print " check_myTempPubkey", check_myTempPubkey.encode("hex")
        #print " my real tempPubkey", my_privkey.public_key.encode(Hex)
        #print " check_theirTempPubkey", check_theirTempPubkey.encode("hex")
        #print " first theirTempPubkey", self.theirTempPubkey.encode(Hex)
        if check_myTempPubkey != my_privkey.public_key.encode():
            raise ValueError("binding failure myTempPubkey")
        if check_theirTempPubkey != self.theirTempPubkey.encode():
            raise ValueError("binding failure theirTempPubkey")

        them = json.loads(their_channel_record_json)
        me = self.getMyPrivateChannelData()
        addressbook_id = self.db.insert(
            "INSERT INTO addressbook"
            " (petname, acked,"
            "  next_outbound_seqnum, my_signkey,"
            "  their_channel_record_json,"
            "  my_CID_key, next_CID_token,"
            "  highest_inbound_seqnum,"
            "  my_old_channel_privkey, my_new_channel_privkey,"
            "  they_used_new_channel_key, their_verfkey)"
            " VALUES (?,?, "
            "         ?,?,"
            "         ?,"
            "         ?,?," # my_CID_key, next_CID_token
            "         ?,"   # highest_inbound_seqnum
            "         ?,?,"
            "         ?,?)",
            (self.petname, 0,
             1, me["my_signkey"],
             json.dumps(them),
             me["my_CID_key"], None,
             0,
             me["my_old_channel_privkey"],
             me["my_new_channel_privkey"],
             0, theirVerfkey.encode(Hex) ),
            "addressbook")
        self.db.update("UPDATE invitations SET addressbook_id=?"
                       " WHERE id=?", (addressbook_id, self.iid),
                       "invitations", self.iid)

        msg3 = "i0:m3:ACK-"+os.urandom(16)
        self.send(msg3)
        self.nextExpectedMessage = 3