]> git.saurik.com Git - apple/security.git/blobdiff - 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
diff --git a/OSX/libsecurity_apple_x509_cl/lib/Session_Crypto.cpp b/OSX/libsecurity_apple_x509_cl/lib/Session_Crypto.cpp
new file mode 100644 (file)
index 0000000..cc1a8ff
--- /dev/null
@@ -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 <Security/keyTemplates.h>
+#include <security_asn1/nssUtils.h>
+#include <Security/oidscert.h>
+#include <Security/cssmapple.h>
+
+/*
+ * 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);
+               }
+       }
+}
+