1 /*******************************************************************************
2   aiounicast_nonblock.cc, asynchronous unicast with nonblocking file descriptors
3 
4    This file is part of LibTMCG.
5 
6  Copyright (C) 2017, 2018  Heiko Stamer <HeikoStamer@gmx.net>
7 
8    LibTMCG is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12 
13    LibTMCG is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with LibTMCG; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *******************************************************************************/
22 
23 // include headers
24 #ifdef HAVE_CONFIG_H
25 	#include "libTMCG_config.h"
26 #endif
27 #include "aiounicast_nonblock.hh"
28 
29 // additional headers
30 #include <stdexcept>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <string.h>
35 #include "mpz_srandom.hh"
36 
aiounicast_nonblock(const size_t n_in,const size_t j_in,const std::vector<int> & fd_in_in,const std::vector<int> & fd_out_in,const std::vector<std::string> & key_in,const size_t aio_default_scheduler_in,const time_t aio_default_timeout_in,const bool aio_is_authenticated_in,const bool aio_is_encrypted_in)37 aiounicast_nonblock::aiounicast_nonblock
38 	(const size_t n_in,
39 	 const size_t j_in,
40 	 const std::vector<int> &fd_in_in,
41 	 const std::vector<int> &fd_out_in,
42 	 const std::vector<std::string> &key_in,
43 	 const size_t aio_default_scheduler_in,
44 	 const time_t aio_default_timeout_in,
45 	 const bool aio_is_authenticated_in,
46 	 const bool aio_is_encrypted_in):
47 		aiounicast(n_in, j_in, aio_default_scheduler_in, aio_default_timeout_in,
48 			aio_is_authenticated_in, aio_is_encrypted_in)
49 {
50 	if (j_in >= n_in)
51 		throw std::invalid_argument("aiounicast_nonblock: j >= n");
52 	if (fd_in_in.size() != n_in)
53 		throw std::invalid_argument("aiounicast_nonblock: |fd_in| != n");
54 	if (fd_in_in.size() != fd_out_in.size())
55 		throw std::invalid_argument("aiounicast_nonblock: |fd_in| != |fd_out|");
56 
57 	// initialize scheduler
58 	aio_schedule_current = 0, aio_schedule_buffer = 0;
59 
60 	// initialize buffers and check for O_NONBLOCK with fcntl(2)
61 	buf_in_size = TMCG_MAX_VALUE_CHARS;
62 	for (size_t i = 0; i < n_in; i++)
63 	{
64 		int flags_in = fcntl(fd_in_in[i], F_GETFL);
65 		int flags_out = fcntl(fd_out_in[i], F_GETFL);
66 		if ((flags_in == -1) || (flags_out == -1))
67 			perror("aiounicast_nonblock (fcntl)");
68 		if (((flags_in & O_NONBLOCK) != O_NONBLOCK) ||
69 			((flags_out & O_NONBLOCK) != O_NONBLOCK))
70 		{
71 			std::cerr << "aiounicast_nonblock: flag O_NONBLOCK is" <<
72 				" not set on fd to " << i << std::endl;
73 			throw std::invalid_argument("aiounicast_nonblock: no O_NONBLOCK");
74 		}
75 		fd_in[i] = fd_in_in[i];
76 		unsigned char *buf = new unsigned char[buf_in_size];
77 		buf_in.push_back(buf), buf_ptr.push_back(0);
78 		buf_flag.push_back(false);
79 		fd_out[i] = fd_out_in[i];
80 	}
81 
82 	// initialize ordered buffer for receiving mpz_t
83 	buf_mpz.resize(n);
84 
85 	// initialize MACs
86 	if (aio_is_authenticated)
87 	{
88 		maclen = gcry_mac_get_algo_maclen(TMCG_GCRY_MAC_ALGO);
89 		if (maclen == 0)
90 		{
91 			aio_is_initialized = false;
92 			std::cerr << "aiounicast_nonblock: gcry_mac_get_algo_maclen()" <<
93 				" failed" << std::endl;
94 			throw std::invalid_argument("aiounicast_nonblock: bad MAC algo");
95 		}
96 	}
97 	else
98 		maclen = 0;
99 	for (size_t i = 0; aio_is_authenticated && (i < n_in); i++)
100 	{
101 		unsigned char salt[maclen];
102 		unsigned char key[maclen];
103 		gcry_error_t err;
104 		gcry_mac_hd_t *mac_in_hd = new gcry_mac_hd_t();
105 		gcry_mac_hd_t *mac_out_hd = new gcry_mac_hd_t();
106 		mac_in.push_back(mac_in_hd), mac_out.push_back(mac_out_hd);
107 		err = gcry_mac_open(mac_in[i], TMCG_GCRY_MAC_ALGO, 0, NULL);
108 		if (err)
109 		{
110 			aio_is_initialized = false;
111 			std::cerr << "aiounicast_nonblock: gcry_mac_open() failed" <<
112 				std::endl << gcry_strerror(err) << std::endl;
113 			throw std::invalid_argument("aiounicast_nonblock: libgcrypt failed");
114 		}
115 		memset(salt, 0, sizeof(salt));
116 		err = gcry_kdf_derive(key_in[i].c_str(), key_in[i].length(),
117 			GCRY_KDF_PBKDF2, TMCG_GCRY_MD_ALGO, salt, sizeof(salt), 25000,
118 			sizeof(key), key);
119 		if (err)
120 		{
121 			aio_is_initialized = false;
122 			std::cerr << "aiounicast_nonblock: gcry_kdf_derive() failed" <<
123 				std::endl << gcry_strerror(err) << std::endl;
124 			throw std::invalid_argument("aiounicast_nonblock: libgcrypt failed");
125 		}
126 		err = gcry_mac_setkey(*mac_in[i], key, sizeof(key));
127 		if (err)
128 		{
129 			aio_is_initialized = false;
130 			std::cerr << "aiounicast_nonblock: gcry_mac_setkey() failed" <<
131 				std::endl << gcry_strerror(err) << std::endl;
132 			throw std::invalid_argument("aiounicast_nonblock: libgcrypt failed");
133 		}
134 		err = gcry_mac_open(mac_out[i], TMCG_GCRY_MAC_ALGO, 0, NULL);
135 		if (err)
136 		{
137 			aio_is_initialized = false;
138 			std::cerr << "aiounicast_nonblock: gcry_mac_open() failed" <<
139 				std::endl << gcry_strerror(err) << std::endl;
140 			throw std::invalid_argument("aiounicast_nonblock: libgcrypt failed");
141 		}
142 		err = gcry_mac_setkey(*mac_out[i], key, sizeof(key));
143 		if (err)
144 		{
145 			aio_is_initialized = false;
146 			std::cerr << "aiounicast_nonblock: gcry_mac_setkey() failed" <<
147 				std::endl << gcry_strerror(err) << std::endl;
148 			throw std::invalid_argument("aiounicast_nonblock: libgcrypt failed");
149 		}
150 	}
151 
152 	// initialize ciphers
153 	if (aio_is_encrypted)
154 	{
155 		keylen = gcry_cipher_get_algo_keylen(TMCG_GCRY_ENC_ALGO);
156 		if (keylen == 0)
157 		{
158 			aio_is_initialized = false;
159 			std::cerr << "aiounicast_nonblock: gcry_cipher_get_algo_keylen()" <<
160 				" failed" << std::endl;
161 			throw std::invalid_argument("aiounicast_nonblock: libgcrypt failed");
162 		}
163 		blklen = gcry_cipher_get_algo_blklen(TMCG_GCRY_ENC_ALGO);
164 		if (blklen == 0)
165 		{
166 			aio_is_initialized = false;
167 			std::cerr << "aiounicast_nonblock: gcry_cipher_get_algo_blklen()" <<
168 				" failed" << std::endl;
169 			throw std::invalid_argument("aiounicast_nonblock: libgcrypt failed");
170 		}
171 	}
172 	else
173 	{
174 		keylen = 0, blklen = 0;
175 	}
176 	for (size_t i = 0; aio_is_encrypted && (i < n_in); i++)
177 	{
178 		unsigned char salt[keylen];
179 		unsigned char key[keylen];
180 		unsigned char iv[blklen];
181 		gcry_error_t err;
182 		gcry_cipher_hd_t *enc_in_hd = new gcry_cipher_hd_t();
183 		gcry_cipher_hd_t *enc_out_hd = new gcry_cipher_hd_t();
184 		enc_in.push_back(enc_in_hd), enc_out.push_back(enc_out_hd);
185 		err = gcry_cipher_open(enc_in[i], TMCG_GCRY_ENC_ALGO,
186 			GCRY_CIPHER_MODE_CFB, 0);
187 		if (err)
188 		{
189 			aio_is_initialized = false;
190 			std::cerr << "aiounicast_nonblock: gcry_cipher_open() failed" <<
191 				std::endl << gcry_strerror(err) << std::endl;
192 			throw std::invalid_argument("aiounicast_nonblock: libgcrypt failed");
193 		}
194 		// use a different salt to derive encryption key
195 		memset(salt, 1, sizeof(salt));
196 		err = gcry_kdf_derive(key_in[i].c_str(), key_in[i].length(),
197 			GCRY_KDF_PBKDF2, TMCG_GCRY_MD_ALGO, salt, sizeof(salt), 25000,
198 			sizeof(key), key);
199 		if (err)
200 		{
201 			aio_is_initialized = false;
202 			std::cerr << "aiounicast_nonblock: gcry_kdf_derive() failed" <<
203 				std::endl << gcry_strerror(err) << std::endl;
204 			throw std::invalid_argument("aiounicast_nonblock: libgcrypt failed");
205 		}
206 		err = gcry_cipher_setkey(*enc_in[i], key, sizeof(key));
207 		if (err)
208 		{
209 			aio_is_initialized = false;
210 			std::cerr << "aiounicast_nonblock: gcry_cipher_setkey() failed" <<
211 				std::endl << gcry_strerror(err) << std::endl;
212 			throw std::invalid_argument("aiounicast_nonblock: libgcrypt failed");
213 		}
214 		iv_flag_in.push_back(false); // flag means: IV not yet received
215 		err = gcry_cipher_open(enc_out[i],
216 			TMCG_GCRY_ENC_ALGO, GCRY_CIPHER_MODE_CFB, 0);
217 		if (err)
218 		{
219 			aio_is_initialized = false;
220 			std::cerr << "aiounicast_nonblock: gcry_cipher_open() failed" <<
221 				std::endl << gcry_strerror(err) << std::endl;
222 			throw std::invalid_argument("aiounicast_nonblock: libgcrypt failed");
223 		}
224 		err = gcry_cipher_setkey(*enc_out[i], key, sizeof(key));
225 		if (err)
226 		{
227 			aio_is_initialized = false;
228 			std::cerr << "aiounicast_nonblock: gcry_cipher_setkey() failed" <<
229 				std::endl << gcry_strerror(err) << std::endl;
230 			throw std::invalid_argument("aiounicast_nonblock: libgcrypt failed");
231 		}
232 		gcry_create_nonce(iv, blklen); // unpredictable IV is sufficient
233 		err = gcry_cipher_setiv(*enc_out[i], iv, sizeof(iv));
234 		if (err)
235 		{
236 			aio_is_initialized = false;
237 			std::cerr << "aiounicast_nonblock: gcry_cipher_setiv() failed" <<
238 				std::endl << gcry_strerror(err) << std::endl;
239 			throw std::invalid_argument("aiounicast_nonblock: libgcrypt failed");
240 		}
241 		iv_flag_out.push_back(false); // flag means: IV not yet sent
242 		unsigned char *ivcopy = new unsigned char[blklen];
243 		memcpy(ivcopy, iv, blklen);
244 		iv_out.push_back(ivcopy); // store a copy of the used IV for Send()
245 	}
246 }
247 
Send(mpz_srcptr m,const size_t i_in,time_t timeout)248 bool aiounicast_nonblock::Send
249 	(mpz_srcptr m,
250 	 const size_t i_in,
251 	 time_t timeout)
252 {
253 	if (!aio_is_initialized)
254 		return false;
255 	if (timeout == aio_timeout_default)
256 		timeout = aio_default_timeout;
257 	// check whether output file descriptor exists
258 	if (!fd_out.count(i_in))
259 		return false;
260 	// prepare write buffer from the message m
261 	mpz_t tmp;
262 	mpz_init_set(tmp, m);
263 	if (aio_is_encrypted)
264 		mpz_add(tmp, tmp, aio_hide_length); // add $2^c$ to hide length
265 	size_t size = mpz_sizeinbase(tmp, TMCG_MPZ_IO_BASE);
266 	if ((size * 2) >= buf_in_size)
267 	{
268 		std::cerr << "aiounicast_nonblock: big integer too large" << std::endl;
269 		return false;
270 	}
271 	size_t bufsize = size + 2;
272 	char *buf = new char[bufsize];
273 	memset(buf, 0, bufsize);
274 	mpz_get_str(buf, TMCG_MPZ_IO_BASE, tmp);
275 	mpz_clear(tmp);
276 	// additionally, determine the real size of the corresponding string
277 	// because mpz_sizeinbase() sometimes does not work correctly
278 	size_t realsize = strnlen(buf, bufsize);
279 	if ((realsize > 0) && (realsize < bufsize))
280 	{
281 		buf[realsize] = '\n'; // set newline as delimiter
282 		realsize++;
283 	}
284 	else
285 	{
286 		std::cerr << "aiounicast_nonblock(" << j <<	"):" <<
287 			" realsize does not fit" << std::endl;
288 		delete [] buf;
289 		return false;
290 	}
291 	// We follow the Encrypt-then-Authenticate (EtA) paradigm, because it
292 	// provides the best security properties with respect to the required
293 	// 'secure channel'. Please note the scientific discussion on that topic,
294 	// e.g., Mihir Bellare and Chanathip Namprempre: 'Authenticated Encryption:
295 	//       Relations among notions and analysis of the generic composition
296 	//       paradigm', Advances in Cryptology - ASIACRYPT 2000, LNCS 1976,
297 	//       pp. 531--545, 2000.
298 	gcry_error_t err;
299 	// encrypt the content of write buffer (without delimiter) and send the
300 	// IV to the receiver
301 	if (aio_is_encrypted)
302 	{
303 		memmove(buf + 1, buf, realsize - 1);
304 		buf[0] = '+'; // use plus-character as a non-zero prefix
305 		err = gcry_cipher_encrypt(*enc_out[i_in], buf + 1, realsize - 1,
306 			NULL, 0);
307 		if (err)
308 		{
309 			std::cerr << "aiounicast_nonblock: gcry_cipher_encrypt() failed" <<
310 				std::endl << gcry_strerror(err) << std::endl;
311 			delete [] buf;
312 			return false;
313 		}
314 		numEncrypted += (realsize - 1);
315 		// convert encrypted content to mpz and adjust write buffer accordingly
316 		mpz_t encval;
317 		mpz_init(encval);
318 		mpz_import(encval, realsize, 1, 1, 1, 0, buf);
319 		delete [] buf;
320 		size = mpz_sizeinbase(encval, TMCG_MPZ_IO_BASE);
321 		bufsize = size + 2;
322 		buf = new char[bufsize];
323 		memset(buf, 0, bufsize); // clear write buffer
324 		mpz_get_str(buf, TMCG_MPZ_IO_BASE, encval);
325 		mpz_clear(encval);
326 		realsize = strnlen(buf, bufsize);
327 		if ((realsize > 0) && (realsize < bufsize))
328 		{
329 			buf[realsize] = '\n'; // set newline as delimiter
330 			realsize++;
331 		}
332 		else
333 		{
334 			std::cerr << "aiounicast_nonblock(" << j << "):" <<
335 				" realsize does not fit" << std::endl;
336 			delete [] buf;
337 			return false;
338 		}
339 		// first, send IV without encryption
340 		if (!iv_flag_out[i_in])
341 		{
342 			time_t entry_time = time(NULL);
343 			size_t realnum = 0;
344 			do
345 			{
346 				ssize_t num = write(fd_out[i_in], iv_out[i_in] + realnum,
347 					blklen - realnum);
348 				if (num < 0)
349 				{
350 					if ((errno == EAGAIN) || (errno == EWOULDBLOCK) ||
351 						(errno == EINTR))
352 					{
353 						if (errno == EAGAIN)
354 							perror("aiounicast_nonblock (write)");
355 						std::cerr << "sleeping ..." << std::endl;
356 						sleep(1);
357 						continue;
358 					}
359 					else
360 					{
361 						perror("aiounicast_nonblock (write)");
362 						delete [] buf;
363 						return false;
364 					}
365 				}
366 				numWrite += num;
367 				realnum += num;
368 			}
369 			while ((realnum < blklen) && (time(NULL) < (entry_time + timeout)));
370 			// timeout occurred?
371 			if (realnum < blklen)
372 			{
373 				std::cerr << "aiounicast_nonblock(" << j <<
374 					"): IV send timeout for " << i_in << std::endl;
375 				delete [] buf;
376 				return false;
377 			}
378 			else
379 				iv_flag_out[i_in] = true; // IV has been sent
380 		}
381 	}
382 	// calculate the MAC over all data including the line delimiter
383 	if (aio_is_authenticated)
384 	{
385 		err = gcry_mac_write(*mac_out[i_in], buf, realsize);
386 		if (err)
387 		{
388 			std::cerr << "aiounicast_nonblock: gcry_mac_write() failed" <<
389 				std::endl << gcry_strerror(err) << std::endl;
390 			delete [] buf;
391 			return false;
392 		}
393 	}
394 	// send content of write buffer to party i_in
395 	time_t entry_time = time(NULL);
396 	size_t realnum = 0;
397 	do
398 	{
399 		ssize_t num = write(fd_out[i_in], buf + realnum, realsize - realnum);
400 		if (num < 0)
401 		{
402 			if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR))
403 			{
404 				if (errno == EAGAIN)
405 					perror("aiounicast_nonblock (write)");
406 				std::cerr << "sleeping ..." << std::endl;
407 				sleep(1);
408 				continue;
409 			}
410 			else
411 			{
412 				perror("aiounicast_nonblock (write)");
413 				delete [] buf;
414 				return false;
415 			}
416 		}
417 		numWrite += num;
418 		realnum += num;
419 	}
420 	while ((realnum < realsize) && (time(NULL) < (entry_time + timeout)));
421 	delete [] buf;
422 	// timeout occurred?
423 	if (realnum < realsize)
424 	{
425 		std::cerr << "aiounicast_nonblock(" << j << "):" <<
426 			" send timeout for " << i_in << std::endl;
427 		return false;
428 	}
429 	if (aio_is_authenticated)
430 	{
431 		// get current MAC buffer and reset MAC
432 		size_t macbuflen = maclen;
433 		unsigned char *macbuf = new unsigned char[macbuflen];
434 		err = gcry_mac_read(*mac_out[i_in], macbuf, &macbuflen);
435 		if (err)
436 		{
437 			std::cerr << "aiounicast_nonblock: gcry_mac_read() failed" <<
438 				std::endl << gcry_strerror(err) << std::endl;
439 			delete [] macbuf;
440 			return false;
441 		}
442 		err = gcry_mac_reset(*mac_out[i_in]);
443 		if (err)
444 		{
445 			std::cerr << "aiounicast_nonblock: gcry_mac_reset() failed" <<
446 				std::endl << gcry_strerror(err) << std::endl;
447 			delete [] macbuf;
448 			return false;
449 		}
450 		// send content of MAC buffer (i.e. authentication tag)
451 		realnum = 0;
452 		do
453 		{
454 			ssize_t num = write(fd_out[i_in], macbuf + realnum,
455 				macbuflen - realnum);
456 			if (num < 0)
457 			{
458 				if ((errno == EAGAIN) || (errno == EWOULDBLOCK) ||
459 					(errno == EINTR))
460 				{
461 					if (errno == EAGAIN)
462 						perror("aiounicast_nonblock (write)");
463 					std::cerr << "sleeping ..." << std::endl;
464 					sleep(1);
465 					continue;
466 				}
467 				else
468 				{
469 					perror("aiounicast_nonblock (write)");
470 					delete [] macbuf;
471 					return false;
472 				}
473 			}
474 			numWrite += num;
475 			realnum += num;
476 		}
477 		while ((realnum < macbuflen) && (time(NULL) < (entry_time + timeout)));
478 		delete [] macbuf;
479 		// timeout occurred?
480 		if (realnum < macbuflen)
481 		{
482 			std::cerr << "aiounicast_nonblock(" << j <<	"):" <<
483 				" MAC send timeout for " << i_in << std::endl;
484 			return false;
485 		}
486 	}
487 	return true;
488 }
489 
Send(const std::vector<mpz_srcptr> & m,const size_t i_in,time_t timeout)490 bool aiounicast_nonblock::Send
491 	(const std::vector<mpz_srcptr> &m,
492 	 const size_t i_in,
493 	 time_t timeout)
494 {
495 	if (!aio_is_initialized)
496 		return false;
497 	if (timeout == aio_timeout_default)
498 		timeout = aio_default_timeout;
499 	for (size_t mm = 0; mm < m.size(); mm++)
500 	{
501 		if (!Send(m[mm], i_in, timeout))
502 			return false;
503 	}
504 	return true;
505 }
506 
Receive(mpz_ptr m,size_t & i_out,size_t scheduler,time_t timeout)507 bool aiounicast_nonblock::Receive
508 	(mpz_ptr m,
509 	 size_t &i_out,
510 	 size_t scheduler,
511 	 time_t timeout)
512 {
513 	if (!aio_is_initialized)
514 		return false;
515 	if (scheduler == aio_scheduler_default)
516 		scheduler = aio_default_scheduler;
517 	if (timeout == aio_timeout_default)
518 		timeout = aio_default_timeout;
519 	time_t entry_time = time(NULL);
520 	do
521 	{
522 		for (size_t round = 0; (round < n); round++)
523 		{
524 			// scheduler
525 			switch (scheduler)
526 			{
527 				case aio_scheduler_roundrobin:
528 					i_out = aio_schedule_current++;
529 					if (aio_schedule_current == n)
530 						aio_schedule_current = 0;
531 					break;
532 				case aio_scheduler_random:
533 					i_out = tmcg_mpz_wrandom_mod(n);
534 					break;
535 				case aio_scheduler_direct:
536 					if (i_out >= n)
537 						return false;
538 					break;
539 				default:
540 					i_out = n;
541 					return false;
542 			}
543 			// anything buffered from previous rounds?
544 			if (buf_flag[i_out])
545 			{
546 				// search for the first line delimiter
547 				bool newline_found = false;
548 				size_t newline_ptr = 0;
549 				for (size_t ptr = 0; ptr < buf_ptr[i_out]; ++ptr)
550 				{
551 					if (buf_in[i_out][ptr] == '\n')
552 					{
553 						newline_ptr = ptr;
554 						newline_found = true;
555 						break;
556 					}
557 				}
558 				// process the buffer
559 				if (newline_found &&
560 					((buf_ptr[i_out] - newline_ptr - 1) >= maclen))
561 				{
562 					// allocate at least one char
563 					size_t tmplen = newline_ptr + 1;
564 					char *tmp = new char[tmplen];
565 					// allocate at least one char, even if maclen == 0
566 					unsigned char *mac = new unsigned char[maclen + 1];
567 					memset(tmp, 0, tmplen);
568 					memset(mac, 0, maclen);
569 					if (newline_ptr > 0)
570 						memcpy(tmp, buf_in[i_out], newline_ptr);
571 					if (maclen > 0)
572 						memcpy(mac, buf_in[i_out] + tmplen, maclen);
573 					// adjust buffer (copy remaining characters)
574 					unsigned char *wptr = buf_in[i_out] + tmplen + maclen;
575 					size_t wnum = buf_ptr[i_out] - newline_ptr - 1 - maclen;
576 					if (wnum > 0)
577 						memmove(buf_in[i_out], wptr, wnum);
578 					else
579 						buf_flag[i_out] = false;
580 					buf_ptr[i_out] = wnum;
581 					// calculate, check, and reset MAC
582 					if (aio_is_authenticated)
583 					{
584 						gcry_error_t err;
585 						err = gcry_mac_write(*mac_in[i_out], tmp, newline_ptr);
586 						if (err)
587 						{
588 							std::cerr << "aiounicast_nonblock:" <<
589 								"gcry_mac_write() failed" << std::endl <<
590 								gcry_strerror(err) << std::endl;
591 							delete [] tmp, delete [] mac;
592 							return false;
593 						}
594 						numAuthenticated += newline_ptr;
595 						unsigned char delim = '\n'; // include line delimiter
596 						err = gcry_mac_write(*mac_in[i_out], &delim, 1);
597 						if (err)
598 						{
599 							std::cerr << "aiounicast_nonblock:" <<
600 								" gcry_mac_write() failed" << std::endl <<
601 								gcry_strerror(err) << std::endl;
602 							delete [] tmp, delete [] mac;
603 							return false;
604 						}
605 						numAuthenticated += 1;
606 						err = gcry_mac_verify(*mac_in[i_out], mac, maclen);
607 						if (err)
608 						{
609 							std::cerr << "aiounicast_nonblock:" <<
610 								" gcry_mac_verify() for " << j << " from " <<
611 								i_out << " failed" << std::endl <<
612 								gcry_strerror(err) << std::endl;
613 							delete [] tmp, delete [] mac;
614 							return false;
615 						}
616 						err = gcry_mac_reset(*mac_in[i_out]);
617 						if (err)
618 						{
619 							std::cerr << "aiounicast_nonblock:" <<
620 								" gcry_mac_reset() failed" << std::endl <<
621 								gcry_strerror(err) << std::endl;
622 							delete [] tmp, delete [] mac;
623 							return false;
624 						}
625 					}
626 					// convert and decrypt the corresponding part of read buffer
627 					if (aio_is_encrypted)
628 					{
629 						mpz_t encval;
630 						mpz_init(encval);
631 						if (mpz_set_str(encval, tmp, TMCG_MPZ_IO_BASE) < 0)
632 						{
633 							std::cerr << "aiounicast_nonblock: mpz_set_str()" <<
634 								" for encval failed" << std::endl;
635 							delete [] tmp, delete [] mac;
636 							return false;
637 						}
638 						size_t realsize = 0;
639 						memset(tmp, 0, tmplen); // clear tmp buffer
640 						mpz_export(tmp, &realsize, 1, 1, 1, 0, encval);
641 						if (realsize == 0)
642 						{
643 							std::cerr << "aiounicast_nonblock: mpz_export()" <<
644 								" failed for " << encval << std::endl;
645 							delete [] tmp, delete [] mac;
646 							return false;
647 						}
648 						mpz_clear(encval);
649 						if (tmp[0] != '+')
650 						{
651 							std::cerr << "aiounicast_nonblock: no prefix" <<
652 								" found" << std::endl;
653 							delete [] tmp, delete [] mac;
654 							return false;
655 						}
656 						gcry_error_t err;
657 						err = gcry_cipher_decrypt(*enc_in[i_out], tmp + 1,
658 							realsize - 1, NULL, 0);
659 						if (err)
660 						{
661 							std::cerr << "aiounicast_nonblock:" <<
662 								" gcry_cipher_decrypt() failed" << std::endl <<
663 								gcry_strerror(err) << std::endl;
664 							delete [] tmp, delete [] mac;
665 							return false;
666 						}
667 						numDecrypted += (realsize - 1);
668 						memmove(tmp, tmp + 1, realsize - 1); // remove prefix
669 						tmp[realsize-1] = 0x00; // append a c-string delimiter
670 					}
671 					// extract value of m
672 					if (mpz_set_str(m, tmp, TMCG_MPZ_IO_BASE) < 0)
673 					{
674 						std::cerr << "aiounicast_nonblock: mpz_set_str() for" <<
675 							" m from " << i_out << " failed" << std::endl;
676 						delete [] tmp, delete [] mac;
677 						return false;
678 					}
679 					delete [] tmp, delete [] mac;
680 					if (aio_is_encrypted)
681 						mpz_sub(m, m, aio_hide_length);
682 					return true;
683 				}
684 				// no delimiter found; invalidate buffer flag
685 				buf_flag[i_out] = false;
686 			}
687 			// check whether input file descriptor exists
688 			if (!fd_in.count(i_out))
689 				continue;
690 			// read(2) -- do everything with asynchronous I/O
691 			size_t maxbuf = buf_in_size - buf_ptr[i_out];
692 			if (maxbuf > 0)
693 			{
694 				unsigned char *rptr = buf_in[i_out] + buf_ptr[i_out];
695 				ssize_t num = read(fd_in[i_out], rptr, maxbuf);
696 				if (num < 0)
697 				{
698 					if ((errno == EAGAIN) ||
699 						(errno == EWOULDBLOCK) ||
700 						(errno == EINTR))
701 					{
702 						continue;
703 					}
704 					else
705 					{
706 						perror("aiounicast_nonblock (read)");
707 						return false;
708 					}
709 				}
710 				if (num == 0)
711 				{
712 					// got EOF
713 					std::cerr << "aiounicast_nonblock(" << j << "):" <<
714 						" got EOF for " << i_out << std::endl;
715 					fd_in.erase(i_out); // erase corresponding file descriptor
716 					fd_out.erase(i_out); // erase corresponding file descriptor
717 					continue;
718 				}
719 				buf_ptr[i_out] += num;
720 				numRead += num;
721 				if (aio_is_encrypted)
722 				{
723 					// take first blklen bytes from sender as IV for cipher
724 					if (!iv_flag_in[i_out] && (buf_ptr[i_out] >= blklen))
725 					{
726 						gcry_error_t err;
727 						err = gcry_cipher_setiv(*enc_in[i_out], buf_in[i_out],
728 							blklen);
729 						if (err)
730 						{
731 							aio_is_initialized = false;
732 							std::cerr << "aiounicast_nonblock:" <<
733 								" gcry_cipher_setiv() failed" << std::endl <<
734 								gcry_strerror(err) << std::endl;
735 						}
736 						iv_flag_in[i_out] = true; // IV is set
737 						num = buf_ptr[i_out] - blklen; // # of remaining bytes
738 						// remove IV from the read buffer
739 						memmove(buf_in[i_out], buf_in[i_out] + blklen, num);
740 						buf_ptr[i_out] = num;
741 					}
742 					if (iv_flag_in[i_out] && (num > 0))
743 						buf_flag[i_out] = true;
744 				}
745 				else if (num > 0)
746 					buf_flag[i_out] = true;
747 			}
748 			else
749 			{
750 				std::cerr << "WARNING: aiounicast_nonblock read buffer" <<
751 					" exceeded" << std::endl;
752 			}
753 		}
754 	}
755 	while (time(NULL) < (entry_time + timeout));
756 	if (scheduler != aio_scheduler_direct)
757 		i_out = n; // timeout for some (unknown) parties
758 	else
759 	{
760 		std::cerr << "aiounicast_nonblock(" << j << "):" <<
761 			" receive timeout for " << i_out << std::endl;
762 	}
763 	return false;
764 }
765 
Receive(std::vector<mpz_ptr> & m,size_t & i_out,size_t scheduler,time_t timeout)766 bool aiounicast_nonblock::Receive
767 	(std::vector<mpz_ptr> &m,
768 	 size_t &i_out,
769 	 size_t scheduler,
770 	 time_t timeout)
771 {
772 	if (!aio_is_initialized)
773 		return false;
774 	if (scheduler == aio_scheduler_default)
775 		scheduler = aio_default_scheduler;
776 	if (timeout == aio_timeout_default)
777 		timeout = aio_default_timeout;
778 	time_t entry_time = time(NULL);
779 	do
780 	{
781 		// scheduler
782 		switch (scheduler)
783 		{
784 			case aio_scheduler_roundrobin:
785 				i_out = aio_schedule_buffer++;
786 				if (aio_schedule_buffer == n)
787 					aio_schedule_buffer = 0;
788 				break;
789 			case aio_scheduler_random:
790 				i_out = tmcg_mpz_wrandom_mod(n);
791 				break;
792 			case aio_scheduler_direct:
793 				if (i_out >= n)
794 					return false;
795 				break;
796 			default:
797 				i_out = n;
798 				return false;
799 		}
800 		// return, if enough messages are received from i_out
801 		if (buf_mpz[i_out].size() >= m.size())
802 		{
803 			// copy results and release buffer
804 			for (size_t mm = 0; mm < m.size(); mm++)
805 			{
806 				mpz_set(m[mm], buf_mpz[i_out].front());
807 				mpz_clear(buf_mpz[i_out].front());
808 				delete [] buf_mpz[i_out].front();
809 				buf_mpz[i_out].pop_front();
810 			}
811 			return true;
812 		}
813 		// receive a message according to the given scheduler
814 		size_t i = n;
815 		if (scheduler == aio_scheduler_direct)
816 			i = i_out;
817 		mpz_ptr tmp = new mpz_t();
818 		mpz_init(tmp);
819 		if (Receive(tmp, i, scheduler, 0))
820 		{
821 			buf_mpz[i].push_back(tmp);
822 		}
823 		else
824 		{
825 			mpz_clear(tmp);
826 			delete [] tmp;
827 			// error at Receive()?
828 			if (i < n)
829 			{
830 				i_out = i;
831 				return false;
832 			}
833 		}
834 	}
835 	while (time(NULL) < (entry_time + timeout));
836 	i_out = n; // timeout for all parties
837 	return false;
838 }
839 
~aiounicast_nonblock()840 aiounicast_nonblock::~aiounicast_nonblock
841 	()
842 {
843 	fd_in.clear(), fd_out.clear();
844 	for (size_t i = 0; i < n; i++)
845 	{
846 		delete [] buf_in[i];
847 		while (buf_mpz[i].size())
848 		{
849 			mpz_clear(buf_mpz[i].front());
850 			delete [] buf_mpz[i].front();
851 			buf_mpz[i].pop_front();
852 		}
853 		buf_mpz[i].clear();
854 		// release MACs
855 		if (aio_is_authenticated)
856 		{
857 			gcry_mac_close(*mac_in[i]), gcry_mac_close(*mac_out[i]);
858 			delete mac_in[i], delete mac_out[i];
859 		}
860 		// release ciphers
861 		if (aio_is_encrypted)
862 		{
863 			gcry_cipher_close(*enc_in[i]), gcry_cipher_close(*enc_out[i]);
864 			delete enc_in[i], delete enc_out[i];
865 			delete [] iv_out[i];
866 		}
867 	}
868 	buf_in.clear(), buf_ptr.clear(), buf_flag.clear();
869 	buf_mpz.clear();
870 	iv_out.clear(), iv_flag_in.clear(), iv_flag_out.clear();
871 	mac_in.clear(), mac_out.clear();
872 	enc_in.clear(), enc_out.clear();
873 }
874 
875