/*
  This file is part of TALER
  Copyright (C) 2020-2024 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 offline_signatures.c
 * @brief Utility functions for Taler exchange offline signatures
 * @author Christian Grothoff
 */
#include "platform.h"
#include "taler_util.h"
#include "taler_signatures.h"


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Signature made by the exchange offline key over the information of
 * an AML officer status change.
 */
struct TALER_MasterAmlOfficerStatusPS
{

  /**
   * Purpose is #TALER_SIGNATURE_MASTER_AML_KEY.   Signed
   * by a `struct TALER_MasterPublicKeyP` using EdDSA.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Time of the change.
   */
  struct GNUNET_TIME_TimestampNBO change_date;

  /**
   * Public key of the AML officer.
   */
  struct TALER_AmlOfficerPublicKeyP officer_pub;

  /**
   * Hash over the AML officer's name.
   */
  struct GNUNET_HashCode h_officer_name GNUNET_PACKED;

  /**
   * Bitmask: 1 if enabled; 2 for read-only access. in NBO.
   */
  uint32_t is_active GNUNET_PACKED;
};
GNUNET_NETWORK_STRUCT_END


void
TALER_exchange_offline_aml_officer_status_sign (
  const struct TALER_AmlOfficerPublicKeyP *officer_pub,
  const char *officer_name,
  struct GNUNET_TIME_Timestamp change_date,
  bool is_active,
  bool read_only,
  const struct TALER_MasterPrivateKeyP *master_priv,
  struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterAmlOfficerStatusPS as = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_AML_KEY),
    .purpose.size = htonl (sizeof (as)),
    .change_date = GNUNET_TIME_timestamp_hton (change_date),
    .officer_pub = *officer_pub,
    .is_active = htonl ((is_active ? 1 : 0) + (read_only ? 2 : 0))
  };

  GNUNET_CRYPTO_hash (officer_name,
                      strlen (officer_name) + 1,
                      &as.h_officer_name);
  GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                            &as,
                            &master_sig->eddsa_signature);
}


enum GNUNET_GenericReturnValue
TALER_exchange_offline_aml_officer_status_verify (
  const struct TALER_AmlOfficerPublicKeyP *officer_pub,
  const char *officer_name,
  struct GNUNET_TIME_Timestamp change_date,
  bool is_active,
  bool read_only,
  const struct TALER_MasterPublicKeyP *master_pub,
  const struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterAmlOfficerStatusPS as = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_AML_KEY),
    .purpose.size = htonl (sizeof (as)),
    .change_date = GNUNET_TIME_timestamp_hton (change_date),
    .officer_pub = *officer_pub,
    .is_active = htonl ((is_active ? 1 : 0) + (read_only ? 2 : 0))
  };

  GNUNET_CRYPTO_hash (officer_name,
                      strlen (officer_name) + 1,
                      &as.h_officer_name);
  return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_AML_KEY,
                                     &as,
                                     &master_sig->eddsa_signature,
                                     &master_pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Signature made by the exchange offline key over the information of
 * an auditor to be added to the exchange's set of auditors.
 */
struct TALER_MasterAddAuditorPS
{

  /**
   * Purpose is #TALER_SIGNATURE_MASTER_ADD_AUDITOR.   Signed
   * by a `struct TALER_MasterPublicKeyP` using EdDSA.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Time of the change.
   */
  struct GNUNET_TIME_TimestampNBO start_date;

  /**
   * Public key of the auditor.
   */
  struct TALER_AuditorPublicKeyP auditor_pub;

  /**
   * Hash over the auditor's URL.
   */
  struct GNUNET_HashCode h_auditor_url GNUNET_PACKED;
};
GNUNET_NETWORK_STRUCT_END


void
TALER_exchange_offline_auditor_add_sign (
  const struct TALER_AuditorPublicKeyP *auditor_pub,
  const char *auditor_url,
  struct GNUNET_TIME_Timestamp start_date,
  const struct TALER_MasterPrivateKeyP *master_priv,
  struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterAddAuditorPS kv = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_ADD_AUDITOR),
    .purpose.size = htonl (sizeof (kv)),
    .start_date = GNUNET_TIME_timestamp_hton (start_date),
    .auditor_pub = *auditor_pub,
  };

  GNUNET_CRYPTO_hash (auditor_url,
                      strlen (auditor_url) + 1,
                      &kv.h_auditor_url);
  GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                            &kv,
                            &master_sig->eddsa_signature);
}


