]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_csp/lib/AppleCSP.cpp
Security-57740.1.18.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 return 0; // placebo
110 }
111 }
112
113
114 //
115 // Session constructor
116 //
117 AppleCSPSession::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
150 AppleCSPSession::~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 //
159 CSPFullPluginSession::CSPContext *
160 AppleCSPSession::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 //
173 void 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 */
225 bool 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 //
255 YarrowContext::YarrowContext(AppleCSPSession &session)
256 : AppleCSPContext(session)
257 {
258 // nothing for now
259 }
260
261 YarrowContext::~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 //
269 void 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
291 void 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.
302 static 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.
319 static 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.
345 BinaryKey *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.
364 void 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 secinfo("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.
388 BinaryKey & 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.
421 void 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 secinfo("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 secinfo("freeKey", "CSP freeKey unknown key");
448 }
449 }
450 }
451 CSPFullPluginSession::FreeKey(AccessCred, KeyPtr, Delete);
452 }
453
454 /* Passthrough, used for key digest */
455 void 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 */
573 void 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
588 void 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
598 void 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 */
619 CSPKeyInfoProvider *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