Beispiel #1
0
/*
 * Given a partially formed DSA public key (with no p, q, or g) and a
 * CssmKey representing a supposedly fully-formed DSA key, populate
 * the public key's p, g, and q with values from the fully formed key.
 */
CSSM_RETURN dsaGetParamsFromKey(
    DSA 			*partialKey,
    const CssmKey	&paramKey,
    AppleCSPSession	&session)
{
    bool allocdKey;
    DSA *dsaParamKey = cssmKeyToDsa(paramKey, session, allocdKey);
    if(dsaParamKey == NULL) {
        errorLog0("dsaGetParamsFromKey: bad paramKey\n");
        return CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE;
    }
    CSSM_RETURN crtn = CSSM_OK;

    /* require fully formed other key of course... */
    if((dsaParamKey->p == NULL) ||
            (dsaParamKey->q == NULL) ||
            (dsaParamKey->g == NULL)) {
        errorLog0("dsaGetParamsFromKey: incomplete paramKey\n");
        crtn = CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE;
        goto abort;
    }
    rsaMiscDebug("dsaGetParamsFromKey: partialKey %p paramKey %p",
                 partialKey, dsaParamKey);

    partialKey->q = BN_dup(dsaParamKey->q);
    partialKey->p = BN_dup(dsaParamKey->p);
    partialKey->g = BN_dup(dsaParamKey->g);

abort:
    if(allocdKey) {
        DSA_free(dsaParamKey);
    }
    return crtn;
}
// 
// Handle various forms of reference key. Symmetric
// keys are stored as SymmetricBinaryKey, with raw key bytes
// in keyData. Our asymmetric keys are stored as BSafeBinaryKeys,
// with an embedded ready-to-use B_KEY_OBJ. 
// 
void BSafe::BSafeContext::setRefKey(CssmKey &key)
{
	bool isPubKey = false;
	
	switch(key.keyClass()) {
		case CSSM_KEYCLASS_SESSION_KEY:
		{
			assert(key.blobFormat() == 
				CSSM_KEYBLOB_REF_FORMAT_INTEGER);
			
			BinaryKey &binKey = session().lookupRefKey(key);
			// fails if this is not a SymmetricBinaryKey
			SymmetricBinaryKey *symBinKey =
				dynamic_cast<SymmetricBinaryKey *>(&binKey);
			if(symBinKey == NULL) {
				errorLog0("BSafe::setRefKey(1): wrong BinaryKey subclass\n");
				CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
			}
			setKeyFromCssmData(KI_Item, symBinKey->mKeyData);
			return;
		}
		case CSSM_KEYCLASS_PUBLIC_KEY:
			isPubKey = true;		// and fall thru
		case CSSM_KEYCLASS_PRIVATE_KEY:
		{
			BinaryKey &binKey = session().lookupRefKey(key);
			destroyBsKey();
			bsBinKey = dynamic_cast<BSafeBinaryKey *>(&binKey);
			/* this cast failing means that this is some other
			 * kind of binary key */
			if(bsBinKey == NULL) {
				errorLog0("BSafe::setRefKey(2): wrong BinaryKey subclass\n");
				CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
			}
			assert(bsBinKey->bsKey() != NULL);
			bsKey = bsBinKey->bsKey();
			if(key.algorithm() == CSSM_ALGID_RSA) {
				setRsaOutSize(isPubKey);
			}
			return;
		}
		default:
		    CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
	}
}
static void checkExclusiveUsage(
	uint32		keyUsage,		// requested usage word
	uint32		checkBits,		// if any of these are set
	uint32		otherBits,		// these are the only other bits which can be set
	const char	*errMsg)
{
	if(keyUsage & checkBits) {
		if(keyUsage & ~otherBits) {
			errorLog0((char *)errMsg);
			CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYUSAGE_MASK);
		}
	}
}
/*
 * CSPKeyInfoProvider for symmetric keys. 
 */
