GNUnet 0.28.1-dev.4
 
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_HashCode h1;
1715 struct GNUNET_HashCode transcript;
1716 struct GNUNET_ShortHashCode es;
1717 struct GNUNET_ShortHashCode ets;
1719
1721 &ihm_e->pk_e,
1722 sizeof (ihm_e->pk_e));
1723 // 5. generate ETS (early_traffic_secret_key, decrypt pk_i
1724 // expand ETS <- expand ES <- extract ss_R
1725 // use ETS to decrypt
1726
1727 /* Forward the transcript hash context over the unencrypted fields to get it
1728 * to the same status that the initiator had when it needed to derive es and
1729 * ets for the encryption */
1732 ihm_e,
1733 sizeof (struct InitiatorHello));
1735 &transcript);
1736#if DEBUG_KX
1738 "Transcript snapshot for derivation of ES, ETS: `%s'\n",
1739 GNUNET_h2s (&transcript));
1740#endif
1741 derive_es_ets (&transcript, ss_R, &es, &ets);
1743 0,
1744 enc_key,
1745 enc_nonce);
1746 {
1747 struct InitiatorHelloPayload *ihmp;
1748 size_t ct_len = ihm_len - sizeof (struct InitiatorHello);
1749 unsigned char ihmp_buf[ct_len - AEAD_TAG_BYTES];
1750 ihmp = (struct InitiatorHelloPayload*) ihmp_buf;
1751 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
1752 ihmp_buf, // unsigned char *m
1753 NULL, // mlen_p message length
1754 NULL, // unsigned char *nsec - unused: NULL
1755 (unsigned char*) &ihm_e[1], // const unsigned char *c - ciphertext
1756 ct_len, // unsigned long long clen - length of ciphertext
1757 // mac, // const unsigned char *mac - authentication tag
1758 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
1759 0, // unsigned long long adlen
1760 enc_nonce, // const unsigned char *npub - nonce
1761 enc_key // const unsigned char *k - key
1762 );
1763 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pid_sender: %s\n",
1764 GNUNET_i2s (&ihmp->pk_I));
1765 if (0 != ret)
1766 {
1768 "Something went wrong decrypting: %d\n", ret);
1769 GNUNET_break_op (0);
1770 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1771 restart_kx (kx);
1772 return;
1773 }
1774 /* now forward it considering the encrypted messages that the initiator was
1775 * able to send after deriving the es and ets */
1777 &ihm_e[1],
1778 ct_len);
1779 GNUNET_memcpy (&kx->peer,
1780 &ihmp->pk_I,
1781 sizeof (struct GNUNET_PeerIdentity));
1782 }
1783
1784 my_identity_hash = GNUNET_PILS_get_identity_hash (GSC_pils);
1785 GNUNET_assert (NULL != my_identity_hash);
1786
1787 // We could follow with the rest of the Key Schedule (dES, HS, ...) for now
1788 /* Check that we are actually in the receiving role */
1789 GNUNET_CRYPTO_hash (&kx->peer, sizeof(struct GNUNET_PeerIdentity), &h1);
1790 if (0 < GNUNET_CRYPTO_hash_cmp (&h1, my_identity_hash))
1791 {
1792 /* peer with "lower" identity starts KX, otherwise we typically end up
1793 with both peers starting the exchange and transmit the 'set key'
1794 message twice */
1795 /* Something went wrong - we have the lower value and should have sent the
1796 * InitiatorHello, but instead received it. TODO handle this case
1797 * We might end up in this case if the initiator didn't initiate the
1798 * handshake long enough and the 'responder' initiates the handshake */
1800 "Something went wrong - we have the lower value and should have sent the InitiatorHello, but instead received it.\n");
1802 kx->transcript_hash_ctx = NULL;
1803 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1804 return;
1805 }
1806
1807 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer ID of other peer: %s\n", GNUNET_i2s
1808 (&kx->peer));
1809 /* We update the monitoring peers here because now we know
1810 * that we can decrypt the message AND know the PID
1811 */
1812 monitor_notify_all (kx);
1813 kx->ss_R = *ss_R;
1814 kx->early_secret_key = es;
1815 kx->early_traffic_secret = ets;
1817}
1818
1819
1820static int
1821check_initiator_hello (void *cls, const struct InitiatorHello *m)
1822{
1823 uint16_t size = ntohs (m->header.size);
1824
1825 if (size < sizeof (*m)
1826 + sizeof (struct InitiatorHelloPayload)
1828 {
1829 return GNUNET_SYSERR;
1830 }
1831 return GNUNET_OK;
1832}
1833
1834
1843static void
1844handle_initiator_hello (void *cls, const struct InitiatorHello *ihm_e)
1845{
1846 const struct GNUNET_HashCode *my_identity_hash;
1848 struct GSC_KeyExchangeInfo *kx = cls;
1849 struct GNUNET_HashCode ih_hash;
1850 struct GNUNET_ShortHashCode ss_R;
1851 size_t ihm_len;
1852
1853 ihm_len = ntohs (ihm_e->header.size);
1854 GNUNET_CRYPTO_hash (ihm_e,
1855 ihm_len,
1856 &ih_hash);
1857 if (ROLE_INITIATOR == kx->role)
1858 {
1859 GNUNET_break_op (0);
1861 "I am an initiator! Tearing down...\n");
1862 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1863 return;
1864 }
1866 {
1867 /* Now that the decapsulation is synchronous nothing can observe this
1868 state from the outside -- #handle_initiator_hello_cont() runs before
1869 we return. Keep the guard anyway: reaching it means the state
1870 machine leaked a state, not that a peer did anything. */
1871 GNUNET_break (0);
1873 "Already received InitiatorHello: %d %d\n", kx->role, kx->status
1874 );
1875 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1876 return;
1877 }
1879 {
1880 if (0 == GNUNET_memcmp (&ih_hash,
1881 &kx->ih_hash))
1882 {
1883 /* Not a new exchange at all: the initiator resent the very hello we
1884 are already answering, because our ResponderHello did not make it
1885 back in time (#resend_initiator_hello() sends a copy of the same
1886 envelope, so a retransmission is byte-identical).
1887
1888 Starting over here is what breaks the pair. A fresh
1889 #send_responder_hello() picks a new @e ss_e and feeds a new
1890 ResponderHello into the transcript, and the transcript is what both
1891 @e finished_R and @e finished_I are computed over. The initiator
1892 answers whichever ResponderHello reaches it first and binds its
1893 InitiatorDone to *that* transcript, while we have moved on to the
1894 transcript of our latest one -- so #handle_initiator_done() cannot
1895 verify @e finished_I and drops it, every retransmission included.
1896 Neither side can make progress and neither side sees an error: the
1897 initiator sits in #GNUNET_CORE_KX_STATE_INITIATOR_DONE_SENT
1898 reporting "Unexpected ResponderHello", we sit in
1899 #GNUNET_CORE_KX_STATE_RESPONDER_HELLO_SENT, and both merely run out
1900 of retries after RESEND_MAX_TRIES and start over -- with no reason
1901 for the next attempt to be any luckier. One InitiatorHello
1902 retransmission, which any hiccup on the path produces, is enough to
1903 lose the peer indefinitely.
1904
1905 Retransmit our flight instead and leave the handshake state alone,
1906 per RFC 9147, Section 5.8: "implementations MUST retransmit their
1907 last flight in response to a retransmitted flight from the peer".
1908 Our own @e resend_task keeps its schedule; this only adds the
1909 answer the initiator is waiting for. */
1911 "InitiatorHello repeated by `%s' in state %d\n",
1912 GNUNET_i2s (&kx->peer),
1913 kx->status);
1915 gettext_noop (
1916 "# InitiatorHello retransmissions received"),
1917 1,
1918 GNUNET_NO);
1920 (NULL != kx->resend_env))
1922 kx->resend_env);
1923 /* Past that state the initiator already had our ResponderHello (we
1924 only leave it once @e finished_I verifies), so this is a duplicate
1925 that crossed with its InitiatorDone. Nothing to answer, and
1926 nothing that may cost us the association we just built. */
1927 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1928 return;
1929 }
1930 /* The initiator has given up on whatever we still hold and started
1931 over. Only the initiator drives this handshake, so follow it rather
1932 than dropping the hello.
1933 This used to return, which deadlocks the pair whenever we are in
1934 #GNUNET_CORE_KX_STATE_RESPONDER_CONNECTED: an InitiatorHello is not
1935 an EncryptedMessage and so does not refresh @e timeout, and nothing
1936 else ever leaves that state, so we would reject every retransmit
1937 until our own idle timeout fires -- five minutes during which the
1938 initiator restarts its exchange every 50s and we report the peer as
1939 connected. In #GNUNET_CORE_KX_STATE_RESPONDER_HELLO_SENT it is a
1940 plain retransmit: the initiator resends precisely because it did not
1941 get our ResponderHello, and answering the hello it actually sent
1942 converges instead of leaving both sides to turn over on unrelated
1943 50s timers that need not ever re-phase.
1944
1945 This is RFC 9147, Section 5.11: "In cases where a server believes it
1946 has an existing association [...] and it receives an epoch=0
1947 ClientHello, it SHOULD proceed with a new handshake but MUST NOT
1948 destroy the existing association until the client has demonstrated
1949 reachability [...] by completing a complete handshake including
1950 delivering a verifiable Finished message."
1951
1952 So only the handshake state goes. An InitiatorHello is not
1953 authenticated -- @e finished_I in the InitiatorDone is our Finished
1954 -- and must not be able to cost us an association on its own. What
1955 we have keeps its traffic keys (@e association_up stays set, and the
1956 record layer keys off that rather than off @e status), its
1957 @e heartbeat_task and its @e timeout, and clients keep being told the
1958 peer is connected. #handle_initiator_done() does the swap once, and
1959 only once, @e finished_I verifies. If it never does, the old
1960 association dies of its own idle timeout exactly as it would have. */
1962 "Peer `%s' restarted the key exchange in state %d, following\n",
1963 GNUNET_i2s (&kx->peer),
1964 kx->status);
1965 reset_handshake (kx);
1966 }
1967 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received InitiatorHello: %d %d\n", kx->
1968 role, kx->status);
1969 GNUNET_assert (NULL == kx->transcript_hash_ctx);
1971 GNUNET_assert (NULL != kx->transcript_hash_ctx);
1972
1974 gettext_noop ("# key exchanges initiated"),
1975 1,
1976 GNUNET_NO);
1977
1979
1980 my_identity_hash = GNUNET_PILS_get_identity_hash (GSC_pils);
1981 GNUNET_assert (NULL != my_identity_hash);
1982
1983 // 1. verify type _INITIATOR_HELLO
1984 // - This is implicytly done by arriving within this handler
1985 // - or is this about verifying the 'additional data' part of aead?
1986 // should it check the encryption + mac? (is this implicitly done
1987 // while decrypting?)
1988 // 2. verify H(pk_R) matches pk_R
1989 if (0 != memcmp (&ihm_e->h_pk_R,
1990 my_identity_hash,
1991 sizeof (struct GNUNET_HashCode)))
1992 {
1994 "This message is not meant for us (H(PID) mismatch)\n");
1996 kx->transcript_hash_ctx = NULL;
1997 /* Leaving @e status at #GNUNET_CORE_KX_STATE_INITIATOR_HELLO_RECEIVED
1998 here wedges the kx: every later hello then hits the "already
1999 received" guard above and is dropped, forever. */
2001 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2002 return;
2003 }
2004 // FIXME this sometimes triggers in the tests - why?
2005 // 3. decaps -> shared_secret_R, c_R (kemChallenge)
2006 /* From here on this is the hello we answer, so a byte-identical one is a
2007 retransmission of it and must not restart the exchange. */
2008 kx->ih_hash = ih_hash;
2010 if (NULL == my_private_key)
2011 {
2012 /* #GSC_KX_start() enables local key access before we ever talk to
2013 TRANSPORT, so this means the key on disk does not match the identity
2014 PILS announced. We cannot answer any hello in that state. */
2016 "No private key for our peer identity, cannot answer hello"
2017 " from `%s'\n",
2018 GNUNET_i2s (&kx->peer));
2020 kx->transcript_hash_ctx = NULL;
2022 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2023 return;
2024 }
2025 if (GNUNET_OK !=
2027 &ihm_e->c_R,
2028 &ss_R))
2029 {
2030 GNUNET_break_op (0);
2032 "Failed to decapsulate c_R of hello from `%s'\n",
2033 GNUNET_i2s (&kx->peer));
2035 kx->transcript_hash_ctx = NULL;
2037 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2038 return;
2039 }
2041 ihm_e,
2042 &ss_R);
2043}
2044
2045
2046struct ResponderHelloCls
2047{
2048 /* Current KX session */
2049 struct GSC_KeyExchangeInfo *kx;
2050
2051 /* responder hello message - encrypted */
2052 struct ResponderHello rhm_e;
2053
2054 /* responder hello message - plain/decrypted */
2055 struct ResponderHelloPayload *rhp;
2056
2057 /* Decrypted finish hash */
2059
2060 /* Encrypted finished CT (for transcript later) */
2061 char finished_enc[sizeof (struct GNUNET_HashCode)
2062 + AEAD_TAG_BYTES];
2063
2064 /* Temporary transcript context */
2065 struct GNUNET_HashContext *hc;
2066
2067 /* Temporary handshake secret */
2068 struct GNUNET_ShortHashCode hs;
2069
2070 /* Temporary handshake secret */
2072
2073 /* Temporary handshake secret */
2075
2076 /* Temporary handshake secret */
2078};
2079
2080static void
2081resend_initiator_done (void *cls)
2082{
2083 struct GSC_KeyExchangeInfo *kx = cls;
2084
2085 kx->resend_task = NULL;
2086 if (0 == kx->resend_tries_left)
2087 {
2089 "Restarting KX\n");
2090 restart_kx (kx);
2091 return;
2092 }
2093 kx->resend_tries_left--;
2095 "Resending initiator done. Retries left: %u\n",
2096 kx->resend_tries_left);
2099}
2100
2101
2111static void
2113 const struct GNUNET_ShortHashCode *ss_I)
2114{
2115 struct GSC_KeyExchangeInfo *kx = rh_ctx->kx;
2116 struct InitiatorDone *idm_e; /* encrypted */
2117 struct InitiatorDone idm_local;
2118 struct InitiatorDone *idm_p; /* plaintext */
2119 struct GNUNET_MQ_Envelope *env;
2120 unsigned char enc_key[AEAD_KEY_BYTES];
2121 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2122 struct ConfirmationAck ack_i;
2123 struct GNUNET_HashCode transcript;
2124 struct GNUNET_ShortHashCode ms;
2125
2126 // XXX valgrind reports uninitialized memory
2127 // the following is a way to check whether this memory was meant
2128 // memset (&rhm_local, 0, sizeof (rhm_local)); - adapt to cls if still needed
2129 memset (&idm_local, 0, sizeof (idm_local));
2130
2131 kx->ss_I = *ss_I;
2132
2133 /* derive *ATS */
2134 derive_ms (&rh_ctx->hs, ss_I, &ms);;
2135 // 5. Create ResponderFinished as per Section 6 and check against decrypted payload.
2136 struct GNUNET_HashCode responder_finished;
2137 // Transcript updates, snapshot again
2138 snapshot_transcript (rh_ctx->hc,
2139 &transcript);
2140#if DEBUG_KX
2142 "Transcript snapshot for derivation of Rfinished: `%s'\n",
2143 GNUNET_h2s (&transcript));
2144#endif
2145 generate_responder_finished (&transcript,
2146 &ms,
2147 &responder_finished);
2148 if (0 != memcmp (&rh_ctx->decrypted_finish,
2149 &responder_finished,
2150 sizeof (struct GNUNET_HashCode)))
2151 {
2152 /* A peer that answers our InitiatorHello with a ResponderHello whose
2153 finished field does not verify must not be able to abort us; this
2154 used to be a GNUNET_assert (0). */
2155 GNUNET_break_op (0);
2157 "Could not verify \"responder finished\" from `%s'\n",
2158 GNUNET_i2s (&kx->peer));
2159 GNUNET_free (rh_ctx->rhp);
2161 GNUNET_free (rh_ctx);
2162 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2163 restart_kx (kx);
2164 return;
2165 }
2166
2167
2168 /* Forward the transcript
2169 * after generating finished_R,
2170 * before deriving *ATS */
2172 rh_ctx->hc,
2173 rh_ctx->finished_enc,
2174 sizeof (rh_ctx->finished_enc));
2175
2176 // At this point we cannot fail anymore and may lock into kx
2178 kx->transcript_hash_ctx = rh_ctx->hc;
2179 kx->ss_I = *ss_I;
2180 kx->handshake_secret = rh_ctx->hs;
2181 kx->ss_e = rh_ctx->ss_e;
2182 kx->ihts = rh_ctx->ihts;
2183 kx->rhts = rh_ctx->rhts;
2184 kx->master_secret = ms;
2185 GNUNET_free (rh_ctx->rhp);
2186 GNUNET_free (rh_ctx);
2187 rh_ctx = NULL;
2188
2190 &transcript);
2191#if DEBUG_KX
2193 "Transcript snapshot for derivation of *ATS: `%s'\n",
2194 GNUNET_h2s (&transcript));
2195#endif
2196 derive_initial_ats (&transcript,
2197 &kx->master_secret,
2199 &kx->their_ats[0]);
2200 for (int i = 0; i < MAX_EPOCHS - 1; i++)
2201 {
2202 derive_next_ats (&kx->their_ats[i],
2203 &kx->their_ats[i + 1]);
2204 }
2205 kx->their_max_epoch = MAX_EPOCHS - 1;
2206
2208 0,
2209 enc_key,
2210 enc_nonce);
2211 /* Create InitiatorDone message */
2212 idm_p = &idm_local; /* plaintext */
2213 env = GNUNET_MQ_msg_extra (idm_e,
2214 sizeof (ack_i)
2217 // 6. Create IteratorFinished as per Section 6.
2218 generate_initiator_finished (&transcript,
2219 &kx->master_secret,
2220 &idm_p->finished);
2222 "InteratorFinished: `%s'\n",
2223 GNUNET_h2s (&idm_p->finished));
2225 "Transcript `%s'\n",
2226 GNUNET_h2s (&transcript));
2227 // 7. Send InteratorFinished message encrypted with the key derived from IHTS to R
2228
2229 GNUNET_assert (0 == crypto_aead_xchacha20poly1305_ietf_encrypt (
2230 (unsigned char*) &idm_e->finished, /* c - ciphertext */
2231 NULL, /* clen_p */
2232 (unsigned char*) &idm_p->finished, /* idm_p - plaintext message */
2233 sizeof (idm_p->finished), // mlen
2234 NULL, 0, // ad, adlen // FIXME should this not be the other, unencrypted
2235 // fields?
2236 NULL, // nsec - unused
2237 enc_nonce, // npub - nonce
2238 enc_key)); // k - key IHTS
2239 /* Forward the transcript hash context
2240 * after generating finished_I and RATS_0
2241 * before deriving IATS_0 */
2243 &idm_e->finished,
2244 sizeof (idm_e->finished)
2245 + AEAD_TAG_BYTES);
2247 &transcript);
2248#if DEBUG_KX
2250 "Transcript snapshot for derivation of *ATS: `%s'\n",
2251 GNUNET_h2s (&transcript));
2252#endif
2253 derive_initial_ats (&transcript,
2254 &kx->master_secret,
2256 &kx->current_ats);
2257 kx->current_epoch = 0;
2258 kx->current_sqn = 0;
2259 /* We start sending under this epoch here, so it has to be dated here too.
2260 #check_if_ack_or_heartbeat() only sets @e current_epoch_expiration once
2261 the responder's Ack arrives; until then it holds whatever the previous
2262 association left (zero for a first exchange), and #check_rekey() treats
2263 a past expiration as "rekey now". Anything we send while waiting for
2264 the Ack -- the Ack we answer an early heartbeat with, say -- would then
2265 burn an epoch the responder has no reason to expect. */
2268 /* Application traffic keys are installed, so from the record layer's
2269 point of view the association exists from here: we have to be able to
2270 deprotect the responder's Ack, which arrives before the handshake is
2271 confirmed. The client-visible session is created only once it does. */
2272 replay_reset_all (kx);
2274 // 8. optionally encrypt payload TODO
2276 kx->current_sqn,
2277 enc_key,
2278 enc_nonce);
2279 kx->current_sqn++;
2280 ack_i.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
2281 ack_i.header.size = htons (sizeof ack_i);
2282 GNUNET_assert (0 == crypto_aead_xchacha20poly1305_ietf_encrypt (
2283 (unsigned char*) &idm_e[1], /* c - ciphertext */
2284 NULL, /* clen_p */
2285 (unsigned char*) &ack_i, /* rhm_p - plaintext message */
2286 sizeof ack_i, // mlen
2287 NULL, 0, // ad, adlen // FIXME should this not be the other, unencrypted
2288 // fields?
2289 NULL, // nsec - unused
2290 enc_nonce, // npub - nonce // FIXME nonce can be reused
2291 enc_key)); // k - key RHTS
2292
2293 GNUNET_MQ_send_copy (kx->mq, env);
2294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sent InitiatorDone: %d %d\n", kx->role,
2295 kx->status);
2296
2297
2298 kx->resend_env = env;
2301 monitor_notify_all (kx);
2302 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2303}
2304
2305
2306static int
2307check_responder_hello (void *cls, const struct ResponderHello *m)
2308{
2309 uint16_t size = ntohs (m->header.size);
2310
2311 if (size < sizeof (*m)
2312 + sizeof (struct ResponderHelloPayload)
2313 + sizeof (struct GNUNET_HashCode)
2314 + AEAD_TAG_BYTES * 2)
2315 {
2316 return GNUNET_SYSERR;
2317 }
2318 return GNUNET_OK;
2319}
2320
2321
2327static void
2328handle_responder_hello (void *cls, const struct ResponderHello *rhm_e)
2329{
2330 struct GSC_KeyExchangeInfo *kx = cls;
2332 struct ResponderHelloCls *rh_ctx;
2333 struct GNUNET_HashCode transcript;
2334 struct GNUNET_HashCode rh_hash;
2335 struct GNUNET_HashContext *hc;
2336 struct GNUNET_ShortHashCode ss_I;
2337 unsigned char enc_key[AEAD_KEY_BYTES];
2338 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2340
2341 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ResponderHello: %d %d\n", kx->
2342 role, kx->status);
2343
2344 GNUNET_CRYPTO_hash (rhm_e,
2345 ntohs (rhm_e->header.size),
2346 &rh_hash);
2347 if (ROLE_RESPONDER == kx->role)
2348 {
2349 GNUNET_break_op (0);
2351 "I am the responder! Ignoring.\n");
2352 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2353 return;
2354 }
2356 {
2358 (0 == GNUNET_memcmp (&rh_hash,
2359 &kx->rh_hash)))
2360 {
2361 /* The responder resent the ResponderHello we already answered, which
2362 means our InitiatorDone did not reach it. That is an ordinary
2363 retransmission, not a protocol violation -- the GNUNET_break_op()
2364 below used to report it as one, which is what "Unexpected
2365 ResponderHello in state 6" in the logs is. Answer it the way
2366 RFC 9147, Section 5.8 requires: "implementations MUST retransmit
2367 their last flight in response to a retransmitted flight from the
2368 peer". Our @e resend_task would get there on its own eventually;
2369 doing it here converges at the pace of the peer's timer instead of
2370 ours, and both are bounded by RESEND_MAX_TRIES. */
2372 "ResponderHello repeated by `%s', resending InitiatorDone\n",
2373 GNUNET_i2s (&kx->peer));
2375 gettext_noop (
2376 "# ResponderHello retransmissions received"),
2377 1,
2378 GNUNET_NO);
2379 if (NULL != kx->resend_env)
2381 kx->resend_env);
2382 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2383 return;
2384 }
2385 /* Outside of that state there is no handshake this message could
2386 belong to. In particular @e transcript_hash_ctx is then NULL, and
2387 #GNUNET_CRYPTO_hash_context_copy() dereferences its argument -- so
2388 a peer could crash us by sending a ResponderHello at any other
2389 time. Note that @e resend_task and @e resend_env below belong to
2390 the exchange we *are* in the middle of and must not be cleared
2391 before this point either. */
2392 GNUNET_break_op (0);
2394 "Unexpected ResponderHello in state %d, ignoring\n",
2395 kx->status);
2396 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2397 return;
2398 }
2399 /* From here on this is the hello we answer; recognising a retransmission
2400 of it is what lets us resend our InitiatorDone above instead of
2401 dropping the peer's flight on the floor. */
2402 kx->rh_hash = rh_hash;
2403 GNUNET_assert (NULL != kx->transcript_hash_ctx);
2405 if (NULL != kx->resend_task)
2406 {
2408 kx->resend_task = NULL;
2409 }
2410 if (NULL != kx->resend_env)
2411 {
2413 kx->resend_env = NULL;
2414 }
2415
2416 /* Forward the transcript hash context */
2418 rhm_e,
2419 sizeof (struct ResponderHello));
2420 // 1. Verify that the message type is CORE_RESPONDER_HELLO
2421 // - implicitly done by handling this message?
2422 // - or is this about verifying the 'additional data' part of aead?
2423 // should it check the encryption + mac? (is this implicitly done
2424 // while decrypting?)
2425 // 2. sse <- Decaps(ske,ce)
2426 rh_ctx = GNUNET_new (struct ResponderHelloCls);
2427 ret = GNUNET_CRYPTO_hpke_kem_decaps (&kx->sk_e, // secret/private ephemeral key of initiator (us)
2428 &rhm_e->c_e, // encapsulated key
2429 &rh_ctx->ss_e); // key - ss_e
2430 if (GNUNET_OK != ret)
2431 {
2433 "Something went wrong decapsulating ss_e\n");
2435 GNUNET_free (rh_ctx);
2436 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2437 return;
2438 }
2439 // 3. Generate IHTS and RHTS from Section 5 and decrypt ServicesInfo, cI and ResponderFinished.
2440 snapshot_transcript (hc, &transcript);
2441#if DEBUG_KX
2443 "Transcript snapshot for derivation of HS, *HTS: `%s'\n",
2444 GNUNET_h2s (&transcript));
2445#endif
2447 &rh_ctx->ss_e,
2448 &rh_ctx->hs);
2449 derive_rhts (&transcript,
2450 &rh_ctx->hs,
2451 &rh_ctx->rhts);
2452 derive_ihts (&transcript,
2453 &rh_ctx->hs,
2454 &rh_ctx->ihts);
2456 0,
2457 enc_key,
2458 enc_nonce);
2459 rh_ctx->kx = kx;
2460 GNUNET_memcpy (&rh_ctx->rhm_e, rhm_e, sizeof (*rhm_e));
2461 {
2462 unsigned long long int c_len;
2463 unsigned char *finished_buf;
2464 // use RHTS to decrypt
2465 c_len = ntohs (rhm_e->header.size) - sizeof (*rhm_e)
2466 - sizeof (struct GNUNET_HashCode)
2467 - AEAD_TAG_BYTES; // finished ct
2468 rh_ctx->rhp = GNUNET_malloc (c_len
2469 -
2471 rh_ctx->hc = hc;
2472 finished_buf = ((unsigned char*) &rhm_e[1]) + c_len;
2473 /* Forward the transcript_hash_ctx
2474 * after rhts has been generated,
2475 * before generating finished_R*/
2477 hc,
2478 &rhm_e[1],
2479 c_len);
2480
2481 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2482 (unsigned char*) rh_ctx->rhp, // unsigned char *m
2483 NULL, // mlen_p message length
2484 NULL, // unsigned char *nsec - unused: NULL
2485 (unsigned char*) &rhm_e[1], // const unsigned char *c - ciphertext
2486 c_len, // unsigned long long clen - length of ciphertext
2487 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2488 0, // unsigned long long adlen
2489 enc_nonce, // const unsigned char *npub - nonce
2490 enc_key // const unsigned char *k - key
2491 );
2492 if (0 != ret)
2493 {
2495 "Something went wrong decrypting: %d\n", ret);
2496 GNUNET_free (rh_ctx->rhp);
2497 GNUNET_free (rh_ctx);
2499 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2500 return;
2501 }
2502 // FIXME nonce reuse (see encryption)
2504 1,
2505 enc_key,
2506 enc_nonce);
2507 c_len = sizeof (struct GNUNET_HashCode)
2509 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2510 (unsigned char*) &rh_ctx->decrypted_finish, // unsigned char *m
2511 NULL, // mlen_p message length
2512 NULL, // unsigned char *nsec - unused: NULL
2513 finished_buf, // const unsigned char *c - ciphertext
2514 c_len, // unsigned long long clen - length of ciphertext
2515 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2516 0, // unsigned long long adlen
2517 enc_nonce, // const unsigned char *npub - nonce
2518 enc_key // const unsigned char *k - key
2519 );
2520 if (0 != ret)
2521 {
2523 "Something went wrong decrypting finished field: %d\n", ret);
2524 GNUNET_free (rh_ctx->rhp);
2525 GNUNET_free (rh_ctx);
2527 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2528 return;
2529 }
2530 GNUNET_memcpy (rh_ctx->finished_enc,
2531 finished_buf,
2532 c_len);
2533 }
2534 // 4. ssI <- Decaps(skI,cI).
2536 if ( (NULL == my_private_key) ||
2537 (GNUNET_OK !=
2539 &rh_ctx->rhp->c_I,
2540 &ss_I)) )
2541 {
2543 "Failed to decapsulate c_I of ResponderHello from `%s'\n",
2544 GNUNET_i2s (&kx->peer));
2545 GNUNET_free (rh_ctx->rhp);
2546 GNUNET_free (rh_ctx);
2548 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2549 restart_kx (kx);
2550 return;
2551 }
2553 &ss_I);
2554}
2555
2556
2557static int
2558check_initiator_done (void *cls, const struct InitiatorDone *m)
2559{
2560 uint16_t size = ntohs (m->header.size);
2561
2562 if (size < sizeof (*m) + sizeof (struct ConfirmationAck))
2563 {
2564 return GNUNET_SYSERR;
2565 }
2566 return GNUNET_OK;
2567}
2568
2569
2575static void
2576handle_initiator_done (void *cls, const struct InitiatorDone *idm_e)
2577{
2578 struct GSC_KeyExchangeInfo *kx = cls;
2579 struct InitiatorDone idm_local;
2580 struct InitiatorDone *idm_p = &idm_local; /* plaintext */
2581 struct GNUNET_HashCode initiator_finished;
2582 struct GNUNET_HashCode transcript;
2583 struct GNUNET_ShortHashCode their_ats;
2584 struct GNUNET_HashContext *hc;
2585 unsigned char enc_key[AEAD_KEY_BYTES];
2586 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2587 struct ConfirmationAck ack_i;
2588 struct ConfirmationAck ack_r;
2589 int8_t ret;
2590
2591 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received InitiatorDone: %d %d\n", kx->
2592 role, kx->status);
2593 if (ROLE_INITIATOR == kx->role)
2594 {
2595 GNUNET_break_op (0);
2597 "I am the initiator! Tearing down...\n");
2598 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2599 return;
2600 }
2602 {
2603 /* The initiator did not see our ConfirmationAck and is resending (it
2604 tries #RESEND_MAX_TRIES times). Our handshake secrets are gone --
2605 #cleanup_handshake_secrets() zeroed @e ihts -- so verifying this
2606 message again is not possible and would only look like a decryption
2607 failure. Send what the initiator is actually missing instead. */
2609 "InitiatorDone repeated by `%s', resending our Ack\n",
2610 GNUNET_i2s (&kx->peer));
2611 ack_r.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
2612 ack_r.header.size = htons (sizeof ack_r);
2614 &ack_r,
2615 sizeof ack_r);
2616 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2617 return;
2618 }
2620 {
2621 /* We have no handshake state this message could be checked against.
2622 Note that @e resend_task and @e resend_env below belong to whatever
2623 exchange we *are* in the middle of, so they must not be cleared
2624 before this point. */
2625 GNUNET_break_op (0);
2627 "Unexpected InitiatorDone in state %d, ignoring\n",
2628 kx->status);
2629 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2630 return;
2631 }
2632 if (NULL != kx->resend_task)
2633 {
2635 kx->resend_task = NULL;
2636 }
2637 if (NULL != kx->resend_env)
2638 {
2640 kx->resend_env = NULL;
2641 }
2643 0,
2644 enc_key,
2645 enc_nonce);
2646 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2647 (unsigned char*) &idm_p->finished, // unsigned char *m
2648 NULL, // mlen_p message length
2649 NULL, // unsigned char *nsec - unused: NULL
2650 (unsigned char*) &idm_e->finished, // const unsigned char *c - ciphertext
2651 sizeof (idm_p->finished) // unsigned long long clen - length of ciphertext
2653 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2654 0, // unsigned long long adlen
2655 enc_nonce, // const unsigned char *npub - nonce
2656 enc_key // const unsigned char *k - key
2657 );
2658 if (0 != ret)
2659 {
2661 "Something went wrong decrypting: %d\n", ret);
2662 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2663 return;
2664 }
2665
2666 // - verify finished_I
2667 /* Generate finished_I
2668 * after Forwarding until {finished_R}RHTS
2669 * (did so while we prepared responder hello)
2670 * before forwarding to [{payload}RATS and] {finished_I}IHTS */
2671 // (look at the end of handle_initiator_hello())
2672 snapshot_transcript (kx->transcript_hash_ctx, &transcript);
2673 generate_initiator_finished (&transcript,
2674 &kx->master_secret,
2675 &initiator_finished);
2676 if (0 != memcmp (&idm_p->finished,
2677 &initiator_finished,
2678 sizeof (struct GNUNET_HashCode)))
2679 {
2681 "Could not verify \"initiator finished\" hash.\n");
2683 "Want: `%s'\n",
2684 GNUNET_h2s (&initiator_finished));
2686 "Have: `%s'\n",
2687 GNUNET_h2s (&idm_p->finished));
2689 "Transcript `%s'\n",
2690 GNUNET_h2s (&transcript));
2691 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2692 return;
2693 }
2694
2695 /* Forward the transcript hash_context_read */
2698 &idm_e->finished,
2699 sizeof (idm_e->finished)
2700 + AEAD_TAG_BYTES);
2701 snapshot_transcript (hc, &transcript);
2702 derive_initial_ats (&transcript,
2703 &kx->master_secret,
2705 &their_ats);
2706 derive_per_message_secrets (&their_ats, // FIXME other HS epoch?
2707 0,
2708 enc_key,
2709 enc_nonce);
2710 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2711 (unsigned char*) &ack_i, // unsigned char *m
2712 NULL, // mlen_p message length
2713 NULL, // unsigned char *nsec - unused: NULL
2714 (unsigned char*) &idm_e[1], // const unsigned char *c - ciphertext
2715 sizeof (ack_i) + AEAD_TAG_BYTES, // unsigned long long clen - length of ciphertext
2716 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2717 0, // unsigned long long adlen
2718 enc_nonce, // const unsigned char *npub - nonce
2719 enc_key // const unsigned char *k - key
2720 );
2721 if (0 != ret)
2722 {
2724 "Something went wrong decrypting the Ack: %d\n", ret);
2726 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2727 return;
2728 }
2729 if ((sizeof ack_i != ntohs (ack_i.header.size)) ||
2730 (GNUNET_MESSAGE_TYPE_CORE_ACK != ntohs (ack_i.header.type)))
2731 {
2733 "Ack invalid!\n");
2735 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2736 return;
2737 }
2738 GNUNET_memcpy (&kx->their_ats[0],
2739 &their_ats,
2740 sizeof their_ats);
2744 for (int i = 0; i < MAX_EPOCHS - 1; i++)
2745 {
2746 derive_next_ats (&kx->their_ats[i],
2747 &kx->their_ats[i + 1]);
2748 }
2750 kx->transcript_hash_ctx = hc;
2755 monitor_notify_all (kx);
2756 /* @e finished_I has verified. RFC 9147, Section 5.11: the peer has now
2757 "demonstrated reachability [...] by completing a complete handshake
2758 including delivering a verifiable Finished message", so this is the
2759 point -- and the only point -- at which the old association may be
2760 destroyed. #handle_initiator_hello() deliberately left it running.
2761 GSC_SESSIONS_create() puts into @e sessions with
2762 #GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY under a
2763 GNUNET_assert(), so a leftover session is not merely untidy. */
2764 GSC_SESSIONS_end (&kx->peer);
2765 if (NULL != kx->heartbeat_task)
2766 {
2768 kx->heartbeat_task = NULL;
2769 }
2770 /* #send_initiator_done() starts the initiator at epoch 0 and we have to
2771 agree: on a kx that had an association before, these still hold the
2772 predecessor's values, and none of them was ever reset here. */
2773 kx->current_epoch = 0;
2774 kx->their_max_epoch = 0;
2775 kx->current_sqn = 1;
2776 replay_reset_all (kx);
2778 GSC_SESSIONS_create (&kx->peer, kx, kx->class);
2779 update_timeout (kx);
2780 ack_r.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
2781 ack_r.header.size = htons (sizeof ack_r);
2783 &ack_r,
2784 sizeof ack_r);
2785
2786 GNUNET_TRANSPORT_core_receive_continue (transport,
2787 &kx->peer);
2788}
2789
2790
2796static int
2797check_encrypted_message (void *cls, const struct EncryptedMessage *m)
2798{
2799 uint16_t size = ntohs (m->header.size) - sizeof(*m);
2800
2801 // TODO check (see check_encrypted ())
2802 // - check epoch
2803 // - check sequence number
2804 if (size < sizeof(struct GNUNET_MessageHeader))
2805 {
2806 GNUNET_break_op (0);
2807 return GNUNET_SYSERR;
2808 }
2809 return GNUNET_OK;
2810}
2811
2812
2818static void
2820 const struct Heartbeat *m)
2821{
2822 struct GNUNET_ShortHashCode new_ats;
2823 struct ConfirmationAck ack;
2824
2826 {
2827 if (kx->current_epoch == UINT64_MAX)
2828 {
2830 "Max epoch reached (you probably will never see this)\n");
2831 }
2832 else
2833 {
2834 kx->current_epoch++;
2837 kx->current_sqn = 0;
2839 &new_ats);
2840 memcpy (&kx->current_ats,
2841 &new_ats,
2842 sizeof new_ats);
2843 }
2844 }
2845 update_timeout (kx);
2846 ack.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
2847 ack.header.size = htons (sizeof ack);
2849 &ack,
2850 sizeof ack);
2851 /* NOTE: no GNUNET_TRANSPORT_core_receive_continue() here. We are called
2852 from #handle_encrypted_message(), which owns the message and issues
2853 exactly one call for it. */
2854}
2855
2856
2857static enum GNUNET_GenericReturnValue
2859 const char *buf,
2860 size_t buf_len)
2861{
2862 struct GNUNET_MessageHeader *msg;
2863 struct ConfirmationAck *ack;
2864 struct Heartbeat *hb;
2865
2866 if (sizeof *msg > buf_len)
2867 return GNUNET_NO;
2868 msg = (struct GNUNET_MessageHeader*) buf;
2869 if (GNUNET_MESSAGE_TYPE_CORE_ACK == ntohs (msg->type))
2870 {
2871 ack = (struct ConfirmationAck *) buf;
2872 if (sizeof *ack != ntohs (ack->header.size))
2873 return GNUNET_NO;
2874 }
2875 else if (GNUNET_MESSAGE_TYPE_CORE_HEARTBEAT == ntohs (msg->type))
2876 {
2877 hb = (struct Heartbeat*) buf;
2878 if (sizeof *hb != ntohs (hb->header.size))
2879 return GNUNET_NO;
2880 handle_heartbeat (kx, hb);
2881 }
2882 else
2883 {
2884 return GNUNET_NO;
2885 }
2886
2891 {
2892 GSC_SESSIONS_create (&kx->peer, kx, kx->class);
2897 if (NULL != kx->resend_task)
2899 kx->resend_task = NULL;
2900 if (NULL != kx->resend_env)
2902 kx->resend_env = NULL;
2903 monitor_notify_all (kx);
2904 }
2905 update_timeout (kx);
2906
2907 return GNUNET_YES;
2908}
2909
2910
2916static void
2917handle_encrypted_message (void *cls, const struct EncryptedMessage *m)
2918{
2919 struct GSC_KeyExchangeInfo *kx = cls;
2920 uint16_t size = ntohs (m->header.size);
2921 char buf[size - sizeof (*m)] GNUNET_ALIGN;
2922 unsigned char seq_enc_k[crypto_stream_chacha20_ietf_KEYBYTES];
2923 const unsigned char *seq_enc_nonce;
2924 unsigned char enc_key[AEAD_KEY_BYTES];
2925 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2926 struct GNUNET_ShortHashCode new_ats[MAX_EPOCHS];
2927 uint32_t seq_enc_ctr;
2928 uint64_t epoch;
2929 uint64_t m_seq;
2930 uint64_t m_seq_nbo;
2931 uint64_t c_len;
2932 int8_t ret;
2933
2934 // TODO look at handle_encrypted
2935 // - statistics
2936
2937 /* The record layer answers to @e association_up, not to @e status: a
2938 handshake may be in flight over an association that is still live
2939 (RFC 9147, Section 5.11), and records of the old epoch have to keep
2940 being deprotected while it is. Conversely a record we have no keys
2941 for is simply an invalid record -- RFC 9147, Section 4.5.2: "In
2942 general, invalid records SHOULD be silently discarded, thus preserving
2943 the association" -- so it must not end a session or restart anything.
2944 If we are idle it does tell us the peer believes in an association we
2945 do not have, which is worth one exchange. */
2946 if (GNUNET_YES != kx->association_up)
2947 {
2949 "Discarding record from `%s': no keys for epoch %" PRIu64 "\n",
2950 GNUNET_i2s (&kx->peer),
2951 GNUNET_ntohll (m->epoch));
2952 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2954 restart_kx (kx);
2955 return;
2956 }
2957 epoch = GNUNET_ntohll (m->epoch);
2962 memcpy (new_ats,
2963 kx->their_ats,
2964 MAX_EPOCHS * sizeof (struct GNUNET_ShortHashCode));
2965 // FIXME here we could introduce logic that sends heartbeats
2966 // with key update request if we have not seen a new
2967 // epoch after a while (e.g. EPOCH_EXPIRATION)
2968 if (kx->their_max_epoch < epoch)
2969 {
2974 if ((epoch - kx->their_max_epoch) > 2 * MAX_EPOCHS)
2975 {
2976 /* @e epoch is plaintext and not covered by the AEAD tag, so this is
2977 reached by a single flipped bit as readily as by a peer that really
2978 did skip ahead. Drop the message like the "too old" case below
2979 does; tearing the session down here means one unauthenticated
2980 header field costs a full re-handshake. */
2982 "Epoch %" PRIu64 " is too new, will not decrypt...\n",
2983 epoch);
2984 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2985 return;
2986 }
2987 for (uint64_t i = kx->their_max_epoch; i < epoch; i++)
2988 {
2989 derive_next_ats (&new_ats[i % MAX_EPOCHS],
2990 &new_ats[(i + 1) % MAX_EPOCHS]);
2991 /* This slot of the ring now holds a different key, so the window
2992 that went with the old one no longer means anything. */
2993 replay_reset (kx, i + 1);
2994 }
2995 }
2996 else if ((kx->their_max_epoch - epoch) > MAX_EPOCHS)
2997 {
2999 "Epoch %" PRIu64 " is too old, cannot decrypt...\n",
3000 epoch);
3001 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3002 return;
3003 }
3004 derive_sn (
3005 &new_ats[epoch % MAX_EPOCHS],
3006 seq_enc_k,
3007 sizeof seq_enc_k);
3008 /* compute the sequence number */
3009 seq_enc_ctr = *((uint32_t*) m->tag);
3010 seq_enc_nonce = &m->tag[sizeof (uint32_t)];
3011#if DEBUG_KX
3012 GNUNET_print_bytes (&new_ats[epoch % MAX_EPOCHS],
3013 sizeof (struct GNUNET_ShortHashCode),
3014 8,
3015 GNUNET_NO);
3016 GNUNET_print_bytes (seq_enc_k,
3017 sizeof seq_enc_k,
3018 8,
3019 GNUNET_NO);
3020 GNUNET_print_bytes ((char*) &seq_enc_ctr,
3021 sizeof seq_enc_ctr,
3022 8,
3023 GNUNET_NO);
3024#endif
3025 crypto_stream_chacha20_ietf_xor_ic (
3026 (unsigned char*) &m_seq_nbo,
3027 (unsigned char*) &m->sequence_number,
3028 sizeof (uint64_t),
3029 seq_enc_nonce,
3030 ntohl (seq_enc_ctr),
3031 seq_enc_k);
3032 m_seq = GNUNET_ntohll (m_seq_nbo);
3034 "Received encrypted message in epoch %" PRIu64
3035 " with E(SQN=%" PRIu64 ")=%" PRIu64
3036 "\n",
3037 epoch,
3038 m_seq,
3039 m->sequence_number);
3040 /* RFC 9147, Section 4.5.1. Cheap enough to do before deprotection, and
3041 doing it first means a flood of replayed records costs no AEAD work.
3042 The window itself is only moved once the record verifies, below. */
3043 if (GNUNET_OK != replay_check (kx, epoch, m_seq))
3044 {
3046 gettext_noop ("# replayed records discarded"),
3047 1,
3048 GNUNET_NO);
3050 "Discarding replayed record %" PRIu64 "/%" PRIu64
3051 " from `%s'\n",
3052 epoch,
3053 m_seq,
3054 GNUNET_i2s (&kx->peer));
3055 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3056 return;
3057 }
3058 /* We are the initiator and as we are going to receive,
3059 * we are using the responder key material */
3060 derive_per_message_secrets (&new_ats[epoch % MAX_EPOCHS],
3061 m_seq,
3062 enc_key,
3063 enc_nonce);
3064 // TODO checking sequence numbers - handle the case of out-of-sync messages!
3065 // for now only decrypt the payload
3066 // TODO encrypt other fields, too!
3067 // TODO
3068 // c_len = size - offsetof ();
3069 c_len = size - sizeof (struct EncryptedMessage);
3070 ret = crypto_aead_xchacha20poly1305_ietf_decrypt_detached (
3071 (unsigned char*) buf, // m - plain message
3072 NULL, // nsec - unused
3073 (unsigned char*) &m[1], // c - ciphertext
3074 c_len, // clen
3075 (const unsigned char*) &m->tag, // mac
3076 NULL, // ad - additional data TODO
3077 0, // adlen
3078 enc_nonce, // npub
3079 enc_key // k
3080 );
3081 if (0 != ret)
3082 {
3083 /* RFC 9147, Section 4.5.2: "invalid records SHOULD be silently
3084 discarded, thus preserving the association; however, an error MAY be
3085 logged for diagnostic purposes." Not a protocol violation on the
3086 peer's part either -- anything at all can arrive here -- so no
3087 GNUNET_break_op(). */
3089 gettext_noop ("# invalid records discarded"),
3090 1,
3091 GNUNET_NO);
3093 "Discarding record %" PRIu64 "/%" PRIu64 " from `%s':"
3094 " does not deprotect\n",
3095 epoch,
3096 m_seq,
3097 GNUNET_i2s (&kx->peer));
3098 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3099 return;
3100 }
3101 /* Deprotected, so the record is authentic and everything derived from it
3102 may now be committed: the epoch ring, the anti-replay window (RFC 9147,
3103 Section 4.5.1: "The window MUST NOT be updated due to a received record
3104 until that record has been deprotected successfully") and @e timeout.
3105 @e timeout is the only liveness signal CORE has and is what
3106 `gnunet-core -m' reports, so refreshing it any earlier would let
3107 anything merely shaped like a record keep a session nominally alive. */
3108 /* Only ever forward: @e their_max_epoch is the *highest* epoch we have
3109 seen, and the ratchet above keys off it. A record that was merely
3110 reordered across an epoch boundary -- entirely normal, the peer starts
3111 the new epoch at sequence number 0 while the old one is still in flight
3112 -- used to pull it back, so the next record of the newer epoch looked
3113 like a fresh advance and ran the loop again, wiping that epoch's
3114 anti-replay window (RFC 9147, Section 4.5.1) every single time. */
3115 if (kx->their_max_epoch < epoch)
3116 kx->their_max_epoch = epoch;
3117 memcpy (&kx->their_ats,
3118 new_ats,
3119 MAX_EPOCHS * sizeof (struct GNUNET_ShortHashCode));
3120 replay_commit (kx, epoch, m_seq);
3121 update_timeout (kx);
3122
3124 buf,
3125 sizeof buf))
3126 {
3128 {
3130 "Dropping message as we are still waiting for handshake ACK\n");
3131 GNUNET_break_op (0);
3132 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3133 return;
3134 }
3135 if (GNUNET_OK !=
3137 buf,
3138 sizeof buf,
3139 GNUNET_YES,
3140 GNUNET_NO))
3141 GNUNET_break_op (0);
3142 }
3143 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3144}
3145
3146
3156static void
3158 const struct GNUNET_PeerIdentity *peer,
3159 void *handler_cls)
3160{
3161 struct GSC_KeyExchangeInfo *kx = handler_cls;
3162 (void) cls;
3163
3165 "Peer `%s' disconnected from us.\n",
3166 GNUNET_i2s (&kx->peer));
3167 GSC_SESSIONS_end (&kx->peer);
3169 gettext_noop ("# key exchanges stopped"),
3170 1,
3171 GNUNET_NO);
3172 if (NULL != kx->resend_task)
3173 {
3175 kx->resend_task = NULL;
3176 }
3177 if (NULL != kx->resend_env)
3178 {
3180 kx->resend_env = NULL;
3181 }
3182 if (NULL != kx->heartbeat_task)
3183 {
3185 kx->heartbeat_task = NULL;
3186 }
3188 monitor_notify_all (kx);
3189 if (kx->transcript_hash_ctx)
3190 {
3192 kx->transcript_hash_ctx = NULL;
3193 }
3195 GNUNET_MST_destroy (kx->mst);
3196 GNUNET_free (kx);
3197}
3198
3199
3200static void
3201resend_initiator_hello (void *cls)
3202{
3203 struct GSC_KeyExchangeInfo *kx = cls;
3204
3205 kx->resend_task = NULL;
3206 if (0 == kx->resend_tries_left)
3207 {
3208 /* The InitiatorHello we keep repeating carries the ephemeral public key
3209 generated by #send_initiator_hello(), and only #restart_kx() ever
3210 generates a new one. Retrying the same message forever therefore
3211 never recovers from a responder that has dropped the exchange -- it
3212 just keeps a retransmit timer running against a peer that is not
3213 answering. Give up like #resend_responder_hello() and
3214 #resend_initiator_done() do and start a fresh exchange. */
3216 "InitiatorHello not answered by `%s', restarting KX\n",
3217 GNUNET_i2s (&kx->peer));
3218 restart_kx (kx);
3219 return;
3220 }
3221 kx->resend_tries_left--;
3223 "Resending InitiatorHello. Retries left: %u\n",
3224 kx->resend_tries_left);
3227}
3228
3229
3235static void
3237{
3238 const struct GNUNET_PeerIdentity *my_identity;
3239 struct GNUNET_MQ_Envelope *env;
3240 struct GNUNET_ShortHashCode es;
3241 struct GNUNET_ShortHashCode ets;
3242 struct GNUNET_ShortHashCode ss_R;
3243 struct InitiatorHelloPayload *ihmp; /* initiator hello message - buffer on stack */
3244 struct InitiatorHello *ihm_e; /* initiator hello message - encrypted */
3245 long long unsigned int c_len;
3246 unsigned char enc_key[AEAD_KEY_BYTES];
3247 unsigned char enc_nonce[AEAD_NONCE_BYTES];
3249 size_t pt_len;
3250
3252 GNUNET_assert (NULL != my_identity);
3253
3254 pt_len = sizeof (*ihmp) + strlen (my_services_info);
3255 c_len = pt_len + AEAD_TAG_BYTES;
3256 env = GNUNET_MQ_msg_extra (ihm_e,
3257 c_len,
3259 ihmp = (struct InitiatorHelloPayload*) &ihm_e[1];
3260 ihmp->peer_class = htons (GNUNET_CORE_CLASS_UNKNOWN); // TODO set this to a meaningful
3261 GNUNET_memcpy (&ihmp->pk_I,
3263 sizeof (struct GNUNET_PeerIdentity));
3264 GNUNET_CRYPTO_hash (&kx->peer, /* what to hash */ // TODO do we do this twice?
3265 sizeof (struct GNUNET_PeerIdentity),
3266 &ihm_e->h_pk_R); /* result */
3267 // TODO init hashcontext/transcript_hash
3268 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Send InitiatorHello: %d %d\n", kx->role,
3269 kx->status);
3270 GNUNET_assert (NULL == kx->transcript_hash_ctx);
3272 GNUNET_assert (NULL != kx->transcript_hash_ctx);
3273 // TODO fill services_info
3274
3275 // 1. Encaps
3276 ret = GNUNET_CRYPTO_eddsa_kem_encaps (&kx->peer.public_key, // public ephemeral key of initiator
3277 &ihm_e->c_R, // encapsulated key
3278 &ss_R); // key - ss_R
3279 if (GNUNET_OK != ret)
3280 {
3282 "Something went wrong encapsulating ss_R\n");
3283 // TODO handle
3284 }
3285 // 2. generate rR (uint64_t) - is this the nonce? Naming seems not quite
3286 // consistent
3287 ihm_e->r_I =
3288 GNUNET_CRYPTO_random_u64 (UINT64_MAX);
3289 // 3. generate sk_e/pk_e - ephemeral key
3292 &kx->sk_e.ecdhe_key,
3293 &kx->pk_e.ecdhe_key);
3294 GNUNET_memcpy (&ihm_e->pk_e,
3295 &kx->pk_e.ecdhe_key,
3296 sizeof (kx->pk_e.ecdhe_key));
3297 // 4. generate ETS to encrypt
3298 // generate ETS (early_traffic_secret_key, decrypt pk_i
3299 // expand ETS <- expand ES <- extract ss_R
3300 // use ETS to decrypt
3302 ihm_e,
3303 sizeof (struct InitiatorHello));
3304 {
3305 struct GNUNET_HashCode transcript;
3307 &transcript);
3308 derive_es_ets (&transcript,
3309 &ss_R,
3310 &es,
3311 &ets);
3313 0,
3314 enc_key,
3315 enc_nonce);
3316 }
3317 // 5. encrypt
3318
3319 ret = crypto_aead_xchacha20poly1305_ietf_encrypt (
3320 (unsigned char*) &ihm_e[1], /* c - ciphertext */
3321 // mac,
3322 // NULL, // maclen_p
3323 &c_len, /* clen_p */
3324 (unsigned char*) ihmp, /* m - plaintext message */
3325 pt_len, // mlen
3326 NULL, 0, // ad, adlen // FIXME maybe over the unencrypted header?
3327 // fields?
3328 NULL, // nsec - unused
3329 enc_nonce, // npub - nonce
3330 enc_key); // k - key
3331 if (0 != ret)
3332 {
3333 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong encrypting\n");
3335 kx->transcript_hash_ctx = NULL;
3337 return;
3338 }
3339 /* Forward the transcript */
3342 &ihm_e[1],
3343 c_len);
3344
3346 kx->early_secret_key = es;
3347 kx->early_traffic_secret = ets;
3348 kx->ss_R = ss_R;
3349 monitor_notify_all (kx);
3350 GNUNET_MQ_send_copy (kx->mq, env);
3351 kx->resend_env = env;
3353}
3354
3355
3363static enum GNUNET_GenericReturnValue
3365{
3366 struct GNUNET_ShortHashCode new_ats;
3367
3368 if ((UINT64_MAX == kx->current_sqn) ||
3370 {
3372 "Epoch expiration %" PRIu64 " SQN %" PRIu64
3373 ", incrementing epoch...\n",
3375 kx->current_sqn);
3376 if (UINT64_MAX == kx->current_epoch)
3377 {
3378 /* RFC 9147, Section 6.1: "Implementations MUST NOT allow the epoch to
3379 wrap, but instead MUST establish a new association, terminating the
3380 old association". This used to be a GNUNET_assert(). */
3382 "Epoch exhausted for `%s', starting a new association\n",
3383 GNUNET_i2s (&kx->peer));
3384 restart_kx (kx);
3385 return GNUNET_SYSERR;
3386 }
3387 kx->current_epoch++;
3390 kx->current_sqn = 0;
3392 &new_ats);
3393 memcpy (&kx->current_ats,
3394 &new_ats,
3395 sizeof new_ats);
3396 }
3397 return GNUNET_OK;
3398}
3399
3400
3407void
3409 const void *payload,
3410 size_t payload_size)
3411{
3412 struct GNUNET_MQ_Envelope *env;
3413 struct EncryptedMessage *encrypted_msg;
3414 unsigned char enc_key[AEAD_KEY_BYTES];
3415 unsigned char enc_nonce[AEAD_NONCE_BYTES];
3416 unsigned char seq_enc_k[crypto_stream_chacha20_ietf_KEYBYTES];
3417 uint64_t sqn;
3418 uint64_t epoch;
3419 int8_t ret;
3420
3421 encrypted_msg = NULL;
3422
3423 if (GNUNET_YES != kx->association_up)
3424 {
3425 /* No application traffic keys installed -- there is nothing to protect
3426 this with. Callers reach this through a session, which only exists
3427 while the association does, so this is a should-not-happen. */
3428 GNUNET_break (0);
3429 return;
3430 }
3431 if (GNUNET_OK != check_rekey (kx))
3432 return; /* association was torn down, @e current_ats is gone */
3433 sqn = kx->current_sqn;
3434 epoch = kx->current_epoch;
3435 /* We are the sender and as we are going to send,
3436 * we are using the initiator key material */
3438 sqn,
3439 enc_key,
3440 enc_nonce);
3441 kx->current_sqn++;
3442 derive_sn (&kx->current_ats,
3443 seq_enc_k,
3444 sizeof seq_enc_k);
3445 env = GNUNET_MQ_msg_extra (encrypted_msg,
3446 payload_size,
3448 // only encrypt the payload for now
3449 // TODO encrypt other fields as well
3450 ret = crypto_aead_xchacha20poly1305_ietf_encrypt_detached (
3451 (unsigned char*) &encrypted_msg[1], // c - resulting ciphertext
3452 (unsigned char*) &encrypted_msg->tag, // mac - resulting mac/tag
3453 NULL, // maclen
3454 (unsigned char*) payload, // m - plain message
3455 payload_size, // mlen
3456 NULL, // ad - additional data TODO also cover the unencrypted part (epoch)
3457 0, // adlen
3458 NULL, // nsec - unused
3459 enc_nonce, // npub nonce
3460 enc_key // k - key
3461 );
3462 if (0 != ret)
3463 {
3465 "Something went wrong encrypting message\n");
3466 GNUNET_assert (0);
3467 }
3468 {
3469 /* compute the sequence number */
3470 unsigned char *seq_enc_nonce;
3471 uint64_t seq_nbo;
3472 uint32_t seq_enc_ctr;
3473
3474 seq_nbo = GNUNET_htonll (sqn);
3475 seq_enc_ctr = *((uint32_t*) encrypted_msg->tag);
3476 seq_enc_nonce = &encrypted_msg->tag[sizeof (uint32_t)];
3477 crypto_stream_chacha20_ietf_xor_ic (
3478 (unsigned char*) &encrypted_msg->sequence_number,
3479 (unsigned char*) &seq_nbo,
3480 sizeof seq_nbo,
3481 seq_enc_nonce,
3482 ntohl (seq_enc_ctr),
3483 seq_enc_k);
3484#if DEBUG_KX
3485 GNUNET_print_bytes (seq_enc_k,
3486 sizeof seq_enc_k,
3487 8,
3488 GNUNET_NO);
3489 GNUNET_print_bytes ((char*) &seq_enc_ctr,
3490 sizeof seq_enc_ctr,
3491 8,
3492 GNUNET_NO);
3493#endif
3495 "Sending encrypted message with E(SQN=%" PRIu64 ")=%" PRIu64
3496 "\n",
3497 sqn,
3498 encrypted_msg->sequence_number);
3499 }
3500 encrypted_msg->epoch = GNUNET_htonll (epoch);
3501
3502 // TODO actually copy payload
3503 GNUNET_MQ_send (kx->mq, env);
3504}
3505
3506
3507void
3508GSC_KX_start (void)
3509{
3510 const struct GNUNET_PeerIdentity *my_identity;
3512 GNUNET_MQ_hd_var_size (initiator_hello,
3514 struct InitiatorHello,
3515 NULL),
3516 GNUNET_MQ_hd_var_size (initiator_done,
3518 struct InitiatorDone,
3519 NULL),
3520 GNUNET_MQ_hd_var_size (responder_hello,
3522 struct ResponderHello,
3523 NULL),
3524 GNUNET_MQ_hd_var_size (encrypted_message, // TODO rename?
3526 struct EncryptedMessage,
3527 NULL),
3529 };
3530
3532 GNUNET_assert (NULL != my_identity);
3533
3534 /* Decapsulate with our peer identity's private key directly instead of
3535 round-tripping through the PILS service. The shared secret is needed
3536 in the middle of processing a handshake message, and an asynchronous
3537 answer meant that every InitiatorHello and ResponderHello had to be
3538 parked with its kx across a callback: the kx could be torn down or
3539 freed underneath it, two hellos could be in flight at once, and if
3540 the answer never came (PILS restarting) the handshake stalled *and*
3541 the message was never acknowledged to TRANSPORT. */
3542 if (GNUNET_OK !=
3544 {
3546 _ ("Failed to load our private key, "
3547 "cannot run key exchange\n"));
3548 GSC_KX_done ();
3549 return;
3550 }
3551
3553 transport =
3556 handlers,
3557 NULL, // cls - this connection-independant
3558 // cls seems not to be needed.
3559 // the connection-specific cls
3560 // will be set as a return value
3561 // of
3562 // handle_transport_notify_connect
3565 if (NULL == transport)
3566 {
3567 GSC_KX_done ();
3568 return;
3569 }
3570
3572 "Connected to TRANSPORT\n");
3573
3575}
3576
3577
3578void
3579pid_change_cb (void *cls,
3580 const struct GNUNET_HELLO_Parser *parser,
3581 const struct GNUNET_HashCode *hash)
3582{
3583 if (NULL != transport)
3584 return;
3585
3586 GSC_KX_start ();
3587}
3588
3589
3595int
3596GSC_KX_init (void)
3597{
3600 NULL);
3601 if (NULL == GSC_pils)
3602 {
3603 GSC_KX_done ();
3604 return GNUNET_SYSERR;
3605 }
3606
3607 return GNUNET_OK;
3608}
3609
3610
3614void
3615GSC_KX_done ()
3616{
3617 if (NULL != GSC_pils)
3618 {
3620 GSC_pils = NULL;
3621 }
3622 if (NULL != transport)
3623 {
3625 transport = NULL;
3626 }
3627 if (NULL != rekey_task)
3628 {
3630 rekey_task = NULL;
3631 }
3632 if (NULL != nc)
3633 {
3635 nc = NULL;
3636 }
3637}
3638
3639
3646unsigned int
3648{
3649 return GNUNET_MQ_get_length (kxinfo->mq);
3650}
3651
3652
3653int
3655{
3656 return kxinfo->has_excess_bandwidth;
3657}
3658
3659
3668void
3670{
3671 struct GNUNET_MQ_Envelope *env;
3672 struct MonitorNotifyMessage *done_msg;
3673 struct GSC_KeyExchangeInfo *kx;
3674
3676 for (kx = kx_head; NULL != kx; kx = kx->next)
3677 {
3678 struct GNUNET_MQ_Envelope *env_notify;
3679 struct MonitorNotifyMessage *msg;
3680
3682 msg->state = htonl ((uint32_t) kx->status);
3683 msg->peer = kx->peer;
3684 msg->timeout = GNUNET_TIME_absolute_hton (kx->timeout);
3685 GNUNET_MQ_send (mq, env_notify);
3686 }
3688 done_msg->state = htonl ((uint32_t) GNUNET_CORE_KX_ITERATION_FINISHED);
3691}
3692
3693
3694/* 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).
@ 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 3237 of file gnunet-service-core_kx.c.

3238{
3239 const struct GNUNET_PeerIdentity *my_identity;
3240 struct GNUNET_MQ_Envelope *env;
3241 struct GNUNET_ShortHashCode es;
3242 struct GNUNET_ShortHashCode ets;
3243 struct GNUNET_ShortHashCode ss_R;
3244 struct InitiatorHelloPayload *ihmp; /* initiator hello message - buffer on stack */
3245 struct InitiatorHello *ihm_e; /* initiator hello message - encrypted */
3246 long long unsigned int c_len;
3247 unsigned char enc_key[AEAD_KEY_BYTES];
3248 unsigned char enc_nonce[AEAD_NONCE_BYTES];
3250 size_t pt_len;
3251
3253 GNUNET_assert (NULL != my_identity);
3254
3255 pt_len = sizeof (*ihmp) + strlen (my_services_info);
3256 c_len = pt_len + AEAD_TAG_BYTES;
3257 env = GNUNET_MQ_msg_extra (ihm_e,
3258 c_len,
3260 ihmp = (struct InitiatorHelloPayload*) &ihm_e[1];
3261 ihmp->peer_class = htons (GNUNET_CORE_CLASS_UNKNOWN); // TODO set this to a meaningful
3262 GNUNET_memcpy (&ihmp->pk_I,
3264 sizeof (struct GNUNET_PeerIdentity));
3265 GNUNET_CRYPTO_hash (&kx->peer, /* what to hash */ // TODO do we do this twice?
3266 sizeof (struct GNUNET_PeerIdentity),
3267 &ihm_e->h_pk_R); /* result */
3268 // TODO init hashcontext/transcript_hash
3269 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Send InitiatorHello: %d %d\n", kx->role,
3270 kx->status);
3271 GNUNET_assert (NULL == kx->transcript_hash_ctx);
3273 GNUNET_assert (NULL != kx->transcript_hash_ctx);
3274 // TODO fill services_info
3275
3276 // 1. Encaps
3277 ret = GNUNET_CRYPTO_eddsa_kem_encaps (&kx->peer.public_key, // public ephemeral key of initiator
3278 &ihm_e->c_R, // encapsulated key
3279 &ss_R); // key - ss_R
3280 if (GNUNET_OK != ret)
3281 {
3283 "Something went wrong encapsulating ss_R\n");
3284 // TODO handle
3285 }
3286 // 2. generate rR (uint64_t) - is this the nonce? Naming seems not quite
3287 // consistent
3288 ihm_e->r_I =
3289 GNUNET_CRYPTO_random_u64 (UINT64_MAX);
3290 // 3. generate sk_e/pk_e - ephemeral key
3293 &kx->sk_e.ecdhe_key,
3294 &kx->pk_e.ecdhe_key);
3295 GNUNET_memcpy (&ihm_e->pk_e,
3296 &kx->pk_e.ecdhe_key,
3297 sizeof (kx->pk_e.ecdhe_key));
3298 // 4. generate ETS to encrypt
3299 // generate ETS (early_traffic_secret_key, decrypt pk_i
3300 // expand ETS <- expand ES <- extract ss_R
3301 // use ETS to decrypt
3303 ihm_e,
3304 sizeof (struct InitiatorHello));
3305 {
3306 struct GNUNET_HashCode transcript;
3308 &transcript);
3309 derive_es_ets (&transcript,
3310 &ss_R,
3311 &es,
3312 &ets);
3314 0,
3315 enc_key,
3316 enc_nonce);
3317 }
3318 // 5. encrypt
3319
3320 ret = crypto_aead_xchacha20poly1305_ietf_encrypt (
3321 (unsigned char*) &ihm_e[1], /* c - ciphertext */
3322 // mac,
3323 // NULL, // maclen_p
3324 &c_len, /* clen_p */
3325 (unsigned char*) ihmp, /* m - plaintext message */
3326 pt_len, // mlen
3327 NULL, 0, // ad, adlen // FIXME maybe over the unencrypted header?
3328 // fields?
3329 NULL, // nsec - unused
3330 enc_nonce, // npub - nonce
3331 enc_key); // k - key
3332 if (0 != ret)
3333 {
3334 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Something went wrong encrypting\n");
3336 kx->transcript_hash_ctx = NULL;
3338 return;
3339 }
3340 /* Forward the transcript */
3343 &ihm_e[1],
3344 c_len);
3345
3347 kx->early_secret_key = es;
3348 kx->early_traffic_secret = ets;
3349 kx->ss_R = ss_R;
3350 monitor_notify_all (kx);
3351 GNUNET_MQ_send_copy (kx->mq, env);
3352 kx->resend_env = env;
3354}

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_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 my_identity_hash = GNUNET_PILS_get_identity_hash (GSC_pils);
1786 GNUNET_assert (NULL != my_identity_hash);
1787
1788 // We could follow with the rest of the Key Schedule (dES, HS, ...) for now
1789 /* Check that we are actually in the receiving role */
1790 GNUNET_CRYPTO_hash (&kx->peer, sizeof(struct GNUNET_PeerIdentity), &h1);
1791 if (0 < GNUNET_CRYPTO_hash_cmp (&h1, my_identity_hash))
1792 {
1793 /* peer with "lower" identity starts KX, otherwise we typically end up
1794 with both peers starting the exchange and transmit the 'set key'
1795 message twice */
1796 /* Something went wrong - we have the lower value and should have sent the
1797 * InitiatorHello, but instead received it. TODO handle this case
1798 * We might end up in this case if the initiator didn't initiate the
1799 * handshake long enough and the 'responder' initiates the handshake */
1801 "Something went wrong - we have the lower value and should have sent the InitiatorHello, but instead received it.\n");
1803 kx->transcript_hash_ctx = NULL;
1804 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1805 return;
1806 }
1807
1808 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer ID of other peer: %s\n", GNUNET_i2s
1809 (&kx->peer));
1810 /* We update the monitoring peers here because now we know
1811 * that we can decrypt the message AND know the PID
1812 */
1813 monitor_notify_all (kx);
1814 kx->ss_R = *ss_R;
1815 kx->early_secret_key = es;
1816 kx->early_traffic_secret = ets;
1818}

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_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_log, 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::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 1822 of file gnunet-service-core_kx.c.

