diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/exchangedb/0003-aml_history.sql | 2 | ||||
| -rw-r--r-- | src/exchangedb/0003-aml_staff.sql | 3 | ||||
| -rw-r--r-- | src/exchangedb/0003-aml_status.sql | 2 | ||||
| -rw-r--r-- | src/exchangedb/pg_lookup_kyc_process_by_account.c | 3 | ||||
| -rw-r--r-- | src/exchangedb/pg_select_purse.c | 28 | ||||
| -rw-r--r-- | src/include/taler_crypto_lib.h | 40 | ||||
| -rw-r--r-- | src/include/taler_exchangedb_plugin.h | 307 | ||||
| -rw-r--r-- | src/lib/exchange_api_common.c | 17 | ||||
| -rw-r--r-- | src/testing/test_exchange_p2p.c | 14 | ||||
| -rw-r--r-- | src/util/offline_signatures.c | 8 | 
10 files changed, 382 insertions, 42 deletions
| diff --git a/src/exchangedb/0003-aml_history.sql b/src/exchangedb/0003-aml_history.sql index 009f79d8..d3650f6c 100644 --- a/src/exchangedb/0003-aml_history.sql +++ b/src/exchangedb/0003-aml_history.sql @@ -57,7 +57,7 @@ BEGIN      ,partition_suffix    );    PERFORM comment_partitioned_column( -     '0 for AML decision required, 1 for AML is OK, -1 for account is frozen (prevents further transactions)' +     '0 for all OK, 1 for AML decision required, 2 for account is frozen (prevents further transactions)'      ,'new_status'      ,table_name      ,partition_suffix diff --git a/src/exchangedb/0003-aml_staff.sql b/src/exchangedb/0003-aml_staff.sql index dca0fc8d..00f60985 100644 --- a/src/exchangedb/0003-aml_staff.sql +++ b/src/exchangedb/0003-aml_staff.sql @@ -21,6 +21,7 @@ CREATE TABLE aml_staff    ,master_sig BYTEA CHECK (LENGTH(master_sig)=64)    ,decider_name VARCHAR NOT NULL    ,is_active BOOLEAN NOT NULL +  ,read_only BOOLEAN NOT NULL    ,last_change INT8 NOT NULL    );  COMMENT ON TABLE aml_staff @@ -33,5 +34,7 @@ COMMENT ON COLUMN aml_staff.decider_name    IS 'Name of the staff member.';  COMMENT ON COLUMN aml_staff.is_active    IS 'true if we are currently supporting the use of this AML staff member.'; +COMMENT ON COLUMN aml_staff.is_active +  IS 'true if the member has read-only access.';  COMMENT ON COLUMN aml_staff.last_change    IS 'Latest time when active status changed. Used to detect replays of old messages.'; diff --git a/src/exchangedb/0003-aml_status.sql b/src/exchangedb/0003-aml_status.sql index 1e676bc1..c0683c0d 100644 --- a/src/exchangedb/0003-aml_status.sql +++ b/src/exchangedb/0003-aml_status.sql @@ -53,7 +53,7 @@ BEGIN      ,partition_suffix    );    PERFORM comment_partitioned_column( -     '0 for AML decision required, 1 for AML is OK, -1 for account is frozen (prevents further transactions)' +     '0 for all OK, 1 for AML decision required, 2 for account is frozen (prevents further transactions)'      ,'status'      ,table_name      ,partition_suffix diff --git a/src/exchangedb/pg_lookup_kyc_process_by_account.c b/src/exchangedb/pg_lookup_kyc_process_by_account.c index 6183ae7a..79a9d6c8 100644 --- a/src/exchangedb/pg_lookup_kyc_process_by_account.c +++ b/src/exchangedb/pg_lookup_kyc_process_by_account.c @@ -25,6 +25,7 @@  #include "pg_lookup_kyc_process_by_account.h"  #include "pg_helper.h" +  enum GNUNET_DB_QueryStatus  TEH_PG_lookup_kyc_process_by_account (    void *cls, @@ -59,7 +60,7 @@ TEH_PG_lookup_kyc_process_by_account (    *provider_account_id = NULL;    *provider_legitimization_id = NULL; -   /* Used in #postgres_lookup_kyc_process_by_account() */ +  /* Used in #postgres_lookup_kyc_process_by_account() */    PREPARE (pg,             "lookup_process_by_account",             "SELECT " diff --git a/src/exchangedb/pg_select_purse.c b/src/exchangedb/pg_select_purse.c index 9143e872..6496d4a2 100644 --- a/src/exchangedb/pg_select_purse.c +++ b/src/exchangedb/pg_select_purse.c @@ -66,20 +66,20 @@ TEH_PG_select_purse (    PREPARE (pg,             "select_purse",             "SELECT " -           " merge_pub" -           ",purse_creation" -           ",purse_expiration" -           ",h_contract_terms" -           ",amount_with_fee_val" -           ",amount_with_fee_frac" -           ",balance_val" -           ",balance_frac" -           ",merge_timestamp" -           ",purse_sig IS NOT NULL AS purse_deleted" -           " FROM purse_requests" -           " LEFT JOIN purse_merges USING (purse_pub)" -           " LEFT JOIN purse_deletion USING (purse_pub)" -           " WHERE purse_pub=$1;"); +           " pr.merge_pub" +           ",pr.purse_creation" +           ",pr.purse_expiration" +           ",pr.h_contract_terms" +           ",pr.amount_with_fee_val" +           ",pr.amount_with_fee_frac" +           ",pr.balance_val" +           ",pr.balance_frac" +           ",pm.merge_timestamp" +           ",pd.purse_sig IS NOT NULL AS purse_deleted" +           " FROM purse_requests pr" +           " LEFT JOIN purse_merges pm ON (pm.purse_pub = pr.purse_pub)" +           " LEFT JOIN purse_deletion pd ON (pd.purse_pub = pr.purse_pub)" +           " WHERE pr.purse_pub=$1;");    *merge_timestamp = GNUNET_TIME_UNIT_FOREVER_TS;    return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,                                                     "select_purse", diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index d63fd7cc..5e7ea605 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -147,6 +147,18 @@ struct TALER_ReserveSignatureP  /** + * (Symmetric) key used to encrypt KYC attribute data in the database. + */ +struct TALER_AttributeKeyP +{ +  /** +   * Actual key material. +   */ +  struct GNUNET_HashCode key; +}; + + +/**   * @brief Type of public keys to for merchant authorizations.   * Merchants can issue refunds using the corresponding   * private key. @@ -537,6 +549,30 @@ struct TALER_AmlOfficerSignatureP  /** + * Bitmask with possible AML decision states. + */ +enum TALER_AmlDecisionState +{ + +  /** +   * All AML requirements are currently satisfied. +   */ +  TALER_AML_NONE = 0, + +  /** +   * An AML investigation is pending. +   */ +  TALER_AML_PENDING = 1, + +  /** +   * An AML decision has concluded that the funds must be frozen. +   */ +  TALER_AML_FROZEN = 2 + +}; + + +/**   * @brief Type of blinding keys for Taler.   * must be 32 bytes (DB)   */ @@ -4597,6 +4633,7 @@ TALER_exchange_online_purse_status_verify (   * @param officer_name name of the officer   * @param change_date when to affect the status change   * @param is_active true to enable the officer + * @param read_only true to only allow read-only access   * @param master_priv private key to sign with   * @param[out] master_sig where to write the signature   */ @@ -4606,6 +4643,7 @@ TALER_exchange_offline_aml_officer_status_sign (    const char *officer_name,    struct GNUNET_TIME_Timestamp change_date,    bool is_active, +  bool read_only,    const struct TALER_MasterPrivateKeyP *master_priv,    struct TALER_MasterSignatureP *master_sig); @@ -4617,6 +4655,7 @@ TALER_exchange_offline_aml_officer_status_sign (   * @param officer_name name of the officer   * @param change_date when to affect the status change   * @param is_active true to enable the officer + * @param read_only true to only allow read-only access   * @param master_pub public key to verify against   * @param master_sig the signature the signature   * @return #GNUNET_OK if the signature is valid @@ -4627,6 +4666,7 @@ TALER_exchange_offline_aml_officer_status_verify (    const char *officer_name,    struct GNUNET_TIME_Timestamp change_date,    bool is_active, +  bool read_only,    const struct TALER_MasterPublicKeyP *master_pub,    const struct TALER_MasterSignatureP *master_sig); diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index da28262a..bba59355 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -2245,6 +2245,31 @@ typedef void  /** + * Callback with KYC attributes about a particular user. + * + * @param cls closure + * @param h_payto account for which the attribute data is stored + * @param provider_section provider that must be checked + * @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL; + *        digits can be 0 if exact day, month or year are unknown + * @param collection_time when was the data collected + * @param expiration_time when does the data expire + * @param enc_attributes_size number of bytes in @a enc_attributes + * @param enc_attributes encrypted attribute data + */ +typedef void +(*TALER_EXCHANGEDB_AttributeCallback)( +  void *cls, +  const struct TALER_PaytoHashP *h_payto, +  const char *provider_section, +  const char *birthdate, +  struct GNUNET_TIME_Timestamp collection_time, +  struct GNUNET_TIME_Timestamp expiration_time, +  size_t enc_attributes_size, +  const void *enc_attributes); + + +/**   * Function called with details about deposits that have been made,   * with the goal of auditing the deposit's execution.   * @@ -3101,6 +3126,46 @@ typedef void  /** + * Return AML status. + * + * @param cls closure + * @param row_id current row in AML status table + * @param h_payto account for which the attribute data is stored + * @param threshold currently monthly threshold that would trigger an AML check + * @param decision_time when was the last decision made + */ +typedef void +(*TALER_EXCHANGEDB_AmlStatusCallback)( +  void *cls, +  uint64_t row_id, +  const struct TALER_PaytoHashP *h_payto, +  const struct TALER_Amount *threshold, +  enum TALER_AmlDecisionState status); + + +/** + * Return historic AML decision. + * + * @param cls closure + * @param new_threshold new monthly threshold that would trigger an AML check + * @param new_status AML decision status + * @param decision_time when was the decision made + * @param justification human-readable text justifying the decision + * @param decider_pub public key of the staff member + * @param decider_sig signature of the staff member + */ +typedef void +(*TALER_EXCHANGEDB_AmlHistoryCallback)( +  void *cls, +  const struct TALER_Amount *new_threshold, +  enum TALER_AmlDecisionState new_status, +  struct GNUNET_TIME_Absolute decision_time, +  const char *justification, +  const struct TALER_AmlOfficerPublicKeyP *decider_pub, +  const struct TALER_AmlOfficerSignatureP *decider_sig); + + +/**   * @brief The plugin API, returned from the plugin's "init" function.   * The argument given to "init" is simply a configuration handle.   */ @@ -6435,6 +6500,248 @@ struct TALER_EXCHANGEDB_Plugin      void *kac_cls); +  // FIXME: functions below here not yet implemented! + +  /** +   * Store KYC attribute data. +   * +   * @param cls closure +   * @param h_payto account for which the attribute data is stored +   * @param kyc_prox key for similarity search +   * @param provider_section provider that must be checked +   * @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL; +   *        digits can be 0 if exact day, month or year are unknown +   * @param collection_time when was the data collected +   * @param expiration_time when does the data expire +   * @param enc_attributes_size number of bytes in @a enc_attributes +   * @param enc_attributes encrypted attribute data +   * @return database transaction status +   */ +  enum GNUNET_DB_QueryStatus +  (*insert_kyc_attributes)( +    void *cls, +    const struct TALER_PaytoHashP *h_payto, +    const struct GNUNET_ShortHashCode *kyc_prox, +    const char *provider_section, +    const char *birthdate, +    struct GNUNET_TIME_Timestamp collection_time, +    struct GNUNET_TIME_Timestamp expiration_time, +    size_t enc_attributes_size, +    const void *enc_attributes); + + +  /** +   * Update KYC attribute data. +   * +   * @param cls closure +   * @param h_payto account for which the attribute data is stored +   * @param kyc_prox key for similarity search +   * @param provider_section provider that must be checked +   * @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL; +   *        digits can be 0 if exact day, month or year are unknown +   * @param collection_time when was the data collected +   * @param expiration_time when does the data expire +   * @param enc_attributes_size number of bytes in @a enc_attributes +   * @param enc_attributes encrypted attribute data +   * @return database transaction status +   */ +  enum GNUNET_DB_QueryStatus +  (*update_kyc_attributes)( +    void *cls, +    const struct TALER_PaytoHashP *h_payto, +    const struct GNUNET_ShortHashCode *kyc_prox, +    const char *provider_section, +    const char *birthdate, +    struct GNUNET_TIME_Timestamp collection_time, +    struct GNUNET_TIME_Timestamp expiration_time, +    size_t enc_attributes_size, +    const void *enc_attributes); + + +  /** +   * Lookup similar KYC attribute data. +   * +   * @param cls closure +   * @param h_payto account for which the attribute data is stored +   * @param kyc_prox key for similarity search +   * @param cb callback to invoke on each match +   * @param cb_cls closure for @a cb +   * @return database transaction status +   */ +  enum GNUNET_DB_QueryStatus +  (*select_similar_kyc_attributes)( +    void *cls, +    const struct GNUNET_ShortHashCode *kyc_prox, +    TALER_EXCHANGEDB_AttributeCallback cb, +    void *cb_cls); + + +  /** +   * Lookup KYC attribute data for a specific account. +   * +   * @param cls closure +   * @param h_payto account for which the attribute data is stored +   * @param cb callback to invoke on each match +   * @param cb_cls closure for @a cb +   * @return database transaction status +   */ +  enum GNUNET_DB_QueryStatus +  (*select_kyc_attributes)( +    void *cls, +    const struct TALER_PaytoHashP *h_payto, +    TALER_EXCHANGEDB_AttributeCallback cb, +    void *cb_cls); + + +  /** +   * Insert AML staff record. +   * +   * @param cls closure +   * @param decider_pub public key of the staff member +   * @param master_sig offline signature affirming the AML officer +   * @param decider_name full name of the staff member +   * @param is_active true to enable, false to set as inactive +   * @param read_only true to set read-only access +   * @param last_change when was the change made effective +   * @return database transaction status +   */ +  enum GNUNET_DB_QueryStatus +  (*insert_aml_officer)( +    void *cls, +    const struct TALER_AmlOfficerPublicKeyP *decider_pub, +    const struct TALER_MasterSignatureP *master_sig, +    const char *decider_name, +    bool is_active, +    bool read_only, +    struct GNUNET_TIME_Absolute last_change); + + +  /** +   * Update AML staff record. +   * +   * @param cls closure +   * @param decider_pub public key of the staff member +   * @param master_sig offline signature affirming the AML officer +   * @param decider_name full name of the staff member +   * @param is_active true to enable, false to set as inactive +   * @param read_only true to set read-only access +   * @param last_change when was the change made effective +   * @return database transaction status +   */ +  enum GNUNET_DB_QueryStatus +  (*update_aml_officer)( +    void *cls, +    const struct TALER_AmlOfficerPublicKeyP *decider_pub, +    const struct TALER_MasterSignatureP *master_sig, +    const char *decider_name, +    bool is_active, +    bool read_only, +    struct GNUNET_TIME_Absolute last_change); + + +  /** +   * Fetch AML staff record. +   * +   * @param cls closure +   * @param decider_pub public key of the staff member +   * @param[out] master_sig offline signature affirming the AML officer +   * @param[out] decider_name full name of the staff member +   * @param[out] is_active true to enable, false to set as inactive +   * @param[out] read_only true to set read-only access +   * @param[out] last_change when was the change made effective +   * @return database transaction status +   */ +  enum GNUNET_DB_QueryStatus +  (*lookup_aml_officer)( +    void *cls, +    const struct TALER_AmlOfficerPublicKeyP *decider_pub, +    struct TALER_MasterSignatureP *master_sig, +    char **decider_name, +    bool *is_active, +    bool *read_only, +    struct GNUNET_TIME_Absolute *last_change); + + +  /** +   * Trigger AML process, an account has crossed the threshold. Inserts or +   * updates the AML status. +   * +   * @param cls closure +   * @param h_payto account for which the attribute data is stored +   * @param threshold_crossed existing threshold that was crossed +   * @return database transaction status +   */ +  enum GNUNET_DB_QueryStatus +  (*trigger_aml_process)( +    void *cls, +    const struct TALER_PaytoHashP *h_payto, +    const struct TALER_Amount *threshold_crossed); + + +  /** +   * Lookup AML decisions that have a particular state. +   * +   * @param cls closure +   * @param decision which decision states to filter by +   * @param row_off offset to start from +   * @param forward true to go forward in time, false to go backwards +   * @param cb callback to invoke on each match +   * @param cb_cls closure for @a cb +   * @return database transaction status +   */ +  enum GNUNET_DB_QueryStatus +  (*select_aml_processes)( +    void *cls, +    enum TALER_AmlDecisionState decision, +    uint64_t row_off, +    bool forward, +    TALER_EXCHANGEDB_AmlStatusCallback cb, +    void *cb_cls); + + +  /** +   * Lookup AML decision history for a particular account. +   * +   * @param cls closure +   * @param h_payto which account should we return the AML decision history for +   * @param cb callback to invoke on each match +   * @param cb_cls closure for @a cb +   * @return database transaction status +   */ +  enum GNUNET_DB_QueryStatus +  (*select_aml_history)( +    void *cls, +    const struct TALER_PaytoHashP *h_payto, +    TALER_EXCHANGEDB_AmlHistoryCallback cb, +    void *cb_cls); + + +  /** +   * Insert an AML decision. Inserts into AML history and insert or updates AML +   * status. +   * +   * @param cls closure +   * @param h_payto account for which the attribute data is stored +   * @param new_threshold new monthly threshold that would trigger an AML check +   * @param new_status AML decision status +   * @param decision_time when was the decision made +   * @param justification human-readable text justifying the decision +   * @param decider_pub public key of the staff member +   * @param decider_sig signature of the staff member +   * @return database transaction status +   */ +  enum GNUNET_DB_QueryStatus +  (*insert_aml_decision)( +    void *cls, +    const struct TALER_PaytoHashP *h_payto, +    const struct TALER_Amount *new_threshold, +    enum TALER_AmlDecisionState new_status, +    struct GNUNET_TIME_Absolute decision_time, +    const char *justification, +    const struct TALER_AmlOfficerPublicKeyP *decider_pub, +    const struct TALER_AmlOfficerSignatureP *decider_sig); + +  };  #endif /* _TALER_EXCHANGE_DB_H */ diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c index 3807b997..b895bf9a 100644 --- a/src/lib/exchange_api_common.c +++ b/src/lib/exchange_api_common.c @@ -1336,15 +1336,11 @@ help_purse_deposit (struct CoinHistoryParseContext *pc,    }    if (refunded)    { -    /* We add the amount to refunds here, the original -       deposit will be added to the balance later because -       we still return GNUNET_YES, thus effectively -       cancelling out this operation with respect to -       the final balance. */ +    /* We wave the deposit fee. */      if (0 >          TALER_amount_add (&pc->rtotal,                            &pc->rtotal, -                          amount)) +                          &pc->dk->fees.deposit))      {        /* overflow in refund history? inconceivable! Bad exchange! */        GNUNET_break_op (0); @@ -1415,15 +1411,6 @@ help_purse_refund (struct CoinHistoryParseContext *pc,      GNUNET_break_op (0);      return GNUNET_SYSERR;    } -  if (0 > -      TALER_amount_add (&pc->rtotal, -                        &pc->rtotal, -                        amount)) -  { -    /* overflow in refund history? inconceivable! Bad exchange! */ -    GNUNET_break_op (0); -    return GNUNET_SYSERR; -  }    return GNUNET_NO;  } diff --git a/src/testing/test_exchange_p2p.c b/src/testing/test_exchange_p2p.c index ad95bf63..7c3bdd57 100644 --- a/src/testing/test_exchange_p2p.c +++ b/src/testing/test_exchange_p2p.c @@ -174,17 +174,17 @@ run (void *cls,      TALER_TESTING_cmd_purse_create_with_deposit (        "purse-with-deposit",        MHD_HTTP_OK, -      "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", +      "{\"amount\":\"EUR:0.99\",\"summary\":\"ice cream\"}",        true, /* upload contract */        GNUNET_TIME_UNIT_MINUTES, /* expiration */        "withdraw-coin-1", -      "EUR:1.01", +      "EUR:1.00",        NULL),      TALER_TESTING_cmd_purse_poll (        "push-poll-purse-before-merge",        MHD_HTTP_OK,        "purse-with-deposit", -      "EUR:1", +      "EUR:0.99",        true,        GNUNET_TIME_UNIT_MINUTES),      TALER_TESTING_cmd_contract_get ( @@ -206,13 +206,13 @@ run (void *cls,      TALER_TESTING_cmd_status (        "push-check-post-merge-reserve-balance-get",        "create-reserve-1", -      "EUR:1.03", +      "EUR:1.02",        MHD_HTTP_OK),      /* POST history doesn't yet support P2P transfers */      TALER_TESTING_cmd_reserve_status (        "push-check-post-merge-reserve-balance-post",        "create-reserve-1", -      "EUR:1.03", +      "EUR:1.02",        MHD_HTTP_OK),      /* Test conflicting merge */      TALER_TESTING_cmd_purse_merge ( @@ -261,12 +261,12 @@ run (void *cls,      TALER_TESTING_cmd_status (        "pull-check-post-merge-reserve-balance-get",        "create-reserve-1", -      "EUR:2.02", +      "EUR:2.01",        MHD_HTTP_OK),      TALER_TESTING_cmd_reserve_status (        "push-check-post-merge-reserve-balance-post",        "create-reserve-1", -      "EUR:2.02", +      "EUR:2.01",        MHD_HTTP_OK),      /* create 2nd purse for a deposit conflict */      TALER_TESTING_cmd_purse_create_with_reserve ( diff --git a/src/util/offline_signatures.c b/src/util/offline_signatures.c index d6638998..b1e3b93a 100644 --- a/src/util/offline_signatures.c +++ b/src/util/offline_signatures.c @@ -54,7 +54,7 @@ struct TALER_MasterAmlOfficerStatusPS    struct GNUNET_HashCode h_officer_name GNUNET_PACKED;    /** -   * 1 if enabled, 0 if disabled, in NBO. +   * Bitmask: 1 if enabled; 2 for read-only access. in NBO.     */    uint32_t is_active GNUNET_PACKED;  }; @@ -67,6 +67,7 @@ TALER_exchange_offline_aml_officer_status_sign (    const char *officer_name,    struct GNUNET_TIME_Timestamp change_date,    bool is_active, +  bool read_only,    const struct TALER_MasterPrivateKeyP *master_priv,    struct TALER_MasterSignatureP *master_sig)  { @@ -75,7 +76,7 @@ TALER_exchange_offline_aml_officer_status_sign (      .purpose.size = htonl (sizeof (as)),      .change_date = GNUNET_TIME_timestamp_hton (change_date),      .officer_pub = *officer_pub, -    .is_active = htonl (is_active ? 1 : 0) +    .is_active = htonl ((is_active ? 1 : 0) + (read_only ? 2 : 0))    };    GNUNET_CRYPTO_hash (officer_name, @@ -93,6 +94,7 @@ TALER_exchange_offline_aml_officer_status_verify (    const char *officer_name,    struct GNUNET_TIME_Timestamp change_date,    bool is_active, +  bool read_only,    const struct TALER_MasterPublicKeyP *master_pub,    const struct TALER_MasterSignatureP *master_sig)  { @@ -101,7 +103,7 @@ TALER_exchange_offline_aml_officer_status_verify (      .purpose.size = htonl (sizeof (as)),      .change_date = GNUNET_TIME_timestamp_hton (change_date),      .officer_pub = *officer_pub, -    .is_active = htonl (is_active ? 1 : 0) +    .is_active = htonl ((is_active ? 1 : 0) + (read_only ? 2 : 0))    };    GNUNET_CRYPTO_hash (officer_name, | 
