From 752f10273860d2496fc3eb1e03de6ad4451e7c0f Mon Sep 17 00:00:00 2001 From: Özgür Kesim Date: Fri, 4 Nov 2022 12:18:16 +0100 Subject: policy extensions and age restriction refactoring - refactoring of extension-plugin-mechanism - refactoring of age restriction extension - added policy extensions plugin plumbing - added DB schema and api - policy_details - policy_fulfillments --- src/include/taler_auditor_service.h | 4 +- src/include/taler_auditordb_plugin.h | 4 +- src/include/taler_crypto_lib.h | 43 +++-- src/include/taler_exchange_service.h | 4 +- src/include/taler_exchangedb_plugin.h | 135 ++++++++++--- src/include/taler_extensions.h | 345 +++++++++++++++++++++------------- src/include/taler_extensions_policy.h | 198 +++++++++++++++++++ src/include/taler_json_lib.h | 20 +- src/include/taler_util.h | 58 ++++++ 9 files changed, 616 insertions(+), 195 deletions(-) create mode 100644 src/include/taler_extensions_policy.h (limited to 'src/include') diff --git a/src/include/taler_auditor_service.h b/src/include/taler_auditor_service.h index 30d18e6e9..c20b789cc 100644 --- a/src/include/taler_auditor_service.h +++ b/src/include/taler_auditor_service.h @@ -233,7 +233,7 @@ typedef void * * @param auditor the auditor handle; the auditor must be ready to operate * @param h_wire hash of merchant wire details - * @param h_extensions hash over the extensions, if any + * @param h_policy hash over the policy, if any * @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the auditor) * @param exchange_timestamp timestamp when the contract was finalized, must not be too far in the future * @param wire_deadline date until which the exchange should wire the funds @@ -257,7 +257,7 @@ struct TALER_AUDITOR_DepositConfirmationHandle * TALER_AUDITOR_deposit_confirmation ( struct TALER_AUDITOR_Handle *auditor, const struct TALER_MerchantWireHashP *h_wire, - const struct TALER_ExtensionContractHashP *h_extensions, + const struct TALER_ExtensionPolicyHashP *h_policy, const struct TALER_PrivateContractHashP *h_contract_terms, struct GNUNET_TIME_Timestamp exchange_timestamp, struct GNUNET_TIME_Timestamp wire_deadline, diff --git a/src/include/taler_auditordb_plugin.h b/src/include/taler_auditordb_plugin.h index 9f46004c1..cf27668b6 100644 --- a/src/include/taler_auditordb_plugin.h +++ b/src/include/taler_auditordb_plugin.h @@ -382,9 +382,9 @@ struct TALER_AUDITORDB_DepositConfirmation struct TALER_PrivateContractHashP h_contract_terms; /** - * Hash over the extensions for the deposit. + * Hash over the policy extension for the deposit. */ - struct TALER_ExtensionContractHashP h_extensions; + struct TALER_ExtensionPolicyHashP h_policy; /** * Hash over the wiring information of the merchant. diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index 4c478cefd..6b8000933 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -638,10 +638,9 @@ struct TALER_PrivateContractHashP /** - * Hash used to represent the "public" extensions to - * a contract that is shared with the exchange. + * Hash used to represent the policy extension to a deposit */ -struct TALER_ExtensionContractHashP +struct TALER_ExtensionPolicyHashP { /** * Actual hash value. @@ -727,10 +726,10 @@ struct TALER_PickupIdentifierP /** - * @brief Salted hash over the JSON object representing the configuration of an - * extension. + * @brief Salted hash over the JSON object representing the manifests of + * extensions. */ -struct TALER_ExtensionConfigHashP +struct TALER_ExtensionManifestsHashP { /** * Actual hash value. @@ -3213,7 +3212,7 @@ TALER_wallet_reserve_attest_request_verify ( * @param h_wire hash of the merchant’s account details * @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange) * @param h_age_commitment hash over the age commitment, if applicable to the denomination (maybe NULL) - * @param h_extensions hash over the extensions + * @param h_policy hash over the policy extension * @param h_denom_pub hash of the coin denomination's public key * @param coin_priv coin’s private key * @param wallet_timestamp timestamp when the contract was finalized, must not be too far in the future @@ -3228,7 +3227,7 @@ TALER_wallet_deposit_sign ( const struct TALER_MerchantWireHashP *h_wire, const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_AgeCommitmentHash *h_age_commitment, - const struct TALER_ExtensionContractHashP *h_extensions, + const struct TALER_ExtensionPolicyHashP *h_policy, const struct TALER_DenominationHashP *h_denom_pub, struct GNUNET_TIME_Timestamp wallet_timestamp, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -3245,7 +3244,7 @@ TALER_wallet_deposit_sign ( * @param h_wire hash of the merchant’s account details * @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange) * @param h_age_commitment hash over the age commitment (maybe all zeroes, if not applicable to the denomination) - * @param h_extensions hash over the extensions + * @param h_policy hash over the policy extension * @param h_denom_pub hash of the coin denomination's public key * @param wallet_timestamp timestamp when the contract was finalized, must not be too far in the future * @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests) @@ -3261,7 +3260,7 @@ TALER_wallet_deposit_verify ( const struct TALER_MerchantWireHashP *h_wire, const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_AgeCommitmentHash *h_age_commitment, - const struct TALER_ExtensionContractHashP *h_extensions, + const struct TALER_ExtensionPolicyHashP *h_policy, const struct TALER_DenominationHashP *h_denom_pub, struct GNUNET_TIME_Timestamp wallet_timestamp, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -3666,7 +3665,7 @@ typedef enum TALER_ErrorCode * @param scb function to call to create the signature * @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange) * @param h_wire hash of the merchant’s account details - * @param h_extensions hash over the extensions, can be NULL + * @param h_policy hash over the policy extension, can be NULL * @param exchange_timestamp timestamp when the contract was finalized, must not be too far off * @param wire_deadline date until which the exchange should wire the funds * @param refund_deadline date until which the merchant can issue a refund to the customer via the exchange (can be zero if refunds are not allowed); must not be after the @a wire_deadline @@ -3682,7 +3681,7 @@ TALER_exchange_online_deposit_confirmation_sign ( TALER_ExchangeSignCallback scb, const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_MerchantWireHashP *h_wire, - const struct TALER_ExtensionContractHashP *h_extensions, + const struct TALER_ExtensionPolicyHashP *h_policy, struct GNUNET_TIME_Timestamp exchange_timestamp, struct GNUNET_TIME_Timestamp wire_deadline, struct GNUNET_TIME_Timestamp refund_deadline, @@ -3698,7 +3697,7 @@ TALER_exchange_online_deposit_confirmation_sign ( * * @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange) * @param h_wire hash of the merchant’s account details - * @param h_extensions hash over the extensions, can be NULL + * @param h_policy hash over the policy extension, can be NULL * @param exchange_timestamp timestamp when the contract was finalized, must not be too far off * @param wire_deadline date until which the exchange should wire the funds * @param refund_deadline date until which the merchant can issue a refund to the customer via the exchange (can be zero if refunds are not allowed); must not be after the @a wire_deadline @@ -3713,7 +3712,7 @@ enum GNUNET_GenericReturnValue TALER_exchange_online_deposit_confirmation_verify ( const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_MerchantWireHashP *h_wire, - const struct TALER_ExtensionContractHashP *h_extensions, + const struct TALER_ExtensionPolicyHashP *h_policy, struct GNUNET_TIME_Timestamp exchange_timestamp, struct GNUNET_TIME_Timestamp wire_deadline, struct GNUNET_TIME_Timestamp refund_deadline, @@ -5257,31 +5256,31 @@ TALER_merchant_contract_sign ( /* **************** /management/extensions offline signing **************** */ /** - * Create a signature for the hash of the configuration of an extension + * Create a signature for the hash of the manifests of extensions * - * @param h_config hash of the JSON object representing the configuration + * @param h_manifests hash of the JSON object representing the manifests * @param master_priv private key to sign with * @param[out] master_sig where to write the signature */ void -TALER_exchange_offline_extension_config_hash_sign ( - const struct TALER_ExtensionConfigHashP *h_config, +TALER_exchange_offline_extension_manifests_hash_sign ( + const struct TALER_ExtensionManifestsHashP *h_manifests, const struct TALER_MasterPrivateKeyP *master_priv, struct TALER_MasterSignatureP *master_sig); /** * Verify the signature in @a master_sig of the given hash, taken over the JSON - * blob representing the configuration of an extension + * blob representing the manifests of extensions * - * @param h_config hash of the JSON blob of a configuration of an extension + * @param h_manifest hash of the JSON blob of manifests of extensions * @param master_pub master public key of the exchange * @param master_sig signature of the exchange * @return #GNUNET_OK if signature is valid */ enum GNUNET_GenericReturnValue -TALER_exchange_offline_extension_config_hash_verify ( - const struct TALER_ExtensionConfigHashP *h_config, +TALER_exchange_offline_extension_manifests_hash_verify ( + const struct TALER_ExtensionManifestsHashP *h_manifest, const struct TALER_MasterPublicKeyP *master_pub, const struct TALER_MasterSignatureP *master_sig ); diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index aa9a5b607..e3a349a92 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -894,9 +894,9 @@ struct TALER_EXCHANGE_DepositContractDetail struct TALER_PrivateContractHashP h_contract_terms; /** - * Extension-specific details about the deposit relevant to the exchange. + * Policy extension specific details about the deposit relevant to the exchange. */ - const json_t *extension_details; + json_t *policy_details; /** * Timestamp when the contract was finalized, must match approximately the diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 9640e052f..f21301e7e 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -26,6 +26,7 @@ #include #include "taler_json_lib.h" #include "taler_signatures.h" +#include "taler_extensions_policy.h" /** @@ -220,7 +221,8 @@ enum TALER_EXCHANGEDB_ReplicatedTable TALER_EXCHANGEDB_RT_RECOUP, TALER_EXCHANGEDB_RT_RECOUP_REFRESH, TALER_EXCHANGEDB_RT_EXTENSIONS, - TALER_EXCHANGEDB_RT_EXTENSION_DETAILS, + TALER_EXCHANGEDB_RT_POLICY_DETAILS, + TALER_EXCHANGEDB_RT_POLICY_FULFILLMENTS, TALER_EXCHANGEDB_RT_PURSE_REQUESTS, TALER_EXCHANGEDB_RT_PURSE_DECISION, TALER_EXCHANGEDB_RT_PURSE_MERGES, @@ -438,8 +440,8 @@ struct TALER_EXCHANGEDB_TableData struct TALER_CoinSpendSignatureP coin_sig; struct TALER_WireSaltP wire_salt; struct TALER_PaytoHashP wire_target_h_payto; - bool extension_blocked; - uint64_t extension_details_serial_id; + bool policy_blocked; + uint64_t policy_details_serial_id; } deposits; struct @@ -510,13 +512,32 @@ struct TALER_EXCHANGEDB_TableData struct { char *name; - char *config; + char *manifest; } extensions; struct { - char *extension_options; - } extension_details; + struct GNUNET_HashCode hash_code; + json_t *policy_json; + bool no_policy_json; + struct GNUNET_TIME_Timestamp deadline; + struct TALER_Amount commitment; + struct TALER_Amount accumulated_total; + struct TALER_Amount fee; + struct TALER_Amount transferable; + uint16_t fulfillment_state; /* will also be recomputed */ + uint64_t fulfillment_id; + bool no_fulfillment_id; + } policy_details; + + struct + { + struct GNUNET_TIME_Timestamp fulfillment_timestamp; + char *fulfillment_proof; + struct GNUNET_HashCode h_fulfillment_proof; + struct GNUNET_HashCode *policy_hash_codes; + size_t policy_hash_codes_count; + } policy_fulfillments; struct { @@ -1511,12 +1532,6 @@ struct TALER_EXCHANGEDB_Deposit */ char *receiver_wire_account; - /** - * Additional details for extensions relevant for this - * deposit operation, possibly NULL! - */ - json_t *extension_details; - /** * Time when this request was generated. Used, for example, to * assess when (roughly) the income was achieved for tax purposes. @@ -1558,6 +1573,16 @@ struct TALER_EXCHANGEDB_Deposit */ struct TALER_Amount deposit_fee; + /* + * True if @e policy_json was provided + */ + bool has_policy; + + /** + * Hash over the policy data for this deposit (remains unknown to the + * Exchange). Needed for the verification of the deposit's signature + */ + struct TALER_ExtensionPolicyHashP h_policy; }; @@ -1656,6 +1681,17 @@ struct TALER_EXCHANGEDB_DepositListEntry */ struct TALER_Amount deposit_fee; + /* + * True if a policy was provided with the deposit request + */ + bool has_policy; + + /** + * Hash over the policy data for this deposit (remains unknown to the + * Exchange). Needed for the verification of the deposit's signature + */ + struct TALER_ExtensionPolicyHashP h_policy; + /** * Has the deposit been wired? */ @@ -3530,6 +3566,40 @@ struct TALER_EXCHANGEDB_Plugin bool *conflict, bool *nonce_reuse); + /** + * Retrieve the details to a policy given by its hash_code + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param hc Hash code that identifies the policy + * @param[out] detail retrieved policy details + * @return query execution status + */ + enum GNUNET_DB_QueryStatus + (*get_policy_details)( + void *cls, + const struct GNUNET_HashCode *hc, + struct TALER_PolicyDetails *detail); + + /** + * Persist the policy details that extends a deposit. The particular policy + * - referenced by details->hash_code - might already exist in the table, in + * which case the call will update the contents of the record with @e details + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param details The parsed `struct TALER_PolicyDetails` according to the responsible policy extension. + * @param[out] policy_details_serial_id The ID of the entry in the policy_details table + * @param[out] accumulated_total The total amount accumulated in that policy + * @param[out] fulfillment_state The state of policy. If the state was Insufficient prior to the call and the provided deposit raises the accumulated_total above the commitment, it will be set to Ready. + * @return query execution status + */ + enum GNUNET_DB_QueryStatus + (*persist_policy_details)( + void *cls, + const struct TALER_PolicyDetails *details, + uint64_t *policy_details_serial_id, + struct TALER_Amount *accumulated_total, + enum TALER_PolicyFulfillmentState *fulfillment_state); + /** * Perform deposit operation, checking for sufficient balance @@ -3539,7 +3609,7 @@ struct TALER_EXCHANGEDB_Plugin * @param deposit deposit operation details * @param known_coin_id row of the coin in the known_coins table * @param h_payto hash of the merchant's payto URI - * @param extension_blocked true if an extension is blocking the wire transfer + * @param policy_details_serial_id (pointer to) the row ID of the policy details, maybe NULL * @param[in,out] exchange_timestamp time to use for the deposit (possibly updated) * @param[out] balance_ok set to true if the balance was sufficient * @param[out] in_conflict set to true if the deposit conflicted @@ -3551,7 +3621,7 @@ struct TALER_EXCHANGEDB_Plugin const struct TALER_EXCHANGEDB_Deposit *deposit, uint64_t known_coin_id, const struct TALER_PaytoHashP *h_payto, - bool extension_blocked, + uint64_t *policy_details_serial_id, struct GNUNET_TIME_Timestamp *exchange_timestamp, bool *balance_ok, bool *in_conflict); @@ -3580,6 +3650,19 @@ struct TALER_EXCHANGEDB_Plugin bool *balance_ok); + /** + * Add a proof of fulfillment of an policy + * + * @param cls the plugin-specific state + * @param[in,out] fulfillment The proof of fulfillment and serial_ids of the policy_details along with their new state and potential new amounts. + * @return query execution status + */ + enum GNUNET_DB_QueryStatus + (*add_policy_fulfillment_proof)( + void *cls, + struct TALER_PolicyFulfillmentTransactionData *fulfillment); + + /** * Check if the given @a nonce was properly locked to the given @a old_coin_pub. If so, check if we already * created CS signatures for the given @a nonce and @a new_denom_pub_hashes, @@ -5559,33 +5642,33 @@ struct TALER_EXCHANGEDB_Plugin /** - * Function called to save the configuration of an extension - * (age-restriction, peer2peer, ...) + * Function called to save the manifest of an extension + * (age-restriction, policy-extension, ...) * * @param cls the @e cls of this struct with the plugin-specific state * @param extension_name the name of the extension - * @param config JSON object of the configuration as string, maybe NULL (== disabled extension) + * @param manifest JSON object of the Manifest as string, maybe NULL (== disabled extension) * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*set_extension_config)(void *cls, - const char *extension_name, - const char *config); + (*set_extension_manifest)(void *cls, + const char *extension_name, + const char *manifest); /** - * Function called to retrieve the configuration of an extension - * (age-restriction, peer2peer, ...) + * Function called to retrieve the manifest of an extension + * (age-restriction, policy-extension, ...) * * @param cls the @e cls of this struct with the plugin-specific state * @param extension_name the name of the extension - * @param[out] config JSON object of the configuration as string, maybe NULL (== disabled extension) + * @param[out] manifest Manifest of the extension in JSON encoding, maybe NULL (== disabled extension) * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*get_extension_config)(void *cls, - const char *extension_name, - char **config); + (*get_extension_manifest)(void *cls, + const char *extension_name, + char **manifest); /** diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h index 8e1823cce..5e53d27f8 100644 --- a/src/include/taler_extensions.h +++ b/src/include/taler_extensions.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA + Copyright (C) 2022 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 @@ -24,48 +24,195 @@ #include #include "taler_crypto_lib.h" #include "taler_json_lib.h" +#include "taler_mhd_lib.h" +#include "taler_extensions_policy.h" #define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-" enum TALER_Extension_Type { - TALER_Extension_AgeRestriction = 0, - TALER_Extension_MaxPredefined = 1 // Must be last of the predefined + TALER_Extension_PolicyNull = 0, + + TALER_Extension_AgeRestriction = 1, + TALER_Extension_PolicyMerchantRefund = 2, + TALER_Extension_PolicyBrandtVickeryAuction = 3, + TALER_Extension_PolicyEscrowedPayment = 4, + + TALER_Extension_MaxPredefined = 5 // Must be last of the predefined }; + +/* Forward declarations */ +enum TALER_PolicyFulfillmentState; +struct TALER_PolicyFulfillmentOutcome; + /* * @brief Represents the implementation of an extension. * - * TODO: add documentation + * An "Extension" is an optional feature for the Exchange. + * There are only two types of extensions: + * + * a) Age restriction: This is a special feature that directly interacts with + * denominations and coins, but is not define policies during deposits, see b). + * The implementation of this extension doesn't have to implement any of the + * http- or depost-handlers in the struct. + * + * b) Policies for deposits: These are extensions that define policies (such + * as refund, escrow or auctions) for deposit requests. These extensions have + * to implement at least the deposit- and post-http-handler in the struct to be + * functional. + * + * In addition to the handlers defined in this struct, an extension must also + * be a plugin in the GNUNET_Plugin sense. That is, it must implement the + * functions + * 1: (void *ext)libtaler_extension__init(void *cfg) + * and + * 2: (void *)libtaler_extension__done(void *) + * + * In 1:, the input will be the GNUNET_CONFIGURATION_Handle to the TALER + * configuration and the output must be the struct TALER_Extension * on + * success, NULL otherwise. + * + * In 2:, no arguments are passed and NULL is expected to be returned. */ struct TALER_Extension { - /* simple linked list */ - struct TALER_Extension *next; - + /** + * Type of the extension. Only one extension of a type can be loaded + * at any time. + */ enum TALER_Extension_Type type; + + /** + * The name of the extension, must be unique among all loaded extensions. It + * is used in URLs for /extension/$NAME as well. + */ char *name; + + /** + * Criticality of the extension. It has the same semantics as "critical" has + * for extensions in X.509: + * - if "true", the client must "understand" the extension before proceeding, + * - if "false", clients can safely skip extensions they do not understand. + * (see https://datatracker.ietf.org/doc/html/rfc5280#section-4.2) + */ bool critical; + + /** + * Version of the extension must be provided in Taler's protocol verison ranges notation, see + * https://docs.taler.net/core/api-common.html#protocol-version-ranges + */ char *version; + + /** + * If the extension is marked as enabled, it will be listed in the + * "extensions" field in the "/keys" response. + */ + bool enabled; + + /** + * Opaque (public) configuration object, set by the extension. + */ void *config; - json_t *config_json; - void (*disable)(struct TALER_Extension *ext); - enum GNUNET_GenericReturnValue (*test_json_config)( - const json_t *config); + /** + * @brief Handler to to disable the extension. + * + * @param ext The current extension object + */ + void (*disable)(struct TALER_Extension *ext); - enum GNUNET_GenericReturnValue (*load_json_config)( + /** + * @brief Handler to read an extension-specific configuration in JSON + * encoding and enable the extension. Must be implemented by the extension. + * + * @param ext The extension object. If NULL, the configuration will only be checked. + * @param config A JSON blob + * @return GNUNET_OK if the json was a valid configuration for the extension. + */ + enum GNUNET_GenericReturnValue (*load_config)( struct TALER_Extension *ext, json_t *config); - json_t *(*config_to_json)( + /** + * @brief Handler to return the manifest of the extension in JSON encoding. + * + * See + * https://docs.taler.net/design-documents/006-extensions.html#tsref-type-Extension + * for the definition. + * + * @param ext The extension object + * @return The JSON encoding of the extension, if enabled, NULL otherwise. + */ + json_t *(*manifest)( const struct TALER_Extension *ext); - enum GNUNET_GenericReturnValue (*load_taler_config)( - struct TALER_Extension *ext, - const struct GNUNET_CONFIGURATION_Handle *cfg); + /* ========================= + * Policy related handlers + * ========================= + */ + + /** + * @brief Handler to check an incoming policy and create a + * TALER_PolicyDetails. Can be NULL; + * + * When a deposit request refers to this extension in its policy + * (see https://docs.taler.net/core/api-exchange.html#deposit), this handler + * will be called before the deposit transaction. + * + * @param[in] policy_json Details about the policy, provided by the client + * during a deposit request. + * @param[out] details On success, will contain the details to the policy, + * evaluated by the corresponding policy handler. + * @param[out] error_hint On error, will contain a hint + * @return GNUNET_OK if the data was accepted by the extension. + */ + enum GNUNET_GenericReturnValue (*create_policy_details)( + const json_t *policy_json, + struct TALER_PolicyDetails *details, + const char **error_hint); + + /** + * @brief Handler for POST-requests to the /extensions/$name endpoint. Can be NULL. + * + * @param[in] root The JSON body from the request + * @param[in] args Additional query parameters of the request. + * @param[in,out] details List of policy details related to the incoming fulfillment proof + * @param[in] details_len Size of the list @e details + * @param[out] output JSON output to return to the client + * @return GNUNET_OK on success. + */ + enum GNUNET_GenericReturnValue (*policy_post_handler)( + const json_t *root, + const char *const args[], + struct TALER_PolicyDetails *details, + size_t details_len, + json_t **output); + + /** + * @brief Handler for GET-requests to the /extensions/$name endpoint. Can be NULL. + * + * @param connection The current connection + * @param root The JSON body from the request + * @param args Additional query parameters of the request. + * @return MDH result + */ + MHD_RESULT (*policy_get_handler)( + struct MHD_Connection *connection, + const char *const args[]); +}; + + +/* + * @brief simply linked list of extensions + */ + +struct TALER_Extensions +{ + struct TALER_Extensions *next; + const struct TALER_Extension *extension; }; /** @@ -73,70 +220,57 @@ struct TALER_Extension */ /* - * @brief Sets the configuration of the extensions from the given TALER - * configuration. + * @brief Loads the extensions as shared libraries, as specified in the given + * TALER configuration. * * @param cfg Handle to the TALER configuration * @return GNUNET_OK on success, GNUNET_SYSERR if unknown extensions were found * or any particular configuration couldn't be parsed. */ enum GNUNET_GenericReturnValue -TALER_extensions_load_taler_config ( +TALER_extensions_init ( const struct GNUNET_CONFIGURATION_Handle *cfg); /* - * @brief Checks the given obj to be a valid extension object and fill the - * fields accordingly. + * @brief Parses a given JSON object as an extension manifest. * - * @param[in] obj Object to verify is a valid extension + * @param[in] obj JSON object to parse as an extension manifest * @param{out] critical will be set to 1 if the extension is critical according to obj * @param[out] version will be set to the version of the extension according to obj * @param[out] config will be set to the configuration of the extension according to obj * @return OK on success, Error otherwise */ enum GNUNET_GenericReturnValue -TALER_extensions_is_json_config ( +TALER_extensions_parse_manifest ( json_t *obj, int *critical, const char **version, json_t **config); /* - * @brief Sets the configuration of the extensions from a given JSON object. + * @brief Loads extensions according to the manifests. * - * The JSON object must be of type ExchangeKeysResponse as described in - * https://docs.taler.net/design-documents/006-extensions.html#exchange + * The JSON object must be of type ExtensionsManifestsResponse as described + * in https://docs.taler.net/design-documents/006-extensions.html#exchange * - * @param cfg JSON object containing the configuration for all extensions + * @param cfg JSON object containing the manifests for all extensions * @return #GNUNET_OK on success, #GNUNET_SYSERR if unknown extensions were * found or any particular configuration couldn't be parsed. */ enum GNUNET_GenericReturnValue -TALER_extensions_load_json_config ( - json_t *cfg); +TALER_extensions_load_manifests ( + json_t *manifests); /* * @brief Returns the head of the linked list of extensions. */ -const struct TALER_Extension * +const struct TALER_Extensions * TALER_extensions_get_head (); -/* - * @brief Adds an extension to the linked list of extensions. - * - * @param new_extension the new extension to be added - * @return GNUNET_OK on success, GNUNET_SYSERR if the extension is invalid - * (missing fields), GNUNET_NO if there is already an extension with that name - * or type. - */ -enum GNUNET_GenericReturnValue -TALER_extensions_add ( - struct TALER_Extension *new_extension); - /** * @brief Finds and returns a supported extension by a given type. * - * @param type type of the extension to lookup + * @param type of the extension to lookup * @return extension found, or NULL (should not happen!) */ const struct TALER_Extension * @@ -154,8 +288,6 @@ const struct TALER_Extension * TALER_extensions_get_by_name ( const char *name); -#define TALER_extensions_is_enabled(ext) (NULL != (ext)->config) - /** * @brief Check if a given type of an extension is enabled * @@ -166,12 +298,21 @@ bool TALER_extensions_is_enabled_type ( enum TALER_Extension_Type type); +/** + * @brief Check if an extension is enabled + * + * @param extension The extension handler. + * @return true enabled, false if not enabled, will assert if type is not found. + */ +bool +TALER_extensions_is_enabled ( + const struct TALER_Extension *extension); /* * Verify the signature of a given JSON object for extensions with the master * key of the exchange. * - * The JSON object must be of type ExchangeKeysResponse as described in + * The JSON object must be of type ExtensionsManifestsResponse as described in * https://docs.taler.net/design-documents/006-extensions.html#exchange * * @param extensions JSON object with the extension configuration @@ -181,14 +322,19 @@ TALER_extensions_is_enabled_type ( * and GNUNET_NO if the signature couldn't be verified. */ enum GNUNET_GenericReturnValue -TALER_extensions_verify_json_config_signature ( - json_t *extensions, +TALER_extensions_verify_manifests_signature ( + json_t *manifests, struct TALER_MasterSignatureP *extensions_sig, struct TALER_MasterPublicKeyP *master_pub); /* * TALER Age Restriction Extension + * + * This extension is special insofar as it directly interacts with coins and + * denominations. + * + * At the same time, it doesn't implement and http- or deposit-handlers. */ #define TALER_EXTENSION_SECTION_AGE_RESTRICTION (TALER_EXTENSION_SECTION_PREFIX \ @@ -204,102 +350,39 @@ TALER_extensions_verify_json_config_signature ( | 1 << 21) #define TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_GROUPS "8:10:12:14:16:18:21" -/** - * @brief Registers the extension for age restriction to the list extensions - */ -enum GNUNET_GenericReturnValue -TALER_extension_age_restriction_register (); -/** - * @brief Parses a string as a list of age groups. - * - * The string must consist of a colon-separated list of increasing integers - * between 0 and 31. Each entry represents the beginning of a new age group. - * F.e. the string - * - * "8:10:12:14:16:18:21" - * - * represents the following list of eight age groups: - * - * | Group | Ages | - * | -----:|:------------- | - * | 0 | 0, 1, ..., 7 | - * | 1 | 8, 9 | - * | 2 | 10, 11 | - * | 3 | 12, 13 | - * | 4 | 14, 15 | - * | 5 | 16, 17 | - * | 6 | 18, 19, 20 | - * | 7 | 21, ... | - * - * which is then encoded as a bit mask with the corresponding bits set: - * - * 31 24 16 8 0 - * | | | | | - * oooooooo oo1oo1o1 o1o1o1o1 ooooooo1 - * - * @param groups String representation of age groups - * @param[out] mask Mask representation for age restriction. - * @return Error, if age groups were invalid, OK otherwise. +/* + * @brief Configuration for Age Restriction */ -enum GNUNET_GenericReturnValue -TALER_parse_age_group_string ( - const char *groups, - struct TALER_AgeMask *mask); +struct TALER_AgeRestrictionConfig +{ + struct TALER_AgeMask mask; + uint8_t num_groups; +}; -/** - * @brief Encodes the age mask into a string, like "8:10:12:14:16:18:21" - * - * @param mask Age mask - * @return String representation of the age mask, allocated by GNUNET_malloc. - * Can be used as value in the TALER config. - */ -char * -TALER_age_mask_to_string ( - const struct TALER_AgeMask *mask); /** - * @brief Returns true when age restriction is configured and enabled. + * @brief Retrieve the age restriction configuration + * + * @return age restriction configuration if present, otherwise NULL. */ -bool -TALER_extensions_age_restriction_is_enabled (); +const struct TALER_AgeRestrictionConfig * +TALER_extensions_get_age_restriction_config (); /** - * @brief Returns true when age restriction is configured (might not be - * _enabled_, though). + * @brief Check if age restriction is enabled + * + * @return true, if age restriction is loaded, configured and enabled; otherwise false. */ bool -TALER_extensions_age_restriction_is_configured (); - -/** - * @brief Returns the currently set age mask. Note that even if age - * restriction is not enabled, the age mask might be have a non-zero value. - */ -struct TALER_AgeMask -TALER_extensions_age_restriction_ageMask (); - +TALER_extensions_is_age_restriction_enabled (); /** - * @brief Returns the amount of age groups defined. 0 means no age restriction - * enabled. - */ -size_t -TALER_extensions_age_restriction_num_groups (); - -/** - * @brief Parses a JSON object { "age_groups": "a:b:...y:z" }. + * @brief Return the age mask for age restriction * - * @param root is the json object - * @param[out] mask on success, will contain the age mask - * @return #GNUNET_OK on success and #GNUNET_SYSERR on failure. - */ -enum GNUNET_GenericReturnValue -TALER_JSON_parse_age_groups (const json_t *root, - struct TALER_AgeMask *mask); - - -/* - * TODO: Add Peer2Peer Extension + * @return configured age mask, if age restriction is loaded, configured and enabled; otherwise zero mask. */ +struct TALER_AgeMask +TALER_extensions_get_age_restriction_mask (); #endif diff --git a/src/include/taler_extensions_policy.h b/src/include/taler_extensions_policy.h new file mode 100644 index 000000000..14a581f3c --- /dev/null +++ b/src/include/taler_extensions_policy.h @@ -0,0 +1,198 @@ +/* + This file is part of TALER + Copyright (C) 2022 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 + */ +/** + * @file include/taler_extensions_policy.h + * @brief Interface for policy extensions + * @author Özgür Kesim + */ +#ifndef TALER_EXTENSIONS_POLICY_H +#define TALER_EXTENSIONS_POLICY_H + +#include +#include "taler_crypto_lib.h" +#include "taler_json_lib.h" +#include "taler_mhd_lib.h" + +/* + * @brief Describes the states of fulfillment of a policy bound to a deposit + */ +enum TALER_PolicyFulfillmentState +{ + /* General error state of an fulfillment. */ + TALER_PolicyFulfillmentFailure = 0, + + /* The policy is not yet ready due to insufficient funding. More deposits are + * necessary for it to become ready . */ + TALER_PolicyFulfillmentInsufficient = 1, + + /* The policy is funded and ready, pending */ + TALER_PolicyFulfillmentReady = 2, + + /* Policy is provably fulfilled. */ + TALER_PolicyFulfillmentSuccess = 3, + + /* Policy fulfillment has timed out */ + TALER_PolicyFulfillmentTimeout = 4, + + TALER_PolicyFulfillmentStateCount = TALER_PolicyFulfillmentTimeout + 1 +}; + + +/* + * @brief Returns a string representation of the state of a policy fulfillment + */ +const char * +TALER_policy_fulfillment_state_str (enum TALER_PolicyFulfillmentState state); + + +/* @brief Details of a policy for a deposit request */ +struct TALER_PolicyDetails +{ + /* Hash code that should be used for the .policy_hash_code field when + * this policy is saved in the policy_details table. */ + struct GNUNET_HashCode hash_code; + + /* Content of the policy in its original JSON form */ + json_t *policy_json; + + /* When the deadline is meat and the policy is still in "Ready" state, + * a timeout-handler will transfer the amount + * (total_amount - policy_fee - refreshable_amount) + * to the payto-URI from the corresponding deposit. The value + * amount_refreshable will be refreshable by the owner of the + * associated deposits's coins */ + struct GNUNET_TIME_Timestamp deadline; + + /* The amount to which this policy commits to. It must be at least as + * large as @e policy_fee. */ + struct TALER_Amount commitment; + + /* The total sum of contributions from coins so far to fund this + * policy. It must be at least as large as @commitment in order to be + * sufficiently funded. */ + struct TALER_Amount accumulated_total; + + /* The fee from the exchange for handling the policy. It is due when + * the state changes to Timeout or Success. */ + struct TALER_Amount policy_fee; + + /* The amount that will be transfered to the payto-URIs from the + * corresponding deposits when the fulfillment state changes to Timeout + * or Success. Note that a fulfillment handler can alter this upon + * arrival of a proof of fulfillment. The remaining amount + * (accumulated_amount - policy_amount - transferable_amount) */ + struct TALER_Amount transferable_amount; + + /* The state of fulfillment of a policy. + * - If the state is Insufficient, the client is required to call + * /deposit -maybe multiple times- with enough coins and the same + * policy details in order to reach the required amount. The state is + * then changed to Ready. + * - If the state changes to Timeout or Success, a handler will transfer + * the amount (total_amount - policy_fee - refreshable_amount) to the + * payto-URI from the corresponding deposit. The value + * amount_refreshable will be refreshable by the owner of the + * associated deposits's coins. */ + enum TALER_PolicyFulfillmentState fulfillment_state; + + /* If there is a proof of fulfillment, the row ID from the + * policy_fulfillment table */ + uint64_t policy_fulfillment_id; + bool no_policy_fulfillment_id; +}; + +/* + * @brief All information required for the database transaction when handling a + * proof of fulfillment request. + */ +struct TALER_PolicyFulfillmentTransactionData +{ + /* The incoming proof, provided by a client */ + const json_t *proof; + + /* The Hash of the proof */ + struct GNUNET_HashCode h_proof; + + /* The timestamp of retrieval of the proof */ + struct GNUNET_TIME_Timestamp timestamp; + + /* The ID of the proof in the policy_fulfillment table. Will be set + * during the transaction. Needed to fill the table + * policy_details_fulfillments. */ + uint64_t fulfillment_id; + + /* The list of policy details. Will be updated by the policy handler */ + struct TALER_PolicyDetails *details; + size_t details_count; +}; + + +/* + * @brief Extracts policy details from the deposit's policy options and the policy extensions + * + * @param[in] policy_options JSON of the policy options from a deposit request + * @param[out] details On GNUNET_OK, the parsed details + * @param[out] error_hint On GNUNET_SYSERR, will contain a hint for the reason why it failed + * @return GNUNET_OK on success, GNUNET_NO, when no extension was found. GNUNET_SYSERR when the JSON was + * invalid, with *error_hint maybe non-NULL. + */ +enum GNUNET_GenericReturnValue +TALER_extensions_create_policy_details ( + const json_t *policy_options, + struct TALER_PolicyDetails *details, + const char **error_hint); + + +/* + * ================================ + * Merchant refund policy + * ================================ + */ +struct TALER_ExtensionPolicyMerchantRefundPolicyConfig +{ + struct GNUNET_TIME_Relative max_timeout; +}; + +/* + * ================================ + * Brandt-Vickrey Auctions policy + * ================================ + */ +/* + * @brief Configuration for Brandt-Vickrey auctions policy + */ +struct TALER_ExtensionPolicyBrandtVickreyAuctionConfig +{ + uint16_t max_bidders; + uint16_t max_prices; + struct TALER_Amount auction_fee; +}; + + +/* + * ================================ + * Escrowed Payments policy + * ================================ + */ +/* + * @brief Configuration for escrowed payments policy + */ +struct TALER_ExtensionPolicyEscrowedPaymentsConfig +{ + struct GNUNET_TIME_Relative max_timeout; +}; + +#endif diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h index 12526e954..d0527cc74 100644 --- a/src/include/taler_json_lib.h +++ b/src/include/taler_json_lib.h @@ -765,25 +765,25 @@ TALER_JSON_wire_to_payto (const json_t *wire_s); /** - * Hash @a extensions in deposits. + * Hash @a policy extensions in deposits. * - * @param extensions contract extensions to hash - * @param[out] ech where to write the extension hash + * @param policy contract policy extension to hash + * @param[out] ech where to write the policy hash */ void -TALER_deposit_extension_hash (const json_t *extensions, - struct TALER_ExtensionContractHashP *ech); +TALER_deposit_policy_hash (const json_t *extensions, + struct TALER_ExtensionPolicyHashP *ech); /** - * Hash the @a config of an extension, given as JSON + * Hash the @a manifests of extensions, given as JSON * - * @param config configuration of the extension - * @param[out] eh where to write the extension hash + * @param manifests Manifests of the extensions + * @param[out] eh where to write the hash * @return GNUNET_OK on success, GNUNET_SYSERR on failure */ enum GNUNET_GenericReturnValue -TALER_JSON_extensions_config_hash (const json_t *config, - struct TALER_ExtensionConfigHashP *eh); +TALER_JSON_extensions_manifests_hash (const json_t *manifests, + struct TALER_ExtensionManifestsHashP *eh); /** * Canonicalize a JSON input to a string according to RFC 8785. diff --git a/src/include/taler_util.h b/src/include/taler_util.h index 079f72ed6..1580f3dca 100644 --- a/src/include/taler_util.h +++ b/src/include/taler_util.h @@ -492,5 +492,63 @@ char *strchrnul (const char *s, int c); #endif +/** + * @brief Parses a string as a list of age groups. + * + * The string must consist of a colon-separated list of increasing integers + * between 0 and 31. Each entry represents the beginning of a new age group. + * F.e. the string + * + * "8:10:12:14:16:18:21" + * + * represents the following list of eight age groups: + * + * | Group | Ages | + * | -----:|:------------- | + * | 0 | 0, 1, ..., 7 | + * | 1 | 8, 9 | + * | 2 | 10, 11 | + * | 3 | 12, 13 | + * | 4 | 14, 15 | + * | 5 | 16, 17 | + * | 6 | 18, 19, 20 | + * | 7 | 21, ... | + * + * which is then encoded as a bit mask with the corresponding bits set: + * + * 31 24 16 8 0 + * | | | | | + * oooooooo oo1oo1o1 o1o1o1o1 ooooooo1 + * + * @param groups String representation of age groups + * @param[out] mask Mask representation for age restriction. + * @return Error, if age groups were invalid, OK otherwise. + */ +enum GNUNET_GenericReturnValue +TALER_parse_age_group_string ( + const char *groups, + struct TALER_AgeMask *mask); + +/** + * @brief Encodes the age mask into a string, like "8:10:12:14:16:18:21" + * + * @param mask Age mask + * @return String representation of the age mask, allocated by GNUNET_malloc. + * Can be used as value in the TALER config. + */ +char * +TALER_age_mask_to_string ( + const struct TALER_AgeMask *mask); + +/** + * @brief Parses a JSON object { "age_groups": "a:b:...y:z" }. + * + * @param root is the json object + * @param[out] mask on success, will contain the age mask + * @return #GNUNET_OK on success and #GNUNET_SYSERR on failure. + */ +enum GNUNET_GenericReturnValue +TALER_JSON_parse_age_groups (const json_t *root, + struct TALER_AgeMask *mask); #endif -- cgit v1.2.3