1823{
1824 uint16_t size = ntohs (m->header.size);
1825
1826 if (size < sizeof (*m)
1827 + sizeof (struct InitiatorHelloPayload)
1829 {
1830 return GNUNET_SYSERR;
1831 }
1832 return GNUNET_OK;
1833}

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 1845 of file gnunet-service-core_kx.c.

1846{
1847 const struct GNUNET_HashCode *my_identity_hash;
1849 struct GSC_KeyExchangeInfo *kx = cls;
1850 struct GNUNET_HashCode ih_hash;
1851 struct GNUNET_ShortHashCode ss_R;
1852 size_t ihm_len;
1853
1854 ihm_len = ntohs (ihm_e->header.size);
1855 GNUNET_CRYPTO_hash (ihm_e,
1856 ihm_len,
1857 &ih_hash);
1858 if (ROLE_INITIATOR == kx->role)
1859 {
1860 GNUNET_break_op (0);
1862 "I am an initiator! Tearing down...\n");
1863 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1864 return;
1865 }
1867 {
1868 /* Now that the decapsulation is synchronous nothing can observe this
1869 state from the outside -- #handle_initiator_hello_cont() runs before
1870 we return. Keep the guard anyway: reaching it means the state
1871 machine leaked a state, not that a peer did anything. */
1872 GNUNET_break (0);
1874 "Already received InitiatorHello: %d %d\n", kx->role, kx->status
1875 );
1876 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1877 return;
1878 }
1880 {
1881 if (0 == GNUNET_memcmp (&ih_hash,
1882 &kx->ih_hash))
1883 {
1884 /* Not a new exchange at all: the initiator resent the very hello we
1885 are already answering, because our ResponderHello did not make it
1886 back in time (#resend_initiator_hello() sends a copy of the same
1887 envelope, so a retransmission is byte-identical).
1888
1889 Starting over here is what breaks the pair. A fresh
1890 #send_responder_hello() picks a new @e ss_e and feeds a new
1891 ResponderHello into the transcript, and the transcript is what both
1892 @e finished_R and @e finished_I are computed over. The initiator
1893 answers whichever ResponderHello reaches it first and binds its
1894 InitiatorDone to *that* transcript, while we have moved on to the
1895 transcript of our latest one -- so #handle_initiator_done() cannot
1896 verify @e finished_I and drops it, every retransmission included.
1897 Neither side can make progress and neither side sees an error: the
1898 initiator sits in #GNUNET_CORE_KX_STATE_INITIATOR_DONE_SENT
1899 reporting "Unexpected ResponderHello", we sit in
1900 #GNUNET_CORE_KX_STATE_RESPONDER_HELLO_SENT, and both merely run out
1901 of retries after RESEND_MAX_TRIES and start over -- with no reason
1902 for the next attempt to be any luckier. One InitiatorHello
1903 retransmission, which any hiccup on the path produces, is enough to
1904 lose the peer indefinitely.
1905
1906 Retransmit our flight instead and leave the handshake state alone,
1907 per RFC 9147, Section 5.8: "implementations MUST retransmit their
1908 last flight in response to a retransmitted flight from the peer".
1909 Our own @e resend_task keeps its schedule; this only adds the
1910 answer the initiator is waiting for. */
1912 "InitiatorHello repeated by `%s' in state %d\n",
1913 GNUNET_i2s (&kx->peer),
1914 kx->status);
1916 gettext_noop (
1917 "# InitiatorHello retransmissions received"),
1918 1,
1919 GNUNET_NO);
1921 (NULL != kx->resend_env))
1923 kx->resend_env);
1924 /* Past that state the initiator already had our ResponderHello (we
1925 only leave it once @e finished_I verifies), so this is a duplicate
1926 that crossed with its InitiatorDone. Nothing to answer, and
1927 nothing that may cost us the association we just built. */
1928 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
1929 return;
1930 }
1931 /* The initiator has given up on whatever we still hold and started
1932 over. Only the initiator drives this handshake, so follow it rather
1933 than dropping the hello.
1934 This used to return, which deadlocks the pair whenever we are in
1935 #GNUNET_CORE_KX_STATE_RESPONDER_CONNECTED: an InitiatorHello is not
1936 an EncryptedMessage and so does not refresh @e timeout, and nothing
1937 else ever leaves that state, so we would reject every retransmit
1938 until our own idle timeout fires -- five minutes during which the
1939 initiator restarts its exchange every 50s and we report the peer as
1940 connected. In #GNUNET_CORE_KX_STATE_RESPONDER_HELLO_SENT it is a
1941 plain retransmit: the initiator resends precisely because it did not
1942 get our ResponderHello, and answering the hello it actually sent
1943 converges instead of leaving both sides to turn over on unrelated
1944 50s timers that need not ever re-phase.
1945
1946 This is RFC 9147, Section 5.11: "In cases where a server believes it
1947 has an existing association [...] and it receives an epoch=0
1948 ClientHello, it SHOULD proceed with a new handshake but MUST NOT
1949 destroy the existing association until the client has demonstrated
1950 reachability [...] by completing a complete handshake including
1951 delivering a verifiable Finished message."
1952
1953 So only the handshake state goes. An InitiatorHello is not
1954 authenticated -- @e finished_I in the InitiatorDone is our Finished
1955 -- and must not be able to cost us an association on its own. What
1956 we have keeps its traffic keys (@e association_up stays set, and the
1957 record layer keys off that rather than off @e status), its
1958 @e heartbeat_task and its @e timeout, and clients keep being told the
1959 peer is connected. #handle_initiator_done() does the swap once, and
1960 only once, @e finished_I verifies. If it never does, the old
1961 association dies of its own idle timeout exactly as it would have. */
1963 "Peer `%s' restarted the key exchange in state %d, following\n",
1964 GNUNET_i2s (&kx->peer),
1965 kx->status);
1966 reset_handshake (kx);
1967 }
1968 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received InitiatorHello: %d %d\n", kx->
1969 role, kx->status);
1970 GNUNET_assert (NULL == kx->transcript_hash_ctx);
1972 GNUNET_assert (NULL != kx->transcript_hash_ctx);
1973
1975 gettext_noop ("# key exchanges initiated"),
1976 1,
1977 GNUNET_NO);
1978
1980
1981 my_identity_hash = GNUNET_PILS_get_identity_hash (GSC_pils);
1982 GNUNET_assert (NULL != my_identity_hash);
1983
1984 // 1. verify type _INITIATOR_HELLO
1985 // - This is implicytly done by arriving within this handler
1986 // - or is this about verifying the 'additional data' part of aead?
1987 // should it check the encryption + mac? (is this implicitly done
1988 // while decrypting?)
1989 // 2. verify H(pk_R) matches pk_R
1990 if (0 != memcmp (&ihm_e->h_pk_R,
1991 my_identity_hash,
1992 sizeof (struct GNUNET_HashCode)))
1993 {
1995 "This message is not meant for us (H(PID) mismatch)\n");
1997 kx->transcript_hash_ctx = NULL;
1998 /* Leaving @e status at #GNUNET_CORE_KX_STATE_INITIATOR_HELLO_RECEIVED
1999 here wedges the kx: every later hello then hits the "already
2000 received" guard above and is dropped, forever. */
2002 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2003 return;
2004 }
2005 // FIXME this sometimes triggers in the tests - why?
2006 // 3. decaps -> shared_secret_R, c_R (kemChallenge)
2007 /* From here on this is the hello we answer, so a byte-identical one is a
2008 retransmission of it and must not restart the exchange. */
2009 kx->ih_hash = ih_hash;
2011 if (NULL == my_private_key)
2012 {
2013 /* #GSC_KX_start() enables local key access before we ever talk to
2014 TRANSPORT, so this means the key on disk does not match the identity
2015 PILS announced. We cannot answer any hello in that state. */
2017 "No private key for our peer identity, cannot answer hello"
2018 " from `%s'\n",
2019 GNUNET_i2s (&kx->peer));
2021 kx->transcript_hash_ctx = NULL;
2023 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2024 return;
2025 }
2026 if (GNUNET_OK !=
2028 &ihm_e->c_R,
2029 &ss_R))
2030 {
2031 GNUNET_break_op (0);
2033 "Failed to decapsulate c_R of hello from `%s'\n",
2034 GNUNET_i2s (&kx->peer));
2036 kx->transcript_hash_ctx = NULL;
2038 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2039 return;
2040 }
2042 ihm_e,
2043 &ss_R);
2044}

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 2082 of file gnunet-service-core_kx.c.

