Topic: Fragmentation of CertificateVerify messages

Hi everyone,

I am using custom signature algorithms for TLS 1.3 authentication.
The sizes of the signatures are huge, so the CertificateVerify message is bigger than the max. size allowed by standard (i.e. MAX_RECORD_SIZE = 16384 and  MAX_PLAINTEXT_SZ   = (1 << 14)), so the extra fragmentation of CertificateVerify message has to be added.

I am trying to implement the fragmentation, but my code does not work correctly and I am not sure how to fix it. I tried to reuse the fragmentation code from the SendTls13Certificate() function as much as possible. For now, the first fragment seems to be correct, but the second fragment contains some garbage value (partially from the previous fragment of the Server Certificate message).

static int SendTls13CertificateVerify(WOLFSSL* ssl)
{
   
    /* 
            ...        
    */
          
          

    switch(ssl->options.asyncState)
    {
        /*  
            ...   
            
        */
        
        case TLS_ASYNC_FINALIZE:
        {
            /* Put the record and handshake headers on. */
            word32 length = args->length + HASH_SIG_SIZE + VERIFY_HEADER;
            word32 payloadSz = length;
            word32 maxFragment = wolfSSL_GetMaxRecordSize(ssl, MAX_RECORD_SIZE);

            while (length > 0 && ret == 0) {
                word32 fragSz = 0;
                args->sendSz = RECORD_HEADER_SZ;

                if (ssl->fragOffset == 0)  {
                    if (length <= maxFragment - HANDSHAKE_HEADER_SZ) {
                        fragSz = length;
                    }
                    else
                        fragSz = maxFragment - HANDSHAKE_HEADER_SZ;

                    args->sendSz += fragSz + HANDSHAKE_HEADER_SZ;
                }
                else {
                    fragSz = min(length, maxFragment);
                    args->sendSz += fragSz;
                }

                args->sendSz += MAX_MSG_EXTRA;

                /* Check buffers are big enough and grow if needed. */
                if ((ret = CheckAvailableSize(ssl, args->sendSz)) != 0)
                    return ret;

                /* Get position in output buffer to write new message to. */
                args->output = ssl->buffers.outputBuffer.buffer +
                        ssl->buffers.outputBuffer.length;

                if (ssl->fragOffset == 0) {
                    AddTls13RecordHeader(args->output, fragSz + HANDSHAKE_HEADER_SZ, handshake, ssl);
                    AddTls13HandShakeHeader(args->output + RECORD_HEADER_SZ, payloadSz, 0, fragSz, certificate_verify, ssl);
                    args->sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ +
                                   fragSz;
                }
                else
                    AddTls13RecordHeader(args->output, fragSz, handshake, ssl);

                ssl->fragOffset += fragSz;
                length -= fragSz;

                /* This message is always encrypted. */
                args->sendSz = BuildTls13Message(ssl, args->output,
                                    MAX_CERT_VERIFY_SZ + MAX_MSG_EXTRA,
                                    args->output + RECORD_HEADER_SZ,
                                    args->sendSz - RECORD_HEADER_SZ, handshake, 1, 0, 0);
                if (args->sendSz < 0)
                    return args->sendSz;

                ssl->buffers.outputBuffer.length += args->sendSz;
                if (!ssl->options.groupMessages)
                    ret = SendBuffered(ssl);
            }

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_END;
        } /* case TLS_ASYNC_FINALIZE */
        FALL_THROUGH;

        case TLS_ASYNC_END:
        {
            /* Clean up the fragment offset */
            ssl->fragOffset = 0;

            if (ret < 0) {
                goto exit_scv;
            }
            else {
                args->sendSz = ret;
                ret = 0;
            }

        #ifdef WOLFSSL_CALLBACKS
            if (ssl->hsInfoOn)
                AddPacketName(ssl, "CertificateVerify");
            if (ssl->toInfoOn) {
                AddPacketInfo(ssl, "CertificateVerify", handshake,
                            args->output, args->sendSz, WRITE_PROTO, ssl->heap);
            }
        #endif

            ssl->buffers.outputBuffer.length += args->sendSz;

            if (!ssl->options.groupMessages)
                ret = SendBuffered(ssl);
            break;
        }
        default:
            ret = INPUT_CASE_ERROR;
    } /* switch(ssl->options.asyncState) */

exit_scv:

    WOLFSSL_LEAVE("SendTls13CertificateVerify", ret);
    WOLFSSL_END(WC_FUNC_CERTIFICATE_VERIFY_SEND);

#ifdef WOLFSSL_ASYNC_CRYPT
    /* Handle async operation */
    if (ret == WC_PENDING_E) {
        return ret;
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    /* Final cleanup */
    FreeScv13Args(ssl, args);
    FreeKeyExchange(ssl);

    return ret;
}

Please give me some clues how to fix this. Thanks in advance!

Regards,
Yulia

Share

Re: Fragmentation of CertificateVerify messages

Hello Yulia,

Can you tell us a bit about what is driving this effort?

From a high level lets' dig into one of your notes:

For now, the first fragment seems to be correct, but the second fragment contains some garbage value (partially from the previous fragment of the Server Certificate message).

WHERE in the second fragment are the garbage values? Are they at the head or the tail of the message?

If they are at the HEAD don't forget the first 5-bytes of any TLS packet are the header denoting what kind of message (a handshake message in this case), the protocol version (TLS 1.3 in this case) and the length of the packet to follow (up to 16k). You'll need to change the length value to reflect the remainder that wasn't sent in the first fragment.

If the garbage values are at the TAIL of the message that might mean you didn't XMEMSET the transmit buffer before copying in the remainder. IE if you copied 16Kb in for the first fragment and the remainder is only 2Kb lets' say, and you just copy it in without clearing the first fragment you'll just be updating the first 2Kb and the final 14Kb will be from the first fragment if that makes sense.

Hope this helps get you on the right track, let me know if you have any followup questions.

- KH

3 (edited by ykuz 2021-01-13 05:31:18)

Re: Fragmentation of CertificateVerify messages

Hello Kaleb,

thanks for your response.

Can you tell us a bit about what is driving this effort?

I am working on a research project on Post-quantum TLS. I already integrated a bunch of PQ algorithms into wolfSSL, but I am struggling with the ones with bigger signatures because of the fragmentation.

WHERE in the second fragment are the garbage values? Are they at the head or the tail of the message?

There is garbage at the beginning of the second fragment (it overwrites the part of the signature data) and also after the end of the signature.

, let me know if you have any followup questions.

I am not sure, if I calculated the overall size correctly; it is also not quite clear, how the fragments should look like.

So the first fragment is:
    | RECORD_HEADER | HANDSHAKE_HEADER | DATA_1 |
   
The second (and the remaining ones) are: 
    | RECORD_HEADER | HANDSHAKE_HEADER | DATA_2 |
   
The data which hast to be send is
        DATA = | DATA_1 | DATA_2 | = | HASH_SIG_SIZE | VERIFY_HEADER | SIGNATURE |
        (divided into 2 or more parts).


Does it make sense so far? Is my understanding correct?

Share