GNUnet 0.28.1-dev.4-24-g0cf3356dd
 
Loading...
Searching...
No Matches
gnunet-service-core_kx.c File Reference

TODO: More...

Include dependency graph for gnunet-service-core_kx.c:

Go to the source code of this file.

Data Structures

struct  GSC_KeyExchangeInfo
 Information about the status of a key exchange with another peer. More...
 
struct  ResponderHelloCls
 

Macros

#define DEBUG_KX   0
 Enable expensive (and possibly problematic for privacy!) logging of KX.
 
#define DECRYPTION_FAILURES_LOG_LEVEL   GNUNET_ERROR_TYPE_DEBUG
 Enable expensive logging of decryption failures.
 
#define RESEND_MAX_TRIES   5
 Number of times we retransmit a handshake flight before giving up on it and starting a fresh exchange.
 
#define AEAD_KEY_BYTES   crypto_aead_xchacha20poly1305_ietf_KEYBYTES
 libsodium has very long symbol names
 
#define AEAD_NONCE_BYTES   crypto_aead_xchacha20poly1305_ietf_NPUBBYTES
 libsodium has very long symbol names
 
#define AEAD_TAG_BYTES   crypto_aead_xchacha20poly1305_ietf_ABYTES
 libsodium has very long symbol names
 
#define RESEND_TIMEOUT    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
 Initial handshake retransmission timer.
 
#define RESEND_TIMEOUT_MAX    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
 Ceiling for the handshake retransmission timer (RFC 9147, Section 5.8).
 
#define REPLAY_WINDOW_SIZE   64
 Size of the per-epoch anti-replay window, in records.
 
#define HEARTBEAT_PROBE_FREQUENCY    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
 How long we wait for the Ack to a heartbeat before sending another one.
 
#define MAX_UNANSWERED_HEARTBEATS   3
 How many heartbeats in a row may go unanswered before we give up on the association.
 
#define MIN_HEARTBEAT_FREQUENCY    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
 What is the minimum frequency for a HEARTBEAT message?
 
#define HEARTBEAT_FREQUENCY    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
 How often do we send a heartbeat?
 
#define MAX_EPOCHS   10
 Maximum number of epochs we keep on hand.
 
#define EPOCH_EXPIRATION    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
 How often do we rekey/switch to a new epoch?
 
#define REKEY_TOLERANCE    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
 What time difference do we tolerate?
 
#define EARLY_DATA_STR   "early data"
 String for expanding early transport secret (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)
 
#define R_HS_TRAFFIC_STR   "r hs traffic"
 String for expanding RHTS (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)
 
#define I_HS_TRAFFIC_STR   "i hs traffic"
 String for expanding IHTS (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)
 
#define R_AP_TRAFFIC_STR   "r ap traffic"
 String for expanding RATS (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)
 
#define I_AP_TRAFFIC_STR   "i ap traffic"
 String for expanding IATS (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)
 
#define DERIVED_STR   "derived"
 String for expanding derived keys (Handshake and Early) (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)
 
#define R_FINISHED_STR   "r finished"
 String for expanding fk_R used for ResponderFinished field (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)
 
#define I_FINISHED_STR   "i finished"
 String for expanding fk_I used for InitiatorFinished field (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)
 
#define CAKE_LABEL   "cake10"
 Labeled expand label for CAKE.
 
#define KEY_STR   "key"
 String for expanding derived keys (Handshake and Early) (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)
 
#define TRAFFIC_UPD_STR   "traffic upd"
 String for expanding derived keys (Handshake and Early) (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)
 
#define IV_STR   "iv"
 String for expanding derived keys (Handshake and Early) (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)
 

Enumerations

enum  GSC_KX_Role { ROLE_INITIATOR = 0 , ROLE_RESPONDER = 1 }
 Indicates whether a peer is in the initiating or receiving role. More...
 

Functions

static void buffer_clear (void *buf, size_t len)
 
static void cleanup_handshake_secrets (struct GSC_KeyExchangeInfo *kx)
 
static void replay_reset (struct GSC_KeyExchangeInfo *kx, uint64_t epoch)
 Forget the anti-replay window of epoch.
 
static void replay_reset_all (struct GSC_KeyExchangeInfo *kx)
 Forget every anti-replay window (all of MAX_EPOCHS).
 
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 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, sliding the window right if needed.
 
static void snapshot_transcript (const struct GNUNET_HashContext *ts_hash, struct GNUNET_HashCode *snapshot)
 
static void monitor_notify_all (struct GSC_KeyExchangeInfo *kx)
 Inform all monitors about the KX state of the given peer.
 
static void restart_kx (struct GSC_KeyExchangeInfo *kx)
 
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 Ack in response to a heartbeat).
 
static void update_timeout (struct GSC_KeyExchangeInfo *kx)
 We've seen a valid message from the other peer.
 
static void send_initiator_hello (struct GSC_KeyExchangeInfo *kx)
 Send initiator hello.
 
static int deliver_message (void *cls, const struct GNUNET_MessageHeader *m)
 Deliver P2P message to interested clients.
 
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 the one after that.
 
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 and backing off from there.
 
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 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 in which a fresh handshake can be run.
 
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 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, but: let each peer be their own initiator (and responder) when opening a channel towards another peer.
 
static void derive_sn (const struct GNUNET_ShortHashCode *secret, unsigned char *sn, size_t sn_len)
 
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 derive_ihts (const struct GNUNET_HashCode *transcript, const struct GNUNET_ShortHashCode *hs, struct GNUNET_ShortHashCode *ihts)
 Derive the initiator handshake secret.
 
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 derive_ms (const struct GNUNET_ShortHashCode *hs, const struct GNUNET_ShortHashCode *ss_I, struct GNUNET_ShortHashCode *ms)
 Derive the master secret.
 
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.3 using per key nonce and sequence number.
 
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", 24)
 
static void derive_next_ats (const struct GNUNET_ShortHashCode *old_ats, struct GNUNET_ShortHashCode *new_ats)
 Derive the next application secret.
 
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 void generate_responder_finished (const struct GNUNET_HashCode *transcript, const struct GNUNET_ShortHashCode *ms, struct GNUNET_HashCode *result)
 Generate the responder finished field.
 
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 resend_responder_hello (void *cls)
 
void send_responder_hello (struct GSC_KeyExchangeInfo *kx)
 
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 identity's private key, is available.
 
static int check_initiator_hello (void *cls, const struct InitiatorHello *m)
 
static void handle_initiator_hello (void *cls, const struct InitiatorHello *ihm_e)
 Handle the InitiatorHello message.
 
static void resend_initiator_done (void *cls)
 
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's private key, is available.
 
static int check_responder_hello (void *cls, const struct ResponderHello *m)
 
static void handle_responder_hello (void *cls, const struct ResponderHello *rhm_e)
 Handle Responder Hello message.
 
static int check_initiator_done (void *cls, const struct InitiatorDone *m)
 
static void handle_initiator_done (void *cls, const struct InitiatorDone *idm_e)
 Handle InitiatorDone message.
 
static int check_encrypted_message (void *cls, const struct EncryptedMessage *m)
 Check an incoming encrypted message before handling it.
 
static void handle_heartbeat (struct GSC_KeyExchangeInfo *kx, const struct Heartbeat *m)
 Handle a key update.
 
static enum GNUNET_GenericReturnValue check_if_ack_or_heartbeat (struct GSC_KeyExchangeInfo *kx, const char *buf, size_t buf_len)
 
static void handle_encrypted_message (void *cls, const struct EncryptedMessage *m)
 handle an encrypted message
 
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 resend_initiator_hello (void *cls)
 
static enum GNUNET_GenericReturnValue check_rekey (struct GSC_KeyExchangeInfo *kx)
 Move to the next epoch if the current one is exhausted.
 
void GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo *kx, const void *payload, size_t payload_size)
 Encrypt and transmit payload.
 
void GSC_KX_start (void)
 
void pid_change_cb (void *cls, const struct GNUNET_HELLO_Parser *parser, const struct GNUNET_HashCode *hash)
 
int GSC_KX_init (void)
 Initialize KX subsystem.
 
void GSC_KX_done ()
 Shutdown KX subsystem.
 
unsigned int GSC_NEIGHBOURS_get_queue_length (const struct GSC_KeyExchangeInfo *kxinfo)
 Check how many messages are queued for the given neighbour.
 
int GSC_NEIGHBOURS_check_excess_bandwidth (const struct GSC_KeyExchangeInfo *kxinfo)
 Check if the given neighbour has excess bandwidth available.
 
void GSC_KX_handle_client_monitor_peers (struct GNUNET_MQ_Handle *mq)
 Handle GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS request.
 

Variables

static struct GNUNET_TRANSPORT_CoreHandletransport
 Transport service.
 
static struct GSC_KeyExchangeInfokx_head
 DLL head.
 
static struct GSC_KeyExchangeInfokx_tail
 DLL tail.
 
static struct GNUNET_SCHEDULER_Taskrekey_task
 Task scheduled for periodic re-generation (and thus rekeying) of our ephemeral key.
 
static struct GNUNET_NotificationContextnc
 Notification context for broadcasting to monitors.
 
static char * my_services_info = ""
 Our services info string TODO.
 

Detailed Description

TODO:

  • We need to implement a rekey (+ACK) that periodically rekeys.
  • We may want to reintroduce a heartbeat that needs to be ACKed. Maybe use / merge with KeyUpdate message. It already contains an update_requested field. Maybe rename to Heartbeat and add key_updated field to indicate a field update. That message then always MUST be Acked, if update_requested, then a Heartbeat is expected in response (w/o update_requested of course).

code for managing the key exchange (SET_KEY, PING, PONG) with other peers

Author
Christian Grothoff, ch3

Definition in file gnunet-service-core_kx.c.

Macro Definition Documentation

◆ DEBUG_KX

#define DEBUG_KX   0

Enable expensive (and possibly problematic for privacy!) logging of KX.

Definition at line 51 of file gnunet-service-core_kx.c.

◆ DECRYPTION_FAILURES_LOG_LEVEL

#define DECRYPTION_FAILURES_LOG_LEVEL   GNUNET_ERROR_TYPE_DEBUG

Enable expensive logging of decryption failures.

Note that protocol violating peers may always cause those en masse.

Definition at line 58 of file gnunet-service-core_kx.c.

◆ RESEND_MAX_TRIES

#define RESEND_MAX_TRIES   5

Number of times we retransmit a handshake flight before giving up on it and starting a fresh exchange.

With the exponential backoff of RFC 9147, Section 5.8, the retransmissions go out 1, 3, 7, 15 and 31 seconds into the flight; the flight is then abandoned one further interval after the last of them, at 63s.

Note that the give-up is observed by the next firing of resend_task, which schedule_resend() has already armed with the doubled delay – so the flight always outlasts its last retransmission by one interval, and raising this by one costs far more than the interval it adds.

Definition at line 72 of file gnunet-service-core_kx.c.

◆ AEAD_KEY_BYTES

#define AEAD_KEY_BYTES   crypto_aead_xchacha20poly1305_ietf_KEYBYTES

libsodium has very long symbol names

Definition at line 77 of file gnunet-service-core_kx.c.

◆ AEAD_NONCE_BYTES

#define AEAD_NONCE_BYTES   crypto_aead_xchacha20poly1305_ietf_NPUBBYTES

libsodium has very long symbol names

Definition at line 82 of file gnunet-service-core_kx.c.

◆ AEAD_TAG_BYTES

#define AEAD_TAG_BYTES   crypto_aead_xchacha20poly1305_ietf_ABYTES

libsodium has very long symbol names

Definition at line 87 of file gnunet-service-core_kx.c.

◆ RESEND_TIMEOUT

#define RESEND_TIMEOUT    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)

Initial handshake retransmission timer.

RFC 9147, Section 5.8: "implementations SHOULD use an initial timer value of 1000 ms and double the value at each retransmission, up to no less than 60 seconds."

Definition at line 94 of file gnunet-service-core_kx.c.

