/*
  unblind: This program should be run by the student to unblind the DSS-signature for either pass or fail of the student's blinded hashed ID and then to verify the signature of the hashed 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[]);
void showHelp ();

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

  // Read the ID from file.
  char*  fileNameID = params[nrParams - 2]; // The second 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;
  GNUNET_CRYPTO_hash (ID, sizeID, &hashedID); // Generate a SHA-512 hash of ID

  // Read the DSS RSA-signature of blinded hashed ID from file.
  char*  fileNameSignedBlindedHashedID = params[nrParams - 1]; // The last argument to the program should be the file.
  struct GNUNET_CRYPTO_rsa_Signature *signedBlindedHashedID;
  if (readSignatureFromFile (fileNameSignedBlindedHashedID, &signedBlindedHashedID))
  {
    return (1);
  }

  /**
   * Pass
   */

  // Read the student RSA blinding pass key from file.
  struct GNUNET_CRYPTO_rsa_BlindingKey *blindingPassKeyStudent;
  if (readBlindingKeyFromFile (STUDENT_BLINDING_PASS_KEY, &blindingPassKeyStudent))
  {
    return (1);
  }

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

  // Unblinds the DSS RSA-signature of blinded hashed ID by using the student's blinding pass key and the DSS public pass key
  struct GNUNET_CRYPTO_rsa_Signature *signedHashedPassID;
  signedHashedPassID = GNUNET_CRYPTO_rsa_unblind (signedBlindedHashedID, blindingPassKeyStudent, publicPassKeyDSS);

  // Free the student RSA blinding pass key
  GNUNET_CRYPTO_rsa_blinding_key_free (blindingPassKeyStudent);

  /**
   * Fail
   */

  // Read the student RSA blinding fail key from file.
  struct GNUNET_CRYPTO_rsa_BlindingKey *blindingFailKeyStudent;
  if (readBlindingKeyFromFile (STUDENT_BLINDING_FAIL_KEY, &blindingFailKeyStudent))
  {
    return (1);
  }

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

  // Unblinds the DSS RSA-signature of blinded hashed ID by using the student's blinding fail key and the DSS public fail key
  struct GNUNET_CRYPTO_rsa_Signature *signedHashedFailID;
  signedHashedFailID = GNUNET_CRYPTO_rsa_unblind (signedBlindedHashedID, blindingFailKeyStudent, publicFailKeyDSS);

  // Free the student RSA blinding fail key
  GNUNET_CRYPTO_rsa_blinding_key_free (blindingFailKeyStudent);

  // Free the signature of blinded hashed ID
  GNUNET_CRYPTO_rsa_signature_free (signedBlindedHashedID);

  /**
   * Verify
   */

  // Verify if the hash corresponds to the DSS-signature and that the signature is valid with respect to the DSS public pass key.
  if (GNUNET_CRYPTO_rsa_verify (&hashedID, signedHashedPassID, publicPassKeyDSS) == GNUNET_OK)
  {
    printf ("The signature of the hashed ID is valid with respect to the public pass key of the DSS, i.e. it corresponds to the grade pass.\n");
    GNUNET_CRYPTO_rsa_public_key_free (publicPassKeyDSS); // Free the DSS RSA public pass key

    // Write the DSS RSA-signed hashed pass ID to file.
    if (writeSignatureToFile (DSS_SIGNED_HASHED_ID, &signedHashedPassID))
    {
      return (1);
    }
    GNUNET_CRYPTO_rsa_signature_free (signedHashedPassID); // Free the DSS RSA-signed hashed pass ID
  }
  else // Verify if the hash corresponds to the DSS-signature and that the signature is valid with respect to the DSS public fail key.
  if (GNUNET_CRYPTO_rsa_verify (&hashedID, signedHashedFailID, publicFailKeyDSS) == GNUNET_OK)
  {
    printf ("The signature of the hashed ID is vaild with respect to the public fail key of the DSS, i.e. it corresponds to the grade fail.\n");
    GNUNET_CRYPTO_rsa_public_key_free (publicFailKeyDSS); // Free the DSS RSA public fail key

    // Write the DSS RSA-signed hashed fail ID to file.
    if (writeSignatureToFile (DSS_SIGNED_HASHED_ID, &signedHashedFailID))
    {
      return (1);
    }
    GNUNET_CRYPTO_rsa_signature_free (signedHashedFailID); // Free the DSS RSA-signed hashed fail ID
  }
  else
  {
    // This can occur if the ID has been changed, e.g. if the signature is for another student's ID data.
    printf ("The signature of the hashed ID is not valid with respect to the public pass and fail key of the DSS.\n");
    return (1);
  }

  return (0);
}

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

  if (nrParams < 3)
  {
    showHelp ();
    return (1);
  }

  for (i = 1 ; i < (nrParams - 1); 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 (i < (nrParams - 2))
    {
      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 ("  unblind: This program should be run by the student to unblind the DSS-signature of the student's blinded hashed ID and then to verify the signature.\n");
  printf ("Usage:\n");
  printf ("  unblind <ID file> <signature of blinded hashed ID file>\n");
  printf ("Output:\n");
  printf ("  The DSS-signature of the student's hashed ID.\n");
  printf ("  Information whether the student's hashed ID has a valid signature made by the DSS.\n");
  printf ("Dependencies:\n");
  printf ("  This program relies on GNUnet and Libgcrypt.\n");
  printf ("License:\n");
  printf ("  GNU General Public License, version 3+.\n");
}

