771bcb92dbc7b55eed155cb0cb83d55339c674a0
[oweals/gnunet.git] / src / revocation / revocation_api.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2013, 2016 GNUnet e.V.
4
5       GNUnet is free software: you can redistribute it and/or modify it
6       under the terms of the GNU Affero General Public License as published
7       by the Free Software Foundation, either version 3 of the License,
8       or (at your option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       Affero General Public License for more details.
14
15       You should have received a copy of the GNU Affero General Public License
16       along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20 /**
21  * @file revocation/revocation_api.c
22  * @brief API to perform and access key revocations
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_revocation_service.h"
27 #include "gnunet_signatures.h"
28 #include "gnunet_protocols.h"
29 #include "revocation.h"
30 #include <inttypes.h>
31
32 /**
33  * Handle for the key revocation query.
34  */
35 struct GNUNET_REVOCATION_Query
36 {
37   /**
38    * Message queue to the service.
39    */
40   struct GNUNET_MQ_Handle *mq;
41
42   /**
43    * Function to call with the result.
44    */
45   GNUNET_REVOCATION_Callback func;
46
47   /**
48    * Closure for @e func.
49    */
50   void *func_cls;
51 };
52
53
54 /**
55  * Helper struct that holds a found pow nonce
56  * and the corresponding number of leading zeroes.
57  */
58 struct BestPow
59 {
60   /**
61    * PoW nonce
62    */
63   uint64_t pow;
64
65   /**
66    * Corresponding zero bits in hash
67    */
68   unsigned int bits;
69 };
70
71
72 /**
73  * The handle to a PoW calculation.
74  * Used in iterative PoW rounds.
75  */
76 struct GNUNET_REVOCATION_PowCalculationHandle
77 {
78   /**
79    * Current set of found PoWs
80    */
81   struct BestPow best[POW_COUNT];
82
83   /**
84    * The final PoW result data structure.
85    */
86   struct GNUNET_REVOCATION_Pow pow;
87
88   /**
89    * The current nonce to try
90    */
91   uint64_t current_pow;
92
93   /**
94    * Epochs how long the PoW should be valid.
95    * This is added on top of the difficulty in the PoW.
96    */
97   unsigned int epochs;
98
99   /**
100    * The difficulty (leading zeros) to achieve.
101    */
102   unsigned int difficulty;
103
104 };
105
106 /**
107  * Generic error handler, called with the appropriate
108  * error code and the same closure specified at the creation of
109  * the message queue.
110  * Not every message queue implementation supports an error handler.
111  *
112  * @param cls closure with the `struct GNUNET_NSE_Handle *`
113  * @param error error code
114  */
115 static void
116 query_mq_error_handler (void *cls,
117                         enum GNUNET_MQ_Error error)
118 {
119   struct GNUNET_REVOCATION_Query *q = cls;
120
121   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
122               "Revocation query MQ error\n");
123   q->func (q->func_cls,
124            GNUNET_SYSERR);
125   GNUNET_REVOCATION_query_cancel (q);
126 }
127
128
129 /**
130  * Handle response to our revocation query.
131  *
132  * @param cls our `struct GNUNET_REVOCATION_Query` handle
133  * @param qrm response we got
134  */
135 static void
136 handle_revocation_query_response (void *cls,
137                                   const struct QueryResponseMessage *qrm)
138 {
139   struct GNUNET_REVOCATION_Query *q = cls;
140
141   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
142               "Revocation query result: %d\n",
143               (uint32_t) ntohl (qrm->is_valid));
144   q->func (q->func_cls,
145            ntohl (qrm->is_valid));
146   GNUNET_REVOCATION_query_cancel (q);
147 }
148
149
150 /**
151  * Check if a key was revoked.
152  *
153  * @param cfg the configuration to use
154  * @param key key to check for revocation
155  * @param func funtion to call with the result of the check
156  * @param func_cls closure to pass to @a func
157  * @return handle to use in #GNUNET_REVOCATION_query_cancel to stop REVOCATION from invoking the callback
158  */
159 struct GNUNET_REVOCATION_Query *
160 GNUNET_REVOCATION_query (const struct GNUNET_CONFIGURATION_Handle *cfg,
161                          const struct GNUNET_CRYPTO_EcdsaPublicKey *key,
162                          GNUNET_REVOCATION_Callback func,
163                          void *func_cls)
164 {
165   struct GNUNET_REVOCATION_Query *q
166     = GNUNET_new (struct GNUNET_REVOCATION_Query);
167   struct GNUNET_MQ_MessageHandler handlers[] = {
168     GNUNET_MQ_hd_fixed_size (revocation_query_response,
169                              GNUNET_MESSAGE_TYPE_REVOCATION_QUERY_RESPONSE,
170                              struct QueryResponseMessage,
171                              q),
172     GNUNET_MQ_handler_end ()
173   };
174   struct QueryMessage *qm;
175   struct GNUNET_MQ_Envelope *env;
176
177   q->mq = GNUNET_CLIENT_connect (cfg,
178                                  "revocation",
179                                  handlers,
180                                  &query_mq_error_handler,
181                                  q);
182   if (NULL == q->mq)
183   {
184     GNUNET_free (q);
185     return NULL;
186   }
187   q->func = func;
188   q->func_cls = func_cls;
189   env = GNUNET_MQ_msg (qm,
190                        GNUNET_MESSAGE_TYPE_REVOCATION_QUERY);
191   qm->reserved = htonl (0);
192   qm->key = *key;
193   GNUNET_MQ_send (q->mq,
194                   env);
195   return q;
196 }
197
198
199 /**
200  * Cancel key revocation check.
201  *
202  * @param q query to cancel
203  */
204 void
205 GNUNET_REVOCATION_query_cancel (struct GNUNET_REVOCATION_Query *q)
206 {
207   if (NULL != q->mq)
208   {
209     GNUNET_MQ_destroy (q->mq);
210     q->mq = NULL;
211   }
212   GNUNET_free (q);
213 }
214
215
216 /**
217  * Handle for the key revocation operation.
218  */
219 struct GNUNET_REVOCATION_Handle
220 {
221   /**
222    * Message queue to the service.
223    */
224   struct GNUNET_MQ_Handle *mq;
225
226   /**
227    * Function to call once we are done.
228    */
229   GNUNET_REVOCATION_Callback func;
230
231   /**
232    * Closure for @e func.
233    */
234   void *func_cls;
235 };
236
237
238 /**
239  * Generic error handler, called with the appropriate
240  * error code and the same closure specified at the creation of
241  * the message queue.
242  * Not every message queue implementation supports an error handler.
243  *
244  * @param cls closure with the `struct GNUNET_NSE_Handle *`
245  * @param error error code
246  */
247 static void
248 revocation_mq_error_handler (void *cls,
249                              enum GNUNET_MQ_Error error)
250 {
251   struct GNUNET_REVOCATION_Handle *h = cls;
252
253   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
254               "Revocation MQ error\n");
255   h->func (h->func_cls,
256            GNUNET_SYSERR);
257   GNUNET_REVOCATION_revoke_cancel (h);
258 }
259
260
261 /**
262  * Handle response to our revocation query.
263  *
264  * @param cls our `struct GNUNET_REVOCATION_Handle` handle
265  * @param rrm response we got
266  */
267 static void
268 handle_revocation_response (void *cls,
269                             const struct RevocationResponseMessage *rrm)
270 {
271   struct GNUNET_REVOCATION_Handle *h = cls;
272
273   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
274               "Revocation transmission result: %d\n",
275               (uint32_t) ntohl (rrm->is_valid));
276   h->func (h->func_cls,
277            ntohl (rrm->is_valid));
278   GNUNET_REVOCATION_revoke_cancel (h);
279 }
280
281
282 /**
283  * Perform key revocation.
284  *
285  * @param cfg the configuration to use
286  * @param key public key of the key to revoke
287  * @param sig signature to use on the revocation (should have been
288  *            created using #GNUNET_REVOCATION_sign_revocation).
289  * @param ts  revocation timestamp
290  * @param pow proof of work to use (should have been created by
291  *            iteratively calling #GNUNET_REVOCATION_check_pow)
292  * @param func funtion to call with the result of the check
293  *             (called with `is_valid` being #GNUNET_NO if
294  *              the revocation worked).
295  * @param func_cls closure to pass to @a func
296  * @return handle to use in #GNUNET_REVOCATION_revoke_cancel to stop REVOCATION from invoking the callback
297  */
298 struct GNUNET_REVOCATION_Handle *
299 GNUNET_REVOCATION_revoke (const struct GNUNET_CONFIGURATION_Handle *cfg,
300                           const struct GNUNET_REVOCATION_Pow *pow,
301                           GNUNET_REVOCATION_Callback func,
302                           void *func_cls)
303 {
304   struct GNUNET_REVOCATION_Handle *h
305     = GNUNET_new (struct GNUNET_REVOCATION_Handle);
306   struct GNUNET_MQ_MessageHandler handlers[] = {
307     GNUNET_MQ_hd_fixed_size (revocation_response,
308                              GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE_RESPONSE,
309                              struct RevocationResponseMessage,
310                              h),
311     GNUNET_MQ_handler_end ()
312   };
313   unsigned long long matching_bits;
314   struct RevokeMessage *rm;
315   struct GNUNET_MQ_Envelope *env;
316
317   if ((GNUNET_OK ==
318        GNUNET_CONFIGURATION_get_value_number (cfg,
319                                               "REVOCATION",
320                                               "WORKBITS",
321                                               &matching_bits)) &&
322       (0 >= GNUNET_REVOCATION_check_pow (pow, (unsigned int) matching_bits)))
323   {
324     GNUNET_break (0);
325     GNUNET_free (h);
326     return NULL;
327   }
328
329   h->mq = GNUNET_CLIENT_connect (cfg,
330                                  "revocation",
331                                  handlers,
332                                  &revocation_mq_error_handler,
333                                  h);
334   if (NULL == h->mq)
335   {
336     GNUNET_free (h);
337     return NULL;
338   }
339   h->func = func;
340   h->func_cls = func_cls;
341   env = GNUNET_MQ_msg (rm,
342                        GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE);
343   rm->reserved = htonl (0);
344   rm->proof_of_work = *pow;
345   GNUNET_MQ_send (h->mq,
346                   env);
347   return h;
348 }
349
350
351 /**
352  * Cancel key revocation.
353  *
354  * @param h operation to cancel
355  */
356 void
357 GNUNET_REVOCATION_revoke_cancel (struct GNUNET_REVOCATION_Handle *h)
358 {
359   if (NULL != h->mq)
360   {
361     GNUNET_MQ_destroy (h->mq);
362     h->mq = NULL;
363   }
364   GNUNET_free (h);
365 }
366
367
368 /**
369  * Count the leading zeroes in hash.
370  *
371  * @param hash to count leading zeros in
372  * @return the number of leading zero bits.
373  */
374 static unsigned int
375 count_leading_zeroes (const struct GNUNET_HashCode *hash)
376 {
377   unsigned int hash_count;
378
379   hash_count = 0;
380   while ((0 == GNUNET_CRYPTO_hash_get_bit (hash, hash_count)))
381     hash_count++;
382   return hash_count;
383 }
384
385
386 /**
387  * Calculate the average zeros in the pows.
388  *
389  * @param ph the PowHandle
390  * @return the average number of zeroes.
391  */
392 static unsigned int
393 calculate_score (const struct GNUNET_REVOCATION_PowCalculationHandle *ph)
394 {
395   double sum = 0.0;
396   for (unsigned int j = 0; j<POW_COUNT; j++)
397     sum += ph->best[j].bits;
398   double avg = sum / POW_COUNT;
399   return avg;
400 }
401
402
403 /**
404  * Check if the given proof-of-work value
405  * would be acceptable for revoking the given key.
406  *
407  * @param key key to check for
408  * @param ts  revocation timestamp
409  * @param pow proof of work value
410  * @param matching_bits how many bits must match (configuration)
411  * @return number of epochs valid if the @a pow is acceptable, -1 if not
412  */
413 int
414 GNUNET_REVOCATION_check_pow (const struct GNUNET_REVOCATION_Pow *pow,
415                              unsigned int difficulty)
416 {
417   char buf[sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)
418            + sizeof (uint64_t)
419            + sizeof (uint64_t)] GNUNET_ALIGN;
420   struct GNUNET_HashCode result;
421   unsigned int score = 0;
422   unsigned int tmp_score = 0;
423   unsigned int epochs;
424   uint64_t pow_val;
425
426   /**
427    * First, check if any duplicates are in the PoW set
428    */
429   for (unsigned int i = 0; i < POW_COUNT; i++)
430   {
431     for (unsigned int j = i + 1; j < POW_COUNT; j++)
432     {
433       if (pow->pow[i] == pow->pow[j])
434         return -1;
435     }
436   }
437   GNUNET_memcpy (&buf[sizeof(uint64_t)],
438                  &pow->timestamp,
439                  sizeof (uint64_t));
440   GNUNET_memcpy (&buf[sizeof(uint64_t) * 2],
441                  &pow->key,
442                  sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey));
443   for (unsigned int i = 0; i < POW_COUNT; i++)
444   {
445     pow_val = GNUNET_ntohll (pow->pow[i]);
446     GNUNET_memcpy (buf, &pow_val, sizeof(uint64_t));
447     GNUNET_CRYPTO_pow_hash ("gnunet-revocation-proof-of-work",
448                             buf,
449                             sizeof(buf),
450                             &result);
451     tmp_score = count_leading_zeroes (&result);
452     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
453                 "Score %u with %" PRIu64 " (#%u)\n",
454                 tmp_score, pow_val, i);
455     score += tmp_score;
456
457   }
458   score = score / POW_COUNT;
459   if (score < difficulty)
460     return -1;
461   epochs = score - difficulty;
462   return epochs;
463 }
464
465
466 /**
467  * Initializes a fresh PoW computation
468  *
469  * @param key the key to calculate the PoW for.
470  * @param epochs the number of epochs for which the PoW must be valid.
471  * @param difficulty the base difficulty of the PoW
472  * @return a handle for use in PoW rounds
473  */
474 struct GNUNET_REVOCATION_PowCalculationHandle*
475 GNUNET_REVOCATION_pow_init (const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
476                             int epochs,
477                             unsigned int difficulty)
478 {
479   struct GNUNET_REVOCATION_PowCalculationHandle *pc;
480   struct GNUNET_TIME_Absolute ts = GNUNET_TIME_absolute_get ();
481
482   pc = GNUNET_new (struct GNUNET_REVOCATION_PowCalculationHandle);
483   pc->pow.timestamp = GNUNET_TIME_absolute_hton (ts);
484   pc->pow.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_REVOCATION);
485   pc->pow.purpose.size = htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
486                              + sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey));
487   GNUNET_CRYPTO_ecdsa_key_get_public (key, &pc->pow.key);
488   GNUNET_assert (GNUNET_OK ==
489                  GNUNET_CRYPTO_ecdsa_sign_ (key,
490                                             &pc->pow.purpose,
491                                             &pc->pow.signature));
492   pc->current_pow = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
493                                               UINT64_MAX);
494   pc->difficulty = difficulty;
495   pc->epochs = epochs;
496   return pc;
497 }
498
499
500 /**
501  * Initializes PoW computation based on an existing PoW.
502  *
503  * @param pow the PoW to continue the calculations from.
504  * @param epochs the number of epochs for which the PoW must be valid.
505  * @param difficulty the base difficulty of the PoW
506  * @return a handle for use in PoW rounds
507  */
508 struct GNUNET_REVOCATION_PowCalculationHandle*
509 GNUNET_REVOCATION_pow_init2 (const struct GNUNET_REVOCATION_Pow *pow,
510                             int epochs,
511                             unsigned int difficulty)
512 {
513   struct GNUNET_REVOCATION_PowCalculationHandle *pc;
514
515   pc = GNUNET_new (struct GNUNET_REVOCATION_PowCalculationHandle);
516   pc->pow.key = pow->key;
517   pc->pow.timestamp = pow->timestamp;
518   pc->current_pow = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
519                                               UINT64_MAX);
520   pc->difficulty = difficulty;
521   pc->epochs = epochs;
522   return pc;
523 }
524
525
526
527 /**
528  * Calculate a key revocation valid for broadcasting for a number
529  * of epochs.
530  *
531  * @param pc handle to the PoW, initially called with NULL.
532  * @param epochs number of epochs for which the revocation must be valid.
533  * @param pow current pow value to try
534  * @param difficulty current base difficulty to achieve
535  * @return #GNUNET_YES if the @a pow is acceptable, #GNUNET_NO if not
536  */
537 int
538 GNUNET_REVOCATION_pow_round (struct GNUNET_REVOCATION_PowCalculationHandle *pc)
539 {
540   char buf[sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)
541            + sizeof (uint64_t)
542            + sizeof (uint64_t)] GNUNET_ALIGN;
543   struct GNUNET_HashCode result;
544   unsigned int zeros;
545
546   pc->current_pow++;
547
548   /**
549    * Do not try duplicates
550    */
551   for (unsigned int i = 0; i < POW_COUNT; i++)
552     if (pc->current_pow == pc->best[i].pow)
553       return GNUNET_NO;
554
555   GNUNET_memcpy (buf, &pc->current_pow, sizeof(uint64_t));
556   GNUNET_memcpy (&buf[sizeof(uint64_t)],
557                  &pc->pow.timestamp,
558                  sizeof (uint64_t));
559   GNUNET_memcpy (&buf[sizeof(uint64_t) * 2],
560                  &pc->pow.key,
561                  sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey));
562   GNUNET_CRYPTO_pow_hash ("gnunet-revocation-proof-of-work",
563                           buf,
564                           sizeof(buf),
565                           &result);
566   zeros = count_leading_zeroes (&result);
567   for (unsigned int i = 0; i < POW_COUNT; i++)
568   {
569     if (pc->best[i].bits < zeros)
570     {
571       pc->best[i].bits = zeros;
572       pc->best[i].pow = pc->current_pow;
573       pc->pow.pow[i] = GNUNET_htonll (pc->current_pow);
574       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
575                   "New best score %u with %" PRIu64 " (#%u)\n",
576                   zeros, pc->current_pow, i);
577       break;
578     }
579   }
580   return calculate_score (pc) >= pc->difficulty + pc->epochs ? GNUNET_YES :
581          GNUNET_NO;
582 }
583
584
585 /**
586  * Return the curren PoW state from the calculation
587  *
588  * @param pc the calculation to get it from
589  * @return a pointer to the PoW
590  */
591 const struct GNUNET_REVOCATION_Pow*
592 GNUNET_REVOCATION_pow_get (const struct
593                            GNUNET_REVOCATION_PowCalculationHandle *pc)
594 {
595   return &pc->pow;
596 }
597
598
599 /**
600  * Cleanup a PoW calculation
601  *
602  * @param pc the calculation to clean up
603  */
604 void
605 GNUNET_REVOCATION_pow_cleanup (struct
606                                GNUNET_REVOCATION_PowCalculationHandle *pc)
607 {
608   GNUNET_free (pc);
609 }
610
611 /* end of revocation_api.c */