//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1995.
//
//  File:       pickle.c
//
//  Contents:
//
//  Classes:
//
//  Functions:
//
//  History:    8-02-95   RichardW   Created
//
//----------------------------------------------------------------------------

#include <spbase.h>
#include <ssl2msg.h>

#define SSL_OFFSET_OF(t, v) ((DWORD)(ULONG_PTR)&(((t)NULL)->v))



#define SIZEOF(pMessage)    (SslRecordSize((PSSL2_MESSAGE_HEADER) pMessage ) )

DWORD
SslRecordSize(
    PSSL2_MESSAGE_HEADER  pHeader)
{
    DWORD   Size;

    if (pHeader->Byte0 & 0x80)
    {
        Size = COMBINEBYTES(pHeader->Byte0, pHeader->Byte1) & 0x7FFF;
    }
    else
    {
        Size = COMBINEBYTES(pHeader->Byte0, pHeader->Byte1) & 0x3FFF;
    }
    return(Size);
}



BOOL
Ssl2MapCipherToExternal(
    Ssl2_Cipher_Kind     FastForm,
    PSsl2_Cipher_Tuple   pTuple)
{
    pTuple->C1 = (UCHAR)((FastForm >> 16) & 0xff);
    pTuple->C2 = (UCHAR)((FastForm >> 8) & 0xff);
    pTuple->C3 = (UCHAR)(FastForm & 0xff);


    return(TRUE);
}

Ssl2_Cipher_Kind
Ssl2MapCipherFromExternal(
    PSsl2_Cipher_Tuple   pTuple)
{

    return SSL_MKFAST(pTuple->C1, pTuple->C2, pTuple->C3);
}



SP_STATUS
Ssl2PackClientHello(
    PSsl2_Client_Hello       pCanonical,
    PSPBuffer                pCommOutput)
{
    DWORD               cbMessage;
    PSSL2_CLIENT_HELLO  pMessage;
    DWORD               Size;
    PUCHAR              pBuffer;
    DWORD               i;

    if(pCanonical == NULL || pCommOutput == NULL)
    {
        return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
    }

    pCommOutput->cbData = 0;

    pCommOutput->cbData = pCanonical->cbSessionID +
                            pCanonical->cbChallenge +
                            pCanonical->cCipherSpecs * sizeof(Ssl2_Cipher_Tuple) +
                            SSL_OFFSET_OF(PSSL2_CLIENT_HELLO, VariantData);

    cbMessage = pCommOutput->cbData - sizeof(SSL2_MESSAGE_HEADER);

    /* are we allocating our own memory? */
    if(pCommOutput->pvBuffer == NULL)
    {
        pCommOutput->pvBuffer = SPExternalAlloc(pCommOutput->cbData);
        if (NULL == pCommOutput->pvBuffer)
        {
            return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
        }
        pCommOutput->cbBuffer = pCommOutput->cbData;
    }
    if(pCommOutput->cbData > pCommOutput->cbBuffer)
    {
        // Required buffer size returned in pCommOutput->cbData.
        return SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL);
    }

    pMessage = pCommOutput->pvBuffer;

    pMessage->MessageId = SSL2_MT_CLIENT_HELLO;

    pMessage->VersionMsb = MSBOF(pCanonical->dwVer);
    pMessage->VersionLsb = LSBOF(pCanonical->dwVer);

    pBuffer = pMessage->VariantData;

    cbMessage -= pCanonical->cCipherSpecs * sizeof(Ssl2_Cipher_Tuple);
    pCommOutput->cbData -= pCanonical->cCipherSpecs * sizeof(Ssl2_Cipher_Tuple);

    Size = 0;

    for (i = 0; i < pCanonical->cCipherSpecs ; i++ )
    {
        if (!Ssl2MapCipherToExternal(pCanonical->CipherSpecs[i],
                                (PSsl2_Cipher_Tuple) pBuffer) )
        {
            continue;
        }

        pBuffer += sizeof(Ssl2_Cipher_Tuple);
        Size += sizeof(Ssl2_Cipher_Tuple);
    }

    cbMessage += Size;
    pCommOutput->cbData += Size;

    pCommOutput->cbData = cbMessage + 2;

    pMessage->Header.Byte0 = MSBOF(cbMessage) | 0x80;
    pMessage->Header.Byte1 = LSBOF(cbMessage);

    pMessage->CipherSpecsLenMsb = MSBOF(Size);
    pMessage->CipherSpecsLenLsb = LSBOF(Size);

    pMessage->SessionIdLenMsb = MSBOF(pCanonical->cbSessionID);
    pMessage->SessionIdLenLsb = LSBOF(pCanonical->cbSessionID);
    if (pCanonical->cbSessionID)
    {
        CopyMemory( pBuffer,
                    pCanonical->SessionID,
                    pCanonical->cbSessionID);

        pBuffer += pCanonical->cbSessionID;
    }

    pMessage->ChallengeLenMsb = MSBOF(pCanonical->cbChallenge);
    pMessage->ChallengeLenLsb = LSBOF(pCanonical->cbChallenge);
    if (pCanonical->cbChallenge)
    {
        CopyMemory( pBuffer,
                    pCanonical->Challenge,
                    pCanonical->cbChallenge);
    }

    return(PCT_ERR_OK);
}


