summaryrefslogtreecommitdiff
path: root/usr/src/lib/libsmbfs/smb/derparse.c
diff options
context:
space:
mode:
authorthurlow <none@none>2008-02-13 19:51:22 -0800
committerthurlow <none@none>2008-02-13 19:51:22 -0800
commit4bff34e37def8a90f9194d81bc345c52ba20086a (patch)
tree7bf2710d9da099e3b07fea38e12788bfd565f3c5 /usr/src/lib/libsmbfs/smb/derparse.c
parenta916d99c7b27a531bf37c57f83b0b74120fd05bb (diff)
downloadillumos-joyent-4bff34e37def8a90f9194d81bc345c52ba20086a.tar.gz
PSARC 2005/695 CIFS Client on Solaris
PSARC 2007/303 pam_smb_login PSARC 2008/073 CIFS Client on Solaris - Updates 6651904 CIFS Client - PSARC 2005/695
Diffstat (limited to 'usr/src/lib/libsmbfs/smb/derparse.c')
-rw-r--r--usr/src/lib/libsmbfs/smb/derparse.c750
1 files changed, 750 insertions, 0 deletions
diff --git a/usr/src/lib/libsmbfs/smb/derparse.c b/usr/src/lib/libsmbfs/smb/derparse.c
new file mode 100644
index 0000000000..cc7d61c6bd
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/derparse.c
@@ -0,0 +1,750 @@
+/*
+// Copyright (C) 2002 Microsoft Corporation
+// All rights reserved.
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS"
+// WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+// OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY
+// AND/OR FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Date - 10/08/2002
+// Author - Sanj Surati
+
+
+/////////////////////////////////////////////////////////////
+//
+// DERPARSE.C
+//
+// SPNEGO Token Handler Source File
+//
+// Contains implementation of ASN.1 DER read/write functions
+// as defined in DERPARSE.H.
+//
+/////////////////////////////////////////////////////////////
+
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <sys/byteorder.h>
+#include "spnego.h"
+#include "derparse.h"
+
+/*
+//
+// The GSS Mechanism OID enumeration values (SPNEGO_MECH_OID) control which offset in
+// the array below, that a mechanism can be found.
+//
+*/
+#pragma error_messages (off,E_INITIALIZATION_TYPE_MISMATCH)
+MECH_OID g_stcMechOIDList [] =
+{
+ {"\x06\x09\x2a\x86\x48\x82\xf7\x12\x01\x02\x02", 11, 9,
+ spnego_mech_oid_Kerberos_V5_Legacy }, // 1.2.840.48018.1.2.2
+ {"\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02", 11, 9,
+ spnego_mech_oid_Kerberos_V5 }, // 1.2.840.113554.1.2.2
+ {"\x06\x06\x2b\x06\x01\x05\x05\x02", 8, 6,
+ spnego_mech_oid_Spnego }, // 1.3.6.1.5.5.2
+ {"\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a", 12, 10,
+ spnego_mech_oid_NTLMSSP }, // 1.3.6.1.4.1.311.2.2.10
+ {"", 0, 0, spnego_mech_oid_NotUsed } // Placeholder
+};
+#pragma error_messages (default,E_INITIALIZATION_TYPE_MISMATCH)
+
+/*
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerGetLength
+//
+// Parameters:
+// [in] pbLengthData - DER Length Data
+// [in] nBoundaryLength - Length that value must not exceed.
+// [out] pnLength - Filled out with length value
+// [out] pnNumLengthBytes - Filled out with number of bytes
+// consumed by DER length.
+//
+// Returns:
+// int Success - SPNEGO_E_SUCCESS
+// Failure - SPNEGO API Error code
+//
+// Comments :
+// Interprets the data at pbLengthData as a DER length. The length must
+// fit within the bounds of nBoundary length. We do not currently
+// process lengths that take more than 4 bytes.
+//
+////////////////////////////////////////////////////////////////////////////
+*/
+
+int ASNDerGetLength( unsigned char* pbLengthData, long nBoundaryLength, long* pnLength,
+ long* pnNumLengthBytes )
+{
+ int nReturn = SPNEGO_E_INVALID_LENGTH;
+ int nNumLengthBytes = 0;
+
+ // First check if the extended length bit is set
+
+ if ( *pbLengthData & LEN_XTND )
+ {
+ // Lower 7 bits contain the number of trailing bytes that describe the length
+ nNumLengthBytes = *pbLengthData & LEN_MASK;
+
+ // Check that the number of bytes we are about to read is within our boundary
+ // constraints
+
+ if ( nNumLengthBytes <= nBoundaryLength - 1 )
+ {
+
+ // For now, our handler won't deal with lengths greater than 4 bytes
+ if ( nNumLengthBytes >= 1 && nNumLengthBytes <= 4 )
+ {
+ // 0 out the initial length
+ *pnLength = 0L;
+
+ // Bump by 1 byte
+ pbLengthData++;
+
+ #ifdef _LITTLE_ENDIAN
+
+ // There may be a cleaner way to do this, but for now, this seems to be
+ // an easy way to do the transformation
+ switch ( nNumLengthBytes )
+ {
+ case 1:
+ {
+ *( ( (unsigned char*) pnLength ) ) = *pbLengthData;
+ break;
+ }
+
+ case 2:
+ {
+ *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 1);
+ *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData);
+
+ break;
+ }
+
+ case 3:
+ {
+ *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 2);
+ *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1);
+ *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData);
+ break;
+ }
+
+ case 4:
+ {
+ *( ( (unsigned char*) pnLength ) ) = *(pbLengthData + 3);
+ *( ( (unsigned char*) pnLength ) + 1 ) = *(pbLengthData + 2);
+ *( ( (unsigned char*) pnLength ) + 2 ) = *(pbLengthData + 1);
+ *( ( (unsigned char*) pnLength ) + 3 ) = *(pbLengthData);
+ break;
+ }
+
+ } // SWITCH ( nNumLengthBytes )
+
+ #else
+ // We are Big-Endian, so the length can be copied in from the source
+ // as is. Ensure that we adjust for the number of bytes we actually
+ // copy.
+
+ memcpy( ( (unsigned char *) pnLength ) + ( 4 - nNumLengthBytes ),
+ pbLengthData, nNumLengthBytes );
+ #endif
+
+ // Account for the initial length byte
+ *pnNumLengthBytes = nNumLengthBytes + 1;
+ nReturn = SPNEGO_E_SUCCESS;
+
+ } // IF Valid Length
+
+ } // IF num bytes to read is within the boundary length
+
+ } // IF xtended length
+ else
+ {
+
+ // Extended bit is not set, so the length is in the value and the one
+ // byte describes the length
+ *pnLength = *pbLengthData & LEN_MASK;
+ *pnNumLengthBytes = 1;
+ nReturn = SPNEGO_E_SUCCESS;
+
+ }
+
+ return nReturn;
+}
+
+
+/*
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerCheckToken
+//
+// Parameters:
+// [in] pbTokenData - Token Data
+// [in] nToken - Token identifier to check for
+// [in] nLengthWithToken - Expected token length (with data)
+// [in] nBoundaryLength - Length that value must not exceed.
+// [out] pnLength - Filled out with data length
+// [out] pnTokenLength - Filled out with number of bytes
+// consumed by token identifier and length.
+//
+// Returns:
+// int Success - SPNEGO_E_SUCCESS
+// Failure - SPNEGO API Error code
+//
+// Comments :
+// Checks the data pointed to by pbTokenData for the specified token
+// identifier and the length that immediately follows. If
+// nLengthWithToken is > 0, the calculated length must match. The
+// length must also not exceed the specified boundary length .
+//
+////////////////////////////////////////////////////////////////////////////
+*/
+
+int ASNDerCheckToken( unsigned char* pbTokenData, unsigned char nToken,
+ long nLengthWithToken, long nBoundaryLength,
+ long* pnLength, long* pnTokenLength )
+{
+
+ int nReturn = SPNEGO_E_INVALID_LENGTH;
+ long nNumLengthBytes = 0L;
+
+ // Make sure that we've at least got 2 bytes of room to work with
+
+ if ( nBoundaryLength >= 2 )
+ {
+ // The first byte of the token data MUST match the specified token
+ if ( *pbTokenData == nToken )
+ {
+ // Next byte indicates the length
+ pbTokenData++;
+
+ // Get the length described by the token
+ if ( ( nReturn = ASNDerGetLength( pbTokenData, nBoundaryLength, pnLength,
+ &nNumLengthBytes ) ) == SPNEGO_E_SUCCESS )
+ {
+ // Verify that the length is LESS THAN the boundary length
+ // (this should prevent us walking out of our buffer)
+ if ( ( nBoundaryLength - ( nNumLengthBytes + 1 ) < *pnLength ) )
+ {
+
+ nReturn = SPNEGO_E_INVALID_LENGTH;
+
+ }
+
+ // If we were passed a length to check, do so now
+ if ( nLengthWithToken > 0L )
+ {
+
+ // Check that the expected length matches
+ if ( ( nLengthWithToken - ( nNumLengthBytes + 1 ) ) != *pnLength )
+ {
+
+ nReturn = SPNEGO_E_INVALID_LENGTH;
+
+ }
+
+ } // IF need to validate length
+
+ if ( SPNEGO_E_SUCCESS == nReturn )
+ {
+ *pnTokenLength = nNumLengthBytes + 1;
+ }
+
+ } // IF ASNDerGetLength
+
+ } // IF token matches
+ else
+ {
+ nReturn = SPNEGO_E_TOKEN_NOT_FOUND;
+ }
+
+ } // IF Boundary Length is at least 2 bytes
+
+ return nReturn;
+}
+
+/*
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerCheckOID
+//
+// Parameters:
+// [in] pbTokenData - Token Data
+// [in] nMechOID - OID we are looking for
+// [in] nBoundaryLength - Length that value must not exceed.
+// [out] pnTokenLength - Filled out with number of bytes
+// consumed by token and data.
+//
+// Returns:
+// int Success - SPNEGO_E_SUCCESS
+// Failure - SPNEGO API Error code
+//
+// Comments :
+// Checks the data pointed to by pbTokenData for the specified OID.
+//
+////////////////////////////////////////////////////////////////////////////
+*/
+
+int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long nBoundaryLength,
+ long* pnTokenLength )
+{
+ int nReturn = 0L;
+ long nLength = 0L;
+
+ // Verify that we have an OID token
+ if ( ( nReturn = ASNDerCheckToken( pbTokenData, OID, 0L, nBoundaryLength,
+ &nLength, pnTokenLength ) ) == SPNEGO_E_SUCCESS )
+ {
+ // Add the data length to the Token Length
+ *pnTokenLength += nLength;
+
+ // Token Lengths plus the actual length must match the length in our OID list element.
+ // If it doesn't, we're done
+ if ( *pnTokenLength == g_stcMechOIDList[nMechOID].iLen )
+ {
+ // Memcompare the token and the expected field
+ if ( memcmp( pbTokenData, g_stcMechOIDList[nMechOID].ucOid, *pnTokenLength ) != 0 )
+ {
+ nReturn = SPNEGO_E_UNEXPECTED_OID;
+ }
+ }
+ else
+ {
+ nReturn = SPNEGO_E_UNEXPECTED_OID;
+ }
+
+ } // IF OID Token CHecks
+
+ return nReturn;
+}
+
+/*
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerCalcNumLengthBytes
+//
+// Parameters:
+// [in] nLength - Length to calculate length bytes for.
+//
+// Returns:
+// int Number of bytes necessary to represent length
+//
+// Comments :
+// Helper function to calculate the number of length bytes necessary to
+// represent a length value. For our purposes, a 32-bit value should be
+// enough to describea length.
+//
+////////////////////////////////////////////////////////////////////////////
+*/
+
+int ASNDerCalcNumLengthBytes( long nLength )
+{
+ if ( nLength <= 0x7F )
+ {
+ // A single byte will be sufficient for describing this length.
+ // The byte will simply contain the length
+ return 1;
+ }
+ else if ( nLength <= 0xFF )
+ {
+ // Two bytes are necessary, one to say how many following bytes
+ // describe the length, and one to give the length
+ return 2;
+ }
+ else if ( nLength <= 0xFFFF )
+ {
+ // Three bytes are necessary, one to say how many following bytes
+ // describe the length, and two to give the length
+ return 3;
+ }
+ else if ( nLength <= 0xFFFFFF )
+ {
+ // Four bytes are necessary, one to say how many following bytes
+ // describe the length, and three to give the length
+ return 4;
+ }
+ else
+ {
+ // Five bytes are necessary, one to say how many following bytes
+ // describe the length, and four to give the length
+ return 5;
+ }
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerCalcTokenLength
+//
+// Parameters:
+// [in] nLength - Length to calculate length bytes for.
+// [in] nDataLength - Actual Data length value.
+//
+// Returns:
+// long Number of bytes necessary to represent a token, length and data
+//
+// Comments :
+// Helper function to calculate a token and value size, based on a
+// supplied length value, and any binary data that will need to be
+// written out.
+//
+////////////////////////////////////////////////////////////////////////////
+
+long ASNDerCalcTokenLength( long nLength, long nDataLength )
+{
+ // Add a byte to the length size to account for a single byte to
+ // hold the token type.
+ long nTotalLength = ASNDerCalcNumLengthBytes( nLength ) + 1;
+
+ return nTotalLength + nDataLength;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerCalcElementLength
+//
+// Parameters:
+// [in] nDataLength - Length of data.
+// [out] pnInternalLength - Filled out with length of element
+// without sequence info.
+//
+// Returns:
+// long Number of bytes necessary to represent an element
+//
+// Comments :
+// Helper function to calculate an element length. An element consists
+// of a sequence token, a type token and then the data.
+//
+////////////////////////////////////////////////////////////////////////////
+
+long ASNDerCalcElementLength( long nDataLength, long* pnInternalLength )
+{
+ // First the type token and the actual data
+ long nTotalLength = ASNDerCalcTokenLength( nDataLength, nDataLength );
+
+ // Internal length is the length without the element sequence token
+ if ( NULL != pnInternalLength )
+ {
+ *pnInternalLength = nTotalLength;
+ }
+
+ // Next add in the element's sequence token (remember that its
+ // length is the total length of the type token and data)
+ nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L );
+
+ return nTotalLength;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerCalcMechListLength
+//
+// Parameters:
+// [in] mechoid - Mech OID to put in list.
+// [out] pnInternalLength - Filled out with length of element
+// without the primary sequence token.
+//
+// Returns:
+// long Number of bytes necessary to represent a mechList
+//
+// Comments :
+// Helper function to calculate a MechList length. A mechlist consists
+// of a NegTokenInit sequence token, a sequence token for the MechList
+// and finally a list of OIDs. In our case, we only really have one
+// OID.
+//
+////////////////////////////////////////////////////////////////////////////
+
+long ASNDerCalcMechListLength( SPNEGO_MECH_OID mechoid, long* pnInternalLength )
+{
+ // First the OID
+ long nTotalLength = g_stcMechOIDList[mechoid].iLen;
+
+ // Next add in a sequence token
+ nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L );
+
+ // Internal length is the length without the element sequence token
+ if ( NULL != pnInternalLength )
+ {
+ *pnInternalLength = nTotalLength;
+ }
+
+ // Finally add in the element's sequence token
+ nTotalLength += ASNDerCalcTokenLength( nTotalLength, 0L );
+
+ return nTotalLength;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerWriteLength
+//
+// Parameters:
+// [out] pbData - Buffer to write into.
+// [in] nLength - Length to write out.
+//
+// Returns:
+// int Number of bytes written out
+//
+// Comments :
+// Helper function to write out a length value following DER rules .
+//
+////////////////////////////////////////////////////////////////////////////
+
+int ASNDerWriteLength( unsigned char* pbData, long nLength )
+{
+ int nNumBytesRequired = ASNDerCalcNumLengthBytes( nLength );
+ int nNumLengthBytes = nNumBytesRequired - 1;
+
+
+ if ( nNumBytesRequired > 1 )
+ {
+
+ // Write out the number of bytes following which will be used
+ *pbData = (unsigned char ) ( LEN_XTND | nNumLengthBytes );
+
+ // Point to where we'll actually write the length
+ pbData++;
+
+#ifdef _LITTLE_ENDIAN
+
+ // There may be a cleaner way to do this, but for now, this seems to be
+ // an easy way to do the transformation
+ switch ( nNumLengthBytes )
+ {
+ case 1:
+ {
+ // Cast the length to a single byte, since we know that it
+ // is 0x7F or less (or we wouldn't only need a single byte).
+
+ *pbData = (unsigned char) nLength;
+ break;
+ }
+
+ case 2:
+ {
+ *pbData = *( ( (unsigned char*) &nLength ) + 1 );
+ *( pbData + 1) = *( ( (unsigned char*) &nLength ) );
+ break;
+ }
+
+ case 3:
+ {
+ *pbData = *( ( (unsigned char*) &nLength ) + 3 );
+ *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 );
+ *( pbData + 2) = *( ( (unsigned char*) &nLength ) );
+ break;
+ }
+
+ case 4:
+ {
+ *pbData = *( ( (unsigned char*) &nLength ) + 3 );
+ *( pbData + 1) = *( ( (unsigned char*) &nLength ) + 2 );
+ *( pbData + 2) = *( ( (unsigned char*) &nLength ) + 1 );
+ *( pbData + 3) = *( ( (unsigned char*) &nLength ) );
+ break;
+ }
+
+ } // SWITCH ( nNumLengthBytes )
+
+#else
+ // We are Big-Endian, so the length can be copied in from the source
+ // as is. Ensure that we adjust for the number of bytes we actually
+ // copy.
+
+ memcpy( pbData,
+ ( (unsigned char *) &nLength ) + ( 4 - nNumLengthBytes ), nNumLengthBytes );
+#endif
+
+ } // IF > 1 byte for length
+ else
+ {
+ // Cast the length to a single byte, since we know that it
+ // is 0x7F or less (or we wouldn't only need a single byte).
+
+ *pbData = (unsigned char) nLength;
+ }
+
+ return nNumBytesRequired;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerWriteToken
+//
+// Parameters:
+// [out] pbData - Buffer to write into.
+// [in] ucType - Token Type
+// [in] pbTokenValue - Actual Value
+// [in] nLength - Length of Data.
+//
+// Returns:
+// int Number of bytes written out
+//
+// Comments :
+// Helper function to write out a token and any associated data. If
+// pbTokenValue is non-NULL, then it is written out in addition to the
+// token identifier and the length bytes.
+//
+////////////////////////////////////////////////////////////////////////////
+
+int ASNDerWriteToken( unsigned char* pbData, unsigned char ucType,
+ unsigned char* pbTokenValue, long nLength )
+{
+ int nTotalBytesWrittenOut = 0L;
+ int nNumLengthBytesWritten = 0L;
+
+ // Write out the type
+ *pbData = ucType;
+
+ // Wrote 1 byte, and move data pointer
+ nTotalBytesWrittenOut++;
+ pbData++;
+
+ // Now write out the length and adjust the number of bytes written out
+ nNumLengthBytesWritten = ASNDerWriteLength( pbData, nLength );
+
+ nTotalBytesWrittenOut += nNumLengthBytesWritten;
+ pbData += nNumLengthBytesWritten;
+
+ // Write out the token value if we got one. The assumption is that the
+ // nLength value indicates how many bytes are in pbTokenValue.
+
+ if ( NULL != pbTokenValue )
+ {
+ memcpy( pbData, pbTokenValue, nLength );
+ nTotalBytesWrittenOut += nLength;
+ }
+
+ return nTotalBytesWrittenOut;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerWriteOID
+//
+// Parameters:
+// [out] pbData - Buffer to write into.
+// [in] eMechOID - OID to write out.
+//
+// Returns:
+// int Number of bytes written out
+//
+// Comments :
+// Helper function to write out an OID. For these we have the raw bytes
+// listed in a global structure. The caller simply indicates which OID
+// should be written and we will splat out the data.
+//
+////////////////////////////////////////////////////////////////////////////
+
+int ASNDerWriteOID( unsigned char* pbData, SPNEGO_MECH_OID eMechOID )
+{
+
+ memcpy( pbData, g_stcMechOIDList[eMechOID].ucOid, g_stcMechOIDList[eMechOID].iLen );
+
+ return g_stcMechOIDList[eMechOID].iLen;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerWriteMechList
+//
+// Parameters:
+// [out] pbData - Buffer to write into.
+// [in] eMechOID - OID to put in MechList.
+//
+// Returns:
+// int Number of bytes written out
+//
+// Comments :
+// Helper function to write out a MechList. A MechList consists of the
+// Init Token Sequence, a sequence token and then the list of OIDs. In
+// our case the OID is from a global array of known OIDs.
+//
+////////////////////////////////////////////////////////////////////////////
+
+long ASNDerWriteMechList( unsigned char* pbData, SPNEGO_MECH_OID mechoid )
+{
+ // First get the length
+ long nInternalLength = 0L;
+ long nMechListLength = ASNDerCalcMechListLength( mechoid, &nInternalLength );
+ long nTempLength = 0L;
+
+ nTempLength = ASNDerWriteToken( pbData, SPNEGO_NEGINIT_ELEMENT_MECHTYPES,
+ NULL, nInternalLength );
+
+ // Adjust the data pointer
+ pbData += nTempLength;
+
+ // Now write the Sequence token and the OID (the OID is a BLOB in the global
+ // structure.
+
+ nTempLength = ASNDerWriteToken( pbData, SPNEGO_CONSTRUCTED_SEQUENCE,
+ g_stcMechOIDList[mechoid].ucOid,
+ g_stcMechOIDList[mechoid].iLen );
+
+ return nMechListLength;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Function:
+// ASNDerWriteElement
+//
+// Parameters:
+// [out] pbData - Buffer to write into.
+// [in] ucElementSequence - Sequence Token
+// [in] ucType - Token Type
+// [in] pbTokenValue - Actual Value
+// [in] nLength - Length of Data.
+//
+// Returns:
+// int Number of bytes written out
+//
+// Comments :
+// Helper function to write out a SPNEGO Token element. An element
+// consists of a sequence token, a type token and the associated data.
+//
+////////////////////////////////////////////////////////////////////////////
+
+int ASNDerWriteElement( unsigned char* pbData, unsigned char ucElementSequence,
+ unsigned char ucType, unsigned char* pbTokenValue, long nLength )
+{
+ // First get the length
+ long nInternalLength = 0L;
+ long nElementLength = ASNDerCalcElementLength( nLength, &nInternalLength );
+ long nTempLength = 0L;
+
+ // Write out the sequence byte and the length of the type and data
+ nTempLength = ASNDerWriteToken( pbData, ucElementSequence, NULL, nInternalLength );
+
+ // Adjust the data pointer
+ pbData += nTempLength;
+
+ // Now write the type and the data.
+ nTempLength = ASNDerWriteToken( pbData, ucType, pbTokenValue, nLength );
+
+ return nElementLength;
+}