235{
236 /* Peer is supposed to initiate the key exchange */
237 ROLE_INITIATOR = 0,
238
239 /* Peer is supposed to wait for the key exchange */
240 ROLE_RESPONDER = 1,
241};
242
243
248{
253
258
263
267 struct GNUNET_MQ_Handle *mq;
268
273
278
279 // TODO check ordering - might make it less confusing
280 // TODO consistent naming: ss_e, shared_secret_e or ephemeral_shared_secret?
281 // TODO consider making all the structs here pointers
282 // - they can be checked to be NULL
283 // - valgrind can detect memory issues better (I guess?)
284
289 enum GSC_KX_Role role;
290
291 // TODO
295
300
305
314
321
328
334
339 struct GNUNET_ShortHashCode early_traffic_secret; /* Decrypts InitiatorHello */
340
346
352
358
364
369
374
378 uint64_t current_epoch;
379
384
389 uint64_t their_max_epoch;
390
396 uint64_t replay_max[MAX_EPOCHS];
397
403 uint64_t replay_bitmap[MAX_EPOCHS];
404
408 uint64_t current_sqn;
409
414
419
424
431
435 unsigned int resend_tries_left;
436
442
448 unsigned int heartbeats_unanswered;
449
455
469 int association_up;
470
475
480 enum GNUNET_CORE_PeerClass class;
481
482};
483
488
492static struct GSC_KeyExchangeInfo *kx_head;
493
497static struct GSC_KeyExchangeInfo *kx_tail;
498
503static struct GNUNET_SCHEDULER_Task *rekey_task;
504
508static struct GNUNET_NotificationContext *nc;
509
513static char *my_services_info = "";
514
515static void
516buffer_clear (void *buf, size_t len)
517{
518#if HAVE_MEMSET_S
519 memset_s (buf, len, 0, len);
520#elif HAVE_EXPLICIT_BZERO
521 explicit_bzero (buf, len);
522#else
523 volatile unsigned char *p = buf;
524 while (len--)
525 *p++ = 0;
526#endif
527}
528
529
530static void
532{
533 buffer_clear (&kx->ihts,
534 sizeof kx->ihts);
535 buffer_clear (&kx->rhts,
536 sizeof kx->rhts);
537 buffer_clear (&kx->sk_e,
538 sizeof kx->sk_e);
539 buffer_clear (&kx->ss_I,
540 sizeof kx->ss_I);
541 buffer_clear (&kx->ss_R,
542 sizeof kx->ss_R);
543 buffer_clear (&kx->ss_e,
544 sizeof kx->ss_e);
546 sizeof kx->master_secret);
548 sizeof kx->early_secret_key);
550 sizeof kx->early_traffic_secret);
552 sizeof kx->handshake_secret);
553}
554
555
565static void
567 uint64_t epoch)
568{
569 kx->replay_max[epoch % MAX_EPOCHS] = 0;
570 kx->replay_bitmap[epoch % MAX_EPOCHS] = 0;
571}
572
573
579static void
581{
582 memset (kx->replay_max, 0, sizeof kx->replay_max);
583 memset (kx->replay_bitmap, 0, sizeof kx->replay_bitmap);
584}
585
586
601replay_check (const struct GSC_KeyExchangeInfo *kx,
602 uint64_t epoch,
603 uint64_t sqn)
604{
605 unsigned int idx = epoch % MAX_EPOCHS;
606 uint64_t max = kx->replay_max[idx];
607 uint64_t behind;
608
609 if (sqn > max)
610 return GNUNET_OK; /* to the right of the window */
611 behind = max - sqn;
612 if (behind >= REPLAY_WINDOW_SIZE)
613 return GNUNET_SYSERR; /* too old to tell, so assume replay */
614 if (0 != (kx->replay_bitmap[idx] & (1ULL << behind)))
615 return GNUNET_SYSERR; /* seen before */
616 return GNUNET_OK;
617}
618
619
628static void
630 uint64_t epoch,
631 uint64_t sqn)
632{
633 unsigned int idx = epoch % MAX_EPOCHS;
634 uint64_t max = kx->replay_max[idx];
635 uint64_t shift;
636
637 if (sqn > max)
638 {
639 shift = sqn - max;
640 kx->replay_bitmap[idx] = (shift >= REPLAY_WINDOW_SIZE)
641 ? 0
642 : (kx->replay_bitmap[idx] << shift);
643 kx->replay_bitmap[idx] |= 1ULL;
644 kx->replay_max[idx] = sqn;
645 return;
646 }
647 kx->replay_bitmap[idx] |= (1ULL << (max - sqn));
648}
649
650
651static void
652snapshot_transcript (const struct GNUNET_HashContext *ts_hash,
653 struct GNUNET_HashCode *snapshot)
654{
655 struct GNUNET_HashContext *tmp;
656
657 tmp = GNUNET_CRYPTO_hash_context_copy (ts_hash);
658 GNUNET_CRYPTO_hash_context_finish (tmp, snapshot);
659}
660
661
667static void
669{
671
673 msg.header.size = htons (sizeof(msg));
674 msg.state = htonl ((uint32_t) kx->status);
675 msg.peer = kx->peer;
676 msg.timeout = GNUNET_TIME_absolute_hton (kx->timeout);
679}
680
681
682static void
684
692static void
693send_heartbeat (void *cls)
694{
695 struct GSC_KeyExchangeInfo *kx = cls;
696 struct GNUNET_TIME_Relative retry;
697 struct GNUNET_TIME_Relative left;
698 struct Heartbeat hb;
699
700 kx->heartbeat_task = NULL;
702 /* A heartbeat is a probe, not a formality: #handle_heartbeat() answers
703 every one of them with an Ack, and that Ack is a record whose
704 deprotection runs #update_timeout() and clears the counter below. So
705 #MAX_UNANSWERED_HEARTBEATS of them in a row without a single record
706 coming back means the association is gone, whatever @e timeout still
707 says. Waiting for @e timeout regardless is what made a lost session
708 cost #GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT (5 min) to notice -- and
709 up to twice that to repair, because the peer that gives up first tears
710 its session down silently and the other one then has to run its own
711 full idle timeout before #restart_kx() gets a chance to re-run the
712 exchange over the virtual link that was there the whole time. */
713 if ((0 == left.rel_value_us) ||
715 {
717 "Session with `%s' timed out (%u heartbeats unanswered)\n",
718 GNUNET_i2s (&kx->peer),
721 gettext_noop ("# sessions terminated by timeout"),
722 1,
723 GNUNET_NO);
724 GSC_SESSIONS_end (&kx->peer);
727 restart_kx (kx);
728 return;
729 }
731 "Sending HEARTBEAT to `%s'\n",
732 GNUNET_i2s (&kx->peer));
734 gettext_noop ("# heartbeat messages sent"),
735 1,
736 GNUNET_NO);
737 hb.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_HEARTBEAT);
738 hb.header.size = htons (sizeof hb);
739 // FIXME when do we request update?
740 hb.flags = 0;
742 GSC_KX_encrypt_and_transmit (kx, &hb, sizeof hb);
743 if (GNUNET_YES != kx->association_up)
744 return; /* #check_rekey() tore it down and restarted the exchange */
745 /* Do not let @e timeout stretch the probe interval: the point of the
746 counter above is that the answer, not the clock, decides. */
749 left),
751 kx->heartbeat_task =
753}
754
755
763static void
765{
767
768 kx->timeout =
770 delta =
772 if (delta.rel_value_us > 5LL * 1000LL * 1000LL)
773 {
774 /* we only notify monitors about timeout changes if those
775 are bigger than the threshold (5s) */
777 }
778 /* The peer answered, so nothing is outstanding any more. */
779 kx->heartbeats_unanswered = 0;
780 if (NULL != kx->heartbeat_task)
782 /* Probe again #HEARTBEAT_PROBE_FREQUENCY after the last thing we heard,
783 not halfway to @e timeout: an idle association that is fine costs one
784 heartbeat and one Ack per interval, while one that is not is noticed
785 within #MAX_UNANSWERED_HEARTBEATS intervals instead of after the full
786 #GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT. A link carrying traffic
787 re-arms this on every record and so still never sends one. */
790 kx);
791}
792
793
799static void
801
802
814static int
815deliver_message (void *cls, const struct GNUNET_MessageHeader *m)
816{
817 struct GSC_KeyExchangeInfo *kx = cls;
818
820 "Decrypted message of type %d from %s\n",
821 ntohs (m->type),
822 GNUNET_i2s (&kx->peer));
824 m,
825 ntohs (m->size),
828 m,
829 sizeof(struct GNUNET_MessageHeader),
831 return GNUNET_OK;
832}
833
834
846static void
849{
850 GNUNET_assert (NULL == kx->resend_task);
852 kx->resend_delay =
855 2));
856}
857
858
866static void
869{
872 schedule_resend (kx, cb);
873}
874
875
884static void
886{
887 /* Any handshake message we were still resending belongs to the exchange
888 we are abandoning here. #send_initiator_hello() /
889 #send_responder_hello() overwrite @e resend_env and @e resend_task
890 without clearing them first, so without this the old envelope leaks
891 and -- worse -- the old task keeps running with its handle lost: a
892 second resend chain that no GNUNET_SCHEDULER_cancel() can reach, still
893 firing on @a kx after #handle_transport_notify_disconnect() has freed
894 it. */
895 if (NULL != kx->resend_task)
896 {
898 kx->resend_task = NULL;
899 }
900 if (NULL != kx->resend_env)
901 {
903 kx->resend_env = NULL;
904 }
905 if (NULL != kx->transcript_hash_ctx)
906 {
908 kx->transcript_hash_ctx = NULL;
909 }
910 /* There is no flight to recognise a retransmission of any more. */
911 memset (&kx->ih_hash, 0, sizeof (kx->ih_hash));
912 memset (&kx->rh_hash, 0, sizeof (kx->rh_hash));
914}
915
916
926static void
928{
929 reset_handshake (kx);
930 GSC_SESSIONS_end (&kx->peer);
932 /* An armed heartbeat task belongs to the association we are dropping
933 here. Left behind it keeps encrypting heartbeats with key material
934 that is no longer current, and #handle_initiator_done() would find it
935 still set. */
936 if (NULL != kx->heartbeat_task)
937 {
939 kx->heartbeat_task = NULL;
940 }
941 kx->heartbeats_unanswered = 0;
942 /* A new association starts over at epoch 0 and sequence number 0.
943 Carrying @e their_max_epoch of a long-lived predecessor into it makes
944 #handle_encrypted_message() reject the first records of the new one as
945 "too old", and a stale anti-replay window would reject them as
946 replays. */
947 kx->their_max_epoch = 0;
948 kx->current_epoch = 0;
949 kx->current_sqn = 0;
950 replay_reset_all (kx);
952}
953
954
955static void
957{
958 const struct GNUNET_HashCode *my_identity_hash;
959 struct GNUNET_HashCode h1;
960
961 // TODO what happens if we're in the middle of a peer id change?
962 // TODO there's a small chance this gets already called when we don't have a
963 // peer id yet. Add a kx, insert into the list, mark it as to be completed
964 // and let the callback to pils finish the rest once we got the peer id
965
967 "Initiating key exchange with peer %s\n",
968 GNUNET_i2s (&kx->peer));
970 gettext_noop ("# key exchanges initiated"),
971 1,
972 GNUNET_NO);
973
974 /* Whatever we still had -- an exchange in progress, an established
975 session, an armed heartbeat -- does not survive this. Drop it before
976 telling monitors where we are, so that they do not see the state of the
977 exchange we are leaving reported as if it were still current: that is
978 why a restart from #GNUNET_CORE_KX_STATE_INITIATOR_HELLO_SENT used to
979 show up as two consecutive "Hello sent (I)" notifications. */
980 abandon_exchange (kx);
982 my_identity_hash = GNUNET_PILS_get_identity_hash (GSC_pils);
983 GNUNET_assert (NULL != my_identity_hash);
984 GNUNET_CRYPTO_hash (&kx->peer, sizeof(struct GNUNET_PeerIdentity), &h1);
985 if (0 < GNUNET_CRYPTO_hash_cmp (&h1, my_identity_hash))
986 {
987 /* peer with "lower" identity starts KX, otherwise we typically end up
988 with both peers starting the exchange and transmit the 'set key'
989 message twice */
991 "I am the initiator, sending hello\n");
992 kx->role = ROLE_INITIATOR;
994 }
995 else
996 {
997 /* peer with "higher" identity starts a delayed KX, if the "lower" peer
998 * does not start a KX since it sees no reasons to do so */
1000 "I am the responder, yielding and await initiator hello\n");
1002 kx->role = ROLE_RESPONDER;
1003 monitor_notify_all (kx);
1004 }
1005}
1006
1007
1018static void *
1020 const struct GNUNET_PeerIdentity *peer_id,
1021 struct GNUNET_MQ_Handle *mq)
1022{
1023 const struct GNUNET_PeerIdentity *my_identity;
1024 struct GSC_KeyExchangeInfo *kx;
1025 (void) cls;
1027 GNUNET_assert (NULL != my_identity);
1028 if (0 == memcmp (peer_id, my_identity, sizeof *peer_id))
1029 {
1031 "Ignoring connection to self\n");
1032 return NULL;
1033 }
1035 "Incoming connection of peer with %s\n",
1037
1038 /* Set up kx struct */
1039 kx = GNUNET_new (struct GSC_KeyExchangeInfo);
1041 kx->mq = mq;
1042 GNUNET_memcpy (&kx->peer, peer_id, sizeof (struct GNUNET_PeerIdentity));
1044
1045 restart_kx (kx);
1046 return kx;
1047}
1048
1049
1089// TODO find a way to assert that a key is not yet existing before generating
1090// TODO find a way to assert that a key is not already existing before using
1091/*
1092 * Derive early secret and transport secret.
1093 * @param kx the key exchange info
1094 */
1095static void
1096derive_es_ets (const struct GNUNET_HashCode *transcript,
1097 const struct GNUNET_ShortHashCode *ss_R,
1098 struct GNUNET_ShortHashCode *es,
1099 struct GNUNET_ShortHashCode *ets)
1100{
1101 uint64_t ret;
1102
1103 ret = GNUNET_CRYPTO_hkdf_extract (es, // prk
1104 0, // salt
1105 0, // salt_len
1106 ss_R, // ikm - initial key material
1107 sizeof (*ss_R));
1108 if (GNUNET_OK != ret)
1109 {
1110 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong extracting ES\n")
1111 ;
1112 GNUNET_assert (0);
1113 }
1115 ets,
1116 sizeof (*ets),
1117 es,
1120 GNUNET_CRYPTO_kdf_arg_auto (transcript));
1121 if (GNUNET_OK != ret)
1122 {
1123 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong expanding ETS\n")
1124 ;
1125 GNUNET_assert (0);
1126 }
1127}
1128
1129
1130/*
1131 * Derive early secret and transport secret.
1132 * @param kx the key exchange info
1133 */
1134static void
1135derive_sn (const struct GNUNET_ShortHashCode *secret,
1136 unsigned char*sn,
1137 size_t sn_len)
1138{
1141 sn,
1142 sn_len,
1143 secret,
1146}
1147
1148
1153static void
1154derive_hs (const struct GNUNET_ShortHashCode *es,
1155 const struct GNUNET_ShortHashCode *ss_e,
1157{
1158 uint64_t ret;
1159 struct GNUNET_ShortHashCode derived_early_secret;
1160
1161 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deriving HS\n");
1163 );
1164 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ss_e: %s\n", GNUNET_B2S (ss_e));
1166 &derived_early_secret,
1167 sizeof (derived_early_secret),
1168 es,
1172 derived_early_secret));
1173 if (GNUNET_OK != ret)
1174 {
1175 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong expanding dES\n")
1176 ;
1177 GNUNET_assert (0);
1178 }
1179 // Handshake secret
1180 // TODO check: are dES the salt and ss_e the ikm or other way round?
1181 ret = GNUNET_CRYPTO_hkdf_extract (handshake_secret, // prk
1182 &derived_early_secret, // salt - dES
1183 sizeof (derived_early_secret), // salt_len
1184 ss_e, // ikm - initial key material
1185 sizeof (*ss_e));
1186 if (GNUNET_OK != ret)
1187 {
1188 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong extracting HS\n")
1189 ;
1190 GNUNET_assert (0);
1191 }
1192}
1193
1194
1199static void
1200derive_ihts (const struct GNUNET_HashCode *transcript,
1201 const struct GNUNET_ShortHashCode *hs,
1202 struct GNUNET_ShortHashCode *ihts)
1203{
1206 ihts, // result
1207 sizeof (*ihts), // result len
1208 hs, // prk?
1211 GNUNET_CRYPTO_kdf_arg_auto (transcript)));
1212}
1213
1214
1219static void
1220derive_rhts (const struct GNUNET_HashCode *transcript,
1221 const struct GNUNET_ShortHashCode *hs,
1222 struct GNUNET_ShortHashCode *rhts)
1223{
1226 rhts,
1227 sizeof (*rhts),
1228 hs, // prk? TODO
1231 GNUNET_CRYPTO_kdf_arg_auto (transcript)));
1232}
1233
1234
1239static void
1240derive_ms (const struct GNUNET_ShortHashCode *hs,
1241 const struct GNUNET_ShortHashCode *ss_I,
1242 struct GNUNET_ShortHashCode *ms)
1243{
1244 uint64_t ret;
1245 struct GNUNET_ShortHashCode derived_handshake_secret;
1246
1248 &derived_handshake_secret,
1249 sizeof (derived_handshake_secret),
1250 hs,
1253 if (GNUNET_OK != ret)
1254 {
1255 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong expanding dHS\n")
1256 ;
1257 GNUNET_assert (0);
1258 }
1259 // TODO check: are dHS the salt and ss_I the ikm or other way round?
1260 ret = GNUNET_CRYPTO_hkdf_extract (ms, // prk
1261 &derived_handshake_secret, // salt - dHS
1262 sizeof (derived_handshake_secret), // salt_len
1263 ss_I, // ikm - initial key material
1264 sizeof (*ss_I));
1265 if (GNUNET_OK != ret)
1266 {
1267 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong extracting MS\n")
1268 ;
1269 GNUNET_assert (0);
1270 }
1271}
1272
1273
1279static void
1281 uint64_t seq,
1282 const uint8_t write_iv[AEAD_NONCE_BYTES],
1283 uint8_t per_record_write_iv[AEAD_NONCE_BYTES])
1284{
1285 uint64_t seq_nbo;
1286 uint64_t *write_iv_ptr;
1287 unsigned int byte_offset;
1288
1289 seq_nbo = GNUNET_htonll (seq);
1290 memcpy (per_record_write_iv,
1291 write_iv,
1293 byte_offset =
1294 AEAD_NONCE_BYTES - sizeof (uint64_t);
1295 write_iv_ptr = (uint64_t*) (per_record_write_iv + byte_offset);
1296 *write_iv_ptr ^= seq_nbo;
1297}
1298
1299
1304static void
1306 const struct GNUNET_ShortHashCode *ts,
1307 uint64_t seq,
1308 unsigned char key[AEAD_KEY_BYTES],
1309 unsigned char nonce[AEAD_NONCE_BYTES])
1310{
1311 unsigned char nonce_tmp[AEAD_NONCE_BYTES];
1312 /* derive actual key */
1315 key,
1317 ts,
1320
1321 /* derive nonce */
1324 nonce_tmp,
1326 ts,
1330 nonce_tmp,
1331 nonce);
1332}
1333
1334
1339static void
1340derive_next_ats (const struct GNUNET_ShortHashCode *old_ats,
1341 struct GNUNET_ShortHashCode *new_ats)
1342{
1343 int8_t ret;
1344
1345 // FIXME: Not sure of PRK and output may overlap here!
1347 new_ats,
1348 sizeof (*new_ats),
1349 old_ats,
1352 if (GNUNET_OK != ret)
1353 {
1355 "Something went wrong deriving next *ATS key\n");
1356 GNUNET_assert (0);
1357 }
1358}
1359
1360
1365static void
1366derive_initial_ats (const struct GNUNET_HashCode *transcript,
1367 const struct GNUNET_ShortHashCode *ms,
1368 enum GSC_KX_Role role,
1369 struct GNUNET_ShortHashCode *initial_ats)
1370{
1371 const char *traffic_str;
1372
1373 if (ROLE_INITIATOR == role)
1374 traffic_str = I_AP_TRAFFIC_STR;
1375 else
1376 traffic_str = R_AP_TRAFFIC_STR;
1379 initial_ats, // result
1380 sizeof (*initial_ats), // result len
1381 ms,
1383 GNUNET_CRYPTO_kdf_arg_string (traffic_str),
1384 GNUNET_CRYPTO_kdf_arg_auto (transcript)));
1385}
1386
1387
1394static void
1395generate_responder_finished (const struct GNUNET_HashCode *transcript,
1396 const struct GNUNET_ShortHashCode *ms,
1397 struct GNUNET_HashCode *result)
1398{
1400 struct GNUNET_CRYPTO_AuthKey fk_R; // We might want to save this in kx?
1401
1403 &fk_R, // result
1404 sizeof (fk_R),
1405 ms,
1408 if (GNUNET_OK != ret)
1409 {
1411 "Something went wrong expanding fk_R\n");
1412 GNUNET_assert (0);
1413 }
1414
1415 GNUNET_CRYPTO_hmac (&fk_R,
1416 transcript,
1417 sizeof (*transcript),
1418 result);
1419}
1420
1421
1428static void
1429generate_initiator_finished (const struct GNUNET_HashCode *transcript,
1430 const struct GNUNET_ShortHashCode *ms,
1431 struct GNUNET_HashCode *result)
1432{
1434 struct GNUNET_CRYPTO_AuthKey fk_I; // We might want to save this in kx?
1435
1437 &fk_I, // result
1438 sizeof (fk_I),
1439 ms,
1442 if (GNUNET_OK != ret)
1443 {
1445 "Something went wrong expanding fk_I\n");
1446 GNUNET_assert (0);
1447 }
1448 GNUNET_CRYPTO_hmac (&fk_I,
1449 transcript,
1450 sizeof (*transcript),
1451 result);
1452}
1453
1454
1455static void
1456resend_responder_hello (void *cls)
1457{
1458 struct GSC_KeyExchangeInfo *kx = cls;
1459
1460 kx->resend_task = NULL;
1461 if (0 == kx->resend_tries_left)
1462 {
1464 "Restarting KX\n");
1465 restart_kx (kx);
1466 return;
1467 }
1468 kx->resend_tries_left--;
1470 "Resending responder hello. Retries left: %u\n",
1471 kx->resend_tries_left);
1474}
1475
1476
1477void
1479{
1482 struct ResponderHello *rhm_e; /* responder hello message - encrypted pointer */
1483 struct GNUNET_MQ_Envelope *env;
1484 struct GNUNET_CRYPTO_HpkeEncapsulation ephemeral_kem_challenge;
1485 struct GNUNET_ShortHashCode rhts;
1486 struct GNUNET_ShortHashCode ihts;
1487 struct GNUNET_ShortHashCode hs;
1488 struct GNUNET_ShortHashCode ms;
1489 struct GNUNET_ShortHashCode ss_e;
1490 struct GNUNET_ShortHashCode ss_I;
1491 struct GNUNET_HashContext *hc;
1492 unsigned char enc_key[AEAD_KEY_BYTES];
1493 unsigned char enc_nonce[AEAD_NONCE_BYTES];
1494
1495 // 4. encaps -> shared_secret_e, c_e (kemChallenge)
1496 // TODO potentially write this directly into rhm?
1497 ret = GNUNET_CRYPTO_hpke_kem_encaps (&kx->pk_e, // public ephemeral key of initiator
1498 &ephemeral_kem_challenge, // encapsulated key
1499 &ss_e); // key - ss_e
1500 if (GNUNET_OK != ret)
1501 {
1503 "Something went wrong encapsulating ss_e\n");
1504 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1505 return;
1506 }
1508 // 6. encaps -> shared_secret_I, c_I
1509 ret = GNUNET_CRYPTO_eddsa_kem_encaps (&kx->peer.public_key, // public key of I
1510 &c_I, // encapsulated key
1511 &ss_I); // where to write the key material
1512 if (GNUNET_OK != ret)
1513 {
1515 "Something went wrong encapsulating ss_I\n");
1517 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1518 return;
1519 }
1520 // 7. generate RHTS (responder_handshare_secret_key) and RATS (responder_application_traffic_secret_key) (section 5)
1521 {
1522 struct GNUNET_HashCode transcript;
1523 snapshot_transcript (hc, &transcript);
1524#if DEBUG_KX
1526 "Transcript snapshot for derivation of HS, MS: `%s'\n",
1527 GNUNET_h2s (&transcript));
1528#endif
1530 &ss_e,
1531 &hs);
1532 derive_ms (&hs, &ss_I, &ms);
1533 }
1534
1535 // send ResponderHello
1536 // TODO fill fields / services_info!
1537 // 1. r_R <- random
1538 struct ResponderHelloPayload *rhp;
1539 size_t rhp_len = sizeof (*rhp) + strlen (my_services_info);
1540 unsigned char rhp_buf[rhp_len];
1541 size_t ct_len;
1542
1543 rhp = (struct ResponderHelloPayload*) rhp_buf;
1544 ct_len = rhp_len // ResponderHelloPayload, fist PT msg
1545 + sizeof (struct GNUNET_HashCode) // Finished hash, second PT msg
1546 + AEAD_TAG_BYTES * 2; // Two tags;
1547 env = GNUNET_MQ_msg_extra (rhm_e,
1548 ct_len,
1550
1551 rhm_e->r_R =
1552 GNUNET_CRYPTO_random_u64 (UINT64_MAX);
1553
1554 // c_e
1555 GNUNET_memcpy (&rhm_e->c_e,
1556 &ephemeral_kem_challenge,
1557 sizeof (ephemeral_kem_challenge));
1559 rhm_e,
1560 sizeof (struct ResponderHello));
1561 // 2. Encrypt ServicesInfo and c_I with RHTS
1562 // derive RHTS
1563 {
1564 struct GNUNET_HashCode transcript;
1566 &transcript);
1567#if DEBUG_KX
1569 "Transcript snapshot for derivation of *HTS: `%s'\n",
1570 GNUNET_h2s (&transcript));
1571#endif
1572 derive_rhts (&transcript,
1573 &hs,
1574 &rhts);
1575 derive_ihts (&transcript,
1576 &hs,
1577 &ihts);
1579 0,
1580 enc_key,
1581 enc_nonce);
1582 }
1583 // c_I
1584 GNUNET_memcpy (&rhp->c_I, &c_I, sizeof (c_I));
1585 // Services info empty for now.
1586 GNUNET_memcpy (&rhp[1],
1588 strlen (my_services_info));
1589
1590 {
1591 unsigned long long out_ct_len;
1593 struct GNUNET_HashCode transcript;
1594 unsigned char *finished_buf;
1595 GNUNET_assert (0 == crypto_aead_xchacha20poly1305_ietf_encrypt (
1596 (unsigned char*) &rhm_e[1], /* c - ciphertext */
1597 &out_ct_len, /* clen_p */
1598 rhp_buf, /* rhm_p - plaintext message */
1599 rhp_len, // mlen
1600 NULL, 0, // ad, adlen // FIXME should this not be the other, unencrypted
1601 // fields?
1602 NULL, // nsec - unused
1603 enc_nonce, // npub - nonce // FIXME nonce can be reused
1604 enc_key)); // k - key RHTS
1606 "Encrypted and wrote %llu bytes\n",
1607 out_ct_len);
1608 // 3. Create ResponderFinished (Section 6)
1609 // Derive fk_I <- HKDF-Expand (MS, "r finished", NULL)
1610 /* Forward the transcript */
1611 /* {svcinfo, c_I}RHTS */
1613 hc,
1614 &rhm_e[1],
1615 out_ct_len);
1616
1617 finished_buf = ((unsigned char*) &rhm_e[1]) + out_ct_len;
1619 &transcript);
1620#if DEBUG_KX
1622 "Transcript snapshot for derivation of Rfinished: `%s'\n",
1623 GNUNET_h2s (&transcript));
1624#endif
1625 generate_responder_finished (&transcript,
1626 &ms,
1627 &finished);
1628 // 4. Encrypt ResponderFinished
1630 1,
1631 enc_key,
1632 enc_nonce);
1633 GNUNET_assert (0 == crypto_aead_xchacha20poly1305_ietf_encrypt (
1634 finished_buf, /* c - ciphertext */
1635 &out_ct_len, /* clen_p */
1636 (unsigned char*) &finished, /* rhm_p - plaintext message */
1637 sizeof (finished), // mlen
1638 NULL, 0, // ad, adlen // FIXME should this not be the other, unencrypted
1639 // fields?
1640 NULL, // nsec - unused
1641 enc_nonce, // npub
1642 enc_key)); // k - key RHTS
1644 "Encrypted and wrote %llu bytes\n",
1645 out_ct_len);
1646 /* Forward the transcript
1647 * after responder finished,
1648 * before deriving *ATS and generating finished_I
1649 * (finished_I will be generated when receiving the InitiatorFinished message
1650 * in order to check it) */
1652 hc,
1653 finished_buf,
1654 out_ct_len);
1655 // 5. optionally send application data - encrypted with RATS
1656 // We do not really have any application data, instead, we send the ACK
1658 &transcript);
1659#if DEBUG_KX
1661 "Transcript snapshot for derivation of *ATS: `%s'\n",
1662 GNUNET_h2s (&transcript));
1663#endif
1664 derive_initial_ats (&transcript,
1665 &ms,
1667 &kx->current_ats);
1668 }
1669 /* Lock into struct */
1671 kx->transcript_hash_ctx = hc;
1672 kx->master_secret = ms;
1673 kx->handshake_secret = hs;
1674 kx->ss_e = ss_e;
1675 kx->ihts = ihts;
1676 kx->rhts = rhts;
1677 kx->ss_I = ss_I;
1678 kx->current_epoch = 0;
1679 kx->current_sqn = 0;
1681 kx->current_sqn,
1682 enc_key,
1683 enc_nonce);
1684
1685 GNUNET_MQ_send_copy (kx->mq, env);
1686 kx->resend_env = env;
1687 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sent ResponderHello: %d %d\n", kx->role,
1688 kx->status);
1691 monitor_notify_all (kx);
1692 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1693}
1694
1695
1705static void
1707 const struct InitiatorHello *ihm_e,
1708 const struct GNUNET_ShortHashCode *ss_R)
1709{
1710 const struct GNUNET_HashCode *my_identity_hash;
1711 uint32_t ihm_len = ntohs (ihm_e->header.size);
1712 unsigned char enc_key[AEAD_KEY_BYTES];
1713 unsigned char enc_nonce[AEAD_NONCE_BYTES];
1714 struct GNUNET_PeerIdentity peer_before = kx->peer;
1715 struct GNUNET_HashCode h1;
1716 struct GNUNET_HashCode transcript;
1717 struct GNUNET_ShortHashCode es;
1718 struct GNUNET_ShortHashCode ets;
1720
1722 &ihm_e->pk_e,
1723 sizeof (ihm_e->pk_e));
1724 // 5. generate ETS (early_traffic_secret_key, decrypt pk_i
1725 // expand ETS <- expand ES <- extract ss_R
1726 // use ETS to decrypt
1727
1728 /* Forward the transcript hash context over the unencrypted fields to get it
1729 * to the same status that the initiator had when it needed to derive es and
1730 * ets for the encryption */
1733 ihm_e,
1734 sizeof (struct InitiatorHello));
1736 &transcript);
1737#if DEBUG_KX
1739 "Transcript snapshot for derivation of ES, ETS: `%s'\n",
1740 GNUNET_h2s (&transcript));
1741#endif
1742 derive_es_ets (&transcript, ss_R, &es, &ets);
1744 0,
1745 enc_key,
1746 enc_nonce);
1747 {
1748 struct InitiatorHelloPayload *ihmp;
1749 size_t ct_len = ihm_len - sizeof (struct InitiatorHello);
1750 unsigned char ihmp_buf[ct_len - AEAD_TAG_BYTES];
1751 ihmp = (struct InitiatorHelloPayload*) ihmp_buf;
1752 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
1753 ihmp_buf, // unsigned char *m
1754 NULL, // mlen_p message length
1755 NULL, // unsigned char *nsec - unused: NULL
1756 (unsigned char*) &ihm_e[1], // const unsigned char *c - ciphertext
1757 ct_len, // unsigned long long clen - length of ciphertext
1758 // mac, // const unsigned char *mac - authentication tag
1759 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
1760 0, // unsigned long long adlen
1761 enc_nonce, // const unsigned char *npub - nonce
1762 enc_key // const unsigned char *k - key
1763 );
1764 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pid_sender: %s\n",
1765 GNUNET_i2s (&ihmp->pk_I));
1766 if (0 != ret)
1767 {
1769 "Something went wrong decrypting: %d\n", ret);
1770 GNUNET_break_op (0);
1771 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1772 restart_kx (kx);
1773 return;
1774 }
1775 /* now forward it considering the encrypted messages that the initiator was
1776 * able to send after deriving the es and ets */
1778 &ihm_e[1],
1779 ct_len);
1780 GNUNET_memcpy (&kx->peer,
1781 &ihmp->pk_I,
1782 sizeof (struct GNUNET_PeerIdentity));
1783 }
1784
1785 /* @e pk_I is the initiator's *claim* about who it is, and nothing has
1786 checked it. It must be the peer transport handed us this @a kx for:
1787 @e role was derived from that identity, GSC_SESSIONS_create() below
1788 keys the session on it, and transport routes everything we send by it.
1789 Letting the claim through means one peer can make us run a session
1790 under another peer's identity, and -- via the role comparison right
1791 below, which is computed over exactly this value -- can pick a @e pk_I
1792 that sends us down the reject path at will. */
1793 if (0 != GNUNET_memcmp (&kx->peer, &peer_before))
1794 {
1795 GNUNET_break_op (0);
1797 "InitiatorHello from `%s' claims to be `%s'\n",
1798 GNUNET_i2s (&peer_before),
1799 GNUNET_i2s2 (&kx->peer));
1800 kx->peer = peer_before;
1802 kx->transcript_hash_ctx = NULL;
1804 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1805 return;
1806 }
1807
1808 my_identity_hash = GNUNET_PILS_get_identity_hash (GSC_pils);
1809 GNUNET_assert (NULL != my_identity_hash);
1810
1811 // We could follow with the rest of the Key Schedule (dES, HS, ...) for now
1812 /* Check that we are actually in the receiving role */
1813 GNUNET_CRYPTO_hash (&kx->peer, sizeof(struct GNUNET_PeerIdentity), &h1);
1814 if (0 < GNUNET_CRYPTO_hash_cmp (&h1, my_identity_hash))
1815 {
1816 /* peer with "lower" identity starts KX, otherwise we typically end up
1817 with both peers starting the exchange and transmit the 'set key'
1818 message twice */
1819 /* Something went wrong - we have the lower value and should have sent the
1820 * InitiatorHello, but instead received it. TODO handle this case
1821 * We might end up in this case if the initiator didn't initiate the
1822 * handshake long enough and the 'responder' initiates the handshake */
1824 "Something went wrong - we have the lower value and should have sent the InitiatorHello, but instead received it.\n");
1826 kx->transcript_hash_ctx = NULL;
1827 /* Same reason the three other reject paths in #handle_initiator_hello()
1828 do this: that function set @e status to INITIATOR_HELLO_RECEIVED
1829 before calling us, and leaving it there makes every *later* hello hit
1830 the "Already received InitiatorHello" guard and be dropped, forever.
1831 Rejecting this hello must not cost us the next one. */
1833 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1834 return;
1835 }
1836
1837 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer ID of other peer: %s\n", GNUNET_i2s
1838 (&kx->peer));
1839 /* We update the monitoring peers here because now we know
1840 * that we can decrypt the message AND know the PID
1841 */
1842 monitor_notify_all (kx);
1843 kx->ss_R = *ss_R;
1844 kx->early_secret_key = es;
1845 kx->early_traffic_secret = ets;
1847}
1848
1849
1850static int
1851check_initiator_hello (void *cls, const struct InitiatorHello *m)
1852{
1853 uint16_t size = ntohs (m->header.size);
1854
1855 if (size < sizeof (*m)
1856 + sizeof (struct InitiatorHelloPayload)
1858 {
1859 return GNUNET_SYSERR;
1860 }
1861 return GNUNET_OK;
1862}
1863
1864
1873static void
1874handle_initiator_hello (void *cls, const struct InitiatorHello *ihm_e)
1875{
1876 const struct GNUNET_HashCode *my_identity_hash;
1878 struct GSC_KeyExchangeInfo *kx = cls;
1879 struct GNUNET_HashCode ih_hash;
1880 struct GNUNET_ShortHashCode ss_R;
1881 size_t ihm_len;
1882
1883 ihm_len = ntohs (ihm_e->header.size);
1884 GNUNET_CRYPTO_hash (ihm_e,
1885 ihm_len,
1886 &ih_hash);
1887 if (ROLE_INITIATOR == kx->role)
1888 {
1889 GNUNET_break_op (0);
1891 "I am an initiator! Tearing down...\n");
1892 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1893 return;
1894 }
1896 {
1897 /* Now that the decapsulation is synchronous nothing can observe this
1898 state from the outside -- #handle_initiator_hello_cont() runs before
1899 we return. Keep the guard anyway: reaching it means the state
1900 machine leaked a state, not that a peer did anything. */
1901 GNUNET_break (0);
1903 "Already received InitiatorHello: %d %d\n", kx->role, kx->status
1904 );
1905 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1906 return;
1907 }
1909 {
1910 if (0 == GNUNET_memcmp (&ih_hash,
1911 &kx->ih_hash))
1912 {
1913 /* Not a new exchange at all: the initiator resent the very hello we
1914 are already answering, because our ResponderHello did not make it
1915 back in time (#resend_initiator_hello() sends a copy of the same
1916 envelope, so a retransmission is byte-identical).
1917
1918 Starting over here is what breaks the pair. A fresh
1919 #send_responder_hello() picks a new @e ss_e and feeds a new
1920 ResponderHello into the transcript, and the transcript is what both
1921 @e finished_R and @e finished_I are computed over. The initiator
1922 answers whichever ResponderHello reaches it first and binds its
1923 InitiatorDone to *that* transcript, while we have moved on to the
1924 transcript of our latest one -- so #handle_initiator_done() cannot
1925 verify @e finished_I and drops it, every retransmission included.
1926 Neither side can make progress and neither side sees an error: the
1927 initiator sits in #GNUNET_CORE_KX_STATE_INITIATOR_DONE_SENT
1928 reporting "Unexpected ResponderHello", we sit in
1929 #GNUNET_CORE_KX_STATE_RESPONDER_HELLO_SENT, and both merely run out
1930 of retries after RESEND_MAX_TRIES and start over -- with no reason
1931 for the next attempt to be any luckier. One InitiatorHello
1932 retransmission, which any hiccup on the path produces, is enough to
1933 lose the peer indefinitely.
1934
1935 Retransmit our flight instead and leave the handshake state alone,
1936 per RFC 9147, Section 5.8: "implementations MUST retransmit their
1937 last flight in response to a retransmitted flight from the peer".
1938 Our own @e resend_task keeps its schedule; this only adds the
1939 answer the initiator is waiting for. */
1941 "InitiatorHello repeated by `%s' in state %d\n",
1942 GNUNET_i2s (&kx->peer),
1943 kx->status);
1945 gettext_noop (
1946 "# InitiatorHello retransmissions received"),
1947 1,
1948 GNUNET_NO);
1950 (NULL != kx->resend_env))
1952 kx->resend_env);
1953 /* Past that state the initiator already had our ResponderHello (we
1954 only leave it once @e finished_I verifies), so this is a duplicate
1955 that crossed with its InitiatorDone. Nothing to answer, and
1956 nothing that may cost us the association we just built. */
1957 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1958 return;
1959 }
1960 /* The initiator has given up on whatever we still hold and started
1961 over. Only the initiator drives this handshake, so follow it rather
1962 than dropping the hello.
1963 This used to return, which deadlocks the pair whenever we are in
1964 #GNUNET_CORE_KX_STATE_RESPONDER_CONNECTED: an InitiatorHello is not
1965 an EncryptedMessage and so does not refresh @e timeout, and nothing
1966 else ever leaves that state, so we would reject every retransmit
1967 until our own idle timeout fires -- five minutes during which the
1968 initiator restarts its exchange every 50s and we report the peer as
1969 connected. In #GNUNET_CORE_KX_STATE_RESPONDER_HELLO_SENT it is a
1970 plain retransmit: the initiator resends precisely because it did not
1971 get our ResponderHello, and answering the hello it actually sent
1972 converges instead of leaving both sides to turn over on unrelated
1973 50s timers that need not ever re-phase.
1974
1975 This is RFC 9147, Section 5.11: "In cases where a server believes it
1976 has an existing association [...] and it receives an epoch=0
1977 ClientHello, it SHOULD proceed with a new handshake but MUST NOT
1978 destroy the existing association until the client has demonstrated
1979 reachability [...] by completing a complete handshake including
1980 delivering a verifiable Finished message."
1981
1982 So only the handshake state goes. An InitiatorHello is not
1983 authenticated -- @e finished_I in the InitiatorDone is our Finished
1984 -- and must not be able to cost us an association on its own. What
1985 we have keeps its traffic keys (@e association_up stays set, and the
1986 record layer keys off that rather than off @e status), its
1987 @e heartbeat_task and its @e timeout, and clients keep being told the
1988 peer is connected. #handle_initiator_done() does the swap once, and
1989 only once, @e finished_I verifies. If it never does, the old
1990 association dies of its own idle timeout exactly as it would have. */
1992 "Peer `%s' restarted the key exchange in state %d, following\n",
1993 GNUNET_i2s (&kx->peer),
1994 kx->status);
1995 reset_handshake (kx);
1996 }
1997 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received InitiatorHello: %d %d\n", kx->
1998 role, kx->status);
1999 GNUNET_assert (NULL == kx->transcript_hash_ctx);
2001 GNUNET_assert (NULL != kx->transcript_hash_ctx);
2002
2004 gettext_noop ("# key exchanges initiated"),
2005 1,
2006 GNUNET_NO);
2007
2009
2010 my_identity_hash = GNUNET_PILS_get_identity_hash (GSC_pils);
2011 GNUNET_assert (NULL != my_identity_hash);
2012
2013 // 1. verify type _INITIATOR_HELLO
2014 // - This is implicytly done by arriving within this handler
2015 // - or is this about verifying the 'additional data' part of aead?
2016 // should it check the encryption + mac? (is this implicitly done
2017 // while decrypting?)
2018 // 2. verify H(pk_R) matches pk_R
2019 if (0 != memcmp (&ihm_e->h_pk_R,
2020 my_identity_hash,
2021 sizeof (struct GNUNET_HashCode)))
2022 {
2024 "This message is not meant for us (H(PID) mismatch)\n");
2026 kx->transcript_hash_ctx = NULL;
2027 /* Leaving @e status at #GNUNET_CORE_KX_STATE_INITIATOR_HELLO_RECEIVED
2028 here wedges the kx: every later hello then hits the "already
2029 received" guard above and is dropped, forever. */
2031 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2032 return;
2033 }
2034 // FIXME this sometimes triggers in the tests - why?
2035 // 3. decaps -> shared_secret_R, c_R (kemChallenge)
2036 /* From here on this is the hello we answer, so a byte-identical one is a
2037 retransmission of it and must not restart the exchange. */
2038 kx->ih_hash = ih_hash;
2040 if (NULL == my_private_key)
2041 {
2042 /* #GSC_KX_start() enables local key access before we ever talk to
2043 TRANSPORT, so this means the key on disk does not match the identity
2044 PILS announced. We cannot answer any hello in that state. */
2046 "No private key for our peer identity, cannot answer hello"
2047 " from `%s'\n",
2048 GNUNET_i2s (&kx->peer));
2050 kx->transcript_hash_ctx = NULL;
2052 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2053 return;
2054 }
2055 if (GNUNET_OK !=
2057 &ihm_e->c_R,
2058 &ss_R))
2059 {
2060 GNUNET_break_op (0);
2062 "Failed to decapsulate c_R of hello from `%s'\n",
2063 GNUNET_i2s (&kx->peer));
2065 kx->transcript_hash_ctx = NULL;
2067 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2068 return;
2069 }
2071 ihm_e,
2072 &ss_R);
2073}
2074
2075
2076struct ResponderHelloCls
2077{
2078 /* Current KX session */
2079 struct GSC_KeyExchangeInfo *kx;
2080
2081 /* responder hello message - encrypted */
2082 struct ResponderHello rhm_e;
2083
2084 /* responder hello message - plain/decrypted */
2085 struct ResponderHelloPayload *rhp;
2086
2087 /* Decrypted finish hash */
2089
2090 /* Encrypted finished CT (for transcript later) */
2091 char finished_enc[sizeof (struct GNUNET_HashCode)
2092 + AEAD_TAG_BYTES];
2093
2094 /* Temporary transcript context */
2095 struct GNUNET_HashContext *hc;
2096
2097 /* Temporary handshake secret */
2098 struct GNUNET_ShortHashCode hs;
2099
2100 /* Temporary handshake secret */
2102
2103 /* Temporary handshake secret */
2105
2106 /* Temporary handshake secret */
2108};
2109
2110static void
2111resend_initiator_done (void *cls)
2112{
2113 struct GSC_KeyExchangeInfo *kx = cls;
2114
2115 kx->resend_task = NULL;
2116 if (0 == kx->resend_tries_left)
2117 {
2119 "Restarting KX\n");
2120 restart_kx (kx);
2121 return;
2122 }
2123 kx->resend_tries_left--;
2125 "Resending initiator done. Retries left: %u\n",
2126 kx->resend_tries_left);
2129}
2130
2131
2141static void
2143 const struct GNUNET_ShortHashCode *ss_I)
2144{
2145 struct GSC_KeyExchangeInfo *kx = rh_ctx->kx;
2146 struct InitiatorDone *idm_e; /* encrypted */
2147 struct InitiatorDone idm_local;
2148 struct InitiatorDone *idm_p; /* plaintext */
2149 struct GNUNET_MQ_Envelope *env;
2150 unsigned char enc_key[AEAD_KEY_BYTES];
2151 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2152 struct ConfirmationAck ack_i;
2153 struct GNUNET_HashCode transcript;
2154 struct GNUNET_ShortHashCode ms;
2155
2156 // XXX valgrind reports uninitialized memory
2157 // the following is a way to check whether this memory was meant
2158 // memset (&rhm_local, 0, sizeof (rhm_local)); - adapt to cls if still needed
2159 memset (&idm_local, 0, sizeof (idm_local));
2160
2161 kx->ss_I = *ss_I;
2162
2163 /* derive *ATS */
2164 derive_ms (&rh_ctx->hs, ss_I, &ms);;
2165 // 5. Create ResponderFinished as per Section 6 and check against decrypted payload.
2166 struct GNUNET_HashCode responder_finished;
2167 // Transcript updates, snapshot again
2168 snapshot_transcript (rh_ctx->hc,
2169 &transcript);
2170#if DEBUG_KX
2172 "Transcript snapshot for derivation of Rfinished: `%s'\n",
2173 GNUNET_h2s (&transcript));
2174#endif
2175 generate_responder_finished (&transcript,
2176 &ms,
2177 &responder_finished);
2178 if (0 != memcmp (&rh_ctx->decrypted_finish,
2179 &responder_finished,
2180 sizeof (struct GNUNET_HashCode)))
2181 {
2182 /* A peer that answers our InitiatorHello with a ResponderHello whose
2183 finished field does not verify must not be able to abort us; this
2184 used to be a GNUNET_assert (0). */
2185 GNUNET_break_op (0);
2187 "Could not verify \"responder finished\" from `%s'\n",
2188 GNUNET_i2s (&kx->peer));
2189 GNUNET_free (rh_ctx->rhp);
2191 GNUNET_free (rh_ctx);
2192 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2193 restart_kx (kx);
2194 return;
2195 }
2196
2197
2198 /* Forward the transcript
2199 * after generating finished_R,
2200 * before deriving *ATS */
2202 rh_ctx->hc,
2203 rh_ctx->finished_enc,
2204 sizeof (rh_ctx->finished_enc));
2205
2206 // At this point we cannot fail anymore and may lock into kx
2208 kx->transcript_hash_ctx = rh_ctx->hc;
2209 kx->ss_I = *ss_I;
2210 kx->handshake_secret = rh_ctx->hs;
2211 kx->ss_e = rh_ctx->ss_e;
2212 kx->ihts = rh_ctx->ihts;
2213 kx->rhts = rh_ctx->rhts;
2214 kx->master_secret = ms;
2215 GNUNET_free (rh_ctx->rhp);
2216 GNUNET_free (rh_ctx);
2217 rh_ctx = NULL;
2218
2220 &transcript);
2221#if DEBUG_KX
2223 "Transcript snapshot for derivation of *ATS: `%s'\n",
2224 GNUNET_h2s (&transcript));
2225#endif
2226 derive_initial_ats (&transcript,
2227 &kx->master_secret,
2229 &kx->their_ats[0]);
2230 for (int i = 0; i < MAX_EPOCHS - 1; i++)
2231 {
2232 derive_next_ats (&kx->their_ats[i],
2233 &kx->their_ats[i + 1]);
2234 }
2235 kx->their_max_epoch = MAX_EPOCHS - 1;
2236
2238 0,
2239 enc_key,
2240 enc_nonce);
2241 /* Create InitiatorDone message */
2242 idm_p = &idm_local; /* plaintext */
2243 env = GNUNET_MQ_msg_extra (idm_e,
2244 sizeof (ack_i)
2247 // 6. Create IteratorFinished as per Section 6.
2248 generate_initiator_finished (&transcript,
2249 &kx->master_secret,
2250 &idm_p->finished);
2252 "InteratorFinished: `%s'\n",
2253 GNUNET_h2s (&idm_p->finished));
2255 "Transcript `%s'\n",
2256 GNUNET_h2s (&transcript));
2257 // 7. Send InteratorFinished message encrypted with the key derived from IHTS to R
2258
2259 GNUNET_assert (0 == crypto_aead_xchacha20poly1305_ietf_encrypt (
2260 (unsigned char*) &idm_e->finished, /* c - ciphertext */
2261 NULL, /* clen_p */
2262 (unsigned char*) &idm_p->finished, /* idm_p - plaintext message */
2263 sizeof (idm_p->finished), // mlen
2264 NULL, 0, // ad, adlen // FIXME should this not be the other, unencrypted
2265 // fields?
2266 NULL, // nsec - unused
2267 enc_nonce, // npub - nonce
2268 enc_key)); // k - key IHTS
2269 /* Forward the transcript hash context
2270 * after generating finished_I and RATS_0
2271 * before deriving IATS_0 */
2273 &idm_e->finished,
2274 sizeof (idm_e->finished)
2275 + AEAD_TAG_BYTES);
2277 &transcript);
2278#if DEBUG_KX
2280 "Transcript snapshot for derivation of *ATS: `%s'\n",
2281 GNUNET_h2s (&transcript));
2282#endif
2283 derive_initial_ats (&transcript,
2284 &kx->master_secret,
2286 &kx->current_ats);
2287 kx->current_epoch = 0;
2288 kx->current_sqn = 0;
2289 /* We start sending under this epoch here, so it has to be dated here too.
2290 #check_if_ack_or_heartbeat() only sets @e current_epoch_expiration once
2291 the responder's Ack arrives; until then it holds whatever the previous
2292 association left (zero for a first exchange), and #check_rekey() treats
2293 a past expiration as "rekey now". Anything we send while waiting for
2294 the Ack -- the Ack we answer an early heartbeat with, say -- would then
2295 burn an epoch the responder has no reason to expect. */
2298 /* Application traffic keys are installed, so from the record layer's
2299 point of view the association exists from here: we have to be able to
2300 deprotect the responder's Ack, which arrives before the handshake is
2301 confirmed. The client-visible session is created only once it does. */
2302 replay_reset_all (kx);
2304 // 8. optionally encrypt payload TODO
2306 kx->current_sqn,
2307 enc_key,
2308 enc_nonce);
2309 kx->current_sqn++;
2310 ack_i.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
2311 ack_i.header.size = htons (sizeof ack_i);
2312 GNUNET_assert (0 == crypto_aead_xchacha20poly1305_ietf_encrypt (
2313 (unsigned char*) &idm_e[1], /* c - ciphertext */
2314 NULL, /* clen_p */
2315 (unsigned char*) &ack_i, /* rhm_p - plaintext message */
2316 sizeof ack_i, // mlen
2317 NULL, 0, // ad, adlen // FIXME should this not be the other, unencrypted
2318 // fields?
2319 NULL, // nsec - unused
2320 enc_nonce, // npub - nonce // FIXME nonce can be reused
2321 enc_key)); // k - key RHTS
2322
2323 GNUNET_MQ_send_copy (kx->mq, env);
2324 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sent InitiatorDone: %d %d\n", kx->role,
2325 kx->status);
2326
2327
2328 kx->resend_env = env;
2331 monitor_notify_all (kx);
2332 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2333}
2334
2335
2336static int
2337check_responder_hello (void *cls, const struct ResponderHello *m)
2338{
2339 uint16_t size = ntohs (m->header.size);
2340
2341 if (size < sizeof (*m)
2342 + sizeof (struct ResponderHelloPayload)
2343 + sizeof (struct GNUNET_HashCode)
2344 + AEAD_TAG_BYTES * 2)
2345 {
2346 return GNUNET_SYSERR;
2347 }
2348 return GNUNET_OK;
2349}
2350
2351
2357static void
2358handle_responder_hello (void *cls, const struct ResponderHello *rhm_e)
2359{
2360 struct GSC_KeyExchangeInfo *kx = cls;
2362 struct ResponderHelloCls *rh_ctx;
2363 struct GNUNET_HashCode transcript;
2364 struct GNUNET_HashCode rh_hash;
2365 struct GNUNET_HashContext *hc;
2366 struct GNUNET_ShortHashCode ss_I;
2367 unsigned char enc_key[AEAD_KEY_BYTES];
2368 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2370
2371 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ResponderHello: %d %d\n", kx->
2372 role, kx->status);
2373
2374 GNUNET_CRYPTO_hash (rhm_e,
2375 ntohs (rhm_e->header.size),
2376 &rh_hash);
2377 if (ROLE_RESPONDER == kx->role)
2378 {
2379 GNUNET_break_op (0);
2381 "I am the responder! Ignoring.\n");
2382 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2383 return;
2384 }
2386 {
2388 (0 == GNUNET_memcmp (&rh_hash,
2389 &kx->rh_hash)))
2390 {
2391 /* The responder resent the ResponderHello we already answered, which
2392 means our InitiatorDone did not reach it. That is an ordinary
2393 retransmission, not a protocol violation -- the GNUNET_break_op()
2394 below used to report it as one, which is what "Unexpected
2395 ResponderHello in state 6" in the logs is. Answer it the way
2396 RFC 9147, Section 5.8 requires: "implementations MUST retransmit
2397 their last flight in response to a retransmitted flight from the
2398 peer". Our @e resend_task would get there on its own eventually;
2399 doing it here converges at the pace of the peer's timer instead of
2400 ours, and both are bounded by RESEND_MAX_TRIES. */
2402 "ResponderHello repeated by `%s', resending InitiatorDone\n",
2403 GNUNET_i2s (&kx->peer));
2405 gettext_noop (
2406 "# ResponderHello retransmissions received"),
2407 1,
2408 GNUNET_NO);
2409 if (NULL != kx->resend_env)
2411 kx->resend_env);
2412 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2413 return;
2414 }
2415 /* Outside of that state there is no handshake this message could
2416 belong to. In particular @e transcript_hash_ctx is then NULL, and
2417 #GNUNET_CRYPTO_hash_context_copy() dereferences its argument -- so
2418 a peer could crash us by sending a ResponderHello at any other
2419 time. Note that @e resend_task and @e resend_env below belong to
2420 the exchange we *are* in the middle of and must not be cleared
2421 before this point either. */
2422 GNUNET_break_op (0);
2424 "Unexpected ResponderHello in state %d, ignoring\n",
2425 kx->status);
2426 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2427 return;
2428 }
2429 /* From here on this is the hello we answer; recognising a retransmission
2430 of it is what lets us resend our InitiatorDone above instead of
2431 dropping the peer's flight on the floor. */
2432 kx->rh_hash = rh_hash;
2433 GNUNET_assert (NULL != kx->transcript_hash_ctx);
2435 if (NULL != kx->resend_task)
2436 {
2438 kx->resend_task = NULL;
2439 }
2440 if (NULL != kx->resend_env)
2441 {
2443 kx->resend_env = NULL;
2444 }
2445
2446 /* Forward the transcript hash context */
2448 rhm_e,
2449 sizeof (struct ResponderHello));
2450 // 1. Verify that the message type is CORE_RESPONDER_HELLO
2451 // - implicitly done by handling this message?
2452 // - or is this about verifying the 'additional data' part of aead?
2453 // should it check the encryption + mac? (is this implicitly done
2454 // while decrypting?)
2455 // 2. sse <- Decaps(ske,ce)
2456 rh_ctx = GNUNET_new (struct ResponderHelloCls);
2457 ret = GNUNET_CRYPTO_hpke_kem_decaps (&kx->sk_e, // secret/private ephemeral key of initiator (us)
2458 &rhm_e->c_e, // encapsulated key
2459 &rh_ctx->ss_e); // key - ss_e
2460 if (GNUNET_OK != ret)
2461 {
2463 "Something went wrong decapsulating ss_e\n");
2465 GNUNET_free (rh_ctx);
2466 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2467 return;
2468 }
2469 // 3. Generate IHTS and RHTS from Section 5 and decrypt ServicesInfo, cI and ResponderFinished.
2470 snapshot_transcript (hc, &transcript);
2471#if DEBUG_KX
2473 "Transcript snapshot for derivation of HS, *HTS: `%s'\n",
2474 GNUNET_h2s (&transcript));
2475#endif
2477 &rh_ctx->ss_e,
2478 &rh_ctx->hs);
2479 derive_rhts (&transcript,
2480 &rh_ctx->hs,
2481 &rh_ctx->rhts);
2482 derive_ihts (&transcript,
2483 &rh_ctx->hs,
2484 &rh_ctx->ihts);
2486 0,
2487 enc_key,
2488 enc_nonce);
2489 rh_ctx->kx = kx;
2490 GNUNET_memcpy (&rh_ctx->rhm_e, rhm_e, sizeof (*rhm_e));
2491 {
2492 unsigned long long int c_len;
2493 unsigned char *finished_buf;
2494 // use RHTS to decrypt
2495 c_len = ntohs (rhm_e->header.size) - sizeof (*rhm_e)
2496 - sizeof (struct GNUNET_HashCode)
2497 - AEAD_TAG_BYTES; // finished ct
2498 rh_ctx->rhp = GNUNET_malloc (c_len
2499 -
2501 rh_ctx->hc = hc;
2502 finished_buf = ((unsigned char*) &rhm_e[1]) + c_len;
2503 /* Forward the transcript_hash_ctx
2504 * after rhts has been generated,
2505 * before generating finished_R*/
2507 hc,
2508 &rhm_e[1],
2509 c_len);
2510
2511 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2512 (unsigned char*) rh_ctx->rhp, // unsigned char *m
2513 NULL, // mlen_p message length
2514 NULL, // unsigned char *nsec - unused: NULL
2515 (unsigned char*) &rhm_e[1], // const unsigned char *c - ciphertext
2516 c_len, // unsigned long long clen - length of ciphertext
2517 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2518 0, // unsigned long long adlen
2519 enc_nonce, // const unsigned char *npub - nonce
2520 enc_key // const unsigned char *k - key
2521 );
2522 if (0 != ret)
2523 {
2525 "Something went wrong decrypting: %d\n", ret);
2526 GNUNET_free (rh_ctx->rhp);
2527 GNUNET_free (rh_ctx);
2529 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2530 return;
2531 }
2532 // FIXME nonce reuse (see encryption)
2534 1,
2535 enc_key,
2536 enc_nonce);
2537 c_len = sizeof (struct GNUNET_HashCode)
2539 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2540 (unsigned char*) &rh_ctx->decrypted_finish, // unsigned char *m
2541 NULL, // mlen_p message length
2542 NULL, // unsigned char *nsec - unused: NULL
2543 finished_buf, // const unsigned char *c - ciphertext
2544 c_len, // unsigned long long clen - length of ciphertext
2545 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2546 0, // unsigned long long adlen
2547 enc_nonce, // const unsigned char *npub - nonce
2548 enc_key // const unsigned char *k - key
2549 );
2550 if (0 != ret)
2551 {
2553 "Something went wrong decrypting finished field: %d\n", ret);
2554 GNUNET_free (rh_ctx->rhp);
2555 GNUNET_free (rh_ctx);
2557 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2558 return;
2559 }
2560 GNUNET_memcpy (rh_ctx->finished_enc,
2561 finished_buf,
2562 c_len);
2563 }
2564 // 4. ssI <- Decaps(skI,cI).
2566 if ( (NULL == my_private_key) ||
2567 (GNUNET_OK !=
2569 &rh_ctx->rhp->c_I,
2570 &ss_I)) )
2571 {
2573 "Failed to decapsulate c_I of ResponderHello from `%s'\n",
2574 GNUNET_i2s (&kx->peer));
2575 GNUNET_free (rh_ctx->rhp);
2576 GNUNET_free (rh_ctx);
2578 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2579 restart_kx (kx);
2580 return;
2581 }
2583 &ss_I);
2584}
2585
2586
2587static int
2588check_initiator_done (void *cls, const struct InitiatorDone *m)
2589{
2590 uint16_t size = ntohs (m->header.size);
2591
2592 if (size < sizeof (*m) + sizeof (struct ConfirmationAck))
2593 {
2594 return GNUNET_SYSERR;
2595 }
2596 return GNUNET_OK;
2597}
2598
2599
2605static void
2606handle_initiator_done (void *cls, const struct InitiatorDone *idm_e)
2607{
2608 struct GSC_KeyExchangeInfo *kx = cls;
2609 struct InitiatorDone idm_local;
2610 struct InitiatorDone *idm_p = &idm_local; /* plaintext */
2611 struct GNUNET_HashCode initiator_finished;
2612 struct GNUNET_HashCode transcript;
2613 struct GNUNET_ShortHashCode their_ats;
2614 struct GNUNET_HashContext *hc;
2615 unsigned char enc_key[AEAD_KEY_BYTES];
2616 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2617 struct ConfirmationAck ack_i;
2618 struct ConfirmationAck ack_r;
2619 int8_t ret;
2620
2621 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received InitiatorDone: %d %d\n", kx->
2622 role, kx->status);
2623 if (ROLE_INITIATOR == kx->role)
2624 {
2625 GNUNET_break_op (0);
2627 "I am the initiator! Tearing down...\n");
2628 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2629 return;
2630 }
2632 {
2633 /* The initiator did not see our ConfirmationAck and is resending (it
2634 tries #RESEND_MAX_TRIES times). Our handshake secrets are gone --
2635 #cleanup_handshake_secrets() zeroed @e ihts -- so verifying this
2636 message again is not possible and would only look like a decryption
2637 failure. Send what the initiator is actually missing instead. */
2639 "InitiatorDone repeated by `%s', resending our Ack\n",
2640 GNUNET_i2s (&kx->peer));
2641 ack_r.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
2642 ack_r.header.size = htons (sizeof ack_r);
2644 &ack_r,
2645 sizeof ack_r);
2646 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2647 return;
2648 }
2650 {
2651 /* We have no handshake state this message could be checked against.
2652 Note that @e resend_task and @e resend_env below belong to whatever
2653 exchange we *are* in the middle of, so they must not be cleared
2654 before this point. */
2655 GNUNET_break_op (0);
2657 "Unexpected InitiatorDone in state %d, ignoring\n",
2658 kx->status);
2659 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2660 return;
2661 }
2662 if (NULL != kx->resend_task)
2663 {
2665 kx->resend_task = NULL;
2666 }
2667 if (NULL != kx->resend_env)
2668 {
2670 kx->resend_env = NULL;
2671 }
2673 0,
2674 enc_key,
2675 enc_nonce);
2676 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2677 (unsigned char*) &idm_p->finished, // unsigned char *m
2678 NULL, // mlen_p message length
2679 NULL, // unsigned char *nsec - unused: NULL
2680 (unsigned char*) &idm_e->finished, // const unsigned char *c - ciphertext
2681 sizeof (idm_p->finished) // unsigned long long clen - length of ciphertext
2683 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2684 0, // unsigned long long adlen
2685 enc_nonce, // const unsigned char *npub - nonce
2686 enc_key // const unsigned char *k - key
2687 );
2688 if (0 != ret)
2689 {
2691 "Something went wrong decrypting: %d\n", ret);
2692 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2693 return;
2694 }
2695
2696 // - verify finished_I
2697 /* Generate finished_I
2698 * after Forwarding until {finished_R}RHTS
2699 * (did so while we prepared responder hello)
2700 * before forwarding to [{payload}RATS and] {finished_I}IHTS */
2701 // (look at the end of handle_initiator_hello())
2702 snapshot_transcript (kx->transcript_hash_ctx, &transcript);
2703 generate_initiator_finished (&transcript,
2704 &kx->master_secret,
2705 &initiator_finished);
2706 if (0 != memcmp (&idm_p->finished,
2707 &initiator_finished,
2708 sizeof (struct GNUNET_HashCode)))
2709 {
2711 "Could not verify \"initiator finished\" hash.\n");
2713 "Want: `%s'\n",
2714 GNUNET_h2s (&initiator_finished));
2716 "Have: `%s'\n",
2717 GNUNET_h2s (&idm_p->finished));
2719 "Transcript `%s'\n",
2720 GNUNET_h2s (&transcript));
2721 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2722 return;
2723 }
2724
2725 /* Forward the transcript hash_context_read */
2728 &idm_e->finished,
2729 sizeof (idm_e->finished)
2730 + AEAD_TAG_BYTES);
2731 snapshot_transcript (hc, &transcript);
2732 derive_initial_ats (&transcript,
2733 &kx->master_secret,
2735 &their_ats);
2736 derive_per_message_secrets (&their_ats, // FIXME other HS epoch?
2737 0,
2738 enc_key,
2739 enc_nonce);
2740 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2741 (unsigned char*) &ack_i, // unsigned char *m
2742 NULL, // mlen_p message length
2743 NULL, // unsigned char *nsec - unused: NULL
2744 (unsigned char*) &idm_e[1], // const unsigned char *c - ciphertext
2745 sizeof (ack_i) + AEAD_TAG_BYTES, // unsigned long long clen - length of ciphertext
2746 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2747 0, // unsigned long long adlen
2748 enc_nonce, // const unsigned char *npub - nonce
2749 enc_key // const unsigned char *k - key
2750 );
2751 if (0 != ret)
2752 {
2754 "Something went wrong decrypting the Ack: %d\n", ret);
2756 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2757 return;
2758 }
2759 if ((sizeof ack_i != ntohs (ack_i.header.size)) ||
2760 (GNUNET_MESSAGE_TYPE_CORE_ACK != ntohs (ack_i.header.type)))
2761 {
2763 "Ack invalid!\n");
2765 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2766 return;
2767 }
2768 GNUNET_memcpy (&kx->their_ats[0],
2769 &their_ats,
2770 sizeof their_ats);
2774 for (int i = 0; i < MAX_EPOCHS - 1; i++)
2775 {
2776 derive_next_ats (&kx->their_ats[i],
2777 &kx->their_ats[i + 1]);
2778 }
2780 kx->transcript_hash_ctx = hc;
2785 monitor_notify_all (kx);
2786 /* @e finished_I has verified. RFC 9147, Section 5.11: the peer has now
2787 "demonstrated reachability [...] by completing a complete handshake
2788 including delivering a verifiable Finished message", so this is the
2789 point -- and the only point -- at which the old association may be
2790 destroyed. #handle_initiator_hello() deliberately left it running.
2791 GSC_SESSIONS_create() puts into @e sessions with
2792 #GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY under a
2793 GNUNET_assert(), so a leftover session is not merely untidy. */
2794 GSC_SESSIONS_end (&kx->peer);
2795 if (NULL != kx->heartbeat_task)
2796 {
2798 kx->heartbeat_task = NULL;
2799 }
2800 /* #send_initiator_done() starts the initiator at epoch 0 and we have to
2801 agree: on a kx that had an association before, these still hold the
2802 predecessor's values, and none of them was ever reset here. */
2803 kx->current_epoch = 0;
2804 kx->their_max_epoch = 0;
2805 kx->current_sqn = 1;
2806 replay_reset_all (kx);
2808 GSC_SESSIONS_create (&kx->peer, kx, kx->class);
2809 update_timeout (kx);
2810 ack_r.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
2811 ack_r.header.size = htons (sizeof ack_r);
2813 &ack_r,
2814 sizeof ack_r);
2815
2816 GNUNET_TRANSPORT_core_receive_continue (transport,
2817 &kx->peer);
2818}
2819
2820
2826static int
2827check_encrypted_message (void *cls, const struct EncryptedMessage *m)
2828{
2829 uint16_t size = ntohs (m->header.size) - sizeof(*m);
2830
2831 // TODO check (see check_encrypted ())
2832 // - check epoch
2833 // - check sequence number
2834 if (size < sizeof(struct GNUNET_MessageHeader))
2835 {
2836 GNUNET_break_op (0);
2837 return GNUNET_SYSERR;
2838 }
2839 return GNUNET_OK;
2840}
2841
2842
2848static void
2850 const struct Heartbeat *m)
2851{
2852 struct GNUNET_ShortHashCode new_ats;
2853 struct ConfirmationAck ack;
2854
2856 {
2857 if (kx->current_epoch == UINT64_MAX)
2858 {
2860 "Max epoch reached (you probably will never see this)\n");
2861 }
2862 else
2863 {
2864 kx->current_epoch++;
2867 kx->current_sqn = 0;
2869 &new_ats);
2870 memcpy (&kx->current_ats,
2871 &new_ats,
2872 sizeof new_ats);
2873 }
2874 }
2875 update_timeout (kx);
2876 ack.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
2877 ack.header.size = htons (sizeof ack);
2879 &ack,
2880 sizeof ack);
2881 /* NOTE: no GNUNET_TRANSPORT_core_receive_continue() here. We are called
2882 from #handle_encrypted_message(), which owns the message and issues
2883 exactly one call for it. */
2884}
2885
2886
2887static enum GNUNET_GenericReturnValue
2889 const char *buf,
2890 size_t buf_len)
2891{
2892 struct GNUNET_MessageHeader *msg;
2893 struct ConfirmationAck *ack;
2894 struct Heartbeat *hb;
2895
2896 if (sizeof *msg > buf_len)
2897 return GNUNET_NO;
2898 msg = (struct GNUNET_MessageHeader*) buf;
2899 if (GNUNET_MESSAGE_TYPE_CORE_ACK == ntohs (msg->type))
2900 {
2901 ack = (struct ConfirmationAck *) buf;
2902 if (sizeof *ack != ntohs (ack->header.size))
2903 return GNUNET_NO;
2904 }
2905 else if (GNUNET_MESSAGE_TYPE_CORE_HEARTBEAT == ntohs (msg->type))
2906 {
2907 hb = (struct Heartbeat*) buf;
2908 if (sizeof *hb != ntohs (hb->header.size))
2909 return GNUNET_NO;
2910 handle_heartbeat (kx, hb);
2911 }
2912 else
2913 {
2914 return GNUNET_NO;
2915 }
2916
2921 {
2922 GSC_SESSIONS_create (&kx->peer, kx, kx->class);
2927 if (NULL != kx->resend_task)
2929 kx->resend_task = NULL;
2930 if (NULL != kx->resend_env)
2932 kx->resend_env = NULL;
2933 monitor_notify_all (kx);
2934 }
2935 update_timeout (kx);
2936
2937 return GNUNET_YES;
2938}
2939
2940
2946static void
2947handle_encrypted_message (void *cls, const struct EncryptedMessage *m)
2948{
2949 struct GSC_KeyExchangeInfo *kx = cls;
2950 uint16_t size = ntohs (m->header.size);
2951 char buf[size - sizeof (*m)] GNUNET_ALIGN;
2952 unsigned char seq_enc_k[crypto_stream_chacha20_ietf_KEYBYTES];
2953 const unsigned char *seq_enc_nonce;
2954 unsigned char enc_key[AEAD_KEY_BYTES];
2955 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2956 struct GNUNET_ShortHashCode new_ats[MAX_EPOCHS];
2957 uint32_t seq_enc_ctr;
2958 uint64_t epoch;
2959 uint64_t m_seq;
2960 uint64_t m_seq_nbo;
2961 uint64_t c_len;
2962 int8_t ret;
2963
2964 // TODO look at handle_encrypted
2965 // - statistics
2966
2967 /* The record layer answers to @e association_up, not to @e status: a
2968 handshake may be in flight over an association that is still live
2969 (RFC 9147, Section 5.11), and records of the old epoch have to keep
2970 being deprotected while it is. Conversely a record we have no keys
2971 for is simply an invalid record -- RFC 9147, Section 4.5.2: "In
2972 general, invalid records SHOULD be silently discarded, thus preserving
2973 the association" -- so it must not end a session or restart anything.
2974 If we are idle it does tell us the peer believes in an association we
2975 do not have, which is worth one exchange. */
2976 if (GNUNET_YES != kx->association_up)
2977 {
2979 "Discarding record from `%s': no keys for epoch %" PRIu64 "\n",
2980 GNUNET_i2s (&kx->peer),
2981 GNUNET_ntohll (m->epoch));
2982 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2984 restart_kx (kx);
2985 return;
2986 }
2987 epoch = GNUNET_ntohll (m->epoch);
2992 memcpy (new_ats,
2993 kx->their_ats,
2994 MAX_EPOCHS * sizeof (struct GNUNET_ShortHashCode));
2995 // FIXME here we could introduce logic that sends heartbeats
2996 // with key update request if we have not seen a new
2997 // epoch after a while (e.g. EPOCH_EXPIRATION)
2998 if (kx->their_max_epoch < epoch)
2999 {
3004 if ((epoch - kx->their_max_epoch) > 2 * MAX_EPOCHS)
3005 {
3006 /* @e epoch is plaintext and not covered by the AEAD tag, so this is
3007 reached by a single flipped bit as readily as by a peer that really
3008 did skip ahead. Drop the message like the "too old" case below
3009 does; tearing the session down here means one unauthenticated
3010 header field costs a full re-handshake. */
3012 "Epoch %" PRIu64 " is too new, will not decrypt...\n",
3013 epoch);
3014 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3015 return;
3016 }
3017 for (uint64_t i = kx->their_max_epoch; i < epoch; i++)
3018 {
3019 derive_next_ats (&new_ats[i % MAX_EPOCHS],
3020 &new_ats[(i + 1) % MAX_EPOCHS]);
3021 /* This slot of the ring now holds a different key, so the window
3022 that went with the old one no longer means anything. */
3023 replay_reset (kx, i + 1);
3024 }
3025 }
3026 else if ((kx->their_max_epoch - epoch) > MAX_EPOCHS)
3027 {
3029 "Epoch %" PRIu64 " is too old, cannot decrypt...\n",
3030 epoch);
3031 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3032 return;
3033 }
3034 derive_sn (
3035 &new_ats[epoch % MAX_EPOCHS],
3036 seq_enc_k,
3037 sizeof seq_enc_k);
3038 /* compute the sequence number */
3039 seq_enc_ctr = *((uint32_t*) m->tag);
3040 seq_enc_nonce = &m->tag[sizeof (uint32_t)];
3041#if DEBUG_KX
3042 GNUNET_print_bytes (&new_ats[epoch % MAX_EPOCHS],
3043 sizeof (struct GNUNET_ShortHashCode),
3044 8,
3045 GNUNET_NO);
3046 GNUNET_print_bytes (seq_enc_k,
3047 sizeof seq_enc_k,
3048 8,
3049 GNUNET_NO);
3050 GNUNET_print_bytes ((char*) &seq_enc_ctr,
3051 sizeof seq_enc_ctr,
3052 8,
3053 GNUNET_NO);
3054#endif
3055 crypto_stream_chacha20_ietf_xor_ic (
3056 (unsigned char*) &m_seq_nbo,
3057 (unsigned char*) &m->sequence_number,
3058 sizeof (uint64_t),
3059 seq_enc_nonce,
3060 ntohl (seq_enc_ctr),
3061 seq_enc_k);
3062 m_seq = GNUNET_ntohll (m_seq_nbo);
3064 "Received encrypted message in epoch %" PRIu64
3065 " with E(SQN=%" PRIu64 ")=%" PRIu64
3066 "\n",
3067 epoch,
3068 m_seq,
3069 m->sequence_number);
3070 /* RFC 9147, Section 4.5.1. Cheap enough to do before deprotection, and
3071 doing it first means a flood of replayed records costs no AEAD work.
3072 The window itself is only moved once the record verifies, below. */
3073 if (GNUNET_OK != replay_check (kx, epoch, m_seq))
3074 {
3076 gettext_noop ("# replayed records discarded"),
3077 1,
3078 GNUNET_NO);
3080 "Discarding replayed record %" PRIu64 "/%" PRIu64
3081 " from `%s'\n",
3082 epoch,
3083 m_seq,
3084 GNUNET_i2s (&kx->peer));
3085 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3086 return;
3087 }
3088 /* We are the initiator and as we are going to receive,
3089 * we are using the responder key material */
3090 derive_per_message_secrets (&new_ats[epoch % MAX_EPOCHS],
3091 m_seq,
3092 enc_key,
3093 enc_nonce);
3094 // TODO checking sequence numbers - handle the case of out-of-sync messages!
3095 // for now only decrypt the payload
3096 // TODO encrypt other fields, too!
3097 // TODO
3098 // c_len = size - offsetof ();
3099 c_len = size - sizeof (struct EncryptedMessage);
3100 ret = crypto_aead_xchacha20poly1305_ietf_decrypt_detached (
3101 (unsigned char*) buf, // m - plain message
3102 NULL, // nsec - unused
3103 (unsigned char*) &m[1], // c - ciphertext
3104 c_len, // clen
3105 (const unsigned char*) &m->tag, // mac
3106 NULL, // ad - additional data TODO
3107 0, // adlen
3108 enc_nonce, // npub
3109 enc_key // k
3110 );
3111 if (0 != ret)
3112 {
3113 /* RFC 9147, Section 4.5.2: "invalid records SHOULD be silently
3114 discarded, thus preserving the association; however, an error MAY be
3115 logged for diagnostic purposes." Not a protocol violation on the
3116 peer's part either -- anything at all can arrive here -- so no
3117 GNUNET_break_op(). */
3119 gettext_noop ("# invalid records discarded"),
3120 1,
3121 GNUNET_NO);
3123 "Discarding record %" PRIu64 "/%" PRIu64 " from `%s':"
3124 " does not deprotect\n",
3125 epoch,
3126 m_seq,
3127 GNUNET_i2s (&kx->peer));
3128 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3129 return;
3130 }
3131 /* Deprotected, so the record is authentic and everything derived from it
3132 may now be committed: the epoch ring, the anti-replay window (RFC 9147,
3133 Section 4.5.1: "The window MUST NOT be updated due to a received record
3134 until that record has been deprotected successfully") and @e timeout.
3135 @e timeout is the only liveness signal CORE has and is what
3136 `gnunet-core -m' reports, so refreshing it any earlier would let
3137 anything merely shaped like a record keep a session nominally alive. */
3138 /* Only ever forward: @e their_max_epoch is the *highest* epoch we have
3139 seen, and the ratchet above keys off it. A record that was merely
3140 reordered across an epoch boundary -- entirely normal, the peer starts
3141 the new epoch at sequence number 0 while the old one is still in flight
3142 -- used to pull it back, so the next record of the newer epoch looked
3143 like a fresh advance and ran the loop again, wiping that epoch's
3144 anti-replay window (RFC 9147, Section 4.5.1) every single time. */
3145 if (kx->their_max_epoch < epoch)
3146 kx->their_max_epoch = epoch;
3147 memcpy (&kx->their_ats,
3148 new_ats,
3149 MAX_EPOCHS * sizeof (struct GNUNET_ShortHashCode));
3150 replay_commit (kx, epoch, m_seq);
3151 update_timeout (kx);
3152
3154 buf,
3155 sizeof buf))
3156 {
3158 {
3160 "Dropping message as we are still waiting for handshake ACK\n");
3161 GNUNET_break_op (0);
3162 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3163 return;
3164 }
3165 if (GNUNET_OK !=
3167 buf,
3168 sizeof buf,
3169 GNUNET_YES,
3170 GNUNET_NO))
3171 GNUNET_break_op (0);
3172 }
3173 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3174}
3175
3176
3186static void
3188 const struct GNUNET_PeerIdentity *peer,
3189 void *handler_cls)
3190{
3191 struct GSC_KeyExchangeInfo *kx = handler_cls;
3192 (void) cls;
3193
3195 "Peer `%s' disconnected from us.\n",
3196 GNUNET_i2s (&kx->peer));
3197 GSC_SESSIONS_end (&kx->peer);
3199 gettext_noop ("# key exchanges stopped"),
3200 1,
3201 GNUNET_NO);
3202 if (NULL != kx->resend_task)
3203 {
3205 kx->resend_task = NULL;
3206 }
3207 if (NULL != kx->resend_env)
3208 {
3210 kx->resend_env = NULL;
3211 }
3212 if (NULL != kx->heartbeat_task)
3213 {
3215 kx->heartbeat_task = NULL;
3216 }
3218 monitor_notify_all (kx);
3219 if (kx->transcript_hash_ctx)
3220 {
3222 kx->transcript_hash_ctx = NULL;
3223 }
3225 GNUNET_MST_destroy (kx->mst);
3226 GNUNET_free (kx);
3227}
3228
3229
3230static void
3231resend_initiator_hello (void *cls)
3232{
3233 struct GSC_KeyExchangeInfo *kx = cls;
3234
3235 kx->resend_task = NULL;
3236 if (0 == kx->resend_tries_left)
3237 {
3238 /* The InitiatorHello we keep repeating carries the ephemeral public key
3239 generated by #send_initiator_hello(), and only #restart_kx() ever
3240 generates a new one. Retrying the same message forever therefore
3241 never recovers from a responder that has dropped the exchange -- it
3242 just keeps a retransmit timer running against a peer that is not
3243 answering. Give up like #resend_responder_hello() and
3244 #resend_initiator_done() do and start a fresh exchange. */
3246 "InitiatorHello not answered by `%s', restarting KX\n",
3247 GNUNET_i2s (&kx->peer));
3248 restart_kx (kx);
3249 return;
3250 }
3251 kx->resend_tries_left--;
3253 "Resending InitiatorHello. Retries left: %u\n",
3254 kx->resend_tries_left);
3257}
3258
3259
3265static void
3267{
3268 const struct GNUNET_PeerIdentity *my_identity;
3269 struct GNUNET_MQ_Envelope *env;
3270 struct GNUNET_ShortHashCode es;
3271 struct GNUNET_ShortHashCode ets;
3272 struct GNUNET_ShortHashCode ss_R;
3273 struct InitiatorHelloPayload *ihmp; /* initiator hello message - buffer on stack */
3274 struct InitiatorHello *ihm_e; /* initiator hello message - encrypted */
3275 long long unsigned int c_len;
3276 unsigned char enc_key[AEAD_KEY_BYTES];
3277 unsigned char enc_nonce[AEAD_NONCE_BYTES];
3279 size_t pt_len;
3280
3282 GNUNET_assert (NULL != my_identity);
3283
3284 pt_len = sizeof (*ihmp) + strlen (my_services_info);
3285 c_len = pt_len + AEAD_TAG_BYTES;
3286 env = GNUNET_MQ_msg_extra (ihm_e,
3287 c_len,
3289 ihmp = (struct InitiatorHelloPayload*) &ihm_e[1];
3290 ihmp->peer_class = htons (GNUNET_CORE_CLASS_UNKNOWN); // TODO set this to a meaningful
3291 GNUNET_memcpy (&ihmp->pk_I,
3293 sizeof (struct GNUNET_PeerIdentity));
3294 GNUNET_CRYPTO_hash (&kx->peer, /* what to hash */ // TODO do we do this twice?
3295 sizeof (struct GNUNET_PeerIdentity),
3296 &ihm_e->h_pk_R); /* result */
3297 // TODO init hashcontext/transcript_hash
3298 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Send InitiatorHello: %d %d\n", kx->role,
3299 kx->status);
3300 GNUNET_assert (NULL == kx->transcript_hash_ctx);
3302 GNUNET_assert (NULL != kx->transcript_hash_ctx);
3303 // TODO fill services_info
3304
3305 // 1. Encaps
3306 ret = GNUNET_CRYPTO_eddsa_kem_encaps (&kx->peer.public_key, // public ephemeral key of initiator
3307 &ihm_e->c_R, // encapsulated key
3308 &ss_R); // key - ss_R
3309 if (GNUNET_OK != ret)
3310 {
3312 "Something went wrong encapsulating ss_R\n");
3313 // TODO handle
3314 }
3315 // 2. generate rR (uint64_t) - is this the nonce? Naming seems not quite
3316 // consistent
3317 ihm_e->r_I =
3318 GNUNET_CRYPTO_random_u64 (UINT64_MAX);
3319 // 3. generate sk_e/pk_e - ephemeral key
3322 &kx->sk_e.ecdhe_key,
3323 &kx->pk_e.ecdhe_key);
3324 GNUNET_memcpy (&ihm_e->pk_e,
3325 &kx->pk_e.ecdhe_key,
3326 sizeof (kx->pk_e.ecdhe_key));
3327 // 4. generate ETS to encrypt
3328 // generate ETS (early_traffic_secret_key, decrypt pk_i
3329 // expand ETS <- expand ES <- extract ss_R
3330 // use ETS to decrypt
3332 ihm_e,
3333 sizeof (struct InitiatorHello));
3334 {
3335 struct GNUNET_HashCode transcript;
3337 &transcript);
3338 derive_es_ets (&transcript,
3339 &ss_R,
3340 &es,
3341 &ets);
3343 0,
3344 enc_key,
3345 enc_nonce);
3346 }
3347 // 5. encrypt
3348
3349 ret = crypto_aead_xchacha20poly1305_ietf_encrypt (
3350 (unsigned char*) &ihm_e[1], /* c - ciphertext */
3351 // mac,
3352 // NULL, // maclen_p
3353 &c_len, /* clen_p */
3354 (unsigned char*) ihmp, /* m - plaintext message */
3355 pt_len, // mlen
3356 NULL, 0, // ad, adlen // FIXME maybe over the unencrypted header?
3357 // fields?
3358 NULL, // nsec - unused
3359 enc_nonce, // npub - nonce
3360 enc_key); // k - key
3361 if (0 != ret)
3362 {
3363 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong encrypting\n");
3365 kx->transcript_hash_ctx = NULL;
3367 return;
3368 }
3369 /* Forward the transcript */
3372 &ihm_e[1],
3373 c_len);
3374
3376 kx->early_secret_key = es;
3377 kx->early_traffic_secret = ets;
3378 kx->ss_R = ss_R;
3379 monitor_notify_all (kx);
3380 GNUNET_MQ_send_copy (kx->mq, env);
3381 kx->resend_env = env;
3383}
3384
3385
3393static enum GNUNET_GenericReturnValue
3395{
3396 struct GNUNET_ShortHashCode new_ats;
3397
3398 if ((UINT64_MAX == kx->current_sqn) ||
3400 {
3402 "Epoch expiration %" PRIu64 " SQN %" PRIu64
3403 ", incrementing epoch...\n",
3405 kx->current_sqn);
3406 if (UINT64_MAX == kx->current_epoch)
3407 {
3408 /* RFC 9147, Section 6.1: "Implementations MUST NOT allow the epoch to
3409 wrap, but instead MUST establish a new association, terminating the
3410 old association". This used to be a GNUNET_assert(). */
3412 "Epoch exhausted for `%s', starting a new association\n",
3413 GNUNET_i2s (&kx->peer));
3414 restart_kx (kx);
3415 return GNUNET_SYSERR;
3416 }
3417 kx->current_epoch++;
3420 kx->current_sqn = 0;
3422 &new_ats);
3423 memcpy (&kx->current_ats,
3424 &new_ats,
3425 sizeof new_ats);
3426 }
3427 return GNUNET_OK;
3428}
3429
3430
3437void
3439 const void *payload,
3440 size_t payload_size)
3441{
3442 struct GNUNET_MQ_Envelope *env;
3443 struct EncryptedMessage *encrypted_msg;
3444 unsigned char enc_key[AEAD_KEY_BYTES];
3445 unsigned char enc_nonce[AEAD_NONCE_BYTES];
3446 unsigned char seq_enc_k[crypto_stream_chacha20_ietf_KEYBYTES];
3447 uint64_t sqn;
3448 uint64_t epoch;
3449 int8_t ret;
3450
3451 encrypted_msg = NULL;
3452
3453 if (GNUNET_YES != kx->association_up)
3454 {
3455 /* No application traffic keys installed -- there is nothing to protect
3456 this with. Callers reach this through a session, which only exists
3457 while the association does, so this is a should-not-happen. */
3458 GNUNET_break (0);
3459 return;
3460 }
3461 if (GNUNET_OK != check_rekey (kx))
3462 return; /* association was torn down, @e current_ats is gone */
3463 sqn = kx->current_sqn;
3464 epoch = kx->current_epoch;
3465 /* We are the sender and as we are going to send,
3466 * we are using the initiator key material */
3468 sqn,
3469 enc_key,
3470 enc_nonce);
3471 kx->current_sqn++;
3472 derive_sn (&kx->current_ats,
3473 seq_enc_k,
3474 sizeof seq_enc_k);
3475 env = GNUNET_MQ_msg_extra (encrypted_msg,
3476 payload_size,
3478 // only encrypt the payload for now
3479 // TODO encrypt other fields as well
3480 ret = crypto_aead_xchacha20poly1305_ietf_encrypt_detached (
3481 (unsigned char*) &encrypted_msg[1], // c - resulting ciphertext
3482 (unsigned char*) &encrypted_msg->tag, // mac - resulting mac/tag
3483 NULL, // maclen
3484 (unsigned char*) payload, // m - plain message
3485 payload_size, // mlen
3486 NULL, // ad - additional data TODO also cover the unencrypted part (epoch)
3487 0, // adlen
3488 NULL, // nsec - unused
3489 enc_nonce, // npub nonce
3490 enc_key // k - key
3491 );
3492 if (0 != ret)
3493 {
3495 "Something went wrong encrypting message\n");
3496 GNUNET_assert (0);
3497 }
3498 {
3499 /* compute the sequence number */
3500 unsigned char *seq_enc_nonce;
3501 uint64_t seq_nbo;
3502 uint32_t seq_enc_ctr;
3503
3504 seq_nbo = GNUNET_htonll (sqn);
3505 seq_enc_ctr = *((uint32_t*) encrypted_msg->tag);
3506 seq_enc_nonce = &encrypted_msg->tag[sizeof (uint32_t)];
3507 crypto_stream_chacha20_ietf_xor_ic (
3508 (unsigned char*) &encrypted_msg->sequence_number,
3509 (unsigned char*) &seq_nbo,
3510 sizeof seq_nbo,
3511 seq_enc_nonce,
3512 ntohl (seq_enc_ctr),
3513 seq_enc_k);
3514#if DEBUG_KX
3515 GNUNET_print_bytes (seq_enc_k,
3516 sizeof seq_enc_k,
3517 8,
3518 GNUNET_NO);
3519 GNUNET_print_bytes ((char*) &seq_enc_ctr,
3520 sizeof seq_enc_ctr,
3521 8,
3522 GNUNET_NO);
3523#endif
3525 "Sending encrypted message with E(SQN=%" PRIu64 ")=%" PRIu64
3526 "\n",
3527 sqn,
3528 encrypted_msg->sequence_number);
3529 }
3530 encrypted_msg->epoch = GNUNET_htonll (epoch);
3531
3532 // TODO actually copy payload
3533 GNUNET_MQ_send (kx->mq, env);
3534}
3535
3536
3537void
3538GSC_KX_start (void)
3539{
3540 const struct GNUNET_PeerIdentity *my_identity;
3542 GNUNET_MQ_hd_var_size (initiator_hello,
3544 struct InitiatorHello,
3545 NULL),
3546 GNUNET_MQ_hd_var_size (initiator_done,
3548 struct InitiatorDone,
3549 NULL),
3550 GNUNET_MQ_hd_var_size (responder_hello,
3552 struct ResponderHello,
3553 NULL),
3554 GNUNET_MQ_hd_var_size (encrypted_message, // TODO rename?
3556 struct EncryptedMessage,
3557 NULL),
3559 };
3560
3562 GNUNET_assert (NULL != my_identity);
3563
3564 /* Decapsulate with our peer identity's private key directly instead of
3565 round-tripping through the PILS service. The shared secret is needed
3566 in the middle of processing a handshake message, and an asynchronous
3567 answer meant that every InitiatorHello and ResponderHello had to be
3568 parked with its kx across a callback: the kx could be torn down or
3569 freed underneath it, two hellos could be in flight at once, and if
3570 the answer never came (PILS restarting) the handshake stalled *and*
3571 the message was never acknowledged to TRANSPORT. */
3572 if (GNUNET_OK !=
3574 {
3576 _ ("Failed to load our private key, "
3577 "cannot run key exchange\n"));
3578 GSC_KX_done ();
3579 return;
3580 }
3581
3583 transport =
3586 handlers,
3587 NULL, // cls - this connection-independant
3588 // cls seems not to be needed.
3589 // the connection-specific cls
3590 // will be set as a return value
3591 // of
3592 // handle_transport_notify_connect
3595 if (NULL == transport)
3596 {
3597 GSC_KX_done ();
3598 return;
3599 }
3600
3602 "Connected to TRANSPORT\n");
3603
3605}
3606
3607
3608void
3609pid_change_cb (void *cls,
3610 const struct GNUNET_HELLO_Parser *parser,
3611 const struct GNUNET_HashCode *hash)
3612{
3613 if (NULL != transport)
3614 return;
3615
3616 GSC_KX_start ();
3617}
3618
3619
3625int
3626GSC_KX_init (void)
3627{
3630 NULL);
3631 if (NULL == GSC_pils)
3632 {
3633 GSC_KX_done ();
3634 return GNUNET_SYSERR;
3635 }
3636
3637 return GNUNET_OK;
3638}
3639
3640
3644void
3645GSC_KX_done ()
3646{
3647 if (NULL != GSC_pils)
3648 {
3650 GSC_pils = NULL;
3651 }
3652 if (NULL != transport)
3653 {
3655 transport = NULL;
3656 }
3657 if (NULL != rekey_task)
3658 {
3660 rekey_task = NULL;
3661 }
3662 if (NULL != nc)
3663 {
3665 nc = NULL;
3666 }
3667}
3668
3669
3676unsigned int
3678{
3679 return GNUNET_MQ_get_length (kxinfo->mq);
3680}
3681
3682
3683int
3685{
3686 return kxinfo->has_excess_bandwidth;
3687}
3688
3689
3698void
3700{
3701 struct GNUNET_MQ_Envelope *env;
3702 struct MonitorNotifyMessage *done_msg;
3703 struct GSC_KeyExchangeInfo *kx;
3704
3706 for (kx = kx_head; NULL != kx; kx = kx->next)
3707 {
3708 struct GNUNET_MQ_Envelope *env_notify;
3709 struct MonitorNotifyMessage *msg;
3710
3712 msg->state = htonl ((uint32_t) kx->status);
3713 msg->peer = kx->peer;
3714 msg->timeout = GNUNET_TIME_absolute_hton (kx->timeout);
3715 GNUNET_MQ_send (mq, env_notify);
3716 }
3718 done_msg->state = htonl ((uint32_t) GNUNET_CORE_KX_ITERATION_FINISHED);
3721}
3722
3723
3724/* 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.
#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.
@ 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
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
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.
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.
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.

◆ RESEND_TIMEOUT_MAX

#define RESEND_TIMEOUT_MAX    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)

Ceiling for the handshake retransmission timer (RFC 9147, Section 5.8).

Definition at line 100 of file gnunet-service-core_kx.c.

◆ REPLAY_WINDOW_SIZE

#define REPLAY_WINDOW_SIZE   64

Size of the per-epoch anti-replay window, in records.

RFC 9147, Section 4.5.1: "The receiver SHOULD pick a window large enough to handle any plausible reordering, which depends on the data rate." One machine word is the largest window we can check in constant time.

Definition at line 109 of file gnunet-service-core_kx.c.

◆ HEARTBEAT_PROBE_FREQUENCY

#define HEARTBEAT_PROBE_FREQUENCY    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)

How long we wait for the Ack to a heartbeat before sending another one.

Every heartbeat is answered – handle_heartbeat() sends a GNUNET_MESSAGE_TYPE_CORE_ACK unconditionally – so this is also how long an association may go unconfirmed before we probe it again.

Definition at line 118 of file gnunet-service-core_kx.c.

◆ MAX_UNANSWERED_HEARTBEATS

#define MAX_UNANSWERED_HEARTBEATS   3

How many heartbeats in a row may go unanswered before we give up on the association.

Together with HEARTBEAT_PROBE_FREQUENCY this bounds how long a dead association can look alive to us (90s).

Definition at line 126 of file gnunet-service-core_kx.c.

◆ MIN_HEARTBEAT_FREQUENCY

#define MIN_HEARTBEAT_FREQUENCY    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)

What is the minimum frequency for a HEARTBEAT message?

Definition at line 131 of file gnunet-service-core_kx.c.

◆ HEARTBEAT_FREQUENCY

#define HEARTBEAT_FREQUENCY    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)

How often do we send a heartbeat?

Definition at line 137 of file gnunet-service-core_kx.c.

◆ MAX_EPOCHS

#define MAX_EPOCHS   10

Maximum number of epochs we keep on hand.

This implicitly defines the maximum age of messages we accept from other peers, depending on their rekey interval.

Definition at line 146 of file gnunet-service-core_kx.c.

◆ EPOCH_EXPIRATION

#define EPOCH_EXPIRATION    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)

How often do we rekey/switch to a new epoch?

Definition at line 151 of file gnunet-service-core_kx.c.

◆ REKEY_TOLERANCE

#define REKEY_TOLERANCE    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)

What time difference do we tolerate?

Definition at line 157 of file gnunet-service-core_kx.c.

◆ EARLY_DATA_STR

#define EARLY_DATA_STR   "early data"

String for expanding early transport secret (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)

Definition at line 164 of file gnunet-service-core_kx.c.

◆ R_HS_TRAFFIC_STR

#define R_HS_TRAFFIC_STR   "r hs traffic"

String for expanding RHTS (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)

Definition at line 170 of file gnunet-service-core_kx.c.

◆ I_HS_TRAFFIC_STR

#define I_HS_TRAFFIC_STR   "i hs traffic"

String for expanding IHTS (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)

Definition at line 176 of file gnunet-service-core_kx.c.

◆ R_AP_TRAFFIC_STR

#define R_AP_TRAFFIC_STR   "r ap traffic"

String for expanding RATS (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)

Definition at line 182 of file gnunet-service-core_kx.c.

◆ I_AP_TRAFFIC_STR

#define I_AP_TRAFFIC_STR   "i ap traffic"

String for expanding IATS (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)

Definition at line 188 of file gnunet-service-core_kx.c.

◆ DERIVED_STR

#define DERIVED_STR   "derived"

String for expanding derived keys (Handshake and Early) (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)

Definition at line 194 of file gnunet-service-core_kx.c.

◆ R_FINISHED_STR

#define R_FINISHED_STR   "r finished"

String for expanding fk_R used for ResponderFinished field (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)

Definition at line 200 of file gnunet-service-core_kx.c.

◆ I_FINISHED_STR

#define I_FINISHED_STR   "i finished"

String for expanding fk_I used for InitiatorFinished field (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)

Definition at line 206 of file gnunet-service-core_kx.c.

◆ CAKE_LABEL

#define CAKE_LABEL   "cake10"

Labeled expand label for CAKE.

Definition at line 211 of file gnunet-service-core_kx.c.

◆ KEY_STR

#define KEY_STR   "key"

String for expanding derived keys (Handshake and Early) (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)

Definition at line 217 of file gnunet-service-core_kx.c.

◆ TRAFFIC_UPD_STR

#define TRAFFIC_UPD_STR   "traffic upd"

String for expanding derived keys (Handshake and Early) (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)

Definition at line 223 of file gnunet-service-core_kx.c.

◆ IV_STR

#define IV_STR   "iv"

String for expanding derived keys (Handshake and Early) (See https://lsd.gnunet.org/lsd0012/draft-schanzen-cake.html)

Definition at line 229 of file gnunet-service-core_kx.c.

Enumeration Type Documentation

◆ GSC_KX_Role

Indicates whether a peer is in the initiating or receiving role.

Enumerator
ROLE_INITIATOR 
ROLE_RESPONDER 

Definition at line 235 of file gnunet-service-core_kx.c.

236{
237 /* Peer is supposed to initiate the key exchange */
238 ROLE_INITIATOR = 0,
239
240 /* Peer is supposed to wait for the key exchange */
241 ROLE_RESPONDER = 1,
242};

