andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: /* andre@0: * pkix_common.c andre@0: * andre@0: * Common utility functions used by various PKIX_PL functions andre@0: * andre@0: */ andre@0: andre@0: #include "pkix_pl_common.h" andre@0: andre@0: /* --Private-Functions-------------------------------------------- */ andre@0: andre@0: /* andre@0: * FUNCTION: pkix_LockObject andre@0: * DESCRIPTION: andre@0: * andre@0: * Locks the object pointed to by "object". andre@0: * andre@0: * PARAMETERS: andre@0: * "object" andre@0: * Address of object. Must be non-NULL andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_LockObject( andre@0: PKIX_PL_Object *object, andre@0: void *plContext) andre@0: { andre@0: PKIX_PL_Object *objectHeader; andre@0: andre@0: PKIX_ENTER(OBJECT, "pkix_LockObject"); andre@0: PKIX_NULLCHECK_ONE(object); andre@0: andre@0: if (object == (PKIX_PL_Object *)PKIX_ALLOC_ERROR()) { andre@0: goto cleanup; andre@0: } andre@0: andre@0: PKIX_OBJECT_DEBUG("\tShifting object pointer).\n"); andre@0: /* The header is sizeof(PKIX_PL_Object) before the object pointer */ andre@0: andre@0: objectHeader = object-1; andre@0: andre@0: PKIX_OBJECT_DEBUG("\tCalling PR_Lock).\n"); andre@0: PR_Lock(objectHeader->lock); andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(OBJECT); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_UnlockObject andre@0: * DESCRIPTION: andre@0: * andre@0: * Unlocks the object pointed to by "object". andre@0: * andre@0: * PARAMETERS: andre@0: * "object" andre@0: * Address of Object. Must be non-NULL andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_UnlockObject( andre@0: PKIX_PL_Object *object, andre@0: void *plContext) andre@0: { andre@0: PKIX_PL_Object *objectHeader; andre@0: PRStatus result; andre@0: andre@0: PKIX_ENTER(OBJECT, "pkix_UnlockObject"); andre@0: PKIX_NULLCHECK_ONE(object); andre@0: andre@0: if (object == (PKIX_PL_Object *)PKIX_ALLOC_ERROR()) { andre@0: goto cleanup; andre@0: } andre@0: andre@0: PKIX_OBJECT_DEBUG("\tShifting object pointer).\n"); andre@0: /* The header is sizeof(PKIX_PL_Object) before the object pointer */ andre@0: andre@0: objectHeader = object-1; andre@0: andre@0: PKIX_OBJECT_DEBUG("\tCalling PR_Unlock).\n"); andre@0: result = PR_Unlock(objectHeader->lock); andre@0: andre@0: if (result == PR_FAILURE) { andre@0: PKIX_OBJECT_DEBUG("\tPR_Unlock failed.).\n"); andre@0: PKIX_ERROR_FATAL(PKIX_ERRORUNLOCKINGOBJECT); andre@0: } andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(OBJECT); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_UInt32_Overflows andre@0: * DESCRIPTION: andre@0: * andre@0: * Returns a PKIX_Boolean indicating whether the unsigned integer andre@0: * represented by "string" is too large to fit in 32-bits (i.e. andre@0: * whether it overflows). With the exception of the string "0", andre@0: * all other strings are stripped of any leading zeros. It is assumed andre@0: * that every character in "string" is from the set {'0' - '9'}. andre@0: * andre@0: * PARAMETERS andre@0: * "string" andre@0: * Address of array of bytes representing PKIX_UInt32 that's being tested andre@0: * for 32-bit overflow andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * PKIX_TRUE if PKIX_UInt32 represented by "string" overflows; andre@0: * PKIX_FALSE otherwise andre@0: */ andre@0: PKIX_Boolean andre@0: pkix_pl_UInt32_Overflows(char *string){ andre@0: char *firstNonZero = NULL; andre@0: PKIX_UInt32 length, i; andre@0: char *MAX_UINT32_STRING = "4294967295"; andre@0: andre@0: PKIX_DEBUG_ENTER(OID); andre@0: andre@0: PKIX_OID_DEBUG("\tCalling PL_strlen).\n"); andre@0: length = PL_strlen(string); andre@0: andre@0: if (length < MAX_DIGITS_32){ andre@0: return (PKIX_FALSE); andre@0: } andre@0: andre@0: firstNonZero = string; andre@0: for (i = 0; i < length; i++){ andre@0: if (*string == '0'){ andre@0: firstNonZero++; andre@0: } andre@0: } andre@0: andre@0: PKIX_OID_DEBUG("\tCalling PL_strlen).\n"); andre@0: length = PL_strlen(firstNonZero); andre@0: andre@0: if (length > MAX_DIGITS_32){ andre@0: return (PKIX_TRUE); andre@0: } andre@0: andre@0: PKIX_OID_DEBUG("\tCalling PL_strlen).\n"); andre@0: if (length == MAX_DIGITS_32){ andre@0: PKIX_OID_DEBUG("\tCalling PORT_Strcmp).\n"); andre@0: if (PORT_Strcmp(firstNonZero, MAX_UINT32_STRING) > 0){ andre@0: return (PKIX_TRUE); andre@0: } andre@0: } andre@0: andre@0: return (PKIX_FALSE); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_getOIDToken andre@0: * DESCRIPTION: andre@0: * andre@0: * Takes the array of DER-encoded bytes pointed to by "derBytes" andre@0: * (representing an OID) and the value of "index" representing the index into andre@0: * the array, and decodes the bytes until an integer token is retrieved. If andre@0: * successful, this function stores the integer component at "pToken" and andre@0: * stores the index representing the next byte in the array at "pIndex" andre@0: * (following the last byte that was used in the decoding). This new output andre@0: * index can be used in subsequent calls as an input index, allowing each andre@0: * token of the OID to be retrieved consecutively. Note that there is a andre@0: * special case for the first byte, in that it encodes two separate integer andre@0: * tokens. For example, the byte {2a} represents the integer tokens {1,2}. andre@0: * This special case is not handled here and must be handled by the caller. andre@0: * andre@0: * PARAMETERS andre@0: * "derBytes" andre@0: * Address of array of bytes representing a DER-encoded OID. andre@0: * Must be non-NULL. andre@0: * "index" andre@0: * Index into the array that this function will begin decoding at. andre@0: * "pToken" andre@0: * Destination for decoded OID token. Must be non-NULL. andre@0: * "pIndex" andre@0: * Destination for index of next byte following last byte used. andre@0: * Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns an Object Error if the function fails in a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: static PKIX_Error * andre@0: pkix_pl_getOIDToken( andre@0: char *derBytes, andre@0: PKIX_UInt32 index, andre@0: PKIX_UInt32 *pToken, andre@0: PKIX_UInt32 *pIndex, andre@0: void *plContext) andre@0: { andre@0: PKIX_UInt32 retval, i, tmp; andre@0: andre@0: PKIX_ENTER(OID, "pkix_pl_getOIDToken"); andre@0: PKIX_NULLCHECK_THREE(derBytes, pToken, pIndex); andre@0: andre@0: /* andre@0: * We should only need to parse a maximum of four bytes, because andre@0: * RFC 3280 "mandates support for OIDs which have arc elements andre@0: * with values that are less than 2^28, that is, they MUST be between andre@0: * 0 and 268,435,455, inclusive. This allows each arc element to be andre@0: * represented within a single 32 bit word." andre@0: */ andre@0: andre@0: for (i = 0, retval = 0; i < 4; i++) { andre@0: retval <<= 7; andre@0: tmp = derBytes[index]; andre@0: index++; andre@0: retval |= (tmp & 0x07f); andre@0: if ((tmp & 0x080) == 0){ andre@0: *pToken = retval; andre@0: *pIndex = index; andre@0: goto cleanup; andre@0: } andre@0: } andre@0: andre@0: PKIX_ERROR(PKIX_INVALIDENCODINGOIDTOKENVALUETOOBIG); andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_RETURN(OID); andre@0: andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_helperBytes2Ascii andre@0: * DESCRIPTION: andre@0: * andre@0: * Converts an array of integers pointed to by "tokens" with a length of andre@0: * "numTokens", to an ASCII string consisting of those integers with dots in andre@0: * between them and stores the result at "pAscii". The ASCII representation is andre@0: * guaranteed to end with a NUL character. This is particularly useful for andre@0: * OID's and IP Addresses. andre@0: * andre@0: * The return value "pAscii" is not reference-counted and will need to andre@0: * be freed with PKIX_PL_Free. andre@0: * andre@0: * PARAMETERS andre@0: * "tokens" andre@0: * Address of array of integers. Must be non-NULL. andre@0: * "numTokens" andre@0: * Length of array of integers. Must be non-zero. andre@0: * "pAscii" andre@0: * Address where object pointer will be stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns an Object Error if the function fails in a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_helperBytes2Ascii( andre@0: PKIX_UInt32 *tokens, andre@0: PKIX_UInt32 numTokens, andre@0: char **pAscii, andre@0: void *plContext) andre@0: { andre@0: char *tempString = NULL; andre@0: char *outputString = NULL; andre@0: char *format = "%d"; andre@0: PKIX_UInt32 i = 0; andre@0: PKIX_UInt32 outputLen = 0; andre@0: PKIX_Int32 error; andre@0: andre@0: PKIX_ENTER(OBJECT, "pkix_pl_helperBytes2Ascii"); andre@0: PKIX_NULLCHECK_TWO(tokens, pAscii); andre@0: andre@0: if (numTokens == 0) { andre@0: PKIX_ERROR_FATAL(PKIX_HELPERBYTES2ASCIINUMTOKENSZERO); andre@0: } andre@0: andre@0: /* andre@0: * tempString will hold the string representation of a PKIX_UInt32 type andre@0: * The maximum value that can be held by an unsigned 32-bit integer andre@0: * is (2^32 - 1) = 4294967295 (which is ten digits long) andre@0: * Since tempString will hold the string representation of a andre@0: * PKIX_UInt32, we allocate 11 bytes for it (1 byte for '\0') andre@0: */ andre@0: andre@0: PKIX_CHECK(PKIX_PL_Malloc andre@0: (MAX_DIGITS_32 + 1, (void **)&tempString, plContext), andre@0: PKIX_MALLOCFAILED); andre@0: andre@0: for (i = 0; i < numTokens; i++){ andre@0: PKIX_OBJECT_DEBUG("\tCalling PR_snprintf).\n"); andre@0: error = PR_snprintf(tempString, andre@0: MAX_DIGITS_32 + 1, andre@0: format, andre@0: tokens[i]); andre@0: if (error == -1){ andre@0: PKIX_ERROR(PKIX_PRSNPRINTFFAILED); andre@0: } andre@0: andre@0: PKIX_OBJECT_DEBUG("\tCalling PL_strlen).\n"); andre@0: outputLen += PL_strlen(tempString); andre@0: andre@0: /* Include a dot to separate each number */ andre@0: outputLen++; andre@0: } andre@0: andre@0: /* Allocate space for the destination string */ andre@0: PKIX_CHECK(PKIX_PL_Malloc andre@0: (outputLen, (void **)&outputString, plContext), andre@0: PKIX_MALLOCFAILED); andre@0: andre@0: *outputString = '\0'; andre@0: andre@0: /* Concatenate all strings together */ andre@0: for (i = 0; i < numTokens; i++){ andre@0: andre@0: PKIX_OBJECT_DEBUG("\tCalling PR_snprintf).\n"); andre@0: error = PR_snprintf(tempString, andre@0: MAX_DIGITS_32 + 1, andre@0: format, andre@0: tokens[i]); andre@0: if (error == -1){ andre@0: PKIX_ERROR(PKIX_PRSNPRINTFFAILED); andre@0: } andre@0: andre@0: PKIX_OBJECT_DEBUG("\tCalling PL_strcat).\n"); andre@0: (void) PL_strcat(outputString, tempString); andre@0: andre@0: /* we don't want to put a "." at the very end */ andre@0: if (i < (numTokens - 1)){ andre@0: PKIX_OBJECT_DEBUG("\tCalling PL_strcat).\n"); andre@0: (void) PL_strcat(outputString, "."); andre@0: } andre@0: } andre@0: andre@0: /* Ensure output string ends with terminating null */ andre@0: outputString[outputLen-1] = '\0'; andre@0: andre@0: *pAscii = outputString; andre@0: outputString = NULL; andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_FREE(outputString); andre@0: PKIX_FREE(tempString); andre@0: andre@0: PKIX_RETURN(OBJECT); andre@0: andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_ipAddrBytes2Ascii andre@0: * DESCRIPTION: andre@0: * andre@0: * Converts the DER encoding of an IPAddress pointed to by "secItem" to an andre@0: * ASCII representation and stores the result at "pAscii". The ASCII andre@0: * representation is guaranteed to end with a NUL character. The input andre@0: * SECItem must contain non-NULL data and must have a positive length. andre@0: * andre@0: * The return value "pAscii" is not reference-counted and will need to andre@0: * be freed with PKIX_PL_Free. andre@0: * XXX this function assumes that IPv4 addresses are being used andre@0: * XXX what about IPv6? can NSS tell the difference andre@0: * andre@0: * PARAMETERS andre@0: * "secItem" andre@0: * Address of SECItem which contains bytes and length of DER encoding. andre@0: * Must be non-NULL. andre@0: * "pAscii" andre@0: * Address where object pointer will be stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns an Object Error if the function fails in a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_ipAddrBytes2Ascii( andre@0: SECItem *secItem, andre@0: char **pAscii, andre@0: void *plContext) andre@0: { andre@0: char *data = NULL; andre@0: PKIX_UInt32 *tokens = NULL; andre@0: PKIX_UInt32 numTokens = 0; andre@0: PKIX_UInt32 i = 0; andre@0: char *asciiString = NULL; andre@0: andre@0: PKIX_ENTER(OBJECT, "pkix_pl_ipAddrBytes2Ascii"); andre@0: PKIX_NULLCHECK_THREE(secItem, pAscii, secItem->data); andre@0: andre@0: if (secItem->len == 0) { andre@0: PKIX_ERROR_FATAL(PKIX_IPADDRBYTES2ASCIIDATALENGTHZERO); andre@0: } andre@0: andre@0: data = (char *)(secItem->data); andre@0: numTokens = secItem->len; andre@0: andre@0: /* allocate space for array of integers */ andre@0: PKIX_CHECK(PKIX_PL_Malloc andre@0: (numTokens * sizeof (PKIX_UInt32), andre@0: (void **)&tokens, andre@0: plContext), andre@0: PKIX_MALLOCFAILED); andre@0: andre@0: /* populate array of integers */ andre@0: for (i = 0; i < numTokens; i++){ andre@0: tokens[i] = data[i]; andre@0: } andre@0: andre@0: /* convert array of integers to ASCII */ andre@0: PKIX_CHECK(pkix_pl_helperBytes2Ascii andre@0: (tokens, numTokens, &asciiString, plContext), andre@0: PKIX_HELPERBYTES2ASCIIFAILED); andre@0: andre@0: *pAscii = asciiString; andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_FREE(tokens); andre@0: andre@0: PKIX_RETURN(OBJECT); andre@0: } andre@0: andre@0: andre@0: /* andre@0: * FUNCTION: pkix_pl_oidBytes2Ascii andre@0: * DESCRIPTION: andre@0: * andre@0: * Converts the DER encoding of an OID pointed to by "secItem" to an ASCII andre@0: * representation and stores it at "pAscii". The ASCII representation is andre@0: * guaranteed to end with a NUL character. The input SECItem must contain andre@0: * non-NULL data and must have a positive length. andre@0: * andre@0: * Example: the six bytes {2a 86 48 86 f7 0d} represent the andre@0: * four integer tokens {1, 2, 840, 113549}, which we will convert andre@0: * into ASCII yielding "1.2.840.113549" andre@0: * andre@0: * The return value "pAscii" is not reference-counted and will need to andre@0: * be freed with PKIX_PL_Free. andre@0: * andre@0: * PARAMETERS andre@0: * "secItem" andre@0: * Address of SECItem which contains bytes and length of DER encoding. andre@0: * Must be non-NULL. andre@0: * "pAscii" andre@0: * Address where object pointer will be stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns an OID Error if the function fails in a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_pl_oidBytes2Ascii( andre@0: SECItem *secItem, andre@0: char **pAscii, andre@0: void *plContext) andre@0: { andre@0: char *data = NULL; andre@0: PKIX_UInt32 *tokens = NULL; andre@0: PKIX_UInt32 token = 0; andre@0: PKIX_UInt32 numBytes = 0; andre@0: PKIX_UInt32 numTokens = 0; andre@0: PKIX_UInt32 i = 0, x = 0, y = 0; andre@0: PKIX_UInt32 index = 0; andre@0: char *asciiString = NULL; andre@0: andre@0: PKIX_ENTER(OID, "pkix_pl_oidBytes2Ascii"); andre@0: PKIX_NULLCHECK_THREE(secItem, pAscii, secItem->data); andre@0: andre@0: if (secItem->len == 0) { andre@0: PKIX_ERROR_FATAL(PKIX_OIDBYTES2ASCIIDATALENGTHZERO); andre@0: } andre@0: andre@0: data = (char *)(secItem->data); andre@0: numBytes = secItem->len; andre@0: numTokens = 0; andre@0: andre@0: /* calculate how many integer tokens are represented by the bytes. */ andre@0: for (i = 0; i < numBytes; i++){ andre@0: if ((data[i] & 0x080) == 0){ andre@0: numTokens++; andre@0: } andre@0: } andre@0: andre@0: /* if we are unable to retrieve any tokens at all, we throw an error */ andre@0: if (numTokens == 0){ andre@0: PKIX_ERROR(PKIX_INVALIDDERENCODINGFOROID); andre@0: } andre@0: andre@0: /* add one more token b/c the first byte always contains two tokens */ andre@0: numTokens++; andre@0: andre@0: /* allocate space for array of integers */ andre@0: PKIX_CHECK(PKIX_PL_Malloc andre@0: (numTokens * sizeof (PKIX_UInt32), andre@0: (void **)&tokens, andre@0: plContext), andre@0: PKIX_MALLOCFAILED); andre@0: andre@0: /* populate array of integers */ andre@0: for (i = 0; i < numTokens; i++){ andre@0: andre@0: /* retrieve integer token */ andre@0: PKIX_CHECK(pkix_pl_getOIDToken andre@0: (data, index, &token, &index, plContext), andre@0: PKIX_GETOIDTOKENFAILED); andre@0: andre@0: if (i == 0){ andre@0: andre@0: /* andre@0: * special case: the first DER-encoded byte represents andre@0: * two tokens. We take advantage of fact that first andre@0: * token must be 0, 1, or 2; and second token must be andre@0: * between {0, 39} inclusive if first token is 0 or 1. andre@0: */ andre@0: andre@0: if (token < 40) andre@0: x = 0; andre@0: else if (token < 80) andre@0: x = 1; andre@0: else andre@0: x = 2; andre@0: y = token - (x * 40); andre@0: andre@0: tokens[0] = x; andre@0: tokens[1] = y; andre@0: i++; andre@0: } else { andre@0: tokens[i] = token; andre@0: } andre@0: } andre@0: andre@0: /* convert array of integers to ASCII */ andre@0: PKIX_CHECK(pkix_pl_helperBytes2Ascii andre@0: (tokens, numTokens, &asciiString, plContext), andre@0: PKIX_HELPERBYTES2ASCIIFAILED); andre@0: andre@0: *pAscii = asciiString; andre@0: andre@0: cleanup: andre@0: andre@0: PKIX_FREE(tokens); andre@0: PKIX_RETURN(OID); andre@0: andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_UTF16_to_EscASCII andre@0: * DESCRIPTION: andre@0: * andre@0: * Converts array of bytes pointed to by "utf16String" with length of andre@0: * "utf16Length" (which must be even) into a freshly allocated Escaped ASCII andre@0: * string and stores a pointer to that string at "pDest" and stores the andre@0: * string's length at "pLength". The Escaped ASCII string's length does not andre@0: * include the final NUL character. The caller is responsible for freeing andre@0: * "pDest" using PKIX_PL_Free. If "debug" is set, uses EscASCII_Debug andre@0: * encoding. andre@0: * andre@0: * PARAMETERS: andre@0: * "utf16String" andre@0: * Address of array of bytes representing data source. Must be non-NULL. andre@0: * "utf16Length" andre@0: * Length of data source. Must be even. andre@0: * "debug" andre@0: * Boolean value indicating whether debug mode is desired. andre@0: * "pDest" andre@0: * Address where data will be stored. Must be non-NULL. andre@0: * "pLength" andre@0: * Address where data length will be stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a String Error if the function fails in a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_UTF16_to_EscASCII( andre@0: const void *utf16String, andre@0: PKIX_UInt32 utf16Length, andre@0: PKIX_Boolean debug, andre@0: char **pDest, andre@0: PKIX_UInt32 *pLength, andre@0: void *plContext) andre@0: { andre@0: char *destPtr = NULL; andre@0: PKIX_UInt32 i, charLen; andre@0: PKIX_UInt32 x = 0, y = 0, z = 0; andre@0: unsigned char *utf16Char = (unsigned char *)utf16String; andre@0: andre@0: PKIX_ENTER(STRING, "pkix_UTF16_to_EscASCII"); andre@0: PKIX_NULLCHECK_THREE(utf16String, pDest, pLength); andre@0: andre@0: /* Assume every pair of bytes becomes &#xNNNN; */ andre@0: charLen = 4*utf16Length; andre@0: andre@0: /* utf16Lenght must be even */ andre@0: if ((utf16Length % 2) != 0){ andre@0: PKIX_ERROR(PKIX_UTF16ALIGNMENTERROR); andre@0: } andre@0: andre@0: /* Count how many bytes we need */ andre@0: for (i = 0; i < utf16Length; i += 2) { andre@0: if ((utf16Char[i] == 0x00)&& andre@0: pkix_isPlaintext(utf16Char[i+1], debug)) { andre@0: if (utf16Char[i+1] == '&') { andre@0: /* Need to convert this to & */ andre@0: charLen -= 3; andre@0: } else { andre@0: /* We can fit this into one char */ andre@0: charLen -= 7; andre@0: } andre@0: } else if ((utf16Char[i] >= 0xD8) && (utf16Char[i] <= 0xDB)) { andre@0: if ((i+3) >= utf16Length) { andre@0: PKIX_ERROR(PKIX_UTF16HIGHZONEALIGNMENTERROR); andre@0: } else if ((utf16Char[i+2] >= 0xDC)&& andre@0: (utf16Char[i+2] <= 0xDF)) { andre@0: /* Quartet of bytes will become &#xNNNNNNNN; */ andre@0: charLen -= 4; andre@0: /* Quartet of bytes will produce 12 chars */ andre@0: i += 2; andre@0: } else { andre@0: /* Second pair should be DC00-DFFF */ andre@0: PKIX_ERROR(PKIX_UTF16LOWZONEERROR); andre@0: } andre@0: } andre@0: } andre@0: andre@0: *pLength = charLen; andre@0: andre@0: /* Ensure this string is null terminated */ andre@0: charLen++; andre@0: andre@0: /* Allocate space for character array */ andre@0: PKIX_CHECK(PKIX_PL_Malloc(charLen, (void **)pDest, plContext), andre@0: PKIX_MALLOCFAILED); andre@0: andre@0: destPtr = *pDest; andre@0: for (i = 0; i < utf16Length; i += 2) { andre@0: if ((utf16Char[i] == 0x00)&& andre@0: pkix_isPlaintext(utf16Char[i+1], debug)) { andre@0: /* Write a single character */ andre@0: *destPtr++ = utf16Char[i+1]; andre@0: } else if ((utf16Char[i+1] == '&') && (utf16Char[i] == 0x00)){ andre@0: *destPtr++ = '&'; andre@0: *destPtr++ = 'a'; andre@0: *destPtr++ = 'm'; andre@0: *destPtr++ = 'p'; andre@0: *destPtr++ = ';'; andre@0: } else if ((utf16Char[i] >= 0xD8)&& andre@0: (utf16Char[i] <= 0xDB)&& andre@0: (utf16Char[i+2] >= 0xDC)&& andre@0: (utf16Char[i+2] <= 0xDF)) { andre@0: /* andre@0: * Special UTF pairs are of the form: andre@0: * x = D800..DBFF; y = DC00..DFFF; andre@0: * The result is of the form: andre@0: * ((x - D800) * 400 + (y - DC00)) + 0001 0000 andre@0: */ andre@0: x = 0x0FFFF & ((utf16Char[i]<<8) | utf16Char[i+1]); andre@0: y = 0x0FFFF & ((utf16Char[i+2]<<8) | utf16Char[i+3]); andre@0: z = ((x - 0xD800) * 0x400 + (y - 0xDC00)) + 0x00010000; andre@0: andre@0: /* Sprintf &#xNNNNNNNN; */ andre@0: PKIX_STRING_DEBUG("\tCalling PR_snprintf).\n"); andre@0: if (PR_snprintf(destPtr, 13, "&#x%08X;", z) == andre@0: (PKIX_UInt32)(-1)) { andre@0: PKIX_ERROR(PKIX_PRSNPRINTFFAILED); andre@0: } andre@0: i += 2; andre@0: destPtr += 12; andre@0: } else { andre@0: /* Sprintf &#xNNNN; */ andre@0: PKIX_STRING_DEBUG("\tCalling PR_snprintf).\n"); andre@0: if (PR_snprintf andre@0: (destPtr, andre@0: 9, andre@0: "&#x%02X%02X;", andre@0: utf16Char[i], andre@0: utf16Char[i+1]) == andre@0: (PKIX_UInt32)(-1)) { andre@0: PKIX_ERROR(PKIX_PRSNPRINTFFAILED); andre@0: } andre@0: destPtr += 8; andre@0: } andre@0: } andre@0: *destPtr = '\0'; andre@0: andre@0: cleanup: andre@0: andre@0: if (PKIX_ERROR_RECEIVED){ andre@0: PKIX_FREE(*pDest); andre@0: } andre@0: andre@0: PKIX_RETURN(STRING); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_EscASCII_to_UTF16 andre@0: * DESCRIPTION: andre@0: * andre@0: * Converts array of bytes pointed to by "escAsciiString" with length of andre@0: * "escAsciiLength" into a freshly allocated UTF-16 string and stores a andre@0: * pointer to that string at "pDest" and stores the string's length at andre@0: * "pLength". The caller is responsible for freeing "pDest" using andre@0: * PKIX_PL_Free. If "debug" is set, uses EscASCII_Debug encoding. andre@0: * andre@0: * PARAMETERS: andre@0: * "escAsciiString" andre@0: * Address of array of bytes representing data source. Must be non-NULL. andre@0: * "escAsciiLength" andre@0: * Length of data source. Must be even. andre@0: * "debug" andre@0: * Boolean value indicating whether debug mode is desired. andre@0: * "pDest" andre@0: * Address where data will be stored. Must be non-NULL. andre@0: * "pLength" andre@0: * Address where data length will be stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a String Error if the function fails in a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_EscASCII_to_UTF16( andre@0: const char *escAsciiString, andre@0: PKIX_UInt32 escAsciiLen, andre@0: PKIX_Boolean debug, andre@0: void **pDest, andre@0: PKIX_UInt32 *pLength, andre@0: void *plContext) andre@0: { andre@0: PKIX_UInt32 newLen, i, j, charSize; andre@0: PKIX_UInt32 x = 0, y = 0, z = 0; andre@0: unsigned char *destPtr = NULL; andre@0: unsigned char testChar, testChar2; andre@0: unsigned char *stringData = (unsigned char *)escAsciiString; andre@0: andre@0: PKIX_ENTER(STRING, "pkix_EscASCII_to_UTF16"); andre@0: PKIX_NULLCHECK_THREE(escAsciiString, pDest, pLength); andre@0: andre@0: if (escAsciiLen == 0) { andre@0: PKIX_CHECK(PKIX_PL_Malloc(escAsciiLen, pDest, plContext), andre@0: PKIX_MALLOCFAILED); andre@0: goto cleanup; andre@0: } andre@0: andre@0: /* Assume each unicode character takes two bytes */ andre@0: newLen = escAsciiLen*2; andre@0: andre@0: /* Count up number of unicode encoded characters */ andre@0: for (i = 0; i < escAsciiLen; i++) { andre@0: if (!pkix_isPlaintext(stringData[i], debug)&& andre@0: (stringData[i] != '&')) { andre@0: PKIX_ERROR(PKIX_ILLEGALCHARACTERINESCAPEDASCII); andre@0: } else if (PL_strstr(escAsciiString+i, "&") == andre@0: escAsciiString+i) { andre@0: /* Convert EscAscii "&" to two bytes */ andre@0: newLen -= 8; andre@0: i += 4; andre@0: } else if ((PL_strstr(escAsciiString+i, "&#x") == andre@0: escAsciiString+i)|| andre@0: (PL_strstr(escAsciiString+i, "&#X") == andre@0: escAsciiString+i)) { andre@0: if (((i+7) <= escAsciiLen)&& andre@0: (escAsciiString[i+7] == ';')) { andre@0: /* Convert &#xNNNN; to two bytes */ andre@0: newLen -= 14; andre@0: i += 7; andre@0: } else if (((i+11) <= escAsciiLen)&& andre@0: (escAsciiString[i+11] == ';')) { andre@0: /* Convert &#xNNNNNNNN; to four bytes */ andre@0: newLen -= 20; andre@0: i += 11; andre@0: } else { andre@0: PKIX_ERROR(PKIX_ILLEGALUSEOFAMP); andre@0: } andre@0: } andre@0: } andre@0: andre@0: PKIX_CHECK(PKIX_PL_Malloc(newLen, pDest, plContext), andre@0: PKIX_MALLOCFAILED); andre@0: andre@0: /* Copy into newly allocated space */ andre@0: destPtr = (unsigned char *)*pDest; andre@0: andre@0: i = 0; andre@0: while (i < escAsciiLen) { andre@0: /* Copy each byte until you hit a & */ andre@0: if (pkix_isPlaintext(escAsciiString[i], debug)) { andre@0: *destPtr++ = 0x00; andre@0: *destPtr++ = escAsciiString[i++]; andre@0: } else if (PL_strstr(escAsciiString+i, "&") == andre@0: escAsciiString+i) { andre@0: /* Convert EscAscii "&" to two bytes */ andre@0: *destPtr++ = 0x00; andre@0: *destPtr++ = '&'; andre@0: i += 5; andre@0: } else if (((PL_strstr(escAsciiString+i, "&#x") == andre@0: escAsciiString+i)|| andre@0: (PL_strstr(escAsciiString+i, "&#X") == andre@0: escAsciiString+i))&& andre@0: ((i+7) <= escAsciiLen)) { andre@0: andre@0: /* We're either looking at &#xNNNN; or &#xNNNNNNNN; */ andre@0: charSize = (escAsciiString[i+7] == ';')?4:8; andre@0: andre@0: /* Skip past the &#x */ andre@0: i += 3; andre@0: andre@0: /* Make sure there is a terminating semi-colon */ andre@0: if (((i+charSize) > escAsciiLen)|| andre@0: (escAsciiString[i+charSize] != ';')) { andre@0: PKIX_ERROR(PKIX_TRUNCATEDUNICODEINESCAPEDASCII); andre@0: } andre@0: andre@0: for (j = 0; j < charSize; j++) { andre@0: if (!PKIX_ISXDIGIT andre@0: (escAsciiString[i+j])) { andre@0: PKIX_ERROR(PKIX_ILLEGALUNICODECHARACTER); andre@0: } else if (charSize == 8) { andre@0: x |= (pkix_hex2i andre@0: (escAsciiString[i+j])) andre@0: <<(4*(7-j)); andre@0: } andre@0: } andre@0: andre@0: testChar = andre@0: (pkix_hex2i(escAsciiString[i])<<4)| andre@0: pkix_hex2i(escAsciiString[i+1]); andre@0: testChar2 = andre@0: (pkix_hex2i(escAsciiString[i+2])<<4)| andre@0: pkix_hex2i(escAsciiString[i+3]); andre@0: andre@0: if (charSize == 4) { andre@0: if ((testChar >= 0xD8)&& andre@0: (testChar <= 0xDF)) { andre@0: PKIX_ERROR(PKIX_ILLEGALSURROGATEPAIR); andre@0: } else if ((testChar == 0x00)&& andre@0: pkix_isPlaintext(testChar2, debug)) { andre@0: PKIX_ERROR( andre@0: PKIX_ILLEGALCHARACTERINESCAPEDASCII); andre@0: } andre@0: *destPtr++ = testChar; andre@0: *destPtr++ = testChar2; andre@0: } else if (charSize == 8) { andre@0: /* First two chars must be 0001-0010 */ andre@0: if (!((testChar == 0x00)&& andre@0: ((testChar2 >= 0x01)&& andre@0: (testChar2 <= 0x10)))) { andre@0: PKIX_ERROR( andre@0: PKIX_ILLEGALCHARACTERINESCAPEDASCII); andre@0: } andre@0: /* andre@0: * Unicode Strings of the form: andre@0: * x = 0001 0000..0010 FFFF andre@0: * Encoded as pairs of UTF-16 where andre@0: * y = ((x - 0001 0000) / 400) + D800 andre@0: * z = ((x - 0001 0000) % 400) + DC00 andre@0: */ andre@0: x -= 0x00010000; andre@0: y = (x/0x400)+ 0xD800; andre@0: z = (x%0x400)+ 0xDC00; andre@0: andre@0: /* Copy four bytes */ andre@0: *destPtr++ = (y&0xFF00)>>8; andre@0: *destPtr++ = (y&0x00FF); andre@0: *destPtr++ = (z&0xFF00)>>8; andre@0: *destPtr++ = (z&0x00FF); andre@0: } andre@0: /* Move past the Hex digits and the semi-colon */ andre@0: i += charSize+1; andre@0: } else { andre@0: /* Do not allow any other non-plaintext character */ andre@0: PKIX_ERROR(PKIX_ILLEGALCHARACTERINESCAPEDASCII); andre@0: } andre@0: } andre@0: andre@0: *pLength = newLen; andre@0: andre@0: cleanup: andre@0: andre@0: if (PKIX_ERROR_RECEIVED){ andre@0: PKIX_FREE(*pDest); andre@0: } andre@0: andre@0: PKIX_RETURN(STRING); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_UTF16_to_UTF8 andre@0: * DESCRIPTION: andre@0: * andre@0: * Converts array of bytes pointed to by "utf16String" with length of andre@0: * "utf16Length" into a freshly allocated UTF-8 string and stores a pointer andre@0: * to that string at "pDest" and stores the string's length at "pLength" (not andre@0: * counting the null terminator, if requested. The caller is responsible for andre@0: * freeing "pDest" using PKIX_PL_Free. andre@0: * andre@0: * PARAMETERS: andre@0: * "utf16String" andre@0: * Address of array of bytes representing data source. Must be non-NULL. andre@0: * "utf16Length" andre@0: * Length of data source. Must be even. andre@0: * "null-term" andre@0: * Boolean value indicating whether output should be null-terminated. andre@0: * "pDest" andre@0: * Address where data will be stored. Must be non-NULL. andre@0: * "pLength" andre@0: * Address where data length will be stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a String Error if the function fails in a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_UTF16_to_UTF8( andre@0: const void *utf16String, andre@0: PKIX_UInt32 utf16Length, andre@0: PKIX_Boolean null_term, andre@0: void **pDest, andre@0: PKIX_UInt32 *pLength, andre@0: void *plContext) andre@0: { andre@0: PKIX_Boolean result; andre@0: PKIX_UInt32 reallocLen; andre@0: char *endPtr = NULL; andre@0: andre@0: PKIX_ENTER(STRING, "pkix_UTF16_to_UTF8"); andre@0: PKIX_NULLCHECK_THREE(utf16String, pDest, pLength); andre@0: andre@0: /* XXX How big can a UTF8 string be compared to a UTF16? */ andre@0: PKIX_CHECK(PKIX_PL_Calloc(1, utf16Length*2, pDest, plContext), andre@0: PKIX_CALLOCFAILED); andre@0: andre@0: PKIX_STRING_DEBUG("\tCalling PORT_UCS2_UTF8Conversion).\n"); andre@0: result = PORT_UCS2_UTF8Conversion andre@0: (PKIX_FALSE, /* False = From UCS2 */ andre@0: (unsigned char *)utf16String, andre@0: utf16Length, andre@0: (unsigned char *)*pDest, andre@0: utf16Length*2, /* Max Size */ andre@0: pLength); andre@0: if (result == PR_FALSE){ andre@0: PKIX_ERROR(PKIX_PORTUCS2UTF8CONVERSIONFAILED); andre@0: } andre@0: andre@0: reallocLen = *pLength; andre@0: andre@0: if (null_term){ andre@0: reallocLen++; andre@0: } andre@0: andre@0: PKIX_CHECK(PKIX_PL_Realloc(*pDest, reallocLen, pDest, plContext), andre@0: PKIX_REALLOCFAILED); andre@0: andre@0: if (null_term){ andre@0: endPtr = (char*)*pDest + reallocLen - 1; andre@0: *endPtr = '\0'; andre@0: } andre@0: andre@0: cleanup: andre@0: andre@0: if (PKIX_ERROR_RECEIVED){ andre@0: PKIX_FREE(*pDest); andre@0: } andre@0: andre@0: PKIX_RETURN(STRING); andre@0: } andre@0: andre@0: /* andre@0: * FUNCTION: pkix_UTF8_to_UTF16 andre@0: * DESCRIPTION: andre@0: * andre@0: * Converts array of bytes pointed to by "utf8String" with length of andre@0: * "utf8Length" into a freshly allocated UTF-16 string and stores a pointer andre@0: * to that string at "pDest" and stores the string's length at "pLength". The andre@0: * caller is responsible for freeing "pDest" using PKIX_PL_Free. andre@0: * andre@0: * PARAMETERS: andre@0: * "utf8String" andre@0: * Address of array of bytes representing data source. Must be non-NULL. andre@0: * "utf8Length" andre@0: * Length of data source. Must be even. andre@0: * "pDest" andre@0: * Address where data will be stored. Must be non-NULL. andre@0: * "pLength" andre@0: * Address where data length will be stored. Must be non-NULL. andre@0: * "plContext" andre@0: * Platform-specific context pointer. andre@0: * THREAD SAFETY: andre@0: * Thread Safe (see Thread Safety Definitions in Programmer's Guide) andre@0: * RETURNS: andre@0: * Returns NULL if the function succeeds. andre@0: * Returns a String Error if the function fails in a non-fatal way. andre@0: * Returns a Fatal Error if the function fails in an unrecoverable way. andre@0: */ andre@0: PKIX_Error * andre@0: pkix_UTF8_to_UTF16( andre@0: const void *utf8String, andre@0: PKIX_UInt32 utf8Length, andre@0: void **pDest, andre@0: PKIX_UInt32 *pLength, andre@0: void *plContext) andre@0: { andre@0: PKIX_Boolean result; andre@0: andre@0: PKIX_ENTER(STRING, "pkix_UTF8_to_UTF16"); andre@0: PKIX_NULLCHECK_THREE(utf8String, pDest, pLength); andre@0: andre@0: /* XXX How big can a UTF8 string be compared to a UTF16? */ andre@0: PKIX_CHECK(PKIX_PL_Calloc(1, utf8Length*2, pDest, plContext), andre@0: PKIX_MALLOCFAILED); andre@0: andre@0: PKIX_STRING_DEBUG("\tCalling PORT_UCS2_UTF8Conversion).\n"); andre@0: result = PORT_UCS2_UTF8Conversion andre@0: (PKIX_TRUE, /* True = From UTF8 */ andre@0: (unsigned char *)utf8String, andre@0: utf8Length, andre@0: (unsigned char *)*pDest, andre@0: utf8Length*2, /* Max Size */ andre@0: pLength); andre@0: if (result == PR_FALSE){ andre@0: PKIX_ERROR(PKIX_PORTUCS2UTF8CONVERSIONFAILED); andre@0: } andre@0: andre@0: PKIX_CHECK(PKIX_PL_Realloc(*pDest, *pLength, pDest, plContext), andre@0: PKIX_REALLOCFAILED); andre@0: andre@0: cleanup: andre@0: andre@0: if (PKIX_ERROR_RECEIVED){ andre@0: PKIX_FREE(*pDest); andre@0: } andre@0: andre@0: PKIX_RETURN(STRING); andre@0: }