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