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