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