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