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