1/* 2 * Copyright (c) 2004,2011-2012,2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25// 26// SDContext - cryptographic contexts for the security server 27// 28#include "SDContext.h" 29 30#include "SDCSPSession.h" 31#include "SDKey.h" 32#include <security_utilities/debugging.h> 33 34#define ssCryptDebug(args...) secdebug("ssCrypt", ## args) 35 36using namespace SecurityServer; 37 38// 39// SDContext 40// 41SDContext::SDContext(SDCSPSession &session) 42: mSession(session), mContext(NULL) 43{ 44} 45 46void SDContext::clearOutBuf() 47{ 48 if(mOutBuf.Data) { 49 mSession.free(mOutBuf.Data); 50 mOutBuf.clear(); 51 } 52} 53 54void SDContext::copyOutBuf(CssmData &out) 55{ 56 if(out.length() < mOutBuf.length()) { 57 CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR); 58 } 59 memmove(out.Data, mOutBuf.Data, mOutBuf.Length); 60 out.Length = mOutBuf.Length; 61 clearOutBuf(); 62} 63 64void 65SDContext::init(const Context &context, 66 bool /* encoding */) // @@@ should be removed from API since it's already in mDirection 67{ 68 mContext = &context; 69 clearOutBuf(); 70} 71 72SecurityServer::ClientSession & 73SDContext::clientSession() 74{ 75 return mSession.clientSession(); 76} 77 78 79// 80// SDRandomContext -- Context for GenerateRandom operations 81// 82SDRandomContext::SDRandomContext(SDCSPSession &session) : SDContext(session) {} 83 84void 85SDRandomContext::init(const Context &context, bool encoding) 86{ 87 SDContext::init(context, encoding); 88 89 // set/freeze output size 90 mOutSize = context.getInt(CSSM_ATTRIBUTE_OUTPUT_SIZE, CSSMERR_CSP_MISSING_ATTR_OUTPUT_SIZE); 91 92#if 0 93 // seed the PRNG (if specified) 94 if (const CssmCryptoData *seed = context.get<CssmCryptoData>(CSSM_ATTRIBUTE_SEED)) { 95 const CssmData &seedValue = (*seed)(); 96 clientSession().seedRandom(seedValue); 97 } 98#endif 99} 100 101size_t 102SDRandomContext::outputSize(bool final, size_t inSize) 103{ 104 return mOutSize; 105} 106 107void 108SDRandomContext::final(CssmData &out) 109{ 110 clientSession().generateRandom(*mContext, out); 111} 112 113 114// signature contexts 115SDSignatureContext::SDSignatureContext(SDCSPSession &session) 116 : SDContext(session), 117 mKeyHandle(noKey), 118 mNullDigest(NULL), 119 mDigest(NULL) 120{ 121 /* nothing else for now */ 122} 123 124SDSignatureContext::~SDSignatureContext() 125{ 126 delete mNullDigest; 127 delete mDigest; 128} 129 130void SDSignatureContext::init(const Context &context, bool signing) 131{ 132 SDContext::init(context, signing); 133 134 /* reusable: skip everything except resetting digest state */ 135 if((mNullDigest != NULL) || (mDigest != NULL)) { 136 if(mNullDigest != NULL) { 137 mNullDigest->digestInit(); 138 } 139 return; 140 } 141 142 /* snag key from context */ 143 const CssmKey &keyInContext = 144 context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY, 145 CSSMERR_CSP_MISSING_ATTR_KEY); 146 mKeyHandle = mSession.lookupKey(keyInContext).keyHandle(); 147 148 /* get digest alg and sig alg from Context.algorithm */ 149 switch(context.algorithm()) { 150 /*** DSA ***/ 151 case CSSM_ALGID_SHA1WithDSA: 152 mDigestAlg = CSSM_ALGID_SHA1; 153 mSigAlg = CSSM_ALGID_DSA; 154 break; 155 case CSSM_ALGID_DSA: // Raw 156 mDigestAlg = CSSM_ALGID_NONE; 157 mSigAlg = CSSM_ALGID_DSA; 158 break; 159 /*** RSA ***/ 160 case CSSM_ALGID_SHA1WithRSA: 161 mDigestAlg = CSSM_ALGID_SHA1; 162 mSigAlg = CSSM_ALGID_RSA; 163 break; 164 case CSSM_ALGID_MD5WithRSA: 165 mDigestAlg = CSSM_ALGID_MD5; 166 mSigAlg = CSSM_ALGID_RSA; 167 break; 168 case CSSM_ALGID_MD2WithRSA: 169 mDigestAlg = CSSM_ALGID_MD2; 170 mSigAlg = CSSM_ALGID_RSA; 171 break; 172 case CSSM_ALGID_RSA: // Raw 173 mDigestAlg = CSSM_ALGID_NONE; 174 mSigAlg = CSSM_ALGID_RSA; 175 break; 176 /*** FEE ***/ 177 case CSSM_ALGID_FEE_SHA1: 178 mDigestAlg = CSSM_ALGID_SHA1; 179 mSigAlg = CSSM_ALGID_FEE; 180 break; 181 case CSSM_ALGID_FEE_MD5: 182 mDigestAlg = CSSM_ALGID_MD5; 183 mSigAlg = CSSM_ALGID_FEE; 184 break; 185 case CSSM_ALGID_FEE: // Raw 186 mDigestAlg = CSSM_ALGID_NONE; 187 mSigAlg = CSSM_ALGID_FEE; 188 break; 189 /*** ECDSA ***/ 190 case CSSM_ALGID_SHA1WithECDSA: 191 mDigestAlg = CSSM_ALGID_SHA1; 192 mSigAlg = CSSM_ALGID_ECDSA; 193 break; 194 case CSSM_ALGID_SHA224WithECDSA: 195 mDigestAlg = CSSM_ALGID_SHA224; 196 mSigAlg = CSSM_ALGID_ECDSA; 197 break; 198 case CSSM_ALGID_SHA256WithECDSA: 199 mDigestAlg = CSSM_ALGID_SHA256; 200 mSigAlg = CSSM_ALGID_ECDSA; 201 break; 202 case CSSM_ALGID_SHA384WithECDSA: 203 mDigestAlg = CSSM_ALGID_SHA384; 204 mSigAlg = CSSM_ALGID_ECDSA; 205 break; 206 case CSSM_ALGID_SHA512WithECDSA: 207 mDigestAlg = CSSM_ALGID_SHA512; 208 mSigAlg = CSSM_ALGID_ECDSA; 209 break; 210 case CSSM_ALGID_ECDSA: // Raw 211 mDigestAlg = CSSM_ALGID_NONE; 212 mSigAlg = CSSM_ALGID_ECDSA; 213 break; 214 default: 215 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); 216 } 217 218 /* set up mNullDigest or mDigest */ 219 if(mDigestAlg == CSSM_ALGID_NONE) { 220 mNullDigest = new NullDigest(); 221 } 222 else { 223 mDigest = new CssmClient::Digest(mSession.mRawCsp, mDigestAlg); 224 } 225} 226 227/* 228 * for raw sign/verify - optionally called after init. 229 * Note that in init (in this case), we set mDigestAlg to ALGID_NONE and set up 230 * a NullDigest. We now overwrite mDigestAlg, and we'll use�this 231 * new value when we do the actual sign/vfy. 232 */ 233void SDSignatureContext::setDigestAlgorithm(CSSM_ALGORITHMS digestAlg) 234{ 235 mDigestAlg = digestAlg; 236} 237 238void SDSignatureContext::update(const CssmData &data) 239{ 240 /* Note that for this context, we really can not deal with an out-of-sequence 241 * update --> final(true, 0) --> update since we lose the pending digest state 242 * when we perform the implied final() during outputSize(true, 0). */ 243 assert(mOutBuf.Data == NULL); 244 245 /* add incoming data to digest or accumulator */ 246 if(mNullDigest) { 247 mNullDigest->digestUpdate(data.data(), data.length()); 248 } 249 else { 250 mDigest->digest(data); 251 } 252} 253 254size_t SDSignatureContext::outputSize(bool final, size_t inSize) 255{ 256 if(!final) { 257 ssCryptDebug("===sig outputSize !final\n"); 258 return 0; 259 } 260 if(!encoding()) { 261 ssCryptDebug("===sig outputSize final, !encoding\n"); 262 /* don't see why this is even called... */ 263 return 0; 264 } 265 if(inSize == 0) { 266 /* 267 * This is the implied signal to go for it. Note that in this case, 268 * we can not go back and re-do the op in case of an unexpected 269 * sequence of update/outputSize(final, 0)/final - we lose the digest 270 * state. Perhaps we should save the digest...? But still it would 271 * be impossible to do another update. 272 */ 273 clearOutBuf(); 274 sign(mOutBuf); 275 ssCryptDebug("===sig outputSize(pre-op) %u", (unsigned)mOutBuf.Length); 276 return (size_t)mOutBuf.Length; 277 } 278 else { 279 /* out-of-band case, ask CSP via SS */ 280 uint32 outSize = clientSession().getOutputSize(*mContext, 281 mKeyHandle, 282 /* FIXME - what to use for inSize here - we don't want to 283 * interrogate mDigest, as that would result in another RPC... 284 * and signature size is not related to input size...right? */ 285 (uint32)inSize, 286 true); 287 ssCryptDebug("===sig outputSize(RPC) %u", (unsigned)outSize); 288 return (size_t)outSize; 289 } 290} 291 292/* sign */ 293 294/* first the common routine shared by final and outputSize */ 295void SDSignatureContext::sign(CssmData &sig) 296{ 297 /* we have to pass down a modified Context, thus.... */ 298 Context tempContext = *mContext; 299 tempContext.AlgorithmType = mSigAlg; 300 301 if(mNullDigest) { 302 CssmData dData(const_cast<void *>(mNullDigest->digestPtr()), 303 mNullDigest->digestSizeInBytes()); 304 clientSession().generateSignature(tempContext, 305 mKeyHandle, 306 dData, 307 sig, 308 mDigestAlg); 309 } 310 else { 311 CssmAutoData d (mDigest->allocator ()); 312 d.set((*mDigest) ()); 313 314 clientSession().generateSignature(tempContext, 315 mKeyHandle, 316 d, 317 sig, 318 mDigestAlg); 319 } 320} 321 322/* this is the one called by CSPFullPluginSession */ 323void SDSignatureContext::final(CssmData &sig) 324{ 325 if(mOutBuf.Data) { 326 /* normal final case in which the actual RPC via SS was done in the 327 * previous outputSize() call. */ 328 ssCryptDebug("===final via pre-op and copy"); 329 copyOutBuf(sig); 330 return; 331 } 332 333 ssCryptDebug("===final via RPC"); 334 sign(sig); 335} 336 337/* verify */ 338void 339SDSignatureContext::final(const CssmData &sig) 340{ 341 /* we have to pass down a modified Context, thus.... */ 342 Context tempContext = *mContext; 343 tempContext.AlgorithmType = mSigAlg; 344 345 if(mNullDigest) { 346 CssmData dData(const_cast<void *>(mNullDigest->digestPtr()), 347 mNullDigest->digestSizeInBytes()); 348 clientSession().verifySignature(tempContext, 349 mKeyHandle, 350 dData, 351 sig, 352 mDigestAlg); 353 } 354 else { 355 clientSession().verifySignature(tempContext, 356 mKeyHandle, 357 (*mDigest)(), 358 sig, 359 mDigestAlg); 360 } 361} 362 363 364// 365// SDCryptContext -- Context for Encrypt and Decrypt operations 366// 367SDCryptContext::SDCryptContext(SDCSPSession &session) 368 : SDContext(session), mKeyHandle(noKey) 369{ 370 /* nothing for now */ 371} 372 373 374SDCryptContext::~SDCryptContext() 375{ 376 /* nothing for now */ 377} 378 379void 380SDCryptContext::init(const Context &context, bool encoding) 381{ 382 ssCryptDebug("===init"); 383 SDContext::init(context, encoding); 384 385 /* reusable; reset accumulator */ 386 mNullDigest.digestInit(); 387 388 const CssmKey &keyInContext = 389 context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY, 390 CSSMERR_CSP_MISSING_ATTR_KEY); 391 mKeyHandle = mSession.lookupKey(keyInContext).keyHandle(); 392} 393 394size_t 395SDCryptContext::inputSize(size_t outSize) 396{ 397 ssCryptDebug("===inputSize outSize=%u", (unsigned)outSize); 398 return UINT_MAX; 399} 400 401size_t 402SDCryptContext::outputSize(bool final, size_t inSize) 403{ 404 ssCryptDebug("===outputSize final %d inSize=%u", final, (unsigned)inSize); 405 if(!final) { 406 /* we buffer until final; no intermediate output */ 407 return 0; 408 } 409 size_t inBufSize = mNullDigest.digestSizeInBytes(); 410 if(inSize == 0) { 411 /* This is the implied signal to go for it */ 412 clearOutBuf(); 413 if(inBufSize == 0) { 414 return 0; 415 } 416 const CssmData in(const_cast<void *>(mNullDigest.digestPtr()), inBufSize); 417 if (encoding()) { 418 clientSession().encrypt(*mContext, mKeyHandle, in, mOutBuf); 419 } 420 else { 421 clientSession().decrypt(*mContext, mKeyHandle, in, mOutBuf); 422 } 423 /* leave the accumulator as is in case of unexpected sequence */ 424 ssCryptDebug(" ===outSize(pre-op) %u", (unsigned)mOutBuf.Length); 425 return mOutBuf.Length; 426 } 427 else { 428 /* out-of-band case, ask CSP via SS */ 429 uint32 outSize = clientSession().getOutputSize(*mContext, 430 mKeyHandle, 431 (uint32)(inBufSize + inSize), 432 encoding()); 433 ssCryptDebug(" ===outSize(RPC) %u", (unsigned)outSize); 434 return (size_t)outSize; 435 } 436} 437 438void 439SDCryptContext::minimumProgress(size_t &in, size_t &out) 440{ 441 in = 1; 442 out = 0; 443} 444 445void 446SDCryptContext::update(void *inp, size_t &inSize, void *outp, size_t &outSize) 447{ 448 ssCryptDebug("===update inSize=%u", (unsigned)inSize); 449 /* add incoming data to accumulator */ 450 mNullDigest.digestUpdate(inp, inSize); 451 outSize = 0; 452 clearOutBuf(); 453} 454 455void 456SDCryptContext::final(CssmData &out) 457{ 458 if(mOutBuf.Data != NULL) { 459 /* normal final case in which the actual RPC via SS was done in the 460 * previous outputSize() call. A memcpy is needed here because 461 * CSPFullPluginSession has just allocated the buf size we need. */ 462 ssCryptDebug("===final via pre-op and copy"); 463 copyOutBuf(out); 464 return; 465 } 466 467 /* when is this path taken...? */ 468 ssCryptDebug("===final via RPC"); 469 size_t inSize = mNullDigest.digestSizeInBytes(); 470 if(!inSize) return; 471 472 const CssmData in(const_cast<void *>(mNullDigest.digestPtr()), inSize); 473 IFDEBUG(size_t origOutSize = out.length()); 474 if (encoding()) { 475 clientSession().encrypt(*mContext, mKeyHandle, in, out); 476 } 477 else { 478 clientSession().decrypt(*mContext, mKeyHandle, in, out); 479 } 480 assert(out.length() <= origOutSize); 481 mNullDigest.digestInit(); 482} 483 484// Digest, using raw CSP 485SDDigestContext::SDDigestContext(SDCSPSession &session) 486 : SDContext(session), mDigest(NULL) 487{ 488 489} 490 491SDDigestContext::~SDDigestContext() 492{ 493 delete mDigest; 494} 495 496void SDDigestContext::init(const Context &context, bool encoding) 497{ 498 CSSM_ALGORITHMS alg; 499 500 SDContext::init(context, encoding); 501 alg = context.algorithm(); 502 mDigest = new CssmClient::Digest(mSession.mRawCsp, alg); 503} 504 505void SDDigestContext::update(const CssmData &data) 506{ 507 mDigest->digest(data); 508} 509 510void SDDigestContext::final(CssmData &out) 511{ 512 (*mDigest)(out); 513} 514 515size_t SDDigestContext::outputSize(bool final, size_t inSize) 516{ 517 if(!final) { 518 return 0; 519 } 520 else { 521 return (size_t)mDigest->getOutputSize((uint32)inSize); 522 } 523} 524 525// MACContext - common class for MAC generate, verify 526SDMACContext::SDMACContext(SDCSPSession &session) 527 : SDContext(session), mKeyHandle(noKey) 528{ 529 530} 531 532void SDMACContext::init(const Context &context, bool encoding) 533{ 534 SDContext::init(context, encoding); 535 536 /* reusable; reset accumulator */ 537 mNullDigest.digestInit(); 538 539 /* snag key from context */ 540 const CssmKey &keyInContext = 541 context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY, 542 CSSMERR_CSP_MISSING_ATTR_KEY); 543 mKeyHandle = mSession.lookupKey(keyInContext).keyHandle(); 544} 545 546void SDMACContext::update(const CssmData &data) 547{ 548 /* add incoming data to accumulator */ 549 mNullDigest.digestUpdate(data.data(), data.length()); 550} 551 552size_t SDMACContext::outputSize(bool final, size_t inSize) 553{ 554 if(!final) { 555 ssCryptDebug("===mac outputSize !final\n"); 556 return 0; 557 } 558 if(!encoding()) { 559 ssCryptDebug("===mac outputSize final, !encoding\n"); 560 /* don't see why this is even called... */ 561 return 0; 562 } 563 if(inSize == 0) { 564 /* 565 * This is the implied signal to go for it. 566 */ 567 clearOutBuf(); 568 genMac(mOutBuf); 569 ssCryptDebug("===mac outputSize(pre-op) %u", (unsigned)mOutBuf.Length); 570 return (size_t)mOutBuf.Length; 571 } 572 else { 573 /* out-of-band case, ask CSP via SS */ 574 uint32 outSize = clientSession().getOutputSize(*mContext, 575 mKeyHandle, 576 (uint32)(inSize + mNullDigest.digestSizeInBytes()), 577 true); 578 ssCryptDebug("===mac outputSize(RPC) %u", (unsigned)outSize); 579 return (size_t)outSize; 580 } 581} 582 583/* generate */ 584 585/* first the common routine used by final() and outputSize() */ 586void SDMACContext::genMac(CssmData &mac) 587{ 588 CssmData allData(const_cast<void *>(mNullDigest.digestPtr()), 589 mNullDigest.digestSizeInBytes()); 590 clientSession().generateMac(*mContext, mKeyHandle, allData, mac); 591} 592 593void SDMACContext::final(CssmData &mac) 594{ 595 genMac(mac); 596} 597 598/* verify */ 599void SDMACContext::final(const CssmData &mac) 600{ 601 CssmData allData(const_cast<void *>(mNullDigest.digestPtr()), 602 mNullDigest.digestSizeInBytes()); 603 clientSession().verifyMac(*mContext, mKeyHandle, allData, mac); 604} 605