Protocol version 1.2 integrations
[silc.git] / lib / silccore / silcprivate.c
1 /*
2
3   silcprivate.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* Includes the Private Message Payload implementation */
21 /* $Id$ */
22
23 #include "silcincludes.h"
24 #include "silcprivate.h"
25 #include "silcprivate_i.h"
26
27 /******************************************************************************
28
29                            Private Message Payload
30
31 ******************************************************************************/
32
33 /* Parses private message payload returning new private mesage payload 
34    structure. This also decrypts the message if the `cipher' is provided. */
35
36 SilcPrivateMessagePayload 
37 silc_private_message_payload_parse(unsigned char *payload,
38                                    SilcUInt32 payload_len,
39                                    SilcCipher cipher,
40                                    SilcHmac hmac)
41 {
42   SilcBufferStruct buffer;
43   SilcPrivateMessagePayload newp;
44   SilcUInt32 mac_len = 0, block_len, pad_len = 0;
45   unsigned char data[16], mac[32];
46   int len, totlen;
47
48   SILC_LOG_DEBUG(("Parsing private message payload"));
49
50   silc_buffer_set(&buffer, payload, payload_len);
51
52   newp = silc_calloc(1, sizeof(*newp));
53   if (!newp)
54     return NULL;
55
56   /* Decrypt the payload */
57   if (cipher) {
58     /* Decrypt first block. This is to get the true length of the data in
59        payload.  It is possible there is additional data after the message
60        payload with private messages. */
61     block_len = silc_cipher_get_block_len(cipher);
62     if (block_len > buffer.len)
63       goto err;
64     silc_cipher_decrypt(cipher, buffer.data, data, block_len,
65                         silc_cipher_get_iv(cipher));
66
67     /* Length of encrypted area */
68     SILC_GET16_MSB(newp->message_len, data + 2);
69     totlen = 4 + newp->message_len;
70     pad_len = SILC_PRIVATE_MESSAGE_PAD(4 + newp->message_len);
71     totlen += pad_len;
72
73     /* Sanity checks */
74     if (totlen > buffer.len || newp->message_len < 1 ||
75         newp->message_len > buffer.len - 4) {
76       SILC_LOG_DEBUG(("Incorrect private message payload in packet"));
77       goto err;
78     }
79
80     /* Compute MAC for integrity check from the cipher text */
81     if (hmac) {
82       SILC_LOG_DEBUG(("Checking private message MAC"));
83       silc_hmac_init(hmac);
84       silc_hmac_update(hmac, buffer.data, totlen);
85       silc_hmac_final(hmac, mac, &mac_len);
86       if (memcmp(mac, buffer.data + totlen, mac_len)) {
87         SILC_LOG_DEBUG(("Private message MAC does not match"));
88         goto err;
89       }
90       SILC_LOG_DEBUG(("MAC is Ok"));
91     }
92
93     /* Now decrypt rest of the data */
94     memcpy(buffer.data, data, block_len);
95     if (totlen - block_len > 0)
96       silc_cipher_decrypt(cipher, buffer.data + block_len,
97                           buffer.data + block_len, totlen - block_len,
98                           silc_cipher_get_iv(cipher));
99     memset(data, 0, sizeof(data));
100   }
101
102   /* Parse the Private Message Payload. */
103   len = silc_buffer_unformat(&buffer,
104                              SILC_STR_UI_SHORT(&newp->flags),
105                              SILC_STR_UI16_NSTRING_ALLOC(&newp->message, 
106                                                          &newp->message_len),
107                              SILC_STR_END);
108   if (len == -1 || newp->message_len < 1 ||
109       newp->message_len > buffer.len - 4) {
110     SILC_LOG_DEBUG(("Incorrect private message payload in packet"));
111     goto err;
112   }
113
114   /* Parse also padding and MAC */
115   if (cipher) {
116     silc_buffer_pull(&buffer, 4 + newp->message_len);
117     len = silc_buffer_unformat(&buffer,
118                                SILC_STR_UI_XNSTRING_ALLOC(&newp->pad,
119                                                           pad_len),
120                                SILC_STR_UI_XNSTRING_ALLOC(&newp->mac,
121                                                           mac_len),
122                                SILC_STR_END);
123     silc_buffer_push(&buffer, 4 + newp->message_len);
124   }
125
126   return newp;
127
128  err:
129   silc_private_message_payload_free(newp);
130   return NULL;
131 }
132
133 /* Encodes private message payload into a buffer and returns it.  If
134    the cipher is provided the packet is also encrypted here.  It is provided
135    if the private message private keys are used. */
136
137 SilcBuffer silc_private_message_payload_encode(SilcUInt16 flags,
138                                                SilcUInt16 data_len,
139                                                const unsigned char *data,
140                                                SilcCipher cipher,
141                                                SilcHmac hmac,
142                                                SilcRng rng)
143 {
144   int i;
145   SilcBuffer buffer;
146   SilcUInt32 len, pad_len = 0, mac_len = 0;
147   unsigned char pad[16], mac[32];
148
149   SILC_LOG_DEBUG(("Encoding private message payload"));
150
151   data_len = SILC_PRIVATE_MESSAGE_DATALEN(data_len);
152   len = 4 + data_len;
153
154   if (cipher) {
155     /* Calculate length of padding. */
156     pad_len = SILC_PRIVATE_MESSAGE_PAD(len);
157     len += pad_len;
158     mac_len = hmac ? silc_hmac_len(hmac) : 0;
159     len += mac_len;
160
161     /* Generate padding */
162     if (rng) {
163       for (i = 0; i < pad_len; i++) pad[i] = silc_rng_get_byte_fast(rng);
164     } else {
165       for (i = 0; i < pad_len; i++) pad[i] = silc_rng_global_get_byte_fast();
166     }
167   }
168
169   /* Allocate private message payload buffer */
170   buffer = silc_buffer_alloc_size(len);
171   if (!buffer)
172     return NULL;
173
174   /* Encode the Channel Message Payload */
175   silc_buffer_format(buffer, 
176                      SILC_STR_UI_SHORT(flags),
177                      SILC_STR_UI_SHORT(data_len),
178                      SILC_STR_UI_XNSTRING(data, data_len),
179                      SILC_STR_UI_XNSTRING(pad, pad_len),
180                      SILC_STR_END);
181
182   if (cipher) {
183     /* Encrypt payload of the packet. */
184     silc_cipher_encrypt(cipher, buffer->data, buffer->data, 
185                         buffer->len - mac_len, silc_cipher_get_iv(cipher));
186     memset(pad, 0, sizeof(pad));
187
188     /* Compute MAC from the ciphertext */
189     if (hmac) {
190       silc_hmac_init(hmac);
191       silc_hmac_update(hmac, buffer->data, buffer->len - mac_len);
192       silc_hmac_final(hmac, mac, &mac_len);
193       memcpy(buffer->data + (buffer->len - mac_len), mac, mac_len);
194       memset(mac, 0, sizeof(mac));
195     }
196   }
197
198   return buffer;
199 }
200
201 /* Frees Private Message Payload */
202
203 void silc_private_message_payload_free(SilcPrivateMessagePayload payload)
204 {
205   if (payload->message) {
206     memset(payload->message, 0, payload->message_len);
207     silc_free(payload->message);
208   }
209   silc_free(payload);
210 }
211
212 /* Return flags */
213
214 SilcUInt16 
215 silc_private_message_get_flags(SilcPrivateMessagePayload payload)
216 {
217   return payload->flags;
218 }
219
220 /* Return message */
221
222 unsigned char *
223 silc_private_message_get_message(SilcPrivateMessagePayload payload,
224                                  SilcUInt32 *message_len)
225 {
226   if (message_len)
227     *message_len = payload->message_len;
228
229   return payload->message;
230 }
231
232 /* Return MAC.  Caller knows its length */
233
234 unsigned char *
235 silc_private_message_get_mac(SilcPrivateMessagePayload payload)
236 {
237   return payload->mac;
238 }