CSPKeyInfoProvider *SymmetricKeyInfoProvider::provider(
		const CssmKey 	&cssmKey,
		AppleCSPSession	&session)
{
	if(cssmKey.blobType() != CSSM_KEYBLOB_RAW) {
		errorLog0("KeyInfoProvider deals only with RAW keys!\n");
		CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
	}
	if(cssmKey.keyClass() != CSSM_KEYCLASS_SESSION_KEY) {
		/* that's all we need to know */
		return NULL;
	}
	return new SymmetricKeyInfoProvider(cssmKey, session);
}
void DeriveKey_DH (
	const Context &context,
	const CssmData &Param,			// other's public key. may be empty
	CSSM_DATA *keyData,				// mallocd by caller
									// we fill in keyData->Length bytes
	AppleCSPSession &session)
{
	bool mallocdPrivKey;
	size_t privSize;
	
	/* private DH key from context - required */
	DH *privKey = contextToDhKey(context, session, CSSM_ATTRIBUTE_KEY,
		CSSM_KEYCLASS_PRIVATE_KEY, CSSM_KEYUSE_DERIVE, mallocdPrivKey);
	if(privKey == NULL) {
		CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_KEY);
	}
	cspDhDebug("DeriveKey_DH, privKey %p", privKey);
	privSize = DH_size(privKey);
	if(privSize < keyData->Length) {
		/* we've been asked for more bits than this key can generate */
		CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE);
	}
	
	/*
	 * Public key ("their" key) can come from two places:
	 * -- in the context as a CSSM_ATTRIBUTE_PUBLIC_KEY. This is how 
	 *    public keys in X509 format must be used in this function
	 * -- in the incoming Param, the raw unformatted (PKCS3) form 
	 */
	bool mallocdPubKey = false;
	BIGNUM *pubKeyBn = NULL;
	bool allocdPubKeyBn = false;
	DH *pubKey = contextToDhKey(context, session, CSSM_ATTRIBUTE_PUBLIC_KEY,
		CSSM_KEYCLASS_PUBLIC_KEY, CSSM_KEYUSE_DERIVE, mallocdPubKey);
	if(pubKey != NULL) {
		if(pubKey->pub_key == NULL) {
			errorLog0("DeriveKey_DH: public key in context with no pub_key\n");
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
		}
		pubKeyBn = pubKey->pub_key;
		cspDhDebug("DeriveKey_DH, pubKey from context %p", pubKey);
	}
	else {
		if((Param.Data == NULL) || (Param.Length == 0)) {
			errorLog0("DeriveKey_DH: no pub_key, no Param\n");
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
		}
		pubKeyBn = BN_bin2bn(Param.Data, Param.Length, NULL);
		if(pubKeyBn == NULL) {
			CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
		}
		allocdPubKeyBn = true;
		cspDhDebug("DeriveKey_DH, no pubKey in context");
	}
	unsigned char *buf = (unsigned char *)session.malloc(privSize);
	int rtn = DH_compute_key(buf, pubKeyBn, privKey);
	if(rtn > 0) {
		/*
		 * FIXME : I have not found a specification describing *which*
		 * bytes of the value we just computed we are supposed to
		 * use as the actual key bytes. We use the M.S. bytes.
		 *
		 * Note that due to modulo arithmetic, we may have gotten fewer
		 * bytes than we asked for. If so, the caller will have
		 * to deal with that if they really need privSize bytes.
		 */
		assert((uint32)rtn <= privSize);
		uint32 toMove = keyData->Length;
		if((uint32)rtn < toMove) {
			toMove = (uint32)rtn;
		}
		memmove(keyData->Data, buf, toMove);
		keyData->Length = toMove;
	}
	if(mallocdPrivKey) {
		DH_free(privKey);
	}
	if(mallocdPubKey) {
		DH_free(pubKey);
	}
	if(allocdPubKeyBn) {
		BN_free(pubKeyBn);
	}
	session.free(buf);
	if(rtn <= 0) {
		throwRsaDsa("DH_compute_key");
	}
}
/*
 * Perform sanity check of incoming key attribute bits for a given
 * key type, and return a cspKeyStorage value.
 *
 * Called from any routine which generates a new key. This specifically
 * excludes WrapKey().
 */
cspKeyStorage cspParseKeyAttr(
	cspKeyType 	keyType,
	uint32 		keyAttr)
{
	uint32 sensitiveBit = (keyAttr & CSSM_KEYATTR_SENSITIVE)    ? 1 : 0;
	uint32 rtnDataBit   = (keyAttr & CSSM_KEYATTR_RETURN_DATA)  ? 1 : 0;
	uint32 rtnRefBit    = (keyAttr & CSSM_KEYATTR_RETURN_REF)   ? 1 : 0;
	uint32 extractBit   = (keyAttr & CSSM_KEYATTR_EXTRACTABLE)  ? 1 : 0;

	cspKeyStorage rtn;

	/* first general CDSA-wide checks */
	if(keyAttr & (CSSM_KEYATTR_ALWAYS_SENSITIVE |
				  CSSM_KEYATTR_NEVER_EXTRACTABLE)) {
		//errorLog0("ALWAYS_SENSITIVE, NEVER_EXTRACTABLE illegal at SPI\n");
		CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
	}
	switch(keyAttr & KEY_ATTR_RETURN_MASK) {
		/* ensure only one bit is set */
		case CSSM_KEYATTR_RETURN_DATA:
			rtn = CKS_Data;
			break;
		case CSSM_KEYATTR_RETURN_REF:
			rtn = CKS_Ref;
			break;
		case CSSM_KEYATTR_RETURN_NONE:
			rtn = CKS_None;
			break;
		case CSSM_KEYATTR_RETURN_DEFAULT:
			/* CSP default */
			rtnRefBit = 1;
			rtn = CKS_Ref;
			break;
		default:
			//errorLog0("Multiple KEYATTR_RETURN bits set\n");
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
	}

	/* now CSP-wide checks for all key types */
	if(keyType != CKT_Session) {
		/* session keys modifiable, no others are */
		if(keyAttr & CSSM_KEYATTR_MODIFIABLE) {
			//errorLog0("CSSM_KEYATTR_MODIFIABLE not supported\n");
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
		}
	}
	if(rtnDataBit) {
		if(!extractBit) {
			//errorLog0("RETURN_DATA and !EXTRACTABLE not supported\n");
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
		}
		if(sensitiveBit) {
			//errorLog0("RETURN_DATA and SENSITIVE not supported\n");
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
		}
	}

	/* now check per keyType. We're ust checking for things specific
	 * to KEYATTR_RETURN_xxx; cspValidateKeyAttr will check other fields. */
	 #if 0
	 // nothing for now
	switch(keyType) {
		case CKT_Session:
			break;

		case MKT_Public:
			break;

		case MKT_Private:
			if(rtnDataBit) {
				errorLog0("Private keys must be generated by ref\n");
				goto errorOut;
			}
			/*
			 * One more restriction - EXTRACTABLE - caller must check since
			 * that involves KEYUSE bits.
			 */
			break;
		default:
			CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
	}
	#endif	// 0

	/* validate other common static attributes */
	cspValidateKeyAttr(keyType, (keyAttr & ~KEY_ATTR_RETURN_MASK));
	return rtn;
}
/*
 * Validate key usage bits for specified key type.
 */
