aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <grothoff@gnunet.org>2022-03-28 13:57:43 +0200
committerChristian Grothoff <grothoff@gnunet.org>2022-03-28 13:57:43 +0200
commita227ee6d1bd979ae87ab9afda27f180c840313bf (patch)
tree4f3a5e0c92dd853f87de01083d00a3500252d8aa
parentee4077ef8001e0570a0333ed6b24c69eb2504760 (diff)
-first cut at contract encryption and decryption
-rw-r--r--src/include/taler_exchange_service.h4
-rw-r--r--src/lib/exchange_api_purse_create_with_deposit.c25
-rw-r--r--src/util/Makefile.am3
-rw-r--r--src/util/crypto_contract.c300
4 files changed, 319 insertions, 13 deletions
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h
index cf4624252..00c8c101f 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -4287,6 +4287,9 @@ struct TALER_EXCHANGE_PurseDeposit
* @param purse_expiration when will the unmerged purse expire
* @param num_deposits length of the @a deposits array
* @param deposits array of deposits to make into the purse
+ * @param upload_contract true to upload the contract; must
+ * be FALSE for repeated calls to this API for the
+ * same purse (i.e. when adding more deposits).
* @param cb function to call with the exchange's result
* @param cb_cls closure for @a cb
* @return the request handle; NULL upon error
@@ -4301,6 +4304,7 @@ TALER_EXCHANGE_purse_create_with_deposit (
struct GNUNET_TIME_Timestamp purse_expiration,
unsigned int num_deposits,
const struct TALER_EXCHANGE_PurseDeposit *deposits,
+ bool upload_contract,
TALER_EXCHANGE_PurseCreateDepositCallback cb,
void *cb_cls);
diff --git a/src/lib/exchange_api_purse_create_with_deposit.c b/src/lib/exchange_api_purse_create_with_deposit.c
index 6765b7771..e39cb076f 100644
--- a/src/lib/exchange_api_purse_create_with_deposit.c
+++ b/src/lib/exchange_api_purse_create_with_deposit.c
@@ -202,6 +202,7 @@ TALER_EXCHANGE_purse_create_with_deposit (
struct GNUNET_TIME_Timestamp purse_expiration,
unsigned int num_deposits,
const struct TALER_EXCHANGE_PurseDeposit *deposits,
+ bool upload_contract,
TALER_EXCHANGE_PurseCreateDepositCallback cb,
void *cb_cls)
{
@@ -335,23 +336,25 @@ TALER_EXCHANGE_purse_create_with_deposit (
}
GNUNET_free (url);
{
- void *econtract;
- size_t econtract_size;
+ void *econtract = NULL;
+ size_t econtract_size = 0;
- TALER_CRYPTO_contract_encrypt_for_merge (&purse_pub,
- contract_priv,
- merge_priv,
- contract_terms,
- &econtract,
- &econtract_size);
+ if (upload_contract)
+ TALER_CRYPTO_contract_encrypt_for_merge (&purse_pub,
+ contract_priv,
+ merge_priv,
+ contract_terms,
+ &econtract,
+ &econtract_size);
create_obj = GNUNET_JSON_PACK (
TALER_JSON_pack_amount ("amount",
&purse_value_after_fees),
GNUNET_JSON_pack_uint64 ("min_age",
min_age),
- GNUNET_JSON_pack_data_varsize ("econtract",
- econtract,
- econtract_size),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_data_varsize ("econtract",
+ econtract,
+ econtract_size)),
GNUNET_JSON_pack_data_auto ("contract_pub",
&contract_pub),
GNUNET_JSON_pack_data_auto ("merge_pub",
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index 8ef9e146d..b92c427b0 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -105,8 +105,11 @@ libtalerutil_la_SOURCES = \
libtalerutil_la_LIBADD = \
-lgnunetutil \
+ -lsodium \
+ -ljansson \
$(LIBGCRYPT_LIBS) \
-lmicrohttpd $(XLIB) \
+ -lz \
-lm
libtalerutil_la_LDFLAGS = \
diff --git a/src/util/crypto_contract.c b/src/util/crypto_contract.c
index 7f684d80a..5117712ea 100644
--- a/src/util/crypto_contract.c
+++ b/src/util/crypto_contract.c
@@ -20,6 +20,191 @@
*/
#include "platform.h"
#include "taler_util.h"
+#include <zlib.h>
+#include "taler_exchange_service.h"
+
+
+/**
+ * Nonce used for encryption, 24 bytes.
+ */
+struct NonceP
+{
+ uint8_t nonce[crypto_secretbox_NONCEBYTES];
+};
+
+/**
+ * Specifies a key used for symmetric encryption, 32 bytes.
+ */
+struct SymKeyP
+{
+ uint32_t key[8];
+};
+
+
+/**
+ * Compute @a key.
+ *
+ * @param key_material key for calculation
+ * @param key_m_len length of key
+ * @param nonce nonce for calculation
+ * @param salt salt value for calculation
+ * @param[out] key where to write the en-/description key
+ */
+static void
+derive_key (const void *key_material,
+ size_t key_m_len,
+ const struct NonceP *nonce,
+ const char *salt,
+ struct SymKeyP *key)
+{
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CRYPTO_kdf (key,
+ sizeof (*key),
+ /* salt / XTS */
+ nonce,
+ sizeof (*nonce),
+ /* ikm */
+ key_material,
+ key_m_len,
+ /* info chunks */
+ /* The "salt" passed here is actually not something random,
+ but a protocol-specific identifier string. Thus
+ we pass it as a context info to the HKDF */
+ salt,
+ strlen (salt),
+ NULL,
+ 0));
+}
+
+
+/**
+ * Encryption of data.
+ *
+ * @param nonce value to use for the nonce
+ * @param key key which is used to derive a key/iv pair from
+ * @param key_len length of key
+ * @param data data to encrypt
+ * @param data_size size of the data
+ * @param salt salt value which is used for key derivation
+ * @param[out] res ciphertext output
+ * @param[out] res_size size of the ciphertext
+ */
+static void
+encrypt (const struct NonceP *nonce,
+ const void *key,
+ size_t key_len,
+ const void *data,
+ size_t data_size,
+ const char *salt,
+ void **res,
+ size_t *res_size)
+{
+ size_t ciphertext_size;
+ struct SymKeyP skey;
+
+ derive_key (key,
+ key_len,
+ nonce,
+ salt,
+ &skey);
+ ciphertext_size = crypto_secretbox_NONCEBYTES
+ + crypto_secretbox_MACBYTES + data_size;
+ *res_size = ciphertext_size;
+ *res = GNUNET_malloc (ciphertext_size);
+ memcpy (*res, nonce, crypto_secretbox_NONCEBYTES);
+ GNUNET_assert (0 ==
+ crypto_secretbox_easy (*res + crypto_secretbox_NONCEBYTES,
+ data,
+ data_size,
+ (void *) nonce,
+ (void *) &skey));
+}
+
+
+/**
+ * Decryption of data like encrypted recovery document etc.
+ *
+ * @param key key which is used to derive a key/iv pair from
+ * @param key_len length of key
+ * @param data data to decrypt
+ * @param data_size size of the data
+ * @param salt salt value which is used for key derivation
+ * @param[out] res plaintext output
+ * @param[out] res_size size of the plaintext
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+decrypt (const void *key,
+ size_t key_len,
+ const void *data,
+ size_t data_size,
+ const char *salt,
+ void **res,
+ size_t *res_size)
+{
+ const struct NonceP *nonce;
+ struct SymKeyP skey;
+ size_t plaintext_size;
+
+ if (data_size < crypto_secretbox_NONCEBYTES + crypto_secretbox_MACBYTES)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ nonce = data;
+ derive_key (key,
+ key_len,
+ nonce,
+ salt,
+ &skey);
+ plaintext_size = data_size - (crypto_secretbox_NONCEBYTES
+ + crypto_secretbox_MACBYTES);
+ *res = GNUNET_malloc (plaintext_size);
+ *res_size = plaintext_size;
+ if (0 != crypto_secretbox_open_easy (*res,
+ data + crypto_secretbox_NONCEBYTES,
+ data_size - crypto_secretbox_NONCEBYTES,
+ (void *) nonce,
+ (void *) &skey))
+ {
+ GNUNET_break (0);
+ GNUNET_free (*res);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Header for encrypted contracts.
+ */
+struct ContractHeader
+{
+ /**
+ * Type of the contract, in NBO.
+ */
+ uint32_t ctype;
+
+ /**
+ * Length of the encrypted contract, in NBO.
+ */
+ uint32_t clen;
+
+ /**
+ * Included key material, depending on @e ctype.
+ */
+ union
+ {
+ struct TALER_PurseMergePrivateKeyP merge_priv;
+ } keys;
+
+};
+
+
+/**
+ * Salt we use when encrypting contracts for merge.
+ */
+#define MERGE_SALT "p2p-merge-contract"
void
@@ -29,9 +214,52 @@ TALER_CRYPTO_contract_encrypt_for_merge (
const struct TALER_PurseMergePrivateKeyP *merge_priv,
const json_t *contract_terms,
void **econtract,
-
size_t *econtract_size)
{
+ struct GNUNET_HashCode key;
+ char *cstr;
+ size_t clen;
+ void *xbuf;
+ struct ContractHeader *hdr;
+ struct NonceP nonce;
+ uLongf cbuf_size;
+ int ret;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_ecdh_eddsa (&contract_priv->ecdhe_priv,
+ &purse_pub->eddsa_pub,
+ &key));
+ cstr = json_dumps (contract_terms,
+ JSON_COMPACT | JSON_SORT_KEYS);
+ clen = strlen (cstr);
+ cbuf_size = compressBound (clen);
+ xbuf = GNUNET_malloc (cbuf_size);
+ ret = compress (xbuf,
+ &cbuf_size,
+ (const Bytef *) cstr,
+ clen);
+ GNUNET_assert (Z_OK == ret);
+ free (cstr);
+ hdr = GNUNET_malloc (sizeof (*hdr) + cbuf_size);
+ hdr->ctype = htonl (TALER_EXCHANGE_CONTRACT_PAYMENT_OFFER);
+ hdr->clen = htonl ((uint32_t) clen);
+ hdr->keys.merge_priv = *merge_priv;
+ memcpy (&hdr[1],
+ xbuf,
+ cbuf_size);
+ GNUNET_free (xbuf);
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
+ &nonce,
+ sizeof (nonce));
+ encrypt (&nonce,
+ &key,
+ sizeof (key),
+ hdr,
+ sizeof (*hdr) + cbuf_size,
+ MERGE_SALT,
+ econtract,
+ econtract_size);
+ GNUNET_free (hdr);
}
@@ -43,5 +271,73 @@ TALER_CRYPTO_contract_decrypt_for_merge (
size_t econtract_size,
struct TALER_PurseMergePrivateKeyP *merge_priv)
{
- return NULL;
+ struct GNUNET_HashCode key;
+ void *xhdr;
+ size_t hdr_size;
+ const struct ContractHeader *hdr;
+ char *cstr;
+ uLongf clen;
+ json_error_t json_error;
+ json_t *ret;
+
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_ecdh_eddsa (&contract_priv->ecdhe_priv,
+ &purse_pub->eddsa_pub,
+ &key))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ if (GNUNET_OK !=
+ decrypt (&key,
+ sizeof (key),
+ econtract,
+ econtract_size,
+ MERGE_SALT,
+ &xhdr,
+ &hdr_size))
+ {
+ GNUNET_break_op (0);
+ return NULL;
+ }
+ if (hdr_size < sizeof (*hdr))
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (xhdr);
+ return NULL;
+ }
+ hdr = xhdr;
+ clen = ntohl (hdr->clen);
+ if (clen >= GNUNET_MAX_MALLOC_CHECKED)
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (xhdr);
+ return NULL;
+ }
+ cstr = GNUNET_malloc (clen + 1);
+ if (Z_OK !=
+ uncompress ((Bytef *) cstr,
+ &clen,
+ (const Bytef *) &hdr[1],
+ hdr_size - sizeof (*hdr)))
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (cstr);
+ GNUNET_free (xhdr);
+ return NULL;
+ }
+ *merge_priv = hdr->keys.merge_priv;
+ GNUNET_free (xhdr);
+ ret = json_loadb ((char *) cstr,
+ clen,
+ JSON_DECODE_ANY,
+ &json_error);
+ if (NULL == ret)
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (cstr);
+ return NULL;
+ }
+ GNUNET_free (cstr);
+ return ret;
}