SP_STATUS
Ssl2UnpackClientHello(
    PSPBuffer              pInput,
    PSsl2_Client_Hello *   ppClient)
{

    PSSL2_CLIENT_HELLO   pMessage;
    DWORD               ReportedSize;
    DWORD               CipherSpecsSize;
    DWORD               cCipherSpecs;
    PSsl2_Client_Hello       pCanonical;
    PUCHAR              pBuffer;
    DWORD               Size;
    DWORD               i, dwVer;

    pMessage = pInput->pvBuffer;

    if(pInput->cbData < 2)
    {
        pInput->cbData = 2;
        return PCT_INT_INCOMPLETE_MSG;
    }

    ReportedSize = SIZEOF(pMessage);
    if ((ReportedSize+2) > pInput->cbData)
    {
        pInput->cbData = ReportedSize+2;
        return PCT_INT_INCOMPLETE_MSG;
    }
    if(ReportedSize < SSL_OFFSET_OF(PSSL2_CLIENT_HELLO, VariantData))
    {
        return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
    }

    if (pMessage->MessageId != SSL2_MT_CLIENT_HELLO) {
        return  SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
    }

    dwVer = COMBINEBYTES(pMessage->VersionMsb, pMessage->VersionLsb);

    if (dwVer  < 2) //VERSION 2 WILL COMPUTE TO 2 (00:02)
    {
        return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
    }

    CipherSpecsSize = COMBINEBYTES( pMessage->CipherSpecsLenMsb,
                                    pMessage->CipherSpecsLenLsb );


    *ppClient = NULL;
    /* check that this all fits into the message */
    if (SSL_OFFSET_OF(PSSL2_CLIENT_HELLO, VariantData)
        - sizeof(SSL2_MESSAGE_HEADER)       /* don't count the header */
        + CipherSpecsSize
        > ReportedSize)
    {
        return  SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
    }

        cCipherSpecs = CipherSpecsSize / sizeof(Ssl2_Cipher_Tuple);


    /* Allocate a buffer for the canonical client hello */
    pCanonical = (PSsl2_Client_Hello)SPExternalAlloc(
                                    sizeof(Ssl2_Client_Hello) +
                                    cCipherSpecs * sizeof(UNICipherMap));

    if (!pCanonical)
    {
        return(SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
    }

    pCanonical->dwVer = COMBINEBYTES(    pMessage->VersionMsb,
                            pMessage->VersionLsb );

    pBuffer = pMessage->VariantData;


    pCanonical->cCipherSpecs = cCipherSpecs;

    for (i = 0 ; i < cCipherSpecs ; i++ )
    {
        pCanonical->CipherSpecs[i] = Ssl2MapCipherFromExternal((PSsl2_Cipher_Tuple)
                                                    pBuffer);

        pBuffer += sizeof(Ssl2_Cipher_Tuple);
    }

    Size = COMBINEBYTES(    pMessage->SessionIdLenMsb,
                            pMessage->SessionIdLenLsb );

    if (Size <= SSL2_SESSION_ID_LEN)
    {
        CopyMemory( pCanonical->SessionID, pBuffer, Size);
        pBuffer += Size;

    }
    else
    {
        SPExternalFree( pCanonical );
        return PCT_ERR_ILLEGAL_MESSAGE;
    }

    pCanonical->cbSessionID = Size;

    Size = COMBINEBYTES(    pMessage->ChallengeLenMsb,
                            pMessage->ChallengeLenLsb );

    if ((Size > 0) && (Size <= SSL2_MAX_CHALLENGE_LEN))
    {
        CopyMemory( pCanonical->Challenge, pBuffer, Size );

        pBuffer += Size;
    }
    else
    {
        SPExternalFree( pCanonical );
        return  SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
    }

    pCanonical->cbChallenge = Size;

    *ppClient = pCanonical;
    pInput->cbData = ReportedSize + sizeof(SSL2_MESSAGE_HEADER);
    return( PCT_ERR_OK );

}

SP_STATUS
Ssl2PackServerHello(
    PSsl2_Server_Hello       pCanonical,
    PSPBuffer          pCommOutput)
{
    DWORD               cbMessage;
    PSSL2_SERVER_HELLO  pMessage;
    DWORD               Size;
    PUCHAR              pBuffer;
    DWORD               i;

    if(pCanonical == NULL || pCommOutput == NULL)
    {
        return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
    }
    pCommOutput->cbData = 0;

    cbMessage = pCanonical->cbConnectionID +
                    pCanonical->cbCertificate +
                    pCanonical->cCipherSpecs * sizeof(Ssl2_Cipher_Tuple) +
                    SSL_OFFSET_OF(PSSL2_SERVER_HELLO, VariantData) -
                    sizeof(SSL2_MESSAGE_HEADER);

    pCommOutput->cbData = cbMessage + 2;


    /* are we allocating our own memory? */
    if(pCommOutput->pvBuffer == NULL)
    {
        pCommOutput->pvBuffer = SPExternalAlloc(pCommOutput->cbData);
        if (NULL == pCommOutput->pvBuffer)
        {
            return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
        }
        pCommOutput->cbBuffer = pCommOutput->cbData;
    }
    if(pCommOutput->cbData > pCommOutput->cbBuffer)
    {
        // Required buffer size returned in pCommOutput->cbData.
        return SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL);
    }

    pMessage = pCommOutput->pvBuffer;


    pMessage->MessageId = SSL2_MT_SERVER_HELLO;
    pMessage->ServerVersionMsb = SSL2_SERVER_VERSION_MSB;
    pMessage->ServerVersionLsb = SSL2_SERVER_VERSION_LSB;
    pMessage->SessionIdHit = (UCHAR) pCanonical->SessionIdHit;
    pMessage->CertificateType = (UCHAR) pCanonical->CertificateType;

    pBuffer = pMessage->VariantData;

    //
    // Pack certificate if present
    //

    pMessage->CertificateLenMsb = MSBOF(pCanonical->cbCertificate);
    pMessage->CertificateLenLsb = LSBOF(pCanonical->cbCertificate);

    if (pCanonical->cbCertificate)
    {
        CopyMemory( pBuffer,
                    pCanonical->pCertificate,
                    pCanonical->cbCertificate);

        pBuffer += pCanonical->cbCertificate ;
    }

    Size = pCanonical->cCipherSpecs * sizeof(Ssl2_Cipher_Tuple);

    for (i = 0; i < pCanonical->cCipherSpecs ; i++ )
    {
        if (Ssl2MapCipherToExternal(pCanonical->pCipherSpecs[i],
                                (PSsl2_Cipher_Tuple) pBuffer) )
        {
            pBuffer += sizeof(Ssl2_Cipher_Tuple);
        }
        else
        {
            Size -= sizeof(Ssl2_Cipher_Tuple);
            cbMessage -= sizeof(Ssl2_Cipher_Tuple);
        }
    }

    pCommOutput->cbData = cbMessage + 2;

    pMessage->Header.Byte0 = MSBOF(cbMessage) | 0x80;
    pMessage->Header.Byte1 = LSBOF(cbMessage);

    pMessage->CipherSpecsLenMsb = MSBOF(Size);
    pMessage->CipherSpecsLenLsb = LSBOF(Size);

    pMessage->ConnectionIdLenMsb = MSBOF(pCanonical->cbConnectionID);
    pMessage->ConnectionIdLenLsb = LSBOF(pCanonical->cbConnectionID);
    if (pCanonical->cbConnectionID)
    {
        CopyMemory( pBuffer,
                    pCanonical->ConnectionID,
                    pCanonical->cbConnectionID);

        pBuffer += pCanonical->cbConnectionID;
    }


    return( PCT_ERR_OK );

}