void cspValidateKeyUsageBits (
	cspKeyType	keyType,
	uint32		keyUsage)
{
	/* general restrictions */
	checkExclusiveUsage(keyUsage,
		CSSM_KEYUSE_ANY,
		CSSM_KEYUSE_ANY,
		"CSSM_KEYUSE_ANY overload");
	checkExclusiveUsage(keyUsage,
			CSSM_KEYUSE_DERIVE,
			CSSM_KEYUSE_DERIVE,
			"CSSM_KEYUSE_DERIVE overload\n");

	/* brute force per key type. */
	switch(keyType) {
		case CKT_Session:
			checkExclusiveUsage(keyUsage,
					CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
					CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
					"session key usage: encrypt/decrypt overload\n");
			checkExclusiveUsage(keyUsage,
					CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY |
						CSSM_KEYUSE_SIGN_RECOVER | CSSM_KEYUSE_VERIFY_RECOVER,
					CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY |
						CSSM_KEYUSE_SIGN_RECOVER | CSSM_KEYUSE_VERIFY_RECOVER,
					"session key usage: sign/verify overload\n");
			checkExclusiveUsage(keyUsage,
					CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP,
					CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP,
					"session key usage: wrap/unwrap overload\n");
			break;

		case CKT_Public:
			checkExclusiveUsage(keyUsage,
					CSSM_KEYUSE_ENCRYPT,
					CSSM_KEYUSE_ENCRYPT,
					"public key usage: encrypt overload\n");
			if(keyUsage & CSSM_KEYUSE_DECRYPT) {
				errorLog0("public key usage: DECRYPT illegal\n");
				CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYUSAGE_MASK);
			}
			if(keyUsage & (CSSM_KEYUSE_SIGN | CSSM_KEYUSE_SIGN_RECOVER)) {
				errorLog0("public key usage: SIGN illegal\n");
				CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYUSAGE_MASK);
			}
			checkExclusiveUsage(keyUsage,
					CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_VERIFY_RECOVER,
					CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_VERIFY_RECOVER,
					"public key usage: verify overload\n");
			checkExclusiveUsage(keyUsage,
					CSSM_KEYUSE_WRAP,
					CSSM_KEYUSE_WRAP,
					"public key usage: wrap overload\n");
			if(keyUsage & CSSM_KEYUSE_UNWRAP) {
				errorLog0("public key usage: UNWRAP illegal\n");
				CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYUSAGE_MASK);
			}
			break;

		case CKT_Private:
			if(keyUsage & CSSM_KEYUSE_ENCRYPT) {
				errorLog0("private key usage: ENCRYPT illegal\n");
				CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYUSAGE_MASK);
			}
			checkExclusiveUsage(keyUsage,
					CSSM_KEYUSE_DECRYPT,
					CSSM_KEYUSE_DECRYPT,
					"private key usage: decrypt overload\n");
			checkExclusiveUsage(keyUsage,
					CSSM_KEYUSE_SIGN | CSSM_KEYUSE_SIGN_RECOVER,
					CSSM_KEYUSE_SIGN | CSSM_KEYUSE_SIGN_RECOVER,
					"private key usage: sign overload\n");
			if(keyUsage & (CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_VERIFY_RECOVER)) {
				errorLog0("private key usage: VERIFY illegal\n");
				CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYUSAGE_MASK);
			}
			if(keyUsage & CSSM_KEYUSE_WRAP) {
				errorLog0("private key usage: WRAP illegal\n");
				CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYUSAGE_MASK);
			}
			checkExclusiveUsage(keyUsage,
					CSSM_KEYUSE_UNWRAP,
					CSSM_KEYUSE_UNWRAP,
					"private key usage: unwrap overload\n");
			break;
		default:
			CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
	}
}
void AppleCSPSession::WrapKey(
		CSSM_CC_HANDLE CCHandle,
		const Context &Context,
        const AccessCredentials &AccessCred,
        const CssmKey &UnwrappedKey,
        const CssmData *DescriptiveData,
        CssmKey &WrappedKey,
		CSSM_PRIVILEGE Privilege)
{
	CssmKey::Header 		&wrappedHdr   = WrappedKey.header();
	bool 					isNullWrap = false;
	CssmKey					*wrappingKey = NULL;
	CSSM_KEYBLOB_FORMAT		wrapFormat;
	
	switch(UnwrappedKey.keyClass()) {
		case CSSM_KEYCLASS_PUBLIC_KEY:
		case CSSM_KEYCLASS_PRIVATE_KEY:
		case CSSM_KEYCLASS_SESSION_KEY:
			break;
		default:
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
	}

	/* wrapping key only required for non-NULL wrap */
	wrappingKey = Context.get<CssmKey>(CSSM_ATTRIBUTE_KEY);
	if(wrappingKey == NULL) {
		if((Context.algorithm() == CSSM_ALGID_NONE) &&
		   (Context.type() == CSSM_ALGCLASS_SYMMETRIC)) {
				// NULL wrap, OK
				isNullWrap = true;
		}
		else {
			errorLog0("WrapKey: missing wrapping key\n");
			CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_KEY);
		}
	}
	
	/*
	 * Validate misc. params as best we can
	 */
	if(isNullWrap) {
		wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_NONE;
	}
	else {
		/*
		 * Can only wrap session and private keys. 
		 */
		#if		!ALLOW_PUB_KEY_WRAP
		if(UnwrappedKey.keyClass() == CSSM_KEYCLASS_PUBLIC_KEY) {
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
		}
		#endif	/* ALLOW_PUB_KEY_WRAP */
		cspValidateIntendedKeyUsage(&wrappingKey->KeyHeader, CSSM_KEYUSE_WRAP);
		cspVerifyKeyTimes(wrappingKey->KeyHeader);
		
		/*
		 * make sure wrapping key type matches context
		 */
		CSSM_CONTEXT_TYPE wrapType;
		switch(wrappingKey->KeyHeader.KeyClass) {
			case CSSM_KEYCLASS_PUBLIC_KEY:
			case CSSM_KEYCLASS_PRIVATE_KEY:
				wrapType = CSSM_ALGCLASS_ASYMMETRIC;
				break;
			case CSSM_KEYCLASS_SESSION_KEY:
				wrapType = CSSM_ALGCLASS_SYMMETRIC;
				break;
			default:
				errorLog0("WrapKey: bad class of wrappingKey\n");
				CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY);
		}
		if(wrapType != Context.type()) {
			errorLog0("WrapKey: mismatch wrappingKey/contextType\n");
			CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT);
		}
		if(Context.algorithm() == CSSM_ALGID_NONE) {
			errorLog0("WrapKey: null wrap alg, non-null key\n");
			CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
		}

		/*
		 * Get optional wrap format, set default per incoming keys
		 * Note: no such atrribute ==> 0 ==> FORMAT_NONE, which we
		 * take to mean "use the default".
		 */
		wrapFormat = Context.getInt(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT);
		if(wrapFormat == CSSM_KEYBLOB_WRAPPED_FORMAT_NONE) {
			/* figure out a default based on unwrapped key */
			switch(UnwrappedKey.keyClass()) {
				case CSSM_KEYCLASS_SESSION_KEY:
					wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7;
					break;
				case CSSM_KEYCLASS_PUBLIC_KEY:
					wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM; 
					break;
				case CSSM_KEYCLASS_PRIVATE_KEY:
					switch(UnwrappedKey.algorithm()) {
						case CSSM_ALGID_FEE:
							wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM; 
							break;
						default:
							wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8; 
							break;
					}
					break;
				default:
					/* NOT REACHED - checked above */
					break;
			}
		}		/* no format present or FORMAT_NONE */
	}
	
	/* make sure we have a valid format here */
	switch(wrapFormat) {
		case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7:
			if(UnwrappedKey.keyClass() != CSSM_KEYCLASS_SESSION_KEY) {
				/* this wrapping style only for symmetric keys */
				CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
			}
			break;
		case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8:
		case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL:
			if(UnwrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) {
				/* these wrapping styles only for private keys */
				CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
			}
			break;
		case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM:
			/* no restrictions (well AES can't be the wrap alg but that will 
			 * be caught later */
			break;
		case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1:
			/* RSA private key, reference format, only */
			if(UnwrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) {
				CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
			}
			if(UnwrappedKey.algorithm() != CSSM_ALGID_RSA) {
				CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
			}
			if(UnwrappedKey.blobType() != CSSM_KEYBLOB_REFERENCE) {
				CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
			}
			break;
		case CSSM_KEYBLOB_WRAPPED_FORMAT_NONE:
			if(isNullWrap) {
				/* only time this is OK */
				break;
			}
			/* else fall thru */
		default:
			dprintf1("KeyWrap: invalid wrapFormat (%d)\n", (int)wrapFormat);
			CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_WRAPPED_KEY_FORMAT);
	}
	/* get the blob to be wrappped */
	CssmData rawBlob;
	bool allocdRawBlob = false;
	CSSM_KEYBLOB_FORMAT rawFormat;
	
	/* 
	 * Outgoing same as incoming unless a partial key is completed during 
	 * generateKeyBlob()
	 */
	const CssmKey::Header &unwrappedHdr = UnwrappedKey.header();
	CSSM_KEYATTR_FLAGS unwrappedKeyAttrFlags = unwrappedHdr.KeyAttr;
	
	switch(UnwrappedKey.blobType()) {
		case CSSM_KEYBLOB_RAW:
			/* 
			 * Trivial case - we already have the blob.
			 * This op - wrapping a raw key - is not supported for the 
			 * CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1 format since that doesn't
			 * operate on a key blob.
			 */
			rawBlob = CssmData::overlay(UnwrappedKey.KeyData);
			rawFormat = UnwrappedKey.blobFormat();
			break;
		case CSSM_KEYBLOB_REFERENCE:
			/* get binary key, then get blob from it */
			{
				BinaryKey &binKey = lookupRefKey(UnwrappedKey);
				
				/*
				 * Subsequent tests for extractability: don't trust the 
				 * caller's header; use the one in the BinaryKey.
				 */
				CSSM_KEYATTR_FLAGS keyAttr = binKey.mKeyHeader.KeyAttr;
				if(!(keyAttr & CSSM_KEYATTR_EXTRACTABLE)) {
					/* this key not extractable in any form */
					CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
				}
				
				/* 
				 * CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1: we're ready to roll; 
				 * all we need is the reference key.
				 */
				if(wrapFormat == CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1) {
					break;
				}
				
				/*
				 * Null wrap - prevent caller from obtaining 
				 * clear bits if CSSM_KEYATTR_SENSITIVE
				 */
				if(isNullWrap && (keyAttr & CSSM_KEYATTR_SENSITIVE)) {
					CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
				}

				/*
				 * Special case for PKCS8 and openssl: need to get blob of a specific
				 * algorithm-dependent format. Caller can override our 
				 * preference with a 
				 * CSSM_ATTRIBUTE_{PRIVATE,PUBLIC,SESSION}_KEY_FORMAT 
				 * context attribute. 
				 */
				rawFormat = requestedKeyFormat(Context, UnwrappedKey);
				if(rawFormat == CSSM_KEYBLOB_RAW_FORMAT_NONE) {
					CSSM_ALGORITHMS keyAlg = binKey.mKeyHeader.AlgorithmId;
					switch(wrapFormat) {
						case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8:
							rawFormat = pkcs8RawKeyFormat(keyAlg);
							break;
						case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL:
							rawFormat = opensslRawKeyFormat(keyAlg);
							break;
						default:
							/* punt and take default for key type */
							break;
					}
				}
	
				/* 
				 * DescriptiveData for encoding, currently only used for 
				 * SSH1 keys.
				 */
				if((DescriptiveData != NULL) && (DescriptiveData->Length != 0)) {
					binKey.descData(*DescriptiveData);
				}
				
				/* optional parameter-bearing key */
				CssmKey *paramKey = Context.get<CssmKey>(CSSM_ATTRIBUTE_PARAM_KEY);
				binKey.generateKeyBlob(privAllocator,
					rawBlob,
					rawFormat,
					*this,
					paramKey,
					unwrappedKeyAttrFlags);
			}
			allocdRawBlob = true;		// remember - we need to free
			break;
			
		default:
			errorLog0("WrapKey: bad unwrappedKey BlobType\n");
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
	}

	/*
	 * Prepare outgoing header.
	 */
	setKeyHeader(wrappedHdr,
		plugin.myGuid(),
		unwrappedHdr.algorithm(),		// same as incoming 
		unwrappedHdr.keyClass(),		// same as incoming
		unwrappedKeyAttrFlags,
		unwrappedHdr.KeyUsage);
	wrappedHdr.LogicalKeySizeInBits = unwrappedHdr.LogicalKeySizeInBits;
	wrappedHdr.WrapAlgorithmId = Context.algorithm(); 	// true for null 
														// and non-Null 
	wrappedHdr.StartDate = unwrappedHdr.StartDate;
	wrappedHdr.EndDate = unwrappedHdr.EndDate;
	wrappedHdr.Format = wrapFormat;
	if(isNullWrap) {
		wrappedHdr.BlobType = CSSM_KEYBLOB_RAW;
	}
	else {
		wrappedHdr.BlobType = CSSM_KEYBLOB_WRAPPED;
	}
	
	/* 
	 * special cases - break out here for Apple Custom and OpenSSHv1  
	 */
	if(!isNullWrap) {
		switch(wrapFormat) {
			case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM:
				try {
					WrapKeyCms(CCHandle,
						Context,
						AccessCred,
						UnwrappedKey,
						rawBlob,
						allocdRawBlob,
						DescriptiveData,
						WrappedKey,
						Privilege);
				}
				catch(...) {
					if(allocdRawBlob) {
						freeCssmData(rawBlob, privAllocator);
					}
					throw;
				}
				if(allocdRawBlob) {
					freeCssmData(rawBlob, privAllocator);
				}
				return;
			case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1:
			{
				/*
				 * 1. We don't have to worry about allocdRawBlob since this 
				 *    operation only works on reference keys and we did not
				 *    obtain the raw blob from the BinaryKey. 
				 * 2. This is a redundant lookupRefKey, I know, but since
				 *    that returns a reference, it would just be too messy to have
				 *    the previous call be in the same scope as this.
				 */
				BinaryKey &binKey = lookupRefKey(UnwrappedKey);
				WrapKeyOpenSSH1(CCHandle,
					Context,
					AccessCred,
					binKey,
					rawBlob,
					allocdRawBlob,
					DescriptiveData,
					WrappedKey,
					Privilege);
				return;
			}
			default:
				/* proceed to encrypt blob */
				break;
		}
	}	/* !isNullWrap */

	
	/*
	 * Generate wrapped blob. Careful, we need to conditionally free
	 * rawBlob on error.
	 */
	CssmData encryptedBlob;
	CssmData remData;
	WrappedKey.KeyData.Data = NULL;		// ignore possible incoming KeyData
	WrappedKey.KeyData.Length = 0;
	
	try {
		if(isNullWrap) {
			/* copy raw blob to caller's wrappedKey */
			copyCssmData(rawBlob, 
				CssmData::overlay(WrappedKey.KeyData), 
				normAllocator);
			wrappedHdr.Format   = rawFormat; 
		}
		else {
			/* encrypt rawBlob using caller's context, then encode to
			 * WrappedKey.KeyData */
			CSSM_SIZE bytesEncrypted;
			EncryptData(CCHandle,
				Context,
				&rawBlob,			// ClearBufs[]
				1,					// ClearBufCount
				&encryptedBlob,		// CipherBufs[],
				1,					// CipherBufCount,
				bytesEncrypted,
				remData,
				Privilege);
	
			// I'm not 100% sure about this....
			assert(remData.Length == 0);
			encryptedBlob.Length = bytesEncrypted;
			WrappedKey.KeyData = encryptedBlob;
			wrappedHdr.BlobType = CSSM_KEYBLOB_WRAPPED;
			// OK to be zero or not present 
			wrappedHdr.WrapMode = Context.getInt(
				CSSM_ATTRIBUTE_MODE);
		}
	}
	catch (...) {
		errorLog0("WrapKey: EncryptData() threw exception\n");
		if(allocdRawBlob) {
			freeCssmData(rawBlob, privAllocator);
		}
		freeCssmData(remData,normAllocator);
		throw;
	}
	if(allocdRawBlob) {
		freeCssmData(rawBlob, privAllocator);
	}
	freeCssmData(remData, normAllocator);
}
/*
 * Unwrap key function. Used for:
 *
 * -- Given key of BlobType CSSM_KEYBLOB_WRAPPED, decode and decrypt
 *    it, yielding a key in either raw or reference format. Unwrapping
 *    key may be either raw or reference. The context must match
 *    the unwrapping key (ALGCLASS_SYMMETRIC  or ALGCLASS_ASYMMETRIC).
 *
 *	  Private keys are assumed to be PKCS8 encoded; session keys  
 *    are assumed to be PKCS7 encoded. 
 *
 * -- Convert a Raw key to a reference key (with no decrypting).
 *    This is called a NULL unwrap; no unwrapping key need be present in
 *    the context, but the context must be of class 
 *    ALGCLASS_SYMMETRIC and algorithm ALGID_NONE.
 */ 
