Steps to decrypt encrypted data in Crypto++ in libgcrypt

六月ゝ 毕业季﹏ 提交于 2019-12-11 01:59:30

问题


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.


回答1:


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!



回答2:


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 };



回答3:


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


来源:https://stackoverflow.com/questions/28756446/steps-to-decrypt-encrypted-data-in-crypto-in-libgcrypt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!