security/nss/cmd/lib/basicutil.c

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     4 /*
     5 ** secutil.c - various functions used by security stuff
     6 **
     7 */
     9 #include "prtypes.h"
    10 #include "prtime.h"
    11 #include "prlong.h"
    12 #include "prerror.h"
    13 #include "prprf.h"
    14 #include "plgetopt.h"
    15 #include "prenv.h"
    16 #include "prnetdb.h"
    18 #include "basicutil.h"
    19 #include <stdarg.h>
    20 #include <sys/stat.h>
    21 #include <errno.h>
    23 #ifdef XP_UNIX
    24 #include <unistd.h>
    25 #endif
    27 #include "secoid.h"
    29 extern long DER_GetInteger(const SECItem *src);
    31 static PRBool wrapEnabled = PR_TRUE;
    33 void
    34 SECU_EnableWrap(PRBool enable)
    35 {
    36     wrapEnabled = enable;
    37 }
    39 PRBool
    40 SECU_GetWrapEnabled(void)
    41 {
    42     return wrapEnabled;
    43 }
    45 void 
    46 SECU_PrintErrMsg(FILE *out, int level, const char *progName, const char *msg,
    47                  ...)
    48 {
    49     va_list args;
    50     PRErrorCode err = PORT_GetError();
    51     const char * errString = PORT_ErrorToString(err);
    53     va_start(args, msg);
    55     SECU_Indent(out, level);
    56     fprintf(out, "%s: ", progName);
    57     vfprintf(out, msg, args);
    58     if (errString != NULL && PORT_Strlen(errString) > 0)
    59 	fprintf(out, ": %s\n", errString);
    60     else
    61 	fprintf(out, ": error %d\n", (int)err);
    63     va_end(args);
    64 }
    66 void 
    67 SECU_PrintError(const char *progName, const char *msg, ...)
    68 {
    69     va_list args;
    70     PRErrorCode err = PORT_GetError();
    71     const char * errName = PR_ErrorToName(err);
    72     const char * errString = PR_ErrorToString(err, 0);
    74     va_start(args, msg);
    76     fprintf(stderr, "%s: ", progName);
    77     vfprintf(stderr, msg, args);
    79     if (errName != NULL) {
    80 	fprintf(stderr, ": %s", errName);
    81     } else {
    82 	fprintf(stderr, ": error %d", (int)err);
    83     }
    85     if (errString != NULL && PORT_Strlen(errString) > 0)
    86 	fprintf(stderr, ": %s\n", errString);
    88     va_end(args);
    89 }
    91 void
    92 SECU_PrintSystemError(const char *progName, const char *msg, ...)
    93 {
    94     va_list args;
    96     va_start(args, msg);
    97     fprintf(stderr, "%s: ", progName);
    98     vfprintf(stderr, msg, args);
    99     fprintf(stderr, ": %s\n", strerror(errno));
   100     va_end(args);
   101 }
   103 SECStatus
   104 secu_StdinToItem(SECItem *dst)
   105 {
   106     unsigned char buf[1000];
   107     PRInt32 numBytes;
   108     PRBool notDone = PR_TRUE;
   110     dst->len = 0;
   111     dst->data = NULL;
   113     while (notDone) {
   114 	numBytes = PR_Read(PR_STDIN, buf, sizeof(buf));
   116 	if (numBytes < 0) {
   117 	    return SECFailure;
   118 	}
   120 	if (numBytes == 0)
   121 	    break;
   123 	if (dst->data) {
   124 	    unsigned char * p = dst->data;
   125 	    dst->data = (unsigned char*)PORT_Realloc(p, dst->len + numBytes);
   126 	    if (!dst->data) {
   127 	    	PORT_Free(p);
   128 	    }
   129 	} else {
   130 	    dst->data = (unsigned char*)PORT_Alloc(numBytes);
   131 	}
   132 	if (!dst->data) {
   133 	    return SECFailure;
   134 	}
   135 	PORT_Memcpy(dst->data + dst->len, buf, numBytes);
   136 	dst->len += numBytes;
   137     }
   139     return SECSuccess;
   140 }
   142 SECStatus
   143 SECU_FileToItem(SECItem *dst, PRFileDesc *src)
   144 {
   145     PRFileInfo info;
   146     PRInt32 numBytes;
   147     PRStatus prStatus;
   149     if (src == PR_STDIN)
   150 	return secu_StdinToItem(dst);
   152     prStatus = PR_GetOpenFileInfo(src, &info);
   154     if (prStatus != PR_SUCCESS) {
   155 	PORT_SetError(SEC_ERROR_IO);
   156 	return SECFailure;
   157     }
   159     /* XXX workaround for 3.1, not all utils zero dst before sending */
   160     dst->data = 0;
   161     if (!SECITEM_AllocItem(NULL, dst, info.size))
   162 	goto loser;
   164     numBytes = PR_Read(src, dst->data, info.size);
   165     if (numBytes != info.size) {
   166 	PORT_SetError(SEC_ERROR_IO);
   167 	goto loser;
   168     }
   170     return SECSuccess;
   171 loser:
   172     SECITEM_FreeItem(dst, PR_FALSE);
   173     dst->data = NULL;
   174     return SECFailure;
   175 }
   177 SECStatus
   178 SECU_TextFileToItem(SECItem *dst, PRFileDesc *src)
   179 {
   180     PRFileInfo info;
   181     PRInt32 numBytes;
   182     PRStatus prStatus;
   183     unsigned char *buf;
   185     if (src == PR_STDIN)
   186 	return secu_StdinToItem(dst);
   188     prStatus = PR_GetOpenFileInfo(src, &info);
   190     if (prStatus != PR_SUCCESS) {
   191 	PORT_SetError(SEC_ERROR_IO);
   192 	return SECFailure;
   193     }
   195     buf = (unsigned char*)PORT_Alloc(info.size);
   196     if (!buf)
   197 	return SECFailure;
   199     numBytes = PR_Read(src, buf, info.size);
   200     if (numBytes != info.size) {
   201 	PORT_SetError(SEC_ERROR_IO);
   202 	goto loser;
   203     }
   205     if (buf[numBytes-1] == '\n') numBytes--;
   206 #ifdef _WINDOWS
   207     if (buf[numBytes-1] == '\r') numBytes--;
   208 #endif
   210     /* XXX workaround for 3.1, not all utils zero dst before sending */
   211     dst->data = 0;
   212     if (!SECITEM_AllocItem(NULL, dst, numBytes))
   213 	goto loser;
   215     memcpy(dst->data, buf, numBytes);
   217     PORT_Free(buf);
   218     return SECSuccess;
   219 loser:
   220     PORT_Free(buf);
   221     return SECFailure;
   222 }
   224 #define INDENT_MULT	4
   225 void
   226 SECU_Indent(FILE *out, int level)
   227 {
   228     int i;
   230     for (i = 0; i < level; i++) {
   231 	fprintf(out, "    ");
   232     }
   233 }
   235 void SECU_Newline(FILE *out)
   236 {
   237     fprintf(out, "\n");
   238 }
   240 void
   241 SECU_PrintAsHex(FILE *out, const SECItem *data, const char *m, int level)
   242 {
   243     unsigned i;
   244     int column;
   245     PRBool isString     = PR_TRUE;
   246     PRBool isWhiteSpace = PR_TRUE;
   247     PRBool printedHex   = PR_FALSE;
   248     unsigned int limit = 15;
   250     if ( m ) {
   251 	SECU_Indent(out, level); fprintf(out, "%s:", m);
   252 	level++;
   253 	if (wrapEnabled)
   254 	    fprintf(out, "\n");
   255     }
   257     if (wrapEnabled) {
   258 	SECU_Indent(out, level); column = level*INDENT_MULT;
   259     }
   260     if (!data->len) {
   261 	fprintf(out, "(empty)\n");
   262 	return;
   263     }
   264     /* take a pass to see if it's all printable. */
   265     for (i = 0; i < data->len; i++) {
   266 	unsigned char val = data->data[i];
   267         if (!val || !isprint(val)) {
   268 	    isString = PR_FALSE;
   269 	    break;
   270 	}
   271 	if (isWhiteSpace && !isspace(val)) {
   272 	    isWhiteSpace = PR_FALSE;
   273 	}
   274     }
   276     /* Short values, such as bit strings (which are printed with this
   277     ** function) often look like strings, but we want to see the bits.
   278     ** so this test assures that short values will be printed in hex,
   279     ** perhaps in addition to being printed as strings.
   280     ** The threshold size (4 bytes) is arbitrary.
   281     */
   282     if (!isString || data->len <= 4) {
   283       for (i = 0; i < data->len; i++) {
   284 	if (i != data->len - 1) {
   285 	    fprintf(out, "%02x:", data->data[i]);
   286 	    column += 3;
   287 	} else {
   288 	    fprintf(out, "%02x", data->data[i]);
   289 	    column += 2;
   290 	    break;
   291 	}
   292 	if (wrapEnabled &&
   293 	    (column > 76 || (i % 16 == limit))) {
   294 	    SECU_Newline(out);
   295 	    SECU_Indent(out, level); 
   296 	    column = level*INDENT_MULT;
   297 	    limit = i % 16;
   298 	}
   299       }
   300       printedHex = PR_TRUE;
   301     }
   302     if (isString && !isWhiteSpace) {
   303 	if (printedHex != PR_FALSE) {
   304 	    SECU_Newline(out);
   305 	    SECU_Indent(out, level); column = level*INDENT_MULT;
   306 	}
   307 	for (i = 0; i < data->len; i++) {
   308 	    unsigned char val = data->data[i];
   310 	    if (val) {
   311 		fprintf(out,"%c",val);
   312 		column++;
   313 	    } else {
   314 		column = 77;
   315 	    }
   316 	    if (wrapEnabled && column > 76) {
   317 		SECU_Newline(out);
   318         	SECU_Indent(out, level); column = level*INDENT_MULT;
   319 	    }
   320 	}
   321     }
   323     if (column != level*INDENT_MULT) {
   324 	SECU_Newline(out);
   325     }
   326 }
   328 const char *hex = "0123456789abcdef";
   330 const char printable[257] = {
   331 	"................"	/* 0x */
   332 	"................"	/* 1x */
   333 	" !\"#$%&'()*+,-./"	/* 2x */
   334 	"0123456789:;<=>?"	/* 3x */
   335 	"@ABCDEFGHIJKLMNO"	/* 4x */
   336 	"PQRSTUVWXYZ[\\]^_"	/* 5x */
   337 	"`abcdefghijklmno"	/* 6x */
   338 	"pqrstuvwxyz{|}~."	/* 7x */
   339 	"................"	/* 8x */
   340 	"................"	/* 9x */
   341 	"................"	/* ax */
   342 	"................"	/* bx */
   343 	"................"	/* cx */
   344 	"................"	/* dx */
   345 	"................"	/* ex */
   346 	"................"	/* fx */
   347 };
   349 void 
   350 SECU_PrintBuf(FILE *out, const char *msg, const void *vp, int len)
   351 {
   352     const unsigned char *cp = (const unsigned char *)vp;
   353     char buf[80];
   354     char *bp;
   355     char *ap;
   357     fprintf(out, "%s [Len: %d]\n", msg, len);
   358     memset(buf, ' ', sizeof buf);
   359     bp = buf;
   360     ap = buf + 50;
   361     while (--len >= 0) {
   362 	unsigned char ch = *cp++;
   363 	*bp++ = hex[(ch >> 4) & 0xf];
   364 	*bp++ = hex[ch & 0xf];
   365 	*bp++ = ' ';
   366 	*ap++ = printable[ch];
   367 	if (ap - buf >= 66) {
   368 	    *ap = 0;
   369 	    fprintf(out, "   %s\n", buf);
   370 	    memset(buf, ' ', sizeof buf);
   371 	    bp = buf;
   372 	    ap = buf + 50;
   373 	}
   374     }
   375     if (bp > buf) {
   376 	*ap = 0;
   377 	fprintf(out, "   %s\n", buf);
   378     }
   379 }
   382 /* This expents i->data[0] to be the MSB of the integer.
   383 ** if you want to print a DER-encoded integer (with the tag and length)
   384 ** call SECU_PrintEncodedInteger();
   385 */
   386 void
   387 SECU_PrintInteger(FILE *out, const SECItem *i, const char *m, int level)
   388 {
   389     int iv;
   391     if (!i || !i->len || !i->data) {
   392 	SECU_Indent(out, level); 
   393 	if (m) {
   394 	    fprintf(out, "%s: (null)\n", m);
   395 	} else {
   396 	    fprintf(out, "(null)\n");
   397 	}
   398     } else if (i->len > 4) {
   399 	SECU_PrintAsHex(out, i, m, level);
   400     } else {
   401    	if (i->type == siUnsignedInteger && *i->data & 0x80) {
   402             /* Make sure i->data has zero in the highest bite 
   403              * if i->data is an unsigned integer */
   404             SECItem tmpI;
   405             char data[] = {0, 0, 0, 0, 0};
   407             PORT_Memcpy(data + 1, i->data, i->len);
   408             tmpI.len = i->len + 1;
   409             tmpI.data = (void*)data;
   411             iv = DER_GetInteger(&tmpI);
   412 	} else {
   413             iv = DER_GetInteger(i);
   414 	}
   415 	SECU_Indent(out, level); 
   416 	if (m) {
   417 	    fprintf(out, "%s: %d (0x%x)\n", m, iv, iv);
   418 	} else {
   419 	    fprintf(out, "%d (0x%x)\n", iv, iv);
   420 	}
   421     }
   422 }
   424 #if defined(DEBUG) || defined(FORCE_PR_ASSERT)
   425 /* Returns true iff a[i].flag has a duplicate in a[i+1 : count-1]  */
   426 static PRBool HasShortDuplicate(int i, secuCommandFlag *a, int count)
   427 {
   428 	char target = a[i].flag;
   429 	int j;
   431 	/* duplicate '\0' flags are okay, they are used with long forms */
   432 	for (j = i+1; j < count; j++) {
   433 		if (a[j].flag && a[j].flag == target) {
   434 			return PR_TRUE;
   435 		}
   436 	}
   437 	return PR_FALSE;
   438 }
   440 /* Returns true iff a[i].longform has a duplicate in a[i+1 : count-1] */
   441 static PRBool HasLongDuplicate(int i, secuCommandFlag *a, int count)
   442 {
   443 	int j;	
   444 	char *target = a[i].longform;
   446 	if (!target)
   447 		return PR_FALSE;
   449 	for (j = i+1; j < count; j++) {
   450 		if (a[j].longform && strcmp(a[j].longform, target) == 0) {
   451 			return PR_TRUE;
   452 		}
   453 	}
   454 	return PR_FALSE;
   455 }
   457 /* Returns true iff a has no short or long form duplicates
   458  */
   459 PRBool HasNoDuplicates(secuCommandFlag *a, int count)
   460 {
   461     int i;
   463 	for (i = 0; i < count; i++) {
   464 		if (a[i].flag && HasShortDuplicate(i, a, count)) {
   465 			return PR_FALSE;
   466 		}
   467 		if (a[i].longform && HasLongDuplicate(i, a, count)) {
   468 			return PR_FALSE;
   469 		}
   470 	}
   471 	return PR_TRUE;
   472 }
   473 #endif
   475 SECStatus
   476 SECU_ParseCommandLine(int argc, char **argv, char *progName,
   477 		      const secuCommand *cmd)
   478 {
   479     PRBool found;
   480     PLOptState *optstate;
   481     PLOptStatus status;
   482     char *optstring;
   483     PLLongOpt *longopts = NULL;
   484     int i, j;
   485     int lcmd = 0, lopt = 0;
   487     PR_ASSERT(HasNoDuplicates(cmd->commands, cmd->numCommands));
   488     PR_ASSERT(HasNoDuplicates(cmd->options, cmd->numOptions));
   490     optstring = (char *)PORT_Alloc(cmd->numCommands + 2*cmd->numOptions+1);
   491     if (optstring == NULL)
   492         return SECFailure;
   494     j = 0;
   495     for (i=0; i<cmd->numCommands; i++) {
   496 	if (cmd->commands[i].flag) /* single character option ? */
   497 	    optstring[j++] = cmd->commands[i].flag;
   498 	if (cmd->commands[i].longform)
   499 	    lcmd++;
   500     }
   501     for (i=0; i<cmd->numOptions; i++) {
   502 	if (cmd->options[i].flag) {
   503 	    optstring[j++] = cmd->options[i].flag;
   504 	    if (cmd->options[i].needsArg)
   505 		optstring[j++] = ':';
   506 	}
   507 	if (cmd->options[i].longform)
   508 	    lopt++;
   509     }
   511     optstring[j] = '\0';
   513     if (lcmd + lopt > 0) {
   514 	longopts = PORT_NewArray(PLLongOpt, lcmd+lopt+1);
   515 	if (!longopts) {
   516 	    PORT_Free(optstring);
   517 	    return SECFailure;
   518 	}
   520 	j = 0;
   521 	for (i=0; j<lcmd && i<cmd->numCommands; i++) {
   522 	    if (cmd->commands[i].longform) {
   523 		longopts[j].longOptName = cmd->commands[i].longform;
   524 		longopts[j].longOption = 0;
   525 		longopts[j++].valueRequired = cmd->commands[i].needsArg;
   526 	    } 
   527 	}
   528 	lopt += lcmd;
   529 	for (i=0; j<lopt && i<cmd->numOptions; i++) {
   530 	    if (cmd->options[i].longform) {
   531 		longopts[j].longOptName = cmd->options[i].longform;
   532 		longopts[j].longOption = 0;
   533 		longopts[j++].valueRequired = cmd->options[i].needsArg;
   534 	    }
   535 	}
   536 	longopts[j].longOptName = NULL;
   537     }
   539     optstate = PL_CreateLongOptState(argc, argv, optstring, longopts);
   540     if (!optstate) {
   541         PORT_Free(optstring);
   542         PORT_Free(longopts);
   543         return SECFailure;
   544     }
   545     /* Parse command line arguments */
   546     while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
   547 	const char *optstatelong;
   548 	char        option = optstate->option;
   550 	/*  positional parameter, single-char option or long opt? */
   551 	if (optstate->longOptIndex == -1) {
   552 	    /* not a long opt */
   553 	    if (option == '\0')
   554 	        continue;               /* it's a positional parameter */
   555 	    optstatelong = "";
   556 	} else {
   557 	    /* long opt */
   558             if (option == '\0')
   559 		option = '\377';        /* force unequal with all flags */
   560 	    optstatelong = longopts[optstate->longOptIndex].longOptName;
   561 	}
   563 	found = PR_FALSE;
   565 	for (i=0; i<cmd->numCommands; i++) {
   566 	    if (cmd->commands[i].flag == option ||
   567 	        cmd->commands[i].longform == optstatelong) {
   568 		cmd->commands[i].activated = PR_TRUE;
   569 		if (optstate->value) {
   570 		    cmd->commands[i].arg = (char *)optstate->value;
   571 		}
   572 		found = PR_TRUE;
   573 		break;
   574 	    }
   575 	}
   577 	if (found)
   578 	    continue;
   580 	for (i=0; i<cmd->numOptions; i++) {
   581 	    if (cmd->options[i].flag == option ||
   582 		cmd->options[i].longform == optstatelong) {
   583 		cmd->options[i].activated = PR_TRUE;
   584 		if (optstate->value) {
   585 		    cmd->options[i].arg = (char *)optstate->value;
   586 		} else if (cmd->options[i].needsArg) {
   587 		    status = PL_OPT_BAD;
   588 		    goto loser;
   589 		}
   590 		found = PR_TRUE;
   591 		break;
   592 	    }
   593 	}
   595 	if (!found) {
   596 	    status = PL_OPT_BAD;
   597 	    break;
   598 	}
   599     }
   601 loser:
   602     PL_DestroyOptState(optstate);
   603     PORT_Free(optstring);
   604     if (longopts)
   605 	PORT_Free(longopts);
   606     if (status == PL_OPT_BAD)
   607 	return SECFailure;
   608     return SECSuccess;
   609 }
   611 char *
   612 SECU_GetOptionArg(const secuCommand *cmd, int optionNum)
   613 {
   614 	if (optionNum < 0 || optionNum >= cmd->numOptions)
   615 		return NULL;
   616 	if (cmd->options[optionNum].activated)
   617 		return PL_strdup(cmd->options[optionNum].arg);
   618 	else
   619 		return NULL;
   620 }
   623 void 
   624 SECU_PrintPRandOSError(const char *progName) 
   625 {
   626     char buffer[513];
   627     PRInt32     errLen = PR_GetErrorTextLength();
   628     if (errLen > 0 && errLen < sizeof buffer) {
   629         PR_GetErrorText(buffer);
   630     }
   631     SECU_PrintError(progName, "function failed");
   632     if (errLen > 0 && errLen < sizeof buffer) {
   633         PR_fprintf(PR_STDERR, "\t%s\n", buffer);
   634     }
   635 }
   637 SECOidTag 
   638 SECU_StringToSignatureAlgTag(const char *alg)
   639 {
   640     SECOidTag hashAlgTag = SEC_OID_UNKNOWN;
   642     if (alg) {
   643 	if (!PL_strcmp(alg, "MD2")) {
   644 	    hashAlgTag = SEC_OID_MD2;
   645 	} else if (!PL_strcmp(alg, "MD4")) {
   646 	    hashAlgTag = SEC_OID_MD4;
   647 	} else if (!PL_strcmp(alg, "MD5")) {
   648 	    hashAlgTag = SEC_OID_MD5;
   649 	} else if (!PL_strcmp(alg, "SHA1")) {
   650 	    hashAlgTag = SEC_OID_SHA1;
   651 	} else if (!PL_strcmp(alg, "SHA224")) {
   652 	    hashAlgTag = SEC_OID_SHA224;
   653 	} else if (!PL_strcmp(alg, "SHA256")) {
   654 	    hashAlgTag = SEC_OID_SHA256;
   655 	} else if (!PL_strcmp(alg, "SHA384")) {
   656 	    hashAlgTag = SEC_OID_SHA384;
   657 	} else if (!PL_strcmp(alg, "SHA512")) {
   658 	    hashAlgTag = SEC_OID_SHA512;
   659 	}
   660     }
   661     return hashAlgTag;
   662 }
   664 /* Caller ensures that dst is at least item->len*2+1 bytes long */
   665 void
   666 SECU_SECItemToHex(const SECItem * item, char * dst)
   667 {
   668     if (dst && item && item->data) {
   669 	unsigned char * src = item->data;
   670 	unsigned int    len = item->len;
   671 	for (; len > 0; --len, dst += 2) {
   672 	    sprintf(dst, "%02x", *src++);
   673 	}
   674 	*dst = '\0';
   675     }
   676 }
   678 static unsigned char nibble(char c) {
   679     c = PORT_Tolower(c);
   680     return ( c >= '0' && c <= '9') ? c - '0' :
   681            ( c >= 'a' && c <= 'f') ? c - 'a' +10 : -1;
   682 }
   684 SECStatus
   685 SECU_SECItemHexStringToBinary(SECItem* srcdest)
   686 {
   687     int i;
   689     if (!srcdest) {
   690         PORT_SetError(SEC_ERROR_INVALID_ARGS);
   691         return SECFailure;
   692     }
   693     if (srcdest->len < 4 || (srcdest->len % 2) ) {
   694         /* too short to convert, or even number of characters */
   695         PORT_SetError(SEC_ERROR_BAD_DATA);
   696         return SECFailure;
   697     }
   698     if (PORT_Strncasecmp((const char*)srcdest->data, "0x", 2)) {
   699         /* wrong prefix */
   700         PORT_SetError(SEC_ERROR_BAD_DATA);
   701         return SECFailure;
   702     }
   704     /* 1st pass to check for hex characters */
   705     for (i=2; i<srcdest->len; i++) {
   706         char c = PORT_Tolower(srcdest->data[i]);
   707         if (! ( ( c >= '0' && c <= '9') ||
   708                 ( c >= 'a' && c <= 'f')
   709               ) ) {
   710             PORT_SetError(SEC_ERROR_BAD_DATA);
   711             return SECFailure;
   712         }
   713     }
   715     /* 2nd pass to convert */
   716     for (i=2; i<srcdest->len; i+=2) {
   717         srcdest->data[(i-2)/2] = (nibble(srcdest->data[i]) << 4) +
   718                                  nibble(srcdest->data[i+1]);
   719     }
   721     /* adjust length */
   722     srcdest->len -= 2;
   723     srcdest->len /= 2;
   724     return SECSuccess;
   725 }

mercurial