FX::QSSLDevice Class Reference
[Writing secure codeFile type i/o devicesSynchronous i/o devices]

#include <QSSLDevice.h>

Inheritance diagram for FX::QSSLDevice:

FX::QIODeviceS FX::QIODevice List of all members.

Detailed Description

An i/o device encrypting what goes through it.

Thanks to the wonderful flexibility of the OpenSSL encryption library, TnFOX can offer completely integrated encryption facilities which can work with any FX::QIODevice. While the most common use will be with FX::QBlkSocket, you could just as easily apply it to file data or indeed anything else. Synchronous devices work the full SSL/TLS negotiation protocol whereas file devices simply apply symmetric or asymmetric encryption based on the FX::FXSSLKey you provide.

QSSLDevice offers SSL v2/v3 & TLS v1 protocols with RC2, RC4, DES, Blowfish, IDEA, 3DES & AES symmetric encryption and RSA asymmetric encryption (ie; public-key) to any bit length. Furthermore RSA, Diffie-Hellman and DSS authentication methods are available. Within these, you have the strongest available encryption currently known.

Unfortunately usage of strong encryption in certain ways is illegal in many illiberal countries (such as the US and some countries in Europe). Some countries won't let you export it, many limit the maximum key length, most require key escrow (where your private key can be obtained by court order and failure to comply results in prison) and in some usage is completely illegal altogether. Where things get even more fun is that you can do something with encryption legal in your country of residence but if you take a holiday to the US say, there they can put you in prison for a very long time.

Warning:
TnFOX, its makers or anyone else do not take responsibility for YOU breaking the law in your country or any other country. TnFOX merely provides the facilities, it is YOU who chooses how to use them. If you do not accept that you take full responsibility for how you use this facility, then you cannot use QSSLDevice nor compile in OpenSSL support!
More fun again is that various encryption algorithms offered by QSSLDevice are patented in some countries but not in others. The following is a non-definitive list and no responsibility is taken for the list being remotely accurate:
AlgorithmLegal StateWhere
SSL itselfRoyalty free unless you sue Netscape?
DESRoyalty free?
3DESRoyalty free?
BlowfishUnpatentedEverywhere
IDEAPatented till 2011USA,Europe
AESRoyalty free?Everywhere?
RC5PatentedUSA,Japan,Europe in 2003
RSAUnpatentedPatent expired in 2000
Diffie-HellmanPatent expired in 1997??
DSA/DSSRoyalty freeEverywhere
Warning:
Again, YOU are responsible for ensuring that your software using TnFOX is not breaking patents
If you want a good overview of cryptography, see http://home.ecn.ab.ca/~jsavard/crypto/intro.htm or the book "Applied Cryptography" by Bruce Schneier.

Usage:

The first QSSLDevice created in the process will take the longest as required data structures are cached in-memory and the random number generator seeded with 4096 bits of randomness. Like most FX::QIODevice's, QSSLDevice is thread-safe[1] though FX::FXSSLKey is not. If the OpenSSL library was not available at compile-time, the first QSSLDevice created throws an exception of code QSSLDEVICE_NOTENABLED.

At the time of writing (August 2003), the minimum symmetric encryption key length should be 128 bits and the minimum asymmetric encryption key length should be 2048 to 3072 bits. If your local legal situation permits it, higher is better though bear in mind that with asymmetric encryption especially, bigger keys means substantial increases in encoding and decoding time.

AlgorithmStrengthNotes
DES56 bitYou don't want to use this except for legacy applications
3DES112 bitTriple DES is 168 bit but due to a weakness is effectively 112 bit
It's also very slow thus probably best to avoid this too
Blowfish32-448Especially useful for file encryption as it's fast
IDEA128Patented for commercial use
AES128/192/256The official US government symmetric encryption standard
RSA512+The traditional asymmetric algorithm
DH (Diffie-Hellman)512+The default for non-authenticated connections
DSA/DSS512+Used only for authentication

Furthermore there are two hashing algorithms available: MD5 and SHA1. MD5 has a collision weakness so use SHA1 where possible. SHA1 was formerly the US government standard cryptographic hash.

