/*
 * Given a context specified via a CSSM_CC_HANDLE, add a new
 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
 * AttributeLength, and an untyped pointer.
 */
CSSM_RETURN impExpAddContextAttribute(CSSM_CC_HANDLE CCHandle,
	uint32 AttributeType,
	uint32 AttributeLength,
	const void *AttributePtr)
{
	CSSM_CONTEXT_ATTRIBUTE		newAttr;

	newAttr.AttributeType     = AttributeType;
	newAttr.AttributeLength   = AttributeLength;
	newAttr.Attribute.Data    = (CSSM_DATA_PTR)AttributePtr;
	return CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
}
/*
 * Given a context specified via a CSSM_CC_HANDLE, add a new
 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
 * AttributeLength, and an untyped pointer.
 */
CSSM_RETURN p12AddContextAttribute(CSSM_CC_HANDLE CCHandle,
	uint32 AttributeType,
	uint32 AttributeLength,
	const void *AttributePtr)
{
	CSSM_CONTEXT_ATTRIBUTE		newAttr;	
	CSSM_RETURN					crtn;
	
	newAttr.AttributeType     = AttributeType;
	newAttr.AttributeLength   = AttributeLength;
	newAttr.Attribute.Data    = (CSSM_DATA_PTR)AttributePtr;
	crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
	if(crtn) {
		p12LogCssmError("CSSM_UpdateContextAttributes", crtn);
	}
	return crtn;
}
/*
 * Wrap a private key, yielding shrouded key bits. 
 */
CSSM_RETURN p12WrapKey(
	CSSM_CSP_HANDLE		cspHand,
	CSSM_KEY_PTR		privKey,
	const CSSM_ACCESS_CREDENTIALS *privKeyCreds,
	CSSM_ALGORITHMS		keyAlg,				// of the unwrapping key
	CSSM_ALGORITHMS		encrAlg,
	CSSM_ALGORITHMS		pbeHashAlg,			// SHA1, MD5 only
	uint32				keySizeInBits,
	uint32				blockSizeInBytes,	// for IV
	CSSM_PADDING		padding,			// CSSM_PADDING_PKCS7, etc.
	CSSM_ENCRYPT_MODE	mode,				// CSSM_ALGMODE_CBCPadIV8, etc.
	uint32				iterCount,
	const CSSM_DATA		&salt,
	const CSSM_DATA		*pwd,		// unicode external representation
	const CSSM_KEY		*passKey,
	SecNssCoder			&coder,		// for mallocing keyBits
	CSSM_DATA			&shroudedKeyBits)	// RETURNED
{
	CSSM_RETURN crtn;
	CSSM_KEY ckey;
	CSSM_CC_HANDLE ccHand = 0;
	CSSM_KEY wrappedKey;
	CSSM_CONTEXT_ATTRIBUTE attr;
	CSSM_DATA descrData = {0, NULL};
	CSSM_ACCESS_CREDENTIALS creds;
	
	/* key must be extractable */
	if (!(privKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE)) {
		return errSecDataNotAvailable;
	}

	if(privKeyCreds == NULL) {
		/* i.e., key is from the bare CSP with no ACL support */
		memset(&creds, 0, sizeof(creds));
		privKeyCreds = &creds;
	}
	
	/* P12 style IV derivation, optional */
	CSSM_DATA iv = {0, NULL};
	CSSM_DATA_PTR ivPtr = NULL;
	if(blockSizeInBytes) {
		coder.allocItem(iv, blockSizeInBytes);
		ivPtr = &iv;
	}
	
	/* P12 style key derivation */
	crtn = p12KeyGen(cspHand, ckey, true, keyAlg, pbeHashAlg,
		keySizeInBits, iterCount, salt, pwd, passKey, iv);
	if(crtn) {
		return crtn;
	}	
	/* subsequent errors to errOut: */
		
	/* CSSM context */
	crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
		encrAlg,
		mode,
		NULL,			// access cred
		&ckey,
		ivPtr,			// InitVector, optional
		padding,	
		NULL,			// Params
		&ccHand);
	if(crtn) {
		p12LogCssmError("CSSM_CSP_CreateSymmetricContext", crtn);
		goto errOut;
	}
	
	memset(&wrappedKey, 0, sizeof(CSSM_KEY));
	
	/* specify PKCS8 wrap format */
	attr.AttributeType = CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT;
	attr.AttributeLength = sizeof(uint32);
	attr.Attribute.Uint32 = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8;
	crtn = CSSM_UpdateContextAttributes(
		ccHand,
		1,
		&attr);
	if(crtn) {
		p12LogCssmError("CSSM_UpdateContextAttributes", crtn);
		goto errOut;
	}
	
	crtn = CSSM_WrapKey(ccHand,
		privKeyCreds,
		privKey,
		&descrData,			// DescriptiveData
		&wrappedKey);
	if(crtn) {
		p12LogCssmError("CSSM_WrapKey", crtn);
	}
	else {
		coder.allocCopyItem(wrappedKey.KeyData, shroudedKeyBits);
		
		/* this was mallocd by CSP */
		freeCssmMemory(cspHand, wrappedKey.KeyData.Data);
	}
