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)
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}
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
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']
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
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
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
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)
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, )
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!"}, )
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)
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)
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
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, )
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!"}, )
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!"}, )
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)})
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, )
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)})
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, )
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!"}, )
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
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)
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, )
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!"}, )
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!"}, )
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
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, )
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!"}, )
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, )
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, )
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, )
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 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
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"] ) )
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]))
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))
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 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}
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
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))
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