Translate

Archives

OpenSSL DES APIs

Now that OpenSSL has finally reached version 1.0.0, I decided to take another look how the various Data Encryption Standard (DES) application programming interfaces (routines) included in OpenSSL can be used to encrypt and decrypt data. Since there is also a lack of simple examples available on the Internet of how to actually use the OpenSSL DES routines, I have included a number of examples in this post to encourage readers to experiment with these routines.

The original author of the DES routines in OpenSSL’s libcrypto was Eric Young. Young and Tim Hudson posted the first version of of a free cryptographic library called SSLeay (eay stands for Eric A, Young) to the Internet in 1995. Amazingly Young managed to single-handedly implement the full suite of cryptosystems used in SSLeay. Since then the SSLeay library has become part of OpenSSL. However you will still frequently come across references to SSLeay in both man pages and the source code. Young is still involved in cryptography and currently works for RSA, the security division of EMC.

Some background information on DES is probably in order for those who have forgotten their college course on cryptography. DES has been around for quite a long time. It was developed by IBM as enhancement to an existing key generator algorithm called Lucifer that was primarily developed by Horst Feistel. It became a standard in 1977 when the National Bureau of Standards (now called NIST) issued Federal Information Processing Standards Publication 46 (FIPS 46). That standard specified that DES be used within the Federal Government for the cryptographic protection of sensitive, but unclassified, computer data.

DES is a member of the class of ciphers (British English: cyphers) called a block cipher. In a block cipher, a block of N bits from the plaintext is replaced with a block of N bits from the ciphertext. Ideally the relationship between the input block and the output block is completely random but invertible. This implies a one-to- one relationship with each input block being mapped to a unique output block. Mathematically, DES maps the set of all possible 64-bit vectors onto itself. Selecting a DES cryptographic key allows a user to select one of the possible mappings

Technically speaking, DES is an iterative, block, product cipher system (encryption algorithm). A product cipher system mixes transposition and substitution operations in an alternating manner. Iterations refers to the use of the output of an operation as the input for another iteration of the same procedure. This is known as a Feistel structure or Feistel network. A cryptographic system based on a Feistel structure uses the same basic algorithm for both encryption and decryption. A large proportion of block ciphers, including DES, use a Festel structure.

Feistel structure

The algorithmic implementation of DES is known as Data Encryption Algorithm (DEA). DEA uses sixteen iterations of a pair of transposition and substitution operations to encrypt or decrypt an input block. All computations are linear except for the Substitution-boxes (S-boxes) which provide the non-linear substitution component of the algorithm. Linear algorithms can be easily broken using a known plaintext attack. The S-boxes in DEA effectively hinder this form of attack (Claude Shannon‘s diffusion property.) The number of rounds (16) is important also. An 8-round DEA can be broken in a few minutes on a PC using a chosen plaintext attack, i.e. differential cryptoanalysis. When DEA was proposed, there was considerable criticism with most of it directed at the S-boxes. It was even suggested the the S-boxes might contain a trapdoor. One useful property of DEA is that it can be implemented very efficiently in software (or in hardware for that matter) using table look-up. DES uses a 56-bit encryption key and a 64-bit block. The key itself is specified with 8 bytes (64-bits), but the last bit of each byte is used as a parity check of the other 7 bits. Round keys are 48-bits and are generated from the 56-bit encryption key by a sequence of permutations.

Several methods of incorporating DES into a cryptographic system are possible. Generally speaking, these can be classified into either block or stream methods. In addition a number of modes of operation are specified by the FIPS 81 (DES Modes of Operation) standard. The modes specify how data will be encrypted and decrypted. These are summarized below.

Electronic Codebook Mode (ECB)

  • 64 bits (i.e. a block) are enciphered at a time.
  • The order of the blocks can be rearranged without detection.
  • A plaintext block always produces the same ciphertext block for the same key.
  • An error only affects one ciphertext block.
  • Use discouraged as vulnerable to a directory attack

Cipher Block Chaining Mode (CBC)

  • Multiples of 64 bits are enciphered at a time.
  • Blocks cannot be rearranged. Each ciphertext block depends on the current and all preceding plaintext blocks.
  • A plaintext block always produces the same ciphertext block for the same key and starting variable.
  • Different starting variables prevent the same plaintext enciphering to the same ciphertext.
  • An error affects the current and following ciphertext blocks.

Cipher Feedback Mode (CFB)

  • Only blocks of j <= 64 bits are enciphered at a time.
  • A small j requires more cycles through the encipherment algorithm per unit of plaintext and thus greater processing overhead.
  • A plaintext block always produces the same ciphertext block for the same key and starting variable.
  • Blocks cannot be rearranged. Each ciphertext block depends on the current and all preceding plaintext blocks.
  • Different starting variables are used to prevent the same plaintext enciphering to the same ciphertext.
  • The strength of this mode depends on the size of the key k (best if j == k).
  • An error will affect the current and the following ciphertext blocks.

