make TALER_MINT_parse_json_data more expressive, add RSA types as well

This commit is contained in:
Christian Grothoff 2015-01-29 15:45:05 +01:00
parent a284561298
commit 4a27969e5e
4 changed files with 690 additions and 526 deletions

View File

@ -127,8 +127,8 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection,
char *wire_enc; char *wire_enc;
size_t len; size_t len;
struct GNUNET_MINT_ParseFieldSpec spec[] = { struct GNUNET_MINT_ParseFieldSpec spec[] = {
TALER_MINT_PARSE_VARIABLE ("denom_pub"), TALER_MINT_PARSE_RSA_PUBLIC_KEY ("denom_pub", &deposit.coin.denom_pub),
TALER_MINT_PARSE_VARIABLE ("ubsig"), TALER_MINT_PARSE_RSA_SIGNATURE ("ubsig", &deposit.coin.denom_sig),
TALER_MINT_PARSE_FIXED ("coin_pub", &deposit.coin.coin_pub), TALER_MINT_PARSE_FIXED ("coin_pub", &deposit.coin.coin_pub),
TALER_MINT_PARSE_FIXED ("merchant_pub", &deposit.merchant_pub), TALER_MINT_PARSE_FIXED ("merchant_pub", &deposit.merchant_pub),
TALER_MINT_PARSE_FIXED ("H_a", &deposit.h_contract), TALER_MINT_PARSE_FIXED ("H_a", &deposit.h_contract),
@ -146,32 +146,9 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection,
return MHD_NO; /* hard failure */ return MHD_NO; /* hard failure */
if (GNUNET_NO == res) if (GNUNET_NO == res)
return MHD_YES; /* failure */ return MHD_YES; /* failure */
deposit.coin.denom_pub
= GNUNET_CRYPTO_rsa_public_key_decode (spec[0].destination,
spec[0].destination_size_out);
if (NULL == deposit.coin.denom_pub)
{
LOG_WARNING ("Failed to parse denomination key for /deposit request\n");
TALER_MINT_release_parsed_data (spec);
return TALER_MINT_reply_arg_invalid (connection,
"denom_pub");
}
deposit.coin.denom_sig
= GNUNET_CRYPTO_rsa_signature_decode (spec[1].destination,
spec[1].destination_size_out);
if (NULL == deposit.coin.denom_sig)
{
LOG_WARNING ("Failed to parse unblinded signature for /deposit request\n");
GNUNET_CRYPTO_rsa_public_key_free (deposit.coin.denom_pub);
TALER_MINT_release_parsed_data (spec);
return TALER_MINT_reply_arg_invalid (connection,
"denom_pub");
}
/* FIXME: check that "wire" is formatted correctly */ /* FIXME: check that "wire" is formatted correctly */
if (NULL == (wire_enc = json_dumps (wire, JSON_COMPACT | JSON_SORT_KEYS))) if (NULL == (wire_enc = json_dumps (wire, JSON_COMPACT | JSON_SORT_KEYS)))
{ {
GNUNET_CRYPTO_rsa_public_key_free (deposit.coin.denom_pub);
GNUNET_CRYPTO_rsa_signature_free (deposit.coin.denom_sig);
LOG_WARNING ("Failed to parse JSON wire format specification for /deposit request\n"); LOG_WARNING ("Failed to parse JSON wire format specification for /deposit request\n");
TALER_MINT_release_parsed_data (spec); TALER_MINT_release_parsed_data (spec);
return TALER_MINT_reply_arg_invalid (connection, return TALER_MINT_reply_arg_invalid (connection,
@ -187,8 +164,6 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection,
deposit.amount = *amount; deposit.amount = *amount;
res = verify_and_execute_deposit (connection, res = verify_and_execute_deposit (connection,
&deposit); &deposit);
GNUNET_CRYPTO_rsa_public_key_free (deposit.coin.denom_pub);
GNUNET_CRYPTO_rsa_signature_free (deposit.coin.denom_sig);
TALER_MINT_release_parsed_data (spec); TALER_MINT_release_parsed_data (spec);
return res; return res;
} }

View File

@ -260,402 +260,6 @@ TALER_MINT_parse_post_cleanup_callback (void *con_cls)
} }
/**
* Navigate through a JSON tree.
*
* Sends an error response if navigation is impossible (i.e.
* the JSON object is invalid)
*
* @param connection the connection to send an error response to
* @param root the JSON node to start the navigation at.
* @param ... navigation specification (see `enum TALER_MINT_JsonNavigationCommand`)
* @return
* #GNUNET_YES if navigation was successful
* #GNUNET_NO if json is malformed, error response was generated
* #GNUNET_SYSERR on internal error (no response was generated,
* connection must be closed)
*/
int
GNUNET_MINT_parse_navigate_json (struct MHD_Connection *connection,
const json_t *root,
...)
{
va_list argp;
int ret;
json_t *path; /* what's our current path from 'root'? */
path = json_array ();
va_start (argp, root);
ret = 2; /* just not any of the valid return values */
while (2 == ret)
{
enum TALER_MINT_JsonNavigationCommand command
= va_arg (argp,
enum TALER_MINT_JsonNavigationCommand);
switch (command)
{
case JNAV_FIELD:
{
const char *fname = va_arg(argp, const char *);
json_array_append_new (path,
json_string (fname));
root = json_object_get (root,
fname);
if (NULL == root)
{
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:s, s:o}",
"error",
"missing field in JSON",
"field",
fname,
"path",
path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
}
break;
case JNAV_INDEX:
{
int fnum = va_arg(argp, int);
json_array_append_new (path,
json_integer (fnum));
root = json_array_get (root,
fnum);
if (NULL == root)
{
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:o}",
"error",
"missing index in JSON",
"path", path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
}
break;
case JNAV_RET_DATA:
{
void *where = va_arg (argp, void *);
size_t len = va_arg (argp, size_t);
const char *str;
int res;
str = json_string_value (root);
if (NULL == str)
{
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:o}",
"error",
"string expected",
"path",
path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
res = GNUNET_STRINGS_string_to_data (str, strlen (str),
where, len);
if (GNUNET_OK != res)
{
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:o}",
"error",
"malformed binary data in JSON",
"path",
path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
ret = GNUNET_OK;
}
break;
case JNAV_RET_DATA_VAR:
{
void **where = va_arg (argp, void **);
size_t *len = va_arg (argp, size_t *);
const char *str;
str = json_string_value (root);
if (NULL == str)
{
ret = (MHD_YES ==
TALER_MINT_reply_internal_error (connection,
"json_string_value() failed"))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
*len = (strlen (str) * 5) / 8;
if (NULL != where)
{
int res;
*where = GNUNET_malloc (*len);
res = GNUNET_STRINGS_string_to_data (str,
strlen (str),
*where,
*len);
if (GNUNET_OK != res)
{
GNUNET_free (*where);
*where = NULL;
*len = 0;
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:o}",
"error",
"malformed binary data in JSON",
"path", path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
}
ret = GNUNET_OK;
}
break;
case JNAV_RET_TYPED_JSON:
{
int typ = va_arg (argp, int);
const json_t **r_json = va_arg (argp, const json_t **);
if ( (-1 != typ) && (json_typeof (root) != typ))
{
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:i, s:i, s:o}",
"error", "wrong JSON field type",
"type_expected", typ,
"type_actual", json_typeof (root),
"path", path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
*r_json = root;
ret = GNUNET_OK;
}
break;
default:
GNUNET_break (0);
ret = (MHD_YES ==
TALER_MINT_reply_internal_error (connection,
"unhandled value in switch"))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
}
va_end (argp);
json_decref (path);
return ret;
}
/**
* Parse JSON object into components based on the given field
* specification.
*
* @param connection the connection to send an error response to
* @param root the JSON node to start the navigation at.
* @param spec field specification for the parser
* @return
* #GNUNET_YES if navigation was successful (caller is responsible
* for freeing allocated variable-size data using
* #TALER_MINT_release_parsed_data() when done)
* #GNUNET_NO if json is malformed, error response was generated
* #GNUNET_SYSERR on internal error
*/
int
TALER_MINT_parse_json_data (struct MHD_Connection *connection,
const json_t *root,
struct GNUNET_MINT_ParseFieldSpec *spec)
{
unsigned int i;
int ret;
void *ptr;
ret = GNUNET_YES;
for (i=0; NULL != spec[i].field_name; i++)
{
switch (spec[i].command)
{
case JNAV_FIELD:
GNUNET_break (0);
return GNUNET_SYSERR;
case JNAV_INDEX:
GNUNET_break (0);
return GNUNET_SYSERR;
case JNAV_RET_DATA:
if (GNUNET_YES != ret)
break;
ret = GNUNET_MINT_parse_navigate_json (connection,
root,
JNAV_FIELD,
spec[i].field_name,
JNAV_RET_DATA,
spec[i].destination,
spec[i].destination_size_in);
break;
case JNAV_RET_DATA_VAR:
if (GNUNET_YES != ret)
break;
ptr = NULL;
ret = GNUNET_MINT_parse_navigate_json (connection,
root,
JNAV_FIELD,
spec[i].field_name,
JNAV_RET_DATA_VAR,
&ptr,
&spec[i].destination_size_out);
spec[i].destination = ptr;
break;
case JNAV_RET_TYPED_JSON:
if (GNUNET_YES != ret)
break;
ptr = NULL;
ret = GNUNET_MINT_parse_navigate_json (connection,
root,
JNAV_FIELD,
spec[i].field_name,
JNAV_RET_TYPED_JSON,
spec[i].type,
&ptr);
spec[i].destination = ptr;
break;
}
}
if (GNUNET_YES != ret)
TALER_MINT_release_parsed_data (spec);
return ret;
}
/**
* Release all memory allocated for the variable-size fields in
* the parser specification.
*
* @param spec specification to free
*/
void
TALER_MINT_release_parsed_data (struct GNUNET_MINT_ParseFieldSpec *spec)
{
unsigned int i;
for (i=0; NULL != spec[i].field_name; i++)
{
switch (spec[i].command)
{
case JNAV_FIELD:
GNUNET_break (0);
return;
case JNAV_INDEX:
GNUNET_break (0);
return;
case JNAV_RET_DATA:
break;
case JNAV_RET_DATA_VAR:
if (0 != spec[i].destination_size_out)
{
GNUNET_free (spec[i].destination);
spec[i].destination = NULL;
spec[i].destination_size_out = 0;
}
break;
case JNAV_RET_TYPED_JSON:
if (NULL != spec[i].destination)
{
json_decref (spec[i].destination);
spec[i].destination = NULL;
}
break;
}
}
}
/**
* Parse amount specified in JSON format.
*
* @param connection the MHD connection (to report errors)
* @param f json specification of the amount
* @param amount[OUT] set to the amount specified in @a f
* @return
* #GNUNET_YES if parsing was successful
* #GNUNET_NO if json is malformed, error response was generated
* #GNUNET_SYSERR on internal error, error response was not generated
*/
int
TALER_MINT_parse_amount_json (struct MHD_Connection *connection,
json_t *f,
struct TALER_Amount *amount)
{
json_int_t value;
json_int_t fraction;
const char *currency;
struct TALER_Amount a;
if (-1 == json_unpack (f,
"{s:I, s:I, s:s}",
"value", &value,
"fraction", &fraction,
"currency", &currency))
{
LOG_WARNING ("Failed to parse JSON amount specification\n");
if (MHD_YES !=
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s}",
"error", "Bad format"))
return GNUNET_SYSERR;
return GNUNET_NO;
}
if ( (value < 0) ||
(fraction < 0) ||
(value > UINT32_MAX) ||
(fraction > UINT32_MAX) )
{
LOG_WARNING ("Amount specified not in allowed range\n");
if (MHD_YES !=
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s}",
"error", "Amount outside of allowed range"))
return GNUNET_SYSERR;
return GNUNET_NO;
}
if (0 != strcmp (currency,
MINT_CURRENCY))
{
LOG_WARNING ("Currency specified not supported by this mint\n");
if (MHD_YES !=
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:s}",
"error", "Currency not supported",
"currency", currency))
return GNUNET_SYSERR;
return GNUNET_NO;
}
a.value = (uint32_t) value;
a.fraction = (uint32_t) fraction;
GNUNET_assert (strlen (MINT_CURRENCY) < TALER_CURRENCY_LEN);
strcpy (a.currency, MINT_CURRENCY);
*amount = TALER_amount_normalize (a);
return GNUNET_OK;
}
/** /**
* Extract base32crockford encoded data from request. * Extract base32crockford encoded data from request.
* *
@ -756,5 +360,576 @@ TALER_MINT_mhd_request_var_arg_data (struct MHD_Connection *connection,
} }
/**
* Navigate through a JSON tree.
*
* Sends an error response if navigation is impossible (i.e.
* the JSON object is invalid)
*
* @param connection the connection to send an error response to
* @param root the JSON node to start the navigation at.
* @param ... navigation specification (see `enum TALER_MINT_JsonNavigationCommand`)
* @return
* #GNUNET_YES if navigation was successful
* #GNUNET_NO if json is malformed, error response was generated
* #GNUNET_SYSERR on internal error (no response was generated,
* connection must be closed)
*/
int
GNUNET_MINT_parse_navigate_json (struct MHD_Connection *connection,
const json_t *root,
...)
{
va_list argp;
int ret;
json_t *path; /* what's our current path from 'root'? */
path = json_array ();
va_start (argp, root);
ret = 2; /* just not any of the valid return values */
while (2 == ret)
{
enum TALER_MINT_JsonNavigationCommand command
= va_arg (argp,
enum TALER_MINT_JsonNavigationCommand);
switch (command)
{
case JNAV_FIELD:
{
const char *fname = va_arg(argp, const char *);
json_array_append_new (path,
json_string (fname));
root = json_object_get (root,
fname);
if (NULL == root)
{
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:s, s:o}",
"error",
"missing field in JSON",
"field",
fname,
"path",
path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
}
break;
case JNAV_INDEX:
{
int fnum = va_arg(argp, int);
json_array_append_new (path,
json_integer (fnum));
root = json_array_get (root,
fnum);
if (NULL == root)
{
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:o}",
"error",
"missing index in JSON",
"path", path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
}
break;
case JNAV_RET_DATA:
{
void *where = va_arg (argp, void *);
size_t len = va_arg (argp, size_t);
const char *str;
int res;
// FIXME: avoidable code duplication here...
str = json_string_value (root);
if (NULL == str)
{
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:o}",
"error",
"string expected",
"path",
path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
res = GNUNET_STRINGS_string_to_data (str, strlen (str),
where, len);
if (GNUNET_OK != res)
{
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:o}",
"error",
"malformed binary data in JSON",
"path",
path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
ret = GNUNET_OK;
}
break;
case JNAV_RET_DATA_VAR:
{
void **where = va_arg (argp, void **);
size_t *len = va_arg (argp, size_t *);
const char *str;
// FIXME: avoidable code duplication here...
str = json_string_value (root);
if (NULL == str)
{
ret = (MHD_YES ==
TALER_MINT_reply_internal_error (connection,
"json_string_value() failed"))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
*len = (strlen (str) * 5) / 8;
if (NULL != where)
{
int res;
*where = GNUNET_malloc (*len);
res = GNUNET_STRINGS_string_to_data (str,
strlen (str),
*where,
*len);
if (GNUNET_OK != res)
{
GNUNET_free (*where);
*where = NULL;
*len = 0;
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:o}",
"error",
"malformed binary data in JSON",
"path", path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
}
ret = GNUNET_OK;
}
break;
case JNAV_RET_TYPED_JSON:
{
int typ = va_arg (argp, int);
const json_t **r_json = va_arg (argp, const json_t **);
if ( (-1 != typ) && (json_typeof (root) != typ))
{
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:i, s:i, s:o}",
"error", "wrong JSON field type",
"type_expected", typ,
"type_actual", json_typeof (root),
"path", path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
*r_json = root;
ret = GNUNET_OK;
}
break;
case JNAV_RET_RSA_PUBLIC_KEY:
{
void **where = va_arg (argp, void **);
size_t len;
const char *str;
int res;
void *buf;
// FIXME: avoidable code duplication here...
str = json_string_value (root);
if (NULL == str)
{
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:o}",
"error",
"string expected",
"path",
path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
len = (strlen (str) * 5) / 8;
buf = GNUNET_malloc (len);
res = GNUNET_STRINGS_string_to_data (str,
strlen (str),
buf,
len);
if (GNUNET_OK != res)
{
GNUNET_free (buf);
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:o}",
"error",
"malformed binary data in JSON",
"path",
path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
*where = GNUNET_CRYPTO_rsa_public_key_decode (buf,
len);
GNUNET_free (buf);
if (NULL == *where)
{
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:o}",
"error",
"malformed RSA public key in JSON",
"path",
path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
ret = GNUNET_OK;
break;
}
case JNAV_RET_RSA_SIGNATURE:
{
void **where = va_arg (argp, void **);
size_t len;
const char *str;
int res;
void *buf;
// FIXME: avoidable code duplication here...
str = json_string_value (root);
if (NULL == str)
{
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:o}",
"error",
"string expected",
"path",
path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
len = (strlen (str) * 5) / 8;
buf = GNUNET_malloc (len);
res = GNUNET_STRINGS_string_to_data (str,
strlen (str),
buf,
len);
if (GNUNET_OK != res)
{
GNUNET_free (buf);
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:o}",
"error",
"malformed binary data in JSON",
"path",
path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
*where = GNUNET_CRYPTO_rsa_signature_decode (buf,
len);
GNUNET_free (buf);
if (NULL == *where)
{
ret = (MHD_YES ==
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:o}",
"error",
"malformed RSA signature in JSON",
"path",
path))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
ret = GNUNET_OK;
break;
}
default:
GNUNET_break (0);
ret = (MHD_YES ==
TALER_MINT_reply_internal_error (connection,
"unhandled value in switch"))
? GNUNET_NO : GNUNET_SYSERR;
break;
}
}
va_end (argp);
json_decref (path);
return ret;
}
/**
* Parse JSON object into components based on the given field
* specification.
*
* @param connection the connection to send an error response to
* @param root the JSON node to start the navigation at.
* @param spec field specification for the parser
* @return
* #GNUNET_YES if navigation was successful (caller is responsible
* for freeing allocated variable-size data using
* #TALER_MINT_release_parsed_data() when done)
* #GNUNET_NO if json is malformed, error response was generated
* #GNUNET_SYSERR on internal error
*/
int
TALER_MINT_parse_json_data (struct MHD_Connection *connection,
const json_t *root,
struct GNUNET_MINT_ParseFieldSpec *spec)
{
unsigned int i;
int ret;
void *ptr;
ret = GNUNET_YES;
for (i=0; NULL != spec[i].field_name; i++)
{
switch (spec[i].command)
{
case JNAV_FIELD:
GNUNET_break (0);
return GNUNET_SYSERR;
case JNAV_INDEX:
GNUNET_break (0);
return GNUNET_SYSERR;
case JNAV_RET_DATA:
if (GNUNET_YES != ret)
break;
ret = GNUNET_MINT_parse_navigate_json (connection,
root,
JNAV_FIELD,
spec[i].field_name,
JNAV_RET_DATA,
spec[i].destination,
spec[i].destination_size_in);
break;
case JNAV_RET_DATA_VAR:
if (GNUNET_YES != ret)
break;
ptr = NULL;
ret = GNUNET_MINT_parse_navigate_json (connection,
root,
JNAV_FIELD,
spec[i].field_name,
JNAV_RET_DATA_VAR,
&ptr,
&spec[i].destination_size_out);
spec[i].destination = ptr;
break;
case JNAV_RET_TYPED_JSON:
if (GNUNET_YES != ret)
break;
ptr = NULL;
ret = GNUNET_MINT_parse_navigate_json (connection,
root,
JNAV_FIELD,
spec[i].field_name,
JNAV_RET_TYPED_JSON,
spec[i].type,
&ptr);
*((void**)spec[i].destination) = ptr;
break;
case JNAV_RET_RSA_PUBLIC_KEY:
if (GNUNET_YES != ret)
break;
ptr = NULL;
ret = GNUNET_MINT_parse_navigate_json (connection,
root,
JNAV_FIELD,
spec[i].field_name,
JNAV_RET_RSA_PUBLIC_KEY,
&ptr);
spec[i].destination = ptr;
break;
case JNAV_RET_RSA_SIGNATURE:
if (GNUNET_YES != ret)
break;
ptr = NULL;
ret = GNUNET_MINT_parse_navigate_json (connection,
root,
JNAV_FIELD,
spec[i].field_name,
JNAV_RET_RSA_SIGNATURE,
&ptr);
spec[i].destination = ptr;
break;
}
}
if (GNUNET_YES != ret)
TALER_MINT_release_parsed_data (spec);
return ret;
}
/**
* Release all memory allocated for the variable-size fields in
* the parser specification.
*
* @param spec specification to free
*/
void
TALER_MINT_release_parsed_data (struct GNUNET_MINT_ParseFieldSpec *spec)
{
unsigned int i;
void *ptr;
for (i=0; NULL != spec[i].field_name; i++)
{
switch (spec[i].command)
{
case JNAV_FIELD:
GNUNET_break (0);
return;
case JNAV_INDEX:
GNUNET_break (0);
return;
case JNAV_RET_DATA:
break;
case JNAV_RET_DATA_VAR:
if (0 != spec[i].destination_size_out)
{
GNUNET_free (spec[i].destination);
spec[i].destination = NULL;
spec[i].destination_size_out = 0;
}
break;
case JNAV_RET_TYPED_JSON:
ptr = *(void **) spec[i].destination;
if (NULL != ptr)
{
json_decref (ptr);
*(void**) spec[i].destination = NULL;
}
break;
case JNAV_RET_RSA_PUBLIC_KEY:
ptr = *(void **) spec[i].destination;
if (NULL != ptr)
{
GNUNET_CRYPTO_rsa_public_key_free (ptr);
*(void**) spec[i].destination = NULL;
}
break;
case JNAV_RET_RSA_SIGNATURE:
ptr = *(void **) spec[i].destination;
if (NULL != ptr)
{
GNUNET_CRYPTO_rsa_signature_free (ptr);
*(void**) spec[i].destination = NULL;
}
break;
}
}
}
/**
* Parse amount specified in JSON format.
*
* @param connection the MHD connection (to report errors)
* @param f json specification of the amount
* @param amount[OUT] set to the amount specified in @a f
* @return
* #GNUNET_YES if parsing was successful
* #GNUNET_NO if json is malformed, error response was generated
* #GNUNET_SYSERR on internal error, error response was not generated
*/
int
TALER_MINT_parse_amount_json (struct MHD_Connection *connection,
json_t *f,
struct TALER_Amount *amount)
{
json_int_t value;
json_int_t fraction;
const char *currency;
struct TALER_Amount a;
if (-1 == json_unpack (f,
"{s:I, s:I, s:s}",
"value", &value,
"fraction", &fraction,
"currency", &currency))
{
LOG_WARNING ("Failed to parse JSON amount specification\n");
if (MHD_YES !=
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s}",
"error", "Bad format"))
return GNUNET_SYSERR;
return GNUNET_NO;
}
if ( (value < 0) ||
(fraction < 0) ||
(value > UINT32_MAX) ||
(fraction > UINT32_MAX) )
{
LOG_WARNING ("Amount specified not in allowed range\n");
if (MHD_YES !=
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s}",
"error", "Amount outside of allowed range"))
return GNUNET_SYSERR;
return GNUNET_NO;
}
if (0 != strcmp (currency,
MINT_CURRENCY))
{
LOG_WARNING ("Currency specified not supported by this mint\n");
if (MHD_YES !=
TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s, s:s}",
"error", "Currency not supported",
"currency", currency))
return GNUNET_SYSERR;
return GNUNET_NO;
}
a.value = (uint32_t) value;
a.fraction = (uint32_t) fraction;
GNUNET_assert (strlen (MINT_CURRENCY) < TALER_CURRENCY_LEN);
strcpy (a.currency, MINT_CURRENCY);
*amount = TALER_amount_normalize (a);
return GNUNET_OK;
}
/* end of taler-mint-httpd_parsing.c */ /* end of taler-mint-httpd_parsing.c */

