/*
 * FUNCTION: pkix_pl_CRL_ToString_Helper
 * DESCRIPTION:
 *
 *  Helper function that creates a string representation of the CRL pointed
 *  to by "crl" and stores it at "pString".
 *
 * PARAMETERS
 *  "crl"
 *      Address of CRL whose string representation is desired.
 *      Must be non-NULL.
 *  "pString"
 *      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 CRL 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_pl_CRL_ToString_Helper(
        PKIX_PL_CRL *crl,
        PKIX_PL_String **pString,
        void *plContext)
{
        char *asciiFormat = NULL;
        PKIX_UInt32 crlVersion;
        PKIX_PL_X500Name *crlIssuer = NULL;
        PKIX_PL_OID *nssSignatureAlgId = NULL;
        PKIX_PL_BigInt *crlNumber = NULL;
        PKIX_List *crlEntryList = NULL;
        PKIX_List *critExtOIDs = NULL;
        PKIX_PL_String *formatString = NULL;
        PKIX_PL_String *crlIssuerString = NULL;
        PKIX_PL_String *lastUpdateString = NULL;
        PKIX_PL_String *nextUpdateString = NULL;
        PKIX_PL_String *nssSignatureAlgIdString = NULL;
        PKIX_PL_String *crlNumberString = NULL;
        PKIX_PL_String *crlEntryListString = NULL;
        PKIX_PL_String *critExtOIDsString = NULL;
        PKIX_PL_String *crlString = NULL;

        PKIX_ENTER(CRL, "pkix_pl_CRL_ToString_Helper");
        PKIX_NULLCHECK_THREE(crl, crl->nssSignedCrl, pString);

        asciiFormat =
                "[\n"
                "\tVersion:         v%d\n"
                "\tIssuer:          %s\n"
                "\tUpdate:   [Last: %s\n"
                "\t           Next: %s]\n"
                "\tSignatureAlgId:  %s\n"
                "\tCRL Number     : %s\n"
                "\n"
                "\tEntry List:      %s\n"
                "\n"
                "\tCritExtOIDs:     %s\n"
                "]\n";

        PKIX_CHECK(PKIX_PL_String_Create
                    (PKIX_ESCASCII,
                    asciiFormat,
                    0,
                    &formatString,
                    plContext),
                    PKIX_STRINGCREATEFAILED);

        /* Version */
        PKIX_CHECK(pkix_pl_CRL_GetVersion(crl, &crlVersion, plContext),
                    PKIX_CRLGETVERSIONFAILED);

        /* Issuer */
        PKIX_CHECK(PKIX_PL_CRL_GetIssuer(crl, &crlIssuer, plContext),
                    PKIX_CRLGETISSUERFAILED);

        PKIX_CHECK(PKIX_PL_Object_ToString
                    ((PKIX_PL_Object *)crlIssuer, &crlIssuerString, plContext),
                    PKIX_X500NAMETOSTRINGFAILED);

        /* This update - No Date object created, use nss data directly */
        PKIX_CHECK(pkix_pl_Date_ToString_Helper
                    (&(crl->nssSignedCrl->crl.lastUpdate),
                    &lastUpdateString,
                    plContext),
                    PKIX_DATETOSTRINGHELPERFAILED);

        /* Next update - No Date object created, use nss data directly */
        PKIX_CHECK(pkix_pl_Date_ToString_Helper
                    (&(crl->nssSignedCrl->crl.nextUpdate),
                    &nextUpdateString,
                    plContext),
                    PKIX_DATETOSTRINGHELPERFAILED);

        /* Signature Algorithm Id */
        PKIX_CHECK(pkix_pl_CRL_GetSignatureAlgId
                    (crl, &nssSignatureAlgId, plContext),
                    PKIX_CRLGETSIGNATUREALGIDFAILED);

        PKIX_CHECK(PKIX_PL_Object_ToString
                    ((PKIX_PL_Object *)nssSignatureAlgId,
                    &nssSignatureAlgIdString,
                    plContext),
                    PKIX_OIDTOSTRINGFAILED);

        /* CRL Number */
        PKIX_CHECK(PKIX_PL_CRL_GetCRLNumber
                    (crl, &crlNumber, plContext),
                    PKIX_CRLGETCRLNUMBERFAILED);

        PKIX_TOSTRING(crlNumber, &crlNumberString, plContext,
                    PKIX_BIGINTTOSTRINGFAILED);

        /* CRL Entries */
        PKIX_CHECK(pkix_pl_CRL_GetCRLEntries(crl, &crlEntryList, plContext),
                    PKIX_CRLGETCRLENTRIESFAILED);

        PKIX_TOSTRING(crlEntryList, &crlEntryListString, plContext,
                    PKIX_LISTTOSTRINGFAILED);

        /* CriticalExtensionOIDs */
        PKIX_CHECK(PKIX_PL_CRL_GetCriticalExtensionOIDs
                    (crl, &critExtOIDs, plContext),
                    PKIX_CRLGETCRITICALEXTENSIONOIDSFAILED);

        PKIX_TOSTRING(critExtOIDs, &critExtOIDsString, plContext,
                    PKIX_LISTTOSTRINGFAILED);

        PKIX_CHECK(PKIX_PL_Sprintf
                    (&crlString,
                    plContext,
                    formatString,
                    crlVersion + 1,
                    crlIssuerString,
                    lastUpdateString,
                    nextUpdateString,
                    nssSignatureAlgIdString,
                    crlNumberString,
                    crlEntryListString,
                    critExtOIDsString),
                    PKIX_SPRINTFFAILED);

        *pString = crlString;

