summaryrefslogtreecommitdiffstats
path: root/src/tpm2/TpmAsn1.c
blob: f23b2a9b8fc2f93efb39810a5c463ce5f612b693 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
/********************************************************************************/
/*										*/
/*			     TPM ASN.1						*/
/*			     Written by Ken Goldman				*/
/*		       IBM Thomas J. Watson Research Center			*/
/*            $Id: TpmAsn1.c 1519 2019-11-15 20:43:51Z kgoldman $		*/
/*										*/
/*  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					*/
/*										*/
/********************************************************************************/

/* 10.2.23	TpmAsn1.c */
/* 10.2.23.1	Includes */
#include "Tpm.h"
#define _OIDS_
#include "OIDs.h"
#include "TpmAsn1.h"
#include "TpmAsn1_fp.h"
/* 10.2.23.2	Unmarshaling Functions */
/* 10.2.23.2.1	ASN1UnmarshalContextInitialize() */
/* Function does standard initialization of a context. */
/* Return Value	Meaning */
/* 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;
}
/* 10.2.23.2.2	ASN1DecodeLength() */
/* This function extracts the length of an element from buffer starting at offset. */
/* Return Value	Meaning */
/* >=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;
}
/* 10.2.23.2.3	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 Value	Meaning */
/*     >=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;
}
/* 10.2.23.2.4	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 Value	Meaning */
/*     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;
}

/* 10.2.23.3	Marshaling Functions */
/* 10.2.23.3.1	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. */
/* 10.2.23.3.2	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;
}
/* 10.2.23.3.3	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;
}
/* 10.2.23.3.4	ASN1EndMarshalContext() */
/* This function restores the end pointer for an encapsulating structure. */
/* Return Value	Meaning */
/* > 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;
}
/* 10.2.23.3.5 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 Value	Meaning */
/*     > 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);
}
/* 10.2.23.3.6	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;
}
/* 10.2.23.3.7	ASN1PushBytes() */
/* Push some raw bytes onto the buffer. count cannot be zero. */
/* Return Value	Meaning */
/* > 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;
}
/* 10.2.23.3.8	ASN1PushNull() */
/* Return Value	Meaning */
/* > 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;
}
/* 10.2.23.3.9	ASN1PushLength() */
/* Push a length value. This will only handle length values that fit in an INT16. */
/* Return Value	Meaning */
/* > 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;
}
/* 10.2.23.3.10	ASN1PushTagAndLength() */
/* Return Value	Meaning */
/* > 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;
}
/* 10.2.23.3.11	ASN1PushTaggedOctetString() */
/* This function will push a random octet string. */
/* Return Value	Meaning */
/* > 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;
}
/* 10.2.23.3.12	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 Value	Meaning */
/* > 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);
}
/* 10.2.23.3.13	ASN1PushInteger */
/* Push a big-endian integer on the end of the buffer */
/* Return Value	Meaning */
/* > 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;
}
/* 10.2.23.3.14	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 Value	Meaning */
/* > 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;
}