make python-crypto an optional dependency

This commit is contained in:
Yann Leboulanger 2007-08-27 13:36:24 +00:00
parent 03dbf641de
commit 62edcc71de
6 changed files with 59 additions and 35 deletions

View File

@ -29,6 +29,7 @@ Gajim is a GTK+ app that loves GNOME. You can do 'make' so you don't require gno
<h2>Optional Runtime Requirements</h2>
<ul>
<li><a href="http://pyopenssl.sourceforge.net/">PyOpenSSL</a> (python-pyopenssl package in Debian) for <em>secure</em> SSL/TLS. Python's default SSL is insecure, so this package is highly recommended!</li>
<li>python-crypto to enable End to end encryption</li>
<li>For zeroconf (bonjour), the "enable link-local messaging" checkbox, you need dbus-glib, python-avahi</li>
<li>dnsutils (or whatever package provides the nslookup binary) for SRV support; if you don't know what that is, you don't need it</li>
<li>gtkspell and aspell-LANG where lang is your locale eg. en, fr etc</li>

View File

@ -1592,8 +1592,11 @@ class ChatControl(ChatControlBase):
toggle_gpg_menuitem.set_property('sensitive', is_sensitive)
# TODO: check that the remote client supports e2e
isactive = int(self.session != None and self.session.enable_encryption)
toggle_e2e_menuitem.set_active(isactive)
if not gajim.HAVE_PYCRYPTO:
toggle_e2e_menuitem.set_sensitive(False)
else:
isactive = int(self.session != None and self.session.enable_encryption)
toggle_e2e_menuitem.set_active(isactive)
# If we don't have resource, we can't do file transfer
# in transports, contact holds our info we need to disable it too

View File

@ -1490,7 +1490,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
return
if msg.getTag('feature') and msg.getTag('feature').namespace == \
common.xmpp.NS_FEATURE:
self._FeatureNegCB(con, msg, session)
if gajim.HAVE_PYCRYPTO:
self._FeatureNegCB(con, msg, session)
return
if msg.getTag('init') and msg.getTag('init').namespace == \
common.xmpp.NS_ESESSION_INIT:

View File

@ -136,6 +136,16 @@ priority_dict = {}
for status in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
priority_dict[status] = config.get('autopriority' + status)
HAVE_PYCRYPTO = True
try:
from Crypto.PublicKey.RSA import generate
except ImportError:
HAVE_PYCRYPTO = False
else:
# public key for XEP-0116
#FIXME os.urandom is not a cryptographic PRNG
pubkey = Crypto.PublicKey.RSA.generate(384, os.urandom)
def get_nick_from_jid(jid):
pos = jid.find('@')
return jid[:pos]

View File

