X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fcore%2Fgnunet-service-core_kx.c;h=8a7cada5c5a1a1b76191e7859bb54ee703c6d760;hb=d23a815951413af100c74b38cdd09a01ca1c280a;hp=798c114a4342b49e2f251db8616793236fa22c33;hpb=b9a3fbdc52b32aa6bd27941aba76da528f4e0669;p=oweals%2Fgnunet.git diff --git a/src/core/gnunet-service-core_kx.c b/src/core/gnunet-service-core_kx.c index 798c114a4..8a7cada5c 100644 --- a/src/core/gnunet-service-core_kx.c +++ b/src/core/gnunet-service-core_kx.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + Copyright (C) 2009-2013, 2016 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -14,8 +14,8 @@ You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /** @@ -26,12 +26,9 @@ #include "platform.h" #include "gnunet-service-core_kx.h" #include "gnunet-service-core.h" -#include "gnunet-service-core_clients.h" -#include "gnunet-service-core_neighbours.h" #include "gnunet-service-core_sessions.h" #include "gnunet_statistics_service.h" -#include "gnunet_peerinfo_service.h" -#include "gnunet_hello_lib.h" +#include "gnunet_transport_core_service.h" #include "gnunet_constants.h" #include "gnunet_signatures.h" #include "gnunet_protocols.h" @@ -41,7 +38,7 @@ /** * How long do we wait for SET_KEY confirmation initially? */ -#define INITIAL_SET_KEY_RETRY_FREQUENCY GNUNET_TIME_relative_multiply (MAX_SET_KEY_DELAY, 1) +#define INITIAL_SET_KEY_RETRY_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) /** * What is the minimum frequency for a PING message? @@ -51,8 +48,12 @@ /** * How often do we rekey? */ -#define REKEY_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 90) +#define REKEY_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12) +/** + * What time difference do we tolerate? + */ +#define REKEY_TOLERANCE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) /** * What is the maximum age of a message for us to consider processing @@ -63,51 +64,71 @@ */ #define MAX_MESSAGE_AGE GNUNET_TIME_UNIT_DAYS -/** - * What is the maximum delay for a SET_KEY message? - */ -#define MAX_SET_KEY_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) GNUNET_NETWORK_STRUCT_BEGIN /** - * We're sending an (encrypted) PING to the other peer to check if he - * can decrypt. The other peer should respond with a PONG with the - * same content, except this time encrypted with the receiver's key. + * Message transmitted with the signed ephemeral key of a peer. The + * session key is then derived from the two ephemeral keys (ECDHE). */ -struct PingMessage +struct EphemeralKeyMessage { + /** - * Message type is CORE_PING. + * Message type is #GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY. */ struct GNUNET_MessageHeader header; /** - * Seed for the IV + * Status of the sender (should be in `enum PeerStateMachine`), nbo. */ - uint32_t iv_seed GNUNET_PACKED; + int32_t sender_status GNUNET_PACKED; /** - * Intended target of the PING, used primarily to check - * that decryption actually worked. + * An ECC signature of the @e origin_identity asserting the validity + * of the given ephemeral key. */ - struct GNUNET_PeerIdentity target; + struct GNUNET_CRYPTO_EddsaSignature signature; /** - * Random number chosen to make reply harder. + * Information about what is being signed. */ - uint32_t challenge GNUNET_PACKED; + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * At what time was this key created (beginning of validity). + */ + struct GNUNET_TIME_AbsoluteNBO creation_time; + + /** + * When does the given ephemeral key expire (end of validity). + */ + struct GNUNET_TIME_AbsoluteNBO expiration_time; + + /** + * Ephemeral public ECC key. + */ + struct GNUNET_CRYPTO_EcdhePublicKey ephemeral_key; + + /** + * Public key of the signing peer (persistent version, not the + * ephemeral public key). + */ + struct GNUNET_PeerIdentity origin_identity; + }; /** - * Response to a PING. Includes data from the original PING. + * We're sending an (encrypted) PING to the other peer to check if he + * can decrypt. The other peer should respond with a PONG with the + * same content, except this time encrypted with the receiver's key. */ -struct PongMessage +struct PingMessage { /** - * Message type is CORE_PONG. + * Message type is #GNUNET_MESSAGE_TYPE_CORE_PING. */ struct GNUNET_MessageHeader header; @@ -116,67 +137,49 @@ struct PongMessage */ uint32_t iv_seed GNUNET_PACKED; - /** - * Random number to make faking the reply harder. Must be - * first field after header (this is where we start to encrypt!). - */ - uint32_t challenge GNUNET_PACKED; - - /** - * Reserved, always 'GNUNET_BANDWIDTH_VALUE_MAX'. - */ - struct GNUNET_BANDWIDTH_Value32NBO reserved; - /** * Intended target of the PING, used primarily to check * that decryption actually worked. */ struct GNUNET_PeerIdentity target; + + /** + * Random number chosen to make replay harder. + */ + uint32_t challenge GNUNET_PACKED; }; /** - * Message transmitted to set (or update) a session key. + * Response to a PING. Includes data from the original PING. */ -struct SetKeyMessage +struct PongMessage { - /** - * Message type is either CORE_SET_KEY. + * Message type is #GNUNET_MESSAGE_TYPE_CORE_PONG. */ struct GNUNET_MessageHeader header; /** - * Status of the sender (should be in "enum PeerStateMachine"), nbo. - */ - int32_t sender_status GNUNET_PACKED; - - /** - * Purpose of the signature, will be - * GNUNET_SIGNATURE_PURPOSE_SET_KEY. + * Seed for the IV */ - struct GNUNET_CRYPTO_RsaSignaturePurpose purpose; + uint32_t iv_seed GNUNET_PACKED; /** - * At what time was this key created? + * Random number to make replay attacks harder. */ - struct GNUNET_TIME_AbsoluteNBO creation_time; + uint32_t challenge GNUNET_PACKED; /** - * The encrypted session key. + * Reserved, always zero. */ - struct GNUNET_CRYPTO_RsaEncryptedData encrypted_key; + uint32_t reserved; /** - * Who is the intended recipient? + * Intended target of the PING, used primarily to check + * that decryption actually worked. */ struct GNUNET_PeerIdentity target; - - /** - * Signature of the stuff above (starting at purpose). - */ - struct GNUNET_CRYPTO_RsaSignature signature; - }; @@ -187,7 +190,7 @@ struct SetKeyMessage struct EncryptedMessage { /** - * Message type is either CORE_ENCRYPTED_MESSAGE. + * Message type is #GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE. */ struct GNUNET_MessageHeader header; @@ -197,12 +200,13 @@ struct EncryptedMessage uint32_t iv_seed GNUNET_PACKED; /** - * MAC of the encrypted message (starting at 'sequence_number'), + * MAC of the encrypted message (starting at @e sequence_number), * used to verify message integrity. Everything after this value - * (excluding this value itself) will be encrypted and authenticated. - * ENCRYPTED_HEADER_SIZE must be set to the offset of the *next* field. + * (excluding this value itself) will be encrypted and + * authenticated. #ENCRYPTED_HEADER_SIZE must be set to the offset + * of the *next* field. */ - GNUNET_HashCode hmac; + struct GNUNET_HashCode hmac; /** * Sequence number, in network byte order. This field @@ -211,12 +215,12 @@ struct EncryptedMessage uint32_t sequence_number GNUNET_PACKED; /** - * Reserved, always 'GNUNET_BANDWIDTH_VALUE_MAX'. + * Reserved, always zero. */ - struct GNUNET_BANDWIDTH_Value32NBO reserved; + uint32_t reserved GNUNET_PACKED; /** - * Timestamp. Used to prevent reply of ancient messages + * Timestamp. Used to prevent replay of ancient messages * (recent messages are caught with the sequence number). */ struct GNUNET_TIME_AbsoluteNBO timestamp; @@ -226,147 +230,80 @@ GNUNET_NETWORK_STRUCT_END /** - * Number of bytes (at the beginning) of "struct EncryptedMessage" + * Number of bytes (at the beginning) of `struct EncryptedMessage` * that are NOT encrypted. */ #define ENCRYPTED_HEADER_SIZE (offsetof(struct EncryptedMessage, sequence_number)) -/** - * State machine for our P2P encryption handshake. Everyone starts in - * "DOWN", if we receive the other peer's key (other peer initiated) - * we start in state RECEIVED (since we will immediately send our - * own); otherwise we start in SENT. If we get back a PONG from - * within either state, we move up to CONFIRMED (the PONG will always - * be sent back encrypted with the key we sent to the other peer). - */ -enum KxStateMachine -{ - /** - * No handshake yet. - */ - KX_STATE_DOWN, - - /** - * We've sent our session key. - */ - KX_STATE_KEY_SENT, - - /** - * We've received the other peers session key. - */ - KX_STATE_KEY_RECEIVED, - - /** - * The other peer has confirmed our session key with a message - * encrypted with his session key (which we got). Key exchange - * is done. - */ - KX_STATE_UP, - - /** - * We're rekeying, so we have received the other peer's session - * key, but he didn't get ours yet. - */ - KX_STATE_REKEY, - - /** - * We're rekeying but have not yet received confirmation for our new - * key from the other peer. - */ - KX_STATE_REKEY_SENT -}; - - /** * Information about the status of a key exchange with another peer. */ struct GSC_KeyExchangeInfo { - /** - * Identity of the peer. - */ - struct GNUNET_PeerIdentity peer; - - /** - * SetKeyMessage to transmit (initialized the first - * time our status goes past 'KX_STATE_KEY_SENT'). - */ - struct SetKeyMessage skm; - - /** - * PING message we transmit to the other peer. - */ - struct PingMessage ping; /** - * SetKeyMessage we received and did not process yet. + * DLL. */ - struct SetKeyMessage *skm_received; + struct GSC_KeyExchangeInfo *next; /** - * PING message we received from the other peer and - * did not process yet (or NULL). + * DLL. */ - struct PingMessage *ping_received; + struct GSC_KeyExchangeInfo *prev; /** - * PONG message we received from the other peer and - * did not process yet (or NULL). + * Identity of the peer. */ - struct PongMessage *pong_received; + const struct GNUNET_PeerIdentity *peer; /** - * Encrypted message we received from the other peer and - * did not process yet (or NULL). + * Message queue for sending messages to @a peer. */ - struct EncryptedMessage *emsg_received; + struct GNUNET_MQ_Handle *mq; /** - * Non-NULL if we are currently looking up HELLOs for this peer. - * for this peer. + * Our message stream tokenizer (for encrypted payload). */ - struct GNUNET_PEERINFO_IteratorContext *pitr; + struct GNUNET_MessageStreamTokenizer *mst; /** - * Public key of the neighbour, NULL if we don't have it yet. + * PING message we transmit to the other peer. */ - struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key; + struct PingMessage ping; /** - * We received a PONG message before we got the "public_key" - * (or the SET_KEY). We keep it here until we have a key - * to decrypt it. NULL if no PONG is pending. + * Ephemeral public ECC key of the other peer. */ - struct PongMessage *pending_pong; + struct GNUNET_CRYPTO_EcdhePublicKey other_ephemeral_key; /** * Key we use to encrypt our messages for the other peer * (initialized by us when we do the handshake). */ - struct GNUNET_CRYPTO_AesSessionKey encrypt_key; + struct GNUNET_CRYPTO_SymmetricSessionKey encrypt_key; /** * Key we use to decrypt messages from the other peer * (given to us by the other peer during the handshake). */ - struct GNUNET_CRYPTO_AesSessionKey decrypt_key; - - /** - * At what time did we generate our encryption key? - */ - struct GNUNET_TIME_Absolute encrypt_key_created; + struct GNUNET_CRYPTO_SymmetricSessionKey decrypt_key; /** * At what time did the other peer generate the decryption key? */ - struct GNUNET_TIME_Absolute decrypt_key_created; + struct GNUNET_TIME_Absolute foreign_key_expires; /** * When should the session time out (if there are no PONGs)? */ struct GNUNET_TIME_Absolute timeout; + /** + * What was the last timeout we informed our monitors about? + */ + struct GNUNET_TIME_Absolute last_notify_timeout; + /** * At what frequency are we currently re-trying SET_KEY messages? */ @@ -375,19 +312,19 @@ struct GSC_KeyExchangeInfo /** * ID of task used for re-trying SET_KEY and PING message. */ - GNUNET_SCHEDULER_TaskIdentifier retry_set_key_task; + struct GNUNET_SCHEDULER_Task *retry_set_key_task; /** * ID of task used for sending keep-alive pings. */ - GNUNET_SCHEDULER_TaskIdentifier keep_alive_task; + struct GNUNET_SCHEDULER_Task *keep_alive_task; /** - * Bit map indicating which of the 32 sequence numbers before the last - * were received (good for accepting out-of-order packets and + * Bit map indicating which of the 32 sequence numbers before the + * last were received (good for accepting out-of-order packets and * estimating reliability of the connection) */ - unsigned int last_packets_bitmap; + uint32_t last_packets_bitmap; /** * last sequence number received on this connection (highest) @@ -404,33 +341,97 @@ struct GSC_KeyExchangeInfo */ uint32_t ping_challenge; + /** + * #GNUNET_YES if this peer currently has excess bandwidth. + */ + int has_excess_bandwidth; + /** * What is our connection status? */ - enum KxStateMachine status; + enum GNUNET_CORE_KxState status; }; /** - * Handle to peerinfo service. + * Transport service. */ -static struct GNUNET_PEERINFO_Handle *peerinfo; +static struct GNUNET_TRANSPORT_CoreHandle *transport; /** * Our private key. */ -static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; +static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; + +/** + * Our ephemeral private key. + */ +static struct GNUNET_CRYPTO_EcdhePrivateKey *my_ephemeral_key; + +/** + * Current message we send for a key exchange. + */ +static struct EphemeralKeyMessage current_ekm; + +/** + * DLL head. + */ +static struct GSC_KeyExchangeInfo *kx_head; + +/** + * DLL tail. + */ +static struct GSC_KeyExchangeInfo *kx_tail; + +/** + * Task scheduled for periodic re-generation (and thus rekeying) of our + * ephemeral key. + */ +static struct GNUNET_SCHEDULER_Task *rekey_task; + +/** + * Notification context for broadcasting to monitors. + */ +static struct GNUNET_NotificationContext *nc; + /** - * Our public key. + * Calculate seed value we should use for a message. + * + * @param kx key exchange context */ -static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; +static uint32_t +calculate_seed (struct GSC_KeyExchangeInfo *kx) +{ + /* Note: may want to make this non-random and instead + derive from key material to avoid having an undetectable + side-channel */ + return htonl (GNUNET_CRYPTO_random_u32 + (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX)); +} + /** - * Our message stream tokenizer (for encrypted payload). + * Inform all monitors about the KX state of the given peer. + * + * @param kx key exchange state to inform about */ -static struct GNUNET_SERVER_MessageStreamTokenizer *mst; +static void +monitor_notify_all (struct GSC_KeyExchangeInfo *kx) +{ + struct MonitorNotifyMessage msg; + + msg.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY); + msg.header.size = htons (sizeof (msg)); + msg.state = htonl ((uint32_t) kx->status); + msg.peer = *kx->peer; + msg.timeout = GNUNET_TIME_absolute_hton (kx->timeout); + GNUNET_notification_context_broadcast (nc, + &msg.header, + GNUNET_NO); + kx->last_notify_timeout = kx->timeout; +} /** @@ -439,20 +440,19 @@ static struct GNUNET_SERVER_MessageStreamTokenizer *mst; * @param akey authentication key to derive * @param skey session key to use * @param seed seed to use - * @param creation_time creation time to use */ static void derive_auth_key (struct GNUNET_CRYPTO_AuthKey *akey, - const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed, - struct GNUNET_TIME_Absolute creation_time) + const struct GNUNET_CRYPTO_SymmetricSessionKey *skey, + uint32_t seed) { static const char ctx[] = "authentication key"; - struct GNUNET_TIME_AbsoluteNBO ctbe; - ctbe = GNUNET_TIME_absolute_hton (creation_time); - GNUNET_CRYPTO_hmac_derive_key (akey, skey, &seed, sizeof (seed), &skey->key, - sizeof (skey->key), &ctbe, sizeof (ctbe), ctx, - sizeof (ctx), NULL); + GNUNET_CRYPTO_hmac_derive_key (akey, skey, + &seed, sizeof (seed), + skey, sizeof (struct GNUNET_CRYPTO_SymmetricSessionKey), + ctx, sizeof (ctx), + NULL); } @@ -465,16 +465,18 @@ derive_auth_key (struct GNUNET_CRYPTO_AuthKey *akey, * @param identity identity of the other peer to use */ static void -derive_iv (struct GNUNET_CRYPTO_AesInitializationVector *iv, - const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed, +derive_iv (struct GNUNET_CRYPTO_SymmetricInitializationVector *iv, + const struct GNUNET_CRYPTO_SymmetricSessionKey *skey, + uint32_t seed, const struct GNUNET_PeerIdentity *identity) { static const char ctx[] = "initialization vector"; - GNUNET_CRYPTO_aes_derive_iv (iv, skey, &seed, sizeof (seed), - &identity->hashPubKey.bits, - sizeof (identity->hashPubKey.bits), ctx, - sizeof (ctx), NULL); + GNUNET_CRYPTO_symmetric_derive_iv (iv, skey, + &seed, sizeof (seed), + identity, + sizeof (struct GNUNET_PeerIdentity), ctx, + sizeof (ctx), NULL); } @@ -488,34 +490,66 @@ derive_iv (struct GNUNET_CRYPTO_AesInitializationVector *iv, * @param identity identity of the other peer to use */ static void -derive_pong_iv (struct GNUNET_CRYPTO_AesInitializationVector *iv, - const struct GNUNET_CRYPTO_AesSessionKey *skey, uint32_t seed, - uint32_t challenge, const struct GNUNET_PeerIdentity *identity) +derive_pong_iv (struct GNUNET_CRYPTO_SymmetricInitializationVector *iv, + const struct GNUNET_CRYPTO_SymmetricSessionKey *skey, + uint32_t seed, + uint32_t challenge, + const struct GNUNET_PeerIdentity *identity) { static const char ctx[] = "pong initialization vector"; - GNUNET_CRYPTO_aes_derive_iv (iv, skey, &seed, sizeof (seed), - &identity->hashPubKey.bits, - sizeof (identity->hashPubKey.bits), &challenge, - sizeof (challenge), ctx, sizeof (ctx), NULL); + GNUNET_CRYPTO_symmetric_derive_iv (iv, skey, + &seed, sizeof (seed), + identity, + sizeof (struct GNUNET_PeerIdentity), + &challenge, sizeof (challenge), + ctx, sizeof (ctx), + NULL); +} + + +/** + * Derive an AES key from key material + * + * @param sender peer identity of the sender + * @param receiver peer identity of the sender + * @param key_material high entropy key material to use + * @param skey set to derived session key + */ +static void +derive_aes_key (const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_PeerIdentity *receiver, + const struct GNUNET_HashCode *key_material, + struct GNUNET_CRYPTO_SymmetricSessionKey *skey) +{ + static const char ctx[] = "aes key generation vector"; + + GNUNET_CRYPTO_kdf (skey, sizeof (struct GNUNET_CRYPTO_SymmetricSessionKey), + ctx, sizeof (ctx), + key_material, sizeof (struct GNUNET_HashCode), + sender, sizeof (struct GNUNET_PeerIdentity), + receiver, sizeof (struct GNUNET_PeerIdentity), + NULL); } /** - * Encrypt size bytes from in and write the result to out. Use the - * key for outbound traffic of the given neighbour. + * Encrypt size bytes from @a in and write the result to @a out. Use the + * @a kx key for outbound traffic of the given neighbour. * * @param kx key information context * @param iv initialization vector to use * @param in ciphertext * @param out plaintext - * @param size size of in/out - * @return GNUNET_OK on success + * @param size size of @a in/@a out + * @return #GNUNET_OK on success */ static int do_encrypt (struct GSC_KeyExchangeInfo *kx, - const struct GNUNET_CRYPTO_AesInitializationVector *iv, - const void *in, void *out, size_t size) + const struct GNUNET_CRYPTO_SymmetricInitializationVector *iv, + const void *in, + void *out, + size_t size) { if (size != (uint16_t) size) { @@ -523,71 +557,86 @@ do_encrypt (struct GSC_KeyExchangeInfo *kx, return GNUNET_NO; } GNUNET_assert (size == - GNUNET_CRYPTO_aes_encrypt (in, (uint16_t) size, - &kx->encrypt_key, iv, out)); - GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# bytes encrypted"), size, + GNUNET_CRYPTO_symmetric_encrypt (in, + (uint16_t) size, + &kx->encrypt_key, + iv, + out)); + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# bytes encrypted"), + size, GNUNET_NO); /* the following is too sensitive to write to log files by accident, so we require manual intervention to get this one... */ #if 0 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Encrypted %u bytes for `%4s' using key %u, IV %u\n", - (unsigned int) size, GNUNET_i2s (&kx->peer), - (unsigned int) kx->encrypt_key.crc32, GNUNET_CRYPTO_crc32_n (iv, - sizeof - (iv))); + "Encrypted %u bytes for `%s' using key %u, IV %u\n", + (unsigned int) size, + GNUNET_i2s (kx->peer), + (unsigned int) kx->encrypt_key.crc32, + GNUNET_CRYPTO_crc32_n (iv, + sizeof (iv))); #endif return GNUNET_OK; } /** - * Decrypt size bytes from in and write the result to out. Use the - * key for inbound traffic of the given neighbour. This function does - * NOT do any integrity-checks on the result. + * Decrypt size bytes from @a in and write the result to @a out. Use + * the @a kx key for inbound traffic of the given neighbour. This + * function does NOT do any integrity-checks on the result. * * @param kx key information context * @param iv initialization vector to use * @param in ciphertext * @param out plaintext - * @param size size of in/out - * @return GNUNET_OK on success + * @param size size of @a in / @a out + * @return #GNUNET_OK on success */ static int do_decrypt (struct GSC_KeyExchangeInfo *kx, - const struct GNUNET_CRYPTO_AesInitializationVector *iv, - const void *in, void *out, size_t size) + const struct GNUNET_CRYPTO_SymmetricInitializationVector *iv, + const void *in, + void *out, + size_t size) { if (size != (uint16_t) size) { GNUNET_break (0); return GNUNET_NO; } - if ( (kx->status != KX_STATE_KEY_RECEIVED) && (kx->status != KX_STATE_UP) && - (kx->status != KX_STATE_REKEY_SENT) && - (kx->status != KX_STATE_REKEY) ) + if ( (kx->status != GNUNET_CORE_KX_STATE_KEY_RECEIVED) && + (kx->status != GNUNET_CORE_KX_STATE_UP) && + (kx->status != GNUNET_CORE_KX_STATE_REKEY_SENT) ) { GNUNET_break_op (0); return GNUNET_SYSERR; } if (size != - GNUNET_CRYPTO_aes_decrypt (in, (uint16_t) size, &kx->decrypt_key, iv, - out)) + GNUNET_CRYPTO_symmetric_decrypt (in, + (uint16_t) size, + &kx->decrypt_key, + iv, + out)) { GNUNET_break (0); return GNUNET_SYSERR; } - GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# bytes decrypted"), size, + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# bytes decrypted"), + size, GNUNET_NO); /* the following is too sensitive to write to log files by accident, so we require manual intervention to get this one... */ #if 0 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Decrypted %u bytes from `%4s' using key %u, IV %u\n", - (unsigned int) size, GNUNET_i2s (&kx->peer), - (unsigned int) kx->decrypt_key.crc32, GNUNET_CRYPTO_crc32_n (iv, - sizeof - (*iv))); + "Decrypted %u bytes from `%s' using key %u, IV %u\n", + (unsigned int) size, + GNUNET_i2s (kx->peer), + (unsigned int) kx->decrypt_key.crc32, + GNUNET_CRYPTO_crc32_n (iv, + sizeof + (*iv))); #endif return GNUNET_OK; } @@ -603,299 +652,420 @@ send_key (struct GSC_KeyExchangeInfo *kx); /** - * Task that will retry "send_key" if our previous attempt failed. + * Task that will retry #send_key() if our previous attempt failed. * - * @param cls our 'struct GSC_KeyExchangeInfo' - * @param tc scheduler context + * @param cls our `struct GSC_KeyExchangeInfo` */ static void -set_key_retry_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +set_key_retry_task (void *cls) { struct GSC_KeyExchangeInfo *kx = cls; - kx->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK; - kx->set_key_retry_frequency = - GNUNET_TIME_relative_multiply (kx->set_key_retry_frequency, 2); + kx->retry_set_key_task = NULL; + kx->set_key_retry_frequency = GNUNET_TIME_STD_BACKOFF (kx->set_key_retry_frequency); + GNUNET_assert (GNUNET_CORE_KX_STATE_DOWN != kx->status); send_key (kx); } /** - * PEERINFO is giving us a HELLO for a peer. Add the public key to - * the neighbour's struct and continue with the key exchange. Or, if - * we did not get a HELLO, just do nothing. + * Create a fresh PING message for transmission to the other peer. * - * @param cls the 'struct GSC_KeyExchangeInfo' to retry sending the key for - * @param peer the peer for which this is the HELLO - * @param hello HELLO message of that peer - * @param err_msg NULL if successful, otherwise contains error message + * @param kx key exchange context to create PING for */ static void -process_hello (void *cls, const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_HELLO_Message *hello, const char *err_msg) +setup_fresh_ping (struct GSC_KeyExchangeInfo *kx) +{ + struct PingMessage pp; + struct PingMessage *pm; + struct GNUNET_CRYPTO_SymmetricInitializationVector iv; + + pm = &kx->ping; + kx->ping_challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + UINT32_MAX); + pm->header.size = htons (sizeof (struct PingMessage)); + pm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PING); + pm->iv_seed = calculate_seed (kx); + derive_iv (&iv, + &kx->encrypt_key, + pm->iv_seed, + kx->peer); + pp.challenge = kx->ping_challenge; + pp.target = *kx->peer; + do_encrypt (kx, + &iv, + &pp.target, + &pm->target, + sizeof (struct PingMessage) - ((void *) &pm->target - + (void *) pm)); +} + + +/** + * 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 + * + * @param cls the `struct GSC_KeyExchangeInfo` + * @param m the message + */ +static int +deliver_message (void *cls, + const struct GNUNET_MessageHeader *m) { struct GSC_KeyExchangeInfo *kx = cls; - struct SetKeyMessage *skm; - if (err_msg != NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("Error in communication with PEERINFO service\n")); - kx->pitr = NULL; - if (GNUNET_SCHEDULER_NO_TASK != kx->retry_set_key_task) - GNUNET_SCHEDULER_cancel (kx->retry_set_key_task); - kx->retry_set_key_task = - GNUNET_SCHEDULER_add_delayed (kx->set_key_retry_frequency, - &set_key_retry_task, kx); - return; - } - if (peer == NULL) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Decrypted message of type %d from %s\n", + ntohs (m->type), + GNUNET_i2s (kx->peer)); + if (GNUNET_CORE_KX_STATE_UP != kx->status) { - kx->pitr = NULL; - if (kx->public_key != NULL) - return; /* done here */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Failed to obtain public key for peer `%4s', delaying processing of SET_KEY\n", - GNUNET_i2s (&kx->peer)); GNUNET_STATISTICS_update (GSC_stats, - gettext_noop - ("# Delayed connecting due to lack of public key"), - 1, GNUNET_NO); - if (GNUNET_SCHEDULER_NO_TASK != kx->retry_set_key_task) - GNUNET_SCHEDULER_cancel (kx->retry_set_key_task); - kx->retry_set_key_task = - GNUNET_SCHEDULER_add_delayed (kx->set_key_retry_frequency, - &set_key_retry_task, kx); - return; - } - if (kx->public_key != NULL) - { - /* already have public key, why are we here? */ - GNUNET_break (0); - return; - } - GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == kx->retry_set_key_task); - kx->public_key = - GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); - if (GNUNET_OK != GNUNET_HELLO_get_key (hello, kx->public_key)) - { - GNUNET_break (0); - GNUNET_free (kx->public_key); - kx->public_key = NULL; - return; + gettext_noop ("# PAYLOAD dropped (out of order)"), + 1, + GNUNET_NO); + return GNUNET_OK; } - send_key (kx); - if (NULL != kx->skm_received) + switch (ntohs (m->type)) { - skm = kx->skm_received; - kx->skm_received = NULL; - GSC_KX_handle_set_key (kx, &skm->header); - GNUNET_free (skm); + case GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP: + case GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP: + GSC_SESSIONS_set_typemap (kx->peer, m); + return GNUNET_OK; + case GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP: + GSC_SESSIONS_confirm_typemap (kx->peer, m); + return GNUNET_OK; + default: + GSC_CLIENTS_deliver_message (kx->peer, + m, + ntohs (m->size), + GNUNET_CORE_OPTION_SEND_FULL_INBOUND); + GSC_CLIENTS_deliver_message (kx->peer, + m, + sizeof (struct GNUNET_MessageHeader), + GNUNET_CORE_OPTION_SEND_HDR_INBOUND); } + return GNUNET_OK; } /** - * Start the key exchange with the given peer. + * 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. * + * @param cls closure (NULL) * @param pid identity of the peer to do a key exchange with * @return key exchange information context */ -struct GSC_KeyExchangeInfo * -GSC_KX_start (const struct GNUNET_PeerIdentity *pid) +static void * +handle_transport_notify_connect (void *cls, + const struct GNUNET_PeerIdentity *pid, + struct GNUNET_MQ_Handle *mq) { struct GSC_KeyExchangeInfo *kx; + struct GNUNET_HashCode h1; + struct GNUNET_HashCode h2; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initiating key exchange with `%s'\n", + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Initiating key exchange with `%s'\n", GNUNET_i2s (pid)); GNUNET_STATISTICS_update (GSC_stats, - gettext_noop ("# key exchanges initiated"), 1, + gettext_noop ("# key exchanges initiated"), + 1, GNUNET_NO); - kx = GNUNET_malloc (sizeof (struct GSC_KeyExchangeInfo)); - kx->peer = *pid; + kx = GNUNET_new (struct GSC_KeyExchangeInfo); + kx->mst = GNUNET_MST_create (&deliver_message, + kx); + kx->mq = mq; + kx->peer = pid; kx->set_key_retry_frequency = INITIAL_SET_KEY_RETRY_FREQUENCY; - kx->pitr = - GNUNET_PEERINFO_iterate (peerinfo, pid, - GNUNET_TIME_UNIT_FOREVER_REL /* timeout? */ , - &process_hello, kx); + GNUNET_CONTAINER_DLL_insert (kx_head, + kx_tail, + kx); + kx->status = GNUNET_CORE_KX_STATE_KEY_SENT; + monitor_notify_all (kx); + GNUNET_CRYPTO_hash (pid, + sizeof (struct GNUNET_PeerIdentity), + &h1); + GNUNET_CRYPTO_hash (&GSC_my_identity, + sizeof (struct GNUNET_PeerIdentity), + &h2); + if (0 < GNUNET_CRYPTO_hash_cmp (&h1, + &h2)) + { + /* peer with "lower" identity starts KX, otherwise we typically end up + with both peers starting the exchange and transmit the 'set key' + message twice */ + send_key (kx); + } + else + { + /* peer with "higher" identity starts a delayed KX, if the "lower" peer + * does not start a KX since he sees no reasons to do so */ + kx->retry_set_key_task + = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &set_key_retry_task, + kx); + } return kx; } /** + * Function called by transport telling us that a peer + * disconnected. * Stop key exchange with the given peer. Clean up key material. * - * @param kx key exchange to stop + * @param cls closure + * @param peer the peer that disconnected + * @param handler_cls the `struct GSC_KeyExchangeInfo` of the peer */ -void -GSC_KX_stop (struct GSC_KeyExchangeInfo *kx) +static void +handle_transport_notify_disconnect (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *handler_cls) { - GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# key exchanges stopped"), - 1, GNUNET_NO); - if (kx->pitr != NULL) - { - GNUNET_PEERINFO_iterate_cancel (kx->pitr); - kx->pitr = NULL; - } - if (kx->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK) + struct GSC_KeyExchangeInfo *kx = handler_cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer `%s' disconnected from us.\n", + GNUNET_i2s (peer)); + GSC_SESSIONS_end (kx->peer); + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# key exchanges stopped"), + 1, + GNUNET_NO); + if (NULL != kx->retry_set_key_task) { GNUNET_SCHEDULER_cancel (kx->retry_set_key_task); - kx->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK; + kx->retry_set_key_task = NULL; } - if (kx->keep_alive_task != GNUNET_SCHEDULER_NO_TASK) + if (NULL != kx->keep_alive_task) { GNUNET_SCHEDULER_cancel (kx->keep_alive_task); - kx->keep_alive_task = GNUNET_SCHEDULER_NO_TASK; + kx->keep_alive_task = NULL; } - GNUNET_free_non_null (kx->skm_received); - GNUNET_free_non_null (kx->ping_received); - GNUNET_free_non_null (kx->pong_received); - GNUNET_free_non_null (kx->emsg_received); - GNUNET_free_non_null (kx->public_key); + kx->status = GNUNET_CORE_KX_PEER_DISCONNECT; + monitor_notify_all (kx); + GNUNET_CONTAINER_DLL_remove (kx_head, + kx_tail, + kx); + GNUNET_MST_destroy (kx->mst); GNUNET_free (kx); } /** - * We received a SET_KEY message. Validate and update - * our key material and status. + * Send our PING to the other peer. * - * @param kx key exchange status for the corresponding peer - * @param msg the set key message we received + * @param kx key exchange context */ -void -GSC_KX_handle_set_key (struct GSC_KeyExchangeInfo *kx, - const struct GNUNET_MessageHeader *msg) +static void +send_ping (struct GSC_KeyExchangeInfo *kx) { - const struct SetKeyMessage *m; - struct GNUNET_TIME_Absolute t; - struct GNUNET_CRYPTO_AesSessionKey k; - struct PingMessage *ping; - struct PongMessage *pong; - enum KxStateMachine sender_status; - uint16_t size; - - size = ntohs (msg->size); - if (size != sizeof (struct SetKeyMessage)) + struct GNUNET_MQ_Envelope *env; + + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# PING messages transmitted"), + 1, + GNUNET_NO); + env = GNUNET_MQ_msg_copy (&kx->ping.header); + GNUNET_MQ_send (kx->mq, + env); +} + + +/** + * Derive fresh session keys from the current ephemeral keys. + * + * @param kx session to derive keys for + */ +static void +derive_session_keys (struct GSC_KeyExchangeInfo *kx) +{ + struct GNUNET_HashCode key_material; + + if (GNUNET_OK != + GNUNET_CRYPTO_ecc_ecdh (my_ephemeral_key, + &kx->other_ephemeral_key, + &key_material)) { - GNUNET_break_op (0); + GNUNET_break (0); return; } - m = (const struct SetKeyMessage *) msg; - GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# session keys received"), - 1, GNUNET_NO); + derive_aes_key (&GSC_my_identity, + kx->peer, + &key_material, + &kx->encrypt_key); + derive_aes_key (kx->peer, + &GSC_my_identity, + &key_material, + &kx->decrypt_key); + memset (&key_material, 0, sizeof (key_material)); + /* fresh key, reset sequence numbers */ + kx->last_sequence_number_received = 0; + kx->last_packets_bitmap = 0; + setup_fresh_ping (kx); +} - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Core service receives `%s' request from `%4s'.\n", "SET_KEY", - GNUNET_i2s (&kx->peer)); - if (kx->public_key == NULL) + +/** + * We received a #GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY message. + * Validate and update our key material and status. + * + * @param cls key exchange status for the corresponding peer + * @param m the set key message we received + */ +static void +handle_ephemeral_key (void *cls, + const struct EphemeralKeyMessage *m) +{ + struct GSC_KeyExchangeInfo *kx = cls; + struct GNUNET_TIME_Absolute start_t; + struct GNUNET_TIME_Absolute end_t; + struct GNUNET_TIME_Absolute now; + enum GNUNET_CORE_KxState sender_status; + + end_t = GNUNET_TIME_absolute_ntoh (m->expiration_time); + if ( ( (GNUNET_CORE_KX_STATE_KEY_RECEIVED == kx->status) || + (GNUNET_CORE_KX_STATE_UP == kx->status) || + (GNUNET_CORE_KX_STATE_REKEY_SENT == kx->status) ) && + (end_t.abs_value_us < kx->foreign_key_expires.abs_value_us) ) { - GNUNET_free_non_null (kx->skm_received); - kx->skm_received = (struct SetKeyMessage *) GNUNET_copy_message (msg); + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# old ephemeral keys ignored"), + 1, GNUNET_NO); return; } + start_t = GNUNET_TIME_absolute_ntoh (m->creation_time); + + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# ephemeral keys received"), + 1, GNUNET_NO); + if (0 != - memcmp (&m->target, &GSC_my_identity, + memcmp (&m->origin_identity, + kx->peer, sizeof (struct GNUNET_PeerIdentity))) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - _("`%s' is for `%s', not for me. Ignoring.\n"), "SET_KEY", - GNUNET_i2s (&m->target)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Received EPHEMERAL_KEY from %s, but expected %s\n", + GNUNET_i2s (&m->origin_identity), + GNUNET_i2s_full (kx->peer)); + GNUNET_break_op (0); return; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Core service receives EPHEMERAL_KEY request from `%s'.\n", + GNUNET_i2s (kx->peer)); if ((ntohl (m->purpose.size) != - sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + + sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + sizeof (struct GNUNET_TIME_AbsoluteNBO) + - sizeof (struct GNUNET_CRYPTO_RsaEncryptedData) + - sizeof (struct GNUNET_PeerIdentity)) || + sizeof (struct GNUNET_TIME_AbsoluteNBO) + + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) + + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)) || (GNUNET_OK != - GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_SET_KEY, &m->purpose, - &m->signature, kx->public_key))) + GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_SET_ECC_KEY, + &m->purpose, + &m->signature, + &m->origin_identity.public_key))) { /* invalid signature */ GNUNET_break_op (0); return; } - t = GNUNET_TIME_absolute_ntoh (m->creation_time); - if (((kx->status == KX_STATE_KEY_RECEIVED) || (kx->status == KX_STATE_UP)) && - (t.abs_value < kx->decrypt_key_created.abs_value)) - { - /* this could rarely happen due to massive re-ordering of - * messages on the network level, but is most likely either - * a bug or some adversary messing with us. Report. */ - GNUNET_break_op (0); - return; - } - if ((GNUNET_CRYPTO_rsa_decrypt - (my_private_key, &m->encrypted_key, &k, - sizeof (struct GNUNET_CRYPTO_AesSessionKey)) != - sizeof (struct GNUNET_CRYPTO_AesSessionKey)) || - (GNUNET_OK != GNUNET_CRYPTO_aes_check_session_key (&k))) + now = GNUNET_TIME_absolute_get (); + if ( (end_t.abs_value_us < GNUNET_TIME_absolute_subtract (now, REKEY_TOLERANCE).abs_value_us) || + (start_t.abs_value_us > GNUNET_TIME_absolute_add (now, REKEY_TOLERANCE).abs_value_us) ) { - /* failed to decrypt !? */ - GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Ephemeral key message from peer `%s' rejected as its validity range does not match our system time (%llu not in [%llu,%llu]).\n"), + GNUNET_i2s (kx->peer), + (unsigned long long) now.abs_value_us, + (unsigned long long) start_t.abs_value_us, + (unsigned long long) end_t.abs_value_us); return; } + kx->other_ephemeral_key = m->ephemeral_key; + kx->foreign_key_expires = end_t; + derive_session_keys (kx); GNUNET_STATISTICS_update (GSC_stats, - gettext_noop ("# SET_KEY messages decrypted"), 1, + gettext_noop ("# EPHEMERAL_KEY messages received"), 1, GNUNET_NO); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received SET_KEY from `%s'\n", - GNUNET_i2s (&kx->peer)); - kx->decrypt_key = k; - if (kx->decrypt_key_created.abs_value != t.abs_value) + + /* check if we still need to send the sender our key */ + sender_status = (enum GNUNET_CORE_KxState) ntohl (m->sender_status); + switch (sender_status) { - /* fresh key, reset sequence numbers */ - kx->last_sequence_number_received = 0; - kx->last_packets_bitmap = 0; - kx->decrypt_key_created = t; + case GNUNET_CORE_KX_STATE_DOWN: + GNUNET_break_op (0); + break; + case GNUNET_CORE_KX_STATE_KEY_SENT: + /* fine, need to send our key after updating our status, see below */ + GSC_SESSIONS_reinit (kx->peer); + break; + case GNUNET_CORE_KX_STATE_KEY_RECEIVED: + /* other peer already got our key, but typemap did go down */ + GSC_SESSIONS_reinit (kx->peer); + break; + case GNUNET_CORE_KX_STATE_UP: + /* other peer already got our key, typemap NOT down */ + break; + case GNUNET_CORE_KX_STATE_REKEY_SENT: + /* other peer already got our key, typemap NOT down */ + break; + default: + GNUNET_break (0); + break; } - sender_status = (enum KxStateMachine) ntohl (m->sender_status); + /* check if we need to confirm everything is fine via PING + PONG */ switch (kx->status) { - case KX_STATE_DOWN: - kx->status = KX_STATE_KEY_RECEIVED; - /* we're not up, so we are already doing 'send_key' */ - break; - case KX_STATE_KEY_SENT: - kx->status = KX_STATE_KEY_RECEIVED; - /* we're not up, so we are already doing 'send_key' */ + case GNUNET_CORE_KX_STATE_DOWN: + GNUNET_assert (NULL == kx->keep_alive_task); + kx->status = GNUNET_CORE_KX_STATE_KEY_RECEIVED; + monitor_notify_all (kx); + if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status) + send_key (kx); + else + send_ping (kx); break; - case KX_STATE_KEY_RECEIVED: - /* we're not up, so we are already doing 'send_key' */ + case GNUNET_CORE_KX_STATE_KEY_SENT: + GNUNET_assert (NULL == kx->keep_alive_task); + kx->status = GNUNET_CORE_KX_STATE_KEY_RECEIVED; + monitor_notify_all (kx); + if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status) + send_key (kx); + else + send_ping (kx); break; - case KX_STATE_UP: - if ((sender_status == KX_STATE_DOWN) || - (sender_status == KX_STATE_KEY_SENT)) - send_key (kx); /* we are up, but other peer is not! */ + case GNUNET_CORE_KX_STATE_KEY_RECEIVED: + GNUNET_assert (NULL == kx->keep_alive_task); + if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status) + send_key (kx); + else + send_ping (kx); break; - case KX_STATE_REKEY: - if ((sender_status == KX_STATE_DOWN) || - (sender_status == KX_STATE_KEY_SENT)) - send_key (kx); /* we are up, but other peer is not! */ + case GNUNET_CORE_KX_STATE_UP: + kx->status = GNUNET_CORE_KX_STATE_REKEY_SENT; + monitor_notify_all (kx); + if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status) + send_key (kx); + else + send_ping (kx); break; - case KX_STATE_REKEY_SENT: - if ((sender_status == KX_STATE_DOWN) || - (sender_status == KX_STATE_KEY_SENT)) - send_key (kx); /* we are up, but other peer is not! */ + case GNUNET_CORE_KX_STATE_REKEY_SENT: + if (GNUNET_CORE_KX_STATE_KEY_SENT == sender_status) + send_key (kx); + else + send_ping (kx); break; default: GNUNET_break (0); break; } - if (kx->ping_received != NULL) - { - ping = kx->ping_received; - kx->ping_received = NULL; - GSC_KX_handle_ping (kx, &ping->header); - GNUNET_free (ping); - } - if (kx->pong_received != NULL) - { - pong = kx->pong_received; - kx->pong_received = NULL; - GSC_KX_handle_pong (kx, &pong->header); - GNUNET_free (pong); - } } @@ -903,44 +1073,47 @@ GSC_KX_handle_set_key (struct GSC_KeyExchangeInfo *kx, * We received a PING message. Validate and transmit * a PONG message. * - * @param kx key exchange status for the corresponding peer - * @param msg the encrypted PING message itself + * @param cls key exchange status for the corresponding peer + * @param m the encrypted PING message itself */ -void -GSC_KX_handle_ping (struct GSC_KeyExchangeInfo *kx, - const struct GNUNET_MessageHeader *msg) +static void +handle_ping (void *cls, + const struct PingMessage *m) { - const struct PingMessage *m; + struct GSC_KeyExchangeInfo *kx = cls; struct PingMessage t; struct PongMessage tx; - struct PongMessage tp; - struct GNUNET_CRYPTO_AesInitializationVector iv; - uint16_t msize; + struct PongMessage *tp; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_CRYPTO_SymmetricInitializationVector iv; - msize = ntohs (msg->size); - if (msize != sizeof (struct PingMessage)) - { - GNUNET_break_op (0); - return; - } GNUNET_STATISTICS_update (GSC_stats, - gettext_noop ("# PING messages received"), 1, + gettext_noop ("# PING messages received"), + 1, GNUNET_NO); - if ((kx->status != KX_STATE_KEY_RECEIVED) && (kx->status != KX_STATE_UP) && - (kx->status != KX_STATE_REKEY_SENT)) + if ( (kx->status != GNUNET_CORE_KX_STATE_KEY_RECEIVED) && + (kx->status != GNUNET_CORE_KX_STATE_UP) && + (kx->status != GNUNET_CORE_KX_STATE_REKEY_SENT)) { - /* defer */ - GNUNET_free_non_null (kx->ping_received); - kx->ping_received = (struct PingMessage *) GNUNET_copy_message (msg); + /* ignore */ + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# PING messages dropped (out of order)"), + 1, + GNUNET_NO); return; } - m = (const struct PingMessage *) msg; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Core service receives `%s' request from `%4s'.\n", "PING", - GNUNET_i2s (&kx->peer)); - derive_iv (&iv, &kx->decrypt_key, m->iv_seed, &GSC_my_identity); + "Core service receives PING request from `%s'.\n", + GNUNET_i2s (kx->peer)); + derive_iv (&iv, + &kx->decrypt_key, + m->iv_seed, + &GSC_my_identity); if (GNUNET_OK != - do_decrypt (kx, &iv, &m->target, &t.target, + do_decrypt (kx, + &iv, + &m->target, + &t.target, sizeof (struct PingMessage) - ((void *) &m->target - (void *) m))) { @@ -948,100 +1121,45 @@ GSC_KX_handle_ping (struct GSC_KeyExchangeInfo *kx, return; } if (0 != - memcmp (&t.target, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity))) + memcmp (&t.target, + &GSC_my_identity, + sizeof (struct GNUNET_PeerIdentity))) { - char sender[9]; - char peer[9]; - - GNUNET_snprintf (sender, sizeof (sender), "%8s", GNUNET_i2s (&kx->peer)); - GNUNET_snprintf (peer, sizeof (peer), "%8s", GNUNET_i2s (&t.target)); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ - ("Received PING from `%s' for different identity: I am `%s', PONG identity: `%s'\n"), - sender, GNUNET_i2s (&GSC_my_identity), peer); + if (GNUNET_CORE_KX_STATE_REKEY_SENT != kx->status) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Decryption of PING from peer `%s' failed\n", + GNUNET_i2s (kx->peer)); + else + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Decryption of PING from peer `%s' failed after rekey (harmless)\n", + GNUNET_i2s (kx->peer)); GNUNET_break_op (0); return; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received PING from `%s'\n", - GNUNET_i2s (&kx->peer)); /* construct PONG */ - tx.reserved = GNUNET_BANDWIDTH_VALUE_MAX; + tx.reserved = 0; tx.challenge = t.challenge; tx.target = t.target; - tp.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PONG); - tp.header.size = htons (sizeof (struct PongMessage)); - tp.iv_seed = - GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); - derive_pong_iv (&iv, &kx->encrypt_key, tp.iv_seed, t.challenge, &kx->peer); - do_encrypt (kx, &iv, &tx.challenge, &tp.challenge, - sizeof (struct PongMessage) - ((void *) &tp.challenge - - (void *) &tp)); - GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# PONG messages created"), - 1, GNUNET_NO); - GSC_NEIGHBOURS_transmit (&kx->peer, &tp.header, - GNUNET_TIME_UNIT_FOREVER_REL /* FIXME: timeout */ ); -} - - -/** - * Create a fresh SET KEY message for transmission to the other peer. - * Also creates a new key. - * - * @param kx key exchange context to create SET KEY message for - */ -static void -setup_fresh_setkey (struct GSC_KeyExchangeInfo *kx) -{ - struct SetKeyMessage *skm; - - GNUNET_CRYPTO_aes_create_session_key (&kx->encrypt_key); - kx->encrypt_key_created = GNUNET_TIME_absolute_get (); - skm = &kx->skm; - skm->header.size = htons (sizeof (struct SetKeyMessage)); - skm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SET_KEY); - skm->purpose.size = - htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + - sizeof (struct GNUNET_TIME_AbsoluteNBO) + - sizeof (struct GNUNET_CRYPTO_RsaEncryptedData) + - sizeof (struct GNUNET_PeerIdentity)); - skm->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SET_KEY); - skm->creation_time = GNUNET_TIME_absolute_hton (kx->encrypt_key_created); - skm->target = kx->peer; - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_rsa_encrypt (&kx->encrypt_key, - sizeof (struct - GNUNET_CRYPTO_AesSessionKey), - kx->public_key, - &skm->encrypted_key)); - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_rsa_sign (my_private_key, &skm->purpose, - &skm->signature)); -} - - -/** - * Create a fresh PING message for transmission to the other peer. - * - * @param kx key exchange context to create PING for - */ -static void -setup_fresh_ping (struct GSC_KeyExchangeInfo *kx) -{ - struct PingMessage pp; - struct PingMessage *pm; - struct GNUNET_CRYPTO_AesInitializationVector iv; - - pm = &kx->ping; - pm->header.size = htons (sizeof (struct PingMessage)); - pm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_PING); - pm->iv_seed = - GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); - derive_iv (&iv, &kx->encrypt_key, pm->iv_seed, &kx->peer); - pp.challenge = kx->ping_challenge; - pp.target = kx->peer; - do_encrypt (kx, &iv, &pp.target, &pm->target, - sizeof (struct PingMessage) - ((void *) &pm->target - - (void *) pm)); + env = GNUNET_MQ_msg (tp, + GNUNET_MESSAGE_TYPE_CORE_PONG); + tp->iv_seed = calculate_seed (kx); + derive_pong_iv (&iv, + &kx->encrypt_key, + tp->iv_seed, + t.challenge, + kx->peer); + do_encrypt (kx, + &iv, + &tx.challenge, + &tp->challenge, + sizeof (struct PongMessage) - ((void *) &tp->challenge - + (void *) tp)); + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# PONG messages created"), + 1, + GNUNET_NO); + GNUNET_MQ_send (kx->mq, + env); } @@ -1049,40 +1167,45 @@ setup_fresh_ping (struct GSC_KeyExchangeInfo *kx) * Task triggered when a neighbour entry is about to time out * (and we should prevent this by sending a PING). * - * @param cls the 'struct GSC_KeyExchangeInfo' - * @param tc scheduler context (not used) + * @param cls the `struct GSC_KeyExchangeInfo` */ static void -send_keep_alive (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +send_keep_alive (void *cls) { struct GSC_KeyExchangeInfo *kx = cls; struct GNUNET_TIME_Relative retry; struct GNUNET_TIME_Relative left; - kx->keep_alive_task = GNUNET_SCHEDULER_NO_TASK; + kx->keep_alive_task = NULL; left = GNUNET_TIME_absolute_get_remaining (kx->timeout); - if (left.rel_value == 0) + if (0 == left.rel_value_us) { GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# sessions terminated by timeout"), - 1, GNUNET_NO); - GSC_SESSIONS_end (&kx->peer); - kx->status = KX_STATE_DOWN; + 1, + GNUNET_NO); + GSC_SESSIONS_end (kx->peer); + kx->status = GNUNET_CORE_KX_STATE_KEY_SENT; + monitor_notify_all (kx); + send_key (kx); return; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending KEEPALIVE to `%s'\n", - GNUNET_i2s (&kx->peer)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending KEEPALIVE to `%s'\n", + GNUNET_i2s (kx->peer)); GNUNET_STATISTICS_update (GSC_stats, - gettext_noop ("# keepalive messages sent"), 1, + gettext_noop ("# keepalive messages sent"), + 1, GNUNET_NO); setup_fresh_ping (kx); - GSC_NEIGHBOURS_transmit (&kx->peer, &kx->ping.header, - kx->set_key_retry_frequency); + send_ping (kx); retry = GNUNET_TIME_relative_max (GNUNET_TIME_relative_divide (left, 2), MIN_PING_FREQUENCY); kx->keep_alive_task = - GNUNET_SCHEDULER_add_delayed (retry, &send_keep_alive, kx); + GNUNET_SCHEDULER_add_delayed (retry, + &send_keep_alive, + kx); } @@ -1096,57 +1219,27 @@ send_keep_alive (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) static void update_timeout (struct GSC_KeyExchangeInfo *kx) { + struct GNUNET_TIME_Relative delta; + kx->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); - if (kx->keep_alive_task != GNUNET_SCHEDULER_NO_TASK) + delta = GNUNET_TIME_absolute_get_difference (kx->last_notify_timeout, + kx->timeout); + if (delta.rel_value_us > 5LL * 1000LL * 1000LL) + { + /* we only notify monitors about timeout changes if those + are bigger than the threshold (5s) */ + monitor_notify_all (kx); + } + if (NULL != kx->keep_alive_task) GNUNET_SCHEDULER_cancel (kx->keep_alive_task); kx->keep_alive_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_divide (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT, - 2), &send_keep_alive, kx); -} - - -/** - * Trigger rekeying event. - * - * @param cls the 'struct GSC_KeyExchangeInfo' - * @param tc schedule context (unused) - */ -static void -trigger_rekey (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct GSC_KeyExchangeInfo *kx = cls; - - kx->status = KX_STATE_REKEY; - kx->set_key_retry_frequency = INITIAL_SET_KEY_RETRY_FREQUENCY; - kx->retry_set_key_task = - GNUNET_SCHEDULER_add_delayed (kx->set_key_retry_frequency, - &set_key_retry_task, kx); -} - - -/** - * Schedule rekey operation. - * - * @param kx key exchange to schedule rekey for - */ -static void -schedule_rekey (struct GSC_KeyExchangeInfo *kx) -{ - struct GNUNET_TIME_Relative rdelay; - - if (GNUNET_SCHEDULER_NO_TASK != kx->retry_set_key_task) - GNUNET_SCHEDULER_cancel (kx->retry_set_key_task); - rdelay = REKEY_FREQUENCY; - /* randomize rekey frequency by one minute to avoid synchronization */ - rdelay.rel_value += GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, - 60 * 1000); - kx->retry_set_key_task = GNUNET_SCHEDULER_add_delayed (REKEY_FREQUENCY, - &trigger_rekey, - kx); + 2), + &send_keep_alive, + kx); } @@ -1154,57 +1247,57 @@ schedule_rekey (struct GSC_KeyExchangeInfo *kx) * We received a PONG message. Validate and update our status. * * @param kx key exchange context for the the PONG - * @param msg the encrypted PONG message itself + * @param m the encrypted PONG message itself */ -void -GSC_KX_handle_pong (struct GSC_KeyExchangeInfo *kx, - const struct GNUNET_MessageHeader *msg) +static void +handle_pong (void *cls, + const struct PongMessage *m) { - const struct PongMessage *m; + struct GSC_KeyExchangeInfo *kx = cls; struct PongMessage t; - struct EncryptedMessage *emsg; - struct GNUNET_CRYPTO_AesInitializationVector iv; - uint16_t msize; + struct GNUNET_CRYPTO_SymmetricInitializationVector iv; - msize = ntohs (msg->size); - if (msize != sizeof (struct PongMessage)) - { - GNUNET_break_op (0); - return; - } GNUNET_STATISTICS_update (GSC_stats, - gettext_noop ("# PONG messages received"), 1, + gettext_noop ("# PONG messages received"), + 1, GNUNET_NO); switch (kx->status) { - case KX_STATE_DOWN: + case GNUNET_CORE_KX_STATE_DOWN: + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# PONG messages dropped (connection down)"), 1, + GNUNET_NO); return; - case KX_STATE_KEY_SENT: - GNUNET_free_non_null (kx->pong_received); - kx->pong_received = (struct PongMessage *) GNUNET_copy_message (msg); + case GNUNET_CORE_KX_STATE_KEY_SENT: + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# PONG messages dropped (out of order)"), 1, + GNUNET_NO); return; - case KX_STATE_KEY_RECEIVED: + case GNUNET_CORE_KX_STATE_KEY_RECEIVED: break; - case KX_STATE_UP: + case GNUNET_CORE_KX_STATE_UP: break; - case KX_STATE_REKEY: - break; - case KX_STATE_REKEY_SENT: + case GNUNET_CORE_KX_STATE_REKEY_SENT: break; default: GNUNET_break (0); return; } - m = (const struct PongMessage *) msg; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Core service receives `%s' response from `%4s'.\n", "PONG", - GNUNET_i2s (&kx->peer)); + "Core service receives PONG response from `%s'.\n", + GNUNET_i2s (kx->peer)); /* mark as garbage, just to be sure */ memset (&t, 255, sizeof (t)); - derive_pong_iv (&iv, &kx->decrypt_key, m->iv_seed, kx->ping_challenge, + derive_pong_iv (&iv, + &kx->decrypt_key, + m->iv_seed, + kx->ping_challenge, &GSC_my_identity); if (GNUNET_OK != - do_decrypt (kx, &iv, &m->challenge, &t.challenge, + do_decrypt (kx, + &iv, + &m->challenge, + &t.challenge, sizeof (struct PongMessage) - ((void *) &m->challenge - (void *) m))) { @@ -1212,63 +1305,67 @@ GSC_KX_handle_pong (struct GSC_KeyExchangeInfo *kx, return; } GNUNET_STATISTICS_update (GSC_stats, - gettext_noop ("# PONG messages decrypted"), 1, + gettext_noop ("# PONG messages decrypted"), + 1, GNUNET_NO); - if ((0 != memcmp (&t.target, &kx->peer, sizeof (struct GNUNET_PeerIdentity))) - || (kx->ping_challenge != t.challenge)) + if ((0 != memcmp (&t.target, + kx->peer, + sizeof (struct GNUNET_PeerIdentity))) || + (kx->ping_challenge != t.challenge)) { /* PONG malformed */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received malformed `%s' wanted sender `%4s' with challenge %u\n", - "PONG", GNUNET_i2s (&kx->peer), + "Received malformed PONG wanted sender `%s' with challenge %u\n", + GNUNET_i2s (kx->peer), (unsigned int) kx->ping_challenge); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received malformed `%s' received from `%4s' with challenge %u\n", - "PONG", GNUNET_i2s (&t.target), (unsigned int) t.challenge); + "Received malformed PONG received from `%s' with challenge %u\n", + GNUNET_i2s (&t.target), + (unsigned int) t.challenge); return; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received PONG from `%s'\n", - GNUNET_i2s (&kx->peer)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received PONG from `%s'\n", + GNUNET_i2s (kx->peer)); + /* no need to resend key any longer */ + if (NULL != kx->retry_set_key_task) + { + GNUNET_SCHEDULER_cancel (kx->retry_set_key_task); + kx->retry_set_key_task = NULL; + } switch (kx->status) { - case KX_STATE_DOWN: - GNUNET_break (0); /* should be impossible */ + case GNUNET_CORE_KX_STATE_DOWN: + GNUNET_assert (0); /* should be impossible */ return; - case KX_STATE_KEY_SENT: - GNUNET_break (0); /* should be impossible */ + case GNUNET_CORE_KX_STATE_KEY_SENT: + GNUNET_assert (0); /* should be impossible */ return; - case KX_STATE_KEY_RECEIVED: + case GNUNET_CORE_KX_STATE_KEY_RECEIVED: GNUNET_STATISTICS_update (GSC_stats, - gettext_noop - ("# session keys confirmed via PONG"), 1, + gettext_noop ("# session keys confirmed via PONG"), + 1, GNUNET_NO); - kx->status = KX_STATE_UP; - GSC_SESSIONS_create (&kx->peer, kx); - schedule_rekey (kx); - GNUNET_assert (kx->keep_alive_task == GNUNET_SCHEDULER_NO_TASK); - if (kx->emsg_received != NULL) - { - emsg = kx->emsg_received; - kx->emsg_received = NULL; - GSC_KX_handle_encrypted_message (kx, &emsg->header, NULL, - 0 /* FIXME: ATSI */ ); - GNUNET_free (emsg); - } + kx->status = GNUNET_CORE_KX_STATE_UP; + monitor_notify_all (kx); + GSC_SESSIONS_create (kx->peer, kx); + GNUNET_assert (NULL == kx->keep_alive_task); update_timeout (kx); break; - case KX_STATE_UP: - update_timeout (kx); - break; - case KX_STATE_REKEY: + case GNUNET_CORE_KX_STATE_UP: + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# timeouts prevented via PONG"), + 1, + GNUNET_NO); update_timeout (kx); break; - case KX_STATE_REKEY_SENT: + case GNUNET_CORE_KX_STATE_REKEY_SENT: GNUNET_STATISTICS_update (GSC_stats, - gettext_noop - ("# rekey operations confirmed via PONG"), 1, + gettext_noop ("# rekey operations confirmed via PONG"), + 1, GNUNET_NO); - kx->status = KX_STATE_UP; - schedule_rekey (kx); + kx->status = GNUNET_CORE_KX_STATE_UP; + monitor_notify_all (kx); update_timeout (kx); break; default: @@ -1279,87 +1376,36 @@ GSC_KX_handle_pong (struct GSC_KeyExchangeInfo *kx, /** - * Send our key (and encrypted PING) to the other peer. + * Send our key to the other peer. * * @param kx key exchange context */ static void send_key (struct GSC_KeyExchangeInfo *kx) { - if (kx->retry_set_key_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (kx->retry_set_key_task); - kx->retry_set_key_task = GNUNET_SCHEDULER_NO_TASK; - } - if (KX_STATE_UP == kx->status) - return; /* nothing to do */ - if (kx->public_key == NULL) - { - /* lookup public key, then try again */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Trying to obtain public key for `%s'\n", - GNUNET_i2s (&kx->peer)); - kx->pitr = - GNUNET_PEERINFO_iterate (peerinfo, &kx->peer, - GNUNET_TIME_UNIT_FOREVER_REL /* timeout? */ , - &process_hello, kx); - return; - } + struct GNUNET_MQ_Envelope *env; - /* update status */ - switch (kx->status) + GNUNET_assert (GNUNET_CORE_KX_STATE_DOWN != kx->status); + if (NULL != kx->retry_set_key_task) { - case KX_STATE_DOWN: - kx->status = KX_STATE_KEY_SENT; - /* setup SET KEY message */ - setup_fresh_setkey (kx); - setup_fresh_ping (kx); - GNUNET_STATISTICS_update (GSC_stats, - gettext_noop - ("# SET_KEY and PING messages created"), 1, - GNUNET_NO); - break; - case KX_STATE_KEY_SENT: - break; - case KX_STATE_KEY_RECEIVED: - break; - case KX_STATE_UP: - GNUNET_break (0); - return; - case KX_STATE_REKEY: - kx->status = KX_STATE_REKEY_SENT; - /* setup fresh SET KEY message */ - setup_fresh_setkey (kx); - setup_fresh_ping (kx); - GNUNET_STATISTICS_update (GSC_stats, - gettext_noop - ("# SET_KEY and PING messages created"), 1, - GNUNET_NO); - GNUNET_STATISTICS_update (GSC_stats, - gettext_noop - ("# REKEY operations performed"), 1, - GNUNET_NO); - break; - case KX_STATE_REKEY_SENT: - break; - default: - GNUNET_break (0); - return; + GNUNET_SCHEDULER_cancel (kx->retry_set_key_task); + kx->retry_set_key_task = NULL; } - /* always update sender status in SET KEY message */ - /* Not sending rekey sent state to be compatible with GNUnet 0.9.2 */ - kx->skm.sender_status = htonl ((int32_t) ((kx->status == KX_STATE_REKEY_SENT) ? - KX_STATE_KEY_RECEIVED : kx->status)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending SET_KEY and PING to `%s'\n", - GNUNET_i2s (&kx->peer)); - GSC_NEIGHBOURS_transmit (&kx->peer, &kx->skm.header, - kx->set_key_retry_frequency); - GSC_NEIGHBOURS_transmit (&kx->peer, &kx->ping.header, - kx->set_key_retry_frequency); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending key to `%s' (my status: %d)\n", + GNUNET_i2s (kx->peer), + kx->status); + current_ekm.sender_status = htonl ((int32_t) (kx->status)); + env = GNUNET_MQ_msg_copy (¤t_ekm.header); + GNUNET_MQ_send (kx->mq, + env); + if (GNUNET_CORE_KX_STATE_KEY_SENT != kx->status) + send_ping (kx); kx->retry_set_key_task = GNUNET_SCHEDULER_add_delayed (kx->set_key_retry_frequency, - &set_key_retry_task, kx); + &set_key_retry_task, + kx); } @@ -1368,158 +1414,188 @@ send_key (struct GSC_KeyExchangeInfo *kx) * * @param kx key exchange context * @param payload payload of the message - * @param payload_size number of bytes in 'payload' + * @param payload_size number of bytes in @a payload */ void GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo *kx, - const void *payload, size_t payload_size) + const void *payload, + size_t payload_size) { size_t used = payload_size + sizeof (struct EncryptedMessage); char pbuf[used]; /* plaintext */ - char cbuf[used]; /* ciphertext */ struct EncryptedMessage *em; /* encrypted message */ struct EncryptedMessage *ph; /* plaintext header */ - struct GNUNET_CRYPTO_AesInitializationVector iv; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_CRYPTO_SymmetricInitializationVector iv; struct GNUNET_CRYPTO_AuthKey auth_key; ph = (struct EncryptedMessage *) pbuf; - ph->iv_seed = - htonl (GNUNET_CRYPTO_random_u32 - (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX)); ph->sequence_number = htonl (++kx->last_sequence_number_sent); - ph->reserved = GNUNET_BANDWIDTH_VALUE_MAX; + ph->iv_seed = calculate_seed (kx); + ph->reserved = 0; ph->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); - memcpy (&ph[1], payload, payload_size); - - em = (struct EncryptedMessage *) cbuf; - em->header.size = htons (used); - em->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE); + GNUNET_memcpy (&ph[1], + payload, + payload_size); + env = GNUNET_MQ_msg_extra (em, + payload_size, + GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE); em->iv_seed = ph->iv_seed; - derive_iv (&iv, &kx->encrypt_key, ph->iv_seed, &kx->peer); + derive_iv (&iv, + &kx->encrypt_key, + ph->iv_seed, + kx->peer); GNUNET_assert (GNUNET_OK == - do_encrypt (kx, &iv, &ph->sequence_number, + do_encrypt (kx, + &iv, + &ph->sequence_number, &em->sequence_number, used - ENCRYPTED_HEADER_SIZE)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypted %u bytes for %s\n", - used - ENCRYPTED_HEADER_SIZE, GNUNET_i2s (&kx->peer)); - derive_auth_key (&auth_key, &kx->encrypt_key, ph->iv_seed, - kx->encrypt_key_created); - GNUNET_CRYPTO_hmac (&auth_key, &em->sequence_number, - used - ENCRYPTED_HEADER_SIZE, &em->hmac); - GSC_NEIGHBOURS_transmit (&kx->peer, &em->header, - GNUNET_TIME_UNIT_FOREVER_REL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypted %u bytes for %s\n", + (unsigned int) (used - ENCRYPTED_HEADER_SIZE), + GNUNET_i2s (kx->peer)); + derive_auth_key (&auth_key, + &kx->encrypt_key, + ph->iv_seed); + GNUNET_CRYPTO_hmac (&auth_key, + &em->sequence_number, + used - ENCRYPTED_HEADER_SIZE, + &em->hmac); + kx->has_excess_bandwidth = GNUNET_NO; + GNUNET_MQ_send (kx->mq, + env); } /** - * Closure for 'deliver_message' + * We received an encrypted message. Check that it is + * well-formed (size-wise). + * + * @param cls key exchange context for encrypting the message + * @param m encrypted message + * @return #GNUNET_OK if @a msg is well-formed (size-wise) */ -struct DeliverMessageContext +static int +check_encrypted (void *cls, + const struct EncryptedMessage *m) { + uint16_t size = ntohs (m->header.size) - sizeof (*m); - /** - * Performance information for the connection. - */ - const struct GNUNET_ATS_Information *atsi; - - /** - * Sender of the message. - */ - const struct GNUNET_PeerIdentity *peer; - - /** - * Number of entries in 'atsi' array. - */ - uint32_t atsi_count; -}; + if (size < sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} /** * We received an encrypted message. Decrypt, validate and * pass on to the appropriate clients. * - * @param kx key exchange context for encrypting the message - * @param msg encrypted message - * @param atsi performance data - * @param atsi_count number of entries in ats (excluding 0-termination) + * @param cls key exchange context for encrypting the message + * @param m encrypted message */ -void -GSC_KX_handle_encrypted_message (struct GSC_KeyExchangeInfo *kx, - const struct GNUNET_MessageHeader *msg, - const struct GNUNET_ATS_Information *atsi, - uint32_t atsi_count) +static void +handle_encrypted (void *cls, + const struct EncryptedMessage *m) { - const struct EncryptedMessage *m; + struct GSC_KeyExchangeInfo *kx = cls; struct EncryptedMessage *pt; /* plaintext */ - GNUNET_HashCode ph; + struct GNUNET_HashCode ph; uint32_t snum; struct GNUNET_TIME_Absolute t; - struct GNUNET_CRYPTO_AesInitializationVector iv; + struct GNUNET_CRYPTO_SymmetricInitializationVector iv; struct GNUNET_CRYPTO_AuthKey auth_key; - struct DeliverMessageContext dmc; - uint16_t size = ntohs (msg->size); + uint16_t size = ntohs (m->header.size); char buf[size] GNUNET_ALIGN; - if (size < - sizeof (struct EncryptedMessage) + sizeof (struct GNUNET_MessageHeader)) + if (GNUNET_CORE_KX_STATE_UP != kx->status) { - GNUNET_break_op (0); + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# DATA message dropped (out of order)"), + 1, + GNUNET_NO); return; } - m = (const struct EncryptedMessage *) msg; - if ((kx->status != KX_STATE_KEY_RECEIVED) && (kx->status != KX_STATE_UP) && - (kx->status != KX_STATE_REKEY_SENT) ) + if (0 == GNUNET_TIME_absolute_get_remaining (kx->foreign_key_expires).rel_value_us) { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Session to peer `%s' went down due to key expiration (should not happen)\n"), + GNUNET_i2s (kx->peer)); GNUNET_STATISTICS_update (GSC_stats, - gettext_noop - ("# failed to decrypt message (no session key)"), + gettext_noop ("# sessions terminated by key expiration"), 1, GNUNET_NO); + GSC_SESSIONS_end (kx->peer); + if (NULL != kx->keep_alive_task) + { + GNUNET_SCHEDULER_cancel (kx->keep_alive_task); + kx->keep_alive_task = NULL; + } + kx->status = GNUNET_CORE_KX_STATE_KEY_SENT; + monitor_notify_all (kx); + send_key (kx); return; } - if (kx->status == KX_STATE_KEY_RECEIVED) - { - /* defer */ - GNUNET_free_non_null (kx->ping_received); - kx->emsg_received = (struct EncryptedMessage *) GNUNET_copy_message (msg); - return; - } + /* validate hash */ - derive_auth_key (&auth_key, &kx->decrypt_key, m->iv_seed, - kx->decrypt_key_created); - GNUNET_CRYPTO_hmac (&auth_key, &m->sequence_number, - size - ENCRYPTED_HEADER_SIZE, &ph); - if (0 != memcmp (&ph, &m->hmac, sizeof (GNUNET_HashCode))) + derive_auth_key (&auth_key, + &kx->decrypt_key, + m->iv_seed); + GNUNET_CRYPTO_hmac (&auth_key, + &m->sequence_number, + size - ENCRYPTED_HEADER_SIZE, + &ph); + if (0 != memcmp (&ph, + &m->hmac, + sizeof (struct GNUNET_HashCode))) { /* checksum failed */ - GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed checksum validation for a message from `%s'\n", + GNUNET_i2s (kx->peer)); return; } - derive_iv (&iv, &kx->decrypt_key, m->iv_seed, &GSC_my_identity); + derive_iv (&iv, + &kx->decrypt_key, + m->iv_seed, + &GSC_my_identity); /* decrypt */ if (GNUNET_OK != - do_decrypt (kx, &iv, &m->sequence_number, &buf[ENCRYPTED_HEADER_SIZE], + do_decrypt (kx, + &iv, + &m->sequence_number, + &buf[ENCRYPTED_HEADER_SIZE], size - ENCRYPTED_HEADER_SIZE)) + { + GNUNET_break_op (0); return; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Decrypted %u bytes from %s\n", - size - ENCRYPTED_HEADER_SIZE, GNUNET_i2s (&kx->peer)); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Decrypted %u bytes from %s\n", + (unsigned int) (size - ENCRYPTED_HEADER_SIZE), + GNUNET_i2s (kx->peer)); pt = (struct EncryptedMessage *) buf; /* validate sequence number */ snum = ntohl (pt->sequence_number); if (kx->last_sequence_number_received == snum) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received duplicate message, ignoring.\n"); /* duplicate, ignore */ GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# bytes dropped (duplicates)"), - size, GNUNET_NO); + size, + GNUNET_NO); return; } if ((kx->last_sequence_number_received > snum) && (kx->last_sequence_number_received - snum > 32)) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ancient out of sequence message, ignoring.\n"); /* ancient out of sequence, ignore */ GNUNET_STATISTICS_update (GSC_stats, @@ -1530,11 +1606,11 @@ GSC_KX_handle_encrypted_message (struct GSC_KeyExchangeInfo *kx, } if (kx->last_sequence_number_received > snum) { - unsigned int rotbit = 1 << (kx->last_sequence_number_received - snum - 1); + uint32_t rotbit = 1U << (kx->last_sequence_number_received - snum - 1); if ((kx->last_packets_bitmap & rotbit) != 0) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received duplicate message, ignoring.\n"); GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# bytes dropped (duplicates)"), @@ -1557,12 +1633,13 @@ GSC_KX_handle_encrypted_message (struct GSC_KeyExchangeInfo *kx, /* check timestamp */ t = GNUNET_TIME_absolute_ntoh (pt->timestamp); - if (GNUNET_TIME_absolute_get_duration (t).rel_value > - MAX_MESSAGE_AGE.rel_value) + if (GNUNET_TIME_absolute_get_duration (t).rel_value_us > + MAX_MESSAGE_AGE.rel_value_us) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _("Message received far too old (%llu ms). Content ignored.\n"), - GNUNET_TIME_absolute_get_duration (t).rel_value); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Message received far too old (%s). Content ignored.\n", + GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (t), + GNUNET_YES)); GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# bytes dropped (ancient message)"), size, @@ -1574,90 +1651,176 @@ GSC_KX_handle_encrypted_message (struct GSC_KeyExchangeInfo *kx, update_timeout (kx); GNUNET_STATISTICS_update (GSC_stats, gettext_noop ("# bytes of payload decrypted"), - size - sizeof (struct EncryptedMessage), GNUNET_NO); - dmc.atsi = atsi; - dmc.atsi_count = atsi_count; - dmc.peer = &kx->peer; + size - sizeof (struct EncryptedMessage), + GNUNET_NO); if (GNUNET_OK != - GNUNET_SERVER_mst_receive (mst, &dmc, - &buf[sizeof (struct EncryptedMessage)], - size - sizeof (struct EncryptedMessage), - GNUNET_YES, GNUNET_NO)) + GNUNET_MST_from_buffer (kx->mst, + &buf[sizeof (struct EncryptedMessage)], + size - sizeof (struct EncryptedMessage), + GNUNET_YES, + GNUNET_NO)) GNUNET_break_op (0); } /** - * 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 + * One of our neighbours has excess bandwidth, remember this. * - * @param cls always NULL - * @param client who sent us the message (struct GSC_KeyExchangeInfo) - * @param m the message + * @param cls NULL + * @param pid identity of the peer with excess bandwidth + * @param connect_cls the `struct Neighbour` */ static void -deliver_message (void *cls, void *client, const struct GNUNET_MessageHeader *m) +handle_transport_notify_excess_bw (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *connect_cls) { - struct DeliverMessageContext *dmc = client; + struct GSC_KeyExchangeInfo *kx = connect_cls; - switch (ntohs (m->type)) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer %s has excess bandwidth available\n", + GNUNET_i2s (pid)); + kx->has_excess_bandwidth = GNUNET_YES; + GSC_SESSIONS_solicit (pid); +} + + +/** + * Setup the message that links the ephemeral key to our persistent + * public key and generate the appropriate signature. + */ +static void +sign_ephemeral_key () +{ + current_ekm.header.size = htons (sizeof (struct EphemeralKeyMessage)); + current_ekm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY); + current_ekm.sender_status = 0; /* to be set later */ + current_ekm.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_SET_ECC_KEY); + current_ekm.purpose.size = htonl (sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) + + sizeof (struct GNUNET_TIME_AbsoluteNBO) + + sizeof (struct GNUNET_TIME_AbsoluteNBO) + + sizeof (struct GNUNET_CRYPTO_EcdhePublicKey) + + sizeof (struct GNUNET_PeerIdentity)); + current_ekm.creation_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (GSC_cfg, + "core", + "USE_EPHEMERAL_KEYS")) { - case GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP: - case GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP: - GSC_SESSIONS_set_typemap (dmc->peer, m); - return; - default: - GSC_CLIENTS_deliver_message (dmc->peer, dmc->atsi, dmc->atsi_count, m, - ntohs (m->size), - GNUNET_CORE_OPTION_SEND_FULL_INBOUND); - GSC_CLIENTS_deliver_message (dmc->peer, dmc->atsi, dmc->atsi_count, m, - sizeof (struct GNUNET_MessageHeader), - GNUNET_CORE_OPTION_SEND_HDR_INBOUND); + current_ekm.expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_add (REKEY_FREQUENCY, + REKEY_TOLERANCE))); + } + else + { + current_ekm.expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_UNIT_FOREVER_ABS); } + GNUNET_CRYPTO_ecdhe_key_get_public (my_ephemeral_key, + ¤t_ekm.ephemeral_key); + current_ekm.origin_identity = GSC_my_identity; + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_eddsa_sign (my_private_key, + ¤t_ekm.purpose, + ¤t_ekm.signature)); } /** - * Initialize KX subsystem. + * Task run to trigger rekeying. * - * @return GNUNET_OK on success, GNUNET_SYSERR on failure + * @param cls closure, NULL */ -int -GSC_KX_init () +static void +do_rekey (void *cls) { - char *keyfile; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (GSC_cfg, "GNUNETD", "HOSTKEY", - &keyfile)) + struct GSC_KeyExchangeInfo *pos; + + rekey_task = GNUNET_SCHEDULER_add_delayed (REKEY_FREQUENCY, + &do_rekey, + NULL); + if (NULL != my_ephemeral_key) + GNUNET_free (my_ephemeral_key); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Rekeying\n"); + my_ephemeral_key = GNUNET_CRYPTO_ecdhe_key_create (); + GNUNET_assert (NULL != my_ephemeral_key); + sign_ephemeral_key (); + for (pos = kx_head; NULL != pos; pos = pos->next) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ - ("Core service is lacking HOSTKEY configuration setting. Exiting.\n")); - return GNUNET_SYSERR; + if (GNUNET_CORE_KX_STATE_UP == pos->status) + { + pos->status = GNUNET_CORE_KX_STATE_REKEY_SENT; + monitor_notify_all (pos); + derive_session_keys (pos); + } + if (GNUNET_CORE_KX_STATE_DOWN == pos->status) + { + pos->status = GNUNET_CORE_KX_STATE_KEY_SENT; + monitor_notify_all (pos); + } + monitor_notify_all (pos); + send_key (pos); } - my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); - GNUNET_free (keyfile); - if (my_private_key == NULL) +} + + +/** + * Initialize KX subsystem. + * + * @param pk private key to use for the peer + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure + */ +int +GSC_KX_init (struct GNUNET_CRYPTO_EddsaPrivateKey *pk) +{ + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_fixed_size (ephemeral_key, + GNUNET_MESSAGE_TYPE_CORE_EPHEMERAL_KEY, + struct EphemeralKeyMessage, + NULL), + GNUNET_MQ_hd_fixed_size (ping, + GNUNET_MESSAGE_TYPE_CORE_PING, + struct PingMessage, + NULL), + GNUNET_MQ_hd_fixed_size (pong, + GNUNET_MESSAGE_TYPE_CORE_PONG, + struct PongMessage, + NULL), + GNUNET_MQ_hd_var_size (encrypted, + GNUNET_MESSAGE_TYPE_CORE_ENCRYPTED_MESSAGE, + struct EncryptedMessage, + NULL), + GNUNET_MQ_handler_end() + }; + + my_private_key = pk; + GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, + &GSC_my_identity.public_key); + my_ephemeral_key = GNUNET_CRYPTO_ecdhe_key_create (); + if (NULL == my_ephemeral_key) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Core service could not access hostkey. Exiting.\n")); + GNUNET_break (0); + GNUNET_free (my_private_key); + my_private_key = NULL; return GNUNET_SYSERR; } - GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key); - GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), - &GSC_my_identity.hashPubKey); - peerinfo = GNUNET_PEERINFO_connect (GSC_cfg); - if (NULL == peerinfo) + sign_ephemeral_key (); + nc = GNUNET_notification_context_create (1); + rekey_task = GNUNET_SCHEDULER_add_delayed (REKEY_FREQUENCY, + &do_rekey, + NULL); + transport + = GNUNET_TRANSPORT_core_connect (GSC_cfg, + &GSC_my_identity, + handlers, + NULL, + &handle_transport_notify_connect, + &handle_transport_notify_disconnect, + &handle_transport_notify_excess_bw); + if (NULL == transport) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Could not access PEERINFO service. Exiting.\n")); - GNUNET_CRYPTO_rsa_key_free (my_private_key); - my_private_key = NULL; + GSC_KX_done (); return GNUNET_SYSERR; } - mst = GNUNET_SERVER_mst_create (&deliver_message, NULL); return GNUNET_OK; } @@ -1668,21 +1831,97 @@ GSC_KX_init () void GSC_KX_done () { - if (my_private_key != NULL) + if (NULL != transport) { - GNUNET_CRYPTO_rsa_key_free (my_private_key); + GNUNET_TRANSPORT_core_disconnect (transport); + transport = NULL; + } + if (NULL != rekey_task) + { + GNUNET_SCHEDULER_cancel (rekey_task); + rekey_task = NULL; + } + if (NULL != my_ephemeral_key) + { + GNUNET_free (my_ephemeral_key); + my_ephemeral_key = NULL; + } + if (NULL != my_private_key) + { + GNUNET_free (my_private_key); my_private_key = NULL; } - if (peerinfo != NULL) + if (NULL != nc) { - GNUNET_PEERINFO_disconnect (peerinfo); - peerinfo = NULL; + GNUNET_notification_context_destroy (nc); + nc = NULL; } - if (mst != NULL) +} + + + /** + * Check how many messages are queued for the given neighbour. + * + * @param kxinfo data about neighbour to check + * @return number of items in the message queue + */ +unsigned int +GSC_NEIGHBOURS_get_queue_length (const struct GSC_KeyExchangeInfo *kxinfo) +{ + return GNUNET_MQ_get_length (kxinfo->mq); +} + + +/** + * Check if the given neighbour has excess bandwidth available. + * + * @param target neighbour to check + * @return #GNUNET_YES if excess bandwidth is available, #GNUNET_NO if not + */ +int +GSC_NEIGHBOURS_check_excess_bandwidth (const struct GSC_KeyExchangeInfo *kxinfo) +{ + return kxinfo->has_excess_bandwidth; +} + + +/** + * 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. + * + * @param mq message queue to add for monitoring + */ +void +GSC_KX_handle_client_monitor_peers (struct GNUNET_MQ_Handle *mq) +{ + struct GNUNET_MQ_Envelope *env; + struct MonitorNotifyMessage *done_msg; + struct GSC_KeyExchangeInfo *kx; + + GNUNET_notification_context_add (nc, + mq); + for (kx = kx_head; NULL != kx; kx = kx->next) { - GNUNET_SERVER_mst_destroy (mst); - mst = NULL; + struct GNUNET_MQ_Envelope *env; + struct MonitorNotifyMessage *msg; + + env = GNUNET_MQ_msg (msg, + GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY); + msg->state = htonl ((uint32_t) kx->status); + msg->peer = *kx->peer; + msg->timeout = GNUNET_TIME_absolute_hton (kx->timeout); + GNUNET_MQ_send (mq, + env); } + env = GNUNET_MQ_msg (done_msg, + GNUNET_MESSAGE_TYPE_CORE_MONITOR_NOTIFY); + done_msg->state = htonl ((uint32_t) GNUNET_CORE_KX_ITERATION_FINISHED); + done_msg->timeout = GNUNET_TIME_absolute_hton (GNUNET_TIME_UNIT_FOREVER_ABS); + GNUNET_MQ_send (mq, + env); } + /* end of gnunet-service-core_kx.c */