void AppleCSPSession::UnwrapKey(
		CSSM_CC_HANDLE CCHandle,
		const Context &Context,
		const CssmKey *PublicKey,
		const CssmKey &WrappedKey,
		uint32 KeyUsage,
		uint32 KeyAttr,
		const CssmData *KeyLabel,
		const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry,
		CssmKey &UnwrappedKey,
		CssmData &DescriptiveData,
		CSSM_PRIVILEGE Privilege)
{
	bool 					isNullUnwrap = false;
	CssmKey					*unwrappingKey = NULL;
	cspKeyType				keyType;				// CKT_Public, etc. 
	CSSM_KEYBLOB_FORMAT		wrapFormat = WrappedKey.blobFormat();
	
	/* obtain unwrapping key if present */
	unwrappingKey = Context.get<CssmKey>(CSSM_ATTRIBUTE_KEY);
	if(unwrappingKey == NULL) {
		if((Context.algorithm() == CSSM_ALGID_NONE) &&
		   (Context.type() == CSSM_ALGCLASS_SYMMETRIC)) {
				// NULL unwrap, OK
				isNullUnwrap = true;
		}
		else {
			errorLog0("UnwrapKey: missing wrapping key\n");
			CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_KEY);
		}
	}

	/* 
	 * validate unwrappingKey 
	 */
	if(!isNullUnwrap) {
		/* make sure unwrapping key type matches context */
		CSSM_CONTEXT_TYPE unwrapType;
		switch(unwrappingKey->KeyHeader.KeyClass) {
			case CSSM_KEYCLASS_PUBLIC_KEY:
			case CSSM_KEYCLASS_PRIVATE_KEY:
				unwrapType = CSSM_ALGCLASS_ASYMMETRIC;
				break;
			case CSSM_KEYCLASS_SESSION_KEY:
				unwrapType = CSSM_ALGCLASS_SYMMETRIC;
				break;
			default:
				errorLog0("UnwrapKey: bad class of wrappingKey\n");
				CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY);
		}
		if(unwrapType != Context.type()) {
			errorLog0("UnwrapKey: mismatch unwrappingKey/contextType\n");
			CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT);
		}
		if(Context.algorithm() == CSSM_ALGID_NONE) {
			errorLog0("UnwrapKey: null wrap alg, non-null key\n");
			CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
		}
		cspValidateIntendedKeyUsage(&unwrappingKey->KeyHeader, CSSM_KEYUSE_UNWRAP);
		cspVerifyKeyTimes(unwrappingKey->KeyHeader);
	}

	/* validate WrappedKey */
	switch(WrappedKey.keyClass()) {
		case CSSM_KEYCLASS_PUBLIC_KEY:
			#if 	!ALLOW_PUB_KEY_WRAP
			if(!isNullUnwrap) {
				errorLog0("UnwrapKey: unwrap of public key illegal\n");
				CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
			}
			#endif	/* ALLOW_PUB_KEY_WRAP */
			keyType = CKT_Public;
			break;
		case CSSM_KEYCLASS_PRIVATE_KEY:
			keyType = CKT_Private;
			break;
		case CSSM_KEYCLASS_SESSION_KEY:
			keyType = CKT_Session;
			break;
		default:
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
	}
	if(isNullUnwrap) {
		if(WrappedKey.blobType() != CSSM_KEYBLOB_RAW) {
			errorLog0("UnwrapKey: expected raw blobType\n");
			CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
		}
	}
	else {
		if(WrappedKey.blobType() != CSSM_KEYBLOB_WRAPPED) {
			errorLog0("UnwrapKey: expected wrapped blobType\n");
			CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
		}
	}

	/* validate requested storage and usage */
	cspKeyStorage keyStorage = cspParseKeyAttr(keyType, KeyAttr);
	switch(keyStorage) {
		case CKS_Ref:
		case CKS_Data:
			break;		// OK
		default:
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
	}
	cspValidateKeyUsageBits(keyType,  KeyUsage);

	/* prepare outgoing header */
	CssmKey::Header &unwrappedHdr = UnwrappedKey.header();
	const CssmKey::Header &wrappedHdr   = WrappedKey.header();
	setKeyHeader(unwrappedHdr,
		plugin.myGuid(),
		wrappedHdr.algorithm(),		// same as incoming 
		wrappedHdr.keyClass(),		// same as incoming
		KeyAttr & ~KEY_ATTR_RETURN_MASK,
		KeyUsage);
	unwrappedHdr.LogicalKeySizeInBits = wrappedHdr.LogicalKeySizeInBits;
	unwrappedHdr.StartDate = wrappedHdr.StartDate;
	unwrappedHdr.EndDate = wrappedHdr.EndDate;
	UnwrappedKey.KeyData.Data = NULL;	// ignore possible incoming KeyData
	UnwrappedKey.KeyData.Length = 0;
	
	/* validate wrappedKey format */
	if(!isNullUnwrap) {
		switch(wrapFormat) {
			case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7:
				if(WrappedKey.keyClass() != CSSM_KEYCLASS_SESSION_KEY) {
					/* this unwrapping style only for symmetric keys */
					CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
				}
				break;
			case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8:
			case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL:
				if(WrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) {
					/* these unwrapping styles only for private keys */
					CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
				}
				break;
			case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM:
				UnwrapKeyCms(CCHandle,
						Context,
						WrappedKey,
						CredAndAclEntry,
						UnwrappedKey,
						DescriptiveData,
						Privilege,
						keyStorage);
				return;
			case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1:
				/* RSA private key, unwrap to ref key only */
				if(WrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) {
					CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
				}
				if(WrappedKey.algorithm() != CSSM_ALGID_RSA) {
					CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
				}
				if(keyStorage != CKS_Ref) {
					errorLog0("UNwrapKey: OPENSSH1 only wraps to reference key\n");
					CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
				}
				UnwrapKeyOpenSSH1(CCHandle,
						Context,
						WrappedKey,
						CredAndAclEntry,
						UnwrappedKey,
						DescriptiveData,
						Privilege,
						keyStorage);
				return;
			default:
				CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_WRAPPED_KEY_FORMAT);
		}
	}

	/* Get key blob, decoding and decrypting if necessary */
	CssmData decodedBlob;
	CssmData remData;
	try {
		if(isNullUnwrap) {
			/* simple copy of raw blob */
			copyData(WrappedKey.KeyData, 
				UnwrappedKey.KeyData, 
				normAllocator);
			unwrappedHdr.BlobType = CSSM_KEYBLOB_RAW;
			unwrappedHdr.Format   = wrapFormat; 
		}
		else {
			decodedBlob = CssmData::overlay(WrappedKey.KeyData);
			CSSM_SIZE bytesDecrypted;		
			CssmData *unwrapData = 
				CssmData::overlay(&UnwrappedKey.KeyData);
				
			DecryptData(CCHandle,
				Context,
				&decodedBlob,		// CipherBufs[],
				1,					// CipherBufCount,
				unwrapData,			// ClearBufs[]
				1,					// ClearBufCount
				bytesDecrypted,
				remData,
				Privilege);
	
			// I'm not 100% sure about this....
			assert(remData.Length == 0);
			UnwrappedKey.KeyData.Length = bytesDecrypted;
			unwrappedHdr.BlobType = CSSM_KEYBLOB_RAW;
			
			/* 
			 * Figure out various header fields from resulting blob
			 */
			switch(wrapFormat) {
				case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7:
					unwrappedHdr.Format = 
						CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
					if(unwrappedHdr.LogicalKeySizeInBits == 0) {
						unwrappedHdr.LogicalKeySizeInBits =
							(unsigned)(bytesDecrypted * 8);
					}
					/* app has to infer/know algorithm */
					break;
				case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8:
					pkcs8InferKeyHeader(UnwrappedKey);
					break;
				case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL:
					/* 
					 * App told us key algorithm (in WrappedKey).
					 * Infer format and key size. 
					 */
					opensslInferKeyHeader(UnwrappedKey);
					break;
			}
		}
	}
	catch (...) {
		errorLog0("UnwrapKey: DecryptData() threw exception\n");
		freeCssmData(remData, normAllocator);
		throw;
	}
	freeCssmData(remData, normAllocator);

	/* 
	 * One more thing: cook up a BinaryKey if caller wants a 
	 * reference key.
	 */
	if(keyStorage == CKS_Ref) {
		/*
		 * We have a key in raw format; convert to BinaryKey.
		 */
		BinaryKey *binKey = NULL;
		CSPKeyInfoProvider *provider = infoProvider(UnwrappedKey);
		/* optional parameter-bearing key */
		CssmKey *paramKey = Context.get<CssmKey>(CSSM_ATTRIBUTE_PARAM_KEY);
		provider->CssmKeyToBinary(paramKey, UnwrappedKey.KeyHeader.KeyAttr, &binKey);
		addRefKey(*binKey, UnwrappedKey);
		delete provider;
	}
}
Beispiel #10
0
void AppleCSPSession::DeriveKey_PBKDF2(
    const Context &context,
    const CssmData &Param,
    CSSM_DATA *keyData)
{
    /* validate algorithm-specific arguments */

    /* Param must point to a CSSM_PKCS5_PBKDF2_PARAMS */
    if(Param.Length != sizeof(CSSM_PKCS5_PBKDF2_PARAMS)) {
        errorLog0("DeriveKey_PBKDF2: Param wrong size\n");
        CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER);
    }
    const CSSM_PKCS5_PBKDF2_PARAMS *pbkdf2Params =
        reinterpret_cast<const CSSM_PKCS5_PBKDF2_PARAMS *>(Param.Data);
    if(pbkdf2Params == NULL) {
        errorLog0("DeriveKey_PBKDF2: null Param.Data\n");
        CssmError::throwMe(CSSMERR_CSP_INVALID_DATA);
    }

    /* Get passphrase from either baseKey or from CSSM_PKCS5_PBKDF2_PARAMS */
    CssmKey *passKey = context.get<CssmKey>(CSSM_ATTRIBUTE_KEY);
    CSSM_SIZE	passphraseLen = 0;
    uint8 	*passphrase = NULL;
    if(passKey != NULL) {
        AppleCSPContext::symmetricKeyBits(context, *this,
                                          CSSM_ALGID_SECURE_PASSPHRASE, CSSM_KEYUSE_DERIVE,
                                          passphrase, passphraseLen);
    }
    else {
        passphraseLen = pbkdf2Params->Passphrase.Length;
        passphrase = pbkdf2Params->Passphrase.Data;
    }

