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