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#ifdef	BSAFE_CSP_ENABLE
19
20
21//
22// bsafeSymmetric.cpp - symmetric encryption contexts and algorithms
23//
24#include "bsafecspi.h"
25#include <security_utilities/debugging.h>
26
27#define bbprintf(args...)	secdebug("BSafeBuf", ## args)
28
29#define VERBOSE_DEBUG	0
30#if		VERBOSE_DEBUG
31static void dumpBuf(
32	char				*title,
33	const CSSM_DATA		*d,
34	uint32 				maxLen)
35{
36	unsigned i;
37	uint32 len;
38
39	if(title) {
40		printf("%s:  ", title);
41	}
42	if(d == NULL) {
43		printf("NO DATA\n");
44		return;
45	}
46	printf("Total Length: %d\n   ", d->Length);
47	len = maxLen;
48	if(d->Length < len) {
49		len = d->Length;
50	}
51	for(i=0; i<len; i++) {
52		printf("%02X ", d->Data[i]);
53		if((i % 16) == 15) {
54			printf("\n   ");
55		}
56	}
57	printf("\n");
58}
59#else
60#define dumpBuf(t, d, m)
61#endif	/* VERBOSE_DEBUG */
62
63void BSafe::SymmetricKeyGenContext::generate(
64	const Context 	&context,
65	CssmKey 		&symKey,
66	CssmKey 		&dummyKey)
67{
68	AppleSymmKeyGenContext::generateSymKey(
69		context,
70		session(),
71		symKey);
72}
73
74// FIXME:
75// We really should match the key algorithm to the en/decrypt
76// algorithm. Also: verify key usage bits.
77void BSafe::BlockCipherContext::init(
78	const Context &context,
79	bool encrypting)
80{
81	bool hasIV = false;
82	bool requirePad = false;
83
84    if (reusing(encrypting))
85        return;	// all set to go
86
87	cssmAlg = context.algorithm();
88    switch(cssmAlg) {
89		// most are handled below; break here to special cases
90		case CSSM_ALGID_RC4:
91			RC4init(context);
92			return;
93		case CSSM_ALGID_DES:
94		case CSSM_ALGID_DESX:
95		case CSSM_ALGID_3DES_3KEY_EDE:
96		case CSSM_ALGID_RC5:
97		case CSSM_ALGID_RC2:
98			break;
99
100		/* others here... */
101        default:
102			// Should never have gotten this far
103			assert(0);
104            CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
105	}
106
107
108	// these variables are used in the switch below and need to
109	// live until after setAlgorithm()
110	BSafeItem 		iv;
111    B_BLK_CIPHER_W_FEEDBACK_PARAMS spec;
112	A_RC5_PARAMS	rc5Params;
113	A_RC2_PARAMS	rc2Params;
114
115    // crypto algorithm
116    spec.encryptionParams = NULL_PTR;	// default, may change
117    switch (cssmAlg) {
118        case CSSM_ALGID_DES:
119            spec.encryptionMethodName = POINTER("des");
120            break;
121        case CSSM_ALGID_DESX:
122            spec.encryptionMethodName = POINTER("desx");
123            break;
124        case CSSM_ALGID_3DES_3KEY_EDE:
125            spec.encryptionMethodName = POINTER("des_ede");
126            break;
127        case CSSM_ALGID_RC5:
128            spec.encryptionMethodName = POINTER("rc5");
129			spec.encryptionParams = POINTER(&rc5Params);
130			rc5Params.version = 0x10;
131			// FIXME - get this from context attr
132			rc5Params.rounds = 1;
133			rc5Params.wordSizeInBits = 32;
134            break;
135        case CSSM_ALGID_RC2:
136		{
137            spec.encryptionMethodName = POINTER("rc2");
138			spec.encryptionParams = POINTER(&rc2Params);
139			// effective key size in bits - either from Context,
140			// or the key
141			uint32 bits = context.getInt(CSSM_ATTRIBUTE_EFFECTIVE_BITS);
142			if(bits == 0) {
143				// OK, try the key
144				CssmKey &key = context.get<CssmKey>(CSSM_ATTRIBUTE_KEY,
145					CSSMERR_CSP_MISSING_ATTR_KEY);
146				bits = key.KeyHeader.LogicalKeySizeInBits;
147			}
148			rc2Params.effectiveKeyBits = bits;
149            break;
150		}
151    }
152
153    // feedback mode
154	cssmMode = context.getInt(CSSM_ATTRIBUTE_MODE);
155    switch (cssmMode) {
156		/* no mode attr --> 0 == CSSM_ALGMODE_NONE, not currently supported */
157 		case CSSM_ALGMODE_CBCPadIV8:
158			requirePad = true;
159			// and fall thru
160		case CSSM_ALGMODE_CBC_IV8:
161		{
162            iv = context.get<CssmData>(CSSM_ATTRIBUTE_INIT_VECTOR,
163				CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR);
164            spec.feedbackMethodName = POINTER("cbc");
165            spec.feedbackParams = POINTER(&iv);
166			hasIV = true;
167            break;
168        }
169        case CSSM_ALGMODE_OFB_IV8: {
170            iv = context.get<CssmData>(CSSM_ATTRIBUTE_INIT_VECTOR,
171				CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR);
172            spec.feedbackMethodName = POINTER("ofb");
173            spec.feedbackParams = POINTER(&iv);
174			hasIV = true;
175            break;
176        }
177        case CSSM_ALGMODE_ECB: {
178            spec.feedbackMethodName = POINTER("ecb");
179            spec.feedbackParams = POINTER(&blockSize);
180            break;
181        }
182        default:
183			errorLog1("BSafe symmetric init: illegal mode (%d)\n", (int)cssmMode);
184            CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_MODE);
185    }
186
187    // padding
188    spec.paddingParams = NULL_PTR;
189	/* no padding attr --> 0 == PADDING_NONE */
190	padEnable = false;
191    uint32 cssmPadding = context.getInt(CSSM_ATTRIBUTE_PADDING);
192	if(requirePad) {
193		switch(cssmPadding) {
194			case CSSM_PADDING_PKCS1:	// for backwards compatibility
195			case CSSM_PADDING_PKCS5:
196			case CSSM_PADDING_PKCS7:
197				spec.paddingMethodName = POINTER("pad");
198				padEnable = true;
199				break;
200			default:
201				CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING);
202		}
203	}
204	else {
205		if(cssmPadding != CSSM_PADDING_NONE) {
206            CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PADDING);
207		}
208		else {
209            spec.paddingMethodName = POINTER("nopad");
210		}
211	}
212
213    // put it all together
214    setAlgorithm(AI_FeedbackCipher, &spec);	// set BSafe algorithm
215    setKeyFromContext(context);				// set BSafe key
216    cipherInit();							// common cryption init
217}
218
219void BSafe::BlockCipherContext::RC4init(
220	const Context &context)
221{
222    setAlgorithm(AI_RC4, NULL);		// set BSafe algorithm
223    setKeyFromContext(context);		// set BSafe key
224	padEnable = false;
225    cipherInit();					// common cryption init
226}
227
228void BSafe::BlockCipherContext::trackUpdate(size_t inSize, size_t outSize)
229{
230	size_t newPending = pending + inSize;
231	pending = newPending % blockSize;
232
233	/*
234	 * Most of the time, the max size buffered by BSAFE is
235	 * blockSize - 1 bytes. When decrypting and padding is enabled,
236	 * BSAFE buffers up to a full block.
237	 */
238	if(!mDirection && 					//�decrypting
239	   padEnable &&						// padding
240	   (pending == 0) &&				// mod result was 0
241	   (newPending > 0)) {				// but nonzero total
242		/* BSAFE is holding a whole block in its buffer */
243		pending = blockSize;
244	}
245	bbprintf("===trackUpdte: %s; inSize=%d newPending=%d pending=%d",
246		(mDirection ? "encrypt" : "decrypt"),
247		inSize, newPending, pending);
248}
249
250size_t BSafe::BlockCipherContext::inputSize(size_t outSize)
251{
252    // if we have an 'outSize' output buffer, how many input bytes may we feed in?
253    size_t wholeBlocks = outSize / blockSize;
254    return wholeBlocks * blockSize - pending + (blockSize - 1);
255}
256
257size_t BSafe::BlockCipherContext::outputSize(bool final, size_t inSize)
258{
259    // how much output buffer will we need for 'size' input bytes?
260
261	size_t totalToGo = inSize + pending;
262	// total to go, rounded up to next block
263	size_t numBlocks = (totalToGo + blockSize - 1) / blockSize;
264	size_t outSize;
265
266	/*
267	 * encrypting: may get one additional block on final() if padding
268	 * decrypting: outsize always <= insize
269	 */
270	if(mDirection && 						// encrypting
271		final &&							// last time
272		padEnable &&  						// padding enabled
273		((totalToGo % blockSize) == 0)) {	// even ptext len
274			numBlocks++;					// extra pad block
275	}
276	outSize = numBlocks * blockSize;
277	bbprintf("===outputSize: %s; final=%d inSize=%d pending=%d outSize=%d",
278		(mDirection ? "encrypt" : "decrypt"),
279		final, inSize, pending, outSize);
280	return outSize;
281}
282
283void BSafe::BlockCipherContext::minimumProgress(size_t &inSize, size_t &outSize)
284{
285    // eat up buffer, proceed one full block
286    inSize = blockSize - pending;
287    outSize = blockSize;
288}
289#endif	/* BSAFE_CSP_ENABLE */
290