#include "silcincludes.h"
#include "silcprivate.h"
+#include "silcprivate_i.h"
/******************************************************************************
******************************************************************************/
-/* Private Message Payload structure. Contents of this structure is parsed
- from SILC packets. */
-struct SilcPrivateMessagePayloadStruct {
- uint16 flags;
- uint16 message_len;
- unsigned char *message;
-};
-
/* Parses private message payload returning new private mesage payload
structure. This also decrypts the message if the `cipher' is provided. */
SilcPrivateMessagePayload
-silc_private_message_payload_parse(SilcBuffer buffer, SilcCipher cipher)
+silc_private_message_payload_parse(unsigned char *payload,
+ SilcUInt32 payload_len,
+ SilcCipher cipher,
+ SilcHmac hmac)
{
- SilcPrivateMessagePayload new;
- int ret;
+ SilcBufferStruct buffer;
+ SilcPrivateMessagePayload newp;
+ SilcUInt32 mac_len = 0, block_len, pad_len = 0;
+ unsigned char data[16], mac[32];
+ int len, totlen;
SILC_LOG_DEBUG(("Parsing private message payload"));
- /* Decrypt the payload */
- if (cipher)
- silc_cipher_decrypt(cipher, buffer->data, buffer->data,
- buffer->len, cipher->iv);
+ silc_buffer_set(&buffer, payload, payload_len);
- new = silc_calloc(1, sizeof(*new));
+ newp = silc_calloc(1, sizeof(*newp));
+ if (!newp)
+ return NULL;
- /* Parse the Private Message Payload. Ignore the padding. */
- ret = silc_buffer_unformat(buffer,
- SILC_STR_UI_SHORT(&new->flags),
- SILC_STR_UI16_NSTRING_ALLOC(&new->message,
- &new->message_len),
+ /* Decrypt the payload */
+ if (cipher) {
+ /* Decrypt first block. This is to get the true length of the data in
+ payload. It is possible there is additional data after the message
+ payload with private messages. */
+ block_len = silc_cipher_get_block_len(cipher);
+ if (block_len > buffer.len)
+ goto err;
+ silc_cipher_decrypt(cipher, buffer.data, data, block_len,
+ silc_cipher_get_iv(cipher));
+
+ /* Length of encrypted area */
+ SILC_GET16_MSB(newp->message_len, data + 2);
+ totlen = 4 + newp->message_len;
+ pad_len = SILC_PRIVATE_MESSAGE_PAD(4 + newp->message_len);
+ totlen += pad_len;
+
+ /* Sanity checks */
+ if (totlen > buffer.len || newp->message_len < 1 ||
+ newp->message_len > buffer.len - 4) {
+ SILC_LOG_DEBUG(("Incorrect private message payload in packet"));
+ goto err;
+ }
+
+ /* Compute MAC for integrity check from the cipher text */
+ if (hmac) {
+ SILC_LOG_DEBUG(("Checking private message MAC"));
+ silc_hmac_init(hmac);
+ silc_hmac_update(hmac, buffer.data, totlen);
+ silc_hmac_final(hmac, mac, &mac_len);
+ if (memcmp(mac, buffer.data + totlen, mac_len)) {
+ SILC_LOG_DEBUG(("Private message MAC does not match"));
+ goto err;
+ }
+ SILC_LOG_DEBUG(("MAC is Ok"));
+ }
+
+ /* Now decrypt rest of the data */
+ memcpy(buffer.data, data, block_len);
+ if (totlen - block_len > 0)
+ silc_cipher_decrypt(cipher, buffer.data + block_len,
+ buffer.data + block_len, totlen - block_len,
+ silc_cipher_get_iv(cipher));
+ memset(data, 0, sizeof(data));
+ }
+
+ /* Parse the Private Message Payload. */
+ len = silc_buffer_unformat(&buffer,
+ SILC_STR_UI_SHORT(&newp->flags),
+ SILC_STR_UI16_NSTRING_ALLOC(&newp->message,
+ &newp->message_len),
SILC_STR_END);
- if (ret == -1) {
- SILC_LOG_ERROR(("Incorrect private message payload"));
+ if (len == -1 || newp->message_len < 1 ||
+ newp->message_len > buffer.len - 4) {
+ SILC_LOG_DEBUG(("Incorrect private message payload in packet"));
goto err;
}
- if ((new->message_len < 1 || new->message_len > buffer->len)) {
- SILC_LOG_ERROR(("Incorrect private message payload in packet, "
- "packet dropped"));
- goto err;
+ /* Parse also padding and MAC */
+ if (cipher) {
+ silc_buffer_pull(&buffer, 4 + newp->message_len);
+ len = silc_buffer_unformat(&buffer,
+ SILC_STR_UI_XNSTRING_ALLOC(&newp->pad,
+ pad_len),
+ SILC_STR_UI_XNSTRING_ALLOC(&newp->mac,
+ mac_len),
+ SILC_STR_END);
+ silc_buffer_push(&buffer, 4 + newp->message_len);
}
- return new;
+ return newp;
err:
- silc_private_message_payload_free(new);
+ silc_private_message_payload_free(newp);
return NULL;
}
the cipher is provided the packet is also encrypted here. It is provided
if the private message private keys are used. */
-SilcBuffer silc_private_message_payload_encode(uint16 flags,
- uint16 data_len,
- unsigned char *data,
- SilcCipher cipher)
+SilcBuffer silc_private_message_payload_encode(SilcUInt16 flags,
+ SilcUInt16 data_len,
+ const unsigned char *data,
+ SilcCipher cipher,
+ SilcHmac hmac,
+ SilcRng rng)
{
int i;
SilcBuffer buffer;
- uint32 len, pad_len = 0;
- unsigned char pad[SILC_PACKET_MAX_PADLEN];
+ SilcUInt32 len, pad_len = 0, mac_len = 0;
+ unsigned char pad[16], mac[32];
SILC_LOG_DEBUG(("Encoding private message payload"));
+ data_len = SILC_PRIVATE_MESSAGE_DATALEN(data_len);
len = 4 + data_len;
if (cipher) {
/* Calculate length of padding. */
- pad_len = SILC_PACKET_PADLEN((len + 2));
+ pad_len = SILC_PRIVATE_MESSAGE_PAD(len);
len += pad_len;
+ mac_len = hmac ? silc_hmac_len(hmac) : 0;
+ len += mac_len;
/* Generate padding */
- for (i = 0; i < pad_len; i++) pad[i] = silc_rng_global_get_byte();
+ if (rng) {
+ for (i = 0; i < pad_len; i++) pad[i] = silc_rng_get_byte_fast(rng);
+ } else {
+ for (i = 0; i < pad_len; i++) pad[i] = silc_rng_global_get_byte_fast();
+ }
}
/* Allocate private message payload buffer */
- buffer = silc_buffer_alloc(len);
+ buffer = silc_buffer_alloc_size(len);
+ if (!buffer)
+ return NULL;
/* Encode the Channel Message Payload */
- silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
silc_buffer_format(buffer,
SILC_STR_UI_SHORT(flags),
SILC_STR_UI_SHORT(data_len),
if (cipher) {
/* Encrypt payload of the packet. */
silc_cipher_encrypt(cipher, buffer->data, buffer->data,
- buffer->len, cipher->iv);
+ buffer->len - mac_len, silc_cipher_get_iv(cipher));
memset(pad, 0, sizeof(pad));
+
+ /* Compute MAC from the ciphertext */
+ if (hmac) {
+ silc_hmac_init(hmac);
+ silc_hmac_update(hmac, buffer->data, buffer->len - mac_len);
+ silc_hmac_final(hmac, mac, &mac_len);
+ memcpy(buffer->data + (buffer->len - mac_len), mac, mac_len);
+ memset(mac, 0, sizeof(mac));
+ }
}
return buffer;
/* Return flags */
-uint16
+SilcUInt16
silc_private_message_get_flags(SilcPrivateMessagePayload payload)
{
return payload->flags;
unsigned char *
silc_private_message_get_message(SilcPrivateMessagePayload payload,
- uint32 *message_len)
+ SilcUInt32 *message_len)
{
if (message_len)
*message_len = payload->message_len;
return payload->message;
}
+
+/* Return MAC. Caller knows its length */
+
+unsigned char *
+silc_private_message_get_mac(SilcPrivateMessagePayload payload)
+{
+ return payload->mac;
+}