2083{
2084 struct GSC_KeyExchangeInfo *kx = cls;
2085
2086 kx->resend_task = NULL;
2087 if (0 == kx->resend_tries_left)
2088 {
2090 "Restarting KX\n");
2091 restart_kx (kx);
2092 return;
2093 }
2094 kx->resend_tries_left--;
2096 "Resending initiator done. Retries left: %u\n",
2097 kx->resend_tries_left);
2100}

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 2113 of file gnunet-service-core_kx.c.

2115{
2116 struct GSC_KeyExchangeInfo *kx = rh_ctx->kx;
2117 struct InitiatorDone *idm_e; /* encrypted */
2118 struct InitiatorDone idm_local;
2119 struct InitiatorDone *idm_p; /* plaintext */
2120 struct GNUNET_MQ_Envelope *env;
2121 unsigned char enc_key[AEAD_KEY_BYTES];
2122 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2123 struct ConfirmationAck ack_i;
2124 struct GNUNET_HashCode transcript;
2125 struct GNUNET_ShortHashCode ms;
2126
2127 // XXX valgrind reports uninitialized memory
2128 // the following is a way to check whether this memory was meant
2129 // memset (&rhm_local, 0, sizeof (rhm_local)); - adapt to cls if still needed
2130 memset (&idm_local, 0, sizeof (idm_local));
2131
2132 kx->ss_I = *ss_I;
2133
2134 /* derive *ATS */
2135 derive_ms (&rh_ctx->hs, ss_I, &ms);;
2136 // 5. Create ResponderFinished as per Section 6 and check against decrypted payload.
2137 struct GNUNET_HashCode responder_finished;
2138 // Transcript updates, snapshot again
2139 snapshot_transcript (rh_ctx->hc,
2140 &transcript);
2141#if DEBUG_KX
2143 "Transcript snapshot for derivation of Rfinished: `%s'\n",
2144 GNUNET_h2s (&transcript));
2145#endif
2146 generate_responder_finished (&transcript,
2147 &ms,
2148 &responder_finished);
2149 if (0 != memcmp (&rh_ctx->decrypted_finish,
2150 &responder_finished,
2151 sizeof (struct GNUNET_HashCode)))
2152 {
2153 /* A peer that answers our InitiatorHello with a ResponderHello whose
2154 finished field does not verify must not be able to abort us; this
2155 used to be a GNUNET_assert (0). */
2156 GNUNET_break_op (0);
2158 "Could not verify \"responder finished\" from `%s'\n",
2159 GNUNET_i2s (&kx->peer));
2160 GNUNET_free (rh_ctx->rhp);
2162 GNUNET_free (rh_ctx);
2163 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2164 restart_kx (kx);
2165 return;
2166 }
2167
2168
2169 /* Forward the transcript
2170 * after generating finished_R,
2171 * before deriving *ATS */
2173 rh_ctx->hc,
2174 rh_ctx->finished_enc,
2175 sizeof (rh_ctx->finished_enc));
2176
2177 // At this point we cannot fail anymore and may lock into kx
2179 kx->transcript_hash_ctx = rh_ctx->hc;
2180 kx->ss_I = *ss_I;
2181 kx->handshake_secret = rh_ctx->hs;
2182 kx->ss_e = rh_ctx->ss_e;
2183 kx->ihts = rh_ctx->ihts;
2184 kx->rhts = rh_ctx->rhts;
2185 kx->master_secret = ms;
2186 GNUNET_free (rh_ctx->rhp);
2187 GNUNET_free (rh_ctx);
2188 rh_ctx = NULL;
2189
2191 &transcript);
2192#if DEBUG_KX
2194 "Transcript snapshot for derivation of *ATS: `%s'\n",
2195 GNUNET_h2s (&transcript));
2196#endif
2197 derive_initial_ats (&transcript,
2198 &kx->master_secret,
2200 &kx->their_ats[0]);
2201 for (int i = 0; i < MAX_EPOCHS - 1; i++)
2202 {
2203 derive_next_ats (&kx->their_ats[i],
2204 &kx->their_ats[i + 1]);
2205 }
2206 kx->their_max_epoch = MAX_EPOCHS - 1;
2207
2209 0,
2210 enc_key,
2211 enc_nonce);
2212 /* Create InitiatorDone message */
2213 idm_p = &idm_local; /* plaintext */
2214 env = GNUNET_MQ_msg_extra (idm_e,
2215 sizeof (ack_i)
2218 // 6. Create IteratorFinished as per Section 6.
2219 generate_initiator_finished (&transcript,
2220 &kx->master_secret,
2221 &idm_p->finished);
2223 "InteratorFinished: `%s'\n",
2224 GNUNET_h2s (&idm_p->finished));
2226 "Transcript `%s'\n",
2227 GNUNET_h2s (&transcript));
2228 // 7. Send InteratorFinished message encrypted with the key derived from IHTS to R
2229
2230 GNUNET_assert (0 == crypto_aead_xchacha20poly1305_ietf_encrypt (
2231 (unsigned char*) &idm_e->finished, /* c - ciphertext */
2232 NULL, /* clen_p */
2233 (unsigned char*) &idm_p->finished, /* idm_p - plaintext message */
2234 sizeof (idm_p->finished), // mlen
2235 NULL, 0, // ad, adlen // FIXME should this not be the other, unencrypted
2236 // fields?
2237 NULL, // nsec - unused
2238 enc_nonce, // npub - nonce
2239 enc_key)); // k - key IHTS
2240 /* Forward the transcript hash context
2241 * after generating finished_I and RATS_0
2242 * before deriving IATS_0 */
2244 &idm_e->finished,
2245 sizeof (idm_e->finished)
2246 + AEAD_TAG_BYTES);
2248 &transcript);
2249#if DEBUG_KX
2251 "Transcript snapshot for derivation of *ATS: `%s'\n",
2252 GNUNET_h2s (&transcript));
2253#endif
2254 derive_initial_ats (&transcript,
2255 &kx->master_secret,
2257 &kx->current_ats);
2258 kx->current_epoch = 0;
2259 kx->current_sqn = 0;
2260 /* We start sending under this epoch here, so it has to be dated here too.
2261 #check_if_ack_or_heartbeat() only sets @e current_epoch_expiration once
2262 the responder's Ack arrives; until then it holds whatever the previous
2263 association left (zero for a first exchange), and #check_rekey() treats
2264 a past expiration as "rekey now". Anything we send while waiting for
2265 the Ack -- the Ack we answer an early heartbeat with, say -- would then
2266 burn an epoch the responder has no reason to expect. */
2269 /* Application traffic keys are installed, so from the record layer's
2270 point of view the association exists from here: we have to be able to
2271 deprotect the responder's Ack, which arrives before the handshake is
2272 confirmed. The client-visible session is created only once it does. */
2273 replay_reset_all (kx);
2275 // 8. optionally encrypt payload TODO
2277 kx->current_sqn,
2278 enc_key,
2279 enc_nonce);
2280 kx->current_sqn++;
2281 ack_i.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
2282 ack_i.header.size = htons (sizeof ack_i);
2283 GNUNET_assert (0 == crypto_aead_xchacha20poly1305_ietf_encrypt (
2284 (unsigned char*) &idm_e[1], /* c - ciphertext */
2285 NULL, /* clen_p */
2286 (unsigned char*) &ack_i, /* rhm_p - plaintext message */
2287 sizeof ack_i, // mlen
2288 NULL, 0, // ad, adlen // FIXME should this not be the other, unencrypted
2289 // fields?
2290 NULL, // nsec - unused
2291 enc_nonce, // npub - nonce // FIXME nonce can be reused
2292 enc_key)); // k - key RHTS
2293
2294 GNUNET_MQ_send_copy (kx->mq, env);
2295 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sent InitiatorDone: %d %d\n", kx->role,
2296 kx->status);
2297
2298
2299 kx->resend_env = env;
2302 monitor_notify_all (kx);
2303 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2304}

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 2308 of file gnunet-service-core_kx.c.

