Streamtransformationfilter: Invalid Pkcs #7 Block Padding Found (Crypto++)

I am trying to decrypt ciphertext provided by a user using a Qt QLineEdit.

If I encrypt the string and then decrypt it using the code below, everything works as expected.

crypto::aes_pbkdf2_decrypt(crypto::aes_pbkdf2_encrypt("ciphertxt", "keystr"), "keystr");

When the string is encrypted, it gets an output to a QtLineEdit. When I attempt to decrypt that ciphertext output, it throws StreamTransformationFilter: invalid PKCS #7 block padding found.

This doesn't make much sense to me since it works perfectly when I directly decrypt the output from the encryption function as shows above.

Here are the functions I am using for encryption/decryption:

Encrypt

std::string crypto::aes_pbkdf2_encrypt(std::string pPlainText, std::string pKey)
{
    CryptoPP::AutoSeededRandomPool  prng;
    CryptoPP::SecByteBlock iv       (CryptoPP::AES::BLOCKSIZE);
    CryptoPP::SecByteBlock salt     (CryptoPP::AES::DEFAULT_KEYLENGTH / 2);
    CryptoPP::SecByteBlock key      (CryptoPP::AES::DEFAULT_KEYLENGTH);

    std::string cipher, output, cipher_str, iv_str, salt_str;

    prng.GenerateBlock(salt, salt.size());
    prng.GenerateBlock(iv, iv.size());
    key = pbkdf2_salt_hash(pKey, salt);

    try {
        CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encrypt;
        encrypt.SetKeyWithIV(key, key.size(), iv);

        CryptoPP::StringSource stringsource(pPlainText, true,
            new CryptoPP::StreamTransformationFilter(encrypt,
                new CryptoPP::StringSink(cipher)
            )
        );
    } catch (const CryptoPP::Exception& e) {
        std::cerr << e.what() << std::endl;
        return "";
    }

    CryptoPP::HexEncoder encoder (new CryptoPP::FileSink(std::cout));

    encoder.Detach(new CryptoPP::StringSink(salt_str));
    encoder.Put(salt, salt.size());
    encoder.MessageEnd();

    encoder.Detach(new CryptoPP::StringSink(iv_str));
    encoder.Put(iv, iv.size());
    encoder.MessageEnd();

    encoder.Detach(new CryptoPP::StringSink(cipher_str));
    encoder.Put((const CryptoPP::byte*) &cipher[0], cipher.size());
    encoder.MessageEnd();

    output = iv_str + cipher_str + salt_str;
    std::cout << "Cipher: " << output << std::endl;

    return output;
}

Decrypt

std::string crypto::aes_pbkdf2_decrypt(std::string pCipherText, std::string pKey)
{
    std::string iv_str = pCipherText.substr(0, 32);
    std::string salt_str = pCipherText.substr(pCipherText.size() - 16, pCipherText.size());
    std::string cipher_str = pCipherText.substr(iv_str.size(), pCipherText.size() - (iv_str.size() + salt_str.size()));

    std::string iv, salt, cipher, output;
    CryptoPP::HexDecoder decoder(new CryptoPP::FileSink(std::cout));

    decoder.Attach(new CryptoPP::StringSink(iv));
    decoder.Put((CryptoPP::byte*) iv_str.data(), iv_str.size());
    decoder.MessageEnd();

    decoder.Attach(new CryptoPP::StringSink(salt));
    decoder.Put((CryptoPP::byte*) salt_str.data(), salt_str.size());
    decoder.MessageEnd();

    decoder.Attach(new CryptoPP::StringSink(cipher));
    decoder.Put((CryptoPP::byte*) cipher_str.data(), cipher_str.size());
    decoder.MessageEnd();

    try {
        CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decryptor;
        CryptoPP::SecByteBlock salt_blk ((CryptoPP::byte*) salt.data(), salt.size());
        CryptoPP::SecByteBlock iv_blk ((CryptoPP::byte*) iv.data(), iv.size());
        CryptoPP::SecByteBlock key = pbkdf2_salt_hash(pKey, salt_blk);
        decryptor.SetKeyWithIV(key.data(), key.size(), iv_blk);

        CryptoPP::StringSource stringsource (cipher, true,
            new CryptoPP::StreamTransformationFilter(decryptor,
                new CryptoPP::StringSink(output)
            )
        );
    } catch (const CryptoPP::Exception& e) {
        std::cerr << e.what() << std::endl;
        return "";
    }

    return output;
}

PBKDF2 Hashing

CryptoPP::SecByteBlock crypto::pbkdf2_salt_hash(std::string pPlainText, CryptoPP::SecByteBlock pSalt)
{
    CryptoPP::PKCS5_PBKDF2_HMAC<CryptoPP::SHA256> pbkdf;
    CryptoPP::SecByteBlock output(CryptoPP::AES::DEFAULT_KEYLENGTH);

    CryptoPP::byte text_bytes[pPlainText.size()];
    std::memcpy(text_bytes, pPlainText.data(), pPlainText.size());

    size_t plaintext_length = strlen((const char*)text_bytes);
    size_t salt_length = pSalt.size();

    pbkdf.DeriveKey(output, output.size(), 0, text_bytes, plaintext_length, pSalt, salt_length, 1024, 0.0f);

    return output;
}

What I have already tried:

I tried each padding type in CryptoPP::BlockPaddingSchemeDef::BlockPaddingScheme to CryptoPP::StringSink in the decryption function. Only creates odd 'OpenType' errors and strangely formatted outputs.

3
James H. Sterling

James H. Sterling

Environmental Science & Climate Journalist

James Sterling reports on renewable energy developments, climate policy, ecological conservation, and green tech innovations around the globe.

Share this article