I have written a simple encrypt & decrypt function and everything works. Except sometimes the HexEncoder
seems to encode the cipher
far too way small. Therefore the decryption fails. But this rarely happens. I have isolated the bug to the HexEncoder
since the log Mismatch on encoding
is only present when the problem appears. A bad workaround is to call the encrypt function again, which solves the problem. Not sure why, perhaps because of a newly generated IV but this seems unlikely to me.
Am I doing something wrong, undefined behaviour? Or is this a bug from the HexEncoder?
Encrypt function:
// Encrypt data.
template <typename Encryption> static inline
int encrypt(Encryption& m_enc, str_t& encrypted, const str_t& data) {
// Generate & set iv.
CryptoPP::byte iv [CryptoPP::AES::BLOCKSIZE];
rand.GenerateBlock(iv, CryptoPP::AES::BLOCKSIZE);
m_enc.Resynchronize(iv, CryptoPP::AES::BLOCKSIZE);
// Encrypt.
str_t cipher;
cipher.resize(data.len() + CryptoPP::AES::BLOCKSIZE);
cipher.len() = data.len() + CryptoPP::AES::BLOCKSIZE;
CryptoPP::ArraySink encrypt_sink(
(Byte*) cipher.data(),
cipher.len()
);
try {
CryptoPP::StringSource(
data.data(),
true,
new CryptoPP::StreamTransformationFilter(m_enc, new CryptoPP::Redirector(encrypt_sink))
);
} catch (...) {
return crypto::error::encrypt;
}
//print("Cipher 1: ", cipher);
// Set cipher text length now that its known
if (cipher.len() != encrypt_sink.TotalPutLength()) {
print("Mismatch on ciper; expected: ", cipher.len(), " sinked: ", encrypt_sink.TotalPutLength());
}
cipher.resize(encrypt_sink.TotalPutLength());
cipher.len() = encrypt_sink.TotalPutLength();
//print("Cipher 2: ", cipher);
// Encode.
encrypted.resize(cipher.len() * 2);
encrypted.len() = cipher.len() * 2;
CryptoPP::ArraySink encode_sink((Byte*) encrypted.data(), encrypted.len());
try {
CryptoPP::StringSource(
cipher.data(),
true,
new CryptoPP::HexEncoder(
new CryptoPP::Redirector(encode_sink)
)
);
} catch (...) {
encrypted.len() = 0;
return crypto::error::encode;
}
print("Encoded cipher 1: ", encrypted);
// Set encrypted length.
if (encrypted.len() != encode_sink.TotalPutLength()) {
print("BUG -> Mismatch on encoding; expected: ", encrypted.len(), " sinked: ", encode_sink.TotalPutLength());
//str_t shortened;
//shortened.resize(encode_sink.TotalPutLength());
//shortened.concat_no_resize(encrypted.data(), encode_sink.TotalPutLength());
//encrypted.swap(shortened);
return encrypt(m_enc, encrypted, data);
}
encrypted.resize(encode_sink.TotalPutLength());
encrypted.len() = encode_sink.TotalPutLength();
print("Encoded cipher 2: ", encrypted);
// Encode IV.
str_t encoded_iv;
encoded_iv.resize(CryptoPP::AES::BLOCKSIZE * 2);
encoded_iv.len() = CryptoPP::AES::BLOCKSIZE * 2;
CryptoPP::ArraySink iv_sink((Byte*) encoded_iv.data(), encoded_iv.len());
try {
CryptoPP::ArraySource(
iv,
CryptoPP::AES::BLOCKSIZE,
true,
new CryptoPP::HexEncoder(
new CryptoPP::Redirector(iv_sink)
)
);
} catch (...) {
encrypted.len() = 0;
return crypto::error::encode;
}
// Set encoded iv length.
if (encoded_iv.len() != iv_sink.TotalPutLength()) {
print("Mismatch on encoding iv; expected: ", encoded_iv.len(), " sinked: ", iv_sink.TotalPutLength());
}
encoded_iv.resize(iv_sink.TotalPutLength());
encoded_iv.len() = iv_sink.TotalPutLength();
print("Encoded IV: ", encoded_iv);
// Concat IV.
encoded_iv.concat(encrypted);
encrypted.swap(encoded_iv);
return 0;
}
Decrypt function:
// Decrypt data.
template <typename Decryption> static inline
int decrypt(Decryption& m_dec, str_t& decrypted, const str_t& data) {
// Decode.
str_t decoded;
decoded.resize(data.len() / 2);
decoded.len() = data.len() / 2;
try {
CryptoPP::StringSource(
data.data(),
true,
new CryptoPP::HexDecoder(
new CryptoPP::ArraySink((Byte*) decoded.data(), decoded.len())
)
);
} catch (...) {
return crypto::error::decode;
}
// Skip for now.
m_dec.Resynchronize((Byte*) decoded.data(), CryptoPP::AES::BLOCKSIZE);
// Recovered text will be less than cipher text
decrypted.resize(decoded.len() - CryptoPP::AES::BLOCKSIZE);
decrypted.len() = decoded.len() - CryptoPP::AES::BLOCKSIZE;
CryptoPP::ArraySink rs((Byte*) decrypted.data(), decrypted.len());
try {
CryptoPP::StringSource(
(Byte*) decoded.data() + CryptoPP::AES::BLOCKSIZE,
decoded.len(),
true,
new CryptoPP::StreamTransformationFilter(
m_dec,
new CryptoPP::Redirector(rs),
CryptoPP::BlockPaddingSchemeDef::BlockPaddingScheme::NO_PADDING
)
);
} catch (...) {
decrypted.len() = 0;
return crypto::error::decrypt;
}
// Set recovered text length now that its known
decrypted.resize(rs.TotalPutLength());
decrypted.len() = rs.TotalPutLength();
return 0;
}
Testing:
...
str_t data = "Hello World!", encrypted, decrypted;
aes.encrypt(encrypted, data);
aes.decrypt(decrypted, encrypted);
print("Encrypted: ", encrypted);
print("Decrypted: ", decrypted);
This are the normal logs (without the bug occurence):
Mismatch on ciper; expected: 28 sinked: 16
Encoded cipher 1: 04A2C4FB074F6ACBA996239224BD5F77
Encoded cipher 2: 04A2C4FB074F6ACBA996239224BD5F77
Encoded IV: 02466AEBCF4AC2066CE2E144FC5B71C8
Encrypted: 02466AEBCF4AC2066CE2E144FC5B71C804A2C4FB074F6ACBA996239224BD5F77
Decrypted: Hello World!
This are the logs when the bug occurs (with the recursive encrypt
call commented out):
Decoded: Hello World!
Mismatch on ciper; expected: 28 sinked: 16
Encoded cipher 1: C97BFE
BUG -> Mismatch on encoding; expected: 32 sinked: 6
Encoded cipher 2: C97BFE
Encoded IV: 06E05C3DAE3E9D6DAC8971E5C9CA0A1A
Encrypted: 06E05C3DAE3E9D6DAC8971E5C9CA0A1AC97BFE
Decrypted:
This are the logs when the bug occurs with the workaround:
Mismatch on ciper; expected: 28 sinked: 16
Encoded cipher 1: A0
BUG -> Mismatch on encoding; expected: 32 sinked: 2
Mismatch on ciper; expected: 28 sinked: 16
Encoded cipher 1: 883A8A644E5B50067A
BUG -> Mismatch on encoding; expected: 32 sinked: 18
Mismatch on ciper; expected: 28 sinked: 16
Encoded cipher 1: 58AD2010C761215422F89D42FC2F2396
Encoded cipher 2: 58AD2010C761215422F89D42FC2F2396
Encoded IV: BE361B7D8C9144052220E143AD54CB63
Encrypted: BE361B7D8C9144052220E143AD54CB6358AD2010C761215422F89D42FC2F2396
Decrypted: Hello World!