2 This file is part of GNUnet.
3 (C) 2013 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public Licerevocation as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
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 General Public Licerevocation for more details.
15 You should have received a copy of the GNU General Public Licerevocation
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file revocation/gnunet-service-revocation.c
23 * @brief key revocation service
24 * @author Christian Grothoff
26 * The purpose of this service is to allow users to permanently revoke
27 * (compromised) keys. This is done by flooding the network with the
28 * revocation requests. To reduce the attack potential offered by such
29 * flooding, revocations must include a proof of work. We use the
30 * set service for efficiently computing the union of revocations of
34 * - handle p2p connect (trigger SET union, #3057)
35 * - optimization: avoid sending revocation back to peer that we got it from;
36 * - optimization: have randomized delay in sending revocations to other peers
37 * to make it rare to traverse each link twice (NSE-style)
41 #include "gnunet_util_lib.h"
42 #include "gnunet_constants.h"
43 #include "gnunet_protocols.h"
44 #include "gnunet_signatures.h"
45 #include "gnunet_statistics_service.h"
46 #include "gnunet_core_service.h"
47 #include "gnunet_revocation_service.h"
48 #include "gnunet_set_service.h"
49 #include "revocation.h"
54 * Per-peer information.
60 * Queue for sending messages to this peer.
62 struct GNUNET_MQ_Handle *mq;
65 * What is the identity of the peer?
67 struct GNUNET_PeerIdentity id;
73 * Set from all revocations known to us.
75 static struct GNUNET_SET_Handle *revocation_set;
78 * Hash map with all revoked keys, maps the hash of the public key
79 * to the respective `struct RevokeMessage`.
81 static struct GNUNET_CONTAINER_MultiHashMap *revocation_map;
84 * Handle to our current configuration.
86 static const struct GNUNET_CONFIGURATION_Handle *cfg;
89 * Handle to the statistics service.
91 static struct GNUNET_STATISTICS_Handle *stats;
94 * Handle to the core service (for flooding)
96 static struct GNUNET_CORE_Handle *core_api;
99 * Map of all connected peers.
101 static struct GNUNET_CONTAINER_MultiPeerMap *peers;
104 * The peer identity of this peer.
106 static struct GNUNET_PeerIdentity my_identity;
109 * Handle to this serivce's server.
111 static struct GNUNET_SERVER_Handle *srv;
114 * Notification context for convenient sending of replies to the clients.
116 static struct GNUNET_SERVER_NotificationContext *nc;
119 * File handle for the revocation database.
121 static struct GNUNET_DISK_FileHandle *revocation_db;
124 * Amount of work required (W-bit collisions) for REVOCATION proofs, in collision-bits.
126 static unsigned long long revocation_work_required;
130 * An revoke message has been received, check that it is well-formed.
132 * @param rm the message to verify
133 * @return #GNUNET_YES if the message is verified
134 * #GNUNET_NO if the key/signature don't verify
137 verify_revoke_message (const struct RevokeMessage *rm)
140 GNUNET_REVOCATION_check_pow (&rm->public_key,
142 (unsigned int) revocation_work_required))
144 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
145 "Proof of work invalid!\n");
150 GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_REVOCATION,
163 * Handle QUERY message from client.
166 * @param client who sent the message
167 * @param message the message received
170 handle_query_message (void *cls,
171 struct GNUNET_SERVER_Client *client,
172 const struct GNUNET_MessageHeader *message)
174 const struct QueryMessage *qm = (const struct QueryMessage *) message;
175 struct QueryResponseMessage qrm;
176 struct GNUNET_HashCode hc;
179 GNUNET_CRYPTO_hash (&qm->key,
180 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
182 res = GNUNET_CONTAINER_multihashmap_contains (revocation_map, &hc);
183 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
185 ? "Received revocation check for valid key `%s' from client\n"
186 : "Received revocation check for revoked key `%s' from client\n",
188 qrm.header.size = htons (sizeof (struct QueryResponseMessage));
189 qrm.header.type = htons (GNUNET_MESSAGE_TYPE_REVOCATION_QUERY_RESPONSE);
190 qrm.is_valid = htonl ((GNUNET_YES == res) ? GNUNET_NO : GNUNET_YES);
191 GNUNET_SERVER_notification_context_add (nc,
193 GNUNET_SERVER_notification_context_unicast (nc,
197 GNUNET_SERVER_receive_done (client, GNUNET_OK);
202 * Flood the given revocation message to all neighbours.
204 * @param cls the `struct RevokeMessage` to flood
205 * @param target a neighbour
206 * @param value our `struct PeerEntry` for the neighbour
207 * @return #GNUNET_OK (continue to iterate)
211 const struct GNUNET_PeerIdentity *target,
214 const struct RevokeMessage *rm = cls;
215 struct PeerEntry *pe = value;
216 struct GNUNET_MQ_Envelope *e;
217 struct RevokeMessage *cp;
219 e = GNUNET_MQ_msg (cp, GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE);
221 GNUNET_MQ_send (pe->mq, e);
227 * Publicize revocation message. Stores the message locally in the
228 * database and passes it to all connected neighbours (and adds it to
229 * the set for future connections).
231 * @param rm message to publicize
232 * @return #GNUNET_OK on success, #GNUNET_NO if we encountered an error,
233 * #GNUNET_SYSERR if the message was malformed
236 publicize_rm (const struct RevokeMessage *rm)
238 struct RevokeMessage *cp;
239 struct GNUNET_HashCode hc;
240 struct GNUNET_SET_Element e;
242 GNUNET_CRYPTO_hash (&rm->public_key,
243 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
246 GNUNET_CONTAINER_multihashmap_contains (revocation_map,
249 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
250 _("Duplicate revocation received from peer. Ignored.\n"));
254 verify_revoke_message (rm))
257 return GNUNET_SYSERR;
260 if (sizeof (struct RevokeMessage) !=
261 GNUNET_DISK_file_write (revocation_db,
263 sizeof (struct RevokeMessage)))
265 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
270 GNUNET_DISK_file_sync (revocation_db))
272 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
276 /* keep copy in memory */
277 cp = (struct RevokeMessage *) GNUNET_copy_message (&rm->header);
278 GNUNET_break (GNUNET_OK ==
279 GNUNET_CONTAINER_multihashmap_put (revocation_map,
282 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
283 /* add to set for future connections */
284 e.size = htons (rm->header.size);
288 GNUNET_SET_add_element (revocation_set,
295 /* flood to neighbours */
296 GNUNET_CONTAINER_multipeermap_iterate (peers,
304 * Handle REVOKE message from client.
307 * @param client who sent the message
308 * @param message the message received
311 handle_revoke_message (void *cls,
312 struct GNUNET_SERVER_Client *client,
313 const struct GNUNET_MessageHeader *message)
315 const struct RevokeMessage *rm;
316 struct RevocationResponseMessage rrm;
319 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
320 "Received REVOKE message from client\n");
321 rm = (const struct RevokeMessage *) message;
322 if (GNUNET_SYSERR == (ret = publicize_rm (rm)))
325 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
328 rrm.header.size = htons (sizeof (struct RevocationResponseMessage));
329 rrm.header.type = htons (GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE_RESPONSE);
330 rrm.is_valid = htonl ((GNUNET_OK == ret) ? GNUNET_NO : GNUNET_YES);
331 GNUNET_SERVER_notification_context_add (nc,
333 GNUNET_SERVER_notification_context_unicast (nc,
337 GNUNET_SERVER_receive_done (client, GNUNET_OK);
342 * Core handler for flooded revocation messages.
344 * @param cls closure unused
345 * @param message message
346 * @param peer peer identity this message is from (ignored)
349 handle_p2p_revoke_message (void *cls,
350 const struct GNUNET_PeerIdentity *peer,
351 const struct GNUNET_MessageHeader *message)
353 const struct RevokeMessage *rm;
355 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
356 "Received REVOKE message from peer\n");
357 rm = (const struct RevokeMessage *) message;
358 GNUNET_break_op (GNUNET_SYSERR != publicize_rm (rm));
364 * Method called whenever a peer connects. Sets up the PeerEntry and
365 * schedules the initial revocation set exchange with this peer.
368 * @param peer peer identity this notification is about
371 handle_core_connect (void *cls,
372 const struct GNUNET_PeerIdentity *peer)
374 struct PeerEntry *peer_entry;
376 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%s' connected to us\n",
378 peer_entry = GNUNET_new (struct PeerEntry);
379 peer_entry->id = *peer;
380 peer_entry->mq = GNUNET_CORE_mq_create (core_api, peer);
381 GNUNET_assert (GNUNET_OK ==
382 GNUNET_CONTAINER_multipeermap_put (peers, peer,
384 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
385 // GNUNET_break (0); // FIXME: implement revocation set union on connect!
387 peer_entry->transmit_task =
388 GNUNET_SCHEDULER_add_delayed (get_transmit_delay (-1), &transmit_task_cb,
391 GNUNET_STATISTICS_update (stats, "# peers connected", 1, GNUNET_NO);
396 * Method called whenever a peer disconnects. Deletes the PeerEntry and cancels
397 * any pending transmission requests to that peer.
400 * @param peer peer identity this notification is about
403 handle_core_disconnect (void *cls,
404 const struct GNUNET_PeerIdentity *peer)
406 struct PeerEntry *pos;
408 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
409 "Peer `%s' disconnected from us\n",
411 pos = GNUNET_CONTAINER_multipeermap_get (peers, peer);
417 GNUNET_assert (GNUNET_YES ==
418 GNUNET_CONTAINER_multipeermap_remove (peers, peer,
420 GNUNET_MQ_destroy (pos->mq);
422 if (pos->transmit_task != GNUNET_SCHEDULER_NO_TASK)
424 GNUNET_SCHEDULER_cancel (pos->transmit_task);
425 pos->transmit_task = GNUNET_SCHEDULER_NO_TASK;
429 GNUNET_STATISTICS_update (stats, "# peers connected", -1, GNUNET_NO);
434 * Free all values in a hash map.
438 * @param value value to free
439 * @return #GNUNET_OK (continue to iterate)
442 free_entry (void *cls,
443 const struct GNUNET_HashCode *key,
452 * Task run during shutdown.
458 shutdown_task (void *cls,
459 const struct GNUNET_SCHEDULER_TaskContext *tc)
461 if (NULL != revocation_set)
463 GNUNET_SET_destroy (revocation_set);
464 revocation_set = NULL;
466 if (NULL != core_api)
468 GNUNET_CORE_disconnect (core_api);
473 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
478 GNUNET_CONTAINER_multipeermap_destroy (peers);
483 GNUNET_SERVER_notification_context_destroy (nc);
486 if (NULL != revocation_db)
488 GNUNET_DISK_file_close (revocation_db);
489 revocation_db = NULL;
491 GNUNET_CONTAINER_multihashmap_iterate (revocation_map,
494 GNUNET_CONTAINER_multihashmap_destroy (revocation_map);
499 * Called on core init/fail.
501 * @param cls service closure
502 * @param identity the public identity of this peer
505 core_init (void *cls,
506 const struct GNUNET_PeerIdentity *identity)
508 if (NULL == identity)
510 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
511 "Connection to core FAILED!\n");
512 GNUNET_SCHEDULER_shutdown ();
515 my_identity = *identity;
520 * Handle network size estimate clients.
523 * @param server the initialized server
524 * @param c configuration to use
528 struct GNUNET_SERVER_Handle *server,
529 const struct GNUNET_CONFIGURATION_Handle *c)
531 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
532 {&handle_query_message, NULL, GNUNET_MESSAGE_TYPE_REVOCATION_QUERY,
533 sizeof (struct QueryMessage)},
534 {&handle_revoke_message, NULL, GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE,
535 sizeof (struct RevokeMessage)},
538 static const struct GNUNET_CORE_MessageHandler core_handlers[] = {
539 {&handle_p2p_revoke_message, GNUNET_MESSAGE_TYPE_REVOCATION_REVOKE,
540 sizeof (struct RevokeMessage)},
545 struct RevokeMessage *rm;
546 struct GNUNET_HashCode hc;
549 GNUNET_CONFIGURATION_get_value_filename (c,
554 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
557 GNUNET_SCHEDULER_shutdown ();
562 revocation_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO);
563 nc = GNUNET_SERVER_notification_context_create (server, 1);
565 GNUNET_CONFIGURATION_get_value_number (cfg, "REVOCATION", "WORKBITS",
566 &revocation_work_required))
568 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
571 GNUNET_SCHEDULER_shutdown ();
575 if (revocation_work_required >= sizeof (struct GNUNET_HashCode) * 8)
577 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
580 _("Value is too large.\n"));
581 GNUNET_SCHEDULER_shutdown ();
585 revocation_set = GNUNET_SET_create (cfg,
586 GNUNET_SET_OPERATION_UNION);
588 revocation_db = GNUNET_DISK_file_open (fn,
589 GNUNET_DISK_OPEN_READWRITE |
590 GNUNET_DISK_OPEN_CREATE,
591 GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE |
592 GNUNET_DISK_PERM_GROUP_READ |
593 GNUNET_DISK_PERM_OTHER_READ);
594 if (NULL == revocation_db)
596 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
599 _("Could not open revocation database file!"));
600 GNUNET_SCHEDULER_shutdown ();
605 GNUNET_DISK_file_size (fn, &left, GNUNET_YES, GNUNET_YES))
607 while (left > sizeof (struct RevokeMessage))
609 rm = GNUNET_new (struct RevokeMessage);
610 if (sizeof (struct RevokeMessage) !=
611 GNUNET_DISK_file_read (revocation_db,
613 sizeof (struct RevokeMessage)))
615 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
619 GNUNET_SCHEDULER_shutdown ();
623 GNUNET_break (0 == ntohl (rm->reserved));
624 GNUNET_CRYPTO_hash (&rm->public_key,
625 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
627 GNUNET_break (GNUNET_OK ==
628 GNUNET_CONTAINER_multihashmap_put (revocation_map,
631 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
635 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
637 peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO);
638 GNUNET_SERVER_add_handlers (srv, handlers);
639 /* Connect to core service and register core handlers */
640 core_api = GNUNET_CORE_connect (cfg, /* Main configuration */
641 NULL, /* Closure passed to functions */
642 &core_init, /* Call core_init once connected */
643 &handle_core_connect, /* Handle connects */
644 &handle_core_disconnect, /* Handle disconnects */
645 NULL, /* Don't want notified about all incoming messages */
646 GNUNET_NO, /* For header only inbound notification */
647 NULL, /* Don't want notified about all outbound messages */
648 GNUNET_NO, /* For header only outbound notification */
649 core_handlers); /* Register these handlers */
650 if (NULL == core_api)
652 GNUNET_SCHEDULER_shutdown ();
655 stats = GNUNET_STATISTICS_create ("revocation", cfg);
660 * The main function for the network size estimation service.
662 * @param argc number of arguments from the command line
663 * @param argv command line arguments
664 * @return 0 ok, 1 on error
671 GNUNET_SERVICE_run (argc, argv, "revocation", GNUNET_SERVICE_OPTION_NONE,
672 &run, NULL)) ? 0 : 1;
680 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
682 void __attribute__ ((constructor))
683 GNUNET_ARM_memory_init ()
685 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
686 mallopt (M_TOP_PAD, 1 * 1024);
693 /* end of gnunet-service-revocation.c */