2309{
2310 uint16_t size = ntohs (m->header.size);
2311
2312 if (size < sizeof (*m)
2313 + sizeof (struct ResponderHelloPayload)
2314 + sizeof (struct GNUNET_HashCode)
2315 + AEAD_TAG_BYTES * 2)
2316 {
2317 return GNUNET_SYSERR;
2318 }
2319 return GNUNET_OK;
2320}

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 2329 of file gnunet-service-core_kx.c.

2330{
2331 struct GSC_KeyExchangeInfo *kx = cls;
2333 struct ResponderHelloCls *rh_ctx;
2334 struct GNUNET_HashCode transcript;
2335 struct GNUNET_HashCode rh_hash;
2336 struct GNUNET_HashContext *hc;
2337 struct GNUNET_ShortHashCode ss_I;
2338 unsigned char enc_key[AEAD_KEY_BYTES];
2339 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2341
2342 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ResponderHello: %d %d\n", kx->
2343 role, kx->status);
2344
2345 GNUNET_CRYPTO_hash (rhm_e,
2346 ntohs (rhm_e->header.size),
2347 &rh_hash);
2348 if (ROLE_RESPONDER == kx->role)
2349 {
2350 GNUNET_break_op (0);
2352 "I am the responder! Ignoring.\n");
2353 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2354 return;
2355 }
2357 {
2359 (0 == GNUNET_memcmp (&rh_hash,
2360 &kx->rh_hash)))
2361 {
2362 /* The responder resent the ResponderHello we already answered, which
2363 means our InitiatorDone did not reach it. That is an ordinary
2364 retransmission, not a protocol violation -- the GNUNET_break_op()
2365 below used to report it as one, which is what "Unexpected
2366 ResponderHello in state 6" in the logs is. Answer it the way
2367 RFC 9147, Section 5.8 requires: "implementations MUST retransmit
2368 their last flight in response to a retransmitted flight from the
2369 peer". Our @e resend_task would get there on its own eventually;
2370 doing it here converges at the pace of the peer's timer instead of
2371 ours, and both are bounded by RESEND_MAX_TRIES. */
2373 "ResponderHello repeated by `%s', resending InitiatorDone\n",
2374 GNUNET_i2s (&kx->peer));
2376 gettext_noop (
2377 "# ResponderHello retransmissions received"),
2378 1,
2379 GNUNET_NO);
2380 if (NULL != kx->resend_env)
2382 kx->resend_env);
2383 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2384 return;
2385 }
2386 /* Outside of that state there is no handshake this message could
2387 belong to. In particular @e transcript_hash_ctx is then NULL, and
2388 #GNUNET_CRYPTO_hash_context_copy() dereferences its argument -- so
2389 a peer could crash us by sending a ResponderHello at any other
2390 time. Note that @e resend_task and @e resend_env below belong to
2391 the exchange we *are* in the middle of and must not be cleared
2392 before this point either. */
2393 GNUNET_break_op (0);
2395 "Unexpected ResponderHello in state %d, ignoring\n",
2396 kx->status);
2397 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2398 return;
2399 }
2400 /* From here on this is the hello we answer; recognising a retransmission
2401 of it is what lets us resend our InitiatorDone above instead of
2402 dropping the peer's flight on the floor. */
2403 kx->rh_hash = rh_hash;
2404 GNUNET_assert (NULL != kx->transcript_hash_ctx);
2406 if (NULL != kx->resend_task)
2407 {
2409 kx->resend_task = NULL;
2410 }
2411 if (NULL != kx->resend_env)
2412 {
2414 kx->resend_env = NULL;
2415 }
2416
2417 /* Forward the transcript hash context */
2419 rhm_e,
2420 sizeof (struct ResponderHello));
2421 // 1. Verify that the message type is CORE_RESPONDER_HELLO
2422 // - implicitly done by handling this message?
2423 // - or is this about verifying the 'additional data' part of aead?
2424 // should it check the encryption + mac? (is this implicitly done
2425 // while decrypting?)
2426 // 2. sse <- Decaps(ske,ce)
2427 rh_ctx = GNUNET_new (struct ResponderHelloCls);
2428 ret = GNUNET_CRYPTO_hpke_kem_decaps (&kx->sk_e, // secret/private ephemeral key of initiator (us)
2429 &rhm_e->c_e, // encapsulated key
2430 &rh_ctx->ss_e); // key - ss_e
2431 if (GNUNET_OK != ret)
2432 {
2434 "Something went wrong decapsulating ss_e\n");
2436 GNUNET_free (rh_ctx);
2437 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2438 return;
2439 }
2440 // 3. Generate IHTS and RHTS from Section 5 and decrypt ServicesInfo, cI and ResponderFinished.
2441 snapshot_transcript (hc, &transcript);
2442#if DEBUG_KX
2444 "Transcript snapshot for derivation of HS, *HTS: `%s'\n",
2445 GNUNET_h2s (&transcript));
2446#endif
2448 &rh_ctx->ss_e,
2449 &rh_ctx->hs);
2450 derive_rhts (&transcript,
2451 &rh_ctx->hs,
2452 &rh_ctx->rhts);
2453 derive_ihts (&transcript,
2454 &rh_ctx->hs,
2455 &rh_ctx->ihts);
2457 0,
2458 enc_key,
2459 enc_nonce);
2460 rh_ctx->kx = kx;
2461 GNUNET_memcpy (&rh_ctx->rhm_e, rhm_e, sizeof (*rhm_e));
2462 {
2463 unsigned long long int c_len;
2464 unsigned char *finished_buf;
2465 // use RHTS to decrypt
2466 c_len = ntohs (rhm_e->header.size) - sizeof (*rhm_e)
2467 - sizeof (struct GNUNET_HashCode)
2468 - AEAD_TAG_BYTES; // finished ct
2469 rh_ctx->rhp = GNUNET_malloc (c_len
2470 -
2472 rh_ctx->hc = hc;
2473 finished_buf = ((unsigned char*) &rhm_e[1]) + c_len;
2474 /* Forward the transcript_hash_ctx
2475 * after rhts has been generated,
2476 * before generating finished_R*/
2478 hc,
2479 &rhm_e[1],
2480 c_len);
2481
2482 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2483 (unsigned char*) rh_ctx->rhp, // unsigned char *m
2484 NULL, // mlen_p message length
2485 NULL, // unsigned char *nsec - unused: NULL
2486 (unsigned char*) &rhm_e[1], // const unsigned char *c - ciphertext
2487 c_len, // unsigned long long clen - length of ciphertext
2488 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2489 0, // unsigned long long adlen
2490 enc_nonce, // const unsigned char *npub - nonce
2491 enc_key // const unsigned char *k - key
2492 );
2493 if (0 != ret)
2494 {
2496 "Something went wrong decrypting: %d\n", ret);
2497 GNUNET_free (rh_ctx->rhp);
2498 GNUNET_free (rh_ctx);
2500 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2501 return;
2502 }
2503 // FIXME nonce reuse (see encryption)
2505 1,
2506 enc_key,
2507 enc_nonce);
2508 c_len = sizeof (struct GNUNET_HashCode)
2510 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2511 (unsigned char*) &rh_ctx->decrypted_finish, // unsigned char *m
2512 NULL, // mlen_p message length
2513 NULL, // unsigned char *nsec - unused: NULL
2514 finished_buf, // const unsigned char *c - ciphertext
2515 c_len, // unsigned long long clen - length of ciphertext
2516 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2517 0, // unsigned long long adlen
2518 enc_nonce, // const unsigned char *npub - nonce
2519 enc_key // const unsigned char *k - key
2520 );
2521 if (0 != ret)
2522 {
2524 "Something went wrong decrypting finished field: %d\n", ret);
2525 GNUNET_free (rh_ctx->rhp);
2526 GNUNET_free (rh_ctx);
2528 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2529 return;
2530 }
2531 GNUNET_memcpy (rh_ctx->finished_enc,
2532 finished_buf,
2533 c_len);
2534 }
2535 // 4. ssI <- Decaps(skI,cI).
2537 if ( (NULL == my_private_key) ||
2538 (GNUNET_OK !=
2540 &rh_ctx->rhp->c_I,
2541 &ss_I)) )
2542 {
2544 "Failed to decapsulate c_I of ResponderHello from `%s'\n",
2545 GNUNET_i2s (&kx->peer));
2546 GNUNET_free (rh_ctx->rhp);
2547 GNUNET_free (rh_ctx);
2549 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2550 restart_kx (kx);
2551 return;
2552 }
2554 &ss_I);
2555}

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 2559 of file gnunet-service-core_kx.c.

