]>
Commit | Line | Data |
---|---|---|
9bccf70c A |
1 | /* $FreeBSD: src/sys/netinet6/esp_rijndael.c,v 1.1.2.1 2001/07/03 11:01:50 ume Exp $ */ |
2 | /* $KAME: esp_rijndael.c,v 1.4 2001/03/02 05:53:05 itojun Exp $ */ | |
3 | ||
4 | /* | |
5 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. | |
6 | * All rights reserved. | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
16 | * 3. Neither the name of the project nor the names of its contributors | |
17 | * may be used to endorse or promote products derived from this software | |
18 | * without specific prior written permission. | |
19 | * | |
20 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND | |
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE | |
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
30 | * SUCH DAMAGE. | |
31 | */ | |
32 | ||
33 | #include <sys/param.h> | |
34 | #include <sys/systm.h> | |
35 | #include <sys/socket.h> | |
36 | #include <sys/queue.h> | |
91447636 A |
37 | #include <sys/syslog.h> |
38 | #include <sys/mbuf.h> | |
39 | ||
40 | #include <kern/locks.h> | |
9bccf70c A |
41 | |
42 | #include <net/if.h> | |
43 | #include <net/route.h> | |
44 | ||
45 | #include <netinet6/ipsec.h> | |
46 | #include <netinet6/esp.h> | |
47 | #include <netinet6/esp_rijndael.h> | |
48 | ||
91447636 | 49 | #include <crypto/aes/aes.h> |
9bccf70c A |
50 | |
51 | #include <net/net_osdep.h> | |
52 | ||
91447636 A |
53 | #define AES_BLOCKLEN 16 |
54 | ||
55 | extern lck_mtx_t *sadb_mutex; | |
56 | ||
9bccf70c | 57 | int |
91447636 | 58 | esp_aes_schedlen(algo) |
9bccf70c A |
59 | const struct esp_algorithm *algo; |
60 | { | |
61 | ||
91447636 | 62 | return sizeof(aes_ctx); |
9bccf70c A |
63 | } |
64 | ||
65 | int | |
91447636 | 66 | esp_aes_schedule(algo, sav) |
9bccf70c A |
67 | const struct esp_algorithm *algo; |
68 | struct secasvar *sav; | |
69 | { | |
91447636 A |
70 | aes_ctx *ctx = (aes_ctx*)sav->sched; |
71 | ||
72 | gen_tabs(); | |
73 | aes_decrypt_key(_KEYBUF(sav->key_enc), _KEYLEN(sav->key_enc), &ctx->decrypt); | |
74 | aes_encrypt_key(_KEYBUF(sav->key_enc), _KEYLEN(sav->key_enc), &ctx->encrypt); | |
75 | ||
9bccf70c A |
76 | return 0; |
77 | } | |
78 | ||
91447636 A |
79 | |
80 | /* The following 2 functions decrypt or encrypt the contents of | |
81 | * the mbuf chain passed in keeping the IP and ESP header's in place, | |
82 | * along with the IV. | |
83 | * The code attempts to call the crypto code with the largest chunk | |
84 | * of data it can based on the amount of source data in | |
85 | * the current source mbuf and the space remaining in the current | |
86 | * destination mbuf. The crypto code requires data to be a multiples | |
87 | * of 16 bytes. A separate buffer is used when a 16 byte block spans | |
88 | * mbufs. | |
89 | * | |
90 | * m = mbuf chain | |
91 | * off = offset to ESP header | |
92 | * | |
93 | * local vars for source: | |
94 | * soff = offset from beginning of the chain to the head of the | |
95 | * current mbuf. | |
96 | * scut = last mbuf that contains headers to be retained | |
97 | * scutoff = offset to end of the headers in scut | |
98 | * s = the current mbuf | |
99 | * sn = current offset to data in s (next source data to process) | |
100 | * | |
101 | * local vars for dest: | |
102 | * d0 = head of chain | |
103 | * d = current mbuf | |
104 | * dn = current offset in d (next location to store result) | |
105 | */ | |
106 | ||
107 | ||
9bccf70c | 108 | int |
91447636 A |
109 | esp_cbc_decrypt_aes(m, off, sav, algo, ivlen) |
110 | struct mbuf *m; | |
111 | size_t off; | |
9bccf70c | 112 | struct secasvar *sav; |
91447636 A |
113 | const struct esp_algorithm *algo; |
114 | int ivlen; | |
9bccf70c | 115 | { |
91447636 A |
116 | struct mbuf *s; |
117 | struct mbuf *d, *d0, *dp; | |
118 | int soff; /* offset from the head of chain, to head of this mbuf */ | |
119 | int sn, dn; /* offset from the head of the mbuf, to meat */ | |
120 | size_t ivoff, bodyoff; | |
121 | u_int8_t iv[AES_BLOCKLEN], *dptr; | |
122 | u_int8_t sbuf[AES_BLOCKLEN], *sp; | |
123 | struct mbuf *scut; | |
124 | int scutoff; | |
125 | int i, len; | |
126 | ||
127 | ||
128 | if (ivlen != AES_BLOCKLEN) { | |
129 | ipseclog((LOG_ERR, "esp_cbc_decrypt %s: " | |
130 | "unsupported ivlen %d\n", algo->name, ivlen)); | |
131 | m_freem(m); | |
132 | return EINVAL; | |
133 | } | |
134 | ||
135 | if (sav->flags & SADB_X_EXT_OLD) { | |
136 | /* RFC 1827 */ | |
137 | ivoff = off + sizeof(struct esp); | |
138 | bodyoff = off + sizeof(struct esp) + ivlen; | |
139 | } else { | |
140 | ivoff = off + sizeof(struct newesp); | |
141 | bodyoff = off + sizeof(struct newesp) + ivlen; | |
142 | } | |
143 | ||
144 | if (m->m_pkthdr.len < bodyoff) { | |
145 | ipseclog((LOG_ERR, "esp_cbc_decrypt %s: bad len %d/%lu\n", | |
146 | algo->name, m->m_pkthdr.len, (unsigned long)bodyoff)); | |
147 | m_freem(m); | |
148 | return EINVAL; | |
149 | } | |
150 | if ((m->m_pkthdr.len - bodyoff) % AES_BLOCKLEN) { | |
151 | ipseclog((LOG_ERR, "esp_cbc_decrypt %s: " | |
152 | "payload length must be multiple of %d\n", | |
153 | algo->name, AES_BLOCKLEN)); | |
154 | m_freem(m); | |
155 | return EINVAL; | |
156 | } | |
157 | ||
158 | /* grab iv */ | |
159 | m_copydata(m, ivoff, ivlen, iv); | |
160 | ||
161 | lck_mtx_unlock(sadb_mutex); | |
162 | s = m; | |
163 | soff = sn = dn = 0; | |
164 | d = d0 = dp = NULL; | |
165 | sp = dptr = NULL; | |
166 | ||
167 | /* skip header/IV offset */ | |
168 | while (soff < bodyoff) { | |
169 | if (soff + s->m_len > bodyoff) { | |
170 | sn = bodyoff - soff; | |
171 | break; | |
172 | } | |
173 | ||
174 | soff += s->m_len; | |
175 | s = s->m_next; | |
176 | } | |
177 | scut = s; | |
178 | scutoff = sn; | |
179 | ||
180 | /* skip over empty mbuf */ | |
181 | while (s && s->m_len == 0) | |
182 | s = s->m_next; | |
183 | ||
184 | while (soff < m->m_pkthdr.len) { | |
185 | /* source */ | |
186 | if (sn + AES_BLOCKLEN <= s->m_len) { | |
187 | /* body is continuous */ | |
188 | sp = mtod(s, u_int8_t *) + sn; | |
189 | len = s->m_len - sn; | |
190 | len -= len % AES_BLOCKLEN; // full blocks only | |
191 | } else { | |
192 | /* body is non-continuous */ | |
193 | m_copydata(s, sn, AES_BLOCKLEN, sbuf); | |
194 | sp = sbuf; | |
195 | len = AES_BLOCKLEN; // 1 block only in sbuf | |
196 | } | |
197 | ||
198 | /* destination */ | |
199 | if (!d || dn + AES_BLOCKLEN > d->m_len) { | |
200 | if (d) | |
201 | dp = d; | |
202 | MGET(d, M_DONTWAIT, MT_DATA); | |
203 | i = m->m_pkthdr.len - (soff + sn); | |
204 | if (d && i > MLEN) { | |
205 | MCLGET(d, M_DONTWAIT); | |
206 | if ((d->m_flags & M_EXT) == 0) { | |
207 | m_free(d); | |
208 | d = NULL; | |
209 | } | |
210 | } | |
211 | if (!d) { | |
212 | m_freem(m); | |
213 | if (d0) | |
214 | m_freem(d0); | |
215 | lck_mtx_lock(sadb_mutex); | |
216 | return ENOBUFS; | |
217 | } | |
218 | if (!d0) | |
219 | d0 = d; | |
220 | if (dp) | |
221 | dp->m_next = d; | |
222 | d->m_len = M_TRAILINGSPACE(d); | |
223 | d->m_len -= d->m_len % AES_BLOCKLEN; | |
224 | if (d->m_len > i) | |
225 | d->m_len = i; | |
226 | dptr = mtod(d, u_int8_t *); | |
227 | dn = 0; | |
228 | } | |
229 | ||
230 | /* adjust len if greater than space available in dest */ | |
231 | if (len > d->m_len - dn) | |
232 | len = d->m_len - dn; | |
233 | ||
234 | /* decrypt */ | |
235 | aes_decrypt_cbc(sp, iv, len >> 4, dptr + dn, | |
236 | (aes_decrypt_ctx*)(&(((aes_ctx*)sav->sched)->decrypt))); | |
237 | ||
238 | /* udpate offsets */ | |
239 | sn += len; | |
240 | dn += len; | |
241 | ||
242 | // next iv | |
243 | bcopy(sp + len - AES_BLOCKLEN, iv, AES_BLOCKLEN); | |
244 | ||
245 | /* find the next source block */ | |
246 | while (s && sn >= s->m_len) { | |
247 | sn -= s->m_len; | |
248 | soff += s->m_len; | |
249 | s = s->m_next; | |
250 | } | |
251 | ||
252 | } | |
253 | ||
254 | /* free un-needed source mbufs and add dest mbufs to chain */ | |
255 | m_freem(scut->m_next); | |
256 | scut->m_len = scutoff; | |
257 | scut->m_next = d0; | |
258 | ||
259 | /* just in case */ | |
260 | bzero(iv, sizeof(iv)); | |
261 | bzero(sbuf, sizeof(sbuf)); | |
262 | lck_mtx_lock(sadb_mutex); | |
263 | ||
9bccf70c A |
264 | return 0; |
265 | } | |
266 | ||
267 | int | |
91447636 A |
268 | esp_cbc_encrypt_aes(m, off, plen, sav, algo, ivlen) |
269 | struct mbuf *m; | |
270 | size_t off; | |
271 | size_t plen; | |
9bccf70c | 272 | struct secasvar *sav; |
91447636 A |
273 | const struct esp_algorithm *algo; |
274 | int ivlen; | |
9bccf70c | 275 | { |
91447636 A |
276 | struct mbuf *s; |
277 | struct mbuf *d, *d0, *dp; | |
278 | int soff, doff; /* offset from the head of chain, to head of this mbuf */ | |
279 | int sn, dn; /* offset from the head of the mbuf, to meat */ | |
280 | size_t ivoff, bodyoff; | |
281 | u_int8_t *ivp, *dptr; | |
282 | u_int8_t sbuf[AES_BLOCKLEN], *sp; | |
283 | struct mbuf *scut; | |
284 | int scutoff; | |
285 | int i, len; | |
286 | ||
287 | if (ivlen != AES_BLOCKLEN) { | |
288 | ipseclog((LOG_ERR, "esp_cbc_encrypt %s: " | |
289 | "unsupported ivlen %d\n", algo->name, ivlen)); | |
290 | m_freem(m); | |
291 | return EINVAL; | |
292 | } | |
293 | ||
294 | if (sav->flags & SADB_X_EXT_OLD) { | |
295 | /* RFC 1827 */ | |
296 | ivoff = off + sizeof(struct esp); | |
297 | bodyoff = off + sizeof(struct esp) + ivlen; | |
298 | } else { | |
299 | ivoff = off + sizeof(struct newesp); | |
300 | bodyoff = off + sizeof(struct newesp) + ivlen; | |
301 | } | |
302 | ||
303 | /* put iv into the packet */ | |
304 | m_copyback(m, ivoff, ivlen, sav->iv); | |
305 | ivp = sav->iv; | |
306 | ||
307 | if (m->m_pkthdr.len < bodyoff) { | |
308 | ipseclog((LOG_ERR, "esp_cbc_encrypt %s: bad len %d/%lu\n", | |
309 | algo->name, m->m_pkthdr.len, (unsigned long)bodyoff)); | |
310 | m_freem(m); | |
311 | return EINVAL; | |
312 | } | |
313 | if ((m->m_pkthdr.len - bodyoff) % AES_BLOCKLEN) { | |
314 | ipseclog((LOG_ERR, "esp_cbc_encrypt %s: " | |
315 | "payload length must be multiple of %lu\n", | |
316 | algo->name, AES_BLOCKLEN)); | |
317 | m_freem(m); | |
318 | return EINVAL; | |
319 | } | |
320 | lck_mtx_unlock(sadb_mutex); | |
321 | ||
322 | s = m; | |
323 | soff = sn = dn = 0; | |
324 | d = d0 = dp = NULL; | |
325 | sp = dptr = NULL; | |
326 | ||
327 | /* skip headers/IV */ | |
328 | while (soff < bodyoff) { | |
329 | if (soff + s->m_len > bodyoff) { | |
330 | sn = bodyoff - soff; | |
331 | break; | |
332 | } | |
333 | ||
334 | soff += s->m_len; | |
335 | s = s->m_next; | |
336 | } | |
337 | scut = s; | |
338 | scutoff = sn; | |
339 | ||
340 | /* skip over empty mbuf */ | |
341 | while (s && s->m_len == 0) | |
342 | s = s->m_next; | |
343 | ||
344 | while (soff < m->m_pkthdr.len) { | |
345 | /* source */ | |
346 | if (sn + AES_BLOCKLEN <= s->m_len) { | |
347 | /* body is continuous */ | |
348 | sp = mtod(s, u_int8_t *) + sn; | |
349 | len = s->m_len - sn; | |
350 | len -= len % AES_BLOCKLEN; // full blocks only | |
351 | } else { | |
352 | /* body is non-continuous */ | |
353 | m_copydata(s, sn, AES_BLOCKLEN, sbuf); | |
354 | sp = sbuf; | |
355 | len = AES_BLOCKLEN; // 1 block only in sbuf | |
356 | } | |
357 | ||
358 | /* destination */ | |
359 | if (!d || dn + AES_BLOCKLEN > d->m_len) { | |
360 | if (d) | |
361 | dp = d; | |
362 | MGET(d, M_DONTWAIT, MT_DATA); | |
363 | i = m->m_pkthdr.len - (soff + sn); | |
364 | if (d && i > MLEN) { | |
365 | MCLGET(d, M_DONTWAIT); | |
366 | if ((d->m_flags & M_EXT) == 0) { | |
367 | m_free(d); | |
368 | d = NULL; | |
369 | } | |
370 | } | |
371 | if (!d) { | |
372 | m_freem(m); | |
373 | if (d0) | |
374 | m_freem(d0); | |
375 | lck_mtx_lock(sadb_mutex); | |
376 | return ENOBUFS; | |
377 | } | |
378 | if (!d0) | |
379 | d0 = d; | |
380 | if (dp) | |
381 | dp->m_next = d; | |
382 | ||
383 | d->m_len = M_TRAILINGSPACE(d); | |
384 | d->m_len -= d->m_len % AES_BLOCKLEN; | |
385 | if (d->m_len > i) | |
386 | d->m_len = i; | |
387 | dptr = mtod(d, u_int8_t *); | |
388 | dn = 0; | |
389 | } | |
390 | ||
391 | /* adjust len if greater than space available */ | |
392 | if (len > d->m_len - dn) | |
393 | len = d->m_len - dn; | |
394 | ||
395 | /* encrypt */ | |
396 | aes_encrypt_cbc(sp, ivp, len >> 4, dptr + dn, | |
397 | (aes_encrypt_ctx*)(&(((aes_ctx*)sav->sched)->encrypt))); | |
398 | ||
399 | /* update offsets */ | |
400 | sn += len; | |
401 | dn += len; | |
402 | ||
403 | /* next iv */ | |
404 | ivp = dptr + dn - AES_BLOCKLEN; // last block encrypted | |
405 | ||
406 | /* find the next source block and skip empty mbufs */ | |
407 | while (s && sn >= s->m_len) { | |
408 | sn -= s->m_len; | |
409 | soff += s->m_len; | |
410 | s = s->m_next; | |
411 | } | |
412 | ||
413 | } | |
414 | ||
415 | /* free un-needed source mbufs and add dest mbufs to chain */ | |
416 | m_freem(scut->m_next); | |
417 | scut->m_len = scutoff; | |
418 | scut->m_next = d0; | |
419 | ||
420 | /* just in case */ | |
421 | bzero(sbuf, sizeof(sbuf)); | |
422 | lck_mtx_lock(sadb_mutex); | |
423 | key_sa_stir_iv(sav); | |
424 | ||
9bccf70c A |
425 | return 0; |
426 | } |