X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilccore%2Fsilcmessage.c;h=b1e3894d9179ac104e253f0c1851803eb8dd78e4;hb=1ea936cbf1bb3b19bd55839b904ef59ada84b8b5;hp=2bb3ef9e1a62026968339d587edcad3bd6d005ce;hpb=0e0503a0c47a3950e0c8e65af965499e3ac270aa;p=silc.git diff --git a/lib/silccore/silcmessage.c b/lib/silccore/silcmessage.c index 2bb3ef9e..b1e3894d 100644 --- a/lib/silccore/silcmessage.c +++ b/lib/silccore/silcmessage.c @@ -36,19 +36,45 @@ /* Payload encoding context */ typedef struct { + SilcBuffer buffer; + SilcBuffer sign; + SilcStack stack; + SilcMessagePayloadEncoded encoded; + void *context; SilcMessageFlags flags; SilcPublicKey public_key; SilcPrivateKey private_key; + SilcRng rng; SilcHash hash; SilcCipher cipher; SilcHmac hmac; unsigned char *iv; + unsigned char *pk; + SilcUInt16 pk_len; + SilcUInt16 iv_len; SilcUInt16 payload_len; + SilcID sid; + SilcID rid; } SilcMessageEncode; /************************* Static utility functions *************************/ +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); + /* Returns the data length that fits to the packet. If data length is too big it will be truncated to fit to the payload. */ @@ -138,126 +164,184 @@ silc_message_signed_payload_parse(const unsigned char *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, +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_alloc_size(message_payload_len + 4 + pk_len); + sign = silc_buffer_salloc_size(stack, 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); + 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_format(sign, - SILC_STR_UI_XNSTRING(pk, pk_len), - SILC_STR_END); + 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 SilcBuffer -silc_message_signed_payload_encode(const unsigned char *message_payload, - SilcUInt32 message_payload_len, - SilcPublicKey public_key, - SilcPrivateKey private_key, - SilcHash hash) +static SilcAsyncOperation +silc_message_signed_payload_encode(SilcBuffer payload, + SilcMessageEncode *e) { - SilcBuffer buffer, sign; - unsigned char auth_data[2048 + 1]; - SilcUInt32 auth_len; + SilcAsyncOperation op; + SilcBuffer sign; unsigned char *pk = NULL; SilcUInt32 pk_len = 0; SilcUInt16 pk_type; - - if (!message_payload || !message_payload_len || !private_key || !hash) + 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) { - pk = silc_pkcs_public_key_encode(public_key, &pk_len); - if (!pk) + 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 */ - sign = silc_message_signed_encode_data(message_payload, - message_payload_len, - pk, pk_len, pk_type); + e->sign = sign = silc_message_signed_encode_data(stack, 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, - TRUE, 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); + e->encoded(NULL, e->context); + silc_sfree(stack, pk); + silc_sfree(stack, e); + silc_stack_free(stack); return NULL; } - silc_buffer_format(buffer, - SILC_STR_UI_SHORT(pk_len), - SILC_STR_UI_SHORT(pk_type), - SILC_STR_END); + /* 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); - 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; + return op; } - /***************************** Payload parsing ******************************/ /* Decrypts the Message Payload. The `data' is the actual Message Payload. */ @@ -268,6 +352,10 @@ SilcBool silc_message_payload_decrypt(unsigned char *data, 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; @@ -290,10 +378,19 @@ SilcBool silc_message_payload_decrypt(unsigned char *data, 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 (silc_unlikely(memcmp(data + (data_len - mac_len), mac, mac_len))) { - SILC_LOG_DEBUG(("Message MAC does not match")); - return FALSE; + /* 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")); } @@ -342,6 +439,10 @@ silc_message_payload_parse(unsigned char *payload, SilcBool static_key, SilcCipher cipher, 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) @@ -359,7 +460,9 @@ silc_message_payload_parse(unsigned char *payload, if (silc_likely(cipher)) { ret = silc_message_payload_decrypt(buffer.data, silc_buffer_len(&buffer), private_message, static_key, - cipher, hmac, TRUE); + cipher, hmac, sender_id, + sender_id_len, receiver_id, + receiver_id_len, TRUE); if (silc_unlikely(ret == FALSE)) return NULL; } @@ -447,16 +550,33 @@ 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 sid[32], rid[32]; + SilcUInt32 sid_len = 0, rid_len = 0; + /* 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_update(hmac, sid, sid_len); + silc_hmac_update(hmac, rid, rid_len); silc_hmac_final(hmac, data + true_len, NULL); return TRUE; @@ -464,7 +584,8 @@ SilcBool silc_message_payload_encrypt(unsigned char *data, /* Encrypt message payload */ -static int silc_message_payload_encode_encrypt(SilcBuffer buffer, +static int silc_message_payload_encode_encrypt(SilcStack stack, + SilcBuffer buffer, void *value, void *context) { SilcMessageEncode *e = context; @@ -480,78 +601,108 @@ static int silc_message_payload_encode_encrypt(SilcBuffer buffer, if (silc_unlikely(!silc_message_payload_encrypt(buffer->head, e->payload_len, silc_buffer_headlen(buffer), - e->iv, e->cipher, e->hmac))) + e->iv, &e->sid, &e->rid, + e->cipher, e->hmac))) return -1; return mac_len; } -/* Compute message signature */ - -static int silc_message_payload_encode_sig(SilcBuffer buffer, - void *value, void *context) +/* 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 = context; - SilcBuffer sig; - int len; - - if (!(e->flags & SILC_MESSAGE_FLAG_SIGNED)) - return 0; + SilcMessageEncode e; - sig = silc_message_signed_payload_encode(buffer->head, - silc_buffer_headlen(buffer), - e->public_key, e->private_key, - e->hash); - if (silc_unlikely(!sig)) - return -1; + 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; - len = silc_buffer_format(buffer, - SILC_STR_DATA(silc_buffer_data(sig), - silc_buffer_len(sig)), - SILC_STR_END); - if (silc_unlikely(len < 0)) { - silc_buffer_free(sig); - return -1; + /* 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; } - silc_buffer_free(sig); - return len; + /* 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, - SilcBool generate_iv, - SilcBool private_message, - SilcCipher cipher, - SilcHmac hmac, - SilcRng rng, - SilcPublicKey public_key, - SilcPrivateKey private_key, - SilcHash hash, - SilcBuffer buffer) +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) { SilcUInt32 pad_len = 0, mac_len = 0, iv_len = 0; unsigned char pad[16], iv[SILC_CIPHER_MAX_IV_SIZE]; - SilcBuffer buf = NULL; - SilcMessageEncode e; + SilcBuffer buffer; int i; SILC_LOG_DEBUG(("Encoding Message Payload")); - if (silc_unlikely(!data_len)) + if (silc_unlikely(!data_len)) { + encoded(NULL, context); return NULL; - if (silc_unlikely(!private_message && (!cipher || !hmac))) + } + if (silc_unlikely(!private_message && (!cipher || !hmac))) { + encoded(NULL, context); return NULL; + } - if (!buffer) { - buf = buffer = silc_buffer_alloc(0); - if (silc_unlikely(!buf)) - 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; } - silc_buffer_reset(buffer); /* For channel messages IV is always generated */ if (!private_message && !generate_iv) @@ -585,39 +736,65 @@ SilcBuffer silc_message_payload_encode(SilcMessageFlags flags, } } - e.flags = flags; - e.public_key = public_key; - e.private_key = private_key; - e.hash = hash; - e.cipher = cipher; - e.hmac = hmac; - e.iv = iv_len ? iv : NULL; - e.payload_len = 6 + data_len + pad_len; - /* Encode the Message Payload */ 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_FUNC(silc_message_payload_encode_sig, - NULL, &e), - SILC_STR_DATA(iv, iv_len), - SILC_STR_FUNC(silc_message_payload_encode_encrypt, - NULL, &e), SILC_STR_END) < 0) { - silc_buffer_free(buf); + silc_buffer_sfree(stack, buffer); + encoded(NULL, context); + silc_stack_free(stack); return NULL; } - return buffer; + 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); + } + + /* 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); if (payload->allocated) @@ -627,7 +804,6 @@ void silc_message_payload_free(SilcMessagePayload payload) silc_free(payload->pad); silc_free(payload); } - silc_message_signed_payload_free(&payload->sig); } /* Return flags */ @@ -656,53 +832,60 @@ unsigned char *silc_message_get_mac(SilcMessagePayload payload) /* Verify the signature in SILC_MESSAGE_FLAG_SIGNED Payload */ -SilcAuthResult silc_message_signed_verify(SilcMessagePayload message, - SilcPublicKey remote_public_key, - SilcHash hash) +SilcAsyncOperation +silc_message_signed_verify(SilcMessagePayload message, + SilcPublicKey remote_public_key, + SilcHash hash, + SilcAuthResultCb result, + void *context) { - int ret = SILC_AUTH_FAILED; + 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) - return ret; + !sig->sign_len || !remote_public_key || !hash) { + result(FALSE, context); + return NULL; + } + + 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, silc_buffer_len(tmp), + 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; + silc_buffer_sfree(stack, tmp); - /* Verify the authentication data */ - if (!silc_pkcs_verify(remote_public_key, sig->sign_data, sig->sign_len, - silc_buffer_data(sign), silc_buffer_len(sign), hash)) { - silc_buffer_clear(sign); - silc_buffer_free(sign); - 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_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 */