errOut:
	if(ccHand) {
		CSSM_DeleteContext(ccHand);
	}
	CSSM_FreeKey(cspHand, NULL, &ckey, CSSM_FALSE);
	return crtn;
}
SECStatus
ssl3_PlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf,
                        PRBool isTLS, KeyType keyType)
{
    SECStatus       rv                  = SECFailure;
    PRBool          doDerEncode         = PR_FALSE;
    unsigned int    signatureLen;
    OSStatus        status              = noErr;
    CSSM_CSP_HANDLE cspHandle           = 0;
    const CSSM_KEY *cssmKey             = NULL;
    CSSM_ALGORITHMS sigAlg;
    CSSM_ALGORITHMS digestAlg;
    const CSSM_ACCESS_CREDENTIALS * cssmCreds = NULL;
    CSSM_RETURN     cssmRv;
    CSSM_DATA       hashData;
    CSSM_DATA       signatureData;
    CSSM_CC_HANDLE  cssmSignature       = 0;
    const SSL3Opaque* prefix;
    unsigned int    prefixLen;
    SSL3Opaque      prefixAndHash[SSL_MAX_DIGEST_INFO_PREFIX + HASH_LENGTH_MAX];

    buf->data = NULL;

    status = SecKeyGetCSPHandle(key, &cspHandle);
    if (status != noErr) {
        PORT_SetError(SEC_ERROR_INVALID_KEY);
        goto done;
    }

    status = SecKeyGetCSSMKey(key, &cssmKey);
    if (status != noErr || !cssmKey) {
        PORT_SetError(SEC_ERROR_NO_KEY);
        goto done;
    }

    /* SecKeyGetBlockSize wasn't addeded until OS X 10.6 - but the
     * needed information is readily available on the key itself.
     */
    signatureLen = (cssmKey->KeyHeader.LogicalKeySizeInBits + 7) / 8;

    if (signatureLen == 0) {
        PORT_SetError(SEC_ERROR_INVALID_KEY);
        goto done;
    }

    buf->data = (unsigned char *)PORT_Alloc(signatureLen);
    if (!buf->data)
        goto done;    /* error code was set. */

    sigAlg = cssmKey->KeyHeader.AlgorithmId;
    digestAlg = CSSM_ALGID_NONE;

    switch (keyType) {
        case rsaKey:
            PORT_Assert(sigAlg == CSSM_ALGID_RSA);
            if (ssl3_GetDigestInfoPrefix(hash->hashAlg, &prefix, &prefixLen) !=
                SECSuccess) {
                goto done;
            }
            if (prefixLen + hash->len > sizeof(prefixAndHash)) {
                PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
                goto done;
            }
            memcpy(prefixAndHash, prefix, prefixLen);
            memcpy(prefixAndHash + prefixLen, hash->u.raw, hash->len);
            hashData.Data   = prefixAndHash;
            hashData.Length = prefixLen + hash->len;
            break;
        case dsaKey:
        case ecKey:
            if (keyType == ecKey) {
                PORT_Assert(sigAlg == CSSM_ALGID_ECDSA);
                doDerEncode = PR_TRUE;
            } else {
                PORT_Assert(sigAlg == CSSM_ALGID_DSA);
                doDerEncode = isTLS;
            }
            if (hash->hashAlg == SEC_OID_UNKNOWN) {
                hashData.Data   = hash->u.s.sha;
                hashData.Length = sizeof(hash->u.s.sha);
            } else {
                hashData.Data   = hash->u.raw;
                hashData.Length = hash->len;
            }
            break;
        default:
            PORT_SetError(SEC_ERROR_INVALID_KEY);
            goto done;
    }
    PRINT_BUF(60, (NULL, "hash(es) to be signed", hashData.Data, hashData.Length));

    /* TODO(rsleevi): Should it be kSecCredentialTypeNoUI? In Win32, at least,
     * you can prevent the UI by setting the provider handle on the
     * certificate to be opened with CRYPT_SILENT, but is there an equivalent?
     */
    status = SecKeyGetCredentials(key, CSSM_ACL_AUTHORIZATION_SIGN,
                                  kSecCredentialTypeDefault, &cssmCreds);
    if (status != noErr) {
        PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, status);
        goto done;
    }

    signatureData.Length = signatureLen;
    signatureData.Data   = (uint8*)buf->data;

    cssmRv = CSSM_CSP_CreateSignatureContext(cspHandle, sigAlg, cssmCreds,
                                             cssmKey, &cssmSignature);
    if (cssmRv) {
        PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, cssmRv);
        goto done;
    }

    /* See "Apple Cryptographic Service Provider Functional Specification" */
    if (cssmKey->KeyHeader.AlgorithmId == CSSM_ALGID_RSA) {
        /* To set RSA blinding for RSA keys */
        CSSM_CONTEXT_ATTRIBUTE blindingAttr;
        blindingAttr.AttributeType   = CSSM_ATTRIBUTE_RSA_BLINDING;
        blindingAttr.AttributeLength = sizeof(uint32);
        blindingAttr.Attribute.Uint32 = 1;
        cssmRv = CSSM_UpdateContextAttributes(cssmSignature, 1, &blindingAttr);
        if (cssmRv) {
            PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, cssmRv);
            goto done;
        }
    }

    cssmRv = CSSM_SignData(cssmSignature, &hashData, 1, digestAlg,
                           &signatureData);
    if (cssmRv) {
        PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, cssmRv);
        goto done;
    }
    buf->len = signatureData.Length;

    if (doDerEncode) {
        SECItem derSig = {siBuffer, NULL, 0};

        /* This also works for an ECDSA signature */
        rv = DSAU_EncodeDerSigWithLen(&derSig, buf, buf->len);
        if (rv == SECSuccess) {
            PORT_Free(buf->data);     /* discard unencoded signature. */
            *buf = derSig;            /* give caller encoded signature. */
        } else if (derSig.data) {
            PORT_Free(derSig.data);
        }
    } else {
        rv = SECSuccess;
    }

    PRINT_BUF(60, (NULL, "signed hashes", buf->data, buf->len));