Function Documentation

◆ buffer_clear()

static void buffer_clear ( void *  buf,
size_t  len 
)
static

Definition at line 517 of file gnunet-service-core_kx.c.

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}

References p.

Referenced by cleanup_handshake_secrets().

Here is the caller graph for this function:

◆ cleanup_handshake_secrets()

static void cleanup_handshake_secrets ( struct GSC_KeyExchangeInfo kx)
static

Definition at line 532 of file gnunet-service-core_kx.c.

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}

References buffer_clear(), GSC_KeyExchangeInfo::early_secret_key, GSC_KeyExchangeInfo::early_traffic_secret, GSC_KeyExchangeInfo::handshake_secret, GSC_KeyExchangeInfo::ihts, GSC_KeyExchangeInfo::master_secret, GSC_KeyExchangeInfo::rhts, GSC_KeyExchangeInfo::sk_e, GSC_KeyExchangeInfo::ss_e, GSC_KeyExchangeInfo::ss_I, and GSC_KeyExchangeInfo::ss_R.

Referenced by check_if_ack_or_heartbeat(), handle_initiator_done(), and reset_handshake().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ replay_reset()

static void replay_reset ( struct GSC_KeyExchangeInfo kx,
uint64_t  epoch 
)
static

Forget the anti-replay window of epoch.