2560{
2561 uint16_t size = ntohs (m->header.size);
2562
2563 if (size < sizeof (*m) + sizeof (struct ConfirmationAck))
2564 {
2565 return GNUNET_SYSERR;
2566 }
2567 return GNUNET_OK;
2568}

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 2577 of file gnunet-service-core_kx.c.

2578{
2579 struct GSC_KeyExchangeInfo *kx = cls;
2580 struct InitiatorDone idm_local;
2581 struct InitiatorDone *idm_p = &idm_local; /* plaintext */
2582 struct GNUNET_HashCode initiator_finished;
2583 struct GNUNET_HashCode transcript;
2584 struct GNUNET_ShortHashCode their_ats;
2585 struct GNUNET_HashContext *hc;
2586 unsigned char enc_key[AEAD_KEY_BYTES];
2587 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2588 struct ConfirmationAck ack_i;
2589 struct ConfirmationAck ack_r;
2590 int8_t ret;
2591
2592 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received InitiatorDone: %d %d\n", kx->
2593 role, kx->status);
2594 if (ROLE_INITIATOR == kx->role)
2595 {
2596 GNUNET_break_op (0);
2598 "I am the initiator! Tearing down...\n");
2599 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2600 return;
2601 }
2603 {
2604 /* The initiator did not see our ConfirmationAck and is resending (it
2605 tries #RESEND_MAX_TRIES times). Our handshake secrets are gone --
2606 #cleanup_handshake_secrets() zeroed @e ihts -- so verifying this
2607 message again is not possible and would only look like a decryption
2608 failure. Send what the initiator is actually missing instead. */
2610 "InitiatorDone repeated by `%s', resending our Ack\n",
2611 GNUNET_i2s (&kx->peer));
2612 ack_r.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
2613 ack_r.header.size = htons (sizeof ack_r);
2615 &ack_r,
2616 sizeof ack_r);
2617 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2618 return;
2619 }
2621 {
2622 /* We have no handshake state this message could be checked against.
2623 Note that @e resend_task and @e resend_env below belong to whatever
2624 exchange we *are* in the middle of, so they must not be cleared
2625 before this point. */
2626 GNUNET_break_op (0);
2628 "Unexpected InitiatorDone in state %d, ignoring\n",
2629 kx->status);
2630 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2631 return;
2632 }
2633 if (NULL != kx->resend_task)
2634 {
2636 kx->resend_task = NULL;
2637 }
2638 if (NULL != kx->resend_env)
2639 {
2641 kx->resend_env = NULL;
2642 }
2644 0,
2645 enc_key,
2646 enc_nonce);
2647 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2648 (unsigned char*) &idm_p->finished, // unsigned char *m
2649 NULL, // mlen_p message length
2650 NULL, // unsigned char *nsec - unused: NULL
2651 (unsigned char*) &idm_e->finished, // const unsigned char *c - ciphertext
2652 sizeof (idm_p->finished) // unsigned long long clen - length of ciphertext
2654 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2655 0, // unsigned long long adlen
2656 enc_nonce, // const unsigned char *npub - nonce
2657 enc_key // const unsigned char *k - key
2658 );
2659 if (0 != ret)
2660 {
2662 "Something went wrong decrypting: %d\n", ret);
2663 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2664 return;
2665 }
2666
2667 // - verify finished_I
2668 /* Generate finished_I
2669 * after Forwarding until {finished_R}RHTS
2670 * (did so while we prepared responder hello)
2671 * before forwarding to [{payload}RATS and] {finished_I}IHTS */
2672 // (look at the end of handle_initiator_hello())
2673 snapshot_transcript (kx->transcript_hash_ctx, &transcript);
2674 generate_initiator_finished (&transcript,
2675 &kx->master_secret,
2676 &initiator_finished);
2677 if (0 != memcmp (&idm_p->finished,
2678 &initiator_finished,
2679 sizeof (struct GNUNET_HashCode)))
2680 {
2682 "Could not verify \"initiator finished\" hash.\n");
2684 "Want: `%s'\n",
2685 GNUNET_h2s (&initiator_finished));
2687 "Have: `%s'\n",
2688 GNUNET_h2s (&idm_p->finished));
2690 "Transcript `%s'\n",
2691 GNUNET_h2s (&transcript));
2692 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2693 return;
2694 }
2695
2696 /* Forward the transcript hash_context_read */
2699 &idm_e->finished,
2700 sizeof (idm_e->finished)
2701 + AEAD_TAG_BYTES);
2702 snapshot_transcript (hc, &transcript);
2703 derive_initial_ats (&transcript,
2704 &kx->master_secret,
2706 &their_ats);
2707 derive_per_message_secrets (&their_ats, // FIXME other HS epoch?
2708 0,
2709 enc_key,
2710 enc_nonce);
2711 ret = crypto_aead_xchacha20poly1305_ietf_decrypt (
2712 (unsigned char*) &ack_i, // unsigned char *m
2713 NULL, // mlen_p message length
2714 NULL, // unsigned char *nsec - unused: NULL
2715 (unsigned char*) &idm_e[1], // const unsigned char *c - ciphertext
2716 sizeof (ack_i) + AEAD_TAG_BYTES, // unsigned long long clen - length of ciphertext
2717 NULL, // const unsigned char *ad - additional data (optional) TODO those should be used, right?
2718 0, // unsigned long long adlen
2719 enc_nonce, // const unsigned char *npub - nonce
2720 enc_key // const unsigned char *k - key
2721 );
2722 if (0 != ret)
2723 {
2725 "Something went wrong decrypting the Ack: %d\n", ret);
2727 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2728 return;
2729 }
2730 if ((sizeof ack_i != ntohs (ack_i.header.size)) ||
2731 (GNUNET_MESSAGE_TYPE_CORE_ACK != ntohs (ack_i.header.type)))
2732 {
2734 "Ack invalid!\n");
2736 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2737 return;
2738 }
2739 GNUNET_memcpy (&kx->their_ats[0],
2740 &their_ats,
2741 sizeof their_ats);
2745 for (int i = 0; i < MAX_EPOCHS - 1; i++)
2746 {
2747 derive_next_ats (&kx->their_ats[i],
2748 &kx->their_ats[i + 1]);
2749 }
2751 kx->transcript_hash_ctx = hc;
2756 monitor_notify_all (kx);
2757 /* @e finished_I has verified. RFC 9147, Section 5.11: the peer has now
2758 "demonstrated reachability [...] by completing a complete handshake
2759 including delivering a verifiable Finished message", so this is the
2760 point -- and the only point -- at which the old association may be
2761 destroyed. #handle_initiator_hello() deliberately left it running.
2762 GSC_SESSIONS_create() puts into @e sessions with
2763 #GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY under a
2764 GNUNET_assert(), so a leftover session is not merely untidy. */
2765 GSC_SESSIONS_end (&kx->peer);
2766 if (NULL != kx->heartbeat_task)
2767 {
2769 kx->heartbeat_task = NULL;
2770 }
2771 /* #send_initiator_done() starts the initiator at epoch 0 and we have to
2772 agree: on a kx that had an association before, these still hold the
2773 predecessor's values, and none of them was ever reset here. */
2774 kx->current_epoch = 0;
2775 kx->their_max_epoch = 0;
2776 kx->current_sqn = 1;
2777 replay_reset_all (kx);
2779 GSC_SESSIONS_create (&kx->peer, kx, kx->class);
2780 update_timeout (kx);
2781 ack_r.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
2782 ack_r.header.size = htons (sizeof ack_r);
2784 &ack_r,
2785 sizeof ack_r);
2786
2787 GNUNET_TRANSPORT_core_receive_continue (transport,
2788 &kx->peer);
2789}

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 2798 of file gnunet-service-core_kx.c.

