diff options
Diffstat (limited to 'src/util')
44 files changed, 7539 insertions, 1590 deletions
diff --git a/src/util/.gitignore b/src/util/.gitignore index c5f8c76dd..d79786ec7 100644 --- a/src/util/.gitignore +++ b/src/util/.gitignore @@ -9,3 +9,4 @@ test_helper_cs test_helper_cs_home/ test_helper_eddsa test_helper_eddsa_home/ +test_conversion diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 94edac021..d2504588b 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -10,6 +10,7 @@ endif pkgcfgdir = $(prefix)/share/taler/config.d/ pkgcfg_DATA = \ + currencies.conf \ paths.conf \ taler-exchange-secmod-eddsa.conf \ taler-exchange-secmod-rsa.conf \ @@ -20,7 +21,8 @@ EXTRA_DIST = \ taler-config.in \ test_helper_eddsa.conf \ test_helper_rsa.conf \ - test_helper_cs.conf + test_helper_cs.conf \ + test_conversion.sh bin_PROGRAMS = \ taler-exchange-secmod-eddsa \ @@ -77,9 +79,12 @@ lib_LTLIBRARIES = \ libtalerutil_la_SOURCES = \ age_restriction.c \ amount.c \ + aml_signatures.c \ auditor_signatures.c \ config.c \ + conversion.c \ crypto.c \ + crypto_confirmation.c \ crypto_contract.c \ crypto_helper_common.c crypto_helper_common.h \ crypto_helper_rsa.c \ @@ -105,15 +110,17 @@ libtalerutil_la_SOURCES = \ libtalerutil_la_LIBADD = \ -lgnunetutil \ + -lgnunetjson \ -lsodium \ -ljansson \ $(LIBGCRYPT_LIBS) \ -lmicrohttpd $(XLIB) \ + -lunistring \ -lz \ -lm libtalerutil_la_LDFLAGS = \ - -version-info 0:0:0 \ + -version-info 3:3:2 \ -no-undefined @@ -122,6 +129,7 @@ AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH= check_PROGRAMS = \ test_age_restriction \ test_amount \ + test_conversion \ test_crypto \ test_helper_eddsa \ test_helper_rsa \ @@ -138,6 +146,14 @@ test_age_restriction_LDADD = \ -lgnunetutil \ libtalerutil.la +test_conversion_SOURCES = \ + test_conversion.c +test_conversion_LDADD = \ + -lgnunetjson \ + -lgnunetutil \ + -ljansson \ + libtalerutil.la + test_amount_SOURCES = \ test_amount.c test_amount_LDADD = \ diff --git a/src/util/age_restriction.c b/src/util/age_restriction.c index 189ec4e8c..c2a7fc07c 100644 --- a/src/util/age_restriction.c +++ b/src/util/age_restriction.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022-2023 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 @@ -21,7 +21,22 @@ #include "platform.h" #include "taler_util.h" #include "taler_signatures.h" +#include <gnunet/gnunet_json_lib.h> #include <gcrypt.h> +#include <stdint.h> + +struct +#ifndef AGE_RESTRICTION_WITH_ECDSA +GNUNET_CRYPTO_Edx25519PublicKey +#else +GNUNET_CRYPTO_EcdsaPublicKey +#endif +TALER_age_commitment_base_public_key = { + .q_y = { 0x64, 0x41, 0xb9, 0xbd, 0xbf, 0x14, 0x39, 0x8e, + 0x46, 0xeb, 0x5c, 0x1d, 0x34, 0xd3, 0x9b, 0x2f, + 0x9b, 0x7d, 0xc8, 0x18, 0xeb, 0x9c, 0x09, 0xfb, + 0x43, 0xad, 0x16, 0x64, 0xbc, 0x18, 0x49, 0xb5}, +}; void TALER_age_commitment_hash ( @@ -39,7 +54,7 @@ TALER_age_commitment_hash ( } GNUNET_assert (__builtin_popcount (commitment->mask.bits) - 1 == - commitment->num); + (int) commitment->num); hash_context = GNUNET_CRYPTO_hash_context_start (); @@ -62,7 +77,7 @@ TALER_age_commitment_hash ( * defined by the given mask. */ uint8_t -get_age_group ( +TALER_get_age_group ( const struct TALER_AgeMask *mask, uint8_t age) { @@ -81,36 +96,97 @@ get_age_group ( } -enum GNUNET_GenericReturnValue +uint8_t +TALER_get_lowest_age ( + const struct TALER_AgeMask *mask, + uint8_t age) +{ + uint32_t m = mask->bits; + uint8_t group = TALER_get_age_group (mask, age); + uint8_t lowest = 0; + + while (group > 0) + { + m = m >> 1; + if (m & 1) + group--; + lowest++; + } + + return lowest; +} + + +#ifdef AGE_RESTRICTION_WITH_ECDSA +/** + * @brief Helper function to generate a ECDSA private key + * + * @param seed Input seed + * @param size Size of the seed in bytes + * @param[out] pkey ECDSA private key + */ +static void +ecdsa_create_from_seed ( + const void *seed, + size_t seed_size, + struct GNUNET_CRYPTO_EcdsaPrivateKey *key) +{ + enum GNUNET_GenericReturnValue ret; + + GNUNET_assert ( + GNUNET_OK == + GNUNET_CRYPTO_kdf (key, + sizeof (*key), + &seed, + seed_size, + "age commitment", + sizeof ("age commitment") - 1, + NULL, 0)); + /* See GNUNET_CRYPTO_ecdsa_key_create */ + key->d[0] &= 248; + key->d[31] &= 127; + key->d[31] |= 64; +} + + +#endif + + +void TALER_age_restriction_commit ( const struct TALER_AgeMask *mask, - const uint8_t age, + uint8_t age, const struct GNUNET_HashCode *seed, - struct TALER_AgeCommitmentProof *new) + struct TALER_AgeCommitmentProof *ncp) { struct GNUNET_HashCode seed_i; - uint8_t num_pub = __builtin_popcount (mask->bits) - 1; - uint8_t num_priv = get_age_group (mask, age); + uint8_t num_pub; + uint8_t num_priv; size_t i; + GNUNET_assert (NULL != mask); GNUNET_assert (NULL != seed); - GNUNET_assert (NULL != new); - GNUNET_assert (mask->bits & 1); /* fist bit must have been set */ + GNUNET_assert (NULL != ncp); + GNUNET_assert (mask->bits & 1); /* first bit must have been set */ + + num_pub = __builtin_popcount (mask->bits) - 1; + num_priv = TALER_get_age_group (mask, age); + GNUNET_assert (31 > num_priv); GNUNET_assert (num_priv <= num_pub); seed_i = *seed; - new->commitment.mask.bits = mask->bits; - new->commitment.num = num_pub; - new->proof.num = num_priv; - new->proof.keys = NULL; + ncp->commitment.mask.bits = mask->bits; + ncp->commitment.num = num_pub; + ncp->proof.num = num_priv; + ncp->proof.keys = NULL; - new->commitment.keys = GNUNET_new_array ( + ncp->commitment.keys = GNUNET_new_array ( num_pub, struct TALER_AgeCommitmentPublicKeyP); if (0 < num_priv) - new->proof.keys = GNUNET_new_array ( + ncp->proof.keys = GNUNET_new_array ( num_priv, struct TALER_AgeCommitmentPrivateKeyP); @@ -125,47 +201,24 @@ TALER_age_restriction_commit ( /* Only save the private keys for age groups less than num_priv */ if (i < num_priv) - pkey = &new->proof.keys[i]; + pkey = &ncp->proof.keys[i]; #ifndef AGE_RESTRICTION_WITH_ECDSA GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i, sizeof(seed_i), &pkey->priv); GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv, - &new->commitment.keys[i].pub); - seed_i.bits[0] += 1; - } - - return GNUNET_OK; + &ncp->commitment.keys[i].pub); #else - if (GNUNET_OK != - GNUNET_CRYPTO_kdf (pkey, - sizeof (*pkey), - &salti, - sizeof (salti), - "age commitment", - strlen ("age commitment"), - NULL, 0)) - goto FAIL; - - /* See GNUNET_CRYPTO_ecdsa_key_create */ - pkey->priv.d[0] &= 248; - pkey->priv.d[31] &= 127; - pkey->priv.d[31] |= 64; - + ecdsa_create_from_seed (&seed_i, + sizeof(seed_i), + &pkey->priv); GNUNET_CRYPTO_ecdsa_key_get_public (&pkey->priv, - &new->commitment.keys[i].pub); + &ncp->commitment.keys[i].pub); +#endif + seed_i.bits[0] += 1; } - - return GNUNET_OK; - -FAIL: - GNUNET_free (new->commitment.keys); - if (NULL != new->proof.keys) - GNUNET_free (new->proof.keys); - return GNUNET_SYSERR; -#endif } @@ -178,7 +231,7 @@ TALER_age_commitment_derive ( GNUNET_assert (NULL != newacp); GNUNET_assert (orig->proof.num <= orig->commitment.num); - GNUNET_assert (orig->commitment.num == + GNUNET_assert (((int) orig->commitment.num) == __builtin_popcount (orig->commitment.mask.bits) - 1); newacp->commitment.mask = orig->commitment.mask; @@ -215,33 +268,30 @@ TALER_age_commitment_derive ( &newacp->proof.keys[i].priv); } #else - char label[sizeof(uint64_t) + 1] = {0}; - - /* Because GNUNET_CRYPTO_ecdsa_public_key_derive expects char * (and calls - * strlen on it), we must avoid 0's in the label. */ - uint64_t nz_salt = salt | 0x8040201008040201; - memcpy (label, &nz_salt, sizeof(nz_salt)); - - /* 1. Derive the public keys */ - for (size_t i = 0; i < orig->commitment.num; i++) - { - GNUNET_CRYPTO_ecdsa_public_key_derive ( - &orig->commitment.keys[i].pub, - label, - "age commitment derive", - &newacp->commitment.keys[i].pub); - } - - /* 2. Derive the private keys */ - for (size_t i = 0; i < orig->proof.num; i++) { - struct GNUNET_CRYPTO_EcdsaPrivateKey *priv; - priv = GNUNET_CRYPTO_ecdsa_private_key_derive ( - &orig->proof.keys[i].priv, - label, - "age commitment derive"); - newacp->proof.keys[i].priv = *priv; - GNUNET_free (priv); + const char *label = GNUNET_h2s (salt); + + /* 1. Derive the public keys */ + for (size_t i = 0; i < orig->commitment.num; i++) + { + GNUNET_CRYPTO_ecdsa_public_key_derive ( + &orig->commitment.keys[i].pub, + label, + "age commitment derive", + &newacp->commitment.keys[i].pub); + } + + /* 2. Derive the private keys */ + for (size_t i = 0; i < orig->proof.num; i++) + { + struct GNUNET_CRYPTO_EcdsaPrivateKey *priv; + priv = GNUNET_CRYPTO_ecdsa_private_key_derive ( + &orig->proof.keys[i].priv, + label, + "age commitment derive"); + newacp->proof.keys[i].priv = *priv; + GNUNET_free (priv); + } } #endif @@ -296,8 +346,8 @@ TALER_age_commitment_attest ( GNUNET_assert (NULL != attest); GNUNET_assert (NULL != cp); - group = get_age_group (&cp->commitment.mask, - age); + group = TALER_get_age_group (&cp->commitment.mask, + age); GNUNET_assert (group < 32); @@ -331,6 +381,7 @@ TALER_age_commitment_attest ( &at, &attest->signature); } +#undef sign return GNUNET_OK; } @@ -347,8 +398,8 @@ TALER_age_commitment_verify ( GNUNET_assert (NULL != attest); GNUNET_assert (NULL != comm); - group = get_age_group (&comm->mask, - age); + group = TALER_get_age_group (&comm->mask, + age); GNUNET_assert (group < 32); @@ -380,6 +431,7 @@ TALER_age_commitment_verify ( &attest->signature, &comm->keys[group - 1].pub); } +#undef verify } @@ -403,6 +455,9 @@ void TALER_age_proof_free ( struct TALER_AgeProof *proof) { + if (NULL == proof) + return; + if (NULL != proof->keys) { GNUNET_CRYPTO_zero_keys ( @@ -418,21 +473,323 @@ TALER_age_proof_free ( void TALER_age_commitment_proof_free ( - struct TALER_AgeCommitmentProof *cp) + struct TALER_AgeCommitmentProof *acp) { - if (NULL != cp->proof.keys) + if (NULL == acp) + return; + + if (NULL != acp->proof.keys) { GNUNET_CRYPTO_zero_keys ( - cp->proof.keys, - sizeof(*cp->proof.keys) * cp->proof.num); + acp->proof.keys, + sizeof(*acp->proof.keys) * acp->proof.num); - GNUNET_free (cp->proof.keys); - cp->proof.keys = NULL; + GNUNET_free (acp->proof.keys); + acp->proof.keys = NULL; } - if (NULL != cp->commitment.keys) + if (NULL != acp->commitment.keys) { - GNUNET_free (cp->commitment.keys); - cp->commitment.keys = NULL; + GNUNET_free (acp->commitment.keys); + acp->commitment.keys = NULL; } } + + +struct TALER_AgeCommitmentProof * +TALER_age_commitment_proof_duplicate ( + const struct TALER_AgeCommitmentProof *acp) +{ + struct TALER_AgeCommitmentProof *nacp; + + GNUNET_assert (NULL != acp); + GNUNET_assert (__builtin_popcount (acp->commitment.mask.bits) - 1 == + (int) acp->commitment.num); + + nacp = GNUNET_new (struct TALER_AgeCommitmentProof); + + TALER_age_commitment_proof_deep_copy (acp,nacp); + return nacp; +} + + +void +TALER_age_commitment_proof_deep_copy ( + const struct TALER_AgeCommitmentProof *acp, + struct TALER_AgeCommitmentProof *nacp) +{ + GNUNET_assert (NULL != acp); + GNUNET_assert (__builtin_popcount (acp->commitment.mask.bits) - 1 == + (int) acp->commitment.num); + + *nacp = *acp; + nacp->commitment.keys = + GNUNET_new_array (acp->commitment.num, + struct TALER_AgeCommitmentPublicKeyP); + nacp->proof.keys = + GNUNET_new_array (acp->proof.num, + struct TALER_AgeCommitmentPrivateKeyP); + + for (size_t i = 0; i < acp->commitment.num; i++) + nacp->commitment.keys[i] = acp->commitment.keys[i]; + + for (size_t i = 0; i < acp->proof.num; i++) + nacp->proof.keys[i] = acp->proof.keys[i]; +} + + +enum GNUNET_GenericReturnValue +TALER_JSON_parse_age_groups (const json_t *root, + struct TALER_AgeMask *mask) +{ + enum GNUNET_GenericReturnValue ret; + const char *str; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("age_groups", + &str), + GNUNET_JSON_spec_end () + }; + + ret = GNUNET_JSON_parse (root, + spec, + NULL, + NULL); + if (GNUNET_OK == ret) + TALER_parse_age_group_string (str, mask); + + GNUNET_JSON_parse_free (spec); + + return ret; +} + + +enum GNUNET_GenericReturnValue +TALER_parse_age_group_string ( + const char *groups, + struct TALER_AgeMask *mask) +{ + + const char *pos = groups; + unsigned int prev = 0; + unsigned int val = 0; + char c; + + /* reset mask */ + mask->bits = 0; + + while (*pos) + { + c = *pos++; + if (':' == c) + { + if (prev >= val) + return GNUNET_SYSERR; + + mask->bits |= 1 << val; + prev = val; + val = 0; + continue; + } + + if ('0'>c || '9'<c) + return GNUNET_SYSERR; + + val = 10 * val + c - '0'; + + if (0>=val || 32<=val) + return GNUNET_SYSERR; + } + + if (32<=val || prev>=val) + return GNUNET_SYSERR; + + mask->bits |= (1 << val); + mask->bits |= 1; // mark zeroth group, too + + return GNUNET_OK; +} + + +const char * +TALER_age_mask_to_string ( + const struct TALER_AgeMask *mask) +{ + static char buf[256] = {0}; + uint32_t bits = mask->bits; + unsigned int n = 0; + char *pos = buf; + + memset (buf, 0, sizeof(buf)); + + while (bits != 0) + { + bits >>= 1; + n++; + if (0 == (bits & 1)) + { + continue; + } + + if (n > 9) + { + *(pos++) = '0' + n / 10; + } + *(pos++) = '0' + n % 10; + + if (0 != (bits >> 1)) + { + *(pos++) = ':'; + } + } + return buf; +} + + +void +TALER_age_restriction_from_secret ( + const struct TALER_PlanchetMasterSecretP *secret, + const struct TALER_AgeMask *mask, + const uint8_t max_age, + struct TALER_AgeCommitmentProof *ncp) +{ + struct GNUNET_HashCode seed_i = {0}; + uint8_t num_pub; + uint8_t num_priv; + + GNUNET_assert (NULL != mask); + GNUNET_assert (NULL != secret); + GNUNET_assert (NULL != ncp); + GNUNET_assert (mask->bits & 1); /* fist bit must have been set */ + + num_pub = __builtin_popcount (mask->bits) - 1; + num_priv = TALER_get_age_group (mask, max_age); + + GNUNET_assert (31 > num_priv); + GNUNET_assert (num_priv <= num_pub); + + ncp->commitment.mask.bits = mask->bits; + ncp->commitment.num = num_pub; + ncp->proof.num = num_priv; + ncp->proof.keys = NULL; + ncp->commitment.keys = GNUNET_new_array ( + num_pub, + struct TALER_AgeCommitmentPublicKeyP); + if (0 < num_priv) + ncp->proof.keys = GNUNET_new_array ( + num_priv, + struct TALER_AgeCommitmentPrivateKeyP); + + /* Create as many private keys as allow with max_age and derive the + * corresponding public keys. The rest of the needed public keys are created + * by scalar multiplication with the TALER_age_commitment_base_public_key. */ + for (size_t i = 0; i < num_pub; i++) + { + enum GNUNET_GenericReturnValue ret; + const char *label = i < num_priv ? "age-commitment" : "age-factor"; + + ret = GNUNET_CRYPTO_kdf (&seed_i, sizeof(seed_i), + secret, sizeof(*secret), + label, strlen (label), + &i, sizeof(i), + NULL, 0); + GNUNET_assert (GNUNET_OK == ret); + + /* Only generate and save the private keys and public keys for age groups + * less than num_priv */ + if (i < num_priv) + { + struct TALER_AgeCommitmentPrivateKeyP *pkey = &ncp->proof.keys[i]; + +#ifndef AGE_RESTRICTION_WITH_ECDSA + GNUNET_CRYPTO_edx25519_key_create_from_seed (&seed_i, + sizeof(seed_i), + &pkey->priv); + GNUNET_CRYPTO_edx25519_key_get_public (&pkey->priv, + &ncp->commitment.keys[i].pub); +#else + ecdsa_create_from_seed (&seed_i, + sizeof(seed_i), + &pkey->priv); + GNUNET_CRYPTO_ecdsa_key_get_public (&pkey->priv, + &ncp->commitment.keys[i].pub); +#endif + } + else + { + /* For all indices larger than num_priv, derive a public key from + * TALER_age_commitment_base_public_key by scalar multiplication */ +#ifndef AGE_RESTRICTION_WITH_ECDSA + GNUNET_CRYPTO_edx25519_public_key_derive ( + &TALER_age_commitment_base_public_key, + &seed_i, + sizeof(seed_i), + &ncp->commitment.keys[i].pub); +#else + + GNUNET_CRYPTO_ecdsa_public_key_derive ( + &TALER_age_commitment_base_public_key, + GNUNET_h2s (&seed_i), + "age withdraw", + &ncp->commitment.keys[i].pub); +#endif + } + } +} + + +enum GNUNET_GenericReturnValue +TALER_parse_coarse_date ( + const char *in, + const struct TALER_AgeMask *mask, + uint32_t *out) +{ + struct tm date = {0}; + struct tm limit = {0}; + time_t seconds; + + if (NULL == in) + { + /* FIXME[oec]: correct behaviour? */ + *out = 0; + return GNUNET_OK; + } + + GNUNET_assert (NULL !=mask); + GNUNET_assert (NULL !=out); + + if (NULL == strptime (in, "%Y-%m-%d", &date)) + { + if (NULL == strptime (in, "%Y-%m-00", &date)) + if (NULL == strptime (in, "%Y-00-00", &date)) + return GNUNET_SYSERR; + /* turns out that the day is off by one in the last two cases */ + date.tm_mday += 1; + } + + seconds = timegm (&date); + if (-1 == seconds) + return GNUNET_SYSERR; + + /* calculate the limit date for the largest age group */ + { + time_t l = time (NULL); + localtime_r (&l, &limit); + } + limit.tm_year -= TALER_adult_age (mask); + GNUNET_assert (-1 != timegm (&limit)); + + if ((limit.tm_year < date.tm_year) + || ((limit.tm_year == date.tm_year) + && (limit.tm_mon < date.tm_mon)) + || ((limit.tm_year == date.tm_year) + && (limit.tm_mon == date.tm_mon) + && (limit.tm_mday < date.tm_mday))) + *out = seconds / 60 / 60 / 24; + else + *out = 0; + + return GNUNET_OK; +} + + +/* end util/age_restriction.c */ diff --git a/src/util/aml_signatures.c b/src/util/aml_signatures.c new file mode 100644 index 000000000..a61646c0d --- /dev/null +++ b/src/util/aml_signatures.c @@ -0,0 +1,201 @@ +/* + This file is part of TALER + Copyright (C) 2023 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 aml_signatures.c + * @brief Utility functions for AML officers + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_util.h" +#include "taler_signatures.h" + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * @brief Format used to generate the signature on an AML decision. + */ +struct TALER_AmlDecisionPS +{ + /** + * Purpose must be #TALER_SIGNATURE_AML_DECISION. + * Used for an EdDSA signature with the `struct TALER_AmlOfficerPublicKeyP`. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Hash over the justification text. + */ + struct GNUNET_HashCode h_justification GNUNET_PACKED; + + /** + * Time when this decision was made. + */ + struct GNUNET_TIME_TimestampNBO decision_time; + + /** + * New threshold for triggering possibly a new AML process. + */ + struct TALER_AmountNBO new_threshold; + + /** + * Hash of the account identifier to which the decision applies. + */ + struct TALER_PaytoHashP h_payto GNUNET_PACKED; + + /** + * Hash over JSON array with KYC requirements that were imposed. All zeros + * for none. + */ + struct GNUNET_HashCode h_kyc_requirements; + + /** + * What is the new AML status? + */ + uint32_t new_state GNUNET_PACKED; + +}; + +GNUNET_NETWORK_STRUCT_END + +void +TALER_officer_aml_decision_sign ( + const char *justification, + struct GNUNET_TIME_Timestamp decision_time, + const struct TALER_Amount *new_threshold, + const struct TALER_PaytoHashP *h_payto, + enum TALER_AmlDecisionState new_state, + const json_t *kyc_requirements, + const struct TALER_AmlOfficerPrivateKeyP *officer_priv, + struct TALER_AmlOfficerSignatureP *officer_sig) +{ + struct TALER_AmlDecisionPS ad = { + .purpose.purpose = htonl (TALER_SIGNATURE_AML_DECISION), + .purpose.size = htonl (sizeof (ad)), + .decision_time = GNUNET_TIME_timestamp_hton (decision_time), + .h_payto = *h_payto, + .new_state = htonl ((uint32_t) new_state) + }; + + GNUNET_CRYPTO_hash (justification, + strlen (justification), + &ad.h_justification); + TALER_amount_hton (&ad.new_threshold, + new_threshold); + if (NULL != kyc_requirements) + TALER_json_hash (kyc_requirements, + &ad.h_kyc_requirements); + GNUNET_CRYPTO_eddsa_sign (&officer_priv->eddsa_priv, + &ad, + &officer_sig->eddsa_signature); +} + + +enum GNUNET_GenericReturnValue +TALER_officer_aml_decision_verify ( + const char *justification, + struct GNUNET_TIME_Timestamp decision_time, + const struct TALER_Amount *new_threshold, + const struct TALER_PaytoHashP *h_payto, + enum TALER_AmlDecisionState new_state, + const json_t *kyc_requirements, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const struct TALER_AmlOfficerSignatureP *officer_sig) +{ + struct TALER_AmlDecisionPS ad = { + .purpose.purpose = htonl (TALER_SIGNATURE_AML_DECISION), + .purpose.size = htonl (sizeof (ad)), + .decision_time = GNUNET_TIME_timestamp_hton (decision_time), + .h_payto = *h_payto, + .new_state = htonl ((uint32_t) new_state) + }; + + GNUNET_CRYPTO_hash (justification, + strlen (justification), + &ad.h_justification); + TALER_amount_hton (&ad.new_threshold, + new_threshold); + if (NULL != kyc_requirements) + TALER_json_hash (kyc_requirements, + &ad.h_kyc_requirements); + return GNUNET_CRYPTO_eddsa_verify ( + TALER_SIGNATURE_AML_DECISION, + &ad, + &officer_sig->eddsa_signature, + &officer_pub->eddsa_pub); +} + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * @brief Format used to generate the signature on any AML query. + */ +struct TALER_AmlQueryPS +{ + /** + * Purpose must be #TALER_SIGNATURE_AML_QUERY. + * Used for an EdDSA signature with the `struct TALER_AmlOfficerPublicKeyP`. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + +}; + +GNUNET_NETWORK_STRUCT_END + + +void +TALER_officer_aml_query_sign ( + const struct TALER_AmlOfficerPrivateKeyP *officer_priv, + struct TALER_AmlOfficerSignatureP *officer_sig) +{ + struct TALER_AmlQueryPS aq = { + .purpose.purpose = htonl (TALER_SIGNATURE_AML_QUERY), + .purpose.size = htonl (sizeof (aq)) + }; + + GNUNET_CRYPTO_eddsa_sign (&officer_priv->eddsa_priv, + &aq, + &officer_sig->eddsa_signature); +} + + +/** + * Verify AML query authorization. + * + * @param officer_pub public key of AML officer + * @param officer_sig signature to verify + * @return #GNUNET_OK if the signature is valid + */ +enum GNUNET_GenericReturnValue +TALER_officer_aml_query_verify ( + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const struct TALER_AmlOfficerSignatureP *officer_sig) +{ + struct TALER_AmlQueryPS aq = { + .purpose.purpose = htonl (TALER_SIGNATURE_AML_QUERY), + .purpose.size = htonl (sizeof (aq)) + }; + + return GNUNET_CRYPTO_eddsa_verify ( + TALER_SIGNATURE_AML_QUERY, + &aq, + &officer_sig->eddsa_signature, + &officer_pub->eddsa_pub); +} + + +/* end of aml_signatures.c */ diff --git a/src/util/amount.c b/src/util/amount.c index 43116af85..cce84d73a 100644 --- a/src/util/amount.c +++ b/src/util/amount.c @@ -40,6 +40,31 @@ invalidate (struct TALER_Amount *a) enum GNUNET_GenericReturnValue +TALER_check_currency (const char *str) +{ + if (strlen (str) >= TALER_CURRENCY_LEN) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Currency code name `%s' is too long\n", + str); + return GNUNET_SYSERR; + } + /* validate str has only legal characters in it! */ + for (unsigned int i = 0; '\0' != str[i]; i++) + { + if ( ('A' > str[i]) || ('Z' < str[i]) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Currency code name `%s' contains illegal characters (only A-Z allowed)\n", + str); + return GNUNET_SYSERR; + } + } + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue TALER_string_to_amount (const char *str, struct TALER_Amount *amount) { @@ -62,6 +87,7 @@ TALER_string_to_amount (const char *str, /* parse currency */ colon = strchr (str, (int) ':'); if ( (NULL == colon) || + (colon == str) || ((colon - str) >= TALER_CURRENCY_LEN) ) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, @@ -72,14 +98,15 @@ TALER_string_to_amount (const char *str, } GNUNET_assert (TALER_CURRENCY_LEN > (colon - str)); - memcpy (amount->currency, - str, - colon - str); + for (unsigned int i = 0; i<colon - str; i++) + amount->currency[i] = str[i]; /* 0-terminate *and* normalize buffer by setting everything to '\0' */ memset (&amount->currency [colon - str], 0, TALER_CURRENCY_LEN - (colon - str)); - + if (GNUNET_OK != + TALER_check_currency (amount->currency)) + return GNUNET_SYSERR; /* skip colon */ value = colon + 1; if ('\0' == value[0]) @@ -193,9 +220,8 @@ TALER_amount_hton (struct TALER_AmountNBO *res, TALER_amount_is_valid (d)); res->value = GNUNET_htonll (d->value); res->fraction = htonl (d->fraction); - memcpy (res->currency, - d->currency, - TALER_CURRENCY_LEN); + for (unsigned int i = 0; i<TALER_CURRENCY_LEN; i++) + res->currency[i] = d->currency[i]; } @@ -205,9 +231,9 @@ TALER_amount_ntoh (struct TALER_Amount *res, { res->value = GNUNET_ntohll (dn->value); res->fraction = ntohl (dn->fraction); - memcpy (res->currency, - dn->currency, - TALER_CURRENCY_LEN); + GNUNET_memcpy (res->currency, + dn->currency, + TALER_CURRENCY_LEN); GNUNET_assert (GNUNET_YES == TALER_amount_is_valid (res)); } @@ -219,15 +245,15 @@ TALER_amount_set_zero (const char *cur, { size_t slen; - slen = strlen (cur); - if (slen >= TALER_CURRENCY_LEN) + if (GNUNET_OK != + TALER_check_currency (cur)) return GNUNET_SYSERR; + slen = strlen (cur); memset (amount, 0, sizeof (struct TALER_Amount)); - memcpy (amount->currency, - cur, - slen); + for (unsigned int i = 0; i<slen; i++) + amount->currency[i] = cur[i]; return GNUNET_OK; } @@ -236,7 +262,10 @@ enum GNUNET_GenericReturnValue TALER_amount_is_valid (const struct TALER_Amount *amount) { if (amount->value > TALER_AMOUNT_MAX_VALUE) + { + GNUNET_break (0); return GNUNET_SYSERR; + } return ('\0' != amount->currency[0]) ? GNUNET_OK : GNUNET_NO; } @@ -553,8 +582,8 @@ const char * TALER_amount2s (const struct TALER_Amount *amount) { /* 24 is sufficient for a uint64_t value in decimal; 3 is for ":.\0" */ - static GNUNET_THREAD_LOCAL char result[TALER_AMOUNT_FRAC_LEN - + TALER_CURRENCY_LEN + 3 + 24]; + static TALER_THREAD_LOCAL char result[TALER_AMOUNT_FRAC_LEN + + TALER_CURRENCY_LEN + 3 + 24]; struct TALER_Amount norm; if (GNUNET_YES != TALER_amount_is_valid (amount)) @@ -680,9 +709,9 @@ TALER_amount_multiply (struct TALER_Amount *result, if (GNUNET_SYSERR == TALER_amount_normalize (&in)) return TALER_AAR_INVALID_NORMALIZATION_FAILED; - memcpy (result->currency, - amount->currency, - TALER_CURRENCY_LEN); + GNUNET_memcpy (result->currency, + amount->currency, + TALER_CURRENCY_LEN); if ( (0 == factor) || ( (0 == in.value) && (0 == in.fraction) ) ) diff --git a/src/util/bench_age_restriction.c b/src/util/bench_age_restriction.c new file mode 100644 index 000000000..abda9416a --- /dev/null +++ b/src/util/bench_age_restriction.c @@ -0,0 +1,208 @@ +/** + * @file util/bench_age_restriction.c + * @brief Measure Commit, Attest, Verify, Derive and Compare + * @author Özgür Kesim + * + * compile in exchange/src/util with + * + * gcc benc_age_restriction.c \ + * -lgnunetutil -lgnunetjson -lsodium -ljansson \ + * -L/usr/lib/x86_64-linux-gnu -lmicrohttpd -ltalerutil \ + * -I../include \ + * -o bench_age_restriction + * + */ +#include "platform.h" +#include <math.h> +#include <gnunet/gnunet_util_lib.h> +#include <taler/taler_util.h> +#include <taler/taler_crypto_lib.h> + +static struct TALER_AgeMask + age_mask = { .bits = 1 + | 1 << 8 | 1 << 10 | 1 << 12 + | 1 << 14 | 1 << 16 | 1 << 18 | 1 << 21 }; + +extern uint8_t +get_age_group ( + const struct TALER_AgeMask *mask, + uint8_t age); + +/** + * Encodes the age mask into a string, like "8:10:12:14:16:18:21" + */ +char * +age_mask_to_string ( + const struct TALER_AgeMask *m) +{ + uint32_t bits = m->bits; + unsigned int n = 0; + char *buf = GNUNET_malloc (32 * 3); // max characters possible + char *pos = buf; + + if (NULL == buf) + { + return buf; + } + + while (bits != 0) + { + bits >>= 1; + n++; + if (0 == (bits & 1)) + { + continue; + } + + if (n > 9) + { + *(pos++) = '0' + n / 10; + } + *(pos++) = '0' + n % 10; + + if (0 != (bits >> 1)) + { + *(pos++) = ':'; + } + } + return buf; +} + + +#define ITER 2000 + +double +average (long *times, size_t size) +{ + double mean = 0.0; + for (int i = 0; i < size; i++) + { + mean += times[i]; + } + return mean / size; +} + + +double +stdev (long *times, size_t size) +{ + double mean = average (times, size); + double V = 0.0; + for (int i = 0; i < size; i++) + { + double d = times[i] - mean; + d *= d; + V += d; + } + return sqrt (V / size); +} + + +#define pr(n,t, i) printf ("%10s (%dx):\t%.2f ± %.2fµs\n", (n), i, average ( \ + &t[0], ITER) / 1000, stdev (&t[0], ITER) / 1000); \ + i = 0; + +#define starttime clock_gettime (CLOCK_MONOTONIC, &tstart) +#define stoptime clock_gettime (CLOCK_MONOTONIC, &tend); \ + times[i] = ((long) tend.tv_sec * 1000 * 1000 * 1000 + tend.tv_nsec) \ + - ((long) tstart.tv_sec * 1000 * 1000 * 1000 + tstart.tv_nsec); + + +int +main (int argc, + const char *const argv[]) +{ + struct timespec tstart = {0,0}, tend = {0,0}; + enum GNUNET_GenericReturnValue ret; + struct TALER_AgeCommitmentProof acp = {0}; + uint8_t age = 21; + uint8_t age_group = get_age_group (&age_mask, age); + struct GNUNET_HashCode seed; + long times[ITER] = {0}; + int i = 0; + + // commit + for (; i < ITER; i++) + { + starttime; + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &seed, + sizeof(seed)); + + ret = TALER_age_restriction_commit (&age_mask, + age, + &seed, + &acp); + stoptime; + + } + pr ("commit", times, i); + + // attest + for (; i < ITER; i++) + { + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &seed, + sizeof(seed)); + + ret = TALER_age_restriction_commit (&age_mask, + age, + &seed, + &acp); + + starttime; + uint8_t min_group = get_age_group (&age_mask, 13); + struct TALER_AgeAttestation at = {0}; + ret = TALER_age_commitment_attest (&acp, + 13, + &at); + stoptime; + } + pr ("attest", times, i); + + // verify + for (; i < ITER; i++) + { + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &seed, + sizeof(seed)); + + ret = TALER_age_restriction_commit (&age_mask, + age, + &seed, + &acp); + + uint8_t min_group = get_age_group (&age_mask, 13); + struct TALER_AgeAttestation at = {0}; + + ret = TALER_age_commitment_attest (&acp, + 13, + &at); + starttime; + ret = TALER_age_commitment_verify (&acp.commitment, + 13, + &at); + stoptime; + } + pr ("verify", times, i); + + // derive + for (; i < ITER; i++) + { + struct TALER_AgeCommitmentProof acp2 = {0}; + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &seed, + sizeof(seed)); + starttime; + TALER_age_commitment_derive (&acp, + &seed, + &acp2); + stoptime; + } + pr ("derive", times, i); + + return 0; +} + + +/* end of tv_age_restriction.c */ diff --git a/src/util/config.c b/src/util/config.c index c00792469..f5accaad8 100644 --- a/src/util/config.c +++ b/src/util/config.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2020 Taler Systems SA + Copyright (C) 2014-2023 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 @@ -21,7 +21,7 @@ */ #include "platform.h" #include "taler_util.h" - +#include <gnunet/gnunet_json_lib.h> enum GNUNET_GenericReturnValue TALER_config_get_amount (const struct GNUNET_CONFIGURATION_Handle *cfg, @@ -166,3 +166,345 @@ TALER_config_get_currency (const struct GNUNET_CONFIGURATION_Handle *cfg, } return GNUNET_OK; } + + +/** + * Closure for #parse_currencies_cb(). + */ +struct CurrencyParserContext +{ + /** + * Current offset in @e cspecs. + */ + unsigned int num_currencies; + + /** + * Length of the @e cspecs array. + */ + unsigned int len_cspecs; + + /** + * Array of currency specifications (see DD 51). + */ + struct TALER_CurrencySpecification *cspecs; + + /** + * Configuration we are parsing. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Set to true if the configuration was malformed. + */ + bool failure; +}; + + +/** + * Function to iterate over section. + * + * @param cls closure with a `struct CurrencyParserContext *` + * @param section name of the section + */ +static void +parse_currencies_cb (void *cls, + const char *section) +{ + struct CurrencyParserContext *cpc = cls; + struct TALER_CurrencySpecification *cspec; + unsigned long long num; + char *str; + + if (cpc->failure) + return; + if (0 != strncasecmp (section, + "currency-", + strlen ("currency-"))) + return; /* not interesting */ + if (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_yesno (cpc->cfg, + section, + "ENABLED")) + return; /* disabled */ + if (cpc->len_cspecs == cpc->num_currencies) + { + GNUNET_array_grow (cpc->cspecs, + cpc->len_cspecs, + cpc->len_cspecs * 2 + 4); + } + cspec = &cpc->cspecs[cpc->num_currencies++]; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cpc->cfg, + section, + "CODE", + &str)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "CODE"); + cpc->failure = true; + return; + } + if (GNUNET_OK != + TALER_check_currency (str)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "CODE", + "Currency code name given is invalid"); + cpc->failure = true; + GNUNET_free (str); + return; + } + memset (cspec->currency, + 0, + sizeof (cspec->currency)); + /* Already checked in TALER_check_currency(), repeated here + just to make static analysis happy */ + GNUNET_assert (strlen (str) < TALER_CURRENCY_LEN); + strcpy (cspec->currency, + str); + GNUNET_free (str); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cpc->cfg, + section, + "NAME", + &str)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "NAME"); + cpc->failure = true; + return; + } + cspec->name = str; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cpc->cfg, + section, + "FRACTIONAL_INPUT_DIGITS", + &num)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "FRACTIONAL_INPUT_DIGITS"); + cpc->failure = true; + return; + } + if (num > TALER_AMOUNT_FRAC_LEN) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "FRACTIONAL_INPUT_DIGITS", + "Number given is too big"); + cpc->failure = true; + return; + } + cspec->num_fractional_input_digits = num; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cpc->cfg, + section, + "FRACTIONAL_NORMAL_DIGITS", + &num)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "FRACTIONAL_NORMAL_DIGITS"); + cpc->failure = true; + return; + } + if (num > TALER_AMOUNT_FRAC_LEN) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "FRACTIONAL_NORMAL_DIGITS", + "Number given is too big"); + cpc->failure = true; + return; + } + cspec->num_fractional_normal_digits = num; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cpc->cfg, + section, + "FRACTIONAL_TRAILING_ZERO_DIGITS", + &num)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "FRACTIONAL_TRAILING_ZERO_DIGITS"); + cpc->failure = true; + return; + } + if (num > TALER_AMOUNT_FRAC_LEN) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "FRACTIONAL_TRAILING_ZERO_DIGITS", + "Number given is too big"); + cpc->failure = true; + return; + } + cspec->num_fractional_trailing_zero_digits = num; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cpc->cfg, + section, + "ALT_UNIT_NAMES", + &str)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + section, + "ALT_UNIT_NAMES"); + cpc->failure = true; + return; + } + { + json_error_t err; + + cspec->map_alt_unit_names = json_loads (str, + JSON_REJECT_DUPLICATES, + &err); + GNUNET_free (str); + if (NULL == cspec->map_alt_unit_names) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "ALT_UNIT_NAMES", + err.text); + cpc->failure = true; + return; + } + } + if (GNUNET_OK != + TALER_check_currency_scale_map (cspec->map_alt_unit_names)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section, + "ALT_UNIT_NAMES", + "invalid map entry detected"); + cpc->failure = true; + json_decref (cspec->map_alt_unit_names); + cspec->map_alt_unit_names = NULL; + return; + } +} + + +enum GNUNET_GenericReturnValue +TALER_check_currency_scale_map (const json_t *map) +{ + /* validate map only maps from decimal numbers to strings! */ + const char *str; + const json_t *val; + bool zf = false; + + if (! json_is_object (map)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Object required for currency scale map\n"); + return GNUNET_SYSERR; + } + json_object_foreach ((json_t *) map, str, val) + { + int idx; + char dummy; + + if ( (1 != sscanf (str, + "%d%c", + &idx, + &dummy)) || + (idx < -12) || + (idx > 24) || + (! json_is_string (val) ) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Invalid entry `%s' in currency scale map\n", + str); + return GNUNET_SYSERR; + } + if (0 == idx) + zf = true; + } + if (! zf) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Entry for 0 missing in currency scale map\n"); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +TALER_CONFIG_parse_currencies (const struct GNUNET_CONFIGURATION_Handle *cfg, + unsigned int *num_currencies, + struct TALER_CurrencySpecification **cspecs) +{ + struct CurrencyParserContext cpc = { + .cfg = cfg + }; + + GNUNET_CONFIGURATION_iterate_sections (cfg, + &parse_currencies_cb, + &cpc); + if (cpc.failure) + { + GNUNET_array_grow (cpc.cspecs, + cpc.len_cspecs, + 0); + return GNUNET_SYSERR; + } + GNUNET_array_grow (cpc.cspecs, + cpc.len_cspecs, + cpc.num_currencies); + *num_currencies = cpc.num_currencies; + *cspecs = cpc.cspecs; + if (0 == *num_currencies) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No currency formatting specification found! Please check your installation!\n"); + return GNUNET_NO; + } + return GNUNET_OK; +} + + +json_t * +TALER_CONFIG_currency_specs_to_json (const struct + TALER_CurrencySpecification *cspec) +{ + return GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("name", + cspec->name), + /* 'currency' is deprecated as of exchange v18 and merchant v6; + remove this line once current-age > 6*/ + GNUNET_JSON_pack_string ("currency", + cspec->currency), + GNUNET_JSON_pack_uint64 ("num_fractional_input_digits", + cspec->num_fractional_input_digits), + GNUNET_JSON_pack_uint64 ("num_fractional_normal_digits", + cspec->num_fractional_normal_digits), + GNUNET_JSON_pack_uint64 ("num_fractional_trailing_zero_digits", + cspec->num_fractional_trailing_zero_digits), + GNUNET_JSON_pack_object_incref ("alt_unit_names", + cspec->map_alt_unit_names)); +} + + +void +TALER_CONFIG_free_currencies ( + unsigned int num_currencies, + struct TALER_CurrencySpecification cspecs[static num_currencies]) +{ + for (unsigned int i = 0; i<num_currencies; i++) + { + struct TALER_CurrencySpecification *cspec = &cspecs[i]; + + GNUNET_free (cspec->name); + json_decref (cspec->map_alt_unit_names); + } + GNUNET_array_grow (cspecs, + num_currencies, + 0); +} diff --git a/src/util/conversion.c b/src/util/conversion.c new file mode 100644 index 000000000..a7bc63789 --- /dev/null +++ b/src/util/conversion.c @@ -0,0 +1,405 @@ +/* + This file is part of TALER + Copyright (C) 2023 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 conversion.c + * @brief helper routines to run some external JSON-to-JSON converter + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_util.h" +#include <gnunet/gnunet_util_lib.h> + + +struct TALER_JSON_ExternalConversion +{ + /** + * Callback to call with the result. + */ + TALER_JSON_JsonCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Handle to the helper process. + */ + struct GNUNET_OS_Process *helper; + + /** + * Pipe for the stdin of the @e helper. + */ + struct GNUNET_DISK_FileHandle *chld_stdin; + + /** + * Pipe for the stdout of the @e helper. + */ + struct GNUNET_DISK_FileHandle *chld_stdout; + + /** + * Handle to wait on the child to terminate. + */ + struct GNUNET_ChildWaitHandle *cwh; + + /** + * Task to read JSON output from the child. + */ + struct GNUNET_SCHEDULER_Task *read_task; + + /** + * Task to send JSON input to the child. + */ + struct GNUNET_SCHEDULER_Task *write_task; + + /** + * Buffer with data we need to send to the helper. + */ + void *write_buf; + + /** + * Buffer for reading data from the helper. + */ + void *read_buf; + + /** + * Total length of @e write_buf. + */ + size_t write_size; + + /** + * Current write position in @e write_buf. + */ + size_t write_pos; + + /** + * Current size of @a read_buf. + */ + size_t read_size; + + /** + * Current offset in @a read_buf. + */ + size_t read_pos; + +}; + + +/** + * Function called when we can read more data from + * the child process. + * + * @param cls our `struct TALER_JSON_ExternalConversion *` + */ +static void +read_cb (void *cls) +{ + struct TALER_JSON_ExternalConversion *ec = cls; + + ec->read_task = NULL; + while (1) + { + ssize_t ret; + + if (ec->read_size == ec->read_pos) + { + /* Grow input buffer */ + size_t ns; + void *tmp; + + ns = GNUNET_MAX (2 * ec->read_size, + 1024); + if (ns > GNUNET_MAX_MALLOC_CHECKED) + ns = GNUNET_MAX_MALLOC_CHECKED; + if (ec->read_size == ns) + { + /* Helper returned more than 40 MB of data! Stop reading! */ + GNUNET_break (0); + GNUNET_break (GNUNET_OK == + GNUNET_DISK_file_close (ec->chld_stdin)); + return; + } + tmp = GNUNET_malloc_large (ns); + if (NULL == tmp) + { + /* out of memory, also stop reading */ + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "malloc"); + GNUNET_break (GNUNET_OK == + GNUNET_DISK_file_close (ec->chld_stdin)); + return; + } + GNUNET_memcpy (tmp, + ec->read_buf, + ec->read_pos); + GNUNET_free (ec->read_buf); + ec->read_buf = tmp; + ec->read_size = ns; + } + ret = GNUNET_DISK_file_read (ec->chld_stdout, + ec->read_buf + ec->read_pos, + ec->read_size - ec->read_pos); + if (ret < 0) + { + if ( (EAGAIN != errno) && + (EWOULDBLOCK != errno) && + (EINTR != errno) ) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "read"); + return; + } + break; + } + if (0 == ret) + { + /* regular end of stream, good! */ + return; + } + GNUNET_assert (ec->read_size >= ec->read_pos + ret); + ec->read_pos += ret; + } + ec->read_task + = GNUNET_SCHEDULER_add_read_file ( + GNUNET_TIME_UNIT_FOREVER_REL, + ec->chld_stdout, + &read_cb, + ec); +} + + +/** + * Function called when we can write more data to + * the child process. + * + * @param cls our `struct TALER_JSON_ExternalConversion *` + */ +static void +write_cb (void *cls) +{ + struct TALER_JSON_ExternalConversion *ec = cls; + ssize_t ret; + + ec->write_task = NULL; + while (ec->write_size > ec->write_pos) + { + ret = GNUNET_DISK_file_write (ec->chld_stdin, + ec->write_buf + ec->write_pos, + ec->write_size - ec->write_pos); + if (ret < 0) + { + if ( (EAGAIN != errno) && + (EINTR != errno) ) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "write"); + break; + } + if (0 == ret) + { + GNUNET_break (0); + break; + } + GNUNET_assert (ec->write_size >= ec->write_pos + ret); + ec->write_pos += ret; + } + if ( (ec->write_size > ec->write_pos) && + ( (EAGAIN == errno) || + (EWOULDBLOCK == errno) || + (EINTR == errno) ) ) + { + ec->write_task + = GNUNET_SCHEDULER_add_write_file ( + GNUNET_TIME_UNIT_FOREVER_REL, + ec->chld_stdin, + &write_cb, + ec); + } + else + { + GNUNET_break (GNUNET_OK == + GNUNET_DISK_file_close (ec->chld_stdin)); + ec->chld_stdin = NULL; + } +} + + +/** + * Defines a GNUNET_ChildCompletedCallback which is sent back + * upon death or completion of a child process. + * + * @param cls handle for the callback + * @param type type of the process + * @param exit_code status code of the process + * + */ +static void +child_done_cb (void *cls, + enum GNUNET_OS_ProcessStatusType type, + long unsigned int exit_code) +{ + struct TALER_JSON_ExternalConversion *ec = cls; + json_t *j = NULL; + json_error_t err; + + ec->cwh = NULL; + if (NULL != ec->read_task) + { + GNUNET_SCHEDULER_cancel (ec->read_task); + /* We could get the process termination notification before having drained + the read buffer. So drain it now, just in case. */ + read_cb (ec); + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Conversion helper exited with status %d and code %llu after outputting %llu bytes of data\n", + (int) type, + (unsigned long long) exit_code, + (unsigned long long) ec->read_pos); + GNUNET_OS_process_destroy (ec->helper); + ec->helper = NULL; + if (0 != ec->read_pos) + { + j = json_loadb (ec->read_buf, + ec->read_pos, + JSON_REJECT_DUPLICATES, + &err); + if (NULL == j) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to parse JSON from helper at %d: %s\n", + err.position, + err.text); + } + } + ec->cb (ec->cb_cls, + type, + exit_code, + j); + json_decref (j); + TALER_JSON_external_conversion_stop (ec); +} + + +struct TALER_JSON_ExternalConversion * +TALER_JSON_external_conversion_start (const json_t *input, + TALER_JSON_JsonCallback cb, + void *cb_cls, + const char *binary, + ...) +{ + struct TALER_JSON_ExternalConversion *ec; + struct GNUNET_DISK_PipeHandle *pipe_stdin; + struct GNUNET_DISK_PipeHandle *pipe_stdout; + va_list ap; + + ec = GNUNET_new (struct TALER_JSON_ExternalConversion); + ec->cb = cb; + ec->cb_cls = cb_cls; + pipe_stdin = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ); + GNUNET_assert (NULL != pipe_stdin); + pipe_stdout = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_WRITE); + GNUNET_assert (NULL != pipe_stdout); + va_start (ap, + binary); + ec->helper = GNUNET_OS_start_process_va (GNUNET_OS_INHERIT_STD_ERR, + pipe_stdin, + pipe_stdout, + NULL, + binary, + ap); + va_end (ap); + if (NULL == ec->helper) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to run conversion helper `%s'\n", + binary); + GNUNET_break (GNUNET_OK == + GNUNET_DISK_pipe_close (pipe_stdin)); + GNUNET_break (GNUNET_OK == + GNUNET_DISK_pipe_close (pipe_stdout)); + GNUNET_free (ec); + return NULL; + } + ec->chld_stdin = + GNUNET_DISK_pipe_detach_end (pipe_stdin, + GNUNET_DISK_PIPE_END_WRITE); + ec->chld_stdout = + GNUNET_DISK_pipe_detach_end (pipe_stdout, + GNUNET_DISK_PIPE_END_READ); + GNUNET_break (GNUNET_OK == + GNUNET_DISK_pipe_close (pipe_stdin)); + GNUNET_break (GNUNET_OK == + GNUNET_DISK_pipe_close (pipe_stdout)); + ec->write_buf = json_dumps (input, JSON_COMPACT); + ec->write_size = strlen (ec->write_buf); + ec->read_task + = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, + ec->chld_stdout, + &read_cb, + ec); + ec->write_task + = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL, + ec->chld_stdin, + &write_cb, + ec); + ec->cwh = GNUNET_wait_child (ec->helper, + &child_done_cb, + ec); + return ec; +} + + +void +TALER_JSON_external_conversion_stop ( + struct TALER_JSON_ExternalConversion *ec) +{ + if (NULL != ec->cwh) + { + GNUNET_wait_child_cancel (ec->cwh); + ec->cwh = NULL; + } + if (NULL != ec->helper) + { + GNUNET_break (0 == + GNUNET_OS_process_kill (ec->helper, + SIGKILL)); + GNUNET_OS_process_destroy (ec->helper); + } + if (NULL != ec->read_task) + { + GNUNET_SCHEDULER_cancel (ec->read_task); + ec->read_task = NULL; + } + if (NULL != ec->write_task) + { + GNUNET_SCHEDULER_cancel (ec->write_task); + ec->write_task = NULL; + } + if (NULL != ec->chld_stdin) + { + GNUNET_break (GNUNET_OK == + GNUNET_DISK_file_close (ec->chld_stdin)); + ec->chld_stdin = NULL; + } + if (NULL != ec->chld_stdout) + { + GNUNET_break (GNUNET_OK == + GNUNET_DISK_file_close (ec->chld_stdout)); + ec->chld_stdout = NULL; + } + GNUNET_free (ec->read_buf); + free (ec->write_buf); + GNUNET_free (ec); +} diff --git a/src/util/crypto.c b/src/util/crypto.c index 5cbba8135..4735af3b0 100644 --- a/src/util/crypto.c +++ b/src/util/crypto.c @@ -27,11 +27,6 @@ #include <gcrypt.h> /** - * Used in TALER_AgeCommitmentHash_isNullOrZero for comparison - */ -const struct TALER_AgeCommitmentHash TALER_ZeroAgeCommitmentHash = {0}; - -/** * Function called by libgcrypt on serious errors. * Prints an error message and aborts the process. * @@ -90,7 +85,9 @@ TALER_test_coin_valid (const struct TALER_CoinPublicInfo *coin_public_info, #endif TALER_coin_pub_hash (&coin_public_info->coin_pub, - &coin_public_info->h_age_commitment, + coin_public_info->no_age_commitment + ? NULL + : &coin_public_info->h_age_commitment, &c_hash); if (GNUNET_OK != @@ -217,7 +214,7 @@ TALER_planchet_secret_to_transfer_priv ( void TALER_cs_withdraw_nonce_derive ( const struct TALER_PlanchetMasterSecretP *ps, - struct TALER_CsNonce *nonce) + struct GNUNET_CRYPTO_CsSessionNonce *nonce) { GNUNET_assert (GNUNET_YES == GNUNET_CRYPTO_kdf (nonce, @@ -235,7 +232,7 @@ void TALER_cs_refresh_nonce_derive ( const struct TALER_RefreshMasterSecretP *rms, uint32_t coin_num_salt, - struct TALER_CsNonce *nonce) + struct GNUNET_CRYPTO_CsSessionNonce *nonce) { uint32_t be_salt = htonl (coin_num_salt); @@ -253,10 +250,31 @@ TALER_cs_refresh_nonce_derive ( } +void +TALER_rsa_pub_hash (const struct GNUNET_CRYPTO_RsaPublicKey *rsa, + struct TALER_RsaPubHashP *h_rsa) +{ + GNUNET_CRYPTO_rsa_public_key_hash (rsa, + &h_rsa->hash); + +} + + +void +TALER_cs_pub_hash (const struct GNUNET_CRYPTO_CsPublicKey *cs, + struct TALER_CsPubHashP *h_cs) +{ + GNUNET_CRYPTO_hash (cs, + sizeof(*cs), + &h_cs->hash); +} + + enum GNUNET_GenericReturnValue TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk, const struct TALER_ExchangeWithdrawValues *alg_values, - const union TALER_DenominationBlindingKeyP *bks, + const union GNUNET_CRYPTO_BlindingSecretP *bks, + const union GNUNET_CRYPTO_BlindSessionNonce *nonce, const struct TALER_CoinSpendPrivateKeyP *coin_priv, const struct TALER_AgeCommitmentHash *ach, struct TALER_CoinPubHashP *c_hash, @@ -265,12 +283,14 @@ TALER_planchet_prepare (const struct TALER_DenominationPublicKey *dk, { struct TALER_CoinSpendPublicKeyP coin_pub; - GNUNET_assert (alg_values->cipher == dk->cipher); + GNUNET_assert (alg_values->blinding_inputs->cipher == + dk->bsign_pub_key->cipher); GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, &coin_pub.eddsa_pub); if (GNUNET_OK != TALER_denom_blind (dk, bks, + nonce, ach, &coin_pub, alg_values, @@ -297,15 +317,21 @@ enum GNUNET_GenericReturnValue TALER_planchet_to_coin ( const struct TALER_DenominationPublicKey *dk, const struct TALER_BlindedDenominationSignature *blind_sig, - const union TALER_DenominationBlindingKeyP *bks, + const union GNUNET_CRYPTO_BlindingSecretP *bks, const struct TALER_CoinSpendPrivateKeyP *coin_priv, const struct TALER_AgeCommitmentHash *ach, const struct TALER_CoinPubHashP *c_hash, const struct TALER_ExchangeWithdrawValues *alg_values, struct TALER_FreshCoin *coin) { - if ( (dk->cipher != blind_sig->cipher) || - (dk->cipher != alg_values->cipher) ) + if (dk->bsign_pub_key->cipher != + blind_sig->blinded_sig->cipher) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (dk->bsign_pub_key->cipher != + alg_values->blinding_inputs->cipher) { GNUNET_break_op (0); return GNUNET_SYSERR; @@ -426,24 +452,28 @@ TALER_coin_pub_hash (const struct TALER_CoinSpendPublicKeyP *coin_pub, { /* Coin comes with age commitment. Take the hash of the age commitment * into account */ - const size_t key_s = sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey); - const size_t age_s = sizeof(struct TALER_AgeCommitmentHash); - char data[key_s + age_s]; - - GNUNET_memcpy (&data[0], - &coin_pub->eddsa_pub, - key_s); - GNUNET_memcpy (&data[key_s], - ach, - age_s); - GNUNET_CRYPTO_hash (&data, - key_s + age_s, - &coin_h->hash); + struct GNUNET_HashContext *hash_context; + + hash_context = GNUNET_CRYPTO_hash_context_start (); + + GNUNET_CRYPTO_hash_context_read ( + hash_context, + &coin_pub->eddsa_pub, + sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)); + + GNUNET_CRYPTO_hash_context_read ( + hash_context, + ach, + sizeof(struct TALER_AgeCommitmentHash)); + + GNUNET_CRYPTO_hash_context_finish ( + hash_context, + &coin_h->hash); } } -enum GNUNET_GenericReturnValue +void TALER_coin_ev_hash (const struct TALER_BlindedPlanchet *blinded_planchet, const struct TALER_DenominationHashP *denom_hash, struct TALER_BlindedCoinHashP *bch) @@ -458,7 +488,56 @@ TALER_coin_ev_hash (const struct TALER_BlindedPlanchet *blinded_planchet, hash_context); GNUNET_CRYPTO_hash_context_finish (hash_context, &bch->hash); - return GNUNET_OK; +} + + +GNUNET_NETWORK_STRUCT_BEGIN +/** + * Structure we hash to compute the group key for + * a denomination group. + */ +struct DenominationGroupP +{ + /** + * Value of coins in this denomination group. + */ + struct TALER_AmountNBO value; + + /** + * Fee structure for all coins in the group. + */ + struct TALER_DenomFeeSetNBOP fees; + + /** + * Age mask for the denomiation, in NBO. + */ + uint32_t age_mask GNUNET_PACKED; + + /** + * Cipher used for the denomination, in NBO. + */ + uint32_t cipher GNUNET_PACKED; +}; +GNUNET_NETWORK_STRUCT_END + + +void +TALER_denomination_group_get_key ( + const struct TALER_DenominationGroup *dg, + struct GNUNET_HashCode *key) +{ + struct DenominationGroupP dgp = { + .age_mask = htonl (dg->age_mask.bits), + .cipher = htonl (dg->cipher) + }; + + TALER_amount_hton (&dgp.value, + &dg->value); + TALER_denom_fee_set_hton (&dgp.fees, + &dg->fees); + GNUNET_CRYPTO_hash (&dgp, + sizeof (dgp), + key); } diff --git a/src/util/crypto_confirmation.c b/src/util/crypto_confirmation.c new file mode 100644 index 000000000..99552f150 --- /dev/null +++ b/src/util/crypto_confirmation.c @@ -0,0 +1,293 @@ +/* + This file is part of TALER + Copyright (C) 2023 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 util/crypto_confirmation.c + * @brief confirmation computation + * @author Christian Grothoff + * @author Priscilla Huang + */ +#include "platform.h" +#include "taler_util.h" +#include "taler_mhd_lib.h" +#include <gnunet/gnunet_db_lib.h> +#include <gcrypt.h> + +/** + * How long is a TOTP code valid? + */ +#define TOTP_VALIDITY_PERIOD GNUNET_TIME_relative_multiply ( \ + GNUNET_TIME_UNIT_SECONDS, 30) + +/** + * Range of time we allow (plus-minus). + */ +#define TIME_INTERVAL_RANGE 2 + + +/** + * Compute TOTP code at current time with offset + * @a time_off for the @a key. + * + * @param ts current time + * @param time_off offset to apply when computing the code + * @param key pos_key in binary + * @param key_size number of bytes in @a key + */ +static uint64_t +compute_totp (struct GNUNET_TIME_Timestamp ts, + int time_off, + const void *key, + size_t key_size) +{ + struct GNUNET_TIME_Absolute now; + time_t t; + uint64_t ctr; + uint8_t hmac[20]; /* SHA1: 20 bytes */ + + now = ts.abs_time; + while (time_off < 0) + { + now = GNUNET_TIME_absolute_subtract (now, + TOTP_VALIDITY_PERIOD); + time_off++; + } + while (time_off > 0) + { + now = GNUNET_TIME_absolute_add (now, + TOTP_VALIDITY_PERIOD); + time_off--; + } + t = now.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us; + ctr = GNUNET_htonll (t / 30LLU); + + { + gcry_md_hd_t md; + const unsigned char *mc; + + GNUNET_assert (GPG_ERR_NO_ERROR == + gcry_md_open (&md, + GCRY_MD_SHA1, + GCRY_MD_FLAG_HMAC)); + GNUNET_assert (GPG_ERR_NO_ERROR == + gcry_md_setkey (md, + key, + key_size)); + gcry_md_write (md, + &ctr, + sizeof (ctr)); + mc = gcry_md_read (md, + GCRY_MD_SHA1); + GNUNET_assert (NULL != mc); + GNUNET_memcpy (hmac, + mc, + sizeof (hmac)); + gcry_md_close (md); + } + + { + uint32_t code = 0; + int offset; + + offset = hmac[sizeof (hmac) - 1] & 0x0f; + for (int count = 0; count < 4; count++) + code |= ((uint32_t) hmac[offset + 3 - count]) << (8 * count); + code &= 0x7fffffff; + /* always use 8 digits (maximum) */ + code = code % 100000000; + return code; + } +} + + +int +TALER_rfc3548_base32decode (const char *val, + size_t val_size, + void *key, + size_t key_len) +{ + /** + * 32 characters for decoding, using RFC 3548. + */ + static const char *decTable__ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + unsigned char *udata = key; + unsigned int wpos = 0; + unsigned int rpos = 0; + unsigned int bits = 0; + unsigned int vbit = 0; + + while ((rpos < val_size) || (vbit >= 8)) + { + if ((rpos < val_size) && (vbit < 8)) + { + char c = val[rpos++]; + + if (c == '=') + { + /* padding character */ + if (rpos == val_size) + break; /* Ok, 1x '=' padding is allowed */ + if ( ('=' == val[rpos]) && + (rpos + 1 == val_size) ) + break; /* Ok, 2x '=' padding is allowed */ + return -1; /* invalid padding */ + } + const char *p = strchr (decTable__, toupper (c)); + if (! p) + { + /* invalid character */ + return -1; + } + bits = (bits << 5) | (p - decTable__); + vbit += 5; + } + if (vbit >= 8) + { + udata[wpos++] = (bits >> (vbit - 8)) & 0xFF; + vbit -= 8; + } + } + return wpos; +} + + +/** + * @brief Builds POS confirmation to verify payment. + * + * @param h_key opaque key for the totp operation + * @param h_key_len size of h_key in bytes + * @param ts current time + * @return Token on success, NULL of failure + */ +static char * +executive_totp (void *h_key, + size_t h_key_len, + struct GNUNET_TIME_Timestamp ts) +{ + uint64_t code; /* totp code */ + char *ret; + ret = NULL; + + for (int i = -TIME_INTERVAL_RANGE; i<= TIME_INTERVAL_RANGE; i++) + { + code = compute_totp (ts, + i, + h_key, + h_key_len); + if (NULL == ret) + { + GNUNET_asprintf (&ret, + "%08llu", + (unsigned long long) code); + } + else + { + char *tmp; + + GNUNET_asprintf (&tmp, + "%s\n%08llu", + ret, + (unsigned long long) code); + GNUNET_free (ret); + ret = tmp; + } + } + return ret; + +} + + +char * +TALER_build_pos_confirmation (const char *pos_key, + enum TALER_MerchantConfirmationAlgorithm pos_alg, + const struct TALER_Amount *total, + struct GNUNET_TIME_Timestamp ts) +{ + size_t pos_key_length = strlen (pos_key); + void *key; /* pos_key in binary */ + size_t key_len; /* length of the key */ + char *ret; + int dret; + + if (TALER_MCA_NONE == pos_alg) + return NULL; + key_len = pos_key_length * 5 / 8; + key = GNUNET_malloc (key_len); + dret = TALER_rfc3548_base32decode (pos_key, + pos_key_length, + key, + key_len); + if (-1 == dret) + { + GNUNET_free (key); + GNUNET_break_op (0); + return NULL; + } + GNUNET_assert (dret <= key_len); + key_len = (size_t) dret; + switch (pos_alg) + { + case TALER_MCA_NONE: + GNUNET_break (0); + GNUNET_free (key); + return NULL; + case TALER_MCA_WITHOUT_PRICE: /* and 30s */ + /* Return all T-OTP codes in range separated by new lines, e.g. + "12345678 + 24522552 + 25262425 + 42543525 + 25253552" + */ + ret = executive_totp (key, + key_len, + ts); + GNUNET_free (key); + return ret; + case TALER_MCA_WITH_PRICE: + { + struct GNUNET_HashCode hkey; + struct TALER_AmountNBO ntotal; + + if ( (NULL == total) || + (GNUNET_YES != + TALER_amount_is_valid (total) ) ) + { + GNUNET_break_op (0); + return NULL; + } + TALER_amount_hton (&ntotal, + total); + GNUNET_assert (GNUNET_YES == + GNUNET_CRYPTO_kdf (&hkey, + sizeof (hkey), + &ntotal, + sizeof (ntotal), + key, + key_len, + NULL, + 0)); + GNUNET_free (key); + ret = executive_totp (&hkey, + sizeof(hkey), + ts); + GNUNET_free (key); + return ret; + } + } + GNUNET_free (key); + GNUNET_break (0); + return NULL; +} diff --git a/src/util/crypto_contract.c b/src/util/crypto_contract.c index fe6b1e6af..bec34c983 100644 --- a/src/util/crypto_contract.c +++ b/src/util/crypto_contract.c @@ -109,14 +109,14 @@ derive_key (const void *key_material, * @param[out] res_size size of the ciphertext */ static void -contract_encrypt (const struct NonceP *nonce, - const void *key, - size_t key_len, - const void *data, - size_t data_size, - const char *salt, - void **res, - size_t *res_size) +blob_encrypt (const struct NonceP *nonce, + const void *key, + size_t key_len, + const void *data, + size_t data_size, + const char *salt, + void **res, + size_t *res_size) { size_t ciphertext_size; struct SymKeyP skey; @@ -127,10 +127,13 @@ contract_encrypt (const struct NonceP *nonce, salt, &skey); ciphertext_size = crypto_secretbox_NONCEBYTES - + crypto_secretbox_MACBYTES + data_size; + + crypto_secretbox_MACBYTES + + data_size; *res_size = ciphertext_size; *res = GNUNET_malloc (ciphertext_size); - memcpy (*res, nonce, crypto_secretbox_NONCEBYTES); + GNUNET_memcpy (*res, + nonce, + crypto_secretbox_NONCEBYTES); GNUNET_assert (0 == crypto_secretbox_easy (*res + crypto_secretbox_NONCEBYTES, data, @@ -153,13 +156,13 @@ contract_encrypt (const struct NonceP *nonce, * @return #GNUNET_OK on success */ static enum GNUNET_GenericReturnValue -contract_decrypt (const void *key, - size_t key_len, - const void *data, - size_t data_size, - const char *salt, - void **res, - size_t *res_size) +blob_decrypt (const void *key, + size_t key_len, + const void *data, + size_t data_size, + const char *salt, + void **res, + size_t *res_size) { const struct NonceP *nonce; struct SymKeyP skey; @@ -271,21 +274,21 @@ TALER_CRYPTO_contract_encrypt_for_merge ( hdr->header.ctype = htonl (TALER_EXCHANGE_CONTRACT_PAYMENT_OFFER); hdr->header.clen = htonl ((uint32_t) clen); hdr->merge_priv = *merge_priv; - memcpy (&hdr[1], - xbuf, - cbuf_size); + GNUNET_memcpy (&hdr[1], + xbuf, + cbuf_size); GNUNET_free (xbuf); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &nonce, sizeof (nonce)); - contract_encrypt (&nonce, - &key, - sizeof (key), - hdr, - sizeof (*hdr) + cbuf_size, - MERGE_SALT, - econtract, - econtract_size); + blob_encrypt (&nonce, + &key, + sizeof (key), + hdr, + sizeof (*hdr) + cbuf_size, + MERGE_SALT, + econtract, + econtract_size); GNUNET_free (hdr); } @@ -316,13 +319,13 @@ TALER_CRYPTO_contract_decrypt_for_merge ( return NULL; } if (GNUNET_OK != - contract_decrypt (&key, - sizeof (key), - econtract, - econtract_size, - MERGE_SALT, - &xhdr, - &hdr_size)) + blob_decrypt (&key, + sizeof (key), + econtract, + econtract_size, + MERGE_SALT, + &xhdr, + &hdr_size)) { GNUNET_break_op (0); return NULL; @@ -407,6 +410,7 @@ TALER_CRYPTO_contract_encrypt_for_deposit ( &key)); cstr = json_dumps (contract_terms, JSON_COMPACT | JSON_SORT_KEYS); + GNUNET_assert (NULL != cstr); clen = strlen (cstr); cbuf_size = compressBound (clen); xbuf = GNUNET_malloc (cbuf_size); @@ -419,30 +423,30 @@ TALER_CRYPTO_contract_encrypt_for_deposit ( hdr = GNUNET_malloc (sizeof (*hdr) + cbuf_size); hdr->ctype = htonl (TALER_EXCHANGE_CONTRACT_PAYMENT_REQUEST); hdr->clen = htonl ((uint32_t) clen); - memcpy (&hdr[1], - xbuf, - cbuf_size); + GNUNET_memcpy (&hdr[1], + xbuf, + cbuf_size); GNUNET_free (xbuf); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &nonce, sizeof (nonce)); - contract_encrypt (&nonce, - &key, - sizeof (key), - hdr, - sizeof (*hdr) + cbuf_size, - DEPOSIT_SALT, - &xecontract, - &xecontract_size); + blob_encrypt (&nonce, + &key, + sizeof (key), + hdr, + sizeof (*hdr) + cbuf_size, + DEPOSIT_SALT, + &xecontract, + &xecontract_size); GNUNET_free (hdr); /* prepend purse_pub */ *econtract = GNUNET_malloc (xecontract_size + sizeof (*purse_pub)); - memcpy (*econtract, - purse_pub, - sizeof (*purse_pub)); - memcpy (sizeof (*purse_pub) + *econtract, - xecontract, - xecontract_size); + GNUNET_memcpy (*econtract, + purse_pub, + sizeof (*purse_pub)); + GNUNET_memcpy (sizeof (*purse_pub) + *econtract, + xecontract, + xecontract_size); *econtract_size = xecontract_size + sizeof (*purse_pub); GNUNET_free (xecontract); } @@ -481,13 +485,13 @@ TALER_CRYPTO_contract_decrypt_for_deposit ( econtract += sizeof (*purse_pub); econtract_size -= sizeof (*purse_pub); if (GNUNET_OK != - contract_decrypt (&key, - sizeof (key), - econtract, - econtract_size, - DEPOSIT_SALT, - &xhdr, - &hdr_size)) + blob_decrypt (&key, + sizeof (key), + econtract, + econtract_size, + DEPOSIT_SALT, + &xhdr, + &hdr_size)) { GNUNET_break_op (0); return NULL; @@ -538,3 +542,120 @@ TALER_CRYPTO_contract_decrypt_for_deposit ( GNUNET_free (cstr); return ret; } + + +/** + * Salt we use when encrypting KYC attributes. + */ +#define ATTRIBUTE_SALT "kyc-attributes" + + +void +TALER_CRYPTO_kyc_attributes_encrypt ( + const struct TALER_AttributeEncryptionKeyP *key, + const json_t *attr, + void **enc_attr, + size_t *enc_attr_size) +{ + uLongf cbuf_size; + char *cstr; + uLongf clen; + void *xbuf; + int ret; + uint32_t belen; + struct NonceP nonce; + + cstr = json_dumps (attr, + JSON_COMPACT | JSON_SORT_KEYS); + GNUNET_assert (NULL != cstr); + clen = strlen (cstr); + GNUNET_assert (clen <= UINT32_MAX); + cbuf_size = compressBound (clen); + xbuf = GNUNET_malloc (cbuf_size + sizeof (uint32_t)); + belen = htonl ((uint32_t) clen); + GNUNET_memcpy (xbuf, + &belen, + sizeof (belen)); + ret = compress (xbuf + 4, + &cbuf_size, + (const Bytef *) cstr, + clen); + GNUNET_assert (Z_OK == ret); + free (cstr); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &nonce, + sizeof (nonce)); + blob_encrypt (&nonce, + key, + sizeof (*key), + xbuf, + cbuf_size + sizeof (uint32_t), + ATTRIBUTE_SALT, + enc_attr, + enc_attr_size); + GNUNET_free (xbuf); +} + + +json_t * +TALER_CRYPTO_kyc_attributes_decrypt ( + const struct TALER_AttributeEncryptionKeyP *key, + const void *enc_attr, + size_t enc_attr_size) +{ + void *xhdr; + size_t hdr_size; + char *cstr; + uLongf clen; + json_error_t json_error; + json_t *ret; + uint32_t belen; + + if (GNUNET_OK != + blob_decrypt (key, + sizeof (*key), + enc_attr, + enc_attr_size, + ATTRIBUTE_SALT, + &xhdr, + &hdr_size)) + { + GNUNET_break_op (0); + return NULL; + } + GNUNET_memcpy (&belen, + xhdr, + sizeof (belen)); + clen = ntohl (belen); + if (clen >= GNUNET_MAX_MALLOC_CHECKED) + { + GNUNET_break_op (0); + GNUNET_free (xhdr); + return NULL; + } + cstr = GNUNET_malloc (clen + 1); + if (Z_OK != + uncompress ((Bytef *) cstr, + &clen, + (const Bytef *) (xhdr + sizeof (uint32_t)), + hdr_size - sizeof (uint32_t))) + { + GNUNET_break_op (0); + GNUNET_free (cstr); + GNUNET_free (xhdr); + return NULL; + } + GNUNET_free (xhdr); + ret = json_loadb ((char *) cstr, + clen, + JSON_DECODE_ANY, + &json_error); + if (NULL == ret) + { + GNUNET_break_op (0); + GNUNET_free (cstr); + return NULL; + } + GNUNET_free (cstr); + return ret; +} diff --git a/src/util/crypto_helper_cs.c b/src/util/crypto_helper_cs.c index e12d5ad61..4c4a56feb 100644 --- a/src/util/crypto_helper_cs.c +++ b/src/util/crypto_helper_cs.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020, 2021 Taler Systems SA + Copyright (C) 2020, 2021, 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 @@ -113,21 +113,27 @@ try_connect (struct TALER_CRYPTO_CsDenominationHelper *dh) struct TALER_CRYPTO_CsDenominationHelper * TALER_CRYPTO_helper_cs_connect ( const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, TALER_CRYPTO_CsDenominationKeyStatusCallback dkc, void *dkc_cls) { struct TALER_CRYPTO_CsDenominationHelper *dh; char *unixpath; + char *secname; + GNUNET_asprintf (&secname, + "%s-secmod-cs", + section); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, - "taler-exchange-secmod-cs", + secname, "UNIXPATH", &unixpath)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-cs", + secname, "UNIXPATH"); + GNUNET_free (secname); return NULL; } /* we use >= here because we want the sun_path to always @@ -135,12 +141,14 @@ TALER_CRYPTO_helper_cs_connect ( if (strlen (unixpath) >= sizeof (dh->sa.sun_path)) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-cs", + secname, "UNIXPATH", "path too long"); GNUNET_free (unixpath); + GNUNET_free (secname); return NULL; } + GNUNET_free (secname); dh = GNUNET_new (struct TALER_CRYPTO_CsDenominationHelper); dh->dkc = dkc; dh->dkc_cls = dkc_cls; @@ -201,13 +209,18 @@ handle_mt_avail (struct TALER_CRYPTO_CsDenominationHelper *dh, } { - struct TALER_DenominationPublicKey denom_pub; + struct GNUNET_CRYPTO_BlindSignPublicKey *bsign_pub; struct TALER_CsPubHashP h_cs; - denom_pub.cipher = TALER_DENOMINATION_CS; - denom_pub.details.cs_public_key = kan->denom_pub; + bsign_pub = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPublicKey); + bsign_pub->cipher = GNUNET_CRYPTO_BSA_CS; + bsign_pub->rc = 1; + bsign_pub->details.cs_public_key = kan->denom_pub; - TALER_cs_pub_hash (&denom_pub.details.cs_public_key, &h_cs); + GNUNET_CRYPTO_hash (&bsign_pub->details.cs_public_key, + sizeof (bsign_pub->details.cs_public_key), + &bsign_pub->pub_key_hash); + h_cs.hash = bsign_pub->pub_key_hash; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received CS key %s (%s)\n", GNUNET_h2s (&h_cs.hash), @@ -222,7 +235,7 @@ handle_mt_avail (struct TALER_CRYPTO_CsDenominationHelper *dh, &kan->secm_sig)) { GNUNET_break_op (0); - TALER_denom_pub_free (&denom_pub); + GNUNET_CRYPTO_blind_sign_pub_decref (bsign_pub); return GNUNET_SYSERR; } dh->dkc (dh->dkc_cls, @@ -230,10 +243,10 @@ handle_mt_avail (struct TALER_CRYPTO_CsDenominationHelper *dh, GNUNET_TIME_timestamp_ntoh (kan->anchor_time), GNUNET_TIME_relative_ntoh (kan->duration_withdraw), &h_cs, - &denom_pub, + bsign_pub, &kan->secm_pub, &kan->secm_sig); - TALER_denom_pub_free (&denom_pub); + GNUNET_CRYPTO_blind_sign_pub_decref (bsign_pub); } return GNUNET_OK; } @@ -378,34 +391,19 @@ more: } -/** - * Request helper @a dh to sign @a msg using the public key corresponding to - * @a h_denom_pub. - * - * This operation will block until the signature has been obtained. Should - * this process receive a signal (that is not ignored) while the operation is - * pending, the operation will fail. Note that the helper may still believe - * that it created the signature. Thus, signals may result in a small - * differences in the signature counters. Retrying in this case may work. - * - * @param dh helper process connection - * @param h_cs hash of the CS public key to use to sign - * @param blinded_planchet blinded planchet containing c and nonce - * @param for_melt true if the HKDF for melt should be used - * @param[out] bs set to the blind signature - * @return #TALER_EC_NONE on success - */ -static enum TALER_ErrorCode -helper_cs_sign ( +enum TALER_ErrorCode +TALER_CRYPTO_helper_cs_sign ( struct TALER_CRYPTO_CsDenominationHelper *dh, - const struct TALER_CsPubHashP *h_cs, - const struct TALER_BlindedCsPlanchet *blinded_planchet, + const struct TALER_CRYPTO_CsSignRequest *req, bool for_melt, struct TALER_BlindedDenominationSignature *bs) { enum TALER_ErrorCode ec = TALER_EC_INVALID; + const struct TALER_CsPubHashP *h_cs = req->h_cs; - bs->cipher = TALER_DENOMINATION_INVALID; + memset (bs, + 0, + sizeof (*bs)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting signature process\n"); if (GNUNET_OK != @@ -419,15 +417,15 @@ helper_cs_sign ( GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Requesting signature\n"); { - char buf[sizeof (struct TALER_CRYPTO_CsSignRequest)]; - struct TALER_CRYPTO_CsSignRequest *sr - = (struct TALER_CRYPTO_CsSignRequest *) buf; + char buf[sizeof (struct TALER_CRYPTO_CsSignRequestMessage)]; + struct TALER_CRYPTO_CsSignRequestMessage *sr + = (struct TALER_CRYPTO_CsSignRequestMessage *) buf; sr->header.size = htons (sizeof (buf)); sr->header.type = htons (TALER_HELPER_CS_MT_REQ_SIGN); sr->for_melt = htonl (for_melt ? 1 : 0); sr->h_cs = *h_cs; - sr->planchet = *blinded_planchet; + sr->message = *req->blinded_planchet; if (GNUNET_OK != TALER_crypto_helper_send_all (dh->sock, buf, @@ -493,7 +491,7 @@ more: switch (ntohs (hdr->type)) { case TALER_HELPER_CS_MT_RES_SIGNATURE: - if (msize < sizeof (struct TALER_CRYPTO_SignResponse)) + if (msize != sizeof (struct TALER_CRYPTO_SignResponse)) { GNUNET_break_op (0); do_disconnect (dh); @@ -510,13 +508,18 @@ more: { const struct TALER_CRYPTO_SignResponse *sr = (const struct TALER_CRYPTO_SignResponse *) buf; + struct GNUNET_CRYPTO_BlindedSignature *blinded_sig; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received signature\n"); ec = TALER_EC_NONE; finished = true; - bs->cipher = TALER_DENOMINATION_CS; - bs->details.blinded_cs_answer = sr->cs_answer; + blinded_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); + blinded_sig->cipher = GNUNET_CRYPTO_BSA_CS; + blinded_sig->rc = 1; + blinded_sig->details.blinded_cs_answer.b = ntohl (sr->b); + blinded_sig->details.blinded_cs_answer.s_scalar = sr->cs_answer; + bs->blinded_sig = blinded_sig; break; } case TALER_HELPER_CS_MT_RES_SIGN_FAILURE: @@ -533,7 +536,8 @@ more: ec = (enum TALER_ErrorCode) ntohl (sf->ec); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Signing failed!\n"); + "Signing failed with status %d!\n", + ec); finished = true; break; } @@ -591,36 +595,6 @@ end: } -enum TALER_ErrorCode -TALER_CRYPTO_helper_cs_sign_melt ( - struct TALER_CRYPTO_CsDenominationHelper *dh, - const struct TALER_CsPubHashP *h_cs, - const struct TALER_BlindedCsPlanchet *blinded_planchet, - struct TALER_BlindedDenominationSignature *bs) -{ - return helper_cs_sign (dh, - h_cs, - blinded_planchet, - true, - bs); -} - - -enum TALER_ErrorCode -TALER_CRYPTO_helper_cs_sign_withdraw ( - struct TALER_CRYPTO_CsDenominationHelper *dh, - const struct TALER_CsPubHashP *h_cs, - const struct TALER_BlindedCsPlanchet *blinded_planchet, - struct TALER_BlindedDenominationSignature *bs) -{ - return helper_cs_sign (dh, - h_cs, - blinded_planchet, - false, - bs); -} - - void TALER_CRYPTO_helper_cs_revoke ( struct TALER_CRYPTO_CsDenominationHelper *dh, @@ -651,31 +625,15 @@ TALER_CRYPTO_helper_cs_revoke ( } -/** - * Ask the helper to derive R using the @a nonce and denomination key - * associated with @a h_cs. - * - * This operation will block until the R has been obtained. Should - * this process receive a signal (that is not ignored) while the operation is - * pending, the operation will fail. Note that the helper may still believe - * that it created the signature. Thus, signals may result in a small - * differences in the signature counters. Retrying in this case may work. - * - * @param dh helper to process connection - * @param h_cs hash of the CS public key to revoke - * @param nonce witdhraw nonce - * @param for_melt true if the HKDF for melt should be used - * @param[out] crp set to the pair of R values - * @return set to the error code (or #TALER_EC_NONE on success) - */ -static enum TALER_ErrorCode -helper_cs_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh, - const struct TALER_CsPubHashP *h_cs, - const struct TALER_CsNonce *nonce, - bool for_melt, - struct TALER_DenominationCSPublicRPairP *crp) +enum TALER_ErrorCode +TALER_CRYPTO_helper_cs_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh, + const struct TALER_CRYPTO_CsDeriveRequest *cdr, + bool for_melt, + struct GNUNET_CRYPTO_CSPublicRPairP *crp) { enum TALER_ErrorCode ec = TALER_EC_INVALID; + const struct TALER_CsPubHashP *h_cs = cdr->h_cs; + const struct GNUNET_CRYPTO_CsSessionNonce *nonce = cdr->nonce; memset (crp, 0, @@ -853,32 +811,495 @@ more: enum TALER_ErrorCode -TALER_CRYPTO_helper_cs_r_derive_withdraw ( +TALER_CRYPTO_helper_cs_batch_sign ( struct TALER_CRYPTO_CsDenominationHelper *dh, - const struct TALER_CsPubHashP *h_cs, - const struct TALER_CsNonce *nonce, - struct TALER_DenominationCSPublicRPairP *crp) + unsigned int reqs_length, + const struct TALER_CRYPTO_CsSignRequest reqs[static reqs_length], + bool for_melt, + struct TALER_BlindedDenominationSignature bss[static reqs_length]) { - return helper_cs_r_derive (dh, - h_cs, - nonce, - false, - crp); + enum TALER_ErrorCode ec = TALER_EC_INVALID; + unsigned int rpos; + unsigned int rend; + unsigned int wpos; + + memset (bss, + 0, + sizeof (*bss) * reqs_length); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting signature process\n"); + if (GNUNET_OK != + try_connect (dh)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to connect to helper\n"); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting %u signatures\n", + reqs_length); + rpos = 0; + rend = 0; + wpos = 0; + while (rpos < reqs_length) + { + unsigned int mlen = sizeof (struct TALER_CRYPTO_BatchSignRequest); + + while ( (rend < reqs_length) && + (mlen + sizeof (struct TALER_CRYPTO_CsSignRequestMessage) + < UINT16_MAX) ) + { + mlen += sizeof (struct TALER_CRYPTO_CsSignRequestMessage); + rend++; + } + { + char obuf[mlen] GNUNET_ALIGN; + struct TALER_CRYPTO_BatchSignRequest *bsr + = (struct TALER_CRYPTO_BatchSignRequest *) obuf; + void *wbuf; + + bsr->header.type = htons (TALER_HELPER_CS_MT_REQ_BATCH_SIGN); + bsr->header.size = htons (mlen); + bsr->batch_size = htonl (rend - rpos); + wbuf = &bsr[1]; + for (unsigned int i = rpos; i<rend; i++) + { + struct TALER_CRYPTO_CsSignRequestMessage *csm = wbuf; + const struct TALER_CRYPTO_CsSignRequest *csr = &reqs[i]; + + csm->header.size = htons (sizeof (*csm)); + csm->header.type = htons (TALER_HELPER_CS_MT_REQ_SIGN); + csm->for_melt = htonl (for_melt ? 1 : 0); + csm->h_cs = *csr->h_cs; + csm->message = *csr->blinded_planchet; + wbuf += sizeof (*csm); + } + GNUNET_assert (wbuf == &obuf[mlen]); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending batch request [%u-%u)\n", + rpos, + rend); + if (GNUNET_OK != + TALER_crypto_helper_send_all (dh->sock, + obuf, + sizeof (obuf))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "send"); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + } /* end of obuf scope */ + rpos = rend; + { + char buf[UINT16_MAX]; + size_t off = 0; + const struct GNUNET_MessageHeader *hdr + = (const struct GNUNET_MessageHeader *) buf; + bool finished = false; + + while (1) + { + uint16_t msize; + ssize_t ret; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Awaiting reply at %u (up to %u)\n", + wpos, + rend); + ret = recv (dh->sock, + &buf[off], + sizeof (buf) - off, + (finished && (0 == off)) + ? MSG_DONTWAIT + : 0); + if (ret < 0) + { + if (EINTR == errno) + continue; + if (EAGAIN == errno) + { + GNUNET_assert (finished); + GNUNET_assert (0 == off); + break; + } + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "recv"); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + if (0 == ret) + { + GNUNET_break (0 == off); + if (! finished) + return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; + if (TALER_EC_NONE == ec) + break; + return ec; + } + off += ret; +more: + if (off < sizeof (struct GNUNET_MessageHeader)) + continue; + msize = ntohs (hdr->size); + if (off < msize) + continue; + switch (ntohs (hdr->type)) + { + case TALER_HELPER_CS_MT_RES_SIGNATURE: + if (msize != sizeof (struct TALER_CRYPTO_SignResponse)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + if (finished) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + { + const struct TALER_CRYPTO_SignResponse *sr = + (const struct TALER_CRYPTO_SignResponse *) buf; + struct GNUNET_CRYPTO_BlindedSignature *blinded_sig; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received %u signature\n", + wpos); + blinded_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); + blinded_sig->cipher = GNUNET_CRYPTO_BSA_CS; + blinded_sig->rc = 1; + blinded_sig->details.blinded_cs_answer.b = ntohl (sr->b); + blinded_sig->details.blinded_cs_answer.s_scalar = sr->cs_answer; + + bss[wpos].blinded_sig = blinded_sig; + wpos++; + if (wpos == rend) + { + if (TALER_EC_INVALID == ec) + ec = TALER_EC_NONE; + finished = true; + } + break; + } + + case TALER_HELPER_CS_MT_RES_SIGN_FAILURE: + if (msize != sizeof (struct TALER_CRYPTO_SignFailure)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + { + const struct TALER_CRYPTO_SignFailure *sf = + (const struct TALER_CRYPTO_SignFailure *) buf; + + ec = (enum TALER_ErrorCode) ntohl (sf->ec); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Signing %u failed with status %d!\n", + wpos, + ec); + wpos++; + if (wpos == rend) + { + finished = true; + } + break; + } + case TALER_HELPER_CS_MT_AVAIL: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received new key!\n"); + if (GNUNET_OK != + handle_mt_avail (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_CS_MT_PURGE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received revocation!\n"); + if (GNUNET_OK != + handle_mt_purge (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_CS_SYNCED: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Synchronized add odd time with CS helper!\n"); + dh->synced = true; + break; + default: + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received unexpected message of type %u\n", + ntohs (hdr->type)); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + memmove (buf, + &buf[msize], + off - msize); + off -= msize; + goto more; + } /* while(1) */ + } /* scope */ + } /* while (rpos < cdrs_length) */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Existing with %u signatures and status %d\n", + wpos, + ec); + return ec; } enum TALER_ErrorCode -TALER_CRYPTO_helper_cs_r_derive_melt ( +TALER_CRYPTO_helper_cs_r_batch_derive ( struct TALER_CRYPTO_CsDenominationHelper *dh, - const struct TALER_CsPubHashP *h_cs, - const struct TALER_CsNonce *nonce, - struct TALER_DenominationCSPublicRPairP *crp) + unsigned int cdrs_length, + const struct TALER_CRYPTO_CsDeriveRequest cdrs[static cdrs_length], + bool for_melt, + struct GNUNET_CRYPTO_CSPublicRPairP crps[static cdrs_length]) { - return helper_cs_r_derive (dh, - h_cs, - nonce, - true, - crp); + enum TALER_ErrorCode ec = TALER_EC_INVALID; + unsigned int rpos; + unsigned int rend; + unsigned int wpos; + + memset (crps, + 0, + sizeof (*crps) * cdrs_length); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting R derivation process\n"); + if (GNUNET_OK != + try_connect (dh)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to connect to helper\n"); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting %u R pairs\n", + cdrs_length); + rpos = 0; + rend = 0; + wpos = 0; + while (rpos < cdrs_length) + { + unsigned int mlen = sizeof (struct TALER_CRYPTO_BatchDeriveRequest); + + while ( (rend < cdrs_length) && + (mlen + sizeof (struct TALER_CRYPTO_CsRDeriveRequest) + < UINT16_MAX) ) + { + mlen += sizeof (struct TALER_CRYPTO_CsRDeriveRequest); + rend++; + } + { + char obuf[mlen] GNUNET_ALIGN; + struct TALER_CRYPTO_BatchDeriveRequest *bdr + = (struct TALER_CRYPTO_BatchDeriveRequest *) obuf; + void *wbuf; + + bdr->header.type = htons (TALER_HELPER_CS_MT_REQ_BATCH_RDERIVE); + bdr->header.size = htons (mlen); + bdr->batch_size = htonl (rend - rpos); + wbuf = &bdr[1]; + for (unsigned int i = rpos; i<rend; i++) + { + struct TALER_CRYPTO_CsRDeriveRequest *rdr = wbuf; + const struct TALER_CRYPTO_CsDeriveRequest *cdr = &cdrs[i]; + + rdr->header.size = htons (sizeof (*rdr)); + rdr->header.type = htons (TALER_HELPER_CS_MT_REQ_RDERIVE); + rdr->for_melt = htonl (for_melt ? 1 : 0); + rdr->h_cs = *cdr->h_cs; + rdr->nonce = *cdr->nonce; + wbuf += sizeof (*rdr); + } + GNUNET_assert (wbuf == &obuf[mlen]); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending batch request [%u-%u)\n", + rpos, + rend); + if (GNUNET_OK != + TALER_crypto_helper_send_all (dh->sock, + obuf, + sizeof (obuf))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "send"); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + } /* end of obuf scope */ + rpos = rend; + { + char buf[UINT16_MAX]; + size_t off = 0; + const struct GNUNET_MessageHeader *hdr + = (const struct GNUNET_MessageHeader *) buf; + bool finished = false; + + while (1) + { + uint16_t msize; + ssize_t ret; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Awaiting reply at %u (up to %u)\n", + wpos, + rend); + ret = recv (dh->sock, + &buf[off], + sizeof (buf) - off, + (finished && (0 == off)) + ? MSG_DONTWAIT + : 0); + if (ret < 0) + { + if (EINTR == errno) + continue; + if (EAGAIN == errno) + { + GNUNET_assert (finished); + GNUNET_assert (0 == off); + break; + } + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "recv"); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + if (0 == ret) + { + GNUNET_break (0 == off); + if (! finished) + return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; + if (TALER_EC_NONE == ec) + break; + return ec; + } + off += ret; +more: + if (off < sizeof (struct GNUNET_MessageHeader)) + continue; + msize = ntohs (hdr->size); + if (off < msize) + continue; + switch (ntohs (hdr->type)) + { + case TALER_HELPER_CS_MT_RES_RDERIVE: + if (msize != sizeof (struct TALER_CRYPTO_RDeriveResponse)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + if (finished) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + { + const struct TALER_CRYPTO_RDeriveResponse *rdr = + (const struct TALER_CRYPTO_RDeriveResponse *) buf; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received %u R pair\n", + wpos); + crps[wpos] = rdr->r_pub; + wpos++; + if (wpos == rend) + { + if (TALER_EC_INVALID == ec) + ec = TALER_EC_NONE; + finished = true; + } + break; + } + case TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE: + if (msize != sizeof (struct TALER_CRYPTO_RDeriveFailure)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + { + const struct TALER_CRYPTO_RDeriveFailure *rdf = + (const struct TALER_CRYPTO_RDeriveFailure *) buf; + + ec = (enum TALER_ErrorCode) ntohl (rdf->ec); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "R derivation %u failed with status %d!\n", + wpos, + ec); + wpos++; + if (wpos == rend) + { + finished = true; + } + break; + } + case TALER_HELPER_CS_MT_AVAIL: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received new key!\n"); + if (GNUNET_OK != + handle_mt_avail (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_CS_MT_PURGE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received revocation!\n"); + if (GNUNET_OK != + handle_mt_purge (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_CS_SYNCED: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Synchronized add odd time with CS helper!\n"); + dh->synced = true; + break; + default: + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received unexpected message of type %u\n", + ntohs (hdr->type)); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + memmove (buf, + &buf[msize], + off - msize); + off -= msize; + goto more; + } /* while(1) */ + } /* scope */ + } /* while (rpos < cdrs_length) */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Existing with %u signatures and status %d\n", + wpos, + ec); + return ec; } diff --git a/src/util/crypto_helper_esign.c b/src/util/crypto_helper_esign.c index 5a9ad74e2..e044d31d1 100644 --- a/src/util/crypto_helper_esign.c +++ b/src/util/crypto_helper_esign.c @@ -111,21 +111,28 @@ try_connect (struct TALER_CRYPTO_ExchangeSignHelper *esh) struct TALER_CRYPTO_ExchangeSignHelper * TALER_CRYPTO_helper_esign_connect ( const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, TALER_CRYPTO_ExchangeKeyStatusCallback ekc, void *ekc_cls) { struct TALER_CRYPTO_ExchangeSignHelper *esh; char *unixpath; + char *secname; + + GNUNET_asprintf (&secname, + "%s-secmod-eddsa", + section); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, - "taler-exchange-secmod-eddsa", + secname, "UNIXPATH", &unixpath)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-eddsa", + secname, "UNIXPATH"); + GNUNET_free (secname); return NULL; } /* we use >= here because we want the sun_path to always @@ -133,12 +140,14 @@ TALER_CRYPTO_helper_esign_connect ( if (strlen (unixpath) >= sizeof (esh->sa.sun_path)) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-eddsa", + secname, "UNIXPATH", "path too long"); GNUNET_free (unixpath); + GNUNET_free (secname); return NULL; } + GNUNET_free (secname); esh = GNUNET_new (struct TALER_CRYPTO_ExchangeSignHelper); esh->ekc = ekc; esh->ekc_cls = ekc_cls; @@ -357,9 +366,9 @@ TALER_CRYPTO_helper_esign_sign_ ( sr->header.size = htons (sizeof (buf)); sr->header.type = htons (TALER_HELPER_EDDSA_MT_REQ_SIGN); sr->reserved = htonl (0); - memcpy (&sr->purpose, - purpose, - purpose_size); + GNUNET_memcpy (&sr->purpose, + purpose, + purpose_size); if (GNUNET_OK != TALER_crypto_helper_send_all (esh->sock, buf, diff --git a/src/util/crypto_helper_rsa.c b/src/util/crypto_helper_rsa.c index 92b79c951..e23e12a88 100644 --- a/src/util/crypto_helper_rsa.c +++ b/src/util/crypto_helper_rsa.c @@ -113,21 +113,28 @@ try_connect (struct TALER_CRYPTO_RsaDenominationHelper *dh) struct TALER_CRYPTO_RsaDenominationHelper * TALER_CRYPTO_helper_rsa_connect ( const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, TALER_CRYPTO_RsaDenominationKeyStatusCallback dkc, void *dkc_cls) { struct TALER_CRYPTO_RsaDenominationHelper *dh; char *unixpath; + char *secname; + + GNUNET_asprintf (&secname, + "%s-secmod-rsa", + section); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, - "taler-exchange-secmod-rsa", + secname, "UNIXPATH", &unixpath)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", + secname, "UNIXPATH"); + GNUNET_free (secname); return NULL; } /* we use >= here because we want the sun_path to always @@ -135,12 +142,14 @@ TALER_CRYPTO_helper_rsa_connect ( if (strlen (unixpath) >= sizeof (dh->sa.sun_path)) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", + secname, "UNIXPATH", "path too long"); GNUNET_free (unixpath); + GNUNET_free (secname); return NULL; } + GNUNET_free (secname); dh = GNUNET_new (struct TALER_CRYPTO_RsaDenominationHelper); dh->dkc = dkc; dh->dkc_cls = dkc_cls; @@ -203,23 +212,27 @@ handle_mt_avail (struct TALER_CRYPTO_RsaDenominationHelper *dh, } { - struct TALER_DenominationPublicKey denom_pub; + struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub; struct TALER_RsaPubHashP h_rsa; - denom_pub.cipher = TALER_DENOMINATION_RSA; - denom_pub.details.rsa_public_key + bs_pub = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPublicKey); + bs_pub->cipher = GNUNET_CRYPTO_BSA_RSA; + bs_pub->details.rsa_public_key = GNUNET_CRYPTO_rsa_public_key_decode (buf, ntohs (kan->pub_size)); - if (NULL == denom_pub.details.rsa_public_key) + if (NULL == bs_pub->details.rsa_public_key) { GNUNET_break_op (0); + GNUNET_free (bs_pub); return GNUNET_SYSERR; } - GNUNET_CRYPTO_rsa_public_key_hash (denom_pub.details.rsa_public_key, - &h_rsa.hash); + bs_pub->rc = 1; + GNUNET_CRYPTO_rsa_public_key_hash (bs_pub->details.rsa_public_key, + &bs_pub->pub_key_hash); + h_rsa.hash = bs_pub->pub_key_hash; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received RSA key %s (%s)\n", - GNUNET_h2s (&h_rsa.hash), + GNUNET_h2s (&bs_pub->pub_key_hash), section_name); if (GNUNET_OK != TALER_exchange_secmod_rsa_verify ( @@ -231,7 +244,7 @@ handle_mt_avail (struct TALER_CRYPTO_RsaDenominationHelper *dh, &kan->secm_sig)) { GNUNET_break_op (0); - TALER_denom_pub_free (&denom_pub); + GNUNET_CRYPTO_blind_sign_pub_decref (bs_pub); return GNUNET_SYSERR; } dh->dkc (dh->dkc_cls, @@ -239,10 +252,10 @@ handle_mt_avail (struct TALER_CRYPTO_RsaDenominationHelper *dh, GNUNET_TIME_timestamp_ntoh (kan->anchor_time), GNUNET_TIME_relative_ntoh (kan->duration_withdraw), &h_rsa, - &denom_pub, + bs_pub, &kan->secm_pub, &kan->secm_sig); - TALER_denom_pub_free (&denom_pub); + GNUNET_CRYPTO_blind_sign_pub_decref (bs_pub); } return GNUNET_OK; } @@ -395,7 +408,9 @@ TALER_CRYPTO_helper_rsa_sign ( { enum TALER_ErrorCode ec = TALER_EC_INVALID; - bs->cipher = TALER_DENOMINATION_INVALID; + memset (bs, + 0, + sizeof (*bs)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting signature process\n"); if (GNUNET_OK != @@ -417,9 +432,9 @@ TALER_CRYPTO_helper_rsa_sign ( sr->header.type = htons (TALER_HELPER_RSA_MT_REQ_SIGN); sr->reserved = htonl (0); sr->h_rsa = *rsr->h_rsa; - memcpy (&sr[1], - rsr->msg, - rsr->msg_size); + GNUNET_memcpy (&sr[1], + rsr->msg, + rsr->msg_size); if (GNUNET_OK != TALER_crypto_helper_send_all (dh->sock, buf, @@ -503,6 +518,7 @@ more: const struct TALER_CRYPTO_SignResponse *sr = (const struct TALER_CRYPTO_SignResponse *) buf; struct GNUNET_CRYPTO_RsaSignature *rsa_signature; + struct GNUNET_CRYPTO_BlindedSignature *blind_sig; rsa_signature = GNUNET_CRYPTO_rsa_signature_decode ( &sr[1], @@ -518,8 +534,11 @@ more: "Received signature\n"); ec = TALER_EC_NONE; finished = true; - bs->cipher = TALER_DENOMINATION_RSA; - bs->details.blinded_rsa_signature = rsa_signature; + blind_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); + blind_sig->cipher = GNUNET_CRYPTO_BSA_RSA; + blind_sig->rc = 1; + blind_sig->details.blinded_rsa_signature = rsa_signature; + bs->blinded_sig = blind_sig; break; } case TALER_HELPER_RSA_MT_RES_SIGN_FAILURE: @@ -597,12 +616,260 @@ end: enum TALER_ErrorCode TALER_CRYPTO_helper_rsa_batch_sign ( struct TALER_CRYPTO_RsaDenominationHelper *dh, - const struct TALER_CRYPTO_RsaSignRequest *rsrs, unsigned int rsrs_length, - struct TALER_BlindedDenominationSignature *bss) + const struct TALER_CRYPTO_RsaSignRequest rsrs[static rsrs_length], + struct TALER_BlindedDenominationSignature bss[static rsrs_length]) { - GNUNET_break (0); - return -1; /* FIXME #7272: NOT IMPLEMENTED! */ + enum TALER_ErrorCode ec = TALER_EC_INVALID; + unsigned int rpos; + unsigned int rend; + unsigned int wpos; + + memset (bss, + 0, + sizeof (*bss) * rsrs_length); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting signature process\n"); + if (GNUNET_OK != + try_connect (dh)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to connect to helper\n"); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Requesting %u signatures\n", + rsrs_length); + rpos = 0; + rend = 0; + wpos = 0; + while (rpos < rsrs_length) + { + unsigned int mlen = sizeof (struct TALER_CRYPTO_BatchSignRequest); + + while ( (rend < rsrs_length) && + (mlen + + sizeof (struct TALER_CRYPTO_SignRequest) + + rsrs[rend].msg_size < UINT16_MAX) ) + { + mlen += sizeof (struct TALER_CRYPTO_SignRequest) + rsrs[rend].msg_size; + rend++; + } + { + char obuf[mlen] GNUNET_ALIGN; + struct TALER_CRYPTO_BatchSignRequest *bsr + = (struct TALER_CRYPTO_BatchSignRequest *) obuf; + void *wbuf; + + bsr->header.type = htons (TALER_HELPER_RSA_MT_REQ_BATCH_SIGN); + bsr->header.size = htons (mlen); + bsr->batch_size = htonl (rend - rpos); + wbuf = &bsr[1]; + for (unsigned int i = rpos; i<rend; i++) + { + struct TALER_CRYPTO_SignRequest *sr = wbuf; + const struct TALER_CRYPTO_RsaSignRequest *rsr = &rsrs[i]; + + sr->header.type = htons (TALER_HELPER_RSA_MT_REQ_SIGN); + sr->header.size = htons (sizeof (*sr) + rsr->msg_size); + sr->reserved = htonl (0); + sr->h_rsa = *rsr->h_rsa; + GNUNET_memcpy (&sr[1], + rsr->msg, + rsr->msg_size); + wbuf += sizeof (*sr) + rsr->msg_size; + } + GNUNET_assert (wbuf == &obuf[mlen]); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending batch request [%u-%u)\n", + rpos, + rend); + if (GNUNET_OK != + TALER_crypto_helper_send_all (dh->sock, + obuf, + sizeof (obuf))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "send"); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + } + } + rpos = rend; + { + char buf[UINT16_MAX]; + size_t off = 0; + const struct GNUNET_MessageHeader *hdr + = (const struct GNUNET_MessageHeader *) buf; + bool finished = false; + + while (1) + { + uint16_t msize; + ssize_t ret; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Awaiting reply at %u (up to %u)\n", + wpos, + rend); + ret = recv (dh->sock, + &buf[off], + sizeof (buf) - off, + (finished && (0 == off)) + ? MSG_DONTWAIT + : 0); + if (ret < 0) + { + if (EINTR == errno) + continue; + if (EAGAIN == errno) + { + GNUNET_assert (finished); + GNUNET_assert (0 == off); + break; + } + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "recv"); + do_disconnect (dh); + ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE; + break; + } + if (0 == ret) + { + GNUNET_break (0 == off); + if (! finished) + ec = TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; + if (TALER_EC_NONE == ec) + break; + return ec; + } + off += ret; +more: + if (off < sizeof (struct GNUNET_MessageHeader)) + continue; + msize = ntohs (hdr->size); + if (off < msize) + continue; + switch (ntohs (hdr->type)) + { + case TALER_HELPER_RSA_MT_RES_SIGNATURE: + if (msize < sizeof (struct TALER_CRYPTO_SignResponse)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + if (finished) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + { + const struct TALER_CRYPTO_SignResponse *sr = + (const struct TALER_CRYPTO_SignResponse *) buf; + struct GNUNET_CRYPTO_RsaSignature *rsa_signature; + struct GNUNET_CRYPTO_BlindedSignature *blind_sig; + + rsa_signature = GNUNET_CRYPTO_rsa_signature_decode ( + &sr[1], + msize - sizeof (*sr)); + if (NULL == rsa_signature) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received %u signature\n", + wpos); + blind_sig = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); + blind_sig->cipher = GNUNET_CRYPTO_BSA_RSA; + blind_sig->rc = 1; + blind_sig->details.blinded_rsa_signature = rsa_signature; + bss[wpos].blinded_sig = blind_sig; + wpos++; + if (wpos == rend) + { + if (TALER_EC_INVALID == ec) + ec = TALER_EC_NONE; + finished = true; + } + break; + } + case TALER_HELPER_RSA_MT_RES_SIGN_FAILURE: + if (msize != sizeof (struct TALER_CRYPTO_SignFailure)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + { + const struct TALER_CRYPTO_SignFailure *sf = + (const struct TALER_CRYPTO_SignFailure *) buf; + + ec = (enum TALER_ErrorCode) ntohl (sf->ec); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Signing %u failed with status %d!\n", + wpos, + ec); + wpos++; + if (wpos == rend) + { + finished = true; + } + break; + } + case TALER_HELPER_RSA_MT_AVAIL: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received new key!\n"); + if (GNUNET_OK != + handle_mt_avail (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_RSA_MT_PURGE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received revocation!\n"); + if (GNUNET_OK != + handle_mt_purge (dh, + hdr)) + { + GNUNET_break_op (0); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + break; /* while(1) loop ensures we recvfrom() again */ + case TALER_HELPER_RSA_SYNCED: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Synchronized add odd time with RSA helper!\n"); + dh->synced = true; + break; + default: + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received unexpected message of type %u\n", + ntohs (hdr->type)); + do_disconnect (dh); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG; + } + memmove (buf, + &buf[msize], + off - msize); + off -= msize; + goto more; + } /* while(1) */ + } /* scope */ + } /* while (rpos < rsrs_length) */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Existing with %u signatures and status %d\n", + wpos, + ec); + return ec; } diff --git a/src/util/currencies.conf b/src/util/currencies.conf new file mode 100644 index 000000000..0fa831bf3 --- /dev/null +++ b/src/util/currencies.conf @@ -0,0 +1,89 @@ +[currency-euro] +ENABLED = YES +name = "Euro" +code = "EUR" +fractional_input_digits = 2 +fractional_normal_digits = 2 +fractional_trailing_zero_digits = 2 +alt_unit_names = {"0":"€"} + +[currency-swiss-francs] +ENABLED = YES +name = "Swiss Francs" +code = "CHF" +fractional_input_digits = 2 +fractional_normal_digits = 2 +fractional_trailing_zero_digits = 2 +alt_unit_names = {"0":"Fr.","-2":"Rp."} + +[currency-forint] +ENABLED = NO +name = "Hungarian Forint" +code = "HUF" +fractional_input_digits = 0 +fractional_normal_digits = 0 +fractional_trailing_zero_digits = 0 +alt_unit_names = {"0":"Ft"} + +[currency-us-dollar] +ENABLED = NO +name = "US Dollar" +code = "USD" +fractional_input_digits = 2 +fractional_normal_digits = 2 +fractional_trailing_zero_digits = 2 +alt_unit_names = {"0":"$"} + +[currency-kudos] +ENABLED = YES +name = "Kudos (Taler Demonstrator)" +code = "KUDOS" +fractional_input_digits = 2 +fractional_normal_digits = 2 +fractional_trailing_zero_digits = 2 +alt_unit_names = {"0":"ク"} + +[currency-testkudos] +ENABLED = YES +name = "Test-kudos (Taler Demonstrator)" +code = "TESTKUDOS" +fractional_input_digits = 2 +fractional_normal_digits = 2 +fractional_trailing_zero_digits = 2 +alt_unit_names = {"0":"テ","3":"kテ","-3":"mテ"} + +[currency-japanese-yen] +ENABLED = NO +name = "Japanese Yen" +code = "JPY" +fractional_input_digits = 2 +fractional_normal_digits = 0 +fractional_trailing_zero_digits = 2 +alt_unit_names = {"0":"¥"} + +[currency-bitcoin-mainnet] +ENABLED = NO +name = "Bitcoin (Mainnet)" +code = "BITCOINBTC" +fractional_input_digits = 8 +fractional_normal_digits = 3 +fractional_trailing_zero_digits = 0 +alt_unit_names = {"0":"BTC","-3":"mBTC"} + +[currency-ethereum] +ENABLED = NO +name = "WAI-ETHER (Ethereum)" +code = "EthereumWAI" +fractional_input_digits = 0 +fractional_normal_digits = 0 +fractional_trailing_zero_digits = 0 +alt_unit_names = {"0":"WAI","3":"KWAI","6":"MWAI","9":"GWAI","12":"Szabo","15":"Finney","18":"Ether","21":"KEther","24":"MEther"} + +[currency-netzbon] +ENABLED=YES +name=NetzBon +code=NETZBON +fractional_input_digits=2 +fractional_normal_digits=2 +fractional_trailing_zero_digits=2 +alt_unit_names = {"0":"NETZBON"} diff --git a/src/util/denom.c b/src/util/denom.c index c1c3cdf5a..cb232c4a3 100644 --- a/src/util/denom.c +++ b/src/util/denom.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2021, 2022 Taler Systems SA + Copyright (C) 2021, 2022, 2023 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 @@ -25,60 +25,27 @@ enum GNUNET_GenericReturnValue TALER_denom_priv_create (struct TALER_DenominationPrivateKey *denom_priv, struct TALER_DenominationPublicKey *denom_pub, - enum TALER_DenominationCipher cipher, + enum GNUNET_CRYPTO_BlindSignatureAlgorithm cipher, ...) { - memset (denom_priv, - 0, - sizeof (*denom_priv)); + enum GNUNET_GenericReturnValue ret; + va_list ap; + memset (denom_pub, 0, sizeof (*denom_pub)); - - switch (cipher) - { - case TALER_DENOMINATION_INVALID: - GNUNET_break (0); - return GNUNET_SYSERR; - case TALER_DENOMINATION_RSA: - { - va_list ap; - unsigned int bits; - - va_start (ap, cipher); - bits = va_arg (ap, unsigned int); - va_end (ap); - if (bits < 512) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - denom_priv->details.rsa_private_key - = GNUNET_CRYPTO_rsa_private_key_create (bits); - } - if (NULL == denom_priv->details.rsa_private_key) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - denom_pub->details.rsa_public_key - = GNUNET_CRYPTO_rsa_private_key_get_public ( - denom_priv->details.rsa_private_key); - denom_priv->cipher = TALER_DENOMINATION_RSA; - denom_pub->cipher = TALER_DENOMINATION_RSA; - return GNUNET_OK; - case TALER_DENOMINATION_CS: - GNUNET_CRYPTO_cs_private_key_generate (&denom_priv->details.cs_private_key); - GNUNET_CRYPTO_cs_private_key_get_public ( - &denom_priv->details.cs_private_key, - &denom_pub->details.cs_public_key); - denom_priv->cipher = TALER_DENOMINATION_CS; - denom_pub->cipher = TALER_DENOMINATION_CS; - return GNUNET_OK; - default: - GNUNET_break (0); - } - return GNUNET_SYSERR; + memset (denom_priv, + 0, + sizeof (*denom_priv)); + va_start (ap, + cipher); + ret = GNUNET_CRYPTO_blind_sign_keys_create_va ( + &denom_priv->bsign_priv_key, + &denom_pub->bsign_pub_key, + cipher, + ap); + va_end (ap); + return ret; } @@ -88,57 +55,13 @@ TALER_denom_sign_blinded (struct TALER_BlindedDenominationSignature *denom_sig, bool for_melt, const struct TALER_BlindedPlanchet *blinded_planchet) { - memset (denom_sig, - 0, - sizeof (*denom_sig)); - if (blinded_planchet->cipher != denom_priv->cipher) - { - GNUNET_break (0); + denom_sig->blinded_sig + = GNUNET_CRYPTO_blind_sign (denom_priv->bsign_priv_key, + for_melt ? "rm" : "rw", + blinded_planchet->blinded_message); + if (NULL == denom_sig->blinded_sig) return GNUNET_SYSERR; - } - switch (denom_priv->cipher) - { - case TALER_DENOMINATION_INVALID: - GNUNET_break (0); - return GNUNET_SYSERR; - case TALER_DENOMINATION_RSA: - denom_sig->details.blinded_rsa_signature - = GNUNET_CRYPTO_rsa_sign_blinded ( - denom_priv->details.rsa_private_key, - blinded_planchet->details.rsa_blinded_planchet.blinded_msg, - blinded_planchet->details.rsa_blinded_planchet.blinded_msg_size); - if (NULL == denom_sig->details.blinded_rsa_signature) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - denom_sig->cipher = TALER_DENOMINATION_RSA; - return GNUNET_OK; - case TALER_DENOMINATION_CS: - { - struct GNUNET_CRYPTO_CsRSecret r[2]; - - GNUNET_CRYPTO_cs_r_derive ( - &blinded_planchet->details.cs_blinded_planchet.nonce.nonce, - for_melt ? "rm" : "rw", - &denom_priv->details.cs_private_key, - r); - denom_sig->details.blinded_cs_answer.b = - GNUNET_CRYPTO_cs_sign_derive (&denom_priv->details.cs_private_key, - r, - blinded_planchet->details. - cs_blinded_planchet.c, - &blinded_planchet->details. - cs_blinded_planchet.nonce.nonce, - &denom_sig->details.blinded_cs_answer. - s_scalar); - denom_sig->cipher = TALER_DENOMINATION_CS; - } - return GNUNET_OK; - default: - GNUNET_break (0); - } - return GNUNET_SYSERR; + return GNUNET_OK; } @@ -146,82 +69,24 @@ enum GNUNET_GenericReturnValue TALER_denom_sig_unblind ( struct TALER_DenominationSignature *denom_sig, const struct TALER_BlindedDenominationSignature *bdenom_sig, - const union TALER_DenominationBlindingKeyP *bks, + const union GNUNET_CRYPTO_BlindingSecretP *bks, const struct TALER_CoinPubHashP *c_hash, const struct TALER_ExchangeWithdrawValues *alg_values, const struct TALER_DenominationPublicKey *denom_pub) { - if (bdenom_sig->cipher != denom_pub->cipher) + denom_sig->unblinded_sig + = GNUNET_CRYPTO_blind_sig_unblind (bdenom_sig->blinded_sig, + bks, + c_hash, + sizeof (*c_hash), + alg_values->blinding_inputs, + denom_pub->bsign_pub_key); + if (NULL == denom_sig->unblinded_sig) { - GNUNET_break (0); + GNUNET_break_op (0); return GNUNET_SYSERR; } - switch (denom_pub->cipher) - { - case TALER_DENOMINATION_INVALID: - GNUNET_break (0); - return GNUNET_SYSERR; - case TALER_DENOMINATION_RSA: - denom_sig->details.rsa_signature - = GNUNET_CRYPTO_rsa_unblind ( - bdenom_sig->details.blinded_rsa_signature, - &bks->rsa_bks, - denom_pub->details.rsa_public_key); - if (NULL == denom_sig->details.rsa_signature) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - denom_sig->cipher = TALER_DENOMINATION_RSA; - return GNUNET_OK; - case TALER_DENOMINATION_CS: - { - struct GNUNET_CRYPTO_CsBlindingSecret bs[2]; - struct GNUNET_CRYPTO_CsC c[2]; - struct TALER_DenominationCSPublicRPairP r_pub_blind; - - GNUNET_CRYPTO_cs_blinding_secrets_derive (&bks->nonce, - bs); - GNUNET_CRYPTO_cs_calc_blinded_c ( - bs, - alg_values->details.cs_values.r_pub, - &denom_pub->details.cs_public_key, - &c_hash->hash, - sizeof(struct GNUNET_HashCode), - c, - r_pub_blind.r_pub); - denom_sig->details.cs_signature.r_point - = r_pub_blind.r_pub[bdenom_sig->details.blinded_cs_answer.b]; - GNUNET_CRYPTO_cs_unblind (&bdenom_sig->details.blinded_cs_answer.s_scalar, - &bs[bdenom_sig->details.blinded_cs_answer.b], - &denom_sig->details.cs_signature.s_scalar); - denom_sig->cipher = TALER_DENOMINATION_CS; - return GNUNET_OK; - } - default: - GNUNET_break (0); - } - return GNUNET_SYSERR; -} - - -void -TALER_rsa_pub_hash (const struct GNUNET_CRYPTO_RsaPublicKey *rsa, - struct TALER_RsaPubHashP *h_rsa) -{ - GNUNET_CRYPTO_rsa_public_key_hash (rsa, - &h_rsa->hash); - -} - - -void -TALER_cs_pub_hash (const struct GNUNET_CRYPTO_CsPublicKey *cs, - struct TALER_CsPubHashP *h_cs) -{ - GNUNET_CRYPTO_hash (cs, - sizeof(*cs), - &h_cs->hash); + return GNUNET_OK; } @@ -229,9 +94,11 @@ void TALER_denom_pub_hash (const struct TALER_DenominationPublicKey *denom_pub, struct TALER_DenominationHashP *denom_hash) { + struct GNUNET_CRYPTO_BlindSignPublicKey *bsp + = denom_pub->bsign_pub_key; uint32_t opt[2] = { htonl (denom_pub->age_mask.bits), - htonl ((uint32_t) denom_pub->cipher) + htonl ((uint32_t) bsp->cipher) }; struct GNUNET_HashContext *hc; @@ -239,15 +106,15 @@ TALER_denom_pub_hash (const struct TALER_DenominationPublicKey *denom_pub, GNUNET_CRYPTO_hash_context_read (hc, opt, sizeof (opt)); - switch (denom_pub->cipher) + switch (bsp->cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_RSA: { void *buf; size_t blen; blen = GNUNET_CRYPTO_rsa_public_key_encode ( - denom_pub->details.rsa_public_key, + bsp->details.rsa_public_key, &buf); GNUNET_CRYPTO_hash_context_read (hc, buf, @@ -255,10 +122,10 @@ TALER_denom_pub_hash (const struct TALER_DenominationPublicKey *denom_pub, GNUNET_free (buf); } break; - case TALER_DENOMINATION_CS: + case GNUNET_CRYPTO_BSA_CS: GNUNET_CRYPTO_hash_context_read (hc, - &denom_pub->details.cs_public_key, - sizeof(denom_pub->details.cs_public_key)); + &bsp->details.cs_public_key, + sizeof(bsp->details.cs_public_key)); break; default: GNUNET_assert (0); @@ -268,37 +135,24 @@ TALER_denom_pub_hash (const struct TALER_DenominationPublicKey *denom_pub, } -void -TALER_denom_priv_to_pub (const struct TALER_DenominationPrivateKey *denom_priv, - const struct TALER_AgeMask age_mask, - struct TALER_DenominationPublicKey *denom_pub) +const struct TALER_ExchangeWithdrawValues * +TALER_denom_ewv_rsa_singleton () { - switch (denom_priv->cipher) - { - case TALER_DENOMINATION_RSA: - denom_pub->cipher = TALER_DENOMINATION_RSA; - denom_pub->age_mask = age_mask; - denom_pub->details.rsa_public_key - = GNUNET_CRYPTO_rsa_private_key_get_public ( - denom_priv->details.rsa_private_key); - return; - case TALER_DENOMINATION_CS: - denom_pub->cipher = TALER_DENOMINATION_CS; - denom_pub->age_mask = age_mask; - GNUNET_CRYPTO_cs_private_key_get_public ( - &denom_priv->details.cs_private_key, - &denom_pub->details.cs_public_key); - return; - default: - GNUNET_assert (0); - } + static struct GNUNET_CRYPTO_BlindingInputValues bi = { + .cipher = GNUNET_CRYPTO_BSA_RSA + }; + static struct TALER_ExchangeWithdrawValues alg_values = { + .blinding_inputs = &bi + }; + return &alg_values; } enum GNUNET_GenericReturnValue TALER_denom_blind ( const struct TALER_DenominationPublicKey *dk, - const union TALER_DenominationBlindingKeyP *coin_bks, + const union GNUNET_CRYPTO_BlindingSecretP *coin_bks, + const union GNUNET_CRYPTO_BlindSessionNonce *nonce, const struct TALER_AgeCommitmentHash *ach, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_ExchangeWithdrawValues *alg_values, @@ -308,44 +162,16 @@ TALER_denom_blind ( TALER_coin_pub_hash (coin_pub, ach, c_hash); - switch (dk->cipher) - { - case TALER_DENOMINATION_RSA: - blinded_planchet->cipher = dk->cipher; - if (GNUNET_YES != - GNUNET_CRYPTO_rsa_blind ( - &c_hash->hash, - &coin_bks->rsa_bks, - dk->details.rsa_public_key, - &blinded_planchet->details.rsa_blinded_planchet.blinded_msg, - &blinded_planchet->details.rsa_blinded_planchet.blinded_msg_size)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; - case TALER_DENOMINATION_CS: - { - struct TALER_DenominationCSPublicRPairP blinded_r_pub; - struct GNUNET_CRYPTO_CsBlindingSecret bs[2]; - - blinded_planchet->cipher = TALER_DENOMINATION_CS; - GNUNET_CRYPTO_cs_blinding_secrets_derive (&coin_bks->nonce, - bs); - GNUNET_CRYPTO_cs_calc_blinded_c ( - bs, - alg_values->details.cs_values.r_pub, - &dk->details.cs_public_key, - c_hash, - sizeof(*c_hash), - blinded_planchet->details.cs_blinded_planchet.c, - blinded_r_pub.r_pub); - return GNUNET_OK; - } - default: - GNUNET_break (0); + blinded_planchet->blinded_message + = GNUNET_CRYPTO_message_blind_to_sign (dk->bsign_pub_key, + coin_bks, + nonce, + c_hash, + sizeof (*c_hash), + alg_values->blinding_inputs); + if (NULL == blinded_planchet->blinded_message) return GNUNET_SYSERR; - } + return GNUNET_OK; } @@ -354,64 +180,20 @@ TALER_denom_pub_verify (const struct TALER_DenominationPublicKey *denom_pub, const struct TALER_DenominationSignature *denom_sig, const struct TALER_CoinPubHashP *c_hash) { - if (denom_pub->cipher != denom_sig->cipher) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - switch (denom_pub->cipher) - { - case TALER_DENOMINATION_INVALID: - GNUNET_break (0); - return GNUNET_NO; - case TALER_DENOMINATION_RSA: - if (GNUNET_OK != - GNUNET_CRYPTO_rsa_verify (&c_hash->hash, - denom_sig->details.rsa_signature, - denom_pub->details.rsa_public_key)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Coin signature is invalid\n"); - return GNUNET_NO; - } - return GNUNET_YES; - case TALER_DENOMINATION_CS: - if (GNUNET_OK != - GNUNET_CRYPTO_cs_verify (&denom_sig->details.cs_signature, - &denom_pub->details.cs_public_key, - &c_hash->hash, - sizeof(struct GNUNET_HashCode))) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Coin signature is invalid\n"); - return GNUNET_NO; - } - return GNUNET_YES; - default: - GNUNET_assert (0); - } + return GNUNET_CRYPTO_blind_sig_verify (denom_pub->bsign_pub_key, + denom_sig->unblinded_sig, + c_hash, + sizeof (*c_hash)); } void TALER_denom_pub_free (struct TALER_DenominationPublicKey *denom_pub) { - switch (denom_pub->cipher) + if (NULL != denom_pub->bsign_pub_key) { - case TALER_DENOMINATION_INVALID: - return; - case TALER_DENOMINATION_RSA: - if (NULL != denom_pub->details.rsa_public_key) - { - GNUNET_CRYPTO_rsa_public_key_free (denom_pub->details.rsa_public_key); - denom_pub->details.rsa_public_key = NULL; - } - denom_pub->cipher = TALER_DENOMINATION_INVALID; - return; - case TALER_DENOMINATION_CS: - return; - default: - GNUNET_assert (0); + GNUNET_CRYPTO_blind_sign_pub_decref (denom_pub->bsign_pub_key); + denom_pub->bsign_pub_key = NULL; } } @@ -419,22 +201,10 @@ TALER_denom_pub_free (struct TALER_DenominationPublicKey *denom_pub) void TALER_denom_priv_free (struct TALER_DenominationPrivateKey *denom_priv) { - switch (denom_priv->cipher) + if (NULL != denom_priv->bsign_priv_key) { - case TALER_DENOMINATION_INVALID: - return; - case TALER_DENOMINATION_RSA: - if (NULL != denom_priv->details.rsa_private_key) - { - GNUNET_CRYPTO_rsa_private_key_free (denom_priv->details.rsa_private_key); - denom_priv->details.rsa_private_key = NULL; - } - denom_priv->cipher = TALER_DENOMINATION_INVALID; - return; - case TALER_DENOMINATION_CS: - return; - default: - GNUNET_assert (0); + GNUNET_CRYPTO_blind_sign_priv_decref (denom_priv->bsign_priv_key); + denom_priv->bsign_priv_key = NULL; } } @@ -442,22 +212,10 @@ TALER_denom_priv_free (struct TALER_DenominationPrivateKey *denom_priv) void TALER_denom_sig_free (struct TALER_DenominationSignature *denom_sig) { - switch (denom_sig->cipher) + if (NULL != denom_sig->unblinded_sig) { - case TALER_DENOMINATION_INVALID: - return; - case TALER_DENOMINATION_RSA: - if (NULL != denom_sig->details.rsa_signature) - { - GNUNET_CRYPTO_rsa_signature_free (denom_sig->details.rsa_signature); - denom_sig->details.rsa_signature = NULL; - } - denom_sig->cipher = TALER_DENOMINATION_INVALID; - return; - case TALER_DENOMINATION_CS: - return; - default: - GNUNET_assert (0); + GNUNET_CRYPTO_unblinded_sig_decref (denom_sig->unblinded_sig); + denom_sig->unblinded_sig = NULL; } } @@ -466,89 +224,73 @@ void TALER_blinded_denom_sig_free ( struct TALER_BlindedDenominationSignature *denom_sig) { - switch (denom_sig->cipher) + if (NULL != denom_sig->blinded_sig) { - case TALER_DENOMINATION_INVALID: - return; - case TALER_DENOMINATION_RSA: - if (NULL != denom_sig->details.blinded_rsa_signature) - { - GNUNET_CRYPTO_rsa_signature_free ( - denom_sig->details.blinded_rsa_signature); - denom_sig->details.blinded_rsa_signature = NULL; - } - denom_sig->cipher = TALER_DENOMINATION_INVALID; - return; - case TALER_DENOMINATION_CS: - return; - default: - GNUNET_assert (0); + GNUNET_CRYPTO_blinded_sig_decref (denom_sig->blinded_sig); + denom_sig->blinded_sig = NULL; } } void -TALER_denom_pub_deep_copy (struct TALER_DenominationPublicKey *denom_dst, - const struct TALER_DenominationPublicKey *denom_src) +TALER_denom_ewv_free (struct TALER_ExchangeWithdrawValues *ewv) { - *denom_dst = *denom_src; /* shallow copy */ - switch (denom_src->cipher) - { - case TALER_DENOMINATION_RSA: - denom_dst->details.rsa_public_key - = GNUNET_CRYPTO_rsa_public_key_dup ( - denom_src->details.rsa_public_key); + if (ewv == TALER_denom_ewv_rsa_singleton ()) return; - case TALER_DENOMINATION_CS: + if (ewv->blinding_inputs == + TALER_denom_ewv_rsa_singleton ()->blinding_inputs) + { + ewv->blinding_inputs = NULL; return; - default: - GNUNET_assert (0); + } + if (NULL != ewv->blinding_inputs) + { + GNUNET_CRYPTO_blinding_input_values_decref (ewv->blinding_inputs); + ewv->blinding_inputs = NULL; } } void -TALER_denom_sig_deep_copy (struct TALER_DenominationSignature *denom_dst, - const struct TALER_DenominationSignature *denom_src) +TALER_denom_ewv_copy (struct TALER_ExchangeWithdrawValues *bi_dst, + const struct TALER_ExchangeWithdrawValues *bi_src) { - *denom_dst = *denom_src; /* shallow copy */ - switch (denom_src->cipher) + if (bi_src == TALER_denom_ewv_rsa_singleton ()) { - case TALER_DENOMINATION_INVALID: - return; - case TALER_DENOMINATION_RSA: - denom_dst->details.rsa_signature - = GNUNET_CRYPTO_rsa_signature_dup ( - denom_src->details.rsa_signature); - return; - case TALER_DENOMINATION_CS: + *bi_dst = *bi_src; return; - default: - GNUNET_assert (0); } + bi_dst->blinding_inputs + = GNUNET_CRYPTO_blinding_input_values_incref (bi_src->blinding_inputs); +} + + +void +TALER_denom_pub_copy (struct TALER_DenominationPublicKey *denom_dst, + const struct TALER_DenominationPublicKey *denom_src) +{ + denom_dst->age_mask = denom_src->age_mask; + denom_dst->bsign_pub_key + = GNUNET_CRYPTO_bsign_pub_incref (denom_src->bsign_pub_key); } void -TALER_blinded_denom_sig_deep_copy ( +TALER_denom_sig_copy (struct TALER_DenominationSignature *denom_dst, + const struct TALER_DenominationSignature *denom_src) +{ + denom_dst->unblinded_sig + = GNUNET_CRYPTO_ub_sig_incref (denom_src->unblinded_sig); +} + + +void +TALER_blinded_denom_sig_copy ( struct TALER_BlindedDenominationSignature *denom_dst, const struct TALER_BlindedDenominationSignature *denom_src) { - *denom_dst = *denom_src; /* shallow copy */ - switch (denom_src->cipher) - { - case TALER_DENOMINATION_INVALID: - return; - case TALER_DENOMINATION_RSA: - denom_dst->details.blinded_rsa_signature - = GNUNET_CRYPTO_rsa_signature_dup ( - denom_src->details.blinded_rsa_signature); - return; - case TALER_DENOMINATION_CS: - return; - default: - GNUNET_assert (0); - } + denom_dst->blinded_sig + = GNUNET_CRYPTO_blind_sig_incref (denom_src->blinded_sig); } @@ -556,24 +298,14 @@ int TALER_denom_pub_cmp (const struct TALER_DenominationPublicKey *denom1, const struct TALER_DenominationPublicKey *denom2) { - if (denom1->cipher != denom2->cipher) - return (denom1->cipher > denom2->cipher) ? 1 : -1; + if (denom1->bsign_pub_key->cipher != + denom2->bsign_pub_key->cipher) + return (denom1->bsign_pub_key->cipher > + denom2->bsign_pub_key->cipher) ? 1 : -1; if (denom1->age_mask.bits != denom2->age_mask.bits) return (denom1->age_mask.bits > denom2->age_mask.bits) ? 1 : -1; - switch (denom1->cipher) - { - case TALER_DENOMINATION_INVALID: - return 0; - case TALER_DENOMINATION_RSA: - return GNUNET_CRYPTO_rsa_public_key_cmp (denom1->details.rsa_public_key, - denom2->details.rsa_public_key); - case TALER_DENOMINATION_CS: - return GNUNET_memcmp (&denom1->details.cs_public_key, - &denom2->details.cs_public_key); - default: - GNUNET_assert (0); - } - return -2; + return GNUNET_CRYPTO_bsign_pub_cmp (denom1->bsign_pub_key, + denom2->bsign_pub_key); } @@ -581,22 +313,8 @@ int TALER_denom_sig_cmp (const struct TALER_DenominationSignature *sig1, const struct TALER_DenominationSignature *sig2) { - if (sig1->cipher != sig2->cipher) - return (sig1->cipher > sig2->cipher) ? 1 : -1; - switch (sig1->cipher) - { - case TALER_DENOMINATION_INVALID: - return 0; - case TALER_DENOMINATION_RSA: - return GNUNET_CRYPTO_rsa_signature_cmp (sig1->details.rsa_signature, - sig2->details.rsa_signature); - case TALER_DENOMINATION_CS: - return GNUNET_memcmp (&sig1->details.cs_signature, - &sig2->details.cs_signature); - default: - GNUNET_assert (0); - } - return -2; + return GNUNET_CRYPTO_ub_sig_cmp (sig1->unblinded_sig, + sig1->unblinded_sig); } @@ -605,27 +323,8 @@ TALER_blinded_planchet_cmp ( const struct TALER_BlindedPlanchet *bp1, const struct TALER_BlindedPlanchet *bp2) { - if (bp1->cipher != bp2->cipher) - return (bp1->cipher > bp2->cipher) ? 1 : -1; - switch (bp1->cipher) - { - case TALER_DENOMINATION_INVALID: - return 0; - case TALER_DENOMINATION_RSA: - if (bp1->details.rsa_blinded_planchet.blinded_msg_size != - bp2->details.rsa_blinded_planchet.blinded_msg_size) - return (bp1->details.rsa_blinded_planchet.blinded_msg_size > - bp2->details.rsa_blinded_planchet.blinded_msg_size) ? 1 : -1; - return memcmp (bp1->details.rsa_blinded_planchet.blinded_msg, - bp2->details.rsa_blinded_planchet.blinded_msg, - bp1->details.rsa_blinded_planchet.blinded_msg_size); - case TALER_DENOMINATION_CS: - return GNUNET_memcmp (&bp1->details.cs_blinded_planchet, - &bp2->details.cs_blinded_planchet); - default: - GNUNET_assert (0); - } - return -2; + return GNUNET_CRYPTO_blinded_message_cmp (bp1->blinded_message, + bp2->blinded_message); } @@ -634,22 +333,8 @@ TALER_blinded_denom_sig_cmp ( const struct TALER_BlindedDenominationSignature *sig1, const struct TALER_BlindedDenominationSignature *sig2) { - if (sig1->cipher != sig2->cipher) - return (sig1->cipher > sig2->cipher) ? 1 : -1; - switch (sig1->cipher) - { - case TALER_DENOMINATION_INVALID: - return 0; - case TALER_DENOMINATION_RSA: - return GNUNET_CRYPTO_rsa_signature_cmp (sig1->details.blinded_rsa_signature, - sig2->details.blinded_rsa_signature); - case TALER_DENOMINATION_CS: - return GNUNET_memcmp (&sig1->details.blinded_cs_answer, - &sig2->details.blinded_cs_answer); - default: - GNUNET_assert (0); - } - return -2; + return GNUNET_CRYPTO_blind_sig_cmp (sig1->blinded_sig, + sig1->blinded_sig); } @@ -657,31 +342,31 @@ void TALER_blinded_planchet_hash_ (const struct TALER_BlindedPlanchet *bp, struct GNUNET_HashContext *hash_context) { - uint32_t cipher = htonl (bp->cipher); + const struct GNUNET_CRYPTO_BlindedMessage *bm = bp->blinded_message; + uint32_t cipher = htonl (bm->cipher); GNUNET_CRYPTO_hash_context_read (hash_context, &cipher, sizeof (cipher)); - switch (bp->cipher) + switch (bm->cipher) { - case TALER_DENOMINATION_INVALID: - break; - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_INVALID: + GNUNET_break (0); + return; + case GNUNET_CRYPTO_BSA_RSA: GNUNET_CRYPTO_hash_context_read ( hash_context, - bp->details.rsa_blinded_planchet.blinded_msg, - bp->details.rsa_blinded_planchet.blinded_msg_size); - break; - case TALER_DENOMINATION_CS: + bm->details.rsa_blinded_message.blinded_msg, + bm->details.rsa_blinded_message.blinded_msg_size); + return; + case GNUNET_CRYPTO_BSA_CS: GNUNET_CRYPTO_hash_context_read ( hash_context, - &bp->details.cs_blinded_planchet, - sizeof (bp->details.cs_blinded_planchet)); - break; - default: - GNUNET_assert (0); - break; + &bm->details.cs_blinded_message, + sizeof (bm->details.cs_blinded_message)); + return; } + GNUNET_assert (0); } @@ -689,14 +374,17 @@ void TALER_planchet_blinding_secret_create ( const struct TALER_PlanchetMasterSecretP *ps, const struct TALER_ExchangeWithdrawValues *alg_values, - union TALER_DenominationBlindingKeyP *bks) + union GNUNET_CRYPTO_BlindingSecretP *bks) { - switch (alg_values->cipher) + const struct GNUNET_CRYPTO_BlindingInputValues *bi = + alg_values->blinding_inputs; + + switch (bi->cipher) { - case TALER_DENOMINATION_INVALID: + case GNUNET_CRYPTO_BSA_INVALID: GNUNET_break (0); return; - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_RSA: GNUNET_assert (GNUNET_YES == GNUNET_CRYPTO_kdf (&bks->rsa_bks, sizeof (bks->rsa_bks), @@ -707,7 +395,7 @@ TALER_planchet_blinding_secret_create ( NULL, 0)); return; - case TALER_DENOMINATION_CS: + case GNUNET_CRYPTO_BSA_CS: GNUNET_assert (GNUNET_YES == GNUNET_CRYPTO_kdf (&bks->nonce, sizeof (bks->nonce), @@ -715,14 +403,13 @@ TALER_planchet_blinding_secret_create ( strlen ("bseed"), ps, sizeof(*ps), - &alg_values->details.cs_values, - sizeof(alg_values->details.cs_values), + &bi->details.cs_values, + sizeof(bi->details.cs_values), NULL, 0)); return; - default: - GNUNET_break (0); } + GNUNET_assert (0); } @@ -732,9 +419,18 @@ TALER_planchet_setup_coin_priv ( const struct TALER_ExchangeWithdrawValues *alg_values, struct TALER_CoinSpendPrivateKeyP *coin_priv) { - switch (alg_values->cipher) + const struct GNUNET_CRYPTO_BlindingInputValues *bi + = alg_values->blinding_inputs; + + switch (bi->cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_INVALID: + GNUNET_break (0); + memset (coin_priv, + 0, + sizeof (*coin_priv)); + return; + case GNUNET_CRYPTO_BSA_RSA: GNUNET_assert (GNUNET_YES == GNUNET_CRYPTO_kdf (coin_priv, sizeof (*coin_priv), @@ -744,8 +440,8 @@ TALER_planchet_setup_coin_priv ( sizeof(*ps), NULL, 0)); - break; - case TALER_DENOMINATION_CS: + return; + case GNUNET_CRYPTO_BSA_CS: GNUNET_assert (GNUNET_YES == GNUNET_CRYPTO_kdf (coin_priv, sizeof (*coin_priv), @@ -753,37 +449,24 @@ TALER_planchet_setup_coin_priv ( strlen ("coin"), ps, sizeof(*ps), - &alg_values->details.cs_values, - sizeof(alg_values->details.cs_values), + &bi->details.cs_values, + sizeof(bi->details.cs_values), NULL, 0)); - break; - default: - GNUNET_break (0); return; } + GNUNET_assert (0); } void TALER_blinded_planchet_free (struct TALER_BlindedPlanchet *blinded_planchet) { - switch (blinded_planchet->cipher) + if (NULL != blinded_planchet->blinded_message) { - case TALER_DENOMINATION_INVALID: - GNUNET_break (0); - return; - case TALER_DENOMINATION_RSA: - GNUNET_free (blinded_planchet->details.rsa_blinded_planchet.blinded_msg); - return; - case TALER_DENOMINATION_CS: - memset (blinded_planchet, - 0, - sizeof (*blinded_planchet)); - /* nothing to do for CS */ - return; + GNUNET_CRYPTO_blinded_message_decref (blinded_planchet->blinded_message); + blinded_planchet->blinded_message = NULL; } - GNUNET_assert (0); } diff --git a/src/util/do_bench_age_restriction b/src/util/do_bench_age_restriction new file mode 100755 index 000000000..a65713439 --- /dev/null +++ b/src/util/do_bench_age_restriction @@ -0,0 +1,8 @@ +#!/bin/sh + +gcc bench_age_restriction.c \ + -lgnunetutil -lgnunetjson -lsodium -ljansson \ + -L/usr/lib/x86_64-linux-gnu -lmicrohttpd -ltalerutil -lm \ + -I../include \ + -o bench_age_restriction && ./bench_age_restriction + diff --git a/src/util/exchange_signatures.c b/src/util/exchange_signatures.c index 3f590325c..aaefb5cec 100644 --- a/src/util/exchange_signatures.c +++ b/src/util/exchange_signatures.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2021, 2022 Taler Systems SA + Copyright (C) 2021-2023 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 @@ -48,10 +48,10 @@ struct TALER_DepositConfirmationPS struct TALER_MerchantWireHashP h_wire GNUNET_PACKED; /** - * Hash over the extension options of the deposit, 0 if there - * were not extension options. + * Hash over the optional policy extension of the deposit, 0 if there + * was no policy. */ - struct TALER_ExtensionContractHashP h_extensions GNUNET_PACKED; + struct TALER_ExtensionPolicyHashP h_policy GNUNET_PACKED; /** * Time when this confirmation was generated / when the exchange received @@ -78,12 +78,12 @@ struct TALER_DepositConfirmationPS * Amount to be deposited, excluding fee. Calculated from the * amount with fee and the fee from the deposit request. */ - struct TALER_AmountNBO amount_without_fee; + struct TALER_AmountNBO total_without_fee; /** - * The public key of the coin that was deposited. + * Hash over all of the coin signatures. */ - struct TALER_CoinSpendPublicKeyP coin_pub; + struct GNUNET_HashCode h_coin_sigs; /** * The Merchant's public key. Allows the merchant to later refund @@ -101,12 +101,13 @@ 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, - const struct TALER_Amount *amount_without_fee, - const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *total_without_fee, + unsigned int num_coins, + const struct TALER_CoinSpendSignatureP *coin_sigs[static num_coins], const struct TALER_MerchantPublicKeyP *merchant_pub, struct TALER_ExchangePublicKeyP *pub, struct TALER_ExchangeSignatureP *sig) @@ -119,14 +120,22 @@ TALER_exchange_online_deposit_confirmation_sign ( .exchange_timestamp = GNUNET_TIME_timestamp_hton (exchange_timestamp), .wire_deadline = GNUNET_TIME_timestamp_hton (wire_deadline), .refund_deadline = GNUNET_TIME_timestamp_hton (refund_deadline), - .coin_pub = *coin_pub, - .merchant_pub = *merchant_pub + .merchant_pub = *merchant_pub, + .h_policy = {{{0}}} }; - - if (NULL != h_extensions) - dcs.h_extensions = *h_extensions; - TALER_amount_hton (&dcs.amount_without_fee, - amount_without_fee); + struct GNUNET_HashContext *hc; + + hc = GNUNET_CRYPTO_hash_context_start (); + for (unsigned int i = 0; i<num_coins; i++) + GNUNET_CRYPTO_hash_context_read (hc, + coin_sigs[i], + sizeof (*coin_sigs[i])); + GNUNET_CRYPTO_hash_context_finish (hc, + &dcs.h_coin_sigs); + if (NULL != h_policy) + dcs.h_policy = *h_policy; + TALER_amount_hton (&dcs.total_without_fee, + total_without_fee); return scb (&dcs.purpose, pub, sig); @@ -137,12 +146,13 @@ 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, - const struct TALER_Amount *amount_without_fee, - const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *total_without_fee, + unsigned int num_coins, + const struct TALER_CoinSpendSignatureP *coin_sigs[static num_coins], const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_ExchangePublicKeyP *exchange_pub, const struct TALER_ExchangeSignatureP *exchange_sig) @@ -155,14 +165,21 @@ TALER_exchange_online_deposit_confirmation_verify ( .exchange_timestamp = GNUNET_TIME_timestamp_hton (exchange_timestamp), .wire_deadline = GNUNET_TIME_timestamp_hton (wire_deadline), .refund_deadline = GNUNET_TIME_timestamp_hton (refund_deadline), - .coin_pub = *coin_pub, .merchant_pub = *merchant_pub }; - - if (NULL != h_extensions) - dcs.h_extensions = *h_extensions; - TALER_amount_hton (&dcs.amount_without_fee, - amount_without_fee); + struct GNUNET_HashContext *hc; + + hc = GNUNET_CRYPTO_hash_context_start (); + for (unsigned int i = 0; i<num_coins; i++) + GNUNET_CRYPTO_hash_context_read (hc, + coin_sigs[i], + sizeof (*coin_sigs[i])); + GNUNET_CRYPTO_hash_context_finish (hc, + &dcs.h_coin_sigs); + if (NULL != h_policy) + dcs.h_policy = *h_policy; + TALER_amount_hton (&dcs.total_without_fee, + total_without_fee); if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT, &dcs, @@ -362,6 +379,91 @@ TALER_exchange_online_melt_confirmation_verify ( GNUNET_NETWORK_STRUCT_BEGIN /** + * @brief Format of the block signed by the Exchange in response to a + * successful "/reserves/$RESERVE_PUB/age-withdraw" request. Hereby the + * exchange affirms that the commitment along with the maximum age group and + * the amount were accepted. This also commits the exchange to a particular + * index to not be revealed during the reveal. + */ +struct TALER_AgeWithdrawConfirmationPS +{ + /** + * Purpose is #TALER_SIGNATURE_EXCHANGE_CONFIRM_AGE_WITHDRAW. Signed by a + * `struct TALER_ExchangePublicKeyP` using EdDSA. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Commitment made in the /reserves/$RESERVE_PUB/age-withdraw. + */ + struct TALER_AgeWithdrawCommitmentHashP h_commitment GNUNET_PACKED; + + /** + * Index that the client will not have to reveal, in NBO. + * Must be smaller than #TALER_CNC_KAPPA. + */ + uint32_t noreveal_index GNUNET_PACKED; + +}; + +GNUNET_NETWORK_STRUCT_END + +enum TALER_ErrorCode +TALER_exchange_online_age_withdraw_confirmation_sign ( + TALER_ExchangeSignCallback scb, + const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, + uint32_t noreveal_index, + struct TALER_ExchangePublicKeyP *pub, + struct TALER_ExchangeSignatureP *sig) +{ + + struct TALER_AgeWithdrawConfirmationPS confirm = { + .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_AGE_WITHDRAW), + .purpose.size = htonl (sizeof (confirm)), + .h_commitment = *h_commitment, + .noreveal_index = htonl (noreveal_index) + }; + + return scb (&confirm.purpose, + pub, + sig); +} + + +enum GNUNET_GenericReturnValue +TALER_exchange_online_age_withdraw_confirmation_verify ( + const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, + uint32_t noreveal_index, + const struct TALER_ExchangePublicKeyP *exchange_pub, + const struct TALER_ExchangeSignatureP *exchange_sig) +{ + struct TALER_AgeWithdrawConfirmationPS confirm = { + .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_AGE_WITHDRAW), + .purpose.size = htonl (sizeof (confirm)), + .h_commitment = *h_commitment, + .noreveal_index = htonl (noreveal_index) + }; + + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify ( + TALER_SIGNATURE_EXCHANGE_CONFIRM_AGE_WITHDRAW, + &confirm, + &exchange_sig->eddsa_signature, + &exchange_pub->eddsa_pub)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/* TODO:oec: add signature for age-withdraw, age-reveal */ + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** * @brief Signature made by the exchange over the full set of keys, used * to detect cheating exchanges that give out different sets to * different users. @@ -449,19 +551,20 @@ struct TALER_ExchangeAccountSetupSuccessPS struct GNUNET_CRYPTO_EccSignaturePurpose purpose; /** - * Hash over the payto for which the signature was - * made. + * Hash over the payto for which the signature was made. */ struct TALER_PaytoHashP h_payto; - // FIXME: include details on *which* KYC process - // was satisfied! + /** + * Hash over details on *which* KYC obligations were discharged! + */ + struct GNUNET_HashCode h_kyc; /** * When was the signature made. - * FIXME: replace by *expiration* time! */ struct GNUNET_TIME_TimestampNBO timestamp; + }; GNUNET_NETWORK_STRUCT_END @@ -471,6 +574,7 @@ enum TALER_ErrorCode TALER_exchange_online_account_setup_success_sign ( TALER_ExchangeSignCallback scb, const struct TALER_PaytoHashP *h_payto, + const json_t *kyc, struct GNUNET_TIME_Timestamp timestamp, struct TALER_ExchangePublicKeyP *pub, struct TALER_ExchangeSignatureP *sig) @@ -480,10 +584,11 @@ TALER_exchange_online_account_setup_success_sign ( .purpose.purpose = htonl ( TALER_SIGNATURE_EXCHANGE_ACCOUNT_SETUP_SUCCESS), .h_payto = *h_payto, - .timestamp = GNUNET_TIME_timestamp_hton ( - timestamp) + .timestamp = GNUNET_TIME_timestamp_hton (timestamp) }; + TALER_json_hash (kyc, + &kyc_purpose.h_kyc); return scb (&kyc_purpose.purpose, pub, sig); @@ -493,6 +598,7 @@ TALER_exchange_online_account_setup_success_sign ( enum GNUNET_GenericReturnValue TALER_exchange_online_account_setup_success_verify ( const struct TALER_PaytoHashP *h_payto, + const json_t *kyc, struct GNUNET_TIME_Timestamp timestamp, const struct TALER_ExchangePublicKeyP *pub, const struct TALER_ExchangeSignatureP *sig) @@ -502,10 +608,11 @@ TALER_exchange_online_account_setup_success_verify ( .purpose.purpose = htonl ( TALER_SIGNATURE_EXCHANGE_ACCOUNT_SETUP_SUCCESS), .h_payto = *h_payto, - .timestamp = GNUNET_TIME_timestamp_hton ( - timestamp) + .timestamp = GNUNET_TIME_timestamp_hton (timestamp) }; + TALER_json_hash (kyc, + &kyc_purpose.h_kyc); return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_ACCOUNT_SETUP_SUCCESS, &kyc_purpose, @@ -853,8 +960,9 @@ TALER_exchange_online_confirm_recoup_sign ( struct TALER_RecoupConfirmationPS pc = { .purpose.size = htonl (sizeof (pc)), .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP), - .reserve_pub = *reserve_pub, - .coin_pub = *coin_pub + .timestamp = GNUNET_TIME_timestamp_hton (timestamp), + .coin_pub = *coin_pub, + .reserve_pub = *reserve_pub }; TALER_amount_hton (&pc.recoup_amount, @@ -877,8 +985,9 @@ TALER_exchange_online_confirm_recoup_verify ( struct TALER_RecoupConfirmationPS pc = { .purpose.size = htonl (sizeof (pc)), .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP), - .reserve_pub = *reserve_pub, - .coin_pub = *coin_pub + .timestamp = GNUNET_TIME_timestamp_hton (timestamp), + .coin_pub = *coin_pub, + .reserve_pub = *reserve_pub }; TALER_amount_hton (&pc.recoup_amount, @@ -1113,10 +1222,10 @@ TALER_exchange_online_denomination_expired_sign ( }; /* strncpy would create a compiler warning */ - memcpy (dua.operation, - op, - GNUNET_MIN (sizeof (dua.operation), - strlen (op))); + GNUNET_memcpy (dua.operation, + op, + GNUNET_MIN (sizeof (dua.operation), + strlen (op))); return scb (&dua.purpose, pub, sig); @@ -1140,10 +1249,10 @@ TALER_exchange_online_denomination_expired_verify ( }; /* strncpy would create a compiler warning */ - memcpy (dua.operation, - op, - GNUNET_MIN (sizeof (dua.operation), - strlen (op))); + GNUNET_memcpy (dua.operation, + op, + GNUNET_MIN (sizeof (dua.operation), + strlen (op))); return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_EXPIRED, &dua, @@ -1381,6 +1490,100 @@ GNUNET_NETWORK_STRUCT_BEGIN /** * Response by which the exchange affirms that it has + * received funds deposited into a purse. + */ +struct TALER_CoinPurseRefundConfirmationPS +{ + + /** + * Purpose is #TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Public key of the purse. + */ + struct TALER_PurseContractPublicKeyP purse_pub; + + /** + * Public key of the coin. + */ + struct TALER_CoinSpendPublicKeyP coin_pub; + + /** + * How much will be refunded to the purse. + */ + struct TALER_AmountNBO refunded_amount; + + /** + * How much was the refund fee. + */ + struct TALER_AmountNBO refund_fee; + +}; + +GNUNET_NETWORK_STRUCT_END + + +enum TALER_ErrorCode +TALER_exchange_online_purse_refund_sign ( + TALER_ExchangeSignCallback scb, + const struct TALER_Amount *amount_without_fee, + const struct TALER_Amount *refund_fee, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_PurseContractPublicKeyP *purse_pub, + struct TALER_ExchangePublicKeyP *pub, + struct TALER_ExchangeSignatureP *sig) +{ + struct TALER_CoinPurseRefundConfirmationPS dc = { + .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND), + .purpose.size = htonl (sizeof (dc)), + .coin_pub = *coin_pub, + .purse_pub = *purse_pub, + }; + + TALER_amount_hton (&dc.refunded_amount, + amount_without_fee); + TALER_amount_hton (&dc.refund_fee, + refund_fee); + return scb (&dc.purpose, + pub, + sig); +} + + +enum GNUNET_GenericReturnValue +TALER_exchange_online_purse_refund_verify ( + const struct TALER_Amount *amount_without_fee, + const struct TALER_Amount *refund_fee, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_PurseContractPublicKeyP *purse_pub, + const struct TALER_ExchangePublicKeyP *pub, + const struct TALER_ExchangeSignatureP *sig) +{ + struct TALER_CoinPurseRefundConfirmationPS dc = { + .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND), + .purpose.size = htonl (sizeof (dc)), + .coin_pub = *coin_pub, + .purse_pub = *purse_pub, + }; + + TALER_amount_hton (&dc.refunded_amount, + amount_without_fee); + TALER_amount_hton (&dc.refund_fee, + refund_fee); + return + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND, + &dc, + &sig->eddsa_signature, + &pub->eddsa_pub); +} + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Response by which the exchange affirms that it has * merged a purse into a reserve. */ struct TALER_PurseMergedConfirmationPS @@ -1589,4 +1792,103 @@ TALER_exchange_online_purse_status_verify ( } +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Message signed by the exchange to affirm that the + * owner of a reserve has certain attributes. + */ +struct TALER_ExchangeAttestPS +{ + + /** + * Purpose is #TALER_SIGNATURE_EXCHANGE_RESERVE_ATTEST_DETAILS + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Time when the attestation was made. + */ + struct GNUNET_TIME_TimestampNBO attest_timestamp; + + /** + * Time when the attestation expires. + */ + struct GNUNET_TIME_TimestampNBO expiration_time; + + /** + * Public key of the reserve for which the attributes + * are attested. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Hash over the attributes. + */ + struct GNUNET_HashCode h_attributes; + +}; + +GNUNET_NETWORK_STRUCT_END + + +enum TALER_ErrorCode +TALER_exchange_online_reserve_attest_details_sign ( + TALER_ExchangeSignCallback scb, + struct GNUNET_TIME_Timestamp attest_timestamp, + struct GNUNET_TIME_Timestamp expiration_time, + const struct TALER_ReservePublicKeyP *reserve_pub, + const json_t *attributes, + struct TALER_ExchangePublicKeyP *pub, + struct TALER_ExchangeSignatureP *sig) +{ + struct TALER_ExchangeAttestPS rap = { + .purpose.size = htonl (sizeof (rap)), + .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_ATTEST_DETAILS), + .attest_timestamp = GNUNET_TIME_timestamp_hton (attest_timestamp), + .expiration_time = GNUNET_TIME_timestamp_hton (expiration_time), + .reserve_pub = *reserve_pub + }; + + TALER_json_hash (attributes, + &rap.h_attributes); + return scb (&rap.purpose, + pub, + sig); +} + + +enum GNUNET_GenericReturnValue +TALER_exchange_online_reserve_attest_details_verify ( + struct GNUNET_TIME_Timestamp attest_timestamp, + struct GNUNET_TIME_Timestamp expiration_time, + const struct TALER_ReservePublicKeyP *reserve_pub, + const json_t *attributes, + struct TALER_ExchangePublicKeyP *pub, + struct TALER_ExchangeSignatureP *sig) +{ + struct TALER_ExchangeAttestPS rap = { + .purpose.size = htonl (sizeof (rap)), + .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_ATTEST_DETAILS), + .attest_timestamp = GNUNET_TIME_timestamp_hton (attest_timestamp), + .expiration_time = GNUNET_TIME_timestamp_hton (expiration_time), + .reserve_pub = *reserve_pub + }; + + TALER_json_hash (attributes, + &rap.h_attributes); + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify ( + TALER_SIGNATURE_EXCHANGE_RESERVE_ATTEST_DETAILS, + &rap, + &sig->eddsa_signature, + &pub->eddsa_pub)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + /* end of exchange_signatures.c */ diff --git a/src/util/iban.c b/src/util/iban.c index efd8c4282..c2274d3cb 100644 --- a/src/util/iban.c +++ b/src/util/iban.c @@ -233,9 +233,9 @@ TALER_iban_validate (const char *iban) return GNUNET_strdup ("IBAN number too short to be valid"); if (len > 34) return GNUNET_strdup ("IBAN number too long to be valid"); - memcpy (cc, iban, 2); - memcpy (ibancpy, iban + 4, len - 4); - memcpy (ibancpy + len - 4, iban, 4); + GNUNET_memcpy (cc, iban, 2); + GNUNET_memcpy (ibancpy, iban + 4, len - 4); + GNUNET_memcpy (ibancpy + len - 4, iban, 4); ibancpy[len] = '\0'; cc_entry.code = cc; cc_entry.english = NULL; diff --git a/src/util/merchant_signatures.c b/src/util/merchant_signatures.c index 36f96499c..35e0b0e07 100644 --- a/src/util/merchant_signatures.c +++ b/src/util/merchant_signatures.c @@ -277,7 +277,7 @@ void TALER_merchant_pay_sign ( const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_MerchantPrivateKeyP *merch_priv, - struct GNUNET_CRYPTO_EddsaSignature *merch_sig) + struct TALER_MerchantSignatureP *merch_sig) { struct TALER_PaymentResponsePS mr = { .purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_PAYMENT_OK), @@ -287,7 +287,7 @@ TALER_merchant_pay_sign ( GNUNET_CRYPTO_eddsa_sign (&merch_priv->eddsa_priv, &mr, - merch_sig); + &merch_sig->eddsa_sig); } diff --git a/src/util/offline_signatures.c b/src/util/offline_signatures.c index 108c665ef..fbff850df 100644 --- a/src/util/offline_signatures.c +++ b/src/util/offline_signatures.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020-2022 Taler Systems SA + Copyright (C) 2020-2023 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 @@ -27,6 +27,99 @@ GNUNET_NETWORK_STRUCT_BEGIN /** * @brief Signature made by the exchange offline key over the information of + * an AML officer status change. + */ +struct TALER_MasterAmlOfficerStatusPS +{ + + /** + * Purpose is #TALER_SIGNATURE_MASTER_AML_KEY. Signed + * by a `struct TALER_MasterPublicKeyP` using EdDSA. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Time of the change. + */ + struct GNUNET_TIME_TimestampNBO change_date; + + /** + * Public key of the AML officer. + */ + struct TALER_AmlOfficerPublicKeyP officer_pub; + + /** + * Hash over the AML officer's name. + */ + struct GNUNET_HashCode h_officer_name GNUNET_PACKED; + + /** + * Bitmask: 1 if enabled; 2 for read-only access. in NBO. + */ + uint32_t is_active GNUNET_PACKED; +}; +GNUNET_NETWORK_STRUCT_END + + +void +TALER_exchange_offline_aml_officer_status_sign ( + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + 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) +{ + struct TALER_MasterAmlOfficerStatusPS as = { + .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_AML_KEY), + .purpose.size = htonl (sizeof (as)), + .change_date = GNUNET_TIME_timestamp_hton (change_date), + .officer_pub = *officer_pub, + .is_active = htonl ((is_active ? 1 : 0) + (read_only ? 2 : 0)) + }; + + GNUNET_CRYPTO_hash (officer_name, + strlen (officer_name) + 1, + &as.h_officer_name); + GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv, + &as, + &master_sig->eddsa_signature); +} + + +enum GNUNET_GenericReturnValue +TALER_exchange_offline_aml_officer_status_verify ( + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + 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) +{ + struct TALER_MasterAmlOfficerStatusPS as = { + .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_AML_KEY), + .purpose.size = htonl (sizeof (as)), + .change_date = GNUNET_TIME_timestamp_hton (change_date), + .officer_pub = *officer_pub, + .is_active = htonl ((is_active ? 1 : 0) + (read_only ? 2 : 0)) + }; + + GNUNET_CRYPTO_hash (officer_name, + strlen (officer_name) + 1, + &as.h_officer_name); + return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_AML_KEY, + &as, + &master_sig->eddsa_signature, + &master_pub->eddsa_pub); +} + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * @brief Signature made by the exchange offline key over the information of * an auditor to be added to the exchange's set of auditors. */ struct TALER_MasterAddAuditorPS @@ -584,6 +677,22 @@ struct TALER_MasterAddWirePS * Hash over the exchange's payto URI. */ struct TALER_PaytoHashP h_payto GNUNET_PACKED; + + /** + * Hash over the conversion URL, all zeros if there + * is no conversion URL. + */ + struct GNUNET_HashCode h_conversion_url; + + /** + * Hash over the debit restrictions. + */ + struct GNUNET_HashCode h_debit_restrictions; + + /** + * Hash over the credit restrictions. + */ + struct GNUNET_HashCode h_credit_restrictions; }; GNUNET_NETWORK_STRUCT_END @@ -592,6 +701,9 @@ GNUNET_NETWORK_STRUCT_END void TALER_exchange_offline_wire_add_sign ( const char *payto_uri, + const char *conversion_url, + const json_t *debit_restrictions, + const json_t *credit_restrictions, struct GNUNET_TIME_Timestamp now, const struct TALER_MasterPrivateKeyP *master_priv, struct TALER_MasterSignatureP *master_sig) @@ -604,6 +716,14 @@ TALER_exchange_offline_wire_add_sign ( TALER_payto_hash (payto_uri, &kv.h_payto); + if (NULL != conversion_url) + GNUNET_CRYPTO_hash (conversion_url, + strlen (conversion_url) + 1, + &kv.h_conversion_url); + TALER_json_hash (debit_restrictions, + &kv.h_debit_restrictions); + TALER_json_hash (credit_restrictions, + &kv.h_credit_restrictions); GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv, &kv, &master_sig->eddsa_signature); @@ -613,6 +733,9 @@ TALER_exchange_offline_wire_add_sign ( enum GNUNET_GenericReturnValue TALER_exchange_offline_wire_add_verify ( const char *payto_uri, + const char *conversion_url, + const json_t *debit_restrictions, + const json_t *credit_restrictions, struct GNUNET_TIME_Timestamp sign_time, const struct TALER_MasterPublicKeyP *master_pub, const struct TALER_MasterSignatureP *master_sig) @@ -625,6 +748,14 @@ TALER_exchange_offline_wire_add_verify ( TALER_payto_hash (payto_uri, &aw.h_payto); + if (NULL != conversion_url) + GNUNET_CRYPTO_hash (conversion_url, + strlen (conversion_url) + 1, + &aw.h_conversion_url); + TALER_json_hash (debit_restrictions, + &aw.h_debit_restrictions); + TALER_json_hash (credit_restrictions, + &aw.h_credit_restrictions); return GNUNET_CRYPTO_eddsa_verify ( TALER_SIGNATURE_MASTER_ADD_WIRE, @@ -840,15 +971,6 @@ struct TALER_MasterGlobalFeePS struct GNUNET_TIME_RelativeNBO purse_timeout; /** - * How long does the exchange promise to keep funds - * an account for which the KYC has never happened - * after a purse was merged into an account? Basically, - * after this time funds in an account without KYC are - * forfeit. - */ - struct GNUNET_TIME_RelativeNBO kyc_timeout; - - /** * How long will the exchange preserve the account history? After an * account was deleted/closed, the exchange will retain the account history * for legal reasons until this time. @@ -878,27 +1000,25 @@ TALER_exchange_offline_global_fee_sign ( struct GNUNET_TIME_Timestamp end_time, const struct TALER_GlobalFeeSet *fees, struct GNUNET_TIME_Relative purse_timeout, - struct GNUNET_TIME_Relative kyc_timeout, struct GNUNET_TIME_Relative history_expiration, uint32_t purse_account_limit, const struct TALER_MasterPrivateKeyP *master_priv, struct TALER_MasterSignatureP *master_sig) { - struct TALER_MasterGlobalFeePS kv = { + struct TALER_MasterGlobalFeePS wf = { .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_GLOBAL_FEES), - .purpose.size = htonl (sizeof (kv)), + .purpose.size = htonl (sizeof (wf)), .start_date = GNUNET_TIME_timestamp_hton (start_time), .end_date = GNUNET_TIME_timestamp_hton (end_time), .purse_timeout = GNUNET_TIME_relative_hton (purse_timeout), - .kyc_timeout = GNUNET_TIME_relative_hton (kyc_timeout), .history_expiration = GNUNET_TIME_relative_hton (history_expiration), .purse_account_limit = htonl (purse_account_limit) }; - TALER_global_fee_set_hton (&kv.fees, + TALER_global_fee_set_hton (&wf.fees, fees); GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv, - &kv, + &wf, &master_sig->eddsa_signature); } @@ -909,7 +1029,6 @@ TALER_exchange_offline_global_fee_verify ( struct GNUNET_TIME_Timestamp end_time, const struct TALER_GlobalFeeSet *fees, struct GNUNET_TIME_Relative purse_timeout, - struct GNUNET_TIME_Relative kyc_timeout, struct GNUNET_TIME_Relative history_expiration, uint32_t purse_account_limit, const struct TALER_MasterPublicKeyP *master_pub, @@ -921,7 +1040,6 @@ TALER_exchange_offline_global_fee_verify ( .start_date = GNUNET_TIME_timestamp_hton (start_time), .end_date = GNUNET_TIME_timestamp_hton (end_time), .purse_timeout = GNUNET_TIME_relative_hton (purse_timeout), - .kyc_timeout = GNUNET_TIME_relative_hton (kyc_timeout), .history_expiration = GNUNET_TIME_relative_hton (history_expiration), .purse_account_limit = htonl (purse_account_limit) }; @@ -939,10 +1057,10 @@ TALER_exchange_offline_global_fee_verify ( GNUNET_NETWORK_STRUCT_BEGIN /** - * @brief Signature made by the exchange offline key over the - * configuration of an extension. + * @brief Signature made by the exchange offline key over the manifest of + * an extension. */ -struct TALER_MasterExtensionConfigurationPS +struct TALER_MasterExtensionManifestPS { /** * Purpose is #TALER_SIGNATURE_MASTER_EXTENSION. Signed @@ -951,24 +1069,24 @@ struct TALER_MasterExtensionConfigurationPS struct GNUNET_CRYPTO_EccSignaturePurpose purpose; /** - * Hash of the JSON object that represents the configuration of an extension. + * Hash of the JSON object that represents the manifests of extensions. */ - struct TALER_ExtensionConfigHashP h_config GNUNET_PACKED; + struct TALER_ExtensionManifestsHashP h_manifest GNUNET_PACKED; }; GNUNET_NETWORK_STRUCT_END 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_manifest, const struct TALER_MasterPrivateKeyP *master_priv, struct TALER_MasterSignatureP *master_sig) { - struct TALER_MasterExtensionConfigurationPS ec = { + struct TALER_MasterExtensionManifestPS ec = { .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION), .purpose.size = htonl (sizeof(ec)), - .h_config = *h_config + .h_manifest = *h_manifest }; GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv, &ec, @@ -977,16 +1095,16 @@ TALER_exchange_offline_extension_config_hash_sign ( 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 ) { - struct TALER_MasterExtensionConfigurationPS ec = { + struct TALER_MasterExtensionManifestPS ec = { .purpose.purpose = htonl (TALER_SIGNATURE_MASTER_EXTENSION), .purpose.size = htonl (sizeof(ec)), - .h_config = *h_config + .h_manifest = *h_manifest }; return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_EXTENSION, @@ -1015,6 +1133,22 @@ struct TALER_MasterWireDetailsPS */ struct TALER_PaytoHashP h_wire_details GNUNET_PACKED; + /** + * Hash over the conversion URL, all zeros if there + * is no conversion URL. + */ + struct GNUNET_HashCode h_conversion_url; + + /** + * Hash over the debit restrictions. + */ + struct GNUNET_HashCode h_debit_restrictions; + + /** + * Hash over the credit restrictions. + */ + struct GNUNET_HashCode h_credit_restrictions; + }; GNUNET_NETWORK_STRUCT_END @@ -1023,6 +1157,9 @@ GNUNET_NETWORK_STRUCT_END enum GNUNET_GenericReturnValue TALER_exchange_wire_signature_check ( const char *payto_uri, + const char *conversion_url, + const json_t *debit_restrictions, + const json_t *credit_restrictions, const struct TALER_MasterPublicKeyP *master_pub, const struct TALER_MasterSignatureP *master_sig) { @@ -1033,6 +1170,14 @@ TALER_exchange_wire_signature_check ( TALER_payto_hash (payto_uri, &wd.h_wire_details); + if (NULL != conversion_url) + GNUNET_CRYPTO_hash (conversion_url, + strlen (conversion_url) + 1, + &wd.h_conversion_url); + TALER_json_hash (debit_restrictions, + &wd.h_debit_restrictions); + TALER_json_hash (credit_restrictions, + &wd.h_credit_restrictions); return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_DETAILS, &wd, &master_sig->eddsa_signature, @@ -1043,6 +1188,9 @@ TALER_exchange_wire_signature_check ( void TALER_exchange_wire_signature_make ( const char *payto_uri, + const char *conversion_url, + const json_t *debit_restrictions, + const json_t *credit_restrictions, const struct TALER_MasterPrivateKeyP *master_priv, struct TALER_MasterSignatureP *master_sig) { @@ -1053,6 +1201,14 @@ TALER_exchange_wire_signature_make ( TALER_payto_hash (payto_uri, &wd.h_wire_details); + if (NULL != conversion_url) + GNUNET_CRYPTO_hash (conversion_url, + strlen (conversion_url) + 1, + &wd.h_conversion_url); + TALER_json_hash (debit_restrictions, + &wd.h_debit_restrictions); + TALER_json_hash (credit_restrictions, + &wd.h_credit_restrictions); GNUNET_CRYPTO_eddsa_sign (&master_priv->eddsa_priv, &wd, &master_sig->eddsa_signature); diff --git a/src/util/paths.conf b/src/util/paths.conf index c1d2194d8..f34ccb41e 100644 --- a/src/util/paths.conf +++ b/src/util/paths.conf @@ -17,13 +17,13 @@ TALER_HOME = ${TALER_TEST_HOME:-${HOME:-${USERPROFILE}}} # for how these should be used. # Persistent data storage -TALER_DATA_HOME = ${XDG_DATA_HOME:-$TALER_HOME/.local/share}/taler/ +TALER_DATA_HOME = ${XDG_DATA_HOME:-${TALER_HOME}/.local/share}/taler/ # Configuration files -TALER_CONFIG_HOME = ${XDG_CONFIG_HOME:-$TALER_HOME/.config}/taler/ +TALER_CONFIG_HOME = ${XDG_CONFIG_HOME:-${TALER_HOME}/.config}/taler/ # Cached data, no big deal if lost -TALER_CACHE_HOME = ${XDG_CACHE_HOME:-$TALER_HOME/.cache}/taler/ +TALER_CACHE_HOME = ${XDG_CACHE_HOME:-${TALER_HOME}/.cache}/taler/ # Runtime data (always lost on system boot) TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/taler-system-runtime/ diff --git a/src/util/payto.c b/src/util/payto.c index 81664b1df..6092b73fd 100644 --- a/src/util/payto.c +++ b/src/util/payto.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2019-2022 Taler Systems SA + Copyright (C) 2019-2024 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 @@ -101,7 +101,9 @@ TALER_payto_get_method (const char *payto_uri) char * TALER_xtalerbank_account_from_payto (const char *payto) { + const char *host; const char *beg; + const char *nxt; const char *end; if (0 != strncasecmp (payto, @@ -111,23 +113,27 @@ TALER_xtalerbank_account_from_payto (const char *payto) GNUNET_break_op (0); return NULL; } - beg = strchr (&payto[strlen (PAYTO "x-taler-bank/")], + host = &payto[strlen (PAYTO "x-taler-bank/")]; + beg = strchr (host, '/'); if (NULL == beg) { GNUNET_break_op (0); return NULL; } - beg++; /* now points to $ACCOUNT */ + beg++; /* now points to $ACCOUNT or $PATH */ + nxt = strchr (beg, + '/'); end = strchr (beg, '?'); if (NULL == end) + end = &beg[strlen (beg)]; + while ( (NULL != nxt) && + (end - nxt > 0) ) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Invalid payto URI `%s'\n", - payto); - GNUNET_break_op (0); - return GNUNET_strdup (beg); /* optional part is missing */ + beg = nxt + 1; + nxt = strchr (beg, + '/'); } return GNUNET_strndup (beg, end - beg); @@ -155,7 +161,6 @@ validate_payto_iban (const char *account_url) IBAN_PREFIX, strlen (IBAN_PREFIX))) return NULL; /* not an IBAN */ - iban = strrchr (account_url, '/') + 1; #undef IBAN_PREFIX q = strchr (iban, @@ -189,6 +194,138 @@ validate_payto_iban (const char *account_url) } +/** + * Validate payto://x-taler-bank/ account URL (only account information, + * wire subject and amount are ignored). + * + * @param account_url payto URL to parse + * @return NULL on success, otherwise an error message + * to be freed by the caller + */ +static char * +validate_payto_xtalerbank (const char *account_url) +{ + const char *user; + const char *nxt; + const char *beg; + const char *end; + const char *host; + bool dot_ok; + bool post_colon; + bool port_ok; + +#define XTALERBANK_PREFIX PAYTO "x-taler-bank/" + if (0 != strncasecmp (account_url, + XTALERBANK_PREFIX, + strlen (XTALERBANK_PREFIX))) + return NULL; /* not an IBAN */ + host = &account_url[strlen (XTALERBANK_PREFIX)]; +#undef XTALERBANK_PREFIX + beg = strchr (host, + '/'); + if (NULL == beg) + { + return GNUNET_strdup ("account name missing"); + } + beg++; /* now points to $ACCOUNT or $PATH */ + nxt = strchr (beg, + '/'); + end = strchr (beg, + '?'); + if (NULL == end) + { + return GNUNET_strdup ("'receiver-name' parameter missing"); + } + while ( (NULL != nxt) && + (end - nxt > 0) ) + { + beg = nxt + 1; + nxt = strchr (beg, + '/'); + } + user = beg; + if (user == host + 1) + { + return GNUNET_strdup ("domain name missing"); + } + if ('-' == host[0]) + return GNUNET_strdup ("invalid character '-' at start of domain name"); + dot_ok = false; + post_colon = false; + port_ok = false; + while (host != user) + { + char c = host[0]; + + if ('/' == c) + { + /* path started, do not care about characters + in the path */ + break; + } + if (':' == c) + { + post_colon = true; + host++; + continue; + } + if (post_colon) + { + if (! ( ('0' <= c) && ('9' >= c) ) ) + { + char *err; + + GNUNET_asprintf (&err, + "invalid character '%c' in port", + c); + return err; + } + port_ok = true; + } + else + { + if ('.' == c) + { + if (! dot_ok) + return GNUNET_strdup ("invalid domain name (misplaced '.')"); + dot_ok = false; + } + else + { + if (! ( ('-' == c) || + ( ('0' <= c) && ('9' >= c) ) || + ( ('a' <= c) && ('z' >= c) ) || + ( ('A' <= c) && ('Z' >= c) ) ) ) + { + char *err; + + GNUNET_asprintf (&err, + "invalid character '%c' in domain name", + c); + return err; + } + dot_ok = true; + } + } + host++; + } + if (post_colon && (! port_ok) ) + { + return GNUNET_strdup ("port missing after ':'"); + } + { + char *target; + + target = payto_get_key (account_url, + "receiver-name="); + if (NULL == target) + return GNUNET_strdup ("'receiver-name' parameter missing"); + GNUNET_free (target); + } + return NULL; +} + + char * TALER_payto_validate (const char *payto_uri) { @@ -205,7 +342,7 @@ TALER_payto_validate (const char *payto_uri) /* This is more strict than RFC 8905, alas we do not need to support messages/instructions/etc., and it is generally better to start with a narrow whitelist; we can be more permissive later ...*/ #define ALLOWED_CHARACTERS \ - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/:&?-.,=+" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/:&?-.,=+%~" if (NULL == strchr (ALLOWED_CHARACTERS, (int) payto_uri[i])) { @@ -229,6 +366,8 @@ TALER_payto_validate (const char *payto_uri) if (NULL != (ret = validate_payto_iban (payto_uri))) return ret; /* got a definitive answer */ + if (NULL != (ret = validate_payto_xtalerbank (payto_uri))) + return ret; /* got a definitive answer */ /* Insert other bank account validation methods here later! */ @@ -256,6 +395,242 @@ TALER_payto_get_receiver_name (const char *payto) } +/** + * Normalize "payto://x-taler-bank/$HOSTNAME/[$PATH/]$USERNAME" + * URI in @a input. + * + * Converts to lower-case, except for [$PATH/]$USERNAME which + * is case-sensitive. + * + * @param len number of bytes in @a input + * @param input input URL + * @return NULL on error, otherwise 0-terminated canonicalized URI. + */ +static char * +normalize_payto_x_taler_bank (size_t len, + const char input[static len]) +{ + char *res = GNUNET_malloc (len + 1); + unsigned int sc = 0; + + for (unsigned int i = 0; i<len; i++) + { + char c = input[i]; + + if ('/' == c) + sc++; + if (sc < 4) + res[i] = (char) tolower ((int) c); + else + res[i] = c; + } + return res; +} + + +/** + * Normalize "payto://iban[/$BIC]/$IBAN" + * URI in @a input. + * + * Removes $BIC (if present) and converts $IBAN to upper-case and prefix to + * lower-case. + * + * @param len number of bytes in @a input + * @param input input URL + * @return NULL on error, otherwise 0-terminated canonicalized URI. + */ +static char * +normalize_payto_iban (size_t len, + const char input[static len]) +{ + char *res; + size_t pos = 0; + unsigned int sc = 0; + bool have_bic; + + for (unsigned int i = 0; i<len; i++) + if ('/' == input[i]) + sc++; + if ( (sc > 4) || + (sc < 3) ) + { + GNUNET_break (0); + return NULL; + } + have_bic = (4 == sc); + res = GNUNET_malloc (len + 1); + sc = 0; + for (unsigned int i = 0; i<len; i++) + { + char c = input[i]; + + if ('/' == c) + sc++; + switch (sc) + { + case 0: /* payto: */ + case 1: /* / */ + case 2: /* /iban */ + res[pos++] = (char) tolower ((int) c); + break; + case 3: /* /$BIC or /$IBAN */ + if (have_bic) + continue; + res[pos++] = (char) toupper ((int) c); + break; + case 4: /* /$IBAN */ + res[pos++] = (char) toupper ((int) c); + break; + } + } + GNUNET_assert (pos <= len); + return res; +} + + +/** + * Normalize "payto://upi/$EMAIL" + * URI in @a input. + * + * Converts to lower-case. + * + * @param len number of bytes in @a input + * @param input input URL + * @return NULL on error, otherwise 0-terminated canonicalized URI. + */ +static char * +normalize_payto_upi (size_t len, + const char input[static len]) +{ + char *res = GNUNET_malloc (len + 1); + + for (unsigned int i = 0; i<len; i++) + { + char c = input[i]; + + res[i] = (char) tolower ((int) c); + } + return res; +} + + +/** + * Normalize "payto://bitcoin/$ADDRESS" + * URI in @a input. + * + * Converts to lower-case, except for $ADDRESS which + * is case-sensitive. + * + * @param len number of bytes in @a input + * @param input input URL + * @return NULL on error, otherwise 0-terminated canonicalized URI. + */ +static char * +normalize_payto_bitcoin (size_t len, + const char input[static len]) +{ + char *res = GNUNET_malloc (len + 1); + unsigned int sc = 0; + + for (unsigned int i = 0; i<len; i++) + { + char c = input[i]; + + if ('/' == c) + sc++; + if (sc < 3) + res[i] = (char) tolower ((int) c); + else + res[i] = c; + } + return res; +} + + +/** + * Normalize "payto://ilp/$NAME" + * URI in @a input. + * + * Converts to lower-case. + * + * @param len number of bytes in @a input + * @param input input URL + * @return NULL on error, otherwise 0-terminated canonicalized URI. + */ +static char * +normalize_payto_ilp (size_t len, + const char input[static len]) +{ + char *res = GNUNET_malloc (len + 1); + + for (unsigned int i = 0; i<len; i++) + { + char c = input[i]; + + res[i] = (char) tolower ((int) c); + } + return res; +} + + +char * +TALER_payto_normalize (const char *input) +{ + char *method; + const char *end; + char *ret; + + { + char *err; + + err = TALER_payto_validate (input); + if (NULL != err) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Malformed payto://-URI `%s': %s\n", + input, + err); + GNUNET_free (err); + return NULL; + } + } + method = TALER_payto_get_method (input); + if (NULL == method) + { + GNUNET_break (0); + return NULL; + } + end = strchr (input, '?'); + if (NULL == end) + end = &input[strlen (input)]; + if (0 == strcasecmp (method, + "x-taler-bank")) + ret = normalize_payto_x_taler_bank (end - input, + input); + else if (0 == strcasecmp (method, + "iban")) + ret = normalize_payto_iban (end - input, + input); + else if (0 == strcasecmp (method, + "upi")) + ret = normalize_payto_upi (end - input, + input); + else if (0 == strcasecmp (method, + "bitcoin")) + ret = normalize_payto_bitcoin (end - input, + input); + else if (0 == strcasecmp (method, + "ilp")) + ret = normalize_payto_ilp (end - input, + input); + else + ret = GNUNET_strndup (input, + end - input); + GNUNET_free (method); + return ret; +} + + void TALER_payto_hash (const char *payto, struct TALER_PaytoHashP *h_payto) @@ -267,9 +642,9 @@ TALER_payto_hash (const char *payto, &sha512); GNUNET_static_assert (sizeof (sha512) > sizeof (*h_payto)); /* truncate */ - memcpy (h_payto, - &sha512, - sizeof (*h_payto)); + GNUNET_memcpy (h_payto, + &sha512, + sizeof (*h_payto)); } diff --git a/src/util/taler-config.in b/src/util/taler-config.in index 07f6401d6..3399aec10 100644 --- a/src/util/taler-config.in +++ b/src/util/taler-config.in @@ -7,7 +7,7 @@ if ! type gnunet-config >/dev/null; then exit 1 fi -GC=`which gnunet-config` -SO=`ls %libdir%/libtalerutil.so.* | sort -n | tail -n1` +GC=$(which gnunet-config) +SO=$(ls %libdir%/libtalerutil.so.* | sort -n | tail -n1) export LD_PRELOAD=${LD_PRELOAD:-}:${SO} exec gnunet-config "$@" diff --git a/src/util/taler-exchange-secmod-cs.c b/src/util/taler-exchange-secmod-cs.c index 01b74868e..3e9ba1558 100644 --- a/src/util/taler-exchange-secmod-cs.c +++ b/src/util/taler-exchange-secmod-cs.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2021 Taler Systems SA + Copyright (C) 2014-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 @@ -161,6 +161,164 @@ struct Denomination /** + * A semaphore. + */ +struct Semaphore +{ + /** + * Mutex for the semaphore. + */ + pthread_mutex_t mutex; + + /** + * Condition variable for the semaphore. + */ + pthread_cond_t cv; + + /** + * Counter of the semaphore. + */ + unsigned int ctr; +}; + + +/** + * Job in a batch sign request. + */ +struct BatchJob; + +/** + * Handle for a thread that does work in batch signing. + */ +struct Worker +{ + /** + * Kept in a DLL. + */ + struct Worker *prev; + + /** + * Kept in a DLL. + */ + struct Worker *next; + + /** + * Job this worker should do next. + */ + struct BatchJob *job; + + /** + * Semaphore to signal the worker that a job is available. + */ + struct Semaphore sem; + + /** + * Handle for this thread. + */ + pthread_t pt; + + /** + * Set to true if the worker should terminate. + */ + bool do_shutdown; +}; + + +/** + * Job in a batch sign request. + */ +struct BatchJob +{ + + /** + * Thread doing the work. + */ + struct Worker *worker; + + /** + * Semaphore to signal that the job is finished. + */ + struct Semaphore sem; + + /** + * Computation status. + */ + enum TALER_ErrorCode ec; + + /** + * Which type of request is this? + */ + enum { TYPE_SIGN, TYPE_RDERIVE } type; + + /** + * Details depending on @e type. + */ + union + { + + /** + * Details if @e type is TYPE_SIGN. + */ + struct + { + /** + * Request we are working on. + */ + const struct TALER_CRYPTO_CsSignRequestMessage *sr; + + /** + * Result with the signature. + */ + struct GNUNET_CRYPTO_CsBlindSignature cs_answer; + } sign; + + /** + * Details if type is TYPE_RDERIVE. + */ + struct + { + /** + * Request we are answering. + */ + const struct TALER_CRYPTO_CsRDeriveRequest *rdr; + + /** + * Pair of points to return. + */ + struct GNUNET_CRYPTO_CSPublicRPairP rpairp; + + } rderive; + + } details; + +}; + +/** + * Head of DLL of workers ready for more work. + */ +static struct Worker *worker_head; + +/** + * Tail of DLL of workers ready for more work. + */ +static struct Worker *worker_tail; + +/** + * Lock for manipulating the worker DLL. + */ +static pthread_mutex_t worker_lock; + +/** + * Total number of workers that were started. + */ +static unsigned int workers; + +/** + * Semaphore used to grab a worker. + */ +static struct Semaphore worker_sem; + +/** * Return value from main(). */ static int global_ret; @@ -183,6 +341,13 @@ static struct GNUNET_TIME_Timestamp now_tmp; static char *keydir; /** + * Name of the configuration section prefix to use. Usually either "taler-exchange" or + * "donau". The actual configuration section will then be + * "$SECTION-secmod-cs". + */ +static char *section; + +/** * How much should coin creation (@e duration_withdraw) duration overlap * with the next denomination? Basically, the starting time of two * denominations is always @e duration_withdraw - #overlap_duration apart. @@ -225,6 +390,12 @@ static pthread_mutex_t keys_lock; */ static uint64_t key_gen; +/** + * Number of workers to launch. Note that connections to + * exchanges are NOT workers. + */ +static unsigned int max_workers = 16; + /** * Generate the announcement message for @a dk. @@ -259,14 +430,139 @@ generate_response (struct DenominationKey *dk) &an->secm_sig); an->secm_pub = TES_smpub; p = (void *) &an[1]; - memcpy (p, - denom->section, - nlen); + GNUNET_memcpy (p, + denom->section, + nlen); dk->an = an; } /** + * Do the actual signing work. + * + * @param h_cs hash of key to sign with + * @param planchet message to sign + * @param for_melt true if for melting + * @param[out] cs_sigp set to the CS signature + * @return #TALER_EC_NONE on success + */ +static enum TALER_ErrorCode +do_sign (const struct TALER_CsPubHashP *h_cs, + const struct GNUNET_CRYPTO_CsBlindedMessage *planchet, + bool for_melt, + struct GNUNET_CRYPTO_CsBlindSignature *cs_sigp) +{ + struct GNUNET_CRYPTO_CsRSecret r[2]; + struct DenominationKey *dk; + + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); + dk = GNUNET_CONTAINER_multihashmap_get (keys, + &h_cs->hash); + if (NULL == dk) + { + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Signing request failed, denomination key %s unknown\n", + GNUNET_h2s (&h_cs->hash)); + return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; + } + if (GNUNET_TIME_absolute_is_future (dk->anchor.abs_time)) + { + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Signing request failed, denomination key %s is not yet valid\n", + GNUNET_h2s (&h_cs->hash)); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received request to sign over bytes with key %s\n", + GNUNET_h2s (&h_cs->hash)); + GNUNET_assert (dk->rc < UINT_MAX); + dk->rc++; + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + GNUNET_CRYPTO_cs_r_derive (&planchet->nonce, + for_melt ? "rm" : "rw", + &dk->denom_priv, + r); + GNUNET_CRYPTO_cs_sign_derive (&dk->denom_priv, + r, + planchet, + cs_sigp); + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); + GNUNET_assert (dk->rc > 0); + dk->rc--; + GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); + return TALER_EC_NONE; +} + + +/** + * Generate error response that signing failed. + * + * @param client client to send response to + * @param ec error code to include + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +fail_sign (struct TES_Client *client, + enum TALER_ErrorCode ec) +{ + struct TALER_CRYPTO_SignFailure sf = { + .header.size = htons (sizeof (sf)), + .header.type = htons (TALER_HELPER_CS_MT_RES_SIGN_FAILURE), + .ec = htonl (ec) + }; + + return TES_transmit (client->csock, + &sf.header); +} + + +/** + * Generate error response that deriving failed. + * + * @param client client to send response to + * @param ec error code to include + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +fail_derive (struct TES_Client *client, + enum TALER_ErrorCode ec) +{ + struct TALER_CRYPTO_RDeriveFailure sf = { + .header.size = htons (sizeof (sf)), + .header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE), + .ec = htonl (ec) + }; + + return TES_transmit (client->csock, + &sf.header); +} + + +/** + * Generate signature response. + * + * @param client client to send response to + * @param cs_answer signature to send + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +send_signature (struct TES_Client *client, + const struct GNUNET_CRYPTO_CsBlindSignature *cs_answer) +{ + struct TALER_CRYPTO_SignResponse sres; + + sres.header.size = htons (sizeof (sres)); + sres.header.type = htons (TALER_HELPER_CS_MT_RES_SIGNATURE); + sres.b = htonl (cs_answer->b); + sres.cs_answer = cs_answer->s_scalar; + return TES_transmit (client->csock, + &sres.header); +} + + +/** * Handle @a client request @a sr to create signature. Create the * signature using the respective key and return the result to * the client. @@ -277,108 +573,510 @@ generate_response (struct DenominationKey *dk) */ static enum GNUNET_GenericReturnValue handle_sign_request (struct TES_Client *client, - const struct TALER_CRYPTO_CsSignRequest *sr) + const struct TALER_CRYPTO_CsSignRequestMessage *sr) { - struct DenominationKey *dk; - struct GNUNET_CRYPTO_CsRSecret r[2]; - struct TALER_BlindedDenominationCsSignAnswer cs_answer; + struct GNUNET_CRYPTO_CsBlindSignature cs_answer; struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - bool for_melt; + enum TALER_ErrorCode ec; + enum GNUNET_GenericReturnValue ret; + + ec = do_sign (&sr->h_cs, + &sr->message, + (0 != ntohl (sr->for_melt)), + &cs_answer); + if (TALER_EC_NONE != ec) + { + return fail_sign (client, + ec); + } + ret = send_signature (client, + &cs_answer); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Sent CS signature after %s\n", + GNUNET_TIME_relative2s ( + GNUNET_TIME_absolute_get_duration (now), + GNUNET_YES)); + return ret; +} + + +/** + * Do the actual deriving work. + * + * @param h_cs key to sign with + * @param nonce nonce to derive from + * @param for_melt true if for melting + * @param[out] rpairp set to the derived values + * @return #TALER_EC_NONE on success + */ +static enum TALER_ErrorCode +do_derive (const struct TALER_CsPubHashP *h_cs, + const struct GNUNET_CRYPTO_CsSessionNonce *nonce, + bool for_melt, + struct GNUNET_CRYPTO_CSPublicRPairP *rpairp) +{ + struct DenominationKey *dk; + struct GNUNET_CRYPTO_CSPrivateRPairP r_priv; GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); dk = GNUNET_CONTAINER_multihashmap_get (keys, - &sr->h_cs.hash); + &h_cs->hash); if (NULL == dk) { - struct TALER_CRYPTO_SignFailure sf = { - .header.size = htons (sizeof (sr)), - .header.type = htons (TALER_HELPER_CS_MT_RES_SIGN_FAILURE), - .ec = htonl (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN) - }; - GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Signing request failed, denomination key %s unknown\n", - GNUNET_h2s (&sr->h_cs.hash)); - return TES_transmit (client->csock, - &sf.header); + "R Derive request failed, denomination key %s unknown\n", + GNUNET_h2s (&h_cs->hash)); + return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; } if (GNUNET_TIME_absolute_is_future (dk->anchor.abs_time)) { - /* it is too early */ - struct TALER_CRYPTO_SignFailure sf = { - .header.size = htons (sizeof (sr)), - .header.type = htons (TALER_HELPER_CS_MT_RES_SIGN_FAILURE), - .ec = htonl (TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY) - }; - GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Signing request failed, denomination key %s is not yet valid\n", - GNUNET_h2s (&sr->h_cs.hash)); - return TES_transmit (client->csock, - &sf.header); + "R Derive request failed, denomination key %s is not yet valid\n", + GNUNET_h2s (&h_cs->hash)); + return TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY; } - for_melt = (0 != ntohl (sr->for_melt)); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Received request to sign over bytes with key %s\n", - GNUNET_h2s (&sr->h_cs.hash)); + "Received request to derive R with key %s\n", + GNUNET_h2s (&h_cs->hash)); GNUNET_assert (dk->rc < UINT_MAX); dk->rc++; GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); - GNUNET_CRYPTO_cs_r_derive (&sr->planchet.nonce.nonce, + GNUNET_CRYPTO_cs_r_derive (nonce, for_melt ? "rm" : "rw", &dk->denom_priv, - r); - cs_answer.b = GNUNET_CRYPTO_cs_sign_derive (&dk->denom_priv, - r, - sr->planchet.c, - &sr->planchet.nonce.nonce, - &cs_answer.s_scalar); - + r_priv.r); GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); GNUNET_assert (dk->rc > 0); dk->rc--; GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); - // if (NULL == cs_answer) - // { - // struct TALER_CRYPTO_SignFailure sf = { - // .header.size = htons (sizeof (sf)), - // .header.type = htons (TALER_HELPER_CS_MT_RES_SIGN_FAILURE), - // .ec = htonl (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE) - // }; - - // GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - // "Signing request failed, worker failed to produce signature\n"); - // return TES_transmit (client->csock, - // &sf.header); - // } - - { - struct TALER_CRYPTO_SignResponse *sr; - size_t tsize; - enum GNUNET_GenericReturnValue ret; - - tsize = sizeof (*sr) + sizeof(cs_answer); - GNUNET_assert (tsize < UINT16_MAX); - sr = GNUNET_malloc (tsize); - sr->header.size = htons (tsize); - sr->header.type = htons (TALER_HELPER_CS_MT_RES_SIGNATURE); - sr->cs_answer = cs_answer; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sending CS signature after %s\n", - GNUNET_TIME_relative2s ( - GNUNET_TIME_absolute_get_duration (now), - GNUNET_YES)); - ret = TES_transmit (client->csock, - &sr->header); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sent CS signature after %s\n", - GNUNET_TIME_relative2s ( - GNUNET_TIME_absolute_get_duration (now), - GNUNET_YES)); - GNUNET_free (sr); - return ret; + GNUNET_CRYPTO_cs_r_get_public (&r_priv.r[0], + &rpairp->r_pub[0]); + GNUNET_CRYPTO_cs_r_get_public (&r_priv.r[1], + &rpairp->r_pub[1]); + return TALER_EC_NONE; +} + + +/** + * Generate derivation response. + * + * @param client client to send response to + * @param r_pub public point value pair to send + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +send_derivation (struct TES_Client *client, + const struct GNUNET_CRYPTO_CSPublicRPairP *r_pub) +{ + struct TALER_CRYPTO_RDeriveResponse rdr = { + .header.size = htons (sizeof (rdr)), + .header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE), + .r_pub = *r_pub + }; + + return TES_transmit (client->csock, + &rdr.header); +} + + +/** + * Initialize a semaphore @a sem with a value of @a val. + * + * @param[out] sem semaphore to initialize + * @param val initial value of the semaphore + */ +static void +sem_init (struct Semaphore *sem, + unsigned int val) +{ + GNUNET_assert (0 == + pthread_mutex_init (&sem->mutex, + NULL)); + GNUNET_assert (0 == + pthread_cond_init (&sem->cv, + NULL)); + sem->ctr = val; +} + + +/** + * Decrement semaphore, blocks until this is possible. + * + * @param[in,out] sem semaphore to decrement + */ +static void +sem_down (struct Semaphore *sem) +{ + GNUNET_assert (0 == pthread_mutex_lock (&sem->mutex)); + while (0 == sem->ctr) + { + pthread_cond_wait (&sem->cv, + &sem->mutex); + } + sem->ctr--; + GNUNET_assert (0 == pthread_mutex_unlock (&sem->mutex)); +} + + +/** + * Increment semaphore, blocks until this is possible. + * + * @param[in,out] sem semaphore to decrement + */ +static void +sem_up (struct Semaphore *sem) +{ + GNUNET_assert (0 == pthread_mutex_lock (&sem->mutex)); + sem->ctr++; + GNUNET_assert (0 == pthread_mutex_unlock (&sem->mutex)); + pthread_cond_signal (&sem->cv); +} + + +/** + * Release resources used by @a sem. + * + * @param[in] sem semaphore to release (except the memory itself) + */ +static void +sem_done (struct Semaphore *sem) +{ + GNUNET_break (0 == pthread_cond_destroy (&sem->cv)); + GNUNET_break (0 == pthread_mutex_destroy (&sem->mutex)); +} + + +/** + * Main logic of a worker thread. Grabs work, does it, + * grabs more work. + * + * @param cls a `struct Worker *` + * @returns cls + */ +static void * +worker (void *cls) +{ + struct Worker *w = cls; + + while (true) + { + GNUNET_assert (0 == pthread_mutex_lock (&worker_lock)); + GNUNET_CONTAINER_DLL_insert (worker_head, + worker_tail, + w); + GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock)); + sem_up (&worker_sem); + sem_down (&w->sem); + if (w->do_shutdown) + break; + { + struct BatchJob *bj = w->job; + + switch (bj->type) + { + case TYPE_SIGN: + { + const struct TALER_CRYPTO_CsSignRequestMessage *sr + = bj->details.sign.sr; + + bj->ec = do_sign (&sr->h_cs, + &sr->message, + (0 != ntohl (sr->for_melt)), + &bj->details.sign.cs_answer); + break; + } + case TYPE_RDERIVE: + { + const struct TALER_CRYPTO_CsRDeriveRequest *rdr + = bj->details.rderive.rdr; + bj->ec = do_derive (&rdr->h_cs, + &rdr->nonce, + (0 != ntohl (rdr->for_melt)), + &bj->details.rderive.rpairp); + break; + } + } + sem_up (&bj->sem); + w->job = NULL; + } + } + return w; +} + + +/** + * Start batch job @a bj to sign @a sr. + * + * @param sr signature request to answer + * @param[out] bj job data structure + */ +static void +start_sign_job (const struct TALER_CRYPTO_CsSignRequestMessage *sr, + struct BatchJob *bj) +{ + sem_init (&bj->sem, + 0); + bj->type = TYPE_SIGN; + bj->details.sign.sr = sr; + sem_down (&worker_sem); + GNUNET_assert (0 == pthread_mutex_lock (&worker_lock)); + bj->worker = worker_head; + GNUNET_CONTAINER_DLL_remove (worker_head, + worker_tail, + bj->worker); + GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock)); + bj->worker->job = bj; + sem_up (&bj->worker->sem); +} + + +/** + * Start batch job @a bj to derive @a rdr. + * + * @param rdr derivation request to answer + * @param[out] bj job data structure + */ +static void +start_derive_job (const struct TALER_CRYPTO_CsRDeriveRequest *rdr, + struct BatchJob *bj) +{ + sem_init (&bj->sem, + 0); + bj->type = TYPE_RDERIVE; + bj->details.rderive.rdr = rdr; + sem_down (&worker_sem); + GNUNET_assert (0 == pthread_mutex_lock (&worker_lock)); + bj->worker = worker_head; + GNUNET_CONTAINER_DLL_remove (worker_head, + worker_tail, + bj->worker); + GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock)); + bj->worker->job = bj; + sem_up (&bj->worker->sem); +} + + +/** + * Finish a job @a bj for a @a client. + * + * @param client who made the request + * @param[in,out] bj job to finish + */ +static void +finish_job (struct TES_Client *client, + struct BatchJob *bj) +{ + sem_down (&bj->sem); + sem_done (&bj->sem); + switch (bj->type) + { + case TYPE_SIGN: + if (TALER_EC_NONE != bj->ec) + { + fail_sign (client, + bj->ec); + return; + } + send_signature (client, + &bj->details.sign.cs_answer); + break; + case TYPE_RDERIVE: + if (TALER_EC_NONE != bj->ec) + { + fail_derive (client, + bj->ec); + return; + } + send_derivation (client, + &bj->details.rderive.rpairp); + break; + } +} + + +/** + * Handle @a client request @a sr to create a batch of signature. Creates the + * signatures using the respective key and return the results to the client. + * + * @param client the client making the request + * @param bsr the request details + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +handle_batch_sign_request (struct TES_Client *client, + const struct TALER_CRYPTO_BatchSignRequest *bsr) +{ + uint32_t bs = ntohl (bsr->batch_size); + uint16_t size = ntohs (bsr->header.size) - sizeof (*bsr); + const void *off = (const void *) &bsr[1]; + unsigned int idx = 0; + struct BatchJob jobs[GNUNET_NZL (bs)]; + bool failure = false; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Handling batch sign request of size %u\n", + (unsigned int) bs); + if (bs > TALER_MAX_FRESH_COINS) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + while ( (bs > 0) && + (size >= sizeof (struct TALER_CRYPTO_CsSignRequestMessage)) ) + { + const struct TALER_CRYPTO_CsSignRequestMessage *sr = off; + uint16_t s = ntohs (sr->header.size); + + if (s > size) + { + failure = true; + bs = idx; + break; + } + start_sign_job (sr, + &jobs[idx++]); + off += s; + size -= s; + } + GNUNET_break_op (0 == size); + bs = GNUNET_MIN (bs, + idx); + for (unsigned int i = 0; i<bs; i++) + finish_job (client, + &jobs[i]); + if (failure) + { + struct TALER_CRYPTO_SignFailure sf = { + .header.size = htons (sizeof (sf)), + .header.type = htons (TALER_HELPER_CS_MT_RES_BATCH_SIGN_FAILURE), + .ec = htonl (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE) + }; + + GNUNET_break (0); + return TES_transmit (client->csock, + &sf.header); + } + return GNUNET_OK; +} + + +/** + * Handle @a client request @a sr to create a batch of derivations. Creates the + * derivations using the respective key and return the results to the client. + * + * @param client the client making the request + * @param bdr the request details + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +handle_batch_derive_request (struct TES_Client *client, + const struct TALER_CRYPTO_BatchDeriveRequest *bdr) +{ + uint32_t bs = ntohl (bdr->batch_size); + uint16_t size = ntohs (bdr->header.size) - sizeof (*bdr); + const void *off = (const void *) &bdr[1]; + unsigned int idx = 0; + struct BatchJob jobs[bs]; + bool failure = false; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Handling batch derivation request of size %u\n", + (unsigned int) bs); + if (bs > TALER_MAX_FRESH_COINS) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + while ( (bs > 0) && + (size >= sizeof (struct TALER_CRYPTO_CsRDeriveRequest)) ) + { + const struct TALER_CRYPTO_CsRDeriveRequest *rdr = off; + uint16_t s = ntohs (rdr->header.size); + + if ( (s > size) || + (s != sizeof (*rdr)) ) + { + failure = true; + bs = idx; + break; + } + start_derive_job (rdr, + &jobs[idx++]); + off += s; + size -= s; + } + GNUNET_break_op (0 == size); + bs = GNUNET_MIN (bs, + idx); + for (unsigned int i = 0; i<bs; i++) + finish_job (client, + &jobs[i]); + if (failure) + { + GNUNET_break (0); + return fail_derive (client, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE); + } + return GNUNET_OK; +} + + +/** + * Start worker thread for batch processing. + * + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +start_worker (void) +{ + struct Worker *w; + + w = GNUNET_new (struct Worker); + sem_init (&w->sem, + 0); + if (0 != pthread_create (&w->pt, + NULL, + &worker, + w)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "pthread_create"); + GNUNET_free (w); + return GNUNET_SYSERR; + } + workers++; + return GNUNET_OK; +} + + +/** + * Stop all worker threads. + */ +static void +stop_workers (void) +{ + while (workers > 0) + { + struct Worker *w; + void *result; + + sem_down (&worker_sem); + GNUNET_assert (0 == pthread_mutex_lock (&worker_lock)); + w = worker_head; + GNUNET_CONTAINER_DLL_remove (worker_head, + worker_tail, + w); + GNUNET_assert (0 == pthread_mutex_unlock (&worker_lock)); + w->do_shutdown = true; + sem_up (&w->sem); + pthread_join (w->pt, + &result); + GNUNET_assert (result == w); + sem_done (&w->sem); + GNUNET_free (w); + workers--; } } @@ -401,8 +1099,9 @@ setup_key (struct DenominationKey *dk, GNUNET_CRYPTO_cs_private_key_generate (&priv); GNUNET_CRYPTO_cs_private_key_get_public (&priv, &pub); - TALER_cs_pub_hash (&pub, - &dk->h_cs); + GNUNET_CRYPTO_hash (&pub, + sizeof (pub), + &dk->h_cs.hash); GNUNET_asprintf (&dk->filename, "%s/%s/%llu", keydir, @@ -550,88 +1249,29 @@ static enum GNUNET_GenericReturnValue handle_r_derive_request (struct TES_Client *client, const struct TALER_CRYPTO_CsRDeriveRequest *rdr) { - struct DenominationKey *dk; - struct TALER_DenominationCSPrivateRPairP r_priv; - struct TALER_DenominationCSPublicRPairP r_pub; + struct GNUNET_CRYPTO_CSPublicRPairP r_pub; struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - bool for_melt; + enum TALER_ErrorCode ec; + enum GNUNET_GenericReturnValue ret; - GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); - dk = GNUNET_CONTAINER_multihashmap_get (keys, - &rdr->h_cs.hash); - if (NULL == dk) + ec = do_derive (&rdr->h_cs, + &rdr->nonce, + (0 != ntohl (rdr->for_melt)), + &r_pub); + if (TALER_EC_NONE != ec) { - struct TALER_CRYPTO_RDeriveFailure rdf = { - .header.size = htons (sizeof (rdr)), - .header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE), - .ec = htonl (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN) - }; - - GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "R Derive request failed, denomination key %s unknown\n", - GNUNET_h2s (&rdr->h_cs.hash)); - return TES_transmit (client->csock, - &rdf.header); + return fail_derive (client, + ec); } - if (GNUNET_TIME_absolute_is_future (dk->anchor.abs_time)) - { - /* it is too early */ - struct TALER_CRYPTO_RDeriveFailure rdf = { - .header.size = htons (sizeof (rdr)), - .header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE), - .ec = htonl (TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY) - }; - GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "R Derive request failed, denomination key %s is not yet valid\n", - GNUNET_h2s (&rdr->h_cs.hash)); - return TES_transmit (client->csock, - &rdf.header); - } - for_melt = (0 != ntohl (rdr->for_melt)); + ret = send_derivation (client, + &r_pub); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Received request to derive R with key %s\n", - GNUNET_h2s (&rdr->h_cs.hash)); - GNUNET_assert (dk->rc < UINT_MAX); - dk->rc++; - GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); - GNUNET_CRYPTO_cs_r_derive (&rdr->nonce.nonce, - for_melt ? "rm" : "rw", - &dk->denom_priv, - r_priv.r); - GNUNET_CRYPTO_cs_r_get_public (&r_priv.r[0], - &r_pub.r_pub[0]); - GNUNET_CRYPTO_cs_r_get_public (&r_priv.r[1], - &r_pub.r_pub[1]); - GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); - GNUNET_assert (dk->rc > 0); - dk->rc--; - GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); - - { - struct TALER_CRYPTO_RDeriveResponse rdr = { - .header.size = htons (sizeof (struct TALER_CRYPTO_RDeriveResponse)), - .header.type = htons (TALER_HELPER_CS_MT_RES_RDERIVE), - .r_pub = r_pub - }; - enum GNUNET_GenericReturnValue ret; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sending CS Derived R after %s\n", - GNUNET_TIME_relative2s ( - GNUNET_TIME_absolute_get_duration (now), - GNUNET_YES)); - ret = TES_transmit (client->csock, - &rdr.header); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Sent CS Derived R after %s\n", - GNUNET_TIME_relative2s ( - GNUNET_TIME_absolute_get_duration (now), - GNUNET_YES)); - return ret; - } + "Sent CS Derived R after %s\n", + GNUNET_TIME_relative2s ( + GNUNET_TIME_absolute_get_duration (now), + GNUNET_YES)); + return ret; } @@ -651,14 +1291,14 @@ cs_work_dispatch (struct TES_Client *client, switch (ntohs (hdr->type)) { case TALER_HELPER_CS_MT_REQ_SIGN: - if (msize < sizeof (struct TALER_CRYPTO_CsSignRequest)) + if (msize < sizeof (struct TALER_CRYPTO_CsSignRequestMessage)) { GNUNET_break_op (0); return GNUNET_SYSERR; } return handle_sign_request ( client, - (const struct TALER_CRYPTO_CsSignRequest *) hdr); + (const struct TALER_CRYPTO_CsSignRequestMessage *) hdr); case TALER_HELPER_CS_MT_REQ_REVOKE: if (msize != sizeof (struct TALER_CRYPTO_CsRevokeRequest)) { @@ -668,6 +1308,24 @@ cs_work_dispatch (struct TES_Client *client, return handle_revoke_request ( client, (const struct TALER_CRYPTO_CsRevokeRequest *) hdr); + case TALER_HELPER_CS_MT_REQ_BATCH_SIGN: + if (msize <= sizeof (struct TALER_CRYPTO_BatchSignRequest)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return handle_batch_sign_request ( + client, + (const struct TALER_CRYPTO_BatchSignRequest *) hdr); + case TALER_HELPER_CS_MT_REQ_BATCH_RDERIVE: + if (msize <= sizeof (struct TALER_CRYPTO_BatchDeriveRequest)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return handle_batch_derive_request ( + client, + (const struct TALER_CRYPTO_BatchDeriveRequest *) hdr); case TALER_HELPER_CS_MT_REQ_RDERIVE: if (msize != sizeof (struct TALER_CRYPTO_CsRDeriveRequest)) { @@ -722,9 +1380,9 @@ cs_client_init (struct TES_Client *client) NULL != dk; dk = dk->next) { - memcpy (&buf[obs], - dk->an, - ntohs (dk->an->header.size)); + GNUNET_memcpy (&buf[obs], + dk->an, + ntohs (dk->an->header.size)); obs += ntohs (dk->an->header.size); } } @@ -821,18 +1479,18 @@ cs_update_client_keys (struct TES_Client *client) .h_cs = key->h_cs }; - memcpy (&buf[obs], - &pn, - sizeof (pn)); + GNUNET_memcpy (&buf[obs], + &pn, + sizeof (pn)); GNUNET_assert (obs + sizeof (pn) > obs); obs += sizeof (pn); } else { - memcpy (&buf[obs], - key->an, - ntohs (key->an->header.size)); + GNUNET_memcpy (&buf[obs], + key->an, + ntohs (key->an->header.size)); GNUNET_assert (obs + ntohs (key->an->header.size) > obs); obs += ntohs (key->an->header.size); @@ -1122,20 +1780,19 @@ parse_key (struct Denomination *denom, return; } { - struct GNUNET_CRYPTO_CsPublicKey pub; struct DenominationKey *dk; struct DenominationKey *before; - GNUNET_CRYPTO_cs_private_key_get_public (priv, - &pub); dk = GNUNET_new (struct DenominationKey); dk->denom_priv = *priv; dk->denom = denom; dk->anchor = anchor; dk->filename = GNUNET_strdup (filename); - TALER_cs_pub_hash (&pub, - &dk->h_cs); - dk->denom_pub = pub; + GNUNET_CRYPTO_cs_private_key_get_public (priv, + &dk->denom_pub); + GNUNET_CRYPTO_hash (&dk->denom_pub, + sizeof (dk->denom_pub), + &dk->h_cs.hash); generate_response (dk); if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put ( @@ -1157,7 +1814,9 @@ parse_key (struct Denomination *denom, NULL != pos; pos = pos->next) { - if (GNUNET_TIME_timestamp_cmp (pos->anchor, >, anchor)) + if (GNUNET_TIME_timestamp_cmp (pos->anchor, + >, + anchor)) break; before = pos; } @@ -1305,6 +1964,11 @@ parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, const char *ct, struct Denomination *denom) { + char *secname; + + GNUNET_asprintf (&secname, + "%s-secmod-cs", + section); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, ct, @@ -1314,6 +1978,7 @@ parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, ct, "DURATION_WITHDRAW"); + GNUNET_free (secname); return GNUNET_SYSERR; } if (GNUNET_TIME_relative_cmp (overlap_duration, @@ -1321,11 +1986,13 @@ parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, denom->duration_withdraw)) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-cs", + secname, "OVERLAP_DURATION", "Value given must be smaller than value for DURATION_WITHDRAW!"); + GNUNET_free (secname); return GNUNET_SYSERR; } + GNUNET_free (secname); denom->section = GNUNET_strdup (ct); return GNUNET_OK; } @@ -1440,28 +2107,36 @@ load_denominations (void *cls, static enum GNUNET_GenericReturnValue load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg) { + char *secname; + + GNUNET_asprintf (&secname, + "%s-secmod-cs", + section); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, - "taler-exchange-secmod-cs", + secname, "OVERLAP_DURATION", &overlap_duration)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-cs", + secname, "OVERLAP_DURATION"); + GNUNET_free (secname); return GNUNET_SYSERR; } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, - "taler-exchange-secmod-cs", + secname, "LOOKAHEAD_SIGN", &lookahead_sign)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-cs", + secname, "LOOKAHEAD_SIGN"); + GNUNET_free (secname); return GNUNET_SYSERR; } + GNUNET_free (secname); return GNUNET_OK; } @@ -1481,6 +2156,8 @@ do_shutdown (void *cls) GNUNET_SCHEDULER_cancel (keygen_task); keygen_task = NULL; } + stop_workers (); + sem_done (&worker_sem); } @@ -1503,6 +2180,7 @@ run (void *cls, .updater = &cs_update_client_keys, .init = &cs_client_init }; + char *secname; (void) cls; (void) args; @@ -1517,31 +2195,62 @@ run (void *cls, /* get current time again, we may be timetraveling! */ now = GNUNET_TIME_timestamp_get (); } + GNUNET_asprintf (&secname, + "%s-secmod-cs", + section); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, - "taler-exchange-secmod-cs", + secname, "KEY_DIR", &keydir)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-cs", + secname, "KEY_DIR"); + GNUNET_free (secname); global_ret = EXIT_NOTCONFIGURED; return; } + GNUNET_free (secname); if (GNUNET_OK != load_durations (cfg)) { global_ret = EXIT_NOTCONFIGURED; return; } - global_ret = TES_listen_start (cfg, - "taler-exchange-secmod-cs", - &cb); + { + char *secname; + + GNUNET_asprintf (&secname, + "%s-secmod-cs", + section); + global_ret = TES_listen_start (cfg, + secname, + &cb); + GNUNET_free (secname); + } if (0 != global_ret) return; + sem_init (&worker_sem, + 0); GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); + if (0 == max_workers) + { + long lret; + + lret = sysconf (_SC_NPROCESSORS_CONF); + if (lret <= 0) + lret = 1; + max_workers = (unsigned int) lret; + } + for (unsigned int i = 0; i<max_workers; i++) + if (GNUNET_OK != + start_worker ()) + { + GNUNET_SCHEDULER_shutdown (); + return; + } /* Load denominations */ keys = GNUNET_CONTAINER_multihashmap_create (65536, GNUNET_YES); @@ -1592,6 +2301,11 @@ main (int argc, char **argv) { struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_string ('s', + "section", + "SECTION", + "name of the configuration section prefix to use, default is 'taler'", + §ion), GNUNET_GETOPT_option_timetravel ('T', "timetravel"), GNUNET_GETOPT_option_timestamp ('t', @@ -1599,13 +2313,18 @@ main (int argc, "TIMESTAMP", "pretend it is a different time for the update", &now_tmp), + GNUNET_GETOPT_option_uint ('w', + "workers", + "COUNT", + "use COUNT workers for parallel processing of batch requests", + &max_workers), GNUNET_GETOPT_OPTION_END }; enum GNUNET_GenericReturnValue ret; /* Restrict permissions for the key files that we create. */ (void) umask (S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH); - + section = GNUNET_strdup ("taler-exchange"); /* force linker to link against libtalerutil; if we do not do this, the linker may "optimize" libtalerutil away and skip #TALER_OS_init(), which we do need */ diff --git a/src/util/taler-exchange-secmod-cs.conf b/src/util/taler-exchange-secmod-cs.conf index 5085eab79..fa3cdba40 100644 --- a/src/util/taler-exchange-secmod-cs.conf +++ b/src/util/taler-exchange-secmod-cs.conf @@ -8,16 +8,16 @@ OVERLAP_DURATION = 5 m # Where do we store the generated private keys. -KEY_DIR = ${TALER_DATA_HOME}/exchange-secmod-cs/keys +KEY_DIR = ${TALER_DATA_HOME}exchange-secmod-cs/keys # Where does the helper listen for requests? -UNIXPATH = $TALER_RUNTIME_DIR/exchange-secmod-cs/server.sock +UNIXPATH = ${TALER_RUNTIME_DIR}exchange-secmod-cs/server.sock # Directory for clients. -CLIENT_DIR = $TALER_RUNTIME_DIR/exchange-secmod-cs/clients +CLIENT_DIR = ${TALER_RUNTIME_DIR}exchange-secmod-cs/clients # Where should the security module store its own private key? -SM_PRIV_KEY = ${TALER_DATA_HOME}/exchange-secmod-cs/secmod-private-key +SM_PRIV_KEY = ${TALER_DATA_HOME}exchange-secmod-cs/secmod-private-key # For how long into the future do we pre-generate keys? LOOKAHEAD_SIGN = 1 year diff --git a/src/util/taler-exchange-secmod-cs.h b/src/util/taler-exchange-secmod-cs.h index c71c3b9af..0321335da 100644 --- a/src/util/taler-exchange-secmod-cs.h +++ b/src/util/taler-exchange-secmod-cs.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020 Taler Systems SA + Copyright (C) 2020-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 @@ -26,17 +26,21 @@ #define TALER_HELPER_CS_MT_PURGE 1 #define TALER_HELPER_CS_MT_AVAIL 2 -#define TALER_HELPER_CS_MT_REQ_INIT 4 +#define TALER_HELPER_CS_MT_REQ_INIT 3 +#define TALER_HELPER_CS_MT_REQ_BATCH_SIGN 4 #define TALER_HELPER_CS_MT_REQ_SIGN 5 #define TALER_HELPER_CS_MT_REQ_REVOKE 6 -#define TALER_HELPER_CS_MT_REQ_RDERIVE 7 +#define TALER_HELPER_CS_MT_REQ_BATCH_RDERIVE 7 +#define TALER_HELPER_CS_MT_REQ_RDERIVE 8 -#define TALER_HELPER_CS_MT_RES_SIGNATURE 8 -#define TALER_HELPER_CS_MT_RES_SIGN_FAILURE 9 -#define TALER_HELPER_CS_MT_RES_RDERIVE 10 -#define TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE 11 +#define TALER_HELPER_CS_MT_RES_SIGNATURE 9 +#define TALER_HELPER_CS_MT_RES_SIGN_FAILURE 10 +#define TALER_HELPER_CS_MT_RES_BATCH_SIGN_FAILURE 11 +#define TALER_HELPER_CS_MT_RES_RDERIVE 12 +#define TALER_HELPER_CS_MT_RES_RDERIVE_FAILURE 13 +#define TALER_HELPER_CS_MT_RES_BATCH_RDERIVE_FAILURE 14 -#define TALER_HELPER_CS_SYNCED 12 +#define TALER_HELPER_CS_SYNCED 15 GNUNET_NETWORK_STRUCT_BEGIN @@ -114,7 +118,7 @@ struct TALER_CRYPTO_CsKeyPurgeNotification /** * Message sent if a signature is requested. */ -struct TALER_CRYPTO_CsSignRequest +struct TALER_CRYPTO_CsSignRequestMessage { /** * Type is #TALER_HELPER_CS_MT_REQ_SIGN. @@ -132,13 +136,35 @@ struct TALER_CRYPTO_CsSignRequest struct TALER_CsPubHashP h_cs; /** - * Planchet containing message to sign - * and nonce to derive R from + * Message to sign. */ - struct TALER_BlindedCsPlanchet planchet; + struct GNUNET_CRYPTO_CsBlindedMessage message; }; + +/** + * Message sent if a batch of signatures is requested. + */ +struct TALER_CRYPTO_BatchSignRequest +{ + /** + * Type is #TALER_HELPER_CS_MT_REQ_BATCH_SIGN. + */ + struct GNUNET_MessageHeader header; + + /** + * Number of signatures to create, in NBO. + */ + uint32_t batch_size; + + /* + * Followed by @e batch_size batch sign requests. + */ + +}; + + /** * Message sent if a signature is requested. */ @@ -162,9 +188,32 @@ struct TALER_CRYPTO_CsRDeriveRequest /** * Withdraw nonce to derive R from */ - struct TALER_CsNonce nonce; + struct GNUNET_CRYPTO_CsSessionNonce nonce; +}; + + +/** + * Message sent if a batch of derivations is requested. + */ +struct TALER_CRYPTO_BatchDeriveRequest +{ + /** + * Type is #TALER_HELPER_CS_MT_REQ_BATCH_RDERIVE. + */ + struct GNUNET_MessageHeader header; + + /** + * Number of derivations to create, in NBO. + */ + uint32_t batch_size; + + /* + * Followed by @e batch_size derive requests. + */ + }; + /** * Message sent if a key was revoked. */ @@ -199,14 +248,14 @@ struct TALER_CRYPTO_SignResponse struct GNUNET_MessageHeader header; /** - * For now, always zero. + * The chosen 'b' (0 or 1). */ - uint32_t reserved; + uint32_t b; /** - * Contains the blindided s and the chosen b + * Contains the blindided s. */ - struct TALER_BlindedDenominationCsSignAnswer cs_answer; + struct GNUNET_CRYPTO_CsBlindS cs_answer; }; /** @@ -225,9 +274,9 @@ struct TALER_CRYPTO_RDeriveResponse uint32_t reserved; /** - * derived R + * Pair of derived R values */ - struct TALER_DenominationCSPublicRPairP r_pub; + struct GNUNET_CRYPTO_CSPublicRPairP r_pub; }; diff --git a/src/util/taler-exchange-secmod-eddsa.c b/src/util/taler-exchange-secmod-eddsa.c index e07e9a71d..0b95447f7 100644 --- a/src/util/taler-exchange-secmod-eddsa.c +++ b/src/util/taler-exchange-secmod-eddsa.c @@ -137,6 +137,13 @@ static struct GNUNET_TIME_Timestamp now_tmp; static char *keydir; /** + * Name of the configuration section prefix to use. Usually either "taler-exchange" or + * "donau". The actual configuration section will then be + * "$SECTION-secmod-eddsa". + */ +static char *section; + +/** * How much should coin creation duration overlap * with the next key? Basically, the starting time of two * keys is always #duration - #overlap_duration apart. @@ -584,11 +591,11 @@ eddsa_client_init (struct TES_Client *client) static enum GNUNET_GenericReturnValue eddsa_update_client_keys (struct TES_Client *client) { + GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Updating client %p to generation %llu\n", client, (unsigned long long) key_gen); - GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); for (struct Key *key = keys_head; NULL != key; key = key->next) @@ -826,9 +833,9 @@ parse_key (const char *filename, filename); return GNUNET_SYSERR; } - memcpy (&priv, - buf, - buf_size); + GNUNET_memcpy (&priv, + buf, + buf_size); { struct GNUNET_CRYPTO_EddsaPublicKey pub; @@ -991,39 +998,48 @@ import_key (void *cls, static enum GNUNET_GenericReturnValue load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg) { + char *secname; + + GNUNET_asprintf (&secname, + "%s-secmod-eddsa", + section); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, - "taler-exchange-secmod-eddsa", + secname, "OVERLAP_DURATION", &overlap_duration)) { + GNUNET_free (secname); GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-eddsa", + secname, "OVERLAP_DURATION"); return GNUNET_SYSERR; } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, - "taler-exchange-secmod-eddsa", + secname, "DURATION", &duration)) { + GNUNET_free (secname); GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-eddsa", + secname, "DURATION"); return GNUNET_SYSERR; } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, - "taler-exchange-secmod-eddsa", + secname, "LOOKAHEAD_SIGN", &lookahead_sign)) { + GNUNET_free (secname); GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-eddsa", + secname, "LOOKAHEAD_SIGN"); return GNUNET_SYSERR; } + GNUNET_free (secname); return GNUNET_OK; } @@ -1065,6 +1081,7 @@ run (void *cls, .updater = eddsa_update_client_keys, .init = eddsa_client_init }; + char *secname; (void) cls; (void) args; @@ -1079,6 +1096,9 @@ run (void *cls, /* get current time again, we may be timetraveling! */ now = GNUNET_TIME_timestamp_get (); } + GNUNET_asprintf (&secname, + "%s-secmod-eddsa", + section); if (GNUNET_OK != load_durations (cfg)) { @@ -1087,21 +1107,31 @@ run (void *cls, } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, - "taler-exchange-secmod-eddsa", + secname, "KEY_DIR", &keydir)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-eddsa", + secname, "KEY_DIR"); + GNUNET_free (secname); global_ret = EXIT_NOTCONFIGURED; return; } + GNUNET_free (secname); GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); - global_ret = TES_listen_start (cfg, - "taler-exchange-secmod-eddsa", - &cb); + { + char *secname; + + GNUNET_asprintf (&secname, + "%s-secmod-eddsa", + section); + global_ret = TES_listen_start (cfg, + secname, + &cb); + GNUNET_free (secname); + } if (0 != global_ret) return; /* Load keys */ @@ -1144,6 +1174,11 @@ main (int argc, char **argv) { struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_string ('s', + "section", + "SECTION", + "name of the configuration section prefix to use, default is 'taler'", + §ion), GNUNET_GETOPT_option_timetravel ('T', "timetravel"), GNUNET_GETOPT_option_timestamp ('t', @@ -1157,7 +1192,7 @@ main (int argc, /* Restrict permissions for the key files that we create. */ (void) umask (S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH); - + section = GNUNET_strdup ("taler-exchange"); /* force linker to link against libtalerutil; if we do not do this, the linker may "optimize" libtalerutil away and skip #TALER_OS_init(), which we do need */ diff --git a/src/util/taler-exchange-secmod-eddsa.conf b/src/util/taler-exchange-secmod-eddsa.conf index ea09f0334..0cb4a4ffc 100644 --- a/src/util/taler-exchange-secmod-eddsa.conf +++ b/src/util/taler-exchange-secmod-eddsa.conf @@ -8,16 +8,16 @@ OVERLAP_DURATION = 5m # Where do we store the private keys. -KEY_DIR = ${TALER_DATA_HOME}/exchange-secmod-eddsa/keys +KEY_DIR = ${TALER_DATA_HOME}exchange-secmod-eddsa/keys # Where does the helper listen for requests? -UNIXPATH = $TALER_RUNTIME_DIR/exchange-secmod-eddsa/server.sock +UNIXPATH = ${TALER_RUNTIME_DIR}exchange-secmod-eddsa/server.sock # Directory for clients. -CLIENT_DIR = $TALER_RUNTIME_DIR/exchange-secmod-eddsa/clients +CLIENT_DIR = ${TALER_RUNTIME_DIR}exchange-secmod-eddsa/clients # Where should the security module store its own private key? -SM_PRIV_KEY = ${TALER_DATA_HOME}/exchange-secmod-eddsa/secmod-private-key +SM_PRIV_KEY = ${TALER_DATA_HOME}exchange-secmod-eddsa/secmod-private-key # For how long into the future do we pre-generate keys? LOOKAHEAD_SIGN = 1 year diff --git a/src/util/taler-exchange-secmod-rsa.c b/src/util/taler-exchange-secmod-rsa.c index 40b4f51de..c80e2e3c4 100644 --- a/src/util/taler-exchange-secmod-rsa.c +++ b/src/util/taler-exchange-secmod-rsa.c @@ -308,6 +308,13 @@ static struct GNUNET_TIME_Timestamp now_tmp; static char *keydir; /** + * Name of the configuration section prefix to use. Usually either "taler-exchange" or + * "donau". The actual configuration section will then be + * "$SECTION-secmod-rsa". + */ +static char *section; + +/** * How much should coin creation (@e duration_withdraw) duration overlap * with the next denomination? Basically, the starting time of two * denominations is always @e duration_withdraw - #overlap_duration apart. @@ -394,13 +401,13 @@ generate_response (struct DenominationKey *dk) &an->secm_sig); an->secm_pub = TES_smpub; p = (void *) &an[1]; - memcpy (p, - buf, - buf_len); + GNUNET_memcpy (p, + buf, + buf_len); GNUNET_free (buf); - memcpy (p + buf_len, - denom->section, - nlen); + GNUNET_memcpy (p + buf_len, + denom->section, + nlen); dk->an = an; } @@ -409,15 +416,13 @@ generate_response (struct DenominationKey *dk) * Do the actual signing work. * * @param h_rsa key to sign with - * @param blinded_msg message to sign - * @param blinded_msg_size number of bytes in @a blinded_msg + * @param bm blinded message to sign * @param[out] rsa_signaturep set to the RSA signature * @return #TALER_EC_NONE on success */ static enum TALER_ErrorCode do_sign (const struct TALER_RsaPubHashP *h_rsa, - const void *blinded_msg, - size_t blinded_msg_size, + const struct GNUNET_CRYPTO_RsaBlindedMessage *bm, struct GNUNET_CRYPTO_RsaSignature **rsa_signaturep) { struct DenominationKey *dk; @@ -447,15 +452,14 @@ do_sign (const struct TALER_RsaPubHashP *h_rsa, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received request to sign over %u bytes with key %s\n", - (unsigned int) blinded_msg_size, + (unsigned int) bm->blinded_msg_size, GNUNET_h2s (&h_rsa->hash)); GNUNET_assert (dk->rc < UINT_MAX); dk->rc++; GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock)); rsa_signature = GNUNET_CRYPTO_rsa_sign_blinded (dk->denom_priv, - blinded_msg, - blinded_msg_size); + bm); GNUNET_assert (0 == pthread_mutex_lock (&keys_lock)); GNUNET_assert (dk->rc > 0); dk->rc--; @@ -524,9 +528,9 @@ send_signature (struct TES_Client *client, sr = GNUNET_malloc (tsize); sr->header.size = htons (tsize); sr->header.type = htons (TALER_HELPER_RSA_MT_RES_SIGNATURE); - memcpy (&sr[1], - buf, - buf_size); + GNUNET_memcpy (&sr[1], + buf, + buf_size); GNUNET_free (buf); ret = TES_transmit (client->csock, &sr->header); @@ -548,14 +552,15 @@ static enum GNUNET_GenericReturnValue handle_sign_request (struct TES_Client *client, const struct TALER_CRYPTO_SignRequest *sr) { - const void *blinded_msg = &sr[1]; - size_t blinded_msg_size = ntohs (sr->header.size) - sizeof (*sr); + struct GNUNET_CRYPTO_RsaBlindedMessage bm = { + .blinded_msg = (void *) &sr[1], + .blinded_msg_size = ntohs (sr->header.size) - sizeof (*sr) + }; struct GNUNET_CRYPTO_RsaSignature *rsa_signature; enum TALER_ErrorCode ec; ec = do_sign (&sr->h_rsa, - blinded_msg, - blinded_msg_size, + &bm, &rsa_signature); if (TALER_EC_NONE != ec) { @@ -583,6 +588,7 @@ sem_init (struct Semaphore *sem, GNUNET_assert (0 == pthread_cond_init (&sem->cv, NULL)); + sem->ctr = val; } @@ -659,12 +665,13 @@ worker (void *cls) { struct BatchJob *bj = w->job; const struct TALER_CRYPTO_SignRequest *sr = bj->sr; - const void *blinded_msg = &sr[1]; - size_t blinded_msg_size = ntohs (sr->header.size) - sizeof (*sr); + struct GNUNET_CRYPTO_RsaBlindedMessage bm = { + .blinded_msg = (void *) &sr[1], + .blinded_msg_size = ntohs (sr->header.size) - sizeof (*sr) + }; bj->ec = do_sign (&sr->h_rsa, - blinded_msg, - blinded_msg_size, + &bm, &bj->rsa_signature); sem_up (&bj->sem); w->job = NULL; @@ -765,6 +772,9 @@ handle_batch_sign_request (struct TES_Client *client, off += s; size -= s; } + GNUNET_break_op (0 == size); + bs = GNUNET_MIN (bs, + idx); for (unsigned int i = 0; i<bs; i++) finish_job (client, &jobs[i]); @@ -876,8 +886,8 @@ setup_key (struct DenominationKey *dk, } buf_size = GNUNET_CRYPTO_rsa_private_key_encode (priv, &buf); - TALER_rsa_pub_hash (pub, - &dk->h_rsa); + GNUNET_CRYPTO_rsa_public_key_hash (pub, + &dk->h_rsa.hash); GNUNET_asprintf (&dk->filename, "%s/%s/%llu", keydir, @@ -1107,9 +1117,9 @@ rsa_client_init (struct TES_Client *client) NULL != dk; dk = dk->next) { - memcpy (&buf[obs], - dk->an, - ntohs (dk->an->header.size)); + GNUNET_memcpy (&buf[obs], + dk->an, + ntohs (dk->an->header.size)); GNUNET_assert (obs + ntohs (dk->an->header.size) > obs); obs += ntohs (dk->an->header.size); @@ -1208,18 +1218,18 @@ rsa_update_client_keys (struct TES_Client *client) .h_rsa = key->h_rsa }; - memcpy (&buf[obs], - &pn, - sizeof (pn)); + GNUNET_memcpy (&buf[obs], + &pn, + sizeof (pn)); GNUNET_assert (obs + sizeof (pn) > obs); obs += sizeof (pn); } else { - memcpy (&buf[obs], - key->an, - ntohs (key->an->header.size)); + GNUNET_memcpy (&buf[obs], + key->an, + ntohs (key->an->header.size)); GNUNET_assert (obs + ntohs (key->an->header.size) > obs); obs += ntohs (key->an->header.size); @@ -1251,6 +1261,7 @@ create_key (struct Denomination *denom, struct GNUNET_TIME_Timestamp anchor; anchor = now; + // FIXME: round down to multiple of 'anchor_round' value from configuration if (NULL != denom->keys_tail) { struct GNUNET_TIME_Absolute abs; @@ -1541,8 +1552,8 @@ parse_key (struct Denomination *denom, dk->denom = denom; dk->anchor = anchor; dk->filename = GNUNET_strdup (filename); - TALER_rsa_pub_hash (pub, - &dk->h_rsa); + GNUNET_CRYPTO_rsa_public_key_hash (pub, + &dk->h_rsa.hash); dk->denom_pub = pub; generate_response (dk); if (GNUNET_OK != @@ -1567,7 +1578,9 @@ parse_key (struct Denomination *denom, NULL != pos; pos = pos->next) { - if (GNUNET_TIME_timestamp_cmp (pos->anchor, >, anchor)) + if (GNUNET_TIME_timestamp_cmp (pos->anchor, + >, + anchor)) break; before = pos; } @@ -1629,7 +1642,6 @@ import_key (void *cls, GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "open", filename); - GNUNET_break (0 == close (fd)); return GNUNET_OK; } if (0 != fstat (fd, @@ -1718,7 +1730,11 @@ parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, struct Denomination *denom) { unsigned long long rsa_keysize; + char *secname; + GNUNET_asprintf (&secname, + "%s-secmod-rsa", + section); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, ct, @@ -1728,6 +1744,7 @@ parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, ct, "DURATION_WITHDRAW"); + GNUNET_free (secname); return GNUNET_SYSERR; } if (GNUNET_TIME_relative_cmp (overlap_duration, @@ -1735,9 +1752,10 @@ parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, denom->duration_withdraw)) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", + section, "OVERLAP_DURATION", "Value given must be smaller than value for DURATION_WITHDRAW!"); + GNUNET_free (secname); return GNUNET_SYSERR; } if (GNUNET_OK != @@ -1749,6 +1767,7 @@ parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, ct, "RSA_KEYSIZE"); + GNUNET_free (secname); return GNUNET_SYSERR; } if ( (rsa_keysize > 4 * 2048) || @@ -1758,8 +1777,10 @@ parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg, ct, "RSA_KEYSIZE", "Given RSA keysize outside of permitted range [1024,8192]\n"); + GNUNET_free (secname); return GNUNET_SYSERR; } + GNUNET_free (secname); denom->rsa_keysize = (unsigned int) rsa_keysize; denom->section = GNUNET_strdup (ct); return GNUNET_OK; @@ -1874,28 +1895,36 @@ load_denominations (void *cls, static enum GNUNET_GenericReturnValue load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg) { + char *secname; + + GNUNET_asprintf (&secname, + "%s-secmod-rsa", + section); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, - "taler-exchange-secmod-rsa", + secname, "OVERLAP_DURATION", &overlap_duration)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", + secname, "OVERLAP_DURATION"); + GNUNET_free (secname); return GNUNET_SYSERR; } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, - "taler-exchange-secmod-rsa", + secname, "LOOKAHEAD_SIGN", &lookahead_sign)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", + secname, "LOOKAHEAD_SIGN"); + GNUNET_free (secname); return GNUNET_SYSERR; } + GNUNET_free (secname); return GNUNET_OK; } @@ -1939,6 +1968,7 @@ run (void *cls, .updater = rsa_update_client_keys, .init = rsa_client_init }; + char *secname; (void) cls; (void) args; @@ -1953,27 +1983,40 @@ run (void *cls, /* get current time again, we may be timetraveling! */ now = GNUNET_TIME_timestamp_get (); } + GNUNET_asprintf (&secname, + "%s-secmod-rsa", + section); if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, - "taler-exchange-secmod-rsa", + secname, "KEY_DIR", &keydir)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "taler-exchange-secmod-rsa", + secname, "KEY_DIR"); + GNUNET_free (secname); global_ret = EXIT_NOTCONFIGURED; return; } + GNUNET_free (secname); if (GNUNET_OK != load_durations (cfg)) { global_ret = EXIT_NOTCONFIGURED; return; } - global_ret = TES_listen_start (cfg, - "taler-exchange-secmod-rsa", - &cb); + { + char *secname; + + GNUNET_asprintf (&secname, + "%s-secmod-rsa", + section); + global_ret = TES_listen_start (cfg, + secname, + &cb); + GNUNET_free (secname); + } if (0 != global_ret) return; sem_init (&worker_sem, @@ -1981,7 +2024,15 @@ run (void *cls, GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); if (0 == max_workers) - max_workers = 1; /* FIXME-#7272: or determine from CPU? */ + { + long lret; + + lret = sysconf (_SC_NPROCESSORS_CONF); + if (lret <= 0) + lret = 1; + max_workers = (unsigned int) lret; + } + for (unsigned int i = 0; i<max_workers; i++) if (GNUNET_OK != start_worker ()) @@ -2039,6 +2090,11 @@ main (int argc, char **argv) { struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_string ('s', + "section", + "SECTION", + "name of the configuration section prefix to use, default is 'taler'", + §ion), GNUNET_GETOPT_option_timetravel ('T', "timetravel"), GNUNET_GETOPT_option_timestamp ('t', @@ -2057,7 +2113,7 @@ main (int argc, /* Restrict permissions for the key files that we create. */ (void) umask (S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH); - + section = GNUNET_strdup ("taler-exchange"); /* force linker to link against libtalerutil; if we do not do this, the linker may "optimize" libtalerutil away and skip #TALER_OS_init(), which we do need */ diff --git a/src/util/taler-exchange-secmod-rsa.conf b/src/util/taler-exchange-secmod-rsa.conf index dfa87f050..978c40258 100644 --- a/src/util/taler-exchange-secmod-rsa.conf +++ b/src/util/taler-exchange-secmod-rsa.conf @@ -5,19 +5,22 @@ # wallets picking one key and then due to network latency # another key being valid. The DURATION_WITHDRAW period # must be longer than this value. -OVERLAP_DURATION = 5 m +OVERLAP_DURATION = 0 m # Where do we store the generated private keys. -KEY_DIR = ${TALER_DATA_HOME}/exchange-secmod-rsa/keys +KEY_DIR = ${TALER_DATA_HOME}exchange-secmod-rsa/keys # Where does the helper listen for requests? -UNIXPATH = $TALER_RUNTIME_DIR/exchange-secmod-rsa/server.sock +UNIXPATH = ${TALER_RUNTIME_DIR}exchange-secmod-rsa/server.sock # Directory for clients. -CLIENT_DIR = $TALER_RUNTIME_DIR/exchange-secmod-rsa/clients +CLIENT_DIR = ${TALER_RUNTIME_DIR}exchange-secmod-rsa/clients # Where should the security module store its own private key? -SM_PRIV_KEY = ${TALER_DATA_HOME}/exchange-secmod-rsa/secmod-private-key +SM_PRIV_KEY = ${TALER_DATA_HOME}exchange-secmod-rsa/secmod-private-key # For how long into the future do we pre-generate keys? LOOKAHEAD_SIGN = 1 year + +# Round down anchor key start date to multiples of this time. +ANCHOR_ROUND = 1 ms
\ No newline at end of file diff --git a/src/util/taler-exchange-secmod-rsa.h b/src/util/taler-exchange-secmod-rsa.h index dddaf3e1c..ffbceb48e 100644 --- a/src/util/taler-exchange-secmod-rsa.h +++ b/src/util/taler-exchange-secmod-rsa.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020 Taler Systems SA + Copyright (C) 2020-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 @@ -136,7 +136,7 @@ struct TALER_CRYPTO_SignRequest /** - * Message sent if a signature is requested. + * Message sent if a batch of signatures is requested. */ struct TALER_CRYPTO_BatchSignRequest { @@ -151,7 +151,7 @@ struct TALER_CRYPTO_BatchSignRequest uint32_t batch_size; /* - * Followed by @e batch_size batch sign requests. + * Followed by @e batch_size sign requests. */ }; diff --git a/src/util/test_age_restriction.c b/src/util/test_age_restriction.c index 3c5d52629..61499e5e0 100644 --- a/src/util/test_age_restriction.c +++ b/src/util/test_age_restriction.c @@ -21,12 +21,7 @@ */ #include "platform.h" #include "taler_util.h" -#include "taler_crypto_lib.h" - -extern uint8_t -get_age_group ( - const struct TALER_AgeMask *mask, - uint8_t age); +#include <gnunet/gnunet_common.h> /** * Encodes the age mask into a string, like "8:10:12:14:16:18:21" @@ -85,24 +80,24 @@ test_groups (void) .bits = 1 | 1 << 5 | 1 << 13 | 1 << 23, - .group = { 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 } + .group = { 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 } }, { .bits = 1 | 1 << 8 | 1 << 10 | 1 << 12 | 1 << 14 | 1 << 16 | 1 << 18 | 1 << 21, - .group = { 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, - 2, 2, - 3, 3, - 4, 4, - 5, 5, - 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7} + .group = { 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, + 2, 2, + 3, 3, + 4, 4, + 5, 5, + 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7} } @@ -114,10 +109,10 @@ test_groups (void) for (uint8_t i = 0; i < 32; i++) { - uint8_t r = get_age_group (&mask, i); + uint8_t r = TALER_get_age_group (&mask, i); char *m = age_mask_to_string (&mask); - printf ("get_age_group(%s, %2d) = %d vs %d (exp)\n", + printf ("TALER_get_age_group(%s, %2d) = %d vs %d (exp)\n", m, i, r, @@ -134,6 +129,177 @@ test_groups (void) } +enum GNUNET_GenericReturnValue +test_dates (void) +{ + struct TALER_AgeMask mask = { + .bits = 1 | 1 << 5 | 1 << 9 | 1 << 13 | 1 << 17 | 1 << 21 + }; + struct + { + char *date; + uint32_t expected; + enum GNUNET_GenericReturnValue ret; + } + test [] = { + {.date = "abcd-00-00", .expected = 0, .ret = GNUNET_SYSERR}, + {.date = "1900-00-01", .expected = 0, .ret = GNUNET_SYSERR}, + {.date = "19000001", .expected = 0, .ret = GNUNET_SYSERR}, + {.date = "2001-33-05", .expected = 0, .ret = GNUNET_SYSERR}, + {.date = "2001-33-35", .expected = 0, .ret = GNUNET_SYSERR}, + + {.date = "1900-00-00", .expected = 0, .ret = GNUNET_OK}, + {.date = "2001-00-00", .expected = 0, .ret = GNUNET_OK}, + {.date = "2001-03-00", .expected = 0, .ret = GNUNET_OK}, + {.date = "2001-03-05", .expected = 0, .ret = GNUNET_OK}, + + /* These dates should be far enough for the near future so that + * the expected values are correct. Will need adjustment in 2044 :) */ + {.date = "2022-11-26", .expected = 19322, .ret = GNUNET_OK }, + {.date = "2022-11-27", .expected = 19323, .ret = GNUNET_OK }, + {.date = "2023-06-26", .expected = 19534, .ret = GNUNET_OK }, + {.date = "2023-06-01", .expected = 19509, .ret = GNUNET_OK }, + {.date = "2023-06-00", .expected = 19509, .ret = GNUNET_OK }, + {.date = "2023-01-01", .expected = 19358, .ret = GNUNET_OK }, + {.date = "2023-00-00", .expected = 19358, .ret = GNUNET_OK }, + + /* Special case: .date == NULL meands birthday == current date, which + * should be 21 years in the future. We will set these values below in the + * loop */ + {.date = NULL, .expected = 0, .ret = GNUNET_OK }, + }; + char buf[256] = {0}; + + for (uint8_t t = 0; t < sizeof(test) / sizeof(test[0]); t++) + { + uint32_t d; + enum GNUNET_GenericReturnValue ret; + char *date = test[t].date; + + if (NULL == test[t].date) + { + /* Special case: We set .date to the current date. */ + time_t tn; + struct tm now; + + time (&tn); + localtime_r (&tn, &now); + strftime (buf, sizeof(buf), "%Y-%m-%d", &now); + date = &buf[0]; + + /* The expected value is the number of days since 1970-01-01, + * counted simplistically */ + test[t].expected = timegm (&now) / 60 / 60 / 24; + } + + ret = TALER_parse_coarse_date (date, + &mask, + &d); + if (ret != test[t].ret) + { + printf ( + "dates[%d] for date `%s` expected parser to return: %d, got: %d\n", + t, date, test[t].ret, ret); + return GNUNET_SYSERR; + } + + if (ret == GNUNET_SYSERR) + continue; + + if (d != test[t].expected) + { + printf ( + "dates[%d] for date `%s` expected value %d, but got %d\n", + t, date, test[t].expected, d); + return GNUNET_SYSERR; + } + + printf ("dates[%d] for date `%s` got expected value %d\n", + t, date, d); + } + + printf ("done with dates\n"); + + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +test_lowest (void) +{ + struct TALER_AgeMask mask = { + .bits = 1 | 1 << 5 | 1 << 9 | 1 << 13 | 1 << 17 | 1 << 21 + }; + + struct { uint8_t age; uint8_t expected; } + test [] = { + {.age = 1, .expected = 0 }, + {.age = 2, .expected = 0 }, + {.age = 3, .expected = 0 }, + {.age = 4, .expected = 0 }, + {.age = 5, .expected = 5 }, + {.age = 6, .expected = 5 }, + {.age = 7, .expected = 5 }, + {.age = 8, .expected = 5 }, + {.age = 9, .expected = 9 }, + {.age = 10, .expected = 9 }, + {.age = 11, .expected = 9 }, + {.age = 12, .expected = 9 }, + {.age = 13, .expected = 13 }, + {.age = 14, .expected = 13 }, + {.age = 15, .expected = 13 }, + {.age = 16, .expected = 13 }, + {.age = 17, .expected = 17 }, + {.age = 18, .expected = 17 }, + {.age = 19, .expected = 17 }, + {.age = 20, .expected = 17 }, + {.age = 21, .expected = 21 }, + {.age = 22, .expected = 21 }, + }; + + for (uint8_t n = 0; n < sizeof(test) / sizeof(test[0]); n++) + { + uint8_t l = TALER_get_lowest_age (&mask, test[n].age); + printf ("lowest[%d] for age %d, expected lowest: %d, got: %d\n", + n, test[n].age, test[n].expected, l); + if (test[n].expected != l) + return GNUNET_SYSERR; + } + + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +test_adult (void) +{ + struct { struct TALER_AgeMask mask; uint8_t expected; } + test[] = { + { .mask = {.bits = 1 | 1 << 2}, + .expected = 2 }, + { .mask = {.bits = 1 | 1 << 2 | 1 << 3}, + .expected = 3 }, + { .mask = {.bits = 1 | 1 << 3}, + .expected = 3 }, + { .mask = {.bits = 1 | 1 << 22}, + .expected = 22 }, + { .mask = {.bits = 1 | 1 << 10 | 1 << 16 | 1 << 22}, + .expected = 22 }, + }; + for (uint8_t n = 0; n < sizeof(test) / sizeof(test[0]); n++) + { + uint8_t l = TALER_adult_age (&test[n].mask); + printf ("adult[%d] for mask %s, expected: %d, got: %d\n", + n, TALER_age_mask_to_string (&test[n].mask), test[n].expected, l); + if (test[n].expected != l) + return GNUNET_SYSERR; + } + printf ("done with adult\n"); + + return GNUNET_OK; +} + + static struct TALER_AgeMask age_mask = { .bits = 1 | 1 << 8 | 1 << 10 | 1 << 12 | 1 << 14 | 1 << 16 | 1 << 18 | 1 << 21 }; @@ -147,7 +313,7 @@ test_attestation (void) enum GNUNET_GenericReturnValue ret; struct TALER_AgeCommitmentProof acp[3] = {0}; struct TALER_AgeAttestation at = {0}; - uint8_t age_group = get_age_group (&age_mask, age); + uint8_t age_group = TALER_get_age_group (&age_mask, age); struct GNUNET_HashCode seed; @@ -155,15 +321,13 @@ test_attestation (void) &seed, sizeof(seed)); - ret = TALER_age_restriction_commit (&age_mask, - age, - &seed, - &acp[0]); - + TALER_age_restriction_commit (&age_mask, + age, + &seed, + &acp[0]); printf ( - "commit(age:%d) == %d; proof.num: %ld; age_group: %d\n", + "commit(age:%d); proof.num: %ld; age_group: %d\n", age, - ret, acp[0].proof.num, age_group); @@ -184,7 +348,7 @@ test_attestation (void) { for (uint8_t min = 0; min < 22; min++) { - uint8_t min_group = get_age_group (&age_mask, min); + uint8_t min_group = TALER_get_age_group (&age_mask, min); ret = TALER_age_commitment_attest (&acp[i], min, @@ -260,11 +424,17 @@ main (int argc, NULL); if (GNUNET_OK != test_groups ()) return 1; + if (GNUNET_OK != test_lowest ()) + return 2; if (GNUNET_OK != test_attestation ()) { GNUNET_break (0); - return 2; + return 3; } + if (GNUNET_OK != test_dates ()) + return 4; + if (GNUNET_OK != test_adult ()) + return 5; return 0; } diff --git a/src/util/test_amount.c b/src/util/test_amount.c index 1af383dcc..57d73b14f 100644 --- a/src/util/test_amount.c +++ b/src/util/test_amount.c @@ -21,7 +21,6 @@ */ #include "platform.h" #include "taler_util.h" -#include "taler_amount_lib.h" int @@ -79,31 +78,31 @@ main (int argc, /* test conversion with leading zero in fraction */ GNUNET_assert (GNUNET_OK == - TALER_string_to_amount ("eur:0.02", + TALER_string_to_amount ("EUR:0.02", &a2)); - GNUNET_assert (0 == strcasecmp ("eur", + GNUNET_assert (0 == strcasecmp ("EUR", a2.currency)); GNUNET_assert (0 == a2.value); GNUNET_assert (TALER_AMOUNT_FRAC_BASE / 100 * 2 == a2.fraction); c = TALER_amount_to_string (&a2); - GNUNET_assert (0 == strcmp ("eur:0.02", - c)); + GNUNET_assert (0 == strcasecmp ("EUR:0.02", + c)); GNUNET_free (c); /* test conversion with leading space and with fraction */ GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (" eur:4.12", + TALER_string_to_amount (" EUR:4.12", &a2)); - GNUNET_assert (0 == strcasecmp ("eur", + GNUNET_assert (0 == strcasecmp ("EUR", a2.currency)); GNUNET_assert (4 == a2.value); GNUNET_assert (TALER_AMOUNT_FRAC_BASE / 100 * 12 == a2.fraction); /* test use of local currency */ GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (" *LOCAL:4444.1000", + TALER_string_to_amount (" LOCAL:4444.1000", &a3)); - GNUNET_assert (0 == strcasecmp ("*LOCAL", + GNUNET_assert (0 == strcasecmp ("LOCAL", a3.currency)); GNUNET_assert (4444 == a3.value); GNUNET_assert (TALER_AMOUNT_FRAC_BASE / 10 == a3.fraction); diff --git a/src/util/test_conversion.c b/src/util/test_conversion.c new file mode 100644 index 000000000..00cb35e72 --- /dev/null +++ b/src/util/test_conversion.c @@ -0,0 +1,149 @@ +/* + This file is part of TALER + (C) 2023 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 util/test_conversion.c + * @brief Tests for conversion logic + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_util.h" +#include <gnunet/gnunet_json_lib.h> + +/** + * Return value from main(). + */ +static int global_ret; + +/** + * Handle to our helper. + */ +static struct TALER_JSON_ExternalConversion *ec; + + +/** + * Type of a callback that receives a JSON @a result. + * + * @param cls closure + * @param status_type how did the process die + * @apram code termination status code from the process + * @param result some JSON result, NULL if we failed to get an JSON output + */ +static void +conv_cb (void *cls, + enum GNUNET_OS_ProcessStatusType status_type, + unsigned long code, + const json_t *result) +{ + json_t *expect; + + (void) cls; + (void) status_type; + ec = NULL; + global_ret = 3; + if (42 != code) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected return value from helper: %u\n", + (unsigned int) code); + return; + } + expect = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("foo", + "arg") + ); + if (1 == json_equal (expect, + result)) + { + global_ret = 0; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected JSON result\n"); + json_dumpf (result, + stderr, + JSON_INDENT (2)); + global_ret = 4; + } + json_decref (expect); +} + + +/** + * Function called on shutdown/CTRL-C. + * + * @param cls NULL + */ +static void +do_shutdown (void *cls) +{ + (void) cls; + if (NULL != ec) + { + GNUNET_break (0); + global_ret = 2; + TALER_JSON_external_conversion_stop (ec); + ec = NULL; + } +} + + +/** + * Main test function. + * + * @param cls NULL + */ +static void +run (void *cls) +{ + json_t *input; + + (void) cls; + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + NULL); + input = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("key", + "foo") + ); + ec = TALER_JSON_external_conversion_start (input, + &conv_cb, + NULL, + "./test_conversion.sh", + "test_conversion.sh", + "arg", + NULL); + json_decref (input); + GNUNET_assert (NULL != ec); +} + + +int +main (int argc, + const char *const argv[]) +{ + (void) argc; + (void) argv; + unsetenv ("XDG_DATA_HOME"); + unsetenv ("XDG_CONFIG_HOME"); + GNUNET_log_setup ("test-conversion", + "WARNING", + NULL); + GNUNET_OS_init (TALER_project_data_default ()); + global_ret = 1; + GNUNET_SCHEDULER_run (&run, + NULL); + return global_ret; +} diff --git a/src/util/test_conversion.sh b/src/util/test_conversion.sh new file mode 100755 index 000000000..26e1a36d8 --- /dev/null +++ b/src/util/test_conversion.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +KEY=$(jq -r .key) +echo -n "{\"$KEY\":\"$1\"}" +exit 42 diff --git a/src/util/test_crypto.c b/src/util/test_crypto.c index 186874e3c..2a2090952 100644 --- a/src/util/test_crypto.c +++ b/src/util/test_crypto.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2015, 2020-2022 Taler Systems SA + (C) 2015, 2020-2023 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 @@ -21,7 +21,6 @@ */ #include "platform.h" #include "taler_util.h" -#include "taler_crypto_lib.h" /** @@ -38,14 +37,21 @@ test_high_level (void) struct TALER_TransferPublicKeyP trans_pub; struct TALER_TransferSecretP secret; struct TALER_TransferSecretP secret2; - union TALER_DenominationBlindingKeyP bks1; - union TALER_DenominationBlindingKeyP bks2; + union GNUNET_CRYPTO_BlindingSecretP bks1; + union GNUNET_CRYPTO_BlindingSecretP bks2; struct TALER_CoinSpendPrivateKeyP coin_priv1; struct TALER_CoinSpendPrivateKeyP coin_priv2; struct TALER_PlanchetMasterSecretP ps1; struct TALER_PlanchetMasterSecretP ps2; - struct TALER_ExchangeWithdrawValues alg1; - struct TALER_ExchangeWithdrawValues alg2; + struct GNUNET_CRYPTO_BlindingInputValues bi = { + .cipher = GNUNET_CRYPTO_BSA_RSA + }; + struct TALER_ExchangeWithdrawValues alg1 = { + .blinding_inputs = &bi + }; + struct TALER_ExchangeWithdrawValues alg2 = { + .blinding_inputs = &bi + }; GNUNET_CRYPTO_eddsa_key_create (&coin_priv.eddsa_priv); GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv, @@ -71,14 +77,12 @@ test_high_level (void) TALER_transfer_secret_to_planchet_secret (&secret, 0, &ps1); - alg1.cipher = TALER_DENOMINATION_RSA; TALER_planchet_setup_coin_priv (&ps1, &alg1, &coin_priv1); TALER_planchet_blinding_secret_create (&ps1, &alg1, &bks1); - alg2.cipher = TALER_DENOMINATION_RSA; TALER_transfer_secret_to_planchet_secret (&secret, 1, &ps2); @@ -117,31 +121,30 @@ test_planchets_rsa (uint8_t age) { struct TALER_PlanchetMasterSecretP ps; struct TALER_CoinSpendPrivateKeyP coin_priv; - union TALER_DenominationBlindingKeyP bks; + union GNUNET_CRYPTO_BlindingSecretP bks; struct TALER_DenominationPrivateKey dk_priv; struct TALER_DenominationPublicKey dk_pub; - struct TALER_ExchangeWithdrawValues alg_values; + const struct TALER_ExchangeWithdrawValues *alg_values; struct TALER_PlanchetDetail pd; struct TALER_BlindedDenominationSignature blind_sig; struct TALER_FreshCoin coin; struct TALER_CoinPubHashP c_hash; struct TALER_AgeCommitmentHash *ach = NULL; + struct TALER_AgeCommitmentHash ah = {0}; + alg_values = TALER_denom_ewv_rsa_singleton (); if (0 < age) { struct TALER_AgeCommitmentProof acp; - struct TALER_AgeCommitmentHash ah = {0}; struct GNUNET_HashCode seed; GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, &seed, sizeof(seed)); - - GNUNET_assert (GNUNET_OK == - TALER_age_restriction_commit (&age_mask, - age, - &seed, - &acp)); + TALER_age_restriction_commit (&age_mask, + age, + &seed, + &acp); TALER_age_commitment_hash (&acp.commitment, &ah); ach = &ah; @@ -151,12 +154,12 @@ test_planchets_rsa (uint8_t age) GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, &ps, sizeof (ps)); - + GNUNET_log_skip (1, GNUNET_YES); GNUNET_assert (GNUNET_SYSERR == TALER_denom_priv_create (&dk_priv, &dk_pub, - TALER_DENOMINATION_INVALID)); - + GNUNET_CRYPTO_BSA_INVALID)); + GNUNET_log_skip (1, GNUNET_YES); GNUNET_assert (GNUNET_SYSERR == TALER_denom_priv_create (&dk_priv, &dk_pub, @@ -165,19 +168,19 @@ test_planchets_rsa (uint8_t age) GNUNET_assert (GNUNET_OK == TALER_denom_priv_create (&dk_priv, &dk_pub, - TALER_DENOMINATION_RSA, + GNUNET_CRYPTO_BSA_RSA, 1024)); - alg_values.cipher = TALER_DENOMINATION_RSA; TALER_planchet_setup_coin_priv (&ps, - &alg_values, + alg_values, &coin_priv); TALER_planchet_blinding_secret_create (&ps, - &alg_values, + alg_values, &bks); GNUNET_assert (GNUNET_OK == TALER_planchet_prepare (&dk_pub, - &alg_values, + alg_values, &bks, + NULL, &coin_priv, ach, &c_hash, @@ -195,7 +198,7 @@ test_planchets_rsa (uint8_t age) &coin_priv, ach, &c_hash, - &alg_values, + alg_values, &coin)); TALER_blinded_denom_sig_free (&blind_sig); TALER_denom_sig_free (&coin.sig); @@ -206,39 +209,6 @@ test_planchets_rsa (uint8_t age) /** - * @brief Function for CS signatures to derive public R_0 and R_1 - * - * @param nonce withdraw nonce from a client - * @param denom_priv denomination privkey as long-term secret - * @param r_pub the resulting R_0 and R_1 - * @return enum GNUNET_GenericReturnValue - */ -static enum GNUNET_GenericReturnValue -derive_r_public ( - const struct TALER_CsNonce *nonce, - const struct TALER_DenominationPrivateKey *denom_priv, - struct TALER_DenominationCSPublicRPairP *r_pub) -{ - struct GNUNET_CRYPTO_CsRSecret r[2]; - - if (denom_priv->cipher != TALER_DENOMINATION_CS) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - GNUNET_CRYPTO_cs_r_derive (&nonce->nonce, - "rw", - &denom_priv->details.cs_private_key, - r); - GNUNET_CRYPTO_cs_r_get_public (&r[0], - &r_pub->r_pub[0]); - GNUNET_CRYPTO_cs_r_get_public (&r[1], - &r_pub->r_pub[1]); - return GNUNET_OK; -} - - -/** * Test the basic planchet functionality of creating a fresh planchet with CS denomination * and extracting the respective signature. * @@ -249,11 +219,12 @@ test_planchets_cs (uint8_t age) { struct TALER_PlanchetMasterSecretP ps; struct TALER_CoinSpendPrivateKeyP coin_priv; - union TALER_DenominationBlindingKeyP bks; + union GNUNET_CRYPTO_BlindingSecretP bks; struct TALER_DenominationPrivateKey dk_priv; struct TALER_DenominationPublicKey dk_pub; struct TALER_PlanchetDetail pd; struct TALER_CoinPubHashP c_hash; + union GNUNET_CRYPTO_BlindSessionNonce nonce; struct TALER_BlindedDenominationSignature blind_sig; struct TALER_FreshCoin coin; struct TALER_ExchangeWithdrawValues alg_values; @@ -268,12 +239,10 @@ test_planchets_cs (uint8_t age) GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, &seed, sizeof(seed)); - - GNUNET_assert (GNUNET_OK == - TALER_age_restriction_commit (&age_mask, - age, - &seed, - &acp)); + TALER_age_restriction_commit (&age_mask, + age, + &seed, + &acp); TALER_age_commitment_hash (&acp.commitment, &ah); ach = &ah; @@ -286,16 +255,17 @@ test_planchets_cs (uint8_t age) GNUNET_assert (GNUNET_OK == TALER_denom_priv_create (&dk_priv, &dk_pub, - TALER_DENOMINATION_CS)); - alg_values.cipher = TALER_DENOMINATION_CS; + GNUNET_CRYPTO_BSA_CS)); TALER_cs_withdraw_nonce_derive ( &ps, - &pd.blinded_planchet.details.cs_blinded_planchet.nonce); - GNUNET_assert (GNUNET_OK == - derive_r_public ( - &pd.blinded_planchet.details.cs_blinded_planchet.nonce, - &dk_priv, - &alg_values.details.cs_values)); + &nonce.cs_nonce); + // FIXME: define Taler abstraction for this: + alg_values.blinding_inputs + = GNUNET_CRYPTO_get_blinding_input_values (dk_priv.bsign_priv_key, + &nonce, + "rw"); + TALER_denom_pub_hash (&dk_pub, + &pd.denom_pub_hash); TALER_planchet_setup_coin_priv (&ps, &alg_values, &coin_priv); @@ -306,6 +276,7 @@ test_planchets_cs (uint8_t age) TALER_planchet_prepare (&dk_pub, &alg_values, &bks, + &nonce, &coin_priv, ach, &c_hash, @@ -315,7 +286,6 @@ test_planchets_cs (uint8_t age) &dk_priv, false, &pd.blinded_planchet)); - TALER_planchet_detail_free (&pd); GNUNET_assert (GNUNET_OK == TALER_planchet_to_coin (&dk_pub, &blind_sig, @@ -356,15 +326,24 @@ test_exchange_sigs (void) struct TALER_MasterPrivateKeyP priv; struct TALER_MasterPublicKeyP pub; struct TALER_MasterSignatureP sig; + json_t *rest; GNUNET_CRYPTO_eddsa_key_create (&priv.eddsa_priv); + rest = json_array (); + GNUNET_assert (NULL != rest); TALER_exchange_wire_signature_make (pt, + NULL, + rest, + rest, &priv, &sig); GNUNET_CRYPTO_eddsa_key_get_public (&priv.eddsa_priv, &pub.eddsa_pub); if (GNUNET_OK != TALER_exchange_wire_signature_check (pt, + NULL, + rest, + rest, &pub, &sig)) { @@ -374,12 +353,28 @@ test_exchange_sigs (void) if (GNUNET_OK == TALER_exchange_wire_signature_check ( "payto://x-taler-bank/localhost/Other", + NULL, + rest, + rest, &pub, &sig)) { GNUNET_break (0); return 1; } + if (GNUNET_OK == + TALER_exchange_wire_signature_check ( + pt, + "http://example.com/", + rest, + rest, + &pub, + &sig)) + { + GNUNET_break (0); + return 1; + } + json_decref (rest); return 0; } @@ -482,12 +477,51 @@ test_contracts (void) } +static int +test_attributes (void) +{ + struct TALER_AttributeEncryptionKeyP key; + void *eattr; + size_t eattr_size; + json_t *c; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &key, + sizeof (key)); + c = json_pack ("{s:s}", "test", "value"); + GNUNET_assert (NULL != c); + TALER_CRYPTO_kyc_attributes_encrypt (&key, + c, + &eattr, + &eattr_size); + json_decref (c); + c = TALER_CRYPTO_kyc_attributes_decrypt (&key, + eattr, + eattr_size); + GNUNET_free (eattr); + if (NULL == c) + { + GNUNET_break (0); + return 1; + } + GNUNET_assert (0 == + strcmp ("value", + json_string_value (json_object_get (c, + "test")))); + json_decref (c); + return 0; +} + + int main (int argc, const char *const argv[]) { (void) argc; (void) argv; + GNUNET_log_setup ("test-crypto", + "WARNING", + NULL); if (0 != test_high_level ()) return 1; if (0 != test_planchets (0)) @@ -500,6 +534,8 @@ main (int argc, return 5; if (0 != test_contracts ()) return 6; + if (0 != test_attributes ()) + return 7; return 0; } diff --git a/src/util/test_helper_cs.c b/src/util/test_helper_cs.c index 566f1d611..93562e459 100644 --- a/src/util/test_helper_cs.c +++ b/src/util/test_helper_cs.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2020, 2021 Taler Systems SA + (C) 2020, 2021, 2023 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 @@ -129,7 +129,7 @@ free_keys (void) * @param validity_duration how long does the key remain available for signing; * zero if the key has been revoked or purged * @param h_cs hash of the @a denom_pub that is available (or was purged) - * @param denom_pub the public key itself, NULL if the key was revoked or purged + * @param bs_pub the public key itself, NULL if the key was revoked or purged * @param sm_pub public key of the security module, NULL if the key was revoked or purged * @param sm_sig signature from the security module, NULL if the key was revoked or purged * The signature was already verified against @a sm_pub. @@ -140,7 +140,7 @@ key_cb (void *cls, struct GNUNET_TIME_Timestamp start_time, struct GNUNET_TIME_Relative validity_duration, const struct TALER_CsPubHashP *h_cs, - const struct TALER_DenominationPublicKey *denom_pub, + struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub, const struct TALER_SecurityModulePublicKeyP *sm_pub, const struct TALER_SecurityModuleSignatureP *sm_sig) { @@ -155,7 +155,7 @@ key_cb (void *cls, { bool found = false; - GNUNET_break (NULL == denom_pub); + GNUNET_break (NULL == bs_pub); GNUNET_break (NULL == section_name); for (unsigned int i = 0; i<MAX_KEYS; i++) if (0 == GNUNET_memcmp (h_cs, @@ -176,7 +176,7 @@ key_cb (void *cls, return; } - GNUNET_break (NULL != denom_pub); + GNUNET_break (NULL != bs_pub); for (unsigned int i = 0; i<MAX_KEYS; i++) if (! keys[i].valid) { @@ -184,8 +184,8 @@ key_cb (void *cls, keys[i].h_cs = *h_cs; keys[i].start_time = start_time; keys[i].validity_duration = validity_duration; - TALER_denom_pub_deep_copy (&keys[i].denom_pub, - denom_pub); + keys[i].denom_pub.bsign_pub_key + = GNUNET_CRYPTO_bsign_pub_incref (bs_pub); num_keys++; return; } @@ -268,9 +268,15 @@ test_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh) bool success = false; struct TALER_PlanchetMasterSecretP ps; struct TALER_CoinSpendPrivateKeyP coin_priv; - union TALER_DenominationBlindingKeyP bks; + union GNUNET_CRYPTO_BlindingSecretP bks; struct TALER_CoinPubHashP c_hash; - struct TALER_ExchangeWithdrawValues alg_values; + struct GNUNET_CRYPTO_BlindingInputValues bi = { + .cipher = GNUNET_CRYPTO_BSA_CS + }; + struct TALER_ExchangeWithdrawValues alg_values = { + .blinding_inputs = &bi + }; + union GNUNET_CRYPTO_BlindSessionNonce nonce; TALER_planchet_master_setup_random (&ps); for (unsigned int i = 0; i<MAX_KEYS; i++) @@ -279,21 +285,26 @@ test_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh) if (! keys[i].valid) continue; - GNUNET_assert (TALER_DENOMINATION_CS == - keys[i].denom_pub.cipher); - pd.blinded_planchet.cipher = TALER_DENOMINATION_CS; + GNUNET_assert (GNUNET_CRYPTO_BSA_CS == + keys[i].denom_pub.bsign_pub_key->cipher); TALER_cs_withdraw_nonce_derive ( &ps, - &pd.blinded_planchet.details.cs_blinded_planchet.nonce); + &nonce.cs_nonce); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Requesting R derivation with key %s\n", GNUNET_h2s (&keys[i].h_cs.hash)); - alg_values.cipher = TALER_DENOMINATION_CS; - ec = TALER_CRYPTO_helper_cs_r_derive_withdraw ( - dh, - &keys[i].h_cs, - &pd.blinded_planchet.details.cs_blinded_planchet.nonce, - &alg_values.details.cs_values); + { + struct TALER_CRYPTO_CsDeriveRequest cdr = { + .h_cs = &keys[i].h_cs, + .nonce = &nonce.cs_nonce + }; + + ec = TALER_CRYPTO_helper_cs_r_derive ( + dh, + &cdr, + false, + &bi.details.cs_values); + } switch (ec) { case TALER_EC_NONE: @@ -329,10 +340,12 @@ test_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh) TALER_planchet_prepare (&keys[i].denom_pub, &alg_values, &bks, + &nonce, &coin_priv, NULL, /* no age commitment */ &c_hash, &pd)); + TALER_blinded_planchet_free (&pd.blinded_planchet); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Successfully prepared planchet"); success = true; @@ -372,8 +385,11 @@ test_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh) /* check R derivation does not work if the key is unknown */ { struct TALER_CsPubHashP rnd; - struct TALER_CsNonce nonce; - struct TALER_DenominationCSPublicRPairP crp; + struct GNUNET_CRYPTO_CSPublicRPairP crp; + struct TALER_CRYPTO_CsDeriveRequest cdr = { + .h_cs = &rnd, + .nonce = &nonce.cs_nonce, + }; GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, &rnd, @@ -381,10 +397,10 @@ test_r_derive (struct TALER_CRYPTO_CsDenominationHelper *dh) GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, &nonce, sizeof (nonce)); - ec = TALER_CRYPTO_helper_cs_r_derive_withdraw (dh, - &rnd, - &nonce, - &crp); + ec = TALER_CRYPTO_helper_cs_r_derive (dh, + &cdr, + false, + &crp); if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec) { GNUNET_break (0); @@ -412,9 +428,15 @@ test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh) bool success = false; struct TALER_PlanchetMasterSecretP ps; struct TALER_CoinSpendPrivateKeyP coin_priv; - union TALER_DenominationBlindingKeyP bks; + union GNUNET_CRYPTO_BlindingSecretP bks; struct TALER_CoinPubHashP c_hash; - struct TALER_ExchangeWithdrawValues alg_values; + struct GNUNET_CRYPTO_BlindingInputValues bi = { + .cipher = GNUNET_CRYPTO_BSA_CS + }; + struct TALER_ExchangeWithdrawValues alg_values = { + .blinding_inputs = &bi + }; + union GNUNET_CRYPTO_BlindSessionNonce nonce; TALER_planchet_master_setup_random (&ps); for (unsigned int i = 0; i<MAX_KEYS; i++) @@ -423,21 +445,19 @@ test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh) continue; { struct TALER_PlanchetDetail pd; - - pd.blinded_planchet.cipher = TALER_DENOMINATION_CS; - // keys[i].denom_pub.cipher = TALER_DENOMINATION_CS; + struct TALER_CRYPTO_CsSignRequest csr; + struct TALER_CRYPTO_CsDeriveRequest cdr = { + .h_cs = &keys[i].h_cs, + .nonce = &nonce.cs_nonce + }; TALER_cs_withdraw_nonce_derive (&ps, - &pd.blinded_planchet.details. - cs_blinded_planchet.nonce); - alg_values.cipher = TALER_DENOMINATION_CS; - ec = TALER_CRYPTO_helper_cs_r_derive_withdraw ( + &nonce.cs_nonce); + ec = TALER_CRYPTO_helper_cs_r_derive ( dh, - &keys[i].h_cs, - &pd.blinded_planchet. - details. - cs_blinded_planchet.nonce, - &alg_values.details.cs_values); + &cdr, + false, + &bi.details.cs_values); if (TALER_EC_NONE != ec) continue; TALER_planchet_setup_coin_priv (&ps, @@ -446,11 +466,11 @@ test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh) TALER_planchet_blinding_secret_create (&ps, &alg_values, &bks); - GNUNET_assert (GNUNET_YES == TALER_planchet_prepare (&keys[i].denom_pub, &alg_values, &bks, + &nonce, &coin_priv, NULL, /* no age commitment */ &c_hash, @@ -458,12 +478,15 @@ test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh) GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Requesting signature with key %s\n", GNUNET_h2s (&keys[i].h_cs.hash)); - ec = TALER_CRYPTO_helper_cs_sign_withdraw ( + csr.h_cs = &keys[i].h_cs; + csr.blinded_planchet + = &pd.blinded_planchet.blinded_message->details.cs_blinded_message; + ec = TALER_CRYPTO_helper_cs_sign ( dh, - &keys[i].h_cs, - &pd.blinded_planchet.details. - cs_blinded_planchet, + &csr, + false, &ds); + TALER_blinded_planchet_free (&pd.blinded_planchet); } switch (ec) { @@ -475,6 +498,7 @@ test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh) { /* key worked too early */ GNUNET_break (0); + TALER_blinded_denom_sig_free (&ds); return 4; } if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration ( @@ -484,6 +508,7 @@ test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh) { /* key worked too later */ GNUNET_break (0); + TALER_blinded_denom_sig_free (&ds); return 5; } { @@ -500,8 +525,11 @@ test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh) &coin)) { GNUNET_break (0); + TALER_blinded_denom_sig_free (&ds); return 6; } + TALER_blinded_denom_sig_free (&ds); + TALER_denom_sig_free (&coin.sig); } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received valid signature for key %s\n", @@ -544,25 +572,29 @@ test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh) { struct TALER_PlanchetDetail pd; struct TALER_CsPubHashP rnd; + struct TALER_CRYPTO_CsSignRequest csr; GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, &rnd, sizeof (rnd)); - pd.blinded_planchet.cipher = TALER_DENOMINATION_CS; GNUNET_assert (GNUNET_YES == TALER_planchet_prepare (&keys[0].denom_pub, &alg_values, &bks, + &nonce, &coin_priv, NULL, /* no age commitment */ &c_hash, &pd)); - - ec = TALER_CRYPTO_helper_cs_sign_withdraw ( + csr.h_cs = &rnd; + csr.blinded_planchet + = &pd.blinded_planchet.blinded_message->details.cs_blinded_message; + ec = TALER_CRYPTO_helper_cs_sign ( dh, - &rnd, - &pd.blinded_planchet.details.cs_blinded_planchet, + &csr, + false, &ds); + TALER_blinded_planchet_free (&pd.blinded_planchet); if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec) { if (TALER_EC_NONE == ec) @@ -579,6 +611,226 @@ test_signing (struct TALER_CRYPTO_CsDenominationHelper *dh) /** + * Test batch signing logic. + * + * @param dh handle to the helper + * @param batch_size how large should the batch be + * @param check_sigs also check unknown key and signatures + * @return 0 on success + */ +static int +test_batch_signing (struct TALER_CRYPTO_CsDenominationHelper *dh, + unsigned int batch_size, + bool check_sigs) +{ + struct TALER_BlindedDenominationSignature ds[batch_size]; + enum TALER_ErrorCode ec; + bool success = false; + struct TALER_PlanchetMasterSecretP ps[batch_size]; + struct TALER_CoinSpendPrivateKeyP coin_priv[batch_size]; + union GNUNET_CRYPTO_BlindingSecretP bks[batch_size]; + struct TALER_CoinPubHashP c_hash[batch_size]; + struct GNUNET_CRYPTO_BlindingInputValues bi[batch_size]; + struct TALER_ExchangeWithdrawValues alg_values[batch_size]; + union GNUNET_CRYPTO_BlindSessionNonce nonces[batch_size]; + + for (unsigned int i = 0; i<batch_size; i++) + TALER_planchet_master_setup_random (&ps[i]); + for (unsigned int k = 0; k<MAX_KEYS; k++) + { + if (! keys[k].valid) + continue; + { + struct TALER_PlanchetDetail pd[batch_size]; + struct TALER_CRYPTO_CsSignRequest csr[batch_size]; + struct TALER_CRYPTO_CsDeriveRequest cdr[batch_size]; + struct GNUNET_CRYPTO_CSPublicRPairP crps[batch_size]; + + for (unsigned int i = 0; i<batch_size; i++) + { + cdr[i].h_cs = &keys[k].h_cs; + cdr[i].nonce = &nonces[i].cs_nonce; + TALER_cs_withdraw_nonce_derive ( + &ps[i], + &nonces[i].cs_nonce); + bi[i].cipher = GNUNET_CRYPTO_BSA_CS; + alg_values[i].blinding_inputs = &bi[i]; + } + ec = TALER_CRYPTO_helper_cs_r_batch_derive ( + dh, + batch_size, + cdr, + false, + crps); + if (TALER_EC_NONE != ec) + continue; + for (unsigned int i = 0; i<batch_size; i++) + { + bi[i].details.cs_values = crps[i]; + TALER_planchet_setup_coin_priv (&ps[i], + &alg_values[i], + &coin_priv[i]); + TALER_planchet_blinding_secret_create (&ps[i], + &alg_values[i], + &bks[i]); + GNUNET_assert (GNUNET_YES == + TALER_planchet_prepare (&keys[k].denom_pub, + &alg_values[i], + &bks[i], + &nonces[i], + &coin_priv[i], + NULL, /* no age commitment */ + &c_hash[i], + &pd[i])); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Requesting signature with key %s\n", + GNUNET_h2s (&keys[k].h_cs.hash)); + csr[i].h_cs = &keys[k].h_cs; + csr[i].blinded_planchet + = &pd[i].blinded_planchet.blinded_message->details.cs_blinded_message; + } + ec = TALER_CRYPTO_helper_cs_batch_sign ( + dh, + batch_size, + csr, + false, + ds); + for (unsigned int i = 0; i<batch_size; i++) + { + TALER_blinded_planchet_free (&pd[i].blinded_planchet); + } + } + switch (ec) + { + case TALER_EC_NONE: + if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining ( + keys[k].start_time.abs_time), + >, + GNUNET_TIME_UNIT_SECONDS)) + { + /* key worked too early */ + GNUNET_break (0); + return 4; + } + if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration ( + keys[k].start_time.abs_time), + >, + keys[k].validity_duration)) + { + /* key worked too later */ + GNUNET_break (0); + return 5; + } + if (check_sigs) + { + for (unsigned int i = 0; i<batch_size; i++) + { + struct TALER_FreshCoin coin; + + if (GNUNET_OK != + TALER_planchet_to_coin (&keys[k].denom_pub, + &ds[i], + &bks[i], + &coin_priv[i], + NULL, /* no age commitment */ + &c_hash[i], + &alg_values[i], + &coin)) + { + GNUNET_break (0); + return 6; + } + TALER_blinded_denom_sig_free (&ds[i]); + TALER_denom_sig_free (&coin.sig); + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received valid signature for key %s\n", + GNUNET_h2s (&keys[k].h_cs.hash)); + } + else + { + for (unsigned int i = 0; i<batch_size; i++) + TALER_blinded_denom_sig_free (&ds[i]); + } + success = true; + break; + case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY: + /* This 'failure' is expected, we're testing also for the + error handling! */ + if ( (GNUNET_TIME_relative_is_zero ( + GNUNET_TIME_absolute_get_remaining ( + keys[k].start_time.abs_time))) && + (GNUNET_TIME_relative_cmp ( + GNUNET_TIME_absolute_get_duration ( + keys[k].start_time.abs_time), + <, + keys[k].validity_duration)) ) + { + /* key should have worked! */ + GNUNET_break (0); + return 6; + } + break; + default: + /* unexpected error */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected error %d\n", + ec); + return 7; + } + } + if (! success) + { + /* no valid key for signing found, also bad */ + GNUNET_break (0); + return 16; + } + + /* check signing does not work if the key is unknown */ + if (check_sigs) + { + struct TALER_PlanchetDetail pd; + struct TALER_CsPubHashP rnd; + struct TALER_CRYPTO_CsSignRequest csr; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &rnd, + sizeof (rnd)); + GNUNET_assert (GNUNET_YES == + TALER_planchet_prepare (&keys[0].denom_pub, + &alg_values[0], + &bks[0], + &nonces[0], + &coin_priv[0], + NULL, /* no age commitment */ + &c_hash[0], + &pd)); + csr.h_cs = &rnd; + csr.blinded_planchet + = &pd.blinded_planchet.blinded_message->details.cs_blinded_message; + ec = TALER_CRYPTO_helper_cs_batch_sign ( + dh, + 1, + &csr, + false, + &ds[0]); + TALER_blinded_planchet_free (&pd.blinded_planchet); + if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec) + { + if (TALER_EC_NONE == ec) + TALER_blinded_denom_sig_free (&ds[0]); + GNUNET_break (0); + return 17; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Signing with invalid key %s failed as desired\n", + GNUNET_h2s (&rnd.hash)); + } + return 0; +} + + +/** * Benchmark signing logic. * * @param dh handle to the helper @@ -593,8 +845,13 @@ perf_signing (struct TALER_CRYPTO_CsDenominationHelper *dh, struct GNUNET_TIME_Relative duration; struct TALER_PlanchetMasterSecretP ps; struct TALER_CoinSpendPrivateKeyP coin_priv; - union TALER_DenominationBlindingKeyP bks; - struct TALER_ExchangeWithdrawValues alg_values; + union GNUNET_CRYPTO_BlindingSecretP bks; + struct GNUNET_CRYPTO_BlindingInputValues bv = { + .cipher = GNUNET_CRYPTO_BSA_CS + }; + struct TALER_ExchangeWithdrawValues alg_values = { + .blinding_inputs = &bv + }; TALER_planchet_master_setup_random (&ps); duration = GNUNET_TIME_UNIT_ZERO; @@ -618,19 +875,20 @@ perf_signing (struct TALER_CRYPTO_CsDenominationHelper *dh, { struct TALER_CoinPubHashP c_hash; struct TALER_PlanchetDetail pd; - - pd.blinded_planchet.cipher = TALER_DENOMINATION_CS; - TALER_cs_withdraw_nonce_derive (&ps, - &pd.blinded_planchet.details. - cs_blinded_planchet.nonce); - alg_values.cipher = TALER_DENOMINATION_CS; - ec = TALER_CRYPTO_helper_cs_r_derive_melt ( + union GNUNET_CRYPTO_BlindSessionNonce nonce; + struct TALER_CRYPTO_CsDeriveRequest cdr = { + .h_cs = &keys[i].h_cs, + .nonce = &nonce.cs_nonce + }; + + TALER_cs_withdraw_nonce_derive ( + &ps, + &nonce.cs_nonce); + ec = TALER_CRYPTO_helper_cs_r_derive ( dh, - &keys[i].h_cs, - &pd.blinded_planchet. - details. - cs_blinded_planchet.nonce, - &alg_values.details.cs_values); + &cdr, + true, + &bv.details.cs_values); if (TALER_EC_NONE != ec) continue; TALER_planchet_setup_coin_priv (&ps, @@ -643,6 +901,7 @@ perf_signing (struct TALER_CRYPTO_CsDenominationHelper *dh, TALER_planchet_prepare (&keys[i].denom_pub, &alg_values, &bks, + &nonce, &coin_priv, NULL, /* no age commitment */ &c_hash, @@ -652,12 +911,15 @@ perf_signing (struct TALER_CRYPTO_CsDenominationHelper *dh, { struct GNUNET_TIME_Absolute start = GNUNET_TIME_absolute_get (); struct GNUNET_TIME_Relative delay; + struct TALER_CRYPTO_CsSignRequest csr; - ec = TALER_CRYPTO_helper_cs_sign_melt ( + csr.h_cs = &keys[i].h_cs; + csr.blinded_planchet + = &pd.blinded_planchet.blinded_message->details.cs_blinded_message; + ec = TALER_CRYPTO_helper_cs_sign ( dh, - &keys[i].h_cs, - &pd.blinded_planchet.details. - cs_blinded_planchet, + &csr, + true, &ds); if (TALER_EC_NONE != ec) break; @@ -669,9 +931,10 @@ perf_signing (struct TALER_CRYPTO_CsDenominationHelper *dh, if (NUM_SIGN_PERFS <= j) break; } + TALER_blinded_planchet_free (&pd.blinded_planchet); } - } /* for i */ - } /* for j */ + } /* for i */ + } /* for j */ fprintf (stderr, "%u (%s) signature operations took %s\n", (unsigned int) NUM_SIGN_PERFS, @@ -707,6 +970,7 @@ par_signing (struct GNUNET_CONFIGURATION_Handle *cfg) int ret; dh = TALER_CRYPTO_helper_cs_connect (cfg, + "taler-exchange", &key_cb, NULL); GNUNET_assert (NULL != dh); @@ -764,6 +1028,7 @@ run_test (void) nanosleep (&req, NULL); dh = TALER_CRYPTO_helper_cs_connect (cfg, + "taler-exchange", &key_cb, NULL); if (NULL != dh) @@ -796,6 +1061,34 @@ run_test (void) if (0 == ret) ret = test_signing (dh); if (0 == ret) + ret = test_batch_signing (dh, + 2, + true); + if (0 == ret) + ret = test_batch_signing (dh, + 256, + true); + for (unsigned int i = 0; i<5; i++) + { + static unsigned int batches[] = { 1, 4, 16, 64, 256 }; + unsigned int batch_size = batches[i]; + struct GNUNET_TIME_Absolute start; + struct GNUNET_TIME_Relative duration; + + start = GNUNET_TIME_absolute_get (); + if (0 != ret) + break; + ret = test_batch_signing (dh, + batch_size, + false); + duration = GNUNET_TIME_absolute_get_duration (start); + fprintf (stderr, + "%4u (batch) signature operations took %s (total real time)\n", + (unsigned int) batch_size, + GNUNET_STRINGS_relative_time_to_string (duration, + GNUNET_YES)); + } + if (0 == ret) ret = perf_signing (dh, "sequential"); TALER_CRYPTO_helper_cs_disconnect (dh); @@ -818,13 +1111,14 @@ main (int argc, int ret; enum GNUNET_OS_ProcessStatusType type; unsigned long code; + const char *loglev = "WARNING"; (void) argc; (void) argv; unsetenv ("XDG_DATA_HOME"); unsetenv ("XDG_CONFIG_HOME"); GNUNET_log_setup ("test-helper-cs", - "WARNING", + loglev, NULL); GNUNET_OS_init (TALER_project_data_default ()); libexec_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR); @@ -840,7 +1134,7 @@ main (int argc, "-c", "test_helper_cs.conf", "-L", - "WARNING", + loglev, NULL); if (NULL == helper) { diff --git a/src/util/test_helper_eddsa.c b/src/util/test_helper_eddsa.c index da1c51b46..0119e4278 100644 --- a/src/util/test_helper_eddsa.c +++ b/src/util/test_helper_eddsa.c @@ -365,6 +365,7 @@ par_signing (struct GNUNET_CONFIGURATION_Handle *cfg) int ret; esh = TALER_CRYPTO_helper_esign_connect (cfg, + "taler-exchange", &key_cb, NULL); if (NULL == esh) @@ -427,6 +428,7 @@ run_test (void) nanosleep (&req, NULL); esh = TALER_CRYPTO_helper_esign_connect (cfg, + "taler-exchange", &key_cb, NULL); if (NULL != esh) diff --git a/src/util/test_helper_rsa.c b/src/util/test_helper_rsa.c index 3f3eafddb..2bc15879f 100644 --- a/src/util/test_helper_rsa.c +++ b/src/util/test_helper_rsa.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2020, 2021 Taler Systems SA + (C) 2020, 2021, 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 @@ -129,7 +129,7 @@ free_keys (void) * @param validity_duration how long does the key remain available for signing; * zero if the key has been revoked or purged * @param h_rsa hash of the @a denom_pub that is available (or was purged) - * @param denom_pub the public key itself, NULL if the key was revoked or purged + * @param bs_pub the public key itself, NULL if the key was revoked or purged * @param sm_pub public key of the security module, NULL if the key was revoked or purged * @param sm_sig signature from the security module, NULL if the key was revoked or purged * The signature was already verified against @a sm_pub. @@ -140,7 +140,7 @@ key_cb (void *cls, struct GNUNET_TIME_Timestamp start_time, struct GNUNET_TIME_Relative validity_duration, const struct TALER_RsaPubHashP *h_rsa, - const struct TALER_DenominationPublicKey *denom_pub, + struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub, const struct TALER_SecurityModulePublicKeyP *sm_pub, const struct TALER_SecurityModuleSignatureP *sm_sig) { @@ -155,7 +155,7 @@ key_cb (void *cls, { bool found = false; - GNUNET_break (NULL == denom_pub); + GNUNET_break (NULL == bs_pub); GNUNET_break (NULL == section_name); for (unsigned int i = 0; i<MAX_KEYS; i++) if (0 == GNUNET_memcmp (h_rsa, @@ -176,7 +176,7 @@ key_cb (void *cls, return; } - GNUNET_break (NULL != denom_pub); + GNUNET_break (NULL != bs_pub); for (unsigned int i = 0; i<MAX_KEYS; i++) if (! keys[i].valid) { @@ -184,8 +184,8 @@ key_cb (void *cls, keys[i].h_rsa = *h_rsa; keys[i].start_time = start_time; keys[i].validity_duration = validity_duration; - TALER_denom_pub_deep_copy (&keys[i].denom_pub, - denom_pub); + keys[i].denom_pub.bsign_pub_key + = GNUNET_CRYPTO_bsign_pub_incref (bs_pub); num_keys++; return; } @@ -268,19 +268,22 @@ test_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh) enum TALER_ErrorCode ec; bool success = false; struct TALER_PlanchetMasterSecretP ps; - struct TALER_ExchangeWithdrawValues alg_values; + const struct TALER_ExchangeWithdrawValues *alg_values + = TALER_denom_ewv_rsa_singleton (); struct TALER_AgeCommitmentHash ach; struct TALER_CoinPubHashP c_hash; struct TALER_CoinSpendPrivateKeyP coin_priv; - union TALER_DenominationBlindingKeyP bks; + union GNUNET_CRYPTO_BlindingSecretP bks; GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, &ps, sizeof (ps)); - - alg_values.cipher = TALER_DENOMINATION_RSA; - TALER_planchet_setup_coin_priv (&ps, &alg_values, &coin_priv); - TALER_planchet_blinding_secret_create (&ps, &alg_values, &bks); + TALER_planchet_setup_coin_priv (&ps, + alg_values, + &coin_priv); + TALER_planchet_blinding_secret_create (&ps, + alg_values, + &bks); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, &ach, sizeof(ach)); @@ -289,17 +292,17 @@ test_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh) { if (! keys[i].valid) continue; - if (TALER_DENOMINATION_RSA != keys[i].denom_pub.cipher) + if (GNUNET_CRYPTO_BSA_RSA != + keys[i].denom_pub.bsign_pub_key->cipher) continue; { - struct TALER_PlanchetDetail pd = { - .blinded_planchet.cipher = TALER_DENOMINATION_RSA - }; + struct TALER_PlanchetDetail pd; GNUNET_assert (GNUNET_YES == TALER_planchet_prepare (&keys[i].denom_pub, - &alg_values, + alg_values, &bks, + NULL, &coin_priv, &ach, &c_hash, @@ -308,9 +311,11 @@ test_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh) struct TALER_CRYPTO_RsaSignRequest rsr = { .h_rsa = &keys[i].h_rsa, .msg = - pd.blinded_planchet.details.rsa_blinded_planchet.blinded_msg, + pd.blinded_planchet.blinded_message->details.rsa_blinded_message. + blinded_msg, .msg_size = - pd.blinded_planchet.details.rsa_blinded_planchet.blinded_msg_size + pd.blinded_planchet.blinded_message->details.rsa_blinded_message. + blinded_msg_size }; GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -352,7 +357,7 @@ test_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh) &ds, &bks, &c_hash, - &alg_values, + alg_values, &keys[i].denom_pub)) { GNUNET_break (0); @@ -441,6 +446,227 @@ test_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh) /** + * Test batch signing logic. + * + * @param dh handle to the helper + * @param batch_size how large should the batch be + * @param check_sigs also check unknown key and signatures + * @return 0 on success + */ +static int +test_batch_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh, + unsigned int batch_size, + bool check_sigs) +{ + struct TALER_BlindedDenominationSignature ds[batch_size]; + enum TALER_ErrorCode ec; + bool success = false; + struct TALER_PlanchetMasterSecretP ps[batch_size]; + const struct TALER_ExchangeWithdrawValues *alg_values; + struct TALER_AgeCommitmentHash ach[batch_size]; + struct TALER_CoinPubHashP c_hash[batch_size]; + struct TALER_CoinSpendPrivateKeyP coin_priv[batch_size]; + union GNUNET_CRYPTO_BlindingSecretP bks[batch_size]; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, + &ps, + sizeof (ps)); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &ach, + sizeof(ach)); + alg_values = TALER_denom_ewv_rsa_singleton (); + for (unsigned int i = 0; i<batch_size; i++) + { + TALER_planchet_setup_coin_priv (&ps[i], + alg_values, + &coin_priv[i]); + TALER_planchet_blinding_secret_create (&ps[i], + alg_values, + &bks[i]); + } + for (unsigned int k = 0; k<MAX_KEYS; k++) + { + if (success && ! check_sigs) + break; /* only do one round */ + if (! keys[k].valid) + continue; + if (GNUNET_CRYPTO_BSA_RSA != + keys[k].denom_pub.bsign_pub_key->cipher) + continue; + { + struct TALER_PlanchetDetail pd[batch_size]; + struct TALER_CRYPTO_RsaSignRequest rsr[batch_size]; + + for (unsigned int i = 0; i<batch_size; i++) + { + GNUNET_assert (GNUNET_YES == + TALER_planchet_prepare (&keys[k].denom_pub, + alg_values, + &bks[i], + NULL, + &coin_priv[i], + &ach[i], + &c_hash[i], + &pd[i])); + rsr[i].h_rsa + = &keys[k].h_rsa; + rsr[i].msg + = pd[i].blinded_planchet.blinded_message->details.rsa_blinded_message. + blinded_msg; + rsr[i].msg_size + = pd[i].blinded_planchet.blinded_message->details.rsa_blinded_message. + blinded_msg_size; + } + ec = TALER_CRYPTO_helper_rsa_batch_sign (dh, + batch_size, + rsr, + ds); + for (unsigned int i = 0; i<batch_size; i++) + { + if (TALER_EC_NONE == ec) + GNUNET_break (GNUNET_CRYPTO_BSA_RSA == + ds[i].blinded_sig->cipher); + TALER_blinded_planchet_free (&pd[i].blinded_planchet); + } + } + switch (ec) + { + case TALER_EC_NONE: + if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining ( + keys[k].start_time.abs_time), + >, + GNUNET_TIME_UNIT_SECONDS)) + { + /* key worked too early */ + GNUNET_break (0); + return 4; + } + if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_duration ( + keys[k].start_time.abs_time), + >, + keys[k].validity_duration)) + { + /* key worked too later */ + GNUNET_break (0); + return 5; + } + for (unsigned int i = 0; i<batch_size; i++) + { + struct TALER_DenominationSignature rs; + + if (check_sigs) + { + if (GNUNET_OK != + TALER_denom_sig_unblind (&rs, + &ds[i], + &bks[i], + &c_hash[i], + alg_values, + &keys[k].denom_pub)) + { + GNUNET_break (0); + return 6; + } + } + TALER_blinded_denom_sig_free (&ds[i]); + if (check_sigs) + { + if (GNUNET_OK != + TALER_denom_pub_verify (&keys[k].denom_pub, + &rs, + &c_hash[i])) + { + /* signature invalid */ + GNUNET_break (0); + TALER_denom_sig_free (&rs); + return 7; + } + TALER_denom_sig_free (&rs); + } + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received valid signature for key %s\n", + GNUNET_h2s (&keys[k].h_rsa.hash)); + success = true; + break; + case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY: + /* This 'failure' is expected, we're testing also for the + error handling! */ + for (unsigned int i = 0; i<batch_size; i++) + TALER_blinded_denom_sig_free (&ds[i]); + if ( (GNUNET_TIME_relative_is_zero ( + GNUNET_TIME_absolute_get_remaining ( + keys[k].start_time.abs_time))) && + (GNUNET_TIME_relative_cmp ( + GNUNET_TIME_absolute_get_duration ( + keys[k].start_time.abs_time), + <, + keys[k].validity_duration)) ) + { + /* key should have worked! */ + GNUNET_break (0); + return 6; + } + break; + case TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN: + for (unsigned int i = 0; i<batch_size; i++) + TALER_blinded_denom_sig_free (&ds[i]); + break; + default: + /* unexpected error */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected error %d at %s:%u\n", + ec, + __FILE__, + __LINE__); + for (unsigned int i = 0; i<batch_size; i++) + TALER_blinded_denom_sig_free (&ds[i]); + return 7; + } + } + if (! success) + { + /* no valid key for signing found, also bad */ + GNUNET_break (0); + return 16; + } + + /* check signing does not work if the key is unknown */ + if (check_sigs) + { + struct TALER_RsaPubHashP rnd; + struct TALER_CRYPTO_RsaSignRequest rsr = { + .h_rsa = &rnd, + .msg = "Hello", + .msg_size = strlen ("Hello") + }; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &rnd, + sizeof (rnd)); + ec = TALER_CRYPTO_helper_rsa_batch_sign (dh, + 1, + &rsr, + ds); + if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Signing with invalid key returned unexpected status %d\n", + ec); + if (TALER_EC_NONE == ec) + TALER_blinded_denom_sig_free (ds); + GNUNET_break (0); + return 17; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Signing with invalid key %s failed as desired\n", + GNUNET_h2s (&rnd.hash)); + } + return 0; +} + + +/** * Benchmark signing logic. * * @param dh handle to the helper @@ -456,13 +682,17 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh, struct TALER_PlanchetMasterSecretP ps; struct TALER_CoinSpendPrivateKeyP coin_priv; struct TALER_AgeCommitmentHash ach; - union TALER_DenominationBlindingKeyP bks; - struct TALER_ExchangeWithdrawValues alg_values; + union GNUNET_CRYPTO_BlindingSecretP bks; + const struct TALER_ExchangeWithdrawValues *alg_values + = TALER_denom_ewv_rsa_singleton (); TALER_planchet_master_setup_random (&ps); - alg_values.cipher = TALER_DENOMINATION_RSA; - TALER_planchet_setup_coin_priv (&ps, &alg_values, &coin_priv); - TALER_planchet_blinding_secret_create (&ps, &alg_values, &bks); + TALER_planchet_setup_coin_priv (&ps, + alg_values, + &coin_priv); + TALER_planchet_blinding_secret_create (&ps, + alg_values, + &bks); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, &ach, sizeof(ach)); @@ -474,7 +704,8 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh, { if (! keys[i].valid) continue; - if (TALER_DENOMINATION_RSA != keys[i].denom_pub.cipher) + if (GNUNET_CRYPTO_BSA_RSA != + keys[i].denom_pub.bsign_pub_key->cipher) continue; if (GNUNET_TIME_relative_cmp (GNUNET_TIME_absolute_get_remaining ( keys[i].start_time.abs_time), @@ -492,8 +723,9 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh, GNUNET_assert (GNUNET_YES == TALER_planchet_prepare (&keys[i].denom_pub, - &alg_values, + alg_values, &bks, + NULL, &coin_priv, &ach, &c_hash, @@ -506,9 +738,11 @@ perf_signing (struct TALER_CRYPTO_RsaDenominationHelper *dh, struct TALER_CRYPTO_RsaSignRequest rsr = { .h_rsa = &keys[i].h_rsa, .msg = - pd.blinded_planchet.details.rsa_blinded_planchet.blinded_msg, + pd.blinded_planchet.blinded_message->details.rsa_blinded_message. + blinded_msg, .msg_size = - pd.blinded_planchet.details.rsa_blinded_planchet.blinded_msg_size + pd.blinded_planchet.blinded_message->details.rsa_blinded_message. + blinded_msg_size }; ec = TALER_CRYPTO_helper_rsa_sign (dh, @@ -563,6 +797,7 @@ par_signing (struct GNUNET_CONFIGURATION_Handle *cfg) int ret; dh = TALER_CRYPTO_helper_rsa_connect (cfg, + "taler-exchange", &key_cb, NULL); GNUNET_assert (NULL != dh); @@ -614,12 +849,14 @@ run_test (void) return 77; } - fprintf (stderr, "Waiting for helper to start ... "); + fprintf (stderr, + "Waiting for helper to start ... "); for (unsigned int i = 0; i<100; i++) { nanosleep (&req, NULL); dh = TALER_CRYPTO_helper_rsa_connect (cfg, + "taler-exchange", &key_cb, NULL); if (NULL != dh) @@ -650,6 +887,34 @@ run_test (void) if (0 == ret) ret = test_signing (dh); if (0 == ret) + ret = test_batch_signing (dh, + 2, + true); + if (0 == ret) + ret = test_batch_signing (dh, + 256, + true); + for (unsigned int i = 0; i<5; i++) + { + static unsigned int batches[] = { 1, 4, 16, 64, 256 }; + unsigned int batch_size = batches[i]; + struct GNUNET_TIME_Absolute start; + struct GNUNET_TIME_Relative duration; + + start = GNUNET_TIME_absolute_get (); + if (0 != ret) + break; + ret = test_batch_signing (dh, + batch_size, + false); + duration = GNUNET_TIME_absolute_get_duration (start); + fprintf (stderr, + "%4u (batch) signature operations took %s (total real time)\n", + (unsigned int) batch_size, + GNUNET_STRINGS_relative_time_to_string (duration, + GNUNET_YES)); + } + if (0 == ret) ret = perf_signing (dh, "sequential"); TALER_CRYPTO_helper_rsa_disconnect (dh); diff --git a/src/util/test_payto.c b/src/util/test_payto.c index 4dc73a964..62ba7d28e 100644 --- a/src/util/test_payto.c +++ b/src/util/test_payto.c @@ -22,16 +22,16 @@ #include "taler_util.h" #define CHECK(a,b) do { \ - GNUNET_assert (a != NULL); \ - GNUNET_assert (b != NULL); \ - if (0 != strcmp (a,b)) { \ - GNUNET_break (0); \ - fprintf (stderr, "Got %s, wanted %s\n", b, a); \ - GNUNET_free (b); \ - return 1; \ - } else { \ - GNUNET_free (b); \ - } \ + GNUNET_assert (a != NULL); \ + GNUNET_assert (b != NULL); \ + if (0 != strcmp (a,b)) { \ + GNUNET_break (0); \ + fprintf (stderr, "Got %s, wanted %s\n", b, a); \ + GNUNET_free (b); \ + return 1; \ + } else { \ + GNUNET_free (b); \ + } \ } while (0) @@ -50,11 +50,55 @@ main (int argc, TALER_iban_validate ("FR1420041010050500013M02606")); GNUNET_assert (NULL == TALER_iban_validate ("DE89370400440532013000")); + r = TALER_payto_validate ( + "payto://x-taler-bank/hostname/username?receiver-name=foo"); + GNUNET_assert (NULL == r); + r = TALER_payto_validate ( + "payto://x-taler-bank/hostname/~path/username?receiver-name=foo"); + GNUNET_assert (NULL == r); + r = TALER_payto_validate ( + "payto://x-taler-bank/hostname/~path/username?receiver-name=fo/o"); + GNUNET_assert (NULL == r); + r = TALER_payto_validate ( + "payto://x-taler-bank/hostname/path/username?receiver-name=foo"); + GNUNET_assert (NULL == r); + r = TALER_payto_validate ( + "payto://x-taler-bank/https://hostname/username?receiver-name=foo"); + GNUNET_assert (NULL != r); + GNUNET_free (r); + r = TALER_payto_validate ( + "payto://x-taler-bank/hostname:4a2/path/username?receiver-name=foo"); + GNUNET_assert (NULL != r); + GNUNET_free (r); + r = TALER_payto_validate ( + "payto://x-taler-bank/-hostname/username?receiver-name=foo"); + GNUNET_assert (NULL != r); + GNUNET_free (r); + r = TALER_payto_validate ( + "payto://x-taler-bank/domain..name/username?receiver-name=foo"); + GNUNET_assert (NULL != r); + GNUNET_free (r); + r = TALER_payto_validate ( + "payto://x-taler-bank/domain..name/?receiver-name=foo"); + GNUNET_assert (NULL != r); + GNUNET_free (r); + r = TALER_payto_validate ( + "payto://x-taler-bank/domain.name/username"); + GNUNET_assert (NULL != r); + GNUNET_free (r); r = TALER_xtalerbank_account_from_payto ( "payto://x-taler-bank/localhost:1080/alice"); CHECK ("alice", r); r = TALER_xtalerbank_account_from_payto ( + "payto://x-taler-bank/localhost:1080/path/alice"); + CHECK ("alice", + r); + r = TALER_xtalerbank_account_from_payto ( + "payto://x-taler-bank/localhost:1080/path/alice?receiver-name=ali/cia"); + CHECK ("alice", + r); + r = TALER_xtalerbank_account_from_payto ( "payto://x-taler-bank/localhost:1080/alice?subject=hello&amount=EUR:1"); CHECK ("alice", r); diff --git a/src/util/url.c b/src/util/url.c index a140a3a2e..bf59ba6ec 100644 --- a/src/util/url.c +++ b/src/util/url.c @@ -212,8 +212,6 @@ TALER_url_join (const char *base_url, ...) { struct GNUNET_Buffer buf = { 0 }; - va_list args; - size_t len; GNUNET_assert (NULL != base_url); GNUNET_assert (NULL != path); @@ -224,40 +222,45 @@ TALER_url_join (const char *base_url, "Empty base URL specified\n"); return NULL; } - if ('/' != base_url[strlen (base_url) - 1]) + if ('\0' != path[0]) { - /* Must be an actual base URL! */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Base URL `%s' does not end with '/', cannot join with `%s'\n", - base_url, - path); - return NULL; + if ('/' != base_url[strlen (base_url) - 1]) + { + /* Must be an actual base URL! */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Base URL `%s' does not end with '/', cannot join with `%s'\n", + base_url, + path); + return NULL; + } + if ('/' == path[0]) + { + /* The path must be relative. */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Path `%s' is not relative\n", + path); + return NULL; + } } - if ('/' == path[0]) + { - /* The path must be relative. */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Path `%s' is not relative\n", - path); - return NULL; + va_list args; + size_t len; + + va_start (args, + path); + len = strlen (base_url) + strlen (path) + 1; + len += calculate_argument_length (args); + GNUNET_buffer_prealloc (&buf, + len); + GNUNET_buffer_write_str (&buf, + base_url); + GNUNET_buffer_write_str (&buf, + path); + serialize_arguments (&buf, + args); + va_end (args); } - - va_start (args, - path); - - len = strlen (base_url) + strlen (path) + 1; - len += calculate_argument_length (args); - - GNUNET_buffer_prealloc (&buf, - len); - GNUNET_buffer_write_str (&buf, - base_url); - GNUNET_buffer_write_str (&buf, - path); - serialize_arguments (&buf, - args); - va_end (args); - return GNUNET_buffer_reap_str (&buf); } @@ -322,7 +325,7 @@ TALER_url_valid_charset (const char *url) for (unsigned int i = 0; '\0' != url[i]; i++) { #define ALLOWED_CHARACTERS \ - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/:;&?-.,=_~%+" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/:;&?-.,=_~%+#" if (NULL == strchr (ALLOWED_CHARACTERS, (int) url[i])) return false; @@ -332,4 +335,20 @@ TALER_url_valid_charset (const char *url) } +bool +TALER_is_web_url (const char *url) +{ + if ( (0 != strncasecmp (url, + "https://", + strlen ("https://"))) && + (0 != strncasecmp (url, + "http://", + strlen ("http://"))) ) + return false; + if (! TALER_url_valid_charset (url) ) + return false; + return true; +} + + /* end of url.c */ diff --git a/src/util/util.c b/src/util/util.c index 2d10fd69d..da5727487 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2020 Taler Systems SA + Copyright (C) 2014-2023 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 @@ -19,16 +19,20 @@ * @author Sree Harsha Totakura <sreeharsha@totakura.in> * @author Florian Dold * @author Benedikt Mueller + * @author Christian Grothoff */ #include "platform.h" #include "taler_util.h" +#include "taler_attributes.h" +#include <gnunet/gnunet_json_lib.h> +#include <unistr.h> const char * TALER_b2s (const void *buf, size_t buf_size) { - static GNUNET_THREAD_LOCAL char ret[9]; + static TALER_THREAD_LOCAL char ret[9]; struct GNUNET_HashCode hc; char *tmp; @@ -37,9 +41,9 @@ TALER_b2s (const void *buf, &hc); tmp = GNUNET_STRINGS_data_to_string_alloc (&hc, sizeof (hc)); - memcpy (ret, - tmp, - 8); + GNUNET_memcpy (ret, + tmp, + 8); GNUNET_free (tmp); ret[8] = '\0'; return ret; @@ -82,8 +86,6 @@ TALER_global_fee_set_hton (struct TALER_GlobalFeeSetNBOP *nbo, { TALER_amount_hton (&nbo->history, &fees->history); - TALER_amount_hton (&nbo->kyc, - &fees->kyc); TALER_amount_hton (&nbo->account, &fees->account); TALER_amount_hton (&nbo->purse, @@ -97,8 +99,6 @@ TALER_global_fee_set_ntoh (struct TALER_GlobalFeeSet *fees, { TALER_amount_ntoh (&fees->history, &nbo->history); - TALER_amount_ntoh (&fees->kyc, - &nbo->kyc); TALER_amount_ntoh (&fees->account, &nbo->account); TALER_amount_ntoh (&fees->purse, @@ -114,8 +114,6 @@ TALER_wire_fee_set_hton (struct TALER_WireFeeSetNBOP *nbo, &fees->wire); TALER_amount_hton (&nbo->closing, &fees->closing); - TALER_amount_hton (&nbo->wad, - &fees->wad); } @@ -127,8 +125,6 @@ TALER_wire_fee_set_ntoh (struct TALER_WireFeeSet *fees, &nbo->wire); TALER_amount_ntoh (&fees->closing, &nbo->closing); - TALER_amount_ntoh (&fees->wad, - &nbo->wad); } @@ -142,10 +138,6 @@ TALER_global_fee_set_cmp (const struct TALER_GlobalFeeSet *f1, &f2->history); if (0 != ret) return ret; - ret = TALER_amount_cmp (&f1->kyc, - &f2->kyc); - if (0 != ret) - return ret; ret = TALER_amount_cmp (&f1->account, &f2->account); if (0 != ret) @@ -172,10 +164,6 @@ TALER_wire_fee_set_cmp (const struct TALER_WireFeeSet *f1, &f2->closing); if (0 != ret) return ret; - ret = TALER_amount_cmp (&f1->wad, - &f2->wad); - if (0 != ret) - return ret; return 0; } @@ -217,6 +205,189 @@ TALER_denom_fee_check_currency ( } +/** + * Dump character in the low range into @a buf + * following RFC 8785. + * + * @param[in,out] buf buffer to modify + * @param val value to dump + */ +static void +lowdump (struct GNUNET_Buffer *buf, + unsigned char val) +{ + char scratch[7]; + + switch (val) + { + case 0x8: + GNUNET_buffer_write (buf, + "\\b", + 2); + break; + case 0x9: + GNUNET_buffer_write (buf, + "\\t", + 2); + break; + case 0xA: + GNUNET_buffer_write (buf, + "\\n", + 2); + break; + case 0xC: + GNUNET_buffer_write (buf, + "\\f", + 2); + break; + case 0xD: + GNUNET_buffer_write (buf, + "\\r", + 2); + break; + default: + GNUNET_snprintf (scratch, + sizeof (scratch), + "\\u%04x", + (unsigned int) val); + GNUNET_buffer_write (buf, + scratch, + 6); + break; + } +} + + +size_t +TALER_rfc8785encode (char **inp) +{ + struct GNUNET_Buffer buf = { 0 }; + size_t left = strlen (*inp) + 1; + size_t olen; + char *in = *inp; + const char *pos = in; + + GNUNET_buffer_prealloc (&buf, + left + 40); + buf.warn_grow = 0; /* disable, + 40 is just a wild guess */ + while (1) + { + int mbl = u8_mblen ((unsigned char *) pos, + left); + unsigned char val; + + if (0 == mbl) + break; + val = (unsigned char) *pos; + if ( (1 == mbl) && + (val <= 0x1F) ) + { + /* Should not happen, as input is produced by + * JSON stringification */ + GNUNET_break (0); + lowdump (&buf, + val); + } + else if ( (1 == mbl) && ('\\' == *pos) ) + { + switch (*(pos + 1)) + { + case '\\': + mbl = 2; + GNUNET_buffer_write (&buf, + pos, + mbl); + break; + case 'u': + { + unsigned int num; + uint32_t n32; + unsigned char res[8]; + size_t rlen; + + GNUNET_assert ( (1 == + sscanf (pos + 2, + "%4x", + &num)) || + (1 == + sscanf (pos + 2, + "%4X", + &num)) ); + mbl = 6; + n32 = (uint32_t) num; + rlen = sizeof (res); + u32_to_u8 (&n32, + 1, + res, + &rlen); + if ( (1 == rlen) && + (res[0] <= 0x1F) ) + { + lowdump (&buf, + res[0]); + } + else + { + GNUNET_buffer_write (&buf, + (const char *) res, + rlen); + } + } + break; + default: + mbl = 2; + GNUNET_buffer_write (&buf, + pos, + mbl); + break; + } + } + else + { + GNUNET_buffer_write (&buf, + pos, + mbl); + } + left -= mbl; + pos += mbl; + } + + /* 0-terminate buffer */ + GNUNET_buffer_write (&buf, + "", + 1); + GNUNET_free (in); + *inp = GNUNET_buffer_reap (&buf, + &olen); + return olen; +} + + +/** + * Hash normalized @a j JSON object or array and + * store the result in @a hc. + * + * @param j JSON to hash + * @param[out] hc where to write the hash + */ +void +TALER_json_hash (const json_t *j, + struct GNUNET_HashCode *hc) +{ + char *cstr; + size_t clen; + + cstr = json_dumps (j, + JSON_COMPACT | JSON_SORT_KEYS); + GNUNET_assert (NULL != cstr); + clen = TALER_rfc8785encode (&cstr); + GNUNET_CRYPTO_hash (cstr, + clen, + hc); + GNUNET_free (cstr); +} + + #ifdef __APPLE__ char * strchrnul (const char *s, @@ -234,4 +405,51 @@ strchrnul (const char *s, #endif +void +TALER_CRYPTO_attributes_to_kyc_prox ( + const json_t *attr, + struct GNUNET_ShortHashCode *kyc_prox) +{ + const char *name = NULL; + const char *birthdate = NULL; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string (TALER_ATTRIBUTE_FULL_NAME, + &name), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string (TALER_ATTRIBUTE_BIRTHDATE, + &birthdate), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (attr, + spec, + NULL, NULL)) + { + GNUNET_break (0); + memset (kyc_prox, + 0, + sizeof (*kyc_prox)); + return; + } + GNUNET_assert (GNUNET_YES == + GNUNET_CRYPTO_kdf ( + kyc_prox, + sizeof (*kyc_prox), + name, + (NULL == name) + ? 0 + : strlen (name), + birthdate, + (NULL == birthdate) + ? 0 + : strlen (birthdate), + NULL, + 0)); +} + + /* end of util.c */ diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c index 4761ab6e7..0b6ab5432 100644 --- a/src/util/wallet_signatures.c +++ b/src/util/wallet_signatures.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2021, 2022 Taler Systems SA + Copyright (C) 2021-2023 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 @@ -17,12 +17,16 @@ * @file wallet_signatures.c * @brief Utility functions for Taler wallet signatures * @author Christian Grothoff + * @author Özgür Kesim */ #include "platform.h" #include "taler_util.h" #include "taler_signatures.h" +#include <gnunet/gnunet_common.h> +GNUNET_NETWORK_STRUCT_BEGIN + /** * @brief Format used to generate the signature on a request to deposit * a coin into the account of a merchant. @@ -47,9 +51,9 @@ struct TALER_DepositRequestPS struct TALER_AgeCommitmentHash h_age_commitment GNUNET_PACKED; /** - * Hash over extension attributes shared with the exchange. + * Hash over optional policy extension attributes shared with the exchange. */ - struct TALER_ExtensionContractHashP h_extensions GNUNET_PACKED; + struct TALER_ExtensionPolicyHashP h_policy GNUNET_PACKED; /** * Hash over the wiring information of the merchant. @@ -107,8 +111,15 @@ struct TALER_DepositRequestPS */ struct TALER_MerchantPublicKeyP merchant; + /** + * Hash over a JSON containing data provided by the + * wallet to complete the contract upon payment. + */ + struct GNUNET_HashCode wallet_data_hash; + }; +GNUNET_NETWORK_STRUCT_END void TALER_wallet_deposit_sign ( @@ -116,8 +127,9 @@ TALER_wallet_deposit_sign ( const struct TALER_Amount *deposit_fee, const struct TALER_MerchantWireHashP *h_wire, const struct TALER_PrivateContractHashP *h_contract_terms, + const struct GNUNET_HashCode *wallet_data_hash, 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, const struct GNUNET_TIME_Timestamp wallet_timestamp, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -136,10 +148,12 @@ TALER_wallet_deposit_sign ( .merchant = *merchant_pub }; + if (NULL != wallet_data_hash) + dr.wallet_data_hash = *wallet_data_hash; if (NULL != h_age_commitment) dr.h_age_commitment = *h_age_commitment; - if (NULL != h_extensions) - dr.h_extensions = *h_extensions; + if (NULL != h_policy) + dr.h_policy = *h_policy; TALER_amount_hton (&dr.amount_with_fee, amount); TALER_amount_hton (&dr.deposit_fee, @@ -156,8 +170,9 @@ TALER_wallet_deposit_verify ( const struct TALER_Amount *deposit_fee, const struct TALER_MerchantWireHashP *h_wire, const struct TALER_PrivateContractHashP *h_contract_terms, + const struct GNUNET_HashCode *wallet_data_hash, 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, @@ -174,14 +189,14 @@ TALER_wallet_deposit_verify ( .wallet_timestamp = GNUNET_TIME_timestamp_hton (wallet_timestamp), .refund_deadline = GNUNET_TIME_timestamp_hton (refund_deadline), .merchant = *merchant_pub, - .h_age_commitment = {{{0}}}, - .h_extensions = {{{0}}} }; + if (NULL != wallet_data_hash) + dr.wallet_data_hash = *wallet_data_hash; if (NULL != h_age_commitment) dr.h_age_commitment = *h_age_commitment; - if (NULL != h_extensions) - dr.h_extensions = *h_extensions; + if (NULL != h_policy) + dr.h_policy = *h_policy; TALER_amount_hton (&dr.amount_with_fee, amount); TALER_amount_hton (&dr.deposit_fee, @@ -199,6 +214,8 @@ TALER_wallet_deposit_verify ( } +GNUNET_NETWORK_STRUCT_BEGIN + /** * @brief Format used for to allow the wallet to authenticate * link data provided by the exchange. @@ -233,6 +250,7 @@ struct TALER_LinkDataPS struct TALER_BlindedCoinHashP coin_envelope_hash; }; +GNUNET_NETWORK_STRUCT_END void TALER_wallet_link_sign (const struct TALER_DenominationHashP *h_denom_pub, @@ -279,6 +297,8 @@ TALER_wallet_link_verify ( } +GNUNET_NETWORK_STRUCT_BEGIN + /** * Signed data to request that a coin should be refunded as part of * the "emergency" /recoup protocol. The refund will go back to the bank @@ -300,15 +320,17 @@ struct TALER_RecoupRequestPS /** * Blinding factor that was used to withdraw the coin. */ - union TALER_DenominationBlindingKeyP coin_blind; + union GNUNET_CRYPTO_BlindingSecretP coin_blind; }; +GNUNET_NETWORK_STRUCT_END + enum GNUNET_GenericReturnValue TALER_wallet_recoup_verify ( const struct TALER_DenominationHashP *h_denom_pub, - const union TALER_DenominationBlindingKeyP *coin_bks, + const union GNUNET_CRYPTO_BlindingSecretP *coin_bks, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendSignatureP *coin_sig) { @@ -329,7 +351,7 @@ TALER_wallet_recoup_verify ( void TALER_wallet_recoup_sign ( const struct TALER_DenominationHashP *h_denom_pub, - const union TALER_DenominationBlindingKeyP *coin_bks, + const union GNUNET_CRYPTO_BlindingSecretP *coin_bks, const struct TALER_CoinSpendPrivateKeyP *coin_priv, struct TALER_CoinSpendSignatureP *coin_sig) { @@ -349,7 +371,7 @@ TALER_wallet_recoup_sign ( enum GNUNET_GenericReturnValue TALER_wallet_recoup_refresh_verify ( const struct TALER_DenominationHashP *h_denom_pub, - const union TALER_DenominationBlindingKeyP *coin_bks, + const union GNUNET_CRYPTO_BlindingSecretP *coin_bks, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendSignatureP *coin_sig) { @@ -370,7 +392,7 @@ TALER_wallet_recoup_refresh_verify ( void TALER_wallet_recoup_refresh_sign ( const struct TALER_DenominationHashP *h_denom_pub, - const union TALER_DenominationBlindingKeyP *coin_bks, + const union GNUNET_CRYPTO_BlindingSecretP *coin_bks, const struct TALER_CoinSpendPrivateKeyP *coin_priv, struct TALER_CoinSpendSignatureP *coin_sig) { @@ -387,6 +409,8 @@ TALER_wallet_recoup_refresh_sign ( } +GNUNET_NETWORK_STRUCT_BEGIN + /** * @brief Message signed by a coin to indicate that the coin should be * melted. @@ -438,6 +462,7 @@ struct TALER_RefreshMeltCoinAffirmationPS struct TALER_AmountNBO melt_fee; }; +GNUNET_NETWORK_STRUCT_END void TALER_wallet_melt_sign ( @@ -504,6 +529,9 @@ TALER_wallet_melt_verify ( } +GNUNET_NETWORK_STRUCT_BEGIN + + /** * @brief Format used for to generate the signature on a request to withdraw * coins from a reserve. @@ -538,6 +566,8 @@ struct TALER_WithdrawRequestPS }; +GNUNET_NETWORK_STRUCT_END + void TALER_wallet_withdraw_sign ( const struct TALER_DenominationHashP *h_denom_pub, @@ -586,45 +616,181 @@ TALER_wallet_withdraw_verify ( } +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * @brief Format used for to generate the signature on a request to + * age-withdraw from a reserve. + */ +struct TALER_AgeWithdrawRequestPS +{ + + /** + * Purpose must be #TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW. + * Used with an EdDSA signature of a `struct TALER_ReservePublicKeyP`. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * The reserve's public key + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Value of the coin being exchanged (matching the denomination key) + * plus the transaction fee. We include this in what is being + * signed so that we can verify a reserve's remaining total balance + * without needing to access the respective denomination key + * information each time. + */ + struct TALER_AmountNBO amount_with_fee; + + /** + * Running SHA512 hash of the commitment of n*kappa coins + */ + struct TALER_AgeWithdrawCommitmentHashP h_commitment; + + /** + * The mask that defines the age groups. MUST be the same for all denominations. + */ + struct TALER_AgeMask mask; + + /** + * Maximum age group that the coins are going to be restricted to. + */ + uint8_t max_age_group; +}; + + +GNUNET_NETWORK_STRUCT_END + +void +TALER_wallet_age_withdraw_sign ( + const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, + const struct TALER_Amount *amount_with_fee, + const struct TALER_AgeMask *mask, + uint8_t max_age, + const struct TALER_ReservePrivateKeyP *reserve_priv, + struct TALER_ReserveSignatureP *reserve_sig) +{ + struct TALER_AgeWithdrawRequestPS req = { + .purpose.size = htonl (sizeof (req)), + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_AGE_WITHDRAW), + .h_commitment = *h_commitment, + .mask = *mask, + .max_age_group = TALER_get_age_group (mask, max_age) + }; + + GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, + &req.reserve_pub.eddsa_pub); + TALER_amount_hton (&req.amount_with_fee, + amount_with_fee); + GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv, + &req, + &reserve_sig->eddsa_signature); +} + + +enum GNUNET_GenericReturnValue +TALER_wallet_age_withdraw_verify ( + const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, + const struct TALER_Amount *amount_with_fee, + const struct TALER_AgeMask *mask, + uint8_t max_age, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_ReserveSignatureP *reserve_sig) +{ + struct TALER_AgeWithdrawRequestPS awsrd = { + .purpose.size = htonl (sizeof (awsrd)), + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_AGE_WITHDRAW), + .reserve_pub = *reserve_pub, + .h_commitment = *h_commitment, + .mask = *mask, + .max_age_group = TALER_get_age_group (mask, max_age) + }; + + TALER_amount_hton (&awsrd.amount_with_fee, + amount_with_fee); + return GNUNET_CRYPTO_eddsa_verify ( + TALER_SIGNATURE_WALLET_RESERVE_AGE_WITHDRAW, + &awsrd, + &reserve_sig->eddsa_signature, + &reserve_pub->eddsa_pub); +} + + +GNUNET_NETWORK_STRUCT_BEGIN + + +/** + * @brief Format used for to generate the signature on a request to withdraw + * coins from a reserve. + */ +struct TALER_AccountSetupRequestSignaturePS +{ + + /** + * Purpose must be #TALER_SIGNATURE_WALLET_ACCOUNT_SETUP. + * Used with an EdDSA signature of a `struct TALER_ReservePublicKeyP`. + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Balance threshold the wallet is about to cross. + */ + struct TALER_AmountNBO threshold; + +}; + + +GNUNET_NETWORK_STRUCT_END + + void TALER_wallet_account_setup_sign ( const struct TALER_ReservePrivateKeyP *reserve_priv, + const struct TALER_Amount *balance_threshold, struct TALER_ReserveSignatureP *reserve_sig) { - struct GNUNET_CRYPTO_EccSignaturePurpose purpose = { - .size = htonl (sizeof (purpose)), - .purpose = htonl (TALER_SIGNATURE_WALLET_ACCOUNT_SETUP) + struct TALER_AccountSetupRequestSignaturePS asap = { + .purpose.size = htonl (sizeof (asap)), + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_ACCOUNT_SETUP) }; - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_eddsa_sign_ (&reserve_priv->eddsa_priv, - &purpose, - &reserve_sig->eddsa_signature)); + TALER_amount_hton (&asap.threshold, + balance_threshold); + GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv, + &asap, + &reserve_sig->eddsa_signature); } enum GNUNET_GenericReturnValue TALER_wallet_account_setup_verify ( const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_Amount *balance_threshold, const struct TALER_ReserveSignatureP *reserve_sig) { - struct GNUNET_CRYPTO_EccSignaturePurpose purpose = { - .size = htonl (sizeof (purpose)), - .purpose = htonl (TALER_SIGNATURE_WALLET_ACCOUNT_SETUP) + struct TALER_AccountSetupRequestSignaturePS asap = { + .purpose.size = htonl (sizeof (asap)), + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_ACCOUNT_SETUP) }; - return GNUNET_CRYPTO_eddsa_verify_ ( + TALER_amount_hton (&asap.threshold, + balance_threshold); + return GNUNET_CRYPTO_eddsa_verify ( TALER_SIGNATURE_WALLET_ACCOUNT_SETUP, - &purpose, + &asap, &reserve_sig->eddsa_signature, &reserve_pub->eddsa_pub); } +GNUNET_NETWORK_STRUCT_BEGIN + + /** - * Response by which a wallet requests a full - * reserve history and indicates it is willing - * to pay for it. + * Response by which a wallet requests a reserve history. */ struct TALER_ReserveHistoryRequestPS { @@ -635,33 +801,27 @@ struct TALER_ReserveHistoryRequestPS struct GNUNET_CRYPTO_EccSignaturePurpose purpose; /** - * When did the wallet make the request. + * Which entries to exclude. Only return above this offset. */ - struct GNUNET_TIME_TimestampNBO request_timestamp; - - /** - * How much does the exchange charge for the history? - */ - struct TALER_AmountNBO history_fee; + uint64_t start_off; }; +GNUNET_NETWORK_STRUCT_END + enum GNUNET_GenericReturnValue TALER_wallet_reserve_history_verify ( - const struct GNUNET_TIME_Timestamp ts, - const struct TALER_Amount *history_fee, + uint64_t start_off, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_ReserveSignatureP *reserve_sig) { struct TALER_ReserveHistoryRequestPS rhr = { .purpose.size = htonl (sizeof (rhr)), .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_HISTORY), - .request_timestamp = GNUNET_TIME_timestamp_hton (ts) + .start_off = GNUNET_htonll (start_off) }; - TALER_amount_hton (&rhr.history_fee, - history_fee); return GNUNET_CRYPTO_eddsa_verify ( TALER_SIGNATURE_WALLET_RESERVE_HISTORY, &rhr, @@ -672,82 +832,84 @@ TALER_wallet_reserve_history_verify ( void TALER_wallet_reserve_history_sign ( - const struct GNUNET_TIME_Timestamp ts, - const struct TALER_Amount *history_fee, + uint64_t start_off, const struct TALER_ReservePrivateKeyP *reserve_priv, struct TALER_ReserveSignatureP *reserve_sig) { struct TALER_ReserveHistoryRequestPS rhr = { .purpose.size = htonl (sizeof (rhr)), .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_HISTORY), - .request_timestamp = GNUNET_TIME_timestamp_hton (ts) + .start_off = GNUNET_htonll (start_off) }; - TALER_amount_hton (&rhr.history_fee, - history_fee); GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv, &rhr, &reserve_sig->eddsa_signature); } +GNUNET_NETWORK_STRUCT_BEGIN + /** - * Response by which a wallet requests an account status. + * Response by which a wallet requests a coin history. */ -struct TALER_ReserveStatusRequestPS +struct TALER_CoinHistoryRequestPS { /** - * Purpose is #TALER_SIGNATURE_WALLET_RESERVE_STATUS + * Purpose is #TALER_SIGNATURE_WALLET_COIN_HISTORY */ struct GNUNET_CRYPTO_EccSignaturePurpose purpose; /** - * When did the wallet make the request. + * Which entries to exclude. Only return above this offset. */ - struct GNUNET_TIME_TimestampNBO request_timestamp; + uint64_t start_off; }; +GNUNET_NETWORK_STRUCT_END enum GNUNET_GenericReturnValue -TALER_wallet_reserve_status_verify ( - const struct GNUNET_TIME_Timestamp ts, - const struct TALER_ReservePublicKeyP *reserve_pub, - const struct TALER_ReserveSignatureP *reserve_sig) +TALER_wallet_coin_history_verify ( + uint64_t start_off, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_CoinSpendSignatureP *coin_sig) { - struct TALER_ReserveStatusRequestPS rsr = { + struct TALER_CoinHistoryRequestPS rsr = { .purpose.size = htonl (sizeof (rsr)), - .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_STATUS), - .request_timestamp = GNUNET_TIME_timestamp_hton (ts) + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_HISTORY), + .start_off = GNUNET_htonll (start_off) }; return GNUNET_CRYPTO_eddsa_verify ( - TALER_SIGNATURE_WALLET_RESERVE_STATUS, + TALER_SIGNATURE_WALLET_COIN_HISTORY, &rsr, - &reserve_sig->eddsa_signature, - &reserve_pub->eddsa_pub); + &coin_sig->eddsa_signature, + &coin_pub->eddsa_pub); } void -TALER_wallet_reserve_status_sign ( - const struct GNUNET_TIME_Timestamp ts, - const struct TALER_ReservePrivateKeyP *reserve_priv, - struct TALER_ReserveSignatureP *reserve_sig) +TALER_wallet_coin_history_sign ( + uint64_t start_off, + const struct TALER_CoinSpendPrivateKeyP *coin_priv, + struct TALER_CoinSpendSignatureP *coin_sig) { - struct TALER_ReserveStatusRequestPS rsr = { + struct TALER_CoinHistoryRequestPS rsr = { .purpose.size = htonl (sizeof (rsr)), - .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_STATUS), - .request_timestamp = GNUNET_TIME_timestamp_hton (ts) + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_HISTORY), + .start_off = GNUNET_htonll (start_off) }; - GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv, + GNUNET_CRYPTO_eddsa_sign (&coin_priv->eddsa_priv, &rsr, - &reserve_sig->eddsa_signature); + &coin_sig->eddsa_signature); } +GNUNET_NETWORK_STRUCT_BEGIN + /** * Message signed to create a purse (without reserve). */ @@ -787,10 +949,13 @@ struct TALER_PurseCreatePS }; +GNUNET_NETWORK_STRUCT_END + + void TALER_wallet_purse_create_sign ( struct GNUNET_TIME_Timestamp purse_expiration, - struct TALER_PrivateContractHashP *h_contract_terms, + const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_PurseMergePublicKeyP *merge_pub, uint32_t min_age, const struct TALER_Amount *amount, @@ -817,7 +982,7 @@ TALER_wallet_purse_create_sign ( enum GNUNET_GenericReturnValue TALER_wallet_purse_create_verify ( struct GNUNET_TIME_Timestamp purse_expiration, - struct TALER_PrivateContractHashP *h_contract_terms, + const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_PurseMergePublicKeyP *merge_pub, uint32_t min_age, const struct TALER_Amount *amount, @@ -843,6 +1008,59 @@ TALER_wallet_purse_create_verify ( } +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Message signed to delete a purse. + */ +struct TALER_PurseDeletePS +{ + + /** + * Purpose is #TALER_SIGNATURE_WALLET_PURSE_DELETE + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + +}; + + +GNUNET_NETWORK_STRUCT_END + + +void +TALER_wallet_purse_delete_sign ( + const struct TALER_PurseContractPrivateKeyP *purse_priv, + struct TALER_PurseContractSignatureP *purse_sig) +{ + struct TALER_PurseDeletePS pm = { + .purpose.size = htonl (sizeof (pm)), + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_PURSE_DELETE) + }; + + GNUNET_CRYPTO_eddsa_sign (&purse_priv->eddsa_priv, + &pm, + &purse_sig->eddsa_signature); +} + + +enum GNUNET_GenericReturnValue +TALER_wallet_purse_delete_verify ( + const struct TALER_PurseContractPublicKeyP *purse_pub, + const struct TALER_PurseContractSignatureP *purse_sig) +{ + struct TALER_PurseDeletePS pm = { + .purpose.size = htonl (sizeof (pm)), + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_PURSE_DELETE) + }; + + return GNUNET_CRYPTO_eddsa_verify ( + TALER_SIGNATURE_WALLET_PURSE_DELETE, + &pm, + &purse_sig->eddsa_signature, + &purse_pub->eddsa_pub); +} + + void TALER_wallet_purse_status_sign ( const struct TALER_PurseContractPrivateKeyP *purse_priv, @@ -877,6 +1095,8 @@ TALER_wallet_purse_status_verify ( } +GNUNET_NETWORK_STRUCT_BEGIN + /** * Message signed to deposit a coin into a purse. */ @@ -916,6 +1136,7 @@ struct TALER_PurseDepositPS struct GNUNET_HashCode h_exchange_base_url GNUNET_PACKED; }; +GNUNET_NETWORK_STRUCT_END void TALER_wallet_purse_deposit_sign ( @@ -977,6 +1198,8 @@ TALER_wallet_purse_deposit_verify ( } +GNUNET_NETWORK_STRUCT_BEGIN + /** * Message signed to merge a purse into a reserve. */ @@ -1006,10 +1229,11 @@ struct TALER_PurseMergePS }; +GNUNET_NETWORK_STRUCT_END void TALER_wallet_purse_merge_sign ( - const char *reserve_url, + const char *reserve_uri, struct GNUNET_TIME_Timestamp merge_timestamp, const struct TALER_PurseContractPublicKeyP *purse_pub, const struct TALER_PurseMergePrivateKeyP *merge_priv, @@ -1022,7 +1246,11 @@ TALER_wallet_purse_merge_sign ( .purse_pub = *purse_pub }; - TALER_payto_hash (reserve_url, + GNUNET_assert (0 == + strncasecmp (reserve_uri, + "payto://taler-reserve", + strlen ("payto://taler-reserve"))); + TALER_payto_hash (reserve_uri, &pm.h_payto); GNUNET_CRYPTO_eddsa_sign (&merge_priv->eddsa_priv, &pm, @@ -1032,7 +1260,7 @@ TALER_wallet_purse_merge_sign ( enum GNUNET_GenericReturnValue TALER_wallet_purse_merge_verify ( - const char *reserve_url, + const char *reserve_uri, struct GNUNET_TIME_Timestamp merge_timestamp, const struct TALER_PurseContractPublicKeyP *purse_pub, const struct TALER_PurseMergePublicKeyP *merge_pub, @@ -1045,7 +1273,15 @@ TALER_wallet_purse_merge_verify ( .purse_pub = *purse_pub }; - TALER_payto_hash (reserve_url, + if (0 != + strncasecmp (reserve_uri, + "payto://taler-reserve", + strlen ("payto://taler-reserve"))) + { + GNUNET_break (0); + return GNUNET_NO; + } + TALER_payto_hash (reserve_uri, &pm.h_payto); return GNUNET_CRYPTO_eddsa_verify ( TALER_SIGNATURE_WALLET_PURSE_MERGE, @@ -1055,6 +1291,8 @@ TALER_wallet_purse_merge_verify ( } +GNUNET_NETWORK_STRUCT_BEGIN + /** * Message signed by account to merge a purse into a reserve. */ @@ -1110,6 +1348,8 @@ struct TALER_AccountMergePS uint32_t flags GNUNET_PACKED; }; +GNUNET_NETWORK_STRUCT_END + void TALER_wallet_account_merge_sign ( @@ -1181,40 +1421,323 @@ TALER_wallet_account_merge_verify ( } +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Message signed by reserve key. + */ +struct TALER_ReserveOpenPS +{ + + /** + * Purpose is #TALER_SIGNATURE_WALLET_RESERVE_OPEN + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Amount to be paid from the reserve balance to open + * the reserve. + */ + struct TALER_AmountNBO reserve_payment; + + /** + * When was the request created. + */ + struct GNUNET_TIME_TimestampNBO request_timestamp; + + /** + * For how long should the reserve be kept open. + * (Determines amount to be paid.) + */ + struct GNUNET_TIME_TimestampNBO reserve_expiration; + + /** + * How many open purses should be included with the + * open reserve? + * (Determines amount to be paid.) + */ + uint32_t purse_limit GNUNET_PACKED; + +}; + +GNUNET_NETWORK_STRUCT_END + + void -TALER_wallet_account_close_sign ( +TALER_wallet_reserve_open_sign ( + const struct TALER_Amount *reserve_payment, + struct GNUNET_TIME_Timestamp request_timestamp, + struct GNUNET_TIME_Timestamp reserve_expiration, + uint32_t purse_limit, const struct TALER_ReservePrivateKeyP *reserve_priv, struct TALER_ReserveSignatureP *reserve_sig) { - struct GNUNET_CRYPTO_EccSignaturePurpose purpose = { - .size = htonl (sizeof (purpose)), - .purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_CLOSE) + struct TALER_ReserveOpenPS rop = { + .purpose.size = htonl (sizeof (rop)), + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_OPEN), + .request_timestamp = GNUNET_TIME_timestamp_hton (request_timestamp), + .reserve_expiration = GNUNET_TIME_timestamp_hton (reserve_expiration), + .purse_limit = htonl (purse_limit) }; + TALER_amount_hton (&rop.reserve_payment, + reserve_payment); GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign_ (&reserve_priv->eddsa_priv, - &purpose, + &rop.purpose, &reserve_sig->eddsa_signature)); } enum GNUNET_GenericReturnValue -TALER_wallet_account_close_verify ( +TALER_wallet_reserve_open_verify ( + const struct TALER_Amount *reserve_payment, + struct GNUNET_TIME_Timestamp request_timestamp, + struct GNUNET_TIME_Timestamp reserve_expiration, + uint32_t purse_limit, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_ReserveSignatureP *reserve_sig) { - struct GNUNET_CRYPTO_EccSignaturePurpose purpose = { - .size = htonl (sizeof (purpose)), - .purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_CLOSE) + struct TALER_ReserveOpenPS rop = { + .purpose.size = htonl (sizeof (rop)), + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_OPEN), + .request_timestamp = GNUNET_TIME_timestamp_hton (request_timestamp), + .reserve_expiration = GNUNET_TIME_timestamp_hton (reserve_expiration), + .purse_limit = htonl (purse_limit) }; + TALER_amount_hton (&rop.reserve_payment, + reserve_payment); + return GNUNET_CRYPTO_eddsa_verify_ (TALER_SIGNATURE_WALLET_RESERVE_OPEN, + &rop.purpose, + &reserve_sig->eddsa_signature, + &reserve_pub->eddsa_pub); +} + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Message signed by + */ +struct TALER_ReserveOpenDepositPS +{ + + /** + * Purpose is #TALER_SIGNATURE_WALLET_RESERVE_OPEN_DEPOSIT + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Which reserve's opening signature should be paid for? + */ + struct TALER_ReserveSignatureP reserve_sig; + + /** + * Specifies how much of the coin's value should be spent on opening this + * reserve. + */ + struct TALER_AmountNBO coin_contribution; +}; + +GNUNET_NETWORK_STRUCT_END + + +// FIXME-#7267: add h_age_commitment, h_denom_pub to have proof! +void +TALER_wallet_reserve_open_deposit_sign ( + const struct TALER_Amount *coin_contribution, + const struct TALER_ReserveSignatureP *reserve_sig, + const struct TALER_CoinSpendPrivateKeyP *coin_priv, + struct TALER_CoinSpendSignatureP *coin_sig) +{ + struct TALER_ReserveOpenDepositPS rod = { + .purpose.size = htonl (sizeof (rod)), + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_OPEN_DEPOSIT), + .reserve_sig = *reserve_sig + }; + + TALER_amount_hton (&rod.coin_contribution, + coin_contribution); + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_eddsa_sign_ (&coin_priv->eddsa_priv, + &rod.purpose, + &coin_sig->eddsa_signature)); +} + + +enum GNUNET_GenericReturnValue +TALER_wallet_reserve_open_deposit_verify ( + const struct TALER_Amount *coin_contribution, + const struct TALER_ReserveSignatureP *reserve_sig, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_CoinSpendSignatureP *coin_sig) +{ + struct TALER_ReserveOpenDepositPS rod = { + .purpose.size = htonl (sizeof (rod)), + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_OPEN_DEPOSIT), + .reserve_sig = *reserve_sig + }; + + TALER_amount_hton (&rod.coin_contribution, + coin_contribution); + return GNUNET_CRYPTO_eddsa_verify_ ( + TALER_SIGNATURE_WALLET_RESERVE_OPEN_DEPOSIT, + &rod.purpose, + &coin_sig->eddsa_signature, + &coin_pub->eddsa_pub); +} + + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Message signed by reserve key. + */ +struct TALER_ReserveClosePS +{ + + /** + * Purpose is #TALER_SIGNATURE_WALLET_RESERVE_CLOSE + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * When was the request created. + */ + struct GNUNET_TIME_TimestampNBO request_timestamp; + + /** + * Hash of the payto://-URI of the target account + * for the closure, or all zeros for the reserve + * origin account. + */ + struct TALER_PaytoHashP target_account_h_payto; + +}; + +GNUNET_NETWORK_STRUCT_END + + +void +TALER_wallet_reserve_close_sign ( + struct GNUNET_TIME_Timestamp request_timestamp, + const struct TALER_PaytoHashP *h_payto, + const struct TALER_ReservePrivateKeyP *reserve_priv, + struct TALER_ReserveSignatureP *reserve_sig) +{ + struct TALER_ReserveClosePS rcp = { + .purpose.size = htonl (sizeof (rcp)), + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_CLOSE), + .request_timestamp = GNUNET_TIME_timestamp_hton (request_timestamp) + }; + + if (NULL != h_payto) + rcp.target_account_h_payto = *h_payto; + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_eddsa_sign_ (&reserve_priv->eddsa_priv, + &rcp.purpose, + &reserve_sig->eddsa_signature)); +} + + +enum GNUNET_GenericReturnValue +TALER_wallet_reserve_close_verify ( + struct GNUNET_TIME_Timestamp request_timestamp, + const struct TALER_PaytoHashP *h_payto, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_ReserveSignatureP *reserve_sig) +{ + struct TALER_ReserveClosePS rcp = { + .purpose.size = htonl (sizeof (rcp)), + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_CLOSE), + .request_timestamp = GNUNET_TIME_timestamp_hton (request_timestamp) + }; + + if (NULL != h_payto) + rcp.target_account_h_payto = *h_payto; return GNUNET_CRYPTO_eddsa_verify_ (TALER_SIGNATURE_WALLET_RESERVE_CLOSE, - &purpose, + &rcp.purpose, &reserve_sig->eddsa_signature, &reserve_pub->eddsa_pub); } +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Message signed by reserve private key. + */ +struct TALER_ReserveAttestRequestPS +{ + + /** + * Purpose is #TALER_SIGNATURE_WALLET_ATTEST_REQUEST + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * When was the request created. + */ + struct GNUNET_TIME_TimestampNBO request_timestamp; + + /** + * Hash over the JSON array of requested attributes. + */ + struct GNUNET_HashCode h_details; + +}; + +GNUNET_NETWORK_STRUCT_END + + +void +TALER_wallet_reserve_attest_request_sign ( + struct GNUNET_TIME_Timestamp request_timestamp, + const json_t *details, + const struct TALER_ReservePrivateKeyP *reserve_priv, + struct TALER_ReserveSignatureP *reserve_sig) +{ + struct TALER_ReserveAttestRequestPS rcp = { + .purpose.size = htonl (sizeof (rcp)), + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_ATTEST_DETAILS), + .request_timestamp = GNUNET_TIME_timestamp_hton (request_timestamp) + }; + + TALER_json_hash (details, + &rcp.h_details); + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_eddsa_sign_ (&reserve_priv->eddsa_priv, + &rcp.purpose, + &reserve_sig->eddsa_signature)); +} + + +enum GNUNET_GenericReturnValue +TALER_wallet_reserve_attest_request_verify ( + struct GNUNET_TIME_Timestamp request_timestamp, + const json_t *details, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_ReserveSignatureP *reserve_sig) +{ + struct TALER_ReserveAttestRequestPS rcp = { + .purpose.size = htonl (sizeof (rcp)), + .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_ATTEST_DETAILS), + .request_timestamp = GNUNET_TIME_timestamp_hton (request_timestamp) + }; + + TALER_json_hash (details, + &rcp.h_details); + return GNUNET_CRYPTO_eddsa_verify_ ( + TALER_SIGNATURE_WALLET_RESERVE_ATTEST_DETAILS, + &rcp.purpose, + &reserve_sig->eddsa_signature, + &reserve_pub->eddsa_pub); +} + + +GNUNET_NETWORK_STRUCT_BEGIN + /** * Message signed by purse to associate an encrypted contract. */ @@ -1237,6 +1760,7 @@ struct TALER_PurseContractPS struct TALER_ContractDiffiePublicP contract_pub; }; +GNUNET_NETWORK_STRUCT_END void TALER_wallet_econtract_upload_sign ( |