/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
//** Includes
#include "Tpm.h"
#define _OIDS_
#include "OIDs.h"
#include "TpmASN1.h"
#include "TpmASN1_fp.h"

//** Unmarshaling Functions

//*** ASN1UnmarshalContextInitialize()
// Function does standard initialization of a context.
//  Return Type: BOOL
//      TRUE(1)     success
//      FALSE(0)    failure
BOOL ASN1UnmarshalContextInitialize(
    ASN1UnmarshalContext* ctx, INT16 size, BYTE* buffer)
{
    VERIFY(buffer != NULL);
    VERIFY(size > 0);
    ctx->buffer = buffer;
    ctx->size   = size;
    ctx->offset = 0;
    ctx->tag    = 0xFF;
    return TRUE;
Error:
    return FALSE;
}

//***ASN1DecodeLength()
// This function extracts the length of an element from 'buffer' starting at 'offset'.
// Return Type: UINT16
//      >=0         the extracted length
//      <0          an error
INT16
ASN1DecodeLength(ASN1UnmarshalContext* ctx)
{
    BYTE  first;  // Next octet in buffer
    INT16 value;
    //
    VERIFY(ctx->offset < ctx->size);
    first = NEXT_OCTET(ctx);
    // If the number of octets of the entity is larger than 127, then the first octet
    // is the number of octets in the length specifier.
    if(first >= 0x80)
    {
        // Make sure that this length field is contained with the structure being
        // parsed
        CHECK_SIZE(ctx, (first & 0x7F));
        if(first == 0x82)
        {
            // Two octets of size
            // get the next value
            value = (INT16)NEXT_OCTET(ctx);
            // Make sure that the result will fit in an INT16
            VERIFY(value < 0x0080);
            // Shift up and add next octet
            value = (value << 8) + NEXT_OCTET(ctx);
        }
        else if(first == 0x81)
            value = NEXT_OCTET(ctx);
        // Sizes larger than will fit in a INT16 are an error
        else
            goto Error;
    }
    else
        value = first;
    // Make sure that the size defined something within the current context
    CHECK_SIZE(ctx, value);
    return value;
Error:
    ctx->size = -1;  // Makes everything fail from now on.
    return -1;
}

//***ASN1NextTag()
// This function extracts the next type from 'buffer' starting at 'offset'.
// It advances 'offset' as it parses the type and the length of the type. It returns
// the length of the type. On return, the 'length' octets starting at 'offset' are the
// octets of the type.
// Return Type: UINT
//     >=0          the number of octets in 'type'
//     <0           an error
INT16
ASN1NextTag(ASN1UnmarshalContext* ctx)
{
    // A tag to get?
    VERIFY(ctx->offset < ctx->size);
    // Get it
    ctx->tag = NEXT_OCTET(ctx);
    // Make sure that it is not an extended tag
    VERIFY((ctx->tag & 0x1F) != 0x1F);
    // Get the length field and return that
    return ASN1DecodeLength(ctx);

Error:
    // Attempt to read beyond the end of the context or an illegal tag
    ctx->size = -1;  // Persistent failure
    ctx->tag  = 0xFF;
    return -1;
}

//*** ASN1GetBitStringValue()
// Try to parse a bit string of up to 32 bits from a value that is expected to be
// a bit string. The bit string is left justified so that the MSb of the input is
// the MSb of the returned value.
// If there is a general parsing error, the context->size is set to -1.
//  Return Type: BOOL
//      TRUE(1)     success
//      FALSE(0)    failure
BOOL ASN1GetBitStringValue(ASN1UnmarshalContext* ctx, UINT32* val)
{
    int    shift;
    INT16  length;
    UINT32 value = 0;
    int    inputBits;
    //
    length = ASN1NextTag(ctx);
    VERIFY(length >= 1);
    VERIFY(ctx->tag == ASN1_BITSTRING);
    // Get the shift value for the bit field (how many bits to lop off of the end)
    shift = NEXT_OCTET(ctx);
    length--;
    // Get the number of bits in the input
    inputBits = (8 * length) - shift;
    // the shift count has to make sense
    VERIFY((shift < 8) && ((length > 0) || (shift == 0)));
    // if there are any bytes left
    for(; length > 1; length--)
    {

        // for all but the last octet, just shift and add the new octet
        VERIFY((value & 0xFF000000) == 0);  // can't loose significant bits
        value = (value << 8) + NEXT_OCTET(ctx);
    }
    if(length == 1)
    {
        // for the last octet, just shift the accumulated value enough to
        // accept the significant bits in the last octet and shift the last
        // octet down
        VERIFY(((value & (0xFF000000 << (8 - shift)))) == 0);
        value = (value << (8 - shift)) + (NEXT_OCTET(ctx) >> shift);
    }
    // 'Left justify' the result
    if(inputBits > 0)
        value <<= (32 - inputBits);
    *val = value;
    return TRUE;
Error:
    ctx->size = -1;
    return FALSE;
}

//*******************************************************************
//** Marshaling Functions
//*******************************************************************

