Niemand
Niemand

Reputation: 127

InvalidCiphertext exception when decrypting ciphertext

I'm working in a new protocol for secure communication and I'm having problems to decrypt the ciphertext.

The data packet is saved in a uint8_t* variable and encrypted. Until this part is all going well. But when I try to decrypt I got the followings problems:

1) If I send the vector and the size (it's really 20 but I just want to decrypt the last 16 bytes):

CBC_Mode< AES >::Decryption decryptor;
decryptor.SetKeyWithIV( key, CryptoPP::AES::DEFAULT_KEYLENGTH, iv );

CryptoPP::StringSource ss( vector+4, 16 , true,
        new CryptoPP::StreamTransformationFilter( decryptor,
             new CryptoPP::StringSink( decryptedtext ) ) );

I get this:

terminate called after throwing an instance of 'CryptoPP::InvalidCiphertext'
  what():  StreamTransformationFilter: invalid PKCS #7 block padding found

2) If I just send the vector without size:

CryptoPP::StringSource ss( vector+4, true,
       new CryptoPP::StreamTransformationFilter( decryptor,
              new CryptoPP::StringSink( decryptedtext ) ) );

The programs runs but I just get all 00:

Text Encrypted (20 bytes)
8c 97 b7 d8 74 80 3d 9f 9f 62 2e 93 38 c7 d1 b de a4 21 80 

Text Decrypted (16 bytes)
0 0 0 0 0 0 0 0 68 0 0 0 0 0 0 0 0 0 0 0 

I read that it could be that the key is not generated correctly, but I'm working with a size of 16 and here is how I do it:

 byte key[ CryptoPP::AES::DEFAULT_KEYLENGTH ], iv[      CryptoPP::AES::BLOCKSIZE ];
 memset( key, 0x00, CryptoPP::AES::DEFAULT_KEYLENGTH );
 memset( iv, 0x00, CryptoPP::AES::BLOCKSIZE );

3) I also tried to cast the vector to char and send it like an string:

CryptoPP::StringSource ss( reinterpret_cast<const unsigned char*>( (vector + 4) ), 16, true,
        new CryptoPP::StreamTransformationFilter( decryptor,
            new CryptoPP::StringSink( decryptedtext ) ) );

But again I get the same thing:

terminate called after throwing an instance of 'CryptoPP::InvalidCiphertext'
  what():  StreamTransformationFilter: invalid PKCS #7 block padding found

Please help, I have tried for days to figure out what's wrong. This is taking me too long and I can't find the solutions.

Does anyone have any idea on what might be happening?

Let me know if you need further details, code or anything.

Edit:

4) One more thing that I tried was (another way to construct the decrypter):

CryptoPP::AES::Decryption aesDecryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH);
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption( aesDecryption, iv );


CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink( decryptedtext ) );
stfDecryptor.Put( reinterpret_cast<const unsigned char*>( (vector + 4) ), 16 );
stfDecryptor.MessageEnd();

But I get the same:

terminate called after throwing an instance of 'CryptoPP::InvalidCiphertext'
  what():  StreamTransformationFilter: invalid PKCS #7 block padding found

Edit2:

The vector is created with this line (the way the vector is fulled is a quite complicated to put it here because I am using a platform for network encoding) :

uint8_t* vector;

Edit3:

This is how I encrypt the vector.

CryptoPP::AES::Encryption aesEncryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH);
        CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption( aesEncryption, iv );

        CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink( ciphertext ) );
        stfEncryptor.Put( reinterpret_cast<const unsigned char*>( (vector + 4) ), 16 );
        stfEncryptor.MessageEnd();

And after that I put the ciphertext again in the vector:

std::cout << std::endl << std::endl;
for(int i=0;i < 16; i++){
    *(vector+ i + 4) = (ciphertext[i]) ; 
}

Upvotes: 2

Views: 4285

Answers (2)

Niemand
Niemand

Reputation: 127

The problem was that I was encoding with padding and trying to decrypt it without. So I add not only in the encryption but in the descryption that it should work without padding.

Creation of the Key and IV:

CBC_Mode< AES >::Encryption encryptor;
encryptor.SetKeyWithIV( key, CryptoPP::AES::DEFAULT_KEYLENGTH, iv );

Encryption:

CryptoPP::StringSource ss( vector + 4 , 16, true, 
     new CryptoPP::StreamTransformationFilter( encryptor,
          new CryptoPP::StringSink( ciphertext ),
              CryptoPP::StreamTransformationFilter::NO_PADDING
     ) // StreamTransformationFilter      
); // StringSource

Decryption:

CBC_Mode< AES >::Decryption decryptor;
    decryptor.SetKeyWithIV( key, CryptoPP::AES::DEFAULT_KEYLENGTH, iv );

        CryptoPP::StringSource ss( reinterpret_cast<const unsigned char*>( (vector + 4) ), 16, true,
                new CryptoPP::StreamTransformationFilter( decryptor,
                    new CryptoPP::StringSink( decryptedtext ), CryptoPP::StreamTransformationFilter::NO_PADDING) );

I did this because I don't want to work with padding anymore.

Upvotes: 0

jww
jww

Reputation: 102205

terminate called after throwing an instance of CryptoPP::InvalidCiphertext'
    what():  StreamTransformationFilter: invalid PKCS #7 block padding found

This is easy enough to fix. Catch a Crypto++ InvalidCiphertext exception. See the InvalidCiphertext Class Reference.

-----

If I just send the vector without size:

