diff options
Diffstat (limited to '')
-rw-r--r-- | security/nss/lib/libpkix/pkix/checker/pkix_signaturechecker.c | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/security/nss/lib/libpkix/pkix/checker/pkix_signaturechecker.c b/security/nss/lib/libpkix/pkix/checker/pkix_signaturechecker.c new file mode 100644 index 0000000000..0ed9ffaecc --- /dev/null +++ b/security/nss/lib/libpkix/pkix/checker/pkix_signaturechecker.c @@ -0,0 +1,443 @@ +/* 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); + +} |