blob: f5bc183be9102f2d24cc736ecd84684224b191d7 [file] [log] [blame]
/* 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);
// 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;
}