// pub-verify -- verify a chain of cm/pub keys
// Copyright (C) 2024-2025 Sergey Matveev <stargrave@stargrave.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <keks/cm/pub.h>
#include <keks/err.h>

#include "../lib/hex.h"
#include "../lib/mmap.h"
#include "../lib/uuid.h"

#include "verifier-ed25519-blake2b.h"
// #include "verifier-gost3410.h"

int
main(int argc, char **argv)
{
    if (argc < 3) {
        fprintf(stderr, "Usage: %s EE.pub [CA.pub ...]\n", argv[0]);
        return EXIT_FAILURE;
    }
    const size_t cersLen = (size_t)argc - 1;
    struct KEKSCMPub *cers = calloc(cersLen, sizeof(struct KEKSCMPub));
    assert(cers != NULL);
    char *failReason = NULL;
    for (int i = 1; i < argc; i++) {
        unsigned char *buf = NULL;
        size_t len = 0;
        if (!Mmap(&buf, &len, argv[i])) {
            fprintf(stderr, "%s: %s\n", argv[i], strerror(errno));
            exit(EXIT_FAILURE);
        }
        fputs(argv[i], stdout);
        fputs(":\t", stdout);
        size_t off = 0;
        enum KEKSErr err = KEKSCMPubParse(&(cers[i - 1]), &off, &failReason, buf, len);
        if (err != KEKSErrNo) {
            fprintf(stderr, "%s: %s\n", argv[i], failReason);
            return EXIT_FAILURE;
        }
        fputs("cid:", stdout);
        UUIDPrint(cers[i - 1].cid);
        {
            fputs(" pubId:", stdout);
            char *hex = HexEnc(cers[i - 1].pubId, 32);
            fputs(hex, stdout);
            free(hex);
        }
        {
            fputs(" sid:", stdout);
            char *hex = HexEnc(cers[i - 1].sid, 32);
            fputs(hex, stdout);
            free(hex);
        }
        fputs("\n", stdout);
    }

    struct timespec now;
    errno = 0;
    if (clock_gettime(CLOCK_REALTIME, &now) != 0) {
        fprintf(stderr, "%s\n", strerror(errno));
        return EXIT_FAILURE;
    }

    struct KEKSCMPubSigVerifier sigVerifiers[] = {
        {.algo = "ed25519-blake2b", .func = ed25519blake2bSignatureVerifier},
        // {.algo = "gost3410-256A", .func = gost3410SignatureVerifier},
        // {.algo = "gost3410-512C", .func = gost3410SignatureVerifier},
        {.algo = NULL},
    };
    struct KEKSCMPubVerifyOpts opts = {.t = now, .sigVerifiers = sigVerifiers};

    struct KEKSCMPub *toVerify = &(cers[0]);
    struct KEKSCMPub *verifier = NULL;
    for (;;) {
        fputs("verifying ", stdout);
        UUIDPrint(toVerify->cid);
        fputs(": ", stdout);
        if (!KEKSCMPubVerify(&failReason, &verifier, toVerify, cers, cersLen, opts)) {
            fputs(failReason, stdout);
            fputs("\n", stdout);
            return EXIT_FAILURE;
        }
        if (memcmp(toVerify->sid, toVerify->pubId, 32) == 0) {
            fputs("ok\n", stdout);
            break;
        }
        fputs("ok\n", stdout);
        toVerify = verifier;
    }
    return EXIT_SUCCESS;
}
