Cosmetic only... as usaual: stanza_session.py. TODO: better variable names
This commit is contained in:
parent
d23aa630ba
commit
593c23ebc8
2 changed files with 97 additions and 99 deletions
|
@ -19,12 +19,13 @@
|
||||||
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
##
|
##
|
||||||
|
|
||||||
import string
|
'''
|
||||||
|
This module defines a number of constants; specifically, large primes suitable
|
||||||
|
for use with the Diffie-Hellman key exchange.
|
||||||
|
|
||||||
# This file defines a number of constants; specifically, large primes suitable for
|
These constants have been obtained from RFC2409 and RFC3526.
|
||||||
# use with the Diffie-Hellman key exchange.
|
'''
|
||||||
#
|
import string
|
||||||
# These constants have been obtained from RFC2409 and RFC3526.
|
|
||||||
|
|
||||||
generators = [ None, # one to get the right offset
|
generators = [ None, # one to get the right offset
|
||||||
2,
|
2,
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
## src/common/stanza_session.py
|
## src/common/stanza_session.py
|
||||||
##
|
##
|
||||||
## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
|
## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
|
||||||
## Stephan Erb <steve-e AT h3c.de>
|
|
||||||
## Copyright (C) 2007-2008 Yann Leboulanger <asterix AT lagaule.org>
|
## Copyright (C) 2007-2008 Yann Leboulanger <asterix AT lagaule.org>
|
||||||
## Brendan Taylor <whateley AT gmail.com>
|
## Brendan Taylor <whateley AT gmail.com>
|
||||||
## Jean-Marie Traissard <jim AT lapin.org>
|
## Jean-Marie Traissard <jim AT lapin.org>
|
||||||
|
@ -24,29 +23,36 @@
|
||||||
##
|
##
|
||||||
|
|
||||||
from common import gajim
|
from common import gajim
|
||||||
|
|
||||||
from common import xmpp
|
from common import xmpp
|
||||||
from common import exceptions
|
from common.exceptions import DecryptionError, NegotiationError
|
||||||
|
import xmpp.c14n
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import xmpp.c14n
|
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
if gajim.HAVE_PYCRYPTO:
|
||||||
|
from Crypto.Cipher import AES
|
||||||
|
from Crypto.Hash import HMAC, SHA256
|
||||||
|
from Crypto.PublicKey import RSA
|
||||||
|
from common import crypto
|
||||||
|
|
||||||
|
from common import dh
|
||||||
|
import secrets
|
||||||
|
|
||||||
XmlDsig = 'http://www.w3.org/2000/09/xmldsig#'
|
XmlDsig = 'http://www.w3.org/2000/09/xmldsig#'
|
||||||
|
|
||||||
class StanzaSession(object):
|
class StanzaSession(object):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
def __init__(self, conn, jid, thread_id, type_):
|
def __init__(self, conn, jid, thread_id, type_):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
self.conn = conn
|
self.conn = conn
|
||||||
|
|
||||||
self.jid = jid
|
self.jid = jid
|
||||||
|
|
||||||
self.type = type_
|
self.type = type_
|
||||||
|
|
||||||
if thread_id:
|
if thread_id:
|
||||||
|
@ -69,9 +75,11 @@ class StanzaSession(object):
|
||||||
def is_loggable(self):
|
def is_loggable(self):
|
||||||
return self.loggable and gajim.config.should_log(self.conn.name, self.jid)
|
return self.loggable and gajim.config.should_log(self.conn.name, self.jid)
|
||||||
|
|
||||||
# remove events associated with this session from the queue
|
|
||||||
# returns True if any events were removed (unlike gajim.events.remove_events)
|
|
||||||
def remove_events(self, types):
|
def remove_events(self, types):
|
||||||
|
'''
|
||||||
|
Remove events associated with this session from the queue.
|
||||||
|
returns True if any events were removed (unlike events.py remove_events)
|
||||||
|
'''
|
||||||
any_removed = False
|
any_removed = False
|
||||||
|
|
||||||
for j in (self.jid, self.jid.getStripped()):
|
for j in (self.jid, self.jid.getStripped()):
|
||||||
|
@ -124,9 +132,10 @@ class StanzaSession(object):
|
||||||
self.cancelled_negotiation()
|
self.cancelled_negotiation()
|
||||||
|
|
||||||
def cancelled_negotiation(self):
|
def cancelled_negotiation(self):
|
||||||
'''A negotiation has been cancelled, so reset this session to its default
|
'''
|
||||||
state.'''
|
A negotiation has been cancelled, so reset this session to its default
|
||||||
|
state.
|
||||||
|
'''
|
||||||
if self.control:
|
if self.control:
|
||||||
self.control.on_cancel_session_negotiation()
|
self.control.on_cancel_session_negotiation()
|
||||||
|
|
||||||
|
@ -156,61 +165,52 @@ class StanzaSession(object):
|
||||||
# we could send an acknowledgement message to the remote client here
|
# we could send an acknowledgement message to the remote client here
|
||||||
self.status = None
|
self.status = None
|
||||||
|
|
||||||
if gajim.HAVE_PYCRYPTO:
|
|
||||||
from Crypto.Cipher import AES
|
|
||||||
from Crypto.Hash import HMAC, SHA256
|
|
||||||
from Crypto.PublicKey import RSA
|
|
||||||
from common import crypto
|
|
||||||
|
|
||||||
from common import dh
|
|
||||||
import secrets
|
|
||||||
|
|
||||||
# an encrypted stanza negotiation has several states. i've represented them
|
|
||||||
# as the following values in the 'status'
|
|
||||||
# attribute of the session object:
|
|
||||||
|
|
||||||
# 1. None:
|
|
||||||
# default state
|
|
||||||
# 2. 'requested-e2e':
|
|
||||||
# this client has initiated an esession negotiation and is waiting
|
|
||||||
# for a response
|
|
||||||
# 3. 'responded-e2e':
|
|
||||||
# this client has responded to an esession negotiation request and
|
|
||||||
# is waiting for the initiator to identify itself and complete the
|
|
||||||
# negotiation
|
|
||||||
# 4. 'identified-alice':
|
|
||||||
# this client identified itself and is waiting for the responder to
|
|
||||||
# identify itself and complete the negotiation
|
|
||||||
# 5. 'active':
|
|
||||||
# an encrypted session has been successfully negotiated. messages
|
|
||||||
# of any of the types listed in 'encryptable_stanzas' should be
|
|
||||||
# encrypted before they're sent.
|
|
||||||
|
|
||||||
# the transition between these states is handled in gajim.py's
|
|
||||||
# handle_session_negotiation method.
|
|
||||||
|
|
||||||
class EncryptedStanzaSession(StanzaSession):
|
class EncryptedStanzaSession(StanzaSession):
|
||||||
|
'''
|
||||||
|
An encrypted stanza negotiation has several states. They arerepresented as
|
||||||
|
the following values in the 'status' attribute of the session object:
|
||||||
|
|
||||||
|
1. None:
|
||||||
|
default state
|
||||||
|
2. 'requested-e2e':
|
||||||
|
this client has initiated an esession negotiation and is waiting
|
||||||
|
for a response
|
||||||
|
3. 'responded-e2e':
|
||||||
|
this client has responded to an esession negotiation request and
|
||||||
|
is waiting for the initiator to identify itself and complete the
|
||||||
|
negotiation
|
||||||
|
4. 'identified-alice':
|
||||||
|
this client identified itself and is waiting for the responder to
|
||||||
|
identify itself and complete the negotiation
|
||||||
|
5. 'active':
|
||||||
|
an encrypted session has been successfully negotiated. messages
|
||||||
|
of any of the types listed in 'encryptable_stanzas' should be
|
||||||
|
encrypted before they're sent.
|
||||||
|
|
||||||
|
The transition between these states is handled in gajim.py's
|
||||||
|
handle_session_negotiation method.
|
||||||
|
'''
|
||||||
def __init__(self, conn, jid, thread_id, type_='chat'):
|
def __init__(self, conn, jid, thread_id, type_='chat'):
|
||||||
StanzaSession.__init__(self, conn, jid, thread_id, type_='chat')
|
StanzaSession.__init__(self, conn, jid, thread_id, type_='chat')
|
||||||
|
|
||||||
self.xes = {}
|
self.xes = {}
|
||||||
self.es = {}
|
self.es = {}
|
||||||
|
|
||||||
self.n = 128
|
self.n = 128
|
||||||
|
|
||||||
self.enable_encryption = False
|
self.enable_encryption = False
|
||||||
|
|
||||||
# _s denotes 'self' (ie. this client)
|
# _s denotes 'self' (ie. this client)
|
||||||
self._kc_s = None
|
self._kc_s = None
|
||||||
|
|
||||||
# _o denotes 'other' (ie. the client at the other end of the session)
|
# _o denotes 'other' (ie. the client at the other end of the session)
|
||||||
self._kc_o = None
|
self._kc_o = None
|
||||||
|
|
||||||
# has the remote contact's identity ever been verified?
|
# has the remote contact's identity ever been verified?
|
||||||
self.verified_identity = False
|
self.verified_identity = False
|
||||||
|
|
||||||
# keep the encrypter updated with my latest cipher key
|
|
||||||
def set_kc_s(self, value):
|
def set_kc_s(self, value):
|
||||||
|
'''
|
||||||
|
keep the encrypter updated with my latest cipher key
|
||||||
|
'''
|
||||||
self._kc_s = value
|
self._kc_s = value
|
||||||
self.encrypter = self.cipher.new(self._kc_s, self.cipher.MODE_CTR,
|
self.encrypter = self.cipher.new(self._kc_s, self.cipher.MODE_CTR,
|
||||||
counter=self.encryptcounter)
|
counter=self.encryptcounter)
|
||||||
|
@ -218,8 +218,10 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
def get_kc_s(self):
|
def get_kc_s(self):
|
||||||
return self._kc_s
|
return self._kc_s
|
||||||
|
|
||||||
# keep the decrypter updated with the other party's latest cipher key
|
|
||||||
def set_kc_o(self, value):
|
def set_kc_o(self, value):
|
||||||
|
'''
|
||||||
|
keep the decrypter updated with the other party's latest cipher key
|
||||||
|
'''
|
||||||
self._kc_o = value
|
self._kc_o = value
|
||||||
self.decrypter = self.cipher.new(self._kc_o, self.cipher.MODE_CTR,
|
self.decrypter = self.cipher.new(self._kc_o, self.cipher.MODE_CTR,
|
||||||
counter=self.decryptcounter)
|
counter=self.decryptcounter)
|
||||||
|
@ -244,10 +246,10 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
return crypto.encode_mpi(gajim.pubkey.sign(hash_, '')[0])
|
return crypto.encode_mpi(gajim.pubkey.sign(hash_, '')[0])
|
||||||
|
|
||||||
def encrypt_stanza(self, stanza):
|
def encrypt_stanza(self, stanza):
|
||||||
encryptable = [x for x in stanza.getChildren() if x.getName() not in ('error', 'amp',
|
encryptable = [x for x in stanza.getChildren() if x.getName() not in
|
||||||
'thread')]
|
('error', 'amp', 'thread')]
|
||||||
|
|
||||||
# XXX can also encrypt contents of <error/> elements in stanzas @type =
|
# FIXME can also encrypt contents of <error/> elements in stanzas @type =
|
||||||
# 'error'
|
# 'error'
|
||||||
# (except for <defined-condition
|
# (except for <defined-condition
|
||||||
# xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> child elements)
|
# xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> child elements)
|
||||||
|
@ -266,7 +268,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
c.setNamespace('http://www.xmpp.org/extensions/xep-0200.html#ns')
|
c.setNamespace('http://www.xmpp.org/extensions/xep-0200.html#ns')
|
||||||
c.NT.data = base64.b64encode(m_final)
|
c.NT.data = base64.b64encode(m_final)
|
||||||
|
|
||||||
# XXX check for rekey request, handle <key/> elements
|
# FIXME check for rekey request, handle <key/> elements
|
||||||
|
|
||||||
m_content = ''.join(map(str, c.getChildren()))
|
m_content = ''.join(map(str, c.getChildren()))
|
||||||
c.NT.mac = base64.b64encode(self.hmac(self.km_s, m_content + \
|
c.NT.mac = base64.b64encode(self.hmac(self.km_s, m_content + \
|
||||||
|
@ -313,7 +315,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
return self.encrypter.encrypt(padded)
|
return self.encrypter.encrypt(padded)
|
||||||
|
|
||||||
def decrypt_stanza(self, stanza):
|
def decrypt_stanza(self, stanza):
|
||||||
# delete the unencrypted explanation body, if it exists
|
''' delete the unencrypted explanation body, if it exists '''
|
||||||
orig_body = stanza.getTag('body')
|
orig_body = stanza.getTag('body')
|
||||||
if orig_body:
|
if orig_body:
|
||||||
stanza.delChild(orig_body)
|
stanza.delChild(orig_body)
|
||||||
|
@ -331,7 +333,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
crypto.encode_mpi_with_padding(self.c_o))
|
crypto.encode_mpi_with_padding(self.c_o))
|
||||||
|
|
||||||
if not calculated_mac == received_mac:
|
if not calculated_mac == received_mac:
|
||||||
raise exceptions.DecryptionError, 'bad signature'
|
raise DecryptionError('bad signature')
|
||||||
|
|
||||||
m_final = base64.b64decode(c.getTagData('data'))
|
m_final = base64.b64decode(c.getTagData('data'))
|
||||||
m_compressed = self.decrypt(m_final)
|
m_compressed = self.decrypt(m_final)
|
||||||
|
@ -340,7 +342,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
try:
|
try:
|
||||||
parsed = xmpp.Node(node='<node>' + plaintext + '</node>')
|
parsed = xmpp.Node(node='<node>' + plaintext + '</node>')
|
||||||
except Exception:
|
except Exception:
|
||||||
raise exceptions.DecryptionError, 'decrypted <data/> not parseable as XML'
|
raise DecryptionError('decrypted <data/> not parseable as XML')
|
||||||
|
|
||||||
for child in parsed.getChildren():
|
for child in parsed.getChildren():
|
||||||
stanza.addChild(node=child)
|
stanza.addChild(node=child)
|
||||||
|
@ -358,7 +360,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
def get_shared_secret(self, e, y, p):
|
def get_shared_secret(self, e, y, p):
|
||||||
if (not 1 < e < (p - 1)):
|
if (not 1 < e < (p - 1)):
|
||||||
raise exceptions.NegotiationError, 'invalid DH value'
|
raise NegotiationError('invalid DH value')
|
||||||
|
|
||||||
return crypto.sha256(crypto.encode_mpi(crypto.powmod(e, y, p)))
|
return crypto.sha256(crypto.encode_mpi(crypto.powmod(e, y, p)))
|
||||||
|
|
||||||
|
@ -374,7 +376,8 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
m_o_calculated = self.hmac(self.km_o, crypto.encode_mpi(self.c_o) + id_o)
|
m_o_calculated = self.hmac(self.km_o, crypto.encode_mpi(self.c_o) + id_o)
|
||||||
|
|
||||||
if m_o_calculated != m_o:
|
if m_o_calculated != m_o:
|
||||||
raise exceptions.NegotiationError, 'calculated m_%s differs from received m_%s' % (i_o, i_o)
|
raise NegotiationError('calculated m_%s differs from received m_%s' %
|
||||||
|
(i_o, i_o))
|
||||||
|
|
||||||
if i_o == 'a' and self.sas_algs == 'sas28x5':
|
if i_o == 'a' and self.sas_algs == 'sas28x5':
|
||||||
# we don't need to calculate this if there's a verified retained secret
|
# we don't need to calculate this if there's a verified retained secret
|
||||||
|
@ -387,7 +390,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
if self.negotiated['recv_pubkey'] == 'hash':
|
if self.negotiated['recv_pubkey'] == 'hash':
|
||||||
# fingerprint = parsed.getTagData('fingerprint')
|
# fingerprint = parsed.getTagData('fingerprint')
|
||||||
# XXX find stored pubkey or terminate session
|
# FIXME find stored pubkey or terminate session
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
else:
|
else:
|
||||||
if self.negotiated['sign_algs'] == (XmlDsig + 'rsa-sha256'):
|
if self.negotiated['sign_algs'] == (XmlDsig + 'rsa-sha256'):
|
||||||
|
@ -399,7 +402,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
pubkey_o = xmpp.c14n.c14n(keyvalue)
|
pubkey_o = xmpp.c14n.c14n(keyvalue)
|
||||||
else:
|
else:
|
||||||
# XXX DSA, etc.
|
# FIXME DSA, etc.
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
enc_sig = parsed.getTag(name='SignatureValue',
|
enc_sig = parsed.getTag(name='SignatureValue',
|
||||||
|
@ -426,10 +429,11 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
hash_ = crypto.sha256(mac_o_calculated)
|
hash_ = crypto.sha256(mac_o_calculated)
|
||||||
|
|
||||||
if not eir_pubkey.verify(hash_, signature):
|
if not eir_pubkey.verify(hash_, signature):
|
||||||
raise exceptions.NegotiationError, 'public key signature verification failed!'
|
raise NegotiationError('public key signature verification failed!')
|
||||||
|
|
||||||
elif mac_o_calculated != mac_o:
|
elif mac_o_calculated != mac_o:
|
||||||
raise exceptions.NegotiationError, 'calculated mac_%s differs from received mac_%s' % (i_o, i_o)
|
raise NegotiationError('calculated mac_%s differs from received mac_%s'
|
||||||
|
% (i_o, i_o))
|
||||||
|
|
||||||
def make_identity(self, form, dh_i):
|
def make_identity(self, form, dh_i):
|
||||||
if self.negotiated['send_pubkey']:
|
if self.negotiated['send_pubkey']:
|
||||||
|
@ -476,7 +480,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
self.sas = crypto.sas_28x5(m_s, self.form_o)
|
self.sas = crypto.sas_28x5(m_s, self.form_o)
|
||||||
|
|
||||||
if self.sigmai:
|
if self.sigmai:
|
||||||
# XXX save retained secret?
|
# FIXME save retained secret?
|
||||||
self.check_identity(tuple)
|
self.check_identity(tuple)
|
||||||
|
|
||||||
return (xmpp.DataField(name='identity', value=base64.b64encode(id_s)),
|
return (xmpp.DataField(name='identity', value=base64.b64encode(id_s)),
|
||||||
|
@ -519,7 +523,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
x.addChild(node=xmpp.DataField(name='init_pubkey', options=['none', 'key',
|
x.addChild(node=xmpp.DataField(name='init_pubkey', options=['none', 'key',
|
||||||
'hash'], typ='list-single'))
|
'hash'], typ='list-single'))
|
||||||
|
|
||||||
# XXX store key, use hash
|
# FIXME store key, use hash
|
||||||
x.addChild(node=xmpp.DataField(name='resp_pubkey', options=['none',
|
x.addChild(node=xmpp.DataField(name='resp_pubkey', options=['none',
|
||||||
'key'], typ='list-single'))
|
'key'], typ='list-single'))
|
||||||
|
|
||||||
|
@ -555,8 +559,8 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
self.send(request)
|
self.send(request)
|
||||||
|
|
||||||
# 4.3 esession response (bob)
|
|
||||||
def verify_options_bob(self, form):
|
def verify_options_bob(self, form):
|
||||||
|
''' 4.3 esession response (bob) '''
|
||||||
negotiated = {'recv_pubkey': None, 'send_pubkey': None}
|
negotiated = {'recv_pubkey': None, 'send_pubkey': None}
|
||||||
not_acceptable = []
|
not_acceptable = []
|
||||||
ask_user = {}
|
ask_user = {}
|
||||||
|
@ -618,14 +622,14 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
if (XmlDsig + 'rsa-sha256') in options:
|
if (XmlDsig + 'rsa-sha256') in options:
|
||||||
negotiated['sign_algs'] = XmlDsig + 'rsa-sha256'
|
negotiated['sign_algs'] = XmlDsig + 'rsa-sha256'
|
||||||
else:
|
else:
|
||||||
# XXX some things are handled elsewhere, some things are
|
# FIXME some things are handled elsewhere, some things are
|
||||||
# not-implemented
|
# not-implemented
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return (negotiated, not_acceptable, ask_user)
|
return (negotiated, not_acceptable, ask_user)
|
||||||
|
|
||||||
# 4.3 esession response (bob)
|
|
||||||
def respond_e2e_bob(self, form, negotiated, not_acceptable):
|
def respond_e2e_bob(self, form, negotiated, not_acceptable):
|
||||||
|
''' 4.3 esession response (bob) '''
|
||||||
response = xmpp.Message()
|
response = xmpp.Message()
|
||||||
feature = response.NT.feature
|
feature = response.NT.feature
|
||||||
feature.setNamespace(xmpp.NS_FEATURE)
|
feature.setNamespace(xmpp.NS_FEATURE)
|
||||||
|
@ -697,8 +701,8 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
self.send(response)
|
self.send(response)
|
||||||
|
|
||||||
# 'Alice Accepts'
|
|
||||||
def verify_options_alice(self, form):
|
def verify_options_alice(self, form):
|
||||||
|
''' 'Alice Accepts' '''
|
||||||
negotiated = {}
|
negotiated = {}
|
||||||
ask_user = {}
|
ask_user = {}
|
||||||
not_acceptable = []
|
not_acceptable = []
|
||||||
|
@ -725,8 +729,8 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
return (negotiated, not_acceptable, ask_user)
|
return (negotiated, not_acceptable, ask_user)
|
||||||
|
|
||||||
# 'Alice Accepts', continued
|
|
||||||
def accept_e2e_alice(self, form, negotiated):
|
def accept_e2e_alice(self, form, negotiated):
|
||||||
|
''' 'Alice Accepts', continued '''
|
||||||
self.encryptable_stanzas = ['message']
|
self.encryptable_stanzas = ['message']
|
||||||
self.sas_algs = 'sas28x5'
|
self.sas_algs = 'sas28x5'
|
||||||
self.cipher = AES
|
self.cipher = AES
|
||||||
|
@ -743,7 +747,6 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
self.c_s = crypto.decode_mpi(base64.b64decode(form['counter']))
|
self.c_s = crypto.decode_mpi(base64.b64decode(form['counter']))
|
||||||
self.c_o = self.c_s ^ (2 ** (self.n - 1))
|
self.c_o = self.c_s ^ (2 ** (self.n - 1))
|
||||||
|
|
||||||
self.n_o = base64.b64decode(form['my_nonce'])
|
self.n_o = base64.b64decode(form['my_nonce'])
|
||||||
|
|
||||||
mod_p = int(form['modp'])
|
mod_p = int(form['modp'])
|
||||||
|
@ -752,7 +755,6 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
e = self.es[mod_p]
|
e = self.es[mod_p]
|
||||||
|
|
||||||
self.d = crypto.decode_mpi(base64.b64decode(form['dhkeys']))
|
self.d = crypto.decode_mpi(base64.b64decode(form['dhkeys']))
|
||||||
|
|
||||||
self.k = self.get_shared_secret(self.d, x, p)
|
self.k = self.get_shared_secret(self.d, x, p)
|
||||||
|
|
||||||
result.addChild(node=xmpp.DataField(name='FORM_TYPE',
|
result.addChild(node=xmpp.DataField(name='FORM_TYPE',
|
||||||
|
@ -798,8 +800,8 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
else:
|
else:
|
||||||
self.status = 'identified-alice'
|
self.status = 'identified-alice'
|
||||||
|
|
||||||
# 4.5 esession accept (bob)
|
|
||||||
def accept_e2e_bob(self, form):
|
def accept_e2e_bob(self, form):
|
||||||
|
''' 4.5 esession accept (bob) '''
|
||||||
response = xmpp.Message()
|
response = xmpp.Message()
|
||||||
|
|
||||||
init = response.NT.init
|
init = response.NT.init
|
||||||
|
@ -808,6 +810,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
x = xmpp.DataForm(typ='result')
|
x = xmpp.DataForm(typ='result')
|
||||||
|
|
||||||
for field in ('nonce', 'dhkeys', 'rshashes', 'identity', 'mac'):
|
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" \
|
assert field in form.asDict(), "alice's form didn't have a %s field" \
|
||||||
% field
|
% field
|
||||||
|
|
||||||
|
@ -816,18 +819,15 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
p = dh.primes[self.modp]
|
p = dh.primes[self.modp]
|
||||||
|
|
||||||
if crypto.sha256(crypto.encode_mpi(e)) != self.negotiated['He']:
|
if crypto.sha256(crypto.encode_mpi(e)) != self.negotiated['He']:
|
||||||
raise exceptions.NegotiationError, 'SHA256(e) != He'
|
raise NegotiationError('SHA256(e) != He')
|
||||||
|
|
||||||
k = self.get_shared_secret(e, self.y, p)
|
k = self.get_shared_secret(e, self.y, p)
|
||||||
|
|
||||||
self.kc_o, self.km_o, self.ks_o = self.generate_initiator_keys(k)
|
self.kc_o, self.km_o, self.ks_o = self.generate_initiator_keys(k)
|
||||||
|
|
||||||
# 4.5.2 verifying alice's identity
|
# 4.5.2 verifying alice's identity
|
||||||
|
|
||||||
self.verify_identity(form, e, False, 'a')
|
self.verify_identity(form, e, False, 'a')
|
||||||
|
|
||||||
# 4.5.4 generating bob's final session keys
|
# 4.5.4 generating bob's final session keys
|
||||||
|
|
||||||
srs = ''
|
srs = ''
|
||||||
|
|
||||||
srses = secrets.secrets().retained_secrets(self.conn.name,
|
srses = secrets.secrets().retained_secrets(self.conn.name,
|
||||||
|
@ -904,7 +904,6 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
self.kc_o, self.km_o, self.ks_o = self.generate_responder_keys(k)
|
self.kc_o, self.km_o, self.ks_o = self.generate_responder_keys(k)
|
||||||
|
|
||||||
# 4.6.2 Verifying Bob's Identity
|
# 4.6.2 Verifying Bob's Identity
|
||||||
|
|
||||||
self.verify_identity(form, self.d, False, 'b')
|
self.verify_identity(form, self.d, False, 'b')
|
||||||
# Note: If Alice discovers an error then she SHOULD ignore any encrypted
|
# Note: If Alice discovers an error then she SHOULD ignore any encrypted
|
||||||
# content she received in the stanza.
|
# content she received in the stanza.
|
||||||
|
@ -919,10 +918,11 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
self.control.print_esession_details()
|
self.control.print_esession_details()
|
||||||
|
|
||||||
def do_retained_secret(self, k, old_srs):
|
def do_retained_secret(self, k, old_srs):
|
||||||
'''calculate the new retained secret. determine if the user needs to check
|
'''
|
||||||
the remote party's identity. set up callbacks for when the identity has
|
Calculate the new retained secret. determine if the user needs to check
|
||||||
been verified.'''
|
the remote party's identity. Set up callbacks for when the identity has
|
||||||
|
been verified.
|
||||||
|
'''
|
||||||
new_srs = self.hmac(k, 'New Retained Secret')
|
new_srs = self.hmac(k, 'New Retained Secret')
|
||||||
self.srs = new_srs
|
self.srs = new_srs
|
||||||
|
|
||||||
|
@ -962,7 +962,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
x = crypto.srand(2 ** (2 * self.n - 1), p - 1)
|
x = crypto.srand(2 ** (2 * self.n - 1), p - 1)
|
||||||
|
|
||||||
# XXX this may be a source of performance issues
|
# FIXME this may be a source of performance issues
|
||||||
e = crypto.powmod(g, x, p)
|
e = crypto.powmod(g, x, p)
|
||||||
|
|
||||||
self.xes[modp] = x
|
self.xes[modp] = x
|
||||||
|
@ -980,21 +980,19 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
def terminate_e2e(self):
|
def terminate_e2e(self):
|
||||||
self.terminate()
|
self.terminate()
|
||||||
|
|
||||||
self.enable_encryption = False
|
self.enable_encryption = False
|
||||||
|
|
||||||
def acknowledge_termination(self):
|
def acknowledge_termination(self):
|
||||||
StanzaSession.acknowledge_termination(self)
|
StanzaSession.acknowledge_termination(self)
|
||||||
|
|
||||||
self.enable_encryption = False
|
self.enable_encryption = False
|
||||||
|
|
||||||
def fail_bad_negotiation(self, reason, fields = None):
|
def fail_bad_negotiation(self, reason, fields=None):
|
||||||
'''sends an error and cancels everything.
|
'''
|
||||||
|
Sends an error and cancels everything.
|
||||||
if fields is None, the remote party has given us a bad cryptographic value of some kind
|
|
||||||
|
If fields is None, the remote party has given us a bad cryptographic
|
||||||
otherwise, list the fields we haven't implemented'''
|
value of some kind. Otherwise, list the fields we haven't implemented
|
||||||
|
'''
|
||||||
err = xmpp.Error(xmpp.Message(), xmpp.ERR_FEATURE_NOT_IMPLEMENTED)
|
err = xmpp.Error(xmpp.Message(), xmpp.ERR_FEATURE_NOT_IMPLEMENTED)
|
||||||
err.T.error.T.text.setData(reason)
|
err.T.error.T.text.setData(reason)
|
||||||
|
|
||||||
|
@ -1020,7 +1018,6 @@ otherwise, list the fields we haven't implemented'''
|
||||||
def cancelled_negotiation(self):
|
def cancelled_negotiation(self):
|
||||||
StanzaSession.cancelled_negotiation(self)
|
StanzaSession.cancelled_negotiation(self)
|
||||||
self.enable_encryption = False
|
self.enable_encryption = False
|
||||||
|
|
||||||
self.km_o = ''
|
self.km_o = ''
|
||||||
|
|
||||||
# vim: se ts=3:
|
# vim: se ts=3:
|
||||||
|
|
Loading…
Add table
Reference in a new issue