diff options
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/.gitignore | 1 | ||||
-rw-r--r-- | src/util/Makefile.am | 13 | ||||
-rw-r--r-- | src/util/age_restriction.c | 3 | ||||
-rw-r--r-- | src/util/amount.c | 27 | ||||
-rw-r--r-- | src/util/conversion.c | 402 | ||||
-rw-r--r-- | src/util/crypto_confirmation.c | 8 | ||||
-rw-r--r-- | src/util/crypto_contract.c | 42 | ||||
-rw-r--r-- | src/util/crypto_helper_esign.c | 6 | ||||
-rw-r--r-- | src/util/crypto_helper_rsa.c | 12 | ||||
-rw-r--r-- | src/util/exchange_signatures.c | 16 | ||||
-rw-r--r-- | src/util/iban.c | 6 | ||||
-rw-r--r-- | src/util/offline_signatures.c | 86 | ||||
-rw-r--r-- | src/util/payto.c | 6 | ||||
-rw-r--r-- | src/util/taler-exchange-secmod-cs.c | 24 | ||||
-rw-r--r-- | src/util/taler-exchange-secmod-eddsa.c | 6 | ||||
-rw-r--r-- | src/util/taler-exchange-secmod-rsa.c | 36 | ||||
-rw-r--r-- | src/util/test_conversion.c | 149 | ||||
-rwxr-xr-x | src/util/test_conversion.sh | 5 | ||||
-rw-r--r-- | src/util/test_crypto.c | 25 | ||||
-rw-r--r-- | src/util/util.c | 169 |
20 files changed, 935 insertions, 107 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 115ea5055..9d1ec9d42 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -20,7 +20,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 \ @@ -80,6 +81,7 @@ libtalerutil_la_SOURCES = \ aml_signatures.c \ auditor_signatures.c \ config.c \ + conversion.c \ crypto.c \ crypto_confirmation.c \ crypto_contract.c \ @@ -125,6 +127,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 \ @@ -141,6 +144,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 beb68e5a6..cf81d915b 100644 --- a/src/util/age_restriction.c +++ b/src/util/age_restriction.c @@ -513,6 +513,9 @@ TALER_parse_age_group_string ( unsigned int val = 0; char c; + /* reset mask */ + mask->bits = 0; + while (*pos) { c = *pos++; diff --git a/src/util/amount.c b/src/util/amount.c index d5698e8b6..9cd0739c9 100644 --- a/src/util/amount.c +++ b/src/util/amount.c @@ -72,9 +72,8 @@ 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] = toupper (str[i]); /* 0-terminate *and* normalize buffer by setting everything to '\0' */ memset (&amount->currency [colon - str], 0, @@ -193,9 +192,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] = toupper (d->currency[i]); } @@ -205,9 +203,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)); } @@ -225,9 +223,8 @@ TALER_amount_set_zero (const char *cur, memset (amount, 0, sizeof (struct TALER_Amount)); - memcpy (amount->currency, - cur, - slen); + for (unsigned int i = 0; i<slen; i++) + amount->currency[i] = toupper (cur[i]); return GNUNET_OK; } @@ -680,9 +677,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/conversion.c b/src/util/conversion.c new file mode 100644 index 000000000..501ca0145 --- /dev/null +++ b/src/util/conversion.c @@ -0,0 +1,402 @@ +/* + 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_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; + 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); + } + if (NULL != ec->read_task) + { + GNUNET_SCHEDULER_cancel (ec->read_task); + ec->read_task = NULL; + } + GNUNET_OS_process_destroy (ec->helper); + ec->helper = NULL; + 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_confirmation.c b/src/util/crypto_confirmation.c index e52562e3e..a238d5376 100644 --- a/src/util/crypto_confirmation.c +++ b/src/util/crypto_confirmation.c @@ -90,9 +90,9 @@ compute_totp (struct GNUNET_TIME_Timestamp ts, mc = gcry_md_read (md, GCRY_MD_SHA1); GNUNET_assert (NULL != mc); - memcpy (hmac, - mc, - sizeof (hmac)); + GNUNET_memcpy (hmac, + mc, + sizeof (hmac)); gcry_md_close (md); } @@ -102,7 +102,7 @@ compute_totp (struct GNUNET_TIME_Timestamp ts, offset = hmac[sizeof (hmac) - 1] & 0x0f; for (int count = 0; count < 4; count++) - code |= hmac[offset + 3 - count] << (8 * count); + code |= ((uint32_t) hmac[offset + 3 - count]) << (8 * count); code &= 0x7fffffff; /* always use 8 digits (maximum) */ code = code % 100000000; diff --git a/src/util/crypto_contract.c b/src/util/crypto_contract.c index 3bfe9eb89..bec34c983 100644 --- a/src/util/crypto_contract.c +++ b/src/util/crypto_contract.c @@ -131,9 +131,9 @@ blob_encrypt (const struct NonceP *nonce, + 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, @@ -274,9 +274,9 @@ 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, @@ -423,9 +423,9 @@ 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, @@ -441,12 +441,12 @@ TALER_CRYPTO_contract_encrypt_for_deposit ( 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); } @@ -573,9 +573,9 @@ TALER_CRYPTO_kyc_attributes_encrypt ( cbuf_size = compressBound (clen); xbuf = GNUNET_malloc (cbuf_size + sizeof (uint32_t)); belen = htonl ((uint32_t) clen); - memcpy (xbuf, - &belen, - sizeof (belen)); + GNUNET_memcpy (xbuf, + &belen, + sizeof (belen)); ret = compress (xbuf + 4, &cbuf_size, (const Bytef *) cstr, @@ -623,9 +623,9 @@ TALER_CRYPTO_kyc_attributes_decrypt ( GNUNET_break_op (0); return NULL; } - memcpy (&belen, - xhdr, - sizeof (belen)); + GNUNET_memcpy (&belen, + xhdr, + sizeof (belen)); clen = ntohl (belen); if (clen >= GNUNET_MAX_MALLOC_CHECKED) { diff --git a/src/util/crypto_helper_esign.c b/src/util/crypto_helper_esign.c index 5a9ad74e2..5b04d0ead 100644 --- a/src/util/crypto_helper_esign.c +++ b/src/util/crypto_helper_esign.c @@ -357,9 +357,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 efded50a1..4098a846b 100644 --- a/src/util/crypto_helper_rsa.c +++ b/src/util/crypto_helper_rsa.c @@ -417,9 +417,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, @@ -655,9 +655,9 @@ TALER_CRYPTO_helper_rsa_batch_sign ( sr->header.size = htons (sizeof (*sr) + rsr->msg_size); 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); wbuf += sizeof (*sr) + rsr->msg_size; } GNUNET_assert (wbuf == &obuf[mlen]); diff --git a/src/util/exchange_signatures.c b/src/util/exchange_signatures.c index d8bf716c7..6f8ebdaff 100644 --- a/src/util/exchange_signatures.c +++ b/src/util/exchange_signatures.c @@ -1177,10 +1177,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); @@ -1204,10 +1204,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, 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/offline_signatures.c b/src/util/offline_signatures.c index b1e3b93a3..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 @@ -677,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 @@ -685,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) @@ -697,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); @@ -706,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) @@ -718,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, @@ -967,9 +1005,9 @@ TALER_exchange_offline_global_fee_sign ( 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), @@ -977,10 +1015,10 @@ TALER_exchange_offline_global_fee_sign ( .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); } @@ -1095,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 @@ -1103,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) { @@ -1113,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, @@ -1123,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) { @@ -1133,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/payto.c b/src/util/payto.c index 81664b1df..9b0e83e85 100644 --- a/src/util/payto.c +++ b/src/util/payto.c @@ -267,9 +267,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-exchange-secmod-cs.c b/src/util/taler-exchange-secmod-cs.c index 2cdf09adf..ed0eba15d 100644 --- a/src/util/taler-exchange-secmod-cs.c +++ b/src/util/taler-exchange-secmod-cs.c @@ -423,9 +423,9 @@ 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; } @@ -1373,9 +1373,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); } } @@ -1472,18 +1472,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); diff --git a/src/util/taler-exchange-secmod-eddsa.c b/src/util/taler-exchange-secmod-eddsa.c index e07e9a71d..3b78e71df 100644 --- a/src/util/taler-exchange-secmod-eddsa.c +++ b/src/util/taler-exchange-secmod-eddsa.c @@ -826,9 +826,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; diff --git a/src/util/taler-exchange-secmod-rsa.c b/src/util/taler-exchange-secmod-rsa.c index c651d78a8..414cb4acc 100644 --- a/src/util/taler-exchange-secmod-rsa.c +++ b/src/util/taler-exchange-secmod-rsa.c @@ -394,13 +394,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; } @@ -524,9 +524,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); @@ -1111,9 +1111,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); @@ -1212,18 +1212,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); 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 80a247770..80ce9083d 100644 --- a/src/util/test_crypto.c +++ b/src/util/test_crypto.c @@ -355,15 +355,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)) { @@ -373,12 +382,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; } diff --git a/src/util/util.c b/src/util/util.c index 96d791912..da5727487 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -25,6 +25,7 @@ #include "taler_util.h" #include "taler_attributes.h" #include <gnunet/gnunet_json_lib.h> +#include <unistr.h> const char * @@ -40,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; @@ -205,6 +206,164 @@ 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. * @@ -221,11 +380,11 @@ TALER_json_hash (const json_t *j, cstr = json_dumps (j, JSON_COMPACT | JSON_SORT_KEYS); GNUNET_assert (NULL != cstr); - clen = strlen (cstr); + clen = TALER_rfc8785encode (&cstr); GNUNET_CRYPTO_hash (cstr, clen, hc); - free (cstr); + GNUNET_free (cstr); } |