/*
  blind: This program should be run by the student to blind the student's ID.
  source: http://www.ter.se/dss
*/

#include <stdio.h>
#include <gnunet/platform.h>
#include <gnunet/gnunet_util_lib.h>
#include "common.c"

/**
 * Declaration of functions
 */
int  checkParams (int nrParams, char* params[], unsigned int* sizeKey);
void showHelp ();

/**
 * Main
 */
int main (int nrParams, char* params[])
{
  // Check parameters to the program.
  unsigned int sizeKey = DEFAULT_KEY_SIZE;
  if (checkParams (nrParams, params, &sizeKey))
  {
    return (1);
  }

  // Read the ID from file.
  char*  fileNameID = params[nrParams - 1]; // The last argument to the program should be the file.
  char*  ID;
  size_t sizeID;
  if (readDataFromFile (fileNameID, &ID, &sizeID))
  {
    return (1);
  }
  printf ("Student's ID: %s", ID);

  // Hash the ID
  struct GNUNET_HashCode hashedID; // Hash of ID
  GNUNET_CRYPTO_hash (ID, sizeID, &hashedID); // Generate a SHA-512 hash of ID

  /**
   * Pass
   */

  // Read the student RSA blinding pass key from file.
  struct GNUNET_CRYPTO_rsa_BlindingKey *blindingPassKeyStudent;
  if (readBlindingKeyFromFile (STUDENT_BLINDING_PASS_KEY, &blindingPassKeyStudent))
  {
    // Unable to open the student RSA blinding pass key file so we generate it instead.
    printf ("Generate the student RSA blinding pass key using a key size of %u bits.\n", sizeKey);
    blindingPassKeyStudent = GNUNET_CRYPTO_rsa_blinding_key_create (sizeKey);
  }

  // Read the DSS RSA public pass key from file.
  struct GNUNET_CRYPTO_rsa_PublicKey *publicPassKeyDSS;
  if (readPublicKeyFromFile (DSS_PUBLIC_PASS_KEY, &publicPassKeyDSS))
  {
    return (1);
  }

  // Blinds the hashed ID by using the student's blinding pass key and the DSS public pass key
  char   *blindedHashedPassID;
  size_t  sizeBlindedHashedPassID;
  sizeBlindedHashedPassID = GNUNET_CRYPTO_rsa_blind (&hashedID, blindingPassKeyStudent, publicPassKeyDSS, &blindedHashedPassID);
  writeDataToFile (STUDENT_BLINDED_HASHED_PASS_ID, &blindedHashedPassID, &sizeBlindedHashedPassID);
  GNUNET_free (blindedHashedPassID); // Free the blinded hashed pass ID
  GNUNET_CRYPTO_rsa_public_key_free (publicPassKeyDSS); // Free the DSS RSA public pass key

  // Write the student RSA blinding pass key to file.
  if (writeBlindingKeyToFile (STUDENT_BLINDING_PASS_KEY, &blindingPassKeyStudent))
  {
    return (1);
  }
  GNUNET_CRYPTO_rsa_blinding_key_free (blindingPassKeyStudent); // Free the student RSA blinding pass key

  /**
   * Fail
   */

  // Read the student RSA blinding fail key from file.
  struct GNUNET_CRYPTO_rsa_BlindingKey *blindingFailKeyStudent;
  if (readBlindingKeyFromFile (STUDENT_BLINDING_FAIL_KEY, &blindingFailKeyStudent))
  {
    // Unable to open the student RSA blinding fail key file so we generate it instead.
    printf ("Generate the student RSA blinding fail key using a key size of %u bits.\n", sizeKey);
    blindingFailKeyStudent = GNUNET_CRYPTO_rsa_blinding_key_create (sizeKey);
  }

  // Read the DSS RSA public fail key from file.
  struct GNUNET_CRYPTO_rsa_PublicKey *publicFailKeyDSS;
  if (readPublicKeyFromFile (DSS_PUBLIC_FAIL_KEY, &publicFailKeyDSS))
  {
    return (1);
  }

  // Blinds the hashed ID by using the student's blinding fail key and the DSS public fail key
  char   *blindedHashedFailID;
  size_t  sizeBlindedHashedFailID;
  sizeBlindedHashedFailID = GNUNET_CRYPTO_rsa_blind (&hashedID, blindingFailKeyStudent, publicFailKeyDSS, &blindedHashedFailID);
  writeDataToFile (STUDENT_BLINDED_HASHED_FAIL_ID, &blindedHashedFailID, &sizeBlindedHashedFailID);
  GNUNET_free (blindedHashedFailID); // Free the blinded hashed fail ID
  GNUNET_CRYPTO_rsa_public_key_free (publicFailKeyDSS); // Free the DSS RSA public fail key

  // Write the student RSA blinding fail key to file.
  if (writeBlindingKeyToFile (STUDENT_BLINDING_FAIL_KEY, &blindingFailKeyStudent))
  {
    return (1);
  }
  GNUNET_CRYPTO_rsa_blinding_key_free (blindingFailKeyStudent); // Free the student RSA blinding fail key

  return (0);
}

/**
 * Check parameters to the program.
 */
int checkParams (int nrParams, char* params[], unsigned int* sizeKey)
{
  int i;

  if (nrParams == 1)
  {
    showHelp ();
    return (1);
  }

  for (i = 1 ; i < nrParams; i++)
  {
    if (strcmp (params[i], "-h") == 0 || strcmp (params[i], "--help") == 0)
    {
      showHelp ();
      return (1);
    }

    if (strcmp (params[i], "-v") == 0 || strcmp (params[i], "--version") == 0)
    {
      printf (VERSION"\n");
      return (1);
    }

    if (strcmp (params[i], "-k") == 0)
    {
      i = i + 1;
      if (i == nrParams)
      {
        printf ("Option -k needs a parameter specifying key size in bits: 1024, 2048 or 4096.\n");
        return (1);
      }
      else
      if (strcmp (params[i], "1024") == 0)
      {
        *sizeKey = 1024;
      }
      else
      if (strcmp (params[i], "2048") == 0)
      {
        *sizeKey = 2048;
      }
      else
      if (strcmp (params[i], "4096") == 0)
      {
        *sizeKey = 4096;
      }
      else
      {
        printf ("Invalid parameter to option -k: %s\n", params[i]);
        printf ("Key size can only be 1024, 2048 or 4096 bits.\n");
        return (1);
      }
    }
    else
    if (i < (nrParams - 1))
    {
      printf ("Invalid option: %s\n", params[i]);
      printf ("Use \"-h\" for help.\n");
      return (1);
    }
  }

  return (0);
}

/**
 * Show help
 */
void showHelp ()
{
  printf ("Description:\n");
  printf ("  blind: This program should be run by the student to blind the student's ID.\n");
  printf ("Usage:\n");
  printf ("  blind [-k key_size] <ID data file>\n");
  printf ("Options:\n");
  printf ("  -k = Key size used if new keys are created: 1024, 2048 or 4096\n");
  printf ("Output:\n");
  printf ("  The student's two blinding keys for the grades pass and fail.\n");
  printf ("  The student's two blinded hashed IDs for the grades pass and fail.\n");
  printf ("Dependencies:\n");
  printf ("  This program relies on GNUnet and Libgcrypt.\n");
  printf ("License:\n");
  printf ("  GNU General Public License, version 3+.\n");
}