View File

@ -108,7 +108,19 @@ enum TALER_MINT_JsonNavigationCommand
* or -1 for any type). * or -1 for any type).
* Params: (int, json_t **) * Params: (int, json_t **)
*/ */
JNAV_RET_TYPED_JSON JNAV_RET_TYPED_JSON,
/**
* Return a `struct GNUNET_CRYPTO_rsa_PublicKey` which was
* encoded as variable-size base32crockford encoded data.
*/
JNAV_RET_RSA_PUBLIC_KEY,
/**
* Return a `struct GNUNET_CRYPTO_rsa_Signature` which was
* encoded as variable-size base32crockford encoded data.
*/
JNAV_RET_RSA_SIGNATURE
}; };
@ -230,15 +242,34 @@ TALER_MINT_release_parsed_data (struct GNUNET_MINT_ParseFieldSpec *spec);
* Generate line in parser specification for JSON array value. * Generate line in parser specification for JSON array value.
* *
* @param field name of the field * @param field name of the field
* @param ptraddr address of pointer to initialize (a `void **`)
*/ */
#define TALER_MINT_PARSE_ARRAY(field) { field, NULL, 0, 0, JNAV_RET_TYPED_JSON, JSON_ARRAY } #define TALER_MINT_PARSE_ARRAY(field,ptraddr) { field, ptraddr, 0, 0, JNAV_RET_TYPED_JSON, JSON_ARRAY }
/** /**
* Generate line in parser specification for JSON object value. * Generate line in parser specification for JSON object value.
* *
* @param field name of the field * @param field name of the field
* @param ptraddr address of pointer to initialize (a `void **`)
*/ */
#define TALER_MINT_PARSE_OBJECT(field) { field, NULL, 0, 0, JNAV_RET_TYPED_JSON, JSON_OBJECT } #define TALER_MINT_PARSE_OBJECT(field,ptraddr) { field, ptraddr, 0, 0, JNAV_RET_TYPED_JSON, JSON_OBJECT }
/**
* Generate line in parser specification for RSA public key.
*
* @param field name of the field
* @param ptraddr address of `struct GNUNET_CRYPTO_rsa_PublicKey *` initialize
*/
#define TALER_MINT_PARSE_RSA_PUBLIC_KEY(field,ptrpk) { field, ptrpk, 0, 0, JNAV_RET_RSA_PUBLIC_KEY, 0 }
/**
* Generate line in parser specification for RSA public key.
*
* @param field name of the field
* @param ptrsig address of `struct GNUNET_CRYPTO_rsa_Signature *` initialize
*/
#define TALER_MINT_PARSE_RSA_SIGNATURE(field,ptrsig) { field, ptrsig, 0, 0, JNAV_RET_RSA_SIGNATURE, 0 }
/** /**

View File

@ -107,9 +107,9 @@ request_json_require_coin_public_info (struct MHD_Connection *connection,
struct GNUNET_CRYPTO_rsa_PublicKey *pk; struct GNUNET_CRYPTO_rsa_PublicKey *pk;
struct GNUNET_MINT_ParseFieldSpec spec[] = struct GNUNET_MINT_ParseFieldSpec spec[] =
{ {
TALER_MINT_PARSE_FIXED("coin_pub", &r_public_info->coin_pub), TALER_MINT_PARSE_FIXED ("coin_pub", &r_public_info->coin_pub),
TALER_MINT_PARSE_VARIABLE("denom_sig"), TALER_MINT_PARSE_RSA_SIGNATURE ("denom_sig", &sig),
TALER_MINT_PARSE_VARIABLE("denom_pub"), TALER_MINT_PARSE_RSA_PUBLIC_KEY ("denom_pub", &pk),
TALER_MINT_PARSE_END TALER_MINT_PARSE_END
}; };
@ -118,21 +118,7 @@ request_json_require_coin_public_info (struct MHD_Connection *connection,
spec); spec);
if (GNUNET_OK != ret) if (GNUNET_OK != ret)
return ret; return ret;
sig = GNUNET_CRYPTO_rsa_signature_decode (spec[1].destination, // TALER_MINT_release_parsed_data (spec);
spec[1].destination_size_out);
pk = GNUNET_CRYPTO_rsa_public_key_decode (spec[2].destination,
spec[2].destination_size_out);
TALER_MINT_release_parsed_data (spec);
if ( (NULL == pk) ||
(NULL == sig) )
{
if (NULL != sig)
GNUNET_CRYPTO_rsa_signature_free (sig);
if (NULL != pk)
GNUNET_CRYPTO_rsa_public_key_free (pk);
// FIXME: send error reply...
return GNUNET_NO;
}
r_public_info->denom_sig = sig; r_public_info->denom_sig = sig;
r_public_info->denom_pub = pk; r_public_info->denom_pub = pk;
return GNUNET_OK; return GNUNET_OK;
@ -152,8 +138,8 @@ request_json_require_coin_public_info (struct MHD_Connection *connection,
*/ */
static int static int
request_json_check_signature (struct MHD_Connection *connection, request_json_check_signature (struct MHD_Connection *connection,
json_t *root, const json_t *root,
struct GNUNET_CRYPTO_EddsaPublicKey *pub, const struct GNUNET_CRYPTO_EddsaPublicKey *pub,
struct GNUNET_CRYPTO_EccSignaturePurpose *purpose) struct GNUNET_CRYPTO_EccSignaturePurpose *purpose)
{ {
struct GNUNET_CRYPTO_EddsaSignature signature; struct GNUNET_CRYPTO_EddsaSignature signature;
@ -230,100 +216,38 @@ request_json_check_signature (struct MHD_Connection *connection,
/** /**
* Handle a "/refresh/melt" request * 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.
* *
* @param rh context of the handler
* @param connection the MHD connection to handle * @param connection the MHD connection to handle
* @param[IN|OUT] connection_cls the connection's closure (can be updated) * @param refresh_session_pub public key of the melt operation
* @param upload_data upload data * @param new_denoms array of denomination keys
* @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data * @param melt_coins array of coins to melt
* @param melt_sig_json signature affirming the melt operation
* @return MHD result code * @return MHD result code
*/ */
int static int
TALER_MINT_handler_refresh_melt (struct RequestHandler *rh, handle_refresh_melt_json (struct MHD_Connection *connection,
struct MHD_Connection *connection, const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub,
void **connection_cls, const json_t *new_denoms,
const char *upload_data, const json_t *melt_coins,
size_t *upload_data_size) const json_t *melt_sig_json)
{ {
json_t *root;
struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub;
int res; int res;
json_t *new_denoms;
unsigned int num_new_denoms; unsigned int num_new_denoms;
unsigned int i; unsigned int i;
struct GNUNET_CRYPTO_rsa_PublicKey **denom_pubs; struct GNUNET_CRYPTO_rsa_PublicKey **denom_pubs;
json_t *melt_coins;
struct TALER_CoinPublicInfo *coin_public_infos; struct TALER_CoinPublicInfo *coin_public_infos;
unsigned int coin_count; unsigned int coin_count;
struct GNUNET_HashContext *hash_context; struct GNUNET_HashContext *hash_context;
struct GNUNET_HashCode melt_hash; struct GNUNET_HashCode melt_hash;
struct MintKeyState *key_state; struct MintKeyState *key_state;
struct RefreshMeltSignatureBody body; struct RefreshMeltSignatureBody body;
json_t *melt_sig_json;
char *buf; char *buf;
size_t buf_size; size_t buf_size;
struct TALER_MINT_DenomKeyIssuePriv *dki; struct TALER_MINT_DenomKeyIssuePriv *dki;
res = TALER_MINT_parse_post_json (connection,
connection_cls,
upload_data,
upload_data_size,
&root);
if (GNUNET_SYSERR == res)
return MHD_NO;
if ( (GNUNET_NO == res) || (NULL == root) )
return MHD_YES;
res = GNUNET_MINT_parse_navigate_json (connection,
root,
JNAV_FIELD,
"session_pub",
JNAV_RET_DATA,
&refresh_session_pub,
sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
if (GNUNET_SYSERR == res)
return MHD_NO;
if (GNUNET_NO == res)
return MHD_YES;
res = GNUNET_MINT_parse_navigate_json (connection,
root,
JNAV_FIELD,
"new_denoms",
JNAV_RET_TYPED_JSON,
JSON_ARRAY,
&new_denoms);
if (GNUNET_SYSERR == res)
return MHD_NO;
if (GNUNET_NO == res)
return MHD_YES;
res = GNUNET_MINT_parse_navigate_json (connection,
root,
JNAV_FIELD,
"melt_coins",
JNAV_RET_TYPED_JSON,
JSON_ARRAY,
&melt_coins);
if (GNUNET_OK != res)
{
// FIXME: leaks!
return res;
}
melt_sig_json = json_object_get (root,
"melt_signature");
if (NULL == melt_sig_json)
{
return TALER_MINT_reply_json_pack (connection,
MHD_HTTP_BAD_REQUEST,
"{s:s}",
"error",
"melt_signature missing");
}
num_new_denoms = json_array_size (new_denoms); num_new_denoms = json_array_size (new_denoms);
denom_pubs = GNUNET_malloc (num_new_denoms * denom_pubs = GNUNET_malloc (num_new_denoms *
@ -331,8 +255,7 @@ TALER_MINT_handler_refresh_melt (struct RequestHandler *rh,
for (i=0;i<num_new_denoms;i++) for (i=0;i<num_new_denoms;i++)
{ {
res = GNUNET_MINT_parse_navigate_json (connection, root, res = GNUNET_MINT_parse_navigate_json (connection, new_denoms,
JNAV_FIELD, "new_denoms",
JNAV_INDEX, (int) i, JNAV_INDEX, (int) i,
JNAV_RET_DATA_VAR, JNAV_RET_DATA_VAR,
&buf, &buf,
@ -380,7 +303,7 @@ TALER_MINT_handler_refresh_melt (struct RequestHandler *rh,
(res = check_confirm_signature (connection, (res = check_confirm_signature (connection,
json_array_get (melt_coins, i), json_array_get (melt_coins, i),
&coin_public_infos[i].coin_pub, &coin_public_infos[i].coin_pub,
&refresh_session_pub))) refresh_session_pub)))
{ {
GNUNET_break (GNUNET_SYSERR != res); GNUNET_break (GNUNET_SYSERR != res);
// FIXME: leaks! // FIXME: leaks!
@ -439,7 +362,7 @@ TALER_MINT_handler_refresh_melt (struct RequestHandler *rh,
if (GNUNET_OK != if (GNUNET_OK !=
(res = request_json_check_signature (connection, (res = request_json_check_signature (connection,
melt_sig_json, melt_sig_json,
&refresh_session_pub, refresh_session_pub,
&body.purpose))) &body.purpose)))
{ {
// FIXME: generate proper error reply // FIXME: generate proper error reply
@ -448,7 +371,7 @@ TALER_MINT_handler_refresh_melt (struct RequestHandler *rh,
res = TALER_MINT_db_execute_refresh_melt (connection, res = TALER_MINT_db_execute_refresh_melt (connection,
&refresh_session_pub, refresh_session_pub,
num_new_denoms, num_new_denoms,
denom_pubs, denom_pubs,
coin_count, coin_count,
@ -458,6 +381,66 @@ TALER_MINT_handler_refresh_melt (struct RequestHandler *rh,
} }
/**
* Handle a "/refresh/melt" request. Parses the request into the JSON
* components and then hands things of to #handle_referesh_melt_json()
* to validate the melted coins, the signature and execute the melt.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
* @param[IN|OUT] connection_cls the connection's closure (can be updated)
* @param upload_data upload data
* @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
* @return MHD result code
*/
int
TALER_MINT_handler_refresh_melt (struct RequestHandler *rh,
struct MHD_Connection *connection,
void **connection_cls,
const char *upload_data,
size_t *upload_data_size)
{
json_t *root;
json_t *new_denoms;
json_t *melt_coins;
json_t *melt_sig_json;
struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub;
int res;
struct GNUNET_MINT_ParseFieldSpec spec[] = {
TALER_MINT_PARSE_FIXED ("session_pub", &refresh_session_pub),
TALER_MINT_PARSE_ARRAY ("new_denoms", &new_denoms),
TALER_MINT_PARSE_ARRAY ("melt_coins", &melt_coins),
TALER_MINT_PARSE_ARRAY ("melt_signature", &melt_sig_json),
TALER_MINT_PARSE_END
};
res = TALER_MINT_parse_post_json (connection,
connection_cls,
upload_data,
upload_data_size,
&root);
if (GNUNET_SYSERR == res)
return MHD_NO;
if ( (GNUNET_NO == res) || (NULL == root) )
return MHD_YES;
res = TALER_MINT_parse_json_data (connection,
root,
spec);
if (GNUNET_SYSERR == res)
return MHD_NO;
if (GNUNET_NO == res)
return MHD_YES;
res = handle_refresh_melt_json (connection,
&refresh_session_pub,
new_denoms,
melt_coins,
melt_sig_json);
TALER_MINT_release_parsed_data (spec);
return res;
}
/** /**
* Handle a "/refresh/commit" request * Handle a "/refresh/commit" request
* *