X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilccore%2Fsilcmessage.c;h=b1e3894d9179ac104e253f0c1851803eb8dd78e4;hb=1ea936cbf1bb3b19bd55839b904ef59ada84b8b5;hp=f1fbe932e7809829167371c26025b2fac51b253b;hpb=054c9b2c9b21c3cbe87e53c0e0bacb2bff918e68;p=silc.git diff --git a/lib/silccore/silcmessage.c b/lib/silccore/silcmessage.c index f1fbe932..b1e3894d 100644 --- a/lib/silccore/silcmessage.c +++ b/lib/silccore/silcmessage.c @@ -1,10 +1,10 @@ /* - silcmessage.c + silcmessage.c Author: Pekka Riikonen - 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 @@ -20,14 +20,10 @@ 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)) @@ -35,87 +31,424 @@ /* 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 mac_len = 0, iv_len = 0; - unsigned char *mac, mac2[32]; + 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_async(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; + unsigned char mac[32], *ivp; mac_len = silc_hmac_len(hmac); + block_len = silc_cipher_get_block_len(cipher); - /* IV is present for channel messages and private messages when static - key (pre-shared key) is used. */ + /* 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) { - /* Take the MAC */ - mac = data + (data_len - mac_len); - + 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_final(hmac, mac2, &mac_len); - if (memcmp(mac, mac2, mac_len)) { - SILC_LOG_DEBUG(("Message MAC does not match")); - return FALSE; + 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 (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 message */ - silc_cipher_decrypt(cipher, data, data, data_len - iv_len - mac_len, - (iv_len ? data + (data_len - iv_len - mac_len) : - silc_cipher_get_iv(cipher))); + /* 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)); + + /* Decrypt block */ + if (silc_unlikely(!silc_cipher_decrypt(cipher, data, data, block_len, + ivp))) { + SILC_ASSERT(FALSE); + return FALSE; + } + + /* Get the payload length and decrypt rest */ + totlen = 2; + SILC_GET16_MSB(len, data + totlen); + totlen += 2 + len; + if (silc_unlikely(totlen + iv_len + mac_len + 2 > data_len)) + return FALSE; + 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; @@ -124,108 +457,252 @@ silc_message_payload_parse(unsigned char *payload, 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 channel messages and private messages when static - key (pre-shared key) is used. */ + /* 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_UI_XNSTRING(&newp->iv, iv_len), - SILC_STR_UI_XNSTRING(&newp->mac, mac_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; } - newp->iv_len = iv_len; + /* Parse Signed Message Payload if provided */ + 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 MAC from the payload */ + if (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. - The `data_len' is the data lenght which is used to create MAC out of. */ - -bool silc_message_payload_encrypt(unsigned char *data, - SilcUInt32 data_len, - unsigned char *iv, - SilcUInt32 iv_len, - SilcCipher cipher, - SilcHmac hmac) + 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. */ + +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_len ? iv : silc_cipher_get_iv(cipher)); + /* 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, data_len); - silc_hmac_final(hmac, mac, &mac_len); - - /* Put rest of the data to the payload */ - silc_buffer_set(&buf, data, data_len + mac_len); - silc_buffer_pull(&buf, data_len); - silc_buffer_put(&buf, mac, mac_len); + silc_hmac_update(hmac, data, true_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) +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 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) @@ -243,18 +720,12 @@ SilcBuffer silc_message_payload_encode(SilcMessageFlags flags, 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) { @@ -266,41 +737,73 @@ SilcBuffer silc_message_payload_encode(SilcMessageFlags flags, } /* Encode the Message Payload */ - silc_buffer_pull_tail(buffer, 6 + data_len + pad_len + iv_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_UI_XNSTRING(iv, iv_len), - SILC_STR_END); - - memset(pad, 0, sizeof(pad)); - - /* Now encrypt the Message Payload */ - if (cipher) { - if (!silc_message_payload_encrypt(buffer->data, buffer->len, - iv, iv_len, cipher, hmac)) { - silc_buffer_free(buffer); + 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; + } + + 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); - 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); } - silc_free(payload->pad); - silc_free(payload); } /* Return flags */ @@ -327,299 +830,85 @@ unsigned char *silc_message_get_mac(SilcMessagePayload payload) return payload->mac; } -/* Return IV. The caller knows the length of the IV */ - -unsigned char *silc_message_get_iv(SilcMessagePayload payload) -{ - return payload->iv; -} - -/****************************************************************************** - - 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_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); - 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); - return NULL; - } - silc_buffer_push(&buffer, 4); - - /* Signature must be provided */ - if (sig->sign_len < 1) { - 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, - bool include_public_key) +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]; - 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 (include_public_key && !public_key) - return NULL; - - if (include_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); + 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; } - /* Sign the buffer */ - - /* Allocate PKCS object */ - if (!silc_pkcs_alloc(private_key->name, &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_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); - return NULL; - } - - silc_buffer_format(sign, - SILC_STR_UI_SHORT(pk_len), - SILC_STR_UI_SHORT(pk_type), - SILC_STR_END); - - if (pk_len && pk) { - silc_buffer_pull(sign, 4); - silc_buffer_format(sign, - SILC_STR_UI_XNSTRING(pk, pk_len), - SILC_STR_END); - silc_buffer_push(sign, 4); - } - - silc_buffer_pull(sign, 4 + pk_len); - silc_buffer_format(sign, - SILC_STR_UI_SHORT(auth_len), - SILC_STR_UI_XNSTRING(auth_data, auth_len), - SILC_STR_END); - silc_buffer_push(sign, 4 + pk_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) -{ - if (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 + - message->iv_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_UI_XNSTRING(message->iv, message->iv_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_async(remote_public_key, sig->sign_data, + sig->sign_len, + silc_buffer_data(sign), silc_buffer_len(sign), + TRUE, 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) +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 || !silc_pkcs_public_key_decode(sig->pk_data, - sig->pk_len, &pk)) + if (!sig->pk_data) return NULL; + if (!silc_pkcs_public_key_alloc(sig->pk_type, sig->pk_data, + sig->pk_len, &pk)) + return NULL; + + if (pk_data) + *pk_data = sig->pk_data; + if (pk_data_len) + *pk_data_len = sig->pk_len; + return pk; }