aboutsummaryrefslogtreecommitdiff
path: root/src/mint/taler-mint-httpd_withdraw.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mint/taler-mint-httpd_withdraw.c')
-rw-r--r--src/mint/taler-mint-httpd_withdraw.c400
1 files changed, 400 insertions, 0 deletions
diff --git a/src/mint/taler-mint-httpd_withdraw.c b/src/mint/taler-mint-httpd_withdraw.c
new file mode 100644
index 000000000..7ffa45706
--- /dev/null
+++ b/src/mint/taler-mint-httpd_withdraw.c
@@ -0,0 +1,400 @@
1/*
2 This file is part of TALER
3 (C) 2014 GNUnet e.V.
4
5 TALER is free software; you can redistribute it and/or modify it under the
6 terms of the GNU Affero General Public License as published by the Free Software
7 Foundation; either version 3, or (at your option) any later version.
8
9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
12
13 You should have received a copy of the GNU Affero General Public License along with
14 TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
15*/
16/**
17 * @file taler-mint-httpd_withdraw.c
18 * @brief Handle /withdraw/ requests
19 * @author Florian Dold
20 * @author Benedikt Mueller
21 * @author Christian Grothoff
22 */
23#include "platform.h"
24#include <gnunet/gnunet_util_lib.h>
25#include <jansson.h>
26#include <microhttpd.h>
27#include <libpq-fe.h>
28#include <pthread.h>
29#include "mint.h"
30#include "mint_db.h"
31#include "taler_types.h"
32#include "taler_signatures.h"
33#include "taler_rsa.h"
34#include "taler_json_lib.h"
35#include "taler_microhttpd_lib.h"
36#include "taler-mint-httpd_keys.h"
37#include "taler-mint-httpd_mhd.h"
38#include "taler-mint-httpd_withdraw.h"
39
40
41/**
42 * Convert a signature (with purpose) to
43 * a JSON object representation.
44 *
45 * @param purpose purpose of the signature
46 * @param signature the signature
47 * @return the JSON reporesentation of the signature with purpose
48 */
49static json_t *
50sig_to_json (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
51 const struct GNUNET_CRYPTO_EddsaSignature *signature)
52{
53 json_t *root;
54 json_t *el;
55
56 root = json_object ();
57
58 el = json_integer ((json_int_t) ntohl (purpose->size));
59 json_object_set_new (root, "size", el);
60
61 el = json_integer ((json_int_t) ntohl (purpose->purpose));
62 json_object_set_new (root, "purpose", el);
63
64 el = TALER_JSON_from_data (signature, sizeof (struct GNUNET_CRYPTO_EddsaSignature));
65 json_object_set_new (root, "sig", el);
66
67 return root;
68}
69
70
71/**
72 * Sign a reserve's status with the current signing key.
73 *
74 * @param reserve the reserve to sign
75 * @param key_state the key state containing the current
76 * signing private key
77 */
78static void
79sign_reserve (struct Reserve *reserve,
80 struct MintKeyState *key_state)
81{
82 reserve->status_sign_pub = key_state->current_sign_key_issue.signkey_pub;
83 reserve->status_sig_purpose.purpose = htonl (TALER_SIGNATURE_RESERVE_STATUS);
84 reserve->status_sig_purpose.size = htonl (sizeof (struct Reserve) -
85 offsetof (struct Reserve, status_sig_purpose));
86 GNUNET_CRYPTO_eddsa_sign (&key_state->current_sign_key_issue.signkey_priv,
87 &reserve->status_sig_purpose,
88 &reserve->status_sig);
89}
90
91
92/**
93 * Handle a "/withdraw/status" request
94 *
95 * @param rh context of the handler
96 * @param connection the MHD connection to handle
97 * @param[IN|OUT] connection_cls the connection's closure (can be updated)
98 * @param upload_data upload data
99 * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
100 * @return MHD result code
101 */
102int
103TALER_MINT_handler_withdraw_status (struct RequestHandler *rh,
104 struct MHD_Connection *connection,
105 void **connection_cls,
106 const char *upload_data,
107 size_t *upload_data_size)
108{
109 struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
110 PGconn *db_conn;
111 int res;
112 struct Reserve reserve;
113 struct MintKeyState *key_state;
114 int must_update = GNUNET_NO;
115 json_t *json;
116
117 res = TALER_MINT_mhd_request_arg_data (connection,
118 "reserve_pub",
119 &reserve_pub,
120 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
121 if (GNUNET_SYSERR == res)
122 {
123 // FIXME: return 'internal error'
124 GNUNET_break (0);
125 return MHD_NO;
126 }
127 if (GNUNET_OK != res)
128 return MHD_YES;
129 if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
130 {
131 // FIXME: return 'internal error'?
132 GNUNET_break (0);
133 return MHD_NO;
134 }
135 res = TALER_MINT_DB_get_reserve (db_conn,
136 &reserve_pub,
137 &reserve);
138 if (GNUNET_SYSERR == res)
139 return TALER_MINT_helper_send_json_pack (rh,
140 connection,
141 connection_cls,
142 0 /* no caching */,
143 MHD_HTTP_NOT_FOUND,
144 "{s:s}",
145 "error",
146 "Reserve not found");
147 if (GNUNET_OK != res)
148 {
149 // FIXME: return 'internal error'?
150 GNUNET_break (0);
151 return MHD_NO;
152 }
153 key_state = TALER_MINT_key_state_acquire ();
154 if (0 != memcmp (&key_state->current_sign_key_issue.signkey_pub,
155 &reserve.status_sign_pub,
156 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
157 {
158 sign_reserve (&reserve, key_state);
159 must_update = GNUNET_YES;
160 }
161 if ((GNUNET_YES == must_update) &&
162 (GNUNET_OK != TALER_MINT_DB_update_reserve (db_conn, &reserve, !must_update)))
163 {
164 GNUNET_break (0);
165 return MHD_YES;
166 }
167
168 /* Convert the public information of a reserve (i.e.
169 excluding private key) to a JSON object. */
170 json = json_object ();
171 json_object_set_new (json,
172 "balance",
173 TALER_JSON_from_amount (TALER_amount_ntoh (reserve.balance)));
174 json_object_set_new (json,
175 "expiration",
176 TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (reserve.expiration)));
177 json_object_set_new (json,
178 "signature",
179 sig_to_json (&reserve.status_sig_purpose,
180 &reserve.status_sig));
181
182 return send_response_json (connection,
183 json,
184 MHD_HTTP_OK);
185}
186
187
188/**
189 * Send positive, normal response for "/withdraw/sign".
190 *
191 * @param connection the connection to send the response to
192 * @param collectable the collectable blindcoin (i.e. the blindly signed coin)
193 * @return a MHD result code
194 */
195static int
196helper_withdraw_sign_send_reply (struct MHD_Connection *connection,
197 const struct CollectableBlindcoin *collectable)
198{
199 json_t *root = json_object ();
200
201 json_object_set_new (root, "ev_sig",
202 TALER_JSON_from_data (&collectable->ev_sig,
203 sizeof (struct TALER_RSA_Signature)));
204 return send_response_json (connection,
205 root,
206 MHD_HTTP_OK);
207}
208
209
210/**
211 * Handle a "/withdraw/sign" request
212 *
213 * @param rh context of the handler
214 * @param connection the MHD connection to handle
215 * @param[IN|OUT] connection_cls the connection's closure (can be updated)
216 * @param upload_data upload data
217 * @param[IN|OUT] upload_data_size number of bytes (left) in @a upload_data
218 * @return MHD result code
219 */
220int
221TALER_MINT_handler_withdraw_sign (struct RequestHandler *rh,
222 struct MHD_Connection *connection,
223 void **connection_cls,
224 const char *upload_data,
225 size_t *upload_data_size)
226{
227 struct TALER_WithdrawRequest wsrd;
228 int res;
229 PGconn *db_conn;
230 struct Reserve reserve;
231 struct MintKeyState *key_state;
232 struct CollectableBlindcoin collectable;
233 struct TALER_MINT_DenomKeyIssue *dki;
234 struct TALER_RSA_Signature ev_sig;
235 struct TALER_Amount amount_required;
236
237 memset (&wsrd,
238 0,
239 sizeof (struct TALER_WithdrawRequest));
240 res = TALER_MINT_mhd_request_arg_data (connection,
241 "reserve_pub",
242 &wsrd.reserve_pub,
243 sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
244 if (GNUNET_SYSERR == res)
245 {
246 // FIXME: return 'internal error'?
247 GNUNET_break (0);
248 return MHD_NO;
249 }
250 if (GNUNET_OK != res)
251 return MHD_YES;
252 res = TALER_MINT_mhd_request_arg_data (connection,
253 "denom_pub",
254 &wsrd.denomination_pub,
255 sizeof (struct TALER_RSA_PublicKeyBinaryEncoded));
256 if (GNUNET_SYSERR == res)
257 {
258 // FIXME: return 'internal error'?
259 GNUNET_break (0);
260 return MHD_NO;
261 }
262 if (GNUNET_OK != res)
263 return MHD_YES;
264 res = TALER_MINT_mhd_request_arg_data (connection,
265 "coin_ev",
266 &wsrd.coin_envelope,
267 sizeof (struct TALER_RSA_Signature));
268 if (GNUNET_SYSERR == res)
269 {
270 // FIXME: return 'internal error'?
271 GNUNET_break (0);
272 return MHD_NO;
273 }
274 if (GNUNET_OK != res)
275 return MHD_YES;
276 res = TALER_MINT_mhd_request_arg_data (connection,
277 "reserve_sig",
278 &wsrd.sig,
279 sizeof (struct GNUNET_CRYPTO_EddsaSignature));
280 if (GNUNET_SYSERR == res)
281 {
282 // FIXME: return 'internal error'?
283 GNUNET_break (0);
284 return MHD_NO;
285 }
286 if (GNUNET_OK != res)
287 return MHD_YES;
288
289 if (NULL == (db_conn = TALER_MINT_DB_get_connection ()))
290 {
291 // FIXME: return 'internal error'?
292 GNUNET_break (0);
293 return MHD_NO;
294 }
295
296 res = TALER_MINT_DB_get_collectable_blindcoin (db_conn,
297 &wsrd.coin_envelope,
298 &collectable);
299 if (GNUNET_SYSERR == res)
300 {
301 // FIXME: return 'internal error'
302 GNUNET_break (0);
303 return MHD_NO;
304 }
305
306 /* Don't sign again if we have already signed the coin */
307 if (GNUNET_YES == res)
308 return helper_withdraw_sign_send_reply (connection,
309 &collectable);
310 GNUNET_assert (GNUNET_NO == res);
311 res = TALER_MINT_DB_get_reserve (db_conn,
312 &wsrd.reserve_pub,
313 &reserve);
314 if (GNUNET_SYSERR == res)
315 {
316 // FIXME: return 'internal error'
317 GNUNET_break (0);
318 return MHD_NO;
319 }
320 if (GNUNET_NO == res)
321 return request_send_json_pack (connection,
322 MHD_HTTP_NOT_FOUND,
323 "{s:s}",
324 "error", "Reserve not found");
325
326 // fill out all the missing info in the request before
327 // we can check the signature on the request
328
329 wsrd.purpose.purpose = htonl (TALER_SIGNATURE_WITHDRAW);
330 wsrd.purpose.size = htonl (sizeof (struct TALER_WithdrawRequest) -
331 offsetof (struct TALER_WithdrawRequest, purpose));
332
333 if (GNUNET_OK !=
334 GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WITHDRAW,
335 &wsrd.purpose,
336 &wsrd.sig,
337 &wsrd.reserve_pub))
338 return request_send_json_pack (connection,
339 MHD_HTTP_UNAUTHORIZED,
340 "{s:s}",
341 "error", "Invalid Signature");
342
343 key_state = TALER_MINT_key_state_acquire ();
344 dki = TALER_MINT_get_denom_key (key_state,
345 &wsrd.denomination_pub);
346 TALER_MINT_key_state_release (key_state);
347 if (NULL == dki)
348 return request_send_json_pack (connection, MHD_HTTP_NOT_FOUND,
349 "{s:s}",
350 "error", "Denomination not found");
351
352 amount_required = TALER_amount_ntoh (dki->value);
353 amount_required = TALER_amount_add (amount_required,
354 TALER_amount_ntoh (dki->fee_withdraw));
355
356 if (0 < TALER_amount_cmp (amount_required,
357 TALER_amount_ntoh (reserve.balance)))
358 return request_send_json_pack (connection,
359 MHD_HTTP_PAYMENT_REQUIRED,
360 "{s:s}",
361 "error", "Insufficient funds");
362 if (GNUNET_OK != TALER_RSA_sign (dki->denom_priv,
363 &wsrd.coin_envelope,
364 sizeof (struct TALER_RSA_BlindedSignaturePurpose),
365 &ev_sig))
366 {
367 // FIXME: return 'internal error'
368 GNUNET_break (0);
369 return MHD_NO;
370 }
371
372 reserve.balance = TALER_amount_hton (TALER_amount_subtract (TALER_amount_ntoh (reserve.balance),
373 amount_required));
374 if (GNUNET_OK !=
375 TALER_MINT_DB_update_reserve (db_conn,
376 &reserve,
377 GNUNET_YES))
378 {
379 // FIXME: return 'internal error'
380 GNUNET_break (0);
381 return MHD_NO;
382 }
383
384 collectable.ev = wsrd.coin_envelope;
385 collectable.ev_sig = ev_sig;
386 collectable.reserve_pub = wsrd.reserve_pub;
387 collectable.reserve_sig = wsrd.sig;
388 if (GNUNET_OK !=
389 TALER_MINT_DB_insert_collectable_blindcoin (db_conn,
390 &collectable))
391 {
392 // FIXME: return 'internal error'
393 GNUNET_break (0);
394 return GNUNET_NO;;
395 }
396 return helper_withdraw_sign_send_reply (connection,
397 &collectable);
398}
399
400/* end of taler-mint-httpd_withdraw.c */