#include <errno.h>
#include <sgx_urts.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "employee.h"
#include "util.h"

#include <openssl/core_names.h>
#include <openssl/evp.h>
#include <openssl/pem.h>

#define HASH_BYTES 32
#define HASH_CHUNK_BYTES 32
#define KEY_BYTES 32

struct EmployeeArgs {
    char* key_path;
    char* firmware_path;
};

char* employee_syntax(void) {
    return
        "employee           mock up implementation of the employee binary\n"
        "                   outputs signature on stdout\n"
        "                   WARNING: output is in binary format, may mess up terminal\n"
        "    -ekey <path>   file path of the PEM encoded private key of the employee\n"
        "    -firm <path>   path of the firmware\n";
}

int handle_employee(int argc, char** argv) {
    struct EmployeeArgs args = {
        NULL,
        NULL
    };
    FILE* key_file = NULL;
    uint8_t* firmware_buf;
    size_t firmware_len;
    EVP_PKEY* key = NULL;
    EVP_MD_CTX *mdctx = NULL;
    size_t sig_len;
    unsigned char* sig = NULL;

    /*
     * Parse Input
     */

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

    if(args.key_path == NULL)
        syntax_exit();

    /*
     * Load Signing Key
     */

    key_file = fopen(args.key_path, "rb");
    if(key_file == NULL){
        perror("Error opening key file");
        exit (EXIT_FAILURE);
    }
    key = PEM_read_PrivateKey(key_file, &key, NULL, NULL);
    if(key == NULL) {
        fprintf(stderr, "failed to read key\n");
        exit (EXIT_FAILURE);
    }

    fclose(key_file);

    /*
     * Sign Firmware
     */


    mdctx = EVP_MD_CTX_new();
    if (EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, key) != 1) {
        fprintf(stderr, "Message digest initialization failed.\n");
        exit (EXIT_FAILURE);
    }

    if (load_file(args.firmware_path, &firmware_buf, &firmware_len) != 0) {
        fprintf(stderr, "failed to read firmware\n");
        exit (EXIT_FAILURE);
    }

    if (EVP_DigestSignUpdate(mdctx, firmware_buf, firmware_len) != 1) {
        printf("Message digest update failed.\n");
        exit(EXIT_FAILURE);
    }
    free(firmware_buf);

    // call with empty sig to get length
    if (EVP_DigestSignFinal(mdctx, NULL, &sig_len) != 1) {
        printf("Message digest finalization failed.\n");
        exit (EXIT_FAILURE);
    }

    // allocate signature buffer
    sig = malloc(sizeof(unsigned char) * sig_len);
    if(sig == NULL){
        perror("could not initialize digest buffer");
        exit (EXIT_FAILURE);
    }

    // load signature into buffer
    if (EVP_DigestSignFinal(mdctx, sig, &sig_len) != 1) {
        printf("Message digest finalization failed.\n");
        exit (EXIT_FAILURE);
    }

    fwrite(sig, sig_len, 1, stdout);
    if (ferror(stdout) != 0) {
        fprintf(stdout, "failed to write signature to stdout\n");
        exit (EXIT_FAILURE);
    }

    fflush(stdout);

    // free all allocated resources
    free(sig);
    EVP_MD_CTX_free(mdctx);
    EVP_PKEY_free(key);
    exit (EXIT_SUCCESS);
}
