1 
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <sys/socket.h>
7 #include <sys/stat.h>
8 
9 #include "mbedtls/platform.h"
10 #include "mbedtls/entropy.h"
11 #include "mbedtls/ctr_drbg.h"
12 #include "mbedtls/ecdsa.h"
13 #include "mbedtls/error.h"
14 #include "mbedtls/rsa.h"
15 #include "mbedtls/x509.h"
16 
17 #include "main.h"
18 #include "conf.h"
19 #include "log.h"
20 #include "utils.h"
21 #include "kad.h"
22 #include "net.h"
23 #include "announces.h"
24 #include "searches.h"
25 #include "ext-bob.h"
26 
27 
28 /*
29 * This is an experimental/naive authentication scheme. Hence called Bob.
30 * Random byte strings (called challenges) are send to all peers.
31 * The peer is expected to encrypt the challenge with the private key.
32 * If the challenge can be decrypted by the public key we have,
33 * we know that the peer has the private key. The ip address will then
34 * be used as result.
35 *
36 * Paket exchange:
37 * Lookup <public-key>.p2p
38 * 1. get IP addresses from DHT
39 * 2. send to every address "BOB" + PUBLICKEY + CHALLENGE
40 *    - remember IP address and challenge
41 * 3. get response "BOB" + SIGNED_CHALLENGE
42 *    - find challenge by sender IP address
43 * 4. verify signature by public key
44 */
45 
46 #define ECPARAMS MBEDTLS_ECP_DP_SECP256R1
47 #define ECPARAMS_NAME "secp256r1"
48 #define ECPARAMS_SIZE 32
49 #define MAX_AUTH_CHALLENGE_SEND 3
50 #define CHALLENGE_BIN_LENGTH 32
51 
52 
53 struct key_t {
54 	struct key_t *next;
55 	char *path; // File path the key was loaded from
56 	mbedtls_pk_context ctx_sign;
57 };
58 
59 struct bob_resource {
60 	mbedtls_pk_context ctx_verify;
61 	uint8_t challenge[32];
62 	uint8_t challenges_send;
63 	char query[QUERY_MAX_SIZE];
64 	IP addr;
65 };
66 
67 static int g_dht_socket = -1;
68 static struct key_t *g_keys = NULL;
69 static time_t g_send_challenges = 0;
70 static struct bob_resource g_bob_resources[8];
71 
72 static mbedtls_entropy_context g_entropy;
73 static mbedtls_ctr_drbg_context g_ctr_drbg;
74 
75 
76 // Decompress key since mbedtls does not have this feature.
mbedtls_ecp_decompress(const mbedtls_ecp_group * grp,const unsigned char * input,size_t ilen,unsigned char * output,size_t * olen,size_t osize)77 int mbedtls_ecp_decompress(
78 	const mbedtls_ecp_group *grp,
79 	const unsigned char *input, size_t ilen,
80 	unsigned char *output, size_t *olen, size_t osize)
81 {
82 	int ret;
83 	size_t plen;
84 	mbedtls_mpi r;
85 	mbedtls_mpi x;
86 	mbedtls_mpi n;
87 
88 	plen = mbedtls_mpi_size(&grp->P);
89 
90 	*olen = 2 * plen + 1;
91 
92 	if (osize < *olen)
93 		return(MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL);
94 
95 	if (ilen != plen + 1)
96 		return(MBEDTLS_ERR_ECP_BAD_INPUT_DATA);
97 
98 	if (input[0] != 0x02 && input[0] != 0x03)
99 		return(MBEDTLS_ERR_ECP_BAD_INPUT_DATA);
100 
101 	// output will consist of 0x04|X|Y
102 	memcpy(output, input, ilen);
103 	output[0] = 0x04;
104 
105 	mbedtls_mpi_init(&r);
106 	mbedtls_mpi_init(&x);
107 	mbedtls_mpi_init(&n);
108 
109 	// x <= input
110 	MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&x, input + 1, plen));
111 
112 	// r = x^2
113 	MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&r, &x, &x));
114 
115 	// r = x^2 + a
116 	if (grp->A.p == NULL) {
117 		// Special case where a is -3
118 		MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&r, &r, 3));
119 	} else {
120 		MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&r, &r, &grp->A));
121 	}
122 
123 	// r = x^3 + ax
124 	MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&r, &r, &x));
125 
126 	// r = x^3 + ax + b
127 	MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&r, &r, &grp->B));
128 
129 	// Calculate quare root of r over finite field P
130 	// r = sqrt(x^3 + ax + b) = (x^3 + ax + b) ^ ((P + 1) / 4) (mod P)
131 
132 	// n = P + 1
133 	MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&n, &grp->P, 1));
134 
135 	// n = (P + 1) / 4
136 	MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&n, 2));
137 
138 	// r ^ ((P + 1) / 4) (mod p)
139 	MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&r, &r, &n, &grp->P, NULL));
140 
141 	// Select solution that has the correct "sign" (equals odd/even solution in finite group)
142 	if ((input[0] == 0x03) != mbedtls_mpi_get_bit(&r, 0)) {
143 		// r = p - r
144 		MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&r, &grp->P, &r));
145 	}
146 
147 	// y => output
148 	ret = mbedtls_mpi_write_binary(&r, output + 1 + plen, plen);
149 
150 cleanup:
151 	mbedtls_mpi_free(&r);
152 	mbedtls_mpi_free(&x);
153 	mbedtls_mpi_free(&n);
154 
155 	return(ret);
156 }
157 
bob_auth_end(struct bob_resource * resource,int state)158 void bob_auth_end(struct bob_resource *resource, int state)
159 {
160 	// Set state of result
161 	searches_set_auth_state(&resource->query[0], &resource->addr, state);
162 
163 	// Mark resource as free
164 	resource->query[0] = '\0';
165 
166 	// Look for next job
167 	bob_trigger_auth();
168 }
169 
170 // Try to create a DHT id from a sanitzed key query
bob_get_id(uint8_t id[],size_t idlen,const char query[])171 int bob_get_id(uint8_t id[], size_t idlen, const char query[])
172 {
173 	size_t querylen = strlen(query);
174 	uint8_t bin[32];
175 
176 	if (EXIT_SUCCESS == bytes_from_base32hex(bin, sizeof(bin), query, querylen)
177 		|| EXIT_SUCCESS == bytes_from_base16hex(bin, sizeof(bin), query, querylen)) {
178 			memcpy(id, bin, idlen);
179 			return EXIT_SUCCESS;
180 	}
181 
182 	return EXIT_FAILURE;
183 }
184 
185 // Find a resource instance that is currently not in use
bob_next_resource(void)186 static struct bob_resource *bob_next_resource(void)
187 {
188 	int i;
189 
190 	for (i = 0; i < ARRAY_SIZE(g_bob_resources); i++) {
191 		if (g_bob_resources[i].query[0] == '\0') {
192 			return &g_bob_resources[i];
193 		}
194 	}
195 
196 	return NULL;
197 }
198 
bob_send_challenge(int sock,struct bob_resource * resource)199 static void bob_send_challenge(int sock, struct bob_resource *resource)
200 {
201 	uint8_t buf[3 + ECPARAMS_SIZE + CHALLENGE_BIN_LENGTH];
202 #ifdef DEBUG
203 	char hexbuf[108 + 1];
204 #endif
205 
206 	// Insert marker
207 	memcpy(buf, "BOB", 3);
208 
209 	// Append X value of public key
210 	mbedtls_mpi_write_binary(&mbedtls_pk_ec(resource->ctx_verify)->Q.X, buf + 3, ECPARAMS_SIZE);
211 
212 	// Append challenge bytes
213 	memcpy(buf + 3 + ECPARAMS_SIZE, resource->challenge, CHALLENGE_BIN_LENGTH);
214 
215 	resource->challenges_send += 1;
216 	log_debug("Send challenge to %s: %s (try %d)",
217 		str_addr(&resource->addr),
218 		bytes_to_base32hex(hexbuf, sizeof(hexbuf), buf, sizeof(buf)),
219 		resource->challenges_send
220 	);
221 
222 	sendto(sock, buf, sizeof(buf), 0, (struct sockaddr*) &resource->addr, sizeof(IP));
223 }
224 
225 // Start auth procedure for result bucket and utilize all resources
bob_trigger_auth(void)226 void bob_trigger_auth(void)
227 {
228 	uint8_t compressed[33]; // 0x02|X
229 	uint8_t decompressed[65]; // 0x04|X|Y
230 	size_t olen;
231 	struct bob_resource *resource;
232 	struct result_t *result;
233 	int ret;
234 
235 	resource = bob_next_resource();
236 	if (resource == NULL) {
237 		return;
238 	}
239 
240 	// Shortcuts
241 	mbedtls_ecp_keypair *kp = mbedtls_pk_ec(resource->ctx_verify);
242 	char *query = &resource->query[0];
243 
244 	// Find new query to authenticate and initialize resource
245 	if ((result = searches_get_auth_target(query, &resource->addr, &bob_trigger_auth)) != NULL) {
246 		result->state = AUTH_PROGRESS;
247 
248 		// Hex to binary and compressed form (assuming even Y => 0x02)
249 		compressed[0] = 0x02;
250 
251 		if (0 != bytes_from_base32hex(compressed + 1, sizeof(compressed) - 1, query, strlen(query))) {
252 			log_error("BOB: Unexpected query length: %s", query);
253 			bob_auth_end(resource, AUTH_ERROR);
254 			return;
255 		}
256 
257 		// Compressed form to decompressed
258 		if ((ret = mbedtls_ecp_decompress(
259 				&kp->grp, compressed, sizeof(compressed),
260 				decompressed, &olen, sizeof(decompressed))) != 0) {
261 			log_error("Error in mbedtls_ecp_decompress: %d\n", ret);
262 			bob_auth_end(resource, AUTH_ERROR);
263 			return;
264 		}
265 
266 		// Decompressed form to Q
267 		if ((ret = mbedtls_ecp_point_read_binary(
268 				&kp->grp, &kp->Q,
269 				decompressed, sizeof(decompressed))) != 0) {
270 			log_error("Error in mbedtls_ecp_point_read_binary: %d\n", ret);
271 			bob_auth_end(resource, AUTH_ERROR);
272 			return;
273 		}
274 
275 		resource->challenges_send = 0;
276 		bytes_random(resource->challenge, CHALLENGE_BIN_LENGTH);
277 		bob_send_challenge(g_dht_socket, resource);
278 	}
279 }
280 
write_pem(const mbedtls_pk_context * key,const char path[])281 static int write_pem(const mbedtls_pk_context *key, const char path[])
282 {
283 	FILE *file;
284 	uint8_t buf[1000];
285 	size_t len;
286 	int ret;
287 
288 	memset(buf, 0, sizeof(buf));
289 
290 	if ((ret = mbedtls_pk_write_key_pem((mbedtls_pk_context *) key, buf, sizeof(buf))) != 0) {
291 		return ret;
292 	}
293 
294 	if ((file = fopen(path, "r")) != NULL) {
295 		fclose(file);
296 		log_error("File already exists: %s", path);
297 		return -1;
298 	}
299 
300 	if ((file = fopen(path, "wb")) == NULL) {
301 		log_error("%s %s", path,  strerror(errno));
302 		return -1;
303 	}
304 
305 	// Set u+rw persmissions
306 	chmod(path, 0600);
307 
308 	len = strlen((char*) buf);
309 	if (fwrite(buf, 1, len, file) != len) {
310 		fclose(file);
311 		log_error("%s: %s", path, strerror(errno));
312 		return -1;
313 	}
314 
315 	fclose(file);
316 
317 	return 0;
318 }
319 
get_pkey_base32hex(const mbedtls_pk_context * ctx)320 static const char *get_pkey_base32hex(const mbedtls_pk_context *ctx)
321 {
322 	static char hexbuf[52 + 1];
323 	uint8_t buf[ECPARAMS_SIZE];
324 
325 	mbedtls_mpi_write_binary(&mbedtls_pk_ec(*ctx)->Q.X, buf, sizeof(buf));
326 
327 	return bytes_to_base32hex(hexbuf, sizeof(hexbuf), buf, sizeof(buf));
328 }
329 
330 // Generate a new key pair, write the secret key
331 // to path and print public key to stdout.
bob_create_key(const char path[])332 int bob_create_key(const char path[])
333 {
334 	mbedtls_entropy_context entropy;
335 	mbedtls_ctr_drbg_context ctr_drbg;
336 	mbedtls_pk_context ctx;
337 	const char *pers = MAIN_SRVNAME;
338 	int ret;
339 
340 	mbedtls_pk_init(&ctx);
341 	mbedtls_ctr_drbg_init(&ctr_drbg);
342 	mbedtls_entropy_init(&entropy);
343 
344 	//TODO: see gen_key.c example for better seed method
345 	if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
346 			(const unsigned char *) pers, strlen(pers))) != 0) {
347 		fprintf(stderr, "mbedtls_ctr_drbg_seed returned %d\n", ret);
348 		return EXIT_FAILURE;
349 	}
350 
351 	printf("Generating %s key pair...\n", ECPARAMS_NAME);
352 
353 	if ((ret = mbedtls_pk_setup(&ctx, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY))) != 0) {
354 		fprintf(stderr, "mbedtls_pk_setup returned -0x%04x\n", -ret);
355 		return EXIT_FAILURE;
356 	}
357 
358 	// Generate key where Y is even (called positive in a prime group)
359 	// This spares us from transmitting the sign along with the public key
360 	do {
361 		if ((ret = mbedtls_ecp_gen_key(ECPARAMS, mbedtls_pk_ec(ctx),
362 			mbedtls_ctr_drbg_random, &ctr_drbg)) != 0) {
363 			fprintf(stderr, "mbedtls_ecp_gen_key returned -0x%04x\n", -ret);
364 			return EXIT_FAILURE;
365 		}
366 	} while (mbedtls_mpi_get_bit(&mbedtls_pk_ec(ctx)->Q.Y, 0) != 0);
367 
368 	if (write_pem(&ctx, path) != 0) {
369 		return EXIT_FAILURE;
370 	}
371 
372 	printf("Public key: %s.%s\n", get_pkey_base32hex(&ctx), gconf->query_tld);
373 	printf("Wrote secret key to %s\n", path);
374 
375 	return EXIT_SUCCESS;
376 }
377 
378 // Add secret key
bob_load_key(const char path[])379 int bob_load_key(const char path[])
380 {
381 	mbedtls_pk_context ctx;
382 	char msg[300];
383 	int ret;
384 
385 	mbedtls_pk_init(&ctx);
386 
387 	if ((ret = mbedtls_pk_parse_keyfile(&ctx, path, NULL)) != 0) {
388 		mbedtls_pk_free(&ctx);
389 		mbedtls_strerror(ret, msg, sizeof(msg));
390 		log_error("Error loading %s: %s", path, msg);
391 		return EXIT_FAILURE;
392 	}
393 
394 	if (mbedtls_pk_ec(ctx)->grp.id != ECPARAMS) {
395 		log_error("Unsupported key type for %s: %s (expected %s)", path,
396 			mbedtls_ecp_curve_info_from_grp_id(mbedtls_pk_ec(ctx)->grp.id)->name,
397 			ECPARAMS_NAME
398 		);
399 		return EXIT_FAILURE;
400 	}
401 
402 	struct key_t *entry = (struct key_t*) calloc(1, sizeof(struct key_t));
403 	memcpy(&entry->ctx_sign, &ctx, sizeof(ctx));
404 	entry->path = strdup(path);
405 
406 	// Prepend to list
407 	if (g_keys) {
408 		entry->next = g_keys;
409 	}
410 	g_keys = entry;
411 
412 	log_info("Loaded %s (Public key: %s)", path, get_pkey_base32hex(&ctx));
413 
414 	return EXIT_SUCCESS;
415 }
416 
417 // Send challenges
bob_send_challenges(int sock)418 void bob_send_challenges(int sock)
419 {
420 	struct bob_resource *resource;
421 	int i;
422 
423 	// Send one packet per request
424 	for (i = 0; i < ARRAY_SIZE(g_bob_resources); ++i) {
425 		resource = &g_bob_resources[i];
426 		if (resource->query[0] == '\0') {
427 			continue;
428 		}
429 
430 		if (resource->challenges_send < MAX_AUTH_CHALLENGE_SEND) {
431 			bob_send_challenge(sock, resource);
432 		} else {
433 			log_debug("BOB: Number of challenges exhausted for query: %s\n", resource->query);
434 			bob_auth_end(resource, AUTH_ERROR);
435 		}
436 	}
437 }
438 
bob_find_resource(const IP * addr)439 struct bob_resource *bob_find_resource(const IP *addr)
440 {
441 	int i;
442 
443 	for (i = 0; i < ARRAY_SIZE(g_bob_resources); ++i) {
444 		if (addr_equal(&g_bob_resources[i].addr, addr)) {
445 			return &g_bob_resources[i];
446 		}
447 	}
448 
449 	return NULL;
450 }
451 
452 // Receive a solved challenge and verify it
bob_verify_challenge(int sock,uint8_t buf[],size_t buflen,IP * addr)453 void bob_verify_challenge(int sock, uint8_t buf[], size_t buflen, IP *addr)
454 {
455 	struct bob_resource *resource;
456 	int ret;
457 
458 	resource = bob_find_resource(addr);
459 
460 	if (resource) {
461 		ret = mbedtls_ecdsa_read_signature(mbedtls_pk_ec(resource->ctx_verify),
462 			resource->challenge, CHALLENGE_BIN_LENGTH, buf + 3, buflen - 3);
463 
464 		log_debug("BOB: Received response from %s does not verify: %s\n", str_addr(addr), resource->query);
465 		bob_auth_end(resource, ret ? AUTH_FAILED : AUTH_OK);
466 	} else {
467 		log_warning("BOB: No session found for address %s", str_addr(addr));
468 	}
469 }
470 
bob_find_key(const uint8_t pkey[])471 struct key_t *bob_find_key(const uint8_t pkey[])
472 {
473 	uint8_t epkey[ECPARAMS_SIZE];
474 	struct key_t *key = g_keys;
475 
476 	while (key) {
477 		mbedtls_mpi_write_binary(&mbedtls_pk_ec(key->ctx_sign)->Q.X, epkey, ECPARAMS_SIZE);
478 		if (memcmp(epkey, pkey, ECPARAMS_SIZE) == 0) {
479 			return key;
480 		}
481 		key = key->next;
482 	}
483 
484 	return key;
485 }
486 
487 // Receive a challenge and solve it using a secret key
bob_encrypt_challenge(int sock,uint8_t buf[],size_t buflen,IP * addr)488 void bob_encrypt_challenge(int sock, uint8_t buf[], size_t buflen, IP *addr)
489 {
490 	struct key_t *key;
491 	uint8_t sig[200];
492 #ifdef DEBUG
493 	char hexbuf[52 + 1];
494 #endif
495 	size_t slen;
496 	int ret;
497 
498 	uint8_t *pkey = buf + 3;
499 	uint8_t *challenge = buf + 3 + ECPARAMS_SIZE;
500 
501 	key = bob_find_key(pkey);
502 	if (key) {
503 		memcpy(sig, "BOB", 3);
504 		ret = mbedtls_ecdsa_write_signature(
505 			mbedtls_pk_ec(key->ctx_sign), MBEDTLS_MD_SHA256,
506 			challenge, CHALLENGE_BIN_LENGTH,
507 			sig + 3, &slen, mbedtls_ctr_drbg_random, &g_ctr_drbg);
508 		slen += 3;
509 
510 		if (ret != 0) {
511 			log_warning("mbedtls_ecdsa_write_signature returned %d\n", ret);
512 		} else {
513 			log_debug("Received challenge from %s and send back response", str_addr(addr));
514 			sendto(sock, sig, slen, 0, (struct sockaddr*) addr, sizeof(IP));
515 		}
516 	} else {
517 		log_debug("BOB: Secret key not found for public key: %s",
518 			bytes_to_base32hex (hexbuf, sizeof(hexbuf), pkey, ECPARAMS_SIZE)
519 		);
520 	}
521 }
522 
bob_handler(int fd,uint8_t buf[],uint32_t buflen,IP * from)523 int bob_handler(int fd, uint8_t buf[], uint32_t buflen, IP *from)
524 {
525 	time_t now;
526 
527 	// Hack to get the DHT socket..
528 	if (g_dht_socket == -1) {
529 		g_dht_socket = fd;
530 	}
531 
532 	if (buflen > 3 && memcmp(buf, "BOB", 3) == 0) {
533 		if (buflen == (3 + ECPARAMS_SIZE + CHALLENGE_BIN_LENGTH)) {
534 			// Answer a challenge request
535 			bob_encrypt_challenge(fd, buf, buflen, from);
536 		} else {
537 			// Handle reply to a challenge request
538 			bob_verify_challenge(fd, buf, buflen, from);
539 		}
540 		return 0;
541 	}
542 
543 	now = time_add_secs(0);
544 
545 	// Send out new challenges every second
546 	if (g_send_challenges < now) {
547 		g_send_challenges = now + 1;
548 		bob_send_challenges(fd);
549 	}
550 
551 	return 1;
552 }
553 
bob_debug_keys(FILE * fp)554 void bob_debug_keys(FILE *fp)
555 {
556 	struct key_t *key;
557 
558 	if (g_keys == NULL) {
559 		fprintf(fp, "No keys found.\n");
560 		return;
561 	}
562 
563 	key = g_keys;
564 	while (key) {
565 		fprintf(fp, "Public key: %s (%s)\n", get_pkey_base32hex(&key->ctx_sign), key->path);
566 		key = key->next;
567 	}
568 }
569 
bob_setup(void)570 int bob_setup(void)
571 {
572 	struct bob_resource *resource;
573 	struct key_t *key;
574 	const char *hkey;
575 	int i;
576 
577 	mbedtls_ctr_drbg_init(&g_ctr_drbg);
578 	mbedtls_entropy_init(&g_entropy);
579 
580 	// Initialize resources handlers ctx_verify value
581 	for (i = 0; i < ARRAY_SIZE(g_bob_resources); ++i) {
582 		resource = &g_bob_resources[i];
583 		mbedtls_pk_setup(&resource->ctx_verify, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
584 		mbedtls_ecp_group_load(&mbedtls_pk_ec(resource->ctx_verify)->grp, ECPARAMS);
585 	}
586 
587 	// Anounce keys via DHT
588 	key = g_keys;
589 	while (key) {
590 		// Start announcing public key for the entire runtime
591 		hkey = get_pkey_base32hex(&key->ctx_sign);
592 		announces_add(hkey, gconf->dht_port, LONG_MAX);
593 		key = key->next;
594 	}
595 
596 	return EXIT_SUCCESS;
597 }
598 
bob_free(void)599 void bob_free(void)
600 {
601 	struct key_t *key;
602 	struct key_t *next;
603 
604 	mbedtls_ctr_drbg_free(&g_ctr_drbg);
605 	mbedtls_entropy_free(&g_entropy);
606 
607 	key = g_keys;
608 	while (key) {
609 		next = key->next;
610 		// Also zeroes the private key in memory
611 		mbedtls_pk_free(&key->ctx_sign);
612 		free(key->path);
613 		free(key);
614 		key = next;
615 	}
616 	g_keys = NULL;
617 }
618