]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_apple_x509_cl/lib/DecodedCert.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_apple_x509_cl / lib / DecodedCert.cpp
diff --git a/OSX/libsecurity_apple_x509_cl/lib/DecodedCert.cpp b/OSX/libsecurity_apple_x509_cl/lib/DecodedCert.cpp
new file mode 100644 (file)
index 0000000..c681d58
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ */
+
+
+/*
+ * DecodedCert.cpp - object representing a decoded cert, in NSS
+ * format, with extensions parsed and decoded (still in NSS format).
+ *
+ * Copyright (c) 2000,2011,2014 Apple Inc. 
+ */
+
+#include "DecodedCert.h"
+#include "clNssUtils.h"
+#include "cldebugging.h"
+#include "AppleX509CLSession.h"
+#include "CSPAttacher.h"
+#include <Security/cssmapple.h>
+#include <Security/oidscert.h>
+
+DecodedCert::DecodedCert(
+       AppleX509CLSession      &session)
+       : DecodedItem(session)
+{
+       memset(&mCert, 0, sizeof(mCert));
+}
+
+/* one-shot constructor, decoding from DER-encoded data */
+DecodedCert::DecodedCert(
+       AppleX509CLSession      &session,
+       const CssmData  &encodedCert)
+       : DecodedItem(session)
+{
+       memset(&mCert, 0, sizeof(mCert));
+       PRErrorCode prtn = mCoder.decode(encodedCert.data(), encodedCert.length(), 
+               kSecAsn1SignedCertTemplate, &mCert);
+       if(prtn) {
+               CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
+       }
+       mDecodedExtensions.decodeFromNss(mCert.tbs.extensions);
+       mState = IS_DecodedAll;
+}
+               
+DecodedCert::~DecodedCert()
+{
+}
+       
+/* decode TBSCert and its extensions */
+void DecodedCert::decodeTbs(
+       const CssmData  &encodedTbs)
+{
+       assert(mState == IS_Empty);
+       
+       memset(&mCert, 0, sizeof(mCert));
+       PRErrorCode prtn = mCoder.decode(encodedTbs.data(), encodedTbs.length(), 
+               kSecAsn1TBSCertificateTemplate, &mCert.tbs);
+       if(prtn) {
+               CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
+       }
+       mDecodedExtensions.decodeFromNss(mCert.tbs.extensions);
+       mState = IS_DecodedTBS;
+}
+
+void DecodedCert::encodeExtensions()
+{
+       NSS_TBSCertificate &tbs = mCert.tbs;
+       assert(mState == IS_Building);
+       assert(tbs.extensions == NULL);
+
+       if(mDecodedExtensions.numExtensions() == 0) {
+               /* no extensions, no error */
+               return;
+       }
+       mDecodedExtensions.encodeToNss(tbs.extensions);
+}
+
+/*
+ * FIXME : how to determine max encoding size at run time!?
+ */
+#define MAX_TEMPLATE_SIZE      (8 * 1024)
+
+/* encode TBS component; only called from CertCreateTemplate */
+void DecodedCert::encodeTbs(
+       CssmOwnedData   &encodedTbs)
+{
+       encodeExtensions();
+       assert(mState == IS_Building);
+
+       /* enforce required fields - could go deeper, maybe we should */
+       NSS_TBSCertificate &tbs = mCert.tbs;
+       if((tbs.signature.algorithm.Data == NULL) ||
+          (tbs.issuer.rdns == NULL) ||
+          (tbs.subject.rdns == NULL) ||
+          (tbs.subjectPublicKeyInfo.subjectPublicKey.Data == NULL)) {
+               clErrorLog("DecodedCert::encodeTbs: incomplete TBS");
+               /* an odd, undocumented error return */
+               CssmError::throwMe(CSSMERR_CL_NO_FIELD_VALUES);
+       }
+       
+       PRErrorCode prtn;
+       prtn = SecNssEncodeItemOdata(&tbs, kSecAsn1TBSCertificateTemplate,
+               encodedTbs);
+       if(prtn) {
+               CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR);
+       }
+}
+
+/*
+ * Cook up CSSM_KEYUSE, gleaning as much as possible from
+ * (optional) extensions. If no applicable extensions available,
+ * we'll just return CSSM_KEYUSE_ANY.
+ *
+ * Note that the standard KeyUsage flags involving 'signing' translate
+ * to verify since we're only dealing with public keys. 
+ */
+CSSM_KEYUSE DecodedCert::inferKeyUsage() const
+{
+       CSSM_KEYUSE keyUse = 0;
+       const DecodedExten *decodedExten;
+       uint32 numFields;
+       
+       /* Basic KeyUsage */
+       decodedExten = DecodedItem::findDecodedExt(CSSMOID_KeyUsage, false, 
+               0, numFields);
+       if(decodedExten) {
+               CSSM_DATA *ku = (CSSM_DATA *)decodedExten->nssObj();
+               assert(ku != NULL);
+               CE_KeyUsage kuse = clBitStringToKeyUsage(*ku);
+               if(kuse & CE_KU_DigitalSignature) {
+                       keyUse |= CSSM_KEYUSE_VERIFY;
+               }
+               if(kuse & CE_KU_NonRepudiation) {
+                       keyUse |= CSSM_KEYUSE_VERIFY;
+               }
+               if(kuse & CE_KU_KeyEncipherment) {
+                       keyUse |= CSSM_KEYUSE_WRAP;
+               }
+               if(kuse & CE_KU_KeyAgreement) {
+                       keyUse |= CSSM_KEYUSE_DERIVE;
+               }
+               if(kuse & CE_KU_KeyCertSign) {
+                       keyUse |= CSSM_KEYUSE_VERIFY;
+               }
+               if(kuse & CE_KU_CRLSign) {
+                       keyUse |= CSSM_KEYUSE_VERIFY;
+               }
+               if(kuse & CE_KU_DataEncipherment) {
+                       keyUse |= CSSM_KEYUSE_ENCRYPT;
+               }
+       }
+       
+       /* Extended key usage */
+       decodedExten = DecodedItem::findDecodedExt(CSSMOID_ExtendedKeyUsage, 
+                       false, 0, numFields);
+       if(decodedExten) {
+               NSS_ExtKeyUsage *euse = (NSS_ExtKeyUsage *)decodedExten->nssObj();
+               assert(euse != NULL);
+               unsigned numUses = clNssArraySize((const void **)euse->purposes);
+               for(unsigned dex=0; dex<numUses; dex++) {
+               const CSSM_OID *thisUse = euse->purposes[dex];
+                       if(clCompareCssmData(thisUse, &CSSMOID_ExtendedKeyUsageAny)) {
+                               /* we're done */
+                               keyUse = CSSM_KEYUSE_ANY;       
+                               break;
+                       }
+                       else if(clCompareCssmData(thisUse, &CSSMOID_ServerAuth)) {
+                               keyUse |= (CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DERIVE);      
+                       }
+                       else if(clCompareCssmData(thisUse, &CSSMOID_ClientAuth)) {
+                               keyUse |= (CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DERIVE);      
+                       }
+                       else if(clCompareCssmData(thisUse, &CSSMOID_ExtendedUseCodeSigning)) {
+                               keyUse |= CSSM_KEYUSE_VERIFY;   
+                       }
+                       else if(clCompareCssmData(thisUse, &CSSMOID_EmailProtection)) {
+                               keyUse |= 
+                                       (CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP | CSSM_KEYUSE_DERIVE);
+                       }
+                       else if(clCompareCssmData(thisUse, &CSSMOID_TimeStamping)) {
+                               keyUse |= CSSM_KEYUSE_VERIFY;   
+                       }
+                       else if(clCompareCssmData(thisUse, &CSSMOID_OCSPSigning)) {
+                               keyUse |= CSSM_KEYUSE_VERIFY;   
+                       }
+                       else if(clCompareCssmData(thisUse, &CSSMOID_APPLE_EKU_SYSTEM_IDENTITY)) {
+                               /* system identity - fairly liberal: CMS as well as SSL */
+                               keyUse |= 
+                                       (CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP | CSSM_KEYUSE_ENCRYPT);
+                       }
+                       else if(clCompareCssmData(thisUse, &CSSMOID_KERBv5_PKINIT_KP_CLIENT_AUTH)) {
+                               /* 
+                                * Kerberos PKINIT client: 
+                                * -- KDC verifies client signature in a CMS msg in AS-REQ
+                                * -- KDC encrypts for client in a CMS msg in AS-REP
+                                */
+                               keyUse |= (CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP);
+                       }
+                       else if(clCompareCssmData(thisUse, &CSSMOID_KERBv5_PKINIT_KP_KDC)) {
+                               /* 
+                                * Kerberos PKINIT server: 
+                                * -- client verifies KDC signature in a CMS msg in AS-REP
+                                */
+                               keyUse |= CSSM_KEYUSE_VERIFY;
+                       }
+               }
+       }
+       
+       /* NetscapeCertType */
+       decodedExten = DecodedItem::findDecodedExt(CSSMOID_NetscapeCertType, 
+                       false, 0, numFields);
+       if(decodedExten) {
+               /* nssObj() is a CSSM_DATA ptr, whose Data points to the bits we want */
+               CSSM_DATA *nctData = (CSSM_DATA *)decodedExten->nssObj();
+               if((nctData != NULL) && (nctData->Length > 0)) {
+                       CE_NetscapeCertType nct = ((uint16)nctData->Data[0]) << 8;
+                       if(nctData->Length > 1) {
+                               nct |= nctData->Data[1];
+                       }
+               
+                       /* All this usage bits imply signature verify capability */
+                       if(nct & (CE_NCT_SSL_Client | CE_NCT_SSL_Server | CE_NCT_SMIME | CE_NCT_ObjSign |
+                                         CE_NCT_SSL_CA | CE_NCT_SMIME_CA | CE_NCT_ObjSignCA)) {
+                               keyUse |= CSSM_KEYUSE_VERIFY;
+                       }
+               }
+       }
+       if(keyUse == 0) {
+               /* Nothing found; take the default. */
+               keyUse = CSSM_KEYUSE_ANY;
+       }
+       return keyUse;
+}
+
+/*
+ * Obtain a CSSM_KEY from a decoded cert, inferring as much as we can
+ * from required fields (subjectPublicKeyInfo) and extensions (for 
+ * KeyUse).
+ */
+CSSM_KEY_PTR DecodedCert::extractCSSMKey(
+       Allocator               &alloc) const
+{
+       const CSSM_X509_SUBJECT_PUBLIC_KEY_INFO &keyInfo = 
+               mCert.tbs.subjectPublicKeyInfo;
+       return CL_extractCSSMKeyNSS(keyInfo, alloc, this);
+}
+