#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <openssl/sha.h>
#include <openssl/ec.h>
#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/core_names.h>

#include "embedded_device.h"

#define BUFSIZE 16384

typedef struct {
    uint8_t *firmware_path;
    uint8_t *public_key_path;
} embedded_device_args;

static void syntax_exit() {
    fprintf(stderr, "syntax error!\n");
    exit(EXIT_FAILURE);
}

static EVP_PKEY *read_public_key(uint8_t *public_key_file, EVP_PKEY **key) {
    if(public_key_file == NULL) {
        fprintf(stderr, "public_key_file is a null pointer!\n");
        return NULL;
    }

    FILE *fd = fopen(public_key_file, "rb");
    if(fd == NULL) {
        fprintf(stderr, "failed to open public key file!\n");
        return NULL;
    }

    *key = PEM_read_PUBKEY(fd, key, NULL, NULL);
    fclose(fd);

    return *key;
}

static void hash_firmware(uint8_t *firmware_path, EVP_MD_CTX **ctx) {
    if(firmware_path == NULL) {
        fprintf(stderr, "firmware_path is a null pointer!\n");
        return;
    }

    FILE *fd = fopen(firmware_path, "rb");
    if(fd == NULL) {
        fprintf(stderr, "failed to open firmware!\n");
        goto exit;
    }

    size_t size;
    uint8_t buf[BUFSIZE];
    while((size = fread(buf, 1, BUFSIZE, fd)) != 0) {
        EVP_DigestVerifyUpdate(*ctx, buf, size);
    }
    
    exit: fclose(fd);
}

static void read_signature(uint8_t *signature, size_t *signature_size) {
    FILE *fd = stdin;
    if(fd == NULL) {
        fprintf(stderr, "failed to stdin!\n");
    }

    // TODO: ersmal ne pause :)

}

int main(int argc, char **argv) {
    embedded_device_args args = {
        .firmware_path = NULL,
        .public_key_path = NULL
    };

    if(argc == 1) {
        syntax_exit();
    }

    for(int i = 1; i < argc; i += 2) {
        if((strcmp(argv[i], "-pub") == 0) && (argc - i >= 2)) {
            args.public_key_path = argv[i+1];
        } else if((strcmp(argv[i], "-firm") == 0) && (argc - i >= 2)) {
            args.firmware_path = argv[i+1];
        } else {
            syntax_exit();
        }
    }

    if((args.firmware_path == NULL) || (args.public_key_path == NULL)) {
        fprintf(stderr, "failed to parse arguments");
        exit(EXIT_FAILURE);
    }

    EVP_PKEY *key = NULL;
    if(read_public_key(args.public_key_path, &key) == NULL) {
        fprintf(stderr, "failed to import public key");
        goto clean;
    }

    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
    if (EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, key) != 1) {
        fprintf(stderr, "failed to initialize context\n");
        goto clean;
    }

    read_signature(NULL, NULL);
    goto clean;

    hash_firmware(args.firmware_path, &ctx);
    if (EVP_DigestVerifyFinal(ctx, NULL, 0) != 1) {
        printf("failed to verify firmware signature\n");
        goto clean;
    }    

    clean: ;
    if(key != NULL)
        EVP_PKEY_free(key);
    if(ctx != NULL)
        EVP_MD_CTX_free(ctx);

    return 0;
}