Output Feedback Mode (OFB)

  • Only blocks of j <= 64 bits are enciphered at a time.
  • A small j requires more cycles through the encipherment algorithm per unit of plaintext and thus greater processing overhead.
  • A plaintext block always produces the same ciphertext block for the same key and starting variable.
  • Different starting variables are used to prevent the same plaintext enciphering to the same ciphertext.
  • Absence of chaining makes this mode vulnerable to specific attacks.
  • Different start variable values prevent the same plaintext enciphering to the same ciphertext, by producing different key streams.
  • An error bit in the ciphertext causes only one bit to be in error in the deciphered plaintext.
  • It is not self-synchronizing.

Triple-DES ECB Mode

  • Encrypt with key1, decrypt with key2 and encrypt with key3 again.
  • As for ECB encryption but increases the key length to 168 bits.
  • If all keys are the same it is equivalent to encrypting once with just one key.
  • If the first and last key are the same, the key length is 112 bits.
  • If all 3 keys are the same, this is effectively the same as normal ECB mode.

Triple-DES CBC Mode

  • Encrypt with key1, decrypt with key2 and then encrypt with key3.
  • As for CBC encryption but increases the key length to 168 bits with the same restrictions as the Triple-DES ESB mode

Our first example shows how to use the basic DES encryption routine, DES_ecb_encrypt(), to encrypt or decrypt a single 64-bit block of plaintext to electronic code book (ECB) mode. If the encrypt argument is DES_ENCRYPT, the input (plaintext) is encrypted into the output (ciphertext) using the specified key_schedule. If the encrypt argument is DES_DECRYPT, the input (ciphertext) is decrypted into the output (plaintext). Note that input and output may overlap.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/des.h>
#include <openssl/rand.h>

#define BUFSIZE 64 

int main(void)
{
    unsigned char in[BUFSIZE], out[BUFSIZE], back[BUFSIZE];
    unsigned char *e = out;

    DES_cblock key;
    DES_cblock seed = {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10};
    DES_key_schedule keysched;

    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    memset(back, 0, sizeof(back));

    RAND_seed(seed, sizeof(DES_cblock));

    DES_random_key(&key);

    DES_set_key((C_Block *)key, &keysched);

    /* 8 bytes of plaintext */
    strcpy(in, "HillTown");

    printf("Plaintext: [%s]\n", in);

    DES_ecb_encrypt((C_Block *)in,(C_Block *)out, &keysched, DES_ENCRYPT);

    printf("Ciphertext:");
    while (*e) printf(" [%02x]", *e++);
    printf("\n");

    DES_ecb_encrypt((C_Block *)out,(C_Block *)back, &keysched, DES_DECRYPT);

    printf("Decrypted Text: [%s]\n", back);

    return(0);
}


Note the use of C_Block. The examples in this post use C_Block because that is what I am used to using. If you are writting new code for modern platforms, you should use DES_cblock rather than C_Block or des_cblock. See des.h for the defintion of DES_cblock, i.e. typedef unsigned char DES_cblock[8].

Here the output when the program is compiled and executed.

$ gcc -o example1 example1.c -lcrypto
$ ./example1
Plaintext: [HillTown]
Ciphertext: [34] [bc] [85] [30] [14] [95] [43] [00]
Decrypted Text: [HillTown]
$


Examining the above code you will see that there are two parts to using DES encryption. The first is the generation of a DES_key_schedule from a key, the second is the actual encryption or decryption. A DES key is of type DES_cblock which consists of 8 bytes with odd parity. The least significant bit in each byte is the parity bit. The key schedule is an expanded platform-dependent form of the key which is used to speed the encryption process. The example uses a seeded PRNG (RAND_seed()) to generate a random 64-bit DES key. Notice that I explicitly zero all storage that the example uses; always a good idea when using OpenSSL library routines. Examining the output, you will see that the size of the ciphertext is the same as the plaintext. This is a characteristic of a block cipher. Note that if you compile and run this example, you will not get the same ciphertext as I got due to the fact that the example uses a randomly generated key.

Our next example demonstrates the Triple-DES mode. For some reason a lot of people are unaware that FIPS-46 actually specifies two modes for Triple-DES:

  • EDE (Encrypt-Decrypt-Encrypt) where ciphertext = Ek3(Dk2(Ek1(plaintext)))
  • EEE (Encrypt-Encrypt-Encrypt) where ciphertext = Ek3(Ek2(Ek1(pliantext)))

