+ 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_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)