SP_STATUS
Ssl2UnpackServerHello(
    PSPBuffer          pInput,
    PSsl2_Server_Hello *     ppServer)
{
    PSsl2_Server_Hello       pCanonical;
    PSSL2_SERVER_HELLO   pMessage;
    PUCHAR              pBuffer;
    DWORD               cbCertificate;
    DWORD               cCipherSpecs;
    DWORD               cbConnId;
    DWORD               i;
    DWORD               ReportedSize;

    pMessage = pInput->pvBuffer;
    if(pInput->cbData < 2)
    {
        pInput->cbData = 2;
        return PCT_INT_INCOMPLETE_MSG;
    }

    ReportedSize = SIZEOF(pMessage);
    if ((ReportedSize+2) > pInput->cbData)
    {
        pInput->cbData = ReportedSize+2;
        return PCT_INT_INCOMPLETE_MSG;
    }


    if(ReportedSize <  SSL_OFFSET_OF(PSSL2_SERVER_HELLO, VariantData) )
    {
        return  SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
    }
    *ppServer = NULL;

    //
    // Verify Header:
    //

    if ((pMessage->MessageId != SSL2_MT_SERVER_HELLO) ||
        (pMessage->ServerVersionMsb != SSL2_SERVER_VERSION_MSB) ||
        (pMessage->ServerVersionLsb != SSL2_SERVER_VERSION_LSB) )
    {
        return  SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
    }

    cbCertificate = COMBINEBYTES(   pMessage->CertificateLenMsb,
                                    pMessage->CertificateLenLsb);

    cCipherSpecs = COMBINEBYTES(pMessage->CipherSpecsLenMsb,
                                pMessage->CipherSpecsLenLsb);

    cCipherSpecs /= sizeof(Ssl2_Cipher_Tuple);

    cbConnId = COMBINEBYTES(pMessage->ConnectionIdLenMsb,
                            pMessage->ConnectionIdLenLsb);

    pCanonical = (PSsl2_Server_Hello)SPExternalAlloc(
                        sizeof(Ssl2_Server_Hello) +
                        cCipherSpecs * sizeof(Ssl2_Cipher_Kind)  +
                        cbCertificate );


    if (!pCanonical)
    {
        return(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY));
    }

    pCanonical->pCertificate = (PUCHAR) (pCanonical + 1);
    pCanonical->pCipherSpecs = (PCipherSpec) (pCanonical + 1);
    pCanonical->pCertificate = (PUCHAR) (pCanonical->pCipherSpecs + cCipherSpecs);



    //
    // Expand out:
    //

    pCanonical->SessionIdHit = (DWORD) pMessage->SessionIdHit;
    pCanonical->CertificateType = (DWORD) pMessage->CertificateType;
    pCanonical->cbCertificate = cbCertificate;
    pCanonical->cCipherSpecs = cCipherSpecs;
    pCanonical->cbConnectionID = cbConnId;

    pBuffer = pMessage->VariantData;

    CopyMemory(pCanonical->pCertificate, pBuffer, cbCertificate);
    pBuffer += cbCertificate;

    for (i = 0 ; i < cCipherSpecs ; i++ )
    {
        pCanonical->pCipherSpecs[i] = Ssl2MapCipherFromExternal((PSsl2_Cipher_Tuple)
                                                                pBuffer);

        pBuffer += sizeof(Ssl2_Cipher_Tuple);
    }

    if ((cbConnId) && (cbConnId <= SSL2_MAX_CONNECTION_ID_LEN))
    {
        CopyMemory(pCanonical->ConnectionID, pBuffer, cbConnId);
    }
    else
    {
        SPExternalFree(pCanonical);
        return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
    }

    *ppServer = pCanonical;
    pInput->cbData = ReportedSize + sizeof(SSL2_MESSAGE_HEADER);
    return( PCT_ERR_OK);

}

