void cspValidateIntendedKeyUsage(
	const CSSM_KEYHEADER	*hdr,
	CSSM_KEYUSE				intendedUsage)
{
	uint32 		keyUsage = hdr->KeyUsage;
	cspKeyType	keyType;

	/* first, the obvious */
	if(keyUsage & CSSM_KEYUSE_ANY) {
		/* OK for now */
		return;
	}
	if(!(keyUsage & intendedUsage)) {
		#if		RELAXED_WRAP_USAGE
		if(! ( ( (keyUsage & CSSM_KEYUSE_WRAP) && 
		         (intendedUsage == CSSM_KEYUSE_ENCRYPT)
			   ) ||
			   ( (keyUsage & CSSM_KEYUSE_UNWRAP) && 
		         (intendedUsage == CSSM_KEYUSE_DECRYPT)
			   )
			 ) )
		#endif
		CssmError::throwMe(CSSMERR_CSP_KEY_USAGE_INCORRECT);
	}

	/* now validate all of the key's usage bits - this is mainly to
	 * prevent and detect tampering */
	switch(hdr->KeyClass) {
		case CSSM_KEYCLASS_SESSION_KEY:
			keyType = CKT_Session;
			break;
		case CSSM_KEYCLASS_PUBLIC_KEY:
			keyType = CKT_Public;
			break;
		case CSSM_KEYCLASS_PRIVATE_KEY:
			keyType = CKT_Private;
			break;
		default:
			CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
	}
	try {
		cspValidateKeyUsageBits(keyType, keyUsage);
	}
	catch (...) {
		/* override error.... */
		CssmError::throwMe(CSSMERR_CSP_KEY_USAGE_INCORRECT);
	}
}
/*
 * 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;
	}
}
/*
 * Called from subclass's generate method. Subclass is also a 
 * AppleCSPContext.
 */
