2

I need to decrypt the encrypted data by Crypto++ in libgcrypt due to C language restriction on target platform. So I've decided to use libgcrypt since it supporting the AES128 and GCM mode.

In Crypto++, the data is encrypted this way:

std::string encrypt_data(const std::string &data,
                         const std::vector<unsigned char> &iv,
                         const std::vector<unsigned char> &key)
{
    CryptoPP::GCM<CryptoPP::AES>::Encryption encryptor;
    encryptor.SetKeyWithIV(&key[0], key.size(), &iv[0]);

    std::string ciphertext;
    CryptoPP::StringSource ss( data, true,
                            new CryptoPP::AuthenticatedEncryptionFilter(
                                encryptor,
                                new CryptoPP::StringSink(ciphertext)
                                )
                            );

    return ciphertext;
}

and successfully decrypted this way:

std::string decrypt_data(const std::string &data,
                         const std::vector<unsigned char> &iv,
                         const std::vector<unsigned char> &key)
{
    CryptoPP::GCM<CryptoPP::AES>::Decryption decryptor;
    decryptor.SetKeyWithIV(&key[0], key.size(), &iv[0]);

    std::string recovered;
    CryptoPP::StringSource ss( data, true,
                            new CryptoPP::AuthenticatedDecryptionFilter(
                                decryptor,
                                new CryptoPP::StringSink( recovered )
                                )
                            );

    return recovered;
}                       

But the decoded data is wrong when I try to decode ciphertext using libgcrypt by these steps:

  1. gcry_cipher_open()
  2. gcry_cipher_setkey()
  3. gcry_cipher_setiv()
  4. Seperate the cipher text and authentication tag
  5. gcry_cipher_decrypt(cipher text)
  6. gcry_cipher_checktag(authentication tag)

Is there any steps I missed to replicate the Crypto++ decoding process?

Gcrypt decryption code (Expected output Decrypted cipher = password):

#include <stdio.h>
#include <stdlib.h>
#include <gcrypt.h>

static unsigned char const aesSymKey[] = { 0x38, 0xb4, 0x8f, 0x1f, 0xcd, 0x63, 0xef, 0x32, 0xc5, 0xd1, 0x3f, 0x52, 0xbc, 0x4f, 0x5b, 0x24 };

static unsigned char const aesIV[] = { 0xE4, 0xEF, 0xC8, 0x08, 0xEB, 0xB8, 0x69, 0x95, 0xF3, 0x44, 0x6C, 0xE9, 0x15, 0xE4, 0x99, 0x7E };

static unsigned char const aesPass[] = { 0xda, 0x84, 0x3f, 0x01, 0xa0, 0x14, 0xfd, 0x85 };

static unsigned char const aesTag[] = { 0xdf, 0x5f, 0x9f, 0xe2, 0x9d, 0x7e, 0xc3, 0xdf, 0x7a, 0x1e, 0x59, 0xd8, 0xe6, 0x61, 0xf7, 0x7e };

#define GCRY_CIPHER GCRY_CIPHER_AES128
#define GCRY_MODE GCRY_CIPHER_MODE_GCM

