1/* 2 * Copyright (c) 2001,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#ifdef ASC_CSP_ENABLE 26 27#include "ascContext.h" 28#include "ascFactory.h" 29#include <security_utilities/debugging.h> 30#include <security_utilities/logging.h> 31#include <Security/cssmapple.h> 32 33#define abprintf(args...) secdebug("ascBuf", ## args) /* buffer sizes */ 34#define aioprintf(args...) secdebug("ascIo", ## args) /* all I/O */ 35 36static Allocator *ascAllocator; 37 38/* 39 * Comcryption-style memory allocator callbacks 40 */ 41static void *ccMalloc(unsigned size) 42{ 43 return ascAllocator->malloc(size); 44} 45static void ccFree(void *data) 46{ 47 ascAllocator->free(data); 48} 49 50/* Given a ComCryption error, throw appropriate CssmError */ 51static void throwComcrypt( 52 comcryptReturn crtn, 53 const char *op) /* optional */ 54{ 55 CSSM_RETURN cerr = CSSM_OK; 56 const char *errStr = "Bad Error String"; 57 58 switch(crtn) { 59 case CCR_SUCCESS: 60 errStr = "CCR_SUCCESS"; 61 break; 62 case CCR_OUTBUFFER_TOO_SMALL: 63 errStr = "CCR_OUTBUFFER_TOO_SMALL"; 64 cerr = CSSMERR_CSP_OUTPUT_LENGTH_ERROR; 65 break; 66 case CCR_MEMORY_ERROR: 67 errStr = "CCR_MEMORY_ERROR"; 68 cerr = CSSMERR_CSP_MEMORY_ERROR; 69 break; 70 case CCR_WRONG_VERSION: 71 errStr = "CCR_WRONG_VERSION"; 72 cerr = CSSMERR_CSP_INVALID_DATA; 73 break; 74 case CCR_BAD_CIPHERTEXT: 75 errStr = "CCR_BAD_CIPHERTEXT"; 76 cerr = CSSMERR_CSP_INVALID_DATA; 77 break; 78 case CCR_INTERNAL: 79 default: 80 errStr = "CCR_INTERNAL"; 81 cerr = CSSMERR_CSP_INTERNAL_ERROR; 82 break; 83 } 84 if(op) { 85 Security::Syslog::error("Apple CSP %s: %s", op, errStr); 86 } 87 if(cerr) { 88 CssmError::throwMe(cerr); 89 } 90} 91 92/* 93 * Algorithm factory. 94 */ 95 96AscAlgFactory::AscAlgFactory( 97 Allocator *normAlloc, 98 Allocator *privAlloc) 99{ 100 /* once-per-address-space init */ 101 ascAllocator = privAlloc; 102 comMallocRegister(ccMalloc, ccFree); 103} 104 105bool AscAlgFactory::setup( 106 AppleCSPSession &session, 107 CSPFullPluginSession::CSPContext * &cspCtx, 108 const Context &context) 109{ 110 if(context.algorithm() != CSSM_ALGID_ASC) { 111 return false; 112 } 113 if(cspCtx != NULL) { 114 /* reusing one of ours; OK */ 115 return true; 116 } 117 switch(context.type()) { 118 case CSSM_ALGCLASS_KEYGEN: 119 cspCtx = new AppleSymmKeyGenerator(session, 120 8, 121 COMCRYPT_MAX_KEYLENGTH * 8, 122 true); // must be byte size 123 return true; 124 case CSSM_ALGCLASS_SYMMETRIC: 125 cspCtx = new ASCContext(session); 126 return true; 127 default: 128 break; 129 } 130 /* not ours */ 131 return false; 132} 133 134ASCContext::~ASCContext() 135{ 136 if(mCcObj != NULL) { 137 comcryptObjFree(mCcObj); 138 } 139} 140 141/* 142 * Standard CSPContext init, called from CSPFullPluginSession::init(). 143 * Reusable, e.g., query followed by en/decrypt. 144 */ 145void ASCContext::init( 146 const Context &context, 147 bool encrypting) 148{ 149 CSSM_SIZE keyLen; 150 uint8 *keyData = NULL; 151 comcryptReturn crtn; 152 153 /* obtain key from context */ 154 symmetricKeyBits(context, session(), CSSM_ALGID_ASC, 155 encrypting ? CSSM_KEYUSE_ENCRYPT : CSSM_KEYUSE_DECRYPT, 156 keyData, keyLen); 157 if((keyLen < 1) || (keyLen > COMCRYPT_MAX_KEYLENGTH)) { 158 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY); 159 } 160 mDecryptBufValid = false; 161 162 /* optional optimization attribute */ 163 comcryptOptimize optimize = CCO_DEFAULT; 164 uint32 opt = context.getInt(CSSM_ATTRIBUTE_ASC_OPTIMIZATION); 165 switch(opt) { 166 case CSSM_ASC_OPTIMIZE_DEFAULT: 167 optimize = CCO_DEFAULT; 168 break; 169 case CSSM_ASC_OPTIMIZE_SIZE: 170 optimize = CCO_SIZE; 171 break; 172 case CSSM_ASC_OPTIMIZE_SECURITY: 173 optimize = CCO_SECURITY; 174 break; 175 case CSSM_ASC_OPTIMIZE_TIME: 176 optimize = CCO_TIME; 177 break; 178 case CSSM_ASC_OPTIMIZE_TIME_SIZE: 179 optimize = CCO_TIME_SIZE; 180 break; 181 case CSSM_ASC_OPTIMIZE_ASCII: 182 optimize = CCO_ASCII; 183 break; 184 default: 185 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS); 186 } 187 188 /* All other context attributes ignored */ 189 /* init the low-level state */ 190 if(mCcObj == NULL) { 191 /* note we allow for context reuse */ 192 mCcObj = comcryptAlloc(); 193 if(mCcObj == NULL) { 194 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR); 195 } 196 } 197 198 crtn = comcryptInit(mCcObj, keyData, (unsigned)keyLen, optimize); 199 if(crtn) { 200 throwComcrypt(crtn, "comcryptInit"); 201 } 202} 203 204/* 205 * All of these functions are called by CSPFullPluginSession. 206 */ 207void ASCContext::update( 208 void *inp, 209 size_t &inSize, // in/out 210 void *outp, 211 size_t &outSize) // in/out 212{ 213 comcryptReturn crtn; 214 unsigned outLen; 215 unsigned char *inText = (unsigned char *)inp; 216 unsigned char *outText = (unsigned char *)outp; 217 218 if(encoding()) { 219 outLen = (unsigned)outSize; 220 crtn = comcryptData(mCcObj, 221 inText, 222 (unsigned)inSize, 223 outText, 224 &outLen, 225 CCE_MORE_TO_COME); // not used on encrypt 226 if(crtn) { 227 throwComcrypt(crtn, "comcryptData"); 228 } 229 } 230 else { 231 /* 232 * Deal with 1-byte buffer hack. First decrypt the existing buffer... 233 */ 234 if(inSize == 0) { 235 CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR); 236 } 237 unsigned thisOutLen; 238 unsigned partialOutLen = 0; 239 if(mDecryptBufValid) { 240 thisOutLen = (unsigned)outSize; 241 crtn = deComcryptData(mCcObj, 242 &mDecryptBuf, 243 1, 244 outText, 245 &thisOutLen, 246 CCE_MORE_TO_COME); 247 mDecryptBufValid = false; 248 if(crtn) { 249 throwComcrypt(crtn, "deComcryptData (1)"); 250 } 251 partialOutLen = thisOutLen; 252 outText += thisOutLen; 253 } 254 255 /* 256 * Now decrypt remaining, less one byte (which is stored in the 257 * buffer). 258 */ 259 thisOutLen = (unsigned)(outSize - partialOutLen); 260 crtn = deComcryptData(mCcObj, 261 inText, 262 (unsigned)(inSize - 1), 263 outText, 264 &thisOutLen, 265 CCE_MORE_TO_COME); 266 if(crtn) { 267 throwComcrypt(crtn, "deComcryptData (2)"); 268 } 269 outLen = partialOutLen + thisOutLen; 270 mDecryptBuf = inText[inSize - 1]; 271 mDecryptBufValid = true; 272 } 273 outSize = outLen; 274 aioprintf("=== ASC::update encrypt %d inSize %ld outSize %ld", 275 encoding() ? 1 : 0, inSize, outSize); 276} 277 278void ASCContext::final( 279 CssmData &out) 280{ 281 if(encoding()) { 282 out.length(0); 283 } 284 else { 285 /* decrypt buffer hack */ 286 if(!mDecryptBufValid) { 287 CssmError::throwMe(CSSMERR_CSP_INPUT_LENGTH_ERROR); 288 } 289 comcryptReturn crtn; 290 unsigned outLen = (unsigned)out.Length; 291 crtn = deComcryptData(mCcObj, 292 &mDecryptBuf, 293 1, 294 (unsigned char *)out.Data, 295 &outLen, 296 CCE_END_OF_STREAM); 297 mDecryptBufValid = false; 298 if(crtn) { 299 throwComcrypt(crtn, "deComcryptData (3)"); 300 } 301 out.length(outLen); 302 } 303 aioprintf("=== ASC::final encrypt %d outSize %ld", 304 encoding() ? 1 : 0, out.Length); 305} 306 307size_t ASCContext::inputSize( 308 size_t outSize) // input for given output size 309{ 310 size_t rtn = comcryptMaxInBufSize(mCcObj, 311 (unsigned)outSize, 312 encoding() ? CCOP_COMCRYPT : CCOP_DECOMCRYPT); 313 abprintf("--- ASCContext::inputSize inSize %ld outSize %ld", 314 rtn, outSize); 315 return rtn; 316} 317 318/* 319 * ComCryption's buffer size calculation really does not lend itself to the 320 * requirements here. For example, there is no guarantee that 321 * inputSize(outputSize(x)) == x. We're just going to fudge it and make 322 * apps (or CSPFullPluginSession) alloc plenty more than they need. 323 */ 324#define ASC_OUTSIZE_FUDGE 1 325#define ASC_OUTSIZE_FUDGE_FACTOR 1.2 326 327size_t ASCContext::outputSize( 328 bool final, 329 size_t inSize) // output for given input size 330{ 331 unsigned effectiveInSize = (unsigned)inSize; 332 size_t rtn; 333 if(encoding()) { 334 rtn = comcryptMaxOutBufSize(mCcObj, 335 effectiveInSize, 336 CCOP_COMCRYPT, 337 final); 338 #if ASC_OUTSIZE_FUDGE 339 float newOutSize = rtn; 340 newOutSize *= ASC_OUTSIZE_FUDGE_FACTOR; 341 rtn = static_cast<size_t>(newOutSize); 342 #endif /* ASC_OUTSIZE_FUDGE */ 343 } 344 else { 345 if(final) { 346 if(mDecryptBufValid) { 347 effectiveInSize++; 348 } 349 } 350 else if(inSize && !mDecryptBufValid) { 351 /* not final and nothing buffered yet - lop off one */ 352 effectiveInSize--; 353 } 354 rtn = comcryptMaxOutBufSize(mCcObj, 355 effectiveInSize, 356 CCOP_DECOMCRYPT, 357 final); 358 } 359 abprintf("--- ASCContext::outputSize inSize %ld outSize %ld final %d ", 360 inSize, rtn, final); 361 return rtn; 362} 363 364void ASCContext::minimumProgress( 365 size_t &in, 366 size_t &out) // minimum progress chunks 367{ 368 if(encoding()) { 369 in = 1; 370 out = comcryptMaxOutBufSize(mCcObj, 371 1, 372 CCOP_COMCRYPT, 373 0); 374 } 375 else { 376 if(mDecryptBufValid) { 377 /* use "everything" */ 378 in = 1; 379 } 380 else { 381 in = 0; 382 } 383 out = comcryptMaxOutBufSize(mCcObj, 384 (unsigned)in, 385 CCOP_DECOMCRYPT, 386 0); 387 } 388 abprintf("--- ASCContext::minProgres in %ld out %ld", in, out); 389} 390 391#endif /* ASC_CSP_ENABLE */ 392