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