Called when the key material behind that slot of the epoch ring is replaced, either by a new association or by advancing past it, so that a sequence number is never checked against a window belonging to a different key.

Parameters
kxkey exchange to update
epochepoch whose window to clear

Definition at line 567 of file gnunet-service-core_kx.c.

569{
570 kx->replay_max[epoch % MAX_EPOCHS] = 0;
571 kx->replay_bitmap[epoch % MAX_EPOCHS] = 0;
572}

References MAX_EPOCHS, GSC_KeyExchangeInfo::replay_bitmap, and GSC_KeyExchangeInfo::replay_max.

Referenced by handle_encrypted_message().

Here is the caller graph for this function:

◆ replay_reset_all()

static void replay_reset_all ( struct GSC_KeyExchangeInfo kx)
static

Forget every anti-replay window (all of MAX_EPOCHS).

Parameters
kxkey exchange to update

Definition at line 581 of file gnunet-service-core_kx.c.

582{
583 memset (kx->replay_max, 0, sizeof kx->replay_max);
584 memset (kx->replay_bitmap, 0, sizeof kx->replay_bitmap);
585}

References GSC_KeyExchangeInfo::replay_bitmap, and GSC_KeyExchangeInfo::replay_max.

Referenced by abandon_exchange(), handle_initiator_done(), and handle_responder_hello_cont().

