/*
This file is part of TALER
Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation; either version 3,
or (at your option) any later version.
TALER 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General
Public License along with TALER; see the file COPYING. If not,
see
*/
/**
* @file taler-exchange-httpd_csr.c
* @brief Handle /csr requests
* @author Lucien Heuzeveldt
* @author Gian Demarmles
* @author Christian Grothoff
*/
#include "platform.h"
#include
#include
#include "taler_json_lib.h"
#include "taler_mhd_lib.h"
#include "taler-exchange-httpd_csr.h"
#include "taler-exchange-httpd_responses.h"
#include "taler-exchange-httpd_keys.h"
MHD_RESULT
TEH_handler_csr_melt (struct TEH_RequestContext *rc,
const json_t *root,
const char *const args[])
{
struct TALER_RefreshMasterSecretP rms;
unsigned int csr_requests_num;
const json_t *csr_requests;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("rms",
&rms),
GNUNET_JSON_spec_array_const ("nks",
&csr_requests),
GNUNET_JSON_spec_end ()
};
enum TALER_ErrorCode ec;
struct TEH_DenominationKey *dk;
(void) args;
/* parse input */
{
enum GNUNET_GenericReturnValue res;
res = TALER_MHD_parse_json_data (rc->connection,
root,
spec);
if (GNUNET_OK != res)
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
}
csr_requests_num = json_array_size (csr_requests);
if ( (TALER_MAX_FRESH_COINS <= csr_requests_num) ||
(0 == csr_requests_num) )
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (
rc->connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_EXCHANGE_GENERIC_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE,
NULL);
}
{
struct GNUNET_CRYPTO_BlindingInputValues ewvs[csr_requests_num];
{
struct GNUNET_CRYPTO_CsSessionNonce nonces[csr_requests_num];
struct TALER_DenominationHashP denom_pub_hashes[csr_requests_num];
struct TEH_CsDeriveData cdds[csr_requests_num];
struct GNUNET_CRYPTO_CSPublicRPairP r_pubs[csr_requests_num];
for (unsigned int i = 0; i < csr_requests_num; i++)
{
uint32_t coin_off;
struct TALER_DenominationHashP *denom_pub_hash = &denom_pub_hashes[i];
struct GNUNET_JSON_Specification csr_spec[] = {
GNUNET_JSON_spec_uint32 ("coin_offset",
&coin_off),
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
denom_pub_hash),
GNUNET_JSON_spec_end ()
};
enum GNUNET_GenericReturnValue res;
res = TALER_MHD_parse_json_array (rc->connection,
csr_requests,
csr_spec,
i,
-1);
if (GNUNET_OK != res)
{
return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
}
TALER_cs_refresh_nonce_derive (&rms,
coin_off,
&nonces[i]);
}
for (unsigned int i = 0; i < csr_requests_num; i++)
{
const struct GNUNET_CRYPTO_CsSessionNonce *nonce = &nonces[i];
const struct TALER_DenominationHashP *denom_pub_hash =
&denom_pub_hashes[i];
ewvs[i].cipher = GNUNET_CRYPTO_BSA_CS;
/* check denomination referenced by denom_pub_hash */
{
struct TEH_KeyStateHandle *ksh;
ksh = TEH_keys_get_state ();
if (NULL == ksh)
{
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
NULL);
}
dk = TEH_keys_denomination_by_hash_from_state (ksh,
denom_pub_hash,
NULL,
NULL);
if (NULL == dk)
{
return TEH_RESPONSE_reply_unknown_denom_pub_hash (
rc->connection,
&denom_pub_hash[i]);
}
if (GNUNET_TIME_absolute_is_past (dk->meta.expire_withdraw.abs_time))
{
/* This denomination is past the expiration time for withdraws/refreshes*/
return TEH_RESPONSE_reply_expired_denom_pub_hash (
rc->connection,
denom_pub_hash,
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
"csr-melt");
}
if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))
{
/* This denomination is not yet valid, no need to check
for idempotency! */
return TEH_RESPONSE_reply_expired_denom_pub_hash (
rc->connection,
denom_pub_hash,
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
"csr-melt");
}
if (dk->recoup_possible)
{
/* This denomination has been revoked */
return TEH_RESPONSE_reply_expired_denom_pub_hash (
rc->connection,
denom_pub_hash,
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
"csr-melt");
}
if (GNUNET_CRYPTO_BSA_CS !=
dk->denom_pub.bsign_pub_key->cipher)
{
/* denomination is valid but not for CS */
return TEH_RESPONSE_reply_invalid_denom_cipher_for_operation (
rc->connection,
denom_pub_hash);
}
}
cdds[i].h_denom_pub = denom_pub_hash;
cdds[i].nonce = nonce;
} /* for (i) */
ec = TEH_keys_denomination_cs_batch_r_pub (csr_requests_num,
cdds,
true,
r_pubs);
if (TALER_EC_NONE != ec)
{
GNUNET_break (0);
return TALER_MHD_reply_with_ec (rc->connection,
ec,
NULL);
}
for (unsigned int i = 0; i < csr_requests_num; i++)
ewvs[i].details.cs_values = r_pubs[i];
} /* end scope */
/* send response */
{
json_t *csr_response_ewvs;
json_t *csr_response;
csr_response_ewvs = json_array ();
for (unsigned int i = 0; i < csr_requests_num; i++)
{
json_t *csr_obj;
struct TALER_ExchangeWithdrawValues exw = {
.blinding_inputs = &ewvs[i]
};
csr_obj = GNUNET_JSON_PACK (
TALER_JSON_pack_exchange_withdraw_values ("ewv",
&exw));
GNUNET_assert (NULL != csr_obj);
GNUNET_assert (0 ==
json_array_append_new (csr_response_ewvs,
csr_obj));
}
csr_response = GNUNET_JSON_PACK (
GNUNET_JSON_pack_array_steal ("ewvs",
csr_response_ewvs));
GNUNET_assert (NULL != csr_response);
return TALER_MHD_reply_json_steal (rc->connection,
csr_response,
MHD_HTTP_OK);
}
}
}
MHD_RESULT
TEH_handler_csr_withdraw (struct TEH_RequestContext *rc,
const json_t *root,
const char *const args[])
{
struct GNUNET_CRYPTO_CsSessionNonce nonce;
struct TALER_DenominationHashP denom_pub_hash;
struct GNUNET_CRYPTO_BlindingInputValues ewv = {
.cipher = GNUNET_CRYPTO_BSA_CS
};
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("nonce",
&nonce),
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
&denom_pub_hash),
GNUNET_JSON_spec_end ()
};
struct TEH_DenominationKey *dk;
(void) args;
{
enum GNUNET_GenericReturnValue res;
res = TALER_MHD_parse_json_data (rc->connection,
root,
spec);
if (GNUNET_OK != res)
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
}
{
struct TEH_KeyStateHandle *ksh;
ksh = TEH_keys_get_state ();
if (NULL == ksh)
{
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
NULL);
}
dk = TEH_keys_denomination_by_hash_from_state (ksh,
&denom_pub_hash,
NULL,
NULL);
if (NULL == dk)
{
return TEH_RESPONSE_reply_unknown_denom_pub_hash (
rc->connection,
&denom_pub_hash);
}
if (GNUNET_TIME_absolute_is_past (dk->meta.expire_withdraw.abs_time))
{
/* This denomination is past the expiration time for withdraws/refreshes*/
return TEH_RESPONSE_reply_expired_denom_pub_hash (
rc->connection,
&denom_pub_hash,
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
"csr-withdraw");
}
if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))
{
/* This denomination is not yet valid, no need to check
for idempotency! */
return TEH_RESPONSE_reply_expired_denom_pub_hash (
rc->connection,
&denom_pub_hash,
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
"csr-withdraw");
}
if (dk->recoup_possible)
{
/* This denomination has been revoked */
return TEH_RESPONSE_reply_expired_denom_pub_hash (
rc->connection,
&denom_pub_hash,
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
"csr-withdraw");
}
if (GNUNET_CRYPTO_BSA_CS !=
dk->denom_pub.bsign_pub_key->cipher)
{
/* denomination is valid but not for CS */
return TEH_RESPONSE_reply_invalid_denom_cipher_for_operation (
rc->connection,
&denom_pub_hash);
}
}
/* derive r_pub */
{
enum TALER_ErrorCode ec;
const struct TEH_CsDeriveData cdd = {
.h_denom_pub = &denom_pub_hash,
.nonce = &nonce
};
ec = TEH_keys_denomination_cs_r_pub (&cdd,
false,
&ewv.details.cs_values);
if (TALER_EC_NONE != ec)
{
GNUNET_break (0);
return TALER_MHD_reply_with_ec (rc->connection,
ec,
NULL);
}
}
{
struct TALER_ExchangeWithdrawValues exw = {
.blinding_inputs = &ewv
};
return TALER_MHD_REPLY_JSON_PACK (
rc->connection,
MHD_HTTP_OK,
TALER_JSON_pack_exchange_withdraw_values ("ewv",
&exw));
}
}
/* end of taler-exchange-httpd_csr.c */