1/* 2 * Copyright (c) 2000-2001,2011,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// AppleCSP.cpp - top-level plugin and session implementation 21// 22#include "AppleCSP.h" 23#include "AppleCSPSession.h" 24#include "AppleCSPUtils.h" 25#include <stdio.h> 26#include "cspdebugging.h" 27#include <security_cdsa_plugin/CSPsession.h> 28#include <security_utilities/alloc.h> 29#ifdef BSAFE_CSP_ENABLE 30#include "bsafecsp.h" 31#include "bsafecspi.h" 32#endif 33#ifdef CRYPTKIT_CSP_ENABLE 34#include "cryptkitcsp.h" 35#include "FEEKeys.h" 36#endif 37#include <miscAlgFactory.h> 38#ifdef ASC_CSP_ENABLE 39#include "ascFactory.h" 40#endif 41#include <RSA_DSA_csp.h> 42#include <RSA_DSA_keys.h> 43#include <DH_csp.h> 44#include <DH_keys.h> 45 46#include "YarrowConnection.h" 47 48// 49// Make and break the plugin object 50// 51AppleCSPPlugin::AppleCSPPlugin() : 52 normAllocator(Allocator::standard(Allocator::normal)), 53 privAllocator(Allocator::standard(Allocator::sensitive)), 54 #ifdef BSAFE_CSP_ENABLE 55 bSafe4Factory(new BSafeFactory(&normAllocator, &privAllocator)), 56 #endif 57 #ifdef CRYPTKIT_CSP_ENABLE 58 cryptKitFactory(new CryptKitFactory(&normAllocator, &privAllocator)), 59 #endif 60 miscAlgFactory(new MiscAlgFactory(&normAllocator, &privAllocator)), 61 #ifdef ASC_CSP_ENABLE 62 ascAlgFactory(new AscAlgFactory(&normAllocator, &privAllocator)), 63 #endif 64 rsaDsaAlgFactory(new RSA_DSA_Factory(&normAllocator, &privAllocator)), 65 dhAlgFactory(new DH_Factory(&normAllocator, &privAllocator)) 66{ 67 // misc. once-per-address-space cruft... 68} 69 70AppleCSPPlugin::~AppleCSPPlugin() 71{ 72 #ifdef BSAFE_CSP_ENABLE 73 delete bSafe4Factory; 74 #endif 75 #ifdef CRYPTKIT_CSP_ENABLE 76 delete cryptKitFactory; 77 #endif 78 delete miscAlgFactory; 79 #ifdef ASC_CSP_ENABLE 80 delete ascAlgFactory; 81 #endif 82 delete rsaDsaAlgFactory; 83 delete dhAlgFactory; 84} 85 86 87// 88// Create a new plugin session, our way 89// 90PluginSession *AppleCSPPlugin::makeSession( 91 CSSM_MODULE_HANDLE handle, 92 const CSSM_VERSION &version, 93 uint32 subserviceId, 94 CSSM_SERVICE_TYPE subserviceType, 95 CSSM_ATTACH_FLAGS attachFlags, 96 const CSSM_UPCALLS &upcalls) 97{ 98 switch (subserviceType) { 99 case CSSM_SERVICE_CSP: 100 return new AppleCSPSession(handle, 101 *this, 102 version, 103 subserviceId, 104 subserviceType, 105 attachFlags, 106 upcalls); 107 default: 108 CssmError::throwMe(CSSMERR_CSSM_INVALID_SERVICE_MASK); 109 return 0; // placebo 110 } 111} 112 113 114// 115// Session constructor 116// 117AppleCSPSession::AppleCSPSession( 118 CSSM_MODULE_HANDLE handle, 119 AppleCSPPlugin &plug, 120 const CSSM_VERSION &version, 121 uint32 subserviceId, 122 CSSM_SERVICE_TYPE subserviceType, 123 CSSM_ATTACH_FLAGS attachFlags, 124 const CSSM_UPCALLS &upcalls) 125 : CSPFullPluginSession(handle, 126 plug, 127 version, 128 subserviceId, 129 subserviceType, 130 attachFlags, 131 upcalls), 132 #ifdef BSAFE_CSP_ENABLE 133 bSafe4Factory(*(dynamic_cast<BSafeFactory *>(plug.bSafe4Factory))), 134 #endif 135 #ifdef CRYPTKIT_CSP_ENABLE 136 cryptKitFactory(*(dynamic_cast<CryptKitFactory *>(plug.cryptKitFactory))), 137 #endif 138 miscAlgFactory(*(dynamic_cast<MiscAlgFactory *>(plug.miscAlgFactory))), 139 #ifdef ASC_CSP_ENABLE 140 ascAlgFactory(*(dynamic_cast<AscAlgFactory *>(plug.ascAlgFactory))), 141 #endif 142 rsaDsaAlgFactory(*(dynamic_cast<RSA_DSA_Factory *>(plug.rsaDsaAlgFactory))), 143 dhAlgFactory(*(dynamic_cast<DH_Factory *>(plug.dhAlgFactory))), 144 normAllocator(*this), 145 privAllocator(plug.privAlloc()) 146{ 147 // anything? 148} 149 150AppleCSPSession::~AppleCSPSession() 151{ 152 // anything? 153} 154 155// 156// Called at (CSSM) context create time. This is ignored; we do a full 157// context setup later, at setupContext time. 158// 159CSPFullPluginSession::CSPContext * 160AppleCSPSession::contextCreate( 161 CSSM_CC_HANDLE handle, 162 const Context &context) 163{ 164 return NULL; 165} 166 167// 168// Called by CSPFullPluginSession when an op is actually commencing. 169// Context can safely assumed to be fully formed and stable for the 170// duration of the op; thus we wait until now to set up our 171// CSPContext as appropriate to the op. 172// 173void AppleCSPSession::setupContext( 174 CSPContext * &cspCtx, 175 const Context &context, 176 bool encoding) 177{ 178 /* 179 * Note we leave the decision as to whether it's OK to 180 * reuse a context to the individual factories. 181 */ 182 #ifdef BSAFE_CSP_ENABLE 183 /* Give BSAFE the firsrt shot if it's present */ 184 if (bSafe4Factory.setup(*this, cspCtx, context)) { 185 CASSERT(cspCtx != NULL); 186 return; 187 } 188 #endif 189 if (rsaDsaAlgFactory.setup(*this, cspCtx, context)) { 190 CASSERT(cspCtx != NULL); 191 return; 192 } 193 if (miscAlgFactory.setup(*this, cspCtx, context)) { 194 CASSERT(cspCtx != NULL); 195 return; 196 } 197 if (dhAlgFactory.setup(*this, cspCtx, context)) { 198 CASSERT(cspCtx != NULL); 199 return; 200 } 201 #ifdef CRYPTKIT_CSP_ENABLE 202 if (cryptKitFactory.setup(*this, cspCtx, context)) { 203 CASSERT(cspCtx != NULL); 204 return; 205 } 206 #endif 207 #ifdef ASC_CSP_ENABLE 208 if (ascAlgFactory.setup(*this, cspCtx, context)) { 209 CASSERT(cspCtx != NULL); 210 return; 211 } 212 #endif 213 if(setup(cspCtx, context)) { 214 CASSERT(cspCtx != NULL); 215 return; 216 } 217 dprintf0("AppleCSPSession::setupContext: invalid algorithm\n"); 218 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); 219} 220 221/* 222 * Used for generating crypto contexts at this level. 223 * Analogous to AlgorithmFactory.setup(). 224 */ 225bool AppleCSPSession::setup( 226 CSPFullPluginSession::CSPContext * &cspCtx, 227 const Context &context) 228{ 229 if (cspCtx) { 230 return false; // not ours or already set 231 } 232 233 switch(context.type()) { 234 case CSSM_ALGCLASS_RANDOMGEN: 235 switch (context.algorithm()) { 236 case CSSM_ALGID_APPLE_YARROW: 237 cspCtx = new YarrowContext(*this); 238 return true; 239 /* other random algs here */ 240 default: 241 return false; 242 } 243 /* other contexts here */ 244 default: 245 return false; 246 } 247 /* NOT REACHED */ 248 return false; 249 250} 251 252// 253// Context for CSSM_ALGID_APPLE_YARROW. 254// 255YarrowContext::YarrowContext(AppleCSPSession &session) 256 : AppleCSPContext(session) 257{ 258 // nothing for now 259} 260 261YarrowContext::~YarrowContext() 262{ 263 // nothing for now 264} 265 266// 267// Only job here is to snag the length and process the optional seed argument 268// 269void YarrowContext::init( 270 const Context &context, 271 bool encoding) 272{ 273 /* stash requested length for use later in outputSize() */ 274 outSize = context.getInt(CSSM_ATTRIBUTE_OUTPUT_SIZE, 275 CSSMERR_CSP_INVALID_ATTR_OUTPUT_SIZE); 276 277 /* optional seed */ 278 CssmCryptoData *cseed = context.get<CssmCryptoData>(CSSM_ATTRIBUTE_SEED); 279 if(cseed == NULL) { 280 /* we're done */ 281 return; 282 } 283 CssmData seed = (*cseed)(); 284 if((seed.Length == 0) || 285 (seed.Data == NULL)) { 286 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SEED); 287 } 288 session().addEntropy((size_t)seed.Length, seed.Data); 289} 290 291void YarrowContext::final( 292 CssmData &out) 293{ 294 session().getRandomBytes((size_t)out.Length, out.Data); 295} 296 297/*** 298 *** Binary Key support. 299 ***/ 300 301// Given a CSSM_DATA, extract its KeyRef. 302static KeyRef CssmDataToKeyRef( 303 const CSSM_DATA &data) 304{ 305 if(data.Length != sizeof(KeyRef)) { 306 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE); 307 } 308 309 uint8 *cp = data.Data + sizeof(KeyRef) - 1; 310 KeyRef keyRef = 0; 311 for(unsigned dex=0; dex<sizeof(KeyRef); dex++) { 312 keyRef <<= 8; 313 keyRef |= *cp--; 314 } 315 return keyRef; 316} 317 318// Place a KeyRef into a CSSM_DATA, mallocing if necessary. 319static void keyRefToCssmData( 320 KeyRef keyRef, 321 CSSM_DATA &data, 322 Allocator &allocator) 323{ 324 if(data.Length > sizeof(keyRef)) { 325 /* don't leave old raw key material lying around */ 326 memset(data.Data + sizeof(keyRef), 0, data.Length - sizeof(keyRef)); 327 } 328 else if(data.Length < sizeof(keyRef)) { 329 /* not enough space for even a keyRef, force realloc */ 330 allocator.free(data.Data); 331 data.Data = NULL; 332 data.Length = 0; 333 } 334 setUpData(data, sizeof(keyRef), allocator); 335 336 uint8 *cp = data.Data; 337 for(unsigned i=0; i<sizeof(keyRef); i++) { 338 *cp++ = keyRef & 0xff; 339 keyRef >>= 8; 340 } 341} 342 343// Look up a BinaryKey by its KeyRef. Returns NULL if not 344// found. refKeyMapLock held on entry and exit. 345BinaryKey *AppleCSPSession::lookupKeyRef( 346 KeyRef keyRef) 347{ 348 const BinaryKey *binKey; 349 350 // use safe version, don't create new entry if this key 351 // isn't there 352 keyMap::iterator it = refKeyMap.find(keyRef); 353 if(it == refKeyMap.end()) { 354 return NULL; 355 } 356 binKey = it->second; 357 assert(binKey == reinterpret_cast<const BinaryKey *>(keyRef)); 358 assert(binKey->mKeyRef == keyRef); 359 return const_cast<BinaryKey *>(binKey); 360} 361 362// add a BinaryKey to our refKeyMap. Sets up cssmKey 363// as appropriate. 364void AppleCSPSession::addRefKey( 365 BinaryKey &binKey, 366 CssmKey &cssmKey) 367{ 368 // for now, KeyRef is just the address of the BinaryKey 369 KeyRef keyRef = reinterpret_cast<KeyRef>(&binKey); 370 371 binKey.mKeyRef = keyRef; 372 binKey.mKeyHeader = CssmKey::Header::overlay(cssmKey.KeyHeader); 373 { 374 StLock<Mutex> _(refKeyMapLock); 375 assert(lookupKeyRef(keyRef) == NULL); 376 refKeyMap[keyRef] = &binKey; 377 } 378 cssmKey.KeyHeader.BlobType = CSSM_KEYBLOB_REFERENCE; 379 cssmKey.KeyHeader.Format = CSSM_KEYBLOB_REF_FORMAT_INTEGER; 380 keyRefToCssmData(keyRef, cssmKey.KeyData, normAllocator); 381 secdebug("freeKey", "CSP addRefKey key %p keyData %p keyRef %p", 382 &cssmKey, cssmKey.KeyData.Data, &binKey); 383} 384 385// Given a CssmKey in reference form, obtain the associated 386// BinaryKey. Throws CSSMERR_CSP_INVALID_KEY_REFERENCE if 387// key not found in session key map. 388BinaryKey & AppleCSPSession::lookupRefKey( 389 const CssmKey &cssmKey) 390{ 391 KeyRef keyRef; 392 BinaryKey *binKey; 393 394 keyRef = CssmDataToKeyRef(cssmKey.KeyData); 395 { 396 StLock<Mutex> _(refKeyMapLock); 397 binKey = lookupKeyRef(keyRef); 398 } 399 if(binKey == NULL) { 400 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE); 401 } 402 assert(Guid::overlay(binKey->mKeyHeader.CspId) == plugin.myGuid()); 403 404 /* 405 * Verify sensitive fields have not changed between when the BinaryKey was 406 * created/stored and when the caller passed in the ref key. 407 * Some fields were changed by addRefKey, so make a local copy.... 408 */ 409 CSSM_KEYHEADER localHdr = cssmKey.KeyHeader; 410 localHdr.BlobType = binKey->mKeyHeader.BlobType; 411 localHdr.Format = binKey->mKeyHeader.Format; 412 if(memcmp(&localHdr, &binKey->mKeyHeader, sizeof(CSSM_KEYHEADER))) { 413 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE); 414 } 415 return (*binKey); 416} 417 418// CSPFullPluginSession declares & implements this. 419// Note that we ignore the delete argument; since we don't 420// store anything, freeing is the same as deleting. 421void AppleCSPSession::FreeKey( 422 const AccessCredentials *AccessCred, 423 CssmKey &KeyPtr, 424 CSSM_BOOL Delete) 425{ 426 427 if((KeyPtr.blobType() == CSSM_KEYBLOB_REFERENCE) && 428 (KeyPtr.cspGuid() == plugin.myGuid())) { 429 // it's a ref key we generated - delete associated BinaryKey 430 KeyRef keyRef = CssmDataToKeyRef(KeyPtr.KeyData); 431 { 432 StLock<Mutex> _(refKeyMapLock); 433 BinaryKey *binKey = lookupKeyRef(keyRef); 434 if(binKey != NULL) { 435 secdebug("freeKey", "CSP FreeKey key %p keyData %p binKey %p", 436 &KeyPtr, KeyPtr.KeyData.Data, binKey); 437 try { 438 refKeyMap.erase(keyRef); 439 delete binKey; 440 } 441 catch (...) { 442 errorLog0("Error deleting/erasing known " 443 "ref key\n"); 444 } 445 } 446 else { 447 secdebug("freeKey", "CSP freeKey unknown key"); 448 } 449 } 450 } 451 CSPFullPluginSession::FreeKey(AccessCred, KeyPtr, Delete); 452} 453 454/* Passthrough, used for key digest */ 455void AppleCSPSession::PassThrough( 456 CSSM_CC_HANDLE CCHandle, 457 const Context &Context, 458 uint32 PassThroughId, 459 const void *InData, 460 void **OutData) 461{ 462 *OutData = NULL; 463 464 /* validate context */ 465 if(Context.type() != CSSM_ALGCLASS_NONE) { 466 CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT); 467 } 468 469 switch(PassThroughId) { 470 case CSSM_APPLECSP_KEYDIGEST: 471 { 472 CssmKey &key = Context.get<CssmKey>( 473 CSSM_ATTRIBUTE_KEY, 474 CSSMERR_CSP_MISSING_ATTR_KEY); 475 476 /* validate key as best we can */ 477 switch(key.keyClass()) { 478 case CSSM_KEYCLASS_PUBLIC_KEY: 479 case CSSM_KEYCLASS_PRIVATE_KEY: 480 case CSSM_KEYCLASS_SESSION_KEY: 481 break; 482 default: 483 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); 484 } 485 486 /* 487 * Ref key: obtain binary, ask it for blob 488 * Raw key: get info provider, ask it for the blob. This 489 * allows for an optimized path which avoids 490 * converting to a BinaryKey. 491 */ 492 CssmData blobToHash; 493 switch(key.blobType()) { 494 case CSSM_KEYBLOB_RAW: 495 { 496 CSPKeyInfoProvider *provider = infoProvider(key); 497 bool converted = 498 provider->getHashableBlob(privAllocator, blobToHash); 499 if(converted) { 500 /* took optimized case; proceed */ 501 delete provider; 502 break; 503 } 504 505 /* convert to BinaryKey and ask it to do the work */ 506 BinaryKey *binKey; 507 CSSM_KEYATTR_FLAGS flags = 0; // not used 508 provider->CssmKeyToBinary(NULL, // no paramKey 509 flags, 510 &binKey); 511 binKey->mKeyHeader = 512 CssmKey::Header::overlay(key.KeyHeader); 513 CSSM_KEYBLOB_FORMAT rawFormat; 514 rawFormat = CSSM_KEYBLOB_RAW_FORMAT_DIGEST; 515 CSSM_KEYATTR_FLAGS attrFlags = 0; 516 binKey->generateKeyBlob(privAllocator, 517 blobToHash, 518 rawFormat, 519 *this, 520 NULL, 521 attrFlags); 522 delete binKey; 523 delete provider; 524 break; 525 } 526 case CSSM_KEYBLOB_REFERENCE: 527 { 528 BinaryKey &binKey = lookupRefKey(key); 529 CSSM_KEYBLOB_FORMAT rawFormat; 530 rawFormat = CSSM_KEYBLOB_RAW_FORMAT_DIGEST; 531 CSSM_KEYATTR_FLAGS attrFlags = 0; 532 binKey.generateKeyBlob(privAllocator, 533 blobToHash, 534 rawFormat, 535 *this, 536 NULL, 537 attrFlags); 538 } 539 break; 540 default: 541 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); 542 } 543 544 /* obtain sha1 hash of blobToHash */ 545 546 CSSM_DATA_PTR outHash = NULL; 547 try { 548 outHash = 549 (CSSM_DATA_PTR)normAllocator.malloc(sizeof(CSSM_DATA)); 550 outHash->Data = 551 (uint8 *)normAllocator.malloc(SHA1_DIGEST_SIZE); 552 outHash->Length = SHA1_DIGEST_SIZE; 553 } 554 catch(...) { 555 freeCssmData(blobToHash, privAllocator); 556 throw; 557 } 558 cspGenSha1Hash(blobToHash.data(), blobToHash.length(), 559 outHash->Data); 560 freeCssmData(blobToHash, privAllocator); 561 *OutData = outHash; 562 return; 563 } 564 default: 565 CssmError::throwMe(CSSMERR_CSP_INVALID_PASSTHROUGH_ID); 566 } 567 /* NOT REACHED */ 568} 569 570/* 571 * CSPSession version of QueryKeySizeInBits. 572 */ 573void AppleCSPSession::getKeySize(const CssmKey &key, 574 CSSM_KEY_SIZE &size) 575{ 576 CSPKeyInfoProvider *provider = infoProvider(key); 577 try { 578 provider->QueryKeySizeInBits(size); 579 } 580 catch(...) { 581 /* don't leak this on error */ 582 delete provider; 583 throw; 584 } 585 delete provider; 586} 587 588void AppleCSPSession::getRandomBytes(size_t length, uint8 *cp) 589{ 590 try { 591 cspGetRandomBytes(cp, (unsigned)length); 592 } 593 catch(...) { 594 errorLog0("CSP: YarrowClient failure\n"); 595 } 596} 597 598void AppleCSPSession::addEntropy(size_t length, const uint8 *cp) 599{ 600 try { 601 cspAddEntropy(cp, (unsigned)length); 602 } 603 catch(...) { 604 #if CSP_ALLOW_FEE_RNG 605 return; 606 #else 607 throw; 608 #endif 609 } 610} 611 612/*** 613 *** CSPKeyInfoProvider support. 614 ***/ 615 616/* 617 * Find a CSPKeyInfoProvider subclass for the specified key. 618 */ 619CSPKeyInfoProvider *AppleCSPSession::infoProvider( 620 const CssmKey &key) 621{ 622 CSPKeyInfoProvider *provider = NULL; 623 624 #ifdef BSAFE_CSP_ENABLE 625 /* Give BSAFE first shot, if it's here */ 626 provider = BSafe::BSafeKeyInfoProvider::provider(key, *this); 627 if(provider != NULL) { 628 return provider; 629 } 630 #endif 631 632 provider = RSAKeyInfoProvider::provider(key, *this); 633 if(provider != NULL) { 634 return provider; 635 } 636 637 provider = SymmetricKeyInfoProvider::provider(key, *this); 638 if(provider != NULL) { 639 return provider; 640 } 641 642 #ifdef CRYPTKIT_CSP_ENABLE 643 provider = CryptKit::FEEKeyInfoProvider::provider(key, *this); 644 if(provider != NULL) { 645 return provider; 646 } 647 #endif 648 649 provider = DSAKeyInfoProvider::provider(key, *this); 650 if(provider != NULL) { 651 return provider; 652 } 653 654 provider = DHKeyInfoProvider::provider(key, *this); 655 if(provider != NULL) { 656 return provider; 657 } 658 659 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); 660} 661 662