CryptoPP::StringSource ss( vector+4, true, ...

I think this might be matching the following StringSource constructor, which means the length is true, which probable means 1. To add insult to injury the attached StreamTransformationFilter is being coerced into the bool pumpAll:

StringSource (const byte *string, size_t length, bool pumpAll, BufferedTransformation *attachment=NULL)

You should probably enable warnings to catch these sorts of things. -Wall -Wextra are good choices. (Nothing will help the coercion of the pointer to the bool, though. See, for example, No pointer conversion warning for "bool" in C/C++ on the GCC mailing list).

-----

If I send the vector and the size (it's really 20 but I
just want to decrypt the last 16 bytes):

...
CryptoPP::StringSource ss( vector+4, 16 , true, ...

I don't think this is going to work as expected. CBC mode is not a random access mode (but I may not be parsing things correctly):

CBC_Mode< AES >::Encryption enc;
cout << "Random access: " << enc.IsRandomAccess() << endl;

Results in:

Random access: 0

I'm fairly certain its not going to work at byte sizes. You might be able to do it by adding a layer above the decryptor, but you will have to manage the initialization vector, then decrypt a block, and finally return the offest into the block.

-----

You should probably do something like the following. Its not efficient because it copes a string to a vector, but I wanted to try an use vectors since you seem to be using them.

encrypted.CopyTo(f2) is where you put your socket code. Your code will not need encrypted.CopyTo(f2).

// Creates the memory block and zero's it.
SecByteBlock key(AES::DEFAULT_KEYLENGTH), iv(AES::BLOCKSIZE);

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

string m1;
vector<char> v1;

m1 = "Now is the time for all good men to come to the aide of their country";
copy(m1.begin(), m1.end(), back_inserter(v1));

CBC_Mode< AES >::Encryption enc;
enc.SetKeyWithIV(key, key.size(), iv, iv.size());

ByteQueue encrypted;
StreamTransformationFilter f1(enc, new Redirector(encrypted));

f1.PutWord32((uint32_t)v1.size(), BIG_ENDIAN_ORDER);
f1.Put((const unsigned char *) &v1[0], v1.size());
f1.MessageEnd();

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

string m2;
vector<char> v2;

CBC_Mode< AES >::Decryption dec;
dec.SetKeyWithIV(key, key.size(), iv, iv.size());

ByteQueue decrypted;
StreamTransformationFilter f2(dec, new Redirector(decrypted));

encrypted.CopyTo(f2);
f2.MessageEnd();

uint32_t len;
decrypted.GetWord32(len, BIG_ENDIAN_ORDER);

v2.resize(len);
decrypted.Get((unsigned char *) &v2[0], v2.size());

copy(v2.begin(), v2.end(), back_inserter(m2));

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

cout << "Message: " << m1 << endl;
cout << "Decrypted: " << m2 << endl;

It produces the expected results:

$ ./cryptopp-test.exe
Message: Now is the time for all good men to come to the aide of their country
Decrypted: Now is the time for all good men to come to the aide of their country

-----

Regarding your use of vector + 4: once the cipher text is decrypted, you can seek in it, and do things like read a length from it. That's what this does:

// Ciphertext is already decrypted
uint32_t len;
decrypted.GetWord32(len, BIG_ENDIAN_ORDER);

If you just want to discard the value, then try:

decrypted.Skip(4);

But then you'll have to figure out how to size your vector.

-----

I'm working in a new protocol for secure communication

Secure communications usually provides both confidentiality and authenticity assurances. You seem to be missing the latter since CBC mode only provides confidentiality.

It might be a good idea to use something that already exists, like IPSec (preferred) or TLS (will do in a pinch). Also, key agreement is going to be a pain point. Its another reason to consider using something like IPSec or TLS.

If you can't use something already available, then you should use an Authenticated Encryption mode so you get confidentiality and authenticity combined into a single mode. Try EAX, GCM or CCM mode.

If you do, then then change is fairly trivial:

    EAX< AES >::Encryption enc;
    enc.SetKeyWithIV(key, key.size(), iv, iv.size());

    ByteQueue encrypted;
    AuthenticatedEncryptionFilter f1(enc, new Redirector(encrypted));

And:

    EAX< AES >::Decryption dec;
    dec.SetKeyWithIV(key, key.size(), iv, iv.size());

    ByteQueue decrypted;
    AuthenticatedDecryptionFilter f2(dec, new Redirector(decrypted));

-----

You can print the cipher text by copying it to an encoder. Copying is non-destructive (unlike moving it with TransferTo):

string encoded;
HexEncoder encoder(new StringSink(encoded));
encrypted.CopyTo(encoder);
encoder.MessageEnd();

Fox CBC mode (less secure):

Encrypted: 7F9FFCAB00704EC79BB5F19C48FE7C668033B16F52E7E00671A38A06F4A7426E7FE31
95CA6A83C7414A76C250B42E63143C93E7A6B97B6304C5782DE3E62BD545706A9F62CD7AD57BC374
19B7510EBED

For EAX mode (more secure):

Encrypted: B75347EB75DF8E1F0424979E91CEECD455F5727B506A8AA932AF07E1DF6A7B037A245
FEC7A2270BFAB8110226769E1C0A12E95C455E9C714AF28DA330A2B01B3F2D541D4E68276193C018
7BA0246166AD26624E848EC8330D3

The extra bytes in EAX mode are the authentication tag. Its used to detect accidental and malicious tampering.

Upvotes: 3

Related Questions