Sunday, 18 March 2007

Mr Self Destruct – Deconstructing GNU.Free

Sorry it's been a while, but I've started a fairly heavy development cycle at work, and haven't had the time to post (along with playing EVE). As promised here's the results of a brief analysis I've been doing of the GNU.Free codebase.

Encryption Techniques

A lot of attention has been paid to encryption of the data going from the clients to servers / inter server on this system, but then the backdoor has been left wide open with some crummy encryption on the DB.

The databases encryption uses symmetric algorithms where the key is derived from the following process:

User enters “password” -> generate hash -> generate key

This technique is so simple to crack it's hardly worth talking about (but we will!). Users enter weak passwords, and the use of the isSafe function to limit the enterable characters means an attacker has a well defined character set to work from, and easy ways to detect generation of the correct key (the ER server DB contains a single char flag that has two possible values, nice). So guessing the key to the databases is relatively straight forward.

Then in the ER server we have the usernames / passwords for every possible user, which are stored as hashes... but individually! This means that an attacker merely needs to generate a list of all possible 8 digit values, compare the decrypted values to the list and BINGO! A big long list of credentials to vote with. What should of happened is to hash the credentials together using a hash algorithm with a salt, this would've delayed an attacker by an enormous amount of time.

The use of this symmetric technique also means it's very easy to conduct a count of votes in the RTServer before the end of polling, you don't even need to crack the key to get totals (you just won't know who the total is for, but you'll be able to see if there is a clear lead). This is a very bad thing. Electoral law requires that no count can be conducted until the end of polling and only then under the authority of a returning officer. Asymmetric encryption should be used here, store the counters public key on the server, stick the private key on a secure device (such as a smart card) encrypt with the public key, decrypt with the private. Not exactly quantum physics is it?

Also alteration of data in the count DB is trivial if you've broken the key. No record is kept in a separate system of the vote (nor in the much heralded, yet ultimately foolhardy SecureAppender), such that there is nothing to check these values against. If you've broken the key, pick your winner!


There's some badly coded methods that look impressive in size, but could be boiled down to something much more elegant very easily. Take the oft repeated (naughty, naughty, refactor early, refactor often) isSafe function, 90 lines that can easily be condensed into 10 lines (no switch statement should be that size ever!)

I ran the various packages through PMD (a static analysis tool similar to FXCop, although it does include a Copy'n'Paste detector similar to Simian as well) and got the following violation counts:

ERServer – 419
Free – 631
Free.DBPool – 97
Free.util – 131
FreeClient – 271
FreeInstall – 360
PollManager - 212

I've not had a chance to review all the violations, but it would be fair to sat at least 50% should be addressed.

Now onto commenting, the following line comes from the comments on the PMProtocol class in PollManager:

The source code is extremely readable and best explains the full functionality.

What follows is a class with one method that's 170 lines long... this is not readable code! Readable code is concise and broken into procedural chunks. See also the process method on the ServerProtocol class in the Free package... 220+ lines in that one.

Exception Handling

Handling of exceptions is extremely inconsistent and often exceptions are caught and then not handled at all (see the SecureAppender class for an appalling example of this, if an audit message fails to be written the whole operation should fail, end of story). No specific Exception classes exist within the system, so all exceptions thrown by the system are of type Exception (naughty, naughty). Also the details of exceptions are not completely recorded, at best all you're going to get is the exception message without any useful diagnostic data such as the stack trace (because all you need to trace a bug is the message surely?)


Rolling your own networking systems is dangerous and should not be attempted by amateurs. GNU.Free makes liberal use of TCP sockets but implements no real management of them. Once a socket is open you're free to stuff as much (or as little) data down the pipe as you like, and there's no timeout management, so simply open lots of sockets, stuff 1Mb of data down it and hold it open. Watch as the heap space for the app disappears in a matter of minutes. Can anyone say Denial of Service attack?

Missing Processes

There's very little verification within GNU.Free of the data that's in any of the databases, there's a utility to check the hashes of log files (and those are virtually impossible to forge aren't they?), but that's about it. Nothing to check the rate of failed authentications from a source address, for example. Coding the core application is only about 30% of the job, everything else is process around it, if you don't bother with that then you've not solved the problem.

Keys are stored in the CODE! Do I really have to say how bad this is? No attempt has been made to provide a mechanism for loading keys from different locations at runtime, the cheapest, easiest, and least secure option has been chosen instead (how, exactly, do you publish the source of your system, when that source contains the secrets for secure operation of the system?)

In Conclusion

The webpages for GNU.Free state:

This was (and still is as far as we know) the only Free Software Internet Voting package designed for legally binding elections and not little web-based polls.

I don't know which country they were planning to use this little puppy in, but in the UK... no chance. You'd need a significant amount of reworking to get this even remotely close to being a viable solution, and at that point it would probably be better to start again from scratch... with real developers... oh and a real architect... real development processes... source control... maybe some ongoing analysis.... design documents (apparently there are some, but I've not seen them, if someone could point me to them I'd be grateful)... clear requirements.... and a basic knowledge of cryptography would be handy.

Updated : Corrected a scary number of typos. Posted it a little too quickly and didn't proof read it first.