X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilccore%2Fsilcmessage.c;h=08ec1feff16ff05c58a09a35a88bfcf2209bae3a;hp=e3fd64f970cb17556abd27cb35a6eec618429aa1;hb=382d15d447b7a95390decfa783836ae4fe255b3d;hpb=3cf38201f9ffaad2da53757ed48a5546e1d03636 diff --git a/lib/silccore/silcmessage.c b/lib/silccore/silcmessage.c index e3fd64f9..08ec1fef 100644 --- a/lib/silccore/silcmessage.c +++ b/lib/silccore/silcmessage.c @@ -54,7 +54,7 @@ struct SilcMessagePayloadStruct { unsigned char *pad; unsigned char *iv; unsigned char *mac; - /*SilcMessageSignedPayload sig;*/ + SilcMessageSignedPayload sig; }; /* Decrypts the Message Payload. The `data' is the actual Message Payload */ @@ -67,13 +67,14 @@ bool silc_message_payload_decrypt(unsigned char *data, SilcHmac hmac, bool check_mac) { - SilcUInt32 mac_len = 0, iv_len = 0; - unsigned char *mac, mac2[32]; - + SilcUInt32 mac_len, iv_len = 0, block_len; + SilcUInt16 len, totlen, dlen; + unsigned char mac[32], *ivp, *dec; + 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 (!private_message || (private_message && static_key)) iv_len = silc_cipher_get_block_len(cipher); @@ -81,24 +82,63 @@ bool silc_message_payload_decrypt(unsigned char *data, return FALSE; if (check_mac) { - /* Take the MAC */ - mac = data + (data_len - mac_len); - /* 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_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; } 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) : NULL)); + /* 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). */ + + /* 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); + return FALSE; + } + SILC_GET16_MSB(len, dec + totlen); + totlen += 2 + len; + if (totlen + iv_len + mac_len > data_len) { + memset(dec, 0, dlen); + silc_free(dec); + return FALSE; + } + + memcpy(data, dec, totlen); + memset(dec, 0, dlen); + silc_free(dec); + return TRUE; } @@ -134,8 +174,8 @@ silc_message_payload_parse(unsigned char *payload, if (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); @@ -150,8 +190,6 @@ silc_message_payload_parse(unsigned char *payload, &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) goto err; @@ -162,7 +200,22 @@ silc_message_payload_parse(unsigned char *payload, goto err; } - newp->iv_len = iv_len; + /* 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); + } + + /* 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; + } + if (mac_len) + newp->mac = buffer.data + (buffer.len - mac_len); return newp; @@ -174,10 +227,11 @@ silc_message_payload_parse(unsigned char *payload, /* 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. */ + 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, @@ -189,17 +243,16 @@ bool silc_message_payload_encrypt(unsigned char *data, /* 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 : NULL); + silc_cipher_encrypt(cipher, data, data, data_len, iv_len ? iv : NULL); /* Compute the MAC of the encrypted message data */ silc_hmac_init(hmac); - silc_hmac_update(hmac, data, data_len); + 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, data_len + mac_len); - silc_buffer_pull(&buf, data_len); + silc_buffer_set(&buf, data, true_len + mac_len); + silc_buffer_pull(&buf, true_len); silc_buffer_put(&buf, mac, mac_len); return TRUE; @@ -214,12 +267,16 @@ SilcBuffer silc_message_payload_encode(SilcMessageFlags flags, bool private_message, SilcCipher cipher, SilcHmac hmac, - SilcRng rng) + SilcRng rng, + SilcPublicKey public_key, + SilcPrivateKey private_key, + SilcHash hash) { int i; SilcBuffer buffer; SilcUInt32 len, pad_len = 0, mac_len = 0, iv_len = 0; unsigned char pad[16], iv[SILC_CIPHER_MAX_IV_SIZE]; + SilcBuffer sig = NULL; SILC_LOG_DEBUG(("Encoding Message Payload")); @@ -265,28 +322,57 @@ 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_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_UI_XNSTRING(iv, iv_len), SILC_STR_END); memset(pad, 0, sizeof(pad)); - /* Now encrypt the Message Payload */ + /* 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); + } + } + } + + /* 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, iv_len, cipher, hmac)) { + 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); return NULL; } } silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer) - buffer->len); + silc_buffer_free(sig); return buffer; } @@ -298,6 +384,8 @@ void silc_message_payload_free(SilcMessagePayload payload) memset(payload->data, 0, payload->data_len); silc_free(payload->data); } + if (payload->sig) + silc_message_signed_payload_free(payload->sig); silc_free(payload->pad); silc_free(payload); } @@ -333,6 +421,14 @@ 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 @@ -392,6 +488,8 @@ silc_message_signed_payload_parse(const unsigned char *data, 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) @@ -404,6 +502,8 @@ silc_message_signed_payload_parse(const unsigned char *data, 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; } @@ -416,12 +516,15 @@ silc_message_signed_payload_parse(const unsigned char *data, 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; } @@ -437,12 +540,11 @@ 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) + SilcHash hash) { SilcBuffer buffer, sign; SilcPKCS pkcs; - unsigned char auth_data[2048]; + unsigned char auth_data[2048 + 1]; SilcUInt32 auth_len; unsigned char *pk = NULL; SilcUInt32 pk_len = 0; @@ -450,10 +552,8 @@ silc_message_signed_payload_encode(const unsigned char *message_payload, if (!message_payload || !message_payload_len || !private_key || !hash) return NULL; - if (include_public_key && !public_key) - return NULL; - - if (include_public_key) + + if (public_key) pk = silc_pkcs_public_key_encode(public_key, &pk_len); /* Now we support only SILC style public key */ @@ -472,6 +572,7 @@ silc_message_signed_payload_encode(const unsigned char *message_payload, /* 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); @@ -483,6 +584,7 @@ silc_message_signed_payload_encode(const unsigned char *message_payload, 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); @@ -502,25 +604,27 @@ silc_message_signed_payload_encode(const unsigned char *message_payload, return NULL; } - silc_buffer_format(sign, + 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(sign, 4); - silc_buffer_format(sign, + silc_buffer_pull(buffer, 4); + silc_buffer_format(buffer, SILC_STR_UI_XNSTRING(pk, pk_len), SILC_STR_END); - silc_buffer_push(sign, 4); + silc_buffer_push(buffer, 4); } - silc_buffer_pull(sign, 4 + pk_len); - silc_buffer_format(sign, + 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(sign, 4 + pk_len); + 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); @@ -535,12 +639,10 @@ silc_message_signed_payload_encode(const unsigned char *message_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); - } + 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 */ @@ -559,15 +661,13 @@ int silc_message_signed_verify(SilcMessageSignedPayload sig, return ret; /* Generate the signature verification data, the Message Payload */ - tmp = silc_buffer_alloc_size(6 + message->data_len + message->pad_len + - message->iv_len); + 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_UI_XNSTRING(message->iv, message->iv_len), SILC_STR_END); sign = silc_message_signed_encode_data(tmp->data, tmp->len, sig->pk_data, sig->pk_len, @@ -612,7 +712,9 @@ int silc_message_signed_verify(SilcMessageSignedPayload sig, /* Return the public key from the payload */ SilcPublicKey -silc_message_signed_get_public_key(SilcMessageSignedPayload sig) +silc_message_signed_get_public_key(SilcMessageSignedPayload sig, + unsigned char **pk_data, + SilcUInt32 *pk_data_len) { SilcPublicKey pk; @@ -620,5 +722,10 @@ silc_message_signed_get_public_key(SilcMessageSignedPayload sig) 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; }