where Ek and Dk denote DES encryption and decryption respectively. In addition, ANSI X9.52 defines three key options for Triple-DES:

  • k1 != k2 != k3
  • k1 != k2, k1 = k3, k2 != k3
  • k1 = k2 = k3

The third option makes Triple-DES backwardly compatible with DES. The recommended usage mode, per FIPS-46, for Triple-DES is EEE or EDE with three independently generated keys, i.e. 168 key bits in total. OpenSSL uses the EDE mode.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/des.h>
#include <openssl/rand.h>

#define BUFSIZE 1024

int main(void)
{
    unsigned char in[BUFSIZE], out[BUFSIZE], back[BUFSIZE];
    unsigned char *e = out;
    int i;

    DES_cblock key1, key2, key3;
    DES_cblock seed = {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10};
    DES_key_schedule ks1, ks2, ks3;

    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    memset(back, 0, sizeof(back));

    RAND_seed(seed, sizeof(DES_cblock));

    DES_random_key(&key1);
    DES_random_key(&key2);
    DES_random_key(&key3);

    DES_set_key((C_Block *)key1, &ks1);
    DES_set_key((C_Block *)key2, &ks2);
    DES_set_key((C_Block *)key3, &ks3);

    /* 64 bytes of plaintext */
    strcpy(in, "Now is the time for all men to stand up and be counted");

    printf("Plaintext: [%s]\n", in);

    for (i = 0; i < 63; i += 8) {
        DES_ecb3_encrypt((C_Block *)(in + i),(C_Block *)(out + i), &ks1, &ks2, &ks3, DES_ENCRYPT);
    }

    printf("Ciphertext:");
    while (e++) printf(" [%02x]", *e++);
    printf("\n");

    for (i = 0; i < 63; i += 8) {
        DES_ecb3_encrypt((C_Block *)(out + i),(C_Block *)(back + i), &ks1, &ks2, &ks3, DES_DECRYPT);
    }

    printf("Decrypted Text: [%s]\n", back);

    exit(0);
}


As you can see, this example uses three different keys (k1 != k2 != k3) which is the recommended way of using Triple-DES. Here is the output when this example is compiled and executed:

[fpm@ultra ~]$ ./example2
Plaintext: [Now is the time for all men to stand up and be counted]
Ciphertext: [b4] [31] [40] [aa] [41] [7d] [fc] [72] [4b] [f7] [46] [b5] [24] [83] [95] [03] [38] [1a] [50] [4e] [65] [5e] [83] [19] [b4] [0f] [74] [8b] [0c] [de] [5f] [34] [bf] [ee] [65] [4a] [b1] [0d] [33] [b4] [db] [cd] [02] [2c] [6c] [39] [7e] [57] [0d] [99] [18] [69] [23] [56] [fb] [00]
Decrypted Text: [Now is the time for all men to stand up and be counted]
[fpm@ultra ~]$ 


The next example demonstrates the Cipher Block Chaining mode. In this mode each block is XOR-ed with the previous cipherblock before encryption.

Because changes in the plaintext propagate forever in the ciphertext, encryption cannot be parallelized. For the first block we start with an initiallization vector (ivec). Note that it is not unusual to start with a zero vector as the initialization vector. Note that there is both a DES_cbc_encrypt() and a DES_ncbc_encrypt() in libcrypto. I recommend you only use the ncbc version (n stands for new). See the BUGS section of the OpenSSL DES manpage and the source code for these functions.

 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/des.h>
#include <openssl/rand.h>

#define BUFSIZE 512 

int main(void)
{
    unsigned char in[BUFSIZE], out[BUFSIZE], back[BUFSIZE];
    unsigned char *e = out;
    int len;

    DES_cblock key;
    DES_cblock seed = {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10};
    DES_cblock ivsetup = {0xE1, 0xE2, 0xE3, 0xD4, 0xD5, 0xC6, 0xC7, 0xA8};
    DES_key_schedule keysched;
    DES_cblock ivec;

    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    memset(back, 0, sizeof(back));

    RAND_seed(seed, sizeof(DES_cblock));

    DES_random_key(&key);
    DES_set_odd_parity(&key);
    if (DES_set_key_checked((C_Block *)key, &keysched))
    {
        fprintf(stderr, "ERROR: Unable to set key schedule\n");
        exit(1);
    }

    /* 64 bytes of plaintext */
    strcpy(in, "Now is the time for all men to stand up and be counted");

    printf("Plaintext: [%s]\n", in);

    len = strlen(in);
    memcpy(ivec, ivsetup, sizeof(ivsetup));
    DES_ncbc_encrypt(in, out, len, &keysched, &ivec, DES_ENCRYPT);

    printf("Ciphertext:");
    while (*e) printf(" [%02x]", *e++);
    printf("\n");

    memcpy(ivec, ivsetup, sizeof(ivsetup));
    DES_ncbc_encrypt(out, back, len, &keysched, &ivec, DES_DECRYPT);

    printf("Decrypted Text: [%s]\n", back);

    exit(0);
}


