/********************************************************************************/
/*										*/
/*			     TPM ASN.1						*/
/*			     Written by Ken Goldman				*/
/*		       IBM Thomas J. Watson Research Center			*/
/*										*/
/*  Licenses and Notices							*/
/*										*/
/*  1. Copyright Licenses:							*/
/*										*/
/*  - Trusted Computing Group (TCG) grants to the user of the source code in	*/
/*    this specification (the "Source Code") a worldwide, irrevocable, 		*/
/*    nonexclusive, royalty free, copyright license to reproduce, create 	*/
/*    derivative works, distribute, display and perform the Source Code and	*/
/*    derivative works thereof, and to grant others the rights granted herein.	*/
/*										*/
/*  - The TCG grants to the user of the other parts of the specification 	*/
/*    (other than the Source Code) the rights to reproduce, distribute, 	*/
/*    display, and perform the specification solely for the purpose of 		*/
/*    developing products based on such documents.				*/
/*										*/
/*  2. Source Code Distribution Conditions:					*/
/*										*/
/*  - Redistributions of Source Code must retain the above copyright licenses, 	*/
/*    this list of conditions and the following disclaimers.			*/
/*										*/
/*  - Redistributions in binary form must reproduce the above copyright 	*/
/*    licenses, this list of conditions	and the following disclaimers in the 	*/
/*    documentation and/or other materials provided with the distribution.	*/
/*										*/
/*  3. Disclaimers:								*/
/*										*/
/*  - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF	*/
/*  LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH	*/
/*  RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES)	*/
/*  THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE.		*/
/*  Contact TCG Administration (admin@trustedcomputinggroup.org) for 		*/
/*  information on specification licensing rights available through TCG 	*/
/*  membership agreements.							*/
/*										*/
/*  - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED 	*/
/*    WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR 	*/
/*    FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR 		*/
/*    NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY 		*/
/*    OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE.		*/
/*										*/
/*  - Without limitation, TCG and its members and licensors disclaim all 	*/
/*    liability, including liability for infringement of any proprietary 	*/
/*    rights, relating to use of information in this specification and to the	*/
/*    implementation of this specification, and TCG disclaims all liability for	*/
/*    cost of procurement of substitute goods or services, lost profits, loss 	*/
/*    of use, loss of data or any incidental, consequential, direct, indirect, 	*/
/*    or special damages, whether under contract, tort, warranty or otherwise, 	*/
/*    arising in any way out of use or reliance upon this specification or any 	*/
/*    information herein.							*/
/*										*/
/*  (c) Copyright IBM Corp. and others, 2019 - 2023				*/
/*										*/
/********************************************************************************/

//** Includes
#include "Tpm.h"
#define _OIDS_
#include "OIDs.h"
#include "TpmASN1.h"
#include "TpmASN1_fp.h"

#if CC_CertifyX509

//** 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)
{
    GOTO_ERROR_UNLESS(buffer != NULL);
    GOTO_ERROR_UNLESS(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;
    //
    GOTO_ERROR_UNLESS(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
		    GOTO_ERROR_UNLESS(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?
    GOTO_ERROR_UNLESS(ctx->offset < ctx->size);
    // Get it
    ctx->tag = NEXT_OCTET(ctx);
    // Make sure that it is not an extended tag
    GOTO_ERROR_UNLESS((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);
    GOTO_ERROR_UNLESS(length >= 1);
    GOTO_ERROR_UNLESS(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
    GOTO_ERROR_UNLESS((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
	    GOTO_ERROR_UNLESS((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
	    GOTO_ERROR_UNLESS(((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--];
    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
    GOTO_ERROR_UNLESS((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
    GOTO_ERROR_UNLESS(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;
    GOTO_ERROR_UNLESS(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);
    // PushTagAndLenght 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;
}

#endif  // CC_CertifyX509
