#include <string.h>

#include "dbg.h"
#include "basedef.h"
#include "parser_certificate.h"
#include "cmd_common.h"
#include "cmd_crypto.h"
#include "tal_operation.h"


AuthnrResult verifyCertificatePath(const BlobData* pBlobIssuer, const TlvCertificate* pTlvIssuerCertificate, const BlobData* pBlobSubject, const TlvCertificate* pTlvSubjectCertificate)
{
    AuthnrResult ret = AUTHNR_SUCCESS;
    boolean retBool = TRUE;

    BlobData blobTmpPlainData = {0};
    BlobData blobTmpSignature = {0};

    uint8* oid = NULL;
    uint32 offset = 0;

    TlvRSAPublicKey tlvRSAPublicKey = {0};

    TAL_Key issuerPublicKey = {.cryptoType = SIGNATURE_TYPE_NONE};
    uint32 rsaKeySize = 0;

    printD("start");

    CHECK_BLOB_POINTER(pBlobIssuer, AUTHNR_ERROR_PARAM);
    CHECK_BLOB_POINTER(pBlobSubject, AUTHNR_ERROR_PARAM);

    do {
        // Input validation
        if (pTlvIssuerCertificate == NULL ||  pTlvSubjectCertificate == NULL) {
            printE("input validation failed");
            ret = AUTHNR_ERROR_PARAM;
            break;
        }

        // Init output

        // Check the signature on the subject certificate
        oid = pBlobSubject->data + pTlvSubjectCertificate->signatureAlgorithm.offset - 2/* TAG | LENGTH */;
        if (memcmp(oid, g_oid_sha256WithRSAEncryption, sizeof(g_oid_sha256WithRSAEncryption)) == 0) {
            printD("signature algorithm is sha256WithRSAEncryption");
            issuerPublicKey.cryptoType = SIGNATURE_TYPE_RSA_SHA256_PKCS1;
        }
        else if (memcmp(oid, g_oid_sha384WithRSAEncryption, sizeof(g_oid_sha384WithRSAEncryption)) == 0) {
            printD("signature algorithm is sha384WithRSAEncryption");
            issuerPublicKey.cryptoType = SIGNATURE_TYPE_RSA_SHA384_PKCS1;
        } else {
            printE("signature algorithm is not supported");
            ret = AUTHNR_ERROR_PARSING;
            break;
        }


        printD("signature length:%d", pTlvSubjectCertificate->signatureValue.length);
        if (0 != *(pBlobSubject->data + pTlvSubjectCertificate->signatureValue.offset)) {
            printE("invalid unused bits");
            ret = AUTHNR_ERROR_PARSING;
            break;
        }

        // Check the public key on the issuer certificate
        oid = pBlobIssuer->data + pTlvIssuerCertificate->tbsCertificate.subjectPublicKeyInfo.algorithm.offset - 2/* TAG | LENGTH */;
        if (memcmp(oid, g_oid_rsaEncryption, sizeof(g_oid_rsaEncryption))) {
            printE("public key algorithm is not supported");
            ret = AUTHNR_ERROR_PARSING;
            break;
        }

        if (0 != *(pBlobIssuer->data + pTlvIssuerCertificate->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.offset)) {
            printE("invalid unused bits");
            ret = AUTHNR_ERROR_PARSING;
            break;
        }

        retBool = parseRSAPublicKey(pBlobIssuer, pTlvIssuerCertificate->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.offset + 1, &tlvRSAPublicKey);
        if (retBool != TRUE) {
            printE("parsePassRSAPublicKey failed");
            ret = AUTHNR_ERROR_PARSING;
            break;
        }

        rsaKeySize = tlvRSAPublicKey.modulus.length;
        if (rsaKeySize % 2 == 1) {
            offset = 1;
            rsaKeySize = tlvRSAPublicKey.modulus.length - 1;
        }
        printD("rsaKeySize:%d", rsaKeySize);

        retBool = allocKey(rsaKeySize, &issuerPublicKey);
        if (retBool != TRUE) {
            printE("allockKey failed");
            ret = AUTHNR_ERROR_ALLOC_KEY;
            break;
        }

        issuerPublicKey.key.rsa.modulus.length = rsaKeySize;
        memcpy(issuerPublicKey.key.rsa.modulus.value, pBlobIssuer->data + tlvRSAPublicKey.modulus.offset + offset, issuerPublicKey.key.rsa.modulus.length);
        issuerPublicKey.key.rsa.exponent.length = tlvRSAPublicKey.publicExponent.length;
        memcpy(issuerPublicKey.key.rsa.exponent.value, pBlobIssuer->data + tlvRSAPublicKey.publicExponent.offset, issuerPublicKey.key.rsa.exponent.length);

        blobTmpPlainData.data = pBlobSubject->data + pTlvSubjectCertificate->tbsCertificate.offset - 1 - pTlvSubjectCertificate->tbsCertificate.lengthOctets;
        blobTmpPlainData.dataSize = pTlvSubjectCertificate->tbsCertificate.length + 1 + pTlvSubjectCertificate->tbsCertificate.lengthOctets;
        blobTmpPlainData.length = blobTmpPlainData.dataSize;

        offset = 0;
        if (pTlvSubjectCertificate->signatureValue.length == rsaKeySize + 1) {
            offset = 1;
        }
        blobTmpSignature.data = pBlobSubject->data + pTlvSubjectCertificate->signatureValue.offset + offset;
        blobTmpSignature.dataSize = pTlvSubjectCertificate->signatureValue.length - offset;
        blobTmpSignature.length = blobTmpSignature.dataSize;
        retBool = gTal.rsa_verify(&issuerPublicKey, &blobTmpPlainData, &blobTmpSignature);
        if (retBool != TRUE) {
            printE("verify fail");
            ret = AUTHNR_ERROR_VERIFY;
            break;
        }

    } while (0);

    freeKey(&issuerPublicKey);

    printD("end");
    return ret;
}
