/* This file is part of Anastasis Copyright (C) 2020 Anastasis SARL Anastasis 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. 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 util/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 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); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Hashed answer %llu to %s\n", (unsigned long long) code, GNUNET_h2s (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), /* salt / XTS */ uuid, sizeof (*uuid), /* skm */ &pow, sizeof (pow), /* info chunks */ "anastasis-secure-question-hashing", strlen ("anastasis-secure-question-hashing"), NULL, 0)); } /** * 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 ANASTASIS_CRYPTO_NonceP *nonce, const char *salt, struct ANASTASIS_CRYPTO_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 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[out] res ciphertext output * @param[out] res_size 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) { size_t ciphertext_size; struct ANASTASIS_CRYPTO_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 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; struct ANASTASIS_CRYPTO_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; } void ANASTASIS_CRYPTO_user_identifier_derive ( const json_t *id_data, const struct ANASTASIS_CRYPTO_ProviderSaltP *provider_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 (&provider_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_kdf (&priv_key->priv, sizeof (priv_key->priv), /* salt / XTS */ NULL, 0, /* ikm */ id, sizeof (struct ANASTASIS_CRYPTO_UserIdentifierP), /* context chunks */ "ver", strlen ("ver"), NULL, 0)) { GNUNET_break (0); return; } } 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 (*id), key_share, sizeof (*key_share), (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 (*id), enc_key_share, sizeof (*enc_key_share), (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_kdf (policy_key, sizeof (*policy_key), /* salt / XTS */ salt, sizeof (*salt), /* ikm */ key_shares, keyshare_length * sizeof (*key_shares), /* info chunks */ "anastasis-policy-key-derive", strlen ("anastasis-policy-key-derive"), NULL, 0); } struct ANASTASIS_CoreSecretEncryptionResult * 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) { struct GNUNET_HashCode master_key; struct ANASTASIS_CoreSecretEncryptionResult *cser; struct ANASTASIS_CRYPTO_NonceP nonce; cser = GNUNET_new (struct ANASTASIS_CoreSecretEncryptionResult); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, &master_key, sizeof (struct GNUNET_HashCode)); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, &nonce, sizeof (struct ANASTASIS_CRYPTO_NonceP)); anastasis_encrypt (&nonce, &master_key, sizeof (struct GNUNET_HashCode), core_secret, core_secret_size, "cse", &cser->enc_core_secret, &cser->enc_core_secret_size); /* Allocate result arrays with NULL-termination so we don't need to store the length to free */ cser->enc_master_key_sizes = GNUNET_new_array (policy_keys_length + 1, size_t); cser->enc_master_keys = GNUNET_new_array (policy_keys_length + 1, void *); for (unsigned int i = 0; i < policy_keys_length; i++) { struct ANASTASIS_CRYPTO_NonceP nonce_i; GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, &nonce_i, sizeof (struct ANASTASIS_CRYPTO_NonceP)); anastasis_encrypt (&nonce_i, &policy_keys[i].key, sizeof (struct GNUNET_HashCode), &master_key, sizeof (struct GNUNET_HashCode), "emk", &cser->enc_master_keys[i], &cser->enc_master_key_sizes[i]); } return cser; } void ANASTASIS_CRYPTO_core_secret_recover ( const void *encrypted_master_key, size_t encrypted_master_key_size, 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) { void *master_key; size_t master_key_size; *core_secret = GNUNET_malloc (encrypted_core_secret_size); anastasis_decrypt (&policy_key->key, sizeof (struct GNUNET_HashCode), encrypted_master_key, encrypted_master_key_size, "emk", &master_key, &master_key_size); GNUNET_break (NULL != master_key); 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); anastasis_decrypt (master_key, master_key_size, encrypted_core_secret, encrypted_core_secret_size, "cse", core_secret, core_secret_size); GNUNET_break (NULL != *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); } void ANASTASIS_CRYPTO_destroy_encrypted_core_secret ( struct ANASTASIS_CoreSecretEncryptionResult *cser) { for (unsigned int i = 0; NULL != cser->enc_master_keys[i]; i++) GNUNET_free (cser->enc_master_keys[i]); GNUNET_free (cser->enc_master_keys); GNUNET_free (cser->enc_master_key_sizes); GNUNET_free (cser->enc_core_secret); GNUNET_free (cser); } const char * ANASTASIS_CRYPTO_uuid2s (const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid) { static char uuids[7]; char *tpk; tpk = GNUNET_STRINGS_data_to_string_alloc (uuid, sizeof (*uuid)); memcpy (uuids, tpk, sizeof (uuids) - 1); GNUNET_free (tpk); return uuids; } void ANASTASIS_CRYPTO_recovery_metadata_encrypt ( const struct ANASTASIS_CRYPTO_UserIdentifierP *id, const void *meta_data, size_t meta_data_size, void **enc_meta_data, size_t *enc_meta_data_size) { const char *salt = "rmd"; struct ANASTASIS_CRYPTO_NonceP nonce; GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &nonce, sizeof (nonce)); anastasis_encrypt (&nonce, id, sizeof (*id), meta_data, meta_data_size, salt, enc_meta_data, enc_meta_data_size); } enum GNUNET_GenericReturnValue ANASTASIS_CRYPTO_recovery_metadata_decrypt ( const struct ANASTASIS_CRYPTO_UserIdentifierP *id, const void *enc_meta_data, size_t enc_meta_data_size, void **meta_data, size_t *meta_data_size) { const char *salt = "rmd"; return anastasis_decrypt (id, sizeof (*id), enc_meta_data, enc_meta_data_size, salt, meta_data, meta_data_size); } /* end of anastasis_crypto.c */