From d623cab1a351040cca8729540327f948eb3027aa Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 27 Apr 2022 13:09:41 +0200 Subject: [PATCH] -work on p2p payments --- .../taler-exchange-httpd_purses_deposit.c | 50 +++++++++++-- .../taler-exchange-httpd_purses_merge.c | 2 +- src/include/taler_exchange_service.h | 8 +-- src/include/taler_testing_lib.h | 2 + src/lib/exchange_api_contracts_get.c | 20 +----- src/lib/exchange_api_purse_deposit.c | 5 ++ src/testing/test_exchange_p2p.c | 2 + src/testing/testing_api_cmd_contract_get.c | 71 ++++++++++++++----- src/testing/testing_api_cmd_purse_deposit.c | 14 ++++ src/util/Makefile.am | 3 +- src/util/crypto_contract.c | 6 ++ src/util/test_crypto.c | 46 ++++++++++++ 12 files changed, 182 insertions(+), 47 deletions(-) diff --git a/src/exchange/taler-exchange-httpd_purses_deposit.c b/src/exchange/taler-exchange-httpd_purses_deposit.c index 08a7d63b1..e2a5d34f4 100644 --- a/src/exchange/taler-exchange-httpd_purses_deposit.c +++ b/src/exchange/taler-exchange-httpd_purses_deposit.c @@ -329,7 +329,7 @@ parse_coin (struct MHD_Connection *connection, if (GNUNET_OK != TALER_wallet_purse_deposit_verify (TEH_base_url, pcc->purse_pub, - &pcc->amount, + &coin->amount, &coin->cpi.coin_pub, &coin->coin_sig)) { @@ -549,11 +549,49 @@ TEH_handler_purses_deposit ( TALER_EC_GENERIC_PARAMETER_MALFORMED, "deposits"); } - /* FIXME: fetch basic purse properties - (min age, purse_expiration, amount, merge_pub, - h_contract_terms) from - DB. Generate 404 or 410 (Gone) replies if - applicable. */ + + { + enum GNUNET_DB_QueryStatus qs; + struct GNUNET_TIME_Timestamp merge_timestamp; + + qs = TEH_plugin->select_purse ( + TEH_plugin->cls, + pcc.purse_pub, + &pcc.purse_expiration, + &pcc.amount, + &pcc.deposit_total, + &pcc.h_contract_terms, + &merge_timestamp); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "select purse"); + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "select purse"); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_PURSE_UNKNOWN, + NULL); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; /* handled below */ + } + if (GNUNET_TIME_absolute_is_past (pcc.purse_expiration.abs_time)) + { + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_GONE, + TALER_EC_EXCHANGE_GENERIC_PURSE_EXPIRED, + NULL); + } + } /* parse deposits */ pcc.coins = GNUNET_new_array (pcc.num_coins, diff --git a/src/exchange/taler-exchange-httpd_purses_merge.c b/src/exchange/taler-exchange-httpd_purses_merge.c index e79c41d85..eb264f486 100644 --- a/src/exchange/taler-exchange-httpd_purses_merge.c +++ b/src/exchange/taler-exchange-httpd_purses_merge.c @@ -384,7 +384,7 @@ TEH_handler_purses_merge ( return TALER_MHD_reply_with_error ( connection, MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_MERGE_PURSE_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_PURSE_UNKNOWN, NULL); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: /* continued below */ diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index a36007757..2a88c1014 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -4004,14 +4004,14 @@ struct TALER_EXCHANGE_ContractGetResponse struct TALER_PurseContractPublicKeyP purse_pub; /** - * Private key of the merge capability. + * Encrypted contract. */ - struct TALER_PurseMergePrivateKeyP merge_priv; + const void *econtract; /** - * Contract terms. + * Number of bytes in @e econtract. */ - const json_t *contract_terms; + size_t econtract_size; } success; diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h index f8ad120d3..fb3b553ce 100644 --- a/src/include/taler_testing_lib.h +++ b/src/include/taler_testing_lib.h @@ -2417,6 +2417,7 @@ TALER_TESTING_cmd_purse_create_with_deposit ( * * @param label command label * @param expected_http_status what HTTP status do we expect to get returned from the exchange + * @param for_merge true if for merge, false if for deposit * @param contract_ref reference to a command providing us with the contract private key * @return the command */ @@ -2424,6 +2425,7 @@ struct TALER_TESTING_Command TALER_TESTING_cmd_contract_get ( const char *label, unsigned int expected_http_status, + bool for_merge, const char *contract_ref); diff --git a/src/lib/exchange_api_contracts_get.c b/src/lib/exchange_api_contracts_get.c index 76ae0052c..263a0cbe9 100644 --- a/src/lib/exchange_api_contracts_get.c +++ b/src/lib/exchange_api_contracts_get.c @@ -106,7 +106,6 @@ handle_contract_get_finished (void *cls, { void *econtract; size_t econtract_size; - json_t *contract; struct TALER_PurseContractSignatureP econtract_sig; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("purse_pub", @@ -143,24 +142,11 @@ handle_contract_get_finished (void *cls, GNUNET_JSON_parse_free (spec); break; } - contract = TALER_CRYPTO_contract_decrypt_for_merge ( - &cgh->contract_priv, - &dr.details.success.purse_pub, - econtract, - econtract_size, - &dr.details.success.merge_priv); - GNUNET_JSON_parse_free (spec); - if (NULL == contract) - { - GNUNET_break (0); - dr.hr.http_status = 0; - dr.hr.ec = TALER_EC_EXCHANGE_CONTRACTS_DECRYPTION_FAILED; - break; - } - dr.details.success.contract_terms = contract; + dr.details.success.econtract = econtract; + dr.details.success.econtract_size = econtract_size; cgh->cb (cgh->cb_cls, &dr); - json_decref (contract); + GNUNET_JSON_parse_free (spec); TALER_EXCHANGE_contract_get_cancel (cgh); return; } diff --git a/src/lib/exchange_api_purse_deposit.c b/src/lib/exchange_api_purse_deposit.c index f604b8ccd..540be0e2c 100644 --- a/src/lib/exchange_api_purse_deposit.c +++ b/src/lib/exchange_api_purse_deposit.c @@ -281,6 +281,7 @@ TALER_EXCHANGE_purse_deposit ( const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i]; json_t *jdeposit; struct TALER_CoinSpendSignatureP coin_sig; + struct TALER_CoinSpendPublicKeyP coin_pub; #if FIXME_OEC struct TALER_AgeCommitmentHash agh; struct TALER_AgeCommitmentHash *aghp = NULL; @@ -300,6 +301,8 @@ TALER_EXCHANGE_purse_deposit ( GNUNET_free (pch); return NULL; } + GNUNET_CRYPTO_eddsa_key_get_public (&deposit->coin_priv.eddsa_priv, + &coin_pub.eddsa_pub); #endif TALER_wallet_purse_deposit_sign ( url, @@ -322,6 +325,8 @@ TALER_EXCHANGE_purse_deposit ( &deposit->h_denom_pub), TALER_JSON_pack_denom_sig ("ub_sig", &deposit->denom_sig), + GNUNET_JSON_pack_data_auto ("coin_pub", + &coin_pub), GNUNET_JSON_pack_data_auto ("coin_sig", &coin_sig)); GNUNET_assert (0 == diff --git a/src/testing/test_exchange_p2p.c b/src/testing/test_exchange_p2p.c index c70fe9d8b..8e5fea0e7 100644 --- a/src/testing/test_exchange_p2p.c +++ b/src/testing/test_exchange_p2p.c @@ -160,6 +160,7 @@ run (void *cls, TALER_TESTING_cmd_contract_get ( "push-get-contract", MHD_HTTP_OK, + true, /* for merge */ "purse-with-deposit"), TALER_TESTING_cmd_purse_merge ( "purse-merge-into-reserve", @@ -181,6 +182,7 @@ run (void *cls, TALER_TESTING_cmd_contract_get ( "pull-get-contract", MHD_HTTP_OK, + false, /* for deposit */ "purse-create-with-reserve"), TALER_TESTING_cmd_purse_deposit_coins ( "purse-deposit-coins", diff --git a/src/testing/testing_api_cmd_contract_get.c b/src/testing/testing_api_cmd_contract_get.c index 9432fdeef..10a43aa3b 100644 --- a/src/testing/testing_api_cmd_contract_get.c +++ b/src/testing/testing_api_cmd_contract_get.c @@ -40,6 +40,11 @@ struct ContractGetState */ json_t *contract_terms; + /** + * Private key to decrypt the contract. + */ + struct TALER_ContractDiffiePrivateP contract_priv; + /** * Set to the returned merge key. */ @@ -70,6 +75,12 @@ struct ContractGetState */ unsigned int expected_response_code; + /** + * True if this is for a 'merge' operation, + * 'false' if this is for a 'deposit' operation. + */ + bool merge; + }; @@ -109,10 +120,42 @@ get_cb (void *cls, const struct TALER_PurseMergePrivateKeyP *mp; const json_t *ct; - /* check that we got what we expected to get! */ - if (GNUNET_OK != - TALER_TESTING_get_trait_merge_priv (ref, - &mp)) + ds->purse_pub = dr->details.success.purse_pub; + if (ds->merge) + { + if (GNUNET_OK != + TALER_TESTING_get_trait_merge_priv (ref, + &mp)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + ds->contract_terms = + TALER_CRYPTO_contract_decrypt_for_merge ( + &ds->contract_priv, + &ds->purse_pub, + dr->details.success.econtract, + dr->details.success.econtract_size, + &ds->merge_priv); + if (0 != + GNUNET_memcmp (mp, + &ds->merge_priv)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (ds->is); + return; + } + } + else + { + ds->contract_terms = + TALER_CRYPTO_contract_decrypt_for_deposit ( + &ds->contract_priv, + dr->details.success.econtract, + dr->details.success.econtract_size); + } + if (NULL == ds->contract_terms) { GNUNET_break (0); TALER_TESTING_interpreter_fail (ds->is); @@ -126,26 +169,14 @@ get_cb (void *cls, TALER_TESTING_interpreter_fail (ds->is); return; } - if (0 != - GNUNET_memcmp (mp, - &dr->details.success.merge_priv)) - { - GNUNET_break (0); - TALER_TESTING_interpreter_fail (ds->is); - return; - } if (1 != /* 1: equal, 0: not equal */ json_equal (ct, - dr->details.success.contract_terms)) + ds->contract_terms)) { GNUNET_break (0); TALER_TESTING_interpreter_fail (ds->is); return; } - ds->contract_terms = json_incref ( - (json_t *) dr->details.success.contract_terms); - ds->merge_priv = dr->details.success.merge_priv; - ds->purse_pub = dr->details.success.purse_pub; } TALER_TESTING_interpreter_next (ds->is); } @@ -180,6 +211,7 @@ get_run (void *cls, TALER_TESTING_interpreter_fail (ds->is); return; } + ds->contract_priv = *contract_priv; ds->dh = TALER_EXCHANGE_contract_get ( is->exchange, contract_priv, @@ -246,7 +278,8 @@ get_traits (void *cls, TALER_TESTING_trait_end () }; - return TALER_TESTING_get_trait (traits, + /* skip 'merge_priv' if we are in 'merge' mode */ + return TALER_TESTING_get_trait (&traits[ds->merge ? 0 : 1], ret, trait, index); @@ -257,6 +290,7 @@ struct TALER_TESTING_Command TALER_TESTING_cmd_contract_get ( const char *label, unsigned int expected_http_status, + bool for_merge, const char *contract_ref) { struct ContractGetState *ds; @@ -264,6 +298,7 @@ TALER_TESTING_cmd_contract_get ( ds = GNUNET_new (struct ContractGetState); ds->expected_response_code = expected_http_status; ds->contract_ref = contract_ref; + ds->merge = for_merge; { struct TALER_TESTING_Command cmd = { .cls = ds, diff --git a/src/testing/testing_api_cmd_purse_deposit.c b/src/testing/testing_api_cmd_purse_deposit.c index c1466088f..ed4967776 100644 --- a/src/testing/testing_api_cmd_purse_deposit.c +++ b/src/testing/testing_api_cmd_purse_deposit.c @@ -154,9 +154,23 @@ deposit_run (void *cls, { struct PurseDepositState *ds = cls; struct TALER_EXCHANGE_PurseDeposit deposits[ds->num_coin_references]; + const struct TALER_PurseContractPublicKeyP *purse_pub; + const struct TALER_TESTING_Command *purse_cmd; (void) cmd; ds->is = is; + + purse_cmd = TALER_TESTING_interpreter_lookup_command (is, + ds->purse_ref); + if (GNUNET_OK != + TALER_TESTING_get_trait_purse_pub (purse_cmd, + &purse_pub)) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + ds->purse_pub = *purse_pub; for (unsigned int i = 0; inum_coin_references; i++) { const struct Coin *cr = &ds->coin_references[i]; diff --git a/src/util/Makefile.am b/src/util/Makefile.am index b92c427b0..94edac021 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -147,8 +147,9 @@ test_amount_LDADD = \ test_crypto_SOURCES = \ test_crypto.c test_crypto_LDADD = \ + libtalerutil.la \ -lgnunetutil \ - libtalerutil.la + -ljansson test_payto_SOURCES = \ test_payto.c diff --git a/src/util/crypto_contract.c b/src/util/crypto_contract.c index fe6b1e6af..bcfe95239 100644 --- a/src/util/crypto_contract.c +++ b/src/util/crypto_contract.c @@ -278,6 +278,9 @@ TALER_CRYPTO_contract_encrypt_for_merge ( GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &nonce, sizeof (nonce)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Encrypting with key %s\n", + TALER_B2S (&key)); contract_encrypt (&nonce, &key, sizeof (key), @@ -315,6 +318,9 @@ TALER_CRYPTO_contract_decrypt_for_merge ( GNUNET_break (0); return NULL; } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Decrypting with key %s\n", + TALER_B2S (&key)); if (GNUNET_OK != contract_decrypt (&key, sizeof (key), diff --git a/src/util/test_crypto.c b/src/util/test_crypto.c index a517b5bc3..186874e3c 100644 --- a/src/util/test_crypto.c +++ b/src/util/test_crypto.c @@ -438,6 +438,50 @@ test_merchant_sigs (void) } +static int +test_contracts (void) +{ + struct TALER_ContractDiffiePrivateP cpriv; + struct TALER_PurseContractPublicKeyP purse_pub; + struct TALER_PurseContractPrivateKeyP purse_priv; + void *econtract; + size_t econtract_size; + struct TALER_PurseMergePrivateKeyP mpriv_in; + struct TALER_PurseMergePrivateKeyP mpriv_out; + json_t *c; + + GNUNET_CRYPTO_ecdhe_key_create (&cpriv.ecdhe_priv); + GNUNET_CRYPTO_eddsa_key_create (&purse_priv.eddsa_priv); + GNUNET_CRYPTO_eddsa_key_get_public (&purse_priv.eddsa_priv, + &purse_pub.eddsa_pub); + memset (&mpriv_in, + 42, + sizeof (mpriv_in)); + c = json_pack ("{s:s}", "test", "value"); + GNUNET_assert (NULL != c); + TALER_CRYPTO_contract_encrypt_for_merge (&purse_pub, + &cpriv, + &mpriv_in, + c, + &econtract, + &econtract_size); + json_decref (c); + c = TALER_CRYPTO_contract_decrypt_for_merge (&cpriv, + &purse_pub, + econtract, + econtract_size, + &mpriv_out); + GNUNET_free (econtract); + if (NULL == c) + return 1; + json_decref (c); + if (0 != GNUNET_memcmp (&mpriv_in, + &mpriv_out)) + return 1; + return 0; +} + + int main (int argc, const char *const argv[]) @@ -454,6 +498,8 @@ main (int argc, return 4; if (0 != test_merchant_sigs ()) return 5; + if (0 != test_contracts ()) + return 6; return 0; }