X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_apple_x509_cl/lib/Session_Crypto.cpp diff --git a/Security/libsecurity_apple_x509_cl/lib/Session_Crypto.cpp b/Security/libsecurity_apple_x509_cl/lib/Session_Crypto.cpp new file mode 100644 index 00000000..cc1a8ffb --- /dev/null +++ b/Security/libsecurity_apple_x509_cl/lib/Session_Crypto.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2000-2001,2011,2014 Apple Inc. All Rights Reserved. + * + * The contents of this file constitute Original Code as defined in and are + * subject to the Apple Public Source License Version 1.2 (the 'License'). + * You may not use this file except in compliance with the License. Please obtain + * a copy of the License at http://www.apple.com/publicsource and read it before + * using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS + * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT + * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the + * specific language governing rights and limitations under the License. + */ + + +/* + * Session_Crypto.cpp: CL session functions: sign, verify, CSSM_KEY extraction. + * + * Copyright (c) 2000,2011,2014 Apple Inc. + */ + +#include "AppleX509CLSession.h" +#include "DecodedCert.h" +#include "cldebugging.h" +#include "CSPAttacher.h" +#include "clNssUtils.h" +#include +#include +#include +#include + +/* + * Given a DER-encoded cert, obtain a fully usable CSSM_KEY representing + * the cert's public key. + */ +void +AppleX509CLSession::CertGetKeyInfo( + const CssmData &Cert, + CSSM_KEY_PTR &Key) +{ + DecodedCert decodedCert(*this, Cert); + Key = decodedCert.extractCSSMKey(*this); +} + +/* + * Given a DER-encoded cert and a fully specified crypto context, verify + * cert's TBS and signature. + */ +void +AppleX509CLSession::CertVerifyWithKey( + CSSM_CC_HANDLE CCHandle, + const CssmData &CertToBeVerified) +{ + CssmAutoData tbs(*this); + CssmAutoData algId(*this); + CssmAutoData sig(*this); + CL_certCrlDecodeComponents(CertToBeVerified, tbs, algId, sig); + verifyData(CCHandle, tbs, sig); +} + +/* + * Verify a DER-encoded cert, obtaining crypto context from either + * caller-specified context or by inference from SignerCert. + */ +void +AppleX509CLSession::CertVerify( + CSSM_CC_HANDLE CCHandle, + const CssmData &CertToBeVerified, + const CssmData *SignerCert, + const CSSM_FIELD *VerifyScope, + uint32 ScopeSize) +{ + if((VerifyScope != NULL) || (ScopeSize != 0)) { + CssmError::throwMe(CSSMERR_CL_SCOPE_NOT_SUPPORTED); + } + if((CCHandle == CSSM_INVALID_HANDLE) && (SignerCert == NULL)) { + /* need one or the other */ + CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE); + } + + /* get top-level components */ + CssmAutoData tbs(*this); // in DER format + CssmAutoData algId(*this); // in DER format + CssmAutoData sig(*this); // in DER format + CL_certCrlDecodeComponents(CertToBeVerified, tbs, algId, sig); + + /* these must be explicitly freed upon exit */ + CSSM_KEY_PTR signerPubKey = NULL; + CSSM_CONTEXT_PTR context = NULL; + CSSM_CSP_HANDLE cspHand = CSSM_INVALID_HANDLE; + CSSM_CC_HANDLE ourCcHand = CSSM_INVALID_HANDLE; + + /* SignerCert optional; if present, obtain its subject key */ + if(SignerCert != NULL) { + CertGetKeyInfo(*SignerCert, signerPubKey); + } + + /* signerPubKey must be explicitly freed in any case */ + try { + if(CCHandle != CSSM_INVALID_HANDLE) { + /* + * We'll use this CCHandle for the sig verify, but + * make sure it matches possible incoming SignerCert parameters + */ + if(SignerCert != NULL) { + CSSM_RETURN crtn; + + /* extract signer's public key as a CSSM_KEY from context */ + crtn = CSSM_GetContext(CCHandle, &context); + if(crtn) { + CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE); + } + CSSM_CONTEXT_ATTRIBUTE_PTR attr; + crtn = CSSM_GetContextAttribute(context, + CSSM_ATTRIBUTE_KEY, + &attr); + if(crtn) { + clErrorLog("CertVerify: valid CCHandle but no key!\n"); + CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE); + } + /* require match */ + assert(signerPubKey != NULL); + CSSM_KEY_PTR contextPubKey = attr->Attribute.Key; + if(contextPubKey->KeyHeader.AlgorithmId != + signerPubKey->KeyHeader.AlgorithmId) { + clErrorLog("CertVerify: AlgorithmId mismatch!\n"); + CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE); + } + + /* TBD - check key size, when we have a CSP which can report it */ + /* TBD - anything else? */ + } /* verifying multiple contexts */ + /* OK to use CCHandle as is for verify context */ + } /* valid CCHandle */ + else { + /* + * All we have is signer cert. We already have its public key; + * get signature alg from CertToBeVerified's Cert.algID, which + * we currently have in DER form. Decode it into temp memory. + */ + assert(SignerCert != NULL); + assert(signerPubKey != NULL); + + CSSM_X509_ALGORITHM_IDENTIFIER cssmAlgId; + SecNssCoder coder; + PRErrorCode prtn; + + CssmData &algIdData = algId.get(); + memset(&cssmAlgId, 0, sizeof(cssmAlgId)); + prtn = coder.decode(algIdData.data(), algIdData.length(), + kSecAsn1AlgorithmIDTemplate, &cssmAlgId); + if(prtn) { + CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); + } + + CSSM_ALGORITHMS vfyAlg = CL_oidToAlg(cssmAlgId.algorithm); + + /* + * Handle CSSMOID_ECDSA_WithSpecified, which requires additional + * decode to get the digest algorithm. + */ + if(vfyAlg == CSSM_ALGID_ECDSA_SPECIFIED) { + vfyAlg = CL_nssDecodeECDSASigAlgParams(cssmAlgId.parameters, coder); + } + + /* attach to CSP, cook up a context */ + cspHand = getGlobalCspHand(true); + CSSM_RETURN crtn; + crtn = CSSM_CSP_CreateSignatureContext(cspHand, + vfyAlg, + NULL, // Access Creds + signerPubKey, + &ourCcHand); + CCHandle = ourCcHand; + } /* inferring sig verify context from SignerCert */ + verifyData(CCHandle, tbs, sig); + } + catch(...) { + /* FIXME - isn't there a better way to do this? Save the + * exception as a CSSM_RETURN and throw it if nonzero later? + */ + if(context != NULL) { + CSSM_FreeContext(context); + } + CL_freeCSSMKey(signerPubKey, *this); + if(ourCcHand != CSSM_INVALID_HANDLE) { + CSSM_DeleteContext(ourCcHand); + } + throw; + } + if(context != NULL) { + CSSM_FreeContext(context); + } + CL_freeCSSMKey(signerPubKey, *this); + if(ourCcHand != CSSM_INVALID_HANDLE) { + CSSM_DeleteContext(ourCcHand); + } +} + +/* + * Given a DER-encoded TBSCert and a fully specified crypto context, + * sign the TBSCert and return the resulting DER-encoded Cert. + */ +void +AppleX509CLSession::CertSign( + CSSM_CC_HANDLE CCHandle, + const CssmData &CertTemplate, + const CSSM_FIELD *SignScope, + uint32 ScopeSize, + CssmData &SignedCert) +{ + if((SignScope != NULL) || (ScopeSize != 0)) { + CssmError::throwMe(CSSMERR_CL_SCOPE_NOT_SUPPORTED); + } + if(CCHandle == CSSM_INVALID_HANDLE) { + CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE); + } + + /* cook up algId from context->(signing key, sig algorithm) */ + CSSM_CONTEXT_PTR context = NULL; // must be freed + CSSM_RETURN crtn; + crtn = CSSM_GetContext(CCHandle, &context); + if(crtn) { + CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE); + } + CSSM_CONTEXT_ATTRIBUTE_PTR attr; // not freed + crtn = CSSM_GetContextAttribute(context, + CSSM_ATTRIBUTE_KEY, + &attr); + if(crtn) { + clErrorLog("CertSign: valid CCHandle but no signing key!\n"); + CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE); + } + CSSM_KEY_PTR signingKey = attr->Attribute.Key; + if(signingKey == NULL) { + clErrorLog("CertSign: valid CCHandle, NULL signing key!\n"); + CssmError::throwMe(CSSMERR_CL_INVALID_CONTEXT_HANDLE); + } + + CssmAutoData encAlgId(*this); + CssmAutoData rawSig(*this); + CssmAutoData fullCert(*this); + try { + /* + * FIXME: we really should break up the template and ensure that its + * signature algId matches the one we're signing with, or just use + * that algId here....for now, this is up to the app to make sure. + */ + + /* temp allocs/encode into here */ + SecNssCoder coder; + + /* CSSM alg --> CSSM_X509_ALGORITHM_IDENTIFIER */ + /*** + *** Note: some ECDSA implementations use CSSMOID_ECDSA_WithSpecified for + *** the algorithm followed by an encoded digest algorithm. We'll handle + *** that on *decode* but we're going to do it the sensible way - with + *** one unique OID to specify the whole thing (e.g. CSSMOID_ECDSA_WithSHA512 + *** which we get from cssmAlgToOid()) unless we're forced to do + *** otherwise by cranky servers. + ***/ + CSSM_X509_ALGORITHM_IDENTIFIER algId; + memset(&algId, 0, sizeof(algId)); + const CSSM_OID *oid = cssmAlgToOid(context->AlgorithmType); + + if(oid == NULL) { + clErrorLog("CertSIgn: unknown alg (%u)\n", + (unsigned)context->AlgorithmType); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); + } + algId.algorithm = *oid; + + /* NULL params - skip for ECDSA */ + switch(context->AlgorithmType) { + case CSSM_ALGID_SHA1WithECDSA: + case CSSM_ALGID_SHA224WithECDSA: + case CSSM_ALGID_SHA256WithECDSA: + case CSSM_ALGID_SHA384WithECDSA: + case CSSM_ALGID_SHA512WithECDSA: + case CSSM_ALGID_ECDSA_SPECIFIED: + break; + default: + CL_nullAlgParams(algId); + break; + } + /* DER-encode the algID */ + PRErrorCode prtn; + prtn = SecNssEncodeItemOdata(&algId, kSecAsn1AlgorithmIDTemplate, + encAlgId); + if(prtn) { + CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR); + } + + /* sign TBS --> rawSig */ + signData(CCHandle, CertTemplate, rawSig); + /* put it all together */ + CL_certEncodeComponents(CertTemplate, encAlgId, rawSig, fullCert); + } + catch (...) { + CSSM_FreeContext(context); + throw; + } + CSSM_FreeContext(context); + SignedCert = fullCert.release(); +} + +/*** Private functions ***/ + +/* + * Sign a CssmData with the specified signing context. Used for + * signing both certs and CRLs; this routine doesn't know anything + * about either one. + */ +void +AppleX509CLSession::signData( + CSSM_CC_HANDLE ccHand, + const CssmData &tbs, + CssmOwnedData &sig) // mallocd and returned +{ + CSSM_RETURN crtn; + CssmData cSig; + + crtn = CSSM_SignData( + ccHand, + &tbs, + 1, // DataBufCount + CSSM_ALGID_NONE, // DigestAlgorithm, + &cSig); + if(crtn) { + clErrorLog("AppleX509CLSession::CSSM_SignData: %ld\n", (long)crtn); + CssmError::throwMe(crtn); + } + sig.set(cSig); +} + +/* + * Verify a block of data given a crypto context and a signature. + * Used for verifying certs and CRLs. Returns a CSSM_RETURN (callers + * always need to clean up after calling us). + */ +void AppleX509CLSession::verifyData( + CSSM_CC_HANDLE ccHand, + const CssmData &tbs, + const CssmData &sig) +{ + CSSM_RETURN crtn; + + crtn = CSSM_VerifyData(ccHand, + &tbs, + 1, + CSSM_ALGID_NONE, // Digest alg + &sig); + if(crtn) { + if(crtn == CSSMERR_CSP_VERIFY_FAILED) { + /* CSP and CL report this differently */ + CssmError::throwMe(CSSMERR_CL_VERIFICATION_FAILURE); + } + else { + CssmError::throwMe(crtn); + } + } +} +