]> git.saurik.com Git - apple/security.git/blob - libsecurity_apple_x509_cl/lib/Session_Crypto.cpp
Security-55471.tar.gz
[apple/security.git] / libsecurity_apple_x509_cl / lib / Session_Crypto.cpp
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 */
40 void
41 AppleX509CLSession::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 */
53 void
54 AppleX509CLSession::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 */
69 void
70 AppleX509CLSession::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 */
208 void
209 AppleX509CLSession::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 */
318 void
319 AppleX509CLSession::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 */
345 void 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