2799{
2800 uint16_t size = ntohs (m->header.size) - sizeof(*m);
2801
2802 // TODO check (see check_encrypted ())
2803 // - check epoch
2804 // - check sequence number
2805 if (size < sizeof(struct GNUNET_MessageHeader))
2806 {
2807 GNUNET_break_op (0);
2808 return GNUNET_SYSERR;
2809 }
2810 return GNUNET_OK;
2811}

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 2820 of file gnunet-service-core_kx.c.

2822{
2823 struct GNUNET_ShortHashCode new_ats;
2824 struct ConfirmationAck ack;
2825
2827 {
2828 if (kx->current_epoch == UINT64_MAX)
2829 {
2831 "Max epoch reached (you probably will never see this)\n");
2832 }
2833 else
2834 {
2835 kx->current_epoch++;
2838 kx->current_sqn = 0;
2840 &new_ats);
2841 memcpy (&kx->current_ats,
2842 &new_ats,
2843 sizeof new_ats);
2844 }
2845 }
2846 update_timeout (kx);
2847 ack.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK);
2848 ack.header.size = htons (sizeof ack);
2850 &ack,
2851 sizeof ack);
2852 /* NOTE: no GNUNET_TRANSPORT_core_receive_continue() here. We are called
2853 from #handle_encrypted_message(), which owns the message and issues
2854 exactly one call for it. */
2855}

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 2859 of file gnunet-service-core_kx.c.

