2007-07-17 10:08:27 +02:00
from common import gajim
2007-06-05 23:26:45 +02:00
from common import xmpp
2007-07-12 08:25:05 +02:00
from common import exceptions
2007-06-05 23:26:45 +02:00
import random
import string
2007-06-17 12:39:19 +02:00
import time
import xmpp . c14n
2007-06-12 00:42:29 +02:00
import base64
2008-08-04 01:01:43 +02:00
import os
2007-06-12 00:42:29 +02:00
2007-08-17 10:39:47 +02:00
XmlDsig = ' http://www.w3.org/2000/09/xmldsig# '
2007-06-17 12:39:19 +02:00
class StanzaSession ( object ) :
2007-06-05 23:26:45 +02:00
def __init__ ( self , conn , jid , thread_id , type ) :
self . conn = conn
2007-07-17 10:08:27 +02:00
self . jid = jid
2007-06-05 23:26:45 +02:00
self . type = type
if thread_id :
self . received_thread_id = True
self . thread_id = thread_id
else :
self . received_thread_id = False
if type == ' normal ' :
self . thread_id = None
else :
self . thread_id = self . generate_thread_id ( )
2008-04-11 05:52:45 +02:00
self . loggable = True
2007-06-05 23:26:45 +02:00
self . last_send = 0
2008-05-25 23:28:32 +02:00
self . last_receive = 0
2007-06-17 12:39:19 +02:00
self . status = None
2007-06-29 06:12:08 +02:00
self . negotiated = { }
2007-12-08 09:51:10 +01:00
2008-04-11 05:52:45 +02:00
def is_loggable ( self ) :
account = self . conn . name
no_log_for = gajim . config . get_per ( ' accounts ' , account , ' no_log_for ' )
if not no_log_for :
no_log_for = ' '
no_log_for = no_log_for . split ( )
return self . loggable and account not in no_log_for and self . jid not in no_log_for
2008-05-14 02:34:02 +02:00
# 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 ) :
any_removed = False
for event in gajim . events . get_events ( self . conn . name , self . jid , types = types ) :
# the event wasn't in this session
if ( event . type_ == ' chat ' and event . parameters [ 8 ] != self ) or \
2008-06-14 18:49:48 +02:00
( event . type_ == ' printed_chat ' and event . parameters [ 0 ] . session != self ) :
2008-05-14 02:34:02 +02:00
continue
# events.remove_events returns True when there were no events
# for some reason
r = gajim . events . remove_events ( self . conn . name , self . jid , event )
if not r :
any_removed = True
return any_removed
2007-06-05 23:26:45 +02:00
def generate_thread_id ( self ) :
2007-08-28 21:38:42 +02:00
return " " . join ( [ random . choice ( string . ascii_letters ) for x in xrange ( 0 , 32 ) ] )
2007-06-08 21:42:02 +02:00
2007-06-17 12:39:19 +02:00
def send ( self , msg ) :
2007-08-18 10:59:36 +02:00
if self . thread_id :
msg . NT . thread = self . thread_id
2007-06-17 12:39:19 +02:00
msg . setAttr ( ' to ' , self . jid )
self . conn . send_stanza ( msg )
2007-12-08 09:51:10 +01:00
2007-07-12 08:25:05 +02:00
if isinstance ( msg , xmpp . Message ) :
self . last_send = time . time ( )
2007-06-08 21:42:02 +02:00
2007-06-29 06:12:08 +02:00
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 cancelled_negotiation ( self ) :
''' A negotiation has been cancelled, so reset this session to its default state. '''
2007-09-29 22:51:01 +02:00
2008-04-26 03:52:03 +02:00
if self . control :
2007-12-08 09:51:10 +01:00
self . control . on_cancel_session_negotiation ( )
2007-12-08 06:49:38 +01:00
2007-06-29 06:12:08 +02:00
self . status = None
self . negotiated = { }
2007-06-27 00:52:50 +02:00
def terminate ( self ) :
2008-05-27 01:53:40 +02:00
# 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 ) :
2008-05-25 23:28:32 +02:00
msg = xmpp . Message ( )
feature = msg . NT . feature
feature . setNamespace ( xmpp . NS_FEATURE )
2007-06-27 00:52:50 +02:00
2008-05-25 23:28:32 +02:00
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 ' ) )
2007-06-27 00:52:50 +02:00
2008-05-25 23:28:32 +02:00
feature . addChild ( node = x )
2007-06-27 00:52:50 +02:00
2008-05-25 23:28:32 +02:00
self . send ( msg )
2007-06-27 00:52:50 +02:00
self . status = None
def acknowledge_termination ( self ) :
2007-12-08 06:49:38 +01:00
# we could send an acknowledgement message to the remote client here
2007-06-27 00:52:50 +02:00
self . status = None
2007-08-27 15:36:24 +02:00
if gajim . HAVE_PYCRYPTO :
from Crypto . Cipher import AES
from Crypto . Hash import HMAC , SHA256
from Crypto . PublicKey import RSA
2007-09-18 14:50:22 +02:00
from common import crypto
2007-08-27 15:36:24 +02:00
2007-09-29 22:51:01 +02:00
from common import dh
import secrets
2007-08-27 15:36:24 +02:00
# an encrypted stanza negotiation has several states. i've represented them
2007-12-08 06:49:38 +01:00
# as the following values in the 'status'
2007-06-26 22:55:49 +02:00
# attribute of the session object:
# 1. None:
# default state
# 2. 'requested-e2e':
2007-08-27 15:36:24 +02:00
# this client has initiated an esession negotiation and is waiting
# for a response
2007-06-26 22:55:49 +02:00
# 3. 'responded-e2e':
2007-08-27 15:36:24 +02:00
# this client has responded to an esession negotiation request and
# is waiting for the initiator to identify itself and complete the
2007-06-26 22:55:49 +02:00
# negotiation
# 4. 'identified-alice':
2007-08-27 15:36:24 +02:00
# this client identified itself and is waiting for the responder to
2007-06-26 22:55:49 +02:00
# identify itself and complete the negotiation
# 5. 'active':
2007-08-27 15:36:24 +02:00
# an encrypted session has been successfully negotiated. messages
# of any of the types listed in 'encryptable_stanzas' should be
# encrypted before they're sent.
2007-06-26 22:55:49 +02:00
# the transition between these states is handled in gajim.py's
# handle_session_negotiation method.
2007-06-12 00:42:29 +02:00
class EncryptedStanzaSession ( StanzaSession ) :
def __init__ ( self , conn , jid , thread_id , type = ' chat ' ) :
StanzaSession . __init__ ( self , conn , jid , thread_id , type = ' chat ' )
2007-06-17 12:39:19 +02:00
self . xes = { }
self . es = { }
2007-06-12 00:42:29 +02:00
2007-06-26 22:55:49 +02:00
self . n = 128
2007-06-17 12:39:19 +02:00
self . enable_encryption = False
2007-06-12 00:42:29 +02:00
2007-06-26 22:55:49 +02:00
# _s denotes 'self' (ie. this client)
2007-06-17 12:39:19 +02:00
self . _kc_s = None
2007-12-08 06:49:38 +01:00
2007-06-26 22:55:49 +02:00
# _o denotes 'other' (ie. the client at the other end of the session)
2007-06-17 12:39:19 +02:00
self . _kc_o = None
2007-06-12 00:42:29 +02:00
2008-06-29 06:39:29 +02:00
# has the remote contact's identity ever been verified?
self . verified_identity = False
2007-06-17 12:39:19 +02:00
# keep the encrypter updated with my latest cipher key
def set_kc_s ( self , value ) :
self . _kc_s = value
2007-08-27 15:36:24 +02:00
self . encrypter = self . cipher . new ( self . _kc_s , self . cipher . MODE_CTR ,
counter = self . encryptcounter )
2007-06-12 00:42:29 +02:00
2007-06-17 12:39:19 +02:00
def get_kc_s ( self ) :
return self . _kc_s
2007-06-12 00:42:29 +02:00
2007-06-17 12:39:19 +02:00
# keep the decrypter updated with the other party's latest cipher key
def set_kc_o ( self , value ) :
self . _kc_o = value
2007-08-27 15:36:24 +02:00
self . decrypter = self . cipher . new ( self . _kc_o , self . cipher . MODE_CTR ,
counter = self . decryptcounter )
2007-12-08 06:49:38 +01:00
2007-06-17 12:39:19 +02:00
def get_kc_o ( self ) :
return self . _kc_o
kc_s = property ( get_kc_s , set_kc_s )
kc_o = property ( get_kc_o , set_kc_o )
2007-12-08 06:49:38 +01:00
2007-06-12 00:42:29 +02:00
def encryptcounter ( self ) :
2007-06-17 12:39:19 +02:00
self . c_s = ( self . c_s + 1 ) % ( 2 * * self . n )
2007-09-16 06:16:45 +02:00
return crypto . encode_mpi_with_padding ( self . c_s )
2007-12-08 06:49:38 +01:00
2007-06-12 00:42:29 +02:00
def decryptcounter ( self ) :
2007-06-17 12:39:19 +02:00
self . c_o = ( self . c_o + 1 ) % ( 2 * * self . n )
2007-09-16 06:16:45 +02:00
return crypto . encode_mpi_with_padding ( self . c_o )
2007-06-12 00:42:29 +02:00
2007-08-17 10:39:47 +02:00
def sign ( self , string ) :
if self . negotiated [ ' sign_algs ' ] == ( XmlDsig + ' rsa-sha256 ' ) :
2007-09-16 06:16:45 +02:00
hash = crypto . sha256 ( string )
return crypto . encode_mpi ( gajim . pubkey . sign ( hash , ' ' ) [ 0 ] )
2007-08-17 10:39:47 +02:00
2007-06-12 00:42:29 +02:00
def encrypt_stanza ( self , stanza ) :
2007-08-27 15:36:24 +02:00
encryptable = filter ( lambda x : x . getName ( ) not in ( ' error ' , ' amp ' ,
' thread ' ) , stanza . getChildren ( ) )
2007-06-12 00:42:29 +02:00
2007-08-27 15:36:24 +02:00
# XXX can also encrypt contents of <error/> elements in stanzas @type =
# 'error'
# (except for <defined-condition
# xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> child elements)
2007-06-12 00:42:29 +02:00
2007-06-17 12:39:19 +02:00
old_en_counter = self . c_s
2007-06-12 00:42:29 +02:00
for element in encryptable :
stanza . delChild ( element )
2007-06-08 21:42:02 +02:00
2007-06-12 00:42:29 +02:00
plaintext = ' ' . join ( map ( str , encryptable ) )
m_compressed = self . compress ( plaintext )
m_final = self . encrypt ( m_compressed )
c = stanza . NT . c
c . setNamespace ( ' http://www.xmpp.org/extensions/xep-0200.html#ns ' )
c . NT . data = base64 . b64encode ( m_final )
# XXX check for rekey request, handle <key/> elements
m_content = ' ' . join ( map ( str , c . getChildren ( ) ) )
2007-08-27 15:36:24 +02:00
c . NT . mac = base64 . b64encode ( self . hmac ( self . km_s , m_content + \
2007-09-16 06:16:45 +02:00
crypto . encode_mpi ( old_en_counter ) ) )
2007-06-12 00:42:29 +02:00
2008-08-04 01:01:43 +02:00
msgtxt = ' [This message is part of an encrypted session. ' \
' If you see this message, something went wrong.] '
lang = os . getenv ( ' LANG ' )
if lang is not None and lang != ' en ' : # we're not english
2008-08-04 10:07:07 +02:00
msgtxt = _ ( ' [This message is part of an encrypted session. '
' If you see this message, something went wrong.] ' ) + ' ( ' + \
msgtxt + ' ) '
2008-08-04 01:01:43 +02:00
stanza . setBody ( msgtxt )
2008-08-03 18:46:27 +02:00
2007-06-12 00:42:29 +02:00
return stanza
2008-05-24 01:27:08 +02:00
def is_xep_200_encrypted ( self , msg ) :
msg . getTag ( ' c ' , namespace = common . xmpp . NS_STANZA_CRYPTO )
2007-06-17 12:39:19 +02:00
def hmac ( self , key , content ) :
return HMAC . new ( key , content , self . hash_alg ) . digest ( )
def generate_initiator_keys ( self , k ) :
return ( self . hmac ( k , ' Initiator Cipher Key ' ) ,
self . hmac ( k , ' Initiator MAC Key ' ) ,
self . hmac ( k , ' Initiator SIGMA Key ' ) )
def generate_responder_keys ( self , k ) :
return ( self . hmac ( k , ' Responder Cipher Key ' ) ,
self . hmac ( k , ' Responder MAC Key ' ) ,
self . hmac ( k , ' Responder SIGMA Key ' ) )
2007-06-12 00:42:29 +02:00
def compress ( self , plaintext ) :
2008-04-18 02:02:56 +02:00
if self . compression is None :
2007-06-12 00:42:29 +02:00
return plaintext
def decompress ( self , compressed ) :
2008-04-18 02:02:56 +02:00
if self . compression is None :
2007-12-08 06:49:38 +01:00
return compressed
2007-06-12 00:42:29 +02:00
def encrypt ( self , encryptable ) :
2007-09-29 22:51:01 +02:00
padded = crypto . pad_to_multiple ( encryptable , 16 , ' ' , False )
2007-06-12 00:42:29 +02:00
2007-09-29 22:51:01 +02:00
return self . encrypter . encrypt ( padded )
2007-06-12 00:42:29 +02:00
def decrypt_stanza ( self , stanza ) :
2008-08-01 02:39:40 +02:00
# delete the unencrypted explanation body, if it exists
orig_body = stanza . getTag ( ' body ' )
if orig_body :
stanza . delChild ( orig_body )
2007-08-27 15:36:24 +02:00
c = stanza . getTag ( name = ' c ' ,
namespace = ' http://www.xmpp.org/extensions/xep-0200.html#ns ' )
2007-06-12 00:42:29 +02:00
stanza . delChild ( c )
# contents of <c>, minus <mac>, minus whitespace
2007-08-27 15:36:24 +02:00
macable = ' ' . join ( map ( str , filter ( lambda x : x . getName ( ) != ' mac ' ,
c . getChildren ( ) ) ) )
2007-06-12 00:42:29 +02:00
received_mac = base64 . b64decode ( c . getTagData ( ' mac ' ) )
2007-08-27 15:36:24 +02:00
calculated_mac = self . hmac ( self . km_o , macable + \
2007-09-16 06:16:45 +02:00
crypto . encode_mpi_with_padding ( self . c_o ) )
2007-06-12 00:42:29 +02:00
if not calculated_mac == received_mac :
2007-08-20 10:16:48 +02:00
raise exceptions . DecryptionError , ' bad signature '
2007-06-12 00:42:29 +02:00
m_final = base64 . b64decode ( c . getTagData ( ' data ' ) )
m_compressed = self . decrypt ( m_final )
plaintext = self . decompress ( m_compressed )
try :
parsed = xmpp . Node ( node = ' <node> ' + plaintext + ' </node> ' )
except :
2007-08-20 10:16:48 +02:00
raise exceptions . DecryptionError , ' decrypted <data/> not parseable as XML '
2007-06-12 00:42:29 +02:00
for child in parsed . getChildren ( ) :
stanza . addChild ( node = child )
return stanza
def decrypt ( self , ciphertext ) :
return self . decrypter . decrypt ( ciphertext )
2007-06-29 06:12:08 +02:00
def logging_preference ( self ) :
if gajim . config . get ( ' log_encrypted_sessions ' ) :
return [ " may " , " mustnot " ]
else :
return [ " mustnot " , " may " ]
2007-09-16 06:16:45 +02:00
2007-08-07 09:21:29 +02:00
def get_shared_secret ( self , e , y , p ) :
if ( not 1 < e < ( p - 1 ) ) :
raise exceptions . NegotiationError , " invalid DH value "
2007-09-20 21:58:31 +02:00
return crypto . sha256 ( crypto . encode_mpi ( crypto . powmod ( e , y , p ) ) )
2007-08-07 09:21:29 +02:00
def c7lize_mac_id ( self , form ) :
kids = form . getChildren ( )
macable = filter ( lambda x : x . getVar ( ) not in ( ' mac ' , ' identity ' ) , kids )
return ' ' . join ( map ( lambda el : xmpp . c14n . c14n ( el ) , macable ) )
2007-08-17 10:39:47 +02:00
def verify_identity ( self , form , dh_i , sigmai , i_o ) :
m_o = base64 . b64decode ( form [ ' mac ' ] )
id_o = base64 . b64decode ( form [ ' identity ' ] )
2007-06-29 06:12:08 +02:00
2007-09-16 06:16:45 +02:00
m_o_calculated = self . hmac ( self . km_o , crypto . encode_mpi ( self . c_o ) + id_o )
2007-08-07 09:42:31 +02:00
2007-08-17 10:39:47 +02:00
if m_o_calculated != m_o :
raise exceptions . NegotiationError , ' calculated m_ %s differs from received m_ %s ' % ( i_o , i_o )
2007-08-07 09:21:29 +02:00
2007-08-17 10:39:47 +02:00
if i_o == ' a ' and self . sas_algs == ' sas28x5 ' :
2008-06-29 06:39:29 +02:00
# we don't need to calculate this if there's a verified retained secret
# (but we do anyways)
2007-09-16 06:16:45 +02:00
self . sas = crypto . sas_28x5 ( m_o , self . form_s )
2007-08-07 09:21:29 +02:00
2007-08-17 10:39:47 +02:00
if self . negotiated [ ' recv_pubkey ' ] :
plaintext = self . decrypt ( id_o )
parsed = xmpp . Node ( node = ' <node> ' + plaintext + ' </node> ' )
2007-08-07 09:21:29 +02:00
2007-08-17 10:39:47 +02:00
if self . negotiated [ ' recv_pubkey ' ] == ' hash ' :
fingerprint = parsed . getTagData ( ' fingerprint ' )
2007-08-07 09:21:29 +02:00
2007-08-17 10:39:47 +02:00
# XXX find stored pubkey or terminate session
raise ' unimplemented '
else :
if self . negotiated [ ' sign_algs ' ] == ( XmlDsig + ' rsa-sha256 ' ) :
keyvalue = parsed . getTag ( name = ' RSAKeyValue ' , namespace = XmlDsig )
2007-08-07 09:21:29 +02:00
2007-09-16 06:16:45 +02:00
n , e = map ( lambda x : crypto . decode_mpi ( base64 . b64decode (
2007-08-27 15:36:24 +02:00
keyvalue . getTagData ( x ) ) ) , ( ' Modulus ' , ' Exponent ' ) )
2007-08-17 10:39:47 +02:00
eir_pubkey = RSA . construct ( ( n , long ( e ) ) )
2007-08-07 09:21:29 +02:00
2007-08-17 10:39:47 +02:00
pubkey_o = xmpp . c14n . c14n ( keyvalue )
else :
# XXX DSA, etc.
raise ' unimplemented '
2007-08-07 09:21:29 +02:00
2007-08-27 15:36:24 +02:00
enc_sig = parsed . getTag ( name = ' SignatureValue ' ,
namespace = XmlDsig ) . getData ( )
2007-09-16 06:16:45 +02:00
signature = ( crypto . decode_mpi ( base64 . b64decode ( enc_sig ) ) , )
2007-08-17 10:39:47 +02:00
else :
mac_o = self . decrypt ( id_o )
pubkey_o = ' '
2007-08-07 09:21:29 +02:00
c7l_form = self . c7lize_mac_id ( form )
2007-09-16 06:16:45 +02:00
content = self . n_s + self . n_o + crypto . encode_mpi ( dh_i ) + pubkey_o
2007-08-07 09:21:29 +02:00
if sigmai :
2007-08-17 10:39:47 +02:00
self . form_o = c7l_form
content + = self . form_o
2007-08-07 09:21:29 +02:00
else :
2007-08-17 10:39:47 +02:00
form_o2 = c7l_form
content + = self . form_o + form_o2
2007-08-07 09:21:29 +02:00
2007-08-17 10:39:47 +02:00
mac_o_calculated = self . hmac ( self . ks_o , content )
2007-12-08 06:49:38 +01:00
2007-08-17 10:39:47 +02:00
if self . negotiated [ ' recv_pubkey ' ] :
2007-09-16 06:16:45 +02:00
hash = crypto . sha256 ( mac_o_calculated )
2007-08-07 09:21:29 +02:00
2007-08-17 10:39:47 +02:00
if not eir_pubkey . verify ( hash , signature ) :
2007-12-08 09:51:10 +01:00
raise exceptions . NegotiationError , ' public key signature verification failed! '
2007-08-17 10:39:47 +02:00
elif mac_o_calculated != mac_o :
raise exceptions . NegotiationError , ' calculated mac_ %s differs from received mac_ %s ' % ( i_o , i_o )
2007-08-07 09:21:29 +02:00
2007-08-17 11:02:53 +02:00
def make_identity ( self , form , dh_i ) :
2007-08-17 10:39:47 +02:00
if self . negotiated [ ' send_pubkey ' ] :
if self . negotiated [ ' sign_algs ' ] == ( XmlDsig + ' rsa-sha256 ' ) :
2007-09-29 22:51:01 +02:00
pubkey = secrets . secrets ( ) . my_pubkey ( self . conn . name )
2007-09-16 06:16:45 +02:00
fields = ( pubkey . n , pubkey . e )
2007-08-17 10:39:47 +02:00
2007-09-16 06:16:45 +02:00
cb_fields = map ( lambda f : base64 . b64encode ( crypto . encode_mpi ( f ) ) , fields )
2007-08-17 10:39:47 +02:00
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 ( ) ) )
2007-08-07 09:21:29 +02:00
old_c_s = self . c_s
2007-09-16 06:16:45 +02:00
content = self . n_o + self . n_s + crypto . encode_mpi ( dh_i ) + pubkey_s + self . form_s + form_s2
2007-08-07 09:21:29 +02:00
2007-08-17 11:02:53 +02:00
mac_s = self . hmac ( self . ks_s , content )
2007-08-17 10:39:47 +02:00
if self . negotiated [ ' send_pubkey ' ] :
2007-08-17 11:02:53 +02:00
signature = self . sign ( mac_s )
2007-08-17 10:39:47 +02:00
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
2007-12-08 06:49:38 +01:00
2007-08-17 11:02:53 +02:00
id_s = self . encrypt ( pubkey_s + sign_s )
2007-08-17 10:39:47 +02:00
else :
2007-08-17 11:02:53 +02:00
id_s = self . encrypt ( mac_s )
2007-08-07 09:21:29 +02:00
2007-09-16 06:16:45 +02:00
m_s = self . hmac ( self . km_s , crypto . encode_mpi ( old_c_s ) + id_s )
2007-08-07 09:21:29 +02:00
2007-08-17 11:02:53 +02:00
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
2007-09-16 06:16:45 +02:00
self . sas = crypto . sas_28x5 ( m_s , self . form_o )
2007-08-07 09:42:31 +02:00
if self . sigmai :
2007-09-29 22:51:01 +02:00
# XXX save retained secret?
self . check_identity ( lambda : ( ) )
2007-12-08 06:49:38 +01:00
2007-08-17 11:02:53 +02:00
return ( xmpp . DataField ( name = ' identity ' , value = base64 . b64encode ( id_s ) ) , \
xmpp . DataField ( name = ' mac ' , value = base64 . b64encode ( m_s ) ) )
2007-08-07 09:21:29 +02:00
def negotiate_e2e ( self , sigmai ) :
2007-06-29 06:12:08 +02:00
self . negotiated = { }
2007-06-17 12:39:19 +02:00
request = xmpp . Message ( )
feature = request . NT . feature
feature . setNamespace ( xmpp . NS_FEATURE )
2007-06-08 21:42:02 +02:00
2007-06-17 12:39:19 +02:00
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
2007-06-29 06:12:08 +02:00
x . addChild ( node = xmpp . DataField ( name = ' logging ' , typ = ' list-single ' , options = self . logging_preference ( ) , required = True ) )
2007-06-17 12:39:19 +02:00
# 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 ' ] ) )
2007-08-17 10:39:47 +02:00
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 ' ) )
2007-06-17 12:39:19 +02:00
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 ' ) )
2007-08-17 10:39:47 +02:00
x . addChild ( node = xmpp . DataField ( name = ' sign_algs ' , value = ' http://www.w3.org/2000/09/xmldsig#rsa-sha256 ' , typ = ' hidden ' ) )
2007-06-17 12:39:19 +02:00
2007-09-16 06:16:45 +02:00
self . n_s = crypto . generate_nonce ( )
2007-12-08 06:49:38 +01:00
2007-06-17 12:39:19 +02:00
x . addChild ( node = xmpp . DataField ( name = ' my_nonce ' , value = base64 . b64encode ( self . n_s ) , typ = ' hidden ' ) )
2008-07-19 22:42:03 +02:00
modp_options = [ int ( g ) for g in gajim . config . get ( ' esession_modp ' ) . split ( ' , ' ) ]
2007-06-17 12:39:19 +02:00
x . addChild ( node = xmpp . DataField ( name = ' modp ' , typ = ' list-single ' , options = map ( lambda x : [ None , x ] , modp_options ) ) )
2007-08-07 09:21:29 +02:00
x . addChild ( node = self . make_dhfield ( modp_options , sigmai ) )
self . sigmai = sigmai
2007-06-17 12:39:19 +02:00
2007-08-17 10:39:47 +02:00
self . form_s = ' ' . join ( map ( lambda el : xmpp . c14n . c14n ( el ) , x . getChildren ( ) ) )
2007-06-17 12:39:19 +02:00
feature . addChild ( node = x )
self . status = ' requested-e2e '
self . send ( request )
2007-12-08 06:49:38 +01:00
2007-06-20 22:44:33 +02:00
# 4.3 esession response (bob)
2007-06-29 06:12:08 +02:00
def verify_options_bob ( self , form ) :
2007-08-17 10:39:47 +02:00
negotiated = { ' recv_pubkey ' : None , ' send_pubkey ' : None }
2007-06-29 06:12:08 +02:00
not_acceptable = [ ]
ask_user = { }
2007-06-17 12:39:19 +02:00
2007-06-20 22:44:33 +02:00
fixed = { ' disclosure ' : ' never ' ,
' security ' : ' e2e ' ,
' crypt_algs ' : ' aes128-ctr ' ,
' hash_algs ' : ' sha256 ' ,
' compress ' : ' none ' ,
' stanzas ' : ' message ' ,
' init_pubkey ' : ' none ' ,
' resp_pubkey ' : ' none ' ,
' ver ' : ' 1.0 ' ,
2007-06-29 06:12:08 +02:00
' sas_algs ' : ' sas28x5 ' }
2007-06-17 12:39:19 +02:00
2007-06-26 22:55:49 +02:00
self . encryptable_stanzas = [ ' message ' ]
2007-06-29 06:12:08 +02:00
2007-06-26 22:55:49 +02:00
self . sas_algs = ' sas28x5 '
self . cipher = AES
self . hash_alg = SHA256
self . compression = None
2007-06-29 06:12:08 +02:00
for name , field in map ( lambda name : ( name , form . getField ( name ) ) , form . asDict ( ) . keys ( ) ) :
2007-06-20 22:44:33 +02:00
options = map ( lambda x : x [ 1 ] , field . getOptions ( ) )
values = field . getValues ( )
2007-06-17 12:39:19 +02:00
2007-06-20 22:44:33 +02:00
if not field . getType ( ) in ( ' list-single ' , ' list-multi ' ) :
options = values
2007-06-17 12:39:19 +02:00
2007-06-20 22:44:33 +02:00
if name in fixed :
if fixed [ name ] in options :
2007-06-29 06:12:08 +02:00
negotiated [ name ] = fixed [ name ]
2007-06-20 22:44:33 +02:00
else :
not_acceptable . append ( name )
elif name == ' rekey_freq ' :
preferred = int ( options [ 0 ] )
2007-06-29 06:12:08 +02:00
negotiated [ ' rekey_freq ' ] = preferred
2007-06-20 22:44:33 +02:00
self . rekey_freq = preferred
2007-06-29 06:12:08 +02:00
elif name == ' logging ' :
my_prefs = self . logging_preference ( )
2007-08-18 10:59:36 +02:00
if my_prefs [ 0 ] in options : # our first choice is offered, select it
2007-06-29 06:12:08 +02:00
pref = my_prefs [ 0 ]
negotiated [ ' logging ' ] = pref
2007-08-18 10:59:36 +02:00
else : # see if other acceptable choices are offered
2007-06-29 06:12:08 +02:00
for pref in my_prefs :
if pref in options :
ask_user [ ' logging ' ] = pref
break
if not ' logging ' in ask_user :
not_acceptable . append ( name )
2007-08-17 10:39:47 +02:00
elif name == ' init_pubkey ' :
for x in ( ' key ' ) :
if x in options :
negotiated [ ' recv_pubkey ' ] = x
break
elif name == ' resp_pubkey ' :
for x in ( ' hash ' , ' key ' ) :
if x in options :
negotiated [ ' send_pubkey ' ] = x
break
elif name == ' sign_algs ' :
if ( XmlDsig + ' rsa-sha256 ' ) in options :
negotiated [ ' sign_algs ' ] = XmlDsig + ' rsa-sha256 '
2007-06-29 06:12:08 +02:00
else :
2007-08-07 09:21:29 +02:00
# XXX some things are handled elsewhere, some things are not-implemented
2007-06-29 06:12:08 +02:00
pass
return ( negotiated , not_acceptable , ask_user )
# 4.3 esession response (bob)
def respond_e2e_bob ( self , form , negotiated , not_acceptable ) :
response = xmpp . Message ( )
feature = response . NT . feature
feature . setNamespace ( xmpp . NS_FEATURE )
2007-06-17 12:39:19 +02:00
2007-06-29 06:12:08 +02:00
x = xmpp . DataForm ( typ = ' submit ' )
2007-06-17 12:39:19 +02:00
2007-06-29 06:12:08 +02:00
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 :
2007-08-18 10:59:36 +02:00
# 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 ] ) )
2007-06-29 06:12:08 +02:00
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 ( )
2007-08-20 10:16:48 +02:00
self . negotiated [ ' He ' ] = base64 . b64decode ( dhhashes [ group_order ] . encode ( " utf8 " ) )
2007-06-17 12:39:19 +02:00
2007-06-20 22:44:33 +02:00
bytes = int ( self . n / 8 )
2007-06-17 12:39:19 +02:00
2007-09-16 06:16:45 +02:00
self . n_s = crypto . generate_nonce ( )
2007-06-20 22:44:33 +02:00
2007-09-16 06:16:45 +02:00
self . c_o = crypto . decode_mpi ( crypto . random_bytes ( bytes ) ) # n-bit random number
2007-06-20 22:44:33 +02:00
self . c_s = self . c_o ^ ( 2 * * ( self . n - 1 ) )
2007-09-16 06:16:45 +02:00
self . y = crypto . srand ( 2 * * ( 2 * self . n - 1 ) , p - 1 )
self . d = crypto . powmod ( g , self . y , p )
2007-06-20 22:44:33 +02:00
2007-06-29 06:12:08 +02:00
to_add = { ' my_nonce ' : self . n_s ,
2007-12-27 19:58:07 +01:00
' dhkeys ' : crypto . encode_mpi ( self . d ) ,
' counter ' : crypto . encode_mpi ( self . c_o ) ,
' nonce ' : self . n_o }
2007-06-20 22:44:33 +02:00
for name in to_add :
b64ed = base64 . b64encode ( to_add [ name ] )
x . addChild ( node = xmpp . DataField ( name = name , value = b64ed ) )
2007-08-17 10:39:47 +02:00
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 ( ) ) )
2007-06-20 22:44:33 +02:00
self . status = ' responded-e2e '
feature . addChild ( node = x )
2007-11-28 20:26:40 +01:00
2007-06-29 06:12:08 +02:00
if not_acceptable :
2007-08-18 10:59:36 +02:00
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 )
2007-06-29 06:12:08 +02:00
2007-06-20 22:44:33 +02:00
self . send ( response )
2007-06-17 12:39:19 +02:00
# 'Alice Accepts'
2007-06-29 06:12:08 +02:00
def verify_options_alice ( self , form ) :
negotiated = { }
ask_user = { }
not_acceptable = [ ]
if not form [ ' logging ' ] in self . logging_preference ( ) :
not_acceptable . append ( form [ ' logging ' ] )
elif form [ ' logging ' ] != self . logging_preference ( ) [ 0 ] :
ask_user [ ' logging ' ] = form [ ' logging ' ]
else :
negotiated [ ' logging ' ] = self . logging_preference ( ) [ 0 ]
2007-08-17 10:39:47 +02:00
for r , a in ( ( ' recv_pubkey ' , ' resp_pubkey ' ) , ( ' send_pubkey ' , ' init_pubkey ' ) ) :
negotiated [ r ] = None
if a in form . asDict ( ) and form [ a ] in ( ' key ' , ' hash ' ) :
negotiated [ r ] = form [ a ]
if ' sign_algs ' in form . asDict ( ) :
if form [ ' sign_algs ' ] in ( XmlDsig + ' rsa-sha256 ' , ) :
negotiated [ ' sign_algs ' ] = form [ ' sign_algs ' ]
else :
not_acceptable . append ( form [ ' sign_algs ' ] )
2007-06-29 06:12:08 +02:00
return ( negotiated , not_acceptable , ask_user )
# 'Alice Accepts', continued
def accept_e2e_alice ( self , form , negotiated ) :
2007-06-26 22:55:49 +02:00
self . encryptable_stanzas = [ ' message ' ]
self . sas_algs = ' sas28x5 '
self . cipher = AES
self . hash_alg = SHA256
self . compression = None
2007-06-29 06:12:08 +02:00
self . negotiated = negotiated
2007-06-17 12:39:19 +02:00
accept = xmpp . Message ( )
feature = accept . NT . feature
feature . setNamespace ( xmpp . NS_FEATURE )
result = xmpp . DataForm ( typ = ' result ' )
2007-09-16 06:16:45 +02:00
self . c_s = crypto . decode_mpi ( base64 . b64decode ( form [ ' counter ' ] ) )
2007-06-17 12:39:19 +02:00
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 ]
2007-09-16 06:16:45 +02:00
self . d = crypto . decode_mpi ( base64 . b64decode ( form [ ' dhkeys ' ] ) )
2007-06-17 12:39:19 +02:00
2007-08-07 09:21:29 +02:00
self . k = self . get_shared_secret ( self . d , x , p )
2007-06-17 12:39:19 +02:00
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 ) ) )
2007-08-07 09:21:29 +02:00
self . kc_s , self . km_s , self . ks_s = self . generate_initiator_keys ( self . k )
2007-07-17 10:08:27 +02:00
2007-08-07 09:21:29 +02:00
if self . sigmai :
self . kc_o , self . km_o , self . ks_o = self . generate_responder_keys ( self . k )
2007-08-17 10:39:47 +02:00
self . verify_identity ( form , self . d , True , ' b ' )
2007-08-07 09:21:29 +02:00
else :
2007-12-08 06:49:38 +01:00
srses = secrets . secrets ( ) . retained_secrets ( self . conn . name , self . jid . getStripped ( ) )
2007-09-29 22:51:01 +02:00
rshashes = [ self . hmac ( self . n_s , rs ) for ( rs , v ) in srses ]
2007-07-17 10:08:27 +02:00
2007-09-29 22:51:01 +02:00
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 ) )
2007-06-17 12:39:19 +02:00
2007-08-07 09:21:29 +02:00
rshashes = [ base64 . b64encode ( rshash ) for rshash in rshashes ]
result . addChild ( node = xmpp . DataField ( name = ' rshashes ' , value = rshashes ) )
2007-09-16 06:16:45 +02:00
result . addChild ( node = xmpp . DataField ( name = ' dhkeys ' , value = base64 . b64encode ( crypto . encode_mpi ( e ) ) ) )
2007-12-08 06:49:38 +01:00
2007-08-17 10:39:47 +02:00
self . form_o = ' ' . join ( map ( lambda el : xmpp . c14n . c14n ( el ) , form . getChildren ( ) ) )
2007-08-07 09:42:31 +02:00
2007-06-17 12:39:19 +02:00
# MUST securely destroy K unless it will be used later to generate the final shared secret
2007-08-17 11:02:53 +02:00
for datafield in self . make_identity ( result , e ) :
2007-08-07 09:21:29 +02:00
result . addChild ( node = datafield )
2007-06-17 12:39:19 +02:00
feature . addChild ( node = result )
self . send ( accept )
2007-12-08 06:49:38 +01:00
2007-08-07 09:21:29 +02:00
if self . sigmai :
self . status = ' active '
self . enable_encryption = True
else :
self . status = ' identified-alice '
2007-12-08 06:49:38 +01:00
2007-06-20 22:44:33 +02:00
# 4.5 esession accept (bob)
def accept_e2e_bob ( self , form ) :
response = xmpp . Message ( )
init = response . NT . init
2007-08-20 21:02:58 +02:00
init . setNamespace ( xmpp . NS_ESESSION_INIT )
2007-06-20 22:44:33 +02:00
x = xmpp . DataForm ( typ = ' result ' )
for field in ( ' nonce ' , ' dhkeys ' , ' rshashes ' , ' identity ' , ' mac ' ) :
2007-07-10 07:41:43 +02:00
assert field in form . asDict ( ) , " alice ' s form didn ' t have a %s field " % field
2007-06-20 22:44:33 +02:00
# 4.5.1 generating provisory session keys
2007-09-16 06:16:45 +02:00
e = crypto . decode_mpi ( base64 . b64decode ( form [ ' dhkeys ' ] ) )
2007-06-20 22:44:33 +02:00
p = dh . primes [ self . modp ]
2007-09-16 06:16:45 +02:00
if crypto . sha256 ( crypto . encode_mpi ( e ) ) != self . negotiated [ ' He ' ] :
2007-08-20 10:16:48 +02:00
raise exceptions . NegotiationError , ' SHA256(e) != He '
2007-08-17 10:39:47 +02:00
2007-08-07 09:21:29 +02:00
k = self . get_shared_secret ( e , self . y , p )
2007-06-20 22:44:33 +02:00
self . kc_o , self . km_o , self . ks_o = self . generate_initiator_keys ( k )
# 4.5.2 verifying alice's identity
2007-08-17 10:39:47 +02:00
self . verify_identity ( form , e , False , ' a ' )
2007-06-20 22:44:33 +02:00
# 4.5.4 generating bob's final session keys
2007-07-17 10:08:27 +02:00
srs = ' '
2007-12-08 06:49:38 +01:00
2007-09-29 22:51:01 +02:00
srses = secrets . secrets ( ) . retained_secrets ( self . conn . name , self . jid . getStripped ( ) )
2007-07-17 10:08:27 +02:00
rshashes = [ base64 . b64decode ( rshash ) for rshash in form . getField ( ' rshashes ' ) . getValues ( ) ]
2007-09-29 22:51:01 +02:00
for ( secret , verified ) in srses :
2007-07-17 10:08:27 +02:00
if self . hmac ( self . n_o , secret ) in rshashes :
srs = secret
break
2007-09-16 06:16:45 +02:00
# other shared secret
# (we're not using one)
2007-06-20 22:44:33 +02:00
oss = ' '
2007-09-16 06:16:45 +02:00
k = crypto . sha256 ( k + srs + oss )
2007-06-20 22:44:33 +02:00
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
2007-07-17 10:08:27 +02:00
if srs :
2007-09-20 21:58:31 +02:00
srshash = self . hmac ( srs , ' Shared Retained Secret ' )
2007-06-20 22:44:33 +02:00
else :
2007-09-16 06:16:45 +02:00
srshash = crypto . random_bytes ( 32 )
2007-06-20 22:44:33 +02:00
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 ) ) )
2007-08-17 11:02:53 +02:00
for datafield in self . make_identity ( x , self . d ) :
2007-08-17 10:39:47 +02:00
x . addChild ( node = datafield )
2007-06-20 22:44:33 +02:00
init . addChild ( node = x )
self . send ( response )
2007-07-17 10:08:27 +02:00
self . do_retained_secret ( k , srs )
2007-06-29 06:12:08 +02:00
if self . negotiated [ ' logging ' ] == ' mustnot ' :
self . loggable = False
2007-06-20 22:44:33 +02:00
self . status = ' active '
self . enable_encryption = True
2007-06-17 12:39:19 +02:00
2008-04-26 03:52:03 +02:00
if self . control :
2007-12-08 06:49:38 +01:00
self . control . print_esession_details ( )
2007-06-17 12:39:19 +02:00
def final_steps_alice ( self , form ) :
2007-07-17 10:08:27 +02:00
srs = ' '
2007-09-29 22:51:01 +02:00
srses = secrets . secrets ( ) . retained_secrets ( self . conn . name , self . jid . getStripped ( ) )
2007-06-17 12:39:19 +02:00
2007-07-17 10:08:27 +02:00
srshash = base64 . b64decode ( form [ ' srshash ' ] )
2007-06-17 12:39:19 +02:00
2007-09-29 22:51:01 +02:00
for ( secret , verified ) in srses :
2007-07-17 10:08:27 +02:00
if self . hmac ( secret , ' Shared Retained Secret ' ) == srshash :
srs = secret
break
2007-06-17 12:39:19 +02:00
oss = ' '
2007-09-16 06:16:45 +02:00
k = crypto . sha256 ( self . k + srs + oss )
2007-07-17 10:08:27 +02:00
del self . k
2007-06-17 12:39:19 +02:00
2007-07-17 10:08:27 +02:00
self . do_retained_secret ( k , srs )
2007-06-17 12:39:19 +02:00
2008-04-26 03:52:03 +02:00
# ks_s doesn't need to be calculated here
2007-07-17 10:08:27 +02:00
self . kc_s , self . km_s , self . ks_s = self . generate_initiator_keys ( k )
self . kc_o , self . km_o , self . ks_o = self . generate_responder_keys ( k )
2007-06-17 12:39:19 +02:00
2007-07-17 10:08:27 +02:00
# 4.6.2 Verifying Bob's Identity
2007-06-17 12:39:19 +02:00
2007-08-17 10:39:47 +02:00
self . verify_identity ( form , self . d , False , ' b ' )
2008-04-26 03:52:03 +02:00
# Note: If Alice discovers an error then she SHOULD ignore any encrypted content she received in the stanza.
2007-12-08 06:49:38 +01:00
2007-06-29 06:12:08 +02:00
if self . negotiated [ ' logging ' ] == ' mustnot ' :
self . loggable = False
2007-06-17 12:39:19 +02:00
self . status = ' active '
self . enable_encryption = True
2007-06-08 21:42:02 +02:00
2008-04-26 03:52:03 +02:00
if self . control :
2007-12-08 06:49:38 +01:00
self . control . print_esession_details ( )
2008-06-29 06:39:29 +02:00
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 been verified. '''
2007-07-17 10:08:27 +02:00
new_srs = self . hmac ( k , ' New Retained Secret ' )
2008-06-29 06:39:29 +02:00
self . srs = new_srs
2007-07-17 10:08:27 +02:00
account = self . conn . name
bjid = self . jid . getStripped ( )
2008-06-29 06:39:29 +02:00
self . verified_identity = False
2007-09-29 22:51:01 +02:00
2008-06-29 06:39:29 +02:00
if old_srs :
if secrets . secrets ( ) . srs_verified ( account , bjid , old_srs ) :
# already had a stored secret verified by the user.
secrets . secrets ( ) . replace_srs ( account , bjid , old_srs , new_srs , True )
# continue without warning.
self . verified_identity = True
else :
# had a secret, but it wasn't verified.
secrets . secrets ( ) . replace_srs ( account , bjid , old_srs , new_srs , False )
2007-07-17 10:08:27 +02:00
else :
2008-06-29 06:39:29 +02:00
# we don't even have an SRS
secrets . secrets ( ) . save_new_srs ( account , bjid , new_srs , False )
2007-09-29 22:51:01 +02:00
2008-06-29 06:39:29 +02:00
def _verified_srs_cb ( self ) :
secrets . secrets ( ) . replace_srs ( self . conn . name , self . jid . getStripped ( ) , self . srs , self . srs , True )
2007-07-17 10:08:27 +02:00
2007-08-07 09:21:29 +02:00
def make_dhfield ( self , modp_options , sigmai ) :
dhs = [ ]
for modp in modp_options :
p = dh . primes [ modp ]
g = dh . generators [ modp ]
2007-06-20 22:44:33 +02:00
2007-09-16 06:16:45 +02:00
x = crypto . srand ( 2 * * ( 2 * self . n - 1 ) , p - 1 )
2007-06-20 22:44:33 +02:00
2007-08-07 09:21:29 +02:00
# XXX this may be a source of performance issues
2007-09-16 06:16:45 +02:00
e = crypto . powmod ( g , x , p )
2007-06-20 22:44:33 +02:00
2007-08-07 09:21:29 +02:00
self . xes [ modp ] = x
self . es [ modp ] = e
2007-06-20 22:44:33 +02:00
2007-08-07 09:21:29 +02:00
if sigmai :
2007-09-16 06:16:45 +02:00
dhs . append ( base64 . b64encode ( crypto . encode_mpi ( e ) ) )
2007-08-07 09:21:29 +02:00
name = ' dhkeys '
else :
2007-09-16 06:16:45 +02:00
He = crypto . sha256 ( crypto . encode_mpi ( e ) )
2007-08-07 09:21:29 +02:00
dhs . append ( base64 . b64encode ( He ) )
name = ' dhhashes '
2007-06-20 22:44:33 +02:00
2007-08-07 09:21:29 +02:00
return xmpp . DataField ( name = name , typ = ' hidden ' , value = dhs )
2007-06-20 22:44:33 +02:00
def terminate_e2e ( self ) :
2007-06-27 00:52:50 +02:00
self . terminate ( )
self . enable_encryption = False
def acknowledge_termination ( self ) :
StanzaSession . acknowledge_termination ( self )
2007-11-28 20:26:40 +01:00
2007-06-27 00:52:50 +02:00
self . enable_encryption = False
2007-06-29 06:12:08 +02:00
2007-11-28 20:26:40 +01:00
def fail_bad_negotiation ( self , reason , fields = None ) :
''' sends an error and cancels everything.
2008-04-18 02:02:56 +02:00
if fields is None , the remote party has given us a bad cryptographic value of some kind
2007-11-28 20:26:40 +01:00
otherwise , list the fields we haven ' t implemented ' ' '
2007-08-20 10:16:48 +02:00
2007-07-12 08:25:05 +02:00
err = xmpp . Error ( xmpp . Message ( ) , xmpp . ERR_FEATURE_NOT_IMPLEMENTED )
err . T . error . T . text . setData ( reason )
2007-11-28 20:26:40 +01:00
if fields :
feature = xmpp . Node ( xmpp . NS_FEATURE + ' feature ' )
for field in fields :
fn = xmpp . Node ( ' field ' )
fn [ ' var ' ] = field
feature . addChild ( node = feature )
err . addChild ( node = feature )
2007-07-12 08:25:05 +02:00
self . send ( err )
2007-08-20 10:16:48 +02:00
2007-07-12 08:25:05 +02:00
self . status = None
2007-08-20 10:16:48 +02:00
self . enable_encryption = False
# this prevents the MAC check on decryption from succeeding,
# preventing falsified messages from going through.
self . km_o = ' '
2007-07-12 08:25:05 +02:00
2007-12-08 09:51:10 +01:00
def cancelled_negotiation ( self ) :
StanzaSession . cancelled_negotiation ( self )
self . enable_encryption = False
self . km_o = ' '
2008-07-29 21:49:31 +02:00
2008-08-03 18:46:27 +02:00
# vim: se ts=3: