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