enum GNUNET_GenericReturnValue
TALER_exchange_offline_auditor_add_verify (
  const struct TALER_AuditorPublicKeyP *auditor_pub,
  const char *auditor_url,
  struct GNUNET_TIME_Timestamp start_date,
  const struct TALER_MasterPublicKeyP *master_pub,
  const struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterAddAuditorPS aa = {
    .purpose.purpose = htonl (
      TALER_SIGNATURE_MASTER_ADD_AUDITOR),
    .purpose.size = htonl (sizeof (aa)),
    .start_date = GNUNET_TIME_timestamp_hton (start_date),
    .auditor_pub = *auditor_pub
  };

  GNUNET_CRYPTO_hash (auditor_url,
                      strlen (auditor_url) + 1,
                      &aa.h_auditor_url);
  return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_ADD_AUDITOR,
                                     &aa,
                                     &master_sig->eddsa_signature,
                                     &master_pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Signature made by the exchange offline key over the information of
 * an auditor to be removed from the exchange's set of auditors.
 */
struct TALER_MasterDelAuditorPS
{

  /**
   * Purpose is #TALER_SIGNATURE_MASTER_DEL_AUDITOR.   Signed
   * by a `struct TALER_MasterPublicKeyP` using EdDSA.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Time of the change.
   */
  struct GNUNET_TIME_TimestampNBO end_date;

  /**
   * Public key of the auditor.
   */
  struct TALER_AuditorPublicKeyP auditor_pub;

};
GNUNET_NETWORK_STRUCT_END


void
TALER_exchange_offline_auditor_del_sign (
  const struct TALER_AuditorPublicKeyP *auditor_pub,
  struct GNUNET_TIME_Timestamp end_date,
  const struct TALER_MasterPrivateKeyP *master_priv,
  struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterDelAuditorPS kv = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DEL_AUDITOR),
    .purpose.size = htonl (sizeof (kv)),
    .end_date = GNUNET_TIME_timestamp_hton (end_date),
    .auditor_pub = *auditor_pub,
  };

  GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                            &kv,
                            &master_sig->eddsa_signature);
}


enum GNUNET_GenericReturnValue
TALER_exchange_offline_auditor_del_verify (
  const struct TALER_AuditorPublicKeyP *auditor_pub,
  struct GNUNET_TIME_Timestamp end_date,
  const struct TALER_MasterPublicKeyP *master_pub,
  const struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterDelAuditorPS da = {
    .purpose.purpose = htonl (
      TALER_SIGNATURE_MASTER_DEL_AUDITOR),
    .purpose.size = htonl (sizeof (da)),
    .end_date = GNUNET_TIME_timestamp_hton (end_date),
    .auditor_pub = *auditor_pub
  };

  return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DEL_AUDITOR,
                                     &da,
                                     &master_sig->eddsa_signature,
                                     &master_pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Message confirming that a denomination key was revoked.
 */
struct TALER_MasterDenominationKeyRevocationPS
{
  /**
   * Purpose is #TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Hash of the denomination key.
   */
  struct TALER_DenominationHashP h_denom_pub;

};

GNUNET_NETWORK_STRUCT_END


void
TALER_exchange_offline_denomination_revoke_sign (
  const struct TALER_DenominationHashP *h_denom_pub,
  const struct TALER_MasterPrivateKeyP *master_priv,
  struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterDenominationKeyRevocationPS rm = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED),
    .purpose.size = htonl (sizeof (rm)),
    .h_denom_pub = *h_denom_pub
  };

  GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                            &rm,
                            &master_sig->eddsa_signature);
}