Note that I reinitialize ivec before decrypting the ciphertext. If you do not reinitialize ivec your decrypted text will be incorrect.

The next example uses DES_ede3_ncbc_encrypt() to implement outer triple CBC DES encryption with three keys. This means that each DES operation inside the CBC mode is C=E(ks3,D(ks2,E(ks1,M))). This is the mode is used by SSL.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/des.h>
#include <openssl/rand.h>

#define BUFSIZE 512 

int main(void)
{
    unsigned char in[BUFSIZE], out[BUFSIZE], back[BUFSIZE];
    unsigned char *e = out;
    int len;

    DES_cblock key1, key2, key3;
    DES_cblock seed = {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10};
    DES_cblock ivsetup = {0xE1, 0xE2, 0xE3, 0xD4, 0xD5, 0xC6, 0xC7, 0xA8};
    DES_cblock ivec;
    DES_key_schedule ks1, ks2, ks3;

    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    memset(back, 0, sizeof(back));

    RAND_seed(seed, sizeof(DES_cblock));

    DES_random_key(&key1);
    DES_random_key(&key2);
    DES_random_key(&key3);

    DES_set_key((C_Block *)key1, &ks1);
    DES_set_key((C_Block *)key2, &ks2);
    DES_set_key((C_Block *)key3, &ks3);

    /* 64 bytes of plaintext */
    strcpy(in, "Now is the time for all men to stand up and be counted");

    printf("Plaintext: [%s]\n", in);

    len = strlen(in);
    memcpy(ivec, ivsetup, sizeof(ivsetup));
    DES_ede3_cbc_encrypt(in, out, len, &ks1, &ks2, &ks3, &ivec, DES_ENCRYPT);

    printf("Ciphertext:");
    while (*e) printf(" [%02x]", *e++);
    printf("\n");

    len = strlen(out);
    memcpy(ivec, ivsetup, sizeof(ivsetup));
    DES_ede3_cbc_encrypt(out, back, len, &ks1, &ks2, &ks3, &ivec, DES_DECRYPT);

    printf("Decrypted Text: [%s]\n", back);

    exit(0);
}


Note the need to reinitialize ivec before decrypting the ciphertext. If you do not do this, the first 64 bits of the resultant plaintext will be incorrect.

The next example shows the use of a modified form of the CBC mode called Triple DES Cipher Block Chaining with Output Feedback Masking. This mode provides stronger protection against dictionary attacks and matching ciphertext attacks that exploit the DES blocksize of 64 bits through the introduction of secret masking values that are XOR-ed with the intermediate outputs of each triple-DES encryption operation. Apparantly, however, Eli Biham and Lars Knudsen have developed an attack on this mode but it requires a lot of work.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/des.h>
#include <openssl/rand.h>

#define BUFSIZE 512 
#define CBCM_ONE