@ -14,10 +14,6 @@ import time
from common import dh
import xmpp.c14n
from Crypto.Cipher import AES
from Crypto.Hash import HMAC, SHA256
from Crypto.PublicKey import RSA
import base64
XmlDsig = 'http://www.w3.org/2000/09/xmldsig#'
@ -99,25 +95,31 @@ class StanzaSession(object):
# we could send an acknowledgement message here, but we won't.
self.status = None
# an encrypted stanza negotiation has several states. i've represented them as the following values in the 'status'
if gajim.HAVE_PYCRYPTO:
from Crypto.Cipher import AES
from Crypto.Hash import HMAC, SHA256
from Crypto.PublicKey import RSA
# an encrypted stanza negotiation has several states. i've represented them
# as the following values in the 'status'
# attribute of the session object:
# 1. None:
# default state
# 2. 'requested-e2e':
# this client has initiated an esession negotiation and is waiting for
# a response
# this client has initiated an esession negotiation and is waiting
# for a response
# 3. 'responded-e2e':
# this client has responded to an esession negotiation request and is
# waiting for the initiator to identify itself and complete the
# this client has responded to an esession negotiation request and
# is waiting for the initiator to identify itself and complete the
# negotiation
# 4. 'identified-alice':
# this client identified itself and is waiting for the responder to
# this client identified itself and is waiting for the responder to
# identify itself and complete the negotiation
# 5. 'active':
# an encrypted session has been successfully negotiated. messages of
# any of the types listed in 'encryptable_stanzas' should be encrypted
# before they're sent.
# an encrypted session has been successfully negotiated. messages
# of any of the types listed in 'encryptable_stanzas' should be
# encrypted before they're sent.
# the transition between these states is handled in gajim.py's
# handle_session_negotiation method.
@ -144,7 +146,8 @@ class EncryptedStanzaSession(StanzaSession):
# keep the encrypter updated with my latest cipher key
def set_kc_s(self, value):
self._kc_s = value
self.encrypter = self.cipher.new(self._kc_s, self.cipher.MODE_CTR, counter=self.encryptcounter)
self.encrypter = self.cipher.new(self._kc_s, self.cipher.MODE_CTR,
counter=self.encryptcounter)
def get_kc_s(self):
return self._kc_s
@ -152,7 +155,8 @@ class EncryptedStanzaSession(StanzaSession):
# keep the decrypter updated with the other party's latest cipher key
def set_kc_o(self, value):
self._kc_o = value
self.decrypter = self.cipher.new(self._kc_o, self.cipher.MODE_CTR, counter=self.decryptcounter)
self.decrypter = self.cipher.new(self._kc_o, self.cipher.MODE_CTR,
counter=self.decryptcounter)
def get_kc_o(self):
return self._kc_o
@ -167,7 +171,8 @@ class EncryptedStanzaSession(StanzaSession):
else:
return chr(n)
# convert a large integer to a big-endian bitstring, padded with \x00s to 16 bytes
# 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)
@ -195,13 +200,16 @@ class EncryptedStanzaSession(StanzaSession):
def sign(self, string):
if self.negotiated['sign_algs'] == (XmlDsig + 'rsa-sha256'):
hash = self.sha256(string)
return self.encode_mpi(gajim.interface.pubkey.sign(hash, '')[0])
return self.encode_mpi(gajim.pubkey.sign(hash, '')[0])
def encrypt_stanza(self, stanza):
encryptable = filter(lambda x: x.getName() not in ('error', 'amp', 'thread'), stanza.getChildren())
encryptable = filter(lambda x: x.getName() not in ('error', 'amp',
'thread'), stanza.getChildren())
# 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)
# 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)
old_en_counter = self.c_s
@ -220,7 +228,8 @@ class EncryptedStanzaSession(StanzaSession):
# XXX check for rekey request, handle <key/> elements
m_content = ''.join(map(str, c.getChildren()))
c.NT.mac = base64.b64encode(self.hmac(self.km_s, m_content + self.encode_mpi(old_en_counter)))
c.NT.mac = base64.b64encode(self.hmac(self.km_s, m_content + \
self.encode_mpi(old_en_counter)))
return stanza
@ -278,15 +287,18 @@ class EncryptedStanzaSession(StanzaSession):
return self.random_bytes(8)
def decrypt_stanza(self, stanza):
c = stanza.getTag(name='c', namespace='http://www.xmpp.org/extensions/xep-0200.html#ns')
c = stanza.getTag(name='c',
namespace='http://www.xmpp.org/extensions/xep-0200.html#ns')
stanza.delChild(c)
# contents of <c>, minus <mac>, minus whitespace
macable = ''.join(map(str, filter(lambda x: x.getName() != 'mac', c.getChildren())))
macable = ''.join(map(str, filter(lambda x: x.getName() != 'mac',
c.getChildren())))
received_mac = base64.b64decode(c.getTagData('mac'))
calculated_mac = self.hmac(self.km_o, macable + self.encode_mpi_with_padding(self.c_o))
calculated_mac = self.hmac(self.km_o, macable + \
self.encode_mpi_with_padding(self.c_o))
if not calculated_mac == received_mac:
raise exceptions.DecryptionError, 'bad signature'
@ -351,7 +363,8 @@ class EncryptedStanzaSession(StanzaSession):
if self.negotiated['sign_algs'] == (XmlDsig + 'rsa-sha256'):
keyvalue = parsed.getTag(name='RSAKeyValue', namespace=XmlDsig)
n, e = map(lambda x: self.decode_mpi(base64.b64decode(keyvalue.getTagData(x))), ('Modulus', 'Exponent'))
n, e = map(lambda x: self.decode_mpi(base64.b64decode(
keyvalue.getTagData(x))), ('Modulus', 'Exponent'))
eir_pubkey = RSA.construct((n,long(e)))
pubkey_o = xmpp.c14n.c14n(keyvalue)
@ -359,7 +372,8 @@ class EncryptedStanzaSession(StanzaSession):
# XXX DSA, etc.
raise 'unimplemented'
enc_sig = parsed.getTag(name='SignatureValue', namespace=XmlDsig).getData()
enc_sig = parsed.getTag(name='SignatureValue',
namespace=XmlDsig).getData()
signature = (self.decode_mpi(base64.b64decode(enc_sig)),)
else:
mac_o = self.decrypt(id_o)
@ -390,7 +404,7 @@ class EncryptedStanzaSession(StanzaSession):
def make_identity(self, form, dh_i):
if self.negotiated['send_pubkey']:
if self.negotiated['sign_algs'] == (XmlDsig + 'rsa-sha256'):
fields = (gajim.interface.pubkey.n, gajim.interface.pubkey.e)
fields = (gajim.pubkey.n, gajim.pubkey.e)
cb_fields = map(lambda f: base64.b64encode(self.encode_mpi(f)), fields)

View File

@ -118,7 +118,6 @@ from chat_control import ChatControlBase
from atom_window import AtomWindow
import negotiation
import Crypto.PublicKey.RSA
from common import exceptions
from common.zeroconf import connection_zeroconf
@ -2765,10 +2764,6 @@ class Interface:
gobject.timeout_add(2000, self.process_connections)
gobject.timeout_add(10000, self.read_sleepy)
# public key for XEP-0116
# XXX os.urandom is not a cryptographic PRNG
self.pubkey = Crypto.PublicKey.RSA.generate(384, os.urandom)
if __name__ == '__main__':
def sigint_cb(num, stack):
sys.exit(5)