The default settings create a SSL v3 & TLS only capable device with ciphers set to "HIGH:@STRENGTH" ie; only the strongest encryption ordered by strength (which are currently SHA1 with AES(256) or 3DES and RSA or DH) but with non-authenticated protocols (ie; certificates are optional). These settings aren't very compatible with most existing servers on the internet so using setCiphers() you may wish to set "DEFAULT:@STRENGTH" (all less non-authenticated protocols) or "ALL:@STRENGTH" (all protocols including non-authenticated). See the OpenSSL documentation for example cipher strings and how they should be formatted.

There is some basic support for certificates. You can specify a private key file and certificate file using setPrivateKeyFile() and setCertificateFile() plus you can compare FX::FXNetwork::dnsReverseLookup() with peerHostNameByCertificate(). All files must be in PEM/X509 format.

Compression:

All encryption methods are weaker if the data they are encrypting has something known about it eg; it is HTML or ASCII text. Another source of weakness is verbosity - the more data there is to analyse, the easier to crack. The simple solution to this is to apply Lempel-Ziv compression on the data before encrypting using something like FX::QGZipDevice. See the examples below.

Randomness:

FX::Secure::Randomness is a high quality source of randomness and thus can take a very long time to generate 4096 bits. If less than 4096 bits is available then to prevent applications just sitting around waiting for it, QSSLDevice uses a number of sources of instantaneous entropy to seed the cryptographically secure Pseudo-Random Number Generator (PNRG):

Thereafter with every new QSSLDevice created, up to 4096 bits of entropy is added from FX::Secure::Randomness to keep the PRNG fresh. Every new FXSSLKey or FXSSLPKey also adds up to its bitsize of entropy to the PRNG.

Synchronous usage:

It is best to use TLS v1 (which is SSL v3.1), then SSL v3 and lastly SSL v2 only if you absolutely have to due to compatibility reasons. Because SSL v2 has security issues, by default it is disabled though you can still connect to SSL v2 servers (you can change this in the constructor parameters). If you enable everything, a negotiation procedure is followed at the start of the connection to agree the best available protocol.

If you don't bother with certificates, the default settings of QSSLDevice use anonymous Diffie-Hellman for key exchange using an internal list of primes for generation of unique keys. A new key is generated per new connection. Obviously this mechanism is liable to man-in-the-middle attack (ie; you can't be sure who you're connecting to is actually who you think), but if you merely want your data going over the wire to be unintelligible, this is very sufficient.

Note:
The embedded lists of primes currently only include 4096 bit and 1024 bit primes. Asking for other bit lengths incurs a very significant time penalty.
If speed is more important than security, consider 128 bit AES with 1024 bit key exchange. This is because 128 bit AES is faster than most other similar strength block-ciphers. It is considerably faster than plain DES and vastly faster than 3DES. AES is also faster than any other for small packet transfers (smaller than 1024 bytes) so bear this in mind.

[1]: OpenSSL itself is only partially threadsafe - in particular, it is not threadsafe when multiple threads use a SSL connection at the same time (which unfortunately TnFOX requires as this is what the synchronous i/o model requires). QSSLDevice sets some conservative options in OpenSSL to prevent packet fragmentation (which would cause reads during writes or writes during reads) and also serialises all reads and writes ie; only one read and write may happen at once - but both a read and a write concurrently. This appears to be safe from testing, but internal changes to OpenSSL may cause future breakage.

File-based usage:

Most of the focus so far in this documentation has been for encrypting communications. However, if you have some data to which you want to FXRESTRICT access, QSSLDevice can also apply straight off symmetric or asymmetric encryption to raw data.

Asymmetric encryption has been implemented as symmetric encryption but encrypting the symmetric key with asymmetric encryption and embedding it with the data. This makes things substantially easier never mind improving performance. You typically use asymmetric encryption if your connection to the destination of the data is insecure (eg; internet, postal mail, telephone etc) whereby your recipient generates a RSA public/private key pair and sends you the public part as a PEM or X509 format file. Read that into a FX::FXSSLPKey and write out your data. Now only your intended recipient can read the data.

