def make_identity(self, form, dh_i): if self.negotiated['send_pubkey']: if self.negotiated['sign_algs'] == (XmlDsig + 'rsa-sha256'): pubkey = secrets.secrets().my_pubkey(self.conn.name) fields = (pubkey.n, pubkey.e) cb_fields = map( lambda f: base64.b64encode(crypto.encode_mpi(f)), fields) pubkey_s = '<RSAKeyValue xmlns="http://www.w3.org/2000/09/xmldsig#"' '><Modulus>%s</Modulus><Exponent>%s</Exponent></RSAKeyValue>' % \ tuple(cb_fields) else: pubkey_s = '' form_s2 = ''.join( map(lambda el: xmpp.c14n.c14n(el), form.getChildren())) old_c_s = self.c_s content = self.n_o + self.n_s + crypto.encode_mpi(dh_i) + pubkey_s + \ self.form_s + form_s2 mac_s = self.hmac(self.ks_s, content) if self.negotiated['send_pubkey']: signature = self.sign(mac_s) sign_s = '<SignatureValue xmlns="http://www.w3.org/2000/09/xmldsig#">' '%s</SignatureValue>' % base64.b64encode(signature) if self.negotiated['send_pubkey'] == 'hash': b64ed = base64.b64encode(self.hash(pubkey_s)) pubkey_s = '<fingerprint>%s</fingerprint>' % b64ed id_s = self.encrypt(pubkey_s + sign_s) else: id_s = self.encrypt(mac_s) m_s = self.hmac(self.km_s, crypto.encode_mpi(old_c_s) + id_s) if self.status == 'requested-e2e' and self.sas_algs == 'sas28x5': # we're alice; check for a retained secret # if none exists, prompt the user with the SAS self.sas = crypto.sas_28x5(m_s, self.form_o) if self.sigmai: # XXX save retained secret? self.check_identity(lambda: ()) return (xmpp.DataField(name='identity', value=base64.b64encode(id_s)), xmpp.DataField(name='mac', value=base64.b64encode(m_s)))
def on_cancel_button_clicked(self, widget): rejection = xmpp.Message(self.jid) rejection.setThread(self.session.thread_id) feature = rejection.NT.feature feature.setNamespace(xmpp.NS_FEATURE) x = xmpp.DataForm(typ='submit') x.addChild(node=xmpp.DataField('FORM_TYPE', value='urn:xmpp:ssn')) x.addChild(node=xmpp.DataField('accept', value='false', typ='boolean')) feature.addChild(node=x) gajim.connections[self.account].send_stanza(rejection) self.window.destroy()
def reject_negotiation(self, body=None): msg = xmpp.Message() feature = msg.NT.feature feature.setNamespace(xmpp.NS_FEATURE) x = xmpp.DataForm(typ='submit') x.addChild(node=xmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn')) x.addChild(node=xmpp.DataField(name='accept', value='0')) feature.addChild(node=x) if body: msg.setBody(body) self.send(msg) self.cancelled_negotiation()
def terminate(self): # only send termination message if we've sent a message and think they # have XEP-0201 support if self.last_send > 0 and \ (self.received_thread_id or self.last_receive == 0): msg = xmpp.Message() feature = msg.NT.feature feature.setNamespace(xmpp.NS_FEATURE) x = xmpp.DataForm(typ='submit') x.addChild( node=xmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn')) x.addChild(node=xmpp.DataField(name='terminate', value='1')) feature.addChild(node=x) self.send(msg) self.status = None
def make_dhfield(self, modp_options, sigmai): dhs = [] for modp in modp_options: p = dh.primes[modp] g = dh.generators[modp] x = crypto.srand(2**(2 * self.n - 1), p - 1) # XXX this may be a source of performance issues e = crypto.powmod(g, x, p) self.xes[modp] = x self.es[modp] = e if sigmai: dhs.append(base64.b64encode(crypto.encode_mpi(e))) name = 'dhkeys' else: He = crypto.sha256(crypto.encode_mpi(e)) dhs.append(base64.b64encode(He)) name = 'dhhashes' return xmpp.DataField(name=name, typ='hidden', value=dhs)
def accept_e2e_bob(self, form): response = xmpp.Message() init = response.NT.init init.setNamespace(xmpp.NS_ESESSION_INIT) x = xmpp.DataForm(typ='result') for field in ('nonce', 'dhkeys', 'rshashes', 'identity', 'mac'): 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 exceptions.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 = '' srses = secrets.secrets().retained_secrets(self.conn.name, self.jid.getStripped()) rshashes = [ base64.b64decode(rshash) for rshash in form.getField('rshashes').getValues() ] for (secret, verified) in srses: if self.hmac(self.n_o, secret) in rshashes: srs = secret break # other shared secret # (we're not using one) oss = '' 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, 'Shared Retained Secret') else: srshash = crypto.random_bytes(32) x.addChild(node=xmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn')) x.addChild(node=xmpp.DataField(name='nonce', value=base64.b64encode(self.n_o))) x.addChild(node=xmpp.DataField(name='srshash', value=base64.b64encode(srshash))) 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()
def accept_e2e_alice(self, form, negotiated): self.encryptable_stanzas = ['message'] self.sas_algs = 'sas28x5' self.cipher = AES self.hash_alg = SHA256 self.compression = None self.negotiated = negotiated accept = xmpp.Message() feature = accept.NT.feature feature.setNamespace(xmpp.NS_FEATURE) result = xmpp.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=xmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn')) result.addChild(node=xmpp.DataField(name='accept', value='1')) result.addChild(node=xmpp.DataField(name='nonce', value=base64.b64encode(self.n_o))) 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) for (rs, v) 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) for rshash in rshashes] result.addChild( node=xmpp.DataField(name='rshashes', value=rshashes)) result.addChild(node=xmpp.DataField( name='dhkeys', value=base64.b64encode(crypto.encode_mpi(e)))) self.form_o = ''.join( map(lambda el: xmpp.c14n.c14n(el), 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'
def respond_e2e_bob(self, form, negotiated, not_acceptable): response = xmpp.Message() feature = response.NT.feature feature.setNamespace(xmpp.NS_FEATURE) x = xmpp.DataForm(typ='submit') x.addChild(node=xmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn')) x.addChild(node=xmpp.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=xmpp.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 self.modp = int(form.getField('modp').getOptions()[group_order][1]) x.addChild(node=xmpp.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 = form.getField('dhhashes').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]) x.addChild(node=xmpp.DataField(name=name, value=b64ed)) self.form_o = ''.join( map(lambda el: xmpp.c14n.c14n(el), form.getChildren())) self.form_s = ''.join( map(lambda el: xmpp.c14n.c14n(el), x.getChildren())) self.status = 'responded-e2e' feature.addChild(node=x) if not_acceptable: response = xmpp.Error(response, xmpp.ERR_NOT_ACCEPTABLE) feature = xmpp.Node(xmpp.NS_FEATURE + ' feature') for f in not_acceptable: n = xmpp.Node('field') n['var'] = f feature.addChild(node=n) response.T.error.addChild(node=feature) self.send(response)
def negotiate_e2e(self, sigmai): self.negotiated = {} request = xmpp.Message() feature = request.NT.feature feature.setNamespace(xmpp.NS_FEATURE) x = xmpp.DataForm(typ='form') x.addChild(node=xmpp.DataField( name='FORM_TYPE', value='urn:xmpp:ssn', typ='hidden')) x.addChild(node=xmpp.DataField( name='accept', value='1', typ='boolean', required=True)) # this field is incorrectly called 'otr' in XEPs 0116 and 0217 x.addChild(node=xmpp.DataField(name='logging', typ='list-single', options=self.logging_preference(), required=True)) # unsupported options: 'disabled', 'enabled' x.addChild(node=xmpp.DataField(name='disclosure', typ='list-single', options=['never'], required=True)) x.addChild(node=xmpp.DataField( name='security', typ='list-single', options=['e2e' ], required=True)) x.addChild(node=xmpp.DataField( name='crypt_algs', value='aes128-ctr', typ='hidden')) x.addChild(node=xmpp.DataField( name='hash_algs', value='sha256', typ='hidden')) x.addChild( node=xmpp.DataField(name='compress', value='none', typ='hidden')) # unsupported options: 'iq', 'presence' x.addChild(node=xmpp.DataField( name='stanzas', typ='list-multi', options=['message'])) x.addChild(node=xmpp.DataField(name='init_pubkey', options=['none', 'key', 'hash'], typ='list-single')) # XXX store key, use hash x.addChild(node=xmpp.DataField( name='resp_pubkey', options=['none', 'key'], typ='list-single')) x.addChild(node=xmpp.DataField(name='ver', value='1.0', typ='hidden')) x.addChild(node=xmpp.DataField( name='rekey_freq', value='4294967295', typ='hidden')) x.addChild(node=xmpp.DataField( name='sas_algs', value='sas28x5', typ='hidden')) x.addChild(node=xmpp.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=xmpp.DataField( name='my_nonce', value=base64.b64encode(self.n_s), typ='hidden')) modp_options = [ int(g) for g in gajim.config.get('esession_modp').split(',') ] x.addChild(node=xmpp.DataField(name='modp', typ='list-single', options=map(lambda x: [None, x], modp_options))) x.addChild(node=self.make_dhfield(modp_options, sigmai)) self.sigmai = sigmai self.form_s = ''.join( map(lambda el: xmpp.c14n.c14n(el), x.getChildren())) feature.addChild(node=x) self.status = 'requested-e2e' self.send(request)