enum GNUNET_GenericReturnValue
TALER_exchange_offline_denomination_revoke_verify (
  const struct TALER_DenominationHashP *h_denom_pub,
  const struct TALER_MasterPublicKeyP *master_pub,
  const struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterDenominationKeyRevocationPS kr = {
    .purpose.purpose = htonl (
      TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED),
    .purpose.size = htonl (sizeof (kr)),
    .h_denom_pub = *h_denom_pub
  };

  return GNUNET_CRYPTO_eddsa_verify (
    TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED,
    &kr,
    &master_sig->eddsa_signature,
    &master_pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Message confirming that an exchange online signing key was revoked.
 */
struct TALER_MasterSigningKeyRevocationPS
{
  /**
   * Purpose is #TALER_SIGNATURE_MASTER_SIGNING_KEY_REVOKED.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * The exchange's public key.
   */
  struct TALER_ExchangePublicKeyP exchange_pub;

};

GNUNET_NETWORK_STRUCT_END


void
TALER_exchange_offline_signkey_revoke_sign (
  const struct TALER_ExchangePublicKeyP *exchange_pub,
  const struct TALER_MasterPrivateKeyP *master_priv,
  struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterSigningKeyRevocationPS kv = {
    .purpose.purpose = htonl (
      TALER_SIGNATURE_MASTER_SIGNING_KEY_REVOKED),
    .purpose.size = htonl (sizeof (kv)),
    .exchange_pub = *exchange_pub
  };

  GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                            &kv,
                            &master_sig->eddsa_signature);
}


enum GNUNET_GenericReturnValue
TALER_exchange_offline_signkey_revoke_verify (
  const struct TALER_ExchangePublicKeyP *exchange_pub,
  const struct TALER_MasterPublicKeyP *master_pub,
  const struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterSigningKeyRevocationPS rm = {
    .purpose.purpose = htonl (
      TALER_SIGNATURE_MASTER_SIGNING_KEY_REVOKED),
    .purpose.size = htonl (sizeof (rm)),
    .exchange_pub = *exchange_pub
  };

  return GNUNET_CRYPTO_eddsa_verify (
    TALER_SIGNATURE_MASTER_SIGNING_KEY_REVOKED,
    &rm,
    &master_sig->eddsa_signature,
    &master_pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Information about a signing key of the exchange.  Signing keys are used
 * to sign exchange messages other than coins, i.e. to confirm that a
 * deposit was successful or that a refresh was accepted.
 */
struct TALER_ExchangeSigningKeyValidityPS
{

  /**
   * Purpose is #TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * When does this signing key begin to be valid?
   */
  struct GNUNET_TIME_TimestampNBO start;

  /**
   * When does this signing key expire? Note: This is currently when
   * the Exchange will definitively stop using it.  Signatures made with
   * the key remain valid until @e end.  When checking validity periods,
   * clients should allow for some overlap between keys and tolerate
   * the use of either key during the overlap time (due to the
   * possibility of clock skew).
   */
  struct GNUNET_TIME_TimestampNBO expire;

  /**
   * When do signatures with this signing key become invalid?  After
   * this point, these signatures cannot be used in (legal) disputes
   * anymore, as the Exchange is then allowed to destroy its side of the
   * evidence.  @e end is expected to be significantly larger than @e
   * expire (by a year or more).
   */
  struct GNUNET_TIME_TimestampNBO end;

  /**
   * The public online signing key that the exchange will use
   * between @e start and @e expire.
   */
  struct TALER_ExchangePublicKeyP signkey_pub;
};

GNUNET_NETWORK_STRUCT_END


void
TALER_exchange_offline_signkey_validity_sign (
  const struct TALER_ExchangePublicKeyP *exchange_pub,
  struct GNUNET_TIME_Timestamp start_sign,
  struct GNUNET_TIME_Timestamp end_sign,
  struct GNUNET_TIME_Timestamp end_legal,
  const struct TALER_MasterPrivateKeyP *master_priv,
  struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_ExchangeSigningKeyValidityPS skv = {
    .purpose.purpose = htonl (
      TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY),
    .purpose.size = htonl (sizeof (skv)),
    .start = GNUNET_TIME_timestamp_hton (start_sign),
    .expire = GNUNET_TIME_timestamp_hton (end_sign),
    .end = GNUNET_TIME_timestamp_hton (end_legal),
    .signkey_pub = *exchange_pub
  };

  GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                            &skv,
                            &master_sig->eddsa_signature);
}


enum GNUNET_GenericReturnValue
TALER_exchange_offline_signkey_validity_verify (
  const struct TALER_ExchangePublicKeyP *exchange_pub,
  struct GNUNET_TIME_Timestamp start_sign,
  struct GNUNET_TIME_Timestamp end_sign,
  struct GNUNET_TIME_Timestamp end_legal,
  const struct TALER_MasterPublicKeyP *master_pub,
  const struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_ExchangeSigningKeyValidityPS skv = {
    .purpose.purpose = htonl (
      TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY),
    .purpose.size = htonl (sizeof (skv)),
    .start = GNUNET_TIME_timestamp_hton (start_sign),
    .expire = GNUNET_TIME_timestamp_hton (end_sign),
    .end = GNUNET_TIME_timestamp_hton (end_legal),
    .signkey_pub = *exchange_pub
  };

  return
    GNUNET_CRYPTO_eddsa_verify (
    TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY,
    &skv,
    &master_sig->eddsa_signature,
    &master_pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Information about a denomination key. Denomination keys
 * are used to sign coins of a certain value into existence.
 */
struct TALER_DenominationKeyValidityPS
{

  /**
   * Purpose is #TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * The long-term offline master key of the exchange that was
   * used to create @e signature.
   *
   * Note: This member is not strictly required, but here for
   * backwards-compatibility. If we ever again badly break
   * compatibility, we might want to remove it.
   */
  struct TALER_MasterPublicKeyP master;

  /**
   * Start time of the validity period for this key.
   */
  struct GNUNET_TIME_TimestampNBO start;

  /**
   * The exchange will sign fresh coins between @e start and this time.
   * @e expire_withdraw will be somewhat larger than @e start to
   * ensure a sufficiently large anonymity set, while also allowing
   * the Exchange to limit the financial damage in case of a key being
   * compromised.  Thus, exchanges with low volume are expected to have a
   * longer withdraw period (@e expire_withdraw - @e start) than exchanges
   * with high transaction volume.  The period may also differ between
   * types of coins.  A exchange may also have a few denomination keys
   * with the same value with overlapping validity periods, to address
   * issues such as clock skew.
   */
  struct GNUNET_TIME_TimestampNBO expire_withdraw;

  /**
   * Coins signed with the denomination key must be spent or refreshed
   * between @e start and this expiration time.  After this time, the
   * exchange will refuse transactions involving this key as it will
   * "drop" the table with double-spending information (shortly after)
   * this time.  Note that wallets should refresh coins significantly
   * before this time to be on the safe side.  @e expire_deposit must be
   * significantly larger than @e expire_withdraw (by months or even
   * years).
   */
  struct GNUNET_TIME_TimestampNBO expire_deposit;

  /**
   * When do signatures with this denomination key become invalid?
   * After this point, these signatures cannot be used in (legal)
   * disputes anymore, as the Exchange is then allowed to destroy its side
   * of the evidence.  @e expire_legal is expected to be significantly
   * larger than @e expire_deposit (by a year or more).
   */
  struct GNUNET_TIME_TimestampNBO expire_legal;

  /**
   * The value of the coins signed with this denomination key.
   */
  struct TALER_AmountNBO value;

  /**
   * Fees for the coin.
   */
  struct TALER_DenomFeeSetNBOP fees;

  /**
   * Hash code of the denomination public key. (Used to avoid having
   * the variable-size RSA key in this struct.)
   */
  struct TALER_DenominationHashP denom_hash GNUNET_PACKED;

};

GNUNET_NETWORK_STRUCT_END


void
TALER_exchange_offline_denom_validity_sign (
  const struct TALER_DenominationHashP *h_denom_pub,
  struct GNUNET_TIME_Timestamp stamp_start,
  struct GNUNET_TIME_Timestamp stamp_expire_withdraw,
  struct GNUNET_TIME_Timestamp stamp_expire_deposit,
  struct GNUNET_TIME_Timestamp stamp_expire_legal,
  const struct TALER_Amount *coin_value,
  const struct TALER_DenomFeeSet *fees,
  const struct TALER_MasterPrivateKeyP *master_priv,
  struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_DenominationKeyValidityPS issue = {
    .purpose.purpose
      = htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY),
    .purpose.size
      = htonl (sizeof (issue)),
    .start = GNUNET_TIME_timestamp_hton (stamp_start),
    .expire_withdraw = GNUNET_TIME_timestamp_hton (stamp_expire_withdraw),
    .expire_deposit = GNUNET_TIME_timestamp_hton (stamp_expire_deposit),
    .expire_legal = GNUNET_TIME_timestamp_hton (stamp_expire_legal),
    .denom_hash = *h_denom_pub
  };

  GNUNET_CRYPTO_eddsa_key_get_public (&master_priv->eddsa_priv,
                                      &issue.master.eddsa_pub);
  TALER_amount_hton (&issue.value,
                     coin_value);
  TALER_denom_fee_set_hton (&issue.fees,
                            fees);
  GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                            &issue,
                            &master_sig->eddsa_signature);
}


enum GNUNET_GenericReturnValue
TALER_exchange_offline_denom_validity_verify (
  const struct TALER_DenominationHashP *h_denom_pub,
  struct GNUNET_TIME_Timestamp stamp_start,
  struct GNUNET_TIME_Timestamp stamp_expire_withdraw,
  struct GNUNET_TIME_Timestamp stamp_expire_deposit,
  struct GNUNET_TIME_Timestamp stamp_expire_legal,
  const struct TALER_Amount *coin_value,
  const struct TALER_DenomFeeSet *fees,
  const struct TALER_MasterPublicKeyP *master_pub,
  const struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_DenominationKeyValidityPS dkv = {
    .purpose.purpose = htonl (
      TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY),
    .purpose.size = htonl (sizeof (dkv)),
    .master = *master_pub,
    .start = GNUNET_TIME_timestamp_hton (stamp_start),
    .expire_withdraw = GNUNET_TIME_timestamp_hton (stamp_expire_withdraw),
    .expire_deposit = GNUNET_TIME_timestamp_hton (stamp_expire_deposit),
    .expire_legal = GNUNET_TIME_timestamp_hton (stamp_expire_legal),
    .denom_hash = *h_denom_pub
  };

  TALER_amount_hton (&dkv.value,
                     coin_value);
  TALER_denom_fee_set_hton (&dkv.fees,
                            fees);
  return
    GNUNET_CRYPTO_eddsa_verify (
    TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY,
    &dkv,
    &master_sig->eddsa_signature,
    &master_pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Signature made by the exchange offline key over the information of
 * a payto:// URI to be added to the exchange's set of active wire accounts.
 */
struct TALER_MasterAddWirePS
{

  /**
   * Purpose is #TALER_SIGNATURE_MASTER_ADD_WIRE.   Signed
   * by a `struct TALER_MasterPublicKeyP` using EdDSA.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Time of the change.
   */
  struct GNUNET_TIME_TimestampNBO start_date;

  /**
   * Hash over the exchange's payto URI.
   */
  struct TALER_FullPaytoHashP h_payto GNUNET_PACKED;

  /**
   * Hash over the conversion URL, all zeros if there
   * is no conversion URL.
   */
  struct GNUNET_HashCode h_conversion_url;

  /**
   * Hash over the debit restrictions.
   */
  struct GNUNET_HashCode h_debit_restrictions;

  /**
   * Hash over the credit restrictions.
   */
  struct GNUNET_HashCode h_credit_restrictions;
};

GNUNET_NETWORK_STRUCT_END


void
TALER_exchange_offline_wire_add_sign (
  const struct TALER_FullPayto payto_uri,
  const char *conversion_url,
  const json_t *debit_restrictions,
  const json_t *credit_restrictions,
  struct GNUNET_TIME_Timestamp now,
  const struct TALER_MasterPrivateKeyP *master_priv,
  struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterAddWirePS kv = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_ADD_WIRE),
    .purpose.size = htonl (sizeof (kv)),
    .start_date = GNUNET_TIME_timestamp_hton (now),
  };

  TALER_full_payto_hash (payto_uri,
                         &kv.h_payto);
  if (NULL != conversion_url)
    GNUNET_CRYPTO_hash (conversion_url,
                        strlen (conversion_url) + 1,
                        &kv.h_conversion_url);
  TALER_json_hash (debit_restrictions,
                   &kv.h_debit_restrictions);
  TALER_json_hash (credit_restrictions,
                   &kv.h_credit_restrictions);
  GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                            &kv,
                            &master_sig->eddsa_signature);
}


enum GNUNET_GenericReturnValue
TALER_exchange_offline_wire_add_verify (
  const struct TALER_FullPayto payto_uri,
  const char *conversion_url,
  const json_t *debit_restrictions,
  const json_t *credit_restrictions,
  struct GNUNET_TIME_Timestamp sign_time,
  const struct TALER_MasterPublicKeyP *master_pub,
  const struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterAddWirePS aw = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_ADD_WIRE),
    .purpose.size = htonl (sizeof (aw)),
    .start_date = GNUNET_TIME_timestamp_hton (sign_time),
  };

  TALER_full_payto_hash (payto_uri,
                         &aw.h_payto);
  if (NULL != conversion_url)
    GNUNET_CRYPTO_hash (conversion_url,
                        strlen (conversion_url) + 1,
                        &aw.h_conversion_url);
  TALER_json_hash (debit_restrictions,
                   &aw.h_debit_restrictions);
  TALER_json_hash (credit_restrictions,
                   &aw.h_credit_restrictions);
  return
    GNUNET_CRYPTO_eddsa_verify (
    TALER_SIGNATURE_MASTER_ADD_WIRE,
    &aw,
    &master_sig->eddsa_signature,
    &master_pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Signature made by the exchange offline key over the information of
 * a  wire method to be removed to the exchange's set of active accounts.
 */
struct TALER_MasterDelWirePS
{

  /**
   * Purpose is #TALER_SIGNATURE_MASTER_DEL_WIRE.   Signed
   * by a `struct TALER_MasterPublicKeyP` using EdDSA.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Time of the change.
   */
  struct GNUNET_TIME_TimestampNBO end_date;

  /**
   * Hash over the exchange's payto URI.
   */
  struct TALER_FullPaytoHashP h_payto GNUNET_PACKED;

};

GNUNET_NETWORK_STRUCT_END


void
TALER_exchange_offline_wire_del_sign (
  const struct TALER_FullPayto payto_uri,
  struct GNUNET_TIME_Timestamp now,
  const struct TALER_MasterPrivateKeyP *master_priv,
  struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterDelWirePS kv = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DEL_WIRE),
    .purpose.size = htonl (sizeof (kv)),
    .end_date = GNUNET_TIME_timestamp_hton (now),
  };

  TALER_full_payto_hash (payto_uri,
                         &kv.h_payto);
  GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                            &kv,
                            &master_sig->eddsa_signature);
}


