From c5ad98da9812a05d078b6b017a545959ada96a3d Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 31 Dec 2022 15:10:35 +0100 Subject: [PATCH] write KYC attribute encryption logic --- src/include/taler_crypto_lib.h | 44 +++++++ src/util/crypto_contract.c | 215 ++++++++++++++++++++++++++------- src/util/test_crypto.c | 45 ++++++- 3 files changed, 255 insertions(+), 49 deletions(-) diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index 5e7ea6059..ca80c6cca 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -603,6 +603,19 @@ struct TALER_RefreshCommitmentP }; +/** + * Symmetric key we use to encrypt KYC attributes + * in our database. + */ +struct TALER_AttributeEncryptionKeyP +{ + /** + * The key is a hash code. + */ + struct GNUNET_HashCode hash; +}; + + /** * Token used for access control to the merchant's unclaimed * orders. @@ -1791,6 +1804,37 @@ TALER_denom_pub_verify (const struct TALER_DenominationPublicKey *denom_pub, const struct TALER_CoinPubHashP *c_hash); +/** + * Encrypts KYC attributes for storage in the database. + * + * @param key encryption key to use + * @param attr set of attributes to encrypt + * @param[out] enc_attr encrypted attribute data + * @param[out] enc_attr_size number of bytes in @a enc_attr + */ +void +TALER_CRYPTO_kyc_attributes_encrypt ( + const struct TALER_AttributeEncryptionKeyP *key, + const json_t *attr, + void **enc_attr, + size_t *enc_attr_size); + + +/** + * Encrypts KYC attributes for storage in the database. + * + * @param key encryption key to use + * @param enc_attr encrypted attribute data + * @param enc_attr_size number of bytes in @a enc_attr + * @return set of decrypted attributes, NULL on failure + */ +json_t * +TALER_CRYPTO_kyc_attributes_decrypt ( + const struct TALER_AttributeEncryptionKeyP *key, + const void *enc_attr, + size_t enc_attr_size); + + /** * Check if a coin is valid; that is, whether the denomination key exists, * is not expired, and the signature is correct. diff --git a/src/util/crypto_contract.c b/src/util/crypto_contract.c index fe6b1e6af..3bfe9eb89 100644 --- a/src/util/crypto_contract.c +++ b/src/util/crypto_contract.c @@ -109,14 +109,14 @@ derive_key (const void *key_material, * @param[out] res_size size of the ciphertext */ static void -contract_encrypt (const struct NonceP *nonce, - const void *key, - size_t key_len, - const void *data, - size_t data_size, - const char *salt, - void **res, - size_t *res_size) +blob_encrypt (const struct NonceP *nonce, + const void *key, + size_t key_len, + const void *data, + size_t data_size, + const char *salt, + void **res, + size_t *res_size) { size_t ciphertext_size; struct SymKeyP skey; @@ -127,10 +127,13 @@ contract_encrypt (const struct NonceP *nonce, salt, &skey); ciphertext_size = crypto_secretbox_NONCEBYTES - + crypto_secretbox_MACBYTES + data_size; + + crypto_secretbox_MACBYTES + + data_size; *res_size = ciphertext_size; *res = GNUNET_malloc (ciphertext_size); - memcpy (*res, nonce, crypto_secretbox_NONCEBYTES); + memcpy (*res, + nonce, + crypto_secretbox_NONCEBYTES); GNUNET_assert (0 == crypto_secretbox_easy (*res + crypto_secretbox_NONCEBYTES, data, @@ -153,13 +156,13 @@ contract_encrypt (const struct NonceP *nonce, * @return #GNUNET_OK on success */ static enum GNUNET_GenericReturnValue -contract_decrypt (const void *key, - size_t key_len, - const void *data, - size_t data_size, - const char *salt, - void **res, - size_t *res_size) +blob_decrypt (const void *key, + size_t key_len, + const void *data, + size_t data_size, + const char *salt, + void **res, + size_t *res_size) { const struct NonceP *nonce; struct SymKeyP skey; @@ -278,14 +281,14 @@ TALER_CRYPTO_contract_encrypt_for_merge ( GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &nonce, sizeof (nonce)); - contract_encrypt (&nonce, - &key, - sizeof (key), - hdr, - sizeof (*hdr) + cbuf_size, - MERGE_SALT, - econtract, - econtract_size); + blob_encrypt (&nonce, + &key, + sizeof (key), + hdr, + sizeof (*hdr) + cbuf_size, + MERGE_SALT, + econtract, + econtract_size); GNUNET_free (hdr); } @@ -316,13 +319,13 @@ TALER_CRYPTO_contract_decrypt_for_merge ( return NULL; } if (GNUNET_OK != - contract_decrypt (&key, - sizeof (key), - econtract, - econtract_size, - MERGE_SALT, - &xhdr, - &hdr_size)) + blob_decrypt (&key, + sizeof (key), + econtract, + econtract_size, + MERGE_SALT, + &xhdr, + &hdr_size)) { GNUNET_break_op (0); return NULL; @@ -407,6 +410,7 @@ TALER_CRYPTO_contract_encrypt_for_deposit ( &key)); cstr = json_dumps (contract_terms, JSON_COMPACT | JSON_SORT_KEYS); + GNUNET_assert (NULL != cstr); clen = strlen (cstr); cbuf_size = compressBound (clen); xbuf = GNUNET_malloc (cbuf_size); @@ -426,14 +430,14 @@ TALER_CRYPTO_contract_encrypt_for_deposit ( GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &nonce, sizeof (nonce)); - contract_encrypt (&nonce, - &key, - sizeof (key), - hdr, - sizeof (*hdr) + cbuf_size, - DEPOSIT_SALT, - &xecontract, - &xecontract_size); + blob_encrypt (&nonce, + &key, + sizeof (key), + hdr, + sizeof (*hdr) + cbuf_size, + DEPOSIT_SALT, + &xecontract, + &xecontract_size); GNUNET_free (hdr); /* prepend purse_pub */ *econtract = GNUNET_malloc (xecontract_size + sizeof (*purse_pub)); @@ -481,13 +485,13 @@ TALER_CRYPTO_contract_decrypt_for_deposit ( econtract += sizeof (*purse_pub); econtract_size -= sizeof (*purse_pub); if (GNUNET_OK != - contract_decrypt (&key, - sizeof (key), - econtract, - econtract_size, - DEPOSIT_SALT, - &xhdr, - &hdr_size)) + blob_decrypt (&key, + sizeof (key), + econtract, + econtract_size, + DEPOSIT_SALT, + &xhdr, + &hdr_size)) { GNUNET_break_op (0); return NULL; @@ -538,3 +542,120 @@ TALER_CRYPTO_contract_decrypt_for_deposit ( GNUNET_free (cstr); return ret; } + + +/** + * Salt we use when encrypting KYC attributes. + */ +#define ATTRIBUTE_SALT "kyc-attributes" + + +void +TALER_CRYPTO_kyc_attributes_encrypt ( + const struct TALER_AttributeEncryptionKeyP *key, + const json_t *attr, + void **enc_attr, + size_t *enc_attr_size) +{ + uLongf cbuf_size; + char *cstr; + uLongf clen; + void *xbuf; + int ret; + uint32_t belen; + struct NonceP nonce; + + cstr = json_dumps (attr, + JSON_COMPACT | JSON_SORT_KEYS); + GNUNET_assert (NULL != cstr); + clen = strlen (cstr); + GNUNET_assert (clen <= UINT32_MAX); + cbuf_size = compressBound (clen); + xbuf = GNUNET_malloc (cbuf_size + sizeof (uint32_t)); + belen = htonl ((uint32_t) clen); + memcpy (xbuf, + &belen, + sizeof (belen)); + ret = compress (xbuf + 4, + &cbuf_size, + (const Bytef *) cstr, + clen); + GNUNET_assert (Z_OK == ret); + free (cstr); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &nonce, + sizeof (nonce)); + blob_encrypt (&nonce, + key, + sizeof (*key), + xbuf, + cbuf_size + sizeof (uint32_t), + ATTRIBUTE_SALT, + enc_attr, + enc_attr_size); + GNUNET_free (xbuf); +} + + +json_t * +TALER_CRYPTO_kyc_attributes_decrypt ( + const struct TALER_AttributeEncryptionKeyP *key, + const void *enc_attr, + size_t enc_attr_size) +{ + void *xhdr; + size_t hdr_size; + char *cstr; + uLongf clen; + json_error_t json_error; + json_t *ret; + uint32_t belen; + + if (GNUNET_OK != + blob_decrypt (key, + sizeof (*key), + enc_attr, + enc_attr_size, + ATTRIBUTE_SALT, + &xhdr, + &hdr_size)) + { + GNUNET_break_op (0); + return NULL; + } + memcpy (&belen, + xhdr, + sizeof (belen)); + clen = ntohl (belen); + if (clen >= GNUNET_MAX_MALLOC_CHECKED) + { + GNUNET_break_op (0); + GNUNET_free (xhdr); + return NULL; + } + cstr = GNUNET_malloc (clen + 1); + if (Z_OK != + uncompress ((Bytef *) cstr, + &clen, + (const Bytef *) (xhdr + sizeof (uint32_t)), + hdr_size - sizeof (uint32_t))) + { + GNUNET_break_op (0); + GNUNET_free (cstr); + GNUNET_free (xhdr); + return NULL; + } + GNUNET_free (xhdr); + ret = json_loadb ((char *) cstr, + clen, + JSON_DECODE_ANY, + &json_error); + if (NULL == ret) + { + GNUNET_break_op (0); + GNUNET_free (cstr); + return NULL; + } + GNUNET_free (cstr); + return ret; +} diff --git a/src/util/test_crypto.c b/src/util/test_crypto.c index ce4181ff8..b1c3c8e8f 100644 --- a/src/util/test_crypto.c +++ b/src/util/test_crypto.c @@ -150,12 +150,12 @@ test_planchets_rsa (uint8_t age) GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, &ps, sizeof (ps)); - + GNUNET_log_skip (1, GNUNET_YES); GNUNET_assert (GNUNET_SYSERR == TALER_denom_priv_create (&dk_priv, &dk_pub, TALER_DENOMINATION_INVALID)); - + GNUNET_log_skip (1, GNUNET_YES); GNUNET_assert (GNUNET_SYSERR == TALER_denom_priv_create (&dk_priv, &dk_pub, @@ -481,12 +481,51 @@ test_contracts (void) } +static int +test_attributes (void) +{ + struct TALER_AttributeEncryptionKeyP key; + void *eattr; + size_t eattr_size; + json_t *c; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &key, + sizeof (key)); + c = json_pack ("{s:s}", "test", "value"); + GNUNET_assert (NULL != c); + TALER_CRYPTO_kyc_attributes_encrypt (&key, + c, + &eattr, + &eattr_size); + json_decref (c); + c = TALER_CRYPTO_kyc_attributes_decrypt (&key, + eattr, + eattr_size); + GNUNET_free (eattr); + if (NULL == c) + { + GNUNET_break (0); + return 1; + } + GNUNET_assert (0 == + strcmp ("value", + json_string_value (json_object_get (c, + "test")))); + json_decref (c); + return 0; +} + + int main (int argc, const char *const argv[]) { (void) argc; (void) argv; + GNUNET_log_setup ("test-crypto", + "WARNING", + NULL); if (0 != test_high_level ()) return 1; if (0 != test_planchets (0)) @@ -499,6 +538,8 @@ main (int argc, return 5; if (0 != test_contracts ()) return 6; + if (0 != test_attributes ()) + return 7; return 0; }