diff options
author | Christian Grothoff <christian@grothoff.org> | 2015-01-08 18:37:20 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2015-01-08 18:37:20 +0100 |
commit | 57d1f08dbca256f5fe16d57b29bfa523dec8f6c4 (patch) | |
tree | 3b3ee5f3b8c174887217e5c465048dea4e79bae2 /src/util |
-initial import for mint
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/Makefile.am | 39 | ||||
-rw-r--r-- | src/util/db.c | 196 | ||||
-rw-r--r-- | src/util/json.c | 194 | ||||
-rw-r--r-- | src/util/microhttpd.c | 417 | ||||
-rw-r--r-- | src/util/misc.supp | 28 | ||||
-rw-r--r-- | src/util/rsa.c | 925 | ||||
-rw-r--r-- | src/util/test_hash_context.c | 48 | ||||
-rw-r--r-- | src/util/test_rsa.c | 112 | ||||
-rw-r--r-- | src/util/util.c | 528 |
9 files changed, 2487 insertions, 0 deletions
diff --git a/src/util/Makefile.am b/src/util/Makefile.am new file mode 100644 index 000000000..f935802a6 --- /dev/null +++ b/src/util/Makefile.am @@ -0,0 +1,39 @@ +AM_CPPFLAGS = -I$(top_srcdir)/src/include $(LIBGCRYPT_CFLAGS) $(POSTGRESQL_CPPFLAGS) + +lib_LTLIBRARIES = \ + libtalerutil.la + +libtalerutil_la_SOURCES = \ + util.c \ + json.c \ + db.c \ + microhttpd.c \ + rsa.c + +libtalerutil_la_LIBADD = \ + -lgnunetutil \ + $(LIBGCRYPT_LIBS) \ + -ljansson \ + -lmicrohttpd \ + -lpq + +libtalerutil_la_LDFLAGS = \ + $(POSTGRESQL_LDFLAGS) \ + -version-info 0:0:0 \ + -export-dynamic -no-undefined + +check_PROGRAMS = \ + test-hash-context \ + test-rsa + +TESTS = \ + $(check_PROGRAMS) + +test_hash_context_SOURCES = test_hash_context.c +test_hash_context_CPPFLAGS = $(AM_CPPFLAGS) $(LIBGCRYPT_CFLAGS) +test_hash_context_LDADD = libtalerutil.la \ + -lgnunetutil $(LIBGCRYPT_LIBS) + +test_rsa_SOURCES = test_rsa.c +test_rsa_LDADD = libtalerutil.la \ + -lgnunetutil $(LIBGCRYPT_LIBS) diff --git a/src/util/db.c b/src/util/db.c new file mode 100644 index 000000000..a0b234a06 --- /dev/null +++ b/src/util/db.c @@ -0,0 +1,196 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + 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, If not, see <http://www.gnu.org/licenses/> +*/ + + +/** + * @file util/db.c + * @brief helper functions for DB interactions + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + * @author Florian Dold + */ + +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include "taler_db_lib.h" + + +/** + * Execute a prepared statement. + */ +PGresult * +TALER_DB_exec_prepared (PGconn *db_conn, + const char *name, + const struct TALER_DB_QueryParam *params) +{ + unsigned len; + unsigned i; + + /* count the number of parameters */ + + { + const struct TALER_DB_QueryParam *x; + for (len = 0, x = params; + x->more; + len +=1, x += 1); + } + + /* new scope to allow stack allocation without alloca */ + + { + void *param_values[len]; + int param_lengths[len]; + int param_formats[len]; + + for (i = 0; i < len; i += 1) + { + param_values[i] = (void *) params[i].data; + param_lengths[i] = params[i].size; + param_formats[i] = 1; + } + return PQexecPrepared (db_conn, name, len, + (const char **) param_values, param_lengths, param_formats, 1); + } +} + + +/** + * Extract results from a query result according to the given specification. + * If colums are NULL, the destination is not modified, and GNUNET_NO + * is returned. + * + * @return + * GNUNET_YES if all results could be extracted + * GNUNET_NO if at least one result was NULL + * GNUNET_SYSERR if a result was invalid (non-existing field) + */ +int +TALER_DB_extract_result (PGresult *result, + struct TALER_DB_ResultSpec *rs, + int row) +{ + int had_null = GNUNET_NO; + + for (; NULL != rs->fname; rs += 1) + { + int fnum; + fnum = PQfnumber (result, rs->fname); + if (fnum < 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "field '%s' does not exist in result\n", rs->fname); + return GNUNET_SYSERR; + } + + /* if a field is null, continue but + * remember that we now return a different result */ + + if (PQgetisnull (result, row, fnum)) + { + had_null = GNUNET_YES; + continue; + } + const char *res; + if (rs->dst_size != PQgetlength (result, row, fnum)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "field '%s' has wrong size (got %u, expected %u)\n", + rs->fname, (int) PQgetlength (result, row, fnum), (int) rs->dst_size); + return GNUNET_SYSERR; + } + res = PQgetvalue (result, row, fnum); + GNUNET_assert (NULL != res); + memcpy (rs->dst, res, rs->dst_size); + } + if (GNUNET_YES == had_null) + return GNUNET_NO; + return GNUNET_YES; +} + + +int +TALER_DB_field_isnull (PGresult *result, + int row, + const char *fname) +{ + int fnum; + fnum = PQfnumber (result, fname); + GNUNET_assert (fnum >= 0); + if (PQgetisnull (result, row, fnum)) + return GNUNET_YES; + return GNUNET_NO; +} + + +int +TALER_DB_extract_amount_nbo (PGresult *result, + int row, + const char *val_name, + const char *frac_name, + const char *curr_name, + struct TALER_AmountNBO *r_amount_nbo) +{ + int val_num; + int frac_num; + int curr_num; + int len; + + GNUNET_assert (NULL != strstr (val_name, "_val")); + GNUNET_assert (NULL != strstr (frac_name, "_frac")); + GNUNET_assert (NULL != strstr (curr_name, "_curr")); + + val_num = PQfnumber (result, val_name); + GNUNET_assert (val_num >= 0); + frac_num = PQfnumber (result, frac_name); + GNUNET_assert (frac_num >= 0); + curr_num = PQfnumber (result, curr_name); + GNUNET_assert (curr_num >= 0); + + r_amount_nbo->value = *(uint32_t *) PQgetvalue (result, row, val_num); + r_amount_nbo->fraction = *(uint32_t *) PQgetvalue (result, row, frac_num); + memset (r_amount_nbo->currency, 0, TALER_CURRENCY_LEN); + // FIXME: overflow? + len = PQgetlength (result, row, curr_num); + len = GNUNET_MIN (TALER_CURRENCY_LEN, len); + memcpy (r_amount_nbo->currency, PQgetvalue (result, row, curr_num), len); + r_amount_nbo->currency[TALER_CURRENCY_LEN - 1] = '\0'; + + return GNUNET_OK; +} + + +int +TALER_DB_extract_amount (PGresult *result, + int row, + const char *val_name, + const char *frac_name, + const char *curr_name, + struct TALER_Amount *r_amount) +{ + struct TALER_AmountNBO amount_nbo; + + (void) + TALER_DB_extract_amount_nbo (result, + row, + val_name, + frac_name, + curr_name, + &amount_nbo); + r_amount->value = ntohl (amount_nbo.value); + r_amount->fraction = ntohl (amount_nbo.fraction); + (void) strncpy (r_amount->currency, amount_nbo.currency, TALER_CURRENCY_LEN); + + return GNUNET_OK; +} + +/* end of util/db.c */ diff --git a/src/util/json.c b/src/util/json.c new file mode 100644 index 000000000..269e6cf26 --- /dev/null +++ b/src/util/json.c @@ -0,0 +1,194 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + 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, If not, see <http://www.gnu.org/licenses/> +*/ + +/** + * @file util/json.c + * @brief helper functions for JSON processing using libjansson + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ + +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include "taler_util.h" +#include "taler_json_lib.h" + +/** + * Shorthand for exit jumps. + */ +#define EXITIF(cond) \ + do { \ + if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ + } while (0) + +/** + * Print JSON parsing related error information + */ +#define WARN_JSON(error) \ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \ + "JSON parsing failed at %s:%u: %s (%s)", \ + __FILE__, __LINE__, error.text, error.source) + +/** + * Shorthand for JSON parsing related exit jumps. + */ +#define UNPACK_EXITIF(cond) \ + do { \ + if (cond) { WARN_JSON(error); goto EXITIF_exit; } \ + } while (0) + +/** + * Convert a TALER amount to a JSON + * object. + * + * @param amount the amount + * @return a json object describing the amount + */ +json_t * +TALER_JSON_from_amount (struct TALER_Amount amount) +{ + json_t *j; + j = json_pack ("{s: s, s:I, s:I}", + "currency", amount.currency, + "value", (json_int_t) amount.value, + "fraction", (json_int_t) amount.fraction); + GNUNET_assert (NULL != j); + return j; +} + + +/** + * Convert absolute timestamp to a json string. + * + * @param the time stamp + * @return a json string with the timestamp in @a stamp + */ +json_t * +TALER_JSON_from_abs (struct GNUNET_TIME_Absolute stamp) +{ + json_t *j; + char *mystr; + int ret; + ret = GNUNET_asprintf (&mystr, "%llu", + (long long) (stamp.abs_value_us / (1000 * 1000))); + GNUNET_assert (ret > 0); + j = json_string (mystr); + GNUNET_free (mystr); + return j; +} + + + +/** + * Convert binary data to a JSON string + * with the base32crockford encoding. + * + * @param data binary data + * @param size size of @a data in bytes + * @return json string that encodes @a data + */ +json_t * +TALER_JSON_from_data (const void *data, size_t size) +{ + char *buf; + json_t *json; + buf = TALER_data_to_string_alloc (data, size); + json = json_string (buf); + GNUNET_free (buf); + return json; +} + + +/** + * Parse given JSON object to Amount + * + * @param json the json object representing Amount + * @param r_amount where the amount has to be written + * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error + */ +int +TALER_JSON_to_amount (json_t *json, + struct TALER_Amount *r_amount) +{ + char *currency; + json_int_t value; + json_int_t fraction; + json_error_t error; + + UNPACK_EXITIF (0 != json_unpack_ex (json, &error, JSON_STRICT, + "{s:s, s:I, s:I}", + "curreny", ¤cy, + "value", &value, + "fraction", &fraction)); + EXITIF (3 < strlen (currency)); + r_amount->value = (uint32_t) value; + r_amount->fraction = (uint32_t) fraction; + return GNUNET_OK; + + EXITIF_exit: + return GNUNET_SYSERR; +} + + +/** + * Parse given JSON object to Amount + * + * @param json the json object representing Amount + * @param r_amount where the amount has to be written + * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error + */ +int +TALER_JSON_to_abs (json_t *json, + struct GNUNET_TIME_Absolute *abs) +{ + const char *str; + unsigned long long abs_value_s; + + GNUNET_assert (NULL != abs); + EXITIF (NULL == (str = json_string_value (json))); + EXITIF (1 > sscanf (str, "%llu", &abs_value_s)); + abs->abs_value_us = abs_value_s * 1000 * 1000; + return GNUNET_OK; + + EXITIF_exit: + return GNUNET_SYSERR; +} + +/** + * Parse given JSON object to data + * + * @param json the json object representing data + * @param out the pointer to hold the parsed data. + * @param out_size the size of r_data. + * @return GNUNET_OK upon successful parsing; GNUNET_SYSERR upon error + */ +int +TALER_JSON_to_data (json_t *json, + void *out, + size_t out_size) +{ + const char *enc; + unsigned int len; + + EXITIF (NULL == (enc = json_string_value (json))); + len = strlen (enc); + EXITIF ((((len * 5) / 8) + ((((len * 5) % 8) == 0) ? 0 : 1)) == out_size); + EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, len, out, out_size)); + return GNUNET_OK; + EXITIF_exit: + return GNUNET_SYSERR; +} + +/* End of util/json.c */ diff --git a/src/util/microhttpd.c b/src/util/microhttpd.c new file mode 100644 index 000000000..f3bea74f8 --- /dev/null +++ b/src/util/microhttpd.c @@ -0,0 +1,417 @@ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include "taler_microhttpd_lib.h" + + + +/** + * Initial size for POST + * request buffer. + */ +#define REQUEST_BUFFER_INITIAL 1024 + +/** + * Maximum POST request size + */ +#define REQUEST_BUFFER_MAX (1024*1024) + + +/** + * Buffer for POST requests. + */ +struct Buffer +{ + /** + * Allocated memory + */ + char *data; + + /** + * Number of valid bytes in buffer. + */ + size_t fill; + + /** + * Number of allocated bytes in buffer. + */ + size_t alloc; +}; + + +/** + * Initialize a buffer. + * + * @param buf the buffer to initialize + * @param data the initial data + * @param data_size size of the initial data + * @param alloc_size size of the buffer + * @param max_size maximum size that the buffer can grow to + * @return a GNUnet result code + */ +static int +buffer_init (struct Buffer *buf, const void *data, size_t data_size, size_t alloc_size, size_t max_size) +{ + if (data_size > max_size || alloc_size > max_size) + return GNUNET_SYSERR; + if (data_size > alloc_size) + alloc_size = data_size; + buf->data = GNUNET_malloc (alloc_size); + memcpy (buf->data, data, data_size); + return GNUNET_OK; +} + + +/** + * Free the data in a buffer. Does *not* free + * the buffer object itself. + * + * @param buf buffer to de-initialize + */ +static void +buffer_deinit (struct Buffer *buf) +{ + GNUNET_free (buf->data); + buf->data = NULL; +} + + +/** + * Append data to a buffer, growing the buffer if necessary. + * + * @param buf the buffer to append to + * @param data the data to append + * @param size the size of @a data + * @param max_size maximum size that the buffer can grow to + * @return GNUNET_OK on success, + * GNUNET_NO if the buffer can't accomodate for the new data + * GNUNET_SYSERR on fatal error (out of memory?) + */ +static int +buffer_append (struct Buffer *buf, const void *data, size_t data_size, size_t max_size) +{ + if (buf->fill + data_size > max_size) + return GNUNET_NO; + if (data_size + buf->fill > buf->alloc) + { + char *new_buf; + size_t new_size = buf->alloc; + while (new_size < buf->fill + data_size) + new_size += 2; + if (new_size > max_size) + return GNUNET_NO; + new_buf = GNUNET_malloc (new_size); + memcpy (new_buf, buf->data, buf->fill); + buf->data = new_buf; + buf->alloc = new_size; + } + memcpy (buf->data + buf->fill, data, data_size); + buf->fill += data_size; + return GNUNET_OK; +} + + +/** + * Send JSON object as response. Decreases the reference count of the + * JSON object. + * + * @param connection the MHD connection + * @param json the json object + * @param status_code the http status code + * @return MHD result code + */ +int +send_response_json (struct MHD_Connection *connection, + json_t *json, + unsigned int status_code) +{ + struct MHD_Response *resp; + char *json_str; + + json_str = json_dumps (json, JSON_INDENT(2)); + json_decref (json); + resp = MHD_create_response_from_buffer (strlen (json_str), json_str, + MHD_RESPMEM_MUST_FREE); + if (NULL == resp) + return MHD_NO; + return MHD_queue_response (connection, status_code, resp); +} + + +/** + * Send a JSON object via an MHD connection, + * specified with the JANSSON pack syntax (see json_pack). + * + * @param connection connection to send the JSON over + * @param http_code HTTP status for the response + * @param fmt format string for pack + * @param ... varargs + * @return MHD_YES on success or MHD_NO on error + */ +int +request_send_json_pack (struct MHD_Connection *connection, + unsigned int http_code, + const char *fmt, ...) +{ + json_t *msg; + va_list argp; + int ret; + + va_start(argp, fmt); + msg = json_vpack_ex (NULL, 0, fmt, argp); + va_end(argp); + if (NULL == msg) + return MHD_NO; + ret = send_response_json (connection, msg, http_code); + json_decref (msg); + return ret; +} + + +/** + * Process a POST request containing a JSON object. + * + * @param connection the MHD connection + * @param con_cs the closure (contains a 'struct Buffer *') + * @param upload_data the POST data + * @param upload_data_size the POST data size + * @param json the JSON object for a completed request + * + * @returns + * GNUNET_YES if json object was parsed + * GNUNET_NO is request incomplete or invalid + * GNUNET_SYSERR on internal error + */ +int +process_post_json (struct MHD_Connection *connection, + void **con_cls, + const char *upload_data, + size_t *upload_data_size, + json_t **json) +{ + struct Buffer *r = *con_cls; + + if (NULL == *con_cls) + { + /* We are seeing a fresh POST request. */ + + r = GNUNET_new (struct Buffer); + if (GNUNET_OK != buffer_init (r, upload_data, *upload_data_size, + REQUEST_BUFFER_INITIAL, REQUEST_BUFFER_MAX)) + { + *con_cls = NULL; + buffer_deinit (r); + GNUNET_free (r); + return GNUNET_SYSERR; + } + *upload_data_size = 0; + *con_cls = r; + return GNUNET_NO; + } + if (0 != *upload_data_size) + { + /* We are seeing an old request with more data available. */ + + if (GNUNET_OK != buffer_append (r, upload_data, *upload_data_size, + REQUEST_BUFFER_MAX)) + { + /* Request too long or we're out of memory. */ + + *con_cls = NULL; + buffer_deinit (r); + GNUNET_free (r); + return GNUNET_SYSERR; + } + *upload_data_size = 0; + return GNUNET_NO; + } + + /* We have seen the whole request. */ + + *json = json_loadb (r->data, r->fill, 0, NULL); + buffer_deinit (r); + GNUNET_free (r); + if (NULL == *json) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Can't parse JSON request body\n"); + return request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, + GNUNET_NO, GNUNET_SYSERR, + "{s:s}", + "error", "invalid json"); + } + *con_cls = NULL; + + return GNUNET_YES; +} + + +/** + * Navigate through a JSON tree. + * + * Sends an error response if navigation is impossible (i.e. + * the JSON object is invalid) + * + * @param connection the connection to send an error response to + * @param root the JSON node to start the navigation at. + * @param ... navigation specification (see JNAV_*) + * @return GNUNET_YES if navigation was successful + * GNUNET_NO if json is malformed, error response was generated + * GNUNET_SYSERR on internal error + */ +int +request_json_require_nav (struct MHD_Connection *connection, + const json_t *root, ...) +{ + va_list argp; + int ignore = GNUNET_NO; + // what's our current path from 'root'? + json_t *path; + + path = json_array (); + + va_start(argp, root); + + while (1) + { + int command = va_arg(argp, int); + switch (command) + { + case JNAV_FIELD: + { + const char *fname = va_arg(argp, const char *); + if (GNUNET_YES == ignore) + break; + json_array_append_new (path, json_string (fname)); + root = json_object_get (root, fname); + if (NULL == root) + { + + (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, + 0, 0, + "{s:s,s:o}", + "error", "missing field in JSON", + "path", path); + ignore = GNUNET_YES; + break; + } + } + break; + case JNAV_INDEX: + { + int fnum = va_arg(argp, int); + if (GNUNET_YES == ignore) + break; + json_array_append_new (path, json_integer (fnum)); + root = json_array_get (root, fnum); + if (NULL == root) + { + (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, + 0, 0, + "{s:s, s:o}", + "error", "missing index in JSON", + "path", path); + ignore = GNUNET_YES; + break; + } + } + break; + case JNAV_RET_DATA: + { + void *where = va_arg (argp, void *); + size_t len = va_arg (argp, size_t); + const char *str; + int res; + + va_end(argp); + if (GNUNET_YES == ignore) + return GNUNET_NO; + str = json_string_value (root); + if (NULL == str) + { + (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, + 0, 0, + "{s:s, s:o}", + "error", "string expected", + "path", path); + return GNUNET_NO; + } + res = GNUNET_STRINGS_string_to_data (str, strlen (str), + where, len); + if (GNUNET_OK != res) + { + (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, + 0, 0, + "{s:s,s:o}", + "error", "malformed binary data in JSON", + "path", path); + return GNUNET_NO; + } + return GNUNET_YES; + } + break; + case JNAV_RET_DATA_VAR: + { + void **where = va_arg (argp, void **); + size_t *len = va_arg (argp, size_t *); + const char *str; + + va_end(argp); + if (GNUNET_YES == ignore) + return GNUNET_NO; + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + *len = (strlen (str) * 5) / 8; + if (where != NULL) + { + int res; + *where = GNUNET_malloc (*len); + res = GNUNET_STRINGS_string_to_data (str, strlen (str), + *where, *len); + if (GNUNET_OK != res) + { + (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, + 0, 0, + "{s:s, s:o}", + "error", "malformed binary data in JSON", + "path", path); + return GNUNET_NO; + } + } + return GNUNET_OK; + } + break; + case JNAV_RET_TYPED_JSON: + { + int typ = va_arg (argp, int); + const json_t **r_json = va_arg (argp, const json_t **); + + va_end(argp); + if (GNUNET_YES == ignore) + return GNUNET_NO; + if (typ != -1 && json_typeof (root) != typ) + { + (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, + 0, 0, + "{s:s, s:i, s:i s:o}", + "error", "wrong JSON field type", + "type_expected", typ, + "type_actual", json_typeof (root), + "path", path); + return GNUNET_NO; + } + *r_json = root; + return GNUNET_OK; + } + break; + default: + GNUNET_assert (0); + } + } + GNUNET_assert (0); +} + + + diff --git a/src/util/misc.supp b/src/util/misc.supp new file mode 100644 index 000000000..afcac6128 --- /dev/null +++ b/src/util/misc.supp @@ -0,0 +1,28 @@ +{ + <gnunet_gcrypt_init> + Memcheck:Leak + match-leak-kinds:reachable + ... + fun:GNUNET_CRYPTO_random_init + fun:call_init.part.0 + ... +} + +{ + <mpi_ec_new> + Memcheck:Leak + match-leak-kinds:reachable + ... + fun:point_from_keyparam + fun:_gcry_mpi_ec_new + ... +} + +{ + <gnunet_log_setup> + Memcheck:Leak + match-leak-kinds:reachable + ... + fun:GNUNET_log_setup + ... +}
\ No newline at end of file diff --git a/src/util/rsa.c b/src/util/rsa.c new file mode 100644 index 000000000..cde56be9e --- /dev/null +++ b/src/util/rsa.c @@ -0,0 +1,925 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + 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, If not, see <http://www.gnu.org/licenses/> +*/ + +/** + * @file util/rsa.c + * @brief RSA key management utilities. Most of the code here is taken from + * gnunet-0.9.5a + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + * + * Authors of the gnunet code: + * Christian Grothoff + * Krista Bennett + * Gerd Knorr <kraxel@bytesex.org> + * Ioana Patrascu + * Tzvetan Horozov + */ + +#include "platform.h" +#include "gcrypt.h" +#include "gnunet/gnunet_util_lib.h" +#include "taler_rsa.h" + + + +#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__) + +#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall) + +#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename) + +/** + * Log an error message at log-level 'level' that indicates + * a failure of the command 'cmd' with the message given + * by gcry_strerror(rc). + */ +#define LOG_GCRY(level, cmd, rc) do { LOG(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0) + +/** + * Shorthand to cleanup non null mpi data types + */ +#define mpi_release_non_null(mpi) \ + if (NULL != mpi) gcry_mpi_release (mpi); + +/** + * The private information of an RSA key pair. + * NOTE: this must match the definition in crypto_ksk.c and gnunet-rsa.c! + */ +struct TALER_RSA_PrivateKey +{ + /** + * Libgcrypt S-expression for the ECC key. + */ + gcry_sexp_t sexp; +}; + + +/** + * Extract values from an S-expression. + * + * @param array where to store the result(s) + * @param sexp S-expression to parse + * @param topname top-level name in the S-expression that is of interest + * @param elems names of the elements to extract + * @return 0 on success + */ +static int +key_from_sexp (gcry_mpi_t * array, gcry_sexp_t sexp, const char *topname, + const char *elems) +{ + gcry_sexp_t list; + gcry_sexp_t l2; + const char *s; + unsigned int i; + unsigned int idx; + + if (! (list = gcry_sexp_find_token (sexp, topname, 0))) + return 1; + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + if (! list) + return 2; + idx = 0; + for (s = elems; *s; s++, idx++) + { + if (! (l2 = gcry_sexp_find_token (list, s, 1))) + { + for (i = 0; i < idx; i++) + { + gcry_free (array[i]); + array[i] = NULL; + } + gcry_sexp_release (list); + return 3; /* required parameter not found */ + } + array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l2); + if (! array[idx]) + { + for (i = 0; i < idx; i++) + { + gcry_free (array[i]); + array[i] = NULL; + } + gcry_sexp_release (list); + return 4; /* required parameter is invalid */ + } + } + gcry_sexp_release (list); + return 0; +} + +/** + * If target != size, move target bytes to the + * end of the size-sized buffer and zero out the + * first target-size bytes. + * + * @param buf original buffer + * @param size number of bytes in the buffer + * @param target target size of the buffer + */ +static void +adjust (unsigned char *buf, size_t size, size_t target) +{ + if (size < target) + { + memmove (&buf[target - size], buf, size); + memset (buf, 0, target - size); + } +} + + +/** + * Create a new private key. Caller must free return value. + * + * @return fresh private key + */ +struct TALER_RSA_PrivateKey * +TALER_RSA_key_create () +{ + struct TALER_RSA_PrivateKey *ret; + gcry_sexp_t s_key; + gcry_sexp_t s_keyparam; + + GNUNET_assert (0 == + gcry_sexp_build (&s_keyparam, NULL, + "(genkey(rsa(nbits %d)(rsa-use-e 3:257)))", + 2048)); + GNUNET_assert (0 == gcry_pk_genkey (&s_key, s_keyparam)); + gcry_sexp_release (s_keyparam); +#if EXTRA_CHECKS + GNUNET_assert (0 == gcry_pk_testkey (s_key)); +#endif + ret = GNUNET_malloc (sizeof (struct TALER_RSA_PrivateKey)); + ret->sexp = s_key; + return ret; +} + + +/** + * Free memory occupied by the private key. + * + * @param key pointer to the memory to free + */ +void +TALER_RSA_key_free (struct TALER_RSA_PrivateKey *key) +{ + gcry_sexp_release (key->sexp); + GNUNET_free (key); +} + + +/** + * Encode the private key in a format suitable for + * storing it into a file. + * @return encoding of the private key + */ +struct TALER_RSA_PrivateKeyBinaryEncoded * +TALER_RSA_encode_key (const struct TALER_RSA_PrivateKey *hostkey) +{ + struct TALER_RSA_PrivateKeyBinaryEncoded *retval; + gcry_mpi_t pkv[6]; + void *pbu[6]; + size_t sizes[6]; + int rc; + int i; + int size; + +#if EXTRA_CHECKS + if (gcry_pk_testkey (hostkey->sexp)) + { + GNUNET_break (0); + return NULL; + } +#endif + + memset (pkv, 0, sizeof (gcry_mpi_t) * 6); + rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "nedpqu"); + if (rc) + rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "nedpqu"); + if (rc) + rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "nedpq"); + if (rc) + rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "nedpq"); + if (rc) + rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "ned"); + if (rc) + rc = key_from_sexp (pkv, hostkey->sexp, "rsa", "ned"); + GNUNET_assert (0 == rc); + size = sizeof (struct TALER_RSA_PrivateKeyBinaryEncoded); + for (i = 0; i < 6; i++) + { + if (NULL != pkv[i]) + { + GNUNET_assert (0 == + gcry_mpi_aprint (GCRYMPI_FMT_USG, + (unsigned char **) &pbu[i], &sizes[i], + pkv[i])); + size += sizes[i]; + } + else + { + pbu[i] = NULL; + sizes[i] = 0; + } + } + GNUNET_assert (size < 65536); + retval = GNUNET_malloc (size); + retval->len = htons (size); + i = 0; + retval->sizen = htons (sizes[0]); + memcpy (&((char *) (&retval[1]))[i], pbu[0], sizes[0]); + i += sizes[0]; + retval->sizee = htons (sizes[1]); + memcpy (&((char *) (&retval[1]))[i], pbu[1], sizes[1]); + i += sizes[1]; + retval->sized = htons (sizes[2]); + memcpy (&((char *) (&retval[1]))[i], pbu[2], sizes[2]); + i += sizes[2]; + /* swap p and q! */ + retval->sizep = htons (sizes[4]); + memcpy (&((char *) (&retval[1]))[i], pbu[4], sizes[4]); + i += sizes[4]; + retval->sizeq = htons (sizes[3]); + memcpy (&((char *) (&retval[1]))[i], pbu[3], sizes[3]); + i += sizes[3]; + retval->sizedmp1 = htons (0); + retval->sizedmq1 = htons (0); + memcpy (&((char *) (&retval[1]))[i], pbu[5], sizes[5]); + for (i = 0; i < 6; i++) + { + if (pkv[i] != NULL) + gcry_mpi_release (pkv[i]); + if (pbu[i] != NULL) + free (pbu[i]); + } + return retval; +} + + +/** + * Extract the public key of the given private key. + * + * @param priv the private key + * @param pub where to write the public key + */ +void +TALER_RSA_key_get_public (const struct TALER_RSA_PrivateKey *priv, + struct TALER_RSA_PublicKeyBinaryEncoded *pub) +{ + gcry_mpi_t skey[2]; + size_t size; + int rc; + + rc = key_from_sexp (skey, priv->sexp, "public-key", "ne"); + if (0 != rc) + rc = key_from_sexp (skey, priv->sexp, "private-key", "ne"); + if (0 != rc) + rc = key_from_sexp (skey, priv->sexp, "rsa", "ne"); + GNUNET_assert (0 == rc); + pub->len = + htons (sizeof (struct TALER_RSA_PublicKeyBinaryEncoded) - + sizeof (pub->padding)); + pub->sizen = htons (TALER_RSA_DATA_ENCODING_LENGTH); + pub->padding = 0; + size = TALER_RSA_DATA_ENCODING_LENGTH; + GNUNET_assert (0 == + gcry_mpi_print (GCRYMPI_FMT_USG, &pub->key[0], size, &size, + skey[0])); + adjust (&pub->key[0], size, TALER_RSA_DATA_ENCODING_LENGTH); + size = TALER_RSA_KEY_LENGTH - TALER_RSA_DATA_ENCODING_LENGTH; + GNUNET_assert (0 == + gcry_mpi_print (GCRYMPI_FMT_USG, + &pub->key + [TALER_RSA_DATA_ENCODING_LENGTH], size, + &size, skey[1])); + adjust (&pub->key[TALER_RSA_DATA_ENCODING_LENGTH], size, + TALER_RSA_KEY_LENGTH - + TALER_RSA_DATA_ENCODING_LENGTH); + gcry_mpi_release (skey[0]); + gcry_mpi_release (skey[1]); +} + + +/** + * Decode the private key from the data-format back + * to the "normal", internal format. + * + * @param buf the buffer where the private key data is stored + * @param len the length of the data in 'buffer' + * @return NULL on error + */ +struct TALER_RSA_PrivateKey * +TALER_RSA_decode_key (const char *buf, uint16_t len) +{ + struct TALER_RSA_PrivateKey *ret; + const struct TALER_RSA_PrivateKeyBinaryEncoded *encoding = + (const struct TALER_RSA_PrivateKeyBinaryEncoded *) buf; + gcry_sexp_t res; + gcry_mpi_t n; + gcry_mpi_t e; + gcry_mpi_t d; + gcry_mpi_t p; + gcry_mpi_t q; + gcry_mpi_t u; + int rc; + size_t size; + size_t pos; + uint16_t enc_len; + size_t erroff; + + enc_len = ntohs (encoding->len); + if (len != enc_len) + return NULL; + + pos = 0; + size = ntohs (encoding->sizen); + rc = gcry_mpi_scan (&n, GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], size, + &size); + pos += ntohs (encoding->sizen); + if (0 != rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + return NULL; + } + size = ntohs (encoding->sizee); + rc = gcry_mpi_scan (&e, GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], size, + &size); + pos += ntohs (encoding->sizee); + if (0 != rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + return NULL; + } + size = ntohs (encoding->sized); + rc = gcry_mpi_scan (&d, GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], size, + &size); + pos += ntohs (encoding->sized); + if (0 != rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + gcry_mpi_release (e); + return NULL; + } + /* swap p and q! */ + size = ntohs (encoding->sizep); + if (size > 0) + { + rc = gcry_mpi_scan (&q, GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], size, + &size); + pos += ntohs (encoding->sizep); + if (0 != rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + gcry_mpi_release (e); + gcry_mpi_release (d); + return NULL; + } + } + else + q = NULL; + size = ntohs (encoding->sizeq); + if (size > 0) + { + rc = gcry_mpi_scan (&p, GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], size, + &size); + pos += ntohs (encoding->sizeq); + if (0 != rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + gcry_mpi_release (e); + gcry_mpi_release (d); + if (NULL != q) + gcry_mpi_release (q); + return NULL; + } + } + else + p = NULL; + pos += ntohs (encoding->sizedmp1); + pos += ntohs (encoding->sizedmq1); + size = + ntohs (encoding->len) - sizeof (struct TALER_RSA_PrivateKeyBinaryEncoded) - pos; + if (size > 0) + { + rc = gcry_mpi_scan (&u, GCRYMPI_FMT_USG, + &((const unsigned char *) (&encoding[1]))[pos], size, + &size); + if (0 != rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + gcry_mpi_release (e); + gcry_mpi_release (d); + if (NULL != p) + gcry_mpi_release (p); + if (NULL != q) + gcry_mpi_release (q); + return NULL; + } + } + else + u = NULL; + + if ((NULL != p) && (NULL != q) && (NULL != u)) + { + rc = gcry_sexp_build (&res, &erroff, + "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)(u %m)))", + n, e, d, p, q, u); + } + else + { + if ((NULL != p) && (NULL != q)) + { + rc = gcry_sexp_build (&res, &erroff, + "(private-key(rsa(n %m)(e %m)(d %m)(p %m)(q %m)))", + n, e, d, p, q); + } + else + { + rc = gcry_sexp_build (&res, &erroff, + "(private-key(rsa(n %m)(e %m)(d %m)))", n, e, d); + } + } + gcry_mpi_release (n); + gcry_mpi_release (e); + gcry_mpi_release (d); + if (NULL != p) + gcry_mpi_release (p); + if (NULL != q) + gcry_mpi_release (q); + if (NULL != u) + gcry_mpi_release (u); + + if (0 != rc) + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); + if (0 != (rc = gcry_pk_testkey (res))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); + return NULL; + } + ret = GNUNET_malloc (sizeof (struct TALER_RSA_PrivateKey)); + ret->sexp = res; + return ret; +} + + +/** + * Convert a public key to a string. + * + * @param pub key to convert + * @return string representing 'pub' + */ +char * +TALER_RSA_public_key_to_string (const struct TALER_RSA_PublicKeyBinaryEncoded *pub) +{ + char *pubkeybuf; + size_t keylen = (sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)) * 8; + char *end; + + if (keylen % 5 > 0) + keylen += 5 - keylen % 5; + keylen /= 5; + pubkeybuf = GNUNET_malloc (keylen + 1); + end = GNUNET_STRINGS_data_to_string ((unsigned char *) pub, + sizeof (struct TALER_RSA_PublicKeyBinaryEncoded), + pubkeybuf, + keylen); + if (NULL == end) + { + GNUNET_free (pubkeybuf); + return NULL; + } + *end = '\0'; + return pubkeybuf; +} + + +/** + * Convert a string representing a public key to a public key. + * + * @param enc encoded public key + * @param enclen number of bytes in enc (without 0-terminator) + * @param pub where to store the public key + * @return GNUNET_OK on success + */ +int +TALER_RSA_public_key_from_string (const char *enc, + size_t enclen, + struct TALER_RSA_PublicKeyBinaryEncoded *pub) +{ + size_t keylen = (sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)) * 8; + + if (keylen % 5 > 0) + keylen += 5 - keylen % 5; + keylen /= 5; + if (enclen != keylen) + return GNUNET_SYSERR; + + if (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, enclen, + (unsigned char*) pub, + sizeof (struct TALER_RSA_PublicKeyBinaryEncoded))) + return GNUNET_SYSERR; + if ( (ntohs (pub->len) != sizeof (struct TALER_RSA_PublicKeyBinaryEncoded)) || + (ntohs (pub->padding) != 0) || + (ntohs (pub->sizen) != TALER_RSA_DATA_ENCODING_LENGTH) ) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * Convert the data specified in the given purpose argument to an + * S-expression suitable for signature operations. + * + * @param ptr pointer to the data to convert + * @param size the size of the data + * @return converted s-expression + */ +static gcry_sexp_t +data_to_sexp (const void *ptr, size_t size) +{ + gcry_mpi_t value; + gcry_sexp_t data; + + value = NULL; + data = NULL; + GNUNET_assert (0 == gcry_mpi_scan (&value, GCRYMPI_FMT_USG, ptr, size, NULL)); + GNUNET_assert (0 == gcry_sexp_build (&data, NULL, "(data (flags raw) (value %M))", value)); + gcry_mpi_release (value); + return data; +} + + +/** + * Sign the given hash block. + * + * @param key private key to use for the signing + * @param hash the block containing the hash of the message to sign + * @param hash_size the size of the hash block + * @param sig where to write the signature + * @return GNUNET_SYSERR on error, GNUNET_OK on success + */ +int +TALER_RSA_sign (const struct TALER_RSA_PrivateKey *key, + const void *hash, + size_t hash_size, + struct TALER_RSA_Signature *sig) +{ + gcry_sexp_t result; + gcry_sexp_t data; + size_t ssize; + gcry_mpi_t rval; + + data = data_to_sexp (hash, hash_size); + GNUNET_assert (0 == gcry_pk_sign (&result, data, key->sexp)); + gcry_sexp_release (data); + GNUNET_assert (0 == key_from_sexp (&rval, result, "rsa", "s")); + gcry_sexp_release (result); + ssize = sizeof (struct TALER_RSA_Signature); + GNUNET_assert (0 == + gcry_mpi_print (GCRYMPI_FMT_USG, (unsigned char *) sig, ssize, + &ssize, rval)); + gcry_mpi_release (rval); + adjust (sig->sig, ssize, sizeof (struct TALER_RSA_Signature)); + return GNUNET_OK; +} + + +/** + * Convert the given public key from the network format to the + * S-expression that can be used by libgcrypt. + * + * @param publicKey public key to decode + * @return NULL on error + */ +static gcry_sexp_t +decode_public_key (const struct TALER_RSA_PublicKeyBinaryEncoded *publicKey) +{ + gcry_sexp_t result; + gcry_mpi_t n; + gcry_mpi_t e; + size_t size; + size_t erroff; + int rc; + + if ((ntohs (publicKey->sizen) != TALER_RSA_DATA_ENCODING_LENGTH) || + (ntohs (publicKey->len) != + sizeof (struct TALER_RSA_PublicKeyBinaryEncoded) - + sizeof (publicKey->padding))) + { + GNUNET_break (0); + return NULL; + } + size = TALER_RSA_DATA_ENCODING_LENGTH; + if (0 != (rc = gcry_mpi_scan (&n, GCRYMPI_FMT_USG, &publicKey->key[0], size, &size))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + return NULL; + } + size = TALER_RSA_KEY_LENGTH - TALER_RSA_DATA_ENCODING_LENGTH; + if (0 != (rc = gcry_mpi_scan (&e, GCRYMPI_FMT_USG, + &publicKey->key[TALER_RSA_DATA_ENCODING_LENGTH], + size, &size))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + gcry_mpi_release (n); + return NULL; + } + rc = gcry_sexp_build (&result, &erroff, "(public-key(rsa(n %m)(e %m)))", n, + e); + gcry_mpi_release (n); + gcry_mpi_release (e); + if (0 != rc) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); /* erroff gives more info */ + return NULL; + } + return result; +} + + +/** + * Verify signature with the given hash. + * + * @param hash the hash code to verify against the signature + * @param sig signature that is being validated + * @param publicKey public key of the signer + * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid + */ +int +TALER_RSA_hash_verify (const struct GNUNET_HashCode *hash, + const struct TALER_RSA_Signature *sig, + const struct TALER_RSA_PublicKeyBinaryEncoded *publicKey) +{ + gcry_sexp_t data; + gcry_sexp_t sigdata; + size_t size; + gcry_mpi_t val; + gcry_sexp_t psexp; + size_t erroff; + int rc; + + size = sizeof (struct TALER_RSA_Signature); + GNUNET_assert (0 == + gcry_mpi_scan (&val, GCRYMPI_FMT_USG, + (const unsigned char *) sig, size, &size)); + GNUNET_assert (0 == + gcry_sexp_build (&sigdata, &erroff, "(sig-val(rsa(s %m)))", + val)); + gcry_mpi_release (val); + data = data_to_sexp (hash, sizeof (struct GNUNET_HashCode)); + if (! (psexp = decode_public_key (publicKey))) + { + gcry_sexp_release (data); + gcry_sexp_release (sigdata); + return GNUNET_SYSERR; + } + rc = gcry_pk_verify (sigdata, data, psexp); + gcry_sexp_release (psexp); + gcry_sexp_release (data); + gcry_sexp_release (sigdata); + if (rc) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("RSA signature verification failed at %s:%d: %s\n"), __FILE__, + __LINE__, gcry_strerror (rc)); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Verify signature on the given message + * + * @param msg the message + * @param size the size of the message + * @param sig signature that is being validated + * @param publicKey public key of the signer + * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid + */ +int +TALER_RSA_verify (const void *msg, size_t size, + const struct TALER_RSA_Signature *sig, + const struct TALER_RSA_PublicKeyBinaryEncoded *publicKey) +{ + struct GNUNET_HashCode hash; + + GNUNET_CRYPTO_hash (msg, size, &hash); + return TALER_RSA_hash_verify (&hash, sig, publicKey); +} + +/** + * The blinding key is equal in length to the RSA modulus + */ +#define TALER_RSA_BLINDING_KEY_LEN TALER_RSA_DATA_ENCODING_LENGTH + +struct TALER_RSA_BlindingKey +{ + /** + * The blinding factor + */ + gcry_mpi_t r; +}; + +struct TALER_RSA_BlindingKey * +TALER_RSA_blinding_key_create () +{ + struct TALER_RSA_BlindingKey *blind; + + blind = GNUNET_new (struct TALER_RSA_BlindingKey); + blind->r = gcry_mpi_new (TALER_RSA_BLINDING_KEY_LEN * 8); + gcry_mpi_randomize (blind->r, TALER_RSA_BLINDING_KEY_LEN * 8, GCRY_STRONG_RANDOM); + return blind; +} + + +void +TALER_RSA_blinding_key_destroy (struct TALER_RSA_BlindingKey *bkey) +{ + gcry_mpi_release (bkey->r); + GNUNET_free (bkey); +} + + +struct TALER_RSA_BlindedSignaturePurpose * +TALER_RSA_message_blind (const void *msg, size_t size, + struct TALER_RSA_BlindingKey *bkey, + struct TALER_RSA_PublicKeyBinaryEncoded *pkey) +{ + struct TALER_RSA_BlindedSignaturePurpose *bsp; + struct GNUNET_HashCode hash; + gcry_sexp_t psexp; + gcry_mpi_t data; + gcry_mpi_t skey[2]; + gcry_mpi_t r_e; + gcry_mpi_t data_r_e; + size_t rsize; + gcry_error_t rc; + int ret; + + bsp = NULL; + psexp = NULL; + data = NULL; + skey[0] = skey[1] = NULL; + r_e = NULL; + data_r_e = NULL; + rsize = 0; + rc = 0; + ret = 0; + if (! (psexp = decode_public_key (pkey))) + return NULL; + ret = key_from_sexp (skey, psexp, "public-key", "ne"); + if (0 != ret) + ret = key_from_sexp (skey, psexp, "rsa", "ne"); + gcry_sexp_release (psexp); + psexp = NULL; + GNUNET_assert (0 == ret); + GNUNET_CRYPTO_hash (msg, size, &hash); + if (0 != (rc=gcry_mpi_scan (&data, GCRYMPI_FMT_USG, + (const unsigned char *) msg, size, &rsize))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_WARNING, "gcry_mpi_scan", rc); + goto cleanup; + } + r_e = gcry_mpi_new (0); + gcry_mpi_powm (r_e, bkey->r, + skey[1], /* e */ + skey[0]); /* n */ + + data_r_e = gcry_mpi_new (0); + gcry_mpi_mulm (data_r_e, data, r_e, skey[0]); + + bsp = GNUNET_new (struct TALER_RSA_BlindedSignaturePurpose); + rc = gcry_mpi_print (GCRYMPI_FMT_USG, + (unsigned char *) bsp, + sizeof (struct TALER_RSA_BlindedSignaturePurpose), + &rsize, + data_r_e); + GNUNET_assert (0 == rc); + adjust ((unsigned char *) bsp, rsize, + sizeof (struct TALER_RSA_BlindedSignaturePurpose)); + + cleanup: + if (NULL != psexp) gcry_sexp_release (psexp); + mpi_release_non_null (skey[0]); + mpi_release_non_null (skey[1]); + mpi_release_non_null (data); + mpi_release_non_null (r_e); + mpi_release_non_null (data_r_e); + return bsp; +} + + +int +TALER_RSA_unblind (struct TALER_RSA_Signature *sig, + struct TALER_RSA_BlindingKey *bkey, + struct TALER_RSA_PublicKeyBinaryEncoded *pkey) +{ + gcry_sexp_t psexp; + gcry_mpi_t skey; + gcry_mpi_t sigval; + gcry_mpi_t r_inv; + gcry_mpi_t ubsig; + size_t rsize; + gcry_error_t rc; + int ret; + + psexp = NULL; + skey = NULL; + sigval = NULL; + r_inv = NULL; + ubsig = NULL; + rsize = 0; + rc = 0; + ret = GNUNET_SYSERR; + if (! (psexp = decode_public_key (pkey))) + return GNUNET_SYSERR; + ret = key_from_sexp (&skey, psexp, "public-key", "n"); + if (0 != ret) + ret = key_from_sexp (&skey, psexp, "rsa", "n"); + gcry_sexp_release (psexp); + psexp = NULL; + if (0 != (rc = gcry_mpi_scan (&sigval, GCRYMPI_FMT_USG, + (const unsigned char *) sig, + sizeof (struct TALER_RSA_Signature), + &rsize))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); + goto cleanup; + } + r_inv = gcry_mpi_new (0); + GNUNET_assert (1 == gcry_mpi_invm (r_inv, bkey->r, skey)); /* n: skey */ + ubsig = gcry_mpi_new (0); + gcry_mpi_mulm (ubsig, sigval, r_inv, skey); + rc = gcry_mpi_print (GCRYMPI_FMT_USG, + (unsigned char *) sig, + sizeof (struct TALER_RSA_Signature), + &rsize, + ubsig); + GNUNET_assert (0 == rc); + adjust ((unsigned char *) sig, rsize, sizeof (struct TALER_RSA_Signature)); + ret = GNUNET_OK; + + cleanup: + if (NULL != psexp) gcry_sexp_release (psexp); + mpi_release_non_null (skey); + mpi_release_non_null (sigval); + mpi_release_non_null (r_inv); + mpi_release_non_null (ubsig); + return ret; +} + + +/** + * Encode a blinding key + * + * @param bkey the blinding key to encode + * @param bkey_enc where to store the encoded binary key + * @return #GNUNET_OK upon successful encoding; #GNUNET_SYSERR upon failure + */ +int +TALER_RSA_blinding_key_encode (struct TALER_RSA_BlindingKey *bkey, + struct TALER_RSA_BlindingKeyBinaryEncoded *bkey_enc) +{ + GNUNET_abort (); /* FIXME: not implemented */ +} + + +/** + * Decode a blinding key from its encoded form + * + * @param bkey_enc the encoded blinding key + * @return the decoded blinding key; NULL upon error + */ +struct TALER_RSA_BlindingKey * +TALER_RSA_blinding_key_decode (struct TALER_RSA_BlindingKeyBinaryEncoded *bkey_enc) +{ + GNUNET_abort (); /* FIXME: not implemented */ +} + +/* end of util/rsa.c */ diff --git a/src/util/test_hash_context.c b/src/util/test_hash_context.c new file mode 100644 index 000000000..e5110f212 --- /dev/null +++ b/src/util/test_hash_context.c @@ -0,0 +1,48 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + 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, If not, see <http://www.gnu.org/licenses/> +*/ + +/** + * @file util/test_hash_context.c + * @brief test case for incremental hashing + * @author Florian Dold + */ + +#include "platform.h" +#include "taler_util.h" +#include <gcrypt.h> + +#define LEN 1234 + +int main() +{ + char data[1234]; + struct GNUNET_HashCode hc1; + struct GNUNET_HashCode hc2; + struct TALER_HashContext hctx; + + memset (data, 42, LEN); + + TALER_hash_context_start (&hctx); + TALER_hash_context_read (&hctx, data, LEN); + TALER_hash_context_finish (&hctx, &hc1); + + GNUNET_CRYPTO_hash (data, LEN, &hc2); + + if (0 == memcmp (&hc1, &hc2, sizeof (struct GNUNET_HashCode))) + return 0; + return 1; +} + diff --git a/src/util/test_rsa.c b/src/util/test_rsa.c new file mode 100644 index 000000000..ac3ae2cd4 --- /dev/null +++ b/src/util/test_rsa.c @@ -0,0 +1,112 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + 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, If not, see <http://www.gnu.org/licenses/> +*/ + +/** + * @file util/test_rsa.c + * @brief testcase for utility functions for RSA cryptography + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ +#include "platform.h" +#include "taler_rsa.h" +#include <gnunet/gnunet_util_lib.h> + +#define TEST_PURPOSE UINT32_MAX + + +#define EXITIF(cond) \ + do { \ + if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ + } while (0) + +int +main (int argc, char *argv[]) +{ +#define RND_BLK_SIZE 4096 + unsigned char rnd_blk[RND_BLK_SIZE]; + struct TALER_RSA_PrivateKey *priv; + struct TALER_RSA_PrivateKeyBinaryEncoded *priv_enc; + struct TALER_RSA_PublicKeyBinaryEncoded pubkey; + struct TALER_RSA_BlindingKey *bkey; + struct TALER_RSA_BlindedSignaturePurpose *bsp; + struct TALER_RSA_Signature sig; + struct GNUNET_HashCode hash; + int ret; + + priv = NULL; + bsp = NULL; + bkey = NULL; + ret = 1; + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, rnd_blk, + RND_BLK_SIZE); + GNUNET_CRYPTO_hash (rnd_blk, RND_BLK_SIZE, &hash); + priv = TALER_RSA_key_create (); + GNUNET_assert (NULL != priv); + EXITIF (GNUNET_OK != TALER_RSA_sign (priv, + &hash, sizeof (hash), + &sig)); + TALER_RSA_key_get_public (priv, &pubkey); + EXITIF (NULL == (priv_enc = TALER_RSA_encode_key (priv))); + TALER_RSA_key_free (priv); + priv = NULL; + EXITIF (NULL == (priv = TALER_RSA_decode_key ((const char *) priv_enc, + ntohs (priv_enc->len)))); + GNUNET_free (priv_enc); + priv_enc = NULL; + EXITIF (GNUNET_OK != TALER_RSA_hash_verify (&hash, + &sig, + &pubkey)); + EXITIF (GNUNET_OK != TALER_RSA_verify (rnd_blk, + RND_BLK_SIZE, + &sig, + &pubkey)); + + /* test blind signing */ + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, rnd_blk, + RND_BLK_SIZE); + GNUNET_CRYPTO_hash (rnd_blk, RND_BLK_SIZE, &hash); + (void) memset (&sig, 0, sizeof (struct TALER_RSA_Signature)); + EXITIF (NULL == (bkey = TALER_RSA_blinding_key_create ())); + EXITIF (NULL == (bsp = + TALER_RSA_message_blind (&hash, sizeof (hash), + bkey, &pubkey))); + EXITIF (GNUNET_OK != TALER_RSA_sign (priv, + bsp, + sizeof (struct TALER_RSA_BlindedSignaturePurpose), + &sig)); + EXITIF (GNUNET_OK != TALER_RSA_unblind (&sig, + bkey, + &pubkey)); + EXITIF (GNUNET_OK != TALER_RSA_hash_verify (&hash, + &sig, + &pubkey)); + ret = 0; /* all OK */ + + EXITIF_exit: + if (NULL != priv) + { + TALER_RSA_key_free (priv); + priv = NULL; + } + if (NULL != priv_enc) + { + GNUNET_free (priv_enc); + priv_enc = NULL; + } + if (NULL != bkey) + TALER_RSA_blinding_key_destroy (bkey); + GNUNET_free_non_null (bsp); + return ret; +} diff --git a/src/util/util.c b/src/util/util.c new file mode 100644 index 000000000..3677bcbde --- /dev/null +++ b/src/util/util.c @@ -0,0 +1,528 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + 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, If not, see <http://www.gnu.org/licenses/> +*/ + +/** + * @file util.c + * @brief Common utility functions + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + * @author Florian Dold + * @author Benedikt Mueller + */ + +#include "platform.h" +#include "taler_util.h" +#include <gnunet/gnunet_common.h> +#include <gnunet/gnunet_util_lib.h> +#include <gcrypt.h> + +#define CURVE "Ed25519" + +#define AMOUNT_FRAC_BASE 1000000 +#define AMOUNT_FRAC_LEN 6 + + + +static void +fatal_error_handler (void *cls, int wtf, const char *msg) +{ + LOG_ERROR("Fatal error in Gcrypt: %s\n", msg); + abort(); +} + + +/** + * Initialize Gcrypt library. + */ +void +TALER_gcrypt_init() +{ + gcry_set_fatalerror_handler (&fatal_error_handler, NULL); + TALER_assert_as(gcry_check_version(NEED_LIBGCRYPT_VERSION), + "libgcrypt version mismatch"); + /* Disable secure memory. */ + gcry_control (GCRYCTL_DISABLE_SECMEM, 0); + gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); +} + + +/** + * Generate a ECC private key. + * + * @return the s-expression representing the generated ECC private key; NULL + * upon error + */ +gcry_sexp_t +TALER_genkey () +{ + gcry_sexp_t priv_sexp; + gcry_sexp_t s_keyparam; + int rc; + + if (0 != (rc = gcry_sexp_build (&s_keyparam, NULL, + "(genkey(ecc(curve \"" CURVE "\")" + "(flags eddsa)))"))) + { + LOG_GCRY_ERROR ("gcry_sexp_build", rc); + return NULL; + } + if (0 != (rc = gcry_pk_genkey (&priv_sexp, s_keyparam))) + { + LOG_GCRY_ERROR ("gcry_pk_genkey", rc); + gcry_sexp_release (s_keyparam); + return NULL; + } + gcry_sexp_release (s_keyparam); + if (0 != (rc = gcry_pk_testkey (priv_sexp))) + { + LOG_GCRY_ERROR("gcry_pk_testkey", rc); + gcry_sexp_release (priv_sexp); + return NULL; + } + return priv_sexp; +} + + +/** + * Parse money amount description, in the format "A:B.C". + * + * @param str amount description + * @param denom amount to write the result to + * @return GNUNET_OK if the string is a valid amount specification, + * GNUNET_SYSERR if it is invalid. + */ +int +TALER_string_to_amount (const char *str, struct TALER_Amount *denom) +{ + unsigned int i; // pos in str + int n; // number tmp + unsigned int c; // currency pos + uint32_t b; // base for suffix + + memset (denom, 0, sizeof (struct TALER_Amount)); + + i = n = c = 0; + + while (isspace(str[i])) + i++; + + if (0 == str[i]) + { + printf("null before currency\n"); + return GNUNET_SYSERR; + } + + while (str[i] != ':') + { + if (0 == str[i]) + { + printf("null before colon"); + return GNUNET_SYSERR; + } + if (c > 3) + { + printf("currency too long\n"); + return GNUNET_SYSERR; + } + denom->currency[c] = str[i]; + c++; + i++; + } + + // skip colon + i++; + + if (0 == str[i]) + { + printf("null before value\n"); + return GNUNET_SYSERR; + } + + while (str[i] != '.') + { + if (0 == str[i]) + { + return GNUNET_OK; + } + n = str[i] - '0'; + if (n < 0 || n > 9) + { + printf("invalid character '%c' before comma at %u\n", (char) n, i); + return GNUNET_SYSERR; + } + denom->value = (denom->value * 10) + n; + i++; + } + + // skip the dot + i++; + + if (0 == str[i]) + { + printf("null after dot"); + return GNUNET_SYSERR; + } + + b = 100000; + + while (0 != str[i]) + { + n = str[i] - '0'; + if (b == 0 || n < 0 || n > 9) + { + printf("error after comma"); + return GNUNET_SYSERR; + } + denom->fraction += n * b; + b /= 10; + i++; + } + + return GNUNET_OK; +} + + +/** + * FIXME + */ +struct TALER_AmountNBO +TALER_amount_hton (struct TALER_Amount d) +{ + struct TALER_AmountNBO dn; + dn.value = htonl (d.value); + dn.fraction = htonl (d.fraction); + memcpy (dn.currency, d.currency, TALER_CURRENCY_LEN); + + return dn; +} + + +/** + * FIXME + */ +struct TALER_Amount +TALER_amount_ntoh (struct TALER_AmountNBO dn) +{ + struct TALER_Amount d; + d.value = ntohl (dn.value); + d.fraction = ntohl (dn.fraction); + memcpy (d.currency, dn.currency, sizeof(dn.currency)); + + return d; +} + + +/** + * Compare the value/fraction of two amounts. Does not compare the currency, + * i.e. comparing amounts with the same value and fraction but different + * currency would return 0. + * + * @param a1 first amount + * @param a2 second amount + * @return result of the comparison + */ +int +TALER_amount_cmp (struct TALER_Amount a1, struct TALER_Amount a2) +{ + a1 = TALER_amount_normalize (a1); + a2 = TALER_amount_normalize (a2); + if (a1.value == a2.value) + { + if (a1.fraction < a2.fraction) + return -1; + if (a1.fraction > a2.fraction) + return 1; + return 0; + } + if (a1.value < a2.value) + return -1; + return 1; +} + + +/** + * Perform saturating subtraction of amounts. + * + * @param a1 amount to subtract from + * @param a2 amount to subtract + * @return (a1-a2) or 0 if a2>=a1 + */ +struct TALER_Amount +TALER_amount_subtract (struct TALER_Amount a1, struct TALER_Amount a2) +{ + a1 = TALER_amount_normalize (a1); + a2 = TALER_amount_normalize (a2); + + if (a1.value < a2.value) + { + a1.value = 0; + a1.fraction = 0; + return a1; + } + + if (a1.fraction < a2.fraction) + { + if (0 == a1.value) + { + a1.fraction = 0; + return a1; + } + a1.fraction += AMOUNT_FRAC_BASE; + a1.value -= 1; + } + + a1.fraction -= a2.fraction; + a1.value -= a2.value; + + return a1; +} + + +/** + * Perform saturating addition of amounts. + * + * @param a1 first amount to add + * @param a2 second amount to add + * @return sum of a1 and a2 + */ +struct TALER_Amount +TALER_amount_add (struct TALER_Amount a1, struct TALER_Amount a2) +{ + a1 = TALER_amount_normalize (a1); + a2 = TALER_amount_normalize (a2); + + a1.value += a2.value; + a1.fraction += a2.fraction; + + if (0 == a1.currency[0]) + { + memcpy (a2.currency, a1.currency, TALER_CURRENCY_LEN); + } + + if (0 == a2.currency[0]) + { + memcpy (a1.currency, a2.currency, TALER_CURRENCY_LEN); + } + + if (0 != a1.currency[0] && 0 != memcmp (a1.currency, a2.currency, TALER_CURRENCY_LEN)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "adding mismatching currencies\n"); + } + + if (a1.value < a2.value) + { + a1.value = UINT32_MAX; + a2.value = UINT32_MAX; + return a1; + } + + return TALER_amount_normalize (a1); +} + + +/** + * Normalize the given amount. + * + * @param amout amount to normalize + * @return normalized amount + */ +struct TALER_Amount +TALER_amount_normalize (struct TALER_Amount amount) +{ + while (amount.value != UINT32_MAX && amount.fraction >= AMOUNT_FRAC_BASE) + { + amount.fraction -= AMOUNT_FRAC_BASE; + amount.value += 1; + } + return amount; +} + + +/** + * Convert amount to string. + * + * @param amount amount to convert to string + * @return freshly allocated string representation + */ +char * +TALER_amount_to_string (struct TALER_Amount amount) +{ + char tail[AMOUNT_FRAC_LEN + 1] = { 0 }; + char curr[TALER_CURRENCY_LEN + 1] = { 0 }; + char *result = NULL; + int len; + + memcpy (curr, amount.currency, TALER_CURRENCY_LEN); + + amount = TALER_amount_normalize (amount); + if (0 != amount.fraction) + { + unsigned int i; + uint32_t n = amount.fraction; + for (i = 0; (i < AMOUNT_FRAC_LEN) && (n != 0); i++) + { + tail[i] = '0' + (n / (AMOUNT_FRAC_BASE / 10)); + n = (n * 10) % (AMOUNT_FRAC_BASE); + } + tail[i] = 0; + len = GNUNET_asprintf (&result, "%s:%lu.%s", curr, (unsigned long) amount.value, tail); + } + else + { + len = GNUNET_asprintf (&result, "%s:%lu", curr, (unsigned long) amount.value); + } + GNUNET_assert (len > 0); + return result; +} + + + +/** + * Return the base32crockford encoding of the given buffer. + * + * The returned string will be freshly allocated, and must be free'd + * with GNUNET_free. + * + * @param buffer with data + * @param size size of the buffer + * @return freshly allocated, null-terminated string + */ +char * +TALER_data_to_string_alloc (const void *buf, size_t size) +{ + char *str_buf; + size_t len = size * 8; + char *end; + + if (len % 5 > 0) + len += 5 - len % 5; + len /= 5; + str_buf = GNUNET_malloc (len + 1); + end = GNUNET_STRINGS_data_to_string (buf, size, str_buf, len); + if (NULL == end) + { + GNUNET_free (str_buf); + return NULL; + } + *end = '\0'; + return str_buf; +} + + +/** + * Get encoded binary data from a configuration. + * + * @return GNUNET_OK on success + * GNUNET_NO is the value does not exist + * GNUNET_SYSERR on encoding error + */ +int +TALER_configuration_get_data (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *section, const char *option, + void *buf, size_t buf_size) +{ + char *enc; + int res; + size_t data_size; + if (GNUNET_OK != (res = GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &enc))) + return res; + data_size = (strlen (enc) * 5) / 8; + if (data_size != buf_size) + { + GNUNET_free (enc); + return GNUNET_SYSERR; + } + if (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, strlen (enc), + buf, buf_size)) + { + GNUNET_free (enc); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +static void +derive_refresh_key (const struct GNUNET_HashCode *secret, + struct GNUNET_CRYPTO_SymmetricInitializationVector *iv, + struct GNUNET_CRYPTO_SymmetricSessionKey *skey) +{ + static const char ctx_key[] = "taler-key-skey"; + static const char ctx_iv[] = "taler-key-iv"; + + GNUNET_assert (GNUNET_YES == + GNUNET_CRYPTO_kdf (skey, sizeof (struct GNUNET_CRYPTO_SymmetricSessionKey), + ctx_key, strlen (ctx_key), + secret, sizeof (struct GNUNET_HashCode), + NULL, 0)); + GNUNET_assert (GNUNET_YES == + GNUNET_CRYPTO_kdf (iv, sizeof (struct GNUNET_CRYPTO_SymmetricInitializationVector), + ctx_iv, strlen (ctx_iv), + secret, sizeof (struct GNUNET_HashCode), + NULL, 0)); +} + + +int +TALER_refresh_decrypt (const void *input, size_t input_size, const struct GNUNET_HashCode *secret, void *result) +{ + struct GNUNET_CRYPTO_SymmetricInitializationVector iv; + struct GNUNET_CRYPTO_SymmetricSessionKey skey; + + derive_refresh_key (secret, &iv, &skey); + + return GNUNET_CRYPTO_symmetric_decrypt (input, input_size, &skey, &iv, result); +} + + +int +TALER_refresh_encrypt (const void *input, size_t input_size, const struct GNUNET_HashCode *secret, void *result) +{ + struct GNUNET_CRYPTO_SymmetricInitializationVector iv; + struct GNUNET_CRYPTO_SymmetricSessionKey skey; + + derive_refresh_key (secret, &iv, &skey); + + return GNUNET_CRYPTO_symmetric_encrypt (input, input_size, &skey, &iv, result); +} + + +void +TALER_hash_context_start (struct TALER_HashContext *hc) +{ + GNUNET_assert (0 == gcry_md_open (&hc->hd, GCRY_MD_SHA512, 0)); +} + + +void +TALER_hash_context_read (struct TALER_HashContext *hc, void *buf, size_t size) +{ + gcry_md_write (hc->hd, buf, size); +} + + +void +TALER_hash_context_finish (struct TALER_HashContext *hc, + struct GNUNET_HashCode *r_hash) +{ + void *res = gcry_md_read (hc->hd, 0); + GNUNET_assert (NULL != res); + if (NULL != r_hash) + memcpy (r_hash, res, sizeof (struct GNUNET_HashCode)); + gcry_md_close (hc->hd); +} + + +/* end of util.c */ |