Here is the caller graph for this function:

◆ replay_check()

static enum GNUNET_GenericReturnValue replay_check ( const struct GSC_KeyExchangeInfo kx,
uint64_t  epoch,
uint64_t  sqn 
)
static

Would a record with sequence number sqn in epoch be a replay?

RFC 9147, Section 4.5.1. Note this only checks: the window must not be updated until the record has been deprotected successfully, which is what replay_commit() is for.

Parameters
kxkey exchange the record arrived on
epochepoch the record claims
sqnsequence number the record claims
Returns
GNUNET_OK if the record is new, GNUNET_SYSERR if it is a replay or falls off the left edge of the window

Definition at line 602 of file gnunet-service-core_kx.c.

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}

References GNUNET_OK, GNUNET_SYSERR, max, MAX_EPOCHS, GSC_KeyExchangeInfo::replay_bitmap, GSC_KeyExchangeInfo::replay_max, and REPLAY_WINDOW_SIZE.

Referenced by handle_encrypted_message().

Here is the caller graph for this function:

◆ replay_commit()

static void replay_commit ( struct GSC_KeyExchangeInfo kx,
uint64_t  epoch,
uint64_t  sqn 
)
static

Record that a record with sequence number sqn in epoch has been deprotected successfully, sliding the window right if needed.

