-first cut at contract encryption and decryption

This commit is contained in:
Christian Grothoff 2022-03-28 13:57:43 +02:00
parent ee4077ef80
commit a227ee6d1b
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
4 changed files with 319 additions and 13 deletions

View File

@ -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);

View File

@ -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",

View File

@ -105,8 +105,11 @@ libtalerutil_la_SOURCES = \
libtalerutil_la_LIBADD = \
-lgnunetutil \
-lsodium \
-ljansson \
$(LIBGCRYPT_LIBS) \
-lmicrohttpd $(XLIB) \
-lz \
-lm
libtalerutil_la_LDFLAGS = \

View File

@ -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;
}