geom_aes.c revision 114491
1218822Sdim/*- 238889Sjdp * Copyright (c) 2002 Poul-Henning Kamp 3218822Sdim * Copyright (c) 2002 Networks Associates Technology, Inc. 4218822Sdim * All rights reserved. 5130561Sobrien * 6130561Sobrien * This software was developed for the FreeBSD Project by Poul-Henning Kamp 7130561Sobrien * and NAI Labs, the Security Research Division of Network Associates, Inc. 838889Sjdp * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 9130561Sobrien * DARPA CHATS research program. 10130561Sobrien * 11130561Sobrien * Redistribution and use in source and binary forms, with or without 12130561Sobrien * modification, are permitted provided that the following conditions 1338889Sjdp * are met: 14218822Sdim * 1. Redistributions of source code must retain the above copyright 15218822Sdim * notice, this list of conditions and the following disclaimer. 16218822Sdim * 2. Redistributions in binary form must reproduce the above copyright 17218822Sdim * notice, this list of conditions and the following disclaimer in the 18218822Sdim * documentation and/or other materials provided with the distribution. 19130561Sobrien * 3. The names of the authors may not be used to endorse or promote 20130561Sobrien * products derived from this software without specific prior written 21130561Sobrien * permission. 22130561Sobrien * 23130561Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24218822Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25130561Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26130561Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27130561Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28130561Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29130561Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30130561Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31218822Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32130561Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33218822Sdim * SUCH DAMAGE. 34130561Sobrien * 35218822Sdim * $FreeBSD: head/sys/geom/geom_aes.c 114491 2003-05-02 05:26:47Z phk $ 36218822Sdim * 37218822Sdim * This method provides AES encryption with a compiled in key (default 38218822Sdim * all zeroes). 39218822Sdim * 40130561Sobrien * XXX: This could probably save a lot of code by pretending to be a slicer. 41130561Sobrien */ 42130561Sobrien 43130561Sobrien#include <sys/param.h> 44130561Sobrien#include <sys/systm.h> 45130561Sobrien#include <sys/kernel.h> 46130561Sobrien#include <sys/conf.h> 47130561Sobrien#include <sys/bio.h> 48130561Sobrien#include <sys/malloc.h> 49130561Sobrien#include <sys/lock.h> 50130561Sobrien#include <sys/mutex.h> 51130561Sobrien#include <sys/libkern.h> 52130561Sobrien#include <sys/endian.h> 53130561Sobrien#include <sys/md5.h> 54130561Sobrien#include <sys/errno.h> 55130561Sobrien#include <geom/geom.h> 56130561Sobrien 57130561Sobrien#include <crypto/rijndael/rijndael.h> 58130561Sobrien 59130561Sobrien#include <crypto/rijndael/rijndael.h> 60130561Sobrien 61130561Sobrien#define AES_CLASS_NAME "AES" 62130561Sobrien 63130561Sobrien#define MASTER_KEY_LENGTH (1024/8) 64130561Sobrien 65130561Sobrienstatic const u_char *aes_magic = "<<FreeBSD-GEOM-AES>>"; 66130561Sobrienstatic const u_char *aes_magic_random = "<<FreeBSD-GEOM-AES-RANDOM>>"; 67130561Sobrienstatic const u_char *aes_magic_test = "<<FreeBSD-GEOM-AES-TEST>>"; 68130561Sobrien 69130561Sobrien 70130561Sobrienstruct g_aes_softc { 71130561Sobrien enum { 72130561Sobrien KEY_ZERO, 73130561Sobrien KEY_RANDOM, 74130561Sobrien KEY_TEST 75130561Sobrien } keying; 76130561Sobrien u_int sectorsize; 77130561Sobrien off_t mediasize; 78130561Sobrien cipherInstance ci; 79130561Sobrien u_char master_key[MASTER_KEY_LENGTH]; 80130561Sobrien}; 81130561Sobrien 82130561Sobrien/* 83130561Sobrien * Generate a sectorkey from the masterkey and the offset position. 84130561Sobrien * 85130561Sobrien * For KEY_ZERO we just return a key of all zeros. 86218822Sdim * 87130561Sobrien * We feed the sector byte offset, 16 bytes of the master-key and 88218822Sdim * the sector byte offset once more to MD5. 89218822Sdim * The sector byte offset is converted to little-endian format first 90218822Sdim * to support multi-architecture operation. 91218822Sdim * We use 16 bytes from the master-key starting at the logical sector 92218822Sdim * number modulus he length of the master-key. If need be we wrap 93218822Sdim * around to the start of the master-key. 94130561Sobrien */ 95218822Sdim 96130561Sobrienstatic void 97130561Sobrieng_aes_makekey(struct g_aes_softc *sc, off_t off, keyInstance *ki, int dir) 98130561Sobrien{ 99130561Sobrien MD5_CTX cx; 100130561Sobrien u_int64_t u64; 101130561Sobrien u_int u, u1; 102130561Sobrien u_char *p, buf[16]; 103130561Sobrien 104130561Sobrien if (sc->keying == KEY_ZERO) { 105130561Sobrien rijndael_makeKey(ki, dir, 128, sc->master_key); 106130561Sobrien return; 107130561Sobrien } 108130561Sobrien MD5Init(&cx); 109130561Sobrien u64 = htole64(off); 110130561Sobrien MD5Update(&cx, (u_char *)&u64, sizeof(u64)); 111130561Sobrien u = off / sc->sectorsize; 11260484Sobrien u %= sizeof sc->master_key; 113130561Sobrien p = sc->master_key + u; 114130561Sobrien if (u + 16 <= sizeof(sc->master_key)) { 115218822Sdim MD5Update(&cx, p, 16); 116218822Sdim } else { 117130561Sobrien u1 = sizeof sc->master_key - u; 11838889Sjdp MD5Update(&cx, p, u1); 119130561Sobrien MD5Update(&cx, sc->master_key, 16 - u1); 120218822Sdim u1 = 0; /* destroy evidence */ 121130561Sobrien } 122218822Sdim u = 0; /* destroy evidence */ 123218822Sdim MD5Update(&cx, (u_char *)&u64, sizeof(u64)); 124218822Sdim u64 = 0; /* destroy evidence */ 125218822Sdim MD5Final(buf, &cx); 126130561Sobrien bzero(&cx, sizeof cx); /* destroy evidence */ 127218822Sdim rijndael_makeKey(ki, dir, 128, buf); 128130561Sobrien bzero(buf, sizeof buf); /* destroy evidence */ 129130561Sobrien 130130561Sobrien} 131130561Sobrien 132130561Sobrienstatic void 133130561Sobrieng_aes_read_done(struct bio *bp) 134130561Sobrien{ 135130561Sobrien struct g_geom *gp; 136130561Sobrien struct g_aes_softc *sc; 137130561Sobrien u_char *p, *b, *e, *sb; 138130561Sobrien keyInstance dkey; 139130561Sobrien off_t o; 140130561Sobrien 141130561Sobrien gp = bp->bio_from->geom; 142130561Sobrien sc = gp->softc; 143130561Sobrien sb = g_malloc(sc->sectorsize, M_WAITOK); 144130561Sobrien b = bp->bio_data; 145130561Sobrien e = bp->bio_data; 146130561Sobrien e += bp->bio_length; 147130561Sobrien o = bp->bio_offset - sc->sectorsize; 148130561Sobrien for (p = b; p < e; p += sc->sectorsize) { 149130561Sobrien g_aes_makekey(sc, o, &dkey, DIR_DECRYPT); 150130561Sobrien rijndael_blockDecrypt(&sc->ci, &dkey, p, sc->sectorsize * 8, sb); 151130561Sobrien bcopy(sb, p, sc->sectorsize); 152130561Sobrien o += sc->sectorsize; 153130561Sobrien } 154130561Sobrien bzero(&dkey, sizeof dkey); /* destroy evidence */ 155130561Sobrien bzero(sb, sc->sectorsize); /* destroy evidence */ 156130561Sobrien g_free(sb); 157130561Sobrien g_std_done(bp); 158130561Sobrien} 159130561Sobrien 160130561Sobrienstatic void 161130561Sobrieng_aes_write_done(struct bio *bp) 162130561Sobrien{ 163130561Sobrien struct g_aes_softc *sc; 164130561Sobrien struct g_geom *gp; 165130561Sobrien 166130561Sobrien gp = bp->bio_to->geom; 167130561Sobrien sc = gp->softc; 168130561Sobrien bzero(bp->bio_data, bp->bio_length); /* destroy evidence */ 169130561Sobrien g_free(bp->bio_data); 170130561Sobrien g_std_done(bp); 171130561Sobrien} 172130561Sobrien 173130561Sobrienstatic void 174130561Sobrieng_aes_start(struct bio *bp) 175130561Sobrien{ 176130561Sobrien struct g_geom *gp; 177130561Sobrien struct g_consumer *cp; 178130561Sobrien struct g_aes_softc *sc; 179130561Sobrien struct bio *bp2; 180130561Sobrien u_char *p1, *p2, *b, *e; 181130561Sobrien keyInstance ekey; 182130561Sobrien off_t o; 183130561Sobrien 184130561Sobrien gp = bp->bio_to->geom; 185130561Sobrien cp = LIST_FIRST(&gp->consumer); 186130561Sobrien sc = gp->softc; 187130561Sobrien switch (bp->bio_cmd) { 188130561Sobrien case BIO_READ: 189130561Sobrien bp2 = g_clone_bio(bp); 190130561Sobrien if (bp2 == NULL) { 191130561Sobrien g_io_deliver(bp, ENOMEM); 192130561Sobrien return; 193130561Sobrien } 194130561Sobrien bp2->bio_done = g_aes_read_done; 195130561Sobrien bp2->bio_offset += sc->sectorsize; 196130561Sobrien g_io_request(bp2, cp); 197130561Sobrien break; 198130561Sobrien case BIO_WRITE: 199130561Sobrien bp2 = g_clone_bio(bp); 200130561Sobrien if (bp2 == NULL) { 201130561Sobrien g_io_deliver(bp, ENOMEM); 202130561Sobrien return; 203130561Sobrien } 204130561Sobrien bp2->bio_done = g_aes_write_done; 205130561Sobrien bp2->bio_offset += sc->sectorsize; 206130561Sobrien bp2->bio_data = g_malloc(bp->bio_length, M_WAITOK); 207130561Sobrien b = bp->bio_data; 208130561Sobrien e = bp->bio_data; 209130561Sobrien e += bp->bio_length; 210130561Sobrien p2 = bp2->bio_data; 211130561Sobrien o = bp->bio_offset; 212130561Sobrien for (p1 = b; p1 < e; p1 += sc->sectorsize) { 213130561Sobrien g_aes_makekey(sc, o, &ekey, DIR_ENCRYPT); 214130561Sobrien rijndael_blockEncrypt(&sc->ci, &ekey, 215130561Sobrien p1, sc->sectorsize * 8, p2); 216130561Sobrien p2 += sc->sectorsize; 217130561Sobrien o += sc->sectorsize; 218130561Sobrien } 219130561Sobrien bzero(&ekey, sizeof ekey); /* destroy evidence */ 220130561Sobrien g_io_request(bp2, cp); 221130561Sobrien break; 222130561Sobrien case BIO_GETATTR: 223130561Sobrien bp2 = g_clone_bio(bp); 224130561Sobrien if (bp2 == NULL) { 225130561Sobrien g_io_deliver(bp, ENOMEM); 226130561Sobrien return; 227130561Sobrien } 228130561Sobrien bp2->bio_done = g_std_done; 229130561Sobrien bp2->bio_offset += sc->sectorsize; 230130561Sobrien g_io_request(bp2, cp); 231130561Sobrien break; 232130561Sobrien default: 233130561Sobrien g_io_deliver(bp, EOPNOTSUPP); 234130561Sobrien return; 235130561Sobrien } 236130561Sobrien return; 237130561Sobrien} 238130561Sobrien 239130561Sobrienstatic void 240130561Sobrieng_aes_orphan(struct g_consumer *cp) 241130561Sobrien{ 242130561Sobrien struct g_geom *gp; 24377298Sobrien struct g_provider *pp; 244130561Sobrien struct g_aes_softc *sc; 245130561Sobrien int error; 246130561Sobrien 247130561Sobrien g_trace(G_T_TOPOLOGY, "g_aes_orphan(%p/%s)", cp, cp->provider->name); 248130561Sobrien g_topology_assert(); 24977298Sobrien KASSERT(cp->provider->error != 0, 250130561Sobrien ("g_aes_orphan with error == 0")); 251130561Sobrien 252130561Sobrien gp = cp->geom; 253130561Sobrien sc = gp->softc; 254130561Sobrien gp->flags |= G_GEOM_WITHER; 255130561Sobrien error = cp->provider->error; 256130561Sobrien LIST_FOREACH(pp, &gp->provider, provider) 257130561Sobrien g_orphan_provider(pp, error); 25877298Sobrien bzero(sc, sizeof(struct g_aes_softc)); /* destroy evidence */ 25977298Sobrien return; 26038889Sjdp} 261130561Sobrien 262130561Sobrienstatic int 263130561Sobrieng_aes_access(struct g_provider *pp, int dr, int dw, int de) 264130561Sobrien{ 265130561Sobrien struct g_geom *gp; 266130561Sobrien struct g_consumer *cp; 267130561Sobrien 268130561Sobrien gp = pp->geom; 269130561Sobrien cp = LIST_FIRST(&gp->consumer); 270130561Sobrien /* On first open, grab an extra "exclusive" bit */ 271130561Sobrien if (cp->acr == 0 && cp->acw == 0 && cp->ace == 0) 272130561Sobrien de++; 273130561Sobrien /* ... and let go of it on last close */ 27438889Sjdp if ((cp->acr + dr) == 0 && (cp->acw + dw) == 0 && (cp->ace + de) == 1) 275218822Sdim de--; 27638889Sjdp return (g_access_rel(cp, dr, dw, de)); 277218822Sdim} 278218822Sdim 279218822Sdimstatic struct g_geom * 280218822Sdimg_aes_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 281218822Sdim{ 282218822Sdim struct g_geom *gp; 283130561Sobrien struct g_consumer *cp; 284218822Sdim struct g_aes_softc *sc; 285130561Sobrien int error; 286130561Sobrien u_int sectorsize; 287130561Sobrien off_t mediasize; 288130561Sobrien u_char *buf; 289130561Sobrien 290130561Sobrien g_trace(G_T_TOPOLOGY, "aes_taste(%s,%s)", mp->name, pp->name); 291130561Sobrien g_topology_assert(); 292130561Sobrien gp = g_new_geomf(mp, "%s.aes", pp->name); 293130561Sobrien gp->start = g_aes_start; 294130561Sobrien gp->orphan = g_aes_orphan; 295130561Sobrien gp->spoiled = g_std_spoiled; 296130561Sobrien cp = g_new_consumer(gp); 297130561Sobrien g_attach(cp, pp); 298130561Sobrien error = g_access_rel(cp, 1, 0, 0); 299130561Sobrien if (error) { 300130561Sobrien g_detach(cp); 301130561Sobrien g_destroy_consumer(cp); 302130561Sobrien g_destroy_geom(gp); 303218822Sdim return (NULL); 304218822Sdim } 305130561Sobrien buf = NULL; 306130561Sobrien g_topology_unlock(); 307218822Sdim do { 308218822Sdim if (gp->rank != 2) 309218822Sdim break; 310130561Sobrien sectorsize = cp->provider->sectorsize; 311130561Sobrien mediasize = cp->provider->mediasize; 312218822Sdim buf = g_read_data(cp, 0, sectorsize, &error); 313218822Sdim if (buf == NULL || error != 0) { 314218822Sdim break; 315218822Sdim } 316218822Sdim sc = g_malloc(sizeof(struct g_aes_softc), M_WAITOK | M_ZERO); 317218822Sdim if (!memcmp(buf, aes_magic, strlen(aes_magic))) { 318130561Sobrien sc->keying = KEY_ZERO; 319130561Sobrien } else if (!memcmp(buf, aes_magic_random, 320130561Sobrien strlen(aes_magic_random))) { 321130561Sobrien sc->keying = KEY_RANDOM; 322130561Sobrien } else if (!memcmp(buf, aes_magic_test, 323130561Sobrien strlen(aes_magic_test))) { 324130561Sobrien sc->keying = KEY_TEST; 325130561Sobrien } else { 326130561Sobrien g_free(sc); 327130561Sobrien break; 328130561Sobrien } 329130561Sobrien gp->softc = sc; 330130561Sobrien gp->access = g_aes_access; 331130561Sobrien sc->sectorsize = sectorsize; 332130561Sobrien sc->mediasize = mediasize - sectorsize; 333130561Sobrien rijndael_cipherInit(&sc->ci, MODE_CBC, NULL); 334130561Sobrien if (sc->keying == KEY_TEST) { 335130561Sobrien int i; 336130561Sobrien u_char *p; 337130561Sobrien 338130561Sobrien p = sc->master_key; 339130561Sobrien for (i = 0; i < (int)sizeof sc->master_key; i ++) 340130561Sobrien *p++ = i; 341130561Sobrien } 342130561Sobrien if (sc->keying == KEY_RANDOM) { 343218822Sdim int i; 344218822Sdim u_int32_t u; 345218822Sdim u_char *p; 346218822Sdim 347218822Sdim p = sc->master_key; 348218822Sdim for (i = 0; i < (int)sizeof sc->master_key; i += sizeof u) { 349130561Sobrien u = arc4random(); 350218822Sdim *p++ = u; 351130561Sobrien *p++ = u >> 8; 352130561Sobrien *p++ = u >> 16; 353130561Sobrien *p++ = u >> 24; 354130561Sobrien } 355218822Sdim } 356130561Sobrien g_topology_lock(); 357218822Sdim pp = g_new_providerf(gp, gp->name); 358130561Sobrien pp->mediasize = mediasize - sectorsize; 359218822Sdim pp->sectorsize = sectorsize; 360218822Sdim g_error_provider(pp, 0); 361218822Sdim g_topology_unlock(); 362218822Sdim } while(0); 363130561Sobrien g_topology_lock(); 364218822Sdim if (buf) 365130561Sobrien g_free(buf); 366218822Sdim g_access_rel(cp, -1, 0, 0); 367218822Sdim if (gp->softc != NULL) 368130561Sobrien return (gp); 369130561Sobrien g_detach(cp); 370130561Sobrien g_destroy_consumer(cp); 371130561Sobrien g_destroy_geom(gp); 372130561Sobrien return (NULL); 373130561Sobrien} 374130561Sobrien 375130561Sobrienstatic struct g_class g_aes_class = { 376130561Sobrien .name = AES_CLASS_NAME, 377130561Sobrien .taste = g_aes_taste, 378130561Sobrien G_CLASS_INITIALIZER 379130561Sobrien}; 38094536Sobrien 381130561SobrienDECLARE_GEOM_CLASS(g_aes_class, g_aes); 382130561Sobrien