GNUnet 0.28.1-dev.4-8-g14b9efcb0
 
Loading...
Searching...
No Matches
gnunet-service-core_kx.c
Go to the documentation of this file.
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2013, 2016, 2024-2026 GNUnet e.V.
4
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.
9
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.
14
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/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
37#include "platform.h"
38#include "gnunet_common.h"
39#include "gnunet_util_lib.h"
43#include "gnunet-service-core.h"
44#include "gnunet_constants.h"
45#include "gnunet_protocols.h"
46#include "gnunet_pils_service.h"
47
51#define DEBUG_KX 0
52
58#define DECRYPTION_FAILURES_LOG_LEVEL GNUNET_ERROR_TYPE_DEBUG
59
72#define RESEND_MAX_TRIES 5
73
77#define AEAD_KEY_BYTES crypto_aead_xchacha20poly1305_ietf_KEYBYTES
78
82#define AEAD_NONCE_BYTES crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
83
87#define AEAD_TAG_BYTES crypto_aead_xchacha20poly1305_ietf_ABYTES
88
94#define RESEND_TIMEOUT \
95 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
96
100#define RESEND_TIMEOUT_MAX \
101 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
102
109#define REPLAY_WINDOW_SIZE 64
110
118#define HEARTBEAT_PROBE_FREQUENCY \
119 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
120
126#define MAX_UNANSWERED_HEARTBEATS 3
127
131#define MIN_HEARTBEAT_FREQUENCY \
132 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
133
137#define HEARTBEAT_FREQUENCY \
138 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
139
146#define MAX_EPOCHS 10
147
151#define EPOCH_EXPIRATION \
152 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
153
157#define REKEY_TOLERANCE \
158 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
159
164#define EARLY_DATA_STR "early data"
165
170#define R_HS_TRAFFIC_STR "r hs traffic"
171
176#define I_HS_TRAFFIC_STR "i hs traffic"
177
182#define R_AP_TRAFFIC_STR "r ap traffic"
183
188#define I_AP_TRAFFIC_STR "i ap traffic"
189
194#define DERIVED_STR "derived"
195
200#define R_FINISHED_STR "r finished"
201
206#define I_FINISHED_STR "i finished"
207
211#define CAKE_LABEL "cake10"
212
217#define KEY_STR "key"
218
223#define TRAFFIC_UPD_STR "traffic upd"
224
229#define IV_STR "iv"
230
231
236{
237 /* Peer is supposed to initiate the key exchange */
239
240 /* Peer is supposed to wait for the key exchange */
242};
243
244
249{
254
259
264
269
274
279
280 // TODO check ordering - might make it less confusing
281 // TODO consistent naming: ss_e, shared_secret_e or ephemeral_shared_secret?
282 // TODO consider making all the structs here pointers
283 // - they can be checked to be NULL
284 // - valgrind can detect memory issues better (I guess?)
285
291
292 // TODO
296
301
306
315
322
329
335
340 struct GNUNET_ShortHashCode early_traffic_secret; /* Decrypts InitiatorHello */
341
347
353
359
365
370
375
380
385
391
398
405
409 uint64_t current_sqn;
410
415
420
425
432
436 unsigned int resend_tries_left;
437
443
450
456
471
476
482
483};
484
489
494
499
505
510
514static char *my_services_info = "";
515
516static void
517buffer_clear (void *buf, size_t len)
518{
519#if HAVE_MEMSET_S
520 memset_s (buf, len, 0, len);
521#elif HAVE_EXPLICIT_BZERO
522 explicit_bzero (buf, len);
523#else
524 volatile unsigned char *p = buf;
525 while (len--)
526 *p++ = 0;
527#endif
528}
529
530
531static void
533{
534 buffer_clear (&kx->ihts,
535 sizeof kx->ihts);
536 buffer_clear (&kx->rhts,
537 sizeof kx->rhts);
538 buffer_clear (&kx->sk_e,
539 sizeof kx->sk_e);
540 buffer_clear (&kx->ss_I,
541 sizeof kx->ss_I);
542 buffer_clear (&kx->ss_R,
543 sizeof kx->ss_R);
544 buffer_clear (&kx->ss_e,
545 sizeof kx->ss_e);
547 sizeof kx->master_secret);
549 sizeof kx->early_secret_key);
551 sizeof kx->early_traffic_secret);
553 sizeof kx->handshake_secret);
554}
555
556
566static void
568 uint64_t epoch)
569{
570 kx->replay_max[epoch % MAX_EPOCHS] = 0;
571 kx->replay_bitmap[epoch % MAX_EPOCHS] = 0;
572}
573
574
580static void
582{
583 memset (kx->replay_max, 0, sizeof kx->replay_max);
584 memset (kx->replay_bitmap, 0, sizeof kx->replay_bitmap);
585}
586
587
603 uint64_t epoch,
604 uint64_t sqn)
605{
606 unsigned int idx = epoch % MAX_EPOCHS;
607 uint64_t max = kx->replay_max[idx];
608 uint64_t behind;
609
610 if (sqn > max)
611 return GNUNET_OK; /* to the right of the window */
612 behind = max - sqn;
613 if (behind >= REPLAY_WINDOW_SIZE)
614 return GNUNET_SYSERR; /* too old to tell, so assume replay */
615 if (0 != (kx->replay_bitmap[idx] & (1ULL << behind)))
616 return GNUNET_SYSERR; /* seen before */
617 return GNUNET_OK;
618}
619
620
629static void
631 uint64_t epoch,
632 uint64_t sqn)
633{
634 unsigned int idx = epoch % MAX_EPOCHS;
635 uint64_t max = kx->replay_max[idx];
636 uint64_t shift;
637
638 if (sqn > max)
639 {
640 shift = sqn - max;
641 kx->replay_bitmap[idx] = (shift >= REPLAY_WINDOW_SIZE)
642 ? 0
643 : (kx->replay_bitmap[idx] << shift);
644 kx->replay_bitmap[idx] |= 1ULL;
645 kx->replay_max[idx] = sqn;
646 return;
647 }
648 kx->replay_bitmap[idx] |= (1ULL << (max - sqn));
649}
650
651
652static void
654 struct GNUNET_HashCode *snapshot)
655{
656 struct GNUNET_HashContext *tmp;
657
658 tmp = GNUNET_CRYPTO_hash_context_copy (ts_hash);
659 GNUNET_CRYPTO_hash_context_finish (tmp, snapshot);
660}
661
662
668static void
670{
672
674 msg.header.size = htons (sizeof(msg));
675 msg.state = htonl ((uint32_t) kx->status);
676 msg.peer = kx->peer;
677 msg.timeout = GNUNET_TIME_absolute_hton (kx->timeout);
680}
681
682
683static void
685
693static void
694send_heartbeat (void *cls)
695{
696 struct GSC_KeyExchangeInfo *kx = cls;
697 struct GNUNET_TIME_Relative retry;
698 struct GNUNET_TIME_Relative left;
699 struct Heartbeat hb;
700
701 kx->heartbeat_task = NULL;
703 /* A heartbeat is a probe, not a formality: #handle_heartbeat() answers
704 every one of them with an Ack, and that Ack is a record whose
705 deprotection runs #update_timeout() and clears the counter below. So
706 #MAX_UNANSWERED_HEARTBEATS of them in a row without a single record
707 coming back means the association is gone, whatever @e timeout still
708 says. Waiting for @e timeout regardless is what made a lost session
709 cost #GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT (5 min) to notice -- and
710 up to twice that to repair, because the peer that gives up first tears
711 its session down silently and the other one then has to run its own
712 full idle timeout before #restart_kx() gets a chance to re-run the
713 exchange over the virtual link that was there the whole time. */
714 if ((0 == left.rel_value_us) ||
716 {
718 "Session with `%s' timed out (%u heartbeats unanswered)\n",
719 GNUNET_i2s (&kx->peer),
722 gettext_noop ("# sessions terminated by timeout"),
723 1,
724 GNUNET_NO);
725 GSC_SESSIONS_end (&kx->peer);
728 restart_kx (kx);
729 return;
730 }
732 "Sending HEARTBEAT to `%s'\n",
733 GNUNET_i2s (&kx->peer));
735 gettext_noop ("# heartbeat messages sent"),
736 1,
737 GNUNET_NO);
739 hb.header.size = htons (sizeof hb);
740 // FIXME when do we request update?
741 hb.flags = 0;
743 GSC_KX_encrypt_and_transmit (kx, &hb, sizeof hb);
744 if (GNUNET_YES != kx->association_up)
745 return; /* #check_rekey() tore it down and restarted the exchange */
746 /* Do not let @e timeout stretch the probe interval: the point of the
747 counter above is that the answer, not the clock, decides. */
750 left),
752 kx->heartbeat_task =
754}
755
756
764static void
766{
768
769 kx->timeout =
771 delta =
773 if (delta.rel_value_us > 5LL * 1000LL * 1000LL)
774 {
775 /* we only notify monitors about timeout changes if those
776 are bigger than the threshold (5s) */
778 }
779 /* The peer answered, so nothing is outstanding any more. */
780 kx->heartbeats_unanswered = 0;
781 if (NULL != kx->heartbeat_task)
783 /* Probe again #HEARTBEAT_PROBE_FREQUENCY after the last thing we heard,
784 not halfway to @e timeout: an idle association that is fine costs one
785 heartbeat and one Ack per interval, while one that is not is noticed
786 within #MAX_UNANSWERED_HEARTBEATS intervals instead of after the full
787 #GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT. A link carrying traffic
788 re-arms this on every record and so still never sends one. */
791 kx);
792}
793
794
800static void
802
803
815static int
816deliver_message (void *cls, const struct GNUNET_MessageHeader *m)
817{
818 struct GSC_KeyExchangeInfo *kx = cls;
819
821 "Decrypted message of type %d from %s\n",
822 ntohs (m->type),
823 GNUNET_i2s (&kx->peer));
825 m,
826 ntohs (m->size),
829 m,
830 sizeof(struct GNUNET_MessageHeader),
832 return GNUNET_OK;
833}
834
835
847static void
858
859
867static void
875
876
885static void
887{
888 /* Any handshake message we were still resending belongs to the exchange
889 we are abandoning here. #send_initiator_hello() /
890 #send_responder_hello() overwrite @e resend_env and @e resend_task
891 without clearing them first, so without this the old envelope leaks
892 and -- worse -- the old task keeps running with its handle lost: a
893 second resend chain that no GNUNET_SCHEDULER_cancel() can reach, still
894 firing on @a kx after #handle_transport_notify_disconnect() has freed
895 it. */
896 if (NULL != kx->resend_task)
897 {
899 kx->resend_task = NULL;
900 }
901 if (NULL != kx->resend_env)
902 {
904 kx->resend_env = NULL;
905 }
906 if (NULL != kx->transcript_hash_ctx)
907 {
909 kx->transcript_hash_ctx = NULL;
910 }
911 /* There is no flight to recognise a retransmission of any more. */
912 memset (&kx->ih_hash, 0, sizeof (kx->ih_hash));
913 memset (&kx->rh_hash, 0, sizeof (kx->rh_hash));
915}
916
917
927static void
929{
930 reset_handshake (kx);
931 GSC_SESSIONS_end (&kx->peer);
933 /* An armed heartbeat task belongs to the association we are dropping
934 here. Left behind it keeps encrypting heartbeats with key material
935 that is no longer current, and #handle_initiator_done() would find it
936 still set. */
937 if (NULL != kx->heartbeat_task)
938 {
940 kx->heartbeat_task = NULL;
941 }
942 kx->heartbeats_unanswered = 0;
943 /* A new association starts over at epoch 0 and sequence number 0.
944 Carrying @e their_max_epoch of a long-lived predecessor into it makes
945 #handle_encrypted_message() reject the first records of the new one as
946 "too old", and a stale anti-replay window would reject them as
947 replays. */
948 kx->their_max_epoch = 0;
949 kx->current_epoch = 0;
950 kx->current_sqn = 0;
951 replay_reset_all (kx);
953}
954
955
956static void
958{
959 const struct GNUNET_HashCode *my_identity_hash;
960 struct GNUNET_HashCode h1;
961
962 // TODO what happens if we're in the middle of a peer id change?
963 // TODO there's a small chance this gets already called when we don't have a
964 // peer id yet. Add a kx, insert into the list, mark it as to be completed
965 // and let the callback to pils finish the rest once we got the peer id
966
968 "Initiating key exchange with peer %s\n",
969 GNUNET_i2s (&kx->peer));
971 gettext_noop ("# key exchanges initiated"),
972 1,
973 GNUNET_NO);
974
975 /* Whatever we still had -- an exchange in progress, an established
976 session, an armed heartbeat -- does not survive this. Drop it before
977 telling monitors where we are, so that they do not see the state of the
978 exchange we are leaving reported as if it were still current: that is
979 why a restart from #GNUNET_CORE_KX_STATE_INITIATOR_HELLO_SENT used to
980 show up as two consecutive "Hello sent (I)" notifications. */
981 abandon_exchange (kx);
983 my_identity_hash = GNUNET_PILS_get_identity_hash (GSC_pils);
984 GNUNET_assert (NULL != my_identity_hash);
985 GNUNET_CRYPTO_hash (&kx->peer, sizeof(struct GNUNET_PeerIdentity), &h1);
986 if (0 < GNUNET_CRYPTO_hash_cmp (&h1, my_identity_hash))
987 {
988 /* peer with "lower" identity starts KX, otherwise we typically end up
989 with both peers starting the exchange and transmit the 'set key'
990 message twice */
992 "I am the initiator, sending hello\n");
993 kx->role = ROLE_INITIATOR;
995 }
996 else
997 {
998 /* peer with "higher" identity starts a delayed KX, if the "lower" peer
999 * does not start a KX since it sees no reasons to do so */
1001 "I am the responder, yielding and await initiator hello\n");
1003 kx->role = ROLE_RESPONDER;
1004 monitor_notify_all (kx);
1005 }
1006}
1007
1008
1019static void *
1021 const struct GNUNET_PeerIdentity *peer_id,
1022 struct GNUNET_MQ_Handle *mq)
1023{
1024 const struct GNUNET_PeerIdentity *my_identity;
1025 struct GSC_KeyExchangeInfo *kx;
1026 (void) cls;
1028 GNUNET_assert (NULL != my_identity);
1029 if (0 == memcmp (peer_id, my_identity, sizeof *peer_id))
1030 {
1032 "Ignoring connection to self\n");
1033 return NULL;
1034 }
1036 "Incoming connection of peer with %s\n",
1038
1039 /* Set up kx struct */
1040 kx = GNUNET_new (struct GSC_KeyExchangeInfo);
1042 kx->mq = mq;
1043 GNUNET_memcpy (&kx->peer, peer_id, sizeof (struct GNUNET_PeerIdentity));
1045
1046 restart_kx (kx);
1047 return kx;
1048}
1049
1050
1090// TODO find a way to assert that a key is not yet existing before generating
1091// TODO find a way to assert that a key is not already existing before using
1092/*
1093 * Derive early secret and transport secret.
1094 * @param kx the key exchange info
1095 */
1096static void
1097derive_es_ets (const struct GNUNET_HashCode *transcript,
1098 const struct GNUNET_ShortHashCode *ss_R,
1099 struct GNUNET_ShortHashCode *es,
1100 struct GNUNET_ShortHashCode *ets)
1101{
1102 uint64_t ret;
1103
1104 ret = GNUNET_CRYPTO_hkdf_extract (es, // prk
1105 0, // salt
1106 0, // salt_len
1107 ss_R, // ikm - initial key material
1108 sizeof (*ss_R));
1109 if (GNUNET_OK != ret)
1110 {
1111 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong extracting ES\n")
1112 ;
1113 GNUNET_assert (0);
1114 }
1116 ets,
1117 sizeof (*ets),
1118 es,
1121 GNUNET_CRYPTO_kdf_arg_auto (transcript));
1122 if (GNUNET_OK != ret)
1123 {
1124 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong expanding ETS\n")
1125 ;
1126 GNUNET_assert (0);
1127 }
1128}
1129
1130
1131/*
1132 * Derive early secret and transport secret.
1133 * @param kx the key exchange info
1134 */
1135static void
1136derive_sn (const struct GNUNET_ShortHashCode *secret,
1137 unsigned char*sn,
1138 size_t sn_len)
1139{
1142 sn,
1143 sn_len,
1144 secret,
1147}
1148
1149
1154static void
1156 const struct GNUNET_ShortHashCode *ss_e,
1158{
1159 uint64_t ret;
1160 struct GNUNET_ShortHashCode derived_early_secret;
1161
1162 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deriving HS\n");
1164 );
1165 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ss_e: %s\n", GNUNET_B2S (ss_e));
1167 &derived_early_secret,
1168 sizeof (derived_early_secret),
1169 es,
1173 derived_early_secret));
1174 if (GNUNET_OK != ret)
1175 {
1176 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong expanding dES\n")
1177 ;
1178 GNUNET_assert (0);
1179 }
1180 // Handshake secret
1181 // TODO check: are dES the salt and ss_e the ikm or other way round?
1182 ret = GNUNET_CRYPTO_hkdf_extract (handshake_secret, // prk
1183 &derived_early_secret, // salt - dES
1184 sizeof (derived_early_secret), // salt_len
1185 ss_e, // ikm - initial key material
1186 sizeof (*ss_e));
1187 if (GNUNET_OK != ret)
1188 {
1189 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong extracting HS\n")
1190 ;
1191 GNUNET_assert (0);
1192 }
1193}
1194
1195
1200static void
1201derive_ihts (const struct GNUNET_HashCode *transcript,
1202 const struct GNUNET_ShortHashCode *hs,
1203 struct GNUNET_ShortHashCode *ihts)
1204{
1207 ihts, // result
1208 sizeof (*ihts), // result len
1209 hs, // prk?
1212 GNUNET_CRYPTO_kdf_arg_auto (transcript)));
1213}
1214
1215
1220static void
1221derive_rhts (const struct GNUNET_HashCode *transcript,
1222 const struct GNUNET_ShortHashCode *hs,
1223 struct GNUNET_ShortHashCode *rhts)
1224{
1227 rhts,
1228 sizeof (*rhts),
1229 hs, // prk? TODO
1232 GNUNET_CRYPTO_kdf_arg_auto (transcript)));
1233}
1234
1235
1240static void
1242 const struct GNUNET_ShortHashCode *ss_I,
1243 struct GNUNET_ShortHashCode *ms)
1244{
1245 uint64_t ret;
1246 struct GNUNET_ShortHashCode derived_handshake_secret;
1247
1249 &derived_handshake_secret,
1250 sizeof (derived_handshake_secret),
1251 hs,
1254 if (GNUNET_OK != ret)
1255 {
1256 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong expanding dHS\n")
1257 ;
1258 GNUNET_assert (0);
1259 }
1260 // TODO check: are dHS the salt and ss_I the ikm or other way round?
1261 ret = GNUNET_CRYPTO_hkdf_extract (ms, // prk
1262 &derived_handshake_secret, // salt - dHS
1263 sizeof (derived_handshake_secret), // salt_len
1264 ss_I, // ikm - initial key material
1265 sizeof (*ss_I));
1266 if (GNUNET_OK != ret)
1267 {
1268 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong extracting MS\n")
1269 ;
1270 GNUNET_assert (0);
1271 }
1272}
1273
1274
1280static void
1282 uint64_t seq,
1283 const uint8_t write_iv[AEAD_NONCE_BYTES],
1284 uint8_t per_record_write_iv[AEAD_NONCE_BYTES])
1285{
1286 uint64_t seq_nbo;
1287 uint64_t *write_iv_ptr;
1288 unsigned int byte_offset;
1289
1290 seq_nbo = GNUNET_htonll (seq);
1291 memcpy (per_record_write_iv,
1292 write_iv,
1294 byte_offset =
1295 AEAD_NONCE_BYTES - sizeof (uint64_t);
1296 write_iv_ptr = (uint64_t*) (per_record_write_iv + byte_offset);
1297 *write_iv_ptr ^= seq_nbo;
1298}
1299
1300
1305static void
1307 const struct GNUNET_ShortHashCode *ts,
1308 uint64_t seq,
1309 unsigned char key[AEAD_KEY_BYTES],
1310 unsigned char nonce[AEAD_NONCE_BYTES])
1311{
1312 unsigned char nonce_tmp[AEAD_NONCE_BYTES];
1313 /* derive actual key */
1316 key,
1318 ts,
1321
1322 /* derive nonce */
1325 nonce_tmp,
1327 ts,
1331 nonce_tmp,
1332 nonce);
1333}
1334
1335
1340static void
1342 struct GNUNET_ShortHashCode *new_ats)
1343{
1344 int8_t ret;
1345
1346 // FIXME: Not sure of PRK and output may overlap here!
1348 new_ats,
1349 sizeof (*new_ats),
1350 old_ats,
1353 if (GNUNET_OK != ret)
1354 {
1356 "Something went wrong deriving next *ATS key\n");
1357 GNUNET_assert (0);
1358 }
1359}
1360
1361
1366static void
1367derive_initial_ats (const struct GNUNET_HashCode *transcript,
1368 const struct GNUNET_ShortHashCode *ms,
1369 enum GSC_KX_Role role,
1370 struct GNUNET_ShortHashCode *initial_ats)
1371{
1372 const char *traffic_str;
1373
1374 if (ROLE_INITIATOR == role)
1375 traffic_str = I_AP_TRAFFIC_STR;
1376 else
1377 traffic_str = R_AP_TRAFFIC_STR;
1380 initial_ats, // result
1381 sizeof (*initial_ats), // result len
1382 ms,
1384 GNUNET_CRYPTO_kdf_arg_string (traffic_str),
1385 GNUNET_CRYPTO_kdf_arg_auto (transcript)));
1386}
1387
1388
1395static void
1397 const struct GNUNET_ShortHashCode *ms,
1398 struct GNUNET_HashCode *result)
1399{
1401 struct GNUNET_CRYPTO_AuthKey fk_R; // We might want to save this in kx?
1402
1404 &fk_R, // result
1405 sizeof (fk_R),
1406 ms,
1409 if (GNUNET_OK != ret)
1410 {
1412 "Something went wrong expanding fk_R\n");
1413 GNUNET_assert (0);
1414 }
1415
1416 GNUNET_CRYPTO_hmac (&fk_R,
1417 transcript,
1418 sizeof (*transcript),
1419 result);
1420}
1421
1422
1429static void
1431 const struct GNUNET_ShortHashCode *ms,
1432 struct GNUNET_HashCode *result)
1433{
1435 struct GNUNET_CRYPTO_AuthKey fk_I; // We might want to save this in kx?
1436
1438 &fk_I, // result
1439 sizeof (fk_I),
1440 ms,
1443 if (GNUNET_OK != ret)
1444 {
1446 "Something went wrong expanding fk_I\n");
1447 GNUNET_assert (0);
1448 }
1449 GNUNET_CRYPTO_hmac (&fk_I,
1450 transcript,
1451 sizeof (*transcript),
1452 result);
1453}
1454
1455
1456static void
1458{
1459 struct GSC_KeyExchangeInfo *kx = cls;
1460
1461 kx->resend_task = NULL;
1462 if (0 == kx->resend_tries_left)
1463 {
1465 "Restarting KX\n");
1466 restart_kx (kx);
1467 return;
1468 }
1469 kx->resend_tries_left--;
1471 "Resending responder hello. Retries left: %u\n",
1472 kx->resend_tries_left);
1475}
1476
1477
1478void
1480{
1483 struct ResponderHello *rhm_e; /* responder hello message - encrypted pointer */
1484 struct GNUNET_MQ_Envelope *env;
1485 struct GNUNET_CRYPTO_HpkeEncapsulation ephemeral_kem_challenge;
1486 struct GNUNET_ShortHashCode rhts;
1487 struct GNUNET_ShortHashCode ihts;
1488 struct GNUNET_ShortHashCode hs;
1489 struct GNUNET_ShortHashCode ms;
1490 struct GNUNET_ShortHashCode ss_e;
1491 struct GNUNET_ShortHashCode ss_I;
1492 struct GNUNET_HashContext *hc;
1493 unsigned char enc_key[AEAD_KEY_BYTES];
1494 unsigned char enc_nonce[AEAD_NONCE_BYTES];
1495
1496 // 4. encaps -> shared_secret_e, c_e (kemChallenge)
1497 // TODO potentially write this directly into rhm?
1498 ret = GNUNET_CRYPTO_hpke_kem_encaps (&kx->pk_e, // public ephemeral key of initiator
1499 &ephemeral_kem_challenge, // encapsulated key
1500 &ss_e); // key - ss_e
1501 if (GNUNET_OK != ret)
1502 {
1504 "Something went wrong encapsulating ss_e\n");
1505 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1506 return;
1507 }
1509 // 6. encaps -> shared_secret_I, c_I
1510 ret = GNUNET_CRYPTO_eddsa_kem_encaps (&kx->peer.public_key, // public key of I
1511 &c_I, // encapsulated key
1512 &ss_I); // where to write the key material
1513 if (GNUNET_OK != ret)
1514 {
1516 "Something went wrong encapsulating ss_I\n");
1518 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1519 return;
1520 }
1521 // 7. generate RHTS (responder_handshare_secret_key) and RATS (responder_application_traffic_secret_key) (section 5)
1522 {
1523 struct GNUNET_HashCode transcript;
1524 snapshot_transcript (hc, &transcript);
1525#if DEBUG_KX
1527 "Transcript snapshot for derivation of HS, MS: `%s'\n",
1528 GNUNET_h2s (&transcript));
1529#endif
1531 &ss_e,
1532 &hs);
1533 derive_ms (&hs, &ss_I, &ms);
1534 }
1535
1536 // send ResponderHello
1537 // TODO fill fields / services_info!
1538 // 1. r_R <- random
1539 struct ResponderHelloPayload *rhp;
1540 size_t rhp_len = sizeof (*rhp) + strlen (my_services_info);
1541 unsigned char rhp_buf[rhp_len];
1542 size_t ct_len;
1543
1544 rhp = (struct ResponderHelloPayload*) rhp_buf;
1545 ct_len = rhp_len // ResponderHelloPayload, fist PT msg
1546 + sizeof (struct GNUNET_HashCode) // Finished hash, second PT msg
1547 + AEAD_TAG_BYTES * 2; // Two tags;
1548 env = GNUNET_MQ_msg_extra (rhm_e,
1549 ct_len,
1551
1552 rhm_e->r_R =
1553 GNUNET_CRYPTO_random_u64 (UINT64_MAX);
1554
1555 // c_e
1556 GNUNET_memcpy (&rhm_e->c_e,
1557 &ephemeral_kem_challenge,
1558 sizeof (ephemeral_kem_challenge));
1560 rhm_e,
1561 sizeof (struct ResponderHello));
1562 // 2. Encrypt ServicesInfo and c_I with RHTS
1563 // derive RHTS
1564 {
1565 struct GNUNET_HashCode transcript;
1567 &transcript);
1568#if DEBUG_KX
1570 "Transcript snapshot for derivation of *HTS: `%s'\n",
1571 GNUNET_h2s (&transcript));
1572#endif
1573 derive_rhts (&transcript,
1574 &hs,
1575 &rhts);
1576 derive_ihts (&transcript,
1577 &hs,
1578 &ihts);
1580 0,
1581 enc_key,
1582 enc_nonce);
1583 }
1584 // c_I
1585 GNUNET_memcpy (&rhp->c_I, &c_I, sizeof (c_I));
1586 // Services info empty for now.
1587 GNUNET_memcpy (&rhp[1],
1589 strlen (my_services_info));
1590
1591 {
1592 unsigned long long out_ct_len;
1594 struct GNUNET_HashCode transcript;
1595 unsigned char *finished_buf;
1596 GNUNET_assert (0 == crypto_aead_xchacha20poly1305_ietf_encrypt (
1597 (unsigned char*) &rhm_e[1], /* c - ciphertext */
1598 &out_ct_len, /* clen_p */
1599 rhp_buf, /* rhm_p - plaintext message */
1600 rhp_len, // mlen
1601 NULL, 0, // ad, adlen // FIXME should this not be the other, unencrypted
1602 // fields?
1603 NULL, // nsec - unused
1604 enc_nonce, // npub - nonce // FIXME nonce can be reused
1605 enc_key)); // k - key RHTS
1607 "Encrypted and wrote %llu bytes\n",
1608 out_ct_len);
1609 // 3. Create ResponderFinished (Section 6)
1610 // Derive fk_I <- HKDF-Expand (MS, "r finished", NULL)
1611 /* Forward the transcript */
1612 /* {svcinfo, c_I}RHTS */
1614 hc,
1615 &rhm_e[1],
1616 out_ct_len);
1617
1618 finished_buf = ((unsigned char*) &rhm_e[1]) + out_ct_len;
1620 &transcript);
1621#if DEBUG_KX
1623 "Transcript snapshot for derivation of Rfinished: `%s'\n",
1624 GNUNET_h2s (&transcript));
1625#endif
1626 generate_responder_finished (&transcript,
1627 &ms,
1628 &finished);
1629 // 4. Encrypt ResponderFinished
1631 1,
1632 enc_key,
1633 enc_nonce);
1634 GNUNET_assert (0 == crypto_aead_xchacha20poly1305_ietf_encrypt (
1635 finished_buf, /* c - ciphertext */
1636 &out_ct_len, /* clen_p */
1637 (unsigned char*) &finished, /* rhm_p - plaintext message */
1638 sizeof (finished), // mlen
1639 NULL, 0, // ad, adlen // FIXME should this not be the other, unencrypted
1640 // fields?
1641 NULL, // nsec - unused
1642 enc_nonce, // npub
1643 enc_key)); // k - key RHTS
1645 "Encrypted and wrote %llu bytes\n",
1646 out_ct_len);
1647 /* Forward the transcript
1648 * after responder finished,
1649 * before deriving *ATS and generating finished_I
1650 * (finished_I will be generated when receiving the InitiatorFinished message
1651 * in order to check it) */
1653 hc,
1654 finished_buf,
1655 out_ct_len);
1656 // 5. optionally send application data - encrypted with RATS
1657 // We do not really have any application data, instead, we send the ACK
1659 &transcript);
1660#if DEBUG_KX
1662 "Transcript snapshot for derivation of *ATS: `%s'\n",
1663 GNUNET_h2s (&transcript));
1664#endif
1665 derive_initial_ats (&transcript,
1666 &ms,
1668 &kx->current_ats);
1669 }
1670 /* Lock into struct */
1672 kx->transcript_hash_ctx = hc;
1673 kx->master_secret = ms;
1674 kx->handshake_secret = hs;
1675 kx->ss_e = ss_e;
1676 kx->ihts = ihts;
1677 kx->rhts = rhts;
1678 kx->ss_I = ss_I;
1679 kx->current_epoch = 0;
1680 kx->current_sqn = 0;
1682 kx->current_sqn,
1683 enc_key,
1684 enc_nonce);
1685
1686 GNUNET_MQ_send_copy (kx->mq, env);
1687 kx->resend_env = env;
1688 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sent ResponderHello: %d %d\n", kx->role,
1689 kx->status);
1692 monitor_notify_all (kx);
1693 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1694}
1695
1696
1706static void
1708 const struct InitiatorHello *ihm_e,
1709 const struct GNUNET_ShortHashCode *ss_R)
1710{
1711 const struct GNUNET_HashCode *my_identity_hash;
1712 uint32_t ihm_len = ntohs (ihm_e->header.size);
1713 unsigned char enc_key[AEAD_KEY_BYTES];
1714 unsigned char enc_nonce[AEAD_NONCE_BYTES];
1715 struct GNUNET_PeerIdentity peer_before = kx->peer;
1716 struct GNUNET_HashCode h1;
1717 struct GNUNET_HashCode transcript;
1718 struct GNUNET_ShortHashCode es;
1719 struct GNUNET_ShortHashCode ets;
1721
1723 &ihm_e->pk_e,
1724 sizeof (ihm_e->pk_e));
1725 // 5. generate ETS (early_traffic_secret_key, decrypt pk_i
1726 // expand ETS <- expand ES <- extract ss_R
1727 // use ETS to decrypt
1728
1729 /* Forward the transcript hash context over the unencrypted fields to get it
1730 * to the same status that the initiator had when it needed to derive es and
1731 * ets for the encryption */
1734 ihm_e,
1735 sizeof (struct InitiatorHello));
1737 &transcript);
1738#if DEBUG_KX
1740 "Transcript snapshot for derivation of ES, ETS: `%s'\n",
1741 GNUNET_h2s (&transcript));
1742#endif
1743 derive_es_ets (&transcript, ss_R, &es, &ets);
1745 0,
1746 enc_key,
1747 enc_nonce);
1748 {
1749 struct InitiatorHelloPayload *ihmp;
1750 size_t ct_len = ihm_len - sizeof (struct InitiatorHello);
1751 unsigned char ihmp_buf[ct_len - AEAD_TAG_BYTES];
1752 ihmp = (struct InitiatorHelloPayload*) ihmp_buf;
1753 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
1754 ihmp_buf, // unsigned char *m
1755 NULL, // mlen_p message length
1756 NULL, // unsigned char *nsec - unused: NULL
1757 (unsigned char*) &ihm_e[1], // const unsigned char *c - ciphertext
1758 ct_len, // unsigned long long clen - length of ciphertext
1759 // mac, // const unsigned char *mac - authentication tag
1760 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
1761 0, // unsigned long long adlen
1762 enc_nonce, // const unsigned char *npub - nonce
1763 enc_key // const unsigned char *k - key
1764 );
1765 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pid_sender: %s\n",
1766 GNUNET_i2s (&ihmp->pk_I));
1767 if (0 != ret)
1768 {
1770 "Something went wrong decrypting: %d\n", ret);
1771 GNUNET_break_op (0);
1772 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1773 restart_kx (kx);
1774 return;
1775 }
1776 /* now forward it considering the encrypted messages that the initiator was
1777 * able to send after deriving the es and ets */
1779 &ihm_e[1],
1780 ct_len);
1781 GNUNET_memcpy (&kx->peer,
1782 &ihmp->pk_I,
1783 sizeof (struct GNUNET_PeerIdentity));
1784 }
1785
1786 /* @e pk_I is the initiator's *claim* about who it is, and nothing has
1787 checked it. It must be the peer transport handed us this @a kx for:
1788 @e role was derived from that identity, GSC_SESSIONS_create() below
1789 keys the session on it, and transport routes everything we send by it.
1790 Letting the claim through means one peer can make us run a session
1791 under another peer's identity, and -- via the role comparison right
1792 below, which is computed over exactly this value -- can pick a @e pk_I
1793 that sends us down the reject path at will. */
1794 if (0 != GNUNET_memcmp (&kx->peer, &peer_before))
1795 {
1796 GNUNET_break_op (0);
1798 "InitiatorHello from `%s' claims to be `%s'\n",
1799 GNUNET_i2s (&peer_before),
1800 GNUNET_i2s2 (&kx->peer));
1801 kx->peer = peer_before;
1803 kx->transcript_hash_ctx = NULL;
1805 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1806 return;
1807 }
1808
1809 my_identity_hash = GNUNET_PILS_get_identity_hash (GSC_pils);
1810 GNUNET_assert (NULL != my_identity_hash);
1811
1812 // We could follow with the rest of the Key Schedule (dES, HS, ...) for now
1813 /* Check that we are actually in the receiving role */
1814 GNUNET_CRYPTO_hash (&kx->peer, sizeof(struct GNUNET_PeerIdentity), &h1);
1815 if (0 < GNUNET_CRYPTO_hash_cmp (&h1, my_identity_hash))
1816 {
1817 /* peer with "lower" identity starts KX, otherwise we typically end up
1818 with both peers starting the exchange and transmit the 'set key'
1819 message twice */
1820 /* Something went wrong - we have the lower value and should have sent the
1821 * InitiatorHello, but instead received it. TODO handle this case
1822 * We might end up in this case if the initiator didn't initiate the
1823 * handshake long enough and the 'responder' initiates the handshake */
1825 "Something went wrong - we have the lower value and should have sent the InitiatorHello, but instead received it.\n");
1827 kx->transcript_hash_ctx = NULL;
1828 /* Same reason the three other reject paths in #handle_initiator_hello()
1829 do this: that function set @e status to INITIATOR_HELLO_RECEIVED
1830 before calling us, and leaving it there makes every *later* hello hit
1831 the "Already received InitiatorHello" guard and be dropped, forever.
1832 Rejecting this hello must not cost us the next one. */
1834 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1835 return;
1836 }
1837
1838 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer ID of other peer: %s\n", GNUNET_i2s
1839 (&kx->peer));
1840 /* We update the monitoring peers here because now we know
1841 * that we can decrypt the message AND know the PID
1842 */
1843 monitor_notify_all (kx);
1844 kx->ss_R = *ss_R;
1845 kx->early_secret_key = es;
1846 kx->early_traffic_secret = ets;
1848}
1849
1850
1851static int
1852check_initiator_hello (void *cls, const struct InitiatorHello *m)
1853{
1854 uint16_t size = ntohs (m->header.size);
1855
1856 if (size < sizeof (*m)
1857 + sizeof (struct InitiatorHelloPayload)
1859 {
1860 return GNUNET_SYSERR;
1861 }
1862 return GNUNET_OK;
1863}
1864
1865
1874static void
1875handle_initiator_hello (void *cls, const struct InitiatorHello *ihm_e)
1876{
1877 const struct GNUNET_HashCode *my_identity_hash;
1879 struct GSC_KeyExchangeInfo *kx = cls;
1880 struct GNUNET_HashCode ih_hash;
1881 struct GNUNET_ShortHashCode ss_R;
1882 size_t ihm_len;
1883
1884 ihm_len = ntohs (ihm_e->header.size);
1885 GNUNET_CRYPTO_hash (ihm_e,
1886 ihm_len,
1887 &ih_hash);
1888 if (ROLE_INITIATOR == kx->role)
1889 {
1890 GNUNET_break_op (0);
1892 "I am an initiator! Tearing down...\n");
1893 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1894 return;
1895 }
1897 {
1898 /* Now that the decapsulation is synchronous nothing can observe this
1899 state from the outside -- #handle_initiator_hello_cont() runs before
1900 we return. Keep the guard anyway: reaching it means the state
1901 machine leaked a state, not that a peer did anything. */
1902 GNUNET_break (0);
1904 "Already received InitiatorHello: %d %d\n", kx->role, kx->status
1905 );
1906 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1907 return;
1908 }
1910 {
1911 if (0 == GNUNET_memcmp (&ih_hash,
1912 &kx->ih_hash))
1913 {
1914 /* Not a new exchange at all: the initiator resent the very hello we
1915 are already answering, because our ResponderHello did not make it
1916 back in time (#resend_initiator_hello() sends a copy of the same
1917 envelope, so a retransmission is byte-identical).
1918
1919 Starting over here is what breaks the pair. A fresh
1920 #send_responder_hello() picks a new @e ss_e and feeds a new
1921 ResponderHello into the transcript, and the transcript is what both
1922 @e finished_R and @e finished_I are computed over. The initiator
1923 answers whichever ResponderHello reaches it first and binds its
1924 InitiatorDone to *that* transcript, while we have moved on to the
1925 transcript of our latest one -- so #handle_initiator_done() cannot
1926 verify @e finished_I and drops it, every retransmission included.
1927 Neither side can make progress and neither side sees an error: the
1928 initiator sits in #GNUNET_CORE_KX_STATE_INITIATOR_DONE_SENT
1929 reporting "Unexpected ResponderHello", we sit in
1930 #GNUNET_CORE_KX_STATE_RESPONDER_HELLO_SENT, and both merely run out
1931 of retries after RESEND_MAX_TRIES and start over -- with no reason
1932 for the next attempt to be any luckier. One InitiatorHello
1933 retransmission, which any hiccup on the path produces, is enough to
1934 lose the peer indefinitely.
1935
1936 Retransmit our flight instead and leave the handshake state alone,
1937 per RFC 9147, Section 5.8: "implementations MUST retransmit their
1938 last flight in response to a retransmitted flight from the peer".
1939 Our own @e resend_task keeps its schedule; this only adds the
1940 answer the initiator is waiting for. */
1942 "InitiatorHello repeated by `%s' in state %d\n",
1943 GNUNET_i2s (&kx->peer),
1944 kx->status);
1946 gettext_noop (
1947 "# InitiatorHello retransmissions received"),
1948 1,
1949 GNUNET_NO);
1951 (NULL != kx->resend_env))
1953 kx->resend_env);
1954 /* Past that state the initiator already had our ResponderHello (we
1955 only leave it once @e finished_I verifies), so this is a duplicate
1956 that crossed with its InitiatorDone. Nothing to answer, and
1957 nothing that may cost us the association we just built. */
1958 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1959 return;
1960 }
1961 /* The initiator has given up on whatever we still hold and started
1962 over. Only the initiator drives this handshake, so follow it rather
1963 than dropping the hello.
1964 This used to return, which deadlocks the pair whenever we are in
1965 #GNUNET_CORE_KX_STATE_RESPONDER_CONNECTED: an InitiatorHello is not
1966 an EncryptedMessage and so does not refresh @e timeout, and nothing
1967 else ever leaves that state, so we would reject every retransmit
1968 until our own idle timeout fires -- five minutes during which the
1969 initiator restarts its exchange every 50s and we report the peer as
1970 connected. In #GNUNET_CORE_KX_STATE_RESPONDER_HELLO_SENT it is a
1971 plain retransmit: the initiator resends precisely because it did not
1972 get our ResponderHello, and answering the hello it actually sent
1973 converges instead of leaving both sides to turn over on unrelated
1974 50s timers that need not ever re-phase.
1975
1976 This is RFC 9147, Section 5.11: "In cases where a server believes it
1977 has an existing association [...] and it receives an epoch=0
1978 ClientHello, it SHOULD proceed with a new handshake but MUST NOT
1979 destroy the existing association until the client has demonstrated
1980 reachability [...] by completing a complete handshake including
1981 delivering a verifiable Finished message."
1982
1983 So only the handshake state goes. An InitiatorHello is not
1984 authenticated -- @e finished_I in the InitiatorDone is our Finished
1985 -- and must not be able to cost us an association on its own. What
1986 we have keeps its traffic keys (@e association_up stays set, and the
1987 record layer keys off that rather than off @e status), its
1988 @e heartbeat_task and its @e timeout, and clients keep being told the
1989 peer is connected. #handle_initiator_done() does the swap once, and
1990 only once, @e finished_I verifies. If it never does, the old
1991 association dies of its own idle timeout exactly as it would have. */
1993 "Peer `%s' restarted the key exchange in state %d, following\n",
1994 GNUNET_i2s (&kx->peer),
1995 kx->status);
1996 reset_handshake (kx);
1997 }
1998 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received InitiatorHello: %d %d\n", kx->
1999 role, kx->status);
2000 GNUNET_assert (NULL == kx->transcript_hash_ctx);
2002 GNUNET_assert (NULL != kx->transcript_hash_ctx);
2003
2005 gettext_noop ("# key exchanges initiated"),
2006 1,
2007 GNUNET_NO);
2008
2010
2011 my_identity_hash = GNUNET_PILS_get_identity_hash (GSC_pils);
2012 GNUNET_assert (NULL != my_identity_hash);
2013
2014 // 1. verify type _INITIATOR_HELLO
2015 // - This is implicytly done by arriving within this handler
2016 // - or is this about verifying the 'additional data' part of aead?
2017 // should it check the encryption + mac? (is this implicitly done
2018 // while decrypting?)
2019 // 2. verify H(pk_R) matches pk_R
2020 if (0 != memcmp (&ihm_e->h_pk_R,
2021 my_identity_hash,
2022 sizeof (struct GNUNET_HashCode)))
2023 {
2025 "This message is not meant for us (H(PID) mismatch)\n");
2027 kx->transcript_hash_ctx = NULL;
2028 /* Leaving @e status at #GNUNET_CORE_KX_STATE_INITIATOR_HELLO_RECEIVED
2029 here wedges the kx: every later hello then hits the "already
2030 received" guard above and is dropped, forever. */
2032 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2033 return;
2034 }
2035 // FIXME this sometimes triggers in the tests - why?
2036 // 3. decaps -> shared_secret_R, c_R (kemChallenge)
2037 /* From here on this is the hello we answer, so a byte-identical one is a
2038 retransmission of it and must not restart the exchange. */
2039 kx->ih_hash = ih_hash;
2041 if (NULL == my_private_key)
2042 {
2043 /* #GSC_KX_start() enables local key access before we ever talk to
2044 TRANSPORT, so this means the key on disk does not match the identity
2045 PILS announced. We cannot answer any hello in that state. */
2047 "No private key for our peer identity, cannot answer hello"
2048 " from `%s'\n",
2049 GNUNET_i2s (&kx->peer));
2051 kx->transcript_hash_ctx = NULL;
2053 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2054 return;
2055 }
2056 if (GNUNET_OK !=
2058 &ihm_e->c_R,
2059 &ss_R))
2060 {
2061 GNUNET_break_op (0);
2063 "Failed to decapsulate c_R of hello from `%s'\n",
2064 GNUNET_i2s (&kx->peer));
2066 kx->transcript_hash_ctx = NULL;
2068 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2069 return;
2070 }
2072 ihm_e,
2073 &ss_R);
2074}
2075
2076
2078{
2079 /* Current KX session */
2081
2082 /* responder hello message - encrypted */
2084
2085 /* responder hello message - plain/decrypted */
2087
2088 /* Decrypted finish hash */
2090
2091 /* Encrypted finished CT (for transcript later) */
2092 char finished_enc[sizeof (struct GNUNET_HashCode)
2093 + AEAD_TAG_BYTES];
2094
2095 /* Temporary transcript context */
2097
2098 /* Temporary handshake secret */
2100
2101 /* Temporary handshake secret */
2103
2104 /* Temporary handshake secret */
2106
2107 /* Temporary handshake secret */
2109};
2110
2111static void
2113{
2114 struct GSC_KeyExchangeInfo *kx = cls;
2115
2116 kx->resend_task = NULL;
2117 if (0 == kx->resend_tries_left)
2118 {
2120 "Restarting KX\n");
2121 restart_kx (kx);
2122 return;
2123 }
2124 kx->resend_tries_left--;
2126 "Resending initiator done. Retries left: %u\n",
2127 kx->resend_tries_left);
2130}
2131
2132
2142static void
2144 const struct GNUNET_ShortHashCode *ss_I)
2145{
2146 struct GSC_KeyExchangeInfo *kx = rh_ctx->kx;
2147 struct InitiatorDone *idm_e; /* encrypted */
2148 struct InitiatorDone idm_local;
2149 struct InitiatorDone *idm_p; /* plaintext */
2150 struct GNUNET_MQ_Envelope *env;
2151 unsigned char enc_key[AEAD_KEY_BYTES];
2152 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2153 struct ConfirmationAck ack_i;
2154 struct GNUNET_HashCode transcript;
2155 struct GNUNET_ShortHashCode ms;
2156
2157 // XXX valgrind reports uninitialized memory
2158 // the following is a way to check whether this memory was meant
2159 // memset (&rhm_local, 0, sizeof (rhm_local)); - adapt to cls if still needed
2160 memset (&idm_local, 0, sizeof (idm_local));
2161
2162 kx->ss_I = *ss_I;
2163
2164 /* derive *ATS */
2165 derive_ms (&rh_ctx->hs, ss_I, &ms);;
2166 // 5. Create ResponderFinished as per Section 6 and check against decrypted payload.
2167 struct GNUNET_HashCode responder_finished;
2168 // Transcript updates, snapshot again
2169 snapshot_transcript (rh_ctx->hc,
2170 &transcript);
2171#if DEBUG_KX
2173 "Transcript snapshot for derivation of Rfinished: `%s'\n",
2174 GNUNET_h2s (&transcript));
2175#endif
2176 generate_responder_finished (&transcript,
2177 &ms,
2178 &responder_finished);
2179 if (0 != memcmp (&rh_ctx->decrypted_finish,
2180 &responder_finished,
2181 sizeof (struct GNUNET_HashCode)))
2182 {
2183 /* A peer that answers our InitiatorHello with a ResponderHello whose
2184 finished field does not verify must not be able to abort us; this
2185 used to be a GNUNET_assert (0). */
2186 GNUNET_break_op (0);
2188 "Could not verify \"responder finished\" from `%s'\n",
2189 GNUNET_i2s (&kx->peer));
2190 GNUNET_free (rh_ctx->rhp);
2192 GNUNET_free (rh_ctx);
2193 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2194 restart_kx (kx);
2195 return;
2196 }
2197
2198
2199 /* Forward the transcript
2200 * after generating finished_R,
2201 * before deriving *ATS */
2203 rh_ctx->hc,
2204 rh_ctx->finished_enc,
2205 sizeof (rh_ctx->finished_enc));
2206
2207 // At this point we cannot fail anymore and may lock into kx
2209 kx->transcript_hash_ctx = rh_ctx->hc;
2210 kx->ss_I = *ss_I;
2211 kx->handshake_secret = rh_ctx->hs;
2212 kx->ss_e = rh_ctx->ss_e;
2213 kx->ihts = rh_ctx->ihts;
2214 kx->rhts = rh_ctx->rhts;
2215 kx->master_secret = ms;
2216 GNUNET_free (rh_ctx->rhp);
2217 GNUNET_free (rh_ctx);
2218 rh_ctx = NULL;
2219
2221 &transcript);
2222#if DEBUG_KX
2224 "Transcript snapshot for derivation of *ATS: `%s'\n",
2225 GNUNET_h2s (&transcript));
2226#endif
2227 derive_initial_ats (&transcript,
2228 &kx->master_secret,
2230 &kx->their_ats[0]);
2231 for (int i = 0; i < MAX_EPOCHS - 1; i++)
2232 {
2233 derive_next_ats (&kx->their_ats[i],
2234 &kx->their_ats[i + 1]);
2235 }
2236 kx->their_max_epoch = MAX_EPOCHS - 1;
2237
2239 0,
2240 enc_key,
2241 enc_nonce);
2242 /* Create InitiatorDone message */
2243 idm_p = &idm_local; /* plaintext */
2244 env = GNUNET_MQ_msg_extra (idm_e,
2245 sizeof (ack_i)
2248 // 6. Create IteratorFinished as per Section 6.
2249 generate_initiator_finished (&transcript,
2250 &kx->master_secret,
2251 &idm_p->finished);
2253 "InteratorFinished: `%s'\n",
2254 GNUNET_h2s (&idm_p->finished));
2256 "Transcript `%s'\n",
2257 GNUNET_h2s (&transcript));
2258 // 7. Send InteratorFinished message encrypted with the key derived from IHTS to R
2259
2260 GNUNET_assert (0 == crypto_aead_xchacha20poly1305_ietf_encrypt (
2261 (unsigned char*) &idm_e->finished, /* c - ciphertext */
2262 NULL, /* clen_p */
2263 (unsigned char*) &idm_p->finished, /* idm_p - plaintext message */
2264 sizeof (idm_p->finished), // mlen
2265 NULL, 0, // ad, adlen // FIXME should this not be the other, unencrypted
2266 // fields?
2267 NULL, // nsec - unused
2268 enc_nonce, // npub - nonce
2269 enc_key)); // k - key IHTS
2270 /* Forward the transcript hash context
2271 * after generating finished_I and RATS_0
2272 * before deriving IATS_0 */
2274 &idm_e->finished,
2275 sizeof (idm_e->finished)
2276 + AEAD_TAG_BYTES);
2278 &transcript);
2279#if DEBUG_KX
2281 "Transcript snapshot for derivation of *ATS: `%s'\n",
2282 GNUNET_h2s (&transcript));
2283#endif
2284 derive_initial_ats (&transcript,
2285 &kx->master_secret,
2287 &kx->current_ats);
2288 kx->current_epoch = 0;
2289 kx->current_sqn = 0;
2290 /* We start sending under this epoch here, so it has to be dated here too.
2291 #check_if_ack_or_heartbeat() only sets @e current_epoch_expiration once
2292 the responder's Ack arrives; until then it holds whatever the previous
2293 association left (zero for a first exchange), and #check_rekey() treats
2294 a past expiration as "rekey now". Anything we send while waiting for
2295 the Ack -- the Ack we answer an early heartbeat with, say -- would then
2296 burn an epoch the responder has no reason to expect. */
2299 /* Application traffic keys are installed, so from the record layer's
2300 point of view the association exists from here: we have to be able to
2301 deprotect the responder's Ack, which arrives before the handshake is
2302 confirmed. The client-visible session is created only once it does. */
2303 replay_reset_all (kx);
2305 // 8. optionally encrypt payload TODO
2307 kx->current_sqn,
2308 enc_key,
2309 enc_nonce);
2310 kx->current_sqn++;
2312 ack_i.header.size = htons (sizeof ack_i);
2313 GNUNET_assert (0 == crypto_aead_xchacha20poly1305_ietf_encrypt (
2314 (unsigned char*) &idm_e[1], /* c - ciphertext */
2315 NULL, /* clen_p */
2316 (unsigned char*) &ack_i, /* rhm_p - plaintext message */
2317 sizeof ack_i, // mlen
2318 NULL, 0, // ad, adlen // FIXME should this not be the other, unencrypted
2319 // fields?
2320 NULL, // nsec - unused
2321 enc_nonce, // npub - nonce // FIXME nonce can be reused
2322 enc_key)); // k - key RHTS
2323
2324 GNUNET_MQ_send_copy (kx->mq, env);
2325 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sent InitiatorDone: %d %d\n", kx->role,
2326 kx->status);
2327
2328
2329 kx->resend_env = env;
2332 monitor_notify_all (kx);
2333 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2334}
2335
2336
2337static int
2338check_responder_hello (void *cls, const struct ResponderHello *m)
2339{
2340 uint16_t size = ntohs (m->header.size);
2341
2342 if (size < sizeof (*m)
2343 + sizeof (struct ResponderHelloPayload)
2344 + sizeof (struct GNUNET_HashCode)
2345 + AEAD_TAG_BYTES * 2)
2346 {
2347 return GNUNET_SYSERR;
2348 }
2349 return GNUNET_OK;
2350}
2351
2352
2358static void
2359handle_responder_hello (void *cls, const struct ResponderHello *rhm_e)
2360{
2361 struct GSC_KeyExchangeInfo *kx = cls;
2363 struct ResponderHelloCls *rh_ctx;
2364 struct GNUNET_HashCode transcript;
2365 struct GNUNET_HashCode rh_hash;
2366 struct GNUNET_HashContext *hc;
2367 struct GNUNET_ShortHashCode ss_I;
2368 unsigned char enc_key[AEAD_KEY_BYTES];
2369 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2371
2372 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ResponderHello: %d %d\n", kx->
2373 role, kx->status);
2374
2375 GNUNET_CRYPTO_hash (rhm_e,
2376 ntohs (rhm_e->header.size),
2377 &rh_hash);
2378 if (ROLE_RESPONDER == kx->role)
2379 {
2380 GNUNET_break_op (0);
2382 "I am the responder! Ignoring.\n");
2383 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2384 return;
2385 }
2387 {
2389 (0 == GNUNET_memcmp (&rh_hash,
2390 &kx->rh_hash)))
2391 {
2392 /* The responder resent the ResponderHello we already answered, which
2393 means our InitiatorDone did not reach it. That is an ordinary
2394 retransmission, not a protocol violation -- the GNUNET_break_op()
2395 below used to report it as one, which is what "Unexpected
2396 ResponderHello in state 6" in the logs is. Answer it the way
2397 RFC 9147, Section 5.8 requires: "implementations MUST retransmit
2398 their last flight in response to a retransmitted flight from the
2399 peer". Our @e resend_task would get there on its own eventually;
2400 doing it here converges at the pace of the peer's timer instead of
2401 ours, and both are bounded by RESEND_MAX_TRIES. */
2403 "ResponderHello repeated by `%s', resending InitiatorDone\n",
2404 GNUNET_i2s (&kx->peer));
2406 gettext_noop (
2407 "# ResponderHello retransmissions received"),
2408 1,
2409 GNUNET_NO);
2410 if (NULL != kx->resend_env)
2412 kx->resend_env);
2413 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2414 return;
2415 }
2416 /* Outside of that state there is no handshake this message could
2417 belong to. In particular @e transcript_hash_ctx is then NULL, and
2418 #GNUNET_CRYPTO_hash_context_copy() dereferences its argument -- so
2419 a peer could crash us by sending a ResponderHello at any other
2420 time. Note that @e resend_task and @e resend_env below belong to
2421 the exchange we *are* in the middle of and must not be cleared
2422 before this point either. */
2423 GNUNET_break_op (0);
2425 "Unexpected ResponderHello in state %d, ignoring\n",
2426 kx->status);
2427 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2428 return;
2429 }
2430 /* From here on this is the hello we answer; recognising a retransmission
2431 of it is what lets us resend our InitiatorDone above instead of
2432 dropping the peer's flight on the floor. */
2433 kx->rh_hash = rh_hash;
2434 GNUNET_assert (NULL != kx->transcript_hash_ctx);
2436 if (NULL != kx->resend_task)
2437 {
2439 kx->resend_task = NULL;
2440 }
2441 if (NULL != kx->resend_env)
2442 {
2444 kx->resend_env = NULL;
2445 }
2446
2447 /* Forward the transcript hash context */
2449 rhm_e,
2450 sizeof (struct ResponderHello));
2451 // 1. Verify that the message type is CORE_RESPONDER_HELLO
2452 // - implicitly done by handling this message?
2453 // - or is this about verifying the 'additional data' part of aead?
2454 // should it check the encryption + mac? (is this implicitly done
2455 // while decrypting?)
2456 // 2. sse <- Decaps(ske,ce)
2457 rh_ctx = GNUNET_new (struct ResponderHelloCls);
2458 ret = GNUNET_CRYPTO_hpke_kem_decaps (&kx->sk_e, // secret/private ephemeral key of initiator (us)
2459 &rhm_e->c_e, // encapsulated key
2460 &rh_ctx->ss_e); // key - ss_e
2461 if (GNUNET_OK != ret)
2462 {
2464 "Something went wrong decapsulating ss_e\n");
2466 GNUNET_free (rh_ctx);
2467 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2468 return;
2469 }
2470 // 3. Generate IHTS and RHTS from Section 5 and decrypt ServicesInfo, cI and ResponderFinished.
2471 snapshot_transcript (hc, &transcript);
2472#if DEBUG_KX
2474 "Transcript snapshot for derivation of HS, *HTS: `%s'\n",
2475 GNUNET_h2s (&transcript));
2476#endif
2478 &rh_ctx->ss_e,
2479 &rh_ctx->hs);
2480 derive_rhts (&transcript,
2481 &rh_ctx->hs,
2482 &rh_ctx->rhts);
2483 derive_ihts (&transcript,
2484 &rh_ctx->hs,
2485 &rh_ctx->ihts);
2487 0,
2488 enc_key,
2489 enc_nonce);
2490 rh_ctx->kx = kx;
2491 GNUNET_memcpy (&rh_ctx->rhm_e, rhm_e, sizeof (*rhm_e));
2492 {
2493 unsigned long long int c_len;
2494 unsigned char *finished_buf;
2495 // use RHTS to decrypt
2496 c_len = ntohs (rhm_e->header.size) - sizeof (*rhm_e)
2497 - sizeof (struct GNUNET_HashCode)
2498 - AEAD_TAG_BYTES; // finished ct
2499 rh_ctx->rhp = GNUNET_malloc (c_len
2500 -
2502 rh_ctx->hc = hc;
2503 finished_buf = ((unsigned char*) &rhm_e[1]) + c_len;
2504 /* Forward the transcript_hash_ctx
2505 * after rhts has been generated,
2506 * before generating finished_R*/
2508 hc,
2509 &rhm_e[1],
2510 c_len);
2511
2512 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2513 (unsigned char*) rh_ctx->rhp, // unsigned char *m
2514 NULL, // mlen_p message length
2515 NULL, // unsigned char *nsec - unused: NULL
2516 (unsigned char*) &rhm_e[1], // const unsigned char *c - ciphertext
2517 c_len, // unsigned long long clen - length of ciphertext
2518 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2519 0, // unsigned long long adlen
2520 enc_nonce, // const unsigned char *npub - nonce
2521 enc_key // const unsigned char *k - key
2522 );
2523 if (0 != ret)
2524 {
2526 "Something went wrong decrypting: %d\n", ret);
2527 GNUNET_free (rh_ctx->rhp);
2528 GNUNET_free (rh_ctx);
2530 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2531 return;
2532 }
2533 // FIXME nonce reuse (see encryption)
2535 1,
2536 enc_key,
2537 enc_nonce);
2538 c_len = sizeof (struct GNUNET_HashCode)
2540 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2541 (unsigned char*) &rh_ctx->decrypted_finish, // unsigned char *m
2542 NULL, // mlen_p message length
2543 NULL, // unsigned char *nsec - unused: NULL
2544 finished_buf, // const unsigned char *c - ciphertext
2545 c_len, // unsigned long long clen - length of ciphertext
2546 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2547 0, // unsigned long long adlen
2548 enc_nonce, // const unsigned char *npub - nonce
2549 enc_key // const unsigned char *k - key
2550 );
2551 if (0 != ret)
2552 {
2554 "Something went wrong decrypting finished field: %d\n", ret);
2555 GNUNET_free (rh_ctx->rhp);
2556 GNUNET_free (rh_ctx);
2558 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2559 return;
2560 }
2561 GNUNET_memcpy (rh_ctx->finished_enc,
2562 finished_buf,
2563 c_len);
2564 }
2565 // 4. ssI <- Decaps(skI,cI).
2567 if ( (NULL == my_private_key) ||
2568 (GNUNET_OK !=
2570 &rh_ctx->rhp->c_I,
2571 &ss_I)) )
2572 {
2574 "Failed to decapsulate c_I of ResponderHello from `%s'\n",
2575 GNUNET_i2s (&kx->peer));
2576 GNUNET_free (rh_ctx->rhp);
2577 GNUNET_free (rh_ctx);
2579 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2580 restart_kx (kx);
2581 return;
2582 }
2584 &ss_I);
2585}
2586
2587
2588static int
2589check_initiator_done (void *cls, const struct InitiatorDone *m)
2590{
2591 uint16_t size = ntohs (m->header.size);
2592
2593 if (size < sizeof (*m) + sizeof (struct ConfirmationAck))
2594 {
2595 return GNUNET_SYSERR;
2596 }
2597 return GNUNET_OK;
2598}
2599
2600
2606static void
2607handle_initiator_done (void *cls, const struct InitiatorDone *idm_e)
2608{
2609 struct GSC_KeyExchangeInfo *kx = cls;
2610 struct InitiatorDone idm_local;
2611 struct InitiatorDone *idm_p = &idm_local; /* plaintext */
2612 struct GNUNET_HashCode initiator_finished;
2613 struct GNUNET_HashCode transcript;
2614 struct GNUNET_ShortHashCode their_ats;
2615 struct GNUNET_HashContext *hc;
2616 unsigned char enc_key[AEAD_KEY_BYTES];
2617 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2618 struct ConfirmationAck ack_i;
2619 struct ConfirmationAck ack_r;
2620 int8_t ret;
2621
2622 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received InitiatorDone: %d %d\n", kx->
2623 role, kx->status);
2624 if (ROLE_INITIATOR == kx->role)
2625 {
2626 GNUNET_break_op (0);
2628 "I am the initiator! Tearing down...\n");
2629 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2630 return;
2631 }
2633 {
2634 /* The initiator did not see our ConfirmationAck and is resending (it
2635 tries #RESEND_MAX_TRIES times). Our handshake secrets are gone --
2636 #cleanup_handshake_secrets() zeroed @e ihts -- so verifying this
2637 message again is not possible and would only look like a decryption
2638 failure. Send what the initiator is actually missing instead. */
2640 "InitiatorDone repeated by `%s', resending our Ack\n",
2641 GNUNET_i2s (&kx->peer));
2643 ack_r.header.size = htons (sizeof ack_r);
2645 &ack_r,
2646 sizeof ack_r);
2647 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2648 return;
2649 }
2651 {
2652 /* We have no handshake state this message could be checked against.
2653 Note that @e resend_task and @e resend_env below belong to whatever
2654 exchange we *are* in the middle of, so they must not be cleared
2655 before this point. */
2656 GNUNET_break_op (0);
2658 "Unexpected InitiatorDone in state %d, ignoring\n",
2659 kx->status);
2660 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2661 return;
2662 }
2663 if (NULL != kx->resend_task)
2664 {
2666 kx->resend_task = NULL;
2667 }
2668 if (NULL != kx->resend_env)
2669 {
2671 kx->resend_env = NULL;
2672 }
2674 0,
2675 enc_key,
2676 enc_nonce);
2677 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2678 (unsigned char*) &idm_p->finished, // unsigned char *m
2679 NULL, // mlen_p message length
2680 NULL, // unsigned char *nsec - unused: NULL
2681 (unsigned char*) &idm_e->finished, // const unsigned char *c - ciphertext
2682 sizeof (idm_p->finished) // unsigned long long clen - length of ciphertext
2684 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2685 0, // unsigned long long adlen
2686 enc_nonce, // const unsigned char *npub - nonce
2687 enc_key // const unsigned char *k - key
2688 );
2689 if (0 != ret)
2690 {
2692 "Something went wrong decrypting: %d\n", ret);
2693 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2694 return;
2695 }
2696
2697 // - verify finished_I
2698 /* Generate finished_I
2699 * after Forwarding until {finished_R}RHTS
2700 * (did so while we prepared responder hello)
2701 * before forwarding to [{payload}RATS and] {finished_I}IHTS */
2702 // (look at the end of handle_initiator_hello())
2703 snapshot_transcript (kx->transcript_hash_ctx, &transcript);
2704 generate_initiator_finished (&transcript,
2705 &kx->master_secret,
2706 &initiator_finished);
2707 if (0 != memcmp (&idm_p->finished,
2708 &initiator_finished,
2709 sizeof (struct GNUNET_HashCode)))
2710 {
2712 "Could not verify \"initiator finished\" hash.\n");
2714 "Want: `%s'\n",
2715 GNUNET_h2s (&initiator_finished));
2717 "Have: `%s'\n",
2718 GNUNET_h2s (&idm_p->finished));
2720 "Transcript `%s'\n",
2721 GNUNET_h2s (&transcript));
2722 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2723 return;
2724 }
2725
2726 /* Forward the transcript hash_context_read */
2729 &idm_e->finished,
2730 sizeof (idm_e->finished)
2731 + AEAD_TAG_BYTES);
2732 snapshot_transcript (hc, &transcript);
2733 derive_initial_ats (&transcript,
2734 &kx->master_secret,
2736 &their_ats);
2737 derive_per_message_secrets (&their_ats, // FIXME other HS epoch?
2738 0,
2739 enc_key,
2740 enc_nonce);
2741 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2742 (unsigned char*) &ack_i, // unsigned char *m
2743 NULL, // mlen_p message length
2744 NULL, // unsigned char *nsec - unused: NULL
2745 (unsigned char*) &idm_e[1], // const unsigned char *c - ciphertext
2746 sizeof (ack_i) + AEAD_TAG_BYTES, // unsigned long long clen - length of ciphertext
2747 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2748 0, // unsigned long long adlen
2749 enc_nonce, // const unsigned char *npub - nonce
2750 enc_key // const unsigned char *k - key
2751 );
2752 if (0 != ret)
2753 {
2755 "Something went wrong decrypting the Ack: %d\n", ret);
2757 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2758 return;
2759 }
2760 if ((sizeof ack_i != ntohs (ack_i.header.size)) ||
2761 (GNUNET_MESSAGE_TYPE_CORE_ACK != ntohs (ack_i.header.type)))
2762 {
2764 "Ack invalid!\n");
2766 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2767 return;
2768 }
2769 GNUNET_memcpy (&kx->their_ats[0],
2770 &their_ats,
2771 sizeof their_ats);
2775 for (int i = 0; i < MAX_EPOCHS - 1; i++)
2776 {
2777 derive_next_ats (&kx->their_ats[i],
2778 &kx->their_ats[i + 1]);
2779 }
2781 kx->transcript_hash_ctx = hc;
2786 monitor_notify_all (kx);
2787 /* @e finished_I has verified. RFC 9147, Section 5.11: the peer has now
2788 "demonstrated reachability [...] by completing a complete handshake
2789 including delivering a verifiable Finished message", so this is the
2790 point -- and the only point -- at which the old association may be
2791 destroyed. #handle_initiator_hello() deliberately left it running.
2792 GSC_SESSIONS_create() puts into @e sessions with
2793 #GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY under a
2794 GNUNET_assert(), so a leftover session is not merely untidy. */
2795 GSC_SESSIONS_end (&kx->peer);
2796 if (NULL != kx->heartbeat_task)
2797 {
2799 kx->heartbeat_task = NULL;
2800 }
2801 /* #send_initiator_done() starts the initiator at epoch 0 and we have to
2802 agree: on a kx that had an association before, these still hold the
2803 predecessor's values, and none of them was ever reset here. */
2804 kx->current_epoch = 0;
2805 kx->their_max_epoch = 0;
2806 kx->current_sqn = 1;
2807 replay_reset_all (kx);
2809 GSC_SESSIONS_create (&kx->peer, kx, kx->class);
2810 update_timeout (kx);
2812 ack_r.header.size = htons (sizeof ack_r);
2814 &ack_r,
2815 sizeof ack_r);
2816
2817 GNUNET_TRANSPORT_core_receive_continue (transport,
2818 &kx->peer);
2819}
2820
2821
2827static int
2829{
2830 uint16_t size = ntohs (m->header.size) - sizeof(*m);
2831
2832 // TODO check (see check_encrypted ())
2833 // - check epoch
2834 // - check sequence number
2835 if (size < sizeof(struct GNUNET_MessageHeader))
2836 {
2837 GNUNET_break_op (0);
2838 return GNUNET_SYSERR;
2839 }
2840 return GNUNET_OK;
2841}
2842
2843
2849static void
2851 const struct Heartbeat *m)
2852{
2853 struct GNUNET_ShortHashCode new_ats;
2854 struct ConfirmationAck ack;
2855
2857 {
2858 if (kx->current_epoch == UINT64_MAX)
2859 {
2861 "Max epoch reached (you probably will never see this)\n");
2862 }
2863 else
2864 {
2865 kx->current_epoch++;
2868 kx->current_sqn = 0;
2870 &new_ats);
2871 memcpy (&kx->current_ats,
2872 &new_ats,
2873 sizeof new_ats);
2874 }
2875 }
2876 update_timeout (kx);
2878 ack.header.size = htons (sizeof ack);
2880 &ack,
2881 sizeof ack);
2882 /* NOTE: no GNUNET_TRANSPORT_core_receive_continue() here. We are called
2883 from #handle_encrypted_message(), which owns the message and issues
2884 exactly one call for it. */
2885}
2886
2887
2888static enum GNUNET_GenericReturnValue
2890 const char *buf,
2891 size_t buf_len)
2892{
2893 struct GNUNET_MessageHeader *msg;
2894 struct ConfirmationAck *ack;
2895 struct Heartbeat *hb;
2896
2897 if (sizeof *msg > buf_len)
2898 return GNUNET_NO;
2899 msg = (struct GNUNET_MessageHeader*) buf;
2900 if (GNUNET_MESSAGE_TYPE_CORE_ACK == ntohs (msg->type))
2901 {
2902 ack = (struct ConfirmationAck *) buf;
2903 if (sizeof *ack != ntohs (ack->header.size))
2904 return GNUNET_NO;
2905 }
2906 else if (GNUNET_MESSAGE_TYPE_CORE_HEARTBEAT == ntohs (msg->type))
2907 {
2908 hb = (struct Heartbeat*) buf;
2909 if (sizeof *hb != ntohs (hb->header.size))
2910 return GNUNET_NO;
2911 handle_heartbeat (kx, hb);
2912 }
2913 else
2914 {
2915 return GNUNET_NO;
2916 }
2917
2922 {
2923 GSC_SESSIONS_create (&kx->peer, kx, kx->class);
2928 if (NULL != kx->resend_task)
2930 kx->resend_task = NULL;
2931 if (NULL != kx->resend_env)
2933 kx->resend_env = NULL;
2934 monitor_notify_all (kx);
2935 }
2936 update_timeout (kx);
2937
2938 return GNUNET_YES;
2939}
2940
2941
2947static void
2949{
2950 struct GSC_KeyExchangeInfo *kx = cls;
2951 uint16_t size = ntohs (m->header.size);
2952 char buf[size - sizeof (*m)] GNUNET_ALIGN;
2953 unsigned char seq_enc_k[crypto_stream_chacha20_ietf_KEYBYTES];
2954 const unsigned char *seq_enc_nonce;
2955 unsigned char enc_key[AEAD_KEY_BYTES];
2956 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2957 struct GNUNET_ShortHashCode new_ats[MAX_EPOCHS];
2958 uint32_t seq_enc_ctr;
2959 uint64_t epoch;
2960 uint64_t m_seq;
2961 uint64_t m_seq_nbo;
2962 uint64_t c_len;
2963 int8_t ret;
2964
2965 // TODO look at handle_encrypted
2966 // - statistics
2967
2968 /* The record layer answers to @e association_up, not to @e status: a
2969 handshake may be in flight over an association that is still live
2970 (RFC 9147, Section 5.11), and records of the old epoch have to keep
2971 being deprotected while it is. Conversely a record we have no keys
2972 for is simply an invalid record -- RFC 9147, Section 4.5.2: "In
2973 general, invalid records SHOULD be silently discarded, thus preserving
2974 the association" -- so it must not end a session or restart anything.
2975 If we are idle it does tell us the peer believes in an association we
2976 do not have, which is worth one exchange. */
2977 if (GNUNET_YES != kx->association_up)
2978 {
2980 "Discarding record from `%s': no keys for epoch %" PRIu64 "\n",
2981 GNUNET_i2s (&kx->peer),
2982 GNUNET_ntohll (m->epoch));
2983 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2985 restart_kx (kx);
2986 return;
2987 }
2988 epoch = GNUNET_ntohll (m->epoch);
2993 memcpy (new_ats,
2994 kx->their_ats,
2995 MAX_EPOCHS * sizeof (struct GNUNET_ShortHashCode));
2996 // FIXME here we could introduce logic that sends heartbeats
2997 // with key update request if we have not seen a new
2998 // epoch after a while (e.g. EPOCH_EXPIRATION)
2999 if (kx->their_max_epoch < epoch)
3000 {
3005 if ((epoch - kx->their_max_epoch) > 2 * MAX_EPOCHS)
3006 {
3007 /* @e epoch is plaintext and not covered by the AEAD tag, so this is
3008 reached by a single flipped bit as readily as by a peer that really
3009 did skip ahead. Drop the message like the "too old" case below
3010 does; tearing the session down here means one unauthenticated
3011 header field costs a full re-handshake. */
3013 "Epoch %" PRIu64 " is too new, will not decrypt...\n",
3014 epoch);
3015 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3016 return;
3017 }
3018 for (uint64_t i = kx->their_max_epoch; i < epoch; i++)
3019 {
3020 derive_next_ats (&new_ats[i % MAX_EPOCHS],
3021 &new_ats[(i + 1) % MAX_EPOCHS]);
3022 /* This slot of the ring now holds a different key, so the window
3023 that went with the old one no longer means anything. */
3024 replay_reset (kx, i + 1);
3025 }
3026 }
3027 else if ((kx->their_max_epoch - epoch) > MAX_EPOCHS)
3028 {
3030 "Epoch %" PRIu64 " is too old, cannot decrypt...\n",
3031 epoch);
3032 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3033 return;
3034 }
3035 derive_sn (
3036 &new_ats[epoch % MAX_EPOCHS],
3037 seq_enc_k,
3038 sizeof seq_enc_k);
3039 /* compute the sequence number */
3040 seq_enc_ctr = *((uint32_t*) m->tag);
3041 seq_enc_nonce = &m->tag[sizeof (uint32_t)];
3042#if DEBUG_KX
3043 GNUNET_print_bytes (&new_ats[epoch % MAX_EPOCHS],
3044 sizeof (struct GNUNET_ShortHashCode),
3045 8,
3046 GNUNET_NO);
3047 GNUNET_print_bytes (seq_enc_k,
3048 sizeof seq_enc_k,
3049 8,
3050 GNUNET_NO);
3051 GNUNET_print_bytes ((char*) &seq_enc_ctr,
3052 sizeof seq_enc_ctr,
3053 8,
3054 GNUNET_NO);
3055#endif
3056 crypto_stream_chacha20_ietf_xor_ic (
3057 (unsigned char*) &m_seq_nbo,
3058 (unsigned char*) &m->sequence_number,
3059 sizeof (uint64_t),
3060 seq_enc_nonce,
3061 ntohl (seq_enc_ctr),
3062 seq_enc_k);
3063 m_seq = GNUNET_ntohll (m_seq_nbo);
3065 "Received encrypted message in epoch %" PRIu64
3066 " with E(SQN=%" PRIu64 ")=%" PRIu64
3067 "\n",
3068 epoch,
3069 m_seq,
3070 m->sequence_number);
3071 /* RFC 9147, Section 4.5.1. Cheap enough to do before deprotection, and
3072 doing it first means a flood of replayed records costs no AEAD work.
3073 The window itself is only moved once the record verifies, below. */
3074 if (GNUNET_OK != replay_check (kx, epoch, m_seq))
3075 {
3077 gettext_noop ("# replayed records discarded"),
3078 1,
3079 GNUNET_NO);
3081 "Discarding replayed record %" PRIu64 "/%" PRIu64
3082 " from `%s'\n",
3083 epoch,
3084 m_seq,
3085 GNUNET_i2s (&kx->peer));
3086 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3087 return;
3088 }
3089 /* We are the initiator and as we are going to receive,
3090 * we are using the responder key material */
3091 derive_per_message_secrets (&new_ats[epoch % MAX_EPOCHS],
3092 m_seq,
3093 enc_key,
3094 enc_nonce);
3095 // TODO checking sequence numbers - handle the case of out-of-sync messages!
3096 // for now only decrypt the payload
3097 // TODO encrypt other fields, too!
3098 // TODO
3099 // c_len = size - offsetof ();
3100 c_len = size - sizeof (struct EncryptedMessage);
3101 ret = crypto_aead_xchacha20poly1305_ietf_decrypt_detached (
3102 (unsigned char*) buf, // m - plain message
3103 NULL, // nsec - unused
3104 (unsigned char*) &m[1], // c - ciphertext
3105 c_len, // clen
3106 (const unsigned char*) &m->tag, // mac
3107 NULL, // ad - additional data TODO
3108 0, // adlen
3109 enc_nonce, // npub
3110 enc_key // k
3111 );
3112 if (0 != ret)
3113 {
3114 /* RFC 9147, Section 4.5.2: "invalid records SHOULD be silently
3115 discarded, thus preserving the association; however, an error MAY be
3116 logged for diagnostic purposes." Not a protocol violation on the
3117 peer's part either -- anything at all can arrive here -- so no
3118 GNUNET_break_op(). */
3120 gettext_noop ("# invalid records discarded"),
3121 1,
3122 GNUNET_NO);
3124 "Discarding record %" PRIu64 "/%" PRIu64 " from `%s':"
3125 " does not deprotect\n",
3126 epoch,
3127 m_seq,
3128 GNUNET_i2s (&kx->peer));
3129 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3130 return;
3131 }
3132 /* Deprotected, so the record is authentic and everything derived from it
3133 may now be committed: the epoch ring, the anti-replay window (RFC 9147,
3134 Section 4.5.1: "The window MUST NOT be updated due to a received record
3135 until that record has been deprotected successfully") and @e timeout.
3136 @e timeout is the only liveness signal CORE has and is what
3137 `gnunet-core -m' reports, so refreshing it any earlier would let
3138 anything merely shaped like a record keep a session nominally alive. */
3139 /* Only ever forward: @e their_max_epoch is the *highest* epoch we have
3140 seen, and the ratchet above keys off it. A record that was merely
3141 reordered across an epoch boundary -- entirely normal, the peer starts
3142 the new epoch at sequence number 0 while the old one is still in flight
3143 -- used to pull it back, so the next record of the newer epoch looked
3144 like a fresh advance and ran the loop again, wiping that epoch's
3145 anti-replay window (RFC 9147, Section 4.5.1) every single time. */
3146 if (kx->their_max_epoch < epoch)
3147 kx->their_max_epoch = epoch;
3148 memcpy (&kx->their_ats,
3149 new_ats,
3150 MAX_EPOCHS * sizeof (struct GNUNET_ShortHashCode));
3151 replay_commit (kx, epoch, m_seq);
3152 update_timeout (kx);
3153
3155 buf,
3156 sizeof buf))
3157 {
3159 {
3161 "Dropping message as we are still waiting for handshake ACK\n");
3162 GNUNET_break_op (0);
3163 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3164 return;
3165 }
3166 if (GNUNET_OK !=
3168 buf,
3169 sizeof buf,
3170 GNUNET_YES,
3171 GNUNET_NO))
3172 GNUNET_break_op (0);
3173 }
3174 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3175}
3176
3177
3187static void
3189 const struct GNUNET_PeerIdentity *peer,
3190 void *handler_cls)
3191{
3192 struct GSC_KeyExchangeInfo *kx = handler_cls;
3193 (void) cls;
3194
3196 "Peer `%s' disconnected from us.\n",
3197 GNUNET_i2s (&kx->peer));
3198 GSC_SESSIONS_end (&kx->peer);
3200 gettext_noop ("# key exchanges stopped"),
3201 1,
3202 GNUNET_NO);
3203 if (NULL != kx->resend_task)
3204 {
3206 kx->resend_task = NULL;
3207 }
3208 if (NULL != kx->resend_env)
3209 {
3211 kx->resend_env = NULL;
3212 }
3213 if (NULL != kx->heartbeat_task)
3214 {
3216 kx->heartbeat_task = NULL;
3217 }
3219 monitor_notify_all (kx);
3220 if (kx->transcript_hash_ctx)
3221 {
3223 kx->transcript_hash_ctx = NULL;
3224 }
3226 GNUNET_MST_destroy (kx->mst);
3227 GNUNET_free (kx);
3228}
3229
3230
3231static void
3233{
3234 struct GSC_KeyExchangeInfo *kx = cls;
3235
3236 kx->resend_task = NULL;
3237 if (0 == kx->resend_tries_left)
3238 {
3239 /* The InitiatorHello we keep repeating carries the ephemeral public key
3240 generated by #send_initiator_hello(), and only #restart_kx() ever
3241 generates a new one. Retrying the same message forever therefore
3242 never recovers from a responder that has dropped the exchange -- it
3243 just keeps a retransmit timer running against a peer that is not
3244 answering. Give up like #resend_responder_hello() and
3245 #resend_initiator_done() do and start a fresh exchange. */
3247 "InitiatorHello not answered by `%s', restarting KX\n",
3248 GNUNET_i2s (&kx->peer));
3249 restart_kx (kx);
3250 return;
3251 }
3252 kx->resend_tries_left--;
3254 "Resending InitiatorHello. Retries left: %u\n",
3255 kx->resend_tries_left);
3258}
3259
3260
3266static void
3268{
3269 const struct GNUNET_PeerIdentity *my_identity;
3270 struct GNUNET_MQ_Envelope *env;
3271 struct GNUNET_ShortHashCode es;
3272 struct GNUNET_ShortHashCode ets;
3273 struct GNUNET_ShortHashCode ss_R;
3274 struct InitiatorHelloPayload *ihmp; /* initiator hello message - buffer on stack */
3275 struct InitiatorHello *ihm_e; /* initiator hello message - encrypted */
3276 long long unsigned int c_len;
3277 unsigned char enc_key[AEAD_KEY_BYTES];
3278 unsigned char enc_nonce[AEAD_NONCE_BYTES];
3280 size_t pt_len;
3281
3283 GNUNET_assert (NULL != my_identity);
3284
3285 pt_len = sizeof (*ihmp) + strlen (my_services_info);
3286 c_len = pt_len + AEAD_TAG_BYTES;
3287 env = GNUNET_MQ_msg_extra (ihm_e,
3288 c_len,
3290 ihmp = (struct InitiatorHelloPayload*) &ihm_e[1];
3291 ihmp->peer_class = htons (GNUNET_CORE_CLASS_UNKNOWN); // TODO set this to a meaningful
3292 GNUNET_memcpy (&ihmp->pk_I,
3294 sizeof (struct GNUNET_PeerIdentity));
3295 GNUNET_CRYPTO_hash (&kx->peer, /* what to hash */ // TODO do we do this twice?
3296 sizeof (struct GNUNET_PeerIdentity),
3297 &ihm_e->h_pk_R); /* result */
3298 // TODO init hashcontext/transcript_hash
3299 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Send InitiatorHello: %d %d\n", kx->role,
3300 kx->status);
3301 GNUNET_assert (NULL == kx->transcript_hash_ctx);
3303 GNUNET_assert (NULL != kx->transcript_hash_ctx);
3304 // TODO fill services_info
3305
3306 // 1. Encaps
3307 ret = GNUNET_CRYPTO_eddsa_kem_encaps (&kx->peer.public_key, // public ephemeral key of initiator
3308 &ihm_e->c_R, // encapsulated key
3309 &ss_R); // key - ss_R
3310 if (GNUNET_OK != ret)
3311 {
3313 "Something went wrong encapsulating ss_R\n");
3314 // TODO handle
3315 }
3316 // 2. generate rR (uint64_t) - is this the nonce? Naming seems not quite
3317 // consistent
3318 ihm_e->r_I =
3319 GNUNET_CRYPTO_random_u64 (UINT64_MAX);
3320 // 3. generate sk_e/pk_e - ephemeral key
3323 &kx->sk_e.ecdhe_key,
3324 &kx->pk_e.ecdhe_key);
3325 GNUNET_memcpy (&ihm_e->pk_e,
3326 &kx->pk_e.ecdhe_key,
3327 sizeof (kx->pk_e.ecdhe_key));
3328 // 4. generate ETS to encrypt
3329 // generate ETS (early_traffic_secret_key, decrypt pk_i
3330 // expand ETS <- expand ES <- extract ss_R
3331 // use ETS to decrypt
3333 ihm_e,
3334 sizeof (struct InitiatorHello));
3335 {
3336 struct GNUNET_HashCode transcript;
3338 &transcript);
3339 derive_es_ets (&transcript,
3340 &ss_R,
3341 &es,
3342 &ets);
3344 0,
3345 enc_key,
3346 enc_nonce);
3347 }
3348 // 5. encrypt
3349
3350 ret = crypto_aead_xchacha20poly1305_ietf_encrypt (
3351 (unsigned char*) &ihm_e[1], /* c - ciphertext */
3352 // mac,
3353 // NULL, // maclen_p
3354 &c_len, /* clen_p */
3355 (unsigned char*) ihmp, /* m - plaintext message */
3356 pt_len, // mlen
3357 NULL, 0, // ad, adlen // FIXME maybe over the unencrypted header?
3358 // fields?
3359 NULL, // nsec - unused
3360 enc_nonce, // npub - nonce
3361 enc_key); // k - key
3362 if (0 != ret)
3363 {
3364 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong encrypting\n");
3366 kx->transcript_hash_ctx = NULL;
3368 return;
3369 }
3370 /* Forward the transcript */
3373 &ihm_e[1],
3374 c_len);
3375
3377 kx->early_secret_key = es;
3378 kx->early_traffic_secret = ets;
3379 kx->ss_R = ss_R;
3380 monitor_notify_all (kx);
3381 GNUNET_MQ_send_copy (kx->mq, env);
3382 kx->resend_env = env;
3384}
3385
3386
3394static enum GNUNET_GenericReturnValue
3396{
3397 struct GNUNET_ShortHashCode new_ats;
3398
3399 if ((UINT64_MAX == kx->current_sqn) ||
3401 {
3403 "Epoch expiration %" PRIu64 " SQN %" PRIu64
3404 ", incrementing epoch...\n",
3406 kx->current_sqn);
3407 if (UINT64_MAX == kx->current_epoch)
3408 {
3409 /* RFC 9147, Section 6.1: "Implementations MUST NOT allow the epoch to
3410 wrap, but instead MUST establish a new association, terminating the
3411 old association". This used to be a GNUNET_assert(). */
3413 "Epoch exhausted for `%s', starting a new association\n",
3414 GNUNET_i2s (&kx->peer));
3415 restart_kx (kx);
3416 return GNUNET_SYSERR;
3417 }
3418 kx->current_epoch++;
3421 kx->current_sqn = 0;
3423 &new_ats);
3424 memcpy (&kx->current_ats,
3425 &new_ats,
3426 sizeof new_ats);
3427 }
3428 return GNUNET_OK;
3429}
3430
3431
3438void
3440 const void *payload,
3441 size_t payload_size)
3442{
3443 struct GNUNET_MQ_Envelope *env;
3444 struct EncryptedMessage *encrypted_msg;
3445 unsigned char enc_key[AEAD_KEY_BYTES];
3446 unsigned char enc_nonce[AEAD_NONCE_BYTES];
3447 unsigned char seq_enc_k[crypto_stream_chacha20_ietf_KEYBYTES];
3448 uint64_t sqn;
3449 uint64_t epoch;
3450 int8_t ret;
3451
3452 encrypted_msg = NULL;
3453
3454 if (GNUNET_YES != kx->association_up)
3455 {
3456 /* No application traffic keys installed -- there is nothing to protect
3457 this with. Callers reach this through a session, which only exists
3458 while the association does, so this is a should-not-happen. */
3459 GNUNET_break (0);
3460 return;
3461 }
3462 if (GNUNET_OK != check_rekey (kx))
3463 return; /* association was torn down, @e current_ats is gone */
3464 sqn = kx->current_sqn;
3465 epoch = kx->current_epoch;
3466 /* We are the sender and as we are going to send,
3467 * we are using the initiator key material */
3469 sqn,
3470 enc_key,
3471 enc_nonce);
3472 kx->current_sqn++;
3473 derive_sn (&kx->current_ats,
3474 seq_enc_k,
3475 sizeof seq_enc_k);
3476 env = GNUNET_MQ_msg_extra (encrypted_msg,
3477 payload_size,
3479 // only encrypt the payload for now
3480 // TODO encrypt other fields as well
3481 ret = crypto_aead_xchacha20poly1305_ietf_encrypt_detached (
3482 (unsigned char*) &encrypted_msg[1], // c - resulting ciphertext
3483 (unsigned char*) &encrypted_msg->tag, // mac - resulting mac/tag
3484 NULL, // maclen
3485 (unsigned char*) payload, // m - plain message
3486 payload_size, // mlen
3487 NULL, // ad - additional data TODO also cover the unencrypted part (epoch)
3488 0, // adlen
3489 NULL, // nsec - unused
3490 enc_nonce, // npub nonce
3491 enc_key // k - key
3492 );
3493 if (0 != ret)
3494 {
3496 "Something went wrong encrypting message\n");
3497 GNUNET_assert (0);
3498 }
3499 {
3500 /* compute the sequence number */
3501 unsigned char *seq_enc_nonce;
3502 uint64_t seq_nbo;
3503 uint32_t seq_enc_ctr;
3504
3505 seq_nbo = GNUNET_htonll (sqn);
3506 seq_enc_ctr = *((uint32_t*) encrypted_msg->tag);
3507 seq_enc_nonce = &encrypted_msg->tag[sizeof (uint32_t)];
3508 crypto_stream_chacha20_ietf_xor_ic (
3509 (unsigned char*) &encrypted_msg->sequence_number,
3510 (unsigned char*) &seq_nbo,
3511 sizeof seq_nbo,
3512 seq_enc_nonce,
3513 ntohl (seq_enc_ctr),
3514 seq_enc_k);
3515#if DEBUG_KX
3516 GNUNET_print_bytes (seq_enc_k,
3517 sizeof seq_enc_k,
3518 8,
3519 GNUNET_NO);
3520 GNUNET_print_bytes ((char*) &seq_enc_ctr,
3521 sizeof seq_enc_ctr,
3522 8,
3523 GNUNET_NO);
3524#endif
3526 "Sending encrypted message with E(SQN=%" PRIu64 ")=%" PRIu64
3527 "\n",
3528 sqn,
3529 encrypted_msg->sequence_number);
3530 }
3531 encrypted_msg->epoch = GNUNET_htonll (epoch);
3532
3533 // TODO actually copy payload
3534 GNUNET_MQ_send (kx->mq, env);
3535}
3536
3537
3538void
3540{
3541 const struct GNUNET_PeerIdentity *my_identity;
3543 GNUNET_MQ_hd_var_size (initiator_hello,
3545 struct InitiatorHello,
3546 NULL),
3547 GNUNET_MQ_hd_var_size (initiator_done,
3549 struct InitiatorDone,
3550 NULL),
3551 GNUNET_MQ_hd_var_size (responder_hello,
3553 struct ResponderHello,
3554 NULL),
3555 GNUNET_MQ_hd_var_size (encrypted_message, // TODO rename?
3557 struct EncryptedMessage,
3558 NULL),
3560 };
3561
3563 GNUNET_assert (NULL != my_identity);
3564
3565 /* Decapsulate with our peer identity's private key directly instead of
3566 round-tripping through the PILS service. The shared secret is needed
3567 in the middle of processing a handshake message, and an asynchronous
3568 answer meant that every InitiatorHello and ResponderHello had to be
3569 parked with its kx across a callback: the kx could be torn down or
3570 freed underneath it, two hellos could be in flight at once, and if
3571 the answer never came (PILS restarting) the handshake stalled *and*
3572 the message was never acknowledged to TRANSPORT. */
3573 if (GNUNET_OK !=
3575 {
3577 _ ("Failed to load our private key, "
3578 "cannot run key exchange\n"));
3579 GSC_KX_done ();
3580 return;
3581 }
3582
3584 transport =
3587 handlers,
3588 NULL, // cls - this connection-independant
3589 // cls seems not to be needed.
3590 // the connection-specific cls
3591 // will be set as a return value
3592 // of
3593 // handle_transport_notify_connect
3596 if (NULL == transport)
3597 {
3598 GSC_KX_done ();
3599 return;
3600 }
3601
3603 "Connected to TRANSPORT\n");
3604
3606}
3607
3608
3609void
3611 const struct GNUNET_HELLO_Parser *parser,
3612 const struct GNUNET_HashCode *hash)
3613{
3614 if (NULL != transport)
3615 return;
3616
3617 GSC_KX_start ();
3618}
3619
3620
3626int
3628{
3631 NULL);
3632 if (NULL == GSC_pils)
3633 {
3634 GSC_KX_done ();
3635 return GNUNET_SYSERR;
3636 }
3637
3638 return GNUNET_OK;
3639}
3640
3641
3645void
3647{
3648 if (NULL != GSC_pils)
3649 {
3651 GSC_pils = NULL;
3652 }
3653 if (NULL != transport)
3654 {
3656 transport = NULL;
3657 }
3658 if (NULL != rekey_task)
3659 {
3661 rekey_task = NULL;
3662 }
3663 if (NULL != nc)
3664 {
3666 nc = NULL;
3667 }
3668}
3669
3670
3677unsigned int
3679{
3680 return GNUNET_MQ_get_length (kxinfo->mq);
3681}
3682
3683
3684int
3686{
3687 return kxinfo->has_excess_bandwidth;
3688}
3689
3690
3699void
3701{
3702 struct GNUNET_MQ_Envelope *env;
3703 struct MonitorNotifyMessage *done_msg;
3704 struct GSC_KeyExchangeInfo *kx;
3705
3707 for (kx = kx_head; NULL != kx; kx = kx->next)
3708 {
3709 struct GNUNET_MQ_Envelope *env_notify;
3710 struct MonitorNotifyMessage *msg;
3711
3713 msg->state = htonl ((uint32_t) kx->status);
3714 msg->peer = kx->peer;
3715 msg->timeout = GNUNET_TIME_absolute_hton (kx->timeout);
3716 GNUNET_MQ_send (mq, env_notify);
3717 }
3719 done_msg->state = htonl ((uint32_t) GNUNET_CORE_KX_ITERATION_FINISHED);
3722}
3723
3724
3725/* end of gnunet-service-core_kx.c */
struct GNUNET_MQ_MessageHandlers handlers[]
Definition 003.c:1
struct GNUNET_MessageHeader * msg
Definition 005.c:2
struct GNUNET_MQ_Envelope * env
Definition 005.c:1
#define GNUNET_CORE_OPTION_SEND_FULL_INBOUND
Client wants all inbound messages in full.
Definition core.h:53
#define GNUNET_CORE_OPTION_SEND_HDR_INBOUND
Client just wants the 4-byte message headers of all inbound messages.
Definition core.h:59
#define gettext_noop(String)
Definition gettext.h:74
static struct GNUNET_ARM_MonitorHandle * m
Monitor connection with ARM.
Definition gnunet-arm.c:103
static int ret
Final status code.
Definition gnunet-arm.c:93
static char * peer_id
Option –peer.
static bool finished
Set to true once we are finished and should exit after sending our final message to the parent.
struct GNUNET_HashCode key
The key used in the DHT.
static int result
Global testing status.
static struct GNUNET_PeerIdentity my_identity
Identity of this peer.
const struct GNUNET_CONFIGURATION_Handle * GSC_cfg
Our configuration.
void GSC_complete_initialization_cb(void)
This function is called from GSC_KX_init() once it got its peer id from pils.
void GSC_CLIENTS_deliver_message(const struct GNUNET_PeerIdentity *sender, const struct GNUNET_MessageHeader *msg, uint16_t msize, uint32_t options)
Deliver P2P message to interested clients.
struct GNUNET_PILS_Handle * GSC_pils
For peer identity access.
struct GNUNET_STATISTICS_Handle * GSC_stats
For creating statistics.
Globals for gnunet-service-core.
#define RESEND_MAX_TRIES
Number of times we retransmit a handshake flight before giving up on it and starting a fresh exchange...
static void * handle_transport_notify_connect(void *cls, const struct GNUNET_PeerIdentity *peer_id, struct GNUNET_MQ_Handle *mq)
Function called by transport to notify us that a peer connected to us (on the network level).
static void cleanup_handshake_secrets(struct GSC_KeyExchangeInfo *kx)
unsigned int GSC_NEIGHBOURS_get_queue_length(const struct GSC_KeyExchangeInfo *kxinfo)
Check how many messages are queued for the given neighbour.
static void replay_commit(struct GSC_KeyExchangeInfo *kx, uint64_t epoch, uint64_t sqn)
Record that a record with sequence number sqn in epoch has been deprotected successfully,...
int GSC_NEIGHBOURS_check_excess_bandwidth(const struct GSC_KeyExchangeInfo *kxinfo)
Check if the given neighbour has excess bandwidth available.
static int check_initiator_hello(void *cls, const struct InitiatorHello *m)
#define MAX_UNANSWERED_HEARTBEATS
How many heartbeats in a row may go unanswered before we give up on the association.
static int check_responder_hello(void *cls, const struct ResponderHello *m)
static struct GSC_KeyExchangeInfo * kx_tail
DLL tail.
static int check_initiator_done(void *cls, const struct InitiatorDone *m)
void GSC_KX_handle_client_monitor_peers(struct GNUNET_MQ_Handle *mq)
Handle GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS request.
static void generate_per_record_nonce(uint64_t seq, const uint8_t write_iv[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES], uint8_t per_record_write_iv[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES])
Generate per record nonce as per https://www.rfc-editor.org/rfc/rfc8446#section-5....
static void handle_responder_hello(void *cls, const struct ResponderHello *rhm_e)
Handle Responder Hello message.
static void send_heartbeat(void *cls)
Task triggered when a neighbour entry is about to time out (and we should prevent this by sending an ...
#define IV_STR
String for expanding derived keys (Handshake and Early) (See https://lsd.gnunet.org/lsd0012/draft-sch...
static void handle_initiator_hello_cont(struct GSC_KeyExchangeInfo *kx, const struct InitiatorHello *ihm_e, const struct GNUNET_ShortHashCode *ss_R)
Finish handling the InitiatorHello ihm_e now that ss_R, the shared secret decapsulated with our peer ...
#define AEAD_TAG_BYTES
libsodium has very long symbol names
#define I_AP_TRAFFIC_STR
String for expanding IATS (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake....
void GSC_KX_start(void)
#define I_FINISHED_STR
String for expanding fk_I used for InitiatorFinished field (See https://lsd.gnunet....
static char * my_services_info
Our services info string TODO.
static void resend_responder_hello(void *cls)
#define REPLAY_WINDOW_SIZE
Size of the per-epoch anti-replay window, in records.
static void handle_transport_notify_disconnect(void *cls, const struct GNUNET_PeerIdentity *peer, void *handler_cls)
Function called by transport telling us that a peer disconnected.
static void handle_responder_hello_cont(struct ResponderHelloCls *rh_ctx, const struct GNUNET_ShortHashCode *ss_I)
Finish handling a ResponderHello now that ss_I, the shared secret decapsulated with our peer identity...
#define MAX_EPOCHS
Maximum number of epochs we keep on hand.
static void derive_ihts(const struct GNUNET_HashCode *transcript, const struct GNUNET_ShortHashCode *hs, struct GNUNET_ShortHashCode *ihts)
Derive the initiator handshake secret.
#define R_FINISHED_STR
String for expanding fk_R used for ResponderFinished field (See https://lsd.gnunet....
static void derive_initial_ats(const struct GNUNET_HashCode *transcript, const struct GNUNET_ShortHashCode *ms, enum GSC_KX_Role role, struct GNUNET_ShortHashCode *initial_ats)
Derive the initiator application secret.
static struct GNUNET_NotificationContext * nc
Notification context for broadcasting to monitors.
#define AEAD_NONCE_BYTES
libsodium has very long symbol names
void GSC_KX_encrypt_and_transmit(struct GSC_KeyExchangeInfo *kx, const void *payload, size_t payload_size)
Encrypt and transmit payload.
#define R_AP_TRAFFIC_STR
String for expanding RATS (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake....
static void derive_next_ats(const struct GNUNET_ShortHashCode *old_ats, struct GNUNET_ShortHashCode *new_ats)
Derive the next application secret.
static void schedule_resend(struct GSC_KeyExchangeInfo *kx, GNUNET_SCHEDULER_TaskCallback cb)
Arm resend_task for the next retransmission of the flight in resend_env and back the timer off for th...
static enum GNUNET_GenericReturnValue replay_check(const struct GSC_KeyExchangeInfo *kx, uint64_t epoch, uint64_t sqn)
Would a record with sequence number sqn in epoch be a replay?
static void buffer_clear(void *buf, size_t len)
#define DECRYPTION_FAILURES_LOG_LEVEL
Enable expensive logging of decryption failures.
static void derive_per_message_secrets(const struct GNUNET_ShortHashCode *ts, uint64_t seq, unsigned char key[crypto_aead_xchacha20poly1305_ietf_KEYBYTES], unsigned char nonce[crypto_aead_xchacha20poly1305_ietf_NPUBBYTES])
key = HKDF-Expand [I,R][A,H]TS, "key", 32) nonce = HKDF-Expand ([I,R][A,H]TS, "iv",...
static void derive_rhts(const struct GNUNET_HashCode *transcript, const struct GNUNET_ShortHashCode *hs, struct GNUNET_ShortHashCode *rhts)
Derive the responder handshake secret.
static void generate_responder_finished(const struct GNUNET_HashCode *transcript, const struct GNUNET_ShortHashCode *ms, struct GNUNET_HashCode *result)
Generate the responder finished field.
static struct GSC_KeyExchangeInfo * kx_head
DLL head.
#define AEAD_KEY_BYTES
libsodium has very long symbol names
GSC_KX_Role
Indicates whether a peer is in the initiating or receiving role.
static void generate_initiator_finished(const struct GNUNET_HashCode *transcript, const struct GNUNET_ShortHashCode *ms, struct GNUNET_HashCode *result)
Generate the initiator finished field.
static void replay_reset_all(struct GSC_KeyExchangeInfo *kx)
Forget every anti-replay window (all of MAX_EPOCHS).
static void reset_handshake(struct GSC_KeyExchangeInfo *kx)
Discard the state of the handshake kx is in the middle of, so that a new one can be started.
static void snapshot_transcript(const struct GNUNET_HashContext *ts_hash, struct GNUNET_HashCode *snapshot)
static void derive_sn(const struct GNUNET_ShortHashCode *secret, unsigned char *sn, size_t sn_len)
#define MIN_HEARTBEAT_FREQUENCY
What is the minimum frequency for a HEARTBEAT message?
#define RESEND_TIMEOUT_MAX
Ceiling for the handshake retransmission timer (RFC 9147, Section 5.8).
void GSC_KX_done()
Shutdown KX subsystem.
static void derive_ms(const struct GNUNET_ShortHashCode *hs, const struct GNUNET_ShortHashCode *ss_I, struct GNUNET_ShortHashCode *ms)
Derive the master secret.
#define KEY_STR
String for expanding derived keys (Handshake and Early) (See https://lsd.gnunet.org/lsd0012/draft-sch...
#define HEARTBEAT_PROBE_FREQUENCY
How long we wait for the Ack to a heartbeat before sending another one.
static void update_timeout(struct GSC_KeyExchangeInfo *kx)
We've seen a valid message from the other peer.
static void derive_hs(const struct GNUNET_ShortHashCode *es, const struct GNUNET_ShortHashCode *ss_e, struct GNUNET_ShortHashCode *handshake_secret)
Derive the handshake secret.
static void send_initiator_hello(struct GSC_KeyExchangeInfo *kx)
Send initiator hello.
#define I_HS_TRAFFIC_STR
String for expanding IHTS (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake....
#define EARLY_DATA_STR
String for expanding early transport secret (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake....
static void handle_initiator_hello(void *cls, const struct InitiatorHello *ihm_e)
Handle the InitiatorHello message.
static void start_resend(struct GSC_KeyExchangeInfo *kx, GNUNET_SCHEDULER_TaskCallback cb)
Start a handshake flight: kx will retransmit it RESEND_MAX_TRIES times, starting after RESEND_TIMEOUT...
static void handle_encrypted_message(void *cls, const struct EncryptedMessage *m)
handle an encrypted message
static void derive_es_ets(const struct GNUNET_HashCode *transcript, const struct GNUNET_ShortHashCode *ss_R, struct GNUNET_ShortHashCode *es, struct GNUNET_ShortHashCode *ets)
TODO propose a new scheme: don't choose an initiator and responder based on hashing the peer ids,...
static int deliver_message(void *cls, const struct GNUNET_MessageHeader *m)
Deliver P2P message to interested clients.
static enum GNUNET_GenericReturnValue check_if_ack_or_heartbeat(struct GSC_KeyExchangeInfo *kx, const char *buf, size_t buf_len)
#define DERIVED_STR
String for expanding derived keys (Handshake and Early) (See https://lsd.gnunet.org/lsd0012/draft-sch...
static void abandon_exchange(struct GSC_KeyExchangeInfo *kx)
Give up on the exchange kx is in and on the session it may have established, and return it to a state...
static int check_encrypted_message(void *cls, const struct EncryptedMessage *m)
Check an incoming encrypted message before handling it.
static void resend_initiator_hello(void *cls)
#define EPOCH_EXPIRATION
How often do we rekey/switch to a new epoch?
static void resend_initiator_done(void *cls)
static enum GNUNET_GenericReturnValue check_rekey(struct GSC_KeyExchangeInfo *kx)
Move to the next epoch if the current one is exhausted.
static void monitor_notify_all(struct GSC_KeyExchangeInfo *kx)
Inform all monitors about the KX state of the given peer.
static struct GNUNET_SCHEDULER_Task * rekey_task
Task scheduled for periodic re-generation (and thus rekeying) of our ephemeral key.
void pid_change_cb(void *cls, const struct GNUNET_HELLO_Parser *parser, const struct GNUNET_HashCode *hash)
#define CAKE_LABEL
Labeled expand label for CAKE.
int GSC_KX_init(void)
Initialize KX subsystem.
static void restart_kx(struct GSC_KeyExchangeInfo *kx)
static struct GNUNET_TRANSPORT_CoreHandle * transport
Transport service.
static void handle_heartbeat(struct GSC_KeyExchangeInfo *kx, const struct Heartbeat *m)
Handle a key update.
static void handle_initiator_done(void *cls, const struct InitiatorDone *idm_e)
Handle InitiatorDone message.
#define R_HS_TRAFFIC_STR
String for expanding RHTS (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake....
void send_responder_hello(struct GSC_KeyExchangeInfo *kx)
#define RESEND_TIMEOUT
Initial handshake retransmission timer.
#define TRAFFIC_UPD_STR
String for expanding derived keys (Handshake and Early) (See https://lsd.gnunet.org/lsd0012/draft-sch...
static void replay_reset(struct GSC_KeyExchangeInfo *kx, uint64_t epoch)
Forget the anti-replay window of epoch.
code for managing the key exchange (SET_KEY, PING, PONG) with other peers
@ GSC_HEARTBEAT_KEY_UPDATE_REQUESTED
A key update is requested.
void GSC_SESSIONS_end(const struct GNUNET_PeerIdentity *pid)
End the session with the given peer (we are no longer connected).
void GSC_SESSIONS_create(const struct GNUNET_PeerIdentity *peer, struct GSC_KeyExchangeInfo *kx, enum GNUNET_CORE_PeerClass class)
Create a session, a key exchange was just completed.
static unsigned long long payload
How much data are we currently storing in the database?
struct GNUNET_CRYPTO_EddsaPrivateKey my_private_key
The current private key.
static struct GNUNET_Process * p
Helper process we started.
Definition gnunet-uri.c:38
commonly used definitions; globals in this file are exempt from the rule that the module name ("commo...
enum GNUNET_GenericReturnValue GNUNET_PILS_enable_private_key(struct GNUNET_PILS_Handle *handle)
Enable local access to the private key of the current peer identity.
Definition pils_api.c:896
struct GNUNET_PILS_Handle * GNUNET_PILS_connect(const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_PILS_PidChangeCallback pid_change_cb, void *cls)
Connect to the PILS service.
Definition pils_api.c:624
void GNUNET_PILS_disconnect(struct GNUNET_PILS_Handle *handle)
Disconnect from the PILS service.
Definition pils_api.c:647
const struct GNUNET_HashCode * GNUNET_PILS_get_identity_hash(const struct GNUNET_PILS_Handle *handle)
Return the hash of the current peer identity from a given handle.
Definition pils_api.c:884
const struct GNUNET_CRYPTO_EddsaPrivateKey * GNUNET_PILS_get_private_key(const struct GNUNET_PILS_Handle *handle)
Return the private key of the current peer identity.
Definition pils_api.c:943
const struct GNUNET_PeerIdentity * GNUNET_PILS_get_identity(const struct GNUNET_PILS_Handle *handle)
Return the current peer identity of a given handle.
Definition pils_api.c:875
Constants for network protocols.
API of the transport service towards the CORE service (TNG version)
struct GNUNET_TRANSPORT_CoreHandle * GNUNET_TRANSPORT_core_connect(const struct GNUNET_CONFIGURATION_Handle *cfg, const struct GNUNET_PeerIdentity *self, const struct GNUNET_MQ_MessageHandler *handlers, void *cls, GNUNET_TRANSPORT_NotifyConnect nc, GNUNET_TRANSPORT_NotifyDisconnect nd)
Connect to the transport service.
void GNUNET_TRANSPORT_core_disconnect(struct GNUNET_TRANSPORT_CoreHandle *handle)
Disconnect from the transport service.
#define GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT
After how long do we consider a connection to a peer dead if we don't receive messages from the peer?
GNUNET_CORE_PeerClass
The peer class gives a hint about the capabilities of a peer.
GNUNET_CORE_KxState
TODO how does this harmonize with CAKE_CRYPTO_ENABLED?
@ GNUNET_CORE_CLASS_UNKNOWN
The device's capabilities are currently unknown.
@ GNUNET_CORE_KX_PEER_DISCONNECT
Last state of a KX (when it is being terminated).
@ GNUNET_CORE_KX_STATE_RESPONDER_CONNECTED
Connected as responder.
@ GNUNET_CORE_KX_STATE_DOWN
No handshake yet.
@ GNUNET_CORE_KX_STATE_INITIATOR_DONE_SENT
We sent initiator done.
@ GNUNET_CORE_KX_STATE_INITIATOR_HELLO_RECEIVED
We've received the initiator hello.
@ GNUNET_CORE_KX_STATE_AWAIT_INITIATION
We are awating the initiator hello.
@ GNUNET_CORE_KX_STATE_INITIATOR_CONNECTED
Connected as initiator.
@ GNUNET_CORE_KX_STATE_INITIATOR_HELLO_SENT
We sent the initiator hello.
@ GNUNET_CORE_KX_STATE_RESPONDER_HELLO_SENT
We sent the responder hello.
@ GNUNET_CORE_KX_ITERATION_FINISHED
This is not a state in a peer's state machine, but a special value used with the GNUNET_CORE_MonitorC...
void GNUNET_CRYPTO_ecdhe_key_create(struct GNUNET_CRYPTO_EcdhePrivateKey *pk)
Create a new private key.
Definition crypto_ecc.c:455
enum GNUNET_GenericReturnValue GNUNET_CRYPTO_eddsa_kem_decaps(const struct GNUNET_CRYPTO_EddsaPrivateKey *priv, const struct GNUNET_CRYPTO_HpkeEncapsulation *c, struct GNUNET_ShortHashCode *prk)
Decapsulate a key for a private EdDSA key.
uint64_t GNUNET_CRYPTO_random_u64(uint64_t max)
Generate a random unsigned 64-bit value.
enum GNUNET_GenericReturnValue GNUNET_CRYPTO_hpke_kem_decaps(const struct GNUNET_CRYPTO_HpkePrivateKey *priv, const struct GNUNET_CRYPTO_HpkeEncapsulation *c, struct GNUNET_ShortHashCode *prk)
Decapsulate a key for a private X25519 key.
enum GNUNET_GenericReturnValue GNUNET_CRYPTO_hpke_kem_encaps(const struct GNUNET_CRYPTO_HpkePublicKey *pkR, struct GNUNET_CRYPTO_HpkeEncapsulation *c, struct GNUNET_ShortHashCode *prk)
Encapsulate key material for a X25519 public key.
enum GNUNET_GenericReturnValue GNUNET_CRYPTO_eddsa_kem_encaps(const struct GNUNET_CRYPTO_EddsaPublicKey *pub, struct GNUNET_CRYPTO_HpkeEncapsulation *c, struct GNUNET_ShortHashCode *prk)
Encapsulate key material for a EdDSA public key.
void GNUNET_CRYPTO_ecdhe_key_get_public(const struct GNUNET_CRYPTO_EcdhePrivateKey *priv, struct GNUNET_CRYPTO_EcdhePublicKey *pub)
Extract the public key for the given private key.
Definition crypto_ecc.c:218
#define GNUNET_CONTAINER_DLL_remove(head, tail, element)
Remove an element from a DLL.
#define GNUNET_CONTAINER_DLL_insert(head, tail, element)
Insert an element at the head of a DLL.
enum GNUNET_GenericReturnValue GNUNET_CRYPTO_hkdf_extract(struct GNUNET_ShortHashCode *prk, const void *salt, size_t salt_len, const void *ikm, size_t ikm_len)
HKDF-Extract using SHA256.
void GNUNET_CRYPTO_hash(const void *block, size_t size, struct GNUNET_HashCode *ret)
Compute hash of a given block.
Definition crypto_hash.c:40
void GNUNET_CRYPTO_hmac(const struct GNUNET_CRYPTO_AuthKey *key, const void *plaintext, size_t plaintext_len, struct GNUNET_HashCode *hmac)
Calculate HMAC of a message (RFC 2104)
#define GNUNET_CRYPTO_hkdf_expand(result, out_len, prk,...)
HKDF-Expand using SHA256.
int GNUNET_CRYPTO_hash_cmp(const struct GNUNET_HashCode *h1, const struct GNUNET_HashCode *h2)
Compare function for HashCodes, producing a total ordering of all hashcodes.
uint16_t type
The type of the message (GNUNET_MESSAGE_TYPE_XXXX), in big-endian format.
#define GNUNET_log(kind,...)
#define GNUNET_B2S(obj)
Convert a fixed-sized object to a string using GNUNET_b2s().
void GNUNET_CRYPTO_hash_context_read(struct GNUNET_HashContext *hc, const void *buf, size_t size)
Add data to be hashed.
struct GNUNET_HashContext * GNUNET_CRYPTO_hash_context_copy(const struct GNUNET_HashContext *hc)
Make a copy of the hash computation.
#define GNUNET_CRYPTO_kdf_arg_string(d)
uint64_t GNUNET_ntohll(uint64_t n)
Convert unsigned 64-bit integer to host byte order.
void * cls
Closure for mv and cb.
void GNUNET_CRYPTO_hash_context_abort(struct GNUNET_HashContext *hc)
Abort hashing, do not bother calculating final result.
#define GNUNET_memcmp(a, b)
Compare memory in a and b, where both must be of the same pointer type.
uint64_t GNUNET_htonll(uint64_t n)
Convert unsigned 64-bit integer to network byte order.
void GNUNET_CRYPTO_hash_context_finish(struct GNUNET_HashContext *hc, struct GNUNET_HashCode *r_hash)
Finish the hash computation.
#define GNUNET_ALIGN
gcc-ism to force alignment; we use this to align char-arrays that may then be cast to 'struct's.
#define GNUNET_CRYPTO_kdf_arg_auto(d)
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
GNUNET_GenericReturnValue
Named constants for return values.
uint16_t size
The length of the struct (in bytes, including the length field itself), in big-endian format.
struct GNUNET_HashContext * GNUNET_CRYPTO_hash_context_start(void)
Start incremental hashing operation.
@ GNUNET_OK
@ GNUNET_YES
@ GNUNET_NO
@ GNUNET_SYSERR
#define GNUNET_break_op(cond)
Use this for assertion violations caused by other peers (i.e.
void GNUNET_print_bytes(const void *buf, size_t buf_len, int fold, int in_be)
Print a byte string in hexadecimal ascii notation.
const char * GNUNET_i2s(const struct GNUNET_PeerIdentity *pid)
Convert a peer identity to a string (for printing debug messages).
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur.
const char * GNUNET_h2s(const struct GNUNET_HashCode *hc)
Convert a hash value to a string (for printing debug messages).
const char * GNUNET_i2s2(const struct GNUNET_PeerIdentity *pid)
Convert a peer identity to a string (for printing debug messages).
@ GNUNET_ERROR_TYPE_WARNING
@ GNUNET_ERROR_TYPE_ERROR
@ GNUNET_ERROR_TYPE_DEBUG
@ GNUNET_ERROR_TYPE_INFO
#define GNUNET_new(type)
Allocate a struct or union of the given type.
#define GNUNET_malloc(size)
Wrapper around malloc.
#define GNUNET_free(ptr)
Wrapper around free.
void GNUNET_notification_context_destroy(struct GNUNET_NotificationContext *nc)
Destroy the context, force disconnect for all subscribers.
Definition nc.c:138
void GNUNET_MQ_send_copy(struct GNUNET_MQ_Handle *mq, const struct GNUNET_MQ_Envelope *ev)
Send a copy of a message with the given message queue.
Definition mq.c:416
unsigned int GNUNET_MQ_get_length(struct GNUNET_MQ_Handle *mq)
Obtain the current length of the message queue.
Definition mq.c:325
void GNUNET_MQ_send(struct GNUNET_MQ_Handle *mq, struct GNUNET_MQ_Envelope *ev)
Send a message with the given message queue.
Definition mq.c:337
#define GNUNET_MQ_handler_end()
End-marker for the handlers array.
void GNUNET_MQ_discard(struct GNUNET_MQ_Envelope *mqm)
Discard the message queue message, free all allocated resources.
Definition mq.c:317
#define GNUNET_MQ_msg_extra(mvar, esize, type)
Allocate an envelope, with extra space allocated after the space needed by the message struct.
struct GNUNET_NotificationContext * GNUNET_notification_context_create(unsigned int queue_length)
Create a new notification context.
Definition nc.c:122
void GNUNET_notification_context_broadcast(struct GNUNET_NotificationContext *nc, const struct GNUNET_MessageHeader *msg, int can_drop)
Send a message to all subscribers of this context.
Definition nc.c:190
#define GNUNET_MQ_msg(mvar, type)
Allocate a GNUNET_MQ_Envelope.
#define GNUNET_MQ_hd_var_size(name, code, str, ctx)
void GNUNET_notification_context_add(struct GNUNET_NotificationContext *nc, struct GNUNET_MQ_Handle *mq)
Add a subscriber to the notification context.
Definition nc.c:161
#define GNUNET_MESSAGE_TYPE_CORE_HEARTBEAT
Message updating the keys of the peers.
#define GNUNET_MESSAGE_TYPE_CORE_ACK
Acknowledgement of prior messages.
#define GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE_CAKE
Encrypted message.
#define GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY
Reply for monitor by CORE service.
#define GNUNET_MESSAGE_TYPE_CORE_INITIATOR_DONE
Third and final message of the handshake, second of the initiator.
#define GNUNET_MESSAGE_TYPE_CORE_RESPONDER_HELLO
Reply to the first message from the initiator - first message sent by the responder.
#define GNUNET_MESSAGE_TYPE_CORE_INITIATOR_HELLO
for more detail on the following messages see https://lsd.gnunet.org/lsd0012/draft-schanzen-cake....
void * GNUNET_SCHEDULER_cancel(struct GNUNET_SCHEDULER_Task *task)
Cancel the task with the specified identifier.
Definition scheduler.c:986
void(* GNUNET_SCHEDULER_TaskCallback)(void *cls)
Signature of the main function of a task.
struct GNUNET_SCHEDULER_Task * GNUNET_SCHEDULER_add_delayed(struct GNUNET_TIME_Relative delay, GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
Schedule a new task to be run with a specified delay.
Definition scheduler.c:1283
enum GNUNET_GenericReturnValue GNUNET_MST_from_buffer(struct GNUNET_MessageStreamTokenizer *mst, const char *buf, size_t size, int purge, int one_shot)
Add incoming data to the receive buffer and call the callback for all complete messages.
Definition mst.c:101
struct GNUNET_MessageStreamTokenizer * GNUNET_MST_create(GNUNET_MessageTokenizerCallback cb, void *cb_cls)
Create a message stream tokenizer.
Definition mst.c:86
void GNUNET_MST_destroy(struct GNUNET_MessageStreamTokenizer *mst)
Destroys a tokenizer.
Definition mst.c:404
void GNUNET_STATISTICS_update(struct GNUNET_STATISTICS_Handle *handle, const char *name, int64_t delta, int make_persistent)
Set statistic value for the peer.
struct GNUNET_TIME_Relative GNUNET_TIME_relative_min(struct GNUNET_TIME_Relative t1, struct GNUNET_TIME_Relative t2)
Return the minimum of two relative time values.
Definition time.c:344
struct GNUNET_TIME_Relative GNUNET_TIME_relative_max(struct GNUNET_TIME_Relative t1, struct GNUNET_TIME_Relative t2)
Return the maximum of two relative time values.
Definition time.c:352
struct GNUNET_TIME_Relative GNUNET_TIME_absolute_get_remaining(struct GNUNET_TIME_Absolute future)
Given a timestamp in the future, how much time remains until then?
Definition time.c:406
struct GNUNET_TIME_Absolute GNUNET_TIME_relative_to_absolute(struct GNUNET_TIME_Relative rel)
Convert relative time to an absolute time in the future.
Definition time.c:316
struct GNUNET_TIME_Relative GNUNET_TIME_relative_multiply(struct GNUNET_TIME_Relative rel, unsigned long long factor)
Multiply relative time by a given factor.
Definition time.c:486
struct GNUNET_TIME_Relative GNUNET_TIME_absolute_get_difference(struct GNUNET_TIME_Absolute start, struct GNUNET_TIME_Absolute end)
Compute the time difference between the given start and end times.
Definition time.c:423
struct GNUNET_TIME_AbsoluteNBO GNUNET_TIME_absolute_hton(struct GNUNET_TIME_Absolute a)
Convert absolute time to network byte order.
Definition time.c:636
bool GNUNET_TIME_absolute_is_past(struct GNUNET_TIME_Absolute abs)
Test if abs is truly in the past (excluding now).
Definition time.c:667
#define GNUNET_TIME_UNIT_FOREVER_ABS
Constant used to specify "forever".
#define max(x, y)
static unsigned int size
Size of the "table".
Definition peer.c:68
#define _(String)
GNU gettext support macro.
Definition platform.h:179
static struct GNUNET_MQ_Handle * mq
Our connection to the resolver service, created on-demand, but then persists until error or shutdown.
static struct GNUNET_TIME_Relative delta
Definition speedup.c:36
struct GNUNET_MessageHeader header
Message type is GNUNET_MESSAGE_TYPE_CORE_ACK.
unsigned char tag[crypto_aead_xchacha20poly1305_ietf_ABYTES]
The Poly1305 tag of the encrypted message (which is starting at sequence_number), used to verify mess...
uint64_t sequence_number
Sequence number, in network byte order.
type for (message) authentication keys
Private ECC key encoded for transmission.
HPKE DHKEM encapsulation (X25519) See RFC 9180.
A public key used for decryption.
struct GNUNET_CRYPTO_EcdhePrivateKey ecdhe_key
An ECDHE/X25519 key.
A public key used for encryption.
struct GNUNET_CRYPTO_EcdhePublicKey ecdhe_key
An ECDHE/X25519 key.
Context for parsing HELLOs.
Definition hello-uri.c:233
A 512-bit hashcode.
Handle to a message queue.
Definition mq.c:87
Message handler for a specific message type.
Header for all communications.
Handle to a message stream tokenizer.
Definition mst.c:45
The notification context is the key datastructure for a convenience API used for transmission of noti...
Definition nc.c:77
The identity of the host (wraps the signing key of the peer).
struct GNUNET_CRYPTO_EddsaPublicKey public_key
Entry in list of pending tasks.
Definition scheduler.c:141
A 256-bit hashcode.
Time for absolute times used by GNUnet, in microseconds.
uint64_t abs_value_us
The actual value.
Time for relative time used by GNUnet, in microseconds.
uint64_t rel_value_us
The actual value.
Handle for the transport service (includes all of the state for the transport service).
Information about the status of a key exchange with another peer.
struct GSC_KeyExchangeInfo * prev
DLL.
struct GNUNET_ShortHashCode their_ats[10]
*ATS - other peers application traffic secret by epoch
struct GNUNET_ShortHashCode ss_R
struct GNUNET_ShortHashCode ihts
IHTS - Initiator handshake secret TODO.
struct GNUNET_TIME_Absolute current_epoch_expiration
Expiration time of our current epoch.
struct GNUNET_ShortHashCode early_secret_key
ES - Early Secret Key TODO uniform naming: _key?
struct GNUNET_ShortHashCode master_secret
Master secret key TODO.
uint64_t current_sqn
Our current sequence number.
struct GNUNET_TIME_Absolute last_notify_timeout
Last time we notified monitors.
enum GSC_KX_Role role
Own role in the key exchange.
struct GNUNET_MessageStreamTokenizer * mst
Our message stream tokenizer (for encrypted payload).
struct GSC_KeyExchangeInfo * next
DLL.
struct GNUNET_CRYPTO_HpkePrivateKey sk_e
Initiator secret key.
unsigned int resend_tries_left
Resend tries left.
struct GNUNET_SCHEDULER_Task * resend_task
Task for resending messages during handshake.
struct GNUNET_HashCode rh_hash
Hash over the entire ResponderHello we are currently answering, or all zeroes if there is none.
struct GNUNET_PeerIdentity peer
Identity of the peer.
struct GNUNET_MQ_Handle * mq
Message queue for sending messages to peer.
struct GNUNET_ShortHashCode early_traffic_secret
ETS - Early traffic secret TODO.
uint64_t their_max_epoch
Highest seen (or used) epoch of responder resp initiator.
int association_up
GNUNET_YES once application traffic keys are installed for this peer, i.e.
struct GNUNET_TIME_Absolute timeout
When should the session time out (if there are no Acks to HEARTBEATs)?
struct GNUNET_MQ_Envelope * resend_env
Env for resending messages.
struct GNUNET_ShortHashCode rhts
RHTS - Responder handshake secret TODO.
struct GNUNET_ShortHashCode ss_I
uint64_t replay_max[10]
Highest sequence number we have successfully deprotected in each epoch; the right edge of that epoch'...
struct GNUNET_ShortHashCode handshake_secret
HS - Handshake secret TODO.
struct GNUNET_ShortHashCode ss_e
int has_excess_bandwidth
GNUNET_YES if this peer currently has excess bandwidth.
struct GNUNET_TIME_Relative resend_delay
How long to wait before the next retransmission of the handshake message in resend_env.
uint64_t replay_bitmap[10]
Anti-replay window for each epoch: bit k is set if the record with sequence number ‘replay_max[i] - k...
enum GNUNET_CORE_KxState status
What is our connection state?
unsigned int heartbeats_unanswered
Heartbeats sent since the last record we deprotected from this peer.
struct GNUNET_CRYPTO_HpkePublicKey pk_e
Initiator ephemeral key.
struct GNUNET_HashCode ih_hash
Hash over the entire InitiatorHello we are currently answering, or all zeroes if there is none.
struct GNUNET_HashContext * transcript_hash_ctx
The transcript hash context.
uint64_t current_epoch
Our currently used epoch for sending.
enum GNUNET_CORE_PeerClass class
Peer class of the other peer TODO still needed?
struct GNUNET_ShortHashCode current_ats
*ATS - our current application traffic secret by epoch
struct GNUNET_SCHEDULER_Task * heartbeat_task
ID of task used for sending keep-alive pings.
struct GNUNET_MessageHeader header
Message type is #GNUNET_MESSAGE_TYPE_CORE_PONG.
uint32_t flags
Flags.
struct GNUNET_HashCode finished
TODO {Finished} - encrypted.
struct GNUNET_PeerIdentity pk_I
Sender Peer ID.
uint16_t peer_class
The peer class of the sending peer TODO part of services info?
uint64_t r_I
Random number to make replay attacks harder.
struct GNUNET_CRYPTO_EcdhePublicKey pk_e
Ephemeral public edx25519 key.
struct GNUNET_CRYPTO_HpkeEncapsulation c_R
Key encapsulation.
struct GNUNET_MessageHeader header
Message type is #GNUNET_MESSAGE_TYPE_CORE_PONG.
struct GNUNET_HashCode h_pk_R
Hash of the responder peer id.
Message sent by the service to monitor clients to notify them about a peer changing status.
Definition core.h:313
uint32_t state
New peer state, an enum GNUNET_CORE_KxState in NBO.
Definition core.h:322
struct GNUNET_TIME_AbsoluteNBO timeout
How long will we stay in this state (if nothing else happens)?
Definition core.h:332
struct GNUNET_ShortHashCode ss_e
struct GNUNET_ShortHashCode ihts
struct GNUNET_ShortHashCode rhts
struct ResponderHello rhm_e
struct ResponderHelloPayload * rhp
struct GSC_KeyExchangeInfo * kx
char finished_enc[sizeof(struct GNUNET_HashCode)+crypto_aead_xchacha20poly1305_ietf_ABYTES]
struct GNUNET_HashContext * hc
struct GNUNET_HashCode decrypted_finish
struct GNUNET_ShortHashCode hs
struct GNUNET_CRYPTO_HpkeEncapsulation c_I
Challenge encapsulation c_I.
struct GNUNET_CRYPTO_HpkeEncapsulation c_e
Ephemeral key encapsulation c_e.
uint64_t r_R
Random number to make replay attacks harder.
struct GNUNET_MessageHeader header
Message type is #GNUNET_MESSAGE_TYPE_CORE_PONG.