After playing with a few encrypted filesystems, and giving up on them (after
a kernel crash or two), I went looking for something else to encrypt.  The
logical choice is the device.

Well, the virtual device.  Like a cryptfs that's based on a loopback mount,
I'm encrypting a virtual device based on the "vn" driver.  This was only a
few hours' work, though it's admittedly incomplete.  This is based on the
Blowfish code in the kernel used by ipsec and such, which an extra ioctl
added to set the key.  Only three source files require modification:

sys/sys/vnioctl.h:
  Define the VNIOCSETKEY ioctl

usr.sbin/vnconfig/vnconfig.c:
  Add a "-k" option to specify that an encryption key should be entered via
  getpass(), and passed in with the above ioctl.

sys/dev/vn/vn.c
  Add a blowfish key entry to the softc structure.  This is set via the
  above ioctl, which converts a passed-in string into the key data.
  Encryption is done around the vn's VOP_READ and VOP_WRITE calls, in
  512-byte CBC chunks.

That's it - 90 lines of new code.  This is for my purposes complete and
working, which is to say neither is quite true.  For production-quality
code, some work remains:

* The vn.c code is tied to blowfish.  It would be better to have some
  dynamically selectable encryption scheme.  I like blowfish and it's
  available, but others may want something else.  The vnconfig hack doesn't
  contain any mention of an encryption scheme - it just passes a key string.

* It doesn't work with swap-backed vn devices.  It wouldn't be hard to put
  the same encrypt-decrypt wrappers around the vm_pager_strategy() call, or
  perhaps even to merely move the wrappers further out to encompass both
  vnode and object access methods.

* It doesn't work with labels, probably because of some kernel function(s)
  that make their own read/write calls outside of the vnstrategy's own
  VOP_READ and VOP_WRITE.  To build a new filesystem, I had to newfs a
  swap-backed vn and dd it to the encrypted vn.  But then I can mount the
  encrypted vn without worrying about labels.

* It requires the blowfish functions to be linked into the kernel, which I
  accomplished by changing them from "optional ... ipsec" to "standard" in
  the kernel.  I suppose I could have somehoe linked them to vn.ko directly,
  but that seems the wrong way around.  So even though vn is a module, it
  requires a kernel recompile to use it, unless you're running ipsec or
  ipv6.

The diffs for what I have are small.  If someone wants to make it (more)
complete, the further diffs should be small as well.

- Jamie





--- sys/sys/vnioctl.h.orig      Wed Sep 25 16:38:53 2002
+++ sys/sys/vnioctl.h   Tue Sep 24 22:17:26 2002
@@ -68,6 +68,9 @@
 #define VNIOCGCLEAR    _IOWR('F', 3, u_long )          /* reset --//-- */
 #define VNIOCUSET      _IOWR('F', 4, u_long )          /* set unit option */
 #define VNIOCUCLEAR    _IOWR('F', 5, u_long )          /* reset --//-- */
+#ifdef VNCRYPT
+#define VNIOCSETKEY    _IOW('F', 47, char * )          /* set key */
+#endif
 
 #define VN_LABELS      0x1     /* Use disk(/slice) labels */
 #define VN_FOLLOW      0x2     /* Debug flow in vn driver */
--- usr.sbin/vnconfig/vnconfig.c.orig   Wed Sep 25 16:38:38 2002
+++ usr.sbin/vnconfig/vnconfig.c        Wed Sep 25 16:43:44 2002
@@ -88,6 +88,9 @@
 #define VN_RESET       0x200
 #define VN_TRUNCATE    0x400
 #define VN_ZERO                0x800
+#ifdef VNCRYPT
+#define VN_SETKEY      0x1000
+#endif
 
 int nvndisks;
 
@@ -118,7 +121,11 @@
        char *s;
 
        configfile = _PATH_VNTAB;
+#ifdef VNCRYPT
+       while ((i = getopt(argc, argv, "acdef:gkr:s:S:TZL:uv")) != -1)
+#else
        while ((i = getopt(argc, argv, "acdef:gr:s:S:TZL:uv")) != -1)