Parameters
kxkey exchange the record arrived on
epochepoch of the record
sqnsequence number of the record

Definition at line 630 of file gnunet-service-core_kx.c.

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}

References max, MAX_EPOCHS, GSC_KeyExchangeInfo::replay_bitmap, GSC_KeyExchangeInfo::replay_max, and REPLAY_WINDOW_SIZE.

Referenced by handle_encrypted_message().

Here is the caller graph for this function:

◆ snapshot_transcript()

static void snapshot_transcript ( const struct GNUNET_HashContext ts_hash,
struct GNUNET_HashCode snapshot 
)
static

Definition at line 653 of file gnunet-service-core_kx.c.

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}

References GNUNET_CRYPTO_hash_context_copy(), and GNUNET_CRYPTO_hash_context_finish().

Referenced by handle_initiator_done(), handle_initiator_hello_cont(), handle_responder_hello(), handle_responder_hello_cont(), send_initiator_hello(), and send_responder_hello().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ monitor_notify_all()

static void monitor_notify_all ( struct GSC_KeyExchangeInfo kx)
static

Inform all monitors about the KX state of the given peer.

Parameters
kxkey exchange state to inform about

Definition at line 669 of file gnunet-service-core_kx.c.

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}

References GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY, GNUNET_NO, GNUNET_notification_context_broadcast(), GNUNET_TIME_absolute_hton(), GSC_KeyExchangeInfo::last_notify_timeout, msg, nc, GSC_KeyExchangeInfo::peer, GNUNET_MessageHeader::size, GSC_KeyExchangeInfo::status, GSC_KeyExchangeInfo::timeout, and GNUNET_MessageHeader::type.

Referenced by check_if_ack_or_heartbeat(), handle_initiator_done(), handle_initiator_hello_cont(), handle_responder_hello_cont(), handle_transport_notify_disconnect(), restart_kx(), send_heartbeat(), send_initiator_hello(), send_responder_hello(), and update_timeout().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ restart_kx()

static void restart_kx ( struct GSC_KeyExchangeInfo kx)
static

Definition at line 957 of file gnunet-service-core_kx.c.

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}

References abandon_exchange(), gettext_noop, GNUNET_assert, GNUNET_CORE_KX_STATE_AWAIT_INITIATION, GNUNET_CRYPTO_hash(), GNUNET_CRYPTO_hash_cmp(), GNUNET_ERROR_TYPE_DEBUG, GNUNET_i2s(), GNUNET_log, GNUNET_NO, GNUNET_PILS_get_identity_hash(), GNUNET_STATISTICS_update(), GSC_pils, GSC_stats, monitor_notify_all(), GSC_KeyExchangeInfo::peer, GSC_KeyExchangeInfo::role, ROLE_INITIATOR, ROLE_RESPONDER, send_initiator_hello(), and GSC_KeyExchangeInfo::status.

Referenced by check_rekey(), handle_encrypted_message(), handle_initiator_hello_cont(), handle_responder_hello(), handle_responder_hello_cont(), handle_transport_notify_connect(), resend_initiator_done(), resend_initiator_hello(), resend_responder_hello(), and send_heartbeat().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ send_heartbeat()

static void send_heartbeat ( void *  cls)
static

Task triggered when a neighbour entry is about to time out (and we should prevent this by sending an Ack in response to a heartbeat).

Parameters
clsthe struct GSC_KeyExchangeInfo

Definition at line 694 of file gnunet-service-core_kx.c.

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);
738 hb.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_HEARTBEAT);
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}

References GSC_KeyExchangeInfo::association_up, Heartbeat::flags, gettext_noop, GNUNET_CORE_KX_STATE_DOWN, GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_INFO, GNUNET_i2s(), GNUNET_log, GNUNET_MESSAGE_TYPE_CORE_HEARTBEAT, GNUNET_NO, GNUNET_SCHEDULER_add_delayed(), GNUNET_STATISTICS_update(), GNUNET_TIME_absolute_get_remaining(), GNUNET_TIME_relative_max(), GNUNET_TIME_relative_min(), GNUNET_YES, GSC_KX_encrypt_and_transmit(), GSC_SESSIONS_end(), GSC_stats, Heartbeat::header, HEARTBEAT_PROBE_FREQUENCY, GSC_KeyExchangeInfo::heartbeat_task, GSC_KeyExchangeInfo::heartbeats_unanswered, MAX_UNANSWERED_HEARTBEATS, MIN_HEARTBEAT_FREQUENCY, monitor_notify_all(), GSC_KeyExchangeInfo::peer, GNUNET_TIME_Relative::rel_value_us, restart_kx(), send_heartbeat(), GNUNET_MessageHeader::size, GSC_KeyExchangeInfo::status, GSC_KeyExchangeInfo::timeout, and GNUNET_MessageHeader::type.

Referenced by send_heartbeat(), and update_timeout().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_timeout()

static void update_timeout ( struct GSC_KeyExchangeInfo kx)
static

We've seen a valid message from the other peer.

Update the time when the session would time out and delay sending our keep alive message further.

Parameters
kxkey exchange where we saw activity

Definition at line 765 of file gnunet-service-core_kx.c.

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}

References delta, GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, GNUNET_SCHEDULER_add_delayed(), GNUNET_SCHEDULER_cancel(), GNUNET_TIME_absolute_get_difference(), GNUNET_TIME_relative_to_absolute(), HEARTBEAT_PROBE_FREQUENCY, GSC_KeyExchangeInfo::heartbeat_task, GSC_KeyExchangeInfo::heartbeats_unanswered, GSC_KeyExchangeInfo::last_notify_timeout, monitor_notify_all(), GNUNET_TIME_Relative::rel_value_us, send_heartbeat(), and GSC_KeyExchangeInfo::timeout.

Referenced by check_if_ack_or_heartbeat(), handle_encrypted_message(), handle_heartbeat(), and handle_initiator_done().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ send_initiator_hello()

static void send_initiator_hello ( struct GSC_KeyExchangeInfo kx)
static

Send initiator hello.

Parameters
kxkey exchange context

Definition at line 3267 of file gnunet-service-core_kx.c.

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}

References AEAD_KEY_BYTES, AEAD_NONCE_BYTES, AEAD_TAG_BYTES, InitiatorHello::c_R, derive_es_ets(), derive_per_message_secrets(), GSC_KeyExchangeInfo::early_secret_key, GSC_KeyExchangeInfo::early_traffic_secret, GNUNET_CRYPTO_HpkePrivateKey::ecdhe_key, GNUNET_CRYPTO_HpkePublicKey::ecdhe_key, env, GNUNET_assert, GNUNET_CORE_CLASS_UNKNOWN, GNUNET_CORE_KX_STATE_INITIATOR_HELLO_SENT, GNUNET_CRYPTO_ecdhe_key_create(), GNUNET_CRYPTO_ecdhe_key_get_public(), GNUNET_CRYPTO_eddsa_kem_encaps(), GNUNET_CRYPTO_hash(), GNUNET_CRYPTO_hash_context_abort(), GNUNET_CRYPTO_hash_context_read(), GNUNET_CRYPTO_hash_context_start(), GNUNET_CRYPTO_random_u64(), GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_ERROR, GNUNET_log, GNUNET_memcpy, GNUNET_MESSAGE_TYPE_CORE_INITIATOR_HELLO, GNUNET_MQ_discard(), GNUNET_MQ_msg_extra, GNUNET_MQ_send_copy(), GNUNET_OK, GNUNET_PILS_get_identity(), GSC_pils, InitiatorHello::h_pk_R, monitor_notify_all(), GSC_KeyExchangeInfo::mq, my_identity, my_services_info, GSC_KeyExchangeInfo::peer, InitiatorHelloPayload::peer_class, GSC_KeyExchangeInfo::pk_e, InitiatorHello::pk_e, InitiatorHelloPayload::pk_I, GNUNET_PeerIdentity::public_key, InitiatorHello::r_I, GSC_KeyExchangeInfo::resend_env, resend_initiator_hello(), ret, GSC_KeyExchangeInfo::role, GSC_KeyExchangeInfo::sk_e, snapshot_transcript(), GSC_KeyExchangeInfo::ss_R, start_resend(), GSC_KeyExchangeInfo::status, and GSC_KeyExchangeInfo::transcript_hash_ctx.

Referenced by restart_kx().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ deliver_message()

static int deliver_message ( void *  cls,
const struct GNUNET_MessageHeader m 
)
static

Deliver P2P message to interested clients.

Invokes send twice, once for clients that want the full message, and once for clients that only want the header

Parameters
clsthe struct GSC_KeyExchangeInfo
mthe message
Returns
GNUNET_OK on success, GNUNET_NO to stop further processing (no error) GNUNET_SYSERR to stop further processing with error

Definition at line 816 of file gnunet-service-core_kx.c.

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}

References GNUNET_CORE_OPTION_SEND_FULL_INBOUND, GNUNET_CORE_OPTION_SEND_HDR_INBOUND, GNUNET_ERROR_TYPE_DEBUG, GNUNET_i2s(), GNUNET_log, GNUNET_OK, GSC_CLIENTS_deliver_message(), m, and GSC_KeyExchangeInfo::peer.

Referenced by handle_transport_notify_connect().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ schedule_resend()

static void schedule_resend ( struct GSC_KeyExchangeInfo kx,
GNUNET_SCHEDULER_TaskCallback  cb 
)
static

Arm resend_task for the next retransmission of the flight in resend_env and back the timer off for the one after that.

RFC 9147, Section 5.8: "implementations SHOULD use an initial timer value of 1000 ms and double the value at each retransmission, up to no less than 60 seconds."

Parameters
kxkey exchange whose flight to schedule
cbtask to run when the timer fires

Definition at line 848 of file gnunet-service-core_kx.c.

References GNUNET_assert, GNUNET_SCHEDULER_add_delayed(), GNUNET_TIME_relative_min(), GNUNET_TIME_relative_multiply(), GSC_KeyExchangeInfo::resend_delay, GSC_KeyExchangeInfo::resend_task, and RESEND_TIMEOUT_MAX.

Referenced by resend_initiator_done(), resend_initiator_hello(), resend_responder_hello(), and start_resend().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ start_resend()

static void start_resend ( struct GSC_KeyExchangeInfo kx,
GNUNET_SCHEDULER_TaskCallback  cb 
)
static

Start a handshake flight: kx will retransmit it RESEND_MAX_TRIES times, starting after RESEND_TIMEOUT and backing off from there.

Parameters
kxkey exchange whose flight to start
cbtask to run when the timer fires

Definition at line 868 of file gnunet-service-core_kx.c.

870{
873 schedule_resend (kx, cb);
874}

References GSC_KeyExchangeInfo::resend_delay, RESEND_MAX_TRIES, RESEND_TIMEOUT, GSC_KeyExchangeInfo::resend_tries_left, and schedule_resend().

Referenced by handle_responder_hello_cont(), send_initiator_hello(), and send_responder_hello().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ reset_handshake()

static void reset_handshake ( struct GSC_KeyExchangeInfo kx)
static

Discard the state of the handshake kx is in the middle of, so that a new one can be started.

Deliberately leaves the session – traffic keys, heartbeat_task, timeout, what clients were told – alone: those stay valid until a new handshake actually completes.

Parameters
kxkey exchange whose handshake state to drop

Definition at line 886 of file gnunet-service-core_kx.c.

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}

References cleanup_handshake_secrets(), GNUNET_CRYPTO_hash_context_abort(), GNUNET_MQ_discard(), GNUNET_SCHEDULER_cancel(), GSC_KeyExchangeInfo::ih_hash, GSC_KeyExchangeInfo::resend_env, GSC_KeyExchangeInfo::resend_task, GSC_KeyExchangeInfo::rh_hash, and GSC_KeyExchangeInfo::transcript_hash_ctx.

Referenced by abandon_exchange(), and handle_initiator_hello().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ abandon_exchange()

static void abandon_exchange ( struct GSC_KeyExchangeInfo kx)
static

