From 7e669bcf6b6336ec429da949bcb4aa456971dba2 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 30 Jul 2021 10:38:27 +0200 Subject: folding history in preparation of GNU Anastasis v0.0.0 release --- src/util/anastasis_crypto.c | 637 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 637 insertions(+) create mode 100644 src/util/anastasis_crypto.c (limited to 'src/util/anastasis_crypto.c') diff --git a/src/util/anastasis_crypto.c b/src/util/anastasis_crypto.c new file mode 100644 index 0000000..ace0162 --- /dev/null +++ b/src/util/anastasis_crypto.c @@ -0,0 +1,637 @@ +/* + This file is part of Anastasis + Copyright (C) 2020 Taler Systems SA + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis 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 + Anastasis; see the file COPYING.GPL. If not, see +*/ +/** + * @file lib/anastasis_crypto.c + * @brief anastasis crypto api + * @author Christian Grothoff + * @author Dominik Meister + * @author Dennis Neufeld + */ + +#include "platform.h" +#include "anastasis_crypto_lib.h" +#include +#include +#include +#include + +#if defined(DEBUG) || defined(DEBUG2) +#define SCRYPT_ITERATION 1 + +#else +#define SCRYPT_ITERATION 1000 +#endif + + +void +ANASTASIS_hash_answer (uint64_t code, + struct GNUNET_HashCode *hashed_code) +{ + char cbuf[40]; + + GNUNET_snprintf (cbuf, + sizeof (cbuf), + "%llu", + (unsigned long long) code); + GNUNET_CRYPTO_hash (cbuf, + strlen (cbuf), + hashed_code); +} + + +void +ANASTASIS_CRYPTO_secure_answer_hash ( + const char *answer, + const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid, + const struct ANASTASIS_CRYPTO_QuestionSaltP *salt, + struct GNUNET_HashCode *result) +{ + struct GNUNET_HashCode pow; + + GNUNET_CRYPTO_pow_hash (&salt->pow_salt, + answer, + strlen (answer), + &pow); + GNUNET_assert (GNUNET_YES == + GNUNET_CRYPTO_kdf ( + result, + sizeof (*result), + "Anastasis-secure-question-uuid-salting", + strlen ("Anastasis-secure-question-uuid-salting"), + &pow, + sizeof (pow), + uuid, + sizeof (*uuid), + NULL, + 0)); +} + + +/** + * Compute @a key and @a iv. + * + * @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 + * @param[out] iv where to write the IV + */ +static void +get_iv_key (const void *key_material, + size_t key_m_len, + const struct ANASTASIS_CRYPTO_NonceP *nonce, + const char *salt, + const struct ANASTASIS_CRYPTO_SymKeyP *key, + struct ANASTASIS_CRYPTO_IvP *iv) +{ + char res[sizeof (struct ANASTASIS_CRYPTO_SymKeyP) + + sizeof (struct ANASTASIS_CRYPTO_IvP)]; + + if (GNUNET_YES != + GNUNET_CRYPTO_hkdf (res, + sizeof (res), + GCRY_MD_SHA512, + GCRY_MD_SHA256, + key_material, + key_m_len, + nonce, + sizeof (struct ANASTASIS_CRYPTO_NonceP), + salt, + strlen (salt), + NULL, + 0)) + { + GNUNET_break (0); + return; + } + memcpy ((void *) key, + res, + sizeof (*key)); + memcpy (iv, + &res[sizeof (*key)], + sizeof (*iv)); +} + + +/** + * Encryption of data like recovery document etc. + * + * @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 res[out] ciphertext output + * @param res_size[out] size of the ciphertext + */ +static void +anastasis_encrypt (const struct ANASTASIS_CRYPTO_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) +{ + struct ANASTASIS_CRYPTO_NonceP *nonceptr; + gcry_cipher_hd_t cipher; + struct ANASTASIS_CRYPTO_SymKeyP sym_key; + struct ANASTASIS_CRYPTO_IvP iv; + int rc; + struct ANASTASIS_CRYPTO_AesTagP *tag; + char *ciphertext; + + *res_size = data_size + + sizeof (struct ANASTASIS_CRYPTO_NonceP) + + sizeof (struct ANASTASIS_CRYPTO_AesTagP); + if (*res_size <= data_size) + { + GNUNET_break (0); + return; + } + *res = GNUNET_malloc (*res_size); + if (*res_size != data_size + + sizeof (struct ANASTASIS_CRYPTO_NonceP) + + sizeof (struct ANASTASIS_CRYPTO_AesTagP)) + { + GNUNET_break (0); + return; + } + nonceptr = (struct ANASTASIS_CRYPTO_NonceP *) *res; + tag = (struct ANASTASIS_CRYPTO_AesTagP *) &nonceptr[1]; + ciphertext = (char *) &tag[1]; + memcpy (nonceptr, + nonce, + sizeof (*nonce)); + get_iv_key (key, + key_len, + nonce, + salt, + &sym_key, + &iv); + GNUNET_assert (0 == + gcry_cipher_open (&cipher, + GCRY_CIPHER_AES256, + GCRY_CIPHER_MODE_GCM, + 0)); + rc = gcry_cipher_setkey (cipher, + &sym_key, + sizeof (sym_key)); + GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY)); + rc = gcry_cipher_setiv (cipher, + &iv, + sizeof (iv)); + GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY)); + + GNUNET_assert (0 == + gcry_cipher_encrypt (cipher, + ciphertext, + data_size, + data, + data_size)); + GNUNET_assert (0 == + gcry_cipher_gettag (cipher, + tag, + sizeof (struct ANASTASIS_CRYPTO_AesTagP))); + gcry_cipher_close (cipher); +} + + +/** + * 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 res[out] plaintext output + * @param res_size[out] size of the plaintext + */ +static void +anastasis_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 ANASTASIS_CRYPTO_NonceP *nonce; + gcry_cipher_hd_t cipher; + const struct ANASTASIS_CRYPTO_SymKeyP sym_key; + struct ANASTASIS_CRYPTO_IvP iv; + int rc; + const struct ANASTASIS_CRYPTO_AesTagP *tag; + const char *ciphertext; + + *res_size = data_size + - sizeof (struct ANASTASIS_CRYPTO_NonceP) + - sizeof (struct ANASTASIS_CRYPTO_AesTagP); + if (*res_size >= data_size) + { + GNUNET_break (0); + return; + } + *res = GNUNET_malloc (*res_size); + if (*res_size != data_size + - sizeof (struct ANASTASIS_CRYPTO_NonceP) + - sizeof (struct ANASTASIS_CRYPTO_AesTagP)) + { + GNUNET_break (0); + GNUNET_free (*res); + return; + } + + nonce = (const struct ANASTASIS_CRYPTO_NonceP *) data; + tag = (struct ANASTASIS_CRYPTO_AesTagP *) &nonce[1]; + ciphertext = (const char *) &tag[1]; + get_iv_key (key, + key_len, + nonce, + salt, + &sym_key, + &iv); + GNUNET_assert (0 == + gcry_cipher_open (&cipher, + GCRY_CIPHER_AES256, + GCRY_CIPHER_MODE_GCM, + 0)); + rc = gcry_cipher_setkey (cipher, + &sym_key, + sizeof (sym_key)); + GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY)); + + rc = gcry_cipher_setiv (cipher, + &iv, + sizeof (iv)); + GNUNET_assert ((0 == rc) || ((char) rc == GPG_ERR_WEAK_KEY)); + + GNUNET_assert (0 == gcry_cipher_decrypt (cipher, + *res, + *res_size, + ciphertext, + *res_size)); + if (0 != + gcry_cipher_checktag (cipher, + tag, + sizeof (struct ANASTASIS_CRYPTO_AesTagP))) + { + GNUNET_break (0); + GNUNET_free (*res); + return; + } + gcry_cipher_close (cipher); +} + + +void +ANASTASIS_CRYPTO_user_identifier_derive ( + const json_t *id_data, + const struct ANASTASIS_CRYPTO_ProviderSaltP *server_salt, + struct ANASTASIS_CRYPTO_UserIdentifierP *id) +{ + char *json_enc; + struct GNUNET_HashCode hash; + + json_enc = json_dumps (id_data, + JSON_COMPACT | JSON_SORT_KEYS); + GNUNET_assert (NULL != json_enc); + GNUNET_CRYPTO_pow_hash (&server_salt->salt, + json_enc, + strlen (json_enc), + &hash); + id->hash = hash; + free (json_enc); +} + + +void +ANASTASIS_CRYPTO_account_private_key_derive ( + const struct ANASTASIS_CRYPTO_UserIdentifierP *id, + struct ANASTASIS_CRYPTO_AccountPrivateKeyP *priv_key) +{ + /* priv_key = ver_secret */ + if (GNUNET_YES != + GNUNET_CRYPTO_hkdf (&priv_key->priv, + sizeof (priv_key->priv), + GCRY_MD_SHA512, + GCRY_MD_SHA256, + id, + sizeof (struct ANASTASIS_CRYPTO_UserIdentifierP), + "ver", + strlen ("ver"), + NULL, + 0)) + { + GNUNET_break (0); + return; + } + /* go from ver_secret to proper private key (eddsa_d_to_a() in spec) */ + priv_key->priv.d[0] = (priv_key->priv.d[0] & 0x7f) | 0x40; + priv_key->priv.d[31] &= 0xf8; +} + + +void +ANASTASIS_CRYPTO_account_public_key_derive ( + const struct ANASTASIS_CRYPTO_UserIdentifierP *id, + struct ANASTASIS_CRYPTO_AccountPublicKeyP *pub_key) +{ + struct ANASTASIS_CRYPTO_AccountPrivateKeyP priv; + + ANASTASIS_CRYPTO_account_private_key_derive (id, + &priv); + GNUNET_CRYPTO_eddsa_key_get_public (&priv.priv, + &pub_key->pub); +} + + +void +ANASTASIS_CRYPTO_recovery_document_encrypt ( + const struct ANASTASIS_CRYPTO_UserIdentifierP *id, + const void *rec_doc, + size_t rd_size, + void **enc_rec_doc, + size_t *erd_size) +{ + const char *salt = "erd"; + struct ANASTASIS_CRYPTO_NonceP nonce; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &nonce, + sizeof (nonce)); + anastasis_encrypt (&nonce, + id, + sizeof (struct ANASTASIS_CRYPTO_UserIdentifierP), + rec_doc, + rd_size, + salt, + enc_rec_doc, + erd_size); +} + + +void +ANASTASIS_CRYPTO_recovery_document_decrypt ( + const struct ANASTASIS_CRYPTO_UserIdentifierP *id, + const void *enc_rec_doc, + size_t erd_size, + void **rec_doc, + size_t *rd_size) +{ + const char *salt = "erd"; + + anastasis_decrypt (id, + sizeof (struct ANASTASIS_CRYPTO_UserIdentifierP), + enc_rec_doc, + erd_size, + salt, + rec_doc, + rd_size); +} + + +void +ANASTASIS_CRYPTO_keyshare_encrypt ( + const struct ANASTASIS_CRYPTO_KeyShareP *key_share, + const struct ANASTASIS_CRYPTO_UserIdentifierP *id, + const char *xsalt, + struct ANASTASIS_CRYPTO_EncryptedKeyShareP *enc_key_share) +{ + const char *salt = "eks"; + size_t eks_size = 0; + void *eks = NULL; + struct ANASTASIS_CRYPTO_NonceP nonce; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &nonce, + sizeof (nonce)); + anastasis_encrypt (&nonce, + id, + sizeof (struct ANASTASIS_CRYPTO_UserIdentifierP), + key_share, + sizeof (struct ANASTASIS_CRYPTO_KeyShareP), + (NULL == xsalt) ? salt : xsalt, + &eks, + &eks_size); + GNUNET_assert (eks_size == + sizeof (struct ANASTASIS_CRYPTO_EncryptedKeyShareP)); + memcpy (enc_key_share, + eks, + sizeof (struct ANASTASIS_CRYPTO_EncryptedKeyShareP)); + GNUNET_free (eks); +} + + +void +ANASTASIS_CRYPTO_keyshare_decrypt ( + const struct ANASTASIS_CRYPTO_EncryptedKeyShareP *enc_key_share, + const struct ANASTASIS_CRYPTO_UserIdentifierP *id, + const char *xsalt, + struct ANASTASIS_CRYPTO_KeyShareP *key_share) +{ + const char *salt = "eks"; + size_t ks_size = 0; + void *ks = NULL; + + anastasis_decrypt (id, + sizeof (struct ANASTASIS_CRYPTO_UserIdentifierP), + enc_key_share, + sizeof (struct ANASTASIS_CRYPTO_EncryptedKeyShareP), + (NULL == xsalt) ? salt : xsalt, + &ks, + &ks_size); + GNUNET_assert (ks_size == + sizeof (struct ANASTASIS_CRYPTO_KeyShareP)); + memcpy (key_share, + ks, + sizeof (struct ANASTASIS_CRYPTO_KeyShareP)); + GNUNET_free (ks); +} + + +void +ANASTASIS_CRYPTO_truth_encrypt ( + const struct ANASTASIS_CRYPTO_NonceP *nonce, + const struct ANASTASIS_CRYPTO_TruthKeyP *truth_enc_key, + const void *truth, + size_t truth_size, + void **enc_truth, + size_t *ect_size) +{ + const char *salt = "ect"; + + anastasis_encrypt (nonce, + truth_enc_key, + sizeof (struct ANASTASIS_CRYPTO_TruthKeyP), + truth, + truth_size, + salt, + enc_truth, + ect_size); +} + + +void +ANASTASIS_CRYPTO_truth_decrypt ( + const struct ANASTASIS_CRYPTO_TruthKeyP *truth_enc_key, + const void *enc_truth, + size_t ect_size, + void **truth, + size_t *truth_size) +{ + const char *salt = "ect"; + + anastasis_decrypt (truth_enc_key, + sizeof (struct ANASTASIS_CRYPTO_TruthKeyP), + enc_truth, + ect_size, + salt, + truth, + truth_size); +} + + +void +ANASTASIS_CRYPTO_keyshare_create ( + struct ANASTASIS_CRYPTO_KeyShareP *key_share) +{ + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, + key_share, + sizeof (struct ANASTASIS_CRYPTO_KeyShareP)); +} + + +void +ANASTASIS_CRYPTO_policy_key_derive ( + const struct ANASTASIS_CRYPTO_KeyShareP *key_shares, + unsigned int keyshare_length, + const struct ANASTASIS_CRYPTO_MasterSaltP *salt, + struct ANASTASIS_CRYPTO_PolicyKeyP *policy_key) +{ + GNUNET_CRYPTO_hkdf (policy_key, + sizeof (*policy_key), + GCRY_MD_SHA512, + GCRY_MD_SHA256, + key_shares, + keyshare_length * sizeof (*key_shares), + salt, + sizeof (*salt), + NULL, 0); +} + + +void +ANASTASIS_CRYPTO_core_secret_encrypt ( + const struct ANASTASIS_CRYPTO_PolicyKeyP *policy_keys, + unsigned int policy_keys_length, + const void *core_secret, + size_t core_secret_size, + void **enc_core_secret, + struct ANASTASIS_CRYPTO_EncryptedMasterKeyP *encrypted_master_keys) +{ + struct GNUNET_CRYPTO_SymmetricSessionKey sk; + struct GNUNET_CRYPTO_SymmetricInitializationVector iv; + struct GNUNET_HashCode master_key; + + *enc_core_secret = GNUNET_malloc (core_secret_size); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, + &master_key, + sizeof (struct GNUNET_HashCode)); + GNUNET_CRYPTO_hash_to_aes_key (&master_key, + &sk, + &iv); + GNUNET_assert (GNUNET_SYSERR != + GNUNET_CRYPTO_symmetric_encrypt (core_secret, + core_secret_size, + &sk, + &iv, + *enc_core_secret)); + for (unsigned int i = 0; i < policy_keys_length; i++) + { + struct GNUNET_CRYPTO_SymmetricSessionKey i_sk; + struct GNUNET_CRYPTO_SymmetricInitializationVector i_iv; + struct GNUNET_HashCode key = policy_keys[i].key; + + GNUNET_CRYPTO_hash_to_aes_key (&key, + &i_sk, + &i_iv); + GNUNET_assert ( + GNUNET_SYSERR != + GNUNET_CRYPTO_symmetric_encrypt (&master_key, + sizeof (struct GNUNET_HashCode), + &i_sk, + &i_iv, + &encrypted_master_keys[i])); + } +} + + +void +ANASTASIS_CRYPTO_core_secret_recover ( + const struct ANASTASIS_CRYPTO_EncryptedMasterKeyP *encrypted_master_key, + const struct ANASTASIS_CRYPTO_PolicyKeyP *policy_key, + const void *encrypted_core_secret, + size_t encrypted_core_secret_size, + void **core_secret, + size_t *core_secret_size) +{ + struct GNUNET_CRYPTO_SymmetricSessionKey mk_sk; + struct GNUNET_CRYPTO_SymmetricInitializationVector mk_iv; + struct GNUNET_CRYPTO_SymmetricSessionKey core_sk; + struct GNUNET_CRYPTO_SymmetricInitializationVector core_iv; + struct GNUNET_HashCode master_key; + struct GNUNET_HashCode key = policy_key->key; + + *core_secret = GNUNET_malloc (encrypted_core_secret_size); + GNUNET_CRYPTO_hash_to_aes_key (&key, + &mk_sk, + &mk_iv); + GNUNET_assert ( + GNUNET_SYSERR != + GNUNET_CRYPTO_symmetric_decrypt ( + encrypted_master_key, + sizeof (struct ANASTASIS_CRYPTO_EncryptedMasterKeyP), + &mk_sk, + &mk_iv, + &master_key)); + GNUNET_CRYPTO_hash_to_aes_key (&master_key, + &core_sk, + &core_iv); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "At %s:%d encrypted core secret is %s-%llu b\n", __FILE__, + __LINE__, + TALER_b2s (encrypted_core_secret, encrypted_core_secret_size), + (unsigned long long) encrypted_core_secret_size); + *core_secret_size = GNUNET_CRYPTO_symmetric_decrypt (encrypted_core_secret, + encrypted_core_secret_size, + &core_sk, + &core_iv, + *core_secret); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "At %s:%d decrypted core secret is %s-%llu b\n", __FILE__, + __LINE__, + TALER_b2s (*core_secret, *core_secret_size), + (unsigned long long) *core_secret_size); + GNUNET_assert (GNUNET_SYSERR != *core_secret_size); +} + + +/* end of anastasis_crypto.c */ -- cgit v1.2.3