+#endif
                switch (i) {
 
                /* all -- use config file */
@@ -154,6 +161,13 @@
                        global = 1 - global;
                        break;
 
+#ifdef VNCRYPT
+               /* set key */
+               case 'k':
+                       flags |= VN_SETKEY;
+                       break;
+#endif
+
                /* reset options */
                case 'r':
                        for (s = strtok(optarg, ","); s; s = strtok(NULL, ",")) {
@@ -399,6 +413,18 @@
                                    dev, vnio.vn_size, file
                                );
                        }
+#ifdef VNCRYPT
+                       if (flags & VN_SETKEY) {
+                               char *key;
+
+                               /* Read an encryption key and set it */
+                               key = getpass("key: ");
+                               if (!key[0])
+                                       key = NULL;
+                               if (ioctl(fileno(f), VNIOCSETKEY, &key))
+                                       warn("VNIOCSETKEY");
+                       }
+#endif
                        /*
                         * autolabel
                         */
--- sys/dev/vn/vn.c.orig        Wed Sep 25 16:39:19 2002
+++ sys/dev/vn/vn.c     Wed Sep 25 16:43:24 2002
@@ -87,6 +87,10 @@
 #include <vm/vm_extern.h>
 #include <vm/vm_zone.h>
 
+#ifdef VNCRYPT
+#include <crypto/blowfish/blowfish.h>
+#endif
+
 static d_ioctl_t       vnioctl;
 static d_open_t        vnopen;
 static d_close_t       vnclose;
@@ -140,6 +144,9 @@
        struct buf       sc_tab;        /* transfer queue               */
        u_long           sc_options;    /* options                      */
        dev_t            sc_devlist;    /* devices that refer to this unit */
+#ifdef VNCRYPT
+       BF_KEY           sc_key;        /* blowfish key                 */
+#endif
        SLIST_ENTRY(vn_softc) sc_list;
 };
 
@@ -385,12 +392,53 @@
                        auio.uio_rw = UIO_WRITE;
                auio.uio_resid = bp->b_bcount;
                auio.uio_procp = curproc;
+#ifdef VNCRYPT
+               if (vn->sc_key.P[0] && !(bp->b_flags & B_READ)) {
+                       BF_LONG *blp;
+
+                       /* Encode data to be written using CBC.
+                        * The chain size is the disk sector size - 512 bytes.
+                        */
+                       for (blp = (BF_LONG *)bp->b_data; blp <
+                            (BF_LONG *)((char *)bp->b_data + bp->b_bcount);
+                            blp += 2) {
+                               if (((char *)blp - (char *)bp->b_bcount) &
+                                   0x1ff)
+                                       blp[0] ^= blp[-2], blp[1] ^= blp[-1];
+                               BF_encrypt(blp, &vn->sc_key);
+                       }
+               }
+#endif
                vn_lock(vn->sc_vp, LK_EXCLUSIVE | LK_RETRY, curproc);
                if (bp->b_flags & B_READ)
                        error = VOP_READ(vn->sc_vp, &auio, IO_DIRECT, vn->sc_cred);
                else
                        error = VOP_WRITE(vn->sc_vp, &auio, IO_NOWDRAIN, vn->sc_cred);
                VOP_UNLOCK(vn->sc_vp, 0, curproc);
+#ifdef VNCRYPT
+               if (vn->sc_key.P[0]) {
+                       int len;
+                       BF_LONG *blp;
+                       BF_LONG blx[2], bly[2];
+
+                       /* Decode data using CBC.  This is for both reads
+                        * (which are encoded in the vnode) and writes
+                        * (which I just encoded before writing).
+                        */
+                       len = (bp->b_flags & B_READ)
+                           ? bp->b_bcount - auio.uio_resid : bp->b_bcount;
+                       for (blp = (BF_LONG *)bp->b_data;
+                            blp < (BF_LONG *)((char *)bp->b_data + len);
+                            blp += 2) {
+                               blx[0] = blp[0], blx[1] = blp[1];
+                               BF_decrypt(blp, &vn->sc_key);
+                               if (((char *)blp - (char *)bp->b_bcount) &
+                                   0x1ff)
+                                       blp[0] ^= bly[0], blp[1] ^= bly[1];
+                               bcopy(blx, bly, sizeof blx);
+                       }
+               }
+#endif
                bp->b_resid = auio.uio_resid;
 
                if (error) {
@@ -443,6 +491,9 @@
        case VNIOCGCLEAR:
        case VNIOCUSET:
        case VNIOCUCLEAR:
+#ifdef VNCRYPT
+       case VNIOCSETKEY:
+#endif
                goto vn_specific;
        }
 