int main(void)
{
    unsigned char in[BUFSIZE], out[BUFSIZE], back[BUFSIZE];
    unsigned char *e = out;
    int len;

    DES_cblock key1, key2, key3;
    DES_cblock seed = {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10};
    DES_cblock ivecstr = {0xE1, 0xE2, 0xE3, 0xD4, 0xD5, 0xC6, 0xC7, 0xA8};
    DES_cblock ivec2, ivec1;
    DES_key_schedule ks1, ks2, ks3;

    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    memset(back, 0, sizeof(back));

    RAND_seed(seed, sizeof(DES_cblock));

    DES_random_key(&key1);
    DES_random_key(&key2);
    DES_random_key(&key3);

    DES_set_key((C_Block *)key1, &ks1);
    DES_set_key((C_Block *)key2, &ks2);
    DES_set_key((C_Block *)key3, &ks3);

    /* 64 bytes of plaintext */
    strcpy(in, "Now is the time for all men to stand up and be counted");

    printf("Plaintext: [%s]\n", in);

    memcpy(ivec2, ivecstr, sizeof(ivecstr));
    memset(ivec1,'\0',sizeof(ivec2));
    len = strlen(in) + 1;

#ifdef CBCM_ONE
    DES_ede3_cbcm_encrypt(in, out, len, &ks1, &ks2, &ks3, &ivec2, &ivec1, DES_ENCRYPT);
#else
    DES_ede3_cbcm_encrypt(in, out, 16, &ks1, &ks2, &ks3, &ivec2, &ivec1, DES_ENCRYPT);
    DES_ede3_cbcm_encrypt(&in[16], &out[16],len-16, &ks1, &ks2, &ks3, &ivec2, &ivec1, DES_ENCRYPT);
#endif

    printf("Ciphertext:");
    while (*e) printf(" [%02x]", *e+);
    printf("\n");

    len = strlen(out) + 1;
    memcpy(ivec2, ivecstr, sizeof(ivecstr));
    memset(ivec1,'\0',sizeof(ivec2));

    DES_ede3_cbcm_encrypt(out, back, len, &ks1, &ks2, &ks3, &ivec2, &ivec1, DES_DECRYPT);

    printf("Decrypted Text: [%s]\n", back);

    exit(0);


The above example showns you two variations for encrypting the plaintext and vice-versa. The two-step approach is useful in some situations.

The next example shows the use of the Cipher Feedback (CFB) mode. CFB is a close relative of CBC but is a self-synchronizing stream cipher if the 1-bit mode is selected.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/des.h>
#include <openssl/rand.h>

#define BUFSIZE 256 
#define CFBMODE 1

int main(void)
{
    unsigned char in[BUFSIZE], out[BUFSIZE], back[BUFSIZE];
    unsigned char *e = out;
    int len;

    DES_cblock key;
    DES_cblock seed = {0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10};
    DES_cblock ivecstr = {0xE1, 0xE2, 0xE3, 0xD4, 0xD5, 0xC6, 0xC7, 0xA8};
    DES_cblock ivec;
    DES_key_schedule ks;

    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    memset(back, 0, sizeof(back));

    RAND_seed(seed, sizeof(DES_cblock));

    DES_random_key(&key);
    DES_set_key((C_Block *)key, &ks);

    /* 11 bytes of plaintext */
    strcpy(in, "Philippines");

    printf("Plaintext: [%s]\n", in);

    memcpy(ivec, ivecstr, sizeof(ivecstr));
    len = strlen(in);

    DES_cfb_encrypt(in, out, CFBMODE, len, &ks, &ivec, DES_ENCRYPT);

    printf("Ciphertext:");
    while (*e) printf(" [%02x]", *e+);
    printf("\n");

    len = strlen(out);
    memcpy(ivec, ivecstr, sizeof(ivecstr));

    DES_cfb_encrypt(out, back, CFBMODE, len, &ks, &ivec, DES_DECRYPT);

    printf("Decrypted Text: [%s]\n", back);

    exit(0);
}


Here is an example which uses the CBF64 mode and also, for the first time, uses ASCII strings for the initialization vector and DES key.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/des.h>

#define BUFSIZE 256 

int main(void)
{
    unsigned char in[BUFSIZE], out[BUFSIZE], back[BUFSIZE];
    unsigned char *e = out;
    int len;
    int n = 0;

    static char *keystr = "0123456789abcdef";
    static char *ivecstr = "0123456789abcdef";

    DES_cblock ivec;
    DES_key_schedule ks;

    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    memset(back, 0, sizeof(back));

    strcpy(in,"Now is the time for all.");
    DES_set_key((C_Block *)keystr, &ks);

    printf("Plaintext: [%s]\n", in);

    memcpy(ivec, (C_Block *)ivecstr, sizeof(ivec));
    len = strlen(in) + 1;

    DES_cfb64_encrypt(in, out, len, &ks, &ivec, &n, DES_ENCRYPT);

    printf("Ciphertext:");
    while (*e) printf(" [%02x]", *e++);
    printf("\n");

    memcpy(ivec, (C_Block *)ivecstr, sizeof(ivec));

    DES_cfb64_encrypt(out, back, len, &ks, &ivec, &n, DES_DECRYPT);

    printf("Decrypted Text: [%s]\n", back);

    exit(0);
}


Note the use of DES_string_to_key() to convert a string into a key. The inputted string should be at least 16 characters in length.DES_string_to_key() sets odd parity so there is no need to invoke DES_set_odd_parity().

The next example demonstrates the use of 8-bit OFB mode. This mode is an additive stream cipher in which errors in the ciphertext are not extended to cause additional errors in the decrypted plaintext. Thus a single bit in error in the ciphertext causes only one bit to be in error in the decrypted plaintext. According to the OpenSSL man page, this mode should only be used for small sizes of plaintext. From experimenting with numbits and the BUGS section of the DES manpage, I suggest you always use a value of 8 for numbits.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/des.h>
#include <openssl/rand.h>

#define BUFSIZE 256 

int main(void)
{
    unsigned char in[BUFSIZE], out[BUFSIZE], back[BUFSIZE];
    unsigned char *e = out;
    char *keystr = "Philippines06235";
    int len, n, result;

    DES_cblock key;
    DES_cblock ivecstr = {0xE1, 0xE2, 0xE3, 0xD4, 0xD5, 0xC6, 0xC7, 0xA8};
    DES_cblock ivec;
    DES_key_schedule ks;

    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    memset(back, 0, sizeof(back));

    DES_string_to_key(keystr, &key);

    if ((result = DES_set_key_checked((C_Block *)key, &ks)) != 0) {
        if (result == -1) {
            printf("ERROR: key parity is incorrect\n");
        } else {
            printf("ERROR: weak or semi-weak key\n");
        }
        exit(1);
    }

    strcpy(in,"The Chocolate Hills of Bohol are wonderful.");

    printf("Plaintext: [%s]\n", in);

    memcpy(ivec, ivecstr, sizeof(ivecstr));
    len = strlen(in);
    printf("Plaintext Length: %d\n", len);

    DES_ofb_encrypt(in, out, 8, len, &ks, &ivec);

    n = 0;
    printf("Ciphertext:");
    while (*e) {
        printf(" [%02x]", *e++);
        n++;
    }
    printf("\n");
    printf("Ciphertext Length: %d\n", n);

    len = strlen(out);
    memcpy(ivec, ivecstr, sizeof(ivecstr));

    DES_ofb_encrypt(out, back, 8, len, &ks, &ivec);

    printf("Decrypted Text: [%s]\n", back);

    exit(0);
}


The final example in this post uses ede3_ofb64_encrypt() to perform the encryption and decryption. It also reads in the plaintext from an external file. To simplify things and shorten the example, I set k1 = k2 = k3. I will leave it up to you to modify the example to support the case where k1 != k2 != k3.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/des.h>

#define BUFSIZE 1024 

int main(int argc, char *argv[])
{
    unsigned char in[BUFSIZE], out[BUFSIZE], back[BUFSIZE];
    char buf[201];
    char *keystr = "Victoria Harbour";
    unsigned char *e = out;
    FILE *fin;
    int i, num, len, result;
    int n = 0;

    DES_cblock key;
    DES_cblock ivsetup = {0xE1, 0xE2, 0xE3, 0xD4, 0xD5, 0xC6, 0xC7, 0xA8};
    DES_key_schedule ks;
    DES_cblock ivec;

    if (argc != 2) {
        printf("ERROR: plaintext filename required\n");
        exit(1);
    }

    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    memset(back, 0, sizeof(back));

    DES_string_to_key(keystr, &key);

    if ((result = DES_set_key_checked((C_Block *)key, &ks)) != 0) {
        if (result == -1) {
            printf("ERROR: key parity is incorrect\n");
        } else {
            printf("ERROR: weak or semi-weak key\n");
        }
        exit(1);
    }

    fin = fopen(argv[1], "r");
    if (!fin) {
        printf(" ERROR: opening input file\n");
        exit(1);
    }
    while(fgets(buf, 200, fin) != NULL) {
        strcat(in, buf);
    }
    fclose(fin);

    printf("Plaintext: [%s]\n", in);
    len = strlen(in);
    printf("Plaintext Length: %d\n", len);

    memcpy(ivec, ivsetup, sizeof(ivsetup));
    num = 0;
    for (i = 0; i < len; i++) {
        DES_ede3_ofb64_encrypt(&(in[i]), &(out[i]), 1, &ks, &ks, &ks, &ivec, &num);
    }

    n = 0;
    printf("Ciphertext:");
    while (*e) {
        printf(" [%02x]", *e++);
        n++;
    }
    printf("\n");
    printf("Ciphertext Length: %d\n", n);

    memcpy(ivec, ivsetup, sizeof(ivsetup));
    num = 0;
    for (i = 0; i < len; i++) {
        DES_ede3_ofb64_encrypt(&(out[i]), &(back[i]), 1, &ks, &ks, &ks, &ivec, &num);
    }

    printf("Decrypted Text: [%s]\n", back);

    exit(0);
}


Here is sample output:

$ echo -n "Now is the time to finish this post" > plaintext
$ ./example9 plaintext
Plaintext: [Now is the time to finish this post]
Plaintext Length: 35
Ciphertext: [92] [cb] [29] [fe] [aa] [94] [d7] [ba] [07] [a5] [8f] [78] [4f] [13] [fa] [c4] [4f] [22] [0d] [fd] [b7] [33] [81] [3e] [3a] [e4] [f5] [c6] [52] [c9] [2b] [4f] [d5] [b8] [00]
Ciphertext Length: 35
Decrypted Text: [Now is the time to finish this post]
$

Well, I think I have covered the OpenSSL v1.0 DES routines in sufficient detail for most readers. There are a number of other routines but these are infrequently used or are slight variations on the routines used in the above examples. You may come across many references to DES routines which start with des_. These are essentially the same as the DES_ routines but are from older versions of libcrypto. The move to DES_ occurred several years ago. You should always use the DES_ version of a routine if it is available.

Armed with your new knowledge, you should now be able to go away and use DES within your applications. Feel free to use any source code included in this post. If you want to learn more about DES, Douglas Stinson does an excellent job in Chapter 3 of his book Crytography Theory and Practice (ISBN 0-8493-8521-0). Another excellent book, with lots of C source code, is Bruce Schneir’s Applied Cryptography (ISBN 0-471-59756-2). Unfortunately, currently the OpenSSL man pages are poor at best and downright inaccurate at worst so you may frequently find yourself examining the libcrypto source code. All of the relevant DES code is in the ../crypto/des subdirectory.

Enjoy!
 

10 comments to OpenSSL DES APIs

  • nguyenthaithuan

    Dear,

    It is excited and beautiful code. You presented and illustrated clearly, the thing help many men can use OpenSLL.
    Thank you so much!!!

    Nguyen Thai Thuan.
    Sai Gon
    Viet Nam

  • Ivan

    Hi,
    Thank you for this nice post.
    I also have a question!
    What might cause that above crypto funcs(e.g DES_ecb3_encrypt) dont return the same result on the different OS platforms ,
    like Solaris (SunOS 5.10) , Linux 32,64 or perhaps Ubuntu or Centos etc?

    Ok, let’s say OS doesn’t matter at all , so logical assumption is :

    1. openssl version.(??, but what about the whole internet , very low probability for this one.. )
    2. some initialization issues
    (Before a DES key can be used, it must be converted into the architecture dependent
    DES_key_schedule via the DES_set_key_checked() or DES_set_key_unchecked() function. )

    I’m very confused about it.

    I use DES_ecb3_encrypt and DES_set_key_unchecked() with the same k1,k2,k3 , same C code.

    Best regards ,

    Ivan

  • Ivan

    Hi again ,

    Ofcourse , I am aware that crypt alg/modes other than DES_ecb3_encrypt
    never (if properly used) generate the same cypher result.

    Ivan.

  • Ivan

    Hi ,

    Problem solved :).
    No doubt about cryptographic libraries interoperability :).
    ofcourse the nature of problem was human :))).
    Misuse of DES_set_key_unchecked method
    caused wrong enc/dec results.

    Best regards,

    Ivan

  • Larry

    I needed a quick tutorial on DES and 3DES routines in openssl and your blog was clearly written, well organized and provided just the right level of detail. Thank you very much.

    Your programs, however, could use a bit of help.
    Considering only the first program (des.c).

    The variable e is misused,
    the byte 0 of the encrypted block isn’t printed
    a trailing NUL is always printed
    and there is no reason to expect that a NUL
    will not be part of the encrypted block.

    The data type DES_Cblock is used to declare the keys, but in the calls to DES_ecb_encrypt, the variables are cast to (C_Block *). C_block is apparently an older form of DES_Cblock.

    The buffers in, out, and back are allocated to 64 bytes which I found confusing, since DES works with 64 bit blocks.

    I see that someone else already commented on the use of DES_set_key rather than the DES_set_key_checked and the checking of its return values.

    I don’t understand the use of the random number generator. The program defines 8 bytes to use as a random number seed and then gets an 8 byte key from DES_random_key. After seeding a random number generator, the returned value shouldn’t be random. (I did read the comments about the prng following the code, I just don’t understand the intent)

  • Larry,

    Good catch on the while (*e++) loop. Fixed. That is what can happen when you tidy up code for publication late at night without testing it fully again!

    I assume DES_Cblock was a typo on your part and you really meant to say DES_cblock. DES_cblock is the latest incarnation of was was previously known as des_cblock and prior to that C_Block. Any one of these is still valid. I am just used to using C_Block.

    BUFSIZE refers to the in, out and back buffers used to store the input text, encrypted text and unencrypted text. The string “HillTown” takes 9 bytes including NULL so for the purposes of example 1, I could have reduced BUFSIZE to 9. Nothing to do with DES internals.

    Regarding the use of DES_set_key() rather than the DES_set_key_checkedi() and the checking of its return values, both are used in examples in this post. If your assertion is that I should always have used DES_set_key_checked, my response is simple – why then is there a DES_set_key API? There are legitimate reasons to use either API.

  • Zack

    Excellent write-up and great examples. This really helped me as the official OpenSSL suite lacks any kind of documentation such as this. I was able to easily implement my task after reviewing. Thank you.

  • yuna

    hi ..
    please help me. my code :

    int newTextLength = newText.length();///// KEY no.2 -> KEY no.1
    for (int i = 0; i < lenOrgTxt ; i += 8)
    {
    DES_ecb3_encrypt((DES_cblock *)(unsignedCharTextForEncrypt+i),(DES_cblock*(encryDES+i),
    &keySched1 , &keySched2 , &keySched1 , DES_ENCRYPT);
    }

    when my text length is less than or equal to 120. Encrypt/Decrypting process is working right.
    but when my text length is greater than 120 (128 or .. 256) ! after encrypting process, length of the encryDES is always 36 and Decrypting process is wrong!

    should my text in this method be 120 ? has input text in this method any limitation?

  • Kedar Sabnis

    Hi,

    i am using DES_ecb3_encrypt to encrypt and decrypt plain text passwords.
    It works fine with some passwords.

    but with password “Imate@005” when i encrypt it with Key1 = [MATRIXSE], Key2 = [ ] , Key3 = [ ] and decypt again it doesn’t give me “Imate@005” , it gives me junk value.

    pls help.

    Following is my code :

    int TDES_Decrypt(char *sCipher, char *sKey, char *sPlainText)
    {
    fplog = fopen(“3DES1.log”,”a”);
    fprintf(fplog,”\n\n\n……………… Inside TDES_Decrypt ……………………..\n”);
    fprintf(fplog,”\n Encrypted Password from Front End :: [%s] :: [%d]…… “,sCipher,strlen(sCipher));
    fprintf(fplog,”\n Keys form Front End :: [%s] :: [%d]…….”,sKey,strlen(sKey));

    int iRetVal=0;
    char SessionKeyFE [KEY_LEN_SESSION];

    DES_cblock key1;
    DES_cblock key2;
    DES_cblock key3;
    DES_cblock inBuffer;
    DES_cblock tempBuffer;

    DES_key_schedule *ks1;
    DES_key_schedule *ks2;
    DES_key_schedule *ks3;

    memcpy(SessionKeyFE, sKey, KEY_LEN_SESSION);

    memset(&key1,’ ‘,DES_KEY_SZ);
    memcpy(&key1,&SessionKeyFE[0],DES_KEY_SZ);
    fprintf(fplog,”\n Key 1 from Front End :: [%s] :: [%d]”,key1,strlen(key1));

    memset(&key2,’ ‘,DES_KEY_SZ);
    memcpy(&key2,&SessionKeyFE[8],DES_KEY_SZ);
    fprintf(fplog,”\n Key 2 from Front End :: [%s] :: [%d]……”,key2,strlen(key2));

    memset(&key3,’ ‘,DES_KEY_SZ);
    memcpy(&key3,&SessionKeyFE[16],DES_KEY_SZ);
    fprintf(fplog,”\n Key 3 from Front End :: [%s] :: [%d]”,key3,strlen(key3));

    ks1=(void *)malloc(DES_SCHEDULE_SZ);
    DES_set_key_unchecked(key1,ks1);

    ks2=(void *)malloc(DES_SCHEDULE_SZ);
    DES_set_key_unchecked(key2,ks2);

    ks3=(void *)malloc(DES_SCHEDULE_SZ);
    DES_set_key_unchecked(key3,ks3);

    memset(&inBuffer,’ ‘,DES_KEY_SZ);
    memset(&tempBuffer,’ ‘,DES_KEY_SZ);

    fprintf(fplog,”\n\n DES_KEY_SZ = [%d] \n”,DES_KEY_SZ);

    memcpy(&inBuffer,sCipher,DES_KEY_SZ);
    fprintf(fplog,”\n\n…………value of inBuffer is :: [%s] :: ..[%d]……”,inBuffer,strlen(inBuffer));
    DES_ecb3_encrypt(&inBuffer,&tempBuffer,ks1,ks2,ks3,DES_DECRYPT);
    memcpy(sPlainText,&tempBuffer,DES_KEY_SZ);
    fprintf(fplog,”\n______ Part 1 of Decrypted Password :: [%s] :: [%d]….”,sPlainText,strlen(sPlainText));

    memset(&inBuffer,’ ‘,DES_KEY_SZ+1);
    memset(&tempBuffer,’ ‘,DES_KEY_SZ+1);

    memcpy(&inBuffer,sCipher + 8,DES_KEY_SZ);
    fprintf(fplog,”\n\n…………value of inBuffer is :: [%s] :: ..[%d]……”,inBuffer,strlen(inBuffer));
    DES_ecb3_encrypt(&inBuffer,&tempBuffer,ks1,ks2,ks3,DES_DECRYPT);
    memcpy(sPlainText + 8,&tempBuffer,DES_KEY_SZ);
    fprintf(fplog,”\n______ Part 2 of Decrypted Password :: [%s] :: [%d]….”,tempBuffer,strlen(tempBuffer));
    fprintf(fplog,”\n______ Final Plain Text Password :: [%s] :: [%d]….”,sPlainText,strlen(sPlainText));

    free(ks1);free(ks2);free(ks3);
    fclose(fplog);
    return iRetVal;
    }