diff options
| author | Christian Grothoff <christian@grothoff.org> | 2015-01-29 16:26:58 +0100 | 
|---|---|---|
| committer | Christian Grothoff <christian@grothoff.org> | 2015-01-29 16:26:58 +0100 | 
| commit | 1e3e6fa5990eb2504a69279039ee776a19ff1ec2 (patch) | |
| tree | 4fb455353ac8075a12f68b2a009a4c83596ce788 /src | |
| parent | 4a27969e5e439fb0f277dcf9f0461817d83e36b4 (diff) | |
finish cleanup of /refresh/melt parsing
Diffstat (limited to 'src')
| -rw-r--r-- | src/mint/taler-mint-httpd_parsing.h | 2 | ||||
| -rw-r--r-- | src/mint/taler-mint-httpd_refresh.c | 460 | 
2 files changed, 241 insertions, 221 deletions
diff --git a/src/mint/taler-mint-httpd_parsing.h b/src/mint/taler-mint-httpd_parsing.h index f1409017..2a2d54ea 100644 --- a/src/mint/taler-mint-httpd_parsing.h +++ b/src/mint/taler-mint-httpd_parsing.h @@ -270,8 +270,6 @@ TALER_MINT_release_parsed_data (struct GNUNET_MINT_ParseFieldSpec *spec);   */  #define TALER_MINT_PARSE_RSA_SIGNATURE(field,ptrsig) { field, ptrsig, 0, 0, JNAV_RET_RSA_SIGNATURE, 0 } - -  /**   * Generate line in parser specification indicating the end of the spec.   */ diff --git a/src/mint/taler-mint-httpd_refresh.c b/src/mint/taler-mint-httpd_refresh.c index 198ad423..0abc82eb 100644 --- a/src/mint/taler-mint-httpd_refresh.c +++ b/src/mint/taler-mint-httpd_refresh.c @@ -43,93 +43,14 @@  #include "taler-mint-httpd_keystate.h" -static int -check_confirm_signature (struct MHD_Connection *connection, -                         json_t *coin_info, -                         const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, -                         const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub) -{ -  struct RefreshMeltConfirmSignRequestBody body; -  struct GNUNET_CRYPTO_EcdsaSignature sig; -  int res; - -  body.purpose.size = htonl (sizeof (struct RefreshMeltConfirmSignRequestBody)); -  body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_CONFIRM); -  body.session_pub = *session_pub; - -  res = GNUNET_MINT_parse_navigate_json (connection, coin_info, -                                  JNAV_FIELD, "confirm_sig", -                                  JNAV_RET_DATA, -                                  &sig, -                                  sizeof (struct GNUNET_CRYPTO_EcdsaSignature)); - -  if (GNUNET_OK != res) -  { -    GNUNET_break (GNUNET_SYSERR != res); -    return res; -  } - -  if (GNUNET_OK != -      GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_REFRESH_MELT_CONFIRM, -                                  &body.purpose, -                                  &sig, -                                  coin_pub)) -  { -    if (MHD_YES != -        TALER_MINT_reply_json_pack (connection, -                                    MHD_HTTP_UNAUTHORIZED, -                                    "{s:s}", -                                    "error", "signature invalid")) -      return GNUNET_SYSERR; -    return GNUNET_NO; -  } - -  return GNUNET_OK; -} - -  /** - * Extract public coin information from a JSON object. - * - * @param connection the connection to send error responses to - * @param root the JSON object to extract the coin info from - * @return #GNUNET_YES if coin public info in JSON was valid - *         #GNUNET_NO JSON was invalid, response was generated - *         #GNUNET_SYSERR on internal error - */ -static int -request_json_require_coin_public_info (struct MHD_Connection *connection, -                                       json_t *root, -                                       struct TALER_CoinPublicInfo *r_public_info) -{ -  int ret; -  struct GNUNET_CRYPTO_rsa_Signature *sig; -  struct GNUNET_CRYPTO_rsa_PublicKey *pk; -  struct GNUNET_MINT_ParseFieldSpec spec[] = -    { -      TALER_MINT_PARSE_FIXED ("coin_pub", &r_public_info->coin_pub), -      TALER_MINT_PARSE_RSA_SIGNATURE ("denom_sig", &sig), -      TALER_MINT_PARSE_RSA_PUBLIC_KEY ("denom_pub", &pk), -      TALER_MINT_PARSE_END -    }; - -  ret = TALER_MINT_parse_json_data (connection, -                                    root, -                                    spec); -  if (GNUNET_OK != ret) -    return ret; -  // TALER_MINT_release_parsed_data (spec); -  r_public_info->denom_sig = sig; -  r_public_info->denom_pub = pk; -  return GNUNET_OK; -} - - -/** - * Verify a signature that is encoded in a JSON object + * Verify a signature that is encoded in a JSON object.  Extracts + * the signature and its associated purpose and checks that it + * matches the specified @a purpose and @a pub public key.  Any + * errors are reported via appropriate response messages.   *   * @param connection the connection to send errors to - * @param root the JSON object with the signature + * @param json_sig the JSON object with the signature   * @param the public key that the signature was created with   * @param purpose the signed message   * @return #GNUNET_YES if the signature was valid @@ -138,9 +59,9 @@ request_json_require_coin_public_info (struct MHD_Connection *connection,   */  static int  request_json_check_signature (struct MHD_Connection *connection, -                              const json_t *root, +                              const json_t *json_sig,                                const struct GNUNET_CRYPTO_EddsaPublicKey *pub, -                              struct GNUNET_CRYPTO_EccSignaturePurpose *purpose) +                              const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose)  {    struct GNUNET_CRYPTO_EddsaSignature signature;    int size; @@ -148,20 +69,25 @@ request_json_check_signature (struct MHD_Connection *connection,    int res;    json_t *el; -  res = GNUNET_MINT_parse_navigate_json (connection, root, -                                  JNAV_FIELD, "sig", -                                  JNAV_RET_DATA, -                                  &signature, -                                  sizeof (struct GNUNET_CRYPTO_EddsaSignature)); +  /* TODO: use specification array to simplify the parsing! */ +  res = GNUNET_MINT_parse_navigate_json (connection, +                                         json_sig, +                                         JNAV_FIELD, +                                         "sig", +                                         JNAV_RET_DATA, +                                         &signature, +                                         sizeof (struct GNUNET_CRYPTO_EddsaSignature));    if (GNUNET_OK != res)      return res; -  res = GNUNET_MINT_parse_navigate_json (connection, root, -                                  JNAV_FIELD, "purpose", -                                  JNAV_RET_TYPED_JSON, -                                  JSON_INTEGER, -                                  &el); +  res = GNUNET_MINT_parse_navigate_json (connection, +                                         json_sig, +                                         JNAV_FIELD, +                                         "purpose", +                                         JNAV_RET_TYPED_JSON, +                                         JSON_INTEGER, +                                         &el);    if (GNUNET_OK != res)      return res; @@ -170,18 +96,20 @@ request_json_check_signature (struct MHD_Connection *connection,    if (purpose_num != ntohl (purpose->purpose))    { -    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "signature invalid (purpose wrong)\n"); +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "signature invalid (purpose wrong)\n");      return TALER_MINT_reply_json_pack (connection,                                         MHD_HTTP_BAD_REQUEST,                                         "{s:s}",                                         "error", "signature invalid (purpose)");    } -  res = GNUNET_MINT_parse_navigate_json (connection, root, -                                  JNAV_FIELD, "size", -                                  JNAV_RET_TYPED_JSON, -                                  JSON_INTEGER, -                                  &el); +  res = GNUNET_MINT_parse_navigate_json (connection, +                                         json_sig, +                                         JNAV_FIELD, "size", +                                         JNAV_RET_TYPED_JSON, +                                         JSON_INTEGER, +                                         &el);    if (GNUNET_OK != res)      return res; @@ -190,12 +118,14 @@ request_json_check_signature (struct MHD_Connection *connection,    if (size != ntohl (purpose->size))    { -    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "signature invalid (size wrong)\n"); +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "signature invalid (size wrong)\n");      return TALER_MINT_reply_json_pack (connection,                                         MHD_HTTP_BAD_REQUEST,                                         GNUNET_NO, GNUNET_SYSERR,                                         "{s:s}", -                                       "error", "signature invalid (size)"); +                                       "error", +                                       "signature invalid (size)");    }    if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (purpose_num, @@ -203,7 +133,8 @@ request_json_check_signature (struct MHD_Connection *connection,                                                 &signature,                                                 pub))    { -    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "signature invalid (did not verify)\n"); +    GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +                "signature invalid (did not verify)\n");      return TALER_MINT_reply_json_pack (connection,                                         MHD_HTTP_UNAUTHORIZED,                                         "{s:s}", @@ -216,7 +147,7 @@ request_json_check_signature (struct MHD_Connection *connection,  /** - * Handle a "/refresh/melt" request after the first parsing has happened. + * Handle a "/refresh/melt" request after the main JSON parsing has happened.   * We now need to validate the coins being melted and the session signature   * and then hand things of to execute the melt operation.   * @@ -228,114 +159,21 @@ request_json_check_signature (struct MHD_Connection *connection,   * @return MHD result code   */  static int -handle_refresh_melt_json (struct MHD_Connection *connection, -                          const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, -                          const json_t *new_denoms, -                          const json_t *melt_coins, -                          const json_t *melt_sig_json) +handle_refresh_melt_binary (struct MHD_Connection *connection, +                            const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, +                            unsigned int num_new_denoms, +                            struct GNUNET_CRYPTO_rsa_PublicKey *const*denom_pubs, +                            unsigned int coin_count, +                            struct TALER_CoinPublicInfo *coin_public_infos, +                            const json_t *melt_sig_json)  {    int res; -  unsigned int num_new_denoms;    unsigned int i; -  struct GNUNET_CRYPTO_rsa_PublicKey **denom_pubs; -  struct TALER_CoinPublicInfo *coin_public_infos; -  unsigned int coin_count;    struct GNUNET_HashContext *hash_context;    struct GNUNET_HashCode melt_hash; -  struct MintKeyState *key_state;    struct RefreshMeltSignatureBody body;    char *buf;    size_t buf_size; -  struct TALER_MINT_DenomKeyIssuePriv *dki; - -  num_new_denoms = json_array_size (new_denoms); - -  denom_pubs = GNUNET_malloc (num_new_denoms * -                              sizeof (struct GNUNET_CRYPTO_rsa_PublicKey *)); - -  for (i=0;i<num_new_denoms;i++) -  { -    res = GNUNET_MINT_parse_navigate_json (connection, new_denoms, -                                           JNAV_INDEX, (int) i, -                                           JNAV_RET_DATA_VAR, -                                           &buf, -                                           &buf_size); -    if (GNUNET_OK != res) -    { -      GNUNET_free (denom_pubs); -      /* FIXME: proper cleanup! */ -      return res; -    } -    denom_pubs[i] = GNUNET_CRYPTO_rsa_public_key_decode (buf, buf_size); -    GNUNET_free (buf); -    if (NULL == denom_pubs[i]) -    { -      GNUNET_free (denom_pubs); -      /* FIXME: proper cleanup! */ -      /* FIXME: generate error reply */ -      return GNUNET_SYSERR; -    } -  } - - - - -  coin_count = json_array_size (melt_coins); -  coin_public_infos = GNUNET_malloc (coin_count * -                                     sizeof (struct TALER_CoinPublicInfo)); -  key_state = TALER_MINT_key_state_acquire (); -  for (i = 0; i < coin_count; i++) -    { -      /* decode JSON data on coin to melt */ -      res = request_json_require_coin_public_info (connection, -                                                   json_array_get (melt_coins, i), -                                                   &coin_public_infos[i]); -      if (GNUNET_OK != res) -        { -          GNUNET_break (GNUNET_SYSERR != res); -          // FIXME: leaks! -          TALER_MINT_key_state_release (key_state); -          return res; -        } -      /* check that this coin's private key was used to sign that -         we should melt it */ -      if (GNUNET_OK != -          (res = check_confirm_signature (connection, -                                          json_array_get (melt_coins, i), -                                          &coin_public_infos[i].coin_pub, -                                          refresh_session_pub))) -      { -        GNUNET_break (GNUNET_SYSERR != res); -        // FIXME: leaks! -        TALER_MINT_key_state_release (key_state); -        return res; -      } -      /* check coin denomination is valid */ -      dki = TALER_MINT_get_denom_key (key_state, -                                      coin_public_infos[i].denom_pub); -      if (NULL == dki) -      { -        TALER_MINT_key_state_release (key_state); -        LOG_WARNING ("Unknown denomination key in /refresh/melt request\n"); -        TALER_MINT_key_state_release (key_state); -        return TALER_MINT_reply_arg_invalid (connection, -                                             "melt_coins"); -      } -      /* check mint signature on the coin */ -      if (GNUNET_OK != -          TALER_test_coin_valid (&coin_public_infos[i])) -        { -          // FIXME: leaks! -          TALER_MINT_key_state_release (key_state); -          return (MHD_YES == -                TALER_MINT_reply_json_pack (connection, -                                            MHD_HTTP_NOT_FOUND, -                                            "{s:s}", -                                            "error", "coin invalid")) -          ? GNUNET_NO : GNUNET_SYSERR; -        } -    } -  TALER_MINT_key_state_release (key_state);    /* check that signature from the session public key is ok */    hash_context = GNUNET_CRYPTO_hash_context_start (); @@ -364,19 +202,202 @@ handle_refresh_melt_json (struct MHD_Connection *connection,                                             melt_sig_json,                                             refresh_session_pub,                                             &body.purpose))) -  { -    // FIXME: generate proper error reply      return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; + +  return TALER_MINT_db_execute_refresh_melt (connection, +                                             refresh_session_pub, +                                             num_new_denoms, +                                             denom_pubs, +                                             coin_count, +                                             coin_public_infos); +} + + +/** + * Extract public coin information from a JSON object and verify + * that the signature shows that this coin is to be melted into + * the given @a session_pub melting session, and that this is + * a valid coin (we know the denomination key and the signature + * on it is valid).  Essentially, this does all of the per-coin + * checks that can be done before the transaction starts. + * + * @param connection the connection to send error responses to + * @param session_pub public key of the session the coin is melted into + * @param coin_info the JSON object to extract the coin info from + * @param r_public_info[OUT] set to the coin's public information + * @return #GNUNET_YES if coin public info in JSON was valid + *         #GNUNET_NO JSON was invalid, response was generated + *         #GNUNET_SYSERR on internal error + */ +static int +get_and_verify_coin_public_info (struct MHD_Connection *connection, +                                 const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, +                                 json_t *coin_info, +                                 struct TALER_CoinPublicInfo *r_public_info) +{ +  int ret; +  struct GNUNET_CRYPTO_EcdsaSignature melt_sig; +  struct GNUNET_CRYPTO_rsa_Signature *sig; +  struct GNUNET_CRYPTO_rsa_PublicKey *pk; +  struct RefreshMeltConfirmSignRequestBody body; +  struct MintKeyState *key_state; +  struct TALER_MINT_DenomKeyIssuePriv *dki; +  struct GNUNET_MINT_ParseFieldSpec spec[] = +    { +      TALER_MINT_PARSE_FIXED ("coin_pub", &r_public_info->coin_pub), +      TALER_MINT_PARSE_RSA_SIGNATURE ("denom_sig", &sig), +      TALER_MINT_PARSE_RSA_PUBLIC_KEY ("denom_pub", &pk), +      TALER_MINT_PARSE_FIXED ("confirm_sig", &melt_sig), +      TALER_MINT_PARSE_END +    }; + +  ret = TALER_MINT_parse_json_data (connection, +                                    coin_info, +                                    spec); +  if (GNUNET_OK != ret) +    return ret; +  /* TODO: include amount of coin value to be melted here!? */ +  body.purpose.size = htonl (sizeof (struct RefreshMeltConfirmSignRequestBody)); +  body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_MELT_CONFIRM); +  body.session_pub = *session_pub; +  if (GNUNET_OK != +      GNUNET_CRYPTO_ecdsa_verify (TALER_SIGNATURE_REFRESH_MELT_CONFIRM, +                                  &body.purpose, +                                  &melt_sig, +                                  &r_public_info->coin_pub)) +  { +    TALER_MINT_release_parsed_data (spec); +    if (MHD_YES != +        TALER_MINT_reply_json_pack (connection, +                                    MHD_HTTP_UNAUTHORIZED, +                                    "{s:s}", +                                    "error", "signature invalid")) +      return GNUNET_SYSERR; +    return GNUNET_NO; +  } +  key_state = TALER_MINT_key_state_acquire (); +  dki = TALER_MINT_get_denom_key (key_state, +                                  pk); +  /* FIXME: need to check if denomination key is still +     valid for issuing! (#3634) */ +  if (NULL == dki) +  { +    TALER_MINT_key_state_release (key_state); +    LOG_WARNING ("Unknown denomination key in /refresh/melt request\n"); +    return TALER_MINT_reply_arg_invalid (connection, +                                         "denom_pub");    } +  TALER_MINT_key_state_release (key_state); +  /* check mint signature on the coin */ +  r_public_info->denom_sig = sig; +  r_public_info->denom_pub = pk; +  if (GNUNET_OK != +      TALER_test_coin_valid (r_public_info)) +  { +    TALER_MINT_release_parsed_data (spec); +    r_public_info->denom_sig = NULL; +    r_public_info->denom_pub = NULL; +    return (MHD_YES == +            TALER_MINT_reply_json_pack (connection, +                                        MHD_HTTP_NOT_FOUND, +                                        "{s:s}", +                                        "error", "coin invalid")) +      ? GNUNET_NO : GNUNET_SYSERR; +  } +  return GNUNET_OK; +} -  res = TALER_MINT_db_execute_refresh_melt (connection, -                                            refresh_session_pub, -                                            num_new_denoms, -                                            denom_pubs, -                                            coin_count, -                                            coin_public_infos); -  // FIXME: free memory + +/** + * Handle a "/refresh/melt" request after the first parsing has happened. + * We now need to validate the coins being melted and the session signature + * and then hand things of to execute the melt operation.  This function + * parses the JSON arrays and then passes processing on to + * #handle_refresh_melt_binary(). + * + * @param connection the MHD connection to handle + * @param refresh_session_pub public key of the melt operation + * @param new_denoms array of denomination keys + * @param melt_coins array of coins to melt + * @param melt_sig_json signature affirming the melt operation + * @return MHD result code + */ +static int +handle_refresh_melt_json (struct MHD_Connection *connection, +                          const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, +                          const json_t *new_denoms, +                          const json_t *melt_coins, +                          const json_t *melt_sig_json) +{ +  int res; +  unsigned int i; +  unsigned int j; +  struct GNUNET_CRYPTO_rsa_PublicKey **denom_pubs; +  unsigned int num_new_denoms; +  struct TALER_CoinPublicInfo *coin_public_infos; +  unsigned int coin_count; + +  num_new_denoms = json_array_size (new_denoms); +  denom_pubs = GNUNET_malloc (num_new_denoms * +                              sizeof (struct GNUNET_CRYPTO_rsa_PublicKey *)); +  for (i=0;i<num_new_denoms;i++) +  { +    res = GNUNET_MINT_parse_navigate_json (connection, new_denoms, +                                           JNAV_INDEX, (int) i, +                                           JNAV_RET_RSA_PUBLIC_KEY, &denom_pubs[i]); +    if (GNUNET_OK != res) +    { +      for (j=0;j<i;j++) +        GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j]); +      GNUNET_free (denom_pubs); +      return res; +    } +  } + +  coin_count = json_array_size (melt_coins); +  coin_public_infos = GNUNET_malloc (coin_count * +                                     sizeof (struct TALER_CoinPublicInfo)); +  for (i=0;i<coin_count;i++) +  { +    /* decode JSON data on coin to melt */ +    res = get_and_verify_coin_public_info (connection, +                                           refresh_session_pub, +                                           json_array_get (melt_coins, i), +                                           &coin_public_infos[i]); +    if (GNUNET_OK != res) +    { +      for (j=0;j<i;j++) +      { +        GNUNET_CRYPTO_rsa_public_key_free (coin_public_infos[j].denom_pub); +        GNUNET_CRYPTO_rsa_signature_free (coin_public_infos[j].denom_sig); +      } +      GNUNET_free (coin_public_infos); +      for (j=0;j<num_new_denoms;j++) +        GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j]); +      GNUNET_free (denom_pubs); +      return (GNUNET_NO == res) ? MHD_YES : MHD_NO; +    } +  } + +  res = handle_refresh_melt_binary (connection, +                                    refresh_session_pub, +                                    num_new_denoms, +                                    denom_pubs, +                                    coin_count, +                                    coin_public_infos, +                                    melt_sig_json); +  for (j=0;j<coin_count;j++) +  { +    GNUNET_CRYPTO_rsa_public_key_free (coin_public_infos[j].denom_pub); +    GNUNET_CRYPTO_rsa_signature_free (coin_public_infos[j].denom_sig); +  } +  GNUNET_free (coin_public_infos); +  for (j=0;j<num_new_denoms;j++) +  { +    GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j]); +  } +  GNUNET_free (denom_pubs);    return res;  } @@ -660,10 +681,11 @@ TALER_MINT_handler_refresh_commit (struct RequestHandler *rh,    body.purpose.purpose = htonl (TALER_SIGNATURE_REFRESH_COMMIT);    body.purpose.size = htonl (sizeof (struct RefreshCommitSignatureBody)); -  if (GNUNET_OK != (res = request_json_check_signature (connection, -                                                        commit_sig_json, -                                                        &refresh_session_pub, -                                                        &body.purpose))) +  if (GNUNET_OK != +      (res = request_json_check_signature (connection, +                                           commit_sig_json, +                                           &refresh_session_pub, +                                           &body.purpose)))    {      return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;    }  | 
