Posts tagged ‘m2crypto’

M2Crypto 0.20 Beta Cycle Begins

Better late than never… I am announcing the first beta of M2Crypto 0.20 release. M2Crypto is the most complete Python wrapper for OpenSSL.

The 0.20 release has been in development for about nine months. About 30 bugs and new features have been implemented by more than ten people. Unit tests now cover 80% of the code base. Tinderbox is used to automatically test changes on various flavors of Ubuntu, Fedora Core, Redhat and Cygwin. We could use more Tinderbox clients, so please drop me a line if you have some spare machine cycles available.

The release include some fairly significant changes, including tricky ones in threading and so forth. See the CHANGES file for list of changes. Please test your applications, and go file bugs on any issues you notice. I’ll wait for feedback for a week, spin the next beta and so forth until there are no more release blockers found within a beta period.

There are a few issues I feel bad about that did not make the first beta. If you can help create fixes for these, I’d be willing to consider including the fixes in 0.20 if the changes don’t look too scary. Here is my wish list:

Download from pypi.

Or use easy_install (may not work on all systems): easy_install M2Crypto

M2Crypto Build Wrapper for Fedora Core -based Distributions

Ever since M2Crypto got support for Elliptic Curves (EC) cryptography, it has been somewhat difficult to build M2Crypto on systems where OpenSSL has been built without EC support. Notably distributions based on Fedora Core, which besides Fedora Core include of course Redhat and CentOS.

Disabled EC support alone wouldn’t be an issue, since normally opensslconf.h defines OPENSSL_NO_EC, but those same systems have also changed OpenSSL build so that instead of opensslconf.h you need to include processor architecture dependent file. And if that wasn’t enough this isn’t actually what you are supposed to be doing, and you will hit a compiler error to notify you of that. The final step in the recipe is to tell SWIG to treat errors as warnings. The distributions make build changes in their own versions of M2Crypto, but unless you know the recipe, you are going to have a hard time building M2Crypto yourself. Miloslav Trmač showed me what kind of changes were needed, and I made a new setup wrapper fedora_setup.sh which should help you get off the ground with M2Crypto if you are having a hard time building it. Let me know if you run into any problems with the setup wrapper.

I am sure the distros did not make these changes just to make it harder to build systems based on OpenSSL, although it sure does feel like that at times. Maybe someone can shed light on why these changes were made in the first place.

My Little Secret

I have been working on an installer system that needs to ask the user a bunch of questions, including passwords, and store the answers in an .ini so that if the user runs the installer again, they will get prefilled answers. After the first installation it should be possible to run the installer in a batch mode since all the answers were recorded. But being security conscious, I did not like all those passwords being stored in plain text.

Luckily for me, I had already implemented almost exactly what I needed in Chandler, namely the password manager feature of Chandler which encrypts account passwords with a master password. I just needed to rip out the Chandler-specific features to get a general purpose encryption and decryption library, which defaults to 256 bit AES (Rijndael) in CBC mode and derives a key from the master password using PBKDF2 algorithm. The encrypted data serializes to just text, which can appear in .ini file. The serialized form does not follow any standards. With a few extra lines of code it can also act as a utility to encrypt and decrypt files. Thus, m2secret was born. It is built using the M2Crypto library.

Here’s how you could use it:

import m2secret
 
# Encrypt
secret = m2secret.Secret()
secret.encrypt('my data', 'my master password')
serialized = secret.serialize()
 
# Decrypt
secret = m2secret.Secret()
secret.deserialize(serialized)
data = secret.decrypt('my master password')
 
assert data == 'my data'

The command line utility is called m2secret.

Installation should be as easy as easy_install m2secret.

Looking for Volunteers to Run M2Crypto Tinderbox Buildslaves

I have set up a Tinderbox2 instance for M2Crypto. If you are unfamiliar with Tinderbox, it is a tool to do continuous builds and tests, similar to Buildbot.

Now I am looking for people who would be able to run buildslaves. Ideally computers that are online 24/7, able to do a build + test of M2Crypto once an hour, but even once a day would be useful. The computer should have the ability to pull sources from svn and email the build & test log. The build and test cycle takes less than a minute on my laptop, so we are talking minimal resource usage.

SSL in Python 2.6

Secure Sockets Layer (SSL) support in Python stdlib took a big leap forward with the introduction of the ssl module in Python 2.6.

Servers

The ssl module provides the means to build safe SSL servers just by using a stock installation of Python 2.6. There are still some pitfalls as usual, but it can be done by limiting compatibility.

