*/
#include <message-buffer.h>
-
-#include <exception.h>
-#include <dpl/log/log.h>
+#include <limits>
namespace CKM {
-void MessageBuffer::Push(RawBuffer &&data)
-{
- m_buffer.Push(std::move(data));
-}
-
-bool MessageBuffer::Ready()
-{
- CountBytesLeft();
-
- if (m_bytesLeft == 0)
- return false;
-
- if (m_bytesLeft > m_buffer.Size())
- return false;
-
- return true;
-}
-
-void MessageBuffer::Read(size_t num, void *bytes)
-{
- CountBytesLeft();
-
- if (num > m_bytesLeft) {
- LogDebug("Protocol broken. OutOfData. Asked for: " << num << " Ready: " <<
- m_bytesLeft << " Buffer.size(): " << m_buffer.Size());
- Throw(Exception::OutOfData);
- }
-
- m_buffer.Pop(bytes, num);
- m_bytesLeft -= num;
-}
-
-void MessageBuffer::Write(size_t, const void *)
-{
- ThrowErr(Exc::InternalError, "unexpected IStream::Write call in MessageBuffer");
-}
-
void MessageSerializer::Sizer::Read(size_t, void*)
{
ThrowErr(Exc::InternalError, "unexpected IStream::Read call in SerializeMessage");
MessageSerializer::Writer::Writer(size_t size)
{
- m_buffer.reserve(sizeof(size_t) + size);
- Serialization::Serialize(*this, size);
+ if (size > std::numeric_limits<uint32_t>::max())
+ ThrowErr(Exc::InternalError, "Message too large for serialization");
+
+ uint32_t size32 = size;
+ m_buffer.reserve(sizeof(size32) + size32);
+ Serialization::Serialize(*this, size32);
}
void MessageSerializer::Writer::Read(size_t, void*)
#include <dpl/exception.h>
#include <dpl/serialization.h>
#include <dpl/raw-buffer.h>
+#include <dpl/log/log.h>
#include <symbol-visibility.h>
+#include <cstdint>
+#include <exception.h>
namespace CKM {
-class COMMON_API MessageBuffer : public CKM::IStream {
+template <typename SIZE_TYPE>
+class COMMON_API MessageBufferT : public CKM::IStream {
public:
class Exception {
public:
DECLARE_EXCEPTION_TYPE(Base, OutOfData)
};
- MessageBuffer() : m_bytesLeft(0) {}
+ MessageBufferT() : m_bytesLeft(0) {}
- MessageBuffer(MessageBuffer &&) = default;
- MessageBuffer &operator=(MessageBuffer &&) = default;
+ MessageBufferT(MessageBufferT &&) = default;
+ MessageBufferT &operator=(MessageBufferT &&) = default;
- void Push(RawBuffer &&data);
+ void Push(RawBuffer &&data) {
+ m_buffer.Push(std::move(data));
+ }
+
+ bool Ready() {
+ CountBytesLeft();
+
+ if (m_bytesLeft == 0)
+ return false;
+
+ if (m_bytesLeft > m_buffer.Size())
+ return false;
+
+ return true;
+ }
- bool Ready();
+ virtual void Read(size_t num, void *bytes) {
+ CountBytesLeft();
- virtual void Read(size_t num, void *bytes);
+ if (num > m_bytesLeft) {
+ LogDebug("Protocol broken. OutOfData. Asked for: " << num << " Ready: " <<
+ m_bytesLeft << " Buffer.size(): " << m_buffer.Size());
+ Throw(typename Exception::OutOfData);
+ }
- virtual void Write(size_t num, const void *bytes);
+ m_buffer.Pop(bytes, num);
+ m_bytesLeft -= num;
+ }
+
+ virtual void Write(size_t, const void *) {
+ ThrowErr(Exc::InternalError, "unexpected IStream::Write call in MessageBufferT");
+ }
// generic deserialization
template <typename... Args>
Deserializer<Args...>::Deserialize(*this, args...);
}
+ bool Empty() const {
+ return m_buffer.Size() == 0;
+ }
+
protected:
inline void CountBytesLeft()
{
if (m_bytesLeft > 0)
return; // we already counted m_bytesLeft nothing to do
- if (m_buffer.Size() < sizeof(size_t))
+ if (m_buffer.Size() < sizeof(SIZE_TYPE))
return; // we cannot count m_bytesLeft because buffer is too small
- m_buffer.Pop(&m_bytesLeft, sizeof(size_t));
+ m_buffer.Pop(&m_bytesLeft, sizeof(SIZE_TYPE));
}
- size_t m_bytesLeft;
+ SIZE_TYPE m_bytesLeft;
CKM::BinaryQueue m_buffer;
};
+using MessageBuffer = MessageBufferT<uint32_t>;
+using MessageBuffer64 = MessageBufferT<uint64_t>;
+
class MessageSerializer final {
struct COMMON_API Sizer : IStream {
void Read(size_t num, void* bytes) override;
RawBuffer Store::unpack(const RawBuffer &packed, const Password &pass)
{
- MessageBuffer buffer;
- buffer.Push(RawBuffer(packed));
int encryptionScheme = EncryptionScheme::NONE;
RawBuffer data;
+
+ auto unpackInternal = [&](auto&& internalBuffer){
+ if (encryptionScheme == EncryptionScheme::NONE) {
+ if (!pass.empty())
+ ThrowErr(Exc::AuthenticationFailed, "Unexpected custom password.");
+ return data;
+ }
+
+ internalBuffer.Push(std::move(data));
+ RawBuffer encrypted;
+ RawBuffer iv;
+ RawBuffer tag;
+
+ // serialization exceptions will be catched as CKM::Exception and will cause
+ // CKM_API_ERROR_SERVER_ERROR
+ internalBuffer.Deserialize(encrypted, iv, tag);
+
+ /*
+ * AES GCM will check data integrity and handle cases where:
+ * - wrong password is used
+ * - password is empty when it shouldn't be
+ */
+ RawBuffer key = passwordToKey(pass, iv, Params::DERIVED_KEY_LENGTH);
+
+ RawBuffer ret;
+
+ try {
+ ret = Crypto::SW::Internals::decryptDataAesGcm(key, encrypted, iv, tag);
+ } catch (const Exc::Crypto::InputParam &e) {
+ ThrowErr(Exc::AuthenticationFailed, "Decryption with custom password failed, authentication failed");
+ } catch (const Exc::Exception &e) {
+ ThrowErr(Exc::InternalError, "Decryption with custom password failed, internal error");
+ }
+ return ret;
+ };
+
+ MessageBuffer buffer;
+ buffer.Push(RawBuffer(packed));
buffer.Deserialize(encryptionScheme, data);
- if (encryptionScheme == EncryptionScheme::NONE) {
- if (!pass.empty())
- ThrowErr(Exc::AuthenticationFailed, "Unexpected custom password.");
- return data;
- }
+ // There is some data left -> this must be a buffer encoded using 64-bit size
+ if (!buffer.Empty()) {
+ MessageBuffer64 buffer64;
+ buffer64.Push(RawBuffer(packed));
+ buffer64.Deserialize(encryptionScheme, data);
+ if (!buffer64.Empty())
+ ThrowErr(Exc::InternalError, "Buffer not fully deserialized");
- MessageBuffer internalBuffer;
- internalBuffer.Push(std::move(data));
- RawBuffer encrypted;
- RawBuffer iv;
- RawBuffer tag;
-
- // serialization exceptions will be catched as CKM::Exception and will cause
- // CKM_API_ERROR_SERVER_ERROR
- internalBuffer.Deserialize(encrypted, iv, tag);
-
- /*
- * AES GCM will check data integrity and handle cases where:
- * - wrong password is used
- * - password is empty when it shouldn't be
- */
- RawBuffer key = passwordToKey(pass, iv, Params::DERIVED_KEY_LENGTH);
-
- RawBuffer ret;
-
- try {
- ret = Crypto::SW::Internals::decryptDataAesGcm(key, encrypted, iv, tag);
- } catch (const Exc::Crypto::InputParam &e) {
- ThrowErr(Exc::AuthenticationFailed, "Decryption with custom password failed, authentication failed");
- } catch (const Exc::Exception &e) {
- ThrowErr(Exc::InternalError, "Decryption with custom password failed, internal error");
+ return unpackInternal(MessageBuffer64());
}
- return ret;
+ return unpackInternal(MessageBuffer());
}
} // namespace SW
{
return m_id;
}
- virtual int getScheme() const
- {
- return m_scheme;
- }
virtual const Pwd& getPassword() const
{
return m_password;
#include <tz-backend/internals.h>
#include <message-buffer.h>
+#include <crypto-logic.h>
namespace CKM {
namespace Crypto {
void Store::unpack(const RawBuffer &packed,
const Password& password,
int &scheme,
- RawBuffer &data,
+ RawBuffer &keyId,
RawBuffer &iv,
RawBuffer &tag)
{
MessageBuffer buffer;
buffer.Push(RawBuffer(packed));
- buffer.Deserialize(scheme);
+ buffer.Deserialize(scheme, keyId);
- if (scheme == EncryptionScheme::PASSWORD) {
- buffer.Deserialize(data, iv, tag);
+ // Hash has invalid length -> this must be a buffer encoded using 64-bit size
+ if (keyId.size() != CryptoLogic::HASH_SIZE) {
+ MessageBuffer64 buffer64;
+ buffer64.Push(RawBuffer(packed));
+
+ buffer64.Deserialize(scheme, keyId);
+
+ if (keyId.size() != CryptoLogic::HASH_SIZE)
+ ThrowErr(Exc::InternalError, "Error during deserialization");
+
+ if (scheme == EncryptionScheme::PASSWORD)
+ buffer64.Deserialize(iv, tag);
} else {
- buffer.Deserialize(data);
+ if (scheme == EncryptionScheme::PASSWORD)
+ buffer.Deserialize(iv, tag);
}
if ((scheme & EncryptionScheme::PASSWORD) && password.empty()) {
static void unpack(const RawBuffer &packed,
const Password& password,
int &scheme,
- RawBuffer &data,
+ RawBuffer &keyId,
RawBuffer &iv,
RawBuffer &tag);
size_t maxChunkSize() const override;
namespace CKM {
+const size_t CryptoLogic::HASH_SIZE = SHA512_DIGEST_LENGTH;
+
namespace {
const static int AES_CBC_KEY_SIZE = 32;
RawBuffer makeHashInternal(const std::string& message)
{
- RawBuffer digest(SHA512_DIGEST_LENGTH);
+ RawBuffer digest(CryptoLogic::HASH_SIZE);
auto msg_ptr = reinterpret_cast<const unsigned char*>(message.data());
if (!SHA512(msg_ptr, message.length(), digest.data()))
*/
static const int ENCRYPTION_V2 = 1;
+ static const size_t HASH_SIZE;
+
private:
// Encryption scheme flags (enable/disable specific encryption type, multiple choice)
static const int ENCR_BASE64 = 1 << 0;
#include <ckm/ckm-raw-buffer.h>
#include <protocols.h>
#include <message-buffer.h>
+#include <sw-backend/store.h>
using namespace CKM;
namespace {
std::string IV_STR("1234567890123456");
std::string AAD_STR("sdfdsgsghrtkghwiuho3irhfoewituhre");
+std::string DATA_STR("test data");
RawBuffer IV(IV_STR.begin(), IV_STR.end());
RawBuffer AAD(AAD_STR.begin(), AAD_STR.end());
+RawBuffer DATA(DATA_STR.begin(), DATA_STR.end());
+const Password PASSWORD = "password";
void checkIntParam(const CryptoAlgorithm &algo, ParamName name,
uint64_t expected)
" should " << (success ? "succeed" : "fail"));
}
+// This function mimics the serialization on a 64-bit system prior to the fix
+RawBuffer pack64(const RawBuffer& data)
+{
+ struct Writer64 : public IStream {
+ explicit Writer64(uint64_t size) {
+ m_buffer.reserve(sizeof(size) + size);
+ Serialization::Serialize(*this, size);
+ }
+
+ void Read(size_t, void*) override {
+ BOOST_FAIL("Unexpected Read call in Writer.");
+ }
+
+ void Write(size_t num, const void* bytes) override {
+ BOOST_REQUIRE(m_buffer.size() + num <= m_buffer.capacity());
+ m_buffer.resize(m_buffer.size() + num);
+ memcpy(&m_buffer[m_buffer.size() - num], bytes, num);
+ }
+
+ RawBuffer m_buffer;
+ };
+
+ int scheme = 0;
+ int dataSize = data.size();
+ uint64_t size64 = sizeof(scheme) + sizeof(dataSize) + data.size();
+ Writer64 writer(sizeof(size64) + size64);
+ Serialization::Serialize(writer, scheme);
+ Serialization::Serialize(writer, data);
+ BOOST_REQUIRE(writer.m_buffer.size() == sizeof(size64) + size64);
+
+ return writer.m_buffer;
+}
+
} // namespace anonymous
BOOST_AUTO_TEST_SUITE(SERIALIZATION_TEST)
BOOST_REQUIRE(o[1].backend == v[1].backend);
}
+POSITIVE_TEST_CASE(Serialization_32_vs_64_bit)
+{
+ using Crypto::SW::Store;
+
+ // pack 32 -> unpack
+ auto packed = Store::pack(DATA, PASSWORD);
+ auto unpacked = Store::unpack(packed, PASSWORD);
+ BOOST_REQUIRE(DATA == unpacked);
+
+ packed = Store::pack(DATA, "");
+ unpacked = Store::unpack(packed, "");
+ BOOST_REQUIRE(DATA == unpacked);
+
+ // pack 64 -> unpack
+ packed = pack64(DATA);
+ unpacked = Store::unpack(packed, "");
+ BOOST_REQUIRE(DATA == unpacked);
+}
+
+NEGATIVE_TEST_CASE(Serialization_32_vs_64_bit)
+{
+ using Crypto::SW::Store;
+
+ // pack 32 -> unpack
+ auto packed = Store::pack(DATA, PASSWORD);
+
+ BOOST_REQUIRE_THROW(Store::unpack(packed, "wrong-password"), Exc::AuthenticationFailed);
+ BOOST_REQUIRE_THROW(Store::unpack(packed, ""), Exc::AuthenticationFailed);
+
+ packed = Store::pack(DATA, "");
+
+ BOOST_REQUIRE_THROW(Store::unpack(packed, PASSWORD), Exc::AuthenticationFailed);
+
+ // pack 64 -> unpack
+ packed = pack64(DATA);
+ BOOST_REQUIRE_THROW(Store::unpack(packed, PASSWORD), Exc::AuthenticationFailed);
+}
+
BOOST_AUTO_TEST_SUITE_END()