1/* 2 * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved. 3 * 4 * The contents of this file constitute Original Code as defined in and are 5 * subject to the Apple Public Source License Version 1.2 (the 'License'). 6 * You may not use this file except in compliance with the License. Please obtain 7 * a copy of the License at http://www.apple.com/publicsource and read it before 8 * using this file. 9 * 10 * This Original Code and all software distributed under the License are 11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS 12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT 13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the 15 * specific language governing rights and limitations under the License. 16 */ 17 18 19/* 20 File: deriveKey.cpp 21 22 Contains: CSSM_DeriveKey functions 23 24 Copyright (c) 2000,2011-2012,2014 Apple Inc. All Rights Reserved. 25 26*/ 27 28#include <HMACSHA1.h> 29#include <pbkdf2.h> 30#include <pbkdDigest.h> 31#include <pkcs12Derive.h> 32#include "AppleCSPSession.h" 33#include "AppleCSPUtils.h" 34#include "AppleCSPContext.h" 35#include "cspdebugging.h" 36#include <security_cdsa_utilities/context.h> 37#include <DH_exchange.h> 38#include "FEEAsymmetricContext.h" 39 40/* minimum legal values */ 41#define PBKDF2_MIN_SALT 8 /* bytes */ 42#define PBKDF2_MIN_ITER_CNT 1000 /* iteration count */ 43 44#define ALLOW_ZERO_PASSWORD 1 45 46void AppleCSPSession::DeriveKey_PBKDF2( 47 const Context &context, 48 const CssmData &Param, 49 CSSM_DATA *keyData) 50{ 51 /* validate algorithm-specific arguments */ 52 53 /* Param must point to a CSSM_PKCS5_PBKDF2_PARAMS */ 54 if(Param.Length != sizeof(CSSM_PKCS5_PBKDF2_PARAMS)) { 55 errorLog0("DeriveKey_PBKDF2: Param wrong size\n"); 56 CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER); 57 } 58 const CSSM_PKCS5_PBKDF2_PARAMS *pbkdf2Params = 59 reinterpret_cast<const CSSM_PKCS5_PBKDF2_PARAMS *>(Param.Data); 60 if(pbkdf2Params == NULL) { 61 errorLog0("DeriveKey_PBKDF2: null Param.Data\n"); 62 CssmError::throwMe(CSSMERR_CSP_INVALID_DATA); 63 } 64 65 /* Get passphrase from either baseKey or from CSSM_PKCS5_PBKDF2_PARAMS */ 66 CssmKey *passKey = context.get<CssmKey>(CSSM_ATTRIBUTE_KEY); 67 CSSM_SIZE passphraseLen = 0; 68 uint8 *passphrase = NULL; 69 if(passKey != NULL) { 70 AppleCSPContext::symmetricKeyBits(context, *this, 71 CSSM_ALGID_SECURE_PASSPHRASE, CSSM_KEYUSE_DERIVE, 72 passphrase, passphraseLen); 73 } 74 else { 75 passphraseLen = pbkdf2Params->Passphrase.Length; 76 passphrase = pbkdf2Params->Passphrase.Data; 77 } 78 79 #if !ALLOW_ZERO_PASSWORD 80 /* passphrase required */ 81 if(passphrase == NULL) { 82 errorLog0("DeriveKey_PBKDF2: null Passphrase\n"); 83 CssmError::throwMe(CSSMERR_CSP_INVALID_DATA); 84 } 85 if(passphraseLen == 0) { 86 /* FIXME - enforce minimum length? */ 87 errorLog0("DeriveKey_PBKDF2: zero length passphrase\n"); 88 CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER); 89 } 90 #endif /* ALLOW_ZERO_PASSWORD */ 91 92 if(pbkdf2Params->PseudoRandomFunction != 93 CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1) { 94 errorLog0("DeriveKey_PBKDF2: invalid PRF\n"); 95 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); 96 } 97 98 /* salt, from context, required */ 99 CssmData salt = context.get<CssmData>(CSSM_ATTRIBUTE_SALT, 100 CSSMERR_CSP_MISSING_ATTR_SALT); 101 if((salt.Data == NULL) || (salt.Length < PBKDF2_MIN_SALT)){ 102 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SALT); 103 } 104 105 /* iteration count, from context, required */ 106 uint32 iterCount = context.getInt(CSSM_ATTRIBUTE_ITERATION_COUNT, 107 CSSMERR_CSP_MISSING_ATTR_ITERATION_COUNT); 108 if(iterCount < PBKDF2_MIN_ITER_CNT) { 109 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ITERATION_COUNT); 110 } 111 112 /* 113 * allocate a temp buffer, length 114 * = MAX (hLen, saltLen + 4) + 2 * hLen 115 * = MAX (kSHA1DigestSize, saltLen + 4) + 2 * kSHA1DigestSize 116 */ 117 size_t tempLen = salt.Length + 4; 118 if(tempLen < kSHA1DigestSize) { 119 tempLen = kSHA1DigestSize; 120 } 121 tempLen += (2 * kSHA1DigestSize); 122 CSSM_DATA tempData = {0, NULL}; 123 setUpData(tempData, tempLen, privAllocator); 124 125 /* go */ 126 pbkdf2 (hmacsha1, 127 kSHA1DigestSize, 128 passphrase, (uint32)passphraseLen, 129 salt.Data, (uint32)salt.Length, 130 iterCount, 131 keyData->Data, (uint32)keyData->Length, 132 tempData.Data); 133 freeData(&tempData, privAllocator, false); 134} 135 136/* 137 * PKCS5 v1.5 key derivation. Also used for traditional openssl key 138 * derivation, which is mighty similar to PKCS5 v1.5, with the addition 139 * of the ability to generate more than (keysize + ivsize) bytes. 140 */ 141void AppleCSPSession::DeriveKey_PKCS5_V1_5( 142 const Context &context, 143 CSSM_ALGORITHMS algId, 144 const CssmData &Param, // IV optional, mallocd by app to indicate 145 // size 146 CSSM_DATA *keyData) // mallocd by caller to indicate size 147{ 148 CSSM_DATA pwd = {0, NULL}; 149 150 /* password from either Seed.Param or from base key */ 151 CssmCryptoData *cryptData = 152 context.get<CssmCryptoData>(CSSM_ATTRIBUTE_SEED); 153 if((cryptData != NULL) && (cryptData->Param.Length != 0)) { 154 pwd = cryptData->Param; 155 } 156 else { 157 /* Get secure passphrase from base key */ 158 CssmKey *passKey = context.get<CssmKey>(CSSM_ATTRIBUTE_KEY); 159 if (passKey != NULL) { 160 AppleCSPContext::symmetricKeyBits(context, *this, 161 CSSM_ALGID_SECURE_PASSPHRASE, CSSM_KEYUSE_DERIVE, 162 pwd.Data, pwd.Length); 163 } 164 } 165 166 if(pwd.Data == NULL) { 167 errorLog0("DeriveKey_PKCS5_V1_5: null Passphrase\n"); 168 CssmError::throwMe(CSSMERR_CSP_INVALID_DATA); 169 } 170 if(pwd.Length == 0) { 171 errorLog0("DeriveKey_PKCS5_V1_5: zero length passphrase\n"); 172 CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER); 173 } 174 175 CSSM_ALGORITHMS hashAlg; 176 unsigned digestLen; 177 bool opensslAlg = false; 178 switch(algId) { 179 case CSSM_ALGID_PKCS5_PBKDF1_MD5: 180 hashAlg = CSSM_ALGID_MD5; 181 digestLen = kMD5DigestSize; 182 break; 183 case CSSM_ALGID_PKCS5_PBKDF1_MD2: 184 hashAlg = CSSM_ALGID_MD2; 185 digestLen = kMD2DigestSize; 186 break; 187 case CSSM_ALGID_PKCS5_PBKDF1_SHA1: 188 hashAlg = CSSM_ALGID_SHA1; 189 digestLen = kSHA1DigestSize; 190 break; 191 case CSSM_ALGID_PBE_OPENSSL_MD5: 192 hashAlg = CSSM_ALGID_MD5; 193 digestLen = kMD5DigestSize; 194 opensslAlg = true; 195 break; 196 default: 197 /* should not have been called */ 198 assert(0); 199 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); 200 } 201 202 /* IV optional */ 203 CSSM_DATA iv = Param; 204 205 /* total requested length can't exceed digest size for struct PKCS5 v1.5*/ 206 if(!opensslAlg && ((keyData->Length + iv.Length) > digestLen)) { 207 errorLog0("DeriveKey_PKCS5_V1_5: requested length larger than digest\n"); 208 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH); 209 } 210 211 /* salt, from context, required */ 212 CssmData salt = context.get<CssmData>(CSSM_ATTRIBUTE_SALT, 213 CSSMERR_CSP_MISSING_ATTR_SALT); 214 if(salt.Data == NULL) { 215 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SALT); 216 } 217 218 /* iteration count, from context, required */ 219 uint32 iterCount = context.getInt(CSSM_ATTRIBUTE_ITERATION_COUNT, 220 CSSMERR_CSP_MISSING_ATTR_ITERATION_COUNT); 221 222 /* 223 * Apply the underlying hash function Hash for c iterations to 224 * the concatenation of the password P and the salt S, then 225 * extract the first dkLen octets to produce a derived key DK: 226 * 227 * T1 = Hash (P || S) , 228 * T2 = Hash (T1) , 229 * ... 230 * Tc = Hash (Tc-1) , 231 * DK = Tc<0..dkLen-1> . 232 */ 233 DigestCtx ctx; 234 uint8 *keyDataP = keyData->Data; 235 size_t keyBytesToGo = keyData->Length; 236 uint8 *ivDataP = iv.Data; 237 size_t ivBytesToGo = iv.Length; 238 bool looping = false; // true for additional bytes for openssl 239 unsigned char digestOut[kMaxDigestSize]; 240 241 for(;;) { 242 /* this loop guaranteed to only run once if !opensslAlg */ 243 DigestCtxInit(&ctx, hashAlg); 244 245 if(looping) { 246 /* openssl addition: re-digest the digest here */ 247 DigestCtxUpdate(&ctx, digestOut, digestLen); 248 } 249 250 /* digest password then salt */ 251 DigestCtxUpdate(&ctx, pwd.Data, (uint32)pwd.Length); 252 DigestCtxUpdate(&ctx, salt.Data, (uint32)salt.Length); 253 254 DigestCtxFinal(&ctx, digestOut); 255 256 /* now iterCount-1 more iterations */ 257 for(unsigned dex=1; dex<iterCount; dex++) { 258 DigestCtxInit(&ctx, hashAlg); 259 DigestCtxUpdate(&ctx, digestOut, digestLen); 260 DigestCtxFinal(&ctx, digestOut); 261 } 262 263 /* first n bytes to the key */ 264 uint32 bytesAvail = digestLen; 265 size_t toMove = (keyBytesToGo > bytesAvail) ? bytesAvail : keyBytesToGo; 266 memmove(keyDataP, digestOut, toMove); 267 uint8 *remainder = digestOut + toMove; 268 bytesAvail -= toMove; 269 keyDataP += toMove; 270 keyBytesToGo -= toMove; 271 272 /* then optionally some to IV */ 273 if(ivBytesToGo && bytesAvail) { 274 toMove = (ivBytesToGo > bytesAvail) ? bytesAvail : ivBytesToGo; 275 memmove(ivDataP, remainder, toMove); 276 ivDataP += toMove; 277 ivBytesToGo -= toMove; 278 } 279 if((keyBytesToGo == 0) && (ivBytesToGo == 0)) { 280 /* guaranteed true for PKCS5 v1.5 */ 281 break; 282 } 283 284 assert(opensslAlg == true); 285 looping = true; 286 } 287 DigestCtxFree(&ctx); 288} 289 290/* 291 * Member function initially declared for CSPAbstractPluginSession; 292 * we're overriding the null version in CSPFullPluginSession. 293 * 294 * We'll generate any type of key (for now). 295 */ 296void AppleCSPSession::DeriveKey( 297 CSSM_CC_HANDLE CCHandle, 298 const Context &context, 299 CssmData &Param, 300 uint32 KeyUsage, 301 uint32 KeyAttr, 302 const CssmData *KeyLabel, 303 const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry, 304 CssmKey &DerivedKey) 305{ 306 /* validate input args, common to all algorithms */ 307 switch(context.algorithm()) { 308 case CSSM_ALGID_PKCS5_PBKDF2: 309 case CSSM_ALGID_DH: 310 case CSSM_ALGID_PKCS12_PBE_ENCR: 311 case CSSM_ALGID_PKCS12_PBE_MAC: 312 case CSSM_ALGID_PKCS5_PBKDF1_MD5: 313 case CSSM_ALGID_PKCS5_PBKDF1_MD2: 314 case CSSM_ALGID_PKCS5_PBKDF1_SHA1: 315 case CSSM_ALGID_PBE_OPENSSL_MD5: 316 case CSSM_ALGID_OPENSSH1: 317 #if CRYPTKIT_CSP_ENABLE 318 case CSSM_ALGID_ECDH: 319 case CSSM_ALGID_ECDH_X963_KDF: 320 #endif 321 break; 322 /* maybe more here, later */ 323 default: 324 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); 325 } 326 DerivedKey.KeyData.Data = NULL; 327 DerivedKey.KeyData.Length = 0; 328 cspKeyStorage keyStorage = cspParseKeyAttr(CKT_Session, KeyAttr); 329 cspValidateKeyUsageBits(CKT_Session, KeyUsage); 330 331 /* outgoing key type, required (though any algorithm is OK) */ 332 uint32 keyType = context.getInt(CSSM_ATTRIBUTE_KEY_TYPE, 333 CSSMERR_CSP_MISSING_ATTR_KEY_TYPE); 334 335 /* outgoing key size, required - any nonzero value is OK */ 336 uint32 reqKeySize = context.getInt( 337 CSSM_ATTRIBUTE_KEY_LENGTH, 338 CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH); 339 340 /* cook up a place to put the key data */ 341 uint32 keySizeInBytes = (reqKeySize + 7) / 8; 342 SymmetricBinaryKey *binKey = NULL; 343 CSSM_DATA_PTR keyData = NULL; 344 345 switch(keyStorage) { 346 case CKS_None: 347 /* no way */ 348 CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK); 349 case CKS_Ref: 350 /* cook up a symmetric binary key */ 351 binKey = new SymmetricBinaryKey(reqKeySize); 352 keyData = &binKey->mKeyData; 353 break; 354 case CKS_Data: 355 /* key bytes --> caller's cssmKey */ 356 keyData = &DerivedKey.KeyData; 357 setUpData(*keyData, keySizeInBytes, 358 normAllocator); 359 break; 360 } 361 362 /* break off to algorithm-specific code, whose job it is 363 * to fill in keyData->Data with keyData->Length bytes */ 364 switch(context.algorithm()) { 365 case CSSM_ALGID_PKCS5_PBKDF2: 366 DeriveKey_PBKDF2(context, 367 Param, 368 keyData); 369 break; 370 case CSSM_ALGID_DH: 371 DeriveKey_DH(context, 372 Param, 373 keyData, 374 *this); 375 break; 376 case CSSM_ALGID_PKCS12_PBE_ENCR: 377 case CSSM_ALGID_PKCS12_PBE_MAC: 378 DeriveKey_PKCS12(context, 379 *this, 380 Param, 381 keyData); 382 break; 383 case CSSM_ALGID_PKCS5_PBKDF1_MD5: 384 case CSSM_ALGID_PKCS5_PBKDF1_MD2: 385 case CSSM_ALGID_PKCS5_PBKDF1_SHA1: 386 case CSSM_ALGID_PBE_OPENSSL_MD5: 387 DeriveKey_PKCS5_V1_5(context, 388 context.algorithm(), 389 Param, 390 keyData); 391 break; 392 case CSSM_ALGID_OPENSSH1: 393 DeriveKey_OpenSSH1(context, 394 context.algorithm(), 395 Param, 396 keyData); 397 break; 398 #if CRYPTKIT_CSP_ENABLE 399 case CSSM_ALGID_ECDH: 400 case CSSM_ALGID_ECDH_X963_KDF: 401 CryptKit::DeriveKey_ECDH(context, 402 context.algorithm(), 403 Param, 404 keyData, 405 *this); 406 break; 407 #endif 408 /* maybe more here, later */ 409 default: 410 assert(0); 411 } 412 413 /* set up outgoing header */ 414 KeyAttr &= ~KEY_ATTR_RETURN_MASK; 415 CSSM_KEYHEADER &hdr = DerivedKey.KeyHeader; 416 setKeyHeader(hdr, 417 plugin.myGuid(), 418 keyType, 419 CSSM_KEYCLASS_SESSION_KEY, 420 KeyAttr, 421 KeyUsage); 422 /* handle derived size < requested size, legal for Diffie-Hellman */ 423 hdr.LogicalKeySizeInBits = (uint32)(keyData->Length * 8); 424 425 if(keyStorage == CKS_Ref) { 426 /* store and convert to ref key */ 427 addRefKey(*binKey, DerivedKey); 428 } 429 else { 430 /* Raw data */ 431 hdr.BlobType = CSSM_KEYBLOB_RAW; 432 hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING; 433 } 434} 435 436