diff options
Diffstat (limited to 'security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_common.c')
-rwxr-xr-x | security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_common.c | 1073 |
1 files changed, 1073 insertions, 0 deletions
diff --git a/security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_common.c b/security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_common.c new file mode 100755 index 000000000..831ce538d --- /dev/null +++ b/security/nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_common.c @@ -0,0 +1,1073 @@ +/* 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); +} |