2 This file is part of GNUnet.
3 Copyright (C) 2012, 2016 GNUnet e.V.
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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file secretsharing/secretsharing_api.c
24 * @author Florian Dold
27 #include "gnunet_util_lib.h"
28 #include "gnunet_secretsharing_service.h"
29 #include "secretsharing.h"
33 #define LOG(kind, ...) GNUNET_log_from(kind, "secretsharing-api", __VA_ARGS__)
36 * Session that will eventually establish a shared secred between
37 * the involved peers and allow encryption and cooperative decryption.
39 struct GNUNET_SECRETSHARING_Session {
41 * Message queue for @e client.
43 struct GNUNET_MQ_Handle *mq;
46 * Called when the secret sharing is done.
48 GNUNET_SECRETSHARING_SecretReadyCallback secret_ready_cb;
51 * Closure for @e secret_ready_cb.
53 void *secret_ready_cls;
58 * Handle to cancel a cooperative decryption operation.
60 struct GNUNET_SECRETSHARING_DecryptionHandle {
62 * Message queue for @e client.
64 struct GNUNET_MQ_Handle *mq;
67 * Called when the secret sharing is done.
69 GNUNET_SECRETSHARING_DecryptCallback decrypt_cb;
72 * Closure for @e decrypt_cb.
79 * The ElGamal prime field order as libgcrypt mpi.
80 * Initialized in #init_crypto_constants.
82 static gcry_mpi_t elgamal_q;
85 * Modulus of the prime field used for ElGamal.
86 * Initialized in #init_crypto_constants.
88 static gcry_mpi_t elgamal_p;
91 * Generator for prime field of order 'elgamal_q'.
92 * Initialized in #init_crypto_constants.
94 static gcry_mpi_t elgamal_g;
98 * Function to initialize #elgamal_q, #elgamal_p and #elgamal_g.
101 ensure_elgamal_initialized(void)
103 if (NULL != elgamal_q)
104 return; /* looks like crypto is already initialized */
106 GNUNET_assert(0 == gcry_mpi_scan(&elgamal_q, GCRYMPI_FMT_HEX,
107 GNUNET_SECRETSHARING_ELGAMAL_Q_HEX, 0, NULL));
108 GNUNET_assert(0 == gcry_mpi_scan(&elgamal_p, GCRYMPI_FMT_HEX,
109 GNUNET_SECRETSHARING_ELGAMAL_P_HEX, 0, NULL));
110 GNUNET_assert(0 == gcry_mpi_scan(&elgamal_g, GCRYMPI_FMT_HEX,
111 GNUNET_SECRETSHARING_ELGAMAL_G_HEX, 0, NULL));
116 * Callback invoked when there is an error communicating with
117 * the service. Notifies the application about the error.
119 * @param cls the `struct GNUNET_SECRETSHARING_Session`
120 * @param error error code
123 handle_session_client_error(void *cls,
124 enum GNUNET_MQ_Error error)
126 struct GNUNET_SECRETSHARING_Session *s = cls;
128 s->secret_ready_cb(s->secret_ready_cls, NULL, NULL, 0, NULL);
129 GNUNET_SECRETSHARING_session_destroy(s);
134 * Callback invoked when there is an error communicating with
135 * the service. Notifies the application about the error.
137 * @param cls the `struct GNUNET_SECRETSHARING_DecryptionHandle`
138 * @param error error code
141 handle_decrypt_client_error(void *cls,
142 enum GNUNET_MQ_Error error)
144 struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
146 dh->decrypt_cb(dh->decrypt_cls, NULL);
147 GNUNET_SECRETSHARING_decrypt_cancel(dh);
152 * Handler invoked with the final result message from
153 * secret sharing. Decodes the message and passes the
154 * result to the application.
156 * @param cls the `struct GNUNET_SECRETSHARING_Session`
157 * @param m message with the result
160 check_secret_ready(void *cls,
161 const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
163 /* FIXME: actually check m is well-formed here! */
169 * Handler invoked with the final result message from
170 * secret sharing. Decodes the message and passes the
171 * result to the application.
173 * @param cls the `struct GNUNET_SECRETSHARING_Session`
174 * @param m message with the result
177 handle_secret_ready(void *cls,
178 const struct GNUNET_SECRETSHARING_SecretReadyMessage *m)
180 struct GNUNET_SECRETSHARING_Session *s = cls;
181 struct GNUNET_SECRETSHARING_Share *share;
184 LOG(GNUNET_ERROR_TYPE_DEBUG,
185 "Got secret ready message of size %u\n",
186 ntohs(m->header.size));
187 share_size = ntohs(m->header.size) - sizeof(struct GNUNET_SECRETSHARING_SecretReadyMessage);
189 share = GNUNET_SECRETSHARING_share_read(&m[1],
192 GNUNET_assert(NULL != share); // FIXME: this can fail!
193 // should have been checked in #check_secret_ready!
194 // FIXME: below we never check &m[1] is valid!
195 // FIXME: do we leak 'share' here?
196 s->secret_ready_cb(s->secret_ready_cls,
200 (const struct GNUNET_PeerIdentity *)&m[1]);
202 GNUNET_SECRETSHARING_session_destroy(s);
207 * Destroy a secret sharing session.
208 * The secret ready callback will not be called.
210 * @param s session to destroy
213 GNUNET_SECRETSHARING_session_destroy(struct GNUNET_SECRETSHARING_Session *s)
215 GNUNET_MQ_destroy(s->mq);
222 * Create a session that will eventually establish a shared secret
223 * with the other peers.
225 * @param cfg configuration to use
226 * @param num_peers number of peers in @a peers
227 * @param peers array of peers that we will share secrets with, can optionally contain the local peer
228 * @param session_id unique session id
229 * @param start When should all peers be available for sharing the secret?
230 * Random number generation can take place before the start time.
231 * @param deadline point in time where the session must be established; taken as hint
232 * by underlying consensus sessions
233 * @param threshold minimum number of peers that must cooperate to decrypt a value
234 * @param cb called when the secret has been established
235 * @param cls closure for @a cb
237 struct GNUNET_SECRETSHARING_Session *
238 GNUNET_SECRETSHARING_create_session(const struct GNUNET_CONFIGURATION_Handle *cfg,
239 unsigned int num_peers,
240 const struct GNUNET_PeerIdentity *peers,
241 const struct GNUNET_HashCode *session_id,
242 struct GNUNET_TIME_Absolute start,
243 struct GNUNET_TIME_Absolute deadline,
244 unsigned int threshold,
245 GNUNET_SECRETSHARING_SecretReadyCallback cb,
248 struct GNUNET_SECRETSHARING_Session *s
249 = GNUNET_new(struct GNUNET_SECRETSHARING_Session);
250 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
251 GNUNET_MQ_hd_var_size(secret_ready,
252 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_SECRET_READY,
253 struct GNUNET_SECRETSHARING_SecretReadyMessage,
255 GNUNET_MQ_handler_end()
257 struct GNUNET_MQ_Envelope *ev;
258 struct GNUNET_SECRETSHARING_CreateMessage *msg;
260 s->mq = GNUNET_CLIENT_connect(cfg,
263 &handle_session_client_error,
267 /* secretsharing not configured correctly */
272 s->secret_ready_cb = cb;
273 s->secret_ready_cls = cls;
274 ev = GNUNET_MQ_msg_extra(msg,
275 num_peers * sizeof(struct GNUNET_PeerIdentity),
276 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_GENERATE);
278 msg->threshold = htons(threshold);
279 msg->num_peers = htons(num_peers);
280 msg->session_id = *session_id;
281 msg->start = GNUNET_TIME_absolute_hton(start);
282 msg->deadline = GNUNET_TIME_absolute_hton(deadline);
283 GNUNET_memcpy(&msg[1], peers, num_peers * sizeof(struct GNUNET_PeerIdentity));
285 GNUNET_MQ_send(s->mq, ev);
287 LOG(GNUNET_ERROR_TYPE_DEBUG,
288 "Secretsharing session created with %u peers\n",
295 handle_decrypt_done(void *cls,
296 const struct GNUNET_SECRETSHARING_DecryptResponseMessage *m)
298 struct GNUNET_SECRETSHARING_DecryptionHandle *dh = cls;
299 const struct GNUNET_SECRETSHARING_Plaintext *plaintext;
304 plaintext = (void *)&m->plaintext;
305 dh->decrypt_cb(dh->decrypt_cls, plaintext);
306 GNUNET_SECRETSHARING_decrypt_cancel(dh);
311 * Publish the given ciphertext for decryption. Once a sufficient (>=k) number of peers has
312 * published the same value, it will be decrypted.
314 * When the operation is canceled, the decrypt_cb is not called anymore, but the calling
315 * peer may already have irrevocably contributed its share for the decryption of the value.
317 * @param share our secret share to use for decryption
318 * @param ciphertext ciphertext to publish in order to decrypt it (if enough peers agree)
319 * @param decrypt_cb callback called once the decryption succeeded
320 * @param decrypt_cb_cls closure for @a decrypt_cb
321 * @return handle to cancel the operation
323 struct GNUNET_SECRETSHARING_DecryptionHandle *
324 GNUNET_SECRETSHARING_decrypt(const struct GNUNET_CONFIGURATION_Handle *cfg,
325 struct GNUNET_SECRETSHARING_Share *share,
326 const struct GNUNET_SECRETSHARING_Ciphertext *ciphertext,
327 struct GNUNET_TIME_Absolute start,
328 struct GNUNET_TIME_Absolute deadline,
329 GNUNET_SECRETSHARING_DecryptCallback decrypt_cb,
330 void *decrypt_cb_cls)
332 struct GNUNET_SECRETSHARING_DecryptionHandle *s
333 = GNUNET_new(struct GNUNET_SECRETSHARING_DecryptionHandle);
334 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
335 GNUNET_MQ_hd_fixed_size(decrypt_done,
336 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT_DONE,
337 struct GNUNET_SECRETSHARING_DecryptResponseMessage,
339 GNUNET_MQ_handler_end()
341 struct GNUNET_MQ_Envelope *ev;
342 struct GNUNET_SECRETSHARING_DecryptRequestMessage *msg;
345 s->decrypt_cb = decrypt_cb;
346 s->decrypt_cls = decrypt_cb_cls;
347 s->mq = GNUNET_CLIENT_connect(cfg,
350 &handle_decrypt_client_error,
357 GNUNET_assert(GNUNET_OK ==
358 GNUNET_SECRETSHARING_share_write(share, NULL, 0,
361 ev = GNUNET_MQ_msg_extra(msg,
363 GNUNET_MESSAGE_TYPE_SECRETSHARING_CLIENT_DECRYPT);
365 GNUNET_assert(GNUNET_OK ==
366 GNUNET_SECRETSHARING_share_write(share,
371 msg->start = GNUNET_TIME_absolute_hton(start);
372 msg->deadline = GNUNET_TIME_absolute_hton(deadline);
373 msg->ciphertext = *ciphertext;
375 GNUNET_MQ_send(s->mq, ev);
377 LOG(GNUNET_ERROR_TYPE_DEBUG,
378 "decrypt session created\n");
384 GNUNET_SECRETSHARING_plaintext_generate_i(struct GNUNET_SECRETSHARING_Plaintext *plaintext,
390 ensure_elgamal_initialized();
392 GNUNET_assert(NULL != (x = gcry_mpi_new(0)));
394 negative = GNUNET_NO;
397 negative = GNUNET_YES;
398 exponent = -exponent;
401 gcry_mpi_set_ui(x, exponent);
403 gcry_mpi_powm(x, elgamal_g, x, elgamal_p);
405 if (GNUNET_YES == negative)
408 res = gcry_mpi_invm(x, x, elgamal_p);
410 return GNUNET_SYSERR;
413 GNUNET_CRYPTO_mpi_print_unsigned(plaintext,
414 sizeof(struct GNUNET_SECRETSHARING_Plaintext),
422 * Encrypt a value. This operation is executed locally, no communication is
425 * This is a helper function, encryption can be done soley with a session's public key
426 * and the crypto system parameters.
428 * @param public_key public key to use for decryption
429 * @param message message to encrypt
430 * @param message_size number of bytes in @a message
431 * @param result_ciphertext pointer to store the resulting ciphertext
432 * @return #GNUNET_YES on succes, #GNUNET_SYSERR if the message is invalid (invalid range)
435 GNUNET_SECRETSHARING_encrypt(const struct GNUNET_SECRETSHARING_PublicKey *public_key,
436 const struct GNUNET_SECRETSHARING_Plaintext *plaintext,
437 struct GNUNET_SECRETSHARING_Ciphertext *result_ciphertext)
443 /* plaintext message */
448 ensure_elgamal_initialized();
450 GNUNET_assert(NULL != (h = gcry_mpi_new(0)));
451 GNUNET_assert(NULL != (y = gcry_mpi_new(0)));
452 GNUNET_assert(NULL != (tmp = gcry_mpi_new(0)));
454 GNUNET_CRYPTO_mpi_scan_unsigned(&h, public_key, sizeof *public_key);
455 GNUNET_CRYPTO_mpi_scan_unsigned(&m, plaintext, sizeof *plaintext);
457 // Randomize y such that 0 < y < elgamal_q.
458 // The '- 1' is necessary as bitlength(q) = bitlength(p) - 1.
461 gcry_mpi_randomize(y, GNUNET_SECRETSHARING_ELGAMAL_BITS - 1, GCRY_WEAK_RANDOM);
463 while ((gcry_mpi_cmp_ui(y, 0) == 0) || (gcry_mpi_cmp(y, elgamal_q) >= 0));
466 gcry_mpi_powm(tmp, elgamal_g, y, elgamal_p);
468 GNUNET_CRYPTO_mpi_print_unsigned(&result_ciphertext->c1_bits,
469 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
472 gcry_mpi_powm(tmp, h, y, elgamal_p);
474 gcry_mpi_mulm(tmp, tmp, m, elgamal_p);
476 GNUNET_CRYPTO_mpi_print_unsigned(&result_ciphertext->c2_bits,
477 GNUNET_SECRETSHARING_ELGAMAL_BITS / 8, tmp);
484 * Cancel a decryption.
486 * The decrypt_cb is not called anymore, but the calling
487 * peer may already have irrevocably contributed its share for the decryption of the value.
489 * @param dh to cancel
492 GNUNET_SECRETSHARING_decrypt_cancel(struct GNUNET_SECRETSHARING_DecryptionHandle *dh)
494 GNUNET_MQ_destroy(dh->mq);
499 /* end of secretsharing_api.c */