//*** Introduction
// Marshaling of an ASN.1 structure is accomplished from the bottom up. That is,
// the things that will be at the end of the structure are added last. To manage the
// collecting of the relative sizes, start a context for the outermost container, if
// there is one, and then placing items in from the bottom up. If the bottom-most
// item is also within a structure, create a nested context by calling
// ASN1StartMarshalingContext().
//
// The context control structure contains a 'buffer' pointer, an 'offset', an 'end'
// and a stack. 'offset' is the offset from the start of the buffer of the last added
// byte. When 'offset' reaches 0, the buffer is full. 'offset' is a signed value so
// that, when it becomes negative, there is an overflow. Only two functions are
// allowed to move bytes into the buffer: ASN1PushByte() and ASN1PushBytes(). These
// functions make sure that no data is written beyond the end of the buffer.
//
// When a new context is started, the current value of 'end' is pushed
// on the stack and 'end' is set to 'offset. As bytes are added, offset gets smaller.
// At any time, the count of bytes in the current context is simply 'end' - 'offset'.
//
// Since starting a new context involves setting 'end' = 'offset', the number of bytes
// in the context starts at 0. The nominal way of ending a context is to use
// 'end' - 'offset' to set the length value, and then a tag is added to the buffer.
// Then the previous 'end' value is popped meaning that the context just ended
// becomes a member of the now current context.
//
// The nominal strategy for building a completed ASN.1 structure is to push everything
// into the buffer and then move everything to the start of the buffer. The move is
// simple as the size of the move is the initial 'end' value minus the final 'offset'
// value. The destination is 'buffer' and the source is 'buffer' + 'offset'. As Skippy
// would say "Easy peasy, Joe."
//
// It is not necessary to provide a buffer into which the data is placed. If no buffer
// is provided, then the marshaling process will return values needed for marshaling.
// On strategy for filling the buffer would be to execute the process for building
// the structure without using a buffer. This would return the overall size of the
// structure. Then that amount of data could be allocated for the buffer and the fill
// process executed again with the data going into the buffer. At the end, the data
// would be in its final resting place.

//*** ASN1InitialializeMarshalContext()
// This creates a structure for handling marshaling of an ASN.1 formatted data
// structure.
void ASN1InitialializeMarshalContext(
    ASN1MarshalContext* ctx, INT16 length, BYTE* buffer)
{
    ctx->buffer = buffer;
    if(buffer)
        ctx->offset = length;
    else
        ctx->offset = INT16_MAX;
    ctx->end   = ctx->offset;
    ctx->depth = -1;
}

//*** ASN1StartMarshalContext()
// This starts a new constructed element. It is constructed on 'top' of the value
// that was previously placed in the structure.
void ASN1StartMarshalContext(ASN1MarshalContext* ctx)
{
    pAssert((ctx->depth + 1) < MAX_DEPTH);
    ctx->depth++;
    ctx->ends[ctx->depth] = ctx->end;
    ctx->end              = ctx->offset;
}

//*** ASN1EndMarshalContext()
// This function restores the end pointer for an encapsulating structure.
//  Return Type: INT16
//      > 0             the size of the encapsulated structure that was just ended
//      <= 0            an error
INT16
ASN1EndMarshalContext(ASN1MarshalContext* ctx)
{
    INT16 length;
    pAssert(ctx->depth >= 0);
    length   = ctx->end - ctx->offset;
    ctx->end = ctx->ends[ctx->depth--];
    if((ctx->depth == -1) && (ctx->buffer))
    {
        MemoryCopy(ctx->buffer, ctx->buffer + ctx->offset, ctx->end - ctx->offset);
    }
    return length;
}

//***ASN1EndEncapsulation()
// This function puts a tag and length in the buffer. In this function, an embedded
// BIT_STRING is assumed to be a collection of octets. To indicate that all bits
// are used, a byte of zero is prepended. If a raw bit-string is needed, a new
// function like ASN1PushInteger() would be needed.
//  Return Type: INT16
//      > 0         number of octets in the encapsulation
//      == 0        failure
UINT16
ASN1EndEncapsulation(ASN1MarshalContext* ctx, BYTE tag)
{
    // only add a leading zero for an encapsulated BIT STRING
    if(tag == ASN1_BITSTRING)
        ASN1PushByte(ctx, 0);
    ASN1PushTagAndLength(ctx, tag, ctx->end - ctx->offset);
    return ASN1EndMarshalContext(ctx);
}

//*** ASN1PushByte()
BOOL ASN1PushByte(ASN1MarshalContext* ctx, BYTE b)
{
    if(ctx->offset > 0)
    {
        ctx->offset -= 1;
        if(ctx->buffer)
            ctx->buffer[ctx->offset] = b;
        return TRUE;
    }
    ctx->offset = -1;
    return FALSE;
}

