/// \file main.c devCrypt main chacha20 decryption library
/// \author Henrik Herranen, VLSI Solution Oy

/// This device creates a disk that is an image of another this.
/// However, data is being decrypted using the chacha20 descryption
/// algorithm.

#include <vo_stdio.h>
#include <volink.h>     // Linker directives like DLLENTRY
#include <apploader.h>  // RunLibraryFunction etc
#include "decryption.h"
#include "chacha20.h"
#include <string.h>
#include <timers.h>
#include <iMem.h>
#include <kernel.h>

struct Context {
	u_int16 inUse;
	struct CryptContext ctx;
};

DEVICE myCryptedDev;

DEVICE *parentDev;

extern FILEOPS myCryptedFileOps;

#define NUMBER_OF_CONTEXTS 4
struct Context ctxArr[NUMBER_OF_CONTEXTS];


#define DEFAULT_INPUT_DEVICE 'D'
#define DEFAULT_OUTPUT_DEVICE 'E'
#define DEFAULT_HEXKEY_FILE_NAME "S:KEY.TXT"
char inOutDev[2] = {DEFAULT_INPUT_DEVICE, DEFAULT_OUTPUT_DEVICE};
char devCryptStr[] = "devCrypt #: to #:";
FILEOPS origFileOps;



const char *DevChacha20Identify(register __i0 void *self, char *buf, u_int16 bufsize) {
  if (buf && bufsize) {
    strncpy(buf, devCryptStr, bufsize-1);
    buf[bufsize-1] = '\0';
  }
  return devCryptStr;
}

ioresult DevChacha20Open(register __i0 VO_FILE *self, const char *name, const char *mode) {
  ioresult ret = origFileOps.Open(self, name, mode);
  self->extraInfo = NULL; /* Invalidate extraInfo */

  if (!ret && self->dev == &myCryptedDev) {
    /* Find free context */
    int i = 0;
    struct CryptContext *ctx = NULL;

    for (i=0; i<NUMBER_OF_CONTEXTS; i++) {
      if (!ctxArr[i].inUse) {
	ctx = &ctxArr[i].ctx;
	ctx->fi = self;
	InitCrypt(ctx, name);
	ctxArr[i].inUse = 1;
	self->extraInfo = &ctxArr[i];
	break;
      }
    }
    if (ctx == NULL) { /* No free context */
      origFileOps.Close(self);
      printf(stderr, "E: No ctx for decrypt\n");
      return S_ERROR;
    }
    self->flags |= (1 << __BIT_APPLICATION_SPECIFIC);
  }
  
  return ret;
}

ioresult DevChacha20Close(register __i0 VO_FILE *self) {
  if (self->extraInfo) ((struct Context *)self->extraInfo)->inUse = 0;
  self->flags &= ~(1 << __BIT_APPLICATION_SPECIFIC);
  return origFileOps.Close(self);
}

IOCTL_RESULT DevChacha20Ioctl(register __i0 VO_FILE *self, s_int16 request, IOCTL_ARGUMENT arg) {
  return origFileOps.Ioctl(self, request, arg);
}

typedef struct DevSdSdHardwareInfo {
  u_int16 flags;
  u_int16 rca;
  u_int32 size;
  u_int32 nextBlock;
} SD_HWINFO;

u_int16 DevChacha20Read(register __i0 VO_FILE *self, void *buf, u_int16 destinationIndex, u_int16 bytes) {
  u_int16 bytesRead;
  int verbose = 0;
  struct DevSdSdHardwareInfo *hwi[2];
  
  hwi[0] = (void *)parentDev->hardwareInfo;
  hwi[1] = (void *)self->dev->hardwareInfo;

  /* Copy current state of device to our own. */
  if (self->dev == &myCryptedDev) memcpy(hwi[1], hwi[0], 6);

  bytesRead = origFileOps.Read(self, buf, destinationIndex, bytes);

  if (self->flags & (1 << __BIT_APPLICATION_SPECIFIC)) {
    Decrypt(buf, destinationIndex, bytesRead,
	    &((struct Context *)self->extraInfo)->ctx, self->pos);
  }

  /* Copy our own device state to default device. */
  if (self->dev == &myCryptedDev) memcpy(hwi[0], hwi[1], 6);

 return bytesRead;
}

