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