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