u_int16 DevChacha20Write(register __i0 VO_FILE *self, void *buf, u_int16 sourceIndex, u_int16 bytes) {
  return origFileOps.Write(self, buf, sourceIndex, bytes);
}

int ghure;

FILEOPS devChacha20FileOps = {
	DevChacha20Open,
	DevChacha20Close,
	DevChacha20Ioctl, //IOCTL
	DevChacha20Read,
	DevChacha20Write,
};



ioresult init(char *parameters) {
  int nParam, i;
  char *p = parameters;
  int devNo = 0;
  ioresult retCode = S_ERROR;
  char *hexKey = NULL;
  int hexRead = 0;

  nParam = RunProgram("ParamSpl", parameters);
  for (i=0; i<nParam; i++) {
    char firstUpperCase = *p &= ~0x20;
    if (!strcmp(p, "-h")) {
      printf("Usage: devCrypt [inDev [outDev]] hexkey\n"
	     "inDev\tInput device (default '%c')\n"
	     "outDev\tOutput device (default '%c')\n"
	     "hexkey\tDecoding key, format: \"HEXKEY:[x]\" where [x] is 64 "
	     "hex digits\n"
	     "-h\tShow this help\n",
	     DEFAULT_INPUT_DEVICE,
	     DEFAULT_OUTPUT_DEVICE);
      goto finally;
    } else if (strlen(p) == 1 && devNo < 2 &&
	       firstUpperCase >= 'A' && firstUpperCase <= 'Z') {
      inOutDev[devNo++] = firstUpperCase;
    } else if (strlen(p) == 71 && !strncmp(p, "HEXKEY:", 7)) {
      hexKey = p;
    } else {
      printf("Unknown parameter \"%s\", ignoring\n", p);
    }
    p += strlen(p)+1;
  }
  
  if (!hexKey) {
    printf("E: no hex key\n");
    inOutDev[1] = 0;
    goto finally;
  }
  
  for (i=0; i<NUMBER_OF_CONTEXTS; i++) {
    if (CryptoStart(hexKey, &ctxArr[i].ctx) != S_OK) {
      printf("E: bad hex key\n");
      inOutDev[1] = 0;
      goto finally;
    }
  }
  
  if (vo_pdevices[inOutDev[1] - 'A']) {
    printf("E: Output device %c: in use\n", inOutDev[1]);
    inOutDev[1] = 0;
    goto finally;
  }

  if (!vo_pdevices[inOutDev[0] - 'A']) {
    printf("E: No input device %c:\n", inOutDev[0]);
    goto finally;
  }

  vo_pdevices[inOutDev[1] - 'A'] = &myCryptedDev;
  parentDev = vo_pdevices[inOutDev[0] - 'A'];

  memcpy(&myCryptedDev, parentDev, sizeof(DEVICE));
  myCryptedDev.Identify = DevChacha20Identify;
  memcpy(&origFileOps, parentDev->fs->op, sizeof(FILEOPS));
  memcpy(parentDev->fs->op, &devChacha20FileOps, sizeof(FILEOPS));
  /* No writing */
  myCryptedDev.flags &= ~__MASK_WRITABLE;
  devCryptStr[9] = inOutDev[0];
  devCryptStr[15] = inOutDev[1];
  
  retCode = S_OK;

 finally:
  return retCode;
}

ioresult main(char *parameters) {
  //  vo_pdevices[(*parameters|0x20)-'a'] = (void *)&chacha20Device;
}

void fini(void) {
  if (inOutDev[1]) {
    vo_pdevices[inOutDev[1] - 'A'] = NULL;
    memcpy(parentDev->fs->op, &origFileOps, sizeof(FILEOPS));
  }
  memset(ctxArr, 0, sizeof(ctxArr));
}