@@ -513,6 +564,42 @@
                *f = vn->sc_options;
                break;
 
+#ifdef VNCRYPT
+       case VNIOCSETKEY:
+           /* Set the blowfish key */
+           {
+               int i, j;
+               unsigned char *kc, *kp;
+               unsigned char kbuf[(BF_ROUNDS + 2) * 4];
+
+               if (!*(char **)data) {
+                       vn->sc_key.P[0] = 0;
+                       break;
+               }
+               kp = zalloc(namei_zone);
+               /* Read in an ASCII string and convert it into binary form.
+                * This may not be a "standard" way to convert it, but I
+                * think it allows for good bit coverage in long strings.
+                */
+               if ((error = copyinstr(*(char **)data, kp, MAXPATHLEN, NULL))) {
+                       zfree(namei_zone, kp);
+                       return error;
+               }
+               bzero(kbuf, sizeof kbuf);
+               for (kc = kp, i = j = 0; *kc; kc++, i++) {
+                       if (i == sizeof kbuf) {
+                               i = 0;
+                               j = (j + 5) & 7;
+                       }
+                       kbuf[i] ^= (*kc >> j) | (*kc << (8 - j));
+               }
+               zfree(namei_zone, kp);
+               /* Now convert that binary key into Blowfish's internal form */
+               BF_set_key(&vn->sc_key, sizeof kbuf, kbuf);
+               break;
+           }
+#endif
+
        default:
                error = ENOTTY;
                break;
@@ -740,6 +827,9 @@
                vn->sc_object = NULL;
        }
        vn->sc_size = 0;
+#ifdef VNCRYPT
+       vn->sc_key.P[0] = 0;
+#endif
 }
 
 static int
--- sys/conf/files.orig Wed Sep 25 16:37:47 2002
+++ sys/conf/files      Tue Sep 24 23:16:16 2002
@@ -80,7 +80,7 @@
 contrib/ipfilter/netinet/ip_proxy.c    optional ipfilter inet
 contrib/ipfilter/netinet/ip_state.c    optional ipfilter inet
 contrib/ipfilter/netinet/mlfk_ipl.c    optional ipfilter inet
-crypto/blowfish/bf_skey.c      optional ipsec ipsec_esp
+crypto/blowfish/bf_skey.c      standard
 crypto/cast128/cast128.c       optional ipsec ipsec_esp
 crypto/des/des_ecb.c   optional ipsec ipsec_esp
 crypto/des/des_setkey.c        optional ipsec ipsec_esp
--- sys/conf/files.i386.orig    Wed Sep 25 16:37:59 2002
+++ sys/conf/files.i386 Tue Sep 24 23:16:32 2002
@@ -73,7 +73,7 @@
 contrib/dev/oltr/trlldbm.c     optional        oltr
 contrib/dev/oltr/trlldhm.c     optional        oltr
 contrib/dev/oltr/trlldmac.c    optional        oltr
-bf_enc.o                       optional        ipsec ipsec_esp         \
+bf_enc.o                       standard                                \
        dependency      "$S/crypto/blowfish/arch/i386/bf_enc.S 
$S/crypto/blowfish/arch/i386/bf_enc_586.S $S/crypto/blowfish/arch/i386/bf_enc_686.S"   
          \
        compile-with    "${CC} -c -I$S/crypto/blowfish/arch/i386 ${ASM_CFLAGS} 
${WERROR} ${.IMPSRC}"    \
        no-implicit-rule

To Unsubscribe: send mail to [EMAIL PROTECTED]
with "unsubscribe freebsd-hackers" in the body of the message

Reply via email to