split common crypto operations into a separate module
This commit is contained in:
parent
9091496812
commit
37b755ecef
2 changed files with 115 additions and 112 deletions
73
src/common/crypto.py
Normal file
73
src/common/crypto.py
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
# common crypto functions (mostly specific to XEP-0116, but useful elsewhere)
|
||||||
|
|
||||||
|
# convert a large integer to a big-endian bitstring
|
||||||
|
def encode_mpi(n):
|
||||||
|
if n >= 256:
|
||||||
|
return encode_mpi(n / 256) + chr(n % 256)
|
||||||
|
else:
|
||||||
|
return chr(n)
|
||||||
|
|
||||||
|
# convert a large integer to a big-endian bitstring, padded with \x00s to
|
||||||
|
# 16 bytes
|
||||||
|
def encode_mpi_with_padding(n):
|
||||||
|
ret = encode_mpi(n)
|
||||||
|
|
||||||
|
mod = len(ret) % 16
|
||||||
|
if mod != 0:
|
||||||
|
ret = ((16 - mod) * '\x00') + ret
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
# convert a big-endian bitstring to an integer
|
||||||
|
def decode_mpi(s):
|
||||||
|
if len(s) == 0:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return 256 * decode_mpi(s[:-1]) + ord(s[-1])
|
||||||
|
|
||||||
|
def sha256(string):
|
||||||
|
sh = SHA256.new()
|
||||||
|
sh.update(string)
|
||||||
|
return sh.digest()
|
||||||
|
|
||||||
|
base28_chr = "acdefghikmopqruvwxy123456789"
|
||||||
|
|
||||||
|
def sas_28x5(m_a, form_b):
|
||||||
|
sha = sha256(m_a + form_b + 'Short Authentication String')
|
||||||
|
lsb24 = decode_mpi(sha[-3:])
|
||||||
|
return base28(lsb24)
|
||||||
|
|
||||||
|
def base28(n):
|
||||||
|
if n >= 28:
|
||||||
|
return base28(n / 28) + base28_chr[n % 28]
|
||||||
|
else:
|
||||||
|
return base28_chr[n]
|
||||||
|
|
||||||
|
def random_bytes(bytes):
|
||||||
|
return os.urandom(bytes)
|
||||||
|
|
||||||
|
def generate_nonce():
|
||||||
|
return random_bytes(8)
|
||||||
|
|
||||||
|
# generate a random number between 'bottom' and 'top'
|
||||||
|
def srand(bottom, top):
|
||||||
|
# minimum number of bytes needed to represent that range
|
||||||
|
bytes = int(math.ceil(math.log(top - bottom, 256)))
|
||||||
|
|
||||||
|
# in retrospect, this is horribly inadequate.
|
||||||
|
return (decode_mpi(random_bytes(bytes)) % (top - bottom)) + bottom
|
||||||
|
|
||||||
|
# a faster version of (base ** exp) % mod
|
||||||
|
# taken from <http://lists.danga.com/pipermail/yadis/2005-September/001445.html>
|
||||||
|
def powmod(base, exp, mod):
|
||||||
|
square = base % mod
|
||||||
|
result = 1
|
||||||
|
|
||||||
|
while exp > 0:
|
||||||
|
if exp & 1: # exponent is odd
|
||||||
|
result = (result * square) % mod
|
||||||
|
|
||||||
|
square = (square * square) % mod
|
||||||
|
exp /= 2
|
||||||
|
|
||||||
|
return result
|
|
@ -12,6 +12,7 @@ import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from common import dh
|
from common import dh
|
||||||
|
from common import crypto
|
||||||
import xmpp.c14n
|
import xmpp.c14n
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
@ -164,43 +165,18 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
kc_s = property(get_kc_s, set_kc_s)
|
kc_s = property(get_kc_s, set_kc_s)
|
||||||
kc_o = property(get_kc_o, set_kc_o)
|
kc_o = property(get_kc_o, set_kc_o)
|
||||||
|
|
||||||
# convert a large integer to a big-endian bitstring
|
|
||||||
def encode_mpi(self, n):
|
|
||||||
if n >= 256:
|
|
||||||
return self.encode_mpi(n / 256) + chr(n % 256)
|
|
||||||
else:
|
|
||||||
return chr(n)
|
|
||||||
|
|
||||||
# convert a large integer to a big-endian bitstring, padded with \x00s to
|
|
||||||
# 16 bytes
|
|
||||||
def encode_mpi_with_padding(self, n):
|
|
||||||
ret = self.encode_mpi(n)
|
|
||||||
|
|
||||||
mod = len(ret) % 16
|
|
||||||
if mod != 0:
|
|
||||||
ret = ((16 - mod) * '\x00') + ret
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
# convert a big-endian bitstring to an integer
|
|
||||||
def decode_mpi(self, s):
|
|
||||||
if len(s) == 0:
|
|
||||||
return 0
|
|
||||||
else:
|
|
||||||
return 256 * self.decode_mpi(s[:-1]) + ord(s[-1])
|
|
||||||
|
|
||||||
def encryptcounter(self):
|
def encryptcounter(self):
|
||||||
self.c_s = (self.c_s + 1) % (2 ** self.n)
|
self.c_s = (self.c_s + 1) % (2 ** self.n)
|
||||||
return self.encode_mpi_with_padding(self.c_s)
|
return crypto.encode_mpi_with_padding(self.c_s)
|
||||||
|
|
||||||
def decryptcounter(self):
|
def decryptcounter(self):
|
||||||
self.c_o = (self.c_o + 1) % (2 ** self.n)
|
self.c_o = (self.c_o + 1) % (2 ** self.n)
|
||||||
return self.encode_mpi_with_padding(self.c_o)
|
return crypto.encode_mpi_with_padding(self.c_o)
|
||||||
|
|
||||||
def sign(self, string):
|
def sign(self, string):
|
||||||
if self.negotiated['sign_algs'] == (XmlDsig + 'rsa-sha256'):
|
if self.negotiated['sign_algs'] == (XmlDsig + 'rsa-sha256'):
|
||||||
hash = self.sha256(string)
|
hash = crypto.sha256(string)
|
||||||
return self.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 = filter(lambda x: x.getName() not in ('error', 'amp',
|
encryptable = filter(lambda x: x.getName() not in ('error', 'amp',
|
||||||
|
@ -229,31 +205,13 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
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 + \
|
||||||
self.encode_mpi(old_en_counter)))
|
crypto.encode_mpi(old_en_counter)))
|
||||||
|
|
||||||
return stanza
|
return stanza
|
||||||
|
|
||||||
def hmac(self, key, content):
|
def hmac(self, key, content):
|
||||||
return HMAC.new(key, content, self.hash_alg).digest()
|
return HMAC.new(key, content, self.hash_alg).digest()
|
||||||
|
|
||||||
def sha256(self, string):
|
|
||||||
sh = SHA256.new()
|
|
||||||
sh.update(string)
|
|
||||||
return sh.digest()
|
|
||||||
|
|
||||||
base28_chr = "acdefghikmopqruvwxy123456789"
|
|
||||||
|
|
||||||
def sas_28x5(self, m_a, form_b):
|
|
||||||
sha = self.sha256(m_a + form_b + 'Short Authentication String')
|
|
||||||
lsb24 = self.decode_mpi(sha[-3:])
|
|
||||||
return self.base28(lsb24)
|
|
||||||
|
|
||||||
def base28(self, n):
|
|
||||||
if n >= 28:
|
|
||||||
return self.base28(n / 28) + self.base28_chr[n % 28]
|
|
||||||
else:
|
|
||||||
return self.base28_chr[n]
|
|
||||||
|
|
||||||
def generate_initiator_keys(self, k):
|
def generate_initiator_keys(self, k):
|
||||||
return (self.hmac(k, 'Initiator Cipher Key'),
|
return (self.hmac(k, 'Initiator Cipher Key'),
|
||||||
self.hmac(k, 'Initiator MAC Key'),
|
self.hmac(k, 'Initiator MAC Key'),
|
||||||
|
@ -279,13 +237,6 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
return self.encrypter.encrypt(encryptable)
|
return self.encrypter.encrypt(encryptable)
|
||||||
|
|
||||||
# FIXME: use a real PRNG
|
|
||||||
def random_bytes(self, bytes):
|
|
||||||
return os.urandom(bytes)
|
|
||||||
|
|
||||||
def generate_nonce(self):
|
|
||||||
return self.random_bytes(8)
|
|
||||||
|
|
||||||
def decrypt_stanza(self, stanza):
|
def decrypt_stanza(self, stanza):
|
||||||
c = stanza.getTag(name='c',
|
c = stanza.getTag(name='c',
|
||||||
namespace='http://www.xmpp.org/extensions/xep-0200.html#ns')
|
namespace='http://www.xmpp.org/extensions/xep-0200.html#ns')
|
||||||
|
@ -298,7 +249,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
received_mac = base64.b64decode(c.getTagData('mac'))
|
received_mac = base64.b64decode(c.getTagData('mac'))
|
||||||
calculated_mac = self.hmac(self.km_o, macable + \
|
calculated_mac = self.hmac(self.km_o, macable + \
|
||||||
self.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 exceptions.DecryptionError, 'bad signature'
|
||||||
|
@ -325,7 +276,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
return ["may", "mustnot"]
|
return ["may", "mustnot"]
|
||||||
else:
|
else:
|
||||||
return ["mustnot", "may"]
|
return ["mustnot", "may"]
|
||||||
|
|
||||||
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 exceptions.NegotiationError, "invalid DH value"
|
||||||
|
@ -341,14 +292,14 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
m_o = base64.b64decode(form['mac'])
|
m_o = base64.b64decode(form['mac'])
|
||||||
id_o = base64.b64decode(form['identity'])
|
id_o = base64.b64decode(form['identity'])
|
||||||
|
|
||||||
m_o_calculated = self.hmac(self.km_o, self.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 exceptions.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':
|
||||||
# XXX not necessary if there's a verified retained secret
|
# XXX not necessary if there's a verified retained secret
|
||||||
self.sas = self.sas_28x5(m_o, self.form_s)
|
self.sas = crypto.sas_28x5(m_o, self.form_s)
|
||||||
|
|
||||||
if self.negotiated['recv_pubkey']:
|
if self.negotiated['recv_pubkey']:
|
||||||
plaintext = self.decrypt(id_o)
|
plaintext = self.decrypt(id_o)
|
||||||
|
@ -363,7 +314,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
if self.negotiated['sign_algs'] == (XmlDsig + 'rsa-sha256'):
|
if self.negotiated['sign_algs'] == (XmlDsig + 'rsa-sha256'):
|
||||||
keyvalue = parsed.getTag(name='RSAKeyValue', namespace=XmlDsig)
|
keyvalue = parsed.getTag(name='RSAKeyValue', namespace=XmlDsig)
|
||||||
|
|
||||||
n, e = map(lambda x: self.decode_mpi(base64.b64decode(
|
n, e = map(lambda x: crypto.decode_mpi(base64.b64decode(
|
||||||
keyvalue.getTagData(x))), ('Modulus', 'Exponent'))
|
keyvalue.getTagData(x))), ('Modulus', 'Exponent'))
|
||||||
eir_pubkey = RSA.construct((n,long(e)))
|
eir_pubkey = RSA.construct((n,long(e)))
|
||||||
|
|
||||||
|
@ -374,14 +325,14 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
enc_sig = parsed.getTag(name='SignatureValue',
|
enc_sig = parsed.getTag(name='SignatureValue',
|
||||||
namespace=XmlDsig).getData()
|
namespace=XmlDsig).getData()
|
||||||
signature = (self.decode_mpi(base64.b64decode(enc_sig)),)
|
signature = (crypto.decode_mpi(base64.b64decode(enc_sig)),)
|
||||||
else:
|
else:
|
||||||
mac_o = self.decrypt(id_o)
|
mac_o = self.decrypt(id_o)
|
||||||
pubkey_o = ''
|
pubkey_o = ''
|
||||||
|
|
||||||
c7l_form = self.c7lize_mac_id(form)
|
c7l_form = self.c7lize_mac_id(form)
|
||||||
|
|
||||||
content = self.n_s + self.n_o + self.encode_mpi(dh_i) + pubkey_o
|
content = self.n_s + self.n_o + crypto.encode_mpi(dh_i) + pubkey_o
|
||||||
|
|
||||||
if sigmai:
|
if sigmai:
|
||||||
self.form_o = c7l_form
|
self.form_o = c7l_form
|
||||||
|
@ -393,7 +344,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
mac_o_calculated = self.hmac(self.ks_o, content)
|
mac_o_calculated = self.hmac(self.ks_o, content)
|
||||||
|
|
||||||
if self.negotiated['recv_pubkey']:
|
if self.negotiated['recv_pubkey']:
|
||||||
hash = self.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 exceptions.NegotiationError, 'public key signature verification failed!'
|
||||||
|
@ -404,9 +355,10 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
def make_identity(self, form, dh_i):
|
def make_identity(self, form, dh_i):
|
||||||
if self.negotiated['send_pubkey']:
|
if self.negotiated['send_pubkey']:
|
||||||
if self.negotiated['sign_algs'] == (XmlDsig + 'rsa-sha256'):
|
if self.negotiated['sign_algs'] == (XmlDsig + 'rsa-sha256'):
|
||||||
fields = (gajim.pubkey.n, gajim.pubkey.e)
|
pubkey = gajim.interface.get_pubkey(self.conn.name)
|
||||||
|
fields = (pubkey.n, pubkey.e)
|
||||||
|
|
||||||
cb_fields = map(lambda f: base64.b64encode(self.encode_mpi(f)), fields)
|
cb_fields = map(lambda f: base64.b64encode(crypto.encode_mpi(f)), fields)
|
||||||
|
|
||||||
pubkey_s = '<RSAKeyValue xmlns="http://www.w3.org/2000/09/xmldsig#"><Modulus>%s</Modulus><Exponent>%s</Exponent></RSAKeyValue>' % tuple(cb_fields)
|
pubkey_s = '<RSAKeyValue xmlns="http://www.w3.org/2000/09/xmldsig#"><Modulus>%s</Modulus><Exponent>%s</Exponent></RSAKeyValue>' % tuple(cb_fields)
|
||||||
else:
|
else:
|
||||||
|
@ -415,7 +367,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
form_s2 = ''.join(map(lambda el: xmpp.c14n.c14n(el), form.getChildren()))
|
form_s2 = ''.join(map(lambda el: xmpp.c14n.c14n(el), form.getChildren()))
|
||||||
|
|
||||||
old_c_s = self.c_s
|
old_c_s = self.c_s
|
||||||
content = self.n_o + self.n_s + self.encode_mpi(dh_i) + pubkey_s + self.form_s + form_s2
|
content = self.n_o + self.n_s + crypto.encode_mpi(dh_i) + pubkey_s + self.form_s + form_s2
|
||||||
|
|
||||||
mac_s = self.hmac(self.ks_s, content)
|
mac_s = self.hmac(self.ks_s, content)
|
||||||
|
|
||||||
|
@ -432,12 +384,12 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
else:
|
else:
|
||||||
id_s = self.encrypt(mac_s)
|
id_s = self.encrypt(mac_s)
|
||||||
|
|
||||||
m_s = self.hmac(self.km_s, self.encode_mpi(old_c_s) + id_s)
|
m_s = self.hmac(self.km_s, crypto.encode_mpi(old_c_s) + id_s)
|
||||||
|
|
||||||
if self.status == 'requested-e2e' and self.sas_algs == 'sas28x5':
|
if self.status == 'requested-e2e' and self.sas_algs == 'sas28x5':
|
||||||
# we're alice; check for a retained secret
|
# we're alice; check for a retained secret
|
||||||
# if none exists, prompt the user with the SAS
|
# if none exists, prompt the user with the SAS
|
||||||
self.sas = self.sas_28x5(m_s, self.form_o)
|
self.sas = crypto.sas_28x5(m_s, self.form_o)
|
||||||
|
|
||||||
if self.sigmai:
|
if self.sigmai:
|
||||||
self.check_identity()
|
self.check_identity()
|
||||||
|
@ -482,7 +434,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
x.addChild(node=xmpp.DataField(name='sas_algs', value='sas28x5', typ='hidden'))
|
x.addChild(node=xmpp.DataField(name='sas_algs', value='sas28x5', typ='hidden'))
|
||||||
x.addChild(node=xmpp.DataField(name='sign_algs', value='http://www.w3.org/2000/09/xmldsig#rsa-sha256', typ='hidden'))
|
x.addChild(node=xmpp.DataField(name='sign_algs', value='http://www.w3.org/2000/09/xmldsig#rsa-sha256', typ='hidden'))
|
||||||
|
|
||||||
self.n_s = self.generate_nonce()
|
self.n_s = crypto.generate_nonce()
|
||||||
|
|
||||||
x.addChild(node=xmpp.DataField(name='my_nonce', value=base64.b64encode(self.n_s), typ='hidden'))
|
x.addChild(node=xmpp.DataField(name='my_nonce', value=base64.b64encode(self.n_s), typ='hidden'))
|
||||||
|
|
||||||
|
@ -607,17 +559,17 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
bytes = int(self.n / 8)
|
bytes = int(self.n / 8)
|
||||||
|
|
||||||
self.n_s = self.generate_nonce()
|
self.n_s = crypto.generate_nonce()
|
||||||
|
|
||||||
self.c_o = self.decode_mpi(self.random_bytes(bytes)) # n-bit random number
|
self.c_o = crypto.decode_mpi(crypto.random_bytes(bytes)) # n-bit random number
|
||||||
self.c_s = self.c_o ^ (2 ** (self.n - 1))
|
self.c_s = self.c_o ^ (2 ** (self.n - 1))
|
||||||
|
|
||||||
self.y = self.srand(2 ** (2 * self.n - 1), p - 1)
|
self.y = crypto.srand(2 ** (2 * self.n - 1), p - 1)
|
||||||
self.d = self.powmod(g, self.y, p)
|
self.d = crypto.powmod(g, self.y, p)
|
||||||
|
|
||||||
to_add = { 'my_nonce': self.n_s,
|
to_add = { 'my_nonce': self.n_s,
|
||||||
'dhkeys': self.encode_mpi(self.d),
|
'dhkeys': crypto.encode_mpi(self.d),
|
||||||
'counter': self.encode_mpi(self.c_o),
|
'counter': crypto.encode_mpi(self.c_o),
|
||||||
'nonce': self.n_o }
|
'nonce': self.n_o }
|
||||||
|
|
||||||
for name in to_add:
|
for name in to_add:
|
||||||
|
@ -688,7 +640,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
result = xmpp.DataForm(typ='result')
|
result = xmpp.DataForm(typ='result')
|
||||||
|
|
||||||
self.c_s = self.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'])
|
||||||
|
@ -698,7 +650,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
x = self.xes[mod_p]
|
x = self.xes[mod_p]
|
||||||
e = self.es[mod_p]
|
e = self.es[mod_p]
|
||||||
|
|
||||||
self.d = self.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)
|
||||||
|
|
||||||
|
@ -720,7 +672,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
rshashes = [base64.b64encode(rshash) for rshash in rshashes]
|
rshashes = [base64.b64encode(rshash) for rshash in rshashes]
|
||||||
result.addChild(node=xmpp.DataField(name='rshashes', value=rshashes))
|
result.addChild(node=xmpp.DataField(name='rshashes', value=rshashes))
|
||||||
result.addChild(node=xmpp.DataField(name='dhkeys', value=base64.b64encode(self.encode_mpi(e))))
|
result.addChild(node=xmpp.DataField(name='dhkeys', value=base64.b64encode(crypto.encode_mpi(e))))
|
||||||
|
|
||||||
self.form_o = ''.join(map(lambda el: xmpp.c14n.c14n(el), form.getChildren()))
|
self.form_o = ''.join(map(lambda el: xmpp.c14n.c14n(el), form.getChildren()))
|
||||||
|
|
||||||
|
@ -752,10 +704,10 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
assert field in form.asDict(), "alice's form didn't have a %s field" % field
|
assert field in form.asDict(), "alice's form didn't have a %s field" % field
|
||||||
|
|
||||||
# 4.5.1 generating provisory session keys
|
# 4.5.1 generating provisory session keys
|
||||||
e = self.decode_mpi(base64.b64decode(form['dhkeys']))
|
e = crypto.decode_mpi(base64.b64decode(form['dhkeys']))
|
||||||
p = dh.primes[self.modp]
|
p = dh.primes[self.modp]
|
||||||
|
|
||||||
if self.sha256(self.encode_mpi(e)) != self.negotiated['He']:
|
if crypto.sha256(crypto.encode_mpi(e)) != self.negotiated['He']:
|
||||||
raise exceptions.NegotiationError, 'SHA256(e) != He'
|
raise exceptions.NegotiationError, 'SHA256(e) != He'
|
||||||
|
|
||||||
k = self.get_shared_secret(e, self.y, p)
|
k = self.get_shared_secret(e, self.y, p)
|
||||||
|
@ -778,19 +730,20 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
srs = secret
|
srs = secret
|
||||||
break
|
break
|
||||||
|
|
||||||
# other shared secret, we haven't got one.
|
# other shared secret
|
||||||
|
# (we're not using one)
|
||||||
oss = ''
|
oss = ''
|
||||||
|
|
||||||
k = self.sha256(k + srs + oss)
|
k = crypto.sha256(k + srs + oss)
|
||||||
|
|
||||||
self.kc_s, self.km_s, self.ks_s = self.generate_responder_keys(k)
|
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)
|
self.kc_o, self.km_o, self.ks_o = self.generate_initiator_keys(k)
|
||||||
|
|
||||||
# 4.5.5
|
# 4.5.5
|
||||||
if srs:
|
if srs:
|
||||||
srshash = self.hmac(srs, 'Shared Retained Secret')
|
srshash = crypto.hmac(srs, 'Shared Retained Secret')
|
||||||
else:
|
else:
|
||||||
srshash = self.random_bytes(32)
|
srshash = crypto.random_bytes(32)
|
||||||
|
|
||||||
x.addChild(node=xmpp.DataField(name='FORM_TYPE', value='urn:xmpp:ssn'))
|
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='nonce', value=base64.b64encode(self.n_o)))
|
||||||
|
@ -823,7 +776,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
break
|
break
|
||||||
|
|
||||||
oss = ''
|
oss = ''
|
||||||
k = self.sha256(self.k + srs + oss)
|
k = crypto.sha256(self.k + srs + oss)
|
||||||
del self.k
|
del self.k
|
||||||
|
|
||||||
self.do_retained_secret(k, srs)
|
self.do_retained_secret(k, srs)
|
||||||
|
@ -857,14 +810,6 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
self.check_identity()
|
self.check_identity()
|
||||||
gajim.interface.save_new_secret(account, bjid, new_srs)
|
gajim.interface.save_new_secret(account, bjid, new_srs)
|
||||||
|
|
||||||
# generate a random number between 'bottom' and 'top'
|
|
||||||
def srand(self, bottom, top):
|
|
||||||
# minimum number of bytes needed to represent that range
|
|
||||||
bytes = int(math.ceil(math.log(top - bottom, 256)))
|
|
||||||
|
|
||||||
# in retrospect, this is horribly inadequate.
|
|
||||||
return (self.decode_mpi(self.random_bytes(bytes)) % (top - bottom)) + bottom
|
|
||||||
|
|
||||||
def make_dhfield(self, modp_options, sigmai):
|
def make_dhfield(self, modp_options, sigmai):
|
||||||
dhs = []
|
dhs = []
|
||||||
|
|
||||||
|
@ -872,39 +817,24 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
p = dh.primes[modp]
|
p = dh.primes[modp]
|
||||||
g = dh.generators[modp]
|
g = dh.generators[modp]
|
||||||
|
|
||||||
x = self.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
|
# XXX this may be a source of performance issues
|
||||||
e = self.powmod(g, x, p)
|
e = crypto.powmod(g, x, p)
|
||||||
|
|
||||||
self.xes[modp] = x
|
self.xes[modp] = x
|
||||||
self.es[modp] = e
|
self.es[modp] = e
|
||||||
|
|
||||||
if sigmai:
|
if sigmai:
|
||||||
dhs.append(base64.b64encode(self.encode_mpi(e)))
|
dhs.append(base64.b64encode(crypto.encode_mpi(e)))
|
||||||
name = 'dhkeys'
|
name = 'dhkeys'
|
||||||
else:
|
else:
|
||||||
He = self.sha256(self.encode_mpi(e))
|
He = crypto.sha256(crypto.encode_mpi(e))
|
||||||
dhs.append(base64.b64encode(He))
|
dhs.append(base64.b64encode(He))
|
||||||
name = 'dhhashes'
|
name = 'dhhashes'
|
||||||
|
|
||||||
return xmpp.DataField(name=name, typ='hidden', value=dhs)
|
return xmpp.DataField(name=name, typ='hidden', value=dhs)
|
||||||
|
|
||||||
# a faster version of (base ** exp) % mod
|
|
||||||
# taken from <http://lists.danga.com/pipermail/yadis/2005-September/001445.html>
|
|
||||||
def powmod(self, base, exp, mod):
|
|
||||||
square = base % mod
|
|
||||||
result = 1
|
|
||||||
|
|
||||||
while exp > 0:
|
|
||||||
if exp & 1: # exponent is odd
|
|
||||||
result = (result * square) % mod
|
|
||||||
|
|
||||||
square = (square * square) % mod
|
|
||||||
exp /= 2
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def terminate_e2e(self):
|
def terminate_e2e(self):
|
||||||
self.terminate()
|
self.terminate()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue