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