TripleDES

From Crypto++ Wiki
Jump to: navigation, search

TripleDES, TDEA or 3DES, is multiple applications of the DES algorithm to plain text data to increase the security of the original DES algorithm. As the name implies, the DES algorithm is applied 3 times. There are two variants of TripleDES: the first is two key; and the second is three key. 2-key TDEA provides approximately 80 bits of security, while 3-key TDEA provides approximately 112 bits of security. In contrast, AES provides a minimum security level of 128.

DES was originally specified by NIST in FIPS 46, effective July 1977. In May 2005, DES was withdrawn, and is now only approved as a component of TDEA. You will still encounter it on occassion, such as with a Microsoft VPN using PPTP.

TripleDES was specified by NIST in May 2004 by SP 800-67. 2-key TDEA is no longer recommended for use in US government information systems. 3-key TDEA and AES are approved, and will coexist as approved algorithms until 2030. See SP 800-57, Part 1.

Note: if your project is using encryption alone to secure your data, encryption alone is usually not enough. Please take a moment to read Authenticated Encryption and understand why you should prefer to use CCM, GCM, or EAX over other modes, such as CBC or CTR.

Sample Programs

There are two sample programs. The first uses 2-key TDEA, and the second exercises 3-key TDEA. Both programs use filters (see Pipelining). The key is declared on the stack using a SecByteBlock to ensure the sensitive material is zeroized. Similar could be used for both plain text and recovered text.

2-Key TripleDES

The two key variant has a block size of 8 bytes (64-bits) and uses a key with 16 bytes. The DES implementation in Crypto++ ignores the parity bits (the least significant bits of each byte) in the key.

cout << "key length: " << DES_EDE2::DEFAULT_KEYLENGTH << endl;
cout << "block size: " << DES_EDE2::BLOCKSIZE << endl;

The snippet above produces the following output. Note that BLOCKSIZE is used for the initialization vector rather than IV_LENGTH because IV_LENGTH returns 0.

key length: 16
block size: 8

A program using two key TripleDES is CBC mode is shown below.

AutoSeededRandomPool prng;

SecByteBlock key(0x00, DES_EDE2::DEFAULT_KEYLENGTH);
prng.GenerateBlock(key, key.size());

byte iv[DES_EDE2::BLOCKSIZE];
prng.GenerateBlock(iv, sizeof(iv));

string plain = "CBC Mode Test";
string cipher, encoded, recovered;

/*********************************\
\*********************************/

try
{
    cout << "plain text: " << plain << endl;

    CBC_Mode< DES_EDE2 >::Encryption e;
    e.SetKeyWithIV(key, key.size(), iv);

    // The StreamTransformationFilter adds padding
    //  as required. ECB and CBC Mode must be padded
    //  to the block size of the cipher.
    StringSource ss1(plain, true, 
        new StreamTransformationFilter(e,
            new StringSink(cipher)
        ) // StreamTransformationFilter      
    ); // StringSource
}
catch(const CryptoPP::Exception& e)
{
    cerr << e.what() << endl;
    exit(1);
}

/*********************************\
\*********************************/

// Pretty print
StringSource ss2(cipher, true,
    new HexEncoder(
        new StringSink(encoded)
    ) // HexEncoder
); // StringSource

cout << "cipher text: " << encoded << endl;

/*********************************\
\*********************************/

try
{
    CBC_Mode< DES_EDE2 >::Decryption d;
    d.SetKeyWithIV(key, key.size(), iv);

    // The StreamTransformationFilter removes
    //  padding as required.
    StringSource ss3(cipher, true, 
        new StreamTransformationFilter(d,
            new StringSink(recovered)
        ) // StreamTransformationFilter
    ); // StringSource

    cout << "recovered text: " << recovered << endl;
}
catch(const CryptoPP::Exception& e)
{
    cerr << e.what() << endl;
    exit(1);
}

A typical output is shown below. Note that each run will produce different results because the key and initialization vector are randomly generated.

$ ./Driver.exe 
key: 9893BDEA99457F04C742B64109C80069
iv: 11F0B687DABAED4B
plain text: CBC Mode Test
cipher text: 82605EECEDF764373E205742AC947A0B
recovered text: CBC Mode Test