2862{
2863 struct GNUNET_MessageHeader *msg;
2864 struct ConfirmationAck *ack;
2865 struct Heartbeat *hb;
2866
2867 if (sizeof *msg > buf_len)
2868 return GNUNET_NO;
2869 msg = (struct GNUNET_MessageHeader*) buf;
2870 if (GNUNET_MESSAGE_TYPE_CORE_ACK == ntohs (msg->type))
2871 {
2872 ack = (struct ConfirmationAck *) buf;
2873 if (sizeof *ack != ntohs (ack->header.size))
2874 return GNUNET_NO;
2875 }
2876 else if (GNUNET_MESSAGE_TYPE_CORE_HEARTBEAT == ntohs (msg->type))
2877 {
2878 hb = (struct Heartbeat*) buf;
2879 if (sizeof *hb != ntohs (hb->header.size))
2880 return GNUNET_NO;
2881 handle_heartbeat (kx, hb);
2882 }
2883 else
2884 {
2885 return GNUNET_NO;
2886 }
2887
2892 {
2893 GSC_SESSIONS_create (&kx->peer, kx, kx->class);
2898 if (NULL != kx->resend_task)
2900 kx->resend_task = NULL;
2901 if (NULL != kx->resend_env)
2903 kx->resend_env = NULL;
2904 monitor_notify_all (kx);
2905 }
2906 update_timeout (kx);
2907
2908 return GNUNET_YES;
2909}

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 2918 of file gnunet-service-core_kx.c.