enum GNUNET_GenericReturnValue
TALER_exchange_offline_wire_del_verify (
  const struct TALER_FullPayto payto_uri,
  struct GNUNET_TIME_Timestamp sign_time,
  const struct TALER_MasterPublicKeyP *master_pub,
  const struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterDelWirePS aw = {
    .purpose.purpose = htonl (
      TALER_SIGNATURE_MASTER_DEL_WIRE),
    .purpose.size = htonl (sizeof (aw)),
    .end_date = GNUNET_TIME_timestamp_hton (sign_time),
  };

  TALER_full_payto_hash (payto_uri,
                         &aw.h_payto);
  return GNUNET_CRYPTO_eddsa_verify (
    TALER_SIGNATURE_MASTER_DEL_WIRE,
    &aw,
    &master_sig->eddsa_signature,
    &master_pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Information signed by the exchange's master
 * key stating the wire fee to be paid per wire transfer.
 */
struct TALER_MasterWireFeePS
{

  /**
   * Purpose is #TALER_SIGNATURE_MASTER_WIRE_FEES.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Hash over the wire method (yes, H("x-taler-bank") or H("iban")), in lower
   * case, including 0-terminator.  Used to uniquely identify which
   * wire method these fees apply to.
   */
  struct GNUNET_HashCode h_wire_method;

  /**
   * Start date when the fee goes into effect.
   */
  struct GNUNET_TIME_TimestampNBO start_date;

  /**
   * End date when the fee stops being in effect (exclusive)
   */
  struct GNUNET_TIME_TimestampNBO end_date;

  /**
   * Fees charged for wire transfers using the
   * given wire method.
   */
  struct TALER_WireFeeSetNBOP fees;

};

GNUNET_NETWORK_STRUCT_END


void
TALER_exchange_offline_wire_fee_sign (
  const char *payment_method,
  struct GNUNET_TIME_Timestamp start_time,
  struct GNUNET_TIME_Timestamp end_time,
  const struct TALER_WireFeeSet *fees,
  const struct TALER_MasterPrivateKeyP *master_priv,
  struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterWireFeePS kv = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_FEES),
    .purpose.size = htonl (sizeof (kv)),
    .start_date = GNUNET_TIME_timestamp_hton (start_time),
    .end_date = GNUNET_TIME_timestamp_hton (end_time),
  };

  GNUNET_CRYPTO_hash (payment_method,
                      strlen (payment_method) + 1,
                      &kv.h_wire_method);
  TALER_wire_fee_set_hton (&kv.fees,
                           fees);
  GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                            &kv,
                            &master_sig->eddsa_signature);
}