int main(){
    gcry_error_t     gcryError;
    gcry_cipher_hd_t gcryCipherHd;

    if (!gcry_check_version(GCRYPT_VERSION))
     {
       fputs("libgcrypt version mismatch\n", stderr);
       exit(2);
     }

    gcry_control(GCRYCTL_DISABLE_SECMEM, 0);

    gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);

    if(!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
    {
        fputs("libgcrypt has not been initialized\n", stderr);
        abort();
    }

    size_t keyLength = gcry_cipher_get_algo_keylen(GCRY_CIPHER);
    size_t blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER);

    char * outBuffer = malloc(blkLength);

    gcryError = gcry_cipher_open(
        &gcryCipherHd, // gcry_cipher_hd_t *
        GCRY_CIPHER,   // int
        GCRY_MODE,     // int
        0);            // unsigned int
    if (gcryError)
    {
        printf("gcry_cipher_open failed:  %s/%s\n",
               gcry_strsource(gcryError),
               gcry_strerror(gcryError));
        return;
    }

    gcryError = gcry_cipher_setkey(gcryCipherHd, aesSymKey, keyLength);
    if (gcryError)
    {
        printf("gcry_cipher_setkey failed:  %s/%s\n",
               gcry_strsource(gcryError),
               gcry_strerror(gcryError));
        return;
    }

    gcryError = gcry_cipher_setiv(gcryCipherHd, aesIV, blkLength);
    if (gcryError)
    {
        printf("gcry_cipher_setiv failed:  %s/%s\n",
               gcry_strsource(gcryError),
               gcry_strerror(gcryError));
        return;
    }

    gcryError = gcry_cipher_decrypt(
        gcryCipherHd, // gcry_cipher_hd_t
        outBuffer,    // void *
        blkLength,    // size_t
        aesPass,      // const void *
        8);           // size_t
    if (gcryError)
    {
        printf("gcry_cipher_decrypt failed:  %s/%s\n",
               gcry_strsource(gcryError),
               gcry_strerror(gcryError));
        return;
    }

    gcryError = gcry_cipher_checktag(
        gcryCipherHd,
        aesTag,
        blkLength);
    if (gcryError)
    {
        printf("gcry_cipher_checktag failed:  %s/%s\n",
               gcry_strsource(gcryError),
               gcry_strerror(gcryError));
        return;
    }

    printf("Decrypted cipher = %s\n", outBuffer);

    // clean up after ourselves
    gcry_cipher_close(gcryCipherHd);
    free(outBuffer);

    return 0;
}

EDIT: Just to be clear, the steps to decrypt I'm searching for is for the ciphertext output of the Crypto++ encryption function shown above; the encrypt_data(). So I won't accept any answer where it can't be applied to successfully decrypt ciphertext.