SP_STATUS
Ssl2PackClientMasterKey(
    PSsl2_Client_Master_Key      pCanonical,
    PSPBuffer              pCommOutput)
{
    DWORD                   cbMessage;
    PSSL2_CLIENT_MASTER_KEY pMessage;
    PUCHAR                  pBuffer;

    cbMessage = pCanonical->ClearKeyLen +
                    pCanonical->EncryptedKeyLen +
                    pCanonical->KeyArgLen +
                    SSL_OFFSET_OF(PSSL2_CLIENT_MASTER_KEY, VariantData) -
                    sizeof(SSL2_MESSAGE_HEADER);

    pCommOutput->cbData = cbMessage + 2;

        /* are we allocating our own memory? */
    if(pCommOutput->pvBuffer == NULL)
    {
        pCommOutput->pvBuffer = SPExternalAlloc(pCommOutput->cbData);
        if (NULL == pCommOutput->pvBuffer)
        {
            return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
        }
        pCommOutput->cbBuffer = pCommOutput->cbData;
    }
    if(pCommOutput->cbData > pCommOutput->cbBuffer)
    {
        // Required buffer size returned in pCommOutput->cbData.
        return SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL);
    }
    pMessage = pCommOutput->pvBuffer;

    pBuffer = pMessage->VariantData;

    pMessage->Header.Byte0 = MSBOF(cbMessage) | 0x80;
    pMessage->Header.Byte1 = LSBOF(cbMessage);

    pMessage->MessageId = SSL2_MT_CLIENT_MASTER_KEY;
    Ssl2MapCipherToExternal(pCanonical->CipherKind, &pMessage->CipherKind);

    pMessage->ClearKeyLenMsb = MSBOF(pCanonical->ClearKeyLen);
    pMessage->ClearKeyLenLsb = LSBOF(pCanonical->ClearKeyLen);

    CopyMemory(pBuffer, pCanonical->ClearKey, pCanonical->ClearKeyLen);
    pBuffer += pCanonical->ClearKeyLen;

    pMessage->EncryptedKeyLenMsb = MSBOF(pCanonical->EncryptedKeyLen);
    pMessage->EncryptedKeyLenLsb = LSBOF(pCanonical->EncryptedKeyLen);

    CopyMemory(pBuffer, pCanonical->pbEncryptedKey, pCanonical->EncryptedKeyLen);
    pBuffer += pCanonical->EncryptedKeyLen;

    pMessage->KeyArgLenMsb = MSBOF(pCanonical->KeyArgLen);
    pMessage->KeyArgLenLsb = LSBOF(pCanonical->KeyArgLen);

    CopyMemory(pBuffer, pCanonical->KeyArg, pCanonical->KeyArgLen);

    return(PCT_ERR_OK);

}