enum GNUNET_GenericReturnValue
TALER_exchange_offline_wire_fee_verify (
  const char *payment_method,
  struct GNUNET_TIME_Timestamp start_time,
  struct GNUNET_TIME_Timestamp end_time,
  const struct TALER_WireFeeSet *fees,
  const struct TALER_MasterPublicKeyP *master_pub,
  const struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterWireFeePS wf = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_FEES),
    .purpose.size = htonl (sizeof (wf)),
    .start_date = GNUNET_TIME_timestamp_hton (start_time),
    .end_date = GNUNET_TIME_timestamp_hton (end_time)
  };

  GNUNET_CRYPTO_hash (payment_method,
                      strlen (payment_method) + 1,
                      &wf.h_wire_method);
  TALER_wire_fee_set_hton (&wf.fees,
                           fees);
  return
    GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_FEES,
                                &wf,
                                &master_sig->eddsa_signature,
                                &master_pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * Global fees charged by the exchange independent of
 * denomination or wire method.
 */
struct TALER_MasterGlobalFeePS
{

  /**
   * Purpose is #TALER_SIGNATURE_MASTER_GLOBAL_FEES.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Start date when the fee goes into effect.
   */
  struct GNUNET_TIME_TimestampNBO start_date;

  /**
   * End date when the fee stops being in effect (exclusive)
   */
  struct GNUNET_TIME_TimestampNBO end_date;