3-Key TripleDES

The three key variant has a block size of 8 bytes (64-bits) and uses a key with 24 bytes. The DES implementation in Crypto++ ignores the parity bits (the least significant bits of each byte) in the key.

cout << "key length: " << DES_EDE3::DEFAULT_KEYLENGTH << endl;
cout << "block size: " << DES_EDE3::BLOCKSIZE << endl;

The snippet above produces the following output. Note that BLOCKSIZE is used for the initialization vector rather than IV_LENGTH because IV_LENGTH returns 0.

key length: 24
block size: 8

A full program using three key TripleDES is CBC mode is shown below.

AutoSeededRandomPool prng;

SecByteBlock key(0x00, DES_EDE3::DEFAULT_KEYLENGTH);
prng.GenerateBlock(key, key.size());

byte iv[DES_EDE3::BLOCKSIZE];
prng.GenerateBlock(iv, sizeof(iv));

string plain = "CBC Mode Test";
string cipher, encoded, recovered;

/*********************************\
\*********************************/

try
{
    cout << "plain text: " << plain << endl;

    CBC_Mode< DES_EDE3 >::Encryption e;
    e.SetKeyWithIV(key, key.size(), iv);

    // The StreamTransformationFilter adds padding
    //  as required. ECB and CBC Mode must be padded
    //  to the block size of the cipher.
    StringSource ss1(plain, true, 
        new StreamTransformationFilter(e,
            new StringSink(cipher)
        ) // StreamTransformationFilter      
    ); // StringSource
}
catch(const CryptoPP::Exception& e)
{
    cerr << e.what() << endl;
    exit(1);
}

/*********************************\
\*********************************/

// Pretty print
StringSource ss2(cipher, true,
    new HexEncoder(
        new StringSink(encoded)
    ) // HexEncoder
); // StringSource

cout << "cipher text: " << encoded << endl;

/*********************************\
\*********************************/

try
{
    CBC_Mode< DES_EDE3 >::Decryption d;
    d.SetKeyWithIV(key, key.size(), iv);

    // The StreamTransformationFilter removes
    //  padding as required.
    StringSource ss3(cipher, true, 
        new StreamTransformationFilter(d,
            new StringSink(recovered)
        ) // StreamTransformationFilter
    ); // StringSource

    cout << "recovered text: " << recovered << endl;
}
catch(const CryptoPP::Exception& e)
{
    cerr << e.what() << endl;
    exit(1);
}

A typical output is shown below. Note that each run will produce different results because the key and initialization vector are randomly generated.

$ ./Driver.exe 
key: 81F8E1ECCDFBBCE1E2FB523CDAB32B10B479AB53D9819DEF
iv: 07DD427883B74970
plain text: CBC Mode Test
cipher text: 6DF4AE5D60BC8FF1AAE4CCFD11A05FB5
recovered text: CBC Mode Test

To manually insert bytes into the filter, perform multiple Puts. Though Get is used below, a StringSink could easily be attached and save the administrivia.

const size_t SIZE = 16 * 4;
string plain(SIZE, 0x00);

for(size_t i = 0; i < plain.size(); i++)
    plain[i] = 'A' + (i%26);
...

CBC_Mode < DES_EDE3 >::Encryption encryption(key, sizeof(key), iv);
StreamTransformationFilter encryptor(encryption, NULL);

for(size_t j = 0; j < plain.size(); j++)
    encryptor.Put((byte)plain[j]);

encryptor.MessageEnd();
size_t ready = encryptor.MaxRetrievable();

string cipher(ready, 0x00);
encryptor.Get((byte*) &cipher[0], cipher.size());

Downloads

3TDEA-CBC-Filter.zip - Demonstrates encryption and decryption using 3-key TripleDES in CBC mode with filters (confidentiality only)

2TDEA-CBC-Filter.zip - Demonstrates encryption and decryption using 2-key TripleDES in CBC mode with filters (confidentiality only)

DES-CBC-Filter.zip - Demonstrates encryption and decryption using DES in CBC mode with filters (confidentiality only)