commit 31c134102eeabd2a0f68e2cb8893790803d5e93f
parent 08387dd6adfd7abb4db184c2f7d663fcaab78eb2
Author: Christian Grothoff <christian@grothoff.org>
Date: Sat, 28 Feb 2026 20:09:18 +0100
implement client logic for /registration endpoint of DD80
Diffstat:
8 files changed, 785 insertions(+), 6 deletions(-)
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am
@@ -43,6 +43,7 @@ libtalerbank_la_SOURCES = \
bank_api_common.c bank_api_common.h \
bank_api_credit.c \
bank_api_debit.c \
+ bank_api_registration.c \
bank_api_transfer.c \
bank_api_parse.c
libtalerbank_la_LIBADD = \
diff --git a/src/bank-lib/bank_api_registration.c b/src/bank-lib/bank_api_registration.c
@@ -0,0 +1,400 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2026 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/bank_api_registration.c
+ * @brief Implementation of the POST /registration request of the bank's HTTP API
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include "bank_api_common.h"
+#include <microhttpd.h> /* just for HTTP status codes */
+#include "taler/taler_signatures.h"
+#include "taler/taler_curl_lib.h"
+
+
+/**
+ * @brief A /registration Handle
+ */
+struct TALER_BANK_RegistrationHandle
+{
+
+ /**
+ * The URL for this request.
+ */
+ char *request_url;
+
+ /**
+ * POST context.
+ */
+ struct TALER_CURL_PostContext post_ctx;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_BANK_RegistrationCallback cb;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+};
+
+
+/**
+ * Convert a #TALER_BANK_SubjectFormat enum value to its JSON string
+ * representation as used by the wire gateway API.
+ *
+ * @param fmt the format enum value
+ * @return corresponding string, or NULL for unknown values
+ */
+static const char *
+format_to_string (enum TALER_BANK_SubjectFormat fmt)
+{
+ switch (fmt)
+ {
+ case TALER_BANK_SUBJECT_FORMAT_SIMPLE:
+ return "SIMPLE";
+ case TALER_BANK_SUBJECT_FORMAT_URI:
+ return "URI";
+ case TALER_BANK_SUBJECT_FORMAT_CH_QR_BILL:
+ return "CH_QR_BILL";
+ }
+ GNUNET_break (0);
+ return NULL;
+}
+
+
+/**
+ * Parse the "subject" field of a successful /registration response.
+ * The field is a JSON object discriminated by "type".
+ *
+ * @param subject_json the JSON object to parse (the inner "subject" value)
+ * @param[out] ts set to the parsed transfer subject on success
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static enum GNUNET_GenericReturnValue
+parse_transfer_subject (const json_t *subject_json,
+ struct TALER_BANK_TransferSubject *ts)
+{
+ const char *type_str;
+ struct GNUNET_JSON_Specification type_spec[] = {
+ GNUNET_JSON_spec_string ("type",
+ &type_str),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (subject_json,
+ type_spec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+
+ if (0 == strcasecmp (type_str,
+ "SIMPLE"))
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount_any ("credit_amount",
+ &ts->details.simple.credit_amount),
+ GNUNET_JSON_spec_string ("subject",
+ &ts->details.simple.subject),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (subject_json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ ts->format = TALER_BANK_SUBJECT_FORMAT_SIMPLE;
+ return GNUNET_OK;
+ }
+ if (0 == strcasecmp (type_str,
+ "URI"))
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("uri",
+ &ts->details.uri.uri),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (subject_json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ ts->format = TALER_BANK_SUBJECT_FORMAT_URI;
+ return GNUNET_OK;
+ }
+ if (0 == strcasecmp (type_str,
+ "CH_QR_BILL"))
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount_any ("credit_amount",
+ &ts->details.ch_qr_bill.credit_amount),
+ GNUNET_JSON_spec_string ("qr_reference_number",
+ &ts->details.ch_qr_bill.qr_reference_number),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (subject_json,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ ts->format = TALER_BANK_SUBJECT_FORMAT_CH_QR_BILL;
+ return GNUNET_OK;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unknown transfer subject type `%s'\n",
+ type_str);
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Function called when we're done processing the HTTP POST /registration
+ * request.
+ *
+ * @param cls the `struct TALER_BANK_RegistrationHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_registration_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_BANK_RegistrationHandle *rh = cls;
+ const json_t *j = response;
+ struct TALER_BANK_RegistrationResponse rr = {
+ .http_status = response_code,
+ .response = response
+ };
+
+ rh->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ rr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ {
+ const json_t *subject_json;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_object_const ("subject",
+ &subject_json),
+ GNUNET_JSON_spec_timestamp ("expiration",
+ &rr.details.ok.expiration),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (j,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ rr.http_status = 0;
+ rr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ if (GNUNET_OK !=
+ parse_transfer_subject (subject_json,
+ &rr.details.ok.subject))
+ {
+ GNUNET_break_op (0);
+ rr.http_status = 0;
+ rr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ }
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ /* Either we or the service is buggy, or there is an API version conflict. */
+ GNUNET_break_op (0);
+ rr.ec = TALER_JSON_get_error_code (j);
+ break;
+ case MHD_HTTP_CONFLICT:
+ /* Covers TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
+ TALER_EC_BANK_UNSUPPORTED_SUBJECT_FORMAT,
+ TALER_EC_BANK_DERIVATION_REUSE, and
+ TALER_EC_BANK_BAD_SIGNATURE. */
+ rr.ec = TALER_JSON_get_error_code (j);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ /* Server had an internal issue; we should retry, but this API
+ leaves that to the application. */
+ rr.ec = TALER_JSON_get_error_code (j);
+ break;
+ default:
+ /* unexpected response code */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u\n",
+ (unsigned int) response_code);
+ GNUNET_break (0);
+ rr.ec = TALER_JSON_get_error_code (j);
+ break;
+ }
+ rh->cb (rh->cb_cls,
+ &rr);
+ TALER_BANK_registration_cancel (rh);
+}
+
+
+struct TALER_BANK_RegistrationHandle *
+TALER_BANK_registration (
+ struct GNUNET_CURL_Context *ctx,
+ const char *base_url,
+ enum TALER_BANK_SubjectFormat format,
+ const struct TALER_Amount *credit_amount,
+ enum TALER_BANK_RegistrationType type,
+ const union TALER_AccountPublicKeyP *account_pub,
+ const struct TALER_ReserveMapAuthorizationPrivateKeyP *authorization_priv,
+ bool recurrent,
+ TALER_BANK_RegistrationCallback res_cb,
+ void *res_cb_cls)
+{
+ struct TALER_ReserveMapAuthorizationPublicKeyP authorization_pub;
+ struct TALER_ReserveMapAuthorizationSignatureP authorization_sig;
+ struct TALER_BANK_RegistrationHandle *rh;
+ const char *format_str;
+ const char *type_str;
+ json_t *reg_obj;
+ CURL *eh;
+
+ TALER_wallet_reserve_map_authorization_sign (account_pub,
+ authorization_priv,
+ &authorization_sig);
+ GNUNET_CRYPTO_eddsa_key_get_public (&authorization_priv->eddsa_priv,
+ &authorization_pub.eddsa_pub);
+ format_str = format_to_string (format);
+ if (NULL == format_str)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ switch (type)
+ {
+ case TALER_BANK_REGISTRATION_TYPE_RESERVE:
+ type_str = "reserve";
+ break;
+ case TALER_BANK_REGISTRATION_TYPE_KYC:
+ type_str = "kyc";
+ break;
+ default:
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ reg_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("format",
+ format_str),
+ TALER_JSON_pack_amount ("credit_amount",
+ credit_amount),
+ GNUNET_JSON_pack_string ("type",
+ type_str),
+ GNUNET_JSON_pack_string ("alg",
+ "EdDSA"),
+ GNUNET_JSON_pack_data_auto ("account_pub",
+ account_pub),
+ GNUNET_JSON_pack_data_auto ("authorization_pub",
+ &authorization_pub),
+ GNUNET_JSON_pack_data_auto ("authorization_signature",
+ &authorization_sig),
+ GNUNET_JSON_pack_bool ("recurrent",
+ recurrent));
+ rh = GNUNET_new (struct TALER_BANK_RegistrationHandle);
+ rh->cb = res_cb;
+ rh->cb_cls = res_cb_cls;
+ rh->request_url = TALER_url_join (base_url,
+ "registration",
+ NULL);
+ if (NULL == rh->request_url)
+ {
+ GNUNET_free (rh);
+ json_decref (reg_obj);
+ return NULL;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Requesting wire transfer subject registration at `%s'\n",
+ rh->request_url);
+ eh = curl_easy_init ();
+ if ( (NULL == eh) ||
+ (CURLE_OK !=
+ curl_easy_setopt (eh,
+ CURLOPT_URL,
+ rh->request_url)) ||
+ (GNUNET_OK !=
+ TALER_curl_easy_post (&rh->post_ctx,
+ eh,
+ reg_obj)) )
+ {
+ GNUNET_break (0);
+ TALER_BANK_registration_cancel (rh);
+ if (NULL != eh)
+ curl_easy_cleanup (eh);
+ json_decref (reg_obj);
+ return NULL;
+ }
+ json_decref (reg_obj);
+
+ rh->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ rh->post_ctx.headers,
+ &handle_registration_finished,
+ rh);
+ return rh;
+}
+
+
+void
+TALER_BANK_registration_cancel (
+ struct TALER_BANK_RegistrationHandle *rh)
+{
+ if (NULL != rh->job)
+ {
+ GNUNET_CURL_job_cancel (rh->job);
+ rh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&rh->post_ctx);
+ GNUNET_free (rh->request_url);
+ GNUNET_free (rh);
+}
+
+
+/* end of bank_api_registration.c */
diff --git a/src/include/taler/taler_bank_service.h b/src/include/taler/taler_bank_service.h
@@ -1035,4 +1035,242 @@ TALER_BANK_auth_free (
struct TALER_BANK_AuthenticationData *auth);
+/* ********************* /registration *********************** */
+
+
+/**
+ * Wire transfer subject formats supported by the registration endpoint.
+ */
+enum TALER_BANK_SubjectFormat
+{
+
+ /**
+ * Simple format: the full key is used as the wire transfer subject.
+ * No entropy constraints apply.
+ */
+ TALER_BANK_SUBJECT_FORMAT_SIMPLE,
+
+ /**
+ * URI format: a prepared-payment confirmation URI is returned.
+ */
+ TALER_BANK_SUBJECT_FORMAT_URI,
+
+ /**
+ * Swiss QR-bill format: a 27-digit QR Reference Number is returned.
+ */
+ TALER_BANK_SUBJECT_FORMAT_CH_QR_BILL
+
+};
+
+
+/**
+ * Transfer type requested at registration.
+ */
+enum TALER_BANK_RegistrationType
+{
+
+ /**
+ * Standard reserve withdrawal.
+ */
+ TALER_BANK_REGISTRATION_TYPE_RESERVE,
+
+ /**
+ * KYC authentication transfer.
+ */
+ TALER_BANK_REGISTRATION_TYPE_KYC
+
+};
+
+
+/**
+ * @brief A /registration Handle
+ */
+struct TALER_BANK_RegistrationHandle;
+
+
+/**
+ * Union holding the subject payload returned by /registration,
+ * discriminated by @e TALER_BANK_SubjectFormat.
+ */
+struct TALER_BANK_TransferSubject
+{
+
+ /**
+ * Which variant is set.
+ */
+ enum TALER_BANK_SubjectFormat format;
+
+ /**
+ * Fields that depend on @e format.
+ */
+ union
+ {
+
+ /**
+ * Details for #TALER_BANK_SUBJECT_FORMAT_SIMPLE.
+ */
+ struct
+ {
+
+ /**
+ * Amount to transfer.
+ */
+ struct TALER_Amount credit_amount;
+
+ /**
+ * Encoded string containing the key or derived short subject.
+ */
+ const char *subject;
+
+ } simple;
+
+ /**
+ * Details for #TALER_BANK_SUBJECT_FORMAT_URI.
+ */
+ struct
+ {
+
+ /**
+ * Prepared-payment confirmation URI.
+ */
+ const char *uri;
+
+ } uri;
+
+ /**
+ * Details for #TALER_BANK_SUBJECT_FORMAT_CH_QR_BILL.
+ */
+ struct
+ {
+
+ /**
+ * Amount to transfer.
+ */
+ struct TALER_Amount credit_amount;
+
+ /**
+ * 27-digit QR Reference Number (NUL-terminated string).
+ */
+ const char *qr_reference_number;
+
+ } ch_qr_bill;
+
+ } details;
+
+};
+
+
+/**
+ * Response details for a /registration POST request.
+ */
+struct TALER_BANK_RegistrationResponse
+{
+
+ /**
+ * HTTP status.
+ */
+ unsigned int http_status;
+
+ /**
+ * Taler error code, #TALER_EC_NONE on success.
+ */
+ enum TALER_ErrorCode ec;
+
+ /**
+ * Full response, NULL if body was not in JSON format.
+ */
+ const json_t *response;
+
+ /**
+ * Details returned depending on the @e http_status.
+ */
+ union
+ {
+
+ /**
+ * Details if status was #MHD_HTTP_OK.
+ */
+ struct
+ {
+
+ /**
+ * The wire transfer subject to communicate to the user.
+ */
+ struct TALER_BANK_TransferSubject subject;
+
+ /**
+ * Expiration time of this registration. The registration is
+ * extended each time the corresponding wire transfer is used.
+ * After expiration, the short subject may be re-assigned to a
+ * different key.
+ */
+ struct GNUNET_TIME_Timestamp expiration;
+
+ } ok;
+
+ } details;
+
+};
+
+
+/**
+ * Callback called with the result of a POST /registration request.
+ *
+ * @param cls closure
+ * @param rr response details
+ */
+typedef void
+(*TALER_BANK_RegistrationCallback) (
+ void *cls,
+ const struct TALER_BANK_RegistrationResponse *rr);
+
+
+/**
+ * Register a public key for wire transfer use, obtaining an appropriate
+ * wire transfer subject linked to the key.
+ *
+ * If the same @a authorization_pub is already registered and has not yet
+ * expired, the endpoint is idempotent and returns the same subject again.
+ *
+ * @param ctx curl context for the event loop
+ * @param base_url base URL for the registration API
+ * @param format the wire transfer subject format to request
+ * @param credit_amount amount to be credited in the wire transfer
+ * @param type transfer type, either reserve withdrawal or KYC authentication
+ * @param account_pub account public key to associate with the registration
+ * @param authorization_priv private key used to sign authorization requests;
+ * the corresponding public key that will be encoded in the subject;
+ * if @a recurrent is true this key may be reused across transfers
+ * @param recurrent true if @a authorization_pub will be reused for
+ * recurring transfers (disables bouncing on reuse)
+ * @param res_cb callback invoked with the final result
+ * @param res_cb_cls closure for @a res_cb
+ * @return NULL if inputs are invalid or an internal error occurred;
+ * in this case @a res_cb is never called
+ */
+struct TALER_BANK_RegistrationHandle *
+TALER_BANK_registration (
+ struct GNUNET_CURL_Context *ctx,
+ const char *base_url,
+ enum TALER_BANK_SubjectFormat format,
+ const struct TALER_Amount *credit_amount,
+ enum TALER_BANK_RegistrationType type,
+ const union TALER_AccountPublicKeyP *account_pub,
+ const struct TALER_ReserveMapAuthorizationPrivateKeyP *authorization_priv,
+ bool recurrent,
+ TALER_BANK_RegistrationCallback res_cb,
+ void *res_cb_cls);
+
+
+/**
+ * Cancel a /registration request. This function must not be called
+ * once the response callback has already been invoked.
+ *
+ * @param[in] rh the registration request handle to cancel
+ */
+void
+TALER_BANK_registration_cancel (
+ struct TALER_BANK_RegistrationHandle *rh);
+
+
#endif /* _TALER_BANK_SERVICE_H */
diff --git a/src/include/taler/taler_crypto_lib.h b/src/include/taler/taler_crypto_lib.h
@@ -199,6 +199,44 @@ struct TALER_ReserveSignatureP
/**
+ * @brief Type of public keys for Taler wire transfer
+ * subject reserve key mapping authorizations.
+ */
+struct TALER_ReserveMapAuthorizationPublicKeyP
+{
+ /**
+ * Taler uses EdDSA for reserves.
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey eddsa_pub;
+};
+
+/**
+ * @brief Type of private keys for Taler wire transfer
+ * subject reserve key mapping authorizations.
+ */
+struct TALER_ReserveMapAuthorizationPrivateKeyP
+{
+ /**
+ * Taler uses EdDSA for reserves.
+ */
+ struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv;
+};
+
+
+/**
+ * @brief Type of signatures used with Taler wire transfer
+ * subject reserve key mapping authorizations.
+ */
+struct TALER_ReserveMapAuthorizationSignatureP
+{
+ /**
+ * Taler uses EdDSA for reserves.
+ */
+ struct GNUNET_CRYPTO_EddsaSignature eddsa_signature;
+};
+
+
+/**
* @brief Type of public keys to for merchant authorizations.
* Merchants can issue refunds using the corresponding
* private key.
@@ -4986,6 +5024,38 @@ TALER_wallet_order_unclaim_verify (
const struct GNUNET_CRYPTO_EddsaSignature *nsig);
+/**
+ * Create reserve map authorization signature.
+ *
+ * @param account_pub account key to authorize mapping to
+ * @param auth_priv private key for the authorization, corresponds to
+ * the wire transfer subject
+ * @param[out] auth_sig set to the signature made with purpose
+ * #TALER_SIGNATURE_WALLET_RESERVE_MAP_AUTHORIZATION
+ */
+void
+TALER_wallet_reserve_map_authorization_sign (
+ const union TALER_AccountPublicKeyP *account_pub,
+ const struct TALER_ReserveMapAuthorizationPrivateKeyP *auth_priv,
+ struct TALER_ReserveMapAuthorizationSignatureP *auth_sig);
+
+
+/**
+ * Verify reserve map authorization signature.
+ *
+ * @param account_pub account key to authorize mapping to
+ * @param auth_pub public key of the authorization, corresponds to
+ * the wire transfer subject
+ * @param auth_sig signature made with purpose
+ * #TALER_SIGNATURE_WALLET_RESERVE_MAP_AUTHORIZATION
+ */
+enum GNUNET_GenericReturnValue
+TALER_wallet_reserve_map_authorization_verify (
+ const union TALER_AccountPublicKeyP *account_pub,
+ const struct TALER_ReserveMapAuthorizationPublicKeyP *auth_pub,
+ const struct TALER_ReserveMapAuthorizationSignatureP *auth_sig);
+
+
/* ********************* merchant signing ************************** */
diff --git a/src/include/taler/taler_error_codes.h b/src/include/taler/taler_error_codes.h
@@ -2776,8 +2776,8 @@ enum TALER_ErrorCode
/**
- * The contract terms version is not invalid.
- * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
+ * The contract terms version is not understood by the merchant backend. Most likely the merchant backend was downgraded to a version incompatible with the content of the database.
+ * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR (500).
* (A value of 0 indicates that the error is generated client-side).
*/
TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_CONTRACT_VERSION = 2107,
@@ -3581,7 +3581,7 @@ enum TALER_ErrorCode
/**
- * The backend could not persist the wire transfer due to the state of the backend. This usually means that the bank account specified is not known to the backend for this instance.
+ * The backend could not persist the wire transfer due to the state of the backend. This usually means that a wire transfer with the same wire transfer subject but a different amount was previously submitted to the backend.
* Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
* (A value of 0 indicates that the error is generated client-side).
*/
diff --git a/src/include/taler/taler_signatures.h b/src/include/taler/taler_signatures.h
@@ -413,6 +413,12 @@
/**
+ * Signature used to set an account public key as new target of a given wire transfer subject identified by the respective authorization key. Used when shortening wire transfer subjects as per DD80. The public keypair used for signing corresponds to the wire transfer subject, while the account key is the reserve public key or the merchant public key the wire transfer should be mapped to. Note that despite its name, this signature is not only created by wallets, but also by merchant backends.
+ */
+#define TALER_SIGNATURE_WALLET_RESERVE_MAP_AUTHORIZATION 1224
+
+
+/**
* Signature on a denomination key announcement.
*/
#define TALER_SIGNATURE_SM_RSA_DENOMINATION_KEY 1250
diff --git a/src/util/taler_error_codes.c b/src/util/taler_error_codes.c
@@ -2761,8 +2761,9 @@ static const struct ErrorCodeAndHint code_hint_pairs[] = {
{
/* 2107 */
.ec = TALER_EC_MERCHANT_GET_ORDERS_ID_INVALID_CONTRACT_VERSION,
- .hint = gettext_noop ("The contract terms version is not invalid."),
- .http_code = MHD_HTTP_FORBIDDEN
+ .hint = gettext_noop (
+ "The contract terms version is not understood by the merchant backend. Most likely the merchant backend was downgraded to a version incompatible with the content of the database."),
+ .http_code = MHD_HTTP_INTERNAL_SERVER_ERROR
},
{
@@ -3561,7 +3562,7 @@ static const struct ErrorCodeAndHint code_hint_pairs[] = {
/* 2557 */
.ec = TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_SUBMISSION,
.hint = gettext_noop (
- "The backend could not persist the wire transfer due to the state of the backend. This usually means that the bank account specified is not known to the backend for this instance."),
+ "The backend could not persist the wire transfer due to the state of the backend. This usually means that a wire transfer with the same wire transfer subject but a different amount was previously submitted to the backend."),
.http_code = MHD_HTTP_CONFLICT
},
diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c
@@ -2174,4 +2174,67 @@ TALER_wallet_order_unclaim_verify (
}
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Message signed by reserve map authorization key.
+ */
+struct TALER_ReserveMapAuthorizationPS
+{
+
+ /**
+ * Purpose is #TALER_SIGNATURE_WALLET_RESERVE_MAP_AUTHORIZATION
+ */
+ struct GNUNET_CRYPTO_SignaturePurpose purpose;
+
+ /**
+ * Account key to associate with the authorization key subject.
+ */
+ union TALER_AccountPublicKeyP account_pub;
+
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+
+void
+TALER_wallet_reserve_map_authorization_sign (
+ const union TALER_AccountPublicKeyP *account_pub,
+ const struct TALER_ReserveMapAuthorizationPrivateKeyP *auth_priv,
+ struct TALER_ReserveMapAuthorizationSignatureP *auth_sig)
+{
+ struct TALER_ReserveMapAuthorizationPS rcp = {
+ .purpose.size = htonl (sizeof (rcp)),
+ .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_MAP_AUTHORIZATION),
+ .account_pub = *account_pub
+ };
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_sign_ (
+ &auth_priv->eddsa_priv,
+ &rcp.purpose,
+ &auth_sig->eddsa_signature));
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_wallet_reserve_map_authorization_verify (
+ const union TALER_AccountPublicKeyP *account_pub,
+ const struct TALER_ReserveMapAuthorizationPublicKeyP *auth_pub,
+ const struct TALER_ReserveMapAuthorizationSignatureP *auth_sig)
+{
+ struct TALER_ReserveMapAuthorizationPS rcp = {
+ .purpose.size = htonl (sizeof (rcp)),
+ .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_MAP_AUTHORIZATION),
+ .account_pub = *account_pub
+ };
+
+ return GNUNET_CRYPTO_eddsa_verify_ (
+ TALER_SIGNATURE_WALLET_RESERVE_MAP_AUTHORIZATION,
+ &rcp.purpose,
+ &auth_sig->eddsa_signature,
+ &auth_pub->eddsa_pub);
+}
+
+
/* end of wallet_signatures.c */