/* * 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 ¶mKey, 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; } }
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); }
/* * 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); }