]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_apple_csp/lib/deriveKey.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_apple_csp / lib / deriveKey.cpp
diff --git a/libsecurity_apple_csp/lib/deriveKey.cpp b/libsecurity_apple_csp/lib/deriveKey.cpp
new file mode 100644 (file)
index 0000000..6229c27
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * The contents of this file constitute Original Code as defined in and are
+ * subject to the Apple Public Source License Version 1.2 (the 'License').
+ * You may not use this file except in compliance with the License. Please obtain
+ * a copy of the License at http://www.apple.com/publicsource and read it before
+ * using this file.
+ * 
+ * This Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
+ * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
+ * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
+ * specific language governing rights and limitations under the License.
+ */
+
+
+/*
+       File:           deriveKey.cpp
+       
+       Contains:       CSSM_DeriveKey functions
+
+       Copyright:      (C) 2000 by Apple Computer, Inc., all rights reserved
+
+       Written by:     Doug Mitchell <dmitch@apple.com>        
+*/
+
+#include <HMACSHA1.h>
+#include <pbkdf2.h>
+#include <pbkdDigest.h>
+#include <pkcs12Derive.h>
+#include "AppleCSPSession.h"
+#include "AppleCSPUtils.h"
+#include "AppleCSPContext.h"
+#include "cspdebugging.h"
+#include <security_cdsa_utilities/context.h>
+#include <DH_exchange.h>
+#include "FEEAsymmetricContext.h"
+
+/* minimum legal values */
+#define PBKDF2_MIN_SALT                        8               /* bytes */
+#define PBKDF2_MIN_ITER_CNT            1000    /* iteration count */
+
+#define ALLOW_ZERO_PASSWORD            1 
+
+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
+        */
+       uint32 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, passphraseLen,
+               salt.Data, salt.Length,
+               iterCount,
+               keyData->Data, 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;
+       uint32 keyBytesToGo = keyData->Length;
+       uint8 *ivDataP          = iv.Data;
+       uint32 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, pwd.Length);
+               DigestCtxUpdate(&ctx, salt.Data, 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;
+               uint32 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);
+}
+
+/*
+ * 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 = 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; 
+       }
+}
+