| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| /* |
| * pkix_signaturechecker.c |
| * |
| * Functions for signature validation |
| * |
| */ |
| |
| #include "pkix_signaturechecker.h" |
| |
| /* |
| * FUNCTION: pkix_SignatureCheckerstate_Destroy |
| * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h) |
| */ |
| static PKIX_Error * |
| pkix_SignatureCheckerState_Destroy( |
| PKIX_PL_Object *object, |
| void *plContext) |
| { |
| pkix_SignatureCheckerState *state = NULL; |
| |
| PKIX_ENTER(SIGNATURECHECKERSTATE, |
| "pkix_SignatureCheckerState_Destroy"); |
| PKIX_NULLCHECK_ONE(object); |
| |
| /* Check that this object is a signature checker state */ |
| PKIX_CHECK(pkix_CheckType |
| (object, PKIX_SIGNATURECHECKERSTATE_TYPE, plContext), |
| PKIX_OBJECTNOTSIGNATURECHECKERSTATE); |
| |
| state = (pkix_SignatureCheckerState *) object; |
| |
| state->prevCertCertSign = PKIX_FALSE; |
| |
| PKIX_DECREF(state->prevPublicKey); |
| PKIX_DECREF(state->prevPublicKeyList); |
| PKIX_DECREF(state->keyUsageOID); |
| |
| cleanup: |
| |
| PKIX_RETURN(SIGNATURECHECKERSTATE); |
| } |
| |
| /* |
| * FUNCTION: pkix_SignatureCheckerState_RegisterSelf |
| * |
| * DESCRIPTION: |
| * Registers PKIX_SIGNATURECHECKERSTATE_TYPE and its related functions |
| * with systemClasses[] |
| * |
| * THREAD SAFETY: |
| * Not Thread Safe (see Thread Safety Definitions in Programmer's Guide) |
| * |
| * Since this function is only called by PKIX_PL_Initialize, which should |
| * only be called once, it is acceptable that this function is not |
| * thread-safe. |
| */ |
| PKIX_Error * |
| pkix_SignatureCheckerState_RegisterSelf(void *plContext) |
| { |
| extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES]; |
| pkix_ClassTable_Entry entry; |
| |
| PKIX_ENTER(SIGNATURECHECKERSTATE, |
| "pkix_SignatureCheckerState_RegisterSelf"); |
| |
| entry.description = "SignatureCheckerState"; |
| entry.objCounter = 0; |
| entry.typeObjectSize = sizeof(pkix_SignatureCheckerState); |
| entry.destructor = pkix_SignatureCheckerState_Destroy; |
| entry.equalsFunction = NULL; |
| entry.hashcodeFunction = NULL; |
| entry.toStringFunction = NULL; |
| entry.comparator = NULL; |
| entry.duplicateFunction = NULL; |
| |
| systemClasses[PKIX_SIGNATURECHECKERSTATE_TYPE] = entry; |
| |
| PKIX_RETURN(SIGNATURECHECKERSTATE); |
| } |
| |
| /* |
| * FUNCTION: pkix_SignatureCheckerState_Create |
| * |
| * DESCRIPTION: |
| * Allocate and initialize SignatureChecker state data. |
| * |
| * PARAMETERS |
| * "trustedPubKey" |
| * Address of trusted Anchor Public Key for verifying first Cert in the |
| * chain. Must be non-NULL. |
| * "certsRemaining" |
| * Number of certificates remaining in the chain. |
| * "pCheckerState" |
| * Address where SignatureCheckerState will be stored. Must be non-NULL. |
| * "plContext" |
| * Platform-specific context pointer. |
| * |
| * THREAD SAFETY: |
| * Not Thread Safe (see Thread Safety Definitions in Programmer's Guide) |
| * |
| * RETURNS: |
| * Returns NULL if the function succeeds. |
| * Returns a SignatureCheckerState Error if the function fails in a |
| * non-fatal way. |
| * Returns a Fatal Error if the function fails in an unrecoverable way. |
| */ |
| static PKIX_Error * |
| pkix_SignatureCheckerState_Create( |
| PKIX_PL_PublicKey *trustedPubKey, |
| PKIX_UInt32 certsRemaining, |
| pkix_SignatureCheckerState **pCheckerState, |
| void *plContext) |
| { |
| pkix_SignatureCheckerState *state = NULL; |
| PKIX_PL_OID *keyUsageOID = NULL; |
| |
| PKIX_ENTER(SIGNATURECHECKERSTATE, "pkix_SignatureCheckerState_Create"); |
| PKIX_NULLCHECK_TWO(trustedPubKey, pCheckerState); |
| |
| PKIX_CHECK(PKIX_PL_Object_Alloc |
| (PKIX_SIGNATURECHECKERSTATE_TYPE, |
| sizeof (pkix_SignatureCheckerState), |
| (PKIX_PL_Object **)&state, |
| plContext), |
| PKIX_COULDNOTCREATESIGNATURECHECKERSTATEOBJECT); |
| |
| /* Initialize fields */ |
| |
| state->prevCertCertSign = PKIX_TRUE; |
| state->prevPublicKeyList = NULL; |
| state->certsRemaining = certsRemaining; |
| |
| PKIX_INCREF(trustedPubKey); |
| state->prevPublicKey = trustedPubKey; |
| |
| PKIX_CHECK(PKIX_PL_OID_Create |
| (PKIX_CERTKEYUSAGE_OID, |
| &keyUsageOID, |
| plContext), |
| PKIX_OIDCREATEFAILED); |
| |
| state->keyUsageOID = keyUsageOID; |
| keyUsageOID = NULL; |
| |
| *pCheckerState = state; |
| state = NULL; |
| |
| cleanup: |
| |
| PKIX_DECREF(keyUsageOID); |
| PKIX_DECREF(state); |
| |
| PKIX_RETURN(SIGNATURECHECKERSTATE); |
| } |
| |
| /* --Private-Functions-------------------------------------------- */ |
| |
| /* |
| * FUNCTION: pkix_SignatureChecker_Check |
| * (see comments for PKIX_CertChainChecker_CheckCallback in pkix_checker.h) |
| */ |
| PKIX_Error * |
| pkix_SignatureChecker_Check( |
| PKIX_CertChainChecker *checker, |
| PKIX_PL_Cert *cert, |
| PKIX_List *unresolvedCriticalExtensions, |
| void **pNBIOContext, |
| void *plContext) |
| { |
| pkix_SignatureCheckerState *state = NULL; |
| PKIX_PL_PublicKey *prevPubKey = NULL; |
| PKIX_PL_PublicKey *currPubKey = NULL; |
| PKIX_PL_PublicKey *newPubKey = NULL; |
| PKIX_PL_PublicKey *pKey = NULL; |
| PKIX_PL_CertBasicConstraints *basicConstraints = NULL; |
| PKIX_Error *checkKeyUsageFail = NULL; |
| PKIX_Error *verifyFail = NULL; |
| PKIX_Boolean certVerified = PKIX_FALSE; |
| |
| PKIX_ENTER(CERTCHAINCHECKER, "pkix_SignatureChecker_Check"); |
| PKIX_NULLCHECK_THREE(checker, cert, pNBIOContext); |
| |
| *pNBIOContext = NULL; /* we never block on pending I/O */ |
| |
| PKIX_CHECK(PKIX_CertChainChecker_GetCertChainCheckerState |
| (checker, (PKIX_PL_Object **)&state, plContext), |
| PKIX_CERTCHAINCHECKERGETCERTCHAINCHECKERSTATEFAILED); |
| |
| (state->certsRemaining)--; |
| |
| PKIX_INCREF(state->prevPublicKey); |
| prevPubKey = state->prevPublicKey; |
| |
| /* |
| * Previous Cert doesn't have CertSign bit on for signature |
| * verification and it is not a self-issued Cert so there is no |
| * old key saved. This is considered error. |
| */ |
| if (state->prevCertCertSign == PKIX_FALSE && |
| state->prevPublicKeyList == NULL) { |
| PKIX_ERROR(PKIX_KEYUSAGEKEYCERTSIGNBITNOTON); |
| } |
| |
| /* Previous Cert is valid for signature verification, try it first */ |
| if (state->prevCertCertSign == PKIX_TRUE) { |
| verifyFail = PKIX_PL_Cert_VerifySignature |
| (cert, prevPubKey, plContext); |
| if (verifyFail == NULL) { |
| certVerified = PKIX_TRUE; |
| } else { |
| certVerified = PKIX_FALSE; |
| } |
| } |
| |
| #ifdef NIST_TEST_4_5_4_AND_4_5_6 |
| |
| /* |
| * Following codes under this compiler flag is implemented for |
| * special cases of NIST tests 4.5.4 and 4.5.6. We are not sure |
| * we should handle these two tests as what is implemented so the |
| * codes are commented out, and the tests fails (for now). |
| * For Cert chain validation, our assumption is all the Certs on |
| * the chain are using its previous Cert's public key to decode |
| * its current key. But for thses two tests, keys are used not |
| * in this precedent order, we can either |
| * 1) Use what is implemented here: take in what Cert order NIST |
| * specified and for continuous self-issued Certs, stacking up |
| * their keys and tries all of them in FILO order. |
| * But this method breaks the idea of chain key presdency. |
| * 2) Use Build Chain facility: we will specify the valid Certs |
| * order (means key precedency is kept) and count on Build Chain |
| * to get the Certs that can fill for the needed keys. This may have |
| * performance impact. |
| * 3) Fetch Certs from CertStore: we will specifiy the valid Certs |
| * order and use CertSelector on SubjectName to get a list of |
| * candidates Certs to fill in for the needed keys. |
| * Anyhow, the codes are kept around just in case we want to use |
| * solution one... |
| */ |
| |
| /* If failed and previous key is self-issued, try its old key(s) */ |
| if (certVerified == PKIX_FALSE && state->prevPublicKeyList != NULL) { |
| |
| /* Verify from keys on the list */ |
| PKIX_CHECK(PKIX_List_GetLength |
| (state->prevPublicKeyList, &numKeys, plContext), |
| PKIX_LISTGETLENGTHFAILED); |
| |
| for (i = numKeys - 1; i >= 0; i--) { |
| |
| PKIX_CHECK(PKIX_List_GetItem |
| (state->prevPublicKeyList, |
| i, |
| (PKIX_PL_Object **) &pKey, |
| plContext), |
| PKIX_LISTGETITEMFAILED); |
| |
| PKIX_DECREF(verifyFail); |
| verifyFail = PKIX_PL_Cert_VerifySignature |
| (cert, pKey, plContext); |
| |
| if (verifyFail == NULL) { |
| certVerified = PKIX_TRUE; |
| break; |
| } else { |
| certVerified = PKIX_FALSE; |
| } |
| |
| PKIX_DECREF(pKey); |
| } |
| } |
| #endif |
| |
| if (certVerified == PKIX_FALSE) { |
| pkixErrorResult = verifyFail; |
| verifyFail = NULL; |
| PKIX_ERROR(PKIX_VALIDATIONFAILEDCERTSIGNATURECHECKING); |
| } |
| |
| #ifdef NIST_TEST_4_5_4_AND_4_5_6 |
| /* |
| * Check if Cert is self-issued. If so, the old key(s) is saved, in |
| * conjunction to the new key, for verifying CERT validity later. |
| */ |
| PKIX_CHECK(pkix_IsCertSelfIssued(cert, &selfIssued, plContext), |
| PKIX_ISCERTSELFISSUEFAILED); |
| |
| /* |
| * Check if Cert is self-issued. If so, the public key of the Cert |
| * that issues this Cert (old key) can be used together with this |
| * current key (new key) for key verification. If there are multiple |
| * self-issued certs, keys of those Certs (old keys) can also be used |
| * for key verification. Old key(s) is saved in a list (PrevPublickKey- |
| * List) and cleared when a Cert is no longer self-issued. PrevPublic- |
| * Key keep key of the previous Cert. |
| */ |
| if (selfIssued == PKIX_TRUE) { |
| |
| /* Make sure previous Cert is valid for signature verification */ |
| if (state->prevCertCertSign == PKIX_TRUE) { |
| |
| if (state->prevPublicKeyList == NULL) { |
| |
| PKIX_CHECK(PKIX_List_Create |
| (&state->prevPublicKeyList, plContext), |
| PKIX_LISTCREATEFALIED); |
| |
| } |
| |
| PKIX_CHECK(PKIX_List_AppendItem |
| (state->prevPublicKeyList, |
| (PKIX_PL_Object *) state->prevPublicKey, |
| plContext), |
| PKIX_LISTAPPENDITEMFAILED); |
| } |
| |
| } else { |
| /* Not self-issued Cert any more, clear old key(s) saved */ |
| PKIX_DECREF(state->prevPublicKeyList); |
| } |
| #endif |
| |
| /* Save current key as prevPublicKey */ |
| PKIX_CHECK(PKIX_PL_Cert_GetSubjectPublicKey |
| (cert, &currPubKey, plContext), |
| PKIX_CERTGETSUBJECTPUBLICKEYFAILED); |
| |
| PKIX_CHECK(PKIX_PL_PublicKey_MakeInheritedDSAPublicKey |
| (currPubKey, prevPubKey, &newPubKey, plContext), |
| PKIX_PUBLICKEYMAKEINHERITEDDSAPUBLICKEYFAILED); |
| |
| if (newPubKey == NULL){ |
| PKIX_INCREF(currPubKey); |
| newPubKey = currPubKey; |
| } |
| |
| PKIX_INCREF(newPubKey); |
| PKIX_DECREF(state->prevPublicKey); |
| |
| state->prevPublicKey = newPubKey; |
| |
| /* Save this Cert key usage CertSign bit */ |
| if (state->certsRemaining != 0) { |
| checkKeyUsageFail = PKIX_PL_Cert_VerifyKeyUsage |
| (cert, PKIX_KEY_CERT_SIGN, plContext); |
| |
| state->prevCertCertSign = (checkKeyUsageFail == NULL)? |
| PKIX_TRUE:PKIX_FALSE; |
| |
| PKIX_DECREF(checkKeyUsageFail); |
| } |
| |
| /* Remove Key Usage Extension OID from list */ |
| if (unresolvedCriticalExtensions != NULL) { |
| |
| PKIX_CHECK(pkix_List_Remove |
| (unresolvedCriticalExtensions, |
| (PKIX_PL_Object *) state->keyUsageOID, |
| plContext), |
| PKIX_LISTREMOVEFAILED); |
| } |
| |
| PKIX_CHECK(PKIX_CertChainChecker_SetCertChainCheckerState |
| (checker, (PKIX_PL_Object *)state, plContext), |
| PKIX_CERTCHAINCHECKERSETCERTCHAINCHECKERSTATEFAILED); |
| |
| cleanup: |
| |
| PKIX_DECREF(state); |
| PKIX_DECREF(pKey); |
| PKIX_DECREF(prevPubKey); |
| PKIX_DECREF(currPubKey); |
| PKIX_DECREF(newPubKey); |
| PKIX_DECREF(basicConstraints); |
| PKIX_DECREF(verifyFail); |
| PKIX_DECREF(checkKeyUsageFail); |
| |
| PKIX_RETURN(CERTCHAINCHECKER); |
| |
| } |
| |
| /* |
| * FUNCTION: pkix_SignatureChecker_Initialize |
| * DESCRIPTION: |
| * |
| * Creates a new CertChainChecker and stores it at "pChecker", where it will |
| * be used by pkix_SignatureChecker_Check to check that the public key in |
| * the checker's state is able to successfully validate the certificate's |
| * signature. The PublicKey pointed to by "trustedPubKey" is used to |
| * initialize the checker's state. |
| * |
| * PARAMETERS: |
| * "trustedPubKey" |
| * Address of PublicKey representing the trusted public key used to |
| * initialize the state of this checker. Must be non-NULL. |
| * "certsRemaining" |
| * Number of certificates remaining in the chain. |
| * "pChecker" |
| * Address where object pointer will be stored. Must be non-NULL. |
| * "plContext" |
| * Platform-specific context pointer. |
| * THREAD SAFETY: |
| * Thread Safe (see Thread Safety Definitions in Programmer's Guide) |
| * RETURNS: |
| * Returns NULL if the function succeeds. |
| * Returns a CertChainChecker Error if the function fails in a non-fatal way. |
| * Returns a Fatal Error if the function fails in an unrecoverable way. |
| */ |
| PKIX_Error * |
| pkix_SignatureChecker_Initialize( |
| PKIX_PL_PublicKey *trustedPubKey, |
| PKIX_UInt32 certsRemaining, |
| PKIX_CertChainChecker **pChecker, |
| void *plContext) |
| { |
| pkix_SignatureCheckerState* state = NULL; |
| PKIX_ENTER(CERTCHAINCHECKER, "PKIX_SignatureChecker_Initialize"); |
| PKIX_NULLCHECK_TWO(pChecker, trustedPubKey); |
| |
| PKIX_CHECK(pkix_SignatureCheckerState_Create |
| (trustedPubKey, certsRemaining, &state, plContext), |
| PKIX_SIGNATURECHECKERSTATECREATEFAILED); |
| |
| PKIX_CHECK(PKIX_CertChainChecker_Create |
| (pkix_SignatureChecker_Check, |
| PKIX_FALSE, |
| PKIX_FALSE, |
| NULL, |
| (PKIX_PL_Object *) state, |
| pChecker, |
| plContext), |
| PKIX_CERTCHAINCHECKERCREATEFAILED); |
| |
| cleanup: |
| |
| PKIX_DECREF(state); |
| |
| PKIX_RETURN(CERTCHAINCHECKER); |
| |
| } |