xref: /titanic_52/usr/src/lib/libsmbfs/smb/derparse.c (revision c0dd49bdd68c0d758a67d56f07826f3b45cfc664)
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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