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