Click here to Skip to main content
15,887,812 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
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).

C++
class my_socket
{
public:

void write(::boost::asio::const_buffers_1 const& buf)
{
  //this signature implies const_buffers_1 i.e. only one buffer at a time
  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);

        // Encrypt the request.
        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.
Posted
Updated 27-Jul-16 22:34pm
v2

1 solution

At first: you can only write the max size which is supported. Normally you write the message piece after piece til all data is written.

Maybe you must split the uncrypted data in small packets, then encrypt and then send til all data is transfered.

Take a look at this example code for packet sending of data.
 
Share this answer
 
Comments
PascalLqX 27-Jul-16 4:01am    
Thank you for the idea to limit the size of the buffer to encrypt. I used 90% of cbMaximumMessage successfully. I am continuing the investigation to get the max size, then to understand what is wrong for an SChannel the description of the EncryptMessage function (https://msdn.microsoft.com/en-us/library/windows/desktop/aa375378%28v=vs.85%29.aspx)
PascalLqX 28-Jul-16 4:37am    
I fixed the bug decreasing the size of the buffer to encrypt by the size of the header+trailer rounded to the cbBlockSize. The code has been updated with that patch.
Thanks a lot to KarstenK for the idea.
dnyan28 29-Jan-18 5:56am    
Hi PascalLqx,

Could you please provide Schannel modified code, as we are also trying the same.
dnyan28 31-Jan-18 4:44am    
I tried with the above solution and sent the data in chunks but not receiving the proper response from server. Did we need to handle this on server also?
PascalLqX 31-Jan-18 5:53am    
@dryan28 We didn't change anything on the server side as we use a public email service (gmail, etc)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900