The main problem with the ssl module in 2.6 in server use is that it is not possible to disable SSL version 2. This is important since SSL version 2 is insecure. And unfortuantely the most compatible mode selection with OpenSSL (on which Python’s SSL support is based) is to select the SSLv23 mode which on the server is compatible with all SSL and TLS versions supported by OpenSSL. OpenSSL does provide a way to select the SSLv23 mode and selectively turn off any SSL version, but this was not implemented in Python 2.6. It is available in at least M2Crypto, which is a 3rd party Python wrapper for OpenSSL that I maintain.

The 2.6 implementation will also not let you choose ciphers, so you are at the mercy of the protocol version you choose and the options that OpenSSL was compiled with. M2Crypto defaults to strong ciphers, and lets the application developers set any ciphers they wish.

With those important caveats, the server side SSL implementation looks reasonable, assuming you remember to provide the keyfile and certfile arguments to ssl.wrap_socket(). Especially if you can forgo some compatibility and limit your server to just TLSv1. (Transport Layer Security or TLS for short is the newer SSL.)

I wrote earlier how you can use M2Crypto to extract CA certificates from NSS project’s certdata.txt. You can also get free SSL server certificates that work in many (but not all) browsers from StartCom, for example. You still need to pay to cover all the mainstream browsers. In some use cases you may also be able to run your own CA and issue your own certificates but it is not for the faint of heart.

Here are sample servers written using stock Python 2.6 with SSL support compiled in, and M2Crypto 0.19:

Python 2.6 server:

import socket, ssl
bindsocket = socket.socket()
bindsocket.bind(('', 8000))
bindsocket.listen(5)
newsocket, fromaddr = bindsocket.accept()
c = ssl.wrap_socket(newsocket, server_side=True, certfile="servercert.pem",
                    keyfile="serverkey.pem", ssl_version=ssl.PROTOCOL_SSLv3)
print c.read()
c.write('200 OK\r\n\r\n')
c.close()
bindsocket.close()

M2Crypto 0.19.1 server:

from M2Crypto import SSL
ctx = SSL.Context('sslv3')
ctx.load_cert(certfile="servercert.pem", keyfile="serverkey.pem")
bindsocket = SSL.Connection(ctx)
bindsocket.bind(('', 8000))
bindsocket.listen(5)
c, fromaddr = bindsocket.accept()
print c.read()
c.write('200 OK\r\n\r\n')
c.close()
bindsocket.close()

The situation is not so good for the client side, although things have definitely improved.

Clients

The same SSLv2 issue and cipher issue is true for the client side as well. As a client developer, besides needing to specify ca_certs pointing to valid CA certificates for ssl.wrap_socket(), you will also need to remember to pass in cert_reqs=ssl.CERT_REQUIRED.

In addition, the client side needs to check that the certificate hostname (in the subjectAltName or commonName) matches the hostname that the client tried to connect to. If you don’t, you won’t know who you are talking to and your connection is subject to a man-in-the-middle attack. I was not able to convince Bill Janssen that this post connection check callback should be provided by the ssl module. I think there is value in doing a hostname check by default, and letting application developers override it if necessary. As it stands, you must code this check in the application code; you can get the peer certificate as a dictionary with getpeercert() method from the wrapped socket. Again, M2Crypto does this hostname check by default, but it can be overridden by the application if necessary. You might want to take a look at the M2Crypto checker code, although I am not convinced it is 100% correct per specifications (but it should err on the side of safety).

Below is a comparison of Python 2.6 and M2Crypto 0.19.1 clients.

Python 2.6 client:

from socket import socket
import ssl
s = socket()
c = ssl.wrap_socket(s, cert_reqs=ssl.CERT_REQUIRED,
                    ssl_version=ssl.PROTOCOL_SSLv3, ca_certs='ca.pem')
c.connect(('www.google.com', 443))
# naive and incomplete check to see if cert matches host
cert = c.getpeercert()
if not cert or ('commonName', u'www.google.com') not in cert['subject'][4]:
    raise Exception('Danger!')
c.write('GET / \n')
c.close()

M2Crypto 0.19.1 client:

from M2Crypto import SSL
ctx = SSL.Context('sslv3')
# If you comment out the next 2 lines, the connection won't be secure
ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, depth=9)
if ctx.load_verify_locations('ca.pem') != 1: raise Exception('No CA certs')
c = SSL.Connection(ctx)
c.connect(('www.google.com', 443)) # automatically checks cert matches host
c.send('GET / \n')
c.close()

Certificate Revocations

Another aspect that affects especially clients, but also servers that have been set up to require peer certificates, is the issue of certificate revocation. The two standard ways of doing this are the Certificate Revocation Lists (CRL) and Online Certificate Status Protocol (OCSP), but the ssl module in Python 2.6 provides no support for these. Granted, they are not easy to deal with in any of the 3rd party libraries either, but at least they provide some APIs to deal with them. Python is not unique here, though, because many (most?) modern applications still do not do any revocation checks by default (if at all).