2919{
2920 struct GSC_KeyExchangeInfo *kx = cls;
2921 uint16_t size = ntohs (m->header.size);
2922 char buf[size - sizeof (*m)] GNUNET_ALIGN;
2923 unsigned char seq_enc_k[crypto_stream_chacha20_ietf_KEYBYTES];
2924 const unsigned char *seq_enc_nonce;
2925 unsigned char enc_key[AEAD_KEY_BYTES];
2926 unsigned char enc_nonce[AEAD_NONCE_BYTES];
2927 struct GNUNET_ShortHashCode new_ats[MAX_EPOCHS];
2928 uint32_t seq_enc_ctr;
2929 uint64_t epoch;
2930 uint64_t m_seq;
2931 uint64_t m_seq_nbo;
2932 uint64_t c_len;
2933 int8_t ret;
2934
2935 // TODO look at handle_encrypted
2936 // - statistics
2937
2938 /* The record layer answers to @e association_up, not to @e status: a
2939 handshake may be in flight over an association that is still live
2940 (RFC 9147, Section 5.11), and records of the old epoch have to keep
2941 being deprotected while it is. Conversely a record we have no keys
2942 for is simply an invalid record -- RFC 9147, Section 4.5.2: "In
2943 general, invalid records SHOULD be silently discarded, thus preserving
2944 the association" -- so it must not end a session or restart anything.
2945 If we are idle it does tell us the peer believes in an association we
2946 do not have, which is worth one exchange. */
2947 if (GNUNET_YES != kx->association_up)
2948 {
2950 "Discarding record from `%s': no keys for epoch %" PRIu64 "\n",
2951 GNUNET_i2s (&kx->peer),
2952 GNUNET_ntohll (m->epoch));
2953 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2955 restart_kx (kx);
2956 return;
2957 }
2958 epoch = GNUNET_ntohll (m->epoch);
2963 memcpy (new_ats,
2964 kx->their_ats,
2965 MAX_EPOCHS * sizeof (struct GNUNET_ShortHashCode));
2966 // FIXME here we could introduce logic that sends heartbeats
2967 // with key update request if we have not seen a new
2968 // epoch after a while (e.g. EPOCH_EXPIRATION)
2969 if (kx->their_max_epoch < epoch)
2970 {
2975 if ((epoch - kx->their_max_epoch) > 2 * MAX_EPOCHS)
2976 {
2977 /* @e epoch is plaintext and not covered by the AEAD tag, so this is
2978 reached by a single flipped bit as readily as by a peer that really
2979 did skip ahead. Drop the message like the "too old" case below
2980 does; tearing the session down here means one unauthenticated
2981 header field costs a full re-handshake. */
2983 "Epoch %" PRIu64 " is too new, will not decrypt...\n",
2984 epoch);
2985 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
2986 return;
2987 }
2988 for (uint64_t i = kx->their_max_epoch; i < epoch; i++)
2989 {
2990 derive_next_ats (&new_ats[i % MAX_EPOCHS],
2991 &new_ats[(i + 1) % MAX_EPOCHS]);
2992 /* This slot of the ring now holds a different key, so the window
2993 that went with the old one no longer means anything. */
2994 replay_reset (kx, i + 1);
2995 }
2996 }
2997 else if ((kx->their_max_epoch - epoch) > MAX_EPOCHS)
2998 {
3000 "Epoch %" PRIu64 " is too old, cannot decrypt...\n",
3001 epoch);
3002 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3003 return;
3004 }
3005 derive_sn (
3006 &new_ats[epoch % MAX_EPOCHS],
3007 seq_enc_k,
3008 sizeof seq_enc_k);
3009 /* compute the sequence number */
3010 seq_enc_ctr = *((uint32_t*) m->tag);
3011 seq_enc_nonce = &m->tag[sizeof (uint32_t)];
3012#if DEBUG_KX
3013 GNUNET_print_bytes (&new_ats[epoch % MAX_EPOCHS],
3014 sizeof (struct GNUNET_ShortHashCode),
3015 8,
3016 GNUNET_NO);
3017 GNUNET_print_bytes (seq_enc_k,
3018 sizeof seq_enc_k,
3019 8,
3020 GNUNET_NO);
3021 GNUNET_print_bytes ((char*) &seq_enc_ctr,
3022 sizeof seq_enc_ctr,
3023 8,
3024 GNUNET_NO);
3025#endif
3026 crypto_stream_chacha20_ietf_xor_ic (
3027 (unsigned char*) &m_seq_nbo,
3028 (unsigned char*) &m->sequence_number,
3029 sizeof (uint64_t),
3030 seq_enc_nonce,
3031 ntohl (seq_enc_ctr),
3032 seq_enc_k);
3033 m_seq = GNUNET_ntohll (m_seq_nbo);
3035 "Received encrypted message in epoch %" PRIu64
3036 " with E(SQN=%" PRIu64 ")=%" PRIu64
3037 "\n",
3038 epoch,
3039 m_seq,
3040 m->sequence_number);
3041 /* RFC 9147, Section 4.5.1. Cheap enough to do before deprotection, and
3042 doing it first means a flood of replayed records costs no AEAD work.
3043 The window itself is only moved once the record verifies, below. */
3044 if (GNUNET_OK != replay_check (kx, epoch, m_seq))
3045 {
3047 gettext_noop ("# replayed records discarded"),
3048 1,
3049 GNUNET_NO);
3051 "Discarding replayed record %" PRIu64 "/%" PRIu64
3052 " from `%s'\n",
3053 epoch,
3054 m_seq,
3055 GNUNET_i2s (&kx->peer));
3056 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3057 return;
3058 }
3059 /* We are the initiator and as we are going to receive,
3060 * we are using the responder key material */
3061 derive_per_message_secrets (&new_ats[epoch % MAX_EPOCHS],
3062 m_seq,
3063 enc_key,
3064 enc_nonce);
3065 // TODO checking sequence numbers - handle the case of out-of-sync messages!
3066 // for now only decrypt the payload
3067 // TODO encrypt other fields, too!
3068 // TODO
3069 // c_len = size - offsetof ();
3070 c_len = size - sizeof (struct EncryptedMessage);
3071 ret = crypto_aead_xchacha20poly1305_ietf_decrypt_detached (
3072 (unsigned char*) buf, // m - plain message
3073 NULL, // nsec - unused
3074 (unsigned char*) &m[1], // c - ciphertext
3075 c_len, // clen
3076 (const unsigned char*) &m->tag, // mac
3077 NULL, // ad - additional data TODO
3078 0, // adlen
3079 enc_nonce, // npub
3080 enc_key // k
3081 );
3082 if (0 != ret)
3083 {
3084 /* RFC 9147, Section 4.5.2: "invalid records SHOULD be silently
3085 discarded, thus preserving the association; however, an error MAY be
3086 logged for diagnostic purposes." Not a protocol violation on the
3087 peer's part either -- anything at all can arrive here -- so no
3088 GNUNET_break_op(). */
3090 gettext_noop ("# invalid records discarded"),
3091 1,
3092 GNUNET_NO);
3094 "Discarding record %" PRIu64 "/%" PRIu64 " from `%s':"
3095 " does not deprotect\n",
3096 epoch,
3097 m_seq,
3098 GNUNET_i2s (&kx->peer));
3099 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3100 return;
3101 }
3102 /* Deprotected, so the record is authentic and everything derived from it
3103 may now be committed: the epoch ring, the anti-replay window (RFC 9147,
3104 Section 4.5.1: "The window MUST NOT be updated due to a received record
3105 until that record has been deprotected successfully") and @e timeout.
3106 @e timeout is the only liveness signal CORE has and is what
3107 `gnunet-core -m' reports, so refreshing it any earlier would let
3108 anything merely shaped like a record keep a session nominally alive. */
3109 /* Only ever forward: @e their_max_epoch is the *highest* epoch we have
3110 seen, and the ratchet above keys off it. A record that was merely
3111 reordered across an epoch boundary -- entirely normal, the peer starts
3112 the new epoch at sequence number 0 while the old one is still in flight
3113 -- used to pull it back, so the next record of the newer epoch looked
3114 like a fresh advance and ran the loop again, wiping that epoch's
3115 anti-replay window (RFC 9147, Section 4.5.1) every single time. */
3116 if (kx->their_max_epoch < epoch)
3117 kx->their_max_epoch = epoch;
3118 memcpy (&kx->their_ats,
3119 new_ats,
3120 MAX_EPOCHS * sizeof (struct GNUNET_ShortHashCode));
3121 replay_commit (kx, epoch, m_seq);
3122 update_timeout (kx);
3123
3125 buf,
3126 sizeof buf))
3127 {
3129 {
3131 "Dropping message as we are still waiting for handshake ACK\n");
3132 GNUNET_break_op (0);
3133 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3134 return;
3135 }
3136 if (GNUNET_OK !=
3138 buf,
3139 sizeof buf,
3140 GNUNET_YES,
3141 GNUNET_NO))
3142 GNUNET_break_op (0);
3143 }
3144 GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer);
3145}

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 3158 of file gnunet-service-core_kx.c.

3161{
3162 struct GSC_KeyExchangeInfo *kx = handler_cls;
3163 (void) cls;
3164
3166 "Peer `%s' disconnected from us.\n",
3167 GNUNET_i2s (&kx->peer));
3168 GSC_SESSIONS_end (&kx->peer);
3170 gettext_noop ("# key exchanges stopped"),
3171 1,
3172 GNUNET_NO);
3173 if (NULL != kx->resend_task)
3174 {
3176 kx->resend_task = NULL;
3177 }
3178 if (NULL != kx->resend_env)
3179 {
3181 kx->resend_env = NULL;
3182 }
3183 if (NULL != kx->heartbeat_task)
3184 {
3186 kx->heartbeat_task = NULL;
3187 }
3189 monitor_notify_all (kx);
3190 if (kx->transcript_hash_ctx)
3191 {
3193 kx->transcript_hash_ctx = NULL;
3194 }
3196 GNUNET_MST_destroy (kx->mst);
3197 GNUNET_free (kx);
3198}

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 3202 of file gnunet-service-core_kx.c.

3203{
3204 struct GSC_KeyExchangeInfo *kx = cls;
3205
3206 kx->resend_task = NULL;
3207 if (0 == kx->resend_tries_left)
3208 {
3209 /* The InitiatorHello we keep repeating carries the ephemeral public key
3210 generated by #send_initiator_hello(), and only #restart_kx() ever
3211 generates a new one. Retrying the same message forever therefore
3212 never recovers from a responder that has dropped the exchange -- it
3213 just keeps a retransmit timer running against a peer that is not
3214 answering. Give up like #resend_responder_hello() and
3215 #resend_initiator_done() do and start a fresh exchange. */
3217 "InitiatorHello not answered by `%s', restarting KX\n",
3218 GNUNET_i2s (&kx->peer));
3219 restart_kx (kx);
3220 return;
3221 }
3222 kx->resend_tries_left--;
3224 "Resending InitiatorHello. Retries left: %u\n",
3225 kx->resend_tries_left);
3228}

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 3365 of file gnunet-service-core_kx.c.

3366{
3367 struct GNUNET_ShortHashCode new_ats;
3368
3369 if ((UINT64_MAX == kx->current_sqn) ||
3371 {
3373 "Epoch expiration %" PRIu64 " SQN %" PRIu64
3374 ", incrementing epoch...\n",
3376 kx->current_sqn);
3377 if (UINT64_MAX == kx->current_epoch)
3378 {
3379 /* RFC 9147, Section 6.1: "Implementations MUST NOT allow the epoch to
3380 wrap, but instead MUST establish a new association, terminating the
3381 old association". This used to be a GNUNET_assert(). */
3383 "Epoch exhausted for `%s', starting a new association\n",
3384 GNUNET_i2s (&kx->peer));
3385 restart_kx (kx);
3386 return GNUNET_SYSERR;
3387 }
3388 kx->current_epoch++;
3391 kx->current_sqn = 0;
3393 &new_ats);
3394 memcpy (&kx->current_ats,
3395 &new_ats,
3396 sizeof new_ats);
3397 }
3398 return GNUNET_OK;
3399}

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 3409 of file gnunet-service-core_kx.c.

3412{
3413 struct GNUNET_MQ_Envelope *env;
3414 struct EncryptedMessage *encrypted_msg;
3415 unsigned char enc_key[AEAD_KEY_BYTES];
3416 unsigned char enc_nonce[AEAD_NONCE_BYTES];
3417 unsigned char seq_enc_k[crypto_stream_chacha20_ietf_KEYBYTES];
3418 uint64_t sqn;
3419 uint64_t epoch;
3420 int8_t ret;
3421
3422 encrypted_msg = NULL;
3423
3424 if (GNUNET_YES != kx->association_up)
3425 {
3426 /* No application traffic keys installed -- there is nothing to protect
3427 this with. Callers reach this through a session, which only exists
3428 while the association does, so this is a should-not-happen. */
3429 GNUNET_break (0);
3430 return;
3431 }
3432 if (GNUNET_OK != check_rekey (kx))
3433 return; /* association was torn down, @e current_ats is gone */
3434 sqn = kx->current_sqn;
3435 epoch = kx->current_epoch;
3436 /* We are the sender and as we are going to send,
3437 * we are using the initiator key material */
3439 sqn,
3440 enc_key,
3441 enc_nonce);
3442 kx->current_sqn++;
3443 derive_sn (&kx->current_ats,
3444 seq_enc_k,
3445 sizeof seq_enc_k);
3446 env = GNUNET_MQ_msg_extra (encrypted_msg,
3447 payload_size,
3449 // only encrypt the payload for now
3450 // TODO encrypt other fields as well
3451 ret = crypto_aead_xchacha20poly1305_ietf_encrypt_detached (
3452 (unsigned char*) &encrypted_msg[1], // c - resulting ciphertext
3453 (unsigned char*) &encrypted_msg->tag, // mac - resulting mac/tag
3454 NULL, // maclen
3455 (unsigned char*) payload, // m - plain message
3456 payload_size, // mlen
3457 NULL, // ad - additional data TODO also cover the unencrypted part (epoch)
3458 0, // adlen
3459 NULL, // nsec - unused
3460 enc_nonce, // npub nonce
3461 enc_key // k - key
3462 );
3463 if (0 != ret)
3464 {
3466 "Something went wrong encrypting message\n");
3467 GNUNET_assert (0);
3468 }
3469 {
3470 /* compute the sequence number */
3471 unsigned char *seq_enc_nonce;
3472 uint64_t seq_nbo;
3473 uint32_t seq_enc_ctr;
3474
3475 seq_nbo = GNUNET_htonll (sqn);
3476 seq_enc_ctr = *((uint32_t*) encrypted_msg->tag);
3477 seq_enc_nonce = &encrypted_msg->tag[sizeof (uint32_t)];
3478 crypto_stream_chacha20_ietf_xor_ic (
3479 (unsigned char*) &encrypted_msg->sequence_number,
3480 (unsigned char*) &seq_nbo,
3481 sizeof seq_nbo,
3482 seq_enc_nonce,
3483 ntohl (seq_enc_ctr),
3484 seq_enc_k);
3485#if DEBUG_KX
3486 GNUNET_print_bytes (seq_enc_k,
3487 sizeof seq_enc_k,
3488 8,
3489 GNUNET_NO);
3490 GNUNET_print_bytes ((char*) &seq_enc_ctr,
3491 sizeof seq_enc_ctr,
3492 8,
3493 GNUNET_NO);
3494#endif
3496 "Sending encrypted message with E(SQN=%" PRIu64 ")=%" PRIu64
3497 "\n",
3498 sqn,
3499 encrypted_msg->sequence_number);
3500 }
3501 encrypted_msg->epoch = GNUNET_htonll (epoch);
3502
3503 // TODO actually copy payload
3504 GNUNET_MQ_send (kx->mq, env);
3505}

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 3509 of file gnunet-service-core_kx.c.

3510{
3511 const struct GNUNET_PeerIdentity *my_identity;
3513 GNUNET_MQ_hd_var_size (initiator_hello,
3515 struct InitiatorHello,
3516 NULL),
3517 GNUNET_MQ_hd_var_size (initiator_done,
3519 struct InitiatorDone,
3520 NULL),
3521 GNUNET_MQ_hd_var_size (responder_hello,
3523 struct ResponderHello,
3524 NULL),
3525 GNUNET_MQ_hd_var_size (encrypted_message, // TODO rename?
3527 struct EncryptedMessage,
3528 NULL),
3530 };
3531
3533 GNUNET_assert (NULL != my_identity);
3534
3535 /* Decapsulate with our peer identity's private key directly instead of
3536 round-tripping through the PILS service. The shared secret is needed
3537 in the middle of processing a handshake message, and an asynchronous
3538 answer meant that every InitiatorHello and ResponderHello had to be
3539 parked with its kx across a callback: the kx could be torn down or
3540 freed underneath it, two hellos could be in flight at once, and if
3541 the answer never came (PILS restarting) the handshake stalled *and*
3542 the message was never acknowledged to TRANSPORT. */
3543 if (GNUNET_OK !=
3545 {
3547 _ ("Failed to load our private key, "
3548 "cannot run key exchange\n"));
3549 GSC_KX_done ();
3550 return;
3551 }
3552
3554 transport =
3557 handlers,
3558 NULL, // cls - this connection-independant
3559 // cls seems not to be needed.
3560 // the connection-specific cls
3561 // will be set as a return value
3562 // of
3563 // handle_transport_notify_connect
3566 if (NULL == transport)
3567 {
3568 GSC_KX_done ();
3569 return;
3570 }
3571
3573 "Connected to TRANSPORT\n");
3574
3576}

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 3580 of file gnunet-service-core_kx.c.

3583{
3584 if (NULL != transport)
3585 return;
3586
3587 GSC_KX_start ();
3588}

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 3597 of file gnunet-service-core_kx.c.

3598{
3601 NULL);
3602 if (NULL == GSC_pils)
3603 {
3604 GSC_KX_done ();
3605 return GNUNET_SYSERR;
3606 }
3607
3608 return GNUNET_OK;
3609}

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 3616 of file gnunet-service-core_kx.c.

3617{
3618 if (NULL != GSC_pils)
3619 {
3621 GSC_pils = NULL;
3622 }
3623 if (NULL != transport)
3624 {
3626 transport = NULL;
3627 }
3628 if (NULL != rekey_task)
3629 {
3631 rekey_task = NULL;
3632 }
3633 if (NULL != nc)
3634 {
3636 nc = NULL;
3637 }
3638}

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 3648 of file gnunet-service-core_kx.c.

3649{
3650 return GNUNET_MQ_get_length (kxinfo->mq);
3651}

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 3655 of file gnunet-service-core_kx.c.

3656{
3657 return kxinfo->has_excess_bandwidth;
3658}

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 3670 of file gnunet-service-core_kx.c.

3671{
3672 struct GNUNET_MQ_Envelope *env;
3673 struct MonitorNotifyMessage *done_msg;
3674 struct GSC_KeyExchangeInfo *kx;
3675
3677 for (kx = kx_head; NULL != kx; kx = kx->next)
3678 {
3679 struct GNUNET_MQ_Envelope *env_notify;
3680 struct MonitorNotifyMessage *msg;
3681
3683 msg->state = htonl ((uint32_t) kx->status);
3684 msg->peer = kx->peer;
3685 msg->timeout = GNUNET_TIME_absolute_hton (kx->timeout);
3686 GNUNET_MQ_send (mq, env_notify);
3687 }
3689 done_msg->state = htonl ((uint32_t) GNUNET_CORE_KX_ITERATION_FINISHED);
3692}

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().