def archiving_accepted(self, form): negotiated = {} ask_user = {} not_acceptable = [] if form['logging'] not in self.archiving_logging_preference(): raise self.negotiated['logging'] = form['logging'] accept = nbxmpp.Message() feature = accept.NT.feature feature.setNamespace(nbxmpp.NS_FEATURE) result = nbxmpp.DataForm(typ='result') result.addChild(node=nbxmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn')) result.addChild(node=nbxmpp.DataField(name='accept', value='1')) feature.addChild(node=result) self.send(accept) if self.negotiated['logging'] == 'mustnot': self.loggable = False log.debug('archiving session accepted: %s' % self.loggable) self.status = 'active' self.archiving = True if self.control: self.control.print_archiving_session_details()
def read_invitation(self, msg): invite = msg.getTag('invite', namespace=NS_GAMES) game = invite.getTag('game') x = game.getTag('x', namespace='jabber:x:data') form = nbxmpp.DataForm(node=str(x)) if form.getField('role'): self.role_o = form.getField('role').getValues()[0] else: self.role_o = 'x' if form.getField('rows'): self.rows = int(form.getField('rows').getValues()[0]) else: self.rows = 3 if form.getField('cols'): self.cols = int(form.getField('cols').getValues()[0]) else: self.cols = 3 # number in a row needed to win if form.getField('strike'): self.strike = int(form.getField('strike').getValues()[0]) else: self.strike = 3
def negotiate_archiving(self): self.negotiated = {} request = nbxmpp.Message() feature = request.NT.feature feature.setNamespace(nbxmpp.NS_FEATURE) x = nbxmpp.DataForm(typ='form') x.addChild(node=nbxmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn', typ='hidden')) x.addChild(node=nbxmpp.DataField(name='accept', value='1', typ='boolean', required=True)) x.addChild(node=nbxmpp.DataField(name='logging', typ='list-single', options=self.archiving_logging_preference(), required=True)) x.addChild(node=nbxmpp.DataField(name='disclosure', typ='list-single', options=['never'], required=True)) x.addChild(node=nbxmpp.DataField(name='security', typ='list-single', options=['none'], required=True)) feature.addChild(node=x) self.status = 'requested-archiving' self.send(request)
def get_archive_query(self, query_id, jid=None, start=None, end=None, with_=None, after=None, max_=30): # Muc archive query? namespace = muc_caps_cache.get_mam_namespace(jid) if namespace is None: # Query to our own archive namespace = self.archiving_namespace iq = nbxmpp.Iq('set', to=jid) query = iq.addChild('query', namespace=namespace) form = query.addChild(node=nbxmpp.DataForm(typ='submit')) field = nbxmpp.DataField(typ='hidden', name='FORM_TYPE', value=namespace) form.addChild(node=field) if start: field = nbxmpp.DataField(typ='text-single', name='start', value=start.strftime('%Y-%m-%dT%H:%M:%SZ')) form.addChild(node=field) if end: field = nbxmpp.DataField(typ='text-single', name='end', value=end.strftime('%Y-%m-%dT%H:%M:%SZ')) form.addChild(node=field) if with_: field = nbxmpp.DataField(typ='jid-single', name='with', value=with_) form.addChild(node=field) set_ = query.setTag('set', namespace=nbxmpp.NS_RSM) set_.setTagData('max', max_) if after: set_.setTagData('after', after) query.setAttr('queryid', query_id) return iq
def _siResultCB(self, con, iq_obj): file_props = FilesProp.getFileProp(self.name, iq_obj.getAttr('id')) if not file_props: return if file_props.request_id: # we have already sent streamhosts info return file_props.receiver = self._ft_get_from(iq_obj) si = iq_obj.getTag('si') file_tag = si.getTag('file') range_tag = None if file_tag: range_tag = file_tag.getTag('range') if range_tag: offset = range_tag.getAttr('offset') if offset: file_props.offset = int(offset) length = range_tag.getAttr('length') if length: file_props.length = int(length) feature = si.setTag('feature') if feature.getNamespace() != nbxmpp.NS_FEATURE: return form_tag = feature.getTag('x') form = nbxmpp.DataForm(node=form_tag) field = form.getField('stream-method') if field.getValue() == nbxmpp.NS_BYTESTREAM: self._send_socks5_info(file_props) raise nbxmpp.NodeProcessed if field.getValue() == nbxmpp.NS_IBB: sid = file_props.sid file_props.transport_sid = sid fp = open(file_props.file_name, 'r') self.OpenStream(sid, file_props.receiver, fp) raise nbxmpp.NodeProcessed
def request_archive(self, start=None, end=None, with_=None, after=None, max=30): iq_ = nbxmpp.Iq('set') query = iq_.addChild('query', namespace=nbxmpp.NS_MAM) x = query.addChild(node=nbxmpp.DataForm(typ='submit')) x.addChild(node=nbxmpp.DataField( typ='hidden', name='FORM_TYPE', value=nbxmpp.NS_MAM)) if start: x.addChild(node=nbxmpp.DataField( typ='text-single', name='start', value=start)) if end: x.addChild(node=nbxmpp.DataField( typ='text-single', name='end', value=end)) if with_: x.addChild(node=nbxmpp.DataField( typ='jid-single', name='with', value=with_)) set_ = query.setTag('set', namespace=nbxmpp.NS_RSM) set_.setTagData('max', max) if after: set_.setTagData('after', after) queryid_ = self.connection.getAnID() query.setAttr('queryid', queryid_) id_ = self.connection.getAnID() iq_.setID(id_) self.awaiting_answers[queryid_] = (MAM_RESULTS_ARRIVED, ) self.connection.send(iq_)
def append(self, stanza): jid = stanza.getFrom() identities, features, data = [], [], [] query_childs = stanza.getQueryChildren() if not query_childs: from gajim.common import app app.log('gajim.muc').warning('%s returned empty disco info', jid) return for child in query_childs: if child.getName() == 'identity': attr = {} for key in child.getAttrs().keys(): attr[key] = child.getAttr(key) identities.append(attr) elif child.getName() == 'feature': features.append(child.getAttr('var')) elif child.getName() == 'x': if child.getNamespace() == nbxmpp.NS_DATA: data.append(nbxmpp.DataForm(node=child)) if nbxmpp.NS_MUC not in features: # Not a MUC, dont cache info return self.cache[jid] = self.DiscoInfo(identities, features, data)
def parse_info_response(cls, stanza): identities, features, data, node = [], [], [], None q = stanza.getTag('query') node = q.getAttr('node') if not node: node = '' qc = stanza.getQueryChildren() if not qc: qc = [] for i in qc: if i.getName() == 'identity': attr = {} for key in i.getAttrs().keys(): attr[key] = i.getAttr(key) identities.append(attr) elif i.getName() == 'feature': var = i.getAttr('var') if var: features.append(var) elif i.getName() == 'x' and i.getNamespace() == nbxmpp.NS_DATA: data.append(nbxmpp.DataForm(node=i)) return identities, features, data, node
def send_file_request(self, file_props): """ Send iq for new FT request """ if not self.connection or self.connected < 2: return file_props.sender = self._ft_get_our_jid() fjid = self._ft_get_receiver_jid(file_props) iq = nbxmpp.Iq(to=fjid, typ='set') iq.setID(file_props.sid) si = iq.setTag('si', namespace=nbxmpp.NS_SI) si.setAttr('profile', nbxmpp.NS_FILE) si.setAttr('id', file_props.sid) file_tag = si.setTag('file', namespace=nbxmpp.NS_FILE) file_tag.setAttr('name', file_props.name) file_tag.setAttr('size', file_props.size) desc = file_tag.setTag('desc') if file_props.desc: desc.setData(file_props.desc) file_tag.setTag('range') feature = si.setTag('feature', namespace=nbxmpp.NS_FEATURE) _feature = nbxmpp.DataForm(typ='form') feature.addChild(node=_feature) field = _feature.setField('stream-method') field.setAttr('type', 'list-single') field.addOption(nbxmpp.NS_BYTESTREAM) field.addOption(nbxmpp.NS_IBB) self.connection.send(iq)
def send_invitation(self): msg = nbxmpp.Message() invite = msg.NT.invite invite.setNamespace(NS_GAMES) invite.setAttr('type', 'new') game = invite.NT.game game.setAttr('var', NS_GAMES_TICTACTOE) x = nbxmpp.DataForm(typ='submit') f = x.setField('role') f.setType('list-single') f.setValue('x') f = x.setField('rows') f.setType('text-single') f.setValue(str(self.base.plugin.config['board_size'])) f = x.setField('cols') f.setType('text-single') f.setValue(str(self.base.plugin.config['board_size'])) f = x.setField('strike') f.setType('text-single') f.setValue(str(self.base.plugin.config['board_size'])) game.addChild(node=x) self.send(msg)
def send_file_approval(self, file_props): """ Send iq, confirming that we want to download the file """ # user response to ConfirmationDialog may come after we've disconneted if not self.connection or self.connected < 2: return # file transfer initiated by a jingle session log.info("send_file_approval: jingle session accept") if file_props.session_type == 'jingle': session = self.get_jingle_session(file_props.sender, file_props.sid) if not session: return content = None for c in session.contents.values(): if c.transport.sid == file_props.transport_sid: content = c break if not content: return if not session.accepted: content = session.get_content('file', content.name) if content.use_security: fingerprint = content.x509_fingerprint if not jingle_xtls.check_cert( gajim.get_jid_without_resource(file_props.sender), fingerprint): id_ = jingle_xtls.send_cert_request( self, file_props.sender) jingle_xtls.key_exchange_pend(id_, content.on_cert_received, []) return session.approve_session() session.approve_content('file', content.name) return iq = nbxmpp.Iq(to=file_props.sender, typ='result') iq.setAttr('id', file_props.request_id) si = iq.setTag('si', namespace=nbxmpp.NS_SI) if file_props.offset: file_tag = si.setTag('file', namespace=nbxmpp.NS_FILE) range_tag = file_tag.setTag('range') range_tag.setAttr('offset', file_props.offset) feature = si.setTag('feature', namespace=nbxmpp.NS_FEATURE) _feature = nbxmpp.DataForm(typ='submit') feature.addChild(node=_feature) field = _feature.setField('stream-method') field.delAttr('type') if nbxmpp.NS_BYTESTREAM in file_props.stream_methods: field.setValue(nbxmpp.NS_BYTESTREAM) else: file_props.transport_sid = file_props.sid field.setValue(nbxmpp.NS_IBB) self.connection.send(iq)
def request_voice(self, room): if not app.account_is_connected(self._account): return message = nbxmpp.Message(to=room) x = nbxmpp.DataForm(typ='submit') x.addChild(node=nbxmpp.DataField(name='FORM_TYPE', value=nbxmpp.NS_MUC + '#request')) x.addChild(node=nbxmpp.DataField( name='muc#role', value='participant', typ='text-single')) message.addChild(node=x) self._con.connection.send(message)
def on_cancel_button_clicked(self, widget): rejection = nbxmpp.Message(self.jid) rejection.setThread(self.session.thread_id) feature = rejection.NT.feature feature.setNamespace(nbxmpp.NS_FEATURE) x = nbxmpp.DataForm(typ='submit') x.addChild(node=nbxmpp.DataField('FORM_TYPE', value='urn:xmpp:ssn')) x.addChild( node=nbxmpp.DataField('accept', value='false', typ='boolean')) feature.addChild(node=x) app.connections[self.account].send_stanza(rejection) self.window.destroy()
def reject_negotiation(self, body=None): msg = nbxmpp.Message() feature = msg.NT.feature feature.setNamespace(nbxmpp.NS_FEATURE) x = nbxmpp.DataForm(typ='submit') x.addChild(node=nbxmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn')) x.addChild(node=nbxmpp.DataField(name='accept', value='0')) feature.addChild(node=x) if body: msg.setBody(body) self.send(msg) self.cancelled_negotiation()
def terminate(self, send_termination = True): # only send termination message if we've sent a message and think they # have XEP-0201 support if send_termination and self.last_send > 0 and \ (self.received_thread_id or self.last_receive == 0): msg = nbxmpp.Message() feature = msg.NT.feature feature.setNamespace(nbxmpp.NS_FEATURE) x = nbxmpp.DataForm(typ='submit') x.addChild(node=nbxmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn')) x.addChild(node=nbxmpp.DataField(name='terminate', value='1')) feature.addChild(node=x) self.send(msg) self.status = None
def respond_archiving(self, form): field = form.getField('logging') options = [x[1] for x in field.getOptions()] values = field.getValues() logging = self.archiving_logging_preference(options) self.negotiated['logging'] = logging response = nbxmpp.Message() feature = response.NT.feature feature.setNamespace(nbxmpp.NS_FEATURE) x = nbxmpp.DataForm(typ='submit') x.addChild(node=nbxmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn')) x.addChild(node=nbxmpp.DataField(name='accept', value='true')) x.addChild(node=nbxmpp.DataField(name='logging', value=logging)) self.status = 'responded-archiving' feature.addChild(node=x) if not logging: response = nbxmpp.Error(response, nbxmpp.ERR_NOT_ACCEPTABLE) feature = nbxmpp.Node(nbxmpp.NS_FEATURE + ' feature') n = nbxmpp.Node('field') n['var'] = 'logging' feature.addChild(node=n) response.T.error.addChild(node=feature) self.send(response)
def negotiate_e2e(self, sigmai): self.negotiated = {} request = nbxmpp.Message() feature = request.NT.feature feature.setNamespace(nbxmpp.NS_FEATURE) x = nbxmpp.DataForm(typ='form') x.addChild(node=nbxmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn', typ='hidden')) x.addChild(node=nbxmpp.DataField(name='accept', value='1', typ='boolean', required=True)) # this field is incorrectly called 'otr' in XEPs 0116 and 0217 x.addChild(node=nbxmpp.DataField(name='logging', typ='list-single', options=self.logging_preference(), required=True)) # unsupported options: 'disabled', 'enabled' x.addChild(node=nbxmpp.DataField(name='disclosure', typ='list-single', options=['never'], required=True)) x.addChild(node=nbxmpp.DataField(name='security', typ='list-single', options=['e2e'], required=True)) x.addChild(node=nbxmpp.DataField(name='crypt_algs', value='aes128-ctr', typ='hidden')) x.addChild(node=nbxmpp.DataField(name='hash_algs', value='sha256', typ='hidden')) x.addChild(node=nbxmpp.DataField(name='compress', value='none', typ='hidden')) # unsupported options: 'iq', 'presence' x.addChild(node=nbxmpp.DataField(name='stanzas', typ='list-multi', options=['message'])) x.addChild(node=nbxmpp.DataField(name='init_pubkey', options=['none', 'key', 'hash'], typ='list-single')) # FIXME store key, use hash x.addChild(node=nbxmpp.DataField(name='resp_pubkey', options=['none', 'key'], typ='list-single')) x.addChild(node=nbxmpp.DataField(name='ver', value='1.0', typ='hidden')) x.addChild(node=nbxmpp.DataField(name='rekey_freq', value='4294967295', typ='hidden')) x.addChild(node=nbxmpp.DataField(name='sas_algs', value='sas28x5', typ='hidden')) x.addChild(node=nbxmpp.DataField(name='sign_algs', value='http://www.w3.org/2000/09/xmldsig#rsa-sha256', typ='hidden')) self.n_s = crypto.generate_nonce() x.addChild(node=nbxmpp.DataField(name='my_nonce', value=base64.b64encode(self.n_s).decode('utf-8'), typ='hidden')) modp_options = [ int(g) for g in gajim.config.get('esession_modp').split( ',') ] x.addChild(node=nbxmpp.DataField(name='modp', typ='list-single', options=[[None, y] for y in modp_options])) x.addChild(node=self.make_dhfield(modp_options, sigmai)) self.sigmai = sigmai self.form_s = ''.join(nbxmpp.c14n.c14n(el, self._is_buggy_gajim()) for \ el in x.getChildren()) feature.addChild(node=x) self.status = 'requested-e2e' self.send(request)
def respond_e2e_bob(self, form, negotiated, not_acceptable): """ 4.3 esession response (bob) """ response = nbxmpp.Message() feature = response.NT.feature feature.setNamespace(nbxmpp.NS_FEATURE) x = nbxmpp.DataForm(typ='submit') x.addChild(node=nbxmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn')) x.addChild(node=nbxmpp.DataField(name='accept', value='true')) for name in negotiated: # some fields are internal and should not be sent if not name in ('send_pubkey', 'recv_pubkey'): x.addChild(node=nbxmpp.DataField(name=name, value=negotiated[name])) self.negotiated = negotiated # the offset of the group we chose (need it to match up with the dhhash) group_order = 0 modp_f = form.getField('modp') if not modp_f: return self.modp = int(modp_f.getOptions()[group_order][1]) x.addChild(node=nbxmpp.DataField(name='modp', value=self.modp)) g = dh.generators[self.modp] p = dh.primes[self.modp] self.n_o = base64.b64decode(form['my_nonce']) dhhashes_f = form.getField('dhhashes') if not dhhashes_f: return dhhashes = dhhashes_f.getValues() self.negotiated['He'] = base64.b64decode(dhhashes[group_order].encode( 'utf8')) bytes = int(self.n / 8) self.n_s = crypto.generate_nonce() # n-bit random number self.c_o = crypto.decode_mpi(crypto.random_bytes(bytes)) self.c_s = self.c_o ^ (2 ** (self.n - 1)) self.y = crypto.srand(2 ** (2 * self.n - 1), p - 1) self.d = crypto.powmod(g, self.y, p) to_add = {'my_nonce': self.n_s, 'dhkeys': crypto.encode_mpi(self.d), 'counter': crypto.encode_mpi(self.c_o), 'nonce': self.n_o} for name in to_add: b64ed = base64.b64encode(to_add[name]).decode('utf-8') x.addChild(node=nbxmpp.DataField(name=name, value=b64ed)) self.form_o = ''.join(nbxmpp.c14n.c14n(el, self._is_buggy_gajim()) for \ el in form.getChildren()) self.form_s = ''.join(nbxmpp.c14n.c14n(el, self._is_buggy_gajim()) for \ el in x.getChildren()) self.status = 'responded-e2e' feature.addChild(node=x) if not_acceptable: response = nbxmpp.Error(response, nbxmpp.ERR_NOT_ACCEPTABLE) feature = nbxmpp.Node(nbxmpp.NS_FEATURE + ' feature') for f in not_acceptable: n = nbxmpp.Node('field') n['var'] = f feature.addChild(node=n) response.T.error.addChild(node=feature) self.send(response)
def accept_e2e_bob(self, form): """ 4.5 esession accept (bob) """ response = nbxmpp.Message() init = response.NT.init init.setNamespace(nbxmpp.NS_ESESSION_INIT) x = nbxmpp.DataForm(typ='result') for field in ('nonce', 'dhkeys', 'rshashes', 'identity', 'mac'): # FIXME: will do nothing in real world... assert field in form.asDict(), "alice's form didn't have a %s field" \ % field # 4.5.1 generating provisory session keys e = crypto.decode_mpi(base64.b64decode(form['dhkeys'])) p = dh.primes[self.modp] if crypto.sha256(crypto.encode_mpi(e)) != self.negotiated['He']: raise NegotiationError('SHA256(e) != He') k = self.get_shared_secret(e, self.y, p) self.kc_o, self.km_o, self.ks_o = self.generate_initiator_keys(k) # 4.5.2 verifying alice's identity self.verify_identity(form, e, False, 'a') # 4.5.4 generating bob's final session keys srs = b'' srses = secrets.secrets().retained_secrets(self.conn.name, self.jid.getStripped()) rshashes = [base64.b64decode(rshash) for rshash in form.getField( 'rshashes').getValues()] for s in srses: secret = s[0] if self.hmac(self.n_o, secret) in rshashes: srs = secret break # other shared secret # (we're not using one) oss = b'' k = crypto.sha256(k + srs + oss) self.kc_s, self.km_s, self.ks_s = self.generate_responder_keys(k) self.kc_o, self.km_o, self.ks_o = self.generate_initiator_keys(k) # 4.5.5 if srs: srshash = self.hmac(srs, b'Shared Retained Secret') else: srshash = crypto.random_bytes(32) x.addChild(node=nbxmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn')) x.addChild(node=nbxmpp.DataField(name='nonce', value=base64.b64encode( self.n_o).decode('utf-8'))) x.addChild(node=nbxmpp.DataField(name='srshash', value=base64.b64encode( srshash).decode('utf-8'))) for datafield in self.make_identity(x, self.d): x.addChild(node=datafield) init.addChild(node=x) self.send(response) self.do_retained_secret(k, srs) if self.negotiated['logging'] == 'mustnot': self.loggable = False self.status = 'active' self.enable_encryption = True if self.control: self.control.print_esession_details() self.stop_archiving_for_session()
def accept_e2e_alice(self, form, negotiated): """ 'Alice Accepts', continued """ self.encryptable_stanzas = ['message'] self.sas_algs = 'sas28x5' self.cipher = AES self.hash_alg = sha256 self.compression = None self.negotiated = negotiated accept = nbxmpp.Message() feature = accept.NT.feature feature.setNamespace(nbxmpp.NS_FEATURE) result = nbxmpp.DataForm(typ='result') self.c_s = crypto.decode_mpi(base64.b64decode(form['counter'])) self.c_o = self.c_s ^ (2 ** (self.n - 1)) self.n_o = base64.b64decode(form['my_nonce']) mod_p = int(form['modp']) p = dh.primes[mod_p] x = self.xes[mod_p] e = self.es[mod_p] self.d = crypto.decode_mpi(base64.b64decode(form['dhkeys'])) self.k = self.get_shared_secret(self.d, x, p) result.addChild(node=nbxmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn')) result.addChild(node=nbxmpp.DataField(name='accept', value='1')) result.addChild(node=nbxmpp.DataField(name='nonce', value=base64.b64encode(self.n_o).decode('utf-8'))) self.kc_s, self.km_s, self.ks_s = self.generate_initiator_keys(self.k) if self.sigmai: self.kc_o, self.km_o, self.ks_o = self.generate_responder_keys(self.k) self.verify_identity(form, self.d, True, 'b') else: srses = secrets.secrets().retained_secrets(self.conn.name, self.jid.getStripped()) rshashes = [self.hmac(self.n_s, rs[0]) for rs in srses] if not rshashes: # we've never spoken before, but we'll pretend we have rshash_size = self.hash_alg().digest_size rshashes.append(crypto.random_bytes(rshash_size)) rshashes = [base64.b64encode(rshash).decode('utf-8') for rshash in \ rshashes] result.addChild(node=nbxmpp.DataField(name='rshashes', value=rshashes)) result.addChild(node=nbxmpp.DataField(name='dhkeys', value=base64.b64encode(crypto.encode_mpi(e)).decode('utf-8'))) self.form_o = ''.join(nbxmpp.c14n.c14n(el, self._is_buggy_gajim()) \ for el in form.getChildren()) # MUST securely destroy K unless it will be used later to generate the # final shared secret for datafield in self.make_identity(result, e): result.addChild(node=datafield) feature.addChild(node=result) self.send(accept) if self.sigmai: self.status = 'active' self.enable_encryption = True else: self.status = 'identified-alice'