44b1117d9240c34a37170e804c3058a4f39e2be3
[strongswan.git] / src / libipsec / esp_context.c
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include <limits.h>
19 #include <stdint.h>
20
21 #include "esp_context.h"
22
23 #include <library.h>
24 #include <utils/debug.h>
25 #include <crypto/crypters/crypter.h>
26 #include <crypto/signers/signer.h>
27
28 /**
29 * Should be a multiple of 8
30 */
31 #define ESP_DEFAULT_WINDOW_SIZE 128
32
33 typedef struct private_esp_context_t private_esp_context_t;
34
35 /**
36 * Private additions to esp_context_t.
37 */
38 struct private_esp_context_t {
39
40 /**
41 * Public members
42 */
43 esp_context_t public;
44
45 /**
46 * Crypter used to encrypt/decrypt ESP packets
47 */
48 crypter_t *crypter;
49
50 /**
51 * Signer to authenticate ESP packets
52 */
53 signer_t *signer;
54
55 /**
56 * The highest sequence number that was successfully verified
57 * and authenticated, or assigned in an outbound context
58 */
59 u_int32_t last_seqno;
60
61 /**
62 * The bit in the window of the highest authenticated sequence number
63 */
64 u_int seqno_index;
65
66 /**
67 * The size of the anti-replay window (in bits)
68 */
69 u_int window_size;
70
71 /**
72 * The anti-replay window buffer
73 */
74 chunk_t window;
75
76 /**
77 * TRUE in case of an inbound ESP context
78 */
79 bool inbound;
80 };
81
82 /**
83 * Set or unset a bit in the window.
84 */
85 static inline void set_window_bit(private_esp_context_t *this,
86 u_int index, bool set)
87 {
88 u_int i = index / CHAR_BIT;
89
90 if (set)
91 {
92 this->window.ptr[i] |= 1 << (index % CHAR_BIT);
93 }
94 else
95 {
96 this->window.ptr[i] &= ~(1 << (index % CHAR_BIT));
97 }
98 }
99
100 /**
101 * Get a bit from the window.
102 */
103 static inline bool get_window_bit(private_esp_context_t *this, u_int index)
104 {
105 u_int i = index / CHAR_BIT;
106
107 return this->window.ptr[i] & (1 << index % CHAR_BIT);
108 }
109
110 /**
111 * Returns TRUE if the supplied seqno is not already marked in the window
112 */
113 static bool check_window(private_esp_context_t *this, u_int32_t seqno)
114 {
115 u_int offset;
116
117 offset = this->last_seqno - seqno;
118 offset = (this->seqno_index - offset) % this->window_size;
119 return !get_window_bit(this, offset);
120 }
121
122 METHOD(esp_context_t, verify_seqno, bool,
123 private_esp_context_t *this, u_int32_t seqno)
124 {
125 if (!this->inbound)
126 {
127 return FALSE;
128 }
129
130 if (seqno > this->last_seqno)
131 { /* |----------------------------------------|
132 * <---------^ ^ or <---------^ ^
133 * WIN H S WIN H S
134 */
135 return TRUE;
136 }
137 else if (seqno > 0 && this->window_size > this->last_seqno - seqno)
138 { /* |----------------------------------------|
139 * <---------^ or <---------^
140 * WIN ^ H WIN ^ H
141 * S S
142 */
143 return check_window(this, seqno);
144 }
145 else
146 { /* |----------------------------------------|
147 * ^ <---------^
148 * S WIN H
149 */
150 return FALSE;
151 }
152 }
153
154 METHOD(esp_context_t, set_authenticated_seqno, void,
155 private_esp_context_t *this, u_int32_t seqno)
156 {
157 u_int i, shift;
158
159 if (!this->inbound)
160 {
161 return;
162 }
163
164 if (seqno > this->last_seqno)
165 { /* shift the window to the new highest authenticated seqno */
166 shift = seqno - this->last_seqno;
167 shift = shift < this->window_size ? shift : this->window_size;
168 for (i = 0; i < shift; ++i)
169 {
170 this->seqno_index = (this->seqno_index + 1) % this->window_size;
171 set_window_bit(this, this->seqno_index, FALSE);
172 }
173 set_window_bit(this, this->seqno_index, TRUE);
174 this->last_seqno = seqno;
175 }
176 else
177 { /* seqno is inside the window, set the corresponding window bit */
178 i = this->last_seqno - seqno;
179 set_window_bit(this, (this->seqno_index - i) % this->window_size, TRUE);
180 }
181 }
182
183 METHOD(esp_context_t, get_seqno, u_int32_t,
184 private_esp_context_t *this)
185 {
186 return this->last_seqno;
187 }
188
189 METHOD(esp_context_t, next_seqno, bool,
190 private_esp_context_t *this, u_int32_t *seqno)
191 {
192 if (this->inbound || this->last_seqno == UINT32_MAX)
193 { /* inbound or segno would cycle */
194 return FALSE;
195 }
196 *seqno = ++this->last_seqno;
197 return TRUE;
198 }
199
200 METHOD(esp_context_t, get_signer, signer_t *,
201 private_esp_context_t *this)
202 {
203 return this->signer;
204 }
205
206 METHOD(esp_context_t, get_crypter, crypter_t *,
207 private_esp_context_t *this)
208 {
209 return this->crypter;
210 }
211
212 METHOD(esp_context_t, destroy, void,
213 private_esp_context_t *this)
214 {
215 chunk_free(&this->window);
216 DESTROY_IF(this->crypter);
217 DESTROY_IF(this->signer);
218 free(this);
219 }
220
221 /**
222 * Described in header.
223 */
224 esp_context_t *esp_context_create(int enc_alg, chunk_t enc_key,
225 int int_alg, chunk_t int_key, bool inbound)
226 {
227 private_esp_context_t *this;
228
229 INIT(this,
230 .public = {
231 .get_crypter = _get_crypter,
232 .get_signer = _get_signer,
233 .get_seqno = _get_seqno,
234 .next_seqno = _next_seqno,
235 .verify_seqno = _verify_seqno,
236 .set_authenticated_seqno = _set_authenticated_seqno,
237 .destroy = _destroy,
238 },
239 .inbound = inbound,
240 .window_size = ESP_DEFAULT_WINDOW_SIZE,
241 );
242
243 switch(enc_alg)
244 {
245 case ENCR_AES_CBC:
246 this->crypter = lib->crypto->create_crypter(lib->crypto, enc_alg,
247 enc_key.len);
248 break;
249 default:
250 break;
251 }
252 if (!this->crypter)
253 {
254 DBG1(DBG_ESP, "failed to create ESP context: unsupported encryption "
255 "algorithm");
256 destroy(this);
257 return NULL;
258 }
259 if (!this->crypter->set_key(this->crypter, enc_key))
260 {
261 DBG1(DBG_ESP, "failed to create ESP context: setting encryption key "
262 "failed");
263 destroy(this);
264 return NULL;
265 }
266
267 switch(int_alg)
268 {
269 case AUTH_HMAC_SHA1_96:
270 case AUTH_HMAC_SHA2_256_128:
271 case AUTH_HMAC_SHA2_384_192:
272 case AUTH_HMAC_SHA2_512_256:
273 this->signer = lib->crypto->create_signer(lib->crypto, int_alg);
274 break;
275 default:
276 break;
277 }
278 if (!this->signer)
279 {
280 DBG1(DBG_ESP, "failed to create ESP context: unsupported integrity "
281 "algorithm");
282 destroy(this);
283 return NULL;
284 }
285 if (!this->signer->set_key(this->signer, int_key))
286 {
287 DBG1(DBG_ESP, "failed to create ESP context: setting signature key "
288 "failed");
289 destroy(this);
290 return NULL;
291 }
292
293 if (inbound)
294 {
295 this->window = chunk_alloc(this->window_size / CHAR_BIT + 1);
296 memset(this->window.ptr, 0, this->window.len);
297 }
298 return &this->public;
299 }
300
301