/*
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 */