Other Libraries

Besides the stdlib’s ssl module and M2Crypto, there are other SSL options for the Python developers out there. You might want to take a look at pyOpenSSL, which is again being maintained. Or if you want a pure Python version which can use various crypto libraries for performance improvements, you might want to check out TLS Lite (although it hasn’t been updated in a while). An exiting new package you may want to pay attention to is python-nss, which is a Python wrapper for the Network Security Services (NSS) libraries from the Mozilla project. If there are other maintained Python SSL packages out there I would be interested in hearing about them.

Update: In my opinion the essential resource for anyone working with OpenSSL-based software is Network Security with OpenSSL, by Viega et al., ISBN 059600270X.

M2Crypto 0.19.1 – OpenSSL without EC

M2Crypto 0.19 shipped with some code that expected OpenSSL with EC support. But since EC can be disabled, and at least Fedora Core ships with that disabled, things didn’t work so well in that case. Thanks to Miloslav Trmac who turned in a quick fix, we now have M2Crypto 0.19.1 that fixes this issue. No other major issues have surfaced in the week since 0.19 release, and since there have been at least 153 downloads according to PyPI, things look pretty stable.

Incidentally, if you were able to build 0.19, you should not need to get 0.19.1.

Released M2Crypto 0.19

I just pushed out M2Crypto 0.19. This ends the longest hiatus in releases (almost a year since 0.18.2) since I took over the project; apologies for the delays. I highlighted the best parts about 0.19 in an earlier post, so I won’t repeat them here. I need to make one clarification regarding Python 2.6 support: the optional timeout parameter added to many network modules is not yet supported in M2Crypto 0.19. I just noticed this too late for this release.

In preparation for 0.19 I did the first ever code coverage analysis of M2Crypto. I installed the latest coverage and nose, and run the M2Crypto unit tests. At first I got 72%. I then added some tests on trunk, and got the number to 75%. Then I added some docstrings and was surprised to note the figure jumped to 78%. Now I just need to write some more docstrings to break the magical 80% code coverage limit 😉

While nose and coverage were surprisingly easy to set up and run, finding out the specific lines of code that were not covered was not very user friendly. For that I installed figleaf. The workflow then became:

nosetests --with-coverage --cover-package=M2Crypto

which wrote the file .figleaf in the current directory. Then I run:

figleaf2html -d build/fig .figleaf

which produced HTML files in the build/fig directory. The HTML files showed the source code, formatted such that it was easy to see what was covered and what not. Basically non-covered lines were colored red.

Update: It seems I messed up the figleaf instructions. The above nosetests line will not produce .figleaf. I know of two ways to produce that. The first one is to add two more options to the nosetests command, which then becomes:

nosetests --with-coverage --cover-package=M2Crypto --with-figleafsections --figleaf-file=.figleaf

Unfortunately trying to process this with figleaf2html leads into:

Traceback (most recent call last):
  File "/usr/bin/figleaf2html", line 8, in <module>
    load_entry_point('figleaf==0.6.1', 'console_scripts', 'figleaf2html')()
  File "/usr/lib/python2.5/site-packages/figleaf-0.6.1-py2.5.egg/figleaf/annotate_html.py", line 256, in main
    coverage = figleaf.combine_coverage(coverage, d)
  File "/usr/lib/python2.5/site-packages/figleaf-0.6.1-py2.5.egg/figleaf/__init__.py", line 89, in combine_coverage
    keys.update(set(d2.keys()))
AttributeError: CodeTracer instance has no attribute 'keys'

The second way which actually works is to use figleaf directly:

figleaf --ignore-pylibs setup.py test -q

in the M2Crypto source tree. Then figleaf2html will work. The downside is that setup.py and test files are included in coverage.

How to Replace Python’s socket.ssl with M2Crypto’s SSL Implementation

It seems like I started a mini-series about “hidden” M2Crypto tools and modules…

Python’s socket.ssl is not secure. If you need any real security you need to look for 3rd party packages (things will improve a little with Python 2.6).

Sometimes you are faced with a library that does SSL, but uses Python’s socket.ssl that you can’t easily replace. For this purpose I wrote a little helper module using M2Crypto. Basically you just need to import this socklib.py before you import the module that is using Python’s socket.ssl, and call socklib.setSSLContextFactory() with context factory that creates secure SSL contexts and your SSL usage just became secure.

The socklib.py implementation is for client side only. It would be easy to expand it for servers, though. It may also lack some features, but it filled the need I had so that is where I stopped. I wrote it for Python 2.5 and haven’t thought what would need to be changed for 2.6.