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 * Session_Crypto.cpp: CL session functions: sign, verify, CSSM_KEY extraction.
21 *
22 * Created 9/1/2000 by Doug Mitchell.
23 * Copyright (c) 2000 by Apple Computer.
24 */
25
26#include "AppleX509CLSession.h"
27#include "DecodedCert.h"
28#include "cldebugging.h"
29#include "CSPAttacher.h"
30#include "clNssUtils.h"
31#include <Security/keyTemplates.h>
32#include <security_asn1/nssUtils.h>
33#include <Security/oidscert.h>
34#include <Security/cssmapple.h>
35
36/*
37 * Given a DER-encoded cert, obtain a fully usable CSSM_KEY representing
38 * the cert's public key.
39 */
40void
41AppleX509CLSession::CertGetKeyInfo(
42	const CssmData &Cert,
43	CSSM_KEY_PTR &Key)
44{
45	DecodedCert decodedCert(*this, Cert);
46	Key = decodedCert.extractCSSMKey(*this);
47}
48
49/*
50 * Given a DER-encoded cert and a fully specified crypto context, verify
51 * cert's TBS and signature.
52 */
53void
54AppleX509CLSession::CertVerifyWithKey(
55	CSSM_CC_HANDLE CCHandle,
56	const CssmData &CertToBeVerified)
57{
58	CssmAutoData tbs(*this);
59	CssmAutoData algId(*this);
60	CssmAutoData sig(*this);
61	CL_certCrlDecodeComponents(CertToBeVerified, tbs, algId, sig);
62	verifyData(CCHandle, tbs, sig);
63}
64
65/*
66 * Verify a DER-encoded cert, obtaining crypto context from either
67 * caller-specified context or by inference from SignerCert.
68 */
69void
70AppleX509CLSession::CertVerify(
71	CSSM_CC_HANDLE CCHandle,
72	const CssmData &CertToBeVerified,
73	const CssmData *SignerCert,
74	const CSSM_FIELD *VerifyScope,
75	uint32 ScopeSize)
76{
77	if((VerifyScope != NULL) || (ScopeSize != 0)) {
78		CssmError::throwMe(CSSMERR_CL_SCOPE_NOT_SUPPORTED);
79	}
80	if((CCHandle == CSSM_INVALID_HANDLE) && (SignerCert == NULL)) {
81		/* need one or the other */
82		CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE);
83	}
84
85	/* get top-level components  */
86	CssmAutoData tbs(*this);		// in DER format
87	CssmAutoData algId(*this);		// in DER format
88	CssmAutoData sig(*this);		// in DER format
89	CL_certCrlDecodeComponents(CertToBeVerified, tbs, algId, sig);
90
91	/* these must be explicitly freed upon exit */
92	CSSM_KEY_PTR signerPubKey = NULL;
93	CSSM_CONTEXT_PTR context = NULL;
94	CSSM_CSP_HANDLE cspHand = CSSM_INVALID_HANDLE;
95	CSSM_CC_HANDLE ourCcHand = CSSM_INVALID_HANDLE;
96
97	/* SignerCert optional; if present, obtain its subject key */
98	if(SignerCert != NULL) {
99		CertGetKeyInfo(*SignerCert, signerPubKey);
100	}
101
102	/* signerPubKey must be explicitly freed in any case */
103	try {
104		if(CCHandle != CSSM_INVALID_HANDLE) {
105			/*
106			 * We'll use this CCHandle for the sig verify, but
107			 * make sure it matches possible incoming SignerCert parameters
108			 */
109			if(SignerCert != NULL) {
110				CSSM_RETURN crtn;
111
112				/* extract signer's public key as a CSSM_KEY from context */
113				crtn = CSSM_GetContext(CCHandle, &context);
114				if(crtn) {
115					CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE);
116				}
117				CSSM_CONTEXT_ATTRIBUTE_PTR attr;
118				crtn = CSSM_GetContextAttribute(context,
119					CSSM_ATTRIBUTE_KEY,
120					&attr);
121				if(crtn) {
122					clErrorLog("CertVerify: valid CCHandle but no key!\n");
123					CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE);
124				}
125				/* require match */
126				assert(signerPubKey != NULL);
127				CSSM_KEY_PTR contextPubKey = attr->Attribute.Key;
128				if(contextPubKey->KeyHeader.AlgorithmId !=
129				   signerPubKey->KeyHeader.AlgorithmId) {
130					clErrorLog("CertVerify: AlgorithmId mismatch!\n");
131					CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE);
132				}
133
134				/* TBD - check key size, when we have a CSP which can report it */
135				/* TBD - anything else? */
136			}	/* verifying multiple contexts */
137			/* OK to use CCHandle as is for verify context */
138		}	/* valid CCHandle */
139		else {
140			/*
141			 * All we have is signer cert. We already have its public key;
142			 * get signature alg from CertToBeVerified's Cert.algID, which
143			 * we currently have in DER form. Decode it into temp memory.
144			 */
145			assert(SignerCert != NULL);
146			assert(signerPubKey != NULL);
147
148			CSSM_X509_ALGORITHM_IDENTIFIER cssmAlgId;
149			SecNssCoder coder;
150			PRErrorCode prtn;
151
152			CssmData &algIdData = algId.get();
153			memset(&cssmAlgId, 0, sizeof(cssmAlgId));
154			prtn = coder.decode(algIdData.data(), algIdData.length(),
155				kSecAsn1AlgorithmIDTemplate, &cssmAlgId);
156			if(prtn) {
157				CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
158			}
159
160			CSSM_ALGORITHMS vfyAlg = CL_oidToAlg(cssmAlgId.algorithm);
161
162			/*
163			 * Handle CSSMOID_ECDSA_WithSpecified, which requires additional
164			 * decode to get the digest algorithm.
165			 */
166			if(vfyAlg == CSSM_ALGID_ECDSA_SPECIFIED) {
167				vfyAlg = CL_nssDecodeECDSASigAlgParams(cssmAlgId.parameters, coder);
168			}
169
170			/* attach to CSP, cook up a context */
171			cspHand = getGlobalCspHand(true);
172			CSSM_RETURN crtn;
173			crtn = CSSM_CSP_CreateSignatureContext(cspHand,
174				vfyAlg,
175				NULL,			// Access Creds
176				signerPubKey,
177				&ourCcHand);
178			CCHandle = ourCcHand;
179		}	/* inferring sig verify context from SignerCert */
180		verifyData(CCHandle, tbs, sig);
181	}
182	catch(...) {
183		/* FIXME - isn't there a better way to do this? Save the
184		 * exception as a CSSM_RETURN and throw it if nonzero later?
185		 */
186		if(context != NULL) {
187			CSSM_FreeContext(context);
188		}
189		CL_freeCSSMKey(signerPubKey, *this);
190		if(ourCcHand != CSSM_INVALID_HANDLE) {
191			CSSM_DeleteContext(ourCcHand);
192		}
193		throw;
194	}
195	if(context != NULL) {
196		CSSM_FreeContext(context);
197	}
198	CL_freeCSSMKey(signerPubKey, *this);
199	if(ourCcHand != CSSM_INVALID_HANDLE) {
200		CSSM_DeleteContext(ourCcHand);
201	}
202}
203
204/*
205 * Given a DER-encoded TBSCert and a fully specified crypto context,
206 * sign the TBSCert and return the resulting DER-encoded Cert.
207 */
208void
209AppleX509CLSession::CertSign(
210	CSSM_CC_HANDLE CCHandle,
211	const CssmData &CertTemplate,
212	const CSSM_FIELD *SignScope,
213	uint32 ScopeSize,
214	CssmData &SignedCert)
215{
216	if((SignScope != NULL) || (ScopeSize != 0)) {
217		CssmError::throwMe(CSSMERR_CL_SCOPE_NOT_SUPPORTED);
218	}
219	if(CCHandle == CSSM_INVALID_HANDLE) {
220		CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE);
221	}
222
223	/* cook up algId from context->(signing key, sig algorithm) */
224	CSSM_CONTEXT_PTR context = NULL;		// must be freed
225	CSSM_RETURN crtn;
226	crtn = CSSM_GetContext(CCHandle, &context);
227	if(crtn) {
228		CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE);
229	}
230	CSSM_CONTEXT_ATTRIBUTE_PTR attr;		// not freed
231	crtn = CSSM_GetContextAttribute(context,
232		CSSM_ATTRIBUTE_KEY,
233		&attr);
234	if(crtn) {
235		clErrorLog("CertSign: valid CCHandle but no signing key!\n");
236		CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE);
237	}
238	CSSM_KEY_PTR signingKey = attr->Attribute.Key;
239	if(signingKey == NULL) {
240		clErrorLog("CertSign: valid CCHandle, NULL signing key!\n");
241		CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE);
242	}
243
244	CssmAutoData encAlgId(*this);
245	CssmAutoData rawSig(*this);
246	CssmAutoData fullCert(*this);
247	try {
248		/*
249		 * FIXME: we really should break up the template and ensure that its
250		 * signature algId matches the one we're signing with, or just use
251		 * that algId here....for now, this is up to the app to make sure.
252		 */
253
254		/* temp allocs/encode into here */
255		SecNssCoder coder;
256
257		/* CSSM alg --> CSSM_X509_ALGORITHM_IDENTIFIER */
258		/***
259		 *** Note: some ECDSA implementations use CSSMOID_ECDSA_WithSpecified for
260		 *** the algorithm followed by an encoded digest algorithm. We'll handle
261		 *** that on *decode* but we're going to do it the sensible way - with
262		 *** one unique OID to specify the whole thing (e.g. CSSMOID_ECDSA_WithSHA512
263		 *** which we get from cssmAlgToOid()) unless we're forced to do
264		 *** otherwise by cranky servers.
265		 ***/
266		CSSM_X509_ALGORITHM_IDENTIFIER algId;
267		memset(&algId, 0, sizeof(algId));
268		const CSSM_OID *oid = cssmAlgToOid(context->AlgorithmType);
269
270		if(oid == NULL) {
271			clErrorLog("CertSIgn: unknown alg (%u)\n",
272				(unsigned)context->AlgorithmType);
273			CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
274		}
275		algId.algorithm = *oid;
276
277		/* NULL params - skip for ECDSA */
278		switch(context->AlgorithmType) {
279			case CSSM_ALGID_SHA1WithECDSA:
280			case CSSM_ALGID_SHA224WithECDSA:
281			case CSSM_ALGID_SHA256WithECDSA:
282			case CSSM_ALGID_SHA384WithECDSA:
283			case CSSM_ALGID_SHA512WithECDSA:
284			case CSSM_ALGID_ECDSA_SPECIFIED:
285				break;
286			default:
287				CL_nullAlgParams(algId);
288				break;
289		}
290		/* DER-encode the algID */
291		PRErrorCode prtn;
292		prtn = SecNssEncodeItemOdata(&algId, kSecAsn1AlgorithmIDTemplate,
293			encAlgId);
294		if(prtn) {
295			CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR);
296		}
297
298		/* sign TBS --> rawSig */
299		signData(CCHandle, CertTemplate, rawSig);
300		/* put it all together */
301		CL_certEncodeComponents(CertTemplate, encAlgId, rawSig, fullCert);
302	}
303	catch (...) {
304		CSSM_FreeContext(context);
305		throw;
306	}
307	CSSM_FreeContext(context);
308	SignedCert = fullCert.release();
309}
310
311/*** Private functions ***/
312
313/*
314 * Sign a CssmData with the specified signing context. Used for
315 * signing both certs and CRLs; this routine doesn't know anything
316 * about either one.
317 */
318void
319AppleX509CLSession::signData(
320	CSSM_CC_HANDLE	ccHand,
321	const CssmData	&tbs,
322	CssmOwnedData	&sig)			// mallocd and returned
323{
324	CSSM_RETURN crtn;
325	CssmData cSig;
326
327	crtn = CSSM_SignData(
328		ccHand,
329		&tbs,
330		1,					// DataBufCount
331		CSSM_ALGID_NONE,	// DigestAlgorithm,
332		&cSig);
333	if(crtn) {
334		clErrorLog("AppleX509CLSession::CSSM_SignData: %ld\n", (long)crtn);
335		CssmError::throwMe(crtn);
336	}
337	sig.set(cSig);
338}
339
340/*
341 * Verify a block of data given a crypto context and a signature.
342 * Used for verifying certs and CRLs. Returns a CSSM_RETURN (callers
343 * always need to clean up after calling us).
344 */
345void AppleX509CLSession::verifyData(
346	CSSM_CC_HANDLE	ccHand,
347	const CssmData	&tbs,
348	const CssmData	&sig)
349{
350	CSSM_RETURN crtn;
351
352	crtn = CSSM_VerifyData(ccHand,
353		&tbs,
354		1,
355		CSSM_ALGID_NONE,		// Digest alg
356		&sig);
357	if(crtn) {
358		if(crtn == CSSMERR_CSP_VERIFY_FAILED) {
359			/* CSP and CL report this differently */
360			CssmError::throwMe(CSSMERR_CL_VERIFICATION_FAILURE);
361		}
362		else {
363			CssmError::throwMe(crtn);
364		}
365	}
366}
367
368