make e2e work with py3

This commit is contained in:
Yann Leboulanger 2014-11-11 15:07:53 +01:00
parent 8a6ed2c04e
commit 8ca7281480
4 changed files with 64 additions and 52 deletions

View File

@ -28,9 +28,9 @@ from hashlib import sha256 as SHA256
# convert a large integer to a big-endian bitstring # convert a large integer to a big-endian bitstring
def encode_mpi(n): def encode_mpi(n):
if n >= 256: if n >= 256:
return encode_mpi(n / 256) + chr(n % 256) return encode_mpi(n // 256) + bytes([n % 256])
else: else:
return chr(n) return bytes([n])
# convert a large integer to a big-endian bitstring, padded with \x00s to # convert a large integer to a big-endian bitstring, padded with \x00s to
# a multiple of 16 bytes # a multiple of 16 bytes
@ -56,7 +56,7 @@ def decode_mpi(s):
if len(s) == 0: if len(s) == 0:
return 0 return 0
else: else:
return 256 * decode_mpi(s[:-1]) + ord(s[-1]) return 256 * decode_mpi(s[:-1]) + s[-1]
def sha256(string): def sha256(string):
sh = SHA256() sh = SHA256()
@ -66,13 +66,13 @@ def sha256(string):
base28_chr = "acdefghikmopqruvwxy123456789" base28_chr = "acdefghikmopqruvwxy123456789"
def sas_28x5(m_a, form_b): def sas_28x5(m_a, form_b):
sha = sha256(m_a + form_b + 'Short Authentication String') sha = sha256(m_a + form_b + b'Short Authentication String')
lsb24 = decode_mpi(sha[-3:]) lsb24 = decode_mpi(sha[-3:])
return base28(lsb24) return base28(lsb24)
def base28(n): def base28(n):
if n >= 28: if n >= 28:
return base28(n / 28) + base28_chr[n % 28] return base28(n // 28) + base28_chr[n % 28]
else: else:
return base28_chr[n] return base28_chr[n]
@ -147,6 +147,5 @@ def powmod(base, exp, mod):
result = (result * square) % mod result = (result * square) % mod
square = (square * square) % mod square = (square * square) % mod
exp /= 2 exp //= 2
return result return result

View File

@ -224,6 +224,7 @@ def hex_to_decimal(stripee):
if not stripee: if not stripee:
return None return None
return int(stripee.translate(all_ascii, string.whitespace), 16) return int(stripee.translate(all_ascii).translate(str.maketrans("", "",
string.whitespace)), 16)
primes = map(hex_to_decimal, hex_primes) primes = list(map(hex_to_decimal, hex_primes))

View File

@ -410,13 +410,13 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
c = stanza.NT.c c = stanza.NT.c
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).decode('utf-8')
# FIXME 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()))).encode('utf-8')
c.NT.mac = base64.b64encode(self.hmac(self.km_s, m_content + \ c.NT.mac = base64.b64encode(self.hmac(self.km_s, m_content + \
crypto.encode_mpi(old_en_counter))) crypto.encode_mpi(old_en_counter))).decode('utf-8')
msgtxt = '[This is part of an encrypted session. ' \ msgtxt = '[This is part of an encrypted session. ' \
'If you see this message, something went wrong.]' 'If you see this message, something went wrong.]'
@ -436,14 +436,14 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
return HMAC(key, content, self.hash_alg).digest() return HMAC(key, content, self.hash_alg).digest()
def generate_initiator_keys(self, k): def generate_initiator_keys(self, k):
return (self.hmac(k, 'Initiator Cipher Key'), return (self.hmac(k, b'Initiator Cipher Key'),
self.hmac(k, 'Initiator MAC Key'), self.hmac(k, b'Initiator MAC Key'),
self.hmac(k, 'Initiator SIGMA Key')) self.hmac(k, b'Initiator SIGMA Key'))
def generate_responder_keys(self, k): def generate_responder_keys(self, k):
return (self.hmac(k, 'Responder Cipher Key'), return (self.hmac(k, b'Responder Cipher Key'),
self.hmac(k, 'Responder MAC Key'), self.hmac(k, b'Responder MAC Key'),
self.hmac(k, 'Responder SIGMA Key')) self.hmac(k, b'Responder SIGMA Key'))
def compress(self, plaintext): def compress(self, plaintext):
if self.compression is None: if self.compression is None:
@ -473,6 +473,7 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
# contents of <c>, minus <mac>, minus whitespace # contents of <c>, minus <mac>, minus whitespace
macable = ''.join(str(x) for x in c.getChildren() if x.getName() != 'mac') macable = ''.join(str(x) for x in c.getChildren() if x.getName() != 'mac')
macable = macable.encode('utf-8')
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 + \
@ -483,7 +484,7 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
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)
plaintext = self.decompress(m_compressed) plaintext = self.decompress(m_compressed).decode('utf-8')
try: try:
parsed = nbxmpp.Node(node='<node>' + plaintext + '</node>') parsed = nbxmpp.Node(node='<node>' + plaintext + '</node>')
@ -536,7 +537,7 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
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
# (but we do anyways) # (but we do anyways)
self.sas = crypto.sas_28x5(m_o, self.form_s) self.sas = crypto.sas_28x5(m_o, self.form_s.encode('utf-8'))
if self.negotiated['recv_pubkey']: if self.negotiated['recv_pubkey']:
plaintext = self.decrypt(id_o) plaintext = self.decrypt(id_o)
@ -564,18 +565,18 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
signature = (crypto.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 = b''
c7l_form = self.c7lize_mac_id(form) c7l_form = self.c7lize_mac_id(form)
content = self.n_s + self.n_o + crypto.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.encode('utf-8')
content += self.form_o content += self.form_o
else: else:
form_o2 = c7l_form form_o2 = c7l_form.encode('utf-8')
content += self.form_o + form_o2 content += self.form_o.encode('utf-8') + form_o2
mac_o_calculated = self.hmac(self.ks_o, content) mac_o_calculated = self.hmac(self.ks_o, content)
@ -598,18 +599,18 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
cb_fields = [base64.b64encode(crypto.encode_mpi(f)) for f in cb_fields = [base64.b64encode(crypto.encode_mpi(f)) for f in
fields] fields]
pubkey_s = '<RSAKeyValue xmlns="http://www.w3.org/2000/09/xmldsig#"' pubkey_s = b'<RSAKeyValue xmlns="http://www.w3.org/2000/09/xmldsig#"'
'><Modulus>%s</Modulus><Exponent>%s</Exponent></RSAKeyValue>' % \ '><Modulus>%s</Modulus><Exponent>%s</Exponent></RSAKeyValue>' % \
tuple(cb_fields) tuple(cb_fields)
else: else:
pubkey_s = '' pubkey_s = b''
form_s2 = ''.join(nbxmpp.c14n.c14n(el, self._is_buggy_gajim()) for el \ form_s2 = ''.join(nbxmpp.c14n.c14n(el, self._is_buggy_gajim()) for el \
in form.getChildren()) in form.getChildren())
old_c_s = self.c_s old_c_s = self.c_s
content = self.n_o + self.n_s + crypto.encode_mpi(dh_i) + pubkey_s + \ content = self.n_o + self.n_s + crypto.encode_mpi(dh_i) + pubkey_s + \
self.form_s + form_s2 self.form_s.encode('utf-8') + form_s2.encode('utf-8')
mac_s = self.hmac(self.ks_s, content) mac_s = self.hmac(self.ks_s, content)
@ -632,14 +633,16 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
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 = crypto.sas_28x5(m_s, self.form_o) self.sas = crypto.sas_28x5(m_s, self.form_o.encode('utf-8'))
if self.sigmai: if self.sigmai:
# FIXME save retained secret? # FIXME save retained secret?
self.check_identity(tuple) self.check_identity(tuple)
return (nbxmpp.DataField(name='identity', value=base64.b64encode(id_s)), return (nbxmpp.DataField(name='identity',
nbxmpp.DataField(name='mac', value=base64.b64encode(m_s))) value=base64.b64encode(id_s).decode('utf-8')),
nbxmpp.DataField(name='mac',
value=base64.b64encode(m_s).decode('utf-8')))
def negotiate_e2e(self, sigmai): def negotiate_e2e(self, sigmai):
self.negotiated = {} self.negotiated = {}
@ -695,7 +698,7 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
self.n_s = crypto.generate_nonce() self.n_s = crypto.generate_nonce()
x.addChild(node=nbxmpp.DataField(name='my_nonce', x.addChild(node=nbxmpp.DataField(name='my_nonce',
value=base64.b64encode(self.n_s), typ='hidden')) value=base64.b64encode(self.n_s).decode('utf-8'), typ='hidden'))
modp_options = [ int(g) for g in gajim.config.get('esession_modp').split( modp_options = [ int(g) for g in gajim.config.get('esession_modp').split(
',') ] ',') ]
@ -845,7 +848,7 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
'nonce': self.n_o} 'nonce': self.n_o}
for name in to_add: for name in to_add:
b64ed = base64.b64encode(to_add[name]) b64ed = base64.b64encode(to_add[name]).decode('utf-8')
x.addChild(node=nbxmpp.DataField(name=name, value=b64ed)) x.addChild(node=nbxmpp.DataField(name=name, value=b64ed))
self.form_o = ''.join(nbxmpp.c14n.c14n(el, self._is_buggy_gajim()) for \ self.form_o = ''.join(nbxmpp.c14n.c14n(el, self._is_buggy_gajim()) for \
@ -935,7 +938,7 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
value='urn:xmpp:ssn')) value='urn:xmpp:ssn'))
result.addChild(node=nbxmpp.DataField(name='accept', value='1')) result.addChild(node=nbxmpp.DataField(name='accept', value='1'))
result.addChild(node=nbxmpp.DataField(name='nonce', result.addChild(node=nbxmpp.DataField(name='nonce',
value=base64.b64encode(self.n_o))) value=base64.b64encode(self.n_o).decode('utf-8')))
self.kc_s, self.km_s, self.ks_s = self.generate_initiator_keys(self.k) self.kc_s, self.km_s, self.ks_s = self.generate_initiator_keys(self.k)
@ -952,11 +955,12 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
rshash_size = self.hash_alg().digest_size rshash_size = self.hash_alg().digest_size
rshashes.append(crypto.random_bytes(rshash_size)) rshashes.append(crypto.random_bytes(rshash_size))
rshashes = [base64.b64encode(rshash) for rshash in rshashes] rshashes = [base64.b64encode(rshash).decode('utf-8') for rshash in \
rshashes]
result.addChild(node=nbxmpp.DataField(name='rshashes', result.addChild(node=nbxmpp.DataField(name='rshashes',
value=rshashes)) value=rshashes))
result.addChild(node=nbxmpp.DataField(name='dhkeys', result.addChild(node=nbxmpp.DataField(name='dhkeys',
value=base64.b64encode(crypto.encode_mpi(e)))) value=base64.b64encode(crypto.encode_mpi(e)).decode('utf-8')))
self.form_o = ''.join(nbxmpp.c14n.c14n(el, self._is_buggy_gajim()) \ self.form_o = ''.join(nbxmpp.c14n.c14n(el, self._is_buggy_gajim()) \
for el in form.getChildren()) for el in form.getChildren())
@ -1006,7 +1010,7 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
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 = b''
srses = secrets.secrets().retained_secrets(self.conn.name, srses = secrets.secrets().retained_secrets(self.conn.name,
self.jid.getStripped()) self.jid.getStripped())
@ -1021,7 +1025,7 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
# other shared secret # other shared secret
# (we're not using one) # (we're not using one)
oss = '' oss = b''
k = crypto.sha256(k + srs + oss) k = crypto.sha256(k + srs + oss)
@ -1030,16 +1034,16 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
# 4.5.5 # 4.5.5
if srs: if srs:
srshash = self.hmac(srs, 'Shared Retained Secret') srshash = self.hmac(srs, b'Shared Retained Secret')
else: else:
srshash = crypto.random_bytes(32) srshash = crypto.random_bytes(32)
x.addChild(node=nbxmpp.DataField(name='FORM_TYPE', x.addChild(node=nbxmpp.DataField(name='FORM_TYPE',
value='urn:xmpp:ssn')) value='urn:xmpp:ssn'))
x.addChild(node=nbxmpp.DataField(name='nonce', value=base64.b64encode( x.addChild(node=nbxmpp.DataField(name='nonce', value=base64.b64encode(
self.n_o))) self.n_o).decode('utf-8')))
x.addChild(node=nbxmpp.DataField(name='srshash', value=base64.b64encode( x.addChild(node=nbxmpp.DataField(name='srshash', value=base64.b64encode(
srshash))) srshash).decode('utf-8')))
for datafield in self.make_identity(x, self.d): for datafield in self.make_identity(x, self.d):
x.addChild(node=datafield) x.addChild(node=datafield)
@ -1062,7 +1066,7 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
self.stop_archiving_for_session() self.stop_archiving_for_session()
def final_steps_alice(self, form): def final_steps_alice(self, form):
srs = '' srs = b''
srses = secrets.secrets().retained_secrets(self.conn.name, srses = secrets.secrets().retained_secrets(self.conn.name,
self.jid.getStripped()) self.jid.getStripped())
@ -1073,11 +1077,11 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
for s in srses: for s in srses:
secret = s[0] secret = s[0]
if self.hmac(secret, 'Shared Retained Secret') == srshash: if self.hmac(secret, b'Shared Retained Secret') == srshash:
srs = secret srs = secret
break break
oss = '' oss = b''
k = crypto.sha256(self.k + srs + oss) k = crypto.sha256(self.k + srs + oss)
del self.k del self.k
@ -1109,7 +1113,7 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
the remote party's identity. Set up callbacks for when the identity has the remote party's identity. Set up callbacks for when the identity has
been verified been verified
""" """
new_srs = self.hmac(k, 'New Retained Secret') new_srs = self.hmac(k, b'New Retained Secret')
self.srs = new_srs self.srs = new_srs
account = self.conn.name account = self.conn.name
@ -1155,11 +1159,11 @@ class EncryptedStanzaSession(ArchivingStanzaSession):
self.es[modp] = e self.es[modp] = e
if sigmai: if sigmai:
dhs.append(base64.b64encode(crypto.encode_mpi(e))) dhs.append(base64.b64encode(crypto.encode_mpi(e)).decode('utf-8'))
name = 'dhkeys' name = 'dhkeys'
else: else:
He = crypto.sha256(crypto.encode_mpi(e)) He = crypto.sha256(crypto.encode_mpi(e))
dhs.append(base64.b64encode(He)) dhs.append(base64.b64encode(He).decode('utf-8'))
name = 'dhhashes' name = 'dhhashes'
return nbxmpp.DataField(name=name, typ='hidden', value=dhs) return nbxmpp.DataField(name=name, typ='hidden', value=dhs)

View File

@ -42,7 +42,7 @@ class Secrets:
raise exceptions.Cancelled raise exceptions.Cancelled
def save(self): def save(self):
f = open(secrets_filename, 'w') f = open(secrets_filename, 'wb')
pickle.dump(self, f) pickle.dump(self, f)
f.close() f.close()
@ -95,14 +95,22 @@ class Secrets:
return pk return pk
def load_secrets(filename): def load_secrets(filename):
f = open(filename, 'r') f = open(filename, 'rb')
try: try:
secrets = pickle.load(f) secrets = pickle.load(f, encoding='latin1')
# We do that to be able to read files written in py2
for acct in secrets.srs:
for jid in secrets.srs[acct]:
for (secret, verified) in list(secrets.srs[acct][jid]):
if type(secret) is str:
secrets.srs[acct][jid].remove((secret, verified))
secrets.srs[acct][jid].append((secret.encode('latin1'), verified))
except (KeyError, EOFError): except (KeyError, EOFError):
f.close() f.close()
secrets = Secrets(filename) secrets = Secrets(filename)
f.close()
return secrets return secrets
def secrets(): def secrets():