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