For symmetric encryption, your two likely choices for ciphers are Blowfish and AES. Both are relatively fast (~40Mb/sec on my machine) and both scale well with key bit size (AES is 40% slower with 256 bit keys than 128 bit). The other ciphers have been left out as they have known weaknesses or are patented.

Strongly consider setting the FX::QIODeviceFlags::IO_ShredTruncate bit when opening any secure file.

Note:
Due to limitations within the OpenSSL library, on 32 bit systems data greater than 2Gb cannot be worked with. Attempting to do so causes undefined operation. This problem goes away on 64 bit systems.
As of v0.86, support for seeking, mixing reads & writes and truncating has been added. This comes at the cost of no longer being able to use Cipher-Block Chaining (CBC) mode (as previous versions did) as you'd need to know all the data up to the seek point. A similar FXRESTRICTion would obviously apply to Cipher Feedback (CFB) mode, so that leaves us with Output Feedback (OFB) mode or Counter (CTR) mode. Counter mode is really Electronic Codebook (ECB) with the input as a combination of nonce and counter with the output XORed with the plaintext to generate the ciphertext.

Now OFB and CTR modes are weaker than CFB and CBC as the plaintext has no effect on the encryption - it is determined entirely by starting conditions. CTR mode has the advantage of instant seeks whereas OFB must be iterated from beginning to the seek point on each seek - so I have opted for CTR mode despite that it is probably slightly weaker.

Performance-wise, throughput is heavily dependent on the speed at which your processor can XOR portions of memory. There is an SSE2 instruction for XORing 16 bytes at a time, but it requires 16 byte aligned memory which is highly unlikely in general purpose usage. This implementation does make use of memory alignment up to eight (for which the src, dest AND offset inside encrypted stream buffer must all be multiples of eight) but profiling shows that even that is rarely used compared against the four byte aligned XOR on 32 bit systems. If however you are using a cipher with a large (>16) block size, you could see substantial speed increases if you always pass a buffer to readBlock() and writeBlock() which is eight byte aligned.

Usage:

Usage is as with all things in TnFOX, ridiculously easy:

Communication-type use (synchronous):

QBlkSocket myserversocket;
...
QBlkSocket newsocketraw=myserversocket.waitForConnection();
// Perhaps spin off a new thread, or authenticate using FX::FXSRP first
// You will need a try...catch() block as negotiation may fail
QSSLDevice newsocket(&newsocketraw);
newsocket.create(newsocketraw.mode());
newsocket.read(NULL, 0);    // Just negotiate, don't read
if(newsocket.peerHostNameByCertificate()!=FXNetwork::dnsReverseLookup(newsocketraw.peerAddress())) reject;
...

File-type use:

QMemMap myfileraw("myencryptedfile.txt");
QSSLDevice myfile(&myfileraw);
// Get password from user into FXString mypassword
myfile.setKey(FXSSLKey(352, FXSSLKey::Blowfish, mypassword));
myfile.open(IO_ReadOnly);
// Read what you like as normal ...

QMemMap myencryptedfile("myencryptedfile.bin");
QSSLDevice myfileencryptor(&myencryptedfile);
myfileencryptor.setKey(thekey);
QGZipDevice myfilecompressor(&myfileencryptor);
QIODevice *myfile=&myfilecompressor;
FXStream s(myfile);
myfile->open(IO_WriteOnly);
s << "Some text to compress, then encrypt, then write to a memory mapped file";
myfile->close();

Just especially as a note to myself who keeps forgetting how I'm supposed to use asymmetric encryption, here's how you do it:

FXSSLPKey &thekey;
FXSSLPKey pthekey(thekey.publicKey());
QMemMap myencryptedfile("myencryptedfile.bin");
QSSLDevice myfileencryptor(&myencryptedfile);
FXSSLKey tempkey(128, FXSSLKey::AES);
tempkey.setAsymmetricKey(&pthekey);
myfileencryptor.setKey(tempkey);
QGZipDevice myfilecompressor(&myfileencryptor);
QIODevice *myfile=&myfilecompressor;
FXStream s(myfile);
myfile->open(IO_WriteOnly);
s << "Some text to compress, then encrypt, then write to a memory mapped file";
myfile->close();
To decrypt using asymmetric encryption, indirect via a temporary symmetric key like so:
FXSSLPKey &thekey;
QSSLDevice &myfileencryptor;
myfileencryptor.setKey(FXSSLKey().setAsymmetricKey(&thekey));
...

