/* This file is part of GNUnet. Copyright (C) 2020--2024 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . SPDX-License-Identifier: AGPL3.0-or-later */ /** * @author Tobias Frisch * @file src/messenger/messenger_api_message.c * @brief messenger api: client and service implementation of GNUnet MESSENGER service */ #include "messenger_api_message.h" #include "gnunet_common.h" #include "gnunet_messenger_service.h" #include "gnunet_signatures.h" #include struct GNUNET_MESSENGER_MessageSignature { struct GNUNET_CRYPTO_EccSignaturePurpose purpose; struct GNUNET_HashCode hash; }; struct GNUNET_MESSENGER_ShortMessage { enum GNUNET_MESSENGER_MessageKind kind; struct GNUNET_MESSENGER_MessageBody body; }; struct GNUNET_MESSENGER_Message* create_message (enum GNUNET_MESSENGER_MessageKind kind) { struct GNUNET_MESSENGER_Message *message = GNUNET_new (struct GNUNET_MESSENGER_Message); message->header.kind = kind; switch (message->header.kind) { case GNUNET_MESSENGER_KIND_NAME: message->body.name.name = NULL; break; case GNUNET_MESSENGER_KIND_TEXT: message->body.text.text = NULL; break; case GNUNET_MESSENGER_KIND_FILE: message->body.file.uri = NULL; break; case GNUNET_MESSENGER_KIND_PRIVATE: message->body.privacy.length = 0; message->body.privacy.data = NULL; break; case GNUNET_MESSENGER_KIND_TRANSCRIPT: message->body.transcript.length = 0; message->body.transcript.data = NULL; break; case GNUNET_MESSENGER_KIND_TAG: message->body.tag.tag = NULL; break; default: break; } return message; } struct GNUNET_MESSENGER_Message* copy_message (const struct GNUNET_MESSENGER_Message *message) { GNUNET_assert (message); struct GNUNET_MESSENGER_Message *copy = GNUNET_new (struct GNUNET_MESSENGER_Message); GNUNET_memcpy (copy, message, sizeof(struct GNUNET_MESSENGER_Message)); switch (message->header.kind) { case GNUNET_MESSENGER_KIND_NAME: copy->body.name.name = GNUNET_strdup (message->body.name.name); break; case GNUNET_MESSENGER_KIND_TEXT: copy->body.text.text = GNUNET_strdup (message->body.text.text); break; case GNUNET_MESSENGER_KIND_FILE: copy->body.file.uri = GNUNET_strdup (message->body.file.uri); break; case GNUNET_MESSENGER_KIND_PRIVATE: copy->body.privacy.data = copy->body.privacy.length ? GNUNET_malloc ( copy->body.privacy.length) : NULL; if (copy->body.privacy.data) GNUNET_memcpy (copy->body.privacy.data, message->body.privacy.data, copy->body.privacy.length); break; case GNUNET_MESSENGER_KIND_TRANSCRIPT: copy->body.transcript.data = copy->body.transcript.length ? GNUNET_malloc ( copy->body.transcript.length) : NULL; if (copy->body.transcript.data) GNUNET_memcpy (copy->body.transcript.data, message->body.transcript.data, copy->body.transcript.length); break; case GNUNET_MESSENGER_KIND_TAG: copy->body.tag.tag = GNUNET_strdup (message->body.tag.tag); break; default: break; } return copy; } static void destroy_message_body (enum GNUNET_MESSENGER_MessageKind kind, struct GNUNET_MESSENGER_MessageBody *body) { switch (kind) { case GNUNET_MESSENGER_KIND_NAME: if (body->name.name) GNUNET_free (body->name.name); break; case GNUNET_MESSENGER_KIND_TEXT: if (body->text.text) GNUNET_free (body->text.text); break; case GNUNET_MESSENGER_KIND_FILE: GNUNET_free (body->file.uri); break; case GNUNET_MESSENGER_KIND_PRIVATE: GNUNET_free (body->privacy.data); break; case GNUNET_MESSENGER_KIND_TRANSCRIPT: GNUNET_free (body->transcript.data); break; case GNUNET_MESSENGER_KIND_TAG: if (body->tag.tag) GNUNET_free (body->tag.tag); break; default: break; } } void cleanup_message (struct GNUNET_MESSENGER_Message *message) { GNUNET_assert (message); destroy_message_body (message->header.kind, &(message->body)); } void destroy_message (struct GNUNET_MESSENGER_Message *message) { GNUNET_assert (message); destroy_message_body (message->header.kind, &(message->body)); GNUNET_free (message); } enum GNUNET_GenericReturnValue is_message_session_bound (const struct GNUNET_MESSENGER_Message *message) { GNUNET_assert (message); if ((GNUNET_MESSENGER_KIND_JOIN == message->header.kind) || (GNUNET_MESSENGER_KIND_LEAVE == message->header.kind) || (GNUNET_MESSENGER_KIND_NAME == message->header.kind) || (GNUNET_MESSENGER_KIND_KEY == message->header.kind) || (GNUNET_MESSENGER_KIND_ID == message->header.kind)) return GNUNET_YES; else return GNUNET_NO; } static void fold_short_message (const struct GNUNET_MESSENGER_Message *message, struct GNUNET_MESSENGER_ShortMessage *shortened) { shortened->kind = message->header.kind; GNUNET_memcpy (&(shortened->body), &(message->body), sizeof(struct GNUNET_MESSENGER_MessageBody)); } static void unfold_short_message (struct GNUNET_MESSENGER_ShortMessage *shortened, struct GNUNET_MESSENGER_Message *message) { destroy_message_body (message->header.kind, &(message->body)); message->header.kind = shortened->kind; GNUNET_memcpy (&(message->body), &(shortened->body), sizeof(struct GNUNET_MESSENGER_MessageBody)); } #define member_size(type, member) sizeof(((type*) NULL)->member) static uint16_t get_message_body_kind_size (enum GNUNET_MESSENGER_MessageKind kind) { uint16_t length = 0; switch (kind) { case GNUNET_MESSENGER_KIND_INFO: length += member_size (struct GNUNET_MESSENGER_Message, body.info.messenger_version); break; case GNUNET_MESSENGER_KIND_PEER: length += member_size (struct GNUNET_MESSENGER_Message, body.peer.peer); break; case GNUNET_MESSENGER_KIND_ID: length += member_size (struct GNUNET_MESSENGER_Message, body.id.id); break; case GNUNET_MESSENGER_KIND_MISS: length += member_size (struct GNUNET_MESSENGER_Message, body.miss.peer); break; case GNUNET_MESSENGER_KIND_MERGE: length += member_size (struct GNUNET_MESSENGER_Message, body.merge.previous); break; case GNUNET_MESSENGER_KIND_REQUEST: length += member_size (struct GNUNET_MESSENGER_Message, body.request.hash); break; case GNUNET_MESSENGER_KIND_INVITE: length += member_size (struct GNUNET_MESSENGER_Message, body.invite.door); length += member_size (struct GNUNET_MESSENGER_Message, body.invite.key); break; case GNUNET_MESSENGER_KIND_FILE: length += member_size (struct GNUNET_MESSENGER_Message, body.file.key); length += member_size (struct GNUNET_MESSENGER_Message, body.file.hash); length += member_size (struct GNUNET_MESSENGER_Message, body.file.name); break; case GNUNET_MESSENGER_KIND_PRIVATE: length += member_size (struct GNUNET_MESSENGER_Message, body.privacy.key); break; case GNUNET_MESSENGER_KIND_DELETE: length += member_size (struct GNUNET_MESSENGER_Message, body.deletion.hash); length += member_size (struct GNUNET_MESSENGER_Message, body.deletion.delay); break; case GNUNET_MESSENGER_KIND_CONNECTION: length += member_size (struct GNUNET_MESSENGER_Message, body.connection.amount); length += member_size (struct GNUNET_MESSENGER_Message, body.connection.flags); break; case GNUNET_MESSENGER_KIND_TICKET: length += member_size (struct GNUNET_MESSENGER_Message, body.ticket.identifier); break; case GNUNET_MESSENGER_KIND_TRANSCRIPT: length += member_size (struct GNUNET_MESSENGER_Message, body.transcript.hash); break; case GNUNET_MESSENGER_KIND_TAG: length += member_size (struct GNUNET_MESSENGER_Message, body.tag.hash); break; default: break; } return length; } typedef uint32_t kind_t; uint16_t get_message_kind_size (enum GNUNET_MESSENGER_MessageKind kind, enum GNUNET_GenericReturnValue include_header) { uint16_t length = 0; if (GNUNET_YES == include_header) { length += member_size (struct GNUNET_MESSENGER_Message, header.timestamp); length += member_size (struct GNUNET_MESSENGER_Message, header.sender_id); length += member_size (struct GNUNET_MESSENGER_Message, header.previous); } length += sizeof(kind_t); return length + get_message_body_kind_size (kind); } static uint16_t get_message_body_size (enum GNUNET_MESSENGER_MessageKind kind, const struct GNUNET_MESSENGER_MessageBody *body) { uint16_t length = 0; switch (kind) { case GNUNET_MESSENGER_KIND_JOIN: length += GNUNET_CRYPTO_public_key_get_length (&(body->join.key)); break; case GNUNET_MESSENGER_KIND_NAME: length += (body->name.name ? strlen (body->name.name) : 0); break; case GNUNET_MESSENGER_KIND_KEY: length += GNUNET_CRYPTO_public_key_get_length (&(body->key.key)); break; case GNUNET_MESSENGER_KIND_TEXT: length += (body->text.text ? strlen (body->text.text) : 0); break; case GNUNET_MESSENGER_KIND_FILE: length += strlen (body->file.uri); break; case GNUNET_MESSENGER_KIND_PRIVATE: length += body->privacy.length; break; case GNUNET_MESSENGER_KIND_TRANSCRIPT: length += GNUNET_CRYPTO_public_key_get_length (&(body->transcript.key)); length += body->transcript.length; break; case GNUNET_MESSENGER_KIND_TAG: length += (body->tag.tag ? strlen (body->tag.tag) : 0); break; default: break; } return length; } uint16_t get_message_size (const struct GNUNET_MESSENGER_Message *message, enum GNUNET_GenericReturnValue include_header) { GNUNET_assert (message); uint16_t length = 0; if (GNUNET_YES == include_header) length += GNUNET_CRYPTO_signature_get_length ( &(message->header.signature)); length += get_message_kind_size (message->header.kind, include_header); length += get_message_body_size (message->header.kind, &(message->body)); return length; } static uint16_t get_short_message_size (const struct GNUNET_MESSENGER_ShortMessage *message, enum GNUNET_GenericReturnValue include_body) { const uint16_t minimum_size = sizeof(struct GNUNET_HashCode) + sizeof(kind_t); if (message) return minimum_size + get_message_body_kind_size (message->kind) + (include_body == GNUNET_YES? get_message_body_size (message->kind, &(message->body)) : 0); else return minimum_size; } static uint16_t calc_usual_padding () { uint16_t padding = 0; uint16_t kind_size; for (unsigned int i = 0; i <= GNUNET_MESSENGER_KIND_MAX; i++) { kind_size = get_message_kind_size ((enum GNUNET_MESSENGER_MessageKind) i, GNUNET_YES); if (kind_size > padding) padding = kind_size; } return padding + GNUNET_MESSENGER_PADDING_MIN; } #define max(x, y) (x > y? x : y) static uint16_t calc_padded_length (uint16_t length) { static uint16_t usual_padding = 0; if (! usual_padding) usual_padding = calc_usual_padding (); const uint16_t padded_length = max ( length + GNUNET_MESSENGER_PADDING_MIN, usual_padding ); if (padded_length <= GNUNET_MESSENGER_PADDING_LEVEL0) return GNUNET_MESSENGER_PADDING_LEVEL0; if (padded_length <= GNUNET_MESSENGER_PADDING_LEVEL1) return GNUNET_MESSENGER_PADDING_LEVEL1; if (padded_length <= GNUNET_MESSENGER_PADDING_LEVEL2) return GNUNET_MESSENGER_PADDING_LEVEL2; return GNUNET_MESSENGER_MAX_MESSAGE_SIZE; } #define min(x, y) (x < y? x : y) #define encode_step_ext(dst, offset, src, size) do { \ GNUNET_memcpy (dst + offset, src, size); \ offset += size; \ } while (0) #define encode_step(dst, offset, src) do { \ encode_step_ext (dst, offset, src, sizeof(*src)); \ } while (0) #define encode_step_key(dst, offset, src, length) do { \ ssize_t result = GNUNET_CRYPTO_write_public_key_to_buffer ( \ src, dst + offset, length - offset \ ); \ if (result < 0) \ GNUNET_break (0); \ else \ offset += result; \ } while (0) #define encode_step_signature(dst, offset, src, length) do { \ ssize_t result = GNUNET_CRYPTO_write_signature_to_buffer ( \ src, dst + offset, length - offset \ ); \ if (result < 0) \ GNUNET_break (0); \ else \ offset += result; \ } while (0) static void encode_message_body (enum GNUNET_MESSENGER_MessageKind kind, const struct GNUNET_MESSENGER_MessageBody *body, uint16_t length, char *buffer, uint16_t offset) { uint32_t value0, value1; switch (kind) { case GNUNET_MESSENGER_KIND_INFO: value0 = GNUNET_htobe32 (body->info.messenger_version); encode_step (buffer, offset, &value0); break; case GNUNET_MESSENGER_KIND_JOIN: encode_step_key (buffer, offset, &(body->join.key), length); break; case GNUNET_MESSENGER_KIND_NAME: if (body->name.name) encode_step_ext (buffer, offset, body->name.name, min (length - offset, strlen ( body->name.name))); break; case GNUNET_MESSENGER_KIND_KEY: encode_step_key (buffer, offset, &(body->key.key), length); break; case GNUNET_MESSENGER_KIND_PEER: encode_step (buffer, offset, &(body->peer.peer)); break; case GNUNET_MESSENGER_KIND_ID: encode_step (buffer, offset, &(body->id.id)); break; case GNUNET_MESSENGER_KIND_MISS: encode_step (buffer, offset, &(body->miss.peer)); break; case GNUNET_MESSENGER_KIND_MERGE: encode_step (buffer, offset, &(body->merge.previous)); break; case GNUNET_MESSENGER_KIND_REQUEST: encode_step (buffer, offset, &(body->request.hash)); break; case GNUNET_MESSENGER_KIND_INVITE: encode_step (buffer, offset, &(body->invite.door)); encode_step (buffer, offset, &(body->invite.key)); break; case GNUNET_MESSENGER_KIND_TEXT: if (body->text.text) encode_step_ext (buffer, offset, body->text.text, min (length - offset, strlen ( body->text.text))); break; case GNUNET_MESSENGER_KIND_FILE: encode_step (buffer, offset, &(body->file.key)); encode_step (buffer, offset, &(body->file.hash)); encode_step_ext (buffer, offset, body->file.name, sizeof(body->file.name)); encode_step_ext (buffer, offset, body->file.uri, min (length - offset, strlen ( body->file.uri))); break; case GNUNET_MESSENGER_KIND_PRIVATE: encode_step (buffer, offset, &(body->privacy.key)); encode_step_ext (buffer, offset, body->privacy.data, min (length - offset, body->privacy. length)); break; case GNUNET_MESSENGER_KIND_DELETE: encode_step (buffer, offset, &(body->deletion.hash)); encode_step (buffer, offset, &(body->deletion.delay)); break; case GNUNET_MESSENGER_KIND_CONNECTION: value0 = GNUNET_htobe32 (body->connection.amount); value1 = GNUNET_htobe32 (body->connection.flags); encode_step (buffer, offset, &value0); encode_step (buffer, offset, &value1); break; case GNUNET_MESSENGER_KIND_TICKET: encode_step (buffer, offset, &(body->ticket.identifier)); break; case GNUNET_MESSENGER_KIND_TRANSCRIPT: encode_step (buffer, offset, &(body->transcript.hash)); encode_step_key (buffer, offset, &(body->transcript.key), length); encode_step_ext (buffer, offset, body->transcript.data, min (length - offset, body->transcript. length)); break; case GNUNET_MESSENGER_KIND_TAG: encode_step (buffer, offset, &(body->tag.hash)); if (body->tag.tag) encode_step_ext (buffer, offset, body->tag.tag, min (length - offset, strlen ( body->tag.tag))); break; default: break; } if (offset >= length) return; const uint16_t padding = length - offset; const uint16_t used_padding = sizeof(padding) + sizeof(char); GNUNET_assert (padding >= used_padding); buffer[offset++] = '\0'; if (padding > used_padding) GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, buffer + offset, padding - used_padding); GNUNET_memcpy (buffer + length - sizeof(padding), &padding, sizeof(padding)); } void encode_message (const struct GNUNET_MESSENGER_Message *message, uint16_t length, char *buffer, enum GNUNET_GenericReturnValue include_header) { GNUNET_assert ((message) && (buffer)); uint16_t offset = 0; if (GNUNET_YES == include_header) encode_step_signature (buffer, offset, &(message->header.signature), length); const kind_t kind = GNUNET_htobe32 ((kind_t) message->header.kind); if (GNUNET_YES == include_header) { encode_step (buffer, offset, &(message->header.timestamp)); encode_step (buffer, offset, &(message->header.sender_id)); encode_step (buffer, offset, &(message->header.previous)); } encode_step (buffer, offset, &kind); encode_message_body (message->header.kind, &(message->body), length, buffer, offset); } static void encode_short_message (const struct GNUNET_MESSENGER_ShortMessage *message, uint16_t length, char *buffer) { struct GNUNET_HashCode hash; uint16_t offset = sizeof(hash); const kind_t kind = GNUNET_htobe32 ((kind_t) message->kind); encode_step (buffer, offset, &kind); encode_message_body (message->kind, &(message->body), length, buffer, offset); GNUNET_CRYPTO_hash ( buffer + sizeof(hash), length - sizeof(hash), &hash ); GNUNET_memcpy (buffer, &hash, sizeof(hash)); } #define decode_step_ext(src, offset, dst, size) do { \ GNUNET_memcpy (dst, src + offset, size); \ offset += size; \ } while (0) #define decode_step(src, offset, dst) do { \ decode_step_ext (src, offset, dst, sizeof(*dst)); \ } while (0) #define decode_step_malloc(src, offset, dst, size, zero) do { \ dst = GNUNET_malloc (size + zero); \ if (zero) dst[size] = 0; \ decode_step_ext (src, offset, dst, size); \ } while (0) #define decode_step_key(src, offset, dst, length) do { \ enum GNUNET_GenericReturnValue result; \ size_t read; \ result = GNUNET_CRYPTO_read_public_key_from_buffer ( \ src + offset, length - offset, dst, &read \ ); \ if (GNUNET_SYSERR == result) \ GNUNET_break (0); \ else \ offset += read; \ } while (0) static uint16_t decode_message_body (enum GNUNET_MESSENGER_MessageKind *kind, struct GNUNET_MESSENGER_MessageBody *body, uint16_t length, const char *buffer, uint16_t offset) { uint16_t padding = 0; GNUNET_memcpy (&padding, buffer + length - sizeof(padding), sizeof(padding)); if (padding > length - offset) padding = 0; const uint16_t end_zero = length - padding; if ((padding) && (buffer[end_zero] != '\0')) padding = 0; length -= padding; uint32_t value0, value1; switch (*kind) { case GNUNET_MESSENGER_KIND_INFO: { decode_step (buffer, offset, &value0); body->info.messenger_version = GNUNET_be32toh (value0); break; } case GNUNET_MESSENGER_KIND_JOIN: { decode_step_key (buffer, offset, &(body->join.key), length); break; } case GNUNET_MESSENGER_KIND_NAME: if (length - offset > 0) decode_step_malloc (buffer, offset, body->name.name, length - offset, 1); else body->name.name = NULL; break; case GNUNET_MESSENGER_KIND_KEY: decode_step_key (buffer, offset, &(body->key.key), length); break; case GNUNET_MESSENGER_KIND_PEER: decode_step (buffer, offset, &(body->peer.peer)); break; case GNUNET_MESSENGER_KIND_ID: decode_step (buffer, offset, &(body->id.id)); break; case GNUNET_MESSENGER_KIND_MISS: decode_step (buffer, offset, &(body->miss.peer)); break; case GNUNET_MESSENGER_KIND_MERGE: decode_step (buffer, offset, &(body->merge.previous)); break; case GNUNET_MESSENGER_KIND_REQUEST: decode_step (buffer, offset, &(body->request.hash)); break; case GNUNET_MESSENGER_KIND_INVITE: decode_step (buffer, offset, &(body->invite.door)); decode_step (buffer, offset, &(body->invite.key)); break; case GNUNET_MESSENGER_KIND_TEXT: decode_step_malloc (buffer, offset, body->text.text, length - offset, 1); break; case GNUNET_MESSENGER_KIND_FILE: decode_step (buffer, offset, &(body->file.key)); decode_step (buffer, offset, &(body->file.hash)); decode_step_ext (buffer, offset, body->file.name, sizeof(body->file.name)); decode_step_malloc (buffer, offset, body->file.uri, length - offset, 1); break; case GNUNET_MESSENGER_KIND_PRIVATE: decode_step (buffer, offset, &(body->privacy.key)); body->privacy.length = (length - offset); decode_step_malloc (buffer, offset, body->privacy.data, length - offset, 0); break; case GNUNET_MESSENGER_KIND_DELETE: decode_step (buffer, offset, &(body->deletion.hash)); decode_step (buffer, offset, &(body->deletion.delay)); break; case GNUNET_MESSENGER_KIND_CONNECTION: decode_step (buffer, offset, &value0); decode_step (buffer, offset, &value1); body->connection.amount = GNUNET_be32toh (value0); body->connection.flags = GNUNET_be32toh (value1); break; case GNUNET_MESSENGER_KIND_TICKET: decode_step (buffer, offset, &(body->ticket.identifier)); break; case GNUNET_MESSENGER_KIND_TRANSCRIPT: decode_step (buffer, offset, &(body->transcript.hash)); decode_step_key (buffer, offset, &(body->transcript.key), length); body->transcript.length = (length - offset); decode_step_malloc (buffer, offset, body->transcript.data, length - offset, 0); break; case GNUNET_MESSENGER_KIND_TAG: decode_step (buffer, offset, &(body->tag.hash)); decode_step_malloc (buffer, offset, body->tag.tag, length - offset, 1); break; default: *kind = GNUNET_MESSENGER_KIND_UNKNOWN; break; } return padding; } enum GNUNET_GenericReturnValue decode_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, const char *buffer, enum GNUNET_GenericReturnValue include_header, uint16_t *padding) { GNUNET_assert ( (message) && (buffer) && (length >= get_message_kind_size (GNUNET_MESSENGER_KIND_UNKNOWN, include_header)) ); uint16_t offset = 0; if (GNUNET_YES == include_header) { ssize_t result = GNUNET_CRYPTO_read_signature_from_buffer ( &(message->header.signature), buffer, length - offset ); if (result < 0) return GNUNET_NO; else offset += result; } const uint16_t count = length - offset; if (count < get_message_kind_size (GNUNET_MESSENGER_KIND_UNKNOWN, include_header)) return GNUNET_NO; kind_t kind; if (GNUNET_YES == include_header) { decode_step (buffer, offset, &(message->header.timestamp)); decode_step (buffer, offset, &(message->header.sender_id)); decode_step (buffer, offset, &(message->header.previous)); } decode_step (buffer, offset, &kind); message->header.kind = (enum GNUNET_MESSENGER_MessageKind) GNUNET_be32toh ( kind); if (count < get_message_kind_size (message->header.kind, include_header)) return GNUNET_NO; const uint16_t result = decode_message_body (&(message->header.kind), &(message->body), length, buffer, offset); if (padding) *padding = result; return GNUNET_YES; } static enum GNUNET_GenericReturnValue decode_short_message (struct GNUNET_MESSENGER_ShortMessage *message, uint16_t length, const char *buffer) { struct GNUNET_HashCode expected, hash; uint16_t offset = sizeof(hash); if (length < get_short_message_size (NULL, GNUNET_NO)) return GNUNET_NO; GNUNET_memcpy (&hash, buffer, sizeof(hash)); GNUNET_CRYPTO_hash ( buffer + sizeof(hash), length - sizeof(hash), &expected ); if (0 != GNUNET_CRYPTO_hash_cmp (&hash, &expected)) return GNUNET_NO; kind_t kind; decode_step (buffer, offset, &kind); message->kind = (enum GNUNET_MESSENGER_MessageKind) GNUNET_be32toh (kind); if (length < get_short_message_size (message, GNUNET_NO)) return GNUNET_NO; decode_message_body (&(message->kind), &(message->body), length, buffer, offset); if (GNUNET_MESSENGER_KIND_UNKNOWN == message->kind) return GNUNET_NO; return GNUNET_YES; } void hash_message (const struct GNUNET_MESSENGER_Message *message, uint16_t length, const char *buffer, struct GNUNET_HashCode *hash) { GNUNET_assert ((message) && (buffer) && (hash)); const ssize_t offset = GNUNET_CRYPTO_signature_get_length ( &(message->header.signature) ); GNUNET_CRYPTO_hash (buffer + offset, length - offset, hash); } void sign_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, char *buffer, const struct GNUNET_HashCode *hash, const struct GNUNET_CRYPTO_PrivateKey *key) { GNUNET_assert ((message) && (buffer) && (hash) && (key)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sign message by member: %s\n", GNUNET_h2s (hash)); struct GNUNET_MESSENGER_MessageSignature signature; signature.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE); signature.purpose.size = htonl (sizeof(signature)); GNUNET_memcpy (&(signature.hash), hash, sizeof(struct GNUNET_HashCode)); GNUNET_CRYPTO_sign (key, &signature, &(message->header.signature)); message->header.signature.type = key->type; uint16_t offset = 0; encode_step_signature (buffer, offset, &(message->header.signature), length); } void sign_message_by_peer (struct GNUNET_MESSENGER_Message *message, uint16_t length, char *buffer, const struct GNUNET_HashCode *hash, const struct GNUNET_CONFIGURATION_Handle *cfg) { GNUNET_assert ((message) && (buffer) && (hash) && (cfg)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sign message by peer: %s\n", GNUNET_h2s (hash)); struct GNUNET_MESSENGER_MessageSignature signature; signature.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE); signature.purpose.size = htonl (sizeof(signature)); GNUNET_memcpy (&(signature.hash), hash, sizeof(struct GNUNET_HashCode)); GNUNET_CRYPTO_sign_by_peer_identity (cfg, &signature.purpose, &(message->header.signature. eddsa_signature)); message->header.signature.type = htonl (GNUNET_PUBLIC_KEY_TYPE_EDDSA); uint16_t offset = 0; encode_step_signature (buffer, offset, &(message->header.signature), length); } enum GNUNET_GenericReturnValue verify_message (const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash, const struct GNUNET_CRYPTO_PublicKey *key) { GNUNET_assert ((message) && (hash) && (key)); if (key->type != message->header.signature.type) return GNUNET_SYSERR; struct GNUNET_MESSENGER_MessageSignature signature; signature.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE); signature.purpose.size = htonl (sizeof(signature)); GNUNET_memcpy (&(signature.hash), hash, sizeof(struct GNUNET_HashCode)); return GNUNET_CRYPTO_signature_verify ( GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE, &signature, &(message->header.signature), key); } enum GNUNET_GenericReturnValue verify_message_by_peer (const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash, const struct GNUNET_PeerIdentity *identity) { GNUNET_assert ((message) && (hash) && (identity)); if (ntohl (GNUNET_PUBLIC_KEY_TYPE_EDDSA) != message->header.signature.type) return GNUNET_SYSERR; struct GNUNET_MESSENGER_MessageSignature signature; signature.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE); signature.purpose.size = htonl (sizeof(signature)); GNUNET_memcpy (&(signature.hash), hash, sizeof(struct GNUNET_HashCode)); return GNUNET_CRYPTO_verify_peer_identity ( GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE, &signature.purpose, &(message->header.signature. eddsa_signature), identity); } enum GNUNET_GenericReturnValue encrypt_message (struct GNUNET_MESSENGER_Message *message, const struct GNUNET_CRYPTO_PublicKey *key) { GNUNET_assert ((message) && (key)); if (GNUNET_YES == is_service_message (message)) return GNUNET_NO; struct GNUNET_MESSENGER_ShortMessage shortened; fold_short_message (message, &shortened); const uint16_t length = get_short_message_size (&shortened, GNUNET_YES); const uint16_t padded_length = calc_padded_length ( length + GNUNET_CRYPTO_ENCRYPT_OVERHEAD_BYTES ); message->header.kind = GNUNET_MESSENGER_KIND_PRIVATE; message->body.privacy.data = GNUNET_malloc (padded_length); message->body.privacy.length = padded_length; const uint16_t encoded_length = ( padded_length - GNUNET_CRYPTO_ENCRYPT_OVERHEAD_BYTES ); encode_short_message (&shortened, encoded_length, message->body.privacy.data); if (GNUNET_OK != GNUNET_CRYPTO_encrypt (message->body.privacy.data, encoded_length, key, message->body.privacy.data, padded_length)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Encrypting message failed!\n"); unfold_short_message (&shortened, message); return GNUNET_NO; } destroy_message_body (shortened.kind, &(shortened.body)); return GNUNET_YES; } enum GNUNET_GenericReturnValue decrypt_message (struct GNUNET_MESSENGER_Message *message, const struct GNUNET_CRYPTO_PrivateKey *key) { GNUNET_assert ((message) && (key)); const uint16_t padded_length = message->body.privacy.length; if (padded_length < GNUNET_CRYPTO_ENCRYPT_OVERHEAD_BYTES) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Message length too short to decrypt!\n"); return GNUNET_NO; } const uint16_t encoded_length = ( padded_length - GNUNET_CRYPTO_ENCRYPT_OVERHEAD_BYTES ); if (GNUNET_OK != GNUNET_CRYPTO_decrypt (message->body.privacy.data, padded_length, key, message->body.privacy.data, encoded_length)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Decrypting message failed!\n"); return GNUNET_NO; } struct GNUNET_MESSENGER_ShortMessage shortened; if (GNUNET_YES != decode_short_message (&shortened, encoded_length, message->body.privacy.data)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Decoding decrypted message failed!\n"); return GNUNET_NO; } unfold_short_message (&shortened, message); return GNUNET_YES; } struct GNUNET_MESSENGER_Message* transcribe_message (const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_CRYPTO_PublicKey *key) { GNUNET_assert ((message) && (key)); if (GNUNET_YES == is_service_message (message)) return NULL; struct GNUNET_MESSENGER_Message *transcript = create_message( GNUNET_MESSENGER_KIND_TRANSCRIPT); if (!transcript) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Transcribing message failed!\n"); return NULL; } GNUNET_memcpy(&(transcript->body.transcript.key), key, sizeof(transcript->body.transcript.key)); struct GNUNET_MESSENGER_ShortMessage shortened; fold_short_message (message, &shortened); const uint16_t data_length = get_short_message_size ( &shortened, GNUNET_YES); transcript->body.transcript.data = GNUNET_malloc (data_length); transcript->body.transcript.length = data_length; encode_short_message (&shortened, data_length, transcript->body.transcript.data); return transcript; } struct GNUNET_MESSENGER_Message* read_transcript_message (const struct GNUNET_MESSENGER_Message *transcript) { GNUNET_assert (transcript); if (GNUNET_MESSENGER_KIND_TRANSCRIPT != transcript->header.kind) return NULL; const uint16_t data_length = transcript->body.transcript.length; struct GNUNET_MESSENGER_ShortMessage shortened; if (GNUNET_YES != decode_short_message (&shortened, data_length, transcript->body.transcript.data)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Decoding decrypted message failed!\n"); return NULL; } struct GNUNET_MESSENGER_Message *message = create_message(shortened.kind); if (!message) return NULL; unfold_short_message(&shortened, message); return message; } struct GNUNET_MQ_Envelope* pack_message (struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash, const GNUNET_MESSENGER_SignFunction sign, enum GNUNET_MESSENGER_PackMode mode, const void *cls) { GNUNET_assert (message); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Packing message kind=%u and sender: %s\n", message->header.kind, GNUNET_sh2s (&(message->header.sender_id))); struct GNUNET_MessageHeader *header; const uint16_t length = get_message_size (message, GNUNET_YES); const uint16_t padded_length = calc_padded_length (length); struct GNUNET_MQ_Envelope *env; char *buffer; if (GNUNET_MESSENGER_PACK_MODE_ENVELOPE == mode) { env = GNUNET_MQ_msg_extra (header, padded_length, GNUNET_MESSAGE_TYPE_CADET_CLI); buffer = (char*) &(header[1]); } else { env = NULL; buffer = GNUNET_malloc (padded_length); } encode_message (message, padded_length, buffer, GNUNET_YES); if (hash) { hash_message (message, length, buffer, hash); if (sign) sign (cls, message, length, buffer, hash); } if (GNUNET_MESSENGER_PACK_MODE_ENVELOPE != mode) GNUNET_free (buffer); return env; } enum GNUNET_GenericReturnValue is_peer_message (const struct GNUNET_MESSENGER_Message *message) { switch (message->header.kind) { case GNUNET_MESSENGER_KIND_INFO: case GNUNET_MESSENGER_KIND_PEER: case GNUNET_MESSENGER_KIND_MISS: case GNUNET_MESSENGER_KIND_MERGE: case GNUNET_MESSENGER_KIND_CONNECTION: return GNUNET_YES; default: return GNUNET_NO; } } enum GNUNET_GenericReturnValue is_service_message (const struct GNUNET_MESSENGER_Message *message) { if (GNUNET_YES == is_peer_message (message)) return GNUNET_YES; switch (message->header.kind) { case GNUNET_MESSENGER_KIND_INFO: return GNUNET_YES; // Reserved for connection handling only! case GNUNET_MESSENGER_KIND_JOIN: return GNUNET_YES; // Reserved for member handling only! case GNUNET_MESSENGER_KIND_LEAVE: return GNUNET_YES; // Reserved for member handling only! case GNUNET_MESSENGER_KIND_NAME: return GNUNET_YES; // Reserved for member name handling only! case GNUNET_MESSENGER_KIND_KEY: return GNUNET_YES; // Reserved for member key handling only! case GNUNET_MESSENGER_KIND_PEER: return GNUNET_YES; // Reserved for connection handling only! case GNUNET_MESSENGER_KIND_ID: return GNUNET_YES; // Reserved for member id handling only! case GNUNET_MESSENGER_KIND_MISS: return GNUNET_YES; // Reserved for connection handling only! case GNUNET_MESSENGER_KIND_MERGE: return GNUNET_YES; // Reserved for peers only! case GNUNET_MESSENGER_KIND_REQUEST: return GNUNET_YES; // Requests should not apply individually! (inefficieny) case GNUNET_MESSENGER_KIND_INVITE: return GNUNET_NO; case GNUNET_MESSENGER_KIND_TEXT: return GNUNET_NO; case GNUNET_MESSENGER_KIND_FILE: return GNUNET_NO; case GNUNET_MESSENGER_KIND_PRIVATE: return GNUNET_YES; // Prevent duplicate encryption breaking all access! case GNUNET_MESSENGER_KIND_DELETE: return GNUNET_YES; // Deletion should not apply individually! (inefficieny) case GNUNET_MESSENGER_KIND_CONNECTION: return GNUNET_YES; // Reserved for connection handling only! case GNUNET_MESSENGER_KIND_TICKET: return GNUNET_NO; case GNUNET_MESSENGER_KIND_TRANSCRIPT: return GNUNET_NO; case GNUNET_MESSENGER_KIND_TAG: return GNUNET_NO; default: return GNUNET_SYSERR; } } enum GNUNET_GenericReturnValue filter_message_sending (const struct GNUNET_MESSENGER_Message *message) { if (GNUNET_YES == is_peer_message (message)) return GNUNET_SYSERR; // Requires signature of peer rather than member! switch (message->header.kind) { case GNUNET_MESSENGER_KIND_INFO: return GNUNET_SYSERR; // Reserved for connection handling only! case GNUNET_MESSENGER_KIND_JOIN: return GNUNET_NO; // Use #GNUNET_MESSENGER_enter_room(...) instead! case GNUNET_MESSENGER_KIND_LEAVE: return GNUNET_NO; // Use #GNUNET_MESSENGER_close_room(...) instead! case GNUNET_MESSENGER_KIND_NAME: return GNUNET_YES; case GNUNET_MESSENGER_KIND_KEY: return GNUNET_NO; // Use #GNUNET_MESSENGER_set_key(...) instead! case GNUNET_MESSENGER_KIND_PEER: return GNUNET_SYSERR; // Use #GNUNET_MESSENGER_open_room(...) instead! case GNUNET_MESSENGER_KIND_ID: return GNUNET_NO; // Reserved for member id handling only! case GNUNET_MESSENGER_KIND_MISS: return GNUNET_SYSERR; // Reserved for connection handling only! case GNUNET_MESSENGER_KIND_MERGE: return GNUNET_SYSERR; // Reserved for peers only! case GNUNET_MESSENGER_KIND_REQUEST: return GNUNET_NO; // Use #GNUNET_MESSENGER_get_message(...) instead! case GNUNET_MESSENGER_KIND_INVITE: return GNUNET_YES; case GNUNET_MESSENGER_KIND_TEXT: return GNUNET_YES; case GNUNET_MESSENGER_KIND_FILE: return GNUNET_YES; case GNUNET_MESSENGER_KIND_PRIVATE: return GNUNET_NO; // Use #GNUNET_MESSENGER_send_message(...) with a contact instead! case GNUNET_MESSENGER_KIND_DELETE: return GNUNET_YES; case GNUNET_MESSENGER_KIND_CONNECTION: return GNUNET_SYSERR; // Reserved for connection handling only! case GNUNET_MESSENGER_KIND_TICKET: return GNUNET_NO; // Use #GNUNET_MESSENGER_send_ticket(...) instead! case GNUNET_MESSENGER_KIND_TRANSCRIPT: return GNUNET_NO; // Use #GNUNET_MESSENGER_send_message(...) with a contact instead! case GNUNET_MESSENGER_KIND_TAG: return GNUNET_YES; default: return GNUNET_SYSERR; } }