//*** ASN1PushBytes()
// Push some raw bytes onto the buffer. 'count' cannot be zero.
//  Return Type: IN16
//      > 0             count bytes
//      == 0            failure unless count was zero
INT16
ASN1PushBytes(ASN1MarshalContext* ctx, INT16 count, const BYTE* buffer)
{
    // make sure that count is not negative which would mess up the math; and that
    // if there is a count, there is a buffer
    VERIFY((count >= 0) && ((buffer != NULL) || (count == 0)));
    // back up the offset to determine where the new octets will get pushed
    ctx->offset -= count;
    // can't go negative
    VERIFY(ctx->offset >= 0);
    // if there are buffers, move the data, otherwise, assume that this is just a
    // test.
    if(count && buffer && ctx->buffer)
        MemoryCopy(&ctx->buffer[ctx->offset], buffer, count);
    return count;
Error:
    ctx->offset = -1;
    return 0;
}

//*** ASN1PushNull()
//  Return Type: IN16
//      > 0             count bytes
//      == 0            failure unless count was zero
INT16
ASN1PushNull(ASN1MarshalContext* ctx)
{
    ASN1PushByte(ctx, 0);
    ASN1PushByte(ctx, ASN1_NULL);
    return (ctx->offset >= 0) ? 2 : 0;
}

//*** ASN1PushLength()
// Push a length value. This will only handle length values that fit in an INT16.
//  Return Type: UINT16
//      > 0         number of bytes added
//      == 0        failure
INT16
ASN1PushLength(ASN1MarshalContext* ctx, INT16 len)
{
    UINT16 start = ctx->offset;
    VERIFY(len >= 0);
    if(len <= 127)
        ASN1PushByte(ctx, (BYTE)len);
    else
    {
        ASN1PushByte(ctx, (BYTE)(len & 0xFF));
        len >>= 8;
        if(len == 0)
            ASN1PushByte(ctx, 0x81);
        else
        {
            ASN1PushByte(ctx, (BYTE)(len));
            ASN1PushByte(ctx, 0x82);
        }
    }
    goto Exit;
Error:
    ctx->offset = -1;
Exit:
    return (ctx->offset > 0) ? start - ctx->offset : 0;
}

//*** ASN1PushTagAndLength()
//  Return Type: INT16
//      > 0         number of bytes added
//      == 0        failure
INT16
ASN1PushTagAndLength(ASN1MarshalContext* ctx, BYTE tag, INT16 length)
{
    INT16 bytes;
    bytes = ASN1PushLength(ctx, length);
    bytes += (INT16)ASN1PushByte(ctx, tag);
    return (ctx->offset < 0) ? 0 : bytes;
}

//*** ASN1PushTaggedOctetString()
// This function will push a random octet string.
//  Return Type: INT16
//      > 0         number of bytes added
//      == 0        failure
INT16
ASN1PushTaggedOctetString(
    ASN1MarshalContext* ctx, INT16 size, const BYTE* string, BYTE tag)
{
    ASN1PushBytes(ctx, size, string);
    // PushTagAndLenght just tells how many octets it added so the total size of this
    // element is the sum of those octets and input size.
    size += ASN1PushTagAndLength(ctx, tag, size);
    return size;
}

//*** ASN1PushUINT()
// This function pushes an native-endian integer value. This just changes a
// native-endian integer into a big-endian byte string and calls ASN1PushInteger().
// That function will remove leading zeros and make sure that the number is positive.
//  Return Type: IN16
//      > 0             count bytes
//      == 0            failure unless count was zero
INT16
ASN1PushUINT(ASN1MarshalContext* ctx, UINT32 integer)
{
    BYTE marshaled[4];
    UINT32_TO_BYTE_ARRAY(integer, marshaled);
    return ASN1PushInteger(ctx, 4, marshaled);
}

//*** ASN1PushInteger
// Push a big-endian integer on the end of the buffer
//  Return Type: UINT16
//      > 0         the number of bytes marshaled for the integer
//      == 0        failure
INT16
ASN1PushInteger(ASN1MarshalContext* ctx,     // IN/OUT: buffer context
                INT16               iLen,    // IN: octets of the integer
                BYTE*               integer  // IN: big-endian integer
)
{
    // no leading 0's
    while((*integer == 0) && (--iLen > 0))
        integer++;
    // Move the bytes to the buffer
    ASN1PushBytes(ctx, iLen, integer);
    // if needed, add a leading byte of 0 to make the number positive
    if(*integer & 0x80)
        iLen += (INT16)ASN1PushByte(ctx, 0);
    // PushTagAndLength just tells how many octets it added so the total size of this
    // element is the sum of those octets and the adjusted input size.
    iLen += ASN1PushTagAndLength(ctx, ASN1_INTEGER, iLen);
    return iLen;
}

//*** ASN1PushOID()
// This function is used to add an OID. An OID is 0x06 followed by a byte of size
// followed by size bytes. This is used to avoid having to do anything special in the
// definition of an OID.
//  Return Type: UINT16
//      > 0         the number of bytes marshaled for the integer
//      == 0        failure
INT16
ASN1PushOID(ASN1MarshalContext* ctx, const BYTE* OID)
{
    if((*OID == ASN1_OBJECT_IDENTIFIER) && ((OID[1] & 0x80) == 0))
    {
        return ASN1PushBytes(ctx, OID[1] + 2, OID);
    }
    ctx->offset = -1;
    return 0;
}
