PORT_Assert( ssl_Have1stHandshakeLock(ss) );
PORT_Assert( ss->opt.noLocks || ssl_Have1stHandshakeLock(ss) );
ssl2_ProduceKeys(sslSocket *    ss,             SECItem *      readKey, 	    SECItem *      writeKey,	    SECItem *      masterKey, 	    PRUint8 *      challenge,	    PRUint8 *      nonce, 	    int            cipherType){    PK11Context * cx        = 0;    unsigned      nkm       = 0; /* number of hashes to generate key mat. */    unsigned      nkd       = 0; /* size of readKey and writeKey. */    unsigned      part;    unsigned      i;    unsigned      off;    SECStatus     rv;    PRUint8       countChar;    PRUint8       km[3*16];	/* buffer for key material. */    readKey->data = 0;    writeKey->data = 0;    PORT_Assert( ssl_Have1stHandshakeLock(ss) );    rv = SECSuccess;    cx = PK11_CreateDigestContext(SEC_OID_MD5);    if (cx == NULL) {	ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE);	return SECFailure;    }    nkm = ssl_Specs[cipherType].nkm;    nkd = ssl_Specs[cipherType].nkd;    readKey->data = (PRUint8*) PORT_Alloc(nkd);    if (!readKey->data)     	goto loser;    readKey->len = nkd;    writeKey->data = (PRUint8*) PORT_Alloc(nkd);    if (!writeKey->data)     	goto loser;    writeKey->len = nkd;    /* Produce key material */    countChar = '0';    for (i = 0, off = 0; i < nkm; i++, off += 16) {	rv  = PK11_DigestBegin(cx);	rv |= PK11_DigestOp(cx, masterKey->data, masterKey->len);	rv |= PK11_DigestOp(cx, &countChar,      1);	rv |= PK11_DigestOp(cx, challenge,       SSL_CHALLENGE_BYTES);	rv |= PK11_DigestOp(cx, nonce,           SSL_CONNECTIONID_BYTES);	rv |= PK11_DigestFinal(cx, km+off, &part, MD5_LENGTH);	if (rv != SECSuccess) {	    ssl_MapLowLevelError(SSL_ERROR_MD5_DIGEST_FAILURE);	    rv = SECFailure;	    goto loser;	}	countChar++;    }    /* Produce keys */    PORT_Memcpy(readKey->data,  km,       nkd);    PORT_Memcpy(writeKey->data, km + nkd, nkd);loser:    PK11_DestroyContext(cx, PR_TRUE);    return rv;}