Give up on the exchange kx is in and on the session it may have established, and return it to a state in which a fresh handshake can be run.

Leaves kx in GNUNET_CORE_KX_STATE_DOWN without notifying monitors; every caller either moves on to a new state right away or notifies itself.

Parameters
kxkey exchange to reset

Definition at line 928 of file gnunet-service-core_kx.c.

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}

References GSC_KeyExchangeInfo::association_up, GSC_KeyExchangeInfo::current_epoch, GSC_KeyExchangeInfo::current_sqn, GNUNET_CORE_KX_STATE_DOWN, GNUNET_NO, GNUNET_SCHEDULER_cancel(), GSC_SESSIONS_end(), GSC_KeyExchangeInfo::heartbeat_task, GSC_KeyExchangeInfo::heartbeats_unanswered, GSC_KeyExchangeInfo::peer, replay_reset_all(), reset_handshake(), GSC_KeyExchangeInfo::status, and GSC_KeyExchangeInfo::their_max_epoch.

Referenced by restart_kx().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ handle_transport_notify_connect()

static void * handle_transport_notify_connect ( void *  cls,
const struct GNUNET_PeerIdentity peer_id,
struct GNUNET_MQ_Handle mq 
)
static

Function called by transport to notify us that a peer connected to us (on the network level).

Starts the key exchange with the given peer.

Parameters
clsclosure (NULL)
mqmessage queue towards peer
peer_id(optional, may be NULL) the peer id of the connecting peer
Returns
key exchange information context

Definition at line 1020 of file gnunet-service-core_kx.c.

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}

References deliver_message(), GNUNET_assert, GNUNET_CONTAINER_DLL_insert, GNUNET_ERROR_TYPE_DEBUG, GNUNET_i2s(), GNUNET_log, GNUNET_memcpy, GNUNET_MST_create(), GNUNET_new, GNUNET_PILS_get_identity(), GSC_pils, kx_head, kx_tail, mq, GSC_KeyExchangeInfo::mq, GSC_KeyExchangeInfo::mst, my_identity, GSC_KeyExchangeInfo::peer, peer_id, and restart_kx().

Referenced by GSC_KX_start().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ derive_es_ets()

static void derive_es_ets ( const struct GNUNET_HashCode transcript,
const struct GNUNET_ShortHashCode ss_R,
struct GNUNET_ShortHashCode es,
struct GNUNET_ShortHashCode ets 
)
static

TODO propose a new scheme: don't choose an initiator and responder based on hashing the peer ids, but: let each peer be their own initiator (and responder) when opening a channel towards another peer.

It should be fine to have two channels in 'both directions' (one as responder, one as initiator) under the hood. This can be opaque to the upper layers. FIXME: (MSC) This is probably a bad idea in terms of security of the AKE! Schedule for

  • forwarding the transcript hash context and
  • deriving/generating keys/finished fields

Forwarding: Deriving Messages -> pk_e -> c_R -> r_I -> H(pk_R) -> ETS -> {pk_I, svcinfo_I}ETS -------------------------------------------------— send InitiatorHello -> c_e -> r_R -> *HTS -> {svcinfo_R, c_I}RHTS -> finished_R -> {finished_R}RHTS -> finished_I -> RATS_0 -> [{payload}RATS] -------------------------------------------------— send ResponderHello -> {finished_I}IHTS -> IATS_0 -------------------------------------------------— send InitiatorDone

Definition at line 1097 of file gnunet-service-core_kx.c.

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}

References CAKE_LABEL, EARLY_DATA_STR, GNUNET_assert, GNUNET_CRYPTO_hkdf_expand, GNUNET_CRYPTO_hkdf_extract(), GNUNET_CRYPTO_kdf_arg_auto, GNUNET_CRYPTO_kdf_arg_string, GNUNET_ERROR_TYPE_ERROR, GNUNET_log, GNUNET_OK, ret, and GSC_KeyExchangeInfo::ss_R.

Referenced by handle_initiator_hello_cont(), and send_initiator_hello().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ derive_sn()

static void derive_sn ( const struct GNUNET_ShortHashCode secret,
unsigned char *  sn,
size_t  sn_len 
)
static

Definition at line 1136 of file gnunet-service-core_kx.c.

1139{
1142 sn,
1143 sn_len,
1144 secret,
1147}

References CAKE_LABEL, GNUNET_assert, GNUNET_CRYPTO_hkdf_expand, GNUNET_CRYPTO_kdf_arg_string, and GNUNET_OK.

Referenced by GSC_KX_encrypt_and_transmit(), and handle_encrypted_message().

Here is the caller graph for this function:

◆ derive_hs()

static void derive_hs ( const struct GNUNET_ShortHashCode es,
const struct GNUNET_ShortHashCode ss_e,
struct GNUNET_ShortHashCode handshake_secret 
)
static

Derive the handshake secret.

Parameters
kxkey exchange info

Definition at line 1155 of file gnunet-service-core_kx.c.

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}

References CAKE_LABEL, DERIVED_STR, GNUNET_assert, GNUNET_B2S, GNUNET_CRYPTO_hkdf_expand, GNUNET_CRYPTO_hkdf_extract(), GNUNET_CRYPTO_kdf_arg_string, GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_ERROR, GNUNET_log, GNUNET_OK, and ret.

Referenced by handle_responder_hello(), and send_responder_hello().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ derive_ihts()

static void derive_ihts ( const struct GNUNET_HashCode transcript,
const struct GNUNET_ShortHashCode hs,
struct GNUNET_ShortHashCode ihts 
)
static

Derive the initiator handshake secret.

Parameters
kxkey exchange info

Definition at line 1201 of file gnunet-service-core_kx.c.

1204{
1207 ihts, // result
1208 sizeof (*ihts), // result len
1209 hs, // prk?
1212 GNUNET_CRYPTO_kdf_arg_auto (transcript)));
1213}

References CAKE_LABEL, GNUNET_assert, GNUNET_CRYPTO_hkdf_expand, GNUNET_CRYPTO_kdf_arg_auto, GNUNET_CRYPTO_kdf_arg_string, GNUNET_OK, and I_HS_TRAFFIC_STR.

Referenced by handle_responder_hello(), and send_responder_hello().

Here is the caller graph for this function:

◆ derive_rhts()

static void derive_rhts ( const struct GNUNET_HashCode transcript,
const struct GNUNET_ShortHashCode hs,
struct GNUNET_ShortHashCode rhts 
)
static

Derive the responder handshake secret.

Parameters
kxkey exchange info

Definition at line 1221 of file gnunet-service-core_kx.c.

1224{
1227 rhts,
1228 sizeof (*rhts),
1229 hs, // prk? TODO
1232 GNUNET_CRYPTO_kdf_arg_auto (transcript)));
1233}

References CAKE_LABEL, GNUNET_assert, GNUNET_CRYPTO_hkdf_expand, GNUNET_CRYPTO_kdf_arg_auto, GNUNET_CRYPTO_kdf_arg_string, GNUNET_OK, and R_HS_TRAFFIC_STR.

Referenced by handle_responder_hello(), and send_responder_hello().

Here is the caller graph for this function:

◆ derive_ms()

static void derive_ms ( const struct GNUNET_ShortHashCode hs,
const struct GNUNET_ShortHashCode ss_I,
struct GNUNET_ShortHashCode ms 
)
static

Derive the master secret.

Parameters
kxkey exchange info

Definition at line 1241 of file gnunet-service-core_kx.c.

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}

References CAKE_LABEL, DERIVED_STR, GNUNET_assert, GNUNET_CRYPTO_hkdf_expand, GNUNET_CRYPTO_hkdf_extract(), GNUNET_CRYPTO_kdf_arg_string, GNUNET_ERROR_TYPE_ERROR, GNUNET_log, GNUNET_OK, and ret.

Referenced by handle_responder_hello_cont(), and send_responder_hello().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ generate_per_record_nonce()

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] 
)
static

Generate per record nonce as per https://www.rfc-editor.org/rfc/rfc8446#section-5.3 using per key nonce and sequence number.

Definition at line 1281 of file gnunet-service-core_kx.c.

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}

References AEAD_NONCE_BYTES, and GNUNET_htonll().

Referenced by derive_per_message_secrets().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ derive_per_message_secrets()

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] 
)
static

key = HKDF-Expand [I,R][A,H]TS, "key", 32) nonce = HKDF-Expand ([I,R][A,H]TS, "iv", 24)

Definition at line 1306 of file gnunet-service-core_kx.c.

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}

References AEAD_KEY_BYTES, AEAD_NONCE_BYTES, CAKE_LABEL, generate_per_record_nonce(), GNUNET_assert, GNUNET_CRYPTO_hkdf_expand, GNUNET_CRYPTO_kdf_arg_string, GNUNET_OK, IV_STR, key, and KEY_STR.

Referenced by GSC_KX_encrypt_and_transmit(), handle_encrypted_message(), handle_initiator_done(), handle_initiator_hello_cont(), handle_responder_hello(), handle_responder_hello_cont(), send_initiator_hello(), and send_responder_hello().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ derive_next_ats()

static void derive_next_ats ( const struct GNUNET_ShortHashCode old_ats,
struct GNUNET_ShortHashCode new_ats 
)
static

Derive the next application secret.

Parameters
kxkey exchange info

Definition at line 1341 of file gnunet-service-core_kx.c.

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}

References CAKE_LABEL, GNUNET_assert, GNUNET_CRYPTO_hkdf_expand, GNUNET_CRYPTO_kdf_arg_string, GNUNET_ERROR_TYPE_ERROR, GNUNET_log, GNUNET_OK, ret, and TRAFFIC_UPD_STR.

Referenced by check_rekey(), handle_encrypted_message(), handle_heartbeat(), handle_initiator_done(), and handle_responder_hello_cont().

Here is the caller graph for this function:

◆ derive_initial_ats()

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 
)
static

Derive the initiator application secret.

Parameters
kxkey exchange info

Definition at line 1367 of file gnunet-service-core_kx.c.

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}

References CAKE_LABEL, GNUNET_assert, GNUNET_CRYPTO_hkdf_expand, GNUNET_CRYPTO_kdf_arg_auto, GNUNET_CRYPTO_kdf_arg_string, GNUNET_OK, I_AP_TRAFFIC_STR, R_AP_TRAFFIC_STR, and ROLE_INITIATOR.

Referenced by handle_initiator_done(), handle_responder_hello_cont(), and send_responder_hello().

Here is the caller graph for this function:

◆ generate_responder_finished()

static void generate_responder_finished ( const struct GNUNET_HashCode transcript,
const struct GNUNET_ShortHashCode ms,
struct GNUNET_HashCode result 
)
static

Generate the responder finished field.

Parameters
kxkey exchange info
resultlocation to which the responder finished field will be written to

Definition at line 1396 of file gnunet-service-core_kx.c.

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}

References CAKE_LABEL, GNUNET_assert, GNUNET_CRYPTO_hkdf_expand, GNUNET_CRYPTO_hmac(), GNUNET_CRYPTO_kdf_arg_string, GNUNET_ERROR_TYPE_ERROR, GNUNET_log, GNUNET_OK, R_FINISHED_STR, result, and ret.

Referenced by handle_responder_hello_cont(), and send_responder_hello().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ generate_initiator_finished()

static void generate_initiator_finished ( const struct GNUNET_HashCode transcript,
const struct GNUNET_ShortHashCode ms,
struct GNUNET_HashCode result 
)
static

Generate the initiator finished field.

Parameters
kxkey exchange info
resultlocation to which the initiator finished field will be written to

Definition at line 1430 of file gnunet-service-core_kx.c.

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}

References CAKE_LABEL, GNUNET_assert, GNUNET_CRYPTO_hkdf_expand, GNUNET_CRYPTO_hmac(), GNUNET_CRYPTO_kdf_arg_string, GNUNET_ERROR_TYPE_ERROR, GNUNET_log, GNUNET_OK, I_FINISHED_STR, result, and ret.

Referenced by handle_initiator_done(), and handle_responder_hello_cont().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ resend_responder_hello()

static void resend_responder_hello ( void *  cls)
static

Definition at line 1457 of file gnunet-service-core_kx.c.

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}

References GNUNET_ERROR_TYPE_WARNING, GNUNET_log, GNUNET_MQ_send_copy(), GSC_KeyExchangeInfo::mq, GSC_KeyExchangeInfo::resend_env, resend_responder_hello(), GSC_KeyExchangeInfo::resend_task, GSC_KeyExchangeInfo::resend_tries_left, restart_kx(), and schedule_resend().

Referenced by resend_responder_hello(), and send_responder_hello().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ send_responder_hello()

void send_responder_hello ( struct GSC_KeyExchangeInfo kx)

Definition at line 1479 of file gnunet-service-core_kx.c.

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}

References AEAD_KEY_BYTES, AEAD_NONCE_BYTES, AEAD_TAG_BYTES, ResponderHello::c_e, ResponderHelloPayload::c_I, GSC_KeyExchangeInfo::current_ats, GSC_KeyExchangeInfo::current_epoch, GSC_KeyExchangeInfo::current_sqn, derive_hs(), derive_ihts(), derive_initial_ats(), derive_ms(), derive_per_message_secrets(), derive_rhts(), GSC_KeyExchangeInfo::early_secret_key, env, finished, generate_responder_finished(), GNUNET_assert, GNUNET_CORE_KX_STATE_RESPONDER_HELLO_SENT, GNUNET_CRYPTO_eddsa_kem_encaps(), GNUNET_CRYPTO_hash_context_abort(), GNUNET_CRYPTO_hash_context_copy(), GNUNET_CRYPTO_hash_context_read(), GNUNET_CRYPTO_hpke_kem_encaps(), GNUNET_CRYPTO_random_u64(), GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_ERROR, GNUNET_h2s(), GNUNET_log, GNUNET_memcpy, GNUNET_MESSAGE_TYPE_CORE_RESPONDER_HELLO, GNUNET_MQ_msg_extra, GNUNET_MQ_send_copy(), GNUNET_OK, GSC_KeyExchangeInfo::handshake_secret, GSC_KeyExchangeInfo::ihts, GSC_KeyExchangeInfo::master_secret, monitor_notify_all(), GSC_KeyExchangeInfo::mq, my_services_info, GSC_KeyExchangeInfo::peer, GSC_KeyExchangeInfo::pk_e, GNUNET_PeerIdentity::public_key, ResponderHello::r_R, GSC_KeyExchangeInfo::resend_env, resend_responder_hello(), ret, GSC_KeyExchangeInfo::rhts, GSC_KeyExchangeInfo::role, ROLE_RESPONDER, snapshot_transcript(), GSC_KeyExchangeInfo::ss_e, GSC_KeyExchangeInfo::ss_I, start_resend(), GSC_KeyExchangeInfo::status, GSC_KeyExchangeInfo::transcript_hash_ctx, and transport.

Referenced by handle_initiator_hello_cont().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ handle_initiator_hello_cont()

static void handle_initiator_hello_cont ( struct GSC_KeyExchangeInfo kx,
const struct InitiatorHello ihm_e,
const struct GNUNET_ShortHashCode ss_R 
)
static

Finish handling the InitiatorHello ihm_e now that ss_R, the shared secret decapsulated with our peer identity's private key, is available.

Parameters
kxkey exchange the hello arrived on
ihm_ethe InitiatorHello, still owned by the message handler
ss_Rdecapsulation of ihm_e's c_R

Definition at line 1707 of file gnunet-service-core_kx.c.

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}

References AEAD_KEY_BYTES, AEAD_NONCE_BYTES, AEAD_TAG_BYTES, DECRYPTION_FAILURES_LOG_LEVEL, derive_es_ets(), derive_per_message_secrets(), GSC_KeyExchangeInfo::early_secret_key, GSC_KeyExchangeInfo::early_traffic_secret, GNUNET_CRYPTO_HpkePublicKey::ecdhe_key, GNUNET_assert, GNUNET_break_op, GNUNET_CORE_KX_STATE_AWAIT_INITIATION, GNUNET_CRYPTO_hash(), GNUNET_CRYPTO_hash_cmp(), GNUNET_CRYPTO_hash_context_abort(), GNUNET_CRYPTO_hash_context_read(), GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_WARNING, GNUNET_h2s(), GNUNET_i2s(), GNUNET_i2s2(), GNUNET_log, GNUNET_memcmp, GNUNET_memcpy, GNUNET_PILS_get_identity_hash(), GSC_pils, InitiatorHello::header, monitor_notify_all(), GSC_KeyExchangeInfo::peer, GSC_KeyExchangeInfo::pk_e, InitiatorHello::pk_e, InitiatorHelloPayload::pk_I, restart_kx(), ret, send_responder_hello(), GNUNET_MessageHeader::size, snapshot_transcript(), GSC_KeyExchangeInfo::ss_R, GSC_KeyExchangeInfo::status, GSC_KeyExchangeInfo::transcript_hash_ctx, and transport.

Referenced by handle_initiator_hello().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_initiator_hello()

static int check_initiator_hello ( void *  cls,
const struct InitiatorHello m 
)
static

Definition at line 1852 of file gnunet-service-core_kx.c.

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}

References AEAD_TAG_BYTES, GNUNET_OK, GNUNET_SYSERR, m, and size.

◆ handle_initiator_hello()

static void handle_initiator_hello ( void *  cls,
const struct InitiatorHello ihm_e 
)
static

Handle the InitiatorHello message.

  • derives necessary keys from the plaintext parts
  • decrypts the encrypted part
  • replies with ResponderHello message
    Parameters
    clsthe key exchange info
    ihm_eInitiatorHello message

Definition at line 1875 of file gnunet-service-core_kx.c.

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}

References InitiatorHello::c_R, gettext_noop, GNUNET_assert, GNUNET_break, GNUNET_break_op, GNUNET_CORE_KX_STATE_AWAIT_INITIATION, GNUNET_CORE_KX_STATE_INITIATOR_HELLO_RECEIVED, GNUNET_CORE_KX_STATE_RESPONDER_HELLO_SENT, GNUNET_CRYPTO_eddsa_kem_decaps(), GNUNET_CRYPTO_hash(), GNUNET_CRYPTO_hash_context_abort(), GNUNET_CRYPTO_hash_context_start(), GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_ERROR, GNUNET_ERROR_TYPE_INFO, GNUNET_ERROR_TYPE_WARNING, GNUNET_i2s(), GNUNET_log, GNUNET_memcmp, GNUNET_MQ_send_copy(), GNUNET_NO, GNUNET_OK, GNUNET_PILS_get_identity_hash(), GNUNET_PILS_get_private_key(), GNUNET_STATISTICS_update(), GSC_pils, GSC_stats, InitiatorHello::h_pk_R, handle_initiator_hello_cont(), InitiatorHello::header, GSC_KeyExchangeInfo::ih_hash, GSC_KeyExchangeInfo::mq, my_private_key, GSC_KeyExchangeInfo::peer, GSC_KeyExchangeInfo::resend_env, reset_handshake(), GSC_KeyExchangeInfo::role, ROLE_INITIATOR, GNUNET_MessageHeader::size, GSC_KeyExchangeInfo::status, GSC_KeyExchangeInfo::transcript_hash_ctx, and transport.

Here is the call graph for this function:

◆ resend_initiator_done()

static void resend_initiator_done ( void *  cls)
static

Definition at line 2112 of file gnunet-service-core_kx.c.

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}

References GNUNET_ERROR_TYPE_WARNING, GNUNET_log, GNUNET_MQ_send_copy(), GSC_KeyExchangeInfo::mq, GSC_KeyExchangeInfo::resend_env, resend_initiator_done(), GSC_KeyExchangeInfo::resend_task, GSC_KeyExchangeInfo::resend_tries_left, restart_kx(), and schedule_resend().

Referenced by handle_responder_hello_cont(), and resend_initiator_done().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ handle_responder_hello_cont()

static void handle_responder_hello_cont ( struct ResponderHelloCls rh_ctx,
const struct GNUNET_ShortHashCode ss_I 
)
static

Finish handling a ResponderHello now that ss_I, the shared secret decapsulated with our peer identity's private key, is available.

Takes ownership of rh_ctx.

Parameters
rh_ctxstate accumulated by handle_responder_hello()
ss_Idecapsulation of the ResponderHello's c_I

Definition at line 2143 of file gnunet-service-core_kx.c.

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++;
2311 ack_i.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
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}

References AEAD_KEY_BYTES, AEAD_NONCE_BYTES, AEAD_TAG_BYTES, GSC_KeyExchangeInfo::association_up, GSC_KeyExchangeInfo::current_ats, GSC_KeyExchangeInfo::current_epoch, GSC_KeyExchangeInfo::current_epoch_expiration, GSC_KeyExchangeInfo::current_sqn, ResponderHelloCls::decrypted_finish, derive_initial_ats(), derive_ms(), derive_next_ats(), derive_per_message_secrets(), env, EPOCH_EXPIRATION, InitiatorDone::finished, ResponderHelloCls::finished_enc, generate_initiator_finished(), generate_responder_finished(), GNUNET_assert, GNUNET_break_op, GNUNET_CORE_KX_STATE_INITIATOR_DONE_SENT, GNUNET_CRYPTO_hash_context_abort(), GNUNET_CRYPTO_hash_context_read(), GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_WARNING, GNUNET_free, GNUNET_h2s(), GNUNET_i2s(), GNUNET_log, GNUNET_MESSAGE_TYPE_CORE_ACK, GNUNET_MESSAGE_TYPE_CORE_INITIATOR_DONE, GNUNET_MQ_msg_extra, GNUNET_MQ_send_copy(), GNUNET_TIME_relative_to_absolute(), GNUNET_YES, GSC_KeyExchangeInfo::handshake_secret, ResponderHelloCls::hc, ConfirmationAck::header, ResponderHelloCls::hs, GSC_KeyExchangeInfo::ihts, ResponderHelloCls::ihts, ResponderHelloCls::kx, GSC_KeyExchangeInfo::master_secret, MAX_EPOCHS, monitor_notify_all(), GSC_KeyExchangeInfo::mq, GSC_KeyExchangeInfo::peer, replay_reset_all(), GSC_KeyExchangeInfo::resend_env, resend_initiator_done(), restart_kx(), ResponderHelloCls::rhp, GSC_KeyExchangeInfo::rhts, ResponderHelloCls::rhts, GSC_KeyExchangeInfo::role, ROLE_INITIATOR, ROLE_RESPONDER, GNUNET_MessageHeader::size, snapshot_transcript(), GSC_KeyExchangeInfo::ss_e, ResponderHelloCls::ss_e, GSC_KeyExchangeInfo::ss_I, start_resend(), GSC_KeyExchangeInfo::status, GSC_KeyExchangeInfo::their_ats, GSC_KeyExchangeInfo::their_max_epoch, GSC_KeyExchangeInfo::transcript_hash_ctx, transport, and GNUNET_MessageHeader::type.