File formats:

QSSLDevice uses a proprietary file format for its secure files. Sorry about this, I did look at the OpenPGP file format and concluded it was too much hassle. I just wanted a basic secure file format with no bells or whistles.

Warning:
This file format by me may contain weaknesses as a result of my inexperience with cryptography. If you are an expert and see one, please notify me. I have built in versioning to allow seamless upgrades.
+0: "TNFXSECD"
+8: File version, currently 2
+9: If "SKEY" then a FX::FXSSLKey but with the key data encrypted by the public part of its asymmetric key. The encryption is done by binding the key data (in big-endian format), the bitsize (little-endian 4 bytes), the salt length (little-endian 4 bytes) and the type (little-endian 2 bytes) together and asymmetrically encrypting
+9[+skeylen]: "TEST" then a 192 bit Tiger hash of the key with 16 bits of salt if salting on the key wasn't used (used to test for bad keys)
+37[+skeylen]: A random nonce of the same size as the cipher's block size
+37+noncelen[+skeylen]: The encrypted data, encrypted by XORing original data with the output of the encryption of the nonce XORed by the file pointer (CTR mode)

Cryptoanalysis: As I previously mentioned, I've never touched cryptography before writing this class and while I have learned lots in the past few weeks, I cannot say I am experienced. What I have done is work on the basis that the less information the attacker has, the better. I've also used what the internet says is best practice though I really need the book by Bruce Schneier (if I had the money, I would). Below I present an analysis of my file format to aid others in finding any weaknesses I may have introduced:

An attacker can not know from the file format:

Known weaknesses: However an attacker can know the following: Therefore it seemed to me that the weakest point was the hash as a brute force strength attack could be used and the Tiger hash function is much quicker than a decrypt - though this would still require the attacker to know which cipher (there are only two available) and its key length. Thus I introduced a 16 bit salt appended to the key before hashing which means for something encrypted by a 128 bit key, an average of 2^(128-1+16)*O(hash) is needed, or 10 with forty-two zeros after it Tiger hashs. I figure that 32768 Tiger hashs are probably slower than a single iteration of an attack on the cipher.

This salting of the hash is disabled however when salting of the key is enabled because of performance reasons. Salting of the key is performed by EORing in random data prior to encryption and up to eight bytes may be used (ie; 64 bits). Key salting should be enabled when the entropic quality of the key is low eg; if it is generated from plaintext.

For keys generated from plaintext, by default the hashing function(s) in FXSSLKey::generateFromText() is run 65536 times and 16 bits of salting set. This means an average of (80^passwordlen)*65536*O(pwhash)*65536*O(hash)/2 which with 128 bit key and a seven character password is 9 with twenty-two zeros after it Tiger hashs. If a straight off attack were used because of the unsalted hash, that would be 17 with thirty-seven zeros after it Tiger hashs - basically, we sacrifice brute-force attack strength for strength in key generation from plaintext. Also of course attacking the

cipher directly is made much harder due to greater key entropy.

Acknowledgements:

This class uses the excellent OpenSSL library developed by Eric Young, Tim Hudson & the OpenSSL team. Since this library does not include any actual OpenSSL code, TnFOX does not need to state:

