// // 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); } }
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; } }
// // Decode a key blob // void DatabaseCryptoCore::decodeKeyCore(KeyBlob *blob, CssmKey &key, void * &pubAcl, void * &privAcl) const { // Assemble the encrypted blob as a CSSM "wrapped key" CssmKey wrappedKey; wrappedKey.KeyHeader = blob->header; h2ni(wrappedKey.KeyHeader); wrappedKey.blobType(blob->wrappedHeader.blobType); wrappedKey.blobFormat(blob->wrappedHeader.blobFormat); wrappedKey.wrapAlgorithm(blob->wrappedHeader.wrapAlgorithm); wrappedKey.wrapMode(blob->wrappedHeader.wrapMode); wrappedKey.KeyData = CssmData(blob->cryptoBlob(), blob->cryptoBlobLength()); bool inTheClear = blob->isClearText(); if(!inTheClear) { // verify signature (check against corruption) assert(isValid()); // need our database secrets CssmData signChunk[] = { CssmData::wrap(blob, fieldOffsetOf(&KeyBlob::blobSignature)), CssmData(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength()) }; CSSM_ALGORITHMS verifyAlgorithm = CSSM_ALGID_SHA1HMAC; #if defined(COMPAT_OSX_10_0) if (blob->version() == blob->version_MacOS_10_0) verifyAlgorithm = CSSM_ALGID_SHA1HMAC_LEGACY; // BSafe bug compatibility #endif VerifyMac verifier(Server::csp(), verifyAlgorithm); verifier.key(mSigningKey); CssmData signature(blob->blobSignature, sizeof(blob->blobSignature)); verifier.verify(signChunk, 2, signature); } /* else signature indicates cleartext */ // extract and hold some header bits the CSP does not want to see uint32 heldAttributes = n2h(blob->header.attributes()) & managedAttributes; CssmData privAclData; if(inTheClear) { /* NULL unwrap */ UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE); wrappedKey.clearAttribute(managedAttributes); //@@@ shouldn't be needed(?) unwrap(wrappedKey, KeySpec(n2h(blob->header.usage()), (n2h(blob->header.attributes()) & ~managedAttributes) | forcedAttributes), key, &privAclData); } else { // decrypt the key using an unwrapping operation UnwrapKey unwrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE); unwrap.key(mEncryptionKey); unwrap.mode(CSSM_ALGMODE_CBCPadIV8); unwrap.padding(CSSM_PADDING_PKCS1); CssmData ivd(blob->iv, sizeof(blob->iv)); unwrap.initVector(ivd); unwrap.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT, uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM)); wrappedKey.clearAttribute(managedAttributes); //@@@ shouldn't be needed(?) unwrap(wrappedKey, KeySpec(n2h(blob->header.usage()), (n2h(blob->header.attributes()) & ~managedAttributes) | forcedAttributes), key, &privAclData); } // compare retrieved key headers with blob headers (sanity check) // @@@ this should probably be checked over carefully CssmKey::Header &real = key.header(); CssmKey::Header &incoming = blob->header; n2hi(incoming); if (real.HeaderVersion != incoming.HeaderVersion || real.cspGuid() != incoming.cspGuid()) CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); if (real.algorithm() != incoming.algorithm()) CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); // re-insert held bits key.header().KeyAttr |= heldAttributes; if(inTheClear && (real.keyClass() != CSSM_KEYCLASS_PUBLIC_KEY)) { /* Spoof - cleartext KeyBlob passed off as private key */ CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); } // got a valid key: return the pieces pubAcl = blob->publicAclBlob(); // points into blob (shared) privAcl = privAclData; // was allocated by CSP decrypt, else NULL for // cleatext keys // key was set by unwrap operation }
// // Encode a key blob // KeyBlob *DatabaseCryptoCore::encodeKeyCore(const CssmKey &inKey, const CssmData &publicAcl, const CssmData &privateAcl, bool inTheClear) const { CssmKey key = inKey; uint8 iv[8]; CssmKey wrappedKey; if(inTheClear && (privateAcl.Length != 0)) { /* can't store private ACL component in the clear */ CssmError::throwMe(CSSMERR_DL_INVALID_ACCESS_CREDENTIALS); } // extract and hold some header bits the CSP does not want to see uint32 heldAttributes = key.attributes() & managedAttributes; key.clearAttribute(managedAttributes); key.setAttribute(forcedAttributes); if(inTheClear) { /* NULL wrap of public key */ WrapKey wrap(Server::csp(), CSSM_ALGID_NONE); wrap(key, wrappedKey, NULL); } else { assert(isValid()); // need our database secrets // create new IV Server::active().random(iv); // use a CMS wrap to encrypt the key WrapKey wrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE); wrap.key(mEncryptionKey); wrap.mode(CSSM_ALGMODE_CBCPadIV8); wrap.padding(CSSM_PADDING_PKCS1); CssmData ivd(iv, sizeof(iv)); wrap.initVector(ivd); wrap.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT, uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM)); wrap(key, wrappedKey, &privateAcl); } // stick the held attribute bits back in key.clearAttribute(forcedAttributes); key.setAttribute(heldAttributes); // allocate the final KeyBlob, uh, blob size_t length = sizeof(KeyBlob) + publicAcl.length() + wrappedKey.length(); KeyBlob *blob = Allocator::standard().malloc<KeyBlob>(length); // assemble the KeyBlob memset(blob, 0, sizeof(KeyBlob)); // fill alignment gaps blob->initialize(); if(!inTheClear) { memcpy(blob->iv, iv, sizeof(iv)); } blob->header = key.header(); h2ni(blob->header); // endian-correct the header blob->wrappedHeader.blobType = wrappedKey.blobType(); blob->wrappedHeader.blobFormat = wrappedKey.blobFormat(); blob->wrappedHeader.wrapAlgorithm = wrappedKey.wrapAlgorithm(); blob->wrappedHeader.wrapMode = wrappedKey.wrapMode(); memcpy(blob->publicAclBlob(), publicAcl, publicAcl.length()); blob->startCryptoBlob = sizeof(KeyBlob) + publicAcl.length(); memcpy(blob->cryptoBlob(), wrappedKey.data(), wrappedKey.length()); blob->totalLength = blob->startCryptoBlob + wrappedKey.length(); if(inTheClear) { /* indicate that this is cleartext for decoding */ blob->setClearTextSignature(); } else { // sign the blob CssmData signChunk[] = { CssmData(blob->data(), fieldOffsetOf(&KeyBlob::blobSignature)), CssmData(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength()) }; CssmData signature(blob->blobSignature, sizeof(blob->blobSignature)); GenerateMac signer(Server::csp(), CSSM_ALGID_SHA1HMAC_LEGACY); //@@@!!! CRUD signer.key(mSigningKey); signer.sign(signChunk, 2, signature); assert(signature.length() == sizeof(blob->blobSignature)); } // all done. Clean up Server::csp()->allocator().free(wrappedKey); return blob; }
void KeyDataAttributeCoder::decode(TokenContext *tokenContext, const MetaAttribute &metaAttribute, Record &record) { const MetaRecord &mr = metaAttribute.metaRecord(); CssmKey key; key.header().cspGuid(Guid::overlay(gGuidAppleSdCSPDL)); key.blobType(CSSM_KEYBLOB_REFERENCE); key.blobFormat(CSSM_KEYBLOB_REF_FORMAT_INTEGER); key.algorithm(mr.metaAttribute(kSecKeyKeyType) .attribute(tokenContext, record).uint32Value()); key.keyClass(mr.metaAttribute(kSecKeyKeyClass) .attribute(tokenContext, record).uint32Value()); key.header().LogicalKeySizeInBits = mr.metaAttribute(kSecKeyKeySizeInBits).attribute(tokenContext, record) .uint32Value(); key.header().KeyAttr = (mr.metaAttribute(kSecKeyPermanent).attribute(tokenContext, record) .boolValue() ? CSSM_KEYATTR_PERMANENT : 0) | (mr.metaAttribute(kSecKeyPrivate).attribute(tokenContext, record) .boolValue() ? CSSM_KEYATTR_PRIVATE : 0) | (mr.metaAttribute(kSecKeyModifiable).attribute(tokenContext, record) .boolValue() ? CSSM_KEYATTR_MODIFIABLE : 0) | (mr.metaAttribute(kSecKeySensitive).attribute(tokenContext, record) .boolValue() ? CSSM_KEYATTR_SENSITIVE : 0) | (mr.metaAttribute(kSecKeyAlwaysSensitive) .attribute(tokenContext, record) .boolValue() ? CSSM_KEYATTR_ALWAYS_SENSITIVE : 0) | (mr.metaAttribute(kSecKeyExtractable).attribute(tokenContext, record) .boolValue() ? CSSM_KEYATTR_EXTRACTABLE : 0) | (mr.metaAttribute(kSecKeyNeverExtractable) .attribute(tokenContext, record) .boolValue() ? CSSM_KEYATTR_NEVER_EXTRACTABLE : 0); CSSM_KEYUSE usage = (mr.metaAttribute(kSecKeyEncrypt).attribute(tokenContext, record) .boolValue() ? CSSM_KEYUSE_ENCRYPT : 0) | (mr.metaAttribute(kSecKeyDecrypt).attribute(tokenContext, record) .boolValue() ? CSSM_KEYUSE_DECRYPT : 0) | (mr.metaAttribute(kSecKeySign).attribute(tokenContext, record) .boolValue() ? CSSM_KEYUSE_SIGN : 0) | (mr.metaAttribute(kSecKeyVerify).attribute(tokenContext, record) .boolValue() ? CSSM_KEYUSE_VERIFY : 0) | (mr.metaAttribute(kSecKeySignRecover).attribute(tokenContext, record) .boolValue() ? CSSM_KEYUSE_SIGN_RECOVER : 0) | (mr.metaAttribute(kSecKeyVerifyRecover) .attribute(tokenContext, record) .boolValue() ? CSSM_KEYUSE_VERIFY_RECOVER : 0) | (mr.metaAttribute(kSecKeyWrap).attribute(tokenContext, record) .boolValue() ? CSSM_KEYUSE_WRAP : 0) | (mr.metaAttribute(kSecKeyUnwrap).attribute(tokenContext, record) .boolValue() ? CSSM_KEYUSE_UNWRAP : 0) | (mr.metaAttribute(kSecKeyDerive).attribute(tokenContext, record) .boolValue() ? CSSM_KEYUSE_DERIVE : 0); if (usage == (CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_SIGN_RECOVER | CSSM_KEYUSE_VERIFY_RECOVER | CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP | CSSM_KEYUSE_DERIVE)) usage = CSSM_KEYUSE_ANY; key.header().KeyUsage = usage; // Dates mr.metaAttribute(kSecKeyStartDate).attribute(tokenContext, record) .getDateValue(key.header().StartDate); mr.metaAttribute(kSecKeyEndDate).attribute(tokenContext, record) .getDateValue(key.header().EndDate); record.attributeAtIndex(metaAttribute.attributeIndex(), new Attribute(&key, sizeof(key))); }