void AppleSymmKeyGenContext::generateSymKey(
	const Context 	&context, 
	AppleCSPSession	&session,		// for ref keys
	CssmKey 		&cssmKey)		// RETURNED 
{
	/* there really is no legal way this should throw... */
	uint32 reqKeySize = context.getInt(
		CSSM_ATTRIBUTE_KEY_LENGTH, 
		CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH);
	if((reqKeySize < minSizeInBits) ||
	   (reqKeySize > maxSizeInBits)) {
		CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE);
	}
	if(mustBeByteSized) {
		if((reqKeySize & 0x7) != 0) {
			CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE);
		}
	}
	
	// validate KeyAtrr and KeyUsage already present in header
	cspKeyStorage 	keyStorage;
	CssmKey::Header	&hdr  = cssmKey.header(); 
	
	keyStorage = cspParseKeyAttr(CKT_Session,  hdr.KeyAttr);
	cspValidateKeyUsageBits(CKT_Session, hdr.KeyUsage);
	hdr.KeyAttr  &= ~KEY_ATTR_RETURN_MASK;
	
	hdr.LogicalKeySizeInBits = reqKeySize;
	uint32 keySizeInBytes = (reqKeySize + 7) / 8;
	SymmetricBinaryKey *binKey = NULL;
	CssmData *keyData = NULL;
	
	switch(keyStorage) {
		case CKS_None:
			/* no way */
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
		case CKS_Ref:
			/* cook up a symmetric binary key */
			binKey = new SymmetricBinaryKey(reqKeySize);
			keyData = &binKey->mKeyData;
			break;
		case CKS_Data:
			/* key bytes --> caller's cssmKey */
			keyData = &(CssmData::overlay(cssmKey.KeyData));
			setUpCssmData(*keyData, keySizeInBytes, 
				session.normAlloc());
			break;
	}
	
	// in any case, fill key bytes with random data
	session.getRandomBytes(keySizeInBytes, keyData->Data);

	if(keyStorage == CKS_Ref) {
		session.addRefKey(*binKey, cssmKey);
	}
	else {
		/* Raw data */
		hdr.BlobType = CSSM_KEYBLOB_RAW;
		hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING; 
	}

	// FIXME - any other header fields?
}
// Called from subclass after it allocates its BinaryKeys.
// Caller frees BinaryKeys if we throw any exception. 
void AppleKeyPairGenContext::generate(
	const Context 	&context, 
	AppleCSPSession	&session,
	CssmKey 		&pubKey, 
	BinaryKey 		*pubBinKey,
	CssmKey 		&privKey,
	BinaryKey		*privBinKey)
{
	uint32			keySize;
	cspKeyStorage 	privStorage;
	cspKeyStorage 	pubStorage;
	CssmKey::Header	&pubHdr  = pubKey.header(); 
	CssmKey::Header	&privHdr = privKey.header(); 
	
	// validate context and key header args
	pubStorage  = cspParseKeyAttr(CKT_Public,  pubHdr.KeyAttr);
	privStorage = cspParseKeyAttr(CKT_Private, privHdr.KeyAttr);
	cspValidateKeyUsageBits(CKT_Public,  pubHdr.KeyUsage);
	cspValidateKeyUsageBits(CKT_Private, privHdr.KeyUsage);
	
	// have subclass generate the key pairs in the form of 
	// its native BinaryKeys
	generate(context, *pubBinKey, *privBinKey, keySize);

	// FIXME - Any other header setup?
	pubHdr.LogicalKeySizeInBits = 
		privHdr.LogicalKeySizeInBits = keySize;
	pubHdr.KeyAttr  &= ~KEY_ATTR_RETURN_MASK;
	privHdr.KeyAttr &= ~KEY_ATTR_RETURN_MASK;
		 
	// Handle key formatting. Delete the BinaryKeys if
	// we're not creating ref keys, after safe completion of 
	// generateKeyBlob (which may throw, in which case the binary keys
	// get deleted by our caller). 
	CSSM_KEYATTR_FLAGS attrFlags = 0;
	switch(pubStorage) {
		case CKS_Ref:
			session.addRefKey(*pubBinKey, pubKey);
			break;
		case CKS_Data:
			pubHdr.Format = requestedKeyFormat(context, pubKey);
			pubBinKey->mKeyHeader  = pubHdr;
			pubBinKey->generateKeyBlob(
				session.normAlloc(),		// alloc in user space
				CssmData::overlay(pubKey.KeyData),
				pubHdr.Format,
				session,
				NULL,						// no paramKey here!
				attrFlags);
			break;
		case CKS_None:
			break;
	}
	switch(privStorage) {
		case CKS_Ref:
			session.addRefKey(*privBinKey, privKey);
			break;
		case CKS_Data:
			privHdr.Format = requestedKeyFormat(context, privKey);
			privBinKey->mKeyHeader = privHdr;
			privBinKey->generateKeyBlob(
				session.normAlloc(),		// alloc in user space
				CssmData::overlay(privKey.KeyData),
				privHdr.Format,
				session,
				NULL,
				attrFlags);
			break;
		case CKS_None:
			break;
	}
	if(pubStorage != CKS_Ref) {
		delete pubBinKey;
	}
	if(privStorage != CKS_Ref) {
		delete privBinKey;
	}
}
Exemple #5
0
/*
 * Member function initially declared for CSPAbstractPluginSession;
 * we're overriding the null version in CSPFullPluginSession.
 *
 * We'll generate any type of key (for now).
 */