  /**
   * How long does an exchange keep a purse around after a purse
   * has expired (or been successfully merged)?  A 'GET' request
   * for a purse will succeed until the purse expiration time
   * plus this value.
   */
  struct GNUNET_TIME_RelativeNBO purse_timeout;

  /**
   * How long will the exchange preserve the account history?  After an
   * account was deleted/closed, the exchange will retain the account history
   * for legal reasons until this time.
   */
  struct GNUNET_TIME_RelativeNBO history_expiration;

  /**
   * Fee charged to the merchant per wire transfer.
   */
  struct TALER_GlobalFeeSetNBOP fees;

  /**
   * Number of concurrent purses that any
   * account holder is allowed to create without having
   * to pay the @e purse_fee. Here given in NBO.
   */
  uint32_t purse_account_limit;

};

GNUNET_NETWORK_STRUCT_END


void
TALER_exchange_offline_global_fee_sign (
  struct GNUNET_TIME_Timestamp start_time,
  struct GNUNET_TIME_Timestamp end_time,
  const struct TALER_GlobalFeeSet *fees,
  struct GNUNET_TIME_Relative purse_timeout,
  struct GNUNET_TIME_Relative history_expiration,
  uint32_t purse_account_limit,
  const struct TALER_MasterPrivateKeyP *master_priv,
  struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterGlobalFeePS wf = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_GLOBAL_FEES),
    .purpose.size = htonl (sizeof (wf)),
    .start_date = GNUNET_TIME_timestamp_hton (start_time),
    .end_date = GNUNET_TIME_timestamp_hton (end_time),
    .purse_timeout = GNUNET_TIME_relative_hton (purse_timeout),
    .history_expiration = GNUNET_TIME_relative_hton (history_expiration),
    .purse_account_limit = htonl (purse_account_limit)
  };

  TALER_global_fee_set_hton (&wf.fees,
                             fees);
  GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                            &wf,
                            &master_sig->eddsa_signature);
}


enum GNUNET_GenericReturnValue
TALER_exchange_offline_global_fee_verify (
  struct GNUNET_TIME_Timestamp start_time,
  struct GNUNET_TIME_Timestamp end_time,
  const struct TALER_GlobalFeeSet *fees,
  struct GNUNET_TIME_Relative purse_timeout,
  struct GNUNET_TIME_Relative history_expiration,
  uint32_t purse_account_limit,
  const struct TALER_MasterPublicKeyP *master_pub,
  const struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterGlobalFeePS wf = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_GLOBAL_FEES),
    .purpose.size = htonl (sizeof (wf)),
    .start_date = GNUNET_TIME_timestamp_hton (start_time),
    .end_date = GNUNET_TIME_timestamp_hton (end_time),
    .purse_timeout = GNUNET_TIME_relative_hton (purse_timeout),
    .history_expiration = GNUNET_TIME_relative_hton (history_expiration),
    .purse_account_limit = htonl (purse_account_limit)
  };

  TALER_global_fee_set_hton (&wf.fees,
                             fees);
  return
    GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_GLOBAL_FEES,
                                &wf,
                                &master_sig->eddsa_signature,
                                &master_pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Signature made by the exchange offline key over the manifest of
 * an extension.
 */