#if 	!ALLOW_ZERO_PASSWORD
    /* passphrase required */
    if(passphrase == NULL) {
        errorLog0("DeriveKey_PBKDF2: null Passphrase\n");
        CssmError::throwMe(CSSMERR_CSP_INVALID_DATA);
    }
    if(passphraseLen == 0) {
        /* FIXME - enforce minimum length? */
        errorLog0("DeriveKey_PBKDF2: zero length passphrase\n");
        CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER);
    }
#endif	/* ALLOW_ZERO_PASSWORD */

    if(pbkdf2Params->PseudoRandomFunction !=
            CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1) {
        errorLog0("DeriveKey_PBKDF2: invalid PRF\n");
        CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
    }

    /* salt, from context, required */
    CssmData salt = context.get<CssmData>(CSSM_ATTRIBUTE_SALT,
                                          CSSMERR_CSP_MISSING_ATTR_SALT);
    if((salt.Data == NULL) || (salt.Length < PBKDF2_MIN_SALT)) {
        CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SALT);
    }

    /* iteration count, from context, required */
    uint32 iterCount = context.getInt(CSSM_ATTRIBUTE_ITERATION_COUNT,
                                      CSSMERR_CSP_MISSING_ATTR_ITERATION_COUNT);
    if(iterCount < PBKDF2_MIN_ITER_CNT) {
        CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ITERATION_COUNT);
    }

    /*
     * allocate a temp buffer, length
     *    = MAX (hLen, saltLen + 4) + 2 * hLen
     *    = MAX (kSHA1DigestSize, saltLen + 4) + 2 * kSHA1DigestSize
     */
    size_t tempLen = salt.Length + 4;
    if(tempLen < kSHA1DigestSize) {
        tempLen = kSHA1DigestSize;
    }
    tempLen += (2 * kSHA1DigestSize);
    CSSM_DATA tempData = {0, NULL};
    setUpData(tempData, tempLen, privAllocator);

    /* go */
    pbkdf2 (hmacsha1,
            kSHA1DigestSize,
            passphrase, (uint32)passphraseLen,
            salt.Data, (uint32)salt.Length,
            iterCount,
            keyData->Data, (uint32)keyData->Length,
            tempData.Data);
    freeData(&tempData, privAllocator, false);
}
Beispiel #11
0
/*
 * PKCS5 v1.5 key derivation. Also used for traditional openssl key
 * derivation, which is mighty similar to PKCS5 v1.5, with the addition
 * of the ability to generate more than (keysize + ivsize) bytes.
 */