smhaziq
  • 71
  • 1
  • 9
  • What I understand from [AuthenticatedEncryptionFilter document in Crypto++ wiki](http://www.cryptopp.com/wiki/AuthenticatedEncryptionFilter), the filter output is a concatenation of both encrypted data and 128-bit tag. So I try to decrypt only the data part without the tag, but still getting wrong decoded result. Is my understanding regarding the filter output is correct? – smhaziq Feb 27 '15 at 03:45
  • Thanks for taking your time to solve this. I'm using libgcrypt version 1.6.2. I'm cross compiling though so I get to use the latest library (since I'm compiling from source). I think it should be implemented in 1.6.0 and above. try `libgcrypt20-dev` – smhaziq Mar 03 '15 at 03:56
  • I think the key to confirm the steps is by understanding the steps in the [GCM docs](http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf). `Section 7.2 Algorithm for the Authenticated Decryption Function` might hold the answer, but I'm still newbie in cryptography. Hope someone can help to translate. – smhaziq Mar 03 '15 at 07:55
  • *"So I won't accept any answer where it can't be applied to successfully decrypt ciphertext"* - Originally, you asked for the steps you had to perform to accomplish your goal. You were given examples of how to do it that worked. Now it seems you want to change the question (and you don't provide all the code needed to answer your question). Good luck with it. – jww Mar 06 '15 at 05:37
  • Yes, your answer is accurate based on my brief title. But I did show the decryption function of Crypto++ used in my code and the goal is to replicate that particular decryption implementation which I thought is clear when I say "_Is there any steps I missed to replicate the Crypto++ decoding process?_". Isn't the "_the_" in my question referring to my example? I'm sorry that you misunderstood my goal (that's why I add the edit after realizing that). – smhaziq Mar 06 '15 at 07:40

3 Answers3

0

Part 1 of 2 for the answer. This is the Crypto++ encryptor. It also prints the parameters it operates upon.

If you turn knobs on the AE or AAD preprocessor macro, then you will need to generate new parameters for the Gcrypt decryption routine.

// g++ -g3 -O1 -Wall -Wextra gcm-cryptopp-encrypt.cpp /usr/local/lib/libcryptopp.a -o gcm-cryptopp-encrypt.exe

#include <iostream>
using std::cout;
using std::endl;

#include <string>
using std::string;

#include <cryptopp/cryptlib.h>
using CryptoPP::DEFAULT_CHANNEL;
using CryptoPP::AAD_CHANNEL;

#include <cryptopp/osrng.h>
using CryptoPP::OS_GenerateRandomBlock;

#include <cryptopp/aes.h>
using CryptoPP::AES;

#include <cryptopp/gcm.h>
using CryptoPP::GCM;

#include <cryptopp/secblock.h>
using CryptoPP::SecByteBlock;

#include <cryptopp/hex.h>
using CryptoPP::HexEncoder;

#include <cryptopp/filters.h>
using CryptoPP::StringSink;
using CryptoPP::AuthenticatedEncryptionFilter;

#define UNUSED(x) ((void)x)

#define AE 1
#define AAD 1

int main(int argc, char* argv[])
{
    UNUSED(argc); UNUSED(argv);

    string hexPre = " { 0x", hexPost = " };";
    string plain = "Now is the time for all good men to come to the aide of the country.";
    string aad = "Attack at dawn!";

    HexEncoder hex(NULL, true, 2, ",0x");
    size_t res = 0;

    SecByteBlock key(AES::DEFAULT_KEYLENGTH), iv(AES::BLOCKSIZE);
    static const size_t TAG_SIZE = AES::BLOCKSIZE;

    // Generate random key and iv
    OS_GenerateRandomBlock(false, key, key.size());
    OS_GenerateRandomBlock(false, iv, iv.size());

    string s1(hexPre), s2(hexPre);

    hex.Detach(new StringSink(s1));
    hex.Put(key, key.size());
    hex.MessageEnd();
    s1 += hexPost;

    hex.Detach(new StringSink(s2));
    hex.Put(iv, iv.size());
    hex.MessageEnd();
    s2 += hexPost;

    cout << "const byte key[] = " << s1 << endl;
    cout << "const byte iv[] = " << s2 << endl;

    /////////////////////////////////////////

    string s3(hexPre), s4(hexPre);

#if defined(AE)
    hex.Detach(new StringSink(s3));
    hex.Put(reinterpret_cast<const byte*>(plain.data()), plain.size() + 1 /*NULL*/);
    hex.MessageEnd();
    s3 += hexPost;

    cout << "const byte plain[] = " << s3 << endl;
#endif

#if defined(AAD)
    hex.Detach(new StringSink(s4));
    hex.Put(reinterpret_cast<const byte*>(aad.data()), aad.size() + 1 /*NULL*/);
    hex.MessageEnd();
    s4 += hexPost;

    cout << "const byte aad[] = " << s4 << endl;
#endif    

    /////////////////////////////////////////

    GCM<AES>::Encryption encryptor;
    encryptor.SetKeyWithIV(key, key.size(), iv, iv.size());

    AuthenticatedEncryptionFilter filter(encryptor);

#if defined(AAD)
    filter.ChannelPut(AAD_CHANNEL, reinterpret_cast<const byte*>(aad.data()), aad.size() + 1 /*NULL*/);
#endif

#if defined(AE)
    filter.ChannelPut(DEFAULT_CHANNEL, reinterpret_cast<const byte*>(plain.data()), plain.size() + 1 /*NULL*/);
#endif

    filter.MessageEnd();

    res= filter.MaxRetrievable();
    SecByteBlock cipher(res - TAG_SIZE), tag(TAG_SIZE);

#if defined(AE)
    res = filter.Get(cipher, cipher.size());
    cipher.resize(res);
#endif

    res = filter.Get(tag, tag.size());
    tag.resize(res);

    /////////////////////////////////////////

    string s5(hexPre), s6(hexPre);

    hex.Detach(new StringSink(s5));
    hex.Put(cipher.data(), cipher.size());
    hex.MessageEnd();
    s5 += hexPost;

    hex.Detach(new StringSink(s6));
    hex.Put(tag.data(), tag.size());
    hex.MessageEnd();
    s6 += hexPost;

#if defined(AE)
    cout << "const byte cipher[] = " << s5 << endl;
#endif

    cout << "const byte tag[] = " << s6 << endl;

    return 0;
}

Its output will be similar to:

$ ./gcm-cryptopp-encrypt.exe
const byte key[] =  { 0xD1,0xB8,0xDC,0xB8,0xF9,0x83,0x8E,0xB8,0xE5,0x0B,0x48,0xB2,0xF5,0x1A,0x71,0x46 };
const byte iv[] =  { 0x05,0x2E,0xAF,0x03,0x23,0xFE,0xFD,0x5C,0xF5,0x90,0x7B,0xDD,0x09,0xBF,0x0A,0x71 };
const byte plain[] =  { 0x4E,0x6F,0x77,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x74,0x69,0x6D,0x65,0x20,0x66,0x6F,0x72,0x20,0x61,0x6C,0x6C,0x20,0x67,0x6F,0x6F,0x64,0x20,0x6D,0x65,0x6E,0x20,0x74,0x6F,0x20,0x63,0x6F,0x6D,0x65,0x20,0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x61,0x69,0x64,0x65,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x63,0x6F,0x75,0x6E,0x74,0x72,0x79,0x2E,0x00 };
const byte aad[] =  { 0x41,0x74,0x74,0x61,0x63,0x6B,0x20,0x61,0x74,0x20,0x64,0x61,0x77,0x6E,0x21,0x00 };
const byte cipher[] =  { 0xD0,0x6D,0x69,0x0F,0x6A,0xDE,0x61,0x81,0x42,0x5A,0xA1,0xF8,0x29,0xFE,0x70,0xCC,0xCC,0x63,0xE4,0xFE,0x8C,0x32,0x58,0xFE,0xB8,0xC1,0x0F,0x38,0xBC,0x3F,0x27,0x2F,0x51,0xC3,0xB4,0x38,0x19,0x8E,0x24,0x97,0x54,0xCA,0xE6,0xA4,0xE6,0x22,0xDA,0x85,0x02,0x17,0xFE,0x76,0x89,0x55,0x85,0xEC,0x94,0x1D,0xD8,0xB4,0x0B,0x79,0x4A,0xE1,0xD6,0x5A,0x6A,0xA4,0x9A };
const byte tag[] =  { 0xA8,0x11,0x3D,0x86,0xE8,0xCA,0x2F,0xAF,0xED,0x09,0x90,0x44,0xCD,0x48,0xC1,0x06 };
jww
  • 97,681
  • 90
  • 411
  • 885
  • @smhaziq - It should also work with *only* AAD, too. That's called a GMAC. – jww Mar 04 '15 at 02:33
  • The sample was updated to include `AE` and `AAD` preprocessor macros for your testing. You will be able to answer your own questions. – jww Mar 04 '15 at 02:50
  • @smhaziq - The `{ciphertext,tag}` are outputs. They depend on four inputs: `{key,iv,plaintext,aad}`. If any of the inputs change, then the output will change. If the inputs remain constant, then the outputs remain unchanged. – jww Mar 04 '15 at 18:05
  • I repeat the test with **fixed** key, iv, and plaintext (aad is not passed), but the outputs produced are different. Is the way I interpret the output is wrong? As you can see, my Crypto++ encryption function return the output `ciphertext` as string from `StringSink` but you use `BufferedTransformation::Get()` instead. – smhaziq Mar 05 '15 at 01:25
  • *"I repeat the test with fixed key, iv, and plaintext, but the outputs produced are different"* - then you have a bug somewhere since this is deterministic. You can verify by using the Crypto++ encryptor to generate a Key and IV, ad then hard coding the Key and IV in the Crypto++ program. It will produce the same output between runs. You might start by passing in the IV size to `SetKeyWithIV`. Or run the program under Valgirnd; or with Clang and its address sanitzer. – jww Mar 05 '15 at 20:36
0

Part 2 of 2 for the answer. This is the Gcrpyt decryptor. It consumes the parameters from Part 1.

In the code below, the call to gcry_cipher_decrypt gets the decrypted text. But I don't know how to get the size of the decrypted text from the library. It does not matter for GCM mode, but it will matter for other modes, like CBC. See this Stack Overflow question: Determine size of decrypted data from gcry_cipher_decrypt?.

The ROUNDUP is for rounding up to a multiple of the cipher's block size. I read it was a requirement for the decryption buffer at Working with Ciphers, but it may not apply here. I left it in place because "things worked", but you should knob turn on it further if it bothers you.

If you turn knobs on the AE or AAD preprocessor macro, then you will need to generate new parameters with the Crypto++ encryption routine.

/* gcc -g3 -O1 -Wall -Wextra -std=c99 gcm-gcrypt-decrypt.c /usr/local/lib/libgcrypt.a /usr/local/lib/libgpg-error.a -o gcm-gcrypt-decrypt.exe */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <gcrypt.h>

typedef unsigned char byte;

/* All of this was generated in Crypto++ */
const byte key[] =  { 0x73,0x12,0xBB,0xDB,0x86,0x73,0x65,0xF7,0x68,0x7D,0xE9,0x2B,0xF8,0xEE,0x66,0xF1 };
const byte iv[] =  { 0x8C,0x70,0x54,0x17,0xD6,0xD9,0x7B,0x18,0x39,0xDC,0x5B,0xBC,0x21,0xDF,0x30,0x74 };
const byte plain[] =  { 0x4E,0x6F,0x77,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x74,0x69,0x6D,0x65,0x20,0x66,0x6F,0x72,0x20,0x61,0x6C,0x6C,0x20,0x67,0x6F,0x6F,0x64,0x20,0x6D,0x65,0x6E,0x20,0x74,0x6F,0x20,0x63,0x6F,0x6D,0x65,0x20,0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x61,0x69,0x64,0x65,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x63,0x6F,0x75,0x6E,0x74,0x72,0x79,0x2E,0x00 };
const byte aad[] =  { 0x41,0x74,0x74,0x61,0x63,0x6B,0x20,0x61,0x74,0x20,0x64,0x61,0x77,0x6E,0x21,0x00 };
const byte cipher[] =  { 0xE8,0x0E,0xEA,0x10,0x32,0x26,0x7D,0xD1,0x75,0xF3,0x33,0x0F,0x30,0xBB,0x36,0xFB,0x3F,0x95,0x24,0x31,0x90,0xD2,0x2C,0xB1,0x34,0x5B,0x69,0x42,0x1E,0x98,0xC4,0x65,0x3B,0x06,0x5D,0x45,0xB6,0xC7,0x7E,0x26,0x7E,0xBC,0xFF,0xB7,0x7F,0xF4,0x11,0xF8,0xF3,0x8B,0x19,0x08,0xE6,0xAE,0x36,0x44,0xEF,0x3F,0xA6,0xC3,0xAE,0x34,0x08,0xB9,0x33,0xD3,0x33,0x63,0x46 };
const byte tag[] =  { 0x00,0xAE,0xDC,0x12,0x55,0xF8,0x87,0xB5,0x10,0x75,0x20,0xB5,0x94,0xCA,0x91,0xDF };

#define COUNTOF(x) ( sizeof(x) / sizeof(x[0]) )
#define ROUNDUP(x, b) ( (x) ? (((x) + (b - 1)) / b) * b : b)
byte recovered[ ROUNDUP(COUNTOF(cipher), 16) ];

#define GCRY_CIPHER GCRY_CIPHER_AES128
#define GCRY_MODE GCRY_CIPHER_MODE_GCM

#define AE 1
#define AAD 1

int main(){
    gcry_error_t     err;
    gcry_cipher_hd_t handle;
    memset(recovered, 0x00, COUNTOF(recovered));

    fprintf(stdout, "Plaintext size: %d\n", (int)COUNTOF(plain));
    fprintf(stdout, "Ciphertext size: %d\n", (int)COUNTOF(cipher));
    fprintf(stdout, "Recovered size: %d\n", (int)COUNTOF(recovered));

    assert(COUNTOF(key) == gcry_cipher_get_algo_keylen(GCRY_CIPHER));
    assert(COUNTOF(iv) == gcry_cipher_get_algo_blklen(GCRY_CIPHER));
    assert(COUNTOF(recovered) % gcry_cipher_get_algo_blklen(GCRY_CIPHER) == 0);

    if (!gcry_check_version(GCRYPT_VERSION))
     {
       fputs("libgcrypt version mismatch\n", stderr);
       exit(2);
     }

    gcry_control(GCRYCTL_DISABLE_SECMEM, 0);

    gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);

    if(!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
    {
        fputs("libgcrypt has not been initialized\n", stderr);
        abort();
    }

    err = gcry_cipher_open(
        &handle,           // gcry_cipher_hd_t *
        GCRY_CIPHER,       // int
        GCRY_MODE,         // int
        0);                // unsigned int
    if (err)
    {
        printf("gcry_cipher_open failed:  %s/%s\n",
               gcry_strsource(err),
               gcry_strerror(err));
        return 1;
    }

    err = gcry_cipher_setkey(handle, key, COUNTOF(key));
    if (err)
    {
        printf("gcry_cipher_setkey failed:  %s/%s\n",
               gcry_strsource(err),
               gcry_strerror(err));
        return 1;
    }

    err = gcry_cipher_setiv(handle, iv, COUNTOF(iv));
    if (err)
    {
        printf("gcry_cipher_setiv failed:  %s/%s\n",
               gcry_strsource(err),
               gcry_strerror(err));
        return 1;
    }

#if defined(AAD)
    err = gcry_cipher_authenticate(
        handle,         // gcry_cipher_hd_t
        aad,            // void *
        COUNTOF(aad));  // size_t
    if (err)
    {
        printf("gcry_cipher_authenticate failed:  %s/%s\n",
               gcry_strsource(err),
               gcry_strerror(err));
        return 1;
    }
#endif

#if defined(AE)
    err = gcry_cipher_decrypt(
        handle,             // gcry_cipher_hd_t
        recovered,          // void *
        COUNTOF(recovered), // size_t
        cipher,             // const void *
        COUNTOF(cipher));   // size_t
    if (err)
    {
        printf("gcry_cipher_decrypt failed:  %s/%s\n",
               gcry_strsource(err),
               gcry_strerror(err));
        return 1;
    }
#endif

    err = gcry_cipher_checktag(
        handle,
        tag,
        COUNTOF(tag));
    if (err)
    {
        printf("gcry_cipher_checktag failed:  %s/%s\n",
               gcry_strsource(err),
               gcry_strerror(err));
        return 1;
    }

#if defined(AE)
    fprintf(stdout, "Decrypted = %s\n", recovered);
#endif

#if defined(AAD)
    fprintf(stdout, "Additional data = %s\n", (char*)aad);
#endif

    gcry_cipher_close(handle);

    return 0;
}

It produces output similar to:

$ ./gcm-gcrypt-decrypt.exe
Plaintext size: 69
Ciphertext size: 69
Recovered size: 80
Decrypted = Now is the time for all good men to come to the aide of the country.
Additional data = Attack at dawn!
Community
  • 1
  • 1
jww
  • 97,681
  • 90
  • 411
  • 885
  • The sample was updated to include `AE` and `AAD` preprocessor macros for your testing. You will be able to answer your own questions. – jww Mar 04 '15 at 02:50
0

The Crpto++ encryption implementation executing this code to set the IV:

encryptor.SetKeyWithIV(&key[0], key.size(), &iv[0]);

Since the IV size is not passed, the default length is used which is 12. This is based on the recommended IV size by the specification which is 96bits.

So in order for my libgrcrypt to decode the cipher correctly, I just need to change this line:

gcryError = gcry_cipher_setiv(gcryCipherHd, aesIV, blkLength);

into this:

gcryError = gcry_cipher_setiv(gcryCipherHd, aesIV, 12);

So I'll get the expected output:

$ ./decrypt
Decrypted cipher = password
smhaziq
  • 71
  • 1
  • 9