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