michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: /* michael@0: ** secutil.c - various functions used by security stuff michael@0: ** michael@0: */ michael@0: michael@0: #include "prtypes.h" michael@0: #include "prtime.h" michael@0: #include "prlong.h" michael@0: #include "prerror.h" michael@0: #include "prprf.h" michael@0: #include "plgetopt.h" michael@0: #include "prenv.h" michael@0: #include "prnetdb.h" michael@0: michael@0: #include "basicutil.h" michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #ifdef XP_UNIX michael@0: #include michael@0: #endif michael@0: michael@0: #include "secoid.h" michael@0: michael@0: extern long DER_GetInteger(const SECItem *src); michael@0: michael@0: static PRBool wrapEnabled = PR_TRUE; michael@0: michael@0: void michael@0: SECU_EnableWrap(PRBool enable) michael@0: { michael@0: wrapEnabled = enable; michael@0: } michael@0: michael@0: PRBool michael@0: SECU_GetWrapEnabled(void) michael@0: { michael@0: return wrapEnabled; michael@0: } michael@0: michael@0: void michael@0: SECU_PrintErrMsg(FILE *out, int level, const char *progName, const char *msg, michael@0: ...) michael@0: { michael@0: va_list args; michael@0: PRErrorCode err = PORT_GetError(); michael@0: const char * errString = PORT_ErrorToString(err); michael@0: michael@0: va_start(args, msg); michael@0: michael@0: SECU_Indent(out, level); michael@0: fprintf(out, "%s: ", progName); michael@0: vfprintf(out, msg, args); michael@0: if (errString != NULL && PORT_Strlen(errString) > 0) michael@0: fprintf(out, ": %s\n", errString); michael@0: else michael@0: fprintf(out, ": error %d\n", (int)err); michael@0: michael@0: va_end(args); michael@0: } michael@0: michael@0: void michael@0: SECU_PrintError(const char *progName, const char *msg, ...) michael@0: { michael@0: va_list args; michael@0: PRErrorCode err = PORT_GetError(); michael@0: const char * errName = PR_ErrorToName(err); michael@0: const char * errString = PR_ErrorToString(err, 0); michael@0: michael@0: va_start(args, msg); michael@0: michael@0: fprintf(stderr, "%s: ", progName); michael@0: vfprintf(stderr, msg, args); michael@0: michael@0: if (errName != NULL) { michael@0: fprintf(stderr, ": %s", errName); michael@0: } else { michael@0: fprintf(stderr, ": error %d", (int)err); michael@0: } michael@0: michael@0: if (errString != NULL && PORT_Strlen(errString) > 0) michael@0: fprintf(stderr, ": %s\n", errString); michael@0: michael@0: va_end(args); michael@0: } michael@0: michael@0: void michael@0: SECU_PrintSystemError(const char *progName, const char *msg, ...) michael@0: { michael@0: va_list args; michael@0: michael@0: va_start(args, msg); michael@0: fprintf(stderr, "%s: ", progName); michael@0: vfprintf(stderr, msg, args); michael@0: fprintf(stderr, ": %s\n", strerror(errno)); michael@0: va_end(args); michael@0: } michael@0: michael@0: SECStatus michael@0: secu_StdinToItem(SECItem *dst) michael@0: { michael@0: unsigned char buf[1000]; michael@0: PRInt32 numBytes; michael@0: PRBool notDone = PR_TRUE; michael@0: michael@0: dst->len = 0; michael@0: dst->data = NULL; michael@0: michael@0: while (notDone) { michael@0: numBytes = PR_Read(PR_STDIN, buf, sizeof(buf)); michael@0: michael@0: if (numBytes < 0) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: if (numBytes == 0) michael@0: break; michael@0: michael@0: if (dst->data) { michael@0: unsigned char * p = dst->data; michael@0: dst->data = (unsigned char*)PORT_Realloc(p, dst->len + numBytes); michael@0: if (!dst->data) { michael@0: PORT_Free(p); michael@0: } michael@0: } else { michael@0: dst->data = (unsigned char*)PORT_Alloc(numBytes); michael@0: } michael@0: if (!dst->data) { michael@0: return SECFailure; michael@0: } michael@0: PORT_Memcpy(dst->data + dst->len, buf, numBytes); michael@0: dst->len += numBytes; michael@0: } michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: SECStatus michael@0: SECU_FileToItem(SECItem *dst, PRFileDesc *src) michael@0: { michael@0: PRFileInfo info; michael@0: PRInt32 numBytes; michael@0: PRStatus prStatus; michael@0: michael@0: if (src == PR_STDIN) michael@0: return secu_StdinToItem(dst); michael@0: michael@0: prStatus = PR_GetOpenFileInfo(src, &info); michael@0: michael@0: if (prStatus != PR_SUCCESS) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* XXX workaround for 3.1, not all utils zero dst before sending */ michael@0: dst->data = 0; michael@0: if (!SECITEM_AllocItem(NULL, dst, info.size)) michael@0: goto loser; michael@0: michael@0: numBytes = PR_Read(src, dst->data, info.size); michael@0: if (numBytes != info.size) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: goto loser; michael@0: } michael@0: michael@0: return SECSuccess; michael@0: loser: michael@0: SECITEM_FreeItem(dst, PR_FALSE); michael@0: dst->data = NULL; michael@0: return SECFailure; michael@0: } michael@0: michael@0: SECStatus michael@0: SECU_TextFileToItem(SECItem *dst, PRFileDesc *src) michael@0: { michael@0: PRFileInfo info; michael@0: PRInt32 numBytes; michael@0: PRStatus prStatus; michael@0: unsigned char *buf; michael@0: michael@0: if (src == PR_STDIN) michael@0: return secu_StdinToItem(dst); michael@0: michael@0: prStatus = PR_GetOpenFileInfo(src, &info); michael@0: michael@0: if (prStatus != PR_SUCCESS) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: return SECFailure; michael@0: } michael@0: michael@0: buf = (unsigned char*)PORT_Alloc(info.size); michael@0: if (!buf) michael@0: return SECFailure; michael@0: michael@0: numBytes = PR_Read(src, buf, info.size); michael@0: if (numBytes != info.size) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: goto loser; michael@0: } michael@0: michael@0: if (buf[numBytes-1] == '\n') numBytes--; michael@0: #ifdef _WINDOWS michael@0: if (buf[numBytes-1] == '\r') numBytes--; michael@0: #endif michael@0: michael@0: /* XXX workaround for 3.1, not all utils zero dst before sending */ michael@0: dst->data = 0; michael@0: if (!SECITEM_AllocItem(NULL, dst, numBytes)) michael@0: goto loser; michael@0: michael@0: memcpy(dst->data, buf, numBytes); michael@0: michael@0: PORT_Free(buf); michael@0: return SECSuccess; michael@0: loser: michael@0: PORT_Free(buf); michael@0: return SECFailure; michael@0: } michael@0: michael@0: #define INDENT_MULT 4 michael@0: void michael@0: SECU_Indent(FILE *out, int level) michael@0: { michael@0: int i; michael@0: michael@0: for (i = 0; i < level; i++) { michael@0: fprintf(out, " "); michael@0: } michael@0: } michael@0: michael@0: void SECU_Newline(FILE *out) michael@0: { michael@0: fprintf(out, "\n"); michael@0: } michael@0: michael@0: void michael@0: SECU_PrintAsHex(FILE *out, const SECItem *data, const char *m, int level) michael@0: { michael@0: unsigned i; michael@0: int column; michael@0: PRBool isString = PR_TRUE; michael@0: PRBool isWhiteSpace = PR_TRUE; michael@0: PRBool printedHex = PR_FALSE; michael@0: unsigned int limit = 15; michael@0: michael@0: if ( m ) { michael@0: SECU_Indent(out, level); fprintf(out, "%s:", m); michael@0: level++; michael@0: if (wrapEnabled) michael@0: fprintf(out, "\n"); michael@0: } michael@0: michael@0: if (wrapEnabled) { michael@0: SECU_Indent(out, level); column = level*INDENT_MULT; michael@0: } michael@0: if (!data->len) { michael@0: fprintf(out, "(empty)\n"); michael@0: return; michael@0: } michael@0: /* take a pass to see if it's all printable. */ michael@0: for (i = 0; i < data->len; i++) { michael@0: unsigned char val = data->data[i]; michael@0: if (!val || !isprint(val)) { michael@0: isString = PR_FALSE; michael@0: break; michael@0: } michael@0: if (isWhiteSpace && !isspace(val)) { michael@0: isWhiteSpace = PR_FALSE; michael@0: } michael@0: } michael@0: michael@0: /* Short values, such as bit strings (which are printed with this michael@0: ** function) often look like strings, but we want to see the bits. michael@0: ** so this test assures that short values will be printed in hex, michael@0: ** perhaps in addition to being printed as strings. michael@0: ** The threshold size (4 bytes) is arbitrary. michael@0: */ michael@0: if (!isString || data->len <= 4) { michael@0: for (i = 0; i < data->len; i++) { michael@0: if (i != data->len - 1) { michael@0: fprintf(out, "%02x:", data->data[i]); michael@0: column += 3; michael@0: } else { michael@0: fprintf(out, "%02x", data->data[i]); michael@0: column += 2; michael@0: break; michael@0: } michael@0: if (wrapEnabled && michael@0: (column > 76 || (i % 16 == limit))) { michael@0: SECU_Newline(out); michael@0: SECU_Indent(out, level); michael@0: column = level*INDENT_MULT; michael@0: limit = i % 16; michael@0: } michael@0: } michael@0: printedHex = PR_TRUE; michael@0: } michael@0: if (isString && !isWhiteSpace) { michael@0: if (printedHex != PR_FALSE) { michael@0: SECU_Newline(out); michael@0: SECU_Indent(out, level); column = level*INDENT_MULT; michael@0: } michael@0: for (i = 0; i < data->len; i++) { michael@0: unsigned char val = data->data[i]; michael@0: michael@0: if (val) { michael@0: fprintf(out,"%c",val); michael@0: column++; michael@0: } else { michael@0: column = 77; michael@0: } michael@0: if (wrapEnabled && column > 76) { michael@0: SECU_Newline(out); michael@0: SECU_Indent(out, level); column = level*INDENT_MULT; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (column != level*INDENT_MULT) { michael@0: SECU_Newline(out); michael@0: } michael@0: } michael@0: michael@0: const char *hex = "0123456789abcdef"; michael@0: michael@0: const char printable[257] = { michael@0: "................" /* 0x */ michael@0: "................" /* 1x */ michael@0: " !\"#$%&'()*+,-./" /* 2x */ michael@0: "0123456789:;<=>?" /* 3x */ michael@0: "@ABCDEFGHIJKLMNO" /* 4x */ michael@0: "PQRSTUVWXYZ[\\]^_" /* 5x */ michael@0: "`abcdefghijklmno" /* 6x */ michael@0: "pqrstuvwxyz{|}~." /* 7x */ michael@0: "................" /* 8x */ michael@0: "................" /* 9x */ michael@0: "................" /* ax */ michael@0: "................" /* bx */ michael@0: "................" /* cx */ michael@0: "................" /* dx */ michael@0: "................" /* ex */ michael@0: "................" /* fx */ michael@0: }; michael@0: michael@0: void michael@0: SECU_PrintBuf(FILE *out, const char *msg, const void *vp, int len) michael@0: { michael@0: const unsigned char *cp = (const unsigned char *)vp; michael@0: char buf[80]; michael@0: char *bp; michael@0: char *ap; michael@0: michael@0: fprintf(out, "%s [Len: %d]\n", msg, len); michael@0: memset(buf, ' ', sizeof buf); michael@0: bp = buf; michael@0: ap = buf + 50; michael@0: while (--len >= 0) { michael@0: unsigned char ch = *cp++; michael@0: *bp++ = hex[(ch >> 4) & 0xf]; michael@0: *bp++ = hex[ch & 0xf]; michael@0: *bp++ = ' '; michael@0: *ap++ = printable[ch]; michael@0: if (ap - buf >= 66) { michael@0: *ap = 0; michael@0: fprintf(out, " %s\n", buf); michael@0: memset(buf, ' ', sizeof buf); michael@0: bp = buf; michael@0: ap = buf + 50; michael@0: } michael@0: } michael@0: if (bp > buf) { michael@0: *ap = 0; michael@0: fprintf(out, " %s\n", buf); michael@0: } michael@0: } michael@0: michael@0: michael@0: /* This expents i->data[0] to be the MSB of the integer. michael@0: ** if you want to print a DER-encoded integer (with the tag and length) michael@0: ** call SECU_PrintEncodedInteger(); michael@0: */ michael@0: void michael@0: SECU_PrintInteger(FILE *out, const SECItem *i, const char *m, int level) michael@0: { michael@0: int iv; michael@0: michael@0: if (!i || !i->len || !i->data) { michael@0: SECU_Indent(out, level); michael@0: if (m) { michael@0: fprintf(out, "%s: (null)\n", m); michael@0: } else { michael@0: fprintf(out, "(null)\n"); michael@0: } michael@0: } else if (i->len > 4) { michael@0: SECU_PrintAsHex(out, i, m, level); michael@0: } else { michael@0: if (i->type == siUnsignedInteger && *i->data & 0x80) { michael@0: /* Make sure i->data has zero in the highest bite michael@0: * if i->data is an unsigned integer */ michael@0: SECItem tmpI; michael@0: char data[] = {0, 0, 0, 0, 0}; michael@0: michael@0: PORT_Memcpy(data + 1, i->data, i->len); michael@0: tmpI.len = i->len + 1; michael@0: tmpI.data = (void*)data; michael@0: michael@0: iv = DER_GetInteger(&tmpI); michael@0: } else { michael@0: iv = DER_GetInteger(i); michael@0: } michael@0: SECU_Indent(out, level); michael@0: if (m) { michael@0: fprintf(out, "%s: %d (0x%x)\n", m, iv, iv); michael@0: } else { michael@0: fprintf(out, "%d (0x%x)\n", iv, iv); michael@0: } michael@0: } michael@0: } michael@0: michael@0: #if defined(DEBUG) || defined(FORCE_PR_ASSERT) michael@0: /* Returns true iff a[i].flag has a duplicate in a[i+1 : count-1] */ michael@0: static PRBool HasShortDuplicate(int i, secuCommandFlag *a, int count) michael@0: { michael@0: char target = a[i].flag; michael@0: int j; michael@0: michael@0: /* duplicate '\0' flags are okay, they are used with long forms */ michael@0: for (j = i+1; j < count; j++) { michael@0: if (a[j].flag && a[j].flag == target) { michael@0: return PR_TRUE; michael@0: } michael@0: } michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: /* Returns true iff a[i].longform has a duplicate in a[i+1 : count-1] */ michael@0: static PRBool HasLongDuplicate(int i, secuCommandFlag *a, int count) michael@0: { michael@0: int j; michael@0: char *target = a[i].longform; michael@0: michael@0: if (!target) michael@0: return PR_FALSE; michael@0: michael@0: for (j = i+1; j < count; j++) { michael@0: if (a[j].longform && strcmp(a[j].longform, target) == 0) { michael@0: return PR_TRUE; michael@0: } michael@0: } michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: /* Returns true iff a has no short or long form duplicates michael@0: */ michael@0: PRBool HasNoDuplicates(secuCommandFlag *a, int count) michael@0: { michael@0: int i; michael@0: michael@0: for (i = 0; i < count; i++) { michael@0: if (a[i].flag && HasShortDuplicate(i, a, count)) { michael@0: return PR_FALSE; michael@0: } michael@0: if (a[i].longform && HasLongDuplicate(i, a, count)) { michael@0: return PR_FALSE; michael@0: } michael@0: } michael@0: return PR_TRUE; michael@0: } michael@0: #endif michael@0: michael@0: SECStatus michael@0: SECU_ParseCommandLine(int argc, char **argv, char *progName, michael@0: const secuCommand *cmd) michael@0: { michael@0: PRBool found; michael@0: PLOptState *optstate; michael@0: PLOptStatus status; michael@0: char *optstring; michael@0: PLLongOpt *longopts = NULL; michael@0: int i, j; michael@0: int lcmd = 0, lopt = 0; michael@0: michael@0: PR_ASSERT(HasNoDuplicates(cmd->commands, cmd->numCommands)); michael@0: PR_ASSERT(HasNoDuplicates(cmd->options, cmd->numOptions)); michael@0: michael@0: optstring = (char *)PORT_Alloc(cmd->numCommands + 2*cmd->numOptions+1); michael@0: if (optstring == NULL) michael@0: return SECFailure; michael@0: michael@0: j = 0; michael@0: for (i=0; inumCommands; i++) { michael@0: if (cmd->commands[i].flag) /* single character option ? */ michael@0: optstring[j++] = cmd->commands[i].flag; michael@0: if (cmd->commands[i].longform) michael@0: lcmd++; michael@0: } michael@0: for (i=0; inumOptions; i++) { michael@0: if (cmd->options[i].flag) { michael@0: optstring[j++] = cmd->options[i].flag; michael@0: if (cmd->options[i].needsArg) michael@0: optstring[j++] = ':'; michael@0: } michael@0: if (cmd->options[i].longform) michael@0: lopt++; michael@0: } michael@0: michael@0: optstring[j] = '\0'; michael@0: michael@0: if (lcmd + lopt > 0) { michael@0: longopts = PORT_NewArray(PLLongOpt, lcmd+lopt+1); michael@0: if (!longopts) { michael@0: PORT_Free(optstring); michael@0: return SECFailure; michael@0: } michael@0: michael@0: j = 0; michael@0: for (i=0; jnumCommands; i++) { michael@0: if (cmd->commands[i].longform) { michael@0: longopts[j].longOptName = cmd->commands[i].longform; michael@0: longopts[j].longOption = 0; michael@0: longopts[j++].valueRequired = cmd->commands[i].needsArg; michael@0: } michael@0: } michael@0: lopt += lcmd; michael@0: for (i=0; jnumOptions; i++) { michael@0: if (cmd->options[i].longform) { michael@0: longopts[j].longOptName = cmd->options[i].longform; michael@0: longopts[j].longOption = 0; michael@0: longopts[j++].valueRequired = cmd->options[i].needsArg; michael@0: } michael@0: } michael@0: longopts[j].longOptName = NULL; michael@0: } michael@0: michael@0: optstate = PL_CreateLongOptState(argc, argv, optstring, longopts); michael@0: if (!optstate) { michael@0: PORT_Free(optstring); michael@0: PORT_Free(longopts); michael@0: return SECFailure; michael@0: } michael@0: /* Parse command line arguments */ michael@0: while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { michael@0: const char *optstatelong; michael@0: char option = optstate->option; michael@0: michael@0: /* positional parameter, single-char option or long opt? */ michael@0: if (optstate->longOptIndex == -1) { michael@0: /* not a long opt */ michael@0: if (option == '\0') michael@0: continue; /* it's a positional parameter */ michael@0: optstatelong = ""; michael@0: } else { michael@0: /* long opt */ michael@0: if (option == '\0') michael@0: option = '\377'; /* force unequal with all flags */ michael@0: optstatelong = longopts[optstate->longOptIndex].longOptName; michael@0: } michael@0: michael@0: found = PR_FALSE; michael@0: michael@0: for (i=0; inumCommands; i++) { michael@0: if (cmd->commands[i].flag == option || michael@0: cmd->commands[i].longform == optstatelong) { michael@0: cmd->commands[i].activated = PR_TRUE; michael@0: if (optstate->value) { michael@0: cmd->commands[i].arg = (char *)optstate->value; michael@0: } michael@0: found = PR_TRUE; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (found) michael@0: continue; michael@0: michael@0: for (i=0; inumOptions; i++) { michael@0: if (cmd->options[i].flag == option || michael@0: cmd->options[i].longform == optstatelong) { michael@0: cmd->options[i].activated = PR_TRUE; michael@0: if (optstate->value) { michael@0: cmd->options[i].arg = (char *)optstate->value; michael@0: } else if (cmd->options[i].needsArg) { michael@0: status = PL_OPT_BAD; michael@0: goto loser; michael@0: } michael@0: found = PR_TRUE; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!found) { michael@0: status = PL_OPT_BAD; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: loser: michael@0: PL_DestroyOptState(optstate); michael@0: PORT_Free(optstring); michael@0: if (longopts) michael@0: PORT_Free(longopts); michael@0: if (status == PL_OPT_BAD) michael@0: return SECFailure; michael@0: return SECSuccess; michael@0: } michael@0: michael@0: char * michael@0: SECU_GetOptionArg(const secuCommand *cmd, int optionNum) michael@0: { michael@0: if (optionNum < 0 || optionNum >= cmd->numOptions) michael@0: return NULL; michael@0: if (cmd->options[optionNum].activated) michael@0: return PL_strdup(cmd->options[optionNum].arg); michael@0: else michael@0: return NULL; michael@0: } michael@0: michael@0: michael@0: void michael@0: SECU_PrintPRandOSError(const char *progName) michael@0: { michael@0: char buffer[513]; michael@0: PRInt32 errLen = PR_GetErrorTextLength(); michael@0: if (errLen > 0 && errLen < sizeof buffer) { michael@0: PR_GetErrorText(buffer); michael@0: } michael@0: SECU_PrintError(progName, "function failed"); michael@0: if (errLen > 0 && errLen < sizeof buffer) { michael@0: PR_fprintf(PR_STDERR, "\t%s\n", buffer); michael@0: } michael@0: } michael@0: michael@0: SECOidTag michael@0: SECU_StringToSignatureAlgTag(const char *alg) michael@0: { michael@0: SECOidTag hashAlgTag = SEC_OID_UNKNOWN; michael@0: michael@0: if (alg) { michael@0: if (!PL_strcmp(alg, "MD2")) { michael@0: hashAlgTag = SEC_OID_MD2; michael@0: } else if (!PL_strcmp(alg, "MD4")) { michael@0: hashAlgTag = SEC_OID_MD4; michael@0: } else if (!PL_strcmp(alg, "MD5")) { michael@0: hashAlgTag = SEC_OID_MD5; michael@0: } else if (!PL_strcmp(alg, "SHA1")) { michael@0: hashAlgTag = SEC_OID_SHA1; michael@0: } else if (!PL_strcmp(alg, "SHA224")) { michael@0: hashAlgTag = SEC_OID_SHA224; michael@0: } else if (!PL_strcmp(alg, "SHA256")) { michael@0: hashAlgTag = SEC_OID_SHA256; michael@0: } else if (!PL_strcmp(alg, "SHA384")) { michael@0: hashAlgTag = SEC_OID_SHA384; michael@0: } else if (!PL_strcmp(alg, "SHA512")) { michael@0: hashAlgTag = SEC_OID_SHA512; michael@0: } michael@0: } michael@0: return hashAlgTag; michael@0: } michael@0: michael@0: /* Caller ensures that dst is at least item->len*2+1 bytes long */ michael@0: void michael@0: SECU_SECItemToHex(const SECItem * item, char * dst) michael@0: { michael@0: if (dst && item && item->data) { michael@0: unsigned char * src = item->data; michael@0: unsigned int len = item->len; michael@0: for (; len > 0; --len, dst += 2) { michael@0: sprintf(dst, "%02x", *src++); michael@0: } michael@0: *dst = '\0'; michael@0: } michael@0: } michael@0: michael@0: static unsigned char nibble(char c) { michael@0: c = PORT_Tolower(c); michael@0: return ( c >= '0' && c <= '9') ? c - '0' : michael@0: ( c >= 'a' && c <= 'f') ? c - 'a' +10 : -1; michael@0: } michael@0: michael@0: SECStatus michael@0: SECU_SECItemHexStringToBinary(SECItem* srcdest) michael@0: { michael@0: int i; michael@0: michael@0: if (!srcdest) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: if (srcdest->len < 4 || (srcdest->len % 2) ) { michael@0: /* too short to convert, or even number of characters */ michael@0: PORT_SetError(SEC_ERROR_BAD_DATA); michael@0: return SECFailure; michael@0: } michael@0: if (PORT_Strncasecmp((const char*)srcdest->data, "0x", 2)) { michael@0: /* wrong prefix */ michael@0: PORT_SetError(SEC_ERROR_BAD_DATA); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* 1st pass to check for hex characters */ michael@0: for (i=2; ilen; i++) { michael@0: char c = PORT_Tolower(srcdest->data[i]); michael@0: if (! ( ( c >= '0' && c <= '9') || michael@0: ( c >= 'a' && c <= 'f') michael@0: ) ) { michael@0: PORT_SetError(SEC_ERROR_BAD_DATA); michael@0: return SECFailure; michael@0: } michael@0: } michael@0: michael@0: /* 2nd pass to convert */ michael@0: for (i=2; ilen; i+=2) { michael@0: srcdest->data[(i-2)/2] = (nibble(srcdest->data[i]) << 4) + michael@0: nibble(srcdest->data[i+1]); michael@0: } michael@0: michael@0: /* adjust length */ michael@0: srcdest->len -= 2; michael@0: srcdest->len /= 2; michael@0: return SECSuccess; michael@0: }