/* * 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; } }
/* * 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; } }