SP_STATUS
Ssl2UnpackClientMasterKey(
    PSPBuffer              pInput,
    PSsl2_Client_Master_Key *    ppClient)
{
    PSsl2_Client_Master_Key  pCanonical;
    PSSL2_CLIENT_MASTER_KEY  pMessage;
    PUCHAR              pBuffer;
    DWORD               ReportedSize;
    DWORD               EncryptedKeyLen;

    pMessage = pInput->pvBuffer;

    if(pInput->cbData < 2)
    {
        pInput->cbData = 2;
        return PCT_INT_INCOMPLETE_MSG;
    }
    ReportedSize = SIZEOF(pMessage);

    if ((ReportedSize+2) > pInput->cbData)
    {
        pInput->cbData = ReportedSize+2;
        return PCT_INT_INCOMPLETE_MSG;
    }

    if(ReportedSize < SSL_OFFSET_OF(PSSL2_CLIENT_MASTER_KEY, VariantData))
    {
        return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
    }

    *ppClient = NULL;

    if ((pMessage->MessageId != SSL2_MT_CLIENT_MASTER_KEY))
    {
        return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
    }

    EncryptedKeyLen = COMBINEBYTES( pMessage->EncryptedKeyLenMsb,
                                    pMessage->EncryptedKeyLenLsb );

    pCanonical = (PSsl2_Client_Master_Key)SPExternalAlloc(
                            sizeof(Ssl2_Client_Master_Key) +
                            EncryptedKeyLen);

    if (!pCanonical)
    {
        return SP_LOG_RESULT( PCT_INT_INTERNAL_ERROR );
    }

    pCanonical->CipherKind = Ssl2MapCipherFromExternal( &pMessage->CipherKind );
    pCanonical->ClearKeyLen = COMBINEBYTES( pMessage->ClearKeyLenMsb,
                                            pMessage->ClearKeyLenLsb );

    pCanonical->EncryptedKeyLen = EncryptedKeyLen;

    pCanonical->KeyArgLen = COMBINEBYTES(   pMessage->KeyArgLenMsb,
                                            pMessage->KeyArgLenLsb );


    //
    // Validate
    //
    if ((pCanonical->ClearKeyLen > SSL2_MASTER_KEY_SIZE) ||
        (pCanonical->KeyArgLen > SSL2_MAX_KEY_ARGS))
    {
        SPExternalFree(pCanonical);
        return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
    }


    if ((SSL_OFFSET_OF(PSSL2_CLIENT_MASTER_KEY, VariantData) -
         sizeof(SSL2_MESSAGE_HEADER) +
         pCanonical->ClearKeyLen +
         pCanonical->EncryptedKeyLen +
         pCanonical->KeyArgLen ) !=
         ReportedSize)
    {
        SPExternalFree(pCanonical);
        return SP_LOG_RESULT(PCT_ERR_ILLEGAL_MESSAGE);
    }

    pBuffer = pMessage->VariantData;

    CopyMemory(pCanonical->ClearKey, pBuffer, pCanonical->ClearKeyLen );

    pBuffer += pCanonical->ClearKeyLen;

    pCanonical->pbEncryptedKey = (PBYTE)(pCanonical + 1);
    CopyMemory(pCanonical->pbEncryptedKey, pBuffer, pCanonical->EncryptedKeyLen );

    pBuffer += pCanonical->EncryptedKeyLen;

    CopyMemory( pCanonical->KeyArg, pBuffer, pCanonical->KeyArgLen );

    *ppClient = pCanonical;

    return( PCT_ERR_OK );
}