cleanup:

        PKIX_DECREF(crlIssuer);
        PKIX_DECREF(nssSignatureAlgId);
        PKIX_DECREF(crlNumber);
        PKIX_DECREF(crlEntryList);
        PKIX_DECREF(critExtOIDs);
        PKIX_DECREF(crlIssuerString);
        PKIX_DECREF(lastUpdateString);
        PKIX_DECREF(nextUpdateString);
        PKIX_DECREF(nssSignatureAlgIdString);
        PKIX_DECREF(crlNumberString);
        PKIX_DECREF(crlEntryListString);
        PKIX_DECREF(critExtOIDsString);
        PKIX_DECREF(formatString);

        PKIX_RETURN(CRL);
}
/*
 * FUNCTION: pkix_DefaultCRLChecker_CheckCRLs
 *
 * DESCRIPTION:
 *  Check validity of "cert" based on CRLs at "crlList" that has correct
 *  signature verification with "publicKey".
 *
 * PARAMETERS
 *  "cert"
 *      Address of Cert which has the certificate data. Must be non-NULL.
 *  "certIssuer"
 *      Address of Issuer that associates with the Cert. Must be non-NULL.
 *  "certSerialNumber"
 *      Address of Serial Number that associates with the Cert. Must be 
 *      non-NULL.
 *  "publicKey"
 *      Address of Public Key that associates with the Cert Issuer.
 *      Must be non-NULL.
 *  "crlList"
 *      A List CRLs that the certificate is verified upon. Must be non-NULL.
 *  "state"
 *      Address of DefaultCRLCheckerState which keeps dynamic state data.
 *      Must be non-NULL.
 *  "pCrlEntryList"
 *      Address of PKIX_PL_CrlEntry List that contains valid CrlEntries for
 *      this Cert. May be NULL.
 *  "plContext"
 *      Platform-specific context pointer.
 *
 * THREAD SAFETY:
 *  Conditionally 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
 */
