/*
- silcmessage.c
+ silcmessage.c
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2002 Pekka Riikonen
+ Copyright (C) 1997 - 2007 Pekka Riikonen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
private messages. */
/* $Id$ */
-#include "silcincludes.h"
+#include "silc.h"
#include "silcmessage.h"
-/******************************************************************************
-
- Message Payload
-
-******************************************************************************/
+/*************************** Types and definitions **************************/
/* Calculates padding length for message payload */
#define SILC_MESSAGE_PAD(__payloadlen) (16 - ((__payloadlen) % 16))
/* Header length plus maximum padding length */
#define SILC_MESSAGE_HLEN 6 + 16
-/* Returns the data length that fits to the packet. If data length is too
- big it will be truncated to fit to the payload. */
-#define SILC_MESSAGE_DATALEN(data_len, header_len) \
- ((data_len + SILC_MESSAGE_HLEN + header_len) > \
- SILC_PACKET_MAX_LEN ? \
- data_len - ((data_len + SILC_MESSAGE_HLEN + header_len) - \
- SILC_PACKET_MAX_LEN) : data_len)
-
-/* Message Payload structure. Contents of this structure is parsed
- from SILC packets. */
-struct SilcMessagePayloadStruct {
+/* Maximum message length */
+#define SILC_MESSAGE_MAX_LEN SILC_PACKET_MAX_LEN - SILC_MESSAGE_HLEN - 16
+
+/* Payload encoding context */
+typedef struct {
+ SilcBuffer buffer;
+ SilcBuffer sign;
+ SilcStack stack;
+ SilcMessagePayloadEncoded encoded;
+ void *context;
SilcMessageFlags flags;
- SilcUInt16 data_len;
- SilcUInt16 pad_len;
- SilcUInt16 iv_len;
- unsigned char *data;
- unsigned char *pad;
+ SilcPublicKey public_key;
+ SilcPrivateKey private_key;
+ SilcRng rng;
+ SilcHash hash;
+ SilcCipher cipher;
+ SilcHmac hmac;
unsigned char *iv;
- unsigned char *mac;
- SilcMessageSignedPayload sig;
-};
+ unsigned char *pk;
+ SilcUInt16 pk_len;
+ SilcUInt16 iv_len;
+ SilcUInt16 payload_len;
+ SilcID sid;
+ SilcID rid;
+} SilcMessageEncode;
+
-/* Decrypts the Message Payload. The `data' is the actual Message Payload */
+/************************* Static utility functions *************************/
-bool silc_message_payload_decrypt(unsigned char *data,
- size_t data_len,
- bool private_message,
- bool static_key,
+static void
+silc_message_payload_encode_final(SilcBuffer buffer,
+ SilcMessageFlags flags,
SilcCipher cipher,
SilcHmac hmac,
- bool check_mac)
+ unsigned char *iv,
+ SilcUInt32 iv_len,
+ SilcUInt32 payload_len,
+ SilcID *sender_id,
+ SilcID *receiver_id,
+ SilcStack stack,
+ SilcBuffer signature,
+ SilcMessagePayloadEncoded encoded,
+ void *context);
+
+/* Returns the data length that fits to the packet. If data length is too
+ big it will be truncated to fit to the payload. */
+
+static inline
+SilcUInt32 silc_message_payload_datalen(SilcUInt32 data_len,
+ SilcUInt32 header_len,
+ SilcUInt32 flags,
+ SilcPublicKey public_key,
+ SilcPrivateKey private_key)
+{
+ SilcUInt32 pklen = (flags & SILC_MESSAGE_FLAG_SIGNED && public_key ?
+ silc_pkcs_public_key_get_len(public_key) : 0);
+ SilcUInt32 prlen = (flags & SILC_MESSAGE_FLAG_SIGNED ?
+ silc_pkcs_private_key_get_len(private_key) / 8 : 0);
+ SilcUInt32 dlen = data_len + SILC_MESSAGE_HLEN + header_len + pklen + prlen;
+
+ if (silc_unlikely(dlen > SILC_MESSAGE_MAX_LEN))
+ data_len -= (dlen - SILC_MESSAGE_MAX_LEN);
+
+ return data_len;
+}
+
+/* Free signed payload */
+
+static void silc_message_signed_payload_free(SilcMessageSignedPayload sig)
+{
+ if (sig->sign_data) {
+ memset(sig->sign_data, 0, sig->sign_len);
+ silc_free(sig->sign_data);
+ }
+ silc_free(sig->pk_data);
+}
+
+/* Parses the SILC_MESSAGE_FLAG_SIGNED Payload */
+
+static SilcBool
+silc_message_signed_payload_parse(const unsigned char *data,
+ SilcUInt32 data_len,
+ SilcMessageSignedPayload sig)
+{
+ SilcBufferStruct buffer;
+ int ret;
+
+ SILC_LOG_DEBUG(("Parsing SILC_MESSAGE_FLAG_SIGNED Payload"));
+
+ SILC_LOG_HEXDUMP(("sig payload"), (unsigned char *)data, data_len);
+
+ silc_buffer_set(&buffer, (unsigned char *)data, data_len);
+
+ /* Parse the payload */
+ ret = silc_buffer_unformat(&buffer,
+ SILC_STR_UI_SHORT(&sig->pk_len),
+ SILC_STR_UI_SHORT(&sig->pk_type),
+ SILC_STR_END);
+ if (ret == -1 || sig->pk_len > data_len - 4) {
+ SILC_LOG_DEBUG(("Malformed public key in SILC_MESSAGE_FLAG_SIGNED "
+ "Payload"));
+ return FALSE;
+ }
+
+ silc_buffer_pull(&buffer, 4);
+ ret = silc_buffer_unformat(&buffer,
+ SILC_STR_UI_XNSTRING_ALLOC(&sig->pk_data,
+ sig->pk_len),
+ SILC_STR_UI16_NSTRING_ALLOC(&sig->sign_data,
+ &sig->sign_len),
+ SILC_STR_END);
+ if (ret == -1 || sig->sign_len > silc_buffer_len(&buffer) -
+ sig->pk_len - 2) {
+ silc_message_signed_payload_free(sig);
+ SILC_LOG_DEBUG(("Malformed SILC_MESSAGE_FLAG_SIGNED Payload"));
+ return FALSE;
+ }
+ silc_buffer_push(&buffer, 4);
+
+ /* Signature must be provided */
+ if (sig->sign_len < 1) {
+ SILC_LOG_DEBUG(("Malformed signature in SILC_MESSAGE_SIGNED_PAYLOAD "
+ "Payload"));
+ silc_message_signed_payload_free(sig);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Encodes the data to be signed to SILC_MESSAGE_FLAG_SIGNED Payload */
+
+static SilcBuffer
+silc_message_signed_encode_data(SilcStack stack,
+ const unsigned char *message_payload,
+ SilcUInt32 message_payload_len,
+ unsigned char *pk,
+ SilcUInt32 pk_len, SilcUInt32 pk_type)
+{
+ SilcBuffer sign;
+
+ sign = silc_buffer_salloc_size(stack, message_payload_len + 4 + pk_len);
+ if (!sign)
+ return NULL;
+
+ silc_buffer_sformat(stack, sign,
+ SILC_STR_DATA(message_payload, message_payload_len),
+ SILC_STR_UI_SHORT(pk_len),
+ SILC_STR_UI_SHORT(pk_type),
+ SILC_STR_END);
+
+ if (pk && pk_len) {
+ silc_buffer_pull(sign, message_payload_len + 4);
+ silc_buffer_sformat(stack, sign,
+ SILC_STR_UI_XNSTRING(pk, pk_len),
+ SILC_STR_END);
+ silc_buffer_push(sign, message_payload_len + 4);
+ }
+
+ return sign;
+}
+
+/* Signature callback */
+
+void silc_message_signed_payload_encode_cb(SilcBool success,
+ const unsigned char *signature,
+ SilcUInt32 signature_len,
+ void *context)
+{
+ SilcMessageEncode *e = context;
+ SilcPKCSType pk_type;
+ SilcStack stack = e->stack;
+ SilcBuffer buffer, payload;
+ SilcMessageFlags flags;
+ SilcCipher cipher;
+ SilcHmac hmac;
+ unsigned char *iv;
+ SilcUInt32 iv_len, payload_len;
+ SilcID *sid, *rid;
+ SilcMessagePayloadEncoded encoded;
+ void *encoded_context;
+
+ if (!success) {
+ e->encoded(NULL, e->context);
+ silc_buffer_sfree(stack, e->sign);
+ silc_sfree(stack, e->pk);
+ silc_sfree(stack, e);
+ silc_stack_free(stack);
+ return;
+ }
+
+ pk_type = silc_pkcs_get_type(e->private_key);
+
+ /* Encode the SILC_MESSAGE_FLAG_SIGNED Payload */
+ buffer = silc_buffer_salloc_size(stack, 4 + e->pk_len + 2 + signature_len);
+ if (!buffer) {
+ e->encoded(NULL, e->context);
+ silc_buffer_sfree(stack, e->sign);
+ silc_sfree(stack, e->pk);
+ silc_sfree(stack, e);
+ silc_stack_free(stack);
+ return;
+ }
+
+ silc_buffer_sformat(stack, buffer,
+ SILC_STR_UI_SHORT(e->pk_len),
+ SILC_STR_UI_SHORT(pk_type),
+ SILC_STR_END);
+
+ if (e->pk_len && e->pk) {
+ silc_buffer_pull(buffer, 4);
+ silc_buffer_sformat(stack, buffer,
+ SILC_STR_DATA(e->pk, e->pk_len),
+ SILC_STR_END);
+ silc_buffer_push(buffer, 4);
+ }
+
+ silc_buffer_pull(buffer, 4 + e->pk_len);
+ silc_buffer_sformat(stack, buffer,
+ SILC_STR_UI_SHORT(signature_len),
+ SILC_STR_DATA(signature, signature_len),
+ SILC_STR_END);
+ silc_buffer_push(buffer, 4 + e->pk_len);
+
+ SILC_LOG_HEXDUMP(("SIG payload"), buffer->data, silc_buffer_len(buffer));
+
+ payload = e->buffer;
+ flags = e->flags;
+ cipher = e->cipher;
+ hmac = e->hmac;
+ iv = e->iv;
+ iv_len = e->iv_len;
+ payload_len = e->payload_len;
+ sid = &e->sid;
+ rid = &e->rid;
+ encoded = e->encoded;
+ encoded_context = e->context;
+
+ silc_sfree(stack, e->pk);
+ silc_buffer_sfree(stack, e->sign);
+ silc_sfree(stack, e);
+
+ /* Finalize message payload encoding */
+ silc_message_payload_encode_final(payload, flags, cipher, hmac,
+ iv, iv_len, payload_len,
+ sid, rid, stack, buffer,
+ encoded, encoded_context);
+}
+
+/* Encodes the SILC_MESSAGE_FLAG_SIGNED Payload and computes the digital
+ signature. */
+
+static SilcAsyncOperation
+silc_message_signed_payload_encode(SilcBuffer payload,
+ SilcMessageEncode *e)
+{
+ SilcAsyncOperation op;
+ SilcBuffer sign;
+ unsigned char *pk = NULL;
+ SilcUInt32 pk_len = 0;
+ SilcUInt16 pk_type;
+ SilcStack stack = e->stack;
+ SilcRng rng = e->rng;
+ SilcHash hash = e->hash;
+ SilcPublicKey public_key = e->public_key;
+ SilcPrivateKey private_key = e->private_key;
+ unsigned char *message_payload;
+ SilcUInt16 message_payload_len;
+
+ message_payload = payload->head;
+ message_payload_len = silc_buffer_headlen(payload);
+
+ if (!message_payload || !message_payload_len || !private_key || !hash) {
+ e->encoded(NULL, e->context);
+ silc_sfree(stack, e);
+ silc_stack_free(stack);
+ return NULL;
+ }
+
+ if (public_key) {
+ e->pk = pk = silc_pkcs_public_key_encode(stack, public_key, &pk_len);
+ if (!pk) {
+ e->encoded(NULL, e->context);
+ silc_sfree(stack, e);
+ silc_stack_free(stack);
+ return NULL;
+ }
+ e->pk_len = pk_len;
+ }
+ pk_type = silc_pkcs_get_type(private_key);
+
+ /* Encode the data to be signed */
+ e->sign = sign = silc_message_signed_encode_data(stack, message_payload,
+ message_payload_len,
+ pk, pk_len, pk_type);
+ if (!sign) {
+ e->encoded(NULL, e->context);
+ silc_sfree(stack, pk);
+ silc_sfree(stack, e);
+ silc_stack_free(stack);
+ return NULL;
+ }
+
+ /* Compute signature */
+ op = silc_pkcs_sign(private_key, sign->data, silc_buffer_len(sign),
+ TRUE, hash, rng,
+ silc_message_signed_payload_encode_cb, e);
+
+ return op;
+}
+
+/***************************** Payload parsing ******************************/
+
+/* Decrypts the Message Payload. The `data' is the actual Message Payload. */
+
+SilcBool silc_message_payload_decrypt(unsigned char *data,
+ size_t data_len,
+ SilcBool private_message,
+ SilcBool static_key,
+ SilcCipher cipher,
+ SilcHmac hmac,
+ unsigned char *sender_id,
+ SilcUInt32 sender_id_len,
+ unsigned char *receiver_id,
+ SilcUInt32 receiver_id_len,
+ SilcBool check_mac)
{
SilcUInt32 mac_len, iv_len = 0, block_len;
- SilcUInt16 len, totlen, dlen;
- unsigned char mac[32], *ivp, *dec;
-
+ SilcUInt16 len, totlen;
+ unsigned char mac[32], *ivp;
+
mac_len = silc_hmac_len(hmac);
+ block_len = silc_cipher_get_block_len(cipher);
- /* IV is present for all channel messages, and private messages when
+ /* IV is present for all channel messages, and private messages when
static key (pre-shared key) is used. */
if (!private_message || (private_message && static_key))
- iv_len = silc_cipher_get_block_len(cipher);
+ iv_len = block_len;
- if (data_len < mac_len)
+ if (silc_unlikely(data_len < (mac_len + iv_len + block_len)))
return FALSE;
- if (check_mac) {
+ if (silc_likely(check_mac)) {
/* Check the MAC of the message */
SILC_LOG_DEBUG(("Checking message MAC"));
silc_hmac_init(hmac);
silc_hmac_update(hmac, data, data_len - mac_len);
+ silc_hmac_update(hmac, sender_id, sender_id_len);
+ silc_hmac_update(hmac, receiver_id, receiver_id_len);
silc_hmac_final(hmac, mac, &mac_len);
- if (memcmp(data + (data_len - mac_len), mac, mac_len)) {
- SILC_LOG_DEBUG(("Message MAC does not match"));
- return FALSE;
+ if (silc_unlikely(memcmp(data + (data_len - mac_len), mac, mac_len))) {
+ /* Check for old style (version 1.2) message MAC. Remove this check
+ at some point. */
+ silc_hmac_init(hmac);
+ silc_hmac_update(hmac, data, data_len - mac_len);
+ silc_hmac_final(hmac, mac, &mac_len);
+ if (silc_unlikely(memcmp(data + (data_len - mac_len), mac, mac_len))) {
+ SILC_LOG_DEBUG(("Message MAC does not match"));
+ return FALSE;
+ }
}
SILC_LOG_DEBUG(("MAC is Ok"));
}
- /* Decrypt the entire buffer into allocated decryption buffer, since we
- do not reliably know its encrypted length (it may include unencrypted
- data at the end). */
+ /* Decrypt first only one block to get the header and then rest of
+ the data. This is done because there might be unencrypted data at
+ the end and we don't know the encrypted length yet. */
/* Get pointer to the IV */
ivp = (iv_len ? data + (data_len - iv_len - mac_len) :
silc_cipher_get_iv(cipher));
- /* Allocate buffer for decryption. Since there might be unencrypted
- data at the end, it might not be multiple by block size, make it so. */
- block_len = silc_cipher_get_block_len(cipher);
- dlen = data_len - iv_len - mac_len;
- if (dlen & (block_len - 1))
- dlen += SILC_MESSAGE_PAD(dlen);
- if (dlen > data_len - iv_len - mac_len)
- dlen -= block_len;
- dec = silc_malloc(dlen);
-
- /* Decrypt */
- silc_cipher_decrypt(cipher, data, dec, dlen, ivp);
-
- /* Now verify the true length of the payload and copy the decrypted
- part over the original data. First get data length, and then padding
- length from the decrypted data. Then, copy over the original data. */
-
- totlen = 2;
- SILC_GET16_MSB(len, dec + totlen);
- totlen += 2 + len;
- if (totlen + iv_len + mac_len + 2 > data_len) {
- memset(dec, 0, dlen);
- silc_free(dec);
+ /* Decrypt block */
+ if (silc_unlikely(!silc_cipher_decrypt(cipher, data, data, block_len,
+ ivp))) {
+ SILC_ASSERT(FALSE);
return FALSE;
}
- SILC_GET16_MSB(len, dec + totlen);
+
+ /* Get the payload length and decrypt rest */
+ totlen = 2;
+ SILC_GET16_MSB(len, data + totlen);
totlen += 2 + len;
- if (totlen + iv_len + mac_len > data_len) {
- memset(dec, 0, dlen);
- silc_free(dec);
+ if (silc_unlikely(totlen + iv_len + mac_len + 2 > data_len))
return FALSE;
- }
-
- memcpy(data, dec, totlen);
- memset(dec, 0, dlen);
- silc_free(dec);
+ totlen += 2;
+ if (totlen >= block_len)
+ if (silc_unlikely(!silc_cipher_decrypt(cipher, data + block_len,
+ data + block_len,
+ (totlen - block_len) +
+ SILC_MESSAGE_PAD(totlen), ivp))) {
+ SILC_ASSERT(FALSE);
+ return FALSE;
+ }
return TRUE;
}
/* Parses Message Payload returning new payload structure. This also
decrypts it and checks the MAC. */
-SilcMessagePayload
+SilcMessagePayload
silc_message_payload_parse(unsigned char *payload,
SilcUInt32 payload_len,
- bool private_message,
- bool static_key,
+ SilcBool private_message,
+ SilcBool static_key,
SilcCipher cipher,
- SilcHmac hmac)
+ SilcHmac hmac,
+ unsigned char *sender_id,
+ SilcUInt32 sender_id_len,
+ unsigned char *receiver_id,
+ SilcUInt32 receiver_id_len,
+ SilcStack stack,
+ SilcBool no_allocation,
+ SilcMessagePayload message)
{
SilcBufferStruct buffer;
- SilcMessagePayload newp;
+ SilcMessagePayload newp = NULL;
int ret;
SilcUInt32 mac_len = 0, iv_len = 0;
silc_buffer_set(&buffer, payload, payload_len);
/* Decrypt the payload */
- if (cipher) {
- ret = silc_message_payload_decrypt(buffer.data, buffer.len,
+ if (silc_likely(cipher)) {
+ ret = silc_message_payload_decrypt(buffer.data, silc_buffer_len(&buffer),
private_message, static_key,
- cipher, hmac, TRUE);
- if (ret == FALSE)
+ cipher, hmac, sender_id,
+ sender_id_len, receiver_id,
+ receiver_id_len, TRUE);
+ if (silc_unlikely(ret == FALSE))
return NULL;
}
- if (hmac)
+ if (silc_likely(hmac))
mac_len = silc_hmac_len(hmac);
- /* IV is present for all channel messages, and private messages when
+ /* IV is present for all channel messages, and private messages when
static key (pre-shared key) is used. */
if (cipher && (!private_message || (private_message && static_key)))
iv_len = silc_cipher_get_block_len(cipher);
- newp = silc_calloc(1, sizeof(*newp));
- if (!newp)
- return NULL;
+ if (!message) {
+ newp = message = silc_calloc(1, sizeof(*newp));
+ if (silc_unlikely(!newp))
+ return NULL;
+ }
+ memset(message, 0, sizeof(*message));
+ message->allocated = (stack || no_allocation ? FALSE : TRUE);
/* Parse the Message Payload. */
- ret = silc_buffer_unformat(&buffer,
- SILC_STR_UI_SHORT(&newp->flags),
- SILC_STR_UI16_NSTRING_ALLOC(&newp->data,
- &newp->data_len),
- SILC_STR_UI16_NSTRING_ALLOC(&newp->pad,
- &newp->pad_len),
- SILC_STR_END);
- if (ret == -1)
+ if (!no_allocation)
+ ret = silc_buffer_sunformat(stack, &buffer,
+ SILC_STR_UI_SHORT(&message->flags),
+ SILC_STR_UI16_NSTRING_ALLOC(&message->data,
+ &message->data_len),
+ SILC_STR_UI16_NSTRING_ALLOC(&message->pad,
+ &message->pad_len),
+ SILC_STR_END);
+ else
+ ret = silc_buffer_unformat(&buffer,
+ SILC_STR_UI_SHORT(&message->flags),
+ SILC_STR_UI16_NSTRING(&message->data,
+ &message->data_len),
+ SILC_STR_UI16_NSTRING(&message->pad,
+ &message->pad_len),
+ SILC_STR_END);
+ if (silc_unlikely(ret == -1))
goto err;
- if ((newp->data_len > buffer.len - 6 - mac_len - iv_len) ||
- (newp->pad_len + newp->data_len > buffer.len - 6 - mac_len - iv_len)) {
+ if (silc_unlikely((message->data_len > silc_buffer_len(&buffer) -
+ 6 - mac_len - iv_len) ||
+ (message->pad_len + message->data_len >
+ silc_buffer_len(&buffer) - 6 - mac_len - iv_len))) {
SILC_LOG_ERROR(("Incorrect Message Payload in packet"));
goto err;
}
/* Parse Signed Message Payload if provided */
- if (newp->flags & SILC_MESSAGE_FLAG_SIGNED &&
- newp->data_len + newp->pad_len + 6 + mac_len + iv_len < buffer.len) {
- newp->sig =
- silc_message_signed_payload_parse(buffer.data + 6 + newp->data_len +
- newp->pad_len,
- buffer.len - iv_len - mac_len);
+ if (message->flags & SILC_MESSAGE_FLAG_SIGNED &&
+ message->data_len + message->pad_len + 6 + mac_len +
+ iv_len < silc_buffer_len(&buffer)) {
+ if (!silc_message_signed_payload_parse(buffer.data + 6 +
+ message->data_len +
+ message->pad_len,
+ silc_buffer_len(&buffer) -
+ iv_len - mac_len - 6 -
+ message->data_len -
+ message->pad_len,
+ &message->sig))
+ goto err;
}
- /* Parse IV and MAC from the payload */
- if (iv_len) {
- newp->iv = buffer.data + (buffer.len - iv_len - mac_len);
- newp->iv_len = iv_len;
- }
+ /* Parse MAC from the payload */
if (mac_len)
- newp->mac = buffer.data + (buffer.len - mac_len);
+ message->mac = buffer.data + (silc_buffer_len(&buffer) - mac_len);
return newp;
err:
- silc_message_payload_free(newp);
+ if (newp)
+ silc_message_payload_free(newp);
return NULL;
}
+
+/***************************** Payload encoding *****************************/
+
/* This function is used to encrypt the Messsage Payload which is
the `data' and `data_len'. This is used internally by the Message
- Payload encoding routines but application may call this too if needed.
+ Payload encoding routines but application may call this too if needed.
The `true_len' is the data length which is used to create MAC out of. */
-bool silc_message_payload_encrypt(unsigned char *data,
- SilcUInt32 data_len,
- SilcUInt32 true_len,
- unsigned char *iv,
- SilcUInt32 iv_len,
- SilcCipher cipher,
- SilcHmac hmac)
+SilcBool silc_message_payload_encrypt(unsigned char *data,
+ SilcUInt32 data_len,
+ SilcUInt32 true_len,
+ unsigned char *iv,
+ SilcID *sender_id,
+ SilcID *receiver_id,
+ SilcCipher cipher,
+ SilcHmac hmac)
{
- unsigned char mac[32];
- SilcUInt32 mac_len;
- SilcBufferStruct buf;
+ unsigned char sid[32], rid[32];
+ SilcUInt32 sid_len = 0, rid_len = 0;
- /* Encrypt payload of the packet. If the IV is added to packet do
- not encrypt that. */
- silc_cipher_encrypt(cipher, data, data, data_len, iv_len ? iv : NULL);
+ /* Encrypt payload of the packet */
+ if (silc_unlikely(!silc_cipher_encrypt(cipher, data, data, data_len, iv)))
+ return FALSE;
+
+ /* Encode IDs */
+ silc_id_id2str(&sender_id->u.client_id, SILC_ID_CLIENT, sid, sizeof(sid),
+ &sid_len);
+ if (receiver_id->type == SILC_ID_CLIENT)
+ silc_id_id2str(&receiver_id->u.client_id, SILC_ID_CLIENT, rid,
+ sizeof(rid), &rid_len);
+ else if (receiver_id->type == SILC_ID_CHANNEL)
+ silc_id_id2str(&receiver_id->u.channel_id, SILC_ID_CHANNEL, rid,
+ sizeof(rid), &rid_len);
/* Compute the MAC of the encrypted message data */
silc_hmac_init(hmac);
silc_hmac_update(hmac, data, true_len);
- silc_hmac_final(hmac, mac, &mac_len);
-
- /* Put rest of the data to the payload */
- silc_buffer_set(&buf, data, true_len + mac_len);
- silc_buffer_pull(&buf, true_len);
- silc_buffer_put(&buf, mac, mac_len);
+ silc_hmac_update(hmac, sid, sid_len);
+ silc_hmac_update(hmac, rid, rid_len);
+ silc_hmac_final(hmac, data + true_len, NULL);
return TRUE;
}
+/* Encrypt message payload */
+
+static int silc_message_payload_encode_encrypt(SilcStack stack,
+ SilcBuffer buffer,
+ void *value, void *context)
+{
+ SilcMessageEncode *e = context;
+ SilcUInt32 mac_len;
+
+ if (!e->cipher || !e->hmac)
+ return 0;
+
+ mac_len = silc_hmac_len(e->hmac);
+ if (silc_unlikely(!silc_buffer_enlarge(buffer, mac_len)))
+ return -1;
+
+ if (silc_unlikely(!silc_message_payload_encrypt(buffer->head,
+ e->payload_len,
+ silc_buffer_headlen(buffer),
+ e->iv, &e->sid, &e->rid,
+ e->cipher, e->hmac)))
+ return -1;
+
+ return mac_len;
+}
+
+/* Finalize message payload encoding */
+
+static void
+silc_message_payload_encode_final(SilcBuffer buffer,
+ SilcMessageFlags flags,
+ SilcCipher cipher,
+ SilcHmac hmac,
+ unsigned char *iv,
+ SilcUInt32 iv_len,
+ SilcUInt32 payload_len,
+ SilcID *sender_id,
+ SilcID *receiver_id,
+ SilcStack stack,
+ SilcBuffer signature,
+ SilcMessagePayloadEncoded encoded,
+ void *context)
+{
+ SilcMessageEncode e;
+
+ e.flags = flags;
+ e.cipher = cipher;
+ e.hmac = hmac;
+ e.sid = *sender_id;
+ e.rid = *receiver_id;
+ e.iv = iv;
+ e.payload_len = payload_len;
+
+ /* Encrypt */
+ if (silc_buffer_format(buffer,
+ SILC_STR_DATA(signature ?
+ silc_buffer_data(signature) : NULL,
+ signature ?
+ silc_buffer_len(signature) : 0),
+ SILC_STR_DATA(iv, iv_len),
+ SILC_STR_FUNC(silc_message_payload_encode_encrypt,
+ NULL, &e),
+ SILC_STR_END) < 0) {
+ silc_buffer_sfree(stack, buffer);
+ encoded(NULL, context);
+ return;
+ }
+
+ /* Deliver message payload */
+ silc_buffer_start(buffer);
+ encoded(buffer, context);
+
+ silc_buffer_sfree(stack, buffer);
+ silc_buffer_sfree(stack, signature);
+ silc_stack_free(stack);
+}
+
/* Encodes Message Payload into a buffer and returns it. */
-SilcBuffer silc_message_payload_encode(SilcMessageFlags flags,
- const unsigned char *data,
- SilcUInt32 data_len,
- bool generate_iv,
- bool private_message,
- SilcCipher cipher,
- SilcHmac hmac,
- SilcRng rng,
- SilcPublicKey public_key,
- SilcPrivateKey private_key,
- SilcHash hash)
+SilcAsyncOperation
+silc_message_payload_encode(SilcMessageFlags flags,
+ const unsigned char *data,
+ SilcUInt32 data_len,
+ SilcBool generate_iv,
+ SilcBool private_message,
+ SilcCipher cipher,
+ SilcHmac hmac,
+ SilcRng rng,
+ SilcPublicKey public_key,
+ SilcPrivateKey private_key,
+ SilcHash hash,
+ SilcID *sender_id,
+ SilcID *receiver_id,
+ SilcStack stack,
+ SilcMessagePayloadEncoded encoded,
+ void *context)
{
- int i;
- SilcBuffer buffer;
- SilcUInt32 len, pad_len = 0, mac_len = 0, iv_len = 0;
+ SilcUInt32 pad_len = 0, mac_len = 0, iv_len = 0;
unsigned char pad[16], iv[SILC_CIPHER_MAX_IV_SIZE];
- SilcBuffer sig = NULL;
+ SilcBuffer buffer;
+ int i;
SILC_LOG_DEBUG(("Encoding Message Payload"));
- if (!data_len)
+ if (silc_unlikely(!data_len)) {
+ encoded(NULL, context);
return NULL;
+ }
+ if (silc_unlikely(!private_message && (!cipher || !hmac))) {
+ encoded(NULL, context);
+ return NULL;
+ }
+
+ stack = silc_stack_alloc(0, stack ? stack : silc_crypto_stack());
+
+ buffer = silc_buffer_salloc(stack, 0);
+ if (silc_unlikely(!buffer)) {
+ encoded(NULL, context);
+ silc_stack_free(stack);
+ return NULL;
+ }
/* For channel messages IV is always generated */
if (!private_message && !generate_iv)
if (hmac)
mac_len = silc_hmac_len(hmac);
- data_len = SILC_MESSAGE_DATALEN(data_len, mac_len + iv_len);
+ data_len = silc_message_payload_datalen(data_len, mac_len + iv_len, flags,
+ public_key, private_key);
/* Calculate length of padding. IV is not included into the calculation
since it is not encrypted. */
- len = 6 + data_len;
- pad_len = SILC_MESSAGE_PAD(len);
-
- /* Allocate payload buffer */
- len += pad_len + iv_len + mac_len;
- buffer = silc_buffer_alloc(len);
- if (!buffer)
- return NULL;
+ pad_len = SILC_MESSAGE_PAD(6 + data_len);
/* Generate padding */
if (cipher) {
}
/* Encode the Message Payload */
- silc_buffer_pull_tail(buffer, 6 + data_len + pad_len);
- silc_buffer_format(buffer,
- SILC_STR_UI_SHORT(flags),
- SILC_STR_UI_SHORT(data_len),
- SILC_STR_UI_XNSTRING(data, data_len),
- SILC_STR_UI_SHORT(pad_len),
- SILC_STR_UI_XNSTRING(pad, pad_len),
- SILC_STR_END);
-
- memset(pad, 0, sizeof(pad));
-
- /* Sign the message if wanted */
- if (flags & SILC_MESSAGE_FLAG_SIGNED && private_key && hash) {
- sig = silc_message_signed_payload_encode(buffer->data, buffer->len,
- public_key, private_key, hash);
- if (sig) {
- buffer = silc_buffer_realloc(buffer, buffer->truelen + sig->len);
- if (buffer) {
- silc_buffer_pull(buffer, 6 + data_len + pad_len);
- silc_buffer_pull_tail(buffer, sig->len);
- silc_buffer_put(buffer, sig->data, sig->len);
- silc_buffer_push(buffer, 6 + data_len + pad_len);
- }
- }
+ if (silc_buffer_format(buffer,
+ SILC_STR_ADVANCE,
+ SILC_STR_UI_SHORT(flags),
+ SILC_STR_UI_SHORT(data_len),
+ SILC_STR_DATA(data, data_len),
+ SILC_STR_UI_SHORT(pad_len),
+ SILC_STR_DATA(pad, pad_len),
+ SILC_STR_END) < 0) {
+ silc_buffer_sfree(stack, buffer);
+ encoded(NULL, context);
+ silc_stack_free(stack);
+ return NULL;
}
- /* Put IV */
- silc_buffer_pull(buffer, 6 + data_len + pad_len + (sig ? sig->len : 0));
- silc_buffer_pull_tail(buffer, iv_len);
- silc_buffer_format(buffer,
- SILC_STR_UI_XNSTRING(iv, iv_len),
- SILC_STR_END);
- silc_buffer_push(buffer, 6 + data_len + pad_len + (sig ? sig->len : 0));
-
- SILC_LOG_HEXDUMP(("foo"), buffer->data, buffer->len);
-
- /* Now encrypt the Message Payload and compute MAC */
- if (cipher) {
- if (!silc_message_payload_encrypt(buffer->data,
- buffer->len - iv_len -
- (sig ? sig->len : 0),
- buffer->len, iv, iv_len,
- cipher, hmac)) {
- silc_buffer_free(buffer);
- silc_buffer_free(sig);
+ if (flags & SILC_MESSAGE_FLAG_SIGNED) {
+ SilcMessageEncode *e = silc_scalloc(stack, 1, sizeof(*e));
+ if (!e) {
+ silc_buffer_sfree(stack, buffer);
+ encoded(NULL, context);
+ silc_stack_free(stack);
return NULL;
}
+
+ e->stack = stack;
+ e->buffer = buffer;
+ e->flags = flags;
+ e->public_key = public_key;
+ e->private_key = private_key;
+ e->rng = rng;
+ e->hash = hash;
+ e->cipher = cipher;
+ e->hmac = hmac;
+ e->sid = *sender_id;
+ e->rid = *receiver_id;
+ e->iv = iv_len ? iv : NULL;
+ e->iv_len = iv_len;
+ e->payload_len = 6 + data_len + pad_len;
+ e->encoded = encoded;
+ e->context = context;
+
+ /* Compute signature */
+ return silc_message_signed_payload_encode(buffer, e);
}
- silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer) - buffer->len);
- silc_buffer_free(sig);
- return buffer;
+ /* Finalize */
+ silc_message_payload_encode_final(buffer, flags, cipher, hmac,
+ iv_len ? iv : NULL, iv_len,
+ 6 + data_len + pad_len,
+ sender_id, receiver_id, stack, NULL,
+ encoded, context);
+ return NULL;
}
/* Free's Message Payload */
void silc_message_payload_free(SilcMessagePayload payload)
{
+ silc_message_signed_payload_free(&payload->sig);
if (payload->data) {
memset(payload->data, 0, payload->data_len);
- silc_free(payload->data);
+ if (payload->allocated)
+ silc_free(payload->data);
+ }
+ if (payload->allocated) {
+ silc_free(payload->pad);
+ silc_free(payload);
}
- if (payload->sig)
- silc_message_signed_payload_free(payload->sig);
- silc_free(payload->pad);
- silc_free(payload);
}
/* Return flags */
return payload->mac;
}
-/* Return IV. The caller knows the length of the IV */
-
-unsigned char *silc_message_get_iv(SilcMessagePayload payload)
-{
- return payload->iv;
-}
-
-/* Return signature of the message */
-
-const SilcMessageSignedPayload
-silc_message_get_signature(SilcMessagePayload payload)
-{
- return (const SilcMessageSignedPayload)payload->sig;
-}
-
-/******************************************************************************
-
- SILC_MESSAGE_FLAG_SIGNED Payload
-
-******************************************************************************/
-
-/* The SILC_MESSAGE_FLAG_SIGNED Payload */
-struct SilcMessageSignedPayloadStruct {
- SilcUInt16 pk_len;
- SilcUInt16 pk_type;
- SilcUInt16 sign_len;
- unsigned char *pk_data;
- unsigned char *sign_data;
-};
-
-/* Encodes the data to be signed to SILC_MESSAGE_FLAG_SIGNED Payload */
-
-static SilcBuffer
-silc_message_signed_encode_data(const unsigned char *message_payload,
- SilcUInt32 message_payload_len,
- unsigned char *pk,
- SilcUInt32 pk_len, SilcUInt32 pk_type)
-{
- SilcBuffer sign;
-
- sign = silc_buffer_alloc_size(message_payload_len + 4 + pk_len);
- if (!sign)
- return NULL;
-
- silc_buffer_format(sign,
- SILC_STR_UI_XNSTRING(message_payload,
- message_payload_len),
- SILC_STR_UI_SHORT(pk_len),
- SILC_STR_UI_SHORT(pk_type),
- SILC_STR_END);
-
- if (pk && pk_len) {
- silc_buffer_pull(sign, message_payload_len + 4);
- silc_buffer_format(sign,
- SILC_STR_UI_XNSTRING(pk, pk_len),
- SILC_STR_END);
- silc_buffer_push(sign, message_payload_len + 4);
- }
-
- return sign;
-}
-
-/* Parses the SILC_MESSAGE_FLAG_SIGNED Payload */
-
-SilcMessageSignedPayload
-silc_message_signed_payload_parse(const unsigned char *data,
- SilcUInt32 data_len)
-{
- SilcMessageSignedPayload sig;
- SilcBufferStruct buffer;
- int ret;
-
- SILC_LOG_DEBUG(("Parsing SILC_MESSAGE_FLAG_SIGNED Payload"));
-
- SILC_LOG_HEXDUMP(("sig payload"), (unsigned char *)data, data_len);
-
- silc_buffer_set(&buffer, (unsigned char *)data, data_len);
- sig = silc_calloc(1, sizeof(*sig));
- if (!sig)
- return NULL;
-
- /* Parse the payload */
- ret = silc_buffer_unformat(&buffer,
- SILC_STR_UI_SHORT(&sig->pk_len),
- SILC_STR_UI_SHORT(&sig->pk_type),
- SILC_STR_END);
- if (ret == -1 || sig->pk_len > data_len - 4) {
- silc_message_signed_payload_free(sig);
- SILC_LOG_DEBUG(("Malformed public key in SILC_MESSAGE_FLAG_SIGNED "
- "Payload"));
- return NULL;
- }
-
- silc_buffer_pull(&buffer, 4);
- ret = silc_buffer_unformat(&buffer,
- SILC_STR_UI_XNSTRING_ALLOC(&sig->pk_data,
- sig->pk_len),
- SILC_STR_UI16_NSTRING_ALLOC(&sig->sign_data,
- &sig->sign_len),
- SILC_STR_END);
- if (ret == -1) {
- silc_message_signed_payload_free(sig);
- SILC_LOG_DEBUG(("Malformed SILC_MESSAGE_FLAG_SIGNED Payload"));
- return NULL;
- }
- silc_buffer_push(&buffer, 4);
-
- /* Signature must be provided */
- if (sig->sign_len < 1) {
- SILC_LOG_DEBUG(("Malformed signature in SILC_MESSAGE_SIGNED_PAYLOAD "
- "Payload"));
- silc_message_signed_payload_free(sig);
- return NULL;
- }
-
- return sig;
-}
-
-/* Encodes the SILC_MESSAGE_FLAG_SIGNED Payload and computes the digital
- signature. */
+/* Verify the signature in SILC_MESSAGE_FLAG_SIGNED Payload */
-SilcBuffer
-silc_message_signed_payload_encode(const unsigned char *message_payload,
- SilcUInt32 message_payload_len,
- SilcPublicKey public_key,
- SilcPrivateKey private_key,
- SilcHash hash)
+SilcAsyncOperation
+silc_message_signed_verify(SilcMessagePayload message,
+ SilcPublicKey remote_public_key,
+ SilcHash hash,
+ SilcAuthResultCb result,
+ void *context)
{
- SilcBuffer buffer, sign;
- SilcPKCS pkcs;
- unsigned char auth_data[2048 + 1];
- SilcUInt32 auth_len;
- unsigned char *pk = NULL;
- SilcUInt32 pk_len = 0;
- SilcUInt16 pk_type;
-
- if (!message_payload || !message_payload_len || !private_key || !hash)
- return NULL;
-
- if (public_key)
- pk = silc_pkcs_public_key_encode(public_key, &pk_len);
-
- /* Now we support only SILC style public key */
- pk_type = SILC_SKE_PK_TYPE_SILC;
-
- /* Encode the data to be signed */
- sign = silc_message_signed_encode_data(message_payload,
- message_payload_len,
- pk, pk_len, pk_type);
- if (!sign) {
- silc_free(pk);
- return NULL;
- }
-
- /* Sign the buffer */
-
- /* Allocate PKCS object */
- if (!silc_pkcs_alloc(private_key->name, &pkcs)) {
- SILC_LOG_ERROR(("Could not allocated PKCS"));
- silc_buffer_clear(sign);
- silc_buffer_free(sign);
- silc_free(pk);
- return NULL;
- }
- silc_pkcs_private_key_set(pkcs, private_key);
-
- /* Compute the hash and the signature. */
- if (silc_pkcs_get_key_len(pkcs) / 8 > sizeof(auth_data) - 1 ||
- !silc_pkcs_sign_with_hash(pkcs, hash, sign->data, sign->len, auth_data,
- &auth_len)) {
- SILC_LOG_ERROR(("Could not compute signature"));
- silc_buffer_clear(sign);
- silc_buffer_free(sign);
- silc_pkcs_free(pkcs);
- silc_free(pk);
- return NULL;
- }
-
- /* Encode the SILC_MESSAGE_FLAG_SIGNED Payload */
-
- buffer = silc_buffer_alloc_size(4 + pk_len + 2 + auth_len);
- if (!buffer) {
- silc_buffer_clear(sign);
- silc_buffer_free(sign);
- silc_pkcs_free(pkcs);
- memset(auth_data, 0, sizeof(auth_data));
- silc_free(pk);
+ SilcAsyncOperation op;
+ SilcBuffer sign, tmp;
+ SilcStack stack = NULL;
+ SilcMessageSignedPayload sig = &message->sig;
+
+ if (!(message->flags & SILC_MESSAGE_FLAG_SIGNED) ||
+ !sig->sign_len || !remote_public_key || !hash) {
+ result(FALSE, context);
return NULL;
}
- silc_buffer_format(buffer,
- SILC_STR_UI_SHORT(pk_len),
- SILC_STR_UI_SHORT(pk_type),
- SILC_STR_END);
-
- if (pk_len && pk) {
- silc_buffer_pull(buffer, 4);
- silc_buffer_format(buffer,
- SILC_STR_UI_XNSTRING(pk, pk_len),
- SILC_STR_END);
- silc_buffer_push(buffer, 4);
- }
-
- silc_buffer_pull(buffer, 4 + pk_len);
- silc_buffer_format(buffer,
- SILC_STR_UI_SHORT(auth_len),
- SILC_STR_UI_XNSTRING(auth_data, auth_len),
- SILC_STR_END);
- silc_buffer_push(buffer, 4 + pk_len);
-
- SILC_LOG_HEXDUMP(("sig payload"), buffer->data, buffer->len);
-
- memset(auth_data, 0, sizeof(auth_data));
- silc_pkcs_free(pkcs);
- silc_buffer_clear(sign);
- silc_buffer_free(sign);
- silc_free(pk);
-
- return buffer;
-}
-
-/* Free the payload */
-
-void silc_message_signed_payload_free(SilcMessageSignedPayload sig)
-{
- memset(sig->sign_data, 0, sig->sign_len);
- silc_free(sig->sign_data);
- silc_free(sig->pk_data);
- silc_free(sig);
-}
-
-/* Verify the signature in SILC_MESSAGE_FLAG_SIGNED Payload */
-
-int silc_message_signed_verify(SilcMessageSignedPayload sig,
- SilcMessagePayload message,
- SilcPublicKey remote_public_key,
- SilcHash hash)
-{
- int ret = SILC_AUTH_FAILED;
- SilcBuffer sign;
- SilcPKCS pkcs;
- SilcBuffer tmp;
-
- if (!sig || !remote_public_key || !hash)
- return ret;
+ if (silc_crypto_stack())
+ stack = silc_stack_alloc(0, silc_crypto_stack());
/* Generate the signature verification data, the Message Payload */
- tmp = silc_buffer_alloc_size(6 + message->data_len + message->pad_len);
- silc_buffer_format(tmp,
- SILC_STR_UI_SHORT(message->flags),
- SILC_STR_UI_SHORT(message->data_len),
- SILC_STR_UI_XNSTRING(message->data, message->data_len),
- SILC_STR_UI_SHORT(message->pad_len),
- SILC_STR_UI_XNSTRING(message->pad, message->pad_len),
- SILC_STR_END);
- sign = silc_message_signed_encode_data(tmp->data, tmp->len,
+ tmp = silc_buffer_salloc_size(stack,
+ 6 + message->data_len + message->pad_len);
+ silc_buffer_sformat(stack, tmp,
+ SILC_STR_UI_SHORT(message->flags),
+ SILC_STR_UI_SHORT(message->data_len),
+ SILC_STR_DATA(message->data, message->data_len),
+ SILC_STR_UI_SHORT(message->pad_len),
+ SILC_STR_DATA(message->pad, message->pad_len),
+ SILC_STR_END);
+ sign = silc_message_signed_encode_data(stack, tmp->data, silc_buffer_len(tmp),
sig->pk_data, sig->pk_len,
sig->pk_type);
silc_buffer_clear(tmp);
- silc_buffer_free(tmp);
-
- if (!sign)
- return ret;
-
- /* Allocate PKCS object */
- if (!silc_pkcs_alloc(remote_public_key->name, &pkcs)) {
- silc_buffer_clear(sign);
- silc_buffer_free(sign);
- return ret;
- }
- silc_pkcs_public_key_set(pkcs, remote_public_key);
+ silc_buffer_sfree(stack, tmp);
- /* Verify the authentication data */
- if (!silc_pkcs_verify_with_hash(pkcs, hash, sig->sign_data,
- sig->sign_len,
- sign->data, sign->len)) {
-
- silc_buffer_clear(sign);
- silc_buffer_free(sign);
- silc_pkcs_free(pkcs);
- SILC_LOG_DEBUG(("Signature verification failed"));
- return ret;
+ if (!sign) {
+ result(FALSE, context);
+ silc_stack_free(stack);
+ return NULL;
}
- ret = SILC_AUTH_OK;
+ /* Verify the authentication data */
+ op = silc_pkcs_verify(remote_public_key, sig->sign_data, sig->sign_len,
+ silc_buffer_data(sign), silc_buffer_len(sign),
+ hash, result, context);
silc_buffer_clear(sign);
- silc_buffer_free(sign);
- silc_pkcs_free(pkcs);
-
- SILC_LOG_DEBUG(("Signature verification successful"));
+ silc_buffer_sfree(stack, sign);
+ silc_stack_free(stack);
- return ret;
+ return op;
}
/* Return the public key from the payload */
SilcPublicKey
-silc_message_signed_get_public_key(SilcMessageSignedPayload sig,
- unsigned char **pk_data,
+silc_message_signed_get_public_key(SilcMessagePayload payload,
+ const unsigned char **pk_data,
SilcUInt32 *pk_data_len)
{
SilcPublicKey pk;
+ SilcMessageSignedPayload sig = &payload->sig;
+
+ if (!sig->pk_data)
+ return NULL;
- if (!sig->pk_data || !silc_pkcs_public_key_decode(sig->pk_data,
- sig->pk_len, &pk))
+ if (!silc_pkcs_public_key_alloc(sig->pk_type, sig->pk_data,
+ sig->pk_len, &pk))
return NULL;
if (pk_data)