diff options
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/exchange_api_common.c | 155 | ||||
| -rw-r--r-- | src/lib/exchange_api_deposit.c | 57 | ||||
| -rw-r--r-- | src/lib/exchange_api_melt.c | 56 | ||||
| -rw-r--r-- | src/lib/exchange_api_recoup.c | 40 | 
4 files changed, 269 insertions, 39 deletions
diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c index 17be0d51..feaef7b3 100644 --- a/src/lib/exchange_api_common.c +++ b/src/lib/exchange_api_common.c @@ -447,6 +447,7 @@ TALER_EXCHANGE_free_reserve_history (   * @param currency expected currency for the coin   * @param coin_pub public key of the coin   * @param history history of the coin in json encoding + * @param[out] h_denom_pub set to the hash of the coin's denomination (if available)   * @param[out] total how much of the coin has been spent according to @a history   * @return #GNUNET_OK if @a history is valid, #GNUNET_SYSERR if not   */ @@ -456,6 +457,7 @@ TALER_EXCHANGE_verify_coin_history (    const char *currency,    const struct TALER_CoinSpendPublicKeyP *coin_pub,    json_t *history, +  struct GNUNET_HashCode *h_denom_pub,    struct TALER_Amount *total)  {    size_t len; @@ -558,6 +560,7 @@ TALER_EXCHANGE_verify_coin_history (          GNUNET_break_op (0);          return GNUNET_SYSERR;        } +      *h_denom_pub = dr.h_denom_pub;        if (NULL != dk)        {          /* check that deposit fee matches our expectations from /keys! */ @@ -615,6 +618,7 @@ TALER_EXCHANGE_verify_coin_history (          GNUNET_break_op (0);          return GNUNET_SYSERR;        } +      *h_denom_pub = rm.h_denom_pub;        if (NULL != dk)        {          /* check that melt fee matches our expectations from /keys! */ @@ -703,16 +707,159 @@ TALER_EXCHANGE_verify_coin_history (      else if (0 == strcasecmp (type,                                "RECOUP"))      { -      struct TALER_RecoupConfirmationPS pc; +      struct TALER_RecoupConfirmationPS pc = { +        .purpose.size = htonl (sizeof (pc)), +        .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP), +        .coin_pub = *coin_pub +      }; +      struct TALER_RecoupRequestPS rr = { +        .purpose.size = htonl (sizeof (pc)), +        .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP), +        .coin_pub = *coin_pub +      };        struct TALER_ExchangePublicKeyP exchange_pub;        struct TALER_ExchangeSignatureP exchange_sig; +      struct TALER_CoinSpendSignatureP coin_sig;        struct GNUNET_JSON_Specification spec[] = { +        TALER_JSON_spec_amount_nbo ("amount", +                                    &pc.recoup_amount),          GNUNET_JSON_spec_fixed_auto ("exchange_sig",                                       &exchange_sig),          GNUNET_JSON_spec_fixed_auto ("exchange_pub",                                       &exchange_pub),          GNUNET_JSON_spec_fixed_auto ("reserve_pub",                                       &pc.reserve_pub), +        GNUNET_JSON_spec_fixed_auto ("coin_sig", +                                     &coin_sig), +        GNUNET_JSON_spec_fixed_auto ("coin_blind", +                                     &rr.coin_blind), +        GNUNET_JSON_spec_fixed_auto ("h_denom_pub", +                                     &rr.h_denom_pub), +        TALER_JSON_spec_absolute_time_nbo ("timestamp", +                                           &pc.timestamp), +        GNUNET_JSON_spec_end () +      }; + +      if (GNUNET_OK != +          GNUNET_JSON_parse (transaction, +                             spec, +                             NULL, NULL)) +      { +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      TALER_amount_hton (&pc.recoup_amount, +                         &amount); +      if (GNUNET_OK != +          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP, +                                      &pc, +                                      &exchange_sig.eddsa_signature, +                                      &exchange_pub.eddsa_pub)) +      { +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      if (GNUNET_OK != +          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP, +                                      &rr, +                                      &coin_sig.eddsa_signature, +                                      &coin_pub->eddsa_pub)) +      { +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      *h_denom_pub = rr.h_denom_pub; +      add = GNUNET_YES; +    } +    else if (0 == strcasecmp (type, +                              "RECOUP-REFRESH")) +    { +      struct TALER_RecoupRefreshConfirmationPS pc = { +        .purpose.size = htonl (sizeof (pc)), +        .purpose.purpose = htonl ( +          TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH), +        .coin_pub = *coin_pub +      }; +      struct TALER_RecoupRequestPS rr = { +        .purpose.size = htonl (sizeof (pc)), +        .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP), +        .coin_pub = *coin_pub +      }; +      struct TALER_ExchangePublicKeyP exchange_pub; +      struct TALER_ExchangeSignatureP exchange_sig; +      struct TALER_CoinSpendSignatureP coin_sig; +      struct GNUNET_JSON_Specification spec[] = { +        TALER_JSON_spec_amount_nbo ("amount", +                                    &pc.recoup_amount), +        GNUNET_JSON_spec_fixed_auto ("exchange_sig", +                                     &exchange_sig), +        GNUNET_JSON_spec_fixed_auto ("exchange_pub", +                                     &exchange_pub), +        GNUNET_JSON_spec_fixed_auto ("coin_sig", +                                     &coin_sig), +        GNUNET_JSON_spec_fixed_auto ("old_coin_pub", +                                     &pc.old_coin_pub), +        GNUNET_JSON_spec_fixed_auto ("coin_blind", +                                     &rr.coin_blind), +        GNUNET_JSON_spec_fixed_auto ("h_denom_pub", +                                     &rr.h_denom_pub), +        TALER_JSON_spec_absolute_time_nbo ("timestamp", +                                           &pc.timestamp), +        GNUNET_JSON_spec_end () +      }; + +      if (GNUNET_OK != +          GNUNET_JSON_parse (transaction, +                             spec, +                             NULL, NULL)) +      { +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      TALER_amount_hton (&pc.recoup_amount, +                         &amount); +      if (GNUNET_OK != +          GNUNET_CRYPTO_eddsa_verify ( +            TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH, +            &pc, +            &exchange_sig.eddsa_signature, +            &exchange_pub.eddsa_pub)) +      { +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      if (GNUNET_OK != +          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP, +                                      &rr, +                                      &coin_sig.eddsa_signature, +                                      &coin_pub->eddsa_pub)) +      { +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      *h_denom_pub = rr.h_denom_pub; +      add = GNUNET_YES; +    } +    else if (0 == strcasecmp (type, +                              "OLD-COIN-RECOUP")) +    { +      struct TALER_RecoupRefreshConfirmationPS pc = { +        .purpose.size = htonl (sizeof (pc)), +        .purpose.purpose = htonl ( +          TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH), +        .old_coin_pub = *coin_pub +      }; +      struct TALER_ExchangePublicKeyP exchange_pub; +      struct TALER_ExchangeSignatureP exchange_sig; +      struct GNUNET_JSON_Specification spec[] = { +        TALER_JSON_spec_amount_nbo ("amount", +                                    &pc.recoup_amount), +        GNUNET_JSON_spec_fixed_auto ("exchange_sig", +                                     &exchange_sig), +        GNUNET_JSON_spec_fixed_auto ("exchange_pub", +                                     &exchange_pub), +        GNUNET_JSON_spec_fixed_auto ("coin_pub", +                                     &pc.coin_pub),          TALER_JSON_spec_absolute_time_nbo ("timestamp",                                             &pc.timestamp),          GNUNET_JSON_spec_end () @@ -726,9 +873,6 @@ TALER_EXCHANGE_verify_coin_history (          GNUNET_break_op (0);          return GNUNET_SYSERR;        } -      pc.purpose.size = htonl (sizeof (pc)); -      pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP); -      pc.coin_pub = *coin_pub;        TALER_amount_hton (&pc.recoup_amount,                           &amount);        if (GNUNET_OK != @@ -749,6 +893,7 @@ TALER_EXCHANGE_verify_coin_history (        GNUNET_assert (GNUNET_SYSERR == add);        return GNUNET_SYSERR;      } +      if (GNUNET_YES == add)      {        /* This amount should be added to the total */ @@ -779,9 +924,11 @@ TALER_EXCHANGE_verify_coin_history (          GNUNET_break_op (0);          return GNUNET_SYSERR;        } +      }    } +    /* Finally, subtract 'rtotal' from total to handle the subtractions */    if (0 >        TALER_amount_subtract (total, diff --git a/src/lib/exchange_api_deposit.c b/src/lib/exchange_api_deposit.c index ba3ba48e..51783dbb 100644 --- a/src/lib/exchange_api_deposit.c +++ b/src/lib/exchange_api_deposit.c @@ -246,13 +246,18 @@ verify_deposit_signature_ok (struct TALER_EXCHANGE_DepositHandle *dh,   * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not   */  static int -verify_deposit_signature_forbidden ( +verify_deposit_signature_conflict (    const struct TALER_EXCHANGE_DepositHandle *dh,    const json_t *json)  {    json_t *history;    struct TALER_Amount total; +  enum TALER_ErrorCode ec; +  struct GNUNET_HashCode h_denom_pub; +  memset (&h_denom_pub, +          0, +          sizeof (h_denom_pub));    history = json_object_get (json,                               "history");    if (GNUNET_OK != @@ -260,30 +265,46 @@ verify_deposit_signature_forbidden (                                            dh->dki.value.currency,                                            &dh->depconf.coin_pub,                                            history, +                                          &h_denom_pub,                                            &total))    {      GNUNET_break_op (0);      return GNUNET_SYSERR;    } -  if (0 > -      TALER_amount_add (&total, -                        &total, -                        &dh->amount_with_fee)) +  ec = TALER_JSON_get_error_code (json); +  switch (ec)    { -    /* clearly not OK if our transaction would have caused -       the overflow... */ -    return GNUNET_OK; -  } +  case TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS: +    if (0 > +        TALER_amount_add (&total, +                          &total, +                          &dh->amount_with_fee)) +    { +      /* clearly not OK if our transaction would have caused +         the overflow... */ +      return GNUNET_OK; +    } -  if (0 >= TALER_amount_cmp (&total, -                             &dh->dki.value)) -  { -    /* transaction should have still fit */ -    GNUNET_break (0); +    if (0 >= TALER_amount_cmp (&total, +                               &dh->dki.value)) +    { +      /* transaction should have still fit */ +      GNUNET_break (0); +      return GNUNET_SYSERR; +    } +    /* everything OK, proof of double-spending was provided */ +    return GNUNET_OK; +  case TALER_EC_COIN_CONFLICTING_DENOMINATION_KEY: +    if (0 != GNUNET_memcmp (&dh->dki.h_key, +                            &h_denom_pub)) +      return GNUNET_OK; /* indeed, proof with different denomination key provided */ +    /* invalid proof provided */ +    return GNUNET_SYSERR; +  default: +    /* unexpected error code */ +    GNUNET_break_op (0);      return GNUNET_SYSERR;    } -  /* everything OK, proof of double-spending was provided */ -  return GNUNET_OK;  } @@ -343,8 +364,8 @@ handle_deposit_finished (void *cls,    case MHD_HTTP_CONFLICT:      /* Double spending; check signatures on transaction history */      if (GNUNET_OK != -        verify_deposit_signature_forbidden (dh, -                                            j)) +        verify_deposit_signature_conflict (dh, +                                           j))      {        GNUNET_break_op (0);        hr.http_status = 0; diff --git a/src/lib/exchange_api_melt.c b/src/lib/exchange_api_melt.c index 1ef83bf7..2c1e85d7 100644 --- a/src/lib/exchange_api_melt.c +++ b/src/lib/exchange_api_melt.c @@ -178,6 +178,8 @@ verify_melt_signature_conflict (struct TALER_EXCHANGE_MeltHandle *mh,      GNUNET_JSON_spec_end ()    };    const struct MeltedCoin *mc; +  enum TALER_ErrorCode ec; +  struct GNUNET_HashCode h_denom_pub;    /* parse JSON reply */    if (GNUNET_OK != @@ -211,6 +213,9 @@ verify_melt_signature_conflict (struct TALER_EXCHANGE_MeltHandle *mh,    }    /* verify coin history */ +  memset (&h_denom_pub, +          0, +          sizeof (h_denom_pub));    history = json_object_get (json,                               "history");    if (GNUNET_OK != @@ -218,6 +223,7 @@ verify_melt_signature_conflict (struct TALER_EXCHANGE_MeltHandle *mh,                                            original_value.currency,                                            &coin_pub,                                            history, +                                          &h_denom_pub,                                            &total))    {      GNUNET_break_op (0); @@ -226,27 +232,43 @@ verify_melt_signature_conflict (struct TALER_EXCHANGE_MeltHandle *mh,    }    json_decref (history); -  /* check if melt operation was really too expensive given history */ -  if (0 > -      TALER_amount_add (&total, -                        &total, -                        &melt_value_with_fee)) +  ec = TALER_JSON_get_error_code (json); +  switch (ec)    { -    /* clearly not OK if our transaction would have caused -       the overflow... */ -    return GNUNET_OK; -  } +  case TALER_EC_MELT_INSUFFICIENT_FUNDS: +    /* check if melt operation was really too expensive given history */ +    if (0 > +        TALER_amount_add (&total, +                          &total, +                          &melt_value_with_fee)) +    { +      /* clearly not OK if our transaction would have caused +         the overflow... */ +      return GNUNET_OK; +    } -  if (0 >= TALER_amount_cmp (&total, -                             &original_value)) -  { -    /* transaction should have still fit */ -    GNUNET_break (0); +    if (0 >= TALER_amount_cmp (&total, +                               &original_value)) +    { +      /* transaction should have still fit */ +      GNUNET_break (0); +      return GNUNET_SYSERR; +    } + +    /* everything OK, valid proof of double-spending was provided */ +    return GNUNET_OK; +  case TALER_EC_COIN_CONFLICTING_DENOMINATION_KEY: +    if (0 != GNUNET_memcmp (&mh->dki.h_key, +                            &h_denom_pub)) +      return GNUNET_OK; /* indeed, proof with different denomination key provided */ +    /* invalid proof provided */ +    GNUNET_break_op (0); +    return GNUNET_SYSERR; +  default: +    /* unexpected error code */ +    GNUNET_break_op (0);      return GNUNET_SYSERR;    } - -  /* everything OK, valid proof of double-spending was provided */ -  return GNUNET_OK;  } diff --git a/src/lib/exchange_api_recoup.c b/src/lib/exchange_api_recoup.c index 723b4178..d8c59827 100644 --- a/src/lib/exchange_api_recoup.c +++ b/src/lib/exchange_api_recoup.c @@ -187,7 +187,9 @@ handle_recoup_finished (void *cls,        /* Insufficient funds, proof attached */        json_t *history;        struct TALER_Amount total; +      struct GNUNET_HashCode h_denom_pub;        const struct TALER_EXCHANGE_DenomPublicKey *dki; +      enum TALER_ErrorCode ec;        dki = &ph->pk;        history = json_object_get (j, @@ -197,6 +199,7 @@ handle_recoup_finished (void *cls,                                                dki->fee_deposit.currency,                                                &ph->coin_pub,                                                history, +                                              &h_denom_pub,                                                &total))        {          GNUNET_break_op (0); @@ -208,6 +211,43 @@ handle_recoup_finished (void *cls,          hr.ec = TALER_JSON_get_error_code (j);          hr.hint = TALER_JSON_get_error_hint (j);        } +      ec = TALER_JSON_get_error_code (j); +      switch (ec) +      { +      case TALER_EC_RECOUP_COIN_BALANCE_ZERO: +        if (0 > TALER_amount_cmp (&total, +                                  &dki->value)) +        { +          /* recoup MAY have still been possible */ +          /* FIXME: This code may falsely complain, as we do not +             know that the smallest denomination offered by the +             exchange is here. We should look at the key +             structure of ph->exchange, and find the smallest +             _currently withdrawable_ denomination and check +             if the value remaining would suffice... */GNUNET_break_op (0); +          hr.http_status = 0; +          hr.ec = TALER_EC_RECOUP_REPLY_MALFORMED; +          break; +        } +        break; +      case TALER_EC_COIN_CONFLICTING_DENOMINATION_KEY: +        if (0 == GNUNET_memcmp (&ph->pk.h_key, +                                &h_denom_pub)) +        { +          /* invalid proof provided */ +          GNUNET_break_op (0); +          hr.http_status = 0; +          hr.ec = TALER_EC_RECOUP_REPLY_MALFORMED; +          break; +        } +        /* valid error from exchange */ +        break; +      default: +        GNUNET_break_op (0); +        hr.http_status = 0; +        hr.ec = TALER_EC_RECOUP_REPLY_MALFORMED; +        break; +      }        ph->cb (ph->cb_cls,                &hr,                NULL,  | 