done:
    /* cspHandle, cssmKey, and cssmCreds are owned by the SecKeyRef and
     * should not be freed. When the PlatformKey is freed, they will be
     * released.
     */
    if (cssmSignature)
        CSSM_DeleteContext(cssmSignature);

    if (rv != SECSuccess && buf->data) {
        PORT_Free(buf->data);
        buf->data = NULL;
    }
    return rv;
}
/* NULL wrap a key to specified format. */
static CSSM_RETURN nullWrapKey(CSSM_CSP_HANDLE cspHand,
	const CSSM_KEY			*refKey,	
	CSSM_KEYBLOB_FORMAT		blobFormat,
	CSSM_KEY_PTR			rawKey)			// RETURNED
{
	CSSM_CC_HANDLE		ccHand;
	CSSM_RETURN			crtn;
	CSSM_ACCESS_CREDENTIALS	creds;
	CSSM_DATA descData = {0, 0};
	
	memset(rawKey, 0, sizeof(CSSM_KEY));
	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
	crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
				CSSM_ALGID_NONE,
				CSSM_ALGMODE_NONE,
				&creds,			// passPhrase
				NULL,			// unwrappingKey
				NULL,			// initVector
				CSSM_PADDING_NONE,
				0,				// Params
				&ccHand);
	if(crtn) {
		printError("cspWrapKey/CreateContext", crtn);
		return crtn;
	}
	if(blobFormat != CSSM_KEYBLOB_WRAPPED_FORMAT_NONE) {
		/* only add this attribute if it's not the default */
		CSSM_ATTRIBUTE_TYPE attrType;
	
		switch(refKey->KeyHeader.KeyClass) {
			case CSSM_KEYCLASS_SESSION_KEY:
				attrType = CSSM_ATTRIBUTE_SYMMETRIC_KEY_FORMAT;
				break;
			case CSSM_KEYCLASS_PUBLIC_KEY:
				attrType = CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT;
				break;
			case CSSM_KEYCLASS_PRIVATE_KEY:
				attrType = CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT;
				break;
			default:
				printf("***Bogus KeyClass in nullWrapKey\n");
				return -1;
		}
		CSSM_CONTEXT_ATTRIBUTE attr;
		attr.AttributeType = attrType;
		attr.AttributeLength = sizeof(uint32);
		attr.Attribute.Uint32 = blobFormat;
		crtn = CSSM_UpdateContextAttributes(
			ccHand,
			1,
			&attr);
		if(crtn) {
			printError("CSSM_UpdateContextAttributes", crtn);
			return crtn;
		}
	}
	crtn = CSSM_WrapKey(ccHand,
		&creds,
		refKey,
		&descData,	
		rawKey);
	if(crtn != CSSM_OK) {
		printError("CSSM_WrapKey", crtn);
	}
	if(CSSM_DeleteContext(ccHand)) {
		printf("CSSM_DeleteContext failure\n");
	}
	return crtn;
}
/*
 * CDSA private key decrypt with blinding option.
 */
