summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-05-05 11:53:27 +0200
committerChristian Grothoff <christian@grothoff.org>2016-05-05 11:53:27 +0200
commit20ceb050d7bc5fd607ad508670ee149854768a98 (patch)
tree31e173d68fcb79242d2cb7fb8300e61125910d52
parent404b586176af67b20cae6ba5f918c78987fd6cf6 (diff)
downloadmerchant-20ceb050d7bc5fd607ad508670ee149854768a98.tar.gz
merchant-20ceb050d7bc5fd607ad508670ee149854768a98.tar.bz2
merchant-20ceb050d7bc5fd607ad508670ee149854768a98.zip
add well-formedness checks for contract
-rw-r--r--src/backend/taler-merchant-httpd_contract.c90
-rw-r--r--src/lib/test_merchant_api.c4
2 files changed, 87 insertions, 7 deletions
diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c
index 213ce903..9acff622 100644
--- a/src/backend/taler-merchant-httpd_contract.c
+++ b/src/backend/taler-merchant-httpd_contract.c
@@ -31,6 +31,58 @@
extern char *TMH_merchant_currency_string;
+
+/**
+ * Check that the given JSON array of products is well-formed.
+ *
+ * @param products JSON array to check
+ * @return #GNUNET_OK if all is fine
+ */
+static int
+check_products (json_t *products)
+{
+ size_t index;
+ json_t *value;
+ int res;
+
+ if (! json_is_array (products))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ json_array_foreach (products, index, value) {
+ const char *description;
+ const char *error_name;
+ unsigned int error_line;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("description", &description),
+ /* FIXME: there are other fields in the product specification
+ that rre currently not labeled as optional. Maybe check
+ those as well, or make them truly optional. */
+ GNUNET_JSON_spec_end()
+ };
+
+ /* extract fields we need to sign separately */
+ res = GNUNET_JSON_parse (value,
+ spec,
+ &error_name,
+ &error_line);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break (0);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Product description parsing failed at #%u: %s:%u\n",
+ (unsigned int) index,
+ error_name,
+ error_line);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_JSON_parse_free (spec);
+ }
+ return GNUNET_OK;
+}
+
+
/**
* Manage a contract request. In practical terms, it adds the fields
* 'exchanges', 'merchant_pub', and 'H_wire' to the contract 'proposition'
@@ -64,10 +116,20 @@ MH_handler_contract (struct TMH_RequestHandler *rh,
struct TALER_Amount total;
struct TALER_Amount max_fee;
uint64_t transaction_id;
+ json_t *products;
+ struct GNUNET_TIME_Absolute timestamp;
+ struct GNUNET_TIME_Absolute refund_deadline;
+ struct GNUNET_TIME_Absolute expiry;
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount ("amount", &total),
TALER_JSON_spec_amount ("max_fee", &max_fee),
GNUNET_JSON_spec_uint64 ("transaction_id", &transaction_id),
+ /* The following entries we don't actually need, except to check that
+ the contract is well-formed */
+ GNUNET_JSON_spec_json ("products", &products),
+ GNUNET_JSON_spec_absolute_time ("timestamp", &timestamp),
+ GNUNET_JSON_spec_absolute_time ("refund_deadline", &refund_deadline),
+ GNUNET_JSON_spec_absolute_time ("expiry", &expiry),
GNUNET_JSON_spec_end()
};
@@ -97,6 +159,7 @@ MH_handler_contract (struct TMH_RequestHandler *rh,
if (NULL == jcontract)
{
+ json_decref (root);
return TMH_RESPONSE_reply_external_error (connection,
"contract request malformed");
}
@@ -105,10 +168,26 @@ MH_handler_contract (struct TMH_RequestHandler *rh,
jcontract,
spec);
if (GNUNET_NO == res)
+ {
+ json_decref (root);
return MHD_YES;
+ }
if (GNUNET_SYSERR == res)
+ {
+ json_decref (root);
return TMH_RESPONSE_reply_external_error (connection,
"contract request malformed");
+ }
+ /* check contract is well-formed */
+ if (GNUNET_OK != check_products (products))
+ {
+ GNUNET_JSON_parse_free (spec);
+ json_decref (root);
+ return TMH_RESPONSE_reply_external_error (connection,
+ "products in contract request malformed");
+ }
+
+
/* add fields to the contract that the backend should provide */
json_object_set (jcontract,
"exchanges",
@@ -126,9 +205,6 @@ MH_handler_contract (struct TMH_RequestHandler *rh,
sizeof (pubkey)));
/* create contract signature */
- GNUNET_assert (GNUNET_OK ==
- TALER_JSON_hash (jcontract,
- &contract.h_contract));
contract.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
contract.purpose.size = htonl (sizeof (contract));
contract.transaction_id = GNUNET_htonll (transaction_id);
@@ -136,6 +212,9 @@ MH_handler_contract (struct TMH_RequestHandler *rh,
&total);
TALER_amount_hton (&contract.max_fee,
&max_fee);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_JSON_hash (jcontract,
+ &contract.h_contract));
GNUNET_CRYPTO_eddsa_sign (privkey,
&contract.purpose,
&contract_sig);
@@ -146,9 +225,10 @@ MH_handler_contract (struct TMH_RequestHandler *rh,
"{s:O, s:O, s:O}",
"contract", jcontract,
"merchant_sig", GNUNET_JSON_from_data (&contract_sig,
- sizeof (contract_sig)),
+ sizeof (contract_sig)),
"H_contract", GNUNET_JSON_from_data (&contract.h_contract,
- sizeof (contract.h_contract)));
+ sizeof (contract.h_contract)));
+ GNUNET_JSON_parse_free (spec);
json_decref (root);
return res;
}
diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c
index d4c5825e..3b08b1f1 100644
--- a/src/lib/test_merchant_api.c
+++ b/src/lib/test_merchant_api.c
@@ -1416,7 +1416,7 @@ run (void *cls)
{ .oc = OC_CONTRACT,
.label = "create-contract-1",
.expected_response_code = MHD_HTTP_OK,
- .details.contract.proposal = "{ \"max_fee\":{\"currency\":\"EUR\", \"value\":0, \"fraction\":500000}, \"transaction_id\":1, \"timestamp\":\"\\/Date(42)\\/\", \"refund_deadline\":\"\\/Date(0)\\/\", \"amount\":{\"currency\":\"EUR\", \"value\":5, \"fraction\":0}, \"items\":[ {\"name\":\"ice cream\", \"value\":\"{EUR:5}\"} ] }" },
+ .details.contract.proposal = "{ \"max_fee\":{\"currency\":\"EUR\", \"value\":0, \"fraction\":500000}, \"transaction_id\":1, \"timestamp\":\"\\/Date(42)\\/\", \"refund_deadline\":\"\\/Date(0)\\/\", \"expiry\":\"\\/Date(999999999)\\/\", \"amount\":{\"currency\":\"EUR\", \"value\":5, \"fraction\":0}, \"products\":[ {\"description\":\"ice cream\", \"value\":\"{EUR:5}\"} ] }" },
{ .oc = OC_PAY,
.label = "deposit-simple",
.expected_response_code = MHD_HTTP_OK,
@@ -1428,7 +1428,7 @@ run (void *cls)
{ .oc = OC_CONTRACT,
.label = "create-contract-2",
.expected_response_code = MHD_HTTP_OK,
- .details.contract.proposal = "{ \"max_fee\":\"{EUR:0.5}\", \"transaction_id\":2, \"timestamp\":\"\\/date(42)\\/\", \"total_amount\":\"{EUR:5}\", \"items\":[ {\"name\":\"ice cream\", \"value\":\"{EUR:5}\"} ] }" },
+ .details.contract.proposal = "{ \"max_fee\":{\"currency\":\"EUR\", \"value\":0, \"fraction\":500000}, \"transaction_id\":2, \"timestamp\":\"\\/Date(42)\\/\", \"refund_deadline\":\"\\/Date(0)\\/\", \"amount\":{\"currency\":\"EUR\", \"value\":5, \"fraction\":0}, \"products\":[ {\"description\":\"ice cream\", \"value\":\"{EUR:5}\"} ] }" },
/* Try to double-spend the 5 EUR coin at the same merchant (but different
transaction ID) */