1 // Copyright (C) 2002 Microsoft Corporation
2 // All rights reserved.
3 //
4 // THIS CODE AND INFORMATION IS PROVIDED "AS IS"
5 // WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
6 // OR IMPLIED, INCLUDING BUT NOT LIMITED
7 // TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY
8 // AND/OR FITNESS FOR A PARTICULAR PURPOSE.
9 //
10 // Date - 10/08/2002
11 // Author - Sanj Surati
12
13
14 /////////////////////////////////////////////////////////////
15 //
16 // DERPARSE.C
17 //
18 // SPNEGO Token Handler Source File
19 //
20 // Contains implementation of ASN.1 DER read/write functions
21 // as defined in DERPARSE.H.
22 //
23 /////////////////////////////////////////////////////////////
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <memory.h>
28 #include <sys/byteorder.h>
29 #include "spnego.h"
30 #include "derparse.h"
31
32 //
33 // The GSS Mechanism OID enumeration values (SPNEGO_MECH_OID) control which offset in
34 // the array below, that a mechanism can be found.
35 //
36
37 #pragma error_messages (off,E_INITIALIZATION_TYPE_MISMATCH)
38 MECH_OID g_stcMechOIDList [] =
39 {
40 {(unsigned char *)"\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02",
41 11, 9, spnego_mech_oid_Kerberos_V5_Legacy}, // 1.2.840.48018.1.2.2
42 {(unsigned char *)"\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02",
43 11, 9, spnego_mech_oid_Kerberos_V5}, // 1.2.840.113554.1.2.2
44 {(unsigned char *)"\x06\x06\x2b\x06\x01\x05\x05\x02",
45 8, 6, spnego_mech_oid_Spnego}, // 1.3.6.1.5.5.2
46 {(unsigned char *)"\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a",
47 12, 10, spnego_mech_oid_NTLMSSP}, // 1.3.6.1.4.1.311.2.2.10
48 {(unsigned char *)"", 0, 0, spnego_mech_oid_NotUsed // Placeholder
49 }
50 };
51 #pragma error_messages (default,E_INITIALIZATION_TYPE_MISMATCH)
52
53 /////////////////////////////////////////////////////////////////////////////
54 //
55 // Function:
56 // ASNDerGetLength
57 //
58 // Parameters:
59 // [in] pbLengthData - DER Length Data
60 // [in] nBoundaryLength - Length that value must not exceed.
61 // [out] pnLength - Filled out with length value
62 // [out] pnNumLengthBytes - Filled out with number of bytes
63 // consumed by DER length.
64 //
65 // Returns:
66 // int Success - SPNEGO_E_SUCCESS
67 // Failure - SPNEGO API Error code
68 //
69 // Comments :
70 // Interprets the data at pbLengthData as a DER length. The length must
71 // fit within the bounds of nBoundary length. We do not currently
72 // process lengths that take more than 4 bytes.
73 //
74 ////////////////////////////////////////////////////////////////////////////
75
ASNDerGetLength(unsigned char * pbLengthData,long nBoundaryLength,long * pnLength,long * pnNumLengthBytes)76 int ASNDerGetLength( unsigned char* pbLengthData, long nBoundaryLength, long* pnLength,
77 long* pnNumLengthBytes )
78 {
79 int nReturn = SPNEGO_E_INVALID_LENGTH;
80 int nNumLengthBytes = 0;
81
82 // First check if the extended length bit is set
83
84 if ( *pbLengthData & LEN_XTND )
85 {
86 // Lower 7 bits contain the number of trailing bytes that describe the length
87 nNumLengthBytes = *pbLengthData & LEN_MASK;
88
89 // Check that the number of bytes we are about to read is within our boundary
90 // constraints
91
92 if ( nNumLengthBytes <= nBoundaryLength - 1 )
93 {
94
95 // For now, our handler won't deal with lengths greater than 4 bytes
96 if ( nNumLengthBytes >= 1 && nNumLengthBytes <= 4 )
97 {
98 // 0 out the initial length
99 *pnLength = 0L;
100
101 // Bump by 1 byte
102 pbLengthData++;
103
104 #ifdef _LITTLE_ENDIAN
105
106 // There may be a cleaner way to do this, but for now, this seems to be
107 // an easy way to do the transformation
108 switch ( nNumLengthBytes )
109 {
110 case 1:
111 {
112 *( ( (unsigned char*) pnLength ) ) = *pbLengthData;
113 break;
114 }
115
116 case 2:
117 {
118 *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 1);
119 *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData);
120
121 break;
122 }
123
124 case 3:
125 {
126 *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 2);
127 *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1);
128 *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData);
129 break;
130 }
131
132 case 4:
133 {
134 *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 3);
135 *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData + 2);
136 *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1);
137 *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData);
138 break;
139 }
140
141 } // SWITCH ( nNumLengthBytes )
142
143 #else
144 // We are Big-Endian, so the length can be copied in from the source
145 // as is. Ensure that we adjust for the number of bytes we actually
146 // copy.
147
148 memcpy( ( (unsigned char *) pnLength ) + ( 4 - nNumLengthBytes ),
149 pbLengthData, nNumLengthBytes );
150 #endif
151
152 // Account for the initial length byte
153 *pnNumLengthBytes = nNumLengthBytes + 1;
154 nReturn = SPNEGO_E_SUCCESS;
155
156 } // IF Valid Length
157
158 } // IF num bytes to read is within the boundary length
159
160 } // IF xtended length
161 else
162 {
163
164 // Extended bit is not set, so the length is in the value and the one
165 // byte describes the length
166 *pnLength = *pbLengthData & LEN_MASK;
167 *pnNumLengthBytes = 1;
168 nReturn = SPNEGO_E_SUCCESS;
169
170 }
171
172 return nReturn;
173 }
174
175
176 /////////////////////////////////////////////////////////////////////////////
177 //
178 // Function:
179 // ASNDerCheckToken
180 //
181 // Parameters:
182 // [in] pbTokenData - Token Data
183 // [in] nToken - Token identifier to check for
184 // [in] nLengthWithToken - Expected token length (with data)
185 // [in] nBoundaryLength - Length that value must not exceed.
186 // [out] pnLength - Filled out with data length
187 // [out] pnTokenLength - Filled out with number of bytes
188 // consumed by token identifier and length.
189 //
190 // Returns:
191 // int Success - SPNEGO_E_SUCCESS
192 // Failure - SPNEGO API Error code
193 //
194 // Comments :
195 // Checks the data pointed to by pbTokenData for the specified token
196 // identifier and the length that immediately follows. If
197 // nLengthWithToken is > 0, the calculated length must match. The
198 // length must also not exceed the specified boundary length .
199 //
200 ////////////////////////////////////////////////////////////////////////////
201
ASNDerCheckToken(unsigned char * pbTokenData,unsigned char nToken,long nLengthWithToken,long nBoundaryLength,long * pnLength,long * pnTokenLength)202 int ASNDerCheckToken( unsigned char* pbTokenData, unsigned char nToken,
203 long nLengthWithToken, long nBoundaryLength,
204 long* pnLength, long* pnTokenLength )
205 {
206
207 int nReturn = SPNEGO_E_INVALID_LENGTH;
208 long nNumLengthBytes = 0L;
209
210 // Make sure that we've at least got 2 bytes of room to work with
211
212 if ( nBoundaryLength >= 2 )
213 {
214 // The first byte of the token data MUST match the specified token
215 if ( *pbTokenData == nToken )
216 {
217 // Next byte indicates the length
218 pbTokenData++;
219
220 // Get the length described by the token
221 if ( ( nReturn = ASNDerGetLength( pbTokenData, nBoundaryLength, pnLength,
222 &nNumLengthBytes ) ) == SPNEGO_E_SUCCESS )
223 {
224 // Verify that the length is LESS THAN the boundary length
225 // (this should prevent us walking out of our buffer)
226 if ( ( nBoundaryLength - ( nNumLengthBytes + 1 ) < *pnLength ) )
227 {
228
229 nReturn = SPNEGO_E_INVALID_LENGTH;
230
231 }
232
233 // If we were passed a length to check, do so now
234 if ( nLengthWithToken > 0L )
235 {
236
237 // Check that the expected length matches
238 if ( ( nLengthWithToken - ( nNumLengthBytes + 1 ) ) != *pnLength )
239 {
240
241 nReturn = SPNEGO_E_INVALID_LENGTH;
242
243 }
244
245 } // IF need to validate length
246
247 if ( SPNEGO_E_SUCCESS == nReturn )
248 {
249 *pnTokenLength = nNumLengthBytes + 1;
250 }
251
252 } // IF ASNDerGetLength
253
254 } // IF token matches
255 else
256 {
257 nReturn = SPNEGO_E_TOKEN_NOT_FOUND;
258 }
259
260 } // IF Boundary Length is at least 2 bytes
261
262 return nReturn;
263 }
264
265 /////////////////////////////////////////////////////////////////////////////
266 //
267 // Function:
268 // ASNDerCheckOID
269 //
270 // Parameters:
271 // [in] pbTokenData - Token Data
272 // [in] nMechOID - OID we are looking for
273 // [in] nBoundaryLength - Length that value must not exceed.
274 // [out] pnTokenLength - Filled out with number of bytes
275 // consumed by token and data.
276 //
277 // Returns:
278 // int Success - SPNEGO_E_SUCCESS
279 // Failure - SPNEGO API Error code
280 //
281 // Comments :
282 // Checks the data pointed to by pbTokenData for the specified OID.
283 //
284 ////////////////////////////////////////////////////////////////////////////
285
ASNDerCheckOID(unsigned char * pbTokenData,SPNEGO_MECH_OID nMechOID,long nBoundaryLength,long * pnTokenLength)286 int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long nBoundaryLength,
287 long* pnTokenLength )
288 {
289 int nReturn = 0L;
290 long nLength = 0L;
291
292 // Verify that we have an OID token
293 if ( ( nReturn = ASNDerCheckToken( pbTokenData, OID, 0L, nBoundaryLength,
294 &nLength, pnTokenLength ) ) == SPNEGO_E_SUCCESS )
295 {
296 // Add the data length to the Token Length
297 *pnTokenLength += nLength;
298
299 // Token Lengths plus the actual length must match the length in our OID list element.
300 // If it doesn't, we're done
301 if ( *pnTokenLength == g_stcMechOIDList[nMechOID].iLen )
302 {
303 // Memcompare the token and the expected field
304 if ( memcmp( pbTokenData, g_stcMechOIDList[nMechOID].ucOid, *pnTokenLength ) != 0 )
305 {
306 nReturn = SPNEGO_E_UNEXPECTED_OID;
307 }
308 }
309 else
310 {
311 nReturn = SPNEGO_E_UNEXPECTED_OID;
312 }
313
314 } // IF OID Token CHecks
315
316 return nReturn;
317 }
318
319 /////////////////////////////////////////////////////////////////////////////
320 //
321 // Function:
322 // ASNDerCalcNumLengthBytes
323 //
324 // Parameters:
325 // [in] nLength - Length to calculate length bytes for.
326 //
327 // Returns:
328 // int Number of bytes necessary to represent length
329 //
330 // Comments :
331 // Helper function to calculate the number of length bytes necessary to
332 // represent a length value. For our purposes, a 32-bit value should be
333 // enough to describea length.
334 //
335 ////////////////////////////////////////////////////////////////////////////
336
ASNDerCalcNumLengthBytes(long nLength)337 int ASNDerCalcNumLengthBytes( long nLength )
338 {
339 if ( nLength <= 0x7F )
340 {
341 // A single byte will be sufficient for describing this length.
342 // The byte will simply contain the length
343 return 1;
344 }
345 else if ( nLength <= 0xFF )
346 {
347 // Two bytes are necessary, one to say how many following bytes
348 // describe the length, and one to give the length
349 return 2;
350 }
351 else if ( nLength <= 0xFFFF )
352 {
353 // Three bytes are necessary, one to say how many following bytes
354 // describe the length, and two to give the length
355 return 3;
356 }
357 else if ( nLength <= 0xFFFFFF )
358 {
359 // Four bytes are necessary, one to say how many following bytes
360 // describe the length, and three to give the length
361 return 4;
362 }
363 else
364 {
365 // Five bytes are necessary, one to say how many following bytes
366 // describe the length, and four to give the length
367 return 5;
368 }
369 }
370
371
372 /////////////////////////////////////////////////////////////////////////////
373 //
374 // Function:
375 // ASNDerCalcTokenLength
376 //
377 // Parameters:
378 // [in] nLength - Length to calculate length bytes for.
379 // [in] nDataLength - Actual Data length value.
380 //
381 // Returns:
382 // long Number of bytes necessary to represent a token, length and data
383 //
384 // Comments :
385 // Helper function to calculate a token and value size, based on a
386 // supplied length value, and any binary data that will need to be
387 // written out.
388 //
389 ////////////////////////////////////////////////////////////////////////////
390
ASNDerCalcTokenLength(long nLength,long nDataLength)391 long ASNDerCalcTokenLength( long nLength, long nDataLength )
392 {
393 // Add a byte to the length size to account for a single byte to
394 // hold the token type.
395 long nTotalLength = ASNDerCalcNumLengthBytes( nLength ) + 1;
396
397 return nTotalLength + nDataLength;
398 }
399
400
401 /////////////////////////////////////////////////////////////////////////////
402 //
403 // Function:
404 // ASNDerCalcElementLength
405 //
406 // Parameters:
407 // [in] nDataLength - Length of data.
408 // [out] pnInternalLength - Filled out with length of element
409 // without sequence info.
410 //
411 // Returns:
412 // long Number of bytes necessary to represent an element
413 //
414 // Comments :
415 // Helper function to calculate an element length. An element consists
416 // of a sequence token, a type token and then the data.
417 //
418 ////////////////////////////////////////////////////////////////////////////
419
ASNDerCalcElementLength(long nDataLength,long * pnInternalLength)420 long ASNDerCalcElementLength( long nDataLength, long* pnInternalLength )
421 {
422 // First the type token and the actual data
423 long nTotalLength = ASNDerCalcTokenLength( nDataLength, nDataLength );
424
425 // Internal length is the length without the element sequence token
426 if ( NULL != pnInternalLength )
427 {
428 *pnInternalLength = nTotalLength;
429 }
430
431 // Next add in the element's sequence token (remember that its
432 // length is the total length of the type token and data)
433 nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L );
434
435 return nTotalLength;
436 }
437
438 /////////////////////////////////////////////////////////////////////////////
439 //
440 // Function:
441 // ASNDerCalcMechListLength
442 //
443 // Parameters:
444 // [in] mechoid - Mech OID to put in list.
445 // [out] pnInternalLength - Filled out with length of element
446 // without the primary sequence token.
447 //
448 // Returns:
449 // long Number of bytes necessary to represent a mechList
450 //
451 // Comments :
452 // Helper function to calculate a MechList length. A mechlist consists
453 // of a NegTokenInit sequence token, a sequence token for the MechList
454 // and finally a list of OIDs. In our case, we only really have one
455 // OID.
456 //
457 ////////////////////////////////////////////////////////////////////////////
458
ASNDerCalcMechListLength(SPNEGO_MECH_OID mechoid,long * pnInternalLength)459 long ASNDerCalcMechListLength( SPNEGO_MECH_OID mechoid, long* pnInternalLength )
460 {
461 // First the OID
462 long nTotalLength = g_stcMechOIDList[mechoid].iLen;
463
464 // Next add in a sequence token
465 nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L );
466
467 // Internal length is the length without the element sequence token
468 if ( NULL != pnInternalLength )
469 {
470 *pnInternalLength = nTotalLength;
471 }
472
473 // Finally add in the element's sequence token
474 nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L );
475
476 return nTotalLength;
477 }
478
479
480 /////////////////////////////////////////////////////////////////////////////
481 //
482 // Function:
483 // ASNDerWriteLength
484 //
485 // Parameters:
486 // [out] pbData - Buffer to write into.
487 // [in] nLength - Length to write out.
488 //
489 // Returns:
490 // int Number of bytes written out
491 //
492 // Comments :
493 // Helper function to write out a length value following DER rules .
494 //
495 ////////////////////////////////////////////////////////////////////////////
496
ASNDerWriteLength(unsigned char * pbData,long nLength)497 int ASNDerWriteLength( unsigned char* pbData, long nLength )
498 {
499 int nNumBytesRequired = ASNDerCalcNumLengthBytes( nLength );
500 int nNumLengthBytes = nNumBytesRequired - 1;
501
502
503 if ( nNumBytesRequired > 1 )
504 {
505
506 // Write out the number of bytes following which will be used
507 *pbData = (unsigned char ) ( LEN_XTND | nNumLengthBytes );
508
509 // Point to where we'll actually write the length
510 pbData++;
511
512 #ifdef _LITTLE_ENDIAN
513
514 // There may be a cleaner way to do this, but for now, this seems to be
515 // an easy way to do the transformation
516 switch ( nNumLengthBytes )
517 {
518 case 1:
519 {
520 // Cast the length to a single byte, since we know that it
521 // is 0x7F or less (or we wouldn't only need a single byte).
522
523 *pbData = (unsigned char) nLength;
524 break;
525 }
526
527 case 2:
528 {
529 *pbData = *( ( (unsigned char*) &nLength ) + 1 );
530 *( pbData + 1) = *( ( (unsigned char*) &nLength ) );
531 break;
532 }
533
534 case 3:
535 {
536 *pbData = *( ( (unsigned char*) &nLength ) + 3 );
537 *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 );
538 *( pbData + 2) = *( ( (unsigned char*) &nLength ) );
539 break;
540 }
541
542 case 4:
543 {
544 *pbData = *( ( (unsigned char*) &nLength ) + 3 );
545 *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 );
546 *( pbData + 2) = *( ( (unsigned char*) &nLength ) + 1 );
547 *( pbData + 3) = *( ( (unsigned char*) &nLength ) );
548 break;
549 }
550
551 } // SWITCH ( nNumLengthBytes )
552
553 #else
554 // We are Big-Endian, so the length can be copied in from the source
555 // as is. Ensure that we adjust for the number of bytes we actually
556 // copy.
557
558 memcpy( pbData,
559 ( (unsigned char *) &nLength ) + ( 4 - nNumLengthBytes ), nNumLengthBytes );
560 #endif
561
562 } // IF > 1 byte for length
563 else
564 {
565 // Cast the length to a single byte, since we know that it
566 // is 0x7F or less (or we wouldn't only need a single byte).
567
568 *pbData = (unsigned char) nLength;
569 }
570
571 return nNumBytesRequired;
572 }
573
574 /////////////////////////////////////////////////////////////////////////////
575 //
576 // Function:
577 // ASNDerWriteToken
578 //
579 // Parameters:
580 // [out] pbData - Buffer to write into.
581 // [in] ucType - Token Type
582 // [in] pbTokenValue - Actual Value
583 // [in] nLength - Length of Data.
584 //
585 // Returns:
586 // int Number of bytes written out
587 //
588 // Comments :
589 // Helper function to write out a token and any associated data. If
590 // pbTokenValue is non-NULL, then it is written out in addition to the
591 // token identifier and the length bytes.
592 //
593 ////////////////////////////////////////////////////////////////////////////
594
ASNDerWriteToken(unsigned char * pbData,unsigned char ucType,unsigned char * pbTokenValue,long nLength)595 int ASNDerWriteToken( unsigned char* pbData, unsigned char ucType,
596 unsigned char* pbTokenValue, long nLength )
597 {
598 int nTotalBytesWrittenOut = 0L;
599 int nNumLengthBytesWritten = 0L;
600
601 // Write out the type
602 *pbData = ucType;
603
604 // Wrote 1 byte, and move data pointer
605 nTotalBytesWrittenOut++;
606 pbData++;
607
608 // Now write out the length and adjust the number of bytes written out
609 nNumLengthBytesWritten = ASNDerWriteLength( pbData, nLength );
610
611 nTotalBytesWrittenOut += nNumLengthBytesWritten;
612 pbData += nNumLengthBytesWritten;
613
614 // Write out the token value if we got one. The assumption is that the
615 // nLength value indicates how many bytes are in pbTokenValue.
616
617 if ( NULL != pbTokenValue )
618 {
619 memcpy( pbData, pbTokenValue, nLength );
620 nTotalBytesWrittenOut += nLength;
621 }
622
623 return nTotalBytesWrittenOut;
624 }
625
626
627 /////////////////////////////////////////////////////////////////////////////
628 //
629 // Function:
630 // ASNDerWriteOID
631 //
632 // Parameters:
633 // [out] pbData - Buffer to write into.
634 // [in] eMechOID - OID to write out.
635 //
636 // Returns:
637 // int Number of bytes written out
638 //
639 // Comments :
640 // Helper function to write out an OID. For these we have the raw bytes
641 // listed in a global structure. The caller simply indicates which OID
642 // should be written and we will splat out the data.
643 //
644 ////////////////////////////////////////////////////////////////////////////
645
ASNDerWriteOID(unsigned char * pbData,SPNEGO_MECH_OID eMechOID)646 int ASNDerWriteOID( unsigned char* pbData, SPNEGO_MECH_OID eMechOID )
647 {
648
649 memcpy( pbData, g_stcMechOIDList[eMechOID].ucOid, g_stcMechOIDList[eMechOID].iLen );
650
651 return g_stcMechOIDList[eMechOID].iLen;
652 }
653
654
655 /////////////////////////////////////////////////////////////////////////////
656 //
657 // Function:
658 // ASNDerWriteMechList
659 //
660 // Parameters:
661 // [out] pbData - Buffer to write into.
662 // [in] eMechOID - OID to put in MechList.
663 //
664 // Returns:
665 // int Number of bytes written out
666 //
667 // Comments :
668 // Helper function to write out a MechList. A MechList consists of the
669 // Init Token Sequence, a sequence token and then the list of OIDs. In
670 // our case the OID is from a global array of known OIDs.
671 //
672 ////////////////////////////////////////////////////////////////////////////
673
ASNDerWriteMechList(unsigned char * pbData,SPNEGO_MECH_OID mechoid)674 long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID mechoid )
675 {
676 // First get the length
677 long nInternalLength = 0L;
678 long nMechListLength = ASNDerCalcMechListLength( mechoid, &nInternalLength );
679 long nTempLength = 0L;
680
681 nTempLength = ASNDerWriteToken( pbData, SPNEGO_NEGINIT_ELEMENT_MECHTYPES,
682 NULL, nInternalLength );
683
684 // Adjust the data pointer
685 pbData += nTempLength;
686
687 // Now write the Sequence token and the OID (the OID is a BLOB in the global
688 // structure.
689
690 nTempLength = ASNDerWriteToken( pbData, SPNEGO_CONSTRUCTED_SEQUENCE,
691 g_stcMechOIDList[mechoid].ucOid,
692 g_stcMechOIDList[mechoid].iLen );
693
694 return nMechListLength;
695 }
696
697
698 /////////////////////////////////////////////////////////////////////////////
699 //
700 // Function:
701 // ASNDerWriteElement
702 //
703 // Parameters:
704 // [out] pbData - Buffer to write into.
705 // [in] ucElementSequence - Sequence Token
706 // [in] ucType - Token Type
707 // [in] pbTokenValue - Actual Value
708 // [in] nLength - Length of Data.
709 //
710 // Returns:
711 // int Number of bytes written out
712 //
713 // Comments :
714 // Helper function to write out a SPNEGO Token element. An element
715 // consists of a sequence token, a type token and the associated data.
716 //
717 ////////////////////////////////////////////////////////////////////////////
718
ASNDerWriteElement(unsigned char * pbData,unsigned char ucElementSequence,unsigned char ucType,unsigned char * pbTokenValue,long nLength)719 int ASNDerWriteElement( unsigned char* pbData, unsigned char ucElementSequence,
720 unsigned char ucType, unsigned char* pbTokenValue, long nLength )
721 {
722 // First get the length
723 long nInternalLength = 0L;
724 long nElementLength = ASNDerCalcElementLength( nLength, &nInternalLength );
725 long nTempLength = 0L;
726
727 // Write out the sequence byte and the length of the type and data
728 nTempLength = ASNDerWriteToken( pbData, ucElementSequence, NULL, nInternalLength );
729
730 // Adjust the data pointer
731 pbData += nTempLength;
732
733 // Now write the type and the data.
734 nTempLength = ASNDerWriteToken( pbData, ucType, pbTokenValue, nLength );
735
736 return nElementLength;
737 }
738