static CSSM_RETURN _cspDecrypt(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_FEED, etc.
		uint32 mode,						// CSSM_ALGMODE_CBC, etc. - only for symmetric algs
		CSSM_PADDING padding,				// CSSM_PADDING_PKCS1, etc. 
		CSSM_BOOL blinding,
		const CSSM_KEY *key,				// public or session key
		const CSSM_DATA *ctext,
		CSSM_DATA_PTR ptext)				// RETURNED
{
	CSSM_CC_HANDLE 	cryptHand;
	CSSM_RETURN		crtn;
	CSSM_RETURN		ocrtn = CSSM_OK;
	CSSM_SIZE		bytesDecrypted;
	CSSM_DATA		remData = {0, NULL};

	cryptHand = genCryptHandle(cspHand, 
		algorithm, 
		mode, 
		padding,
		key, 
		NULL,		// pubKey, 
		NULL,		// iv,
		0,			// effectiveKeySizeInBits,
		0);			// rounds
	if(cryptHand == 0) {
		return CSSMERR_CSP_INTERNAL_ERROR;
	}
	if(blinding) {
		CSSM_CONTEXT_ATTRIBUTE	newAttr;	
		newAttr.AttributeType     = CSSM_ATTRIBUTE_RSA_BLINDING;
		newAttr.AttributeLength   = sizeof(uint32);
		newAttr.Attribute.Uint32  = 1;
		crtn = CSSM_UpdateContextAttributes(cryptHand, 1, &newAttr);
		if(crtn) {
			printError("CSSM_UpdateContextAttributes", crtn);
			return crtn;
		}
	}

	crtn = CSSM_DecryptData(cryptHand,
		ctext,
		1,
		ptext,
		1,
		&bytesDecrypted,
		&remData);
	if(crtn == CSSM_OK) {
		// NOTE: We return the proper length in ptext....
		ptext->Length = bytesDecrypted;
		
		// FIXME - sometimes get mallocd RemData here, but never any valid data
		// there...side effect of CSPFullPluginSession's buffer handling logic;
		// but will we ever actually see valid data in RemData? So far we never
		// have....
		if(remData.Data != NULL) {
			appFree(remData.Data, NULL);
		}
	}
	else {
		printError("CSSM_DecryptData", crtn);
		ocrtn = crtn;
	}
	crtn = CSSM_DeleteContext(cryptHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	return ocrtn;
}
/* sign with RSA blinging option */
static CSSM_RETURN _cspSign(CSSM_CSP_HANDLE cspHand,
		uint32 algorithm,					// CSSM_ALGID_FEE_MD5, etc.
		CSSM_KEY_PTR key,					// private key
		const CSSM_DATA *text,
		CSSM_BOOL rsaBlinding,
		CSSM_DATA_PTR sig)					// RETURNED
{
	CSSM_CC_HANDLE	sigHand;
	CSSM_RETURN		crtn;
	CSSM_RETURN		ocrtn = CSSM_OK;
	const CSSM_DATA	*ptext;
	CSSM_DATA		digest = {0, NULL};
	CSSM_ALGORITHMS	digestAlg = CSSM_ALGID_NONE;

	/* handle special cases for raw sign */
	switch(algorithm) {
		case CSSM_ALGID_SHA1:
			digestAlg = CSSM_ALGID_SHA1;
			algorithm = CSSM_ALGID_RSA;
			break;
		case CSSM_ALGID_MD5:
			digestAlg = CSSM_ALGID_MD5;
			algorithm = CSSM_ALGID_RSA;
			break;
		case CSSM_ALGID_DSA:
			digestAlg = CSSM_ALGID_SHA1;
			algorithm = CSSM_ALGID_DSA;
			break;
		default:
			break;
	}
	if(digestAlg != CSSM_ALGID_NONE) {
		crtn = cspDigest(cspHand,
			digestAlg,
			CSSM_FALSE,			// mallocDigest
			text,
			&digest);
		if(crtn) {
			return crtn;
		}	
		/* sign digest with raw RSA/DSA */
		ptext = &digest;
	}
	else {
		ptext = text;
	}
	crtn = CSSM_CSP_CreateSignatureContext(cspHand,
		algorithm,
		NULL,				// passPhrase
		key,
		&sigHand);
	if(crtn) {
		printError("CSSM_CSP_CreateSignatureContext (1)", crtn);
		return crtn;
	}
	if(rsaBlinding) {
		CSSM_CONTEXT_ATTRIBUTE	newAttr;	
		newAttr.AttributeType     = CSSM_ATTRIBUTE_RSA_BLINDING;
		newAttr.AttributeLength   = sizeof(uint32);
		newAttr.Attribute.Uint32  = 1;
		crtn = CSSM_UpdateContextAttributes(sigHand, 1, &newAttr);
		if(crtn) {
			printError("CSSM_UpdateContextAttributes", crtn);
			return crtn;
		}
	}
	crtn = CSSM_SignData(sigHand,
		ptext,
		1,
		digestAlg,
		sig);
	if(crtn) {
		printError("CSSM_SignData", crtn);
		ocrtn = crtn;
	}
	crtn = CSSM_DeleteContext(sigHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
		ocrtn = crtn;
	}
	if(digest.Data != NULL) {
		CSSM_FREE(digest.Data);
	}
	return ocrtn;
}
/* wrap key function. */
static CSSM_RETURN wrapKey(CSSM_CSP_HANDLE cspHand,
	const CSSM_KEY_PTR		unwrappedKey,		// must be ref
	const CSSM_KEY_PTR		wrappingKey,
	CSSM_ALGORITHMS			wrapAlg,
	CSSM_ENCRYPT_MODE		wrapMode,
	CSSM_KEYBLOB_FORMAT		wrapFormat,			// NONE, PKCS7, PKCS8
	CSSM_PADDING 			wrapPad,
	CSSM_KEY_PTR			wrappedKey)			// RETURNED
{
	CSSM_CC_HANDLE		ccHand;
	CSSM_RETURN			crtn;
	CSSM_RETURN			crtn2;
	#if	WRAP_KEY_REQUIRES_CREDS
	CSSM_ACCESS_CREDENTIALS	creds;
	#endif
	
	#if 0
	if(unwrappedKey->KeyHeader.BlobType != CSSM_KEYBLOB_REFERENCE) {
		printf("Hey! you can only wrap a reference key!\n");
		return CSSM_ERRCODE_INTERNAL_ERROR;
	}
	#endif
	memset(wrappedKey, 0, sizeof(CSSM_KEY));
	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
	/* special case for NULL wrap - no wrapping key */
	if((wrappingKey == NULL) ||
	   (wrappingKey->KeyHeader.KeyClass == CSSM_KEYCLASS_SESSION_KEY)) {
		crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
				wrapAlg,
				wrapMode,
				&creds,					// accessCred
				wrappingKey,
				&initVector,
				wrapPad,				// Padding
				NULL,					// Reserved
				&ccHand);
		if(crtn) {
			printError("cspWrapKey/CreateContext", crtn);
			return CSSM_ERRCODE_INTERNAL_ERROR;
		}
	}
	else {
		crtn = CSSM_CSP_CreateAsymmetricContext(cspHand,
				wrapAlg,
				&creds,			// passPhrase
				wrappingKey,
				wrapPad,		// Padding
				&ccHand);
		if(crtn) {
			printError("cspWrapKey/CreateContext", crtn);
			return CSSM_ERRCODE_INTERNAL_ERROR;
		}
		/* CMS requires 8-byte IV */
		crtn = AddContextAttribute(ccHand,
			CSSM_ATTRIBUTE_INIT_VECTOR,
			sizeof(CSSM_DATA),
			CAT_Ptr,
			&initVector,
			0);
		if(crtn) {
			printError("CSSM_UpdateContextAttributes", crtn);
			return crtn;
		}
	}
	if(wrapFormat != CSSM_KEYBLOB_WRAPPED_FORMAT_NONE) {
		/* only add this attribute if it's not the default */
		CSSM_CONTEXT_ATTRIBUTE attr;
		attr.AttributeType = CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT;
		attr.AttributeLength = sizeof(uint32);
		attr.Attribute.Uint32 = wrapFormat;
		crtn = CSSM_UpdateContextAttributes(
			ccHand,
			1,
			&attr);
		if(crtn) {
			printError("CSSM_UpdateContextAttributes", crtn);
			return crtn;
		}
	}
	crtn = CSSM_WrapKey(ccHand,
		#if	WRAP_KEY_REQUIRES_CREDS
		&creds,
		#else
		NULL,			// AccessCred
		#endif
		unwrappedKey,
		NULL,			// DescriptiveData
		wrappedKey);
	if(crtn != CSSM_OK) {
		printError("CSSM_WrapKey", crtn);
	}
	if((crtn2 = CSSM_DeleteContext(ccHand))) {
		printError("CSSM_DeleteContext", crtn2);
	}
	return crtn;
}