1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 from common import gajim
26 from common import xmpp
27 from common.exceptions import DecryptionError, NegotiationError
28 import xmpp.c14n
29
30 import itertools
31 import random
32 import string
33 import time
34 import base64
35 import os
36 from hashlib import sha256
37 from hmac import HMAC
38 from common import crypto
39
40 if gajim.HAVE_PYCRYPTO:
41 from Crypto.Cipher import AES
42 from Crypto.PublicKey import RSA
43
44 from common import dh
45 import secrets
46
47 XmlDsig = 'http://www.w3.org/2000/09/xmldsig#'
48
50 '''
51 '''
52 - def __init__(self, conn, jid, thread_id, type_):
53 '''
54 '''
55 self.conn = conn
56 self.jid = jid
57 self.type = type_
58 self.resource = None
59
60 if thread_id:
61 self.received_thread_id = True
62 self.thread_id = thread_id
63 else:
64 self.received_thread_id = False
65 if type_ == 'normal':
66 self.thread_id = None
67 else:
68 self.thread_id = self.generate_thread_id()
69
70 self.loggable = True
71
72 self.last_send = 0
73 self.last_receive = 0
74 self.status = None
75 self.negotiated = {}
76
79
81 to = str(self.jid)
82 if self.resource and not to.endswith(self.resource):
83 to += '/' + self.resource
84 return to
85
87 """
88 Remove events associated with this session from the queue
89
90 Returns True if any events were removed (unlike events.py remove_events)
91 """
92 any_removed = False
93
94 for j in (self.jid, self.jid.getStripped()):
95 for event in gajim.events.get_events(self.conn.name, j, types=types):
96
97 if (event.type_ == 'chat' and event.parameters[8] != self) or \
98 (event.type_ == 'printed_chat' and event.parameters[0].session != \
99 self):
100 continue
101
102
103
104 r = gajim.events.remove_events(self.conn.name, j, event)
105
106 if not r:
107 any_removed = True
108
109 return any_removed
110
112 return ''.join([f(string.ascii_letters) for f in itertools.repeat(
113 random.choice, 32)])
114
115 - def send(self, msg):
116 if self.thread_id:
117 msg.NT.thread = self.thread_id
118
119 msg.setAttr('to', self.get_to())
120 self.conn.send_stanza(msg)
121
122 if isinstance(msg, xmpp.Message):
123 self.last_send = time.time()
124
142
144 """
145 A negotiation has been cancelled, so reset this session to its default
146 state
147 """
148 if self.control:
149 self.control.on_cancel_session_negotiation()
150
151 self.status = None
152 self.negotiated = {}
153
154 - def terminate(self, send_termination = True):
172
176
177
179 """
180 An encrypted stanza negotiation has several states. They arerepresented as
181 the following values in the 'status' attribute of the session object:
182
183 1. None:
184 default state
185 2. 'requested-e2e':
186 this client has initiated an esession negotiation and is waiting
187 for a response
188 3. 'responded-e2e':
189 this client has responded to an esession negotiation request and
190 is waiting for the initiator to identify itself and complete the
191 negotiation
192 4. 'identified-alice':
193 this client identified itself and is waiting for the responder to
194 identify itself and complete the negotiation
195 5. 'active':
196 an encrypted session has been successfully negotiated. messages
197 of any of the types listed in 'encryptable_stanzas' should be
198 encrypted before they're sent.
199
200 The transition between these states is handled in gajim.py's
201 handle_session_negotiation method.
202 """
203
204 - def __init__(self, conn, jid, thread_id, type_='chat'):
205 StanzaSession.__init__(self, conn, jid, thread_id, type_='chat')
206
207 self.xes = {}
208 self.es = {}
209 self.n = 128
210 self.enable_encryption = False
211
212
213 self._kc_s = None
214
215 self._kc_o = None
216
217
218 self.verified_identity = False
219
225
231
233 """
234 Keep the encrypter updated with my latest cipher key
235 """
236 self._kc_s = value
237 self.encrypter = self.cipher.new(self._kc_s, self.cipher.MODE_CTR,
238 counter=self.encryptcounter)
239
242
244 """
245 Keep the decrypter updated with the other party's latest cipher key
246 """
247 self._kc_o = value
248 self.decrypter = self.cipher.new(self._kc_o, self.cipher.MODE_CTR,
249 counter=self.decryptcounter)
250
253
254 kc_s = property(get_kc_s, set_kc_s)
255 kc_o = property(get_kc_o, set_kc_o)
256
260
264
265 - def sign(self, string):
269
271 encryptable = [x for x in stanza.getChildren() if x.getName() not in
272 ('error', 'amp', 'thread')]
273
274
275
276
277
278
279 old_en_counter = self.c_s
280
281 for element in encryptable:
282 stanza.delChild(element)
283
284 plaintext = ''.join(map(str, encryptable))
285
286 m_compressed = self.compress(plaintext)
287 m_final = self.encrypt(m_compressed)
288
289 c = stanza.NT.c
290 c.setNamespace('http://www.xmpp.org/extensions/xep-0200.html#ns')
291 c.NT.data = base64.b64encode(m_final)
292
293
294
295 m_content = ''.join(map(str, c.getChildren()))
296 c.NT.mac = base64.b64encode(self.hmac(self.km_s, m_content + \
297 crypto.encode_mpi(old_en_counter)))
298
299 msgtxt = '[This is part of an encrypted session. ' \
300 'If you see this message, something went wrong.]'
301 lang = os.getenv('LANG')
302 if lang is not None and lang != 'en':
303 msgtxt = _('[This is part of an encrypted session. '
304 'If you see this message, something went wrong.]') + ' (' + \
305 msgtxt + ')'
306 stanza.setBody(msgtxt)
307
308 return stanza
309
312
313 - def hmac(self, key, content):
314 return HMAC(key, content, self.hash_alg).digest()
315
317 return (self.hmac(k, 'Initiator Cipher Key'),
318 self.hmac(k, 'Initiator MAC Key'),
319 self.hmac(k, 'Initiator SIGMA Key'))
320
322 return (self.hmac(k, 'Responder Cipher Key'),
323 self.hmac(k, 'Responder MAC Key'),
324 self.hmac(k, 'Responder SIGMA Key'))
325
327 if self.compression is None:
328 return plaintext
329
331 if self.compression is None:
332 return compressed
333
338
340 """
341 Delete the unencrypted explanation body, if it exists
342 """
343 orig_body = stanza.getTag('body')
344 if orig_body:
345 stanza.delChild(orig_body)
346
347 c = stanza.getTag(name='c',
348 namespace='http://www.xmpp.org/extensions/xep-0200.html#ns')
349
350 stanza.delChild(c)
351
352
353 macable = ''.join(str(x) for x in c.getChildren() if x.getName() != 'mac')
354
355 received_mac = base64.b64decode(c.getTagData('mac'))
356 calculated_mac = self.hmac(self.km_o, macable + \
357 crypto.encode_mpi_with_padding(self.c_o))
358
359 if not calculated_mac == received_mac:
360 raise DecryptionError('bad signature')
361
362 m_final = base64.b64decode(c.getTagData('data'))
363 m_compressed = self.decrypt(m_final)
364 plaintext = self.decompress(m_compressed)
365
366 try:
367 parsed = xmpp.Node(node='<node>' + plaintext + '</node>')
368 except Exception:
369 raise DecryptionError('decrypted <data/> not parseable as XML')
370
371 for child in parsed.getChildren():
372 stanza.addChild(node=child)
373
374 return stanza
375
377 return self.decrypter.decrypt(ciphertext)
378
380 if gajim.config.get_per('accounts', self.conn.name,
381 'log_encrypted_sessions'):
382 return ['may', 'mustnot']
383 else:
384 return ['mustnot', 'may']
385
391
397
399 m_o = base64.b64decode(form['mac'])
400 id_o = base64.b64decode(form['identity'])
401
402 m_o_calculated = self.hmac(self.km_o, crypto.encode_mpi(self.c_o) + id_o)
403
404 if m_o_calculated != m_o:
405 raise NegotiationError('calculated m_%s differs from received m_%s' %
406 (i_o, i_o))
407
408 if i_o == 'a' and self.sas_algs == 'sas28x5':
409
410
411 self.sas = crypto.sas_28x5(m_o, self.form_s)
412
413 if self.negotiated['recv_pubkey']:
414 plaintext = self.decrypt(id_o)
415 parsed = xmpp.Node(node='<node>' + plaintext + '</node>')
416
417 if self.negotiated['recv_pubkey'] == 'hash':
418
419
420 raise NotImplementedError()
421 else:
422 if self.negotiated['sign_algs'] == (XmlDsig + 'rsa-sha256'):
423 keyvalue = parsed.getTag(name='RSAKeyValue', namespace=XmlDsig)
424
425 n, e = (crypto.decode_mpi(base64.b64decode(
426 keyvalue.getTagData(x))) for x in ('Modulus', 'Exponent'))
427 eir_pubkey = RSA.construct((n, long(e)))
428
429 pubkey_o = xmpp.c14n.c14n(keyvalue, self._is_buggy_gajim())
430 else:
431
432 raise NotImplementedError()
433
434 enc_sig = parsed.getTag(name='SignatureValue',
435 namespace=XmlDsig).getData()
436 signature = (crypto.decode_mpi(base64.b64decode(enc_sig)), )
437 else:
438 mac_o = self.decrypt(id_o)
439 pubkey_o = ''
440
441 c7l_form = self.c7lize_mac_id(form)
442
443 content = self.n_s + self.n_o + crypto.encode_mpi(dh_i) + pubkey_o
444
445 if sigmai:
446 self.form_o = c7l_form
447 content += self.form_o
448 else:
449 form_o2 = c7l_form
450 content += self.form_o + form_o2
451
452 mac_o_calculated = self.hmac(self.ks_o, content)
453
454 if self.negotiated['recv_pubkey']:
455 hash_ = crypto.sha256(mac_o_calculated)
456
457 if not eir_pubkey.verify(hash_, signature):
458 raise NegotiationError('public key signature verification failed!')
459
460 elif mac_o_calculated != mac_o:
461 raise NegotiationError('calculated mac_%s differs from received mac_%s'
462 % (i_o, i_o))
463
465 if self.negotiated['send_pubkey']:
466 if self.negotiated['sign_algs'] == (XmlDsig + 'rsa-sha256'):
467 pubkey = secrets.secrets().my_pubkey(self.conn.name)
468 fields = (pubkey.n, pubkey.e)
469
470 cb_fields = [base64.b64encode(crypto.encode_mpi(f)) for f in
471 fields]
472
473 pubkey_s = '<RSAKeyValue xmlns="http://www.w3.org/2000/09/xmldsig#"'
474 '><Modulus>%s</Modulus><Exponent>%s</Exponent></RSAKeyValue>' % \
475 tuple(cb_fields)
476 else:
477 pubkey_s = ''
478
479 form_s2 = ''.join(xmpp.c14n.c14n(el, self._is_buggy_gajim()) for el in \
480 form.getChildren())
481
482 old_c_s = self.c_s
483 content = self.n_o + self.n_s + crypto.encode_mpi(dh_i) + pubkey_s + \
484 self.form_s + form_s2
485
486 mac_s = self.hmac(self.ks_s, content)
487
488 if self.negotiated['send_pubkey']:
489 signature = self.sign(mac_s)
490
491 sign_s = '<SignatureValue xmlns="http://www.w3.org/2000/09/xmldsig#">'
492 '%s</SignatureValue>' % base64.b64encode(signature)
493
494 if self.negotiated['send_pubkey'] == 'hash':
495 b64ed = base64.b64encode(self.hash(pubkey_s))
496 pubkey_s = '<fingerprint>%s</fingerprint>' % b64ed
497
498 id_s = self.encrypt(pubkey_s + sign_s)
499 else:
500 id_s = self.encrypt(mac_s)
501
502 m_s = self.hmac(self.km_s, crypto.encode_mpi(old_c_s) + id_s)
503
504 if self.status == 'requested-e2e' and self.sas_algs == 'sas28x5':
505
506
507 self.sas = crypto.sas_28x5(m_s, self.form_o)
508
509 if self.sigmai:
510
511 self.check_identity(tuple)
512
513 return (xmpp.DataField(name='identity', value=base64.b64encode(id_s)),
514 xmpp.DataField(name='mac', value=base64.b64encode(m_s)))
515
517 self.negotiated = {}
518
519 request = xmpp.Message()
520 feature = request.NT.feature
521 feature.setNamespace(xmpp.NS_FEATURE)
522
523 x = xmpp.DataForm(typ='form')
524
525 x.addChild(node=xmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn',
526 typ='hidden'))
527 x.addChild(node=xmpp.DataField(name='accept', value='1', typ='boolean',
528 required=True))
529
530
531 x.addChild(node=xmpp.DataField(name='logging', typ='list-single',
532 options=self.logging_preference(), required=True))
533
534
535 x.addChild(node=xmpp.DataField(name='disclosure', typ='list-single',
536 options=['never'], required=True))
537 x.addChild(node=xmpp.DataField(name='security', typ='list-single',
538 options=['e2e'], required=True))
539 x.addChild(node=xmpp.DataField(name='crypt_algs', value='aes128-ctr',
540 typ='hidden'))
541 x.addChild(node=xmpp.DataField(name='hash_algs', value='sha256',
542 typ='hidden'))
543 x.addChild(node=xmpp.DataField(name='compress', value='none',
544 typ='hidden'))
545
546
547 x.addChild(node=xmpp.DataField(name='stanzas', typ='list-multi',
548 options=['message']))
549
550 x.addChild(node=xmpp.DataField(name='init_pubkey', options=['none', 'key',
551 'hash'], typ='list-single'))
552
553
554 x.addChild(node=xmpp.DataField(name='resp_pubkey', options=['none',
555 'key'], typ='list-single'))
556
557 x.addChild(node=xmpp.DataField(name='ver', value='1.0', typ='hidden'))
558
559 x.addChild(node=xmpp.DataField(name='rekey_freq', value='4294967295',
560 typ='hidden'))
561
562 x.addChild(node=xmpp.DataField(name='sas_algs', value='sas28x5',
563 typ='hidden'))
564 x.addChild(node=xmpp.DataField(name='sign_algs',
565 value='http://www.w3.org/2000/09/xmldsig#rsa-sha256', typ='hidden'))
566
567 self.n_s = crypto.generate_nonce()
568
569 x.addChild(node=xmpp.DataField(name='my_nonce',
570 value=base64.b64encode(self.n_s), typ='hidden'))
571
572 modp_options = [ int(g) for g in gajim.config.get('esession_modp').split(
573 ',') ]
574
575 x.addChild(node=xmpp.DataField(name='modp', typ='list-single',
576 options=[[None, y] for y in modp_options]))
577
578 x.addChild(node=self.make_dhfield(modp_options, sigmai))
579 self.sigmai = sigmai
580
581 self.form_s = ''.join(xmpp.c14n.c14n(el, self._is_buggy_gajim()) for el \
582 in x.getChildren())
583
584 feature.addChild(node=x)
585
586 self.status = 'requested-e2e'
587
588 self.send(request)
589
591 """
592 4.3 esession response (bob)
593 """
594 negotiated = {'recv_pubkey': None, 'send_pubkey': None}
595 not_acceptable = []
596 ask_user = {}
597
598 fixed = { 'disclosure': 'never', 'security': 'e2e',
599 'crypt_algs': 'aes128-ctr', 'hash_algs': 'sha256', 'compress': 'none',
600 'stanzas': 'message', 'init_pubkey': 'none', 'resp_pubkey': 'none',
601 'ver': '1.0', 'sas_algs': 'sas28x5' }
602
603 self.encryptable_stanzas = ['message']
604
605 self.sas_algs = 'sas28x5'
606 self.cipher = AES
607 self.hash_alg = sha256
608 self.compression = None
609
610 for name in form.asDict():
611 field = form.getField(name)
612 options = [x[1] for x in field.getOptions()]
613 values = field.getValues()
614
615 if not field.getType() in ('list-single', 'list-multi'):
616 options = values
617
618 if name in fixed:
619 if fixed[name] in options:
620 negotiated[name] = fixed[name]
621 else:
622 not_acceptable.append(name)
623 elif name == 'rekey_freq':
624 preferred = int(options[0])
625 negotiated['rekey_freq'] = preferred
626 self.rekey_freq = preferred
627 elif name == 'logging':
628 my_prefs = self.logging_preference()
629
630 if my_prefs[0] in options:
631 pref = my_prefs[0]
632 negotiated['logging'] = pref
633 else:
634 for pref in my_prefs:
635 if pref in options:
636 ask_user['logging'] = pref
637 break
638
639 if not 'logging' in ask_user:
640 not_acceptable.append(name)
641 elif name == 'init_pubkey':
642 for x in ('key'):
643 if x in options:
644 negotiated['recv_pubkey'] = x
645 break
646 elif name == 'resp_pubkey':
647 for x in ('hash', 'key'):
648 if x in options:
649 negotiated['send_pubkey'] = x
650 break
651 elif name == 'sign_algs':
652 if (XmlDsig + 'rsa-sha256') in options:
653 negotiated['sign_algs'] = XmlDsig + 'rsa-sha256'
654 else:
655
656
657 pass
658
659 return (negotiated, not_acceptable, ask_user)
660
662 """
663 4.3 esession response (bob)
664 """
665 response = xmpp.Message()
666 feature = response.NT.feature
667 feature.setNamespace(xmpp.NS_FEATURE)
668
669 x = xmpp.DataForm(typ='submit')
670
671 x.addChild(node=xmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn'))
672 x.addChild(node=xmpp.DataField(name='accept', value='true'))
673
674 for name in negotiated:
675
676 if not name in ('send_pubkey', 'recv_pubkey'):
677 x.addChild(node=xmpp.DataField(name=name, value=negotiated[name]))
678
679 self.negotiated = negotiated
680
681
682 group_order = 0
683 self.modp = int(form.getField('modp').getOptions()[group_order][1])
684 x.addChild(node=xmpp.DataField(name='modp', value=self.modp))
685
686 g = dh.generators[self.modp]
687 p = dh.primes[self.modp]
688
689 self.n_o = base64.b64decode(form['my_nonce'])
690
691 dhhashes = form.getField('dhhashes').getValues()
692 self.negotiated['He'] = base64.b64decode(dhhashes[group_order].encode(
693 'utf8'))
694
695 bytes = int(self.n / 8)
696
697 self.n_s = crypto.generate_nonce()
698
699
700 self.c_o = crypto.decode_mpi(crypto.random_bytes(bytes))
701 self.c_s = self.c_o ^ (2 ** (self.n - 1))
702
703 self.y = crypto.srand(2 ** (2 * self.n - 1), p - 1)
704 self.d = crypto.powmod(g, self.y, p)
705
706 to_add = {'my_nonce': self.n_s,
707 'dhkeys': crypto.encode_mpi(self.d),
708 'counter': crypto.encode_mpi(self.c_o),
709 'nonce': self.n_o}
710
711 for name in to_add:
712 b64ed = base64.b64encode(to_add[name])
713 x.addChild(node=xmpp.DataField(name=name, value=b64ed))
714
715 self.form_o = ''.join(xmpp.c14n.c14n(el, self._is_buggy_gajim()) for el \
716 in form.getChildren())
717 self.form_s = ''.join(xmpp.c14n.c14n(el, self._is_buggy_gajim()) for el \
718 in x.getChildren())
719
720 self.status = 'responded-e2e'
721
722 feature.addChild(node=x)
723
724 if not_acceptable:
725 response = xmpp.Error(response, xmpp.ERR_NOT_ACCEPTABLE)
726
727 feature = xmpp.Node(xmpp.NS_FEATURE + ' feature')
728
729 for f in not_acceptable:
730 n = xmpp.Node('field')
731 n['var'] = f
732 feature.addChild(node=n)
733
734 response.T.error.addChild(node=feature)
735
736 self.send(response)
737
739 """
740 'Alice Accepts'
741 """
742 negotiated = {}
743 ask_user = {}
744 not_acceptable = []
745
746 if not form['logging'] in self.logging_preference():
747 not_acceptable.append(form['logging'])
748 elif form['logging'] != self.logging_preference()[0]:
749 ask_user['logging'] = form['logging']
750 else:
751 negotiated['logging'] = self.logging_preference()[0]
752
753 for r, a in (('recv_pubkey', 'resp_pubkey'), ('send_pubkey',
754 'init_pubkey')):
755 negotiated[r] = None
756
757 if a in form.asDict() and form[a] in ('key', 'hash'):
758 negotiated[r] = form[a]
759
760 if 'sign_algs' in form.asDict():
761 if form['sign_algs'] in (XmlDsig + 'rsa-sha256', ):
762 negotiated['sign_algs'] = form['sign_algs']
763 else:
764 not_acceptable.append(form['sign_algs'])
765
766 return (negotiated, not_acceptable, ask_user)
767
769 """
770 'Alice Accepts', continued
771 """
772 self.encryptable_stanzas = ['message']
773 self.sas_algs = 'sas28x5'
774 self.cipher = AES
775 self.hash_alg = sha256
776 self.compression = None
777
778 self.negotiated = negotiated
779
780 accept = xmpp.Message()
781 feature = accept.NT.feature
782 feature.setNamespace(xmpp.NS_FEATURE)
783
784 result = xmpp.DataForm(typ='result')
785
786 self.c_s = crypto.decode_mpi(base64.b64decode(form['counter']))
787 self.c_o = self.c_s ^ (2 ** (self.n - 1))
788 self.n_o = base64.b64decode(form['my_nonce'])
789
790 mod_p = int(form['modp'])
791 p = dh.primes[mod_p]
792 x = self.xes[mod_p]
793 e = self.es[mod_p]
794
795 self.d = crypto.decode_mpi(base64.b64decode(form['dhkeys']))
796 self.k = self.get_shared_secret(self.d, x, p)
797
798 result.addChild(node=xmpp.DataField(name='FORM_TYPE',
799 value='urn:xmpp:ssn'))
800 result.addChild(node=xmpp.DataField(name='accept', value='1'))
801 result.addChild(node=xmpp.DataField(name='nonce',
802 value=base64.b64encode(self.n_o)))
803
804 self.kc_s, self.km_s, self.ks_s = self.generate_initiator_keys(self.k)
805
806 if self.sigmai:
807 self.kc_o, self.km_o, self.ks_o = self.generate_responder_keys(self.k)
808 self.verify_identity(form, self.d, True, 'b')
809 else:
810 srses = secrets.secrets().retained_secrets(self.conn.name,
811 self.jid.getStripped())
812 rshashes = [self.hmac(self.n_s, rs[0]) for rs in srses]
813
814 if not rshashes:
815
816 rshash_size = self.hash_alg().digest_size
817 rshashes.append(crypto.random_bytes(rshash_size))
818
819 rshashes = [base64.b64encode(rshash) for rshash in rshashes]
820 result.addChild(node=xmpp.DataField(name='rshashes', value=rshashes))
821 result.addChild(node=xmpp.DataField(name='dhkeys',
822 value=base64.b64encode(crypto.encode_mpi(e))))
823
824 self.form_o = ''.join(xmpp.c14n.c14n(el, self._is_buggy_gajim()) for \
825 el in form.getChildren())
826
827
828
829
830 for datafield in self.make_identity(result, e):
831 result.addChild(node=datafield)
832
833 feature.addChild(node=result)
834 self.send(accept)
835
836 if self.sigmai:
837 self.status = 'active'
838 self.enable_encryption = True
839 else:
840 self.status = 'identified-alice'
841
843 """
844 4.5 esession accept (bob)
845 """
846 response = xmpp.Message()
847
848 init = response.NT.init
849 init.setNamespace(xmpp.NS_ESESSION_INIT)
850
851 x = xmpp.DataForm(typ='result')
852
853 for field in ('nonce', 'dhkeys', 'rshashes', 'identity', 'mac'):
854
855 assert field in form.asDict(), "alice's form didn't have a %s field" \
856 % field
857
858
859 e = crypto.decode_mpi(base64.b64decode(form['dhkeys']))
860 p = dh.primes[self.modp]
861
862 if crypto.sha256(crypto.encode_mpi(e)) != self.negotiated['He']:
863 raise NegotiationError('SHA256(e) != He')
864
865 k = self.get_shared_secret(e, self.y, p)
866 self.kc_o, self.km_o, self.ks_o = self.generate_initiator_keys(k)
867
868
869 self.verify_identity(form, e, False, 'a')
870
871
872 srs = ''
873
874 srses = secrets.secrets().retained_secrets(self.conn.name,
875 self.jid.getStripped())
876 rshashes = [base64.b64decode(rshash) for rshash in form.getField(
877 'rshashes').getValues()]
878
879 for s in srses:
880 secret = s[0]
881 if self.hmac(self.n_o, secret) in rshashes:
882 srs = secret
883 break
884
885
886
887 oss = ''
888
889 k = crypto.sha256(k + srs + oss)
890
891 self.kc_s, self.km_s, self.ks_s = self.generate_responder_keys(k)
892 self.kc_o, self.km_o, self.ks_o = self.generate_initiator_keys(k)
893
894
895 if srs:
896 srshash = self.hmac(srs, 'Shared Retained Secret')
897 else:
898 srshash = crypto.random_bytes(32)
899
900 x.addChild(node=xmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn'))
901 x.addChild(node=xmpp.DataField(name='nonce', value=base64.b64encode(
902 self.n_o)))
903 x.addChild(node=xmpp.DataField(name='srshash', value=base64.b64encode(
904 srshash)))
905
906 for datafield in self.make_identity(x, self.d):
907 x.addChild(node=datafield)
908
909 init.addChild(node=x)
910
911 self.send(response)
912
913 self.do_retained_secret(k, srs)
914
915 if self.negotiated['logging'] == 'mustnot':
916 self.loggable = False
917
918 self.status = 'active'
919 self.enable_encryption = True
920
921 if self.control:
922 self.control.print_esession_details()
923
925 srs = ''
926 srses = secrets.secrets().retained_secrets(self.conn.name,
927 self.jid.getStripped())
928
929 try:
930 srshash = base64.b64decode(form['srshash'])
931 except IndexError:
932 return
933
934 for s in srses:
935 secret = s[0]
936 if self.hmac(secret, 'Shared Retained Secret') == srshash:
937 srs = secret
938 break
939
940 oss = ''
941 k = crypto.sha256(self.k + srs + oss)
942 del self.k
943
944 self.do_retained_secret(k, srs)
945
946
947 self.kc_s, self.km_s, self.ks_s = self.generate_initiator_keys(k)
948 self.kc_o, self.km_o, self.ks_o = self.generate_responder_keys(k)
949
950
951 self.verify_identity(form, self.d, False, 'b')
952
953
954
955 if self.negotiated['logging'] == 'mustnot':
956 self.loggable = False
957
958 self.status = 'active'
959 self.enable_encryption = True
960
961 if self.control:
962 self.control.print_esession_details()
963
965 """
966 Calculate the new retained secret. determine if the user needs to check
967 the remote party's identity. Set up callbacks for when the identity has
968 been verified
969 """
970 new_srs = self.hmac(k, 'New Retained Secret')
971 self.srs = new_srs
972
973 account = self.conn.name
974 bjid = self.jid.getStripped()
975
976 self.verified_identity = False
977
978 if old_srs:
979 if secrets.secrets().srs_verified(account, bjid, old_srs):
980
981 secrets.secrets().replace_srs(account, bjid, old_srs, new_srs, True)
982
983 self.verified_identity = True
984 else:
985
986 secrets.secrets().replace_srs(account, bjid, old_srs, new_srs,
987 False)
988 else:
989
990 secrets.secrets().save_new_srs(account, bjid, new_srs, False)
991
995
999
1024
1026 self.terminate()
1027 self.enable_encryption = False
1028
1032
1034 """
1035 Send an error and cancels everything
1036
1037 If fields is None, the remote party has given us a bad cryptographic
1038 value of some kind. Otherwise, list the fields we haven't implemented.
1039 """
1040 err = xmpp.Error(xmpp.Message(), xmpp.ERR_FEATURE_NOT_IMPLEMENTED)
1041 err.T.error.T.text.setData(reason)
1042
1043 if fields:
1044 feature = xmpp.Node(xmpp.NS_FEATURE + ' feature')
1045
1046 for field in fields:
1047 fn = xmpp.Node('field')
1048 fn['var'] = field
1049 feature.addChild(node=feature)
1050
1051 err.addChild(node=feature)
1052
1053 self.send(err)
1054
1055 self.status = None
1056 self.enable_encryption = False
1057
1058
1059
1060 self.km_o = ''
1061
1066