Referenced by handle_responder_hello().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_responder_hello()

static int check_responder_hello ( void *  cls,
const struct ResponderHello m 
)
static

Definition at line 2338 of file gnunet-service-core_kx.c.

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}

References AEAD_TAG_BYTES, GNUNET_OK, GNUNET_SYSERR, m, and size.

◆ handle_responder_hello()

static void handle_responder_hello ( void *  cls,
const struct ResponderHello rhm_e 
)
static

Handle Responder Hello message.

Parameters
clskey exchange info
rhm_eResponderHello message

Definition at line 2359 of file gnunet-service-core_kx.c.

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}

References AEAD_KEY_BYTES, AEAD_NONCE_BYTES, AEAD_TAG_BYTES, ResponderHello::c_e, ResponderHelloPayload::c_I, ResponderHelloCls::decrypted_finish, DECRYPTION_FAILURES_LOG_LEVEL, derive_hs(), derive_ihts(), derive_per_message_secrets(), derive_rhts(), GSC_KeyExchangeInfo::early_secret_key, ResponderHelloCls::finished_enc, gettext_noop, GNUNET_assert, GNUNET_break_op, GNUNET_CORE_KX_STATE_INITIATOR_DONE_SENT, GNUNET_CORE_KX_STATE_INITIATOR_HELLO_SENT, GNUNET_CRYPTO_eddsa_kem_decaps(), GNUNET_CRYPTO_hash(), GNUNET_CRYPTO_hash_context_abort(), GNUNET_CRYPTO_hash_context_copy(), GNUNET_CRYPTO_hash_context_read(), GNUNET_CRYPTO_hpke_kem_decaps(), GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_ERROR, GNUNET_ERROR_TYPE_WARNING, GNUNET_free, GNUNET_h2s(), GNUNET_i2s(), GNUNET_log, GNUNET_malloc, GNUNET_memcmp, GNUNET_memcpy, GNUNET_MQ_discard(), GNUNET_MQ_send_copy(), GNUNET_new, GNUNET_NO, GNUNET_OK, GNUNET_PILS_get_private_key(), GNUNET_SCHEDULER_cancel(), GNUNET_STATISTICS_update(), GSC_pils, GSC_stats, handle_responder_hello_cont(), ResponderHelloCls::hc, ResponderHello::header, ResponderHelloCls::hs, ResponderHelloCls::ihts, ResponderHelloCls::kx, GSC_KeyExchangeInfo::mq, my_private_key, GSC_KeyExchangeInfo::peer, GSC_KeyExchangeInfo::resend_env, GSC_KeyExchangeInfo::resend_task, restart_kx(), ret, GSC_KeyExchangeInfo::rh_hash, ResponderHelloCls::rhm_e, ResponderHelloCls::rhp, ResponderHelloCls::rhts, GSC_KeyExchangeInfo::role, ROLE_RESPONDER, GNUNET_MessageHeader::size, GSC_KeyExchangeInfo::sk_e, snapshot_transcript(), ResponderHelloCls::ss_e, GSC_KeyExchangeInfo::status, GSC_KeyExchangeInfo::transcript_hash_ctx, and transport.

Here is the call graph for this function:

◆ check_initiator_done()

static int check_initiator_done ( void *  cls,
const struct InitiatorDone m 
)
static

Definition at line 2589 of file gnunet-service-core_kx.c.

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}

References GNUNET_OK, GNUNET_SYSERR, m, and size.

◆ handle_initiator_done()

static void handle_initiator_done ( void *  cls,
const struct InitiatorDone idm_e 
)
static

Handle InitiatorDone message.

Parameters
clskey exchange info
idm_eInitiatorDone message

FIXME we do not really have to calculate all this now

Definition at line 2607 of file gnunet-service-core_kx.c.

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));
2642 ack_r.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
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);
2811 ack_r.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
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}

References AEAD_KEY_BYTES, AEAD_NONCE_BYTES, AEAD_TAG_BYTES, GSC_KeyExchangeInfo::association_up, GSC_KeyExchangeInfo::class, cleanup_handshake_secrets(), GSC_KeyExchangeInfo::current_epoch, GSC_KeyExchangeInfo::current_epoch_expiration, GSC_KeyExchangeInfo::current_sqn, DECRYPTION_FAILURES_LOG_LEVEL, derive_initial_ats(), derive_next_ats(), derive_per_message_secrets(), EPOCH_EXPIRATION, InitiatorDone::finished, generate_initiator_finished(), GNUNET_break_op, GNUNET_CORE_KX_STATE_RESPONDER_CONNECTED, GNUNET_CORE_KX_STATE_RESPONDER_HELLO_SENT, GNUNET_CRYPTO_hash_context_abort(), GNUNET_CRYPTO_hash_context_copy(), GNUNET_CRYPTO_hash_context_read(), GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_ERROR, GNUNET_ERROR_TYPE_WARNING, GNUNET_h2s(), GNUNET_i2s(), GNUNET_log, GNUNET_memcpy, GNUNET_MESSAGE_TYPE_CORE_ACK, GNUNET_MQ_discard(), GNUNET_SCHEDULER_cancel(), GNUNET_TIME_relative_to_absolute(), GNUNET_YES, GSC_KX_encrypt_and_transmit(), GSC_SESSIONS_create(), GSC_SESSIONS_end(), ConfirmationAck::header, GSC_KeyExchangeInfo::heartbeat_task, GSC_KeyExchangeInfo::ihts, GSC_KeyExchangeInfo::master_secret, MAX_EPOCHS, monitor_notify_all(), GSC_KeyExchangeInfo::peer, replay_reset_all(), GSC_KeyExchangeInfo::resend_env, GSC_KeyExchangeInfo::resend_task, ret, GSC_KeyExchangeInfo::role, ROLE_INITIATOR, GNUNET_MessageHeader::size, snapshot_transcript(), GSC_KeyExchangeInfo::status, GSC_KeyExchangeInfo::their_ats, GSC_KeyExchangeInfo::their_max_epoch, GSC_KeyExchangeInfo::transcript_hash_ctx, transport, GNUNET_MessageHeader::type, and update_timeout().

Here is the call graph for this function:

◆ check_encrypted_message()

static int check_encrypted_message ( void *  cls,
const struct EncryptedMessage m 
)
static

Check an incoming encrypted message before handling it.

Parameters
clskey exchange info
mthe encrypted message

Definition at line 2828 of file gnunet-service-core_kx.c.

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}

References GNUNET_break_op, GNUNET_OK, GNUNET_SYSERR, m, and size.

◆ handle_heartbeat()

static void handle_heartbeat ( struct GSC_KeyExchangeInfo kx,
const struct Heartbeat m 
)
static

Handle a key update.

Parameters
clskey exchange info
mKeyUpdate message

Definition at line 2850 of file gnunet-service-core_kx.c.

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);
2877 ack.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
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}

References GSC_KeyExchangeInfo::current_ats, GSC_KeyExchangeInfo::current_epoch, GSC_KeyExchangeInfo::current_epoch_expiration, GSC_KeyExchangeInfo::current_sqn, derive_next_ats(), EPOCH_EXPIRATION, GNUNET_ERROR_TYPE_WARNING, GNUNET_log, GNUNET_MESSAGE_TYPE_CORE_ACK, GNUNET_TIME_relative_to_absolute(), GSC_HEARTBEAT_KEY_UPDATE_REQUESTED, GSC_KX_encrypt_and_transmit(), ConfirmationAck::header, m, GNUNET_MessageHeader::size, GNUNET_MessageHeader::type, and update_timeout().

Referenced by check_if_ack_or_heartbeat().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_if_ack_or_heartbeat()

static enum GNUNET_GenericReturnValue check_if_ack_or_heartbeat ( struct GSC_KeyExchangeInfo kx,
const char *  buf,
size_t  buf_len 
)
static

Waiting for ACK or heartbeat

Definition at line 2889 of file gnunet-service-core_kx.c.

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}

References GSC_KeyExchangeInfo::class, cleanup_handshake_secrets(), GSC_KeyExchangeInfo::current_epoch_expiration, EPOCH_EXPIRATION, GNUNET_CORE_KX_STATE_INITIATOR_CONNECTED, GNUNET_CORE_KX_STATE_INITIATOR_DONE_SENT, GNUNET_MESSAGE_TYPE_CORE_ACK, GNUNET_MESSAGE_TYPE_CORE_HEARTBEAT, GNUNET_MQ_discard(), GNUNET_NO, GNUNET_SCHEDULER_cancel(), GNUNET_TIME_relative_to_absolute(), GNUNET_YES, GSC_SESSIONS_create(), handle_heartbeat(), ConfirmationAck::header, Heartbeat::header, monitor_notify_all(), msg, GSC_KeyExchangeInfo::peer, GSC_KeyExchangeInfo::resend_env, GSC_KeyExchangeInfo::resend_task, GNUNET_MessageHeader::size, GSC_KeyExchangeInfo::status, GNUNET_MessageHeader::type, and update_timeout().

Referenced by handle_encrypted_message().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ handle_encrypted_message()

static void handle_encrypted_message ( void *  cls,
const struct EncryptedMessage m 
)
static

handle an encrypted message

Parameters
clskey exchange info
mencrypted message

Derive temporarily as we want to discard on decryption failure(s)

Prevent DoS FIXME maybe requires its own limit.

Definition at line 2948 of file gnunet-service-core_kx.c.

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}

References AEAD_KEY_BYTES, AEAD_NONCE_BYTES, GSC_KeyExchangeInfo::association_up, check_if_ack_or_heartbeat(), derive_next_ats(), derive_per_message_secrets(), derive_sn(), EncryptedMessage::epoch, gettext_noop, GNUNET_ALIGN, GNUNET_break_op, GNUNET_CORE_KX_STATE_DOWN, GNUNET_CORE_KX_STATE_INITIATOR_DONE_SENT, GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_INFO, GNUNET_ERROR_TYPE_WARNING, GNUNET_i2s(), GNUNET_log, GNUNET_MST_from_buffer(), GNUNET_NO, GNUNET_ntohll(), GNUNET_OK, GNUNET_print_bytes(), GNUNET_STATISTICS_update(), GNUNET_YES, GSC_stats, m, MAX_EPOCHS, GSC_KeyExchangeInfo::mst, GSC_KeyExchangeInfo::peer, replay_check(), replay_commit(), replay_reset(), restart_kx(), ret, size, GSC_KeyExchangeInfo::status, GSC_KeyExchangeInfo::their_ats, GSC_KeyExchangeInfo::their_max_epoch, transport, and update_timeout().

Here is the call graph for this function:

◆ handle_transport_notify_disconnect()

static void handle_transport_notify_disconnect ( void *  cls,
const struct GNUNET_PeerIdentity peer,
void *  handler_cls 
)
static

Function called by transport telling us that a peer disconnected.

Stop key exchange with the given peer. Clean up key material.

Parameters
clsclosure
peerthe peer that disconnected
handler_clsthe struct GSC_KeyExchangeInfo of the peer

Definition at line 3188 of file gnunet-service-core_kx.c.

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}

References gettext_noop, GNUNET_CONTAINER_DLL_remove, GNUNET_CORE_KX_PEER_DISCONNECT, GNUNET_CRYPTO_hash_context_abort(), GNUNET_ERROR_TYPE_DEBUG, GNUNET_free, GNUNET_i2s(), GNUNET_log, GNUNET_MQ_discard(), GNUNET_MST_destroy(), GNUNET_NO, GNUNET_SCHEDULER_cancel(), GNUNET_STATISTICS_update(), GSC_SESSIONS_end(), GSC_stats, GSC_KeyExchangeInfo::heartbeat_task, kx_head, kx_tail, monitor_notify_all(), GSC_KeyExchangeInfo::mst, GSC_KeyExchangeInfo::peer, GSC_KeyExchangeInfo::resend_env, GSC_KeyExchangeInfo::resend_task, GSC_KeyExchangeInfo::status, and GSC_KeyExchangeInfo::transcript_hash_ctx.

Referenced by GSC_KX_start().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ resend_initiator_hello()

static void resend_initiator_hello ( void *  cls)
static

Definition at line 3232 of file gnunet-service-core_kx.c.

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}

References GNUNET_ERROR_TYPE_WARNING, GNUNET_i2s(), GNUNET_log, GNUNET_MQ_send_copy(), GSC_KeyExchangeInfo::mq, GSC_KeyExchangeInfo::peer, GSC_KeyExchangeInfo::resend_env, resend_initiator_hello(), GSC_KeyExchangeInfo::resend_task, GSC_KeyExchangeInfo::resend_tries_left, restart_kx(), and schedule_resend().

Referenced by resend_initiator_hello(), and send_initiator_hello().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ check_rekey()

static enum GNUNET_GenericReturnValue check_rekey ( struct GSC_KeyExchangeInfo kx)
static

Move to the next epoch if the current one is exhausted.

Parameters
kxkey exchange to check
Returns
GNUNET_OK if kx may be used to send, GNUNET_SYSERR if the association had to be torn down instead

Definition at line 3395 of file gnunet-service-core_kx.c.

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}

References GNUNET_TIME_Absolute::abs_value_us, GSC_KeyExchangeInfo::current_ats, GSC_KeyExchangeInfo::current_epoch, GSC_KeyExchangeInfo::current_epoch_expiration, GSC_KeyExchangeInfo::current_sqn, derive_next_ats(), EPOCH_EXPIRATION, GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_WARNING, GNUNET_i2s(), GNUNET_log, GNUNET_OK, GNUNET_SYSERR, GNUNET_TIME_absolute_is_past(), GNUNET_TIME_relative_to_absolute(), GSC_KeyExchangeInfo::peer, and restart_kx().

Referenced by GSC_KX_encrypt_and_transmit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GSC_KX_encrypt_and_transmit()

void GSC_KX_encrypt_and_transmit ( struct GSC_KeyExchangeInfo kx,
const void *  payload,
size_t  payload_size 
)

Encrypt and transmit payload.

Encrypt and transmit a message with the given payload.

Parameters
kxkey exchange info
payloadthe payload
payload_sizesize of the payload

Definition at line 3439 of file gnunet-service-core_kx.c.

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}

References AEAD_KEY_BYTES, AEAD_NONCE_BYTES, GSC_KeyExchangeInfo::association_up, check_rekey(), GSC_KeyExchangeInfo::current_ats, GSC_KeyExchangeInfo::current_epoch, GSC_KeyExchangeInfo::current_sqn, derive_per_message_secrets(), derive_sn(), env, EncryptedMessage::epoch, GNUNET_assert, GNUNET_break, GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_ERROR, GNUNET_htonll(), GNUNET_log, GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE_CAKE, GNUNET_MQ_msg_extra, GNUNET_MQ_send(), GNUNET_NO, GNUNET_OK, GNUNET_print_bytes(), GNUNET_YES, GSC_KeyExchangeInfo::mq, payload, ret, EncryptedMessage::sequence_number, and EncryptedMessage::tag.

Referenced by handle_heartbeat(), handle_initiator_done(), send_heartbeat(), and try_transmission().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GSC_KX_start()

void GSC_KX_start ( void  )

Definition at line 3539 of file gnunet-service-core_kx.c.

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}

References _, GNUNET_assert, GNUNET_ERROR_TYPE_DEBUG, GNUNET_ERROR_TYPE_ERROR, GNUNET_log, GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE_CAKE, GNUNET_MESSAGE_TYPE_CORE_INITIATOR_DONE, GNUNET_MESSAGE_TYPE_CORE_INITIATOR_HELLO, GNUNET_MESSAGE_TYPE_CORE_RESPONDER_HELLO, GNUNET_MQ_handler_end, GNUNET_MQ_hd_var_size, GNUNET_notification_context_create(), GNUNET_OK, GNUNET_PILS_enable_private_key(), GNUNET_PILS_get_identity(), GNUNET_TRANSPORT_core_connect(), GSC_cfg, GSC_complete_initialization_cb(), GSC_KX_done(), GSC_pils, handle_transport_notify_connect(), handle_transport_notify_disconnect(), handlers, my_identity, nc, and transport.

Referenced by pid_change_cb().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ pid_change_cb()

void pid_change_cb ( void *  cls,
const struct GNUNET_HELLO_Parser parser,
const struct GNUNET_HashCode hash 
)

Definition at line 3610 of file gnunet-service-core_kx.c.

3613{
3614 if (NULL != transport)
3615 return;
3616
3617 GSC_KX_start ();
3618}

References GSC_KX_start(), and transport.

Referenced by GSC_KX_init().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GSC_KX_init()

int GSC_KX_init ( void  )

Initialize KX subsystem.

Returns
GNUNET_OK on success, GNUNET_SYSERR on failure

Definition at line 3627 of file gnunet-service-core_kx.c.

3628{
3631 NULL);
3632 if (NULL == GSC_pils)
3633 {
3634 GSC_KX_done ();
3635 return GNUNET_SYSERR;
3636 }
3637
3638 return GNUNET_OK;
3639}

References GNUNET_OK, GNUNET_PILS_connect(), GNUNET_SYSERR, GSC_cfg, GSC_KX_done(), GSC_pils, and pid_change_cb().

Referenced by run().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GSC_KX_done()

void GSC_KX_done ( void  )

Shutdown KX subsystem.

Definition at line 3646 of file gnunet-service-core_kx.c.

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}

References GNUNET_notification_context_destroy(), GNUNET_PILS_disconnect(), GNUNET_SCHEDULER_cancel(), GNUNET_TRANSPORT_core_disconnect(), GSC_pils, nc, rekey_task, and transport.

Referenced by GSC_KX_init(), GSC_KX_start(), and shutdown_task().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GSC_NEIGHBOURS_get_queue_length()

unsigned int GSC_NEIGHBOURS_get_queue_length ( const struct GSC_KeyExchangeInfo kxinfo)

Check how many messages are queued for the given neighbour.

Parameters
kxinfodata about neighbour to check
Returns
number of items in the message queue

Definition at line 3678 of file gnunet-service-core_kx.c.

3679{
3680 return GNUNET_MQ_get_length (kxinfo->mq);
3681}

References GNUNET_MQ_get_length(), and GSC_KeyExchangeInfo::mq.

Referenced by try_transmission().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GSC_NEIGHBOURS_check_excess_bandwidth()

int GSC_NEIGHBOURS_check_excess_bandwidth ( const struct GSC_KeyExchangeInfo target)

Check if the given neighbour has excess bandwidth available.

Parameters
targetneighbour to check
Returns
GNUNET_YES if excess bandwidth is available, GNUNET_NO if not

Definition at line 3685 of file gnunet-service-core_kx.c.

3686{
3687 return kxinfo->has_excess_bandwidth;
3688}

References GSC_KeyExchangeInfo::has_excess_bandwidth.

Referenced by try_transmission().

Here is the caller graph for this function:

◆ GSC_KX_handle_client_monitor_peers()

void GSC_KX_handle_client_monitor_peers ( struct GNUNET_MQ_Handle mq)

Handle GNUNET_MESSAGE_TYPE_CORE_MONITOR_PEERS request.

For this request type, the client does not have to have transmitted an INIT request. All current peers are returned, regardless of which message types they accept.

Parameters
mqmessage queue to add for monitoring

Definition at line 3700 of file gnunet-service-core_kx.c.

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}

References env, GNUNET_CORE_KX_ITERATION_FINISHED, GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY, GNUNET_MQ_msg, GNUNET_MQ_send(), GNUNET_notification_context_add(), GNUNET_TIME_absolute_hton(), GNUNET_TIME_UNIT_FOREVER_ABS, kx_head, mq, msg, nc, GSC_KeyExchangeInfo::next, GSC_KeyExchangeInfo::peer, MonitorNotifyMessage::state, GSC_KeyExchangeInfo::status, MonitorNotifyMessage::timeout, and GSC_KeyExchangeInfo::timeout.

Referenced by handle_client_monitor_peers().

Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ transport

◆ kx_head

◆ kx_tail

struct GSC_KeyExchangeInfo* kx_tail
static

◆ rekey_task

struct GNUNET_SCHEDULER_Task* rekey_task
static

Task scheduled for periodic re-generation (and thus rekeying) of our ephemeral key.

Definition at line 504 of file gnunet-service-core_kx.c.

Referenced by GSC_KX_done().

◆ nc

◆ my_services_info

char* my_services_info = ""
static

Our services info string TODO.

Definition at line 514 of file gnunet-service-core_kx.c.

Referenced by send_initiator_hello(), and send_responder_hello().