static PKIX_Error *
pkix_DefaultCRLChecker_CheckCRLs(
        PKIX_PL_Cert *cert,
        PKIX_PL_X500Name *certIssuer,
        PKIX_PL_BigInt *certSerialNumber,
        PKIX_PL_PublicKey *publicKey,
        PKIX_List *crlList,
        pkix_DefaultCRLCheckerState *state,
        PKIX_List **pCrlEntryList,
        void *plContext)
{
        PKIX_PL_CRL *crl = NULL;
        PKIX_PL_CRLEntry *crlEntry = NULL;
        PKIX_PL_PublicKey *pKey = NULL;
        PKIX_List *unresCrlCritExtOIDs = NULL;
        PKIX_List *unresCrlEntryCritExtOIDs = NULL;
        PKIX_List *crlEntryList = NULL;
        PKIX_Error *verifyFail = NULL;
        PKIX_UInt32 numCrls = 0;
        PKIX_UInt32 numKeys = 0;
        PKIX_UInt32 numCritExtOIDs = 0;
        PKIX_Boolean crlVerified = PKIX_FALSE;
        PKIX_Boolean crlRevoking = PKIX_FALSE;
        PKIX_Int32 reasonCode = 0;
        PKIX_UInt32 i;
        PKIX_Int32 j;

        PKIX_ENTER(CERTCHAINCHECKER,
                    "pkix_DefaultCRLChecker_CheckCRLs");
        PKIX_NULLCHECK_FOUR(cert, publicKey, crlList, state);

        PKIX_CHECK(PKIX_List_GetLength(crlList, &numCrls, plContext),
                    PKIX_LISTGETLENGTHFAILED);

        if (state->prevPublicKeyList != NULL) {

                PKIX_CHECK(PKIX_List_GetLength
                    (state->prevPublicKeyList, &numKeys, plContext),
                    PKIX_LISTGETLENGTHFAILED);
        }

        /* Check if Cert is not revoked by any the the CRLs */

        for (i = 0; i < numCrls; i++){

                PKIX_CHECK(PKIX_List_GetItem
                            (crlList, i, (PKIX_PL_Object **)&crl, plContext),
                            PKIX_LISTGETITEMFAILED);

                /*
                 * Checking serial number (issuer done in selector) then
                 * verify signature. If matches, get the CRL reason(s).
                 */

                if (state->prevCertCrlSign == PKIX_TRUE) {
                        verifyFail = PKIX_PL_CRL_VerifySignature
                                (crl, publicKey, plContext);
                        if (verifyFail == NULL) {
                                crlVerified = PKIX_TRUE;
                        } else {
                                crlVerified = PKIX_FALSE;
                                PKIX_DECREF(verifyFail);
                        }
                }

                if (crlVerified == PKIX_FALSE) {

                    /* Verify from old key(s) on the list */
                    for (j = numKeys - 1; j >= 0; j--) {

                            PKIX_CHECK(PKIX_List_GetItem
                                (state->prevPublicKeyList,
                                j,
                                (PKIX_PL_Object **) &pKey,
                                plContext),
                                PKIX_LISTGETITEMFAILED);

                            verifyFail = PKIX_PL_CRL_VerifySignature
                                (crl, pKey, plContext);

                            if (verifyFail == NULL) {
                                crlVerified = PKIX_TRUE;
                                break;
                            } else {
                                crlVerified = PKIX_FALSE;
                                PKIX_DECREF(verifyFail);
                            }

                            PKIX_DECREF(pKey);
                    }
                }

                if (crlVerified == PKIX_FALSE) {
                    /* try next one ... */
                    goto cleanup_loop;
                }

                state->certHasValidCrl = PKIX_TRUE;

                PKIX_CHECK(PKIX_PL_CRL_GetCriticalExtensionOIDs
                            (crl, &unresCrlCritExtOIDs, plContext),
                            PKIX_CRLGETCRITICALEXTENSIONOIDSFAILED);

                /*
                 * XXX Advanced CRL work - should put a
                 * Loop here to process and remove critical
                 * extension oids.
                 */

                if (unresCrlCritExtOIDs) {

                    PKIX_CHECK(PKIX_List_GetLength(unresCrlCritExtOIDs,
                        &numCritExtOIDs,
                        plContext),
                        PKIX_LISTGETLENGTHFAILED);

                    if (numCritExtOIDs != 0) {
                        PKIX_DEFAULTCRLCHECKERSTATE_DEBUG
                                (PKIX_CRLCRITICALEXTENSIONOIDSNOTPROCESSED);
                        /*
                         * Uncomment this after we have implemented
                         * checkers for all the critical extensions.
                         *
                         * PKIX_ERROR
                         *      ("Unrecognized CRL Critical Extension");
                         */
                    }
                }

                PKIX_CHECK(PKIX_PL_CRL_GetCRLEntryForSerialNumber
                            (crl, certSerialNumber, &crlEntry, plContext),
                            PKIX_CRLGETCRLENTRYFORSERIALNUMBERFAILED);

                if (crlEntry == NULL) {
                    goto cleanup_loop;
                }

                crlRevoking = PKIX_TRUE;

                PKIX_CHECK(PKIX_PL_CRLEntry_GetCRLEntryReasonCode
                            (crlEntry,
                            &reasonCode,
                            plContext),
                            PKIX_CRLENTRYGETCRLENTRYREASONCODEFAILED);

                /* This is a valid CRLEntry, return it for caching */
                if (crlEntryList == NULL) {
                    PKIX_CHECK(PKIX_List_Create(&crlEntryList, plContext),
                            PKIX_LISTCREATEFAILED);

                }

                PKIX_CHECK(PKIX_List_AppendItem
                        (crlEntryList, (PKIX_PL_Object *) crlEntry, plContext),
                        PKIX_LISTAPPENDITEMFAILED);

                /* Set reason code in state for advance CRL reviewing */

                if (reasonCode >= 0) {
                    if (reasonCode >= numReasonCodes) 
		        reasonCode = 0;

                    state->reasonCodeMask |= 1 << reasonCode;
                    PKIX_DEFAULTCRLCHECKERSTATE_DEBUG_ARG
                        ("CRL revocation Reason: %s\n ",
                        reasonCodeMsgString[reasonCode]);

                } else {
                    PKIX_DEFAULTCRLCHECKERSTATE_DEBUG
                        ("Revoked by Unknown CRL ReasonCode");
                }

                PKIX_CHECK(PKIX_PL_CRLEntry_GetCriticalExtensionOIDs
                            (crlEntry, &unresCrlEntryCritExtOIDs, plContext),
                            PKIX_CRLENTRYGETCRITICALEXTENSIONOIDSFAILED);
                if (unresCrlEntryCritExtOIDs) {

                    PKIX_CHECK(pkix_List_Remove
                            (unresCrlEntryCritExtOIDs,
                            (PKIX_PL_Object *) state->crlReasonCodeOID,
                            plContext),
                            PKIX_LISTREMOVEFAILED);

                    PKIX_CHECK(PKIX_List_GetLength(unresCrlEntryCritExtOIDs,
                        &numCritExtOIDs,
                        plContext),
                        PKIX_LISTGETLENGTHFAILED);

                    if (numCritExtOIDs != 0) {

                        PKIX_DEFAULTCRLCHECKERSTATE_DEBUG
                            (PKIX_CRLENTRYCRITICALEXTENSIONWASNOTPROCESSED);
                        PKIX_ERROR(PKIX_UNRECOGNIZEDCRLENTRYCRITICALEXTENSION);
                    }
                }

        cleanup_loop:

                PKIX_DECREF(pKey);
                PKIX_DECREF(verifyFail);
                PKIX_DECREF(pKey);
                PKIX_DECREF(crlEntry);
                PKIX_DECREF(crl);
                PKIX_DECREF(unresCrlCritExtOIDs);
                PKIX_DECREF(unresCrlEntryCritExtOIDs);
        }

        *pCrlEntryList = crlEntryList;

        if (crlRevoking == PKIX_TRUE) {

                PKIX_ERROR(PKIX_CERTIFICATEREVOKEDBYCRL);
        }

cleanup:

        PKIX_DECREF(pKey);
        PKIX_DECREF(verifyFail);
        PKIX_DECREF(crlEntry);
        PKIX_DECREF(crl);
        PKIX_DECREF(unresCrlCritExtOIDs);
        PKIX_DECREF(unresCrlEntryCritExtOIDs);

        PKIX_RETURN(CERTCHAINCHECKER);
}