+/************************* Static utility functions *************************/
+
+/* 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 (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(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;
+}
+
+/* Encodes the SILC_MESSAGE_FLAG_SIGNED Payload and computes the digital
+ signature. */
+
+static SilcBuffer
+silc_message_signed_payload_encode(const unsigned char *message_payload,
+ SilcUInt32 message_payload_len,
+ SilcPublicKey public_key,
+ SilcPrivateKey private_key,
+ SilcHash hash)
+{
+ SilcBuffer buffer, sign;
+ 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);
+ if (!pk)
+ return NULL;
+ }
+ pk_type = silc_pkcs_get_type(public_key);
+
+ /* 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 */
+
+ /* Compute the hash and the signature. */
+ if (!silc_pkcs_sign(private_key, sign->data, silc_buffer_len(sign),
+ auth_data, sizeof(auth_data) - 1, &auth_len, hash)) {
+ SILC_LOG_ERROR(("Could not compute signature"));
+ silc_buffer_clear(sign);
+ silc_buffer_free(sign);
+ 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);
+ memset(auth_data, 0, sizeof(auth_data));
+ silc_free(pk);
+ 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, silc_buffer_len(buffer));
+
+ memset(auth_data, 0, sizeof(auth_data));
+ silc_buffer_clear(sign);
+ silc_buffer_free(sign);
+ silc_free(pk);
+
+ return buffer;
+}
+
+
+/***************************** Payload parsing ******************************/