/ OSX / libsecurity_apple_csp / lib / ascContext.cpp
ascContext.cpp
  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...)	secinfo("ascBuf", ## args)		/* buffer sizes */
 34  #define aioprintf(args...)	secinfo("ascIo", ## args)		/* all I/O */
 35  
 36  static Allocator *ascAllocator;
 37  
 38  /*
 39   * Comcryption-style memory allocator callbacks
 40   */
 41  static void *ccMalloc(unsigned size)
 42  {
 43  	return ascAllocator->malloc(size);
 44  }
 45  static void ccFree(void *data)
 46  {
 47  	ascAllocator->free(data);
 48  }
 49  
 50  /* Given a ComCryption error, throw appropriate CssmError */
 51  static 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   
 96  AscAlgFactory::AscAlgFactory(
 97  	Allocator *normAlloc, 
 98  	Allocator *privAlloc)
 99  {
100  	/* once-per-address-space init */
101  	ascAllocator = privAlloc;
102  	comMallocRegister(ccMalloc, ccFree);
103  }
104  
105  bool 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  
134  ASCContext::~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   */
145  void 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   */
207  void 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  
278  void 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  
307  size_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  
327  size_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  
364  void 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 */