void AppleCSPSession::DeriveKey_PKCS5_V1_5(
    const Context &context,
    CSSM_ALGORITHMS algId,
    const CssmData &Param,			// IV optional, mallocd by app to indicate
    //   size
    CSSM_DATA *keyData)				// mallocd by caller to indicate size
{
    CSSM_DATA pwd = {0, NULL};

    /* password from either Seed.Param or from base key */
    CssmCryptoData *cryptData =
        context.get<CssmCryptoData>(CSSM_ATTRIBUTE_SEED);
    if((cryptData != NULL) && (cryptData->Param.Length != 0)) {
        pwd = cryptData->Param;
    }
    else {
        /* Get secure passphrase from base key */
        CssmKey *passKey = context.get<CssmKey>(CSSM_ATTRIBUTE_KEY);
        if (passKey != NULL) {
            AppleCSPContext::symmetricKeyBits(context, *this,
                                              CSSM_ALGID_SECURE_PASSPHRASE, CSSM_KEYUSE_DERIVE,
                                              pwd.Data, pwd.Length);
        }
    }

    if(pwd.Data == NULL) {
        errorLog0("DeriveKey_PKCS5_V1_5: null Passphrase\n");
        CssmError::throwMe(CSSMERR_CSP_INVALID_DATA);
    }
    if(pwd.Length == 0) {
        errorLog0("DeriveKey_PKCS5_V1_5: zero length passphrase\n");
        CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER);
    }

    CSSM_ALGORITHMS hashAlg;
    unsigned digestLen;
    bool opensslAlg = false;
    switch(algId) {
    case CSSM_ALGID_PKCS5_PBKDF1_MD5:
        hashAlg = CSSM_ALGID_MD5;
        digestLen = kMD5DigestSize;
        break;
    case CSSM_ALGID_PKCS5_PBKDF1_MD2:
        hashAlg = CSSM_ALGID_MD2;
        digestLen = kMD2DigestSize;
        break;
    case CSSM_ALGID_PKCS5_PBKDF1_SHA1:
        hashAlg = CSSM_ALGID_SHA1;
        digestLen = kSHA1DigestSize;
        break;
    case CSSM_ALGID_PBE_OPENSSL_MD5:
        hashAlg = CSSM_ALGID_MD5;
        digestLen = kMD5DigestSize;
        opensslAlg = true;
        break;
    default:
        /* should not have been called */
        assert(0);
        CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
    }

    /* IV optional */
    CSSM_DATA iv = Param;

    /* total requested length can't exceed digest size for struct PKCS5 v1.5*/
    if(!opensslAlg && ((keyData->Length + iv.Length) > digestLen)) {
        errorLog0("DeriveKey_PKCS5_V1_5: requested length larger than digest\n");
        CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH);
    }

    /* salt, from context, required */
    CssmData salt = context.get<CssmData>(CSSM_ATTRIBUTE_SALT,
                                          CSSMERR_CSP_MISSING_ATTR_SALT);
    if(salt.Data == NULL) {
        CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SALT);
    }

    /* iteration count, from context, required */
    uint32 iterCount = context.getInt(CSSM_ATTRIBUTE_ITERATION_COUNT,
                                      CSSMERR_CSP_MISSING_ATTR_ITERATION_COUNT);

    /*
     * Apply the underlying hash function Hash for c iterations to
     *  the concatenation of the password P and the salt S, then
     * extract the first dkLen octets to produce a derived key DK:
     *
     *		T1 = Hash (P || S) ,
     *		T2 = Hash (T1) ,
     *		...
     *		Tc = Hash (Tc-1) ,
     *		DK = Tc<0..dkLen-1> .
     */
    DigestCtx ctx;
    uint8 *keyDataP		= keyData->Data;
    size_t keyBytesToGo = keyData->Length;
    uint8 *ivDataP		= iv.Data;
    size_t ivBytesToGo  = iv.Length;
    bool looping		= false;		// true for additional bytes for openssl
    unsigned char digestOut[kMaxDigestSize];

    for(;;) {
        /* this loop guaranteed to only run once if !opensslAlg */
        DigestCtxInit(&ctx, hashAlg);

        if(looping) {
            /* openssl addition: re-digest the digest here */
            DigestCtxUpdate(&ctx, digestOut, digestLen);
        }

        /* digest password then salt */
        DigestCtxUpdate(&ctx, pwd.Data, (uint32)pwd.Length);
        DigestCtxUpdate(&ctx, salt.Data, (uint32)salt.Length);

        DigestCtxFinal(&ctx, digestOut);

        /* now iterCount-1 more iterations */
        for(unsigned dex=1; dex<iterCount; dex++) {
            DigestCtxInit(&ctx, hashAlg);
            DigestCtxUpdate(&ctx, digestOut, digestLen);
            DigestCtxFinal(&ctx, digestOut);
        }

        /* first n bytes to the key */
        uint32 bytesAvail = digestLen;
        size_t toMove = (keyBytesToGo > bytesAvail) ? bytesAvail : keyBytesToGo;
        memmove(keyDataP, digestOut, toMove);
        uint8 *remainder = digestOut + toMove;
        bytesAvail   -= toMove;
        keyDataP     += toMove;
        keyBytesToGo -= toMove;

        /* then optionally some to IV */
        if(ivBytesToGo && bytesAvail) {
            toMove = (ivBytesToGo > bytesAvail) ? bytesAvail : ivBytesToGo;
            memmove(ivDataP, remainder, toMove);
            ivDataP     += toMove;
            ivBytesToGo -= toMove;
        }
        if((keyBytesToGo == 0) && (ivBytesToGo == 0)) {
            /* guaranteed true for PKCS5 v1.5 */
            break;
        }

        assert(opensslAlg == true);
        looping = true;
    }
    DigestCtxFree(&ctx);
}