struct TALER_MasterExtensionManifestPS
{
  /**
   * Purpose is #TALER_SIGNATURE_MASTER_EXTENSION.   Signed
   * by a `struct TALER_MasterPublicKeyP` using EdDSA.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Hash of the JSON object that represents the manifests of extensions.
   */
  struct TALER_ExtensionManifestsHashP h_manifest GNUNET_PACKED;
};

GNUNET_NETWORK_STRUCT_END


void
TALER_exchange_offline_extension_manifests_hash_sign (
  const struct TALER_ExtensionManifestsHashP *h_manifest,
  const struct TALER_MasterPrivateKeyP *master_priv,
  struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterExtensionManifestPS ec = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
    .purpose.size = htonl (sizeof(ec)),
    .h_manifest = *h_manifest
  };
  GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                            &ec,
                            &master_sig->eddsa_signature);
}


enum GNUNET_GenericReturnValue
TALER_exchange_offline_extension_manifests_hash_verify (
  const struct TALER_ExtensionManifestsHashP *h_manifest,
  const struct TALER_MasterPublicKeyP *master_pub,
  const struct TALER_MasterSignatureP *master_sig
  )
{
  struct TALER_MasterExtensionManifestPS ec = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION),
    .purpose.size = htonl (sizeof(ec)),
    .h_manifest = *h_manifest
  };

  return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_EXTENSION,
                                     &ec,
                                     &master_sig->eddsa_signature,
                                     &master_pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Information signed by the exchange's master
 * key affirming the IBAN details for the exchange.
 */
struct TALER_MasterWireDetailsPS
{

  /**
   * Purpose is #TALER_SIGNATURE_MASTER_WIRE_DETAILS.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Hash over the account holder's payto:// URL.
   */
  struct TALER_FullPaytoHashP h_wire_details GNUNET_PACKED;

  /**
   * Hash over the conversion URL, all zeros if there
   * is no conversion URL.
   */
  struct GNUNET_HashCode h_conversion_url;

  /**
   * Hash over the debit restrictions.
   */
  struct GNUNET_HashCode h_debit_restrictions;

  /**
   * Hash over the credit restrictions.
   */
  struct GNUNET_HashCode h_credit_restrictions;

};

GNUNET_NETWORK_STRUCT_END


enum GNUNET_GenericReturnValue
TALER_exchange_wire_signature_check (
  const struct TALER_FullPayto payto_uri,
  const char *conversion_url,
  const json_t *debit_restrictions,
  const json_t *credit_restrictions,
  const struct TALER_MasterPublicKeyP *master_pub,
  const struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterWireDetailsPS wd = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_DETAILS),
    .purpose.size = htonl (sizeof (wd))
  };

  TALER_full_payto_hash (payto_uri,
                         &wd.h_wire_details);
  if (NULL != conversion_url)
    GNUNET_CRYPTO_hash (conversion_url,
                        strlen (conversion_url) + 1,
                        &wd.h_conversion_url);
  TALER_json_hash (debit_restrictions,
                   &wd.h_debit_restrictions);
  TALER_json_hash (credit_restrictions,
                   &wd.h_credit_restrictions);
  return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_DETAILS,
                                     &wd,
                                     &master_sig->eddsa_signature,
                                     &master_pub->eddsa_pub);
}


void
TALER_exchange_wire_signature_make (
  const struct TALER_FullPayto payto_uri,
  const char *conversion_url,
  const json_t *debit_restrictions,
  const json_t *credit_restrictions,
  const struct TALER_MasterPrivateKeyP *master_priv,
  struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_MasterWireDetailsPS wd = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_DETAILS),
    .purpose.size = htonl (sizeof (wd))
  };

  TALER_full_payto_hash (payto_uri,
                         &wd.h_wire_details);
  if (NULL != conversion_url)
    GNUNET_CRYPTO_hash (conversion_url,
                        strlen (conversion_url) + 1,
                        &wd.h_conversion_url);
  TALER_json_hash (debit_restrictions,
                   &wd.h_debit_restrictions);
  TALER_json_hash (credit_restrictions,
                   &wd.h_credit_restrictions);
  GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                            &wd,
                            &master_sig->eddsa_signature);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * Message signed by account to merge a purse into a reserve.
 */
struct TALER_PartnerConfigurationPS
{

