/*
  This file is part of TALER
  Copyright (C) 2020 Taler Systems SA

  TALER is free software; you can redistribute it and/or modify it under the
  terms of the GNU General Public License as published by the Free Software
  Foundation; either version 3, or (at your option) any later version.

  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

  You should have received a copy of the GNU General Public License along with
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
*/
/**
 * @file charity_signatures.c
 * @brief Utility functions for Taler charity signatures
 * @author Christian Grothoff
 * @author Lukas Matyja
 */
#include "donau_util.h"
#include <taler/taler_util.h>
#include <gnunet/gnunet_common.h>
#include <taler/taler_signatures.h>
#include "donau_signatures.h"


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Format used to generate the charity signature on all blinded
 * identifiers and key pairs as a agreement of the charity with the
 * donation request from the donor.
 */
struct DONAU_BudiKeyPairTrackPS
{
  /**
   * Purpose must be #DONAU_SIGNATURE_DONAU_CHARITY_DONATION_CONFIRMATION. Signed
   * by a `struct DONAU_CharityPublicKeyP` using EdDSA.
   */
  struct GNUNET_CRYPTO_SignaturePurpose purpose;

  /**
   * List of BUDI-Key-Pairs. A BUID-Key-Pair contains the BUDI value which must be
   * signed (blindly) by the Donau.
   */
  struct GNUNET_HashCode bkps_hash;

  /**
   * num of bkps
   */
  uint32_t num_bkp;

};
GNUNET_NETWORK_STRUCT_END

void
DONAU_charity_bkp_sign (
  const size_t num_bkp,
  const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkp,
  const struct DONAU_CharityPrivateKeyP *charity_priv,
  struct DONAU_CharitySignatureP *charity_sig)
{
  struct DONAU_BudiKeyPairTrackPS tps = {
    .purpose.purpose = htonl (DONAU_SIGNATURE_CHARITY_DONATION_CONFIRMATION),
    .purpose.size = htonl (sizeof (tps)),
    .num_bkp = htonl (num_bkp)
  };
  struct GNUNET_HashContext *hc;

  hc = GNUNET_CRYPTO_hash_context_start ();
  for (unsigned int i = 0; i < num_bkp; i++)
  {
    const struct GNUNET_CRYPTO_BlindedMessage *bm
      = bkp[i].blinded_udi.blinded_message;

    GNUNET_CRYPTO_hash_context_read (hc,
                                     &bkp[i].h_donation_unit_pub,
                                     sizeof (bkp[i].h_donation_unit_pub));
    switch (bm->cipher)
    {
    case GNUNET_CRYPTO_BSA_INVALID:
      GNUNET_assert (0);
    case GNUNET_CRYPTO_BSA_CS:
      GNUNET_CRYPTO_hash_context_read (
        hc,
        &bm->details.cs_blinded_message,
        sizeof (bm->details.cs_blinded_message));
      break;
    case GNUNET_CRYPTO_BSA_RSA:
      GNUNET_CRYPTO_hash_context_read (
        hc,
        bm->details.rsa_blinded_message.blinded_msg,
        bm->details.rsa_blinded_message.blinded_msg_size);
      break;
    }
  }
  GNUNET_CRYPTO_hash_context_finish (hc,
                                     &tps.bkps_hash);

  GNUNET_CRYPTO_eddsa_sign (&charity_priv->eddsa_priv,
                            &tps,
                            &charity_sig->eddsa_sig);
}


enum GNUNET_GenericReturnValue
DONAU_charity_bkp_verify (
  const size_t num_bkp,
  const struct DONAU_BlindedUniqueDonorIdentifierKeyPair *bkp,
  const struct DONAU_CharityPublicKeyP *charity_pub,
  const struct DONAU_CharitySignatureP *charity_sig)
{
  struct DONAU_BudiKeyPairTrackPS tps = {
    .purpose.purpose = htonl (DONAU_SIGNATURE_CHARITY_DONATION_CONFIRMATION),
    .purpose.size = htonl (sizeof (tps)),
    .num_bkp = htonl (num_bkp)
  };
  struct GNUNET_HashContext *hc;

  hc = GNUNET_CRYPTO_hash_context_start ();
  for (unsigned int i = 0; i < num_bkp; i++)
  {
    const struct GNUNET_CRYPTO_BlindedMessage *bm
      = bkp[i].blinded_udi.blinded_message;

    GNUNET_CRYPTO_hash_context_read (hc,
                                     &bkp[i].h_donation_unit_pub,
                                     sizeof (bkp[i].h_donation_unit_pub));
    switch (bm->cipher)
    {
    case GNUNET_CRYPTO_BSA_INVALID:
      GNUNET_assert (0);
    case GNUNET_CRYPTO_BSA_CS:
      GNUNET_CRYPTO_hash_context_read (
        hc,
        &bm->details.cs_blinded_message,
        sizeof (bm->details.cs_blinded_message));
      break;
    case GNUNET_CRYPTO_BSA_RSA:
      GNUNET_CRYPTO_hash_context_read (
        hc,
        bm->details.rsa_blinded_message.blinded_msg,
        bm->details.rsa_blinded_message.blinded_msg_size);
      break;
    }
  }
  GNUNET_CRYPTO_hash_context_finish (hc,
                                     &tps.bkps_hash);

  return
    GNUNET_CRYPTO_eddsa_verify (DONAU_SIGNATURE_CHARITY_DONATION_CONFIRMATION,
                                &tps,
                                &charity_sig->eddsa_sig,
                                &charity_pub->eddsa_pub);
}


enum GNUNET_GenericReturnValue
DONAU_charity_get_info_verify (
  const struct DONAU_CharityPublicKeyP *charity_pub,
  const struct DONAU_CharitySignatureP *charity_sig)
{
  struct GNUNET_CRYPTO_SignaturePurpose purpose = {
    .purpose = htonl (DONAU_SIGNATURE_CHARITY_GET_INFO),
    .size = htonl (sizeof (purpose))
  };

  return GNUNET_CRYPTO_eddsa_verify_ (
    DONAU_SIGNATURE_CHARITY_GET_INFO,
    &purpose,
    &charity_sig->eddsa_sig,
    &charity_pub->eddsa_pub);
}


void
DONAU_charity_get_info_sign (
  const struct DONAU_CharityPrivateKeyP *charity_priv,
  struct DONAU_CharitySignatureP *charity_sig)
{
  struct GNUNET_CRYPTO_SignaturePurpose purpose = {
    .purpose = htonl (DONAU_SIGNATURE_CHARITY_GET_INFO),
    .size = htonl (sizeof (purpose))
  };

  GNUNET_CRYPTO_eddsa_sign_ (&charity_priv->eddsa_priv,
                             &purpose,
                             &charity_sig->eddsa_sig);
}


/* end of charity_signatures.c */