This product includes software developed by the OpenSSL Project
for use in the OpenSSL Toolkit. (http://www.openssl.org/)
... but if OpenSSL support is present in the build of the library you use in your end product, then you must place the above notice in all advertising of your product as per the OpenSSL license. This is irrespective of whether you use QSSLDevice or not!

Definition at line 670 of file QSSLDevice.h.

Public Types

typedef FXfval Offset
 Default
 Unix
 MacOS
 MSDOS
 NoTranslation
 UTF8
 UTF16
 UTF16LE
 UTF32
 UTF32LE
enum  CRLFType { Default, Unix, MacOS, MSDOS }
enum  UnicodeType {
  NoTranslation, UTF8, UTF16, UTF16LE,
  UTF32, UTF32LE
}

Public Member Functions

 QSSLDevice (QIODevice *encrypteddev=0, bool enablev2=false)
 ~QSSLDevice ()
QIODeviceencryptedDev () const throw ()
void setEncryptedDev (QIODevice *dev)
const FXSSLKeykey () const
void setKey (const FXSSLKey &key)
bool SSLv2Available () const throw ()
void setSSLv2Available (bool a)
bool SSLv3Available () const throw ()
void setSSLv3Available (bool a)
FXString ciphers () const
void setCiphers (const FXString &list)
bool usingSSLv2 () const
bool usingSSLv3 () const
bool usingTLSv1 () const
FXString peerHostNameByCertificate () const
FXString cipherName () const
FXuint cipherBits () const
FXString cipherDescription () const
void renegotiate ()
FXuint fileHeaderLen () const throw ()
virtual bool isSynchronous () const
virtual bool create (FXuint mode=IO_ReadWrite)
virtual bool open (FXuint mode=IO_ReadWrite)
virtual void close ()
virtual void flush ()
virtual FXfval size () const
virtual void truncate (FXfval size)
virtual FXfval at () const
virtual bool at (FXfval newpos)
virtual bool atEnd () const
virtual const FXACLpermissions () const
virtual void setPermissions (const FXACL &)
virtual FXuval readBlock (char *data, FXuval maxlen)
virtual FXuval writeBlock (const char *data, FXuval maxlen)
virtual FXuval readBlockFrom (char *data, FXuval maxlen, FXfval pos)
virtual FXuval writeBlockTo (FXfval pos, const char *data, FXuval maxlen)
virtual int ungetch (int c)
FXuval readBlockFrom (FXuchar *data, FXuval maxlen, FXfval pos)
FXuval writeBlockTo (FXfval pos, const FXuchar *data, FXuval maxlen)
FXuint flags () const
FXuint mode () const
FXuint state () const
CRLFType crlfFormat () const
void setCRLFFormat (CRLFType type)
UnicodeType unicodeTranslation () const
void setUnicodeTranslation (UnicodeType type)
bool isBuffered () const
bool isRaw () const
bool isTranslated () const
bool isUTF16Translated () const
bool isUTF32Translated () const
bool isReadable () const
bool isWriteable () const
bool isWritable () const
bool isReadWrite () const
bool isClosed () const
bool isInactive () const
bool isOpen () const
FXuval readBlock (FXuchar *data, FXuval maxlen)
FXuval writeBlock (const FXuchar *data, FXuval maxlen)
virtual FXuval readLine (char *data, FXuval maxlen)
virtual int getch ()
virtual int putch (int c)
FXfval shredData (FXfval offset, FXfval len=(FXfval)-1)

Static Public Member Functions

static void setCertificateFile (const FXString &path)
static void setPrivateKeyFile (const FXString &path, const FXString &password)
static const FXStringstrongestAnonCipher ()
static const FXStringfastestAnonCipher ()
static bool waitForData (QIODeviceS **signalled, FXuint no, QIODeviceS **list, FXuint waitfor=FXINFINITE)
static FXuint waitForDataMax () throw ()
static UnicodeType determineUnicodeType (FXuchar *data, FXuval len) throw ()
static FXuval applyCRLF (FXuchar *FXRESTRICT output, const FXuchar *FXRESTRICT input, FXuval outputlen, FXuval &inputlen, CRLFType crlftype=Default, UnicodeType utftype=NoTranslation)
static FXuval removeCRLF (FXuchar *FXRESTRICT output, const FXuchar *FXRESTRICT input, FXuval outputlen, FXuval &inputlen, UnicodeType utftype=NoTranslation)

Protected Member Functions

void setFlags (int f)
void setMode (int m)
void setState (int s)

Protected Attributes

FXfval ioIndex

Friends

FXAPI FXStreamoperator<< (FXStream &s, QIODevice &i)
FXAPI FXStreamoperator>> (FXStream &s, QIODevice &i)


The documentation for this class was generated from the following file:
(C) 2002-2009 Niall Douglas. Some parts (C) to assorted authors.
Generated on Fri Nov 20 18:37:44 2009 for TnFOX by doxygen v1.4.7