  /**
   * Purpose is #TALER_SIGNATURE_MASTER_PARNTER_DETAILS
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
  struct TALER_MasterPublicKeyP partner_pub;
  struct GNUNET_TIME_TimestampNBO start_date;
  struct GNUNET_TIME_TimestampNBO end_date;
  struct GNUNET_TIME_RelativeNBO wad_frequency;
  struct TALER_AmountNBO wad_fee;
  struct GNUNET_HashCode h_url;
};

GNUNET_NETWORK_STRUCT_END


void
TALER_exchange_offline_partner_details_sign (
  const struct TALER_MasterPublicKeyP *partner_pub,
  struct GNUNET_TIME_Timestamp start_date,
  struct GNUNET_TIME_Timestamp end_date,
  struct GNUNET_TIME_Relative wad_frequency,
  const struct TALER_Amount *wad_fee,
  const char *partner_base_url,
  const struct TALER_MasterPrivateKeyP *master_priv,
  struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_PartnerConfigurationPS wd = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_PARTNER_DETAILS),
    .purpose.size = htonl (sizeof (wd)),
    .partner_pub = *partner_pub,
    .start_date = GNUNET_TIME_timestamp_hton (start_date),
    .end_date = GNUNET_TIME_timestamp_hton (end_date),
    .wad_frequency = GNUNET_TIME_relative_hton (wad_frequency),
  };

  GNUNET_CRYPTO_hash (partner_base_url,
                      strlen (partner_base_url) + 1,
                      &wd.h_url);
  TALER_amount_hton (&wd.wad_fee,
                     wad_fee);
  GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                            &wd,
                            &master_sig->eddsa_signature);
}


enum GNUNET_GenericReturnValue
TALER_exchange_offline_partner_details_verify (
  const struct TALER_MasterPublicKeyP *partner_pub,
  struct GNUNET_TIME_Timestamp start_date,
  struct GNUNET_TIME_Timestamp end_date,
  struct GNUNET_TIME_Relative wad_frequency,
  const struct TALER_Amount *wad_fee,
  const char *partner_base_url,
  const struct TALER_MasterPublicKeyP *master_pub,
  const struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_PartnerConfigurationPS wd = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_PARTNER_DETAILS),
    .purpose.size = htonl (sizeof (wd)),
    .partner_pub = *partner_pub,
    .start_date = GNUNET_TIME_timestamp_hton (start_date),
    .end_date = GNUNET_TIME_timestamp_hton (end_date),
    .wad_frequency = GNUNET_TIME_relative_hton (wad_frequency),
  };

  GNUNET_CRYPTO_hash (partner_base_url,
                      strlen (partner_base_url) + 1,
                      &wd.h_url);
  TALER_amount_hton (&wd.wad_fee,
                     wad_fee);
  return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_PARTNER_DETAILS,
                                     &wd,
                                     &master_sig->eddsa_signature,
                                     &master_pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * Message signed by account to drain profits
 * from the escrow account of the exchange.
 */
struct TALER_DrainProfitPS
{

  /**
   * Purpose is #TALER_SIGNATURE_MASTER_DRAIN_PROFITS
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
  struct TALER_WireTransferIdentifierRawP wtid;
  struct GNUNET_TIME_TimestampNBO date;
  struct TALER_AmountNBO amount;
  struct GNUNET_HashCode h_section;
  struct TALER_FullPaytoHashP h_payto;
};

GNUNET_NETWORK_STRUCT_END


void
TALER_exchange_offline_profit_drain_sign (
  const struct TALER_WireTransferIdentifierRawP *wtid,
  struct GNUNET_TIME_Timestamp date,
  const struct TALER_Amount *amount,
  const char *account_section,
  const struct TALER_FullPayto payto_uri,
  const struct TALER_MasterPrivateKeyP *master_priv,
  struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_DrainProfitPS wd = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DRAIN_PROFIT),
    .purpose.size = htonl (sizeof (wd)),
    .wtid = *wtid,
    .date = GNUNET_TIME_timestamp_hton (date),
  };

  GNUNET_CRYPTO_hash (account_section,
                      strlen (account_section) + 1,
                      &wd.h_section);
  TALER_full_payto_hash (payto_uri,
                         &wd.h_payto);
  TALER_amount_hton (&wd.amount,
                     amount);
  GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv,
                            &wd,
                            &master_sig->eddsa_signature);
}


enum GNUNET_GenericReturnValue
TALER_exchange_offline_profit_drain_verify (
  const struct TALER_WireTransferIdentifierRawP *wtid,
  struct GNUNET_TIME_Timestamp date,
  const struct TALER_Amount *amount,
  const char *account_section,
  const struct TALER_FullPayto payto_uri,
  const struct TALER_MasterPublicKeyP *master_pub,
  const struct TALER_MasterSignatureP *master_sig)
{
  struct TALER_DrainProfitPS wd = {
    .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DRAIN_PROFIT),
    .purpose.size = htonl (sizeof (wd)),
    .wtid = *wtid,
    .date = GNUNET_TIME_timestamp_hton (date),
  };

  GNUNET_CRYPTO_hash (account_section,
                      strlen (account_section) + 1,
                      &wd.h_section);
  TALER_full_payto_hash (payto_uri,
                         &wd.h_payto);
  TALER_amount_hton (&wd.amount,
                     amount);
  return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DRAIN_PROFIT,
                                     &wd,
                                     &master_sig->eddsa_signature,
                                     &master_pub->eddsa_pub);
}


/* end of offline_signatures.c */
