diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/mint-lib/mint_api_json.c | 36 | ||||
| -rw-r--r-- | src/mint-lib/mint_api_json.h | 20 | ||||
| -rw-r--r-- | src/mint-lib/mint_api_withdraw.c | 242 | 
3 files changed, 285 insertions, 13 deletions
diff --git a/src/mint-lib/mint_api_json.c b/src/mint-lib/mint_api_json.c index e2a73bdd..a28293cf 100644 --- a/src/mint-lib/mint_api_json.c +++ b/src/mint-lib/mint_api_json.c @@ -72,6 +72,20 @@ parse_json (json_t *root,        }        break; +    case MAJ_CMD_STRING: +      { +        const char *str; + +        str = json_string_value (pos); +        if (NULL == str) +        { +          GNUNET_break_op (0); +          return i; +        } +        *spec[i].details.strptr = str; +      } +      break; +      case MAJ_CMD_BINARY_FIXED:        {          const char *str; @@ -274,6 +288,8 @@ parse_free (struct MAJ_Specification *spec,        break;      case MAJ_CMD_BINARY_FIXED:        break; +    case MAJ_CMD_STRING: +      break;      case MAJ_CMD_BINARY_VARIABLE:        GNUNET_free (*spec[i].details.variable_data.dest_p);        *spec[i].details.variable_data.dest_p = NULL; @@ -341,6 +357,26 @@ MAJ_parse_free (struct MAJ_Specification *spec)  /** + * The expected field stores a string. + * + * @param name name of the JSON field + * @param strptr where to store a pointer to the field + */ +struct MAJ_Specification +MAJ_spec_string (const char *name, +                 const char **strptr) +{ +  struct MAJ_Specification ret = +    { +      .cmd = MAJ_CMD_STRING, +      .field = name, +      .details.strptr = strptr +    }; +  return ret; +} + + +/**   * Specification for parsing an absolute time value.   *   * @param name name of the JSON field diff --git a/src/mint-lib/mint_api_json.h b/src/mint-lib/mint_api_json.h index ec3b63cb..69019251 100644 --- a/src/mint-lib/mint_api_json.h +++ b/src/mint-lib/mint_api_json.h @@ -74,9 +74,9 @@ enum MAJ_Command    MAJ_CMD_EDDSA_SIGNATURE,    /** -   * Parse  at current position. +   * Parse `const char *` JSON string at current position.     */ -  MAJ_CMD_B, +  MAJ_CMD_STRING,    /**     * Parse  at current position. @@ -176,6 +176,11 @@ struct MAJ_Specification      } eddsa_signature; +    /** +     * Where to store a pointer to the string. +     */ +    const char **strptr; +    } details;  }; @@ -230,6 +235,17 @@ MAJ_parse_free (struct MAJ_Specification *spec);  /** + * The expected field stores a string. + * + * @param name name of the JSON field + * @param strptr where to store a pointer to the field + */ +struct MAJ_Specification +MAJ_spec_string (const char *name, +                 const char **strptr); + + +/**   * Absolute time.   *   * @param name name of the JSON field diff --git a/src/mint-lib/mint_api_withdraw.c b/src/mint-lib/mint_api_withdraw.c index c4b81b4b..c5f7b33a 100644 --- a/src/mint-lib/mint_api_withdraw.c +++ b/src/mint-lib/mint_api_withdraw.c @@ -360,6 +360,11 @@ struct TALER_MINT_WithdrawSignHandle    struct GNUNET_HashCode c_hash;    /** +   * Public key of the reserve we are withdrawing from. +   */ +  struct TALER_ReservePublicKeyP reserve_pub; + +  /**     * The size of the download buffer     */    size_t buf_size; @@ -433,6 +438,213 @@ withdraw_sign_ok (struct TALER_MINT_WithdrawSignHandle *wsh,  /** + * We got a 402 PAYMENT REQUIRED response for the /withdraw/sign operation. + * Check the signatures on the withdraw transactions in the provided + * history and that the balances add up.  We don't do anything directly + * with the information, as the JSON will be returned to the application. + * However, our job is ensuring that the mint followed the protocol, and + * this in particular means checking all of the signatures in the history. + * + * @param wsh operation handle + * @param json reply from the mint + * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors + */ +static int +withdraw_sign_payment_required (struct TALER_MINT_WithdrawSignHandle *wsh, +                                json_t *json) +{ +  struct TALER_Amount balance; +  struct TALER_Amount balance_from_history; +  struct TALER_Amount total_in; +  struct TALER_Amount total_out; +  struct TALER_Amount requested_amount; +  json_t *history; +  size_t len; +  size_t off; +  struct MAJ_Specification spec[] = { +    MAJ_spec_amount ("balance", &balance), +    MAJ_spec_end +  }; + +  if (GNUNET_OK != +      MAJ_parse_json (json, +                      spec)) +  { +    GNUNET_break_op (0); +    return GNUNET_SYSERR; +  } +  history = json_object_get (json, +                             "history"); +  if (NULL == history) +  { +    GNUNET_break_op (0); +    return GNUNET_SYSERR; +  } + +  /* FIXME: re-use/share this code with history processing +     on /withdraw/status above! */ +  /* go over transaction history and compute +     total incoming and outgoing amounts */ +  len = json_array_size (history); +  TALER_amount_get_zero (balance.currency, +                         &total_in); +  TALER_amount_get_zero (balance.currency, +                         &total_out); +  for (off=0;off<len;off++) +  { +    json_t *transaction; +    struct TALER_Amount amount; +    const char *type; +    struct MAJ_Specification hist_spec[] = { +      MAJ_spec_string ("type", &type), +      MAJ_spec_amount ("amount", +                       &amount), +      /* 'wire' and 'signature' are optional depending on 'type'! */ +      MAJ_spec_end +    }; + +    transaction = json_array_get (history, +                                  off); +    if (GNUNET_OK != +        MAJ_parse_json (transaction, +                        hist_spec)) +    { +      GNUNET_break_op (0); +      return GNUNET_SYSERR; +    } + +    if (0 == strcasecmp (type, +                         "DEPOSIT")) +    { +      json_t *wire; + +      if (GNUNET_OK != +          TALER_amount_add (&total_in, +                            &total_in, +                            &amount)) +      { +        /* overflow in history already!? inconceivable! Bad mint! */ +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      wire = json_object_get (transaction, +                              "wire"); +      /* check 'wire' is a JSON object (no need to check wireformat, +         but we do at least expect "some" JSON object here) */ +      if ( (NULL == wire) || +           (! json_is_object (wire)) ) +      { +        /* not even a JSON 'wire' specification, not acceptable */ +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      /* end type==DEPOSIT */ +    } +    else if (0 == strcasecmp (type, +                              "WITHDRAW")) +    { +      struct GNUNET_CRYPTO_EccSignaturePurpose *purpose; +      const struct TALER_WithdrawRequestPS *withdraw_purpose; +      struct TALER_Amount amount_from_purpose; +      struct MAJ_Specification withdraw_spec[] = { +        MAJ_spec_eddsa_signed_purpose ("signature", +                                       &purpose, +                                       &wsh->reserve_pub.eddsa_pub), +        MAJ_spec_end +      }; + +      if (GNUNET_OK != +          MAJ_parse_json (transaction, +                          withdraw_spec)) +      { +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      /* Check that the signature actually signed a withdraw request */ +      if ( (ntohl (purpose->purpose) != TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW) || +           (ntohl (purpose->size) != sizeof (struct TALER_WithdrawRequestPS)) ) +      { +        GNUNET_break_op (0); +        MAJ_parse_free (withdraw_spec); +        return GNUNET_SYSERR; +      } +      withdraw_purpose = (const struct TALER_WithdrawRequestPS *) purpose; +      TALER_amount_ntoh (&amount_from_purpose, +                         &withdraw_purpose->amount_with_fee); +      if (0 != TALER_amount_cmp (&amount, +                                 &amount_from_purpose)) +      { +        GNUNET_break_op (0); +        MAJ_parse_free (withdraw_spec); +        return GNUNET_SYSERR; +      } + +      /* FIXME: ought to also check that the same withdraw transaction +         isn't listed twice by the mint... */ +      if (GNUNET_OK != +          TALER_amount_add (&total_out, +                            &total_out, +                            &amount)) +      { +        /* overflow in history already!? inconceivable! Bad mint! */ +        GNUNET_break_op (0); +        MAJ_parse_free (withdraw_spec); +        return GNUNET_SYSERR; +      } +      /* end type==WITHDRAW */ +    } +    else +    { +      /* unexpected 'type', protocol incompatibility, complain! */ +      GNUNET_break_op (0); +      return GNUNET_SYSERR; +    } +  } + +  /* check balance = total_in - total_out < withdraw-amount */ +  if (GNUNET_SYSERR == +      TALER_amount_subtract (&balance_from_history, +                             &total_in, +                             &total_out)) +  { +    /* total_in < total_out, why did the mint ever allow this!? */ +    GNUNET_break_op (0); +    return GNUNET_SYSERR; +  } +  if (0 != +      TALER_amount_cmp (&balance_from_history, +                        &balance)) +  { +    /* mint cannot add up balances!? */ +    GNUNET_break_op (0); +    return GNUNET_SYSERR; +  } +  /* Compute how much we expected to charge to the reserve */ +  if (GNUNET_OK != +      TALER_amount_add (&requested_amount, +                        &wsh->pk->value, +                        &wsh->pk->fee_withdraw)) +  { +    /* Overflow here? Very strange, our CPU must be fried... */ +    GNUNET_break (0); +    return GNUNET_SYSERR; +  } +  /* Check that funds were really insufficient */ +  if (0 < /* >= ??? -- FIXME: check operator! */ +      TALER_amount_cmp (&requested_amount, +                        &balance)) +  { +    /* mint cannot add up balances!? */ +    GNUNET_break_op (0); +    return GNUNET_SYSERR; +  } + + +  return GNUNET_OK; +} + + +/**   * Function called when we're done processing the   * HTTP /withdraw/sign request.   * @@ -487,19 +699,28 @@ handle_withdraw_sign_finished (void *cls,      /* This should never happen, either us or the mint is buggy         (or API version conflict); just pass JSON reply to the application */      break; -  case MHD_HTTP_FORBIDDEN: -    GNUNET_break (0); // FIXME: not implemented +  case MHD_HTTP_PAYMENT_REQUIRED: +    /* The mint says that the reserve has insufficient funds; +       check the signatures in the history... */ +    if (GNUNET_OK != +        withdraw_sign_payment_required (wsh, +                                        json)) +    { +      GNUNET_break_op (0); +      response_code = 0; +    }      break;    case MHD_HTTP_UNAUTHORIZED: -    GNUNET_break (0); // FIXME: not implemented +    GNUNET_break (0);      /* Nothing really to verify, mint says one of the signatures is         invalid; as we checked them, this should never happen, we         should pass the JSON reply to the application */      break;    case MHD_HTTP_NOT_FOUND: -    GNUNET_break (0); // FIXME: not implemented -    /* Nothing really to verify, this should never -       happen, we should pass the JSON reply to the application */ +    /* Nothing really to verify, the mint basically just says +       that it doesn't know this reserve.  Can happen if we +       query before the wire transfer went through. +       We should simply pass the JSON reply to the application. */      break;    case MHD_HTTP_INTERNAL_SERVER_ERROR:      /* Server had an internal issue; we should retry, but this API @@ -594,7 +815,6 @@ TALER_MINT_withdraw_sign (struct TALER_MINT_Handle *mint,  {    struct TALER_MINT_WithdrawSignHandle *wsh;    struct TALER_WithdrawRequestPS req; -  struct TALER_ReservePublicKeyP reserve_pub;    struct TALER_ReserveSignatureP reserve_sig;    struct TALER_CoinSpendPublicKeyP coin_pub;    struct TALER_MINT_Context *ctx; @@ -619,10 +839,10 @@ TALER_MINT_withdraw_sign (struct TALER_MINT_Handle *mint,                                            pk->key.rsa_public_key,                                            &coin_ev);    GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, -                                      &reserve_pub.eddsa_pub); +                                      &wsh->reserve_pub.eddsa_pub);    req.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS));    req.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW); -  req.reserve_pub = reserve_pub; +  req.reserve_pub = wsh->reserve_pub;    if (GNUNET_OK !=        TALER_amount_add (&amount_with_fee,                          &pk->fee_withdraw, @@ -652,8 +872,8 @@ TALER_MINT_withdraw_sign (struct TALER_MINT_Handle *mint,                              "denom_pub", TALER_json_from_rsa_public_key (pk->key.rsa_public_key),                              "coin_ev", TALER_json_from_data (coin_ev,                                                               coin_ev_size), -                            "reserve_pub", TALER_json_from_data (&reserve_pub, -                                                                 sizeof (reserve_pub)), +                            "reserve_pub", TALER_json_from_data (&wsh->reserve_pub, +                                                                 sizeof (struct TALER_ReservePublicKeyP)),                              "reserve_sig", TALER_json_from_data (&reserve_sig,                                                                   sizeof (reserve_sig)));    GNUNET_free (coin_ev);  | 
