Don't allocate extra memory to MAC the TLS header
[strongswan.git] / src / libtls / tls_protection.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "tls_protection.h"
17
18 #include <debug.h>
19
20 typedef struct private_tls_protection_t private_tls_protection_t;
21
22 /**
23 * Private data of an tls_protection_t object.
24 */
25 struct private_tls_protection_t {
26
27 /**
28 * Public tls_protection_t interface.
29 */
30 tls_protection_t public;
31
32 /**
33 * negotiated TLS version
34 */
35 tls_version_t version;
36
37 /**
38 * Upper layer, TLS record compression
39 */
40 tls_compression_t *compression;
41
42 /**
43 * TLS alert handler
44 */
45 tls_alert_t *alert;
46
47 /**
48 * RNG if we generate IVs ourself
49 */
50 rng_t *rng;
51
52 /**
53 * Sequence number of incoming records
54 */
55 u_int32_t seq_in;
56
57 /**
58 * Sequence number for outgoing records
59 */
60 u_int32_t seq_out;
61
62 /**
63 * Signer instance for inbound traffic
64 */
65 signer_t *signer_in;
66
67 /**
68 * Signer instance for outbound traffic
69 */
70 signer_t *signer_out;
71
72 /**
73 * Crypter instance for inbound traffic
74 */
75 crypter_t *crypter_in;
76
77 /**
78 * Crypter instance for outbound traffic
79 */
80 crypter_t *crypter_out;
81
82 /**
83 * Current IV for input decryption
84 */
85 chunk_t iv_in;
86
87 /**
88 * Current IV for output decryption
89 */
90 chunk_t iv_out;
91 };
92
93 /**
94 * Create the header and feed it into a signer for MAC verification
95 */
96 static void sigheader(signer_t *signer, u_int32_t seq, u_int8_t type,
97 u_int16_t version, u_int16_t length)
98 {
99 /* we only support 32 bit sequence numbers, but TLS uses 64 bit */
100 struct __attribute__((__packed__)) {
101 u_int32_t seq_high;
102 u_int32_t seq_low;
103 u_int8_t type;
104 u_int16_t version;
105 u_int16_t length;
106 } header = {
107 .type = type,
108 };
109 htoun32(&header.seq_low, seq);
110 htoun16(&header.version, version);
111 htoun16(&header.length, length);
112
113 signer->get_signature(signer, chunk_from_thing(header), NULL);
114 }
115
116 METHOD(tls_protection_t, process, status_t,
117 private_tls_protection_t *this, tls_content_type_t type, chunk_t data)
118 {
119 if (this->alert->fatal(this->alert))
120 { /* don't accept more input, fatal error occurred */
121 return NEED_MORE;
122 }
123
124 if (this->crypter_in)
125 {
126 chunk_t iv, next_iv = chunk_empty;
127 u_int8_t bs, padding_length;
128
129 bs = this->crypter_in->get_block_size(this->crypter_in);
130 if (this->iv_in.len)
131 { /* < TLSv1.1 uses IV from key derivation/last block */
132 if (data.len < bs || data.len % bs)
133 {
134 DBG1(DBG_TLS, "encrypted TLS record length invalid");
135 this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC);
136 return NEED_MORE;
137 }
138 iv = this->iv_in;
139 next_iv = chunk_clone(chunk_create(data.ptr + data.len - bs, bs));
140 }
141 else
142 { /* TLSv1.1 uses random IVs, prepended to record */
143 iv.len = this->crypter_in->get_iv_size(this->crypter_in);
144 iv = chunk_create(data.ptr, iv.len);
145 data = chunk_skip(data, iv.len);
146 if (data.len < bs || data.len % bs)
147 {
148 DBG1(DBG_TLS, "encrypted TLS record length invalid");
149 this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC);
150 return NEED_MORE;
151 }
152 }
153 this->crypter_in->decrypt(this->crypter_in, data, iv, NULL);
154
155 if (next_iv.len)
156 { /* next record IV is last ciphertext block of this record */
157 memcpy(this->iv_in.ptr, next_iv.ptr, next_iv.len);
158 free(next_iv.ptr);
159 }
160
161 padding_length = data.ptr[data.len - 1];
162 if (padding_length < data.len)
163 { /* remove padding if it looks valid. Continue with no padding, try
164 * to prevent timing attacks. */
165 data.len -= padding_length + 1;
166 }
167 }
168 if (this->signer_in)
169 {
170 chunk_t mac;
171 u_int8_t bs;
172
173 bs = this->signer_in->get_block_size(this->signer_in);
174 if (data.len < bs)
175 {
176 DBG1(DBG_TLS, "TLS record too short to verify MAC");
177 this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC);
178 return NEED_MORE;
179 }
180 mac = chunk_skip(data, data.len - bs);
181 data.len -= bs;
182
183 sigheader(this->signer_in, this->seq_in, type, this->version, data.len);
184 if (!this->signer_in->verify_signature(this->signer_in, data, mac))
185 {
186 DBG1(DBG_TLS, "TLS record MAC verification failed");
187 this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC);
188 return NEED_MORE;
189 }
190 }
191
192 if (type == TLS_CHANGE_CIPHER_SPEC)
193 {
194 this->seq_in = 0;
195 }
196 else
197 {
198 this->seq_in++;
199 }
200 return this->compression->process(this->compression, type, data);
201 }
202
203 METHOD(tls_protection_t, build, status_t,
204 private_tls_protection_t *this, tls_content_type_t *type, chunk_t *data)
205 {
206 status_t status;
207
208 status = this->compression->build(this->compression, type, data);
209 if (*type == TLS_CHANGE_CIPHER_SPEC)
210 {
211 this->seq_out = 0;
212 return status;
213 }
214
215 if (status == NEED_MORE)
216 {
217 if (this->signer_out)
218 {
219 chunk_t mac;
220
221 sigheader(this->signer_out, this->seq_out, *type,
222 this->version, data->len);
223 this->signer_out->allocate_signature(this->signer_out, *data, &mac);
224 if (this->crypter_out)
225 {
226 chunk_t padding, iv;
227 u_int8_t bs, padding_length;
228
229 bs = this->crypter_out->get_block_size(this->crypter_out);
230 padding_length = bs - ((data->len + mac.len + 1) % bs);
231
232 padding = chunk_alloca(padding_length);
233 memset(padding.ptr, padding_length, padding.len);
234
235 if (this->iv_out.len)
236 { /* < TLSv1.1 uses IV from key derivation/last block */
237 iv = this->iv_out;
238 }
239 else
240 { /* TLSv1.1 uses random IVs, prepended to record */
241 if (!this->rng)
242 {
243 DBG1(DBG_TLS, "no RNG supported to generate TLS IV");
244 free(data->ptr);
245 return FAILED;
246 }
247 iv.len = this->crypter_out->get_iv_size(this->crypter_out);
248 this->rng->allocate_bytes(this->rng, iv.len, &iv);
249 }
250
251 *data = chunk_cat("mmcc", *data, mac, padding,
252 chunk_from_thing(padding_length));
253 /* encrypt inline */
254 this->crypter_out->encrypt(this->crypter_out, *data, iv, NULL);
255
256 if (this->iv_out.len)
257 { /* next record IV is last ciphertext block of this record */
258 memcpy(this->iv_out.ptr, data->ptr + data->len -
259 this->iv_out.len, this->iv_out.len);
260 }
261 else
262 { /* prepend IV */
263 *data = chunk_cat("mm", iv, *data);
264 }
265 }
266 else
267 { /* NULL encryption */
268 *data = chunk_cat("mm", *data, mac);
269 }
270 }
271 this->seq_out++;
272 }
273 return status;
274 }
275
276 METHOD(tls_protection_t, set_cipher, void,
277 private_tls_protection_t *this, bool inbound, signer_t *signer,
278 crypter_t *crypter, chunk_t iv)
279 {
280 if (inbound)
281 {
282 this->signer_in = signer;
283 this->crypter_in = crypter;
284 this->iv_in = iv;
285 }
286 else
287 {
288 this->signer_out = signer;
289 this->crypter_out = crypter;
290 this->iv_out = iv;
291 if (!iv.len)
292 { /* generate IVs if none given */
293 this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
294 }
295 }
296 }
297
298 METHOD(tls_protection_t, set_version, void,
299 private_tls_protection_t *this, tls_version_t version)
300 {
301 this->version = version;
302 }
303
304 METHOD(tls_protection_t, destroy, void,
305 private_tls_protection_t *this)
306 {
307 DESTROY_IF(this->rng);
308 free(this);
309 }
310
311 /**
312 * See header
313 */
314 tls_protection_t *tls_protection_create(tls_compression_t *compression,
315 tls_alert_t *alert)
316 {
317 private_tls_protection_t *this;
318
319 INIT(this,
320 .public = {
321 .process = _process,
322 .build = _build,
323 .set_cipher = _set_cipher,
324 .set_version = _set_version,
325 .destroy = _destroy,
326 },
327 .alert = alert,
328 .compression = compression,
329 );
330
331 return &this->public;
332 }