void AppleCSPSession::DeriveKey(
    CSSM_CC_HANDLE CCHandle,
    const Context &context,
    CssmData &Param,
    uint32 KeyUsage,
    uint32 KeyAttr,
    const CssmData *KeyLabel,
    const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry,
    CssmKey &DerivedKey)
{
    /* validate input args, common to all algorithms */
    switch(context.algorithm()) {
    case CSSM_ALGID_PKCS5_PBKDF2:
    case CSSM_ALGID_DH:
    case CSSM_ALGID_PKCS12_PBE_ENCR:
    case CSSM_ALGID_PKCS12_PBE_MAC:
    case CSSM_ALGID_PKCS5_PBKDF1_MD5:
    case CSSM_ALGID_PKCS5_PBKDF1_MD2:
    case CSSM_ALGID_PKCS5_PBKDF1_SHA1:
    case CSSM_ALGID_PBE_OPENSSL_MD5:
    case CSSM_ALGID_OPENSSH1:
#if CRYPTKIT_CSP_ENABLE
    case CSSM_ALGID_ECDH:
    case CSSM_ALGID_ECDH_X963_KDF:
#endif
        break;
    /* maybe more here, later */
    default:
        CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
    }
    DerivedKey.KeyData.Data = NULL;
    DerivedKey.KeyData.Length = 0;
    cspKeyStorage keyStorage = cspParseKeyAttr(CKT_Session, KeyAttr);
    cspValidateKeyUsageBits(CKT_Session, KeyUsage);

    /* outgoing key type, required (though any algorithm is OK) */
    uint32 keyType = context.getInt(CSSM_ATTRIBUTE_KEY_TYPE,
                                    CSSMERR_CSP_MISSING_ATTR_KEY_TYPE);

    /* outgoing key size, required - any nonzero value is OK */
    uint32 reqKeySize = context.getInt(
                            CSSM_ATTRIBUTE_KEY_LENGTH,
                            CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH);

    /* cook up a place to put the key data */
    uint32 keySizeInBytes = (reqKeySize + 7) / 8;
    SymmetricBinaryKey *binKey = NULL;
    CSSM_DATA_PTR keyData = NULL;

    switch(keyStorage) {
    case CKS_None:
        /* no way */
        CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
    case CKS_Ref:
        /* cook up a symmetric binary key */
        binKey = new SymmetricBinaryKey(reqKeySize);
        keyData = &binKey->mKeyData;
        break;
    case CKS_Data:
        /* key bytes --> caller's cssmKey */
        keyData = &DerivedKey.KeyData;
        setUpData(*keyData, keySizeInBytes,
                  normAllocator);
        break;
    }

    /* break off to algorithm-specific code, whose job it is
     * to fill in keyData->Data with keyData->Length bytes */
    switch(context.algorithm()) {
    case CSSM_ALGID_PKCS5_PBKDF2:
        DeriveKey_PBKDF2(context,
                         Param,
                         keyData);
        break;
    case CSSM_ALGID_DH:
        DeriveKey_DH(context,
                     Param,
                     keyData,
                     *this);
        break;
    case CSSM_ALGID_PKCS12_PBE_ENCR:
    case CSSM_ALGID_PKCS12_PBE_MAC:
        DeriveKey_PKCS12(context,
                         *this,
                         Param,
                         keyData);
        break;
    case CSSM_ALGID_PKCS5_PBKDF1_MD5:
    case CSSM_ALGID_PKCS5_PBKDF1_MD2:
    case CSSM_ALGID_PKCS5_PBKDF1_SHA1:
    case CSSM_ALGID_PBE_OPENSSL_MD5:
        DeriveKey_PKCS5_V1_5(context,
                             context.algorithm(),
                             Param,
                             keyData);
        break;
    case CSSM_ALGID_OPENSSH1:
        DeriveKey_OpenSSH1(context,
                           context.algorithm(),
                           Param,
                           keyData);
        break;
#if CRYPTKIT_CSP_ENABLE
    case CSSM_ALGID_ECDH:
    case CSSM_ALGID_ECDH_X963_KDF:
        CryptKit::DeriveKey_ECDH(context,
                                 context.algorithm(),
                                 Param,
                                 keyData,
                                 *this);
        break;
#endif
    /* maybe more here, later */
    default:
        assert(0);
    }

    /* set up outgoing header */
    KeyAttr &= ~KEY_ATTR_RETURN_MASK;
    CSSM_KEYHEADER &hdr = DerivedKey.KeyHeader;
    setKeyHeader(hdr,
                 plugin.myGuid(),
                 keyType,
                 CSSM_KEYCLASS_SESSION_KEY,
                 KeyAttr,
                 KeyUsage);
    /* handle derived size < requested size, legal for Diffie-Hellman */
    hdr.LogicalKeySizeInBits = (uint32)(keyData->Length * 8);

    if(keyStorage == CKS_Ref) {
        /* store and convert to ref key */
        addRefKey(*binKey, DerivedKey);
    }
    else {
        /* Raw data */
        hdr.BlobType = CSSM_KEYBLOB_RAW;
        hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
    }
}