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> 37#include <sys/syslog.h> 38#include <sys/mbuf.h> 39 40#include <kern/locks.h> 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 49#include <crypto/aes/aes.h> 50 51#include <netkey/key.h> 52 53#include <net/net_osdep.h> 54 55#define AES_BLOCKLEN 16 56 57extern lck_mtx_t *sadb_mutex; 58 59int 60esp_aes_schedlen( 61 __unused const struct esp_algorithm *algo) 62{ 63 64 return sizeof(aes_ctx); 65} 66 67int 68esp_aes_schedule( 69 __unused const struct esp_algorithm *algo, 70 struct secasvar *sav) 71{ 72 73 lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED); 74 aes_ctx *ctx = (aes_ctx*)sav->sched; 75 76 aes_decrypt_key(_KEYBUF(sav->key_enc), _KEYLEN(sav->key_enc), &ctx->decrypt); 77 aes_encrypt_key(_KEYBUF(sav->key_enc), _KEYLEN(sav->key_enc), &ctx->encrypt); 78 79 return 0; 80} 81 82 83/* The following 2 functions decrypt or encrypt the contents of 84 * the mbuf chain passed in keeping the IP and ESP header's in place, 85 * along with the IV. 86 * The code attempts to call the crypto code with the largest chunk 87 * of data it can based on the amount of source data in 88 * the current source mbuf and the space remaining in the current 89 * destination mbuf. The crypto code requires data to be a multiples 90 * of 16 bytes. A separate buffer is used when a 16 byte block spans 91 * mbufs. 92 * 93 * m = mbuf chain 94 * off = offset to ESP header 95 * 96 * local vars for source: 97 * soff = offset from beginning of the chain to the head of the 98 * current mbuf. 99 * scut = last mbuf that contains headers to be retained 100 * scutoff = offset to end of the headers in scut 101 * s = the current mbuf 102 * sn = current offset to data in s (next source data to process) 103 * 104 * local vars for dest: 105 * d0 = head of chain 106 * d = current mbuf 107 * dn = current offset in d (next location to store result) 108 */ 109 110 111int 112esp_cbc_decrypt_aes(m, off, sav, algo, ivlen) 113 struct mbuf *m; 114 size_t off; 115 struct secasvar *sav; 116 const struct esp_algorithm *algo; 117 int ivlen; 118{ 119 struct mbuf *s; 120 struct mbuf *d, *d0, *dp; 121 int soff; /* offset from the head of chain, to head of this mbuf */ 122 int sn, dn; /* offset from the head of the mbuf, to meat */ 123 size_t ivoff, bodyoff; 124 u_int8_t iv[AES_BLOCKLEN], *dptr; 125 u_int8_t sbuf[AES_BLOCKLEN], *sp; 126 struct mbuf *scut; 127 int scutoff; 128 int i, len; 129 130 131 if (ivlen != AES_BLOCKLEN) { 132 ipseclog((LOG_ERR, "esp_cbc_decrypt %s: " 133 "unsupported ivlen %d\n", algo->name, ivlen)); 134 m_freem(m); 135 return EINVAL; 136 } 137 138 if (sav->flags & SADB_X_EXT_OLD) { 139 /* RFC 1827 */ 140 ivoff = off + sizeof(struct esp); 141 bodyoff = off + sizeof(struct esp) + ivlen; 142 } else { 143 ivoff = off + sizeof(struct newesp); 144 bodyoff = off + sizeof(struct newesp) + ivlen; 145 } 146 147 if (m->m_pkthdr.len < bodyoff) { 148 ipseclog((LOG_ERR, "esp_cbc_decrypt %s: bad len %d/%lu\n", 149 algo->name, m->m_pkthdr.len, (unsigned long)bodyoff)); 150 m_freem(m); 151 return EINVAL; 152 } 153 if ((m->m_pkthdr.len - bodyoff) % AES_BLOCKLEN) { 154 ipseclog((LOG_ERR, "esp_cbc_decrypt %s: " 155 "payload length must be multiple of %d\n", 156 algo->name, AES_BLOCKLEN)); 157 m_freem(m); 158 return EINVAL; 159 } 160 161 /* grab iv */ 162 m_copydata(m, ivoff, ivlen, iv); 163 164 s = m; 165 soff = sn = dn = 0; 166 d = d0 = dp = NULL; 167 sp = dptr = NULL; 168 169 /* skip header/IV offset */ 170 while (soff < bodyoff) { 171 if (soff + s->m_len > bodyoff) { 172 sn = bodyoff - soff; 173 break; 174 } 175 176 soff += s->m_len; 177 s = s->m_next; 178 } 179 scut = s; 180 scutoff = sn; 181 182 /* skip over empty mbuf */ 183 while (s && s->m_len == 0) 184 s = s->m_next; 185 186 while (soff < m->m_pkthdr.len) { 187 /* source */ 188 if (sn + AES_BLOCKLEN <= s->m_len) { 189 /* body is continuous */ 190 sp = mtod(s, u_int8_t *) + sn; 191 len = s->m_len - sn; 192 len -= len % AES_BLOCKLEN; // full blocks only 193 } else { 194 /* body is non-continuous */ 195 m_copydata(s, sn, AES_BLOCKLEN, sbuf); 196 sp = sbuf; 197 len = AES_BLOCKLEN; // 1 block only in sbuf 198 } 199 200 /* destination */ 201 if (!d || dn + AES_BLOCKLEN > d->m_len) { 202 if (d) 203 dp = d; 204 MGET(d, M_DONTWAIT, MT_DATA); 205 i = m->m_pkthdr.len - (soff + sn); 206 if (d && i > MLEN) { 207 MCLGET(d, M_DONTWAIT); 208 if ((d->m_flags & M_EXT) == 0) { 209 d = m_mbigget(d, M_DONTWAIT); 210 if ((d->m_flags & M_EXT) == 0) { 211 m_free(d); 212 d = NULL; 213 } 214 } 215 } 216 if (!d) { 217 m_freem(m); 218 if (d0) 219 m_freem(d0); 220 return ENOBUFS; 221 } 222 if (!d0) 223 d0 = d; 224 if (dp) 225 dp->m_next = d; 226 d->m_len = M_TRAILINGSPACE(d); 227 d->m_len -= d->m_len % AES_BLOCKLEN; 228 if (d->m_len > i) 229 d->m_len = i; 230 dptr = mtod(d, u_int8_t *); 231 dn = 0; 232 } 233 234 /* adjust len if greater than space available in dest */ 235 if (len > d->m_len - dn) 236 len = d->m_len - dn; 237 238 /* decrypt */ 239 aes_decrypt_cbc(sp, iv, len >> 4, dptr + dn, 240 (aes_decrypt_ctx*)(&(((aes_ctx*)sav->sched)->decrypt))); 241 242 /* udpate offsets */ 243 sn += len; 244 dn += len; 245 246 // next iv 247 bcopy(sp + len - AES_BLOCKLEN, iv, AES_BLOCKLEN); 248 249 /* find the next source block */ 250 while (s && sn >= s->m_len) { 251 sn -= s->m_len; 252 soff += s->m_len; 253 s = s->m_next; 254 } 255 256 } 257 258 /* free un-needed source mbufs and add dest mbufs to chain */ 259 m_freem(scut->m_next); 260 scut->m_len = scutoff; 261 scut->m_next = d0; 262 263 /* just in case */ 264 bzero(iv, sizeof(iv)); 265 bzero(sbuf, sizeof(sbuf)); 266 267 return 0; 268} 269 270int 271esp_cbc_encrypt_aes( 272 struct mbuf *m, 273 size_t off, 274 __unused size_t plen, 275 struct secasvar *sav, 276 const struct esp_algorithm *algo, 277 int ivlen) 278{ 279 struct mbuf *s; 280 struct mbuf *d, *d0, *dp; 281 int soff; /* offset from the head of chain, to head of this mbuf */ 282 int sn, dn; /* offset from the head of the mbuf, to meat */ 283 size_t ivoff, bodyoff; 284 u_int8_t *ivp, *dptr; 285 u_int8_t sbuf[AES_BLOCKLEN], *sp; 286 struct mbuf *scut; 287 int scutoff; 288 int i, len; 289 290 if (ivlen != AES_BLOCKLEN) { 291 ipseclog((LOG_ERR, "esp_cbc_encrypt %s: " 292 "unsupported ivlen %d\n", algo->name, ivlen)); 293 m_freem(m); 294 return EINVAL; 295 } 296 297 if (sav->flags & SADB_X_EXT_OLD) { 298 /* RFC 1827 */ 299 ivoff = off + sizeof(struct esp); 300 bodyoff = off + sizeof(struct esp) + ivlen; 301 } else { 302 ivoff = off + sizeof(struct newesp); 303 bodyoff = off + sizeof(struct newesp) + ivlen; 304 } 305 306 /* put iv into the packet */ 307 m_copyback(m, ivoff, ivlen, sav->iv); 308 ivp = sav->iv; 309 310 if (m->m_pkthdr.len < bodyoff) { 311 ipseclog((LOG_ERR, "esp_cbc_encrypt %s: bad len %d/%lu\n", 312 algo->name, m->m_pkthdr.len, (unsigned long)bodyoff)); 313 m_freem(m); 314 return EINVAL; 315 } 316 if ((m->m_pkthdr.len - bodyoff) % AES_BLOCKLEN) { 317 ipseclog((LOG_ERR, "esp_cbc_encrypt %s: " 318 "payload length must be multiple of %lu\n", 319 algo->name, AES_BLOCKLEN)); 320 m_freem(m); 321 return EINVAL; 322 } 323 324 s = m; 325 soff = sn = dn = 0; 326 d = d0 = dp = NULL; 327 sp = dptr = NULL; 328 329 /* skip headers/IV */ 330 while (soff < bodyoff) { 331 if (soff + s->m_len > bodyoff) { 332 sn = bodyoff - soff; 333 break; 334 } 335 336 soff += s->m_len; 337 s = s->m_next; 338 } 339 scut = s; 340 scutoff = sn; 341 342 /* skip over empty mbuf */ 343 while (s && s->m_len == 0) 344 s = s->m_next; 345 346 while (soff < m->m_pkthdr.len) { 347 /* source */ 348 if (sn + AES_BLOCKLEN <= s->m_len) { 349 /* body is continuous */ 350 sp = mtod(s, u_int8_t *) + sn; 351 len = s->m_len - sn; 352 len -= len % AES_BLOCKLEN; // full blocks only 353 } else { 354 /* body is non-continuous */ 355 m_copydata(s, sn, AES_BLOCKLEN, sbuf); 356 sp = sbuf; 357 len = AES_BLOCKLEN; // 1 block only in sbuf 358 } 359 360 /* destination */ 361 if (!d || dn + AES_BLOCKLEN > d->m_len) { 362 if (d) 363 dp = d; 364 MGET(d, M_DONTWAIT, MT_DATA); 365 i = m->m_pkthdr.len - (soff + sn); 366 if (d && i > MLEN) { 367 MCLGET(d, M_DONTWAIT); 368 if ((d->m_flags & M_EXT) == 0) { 369 d = m_mbigget(d, M_DONTWAIT); 370 if ((d->m_flags & M_EXT) == 0) { 371 m_free(d); 372 d = NULL; 373 } 374 } 375 } 376 if (!d) { 377 m_freem(m); 378 if (d0) 379 m_freem(d0); 380 return ENOBUFS; 381 } 382 if (!d0) 383 d0 = d; 384 if (dp) 385 dp->m_next = d; 386 387 d->m_len = M_TRAILINGSPACE(d); 388 d->m_len -= d->m_len % AES_BLOCKLEN; 389 if (d->m_len > i) 390 d->m_len = i; 391 dptr = mtod(d, u_int8_t *); 392 dn = 0; 393 } 394 395 /* adjust len if greater than space available */ 396 if (len > d->m_len - dn) 397 len = d->m_len - dn; 398 399 /* encrypt */ 400 aes_encrypt_cbc(sp, ivp, len >> 4, dptr + dn, 401 (aes_encrypt_ctx*)(&(((aes_ctx*)sav->sched)->encrypt))); 402 403 /* update offsets */ 404 sn += len; 405 dn += len; 406 407 /* next iv */ 408 ivp = dptr + dn - AES_BLOCKLEN; // last block encrypted 409 410 /* find the next source block and skip empty mbufs */ 411 while (s && sn >= s->m_len) { 412 sn -= s->m_len; 413 soff += s->m_len; 414 s = s->m_next; 415 } 416 417 } 418 419 /* free un-needed source mbufs and add dest mbufs to chain */ 420 m_freem(scut->m_next); 421 scut->m_len = scutoff; 422 scut->m_next = d0; 423 424 /* just in case */ 425 bzero(sbuf, sizeof(sbuf)); 426 key_sa_stir_iv(sav); 427 428 return 0; 429} 430