Hi,
I have problem sending SChannel TLS message larger than the negotiated maximum length. I defined a class handling the SChannel context (class my_context) and a class handling the socket that uses my_context class to encrypt the data to send.
When my_socket.write() is called with a buffer larger than SecPkgContext_StreamSizes.cbMaximumMessage, the part greater than SecPkgContext_StreamSizes.cbMaximumMessage is not understood by the server (nor by Wireshark).
class my_socket
{
public:
void write(::boost::asio::const_buffers_1 const& buf)
{
auto b = *buf.begin();
auto util = IoBuffer.size() - phContext->GetBufferReservedSize();
char const* begin = ::boost::asio::buffer_cast<char const*>(b);
char const* curr = begin;
char const* end = begin + ::boost::asio::buffer_size(b);
while (curr != end) {
auto const buffSz = std::min(util, (size_t)(end - curr));
auto const encryptedMessage = phContext->EncryptMessage(
::boost::asio::buffer(curr, buffSz), IoBuffer);
auto const data = ::boost::asio::buffer_cast<char const*>(encryptedMessage);
auto const sz = ::boost::asio::buffer_size(encryptedMessage);
auto cbData = send(Socket, data, sz, 0);
if (cbData != sz)
throw std::invalid_argument("Can't send data to the server (error code:" +
std::to_string(WSAGetLastError()) + ")");
curr += buffSz;
}
}
private:
void _AllocateIoBuffer() {
phContext->ResizeBuffer(IoBuffer);
}
SOCKET Socket;
my_context *phContext;
std::vector<char> IoBuffer;
};
class my_context
{
[...]
public:
long RoundUp(long n, long s) {
return ((n + s - 1) / s)*s;
}
int GetBufferReservedSize() {
auto const& sz = GetSizes_();
return RoundUp(sz.cbHeader, sz.cbBlockSize) + RoundUp(sz.cbTrailer, sz.cbBlockSize) ;
}
void ResizeBuffer(std::vector<char>& buf) {
auto const& sz = GetSizes_();
buf.reserve(sz.cbHeader + sz.cbMaximumMessage + sz.cbTrailer);
buf.resize(sz.cbMaximumMessage);
}
boost::asio::const_buffer EncryptMessage(
boost::asio::const_buffer const& message,
std::vector<char>& IoBuffer) const
{
SecBufferDesc Message;
SecBuffer Buffers[4];
auto sz = boost::asio::buffer_size(message);
char const* data = boost::asio::buffer_cast<char const*>(message);
auto const headerSize = GetSizes_().cbHeader;
Buffers[0].pvBuffer = IoBuffer.data();
Buffers[0].cbBuffer = headerSize;
Buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
CopyMemory(IoBuffer.data() + headerSize, data, sz);
Buffers[1].pvBuffer = IoBuffer.data() + headerSize;
Buffers[1].cbBuffer = sz;
Buffers[1].BufferType = SECBUFFER_DATA;
Buffers[2].pvBuffer = IoBuffer.data() + headerSize + sz;
Buffers[2].cbBuffer = GetSizes_().cbTrailer;
Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
Buffers[3].BufferType = SECBUFFER_EMPTY;
Message.ulVersion = SECBUFFER_VERSION;
Message.cBuffers = 4;
Message.pBuffers = Buffers;
SECURITY_STATUS const scRet = ::EncryptMessage(
const_cast<PCtxtHandle>(&hContext), 0, &Message, 0);
if (FAILED(scRet)) {
throw std::invalid_argument("Unable to ecrypt message (error code:" +
std::to_string(scRet) + ")");
}
return boost::asio::buffer(IoBuffer, Buffers[0].cbBuffer +
Buffers[1].cbBuffer + Buffers[2].cbBuffer);
}
SecPkgContext_StreamSizes GetSizes_() const {
if (Sizes_.cbHeader == Sizes_.cbMaximumMessage == Sizes_.cbTrailer == 0) {
SECURITY_STATUS const scRet = ::QueryContextAttributes(
const_cast<CtxtHandle*>(&hContext),
SECPKG_ATTR_STREAM_SIZES, &Sizes_);
if (scRet != SEC_E_OK) {
throw std::invalid_argument("Can't get the buffer sizes (error code:" +
std::to_string(scRet) + ")");
}
}
return Sizes_;
}
CredHandle hClientCreds;
CtxtHandle hContext;
mutable SecPkgContext_StreamSizes Sizes_;
const DWORD dwProtocol = SP_PROT_TLS1_2;
};
What I have tried:
I tried to encrypt the message in one call but this has failed.