diff --git a/configure.ac b/configure.ac
index 0297390..b2a6987 100644
--- a/configure.ac
+++ b/configure.ac
@@ -208,7 +208,7 @@ AC_CHECK_FUNCS([ceil floor round])
 
 # Search for the required modules.
 
-VTE_PKGS="glib-2.0 >= $GLIB_REQUIRED gobject-2.0 pango >= $PANGO_REQUIRED gtk+-$GTK_API_VERSION >= $GTK_REQUIRED gobject-2.0 gio-2.0 gio-unix-2.0"
+VTE_PKGS="glib-2.0 >= $GLIB_REQUIRED gobject-2.0 pango >= $PANGO_REQUIRED gtk+-$GTK_API_VERSION >= $GTK_REQUIRED gobject-2.0 gio-2.0 gio-unix-2.0 gnutls >= 3.2.0"
 PKG_CHECK_MODULES([VTE],[$VTE_PKGS])
 AC_SUBST([VTE_PKGS])
 
diff --git a/src/vtestream-file.h b/src/vtestream-file.h
index d54d9ba..729b46c 100644
--- a/src/vtestream-file.h
+++ b/src/vtestream-file.h
@@ -22,7 +22,7 @@
  */
 
 /*
- * VteFileStream is implemented as two layers above each other.
+ * VteFileStream is implemented as three layers above each other.
  *
  * o The bottom layer is VteSnake. It provides a mapping from logical offsets
  *   to physical file offsets, storing the stream in at most 3 continuous
@@ -48,13 +48,23 @@
  *   the tail is kinda like a snake, and the mapping to file offsets reminds
  *   me of the well-known game on old mobile phones.
  *
+ * o The middle layer is called VteBoa. It does encryption and integrity
+ *   check. It has the same API as the snake, except that the blocksize is a
+ *   bit smaller (4080 bytes) to leave room for the integrity data.
+ *
+ *   The name was chosen because the world of encryption is full of three
+ *   letter abbreviations. At this moment we use GNU TLS's method for doing
+ *   AES GCM. Also, because grown-ups might think it's a hat, when actually
+ *   it's a boa constrictor digesting an elephant :)
+ *
  * o The top layer is VteFileStream. Its basic goal is to do buffering and
  *   caching. As opposed to the previous layers, this one provides methods on
  *   arbitrary amount of data. It doesn't offer random-access-writes, instead,
  *   it offers appending data, and truncating the head (undoing the latest
  *   appends). Write requests are batched up until there's a complete block to
- *   be written to disk. Read requests are answered by reading possibly more
- *   underlying blocks, and sped up by caching the most recently read block.
+ *   be encrypted and written to disk. Read requests are answered by reading
+ *   and decrypting possibly more underlying blocks, and sped up by caching
+ *   the most recently read and decrypted block.
  *
  * Design discussions: https://bugzilla.gnome.org/show_bug.cgi?id=738601
  */
@@ -63,17 +73,28 @@
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
 
 #include "vteutils.h"
 
+#define VTE_CIPHER_ALGORITHM  GNUTLS_CIPHER_AES_256_GCM
+#define VTE_CIPHER_KEY_SIZE   32
+#define VTE_CIPHER_BLOCK_SIZE 16
+#define VTE_CIPHER_IV_SIZE    12
+#define VTE_CIPHER_TAG_SIZE   16
+
 #ifndef VTESTREAM_MAIN
 #define VTE_SNAKE_BLOCKSIZE 4096
+#define VTE_BOA_BLOCKSIZE   (VTE_SNAKE_BLOCKSIZE - VTE_CIPHER_TAG_SIZE)
 #else
 #define VTE_SNAKE_BLOCKSIZE 4  /* Small block size for unit testing */
+#define VTE_BOA_BLOCKSIZE   3
 #endif
 
-#define ALIGN_SNAKE(x) ((x) / VTE_SNAKE_BLOCKSIZE * VTE_SNAKE_BLOCKSIZE)
-#define MOD_SNAKE(x)   ((x) % VTE_SNAKE_BLOCKSIZE)
+#define OFFSET_BOA_TO_SNAKE(x) ((x) / VTE_BOA_BLOCKSIZE * VTE_SNAKE_BLOCKSIZE)
+#define ALIGN_BOA(x) ((x) / VTE_BOA_BLOCKSIZE * VTE_BOA_BLOCKSIZE)
+#define MOD_BOA(x)   ((x) % VTE_BOA_BLOCKSIZE)
 
 
 #ifndef HAVE_PREAD
@@ -488,13 +509,242 @@ _vte_snake_class_init (VteSnakeClass *klass)
 /******************************************************************************************/
 
 /*
- * VteFileStream: Implement buffering/caching on top of VteSnake.
+ * VteBoa: Encrypt an elephant to make it look like a hat.
+ *
+ *                 _.----._
+ *                /         \._______
+ *               /                    \.
+ *              |                       \
+ *              |                        \
+ *             /                          \
+ *   ________/                             \_________________
+ *  '------------------------------------------------------'-'
+ */
+
+typedef struct _VteBoa {
+        VteSnake parent;
+        gsize tail, head;
+
+        gnutls_cipher_hd_t cipher_hd;
+        /* The IV (nonce) consists of the offset within the stream, and a reset counter so that
+         * we don't reuse the same IVs when the offset is reset back to 0.
+         * The padding is there to make sure the structure is at least VTE_CIPHER_IV_SIZE bytes large.
+         * Assertion is made later that the real data fits in its first VTE_CIPHER_IV_SIZE bytes.
+         */
+        struct _VteIv {
+                gsize offset;
+                guint32 reset_counter;
+                unsigned char padding[VTE_CIPHER_IV_SIZE];
+        } iv;
+        char *buf;
+} VteBoa;
+
+typedef struct _VteBoaClass {
+        GObjectClass parent_class;
+
+        void (*reset) (VteBoa *boa, gsize offset);
+        void (*write) (VteBoa *boa, gsize offset, const char *data);
+        gboolean (*read) (VteBoa *boa, gsize offset, char *data);
+        void (*advance_tail) (VteBoa *boa, gsize offset);
+        gsize (*tail) (VteBoa *boa);
+        gsize (*head) (VteBoa *boa);
+} VteBoaClass;
+
+static GType _vte_boa_get_type (void);
+#define VTE_TYPE_BOA _vte_boa_get_type ()
+#define VTE_BOA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VTE_TYPE_BOA, VteBoaClass))
+
+G_DEFINE_TYPE (VteBoa, _vte_boa, VTE_TYPE_SNAKE)
+
+static void
+_vte_boa_init (VteBoa *boa)
+{
+        unsigned char key[VTE_CIPHER_KEY_SIZE];
+        gnutls_datum_t datum_key;
+
+        boa->buf = g_malloc(VTE_SNAKE_BLOCKSIZE);
+
+        gnutls_global_init ();
+
+        /* Assert that VTE_CIPHER_* constants are defined correctly. Should happen compile-time, nevermind. */
+        g_assert_cmpuint (gnutls_cipher_get_block_size(VTE_CIPHER_ALGORITHM), ==, VTE_CIPHER_BLOCK_SIZE);
+        g_assert_cmpuint (gnutls_cipher_get_iv_size(VTE_CIPHER_ALGORITHM), ==, VTE_CIPHER_IV_SIZE);
+        g_assert_cmpuint (gnutls_cipher_get_tag_size(VTE_CIPHER_ALGORITHM), ==, VTE_CIPHER_TAG_SIZE);
+
+        /* Assert that IV does indeed include all the data we want to use (offset and reset_counter). */
+        g_assert_cmpuint (offsetof(struct _VteIv, padding), <=, VTE_CIPHER_IV_SIZE);
+
+        /* Strong random for the key. */
+        gnutls_rnd_refresh();
+        gnutls_rnd(GNUTLS_RND_KEY, key, VTE_CIPHER_KEY_SIZE);
+
+        datum_key.data = key;
+        datum_key.size = VTE_CIPHER_KEY_SIZE;
+        gnutls_cipher_init(&boa->cipher_hd, VTE_CIPHER_ALGORITHM, &datum_key, NULL);
+        /* FIXME: 738601#c52 the compiler might optimize this away, how to make sure it's erased?
+         * It's on the stack so maybe we can rest assured it'll be overwritten pretty soon. */
+        memset(key, 0, VTE_CIPHER_KEY_SIZE);
+
+        /* Weak random for IV's reset_counter, zeros for the rest. */
+        memset(&boa->iv, 0, sizeof(boa->iv));
+        gnutls_rnd(GNUTLS_RND_NONCE, &boa->iv.reset_counter, sizeof(boa->iv.reset_counter));
+}
+
+static void
+_vte_boa_finalize (GObject *object)
+{
+        VteBoa *boa = (VteBoa *) object;
+
+        g_free(boa->buf);
+
+        memset(&boa->iv, 0, sizeof(boa->iv));
+
+        gnutls_cipher_deinit (boa->cipher_hd);
+        gnutls_global_deinit ();
+
+        G_OBJECT_CLASS (_vte_boa_parent_class)->finalize(object);
+}
+
+static void
+_vte_boa_reset (VteBoa *boa, gsize offset)
+{
+        g_assert_cmpuint (offset % VTE_BOA_BLOCKSIZE, ==, 0);
+
+        /* The stream is empty, there's no data to decrypt. Offset is likely set back to 0.
+         * To avoid reusing the same IVs, increment reset_counter. */
+        boa->iv.reset_counter++;
+
+        _vte_snake_reset (&boa->parent, OFFSET_BOA_TO_SNAKE(offset));
+
+        boa->tail = boa->head = offset;
+}
+
+/*
+ * offset is either within the stream (overwrite data), or at its head (append data).
+ * data is VTE_BOA_BLOCKSIZE bytes large.
+ */
+static void
+_vte_boa_write (VteBoa *boa, gsize offset, const char *data)
+{
+        g_assert_cmpuint (offset, >=, boa->tail);
+        g_assert_cmpuint (offset, <=, boa->head);
+        g_assert_cmpuint (offset % VTE_BOA_BLOCKSIZE, ==, 0);
+
+#ifndef VTESTREAM_MAIN
+        boa->iv.offset = offset;
+        gnutls_cipher_set_iv (boa->cipher_hd, &boa->iv, VTE_CIPHER_IV_SIZE);
+        gnutls_cipher_encrypt2 (boa->cipher_hd, data, VTE_BOA_BLOCKSIZE, boa->buf, VTE_BOA_BLOCKSIZE);
+        gnutls_cipher_tag (boa->cipher_hd, boa->buf + VTE_BOA_BLOCKSIZE, VTE_CIPHER_TAG_SIZE);
+#else
+        /* Fake encryption (lowercase<->uppercase) and fake integrity data ('#') for unit testing */
+        boa->buf[0] = data[0] ^ 0x20;
+        boa->buf[1] = data[1] ^ 0x20;
+        boa->buf[2] = data[2] ^ 0x20;
+        boa->buf[3] = '#';
+#endif
+
+        _vte_snake_write (&boa->parent, OFFSET_BOA_TO_SNAKE(offset), boa->buf);
+
+        if (G_LIKELY (offset == boa->head)) {
+                boa->head += VTE_BOA_BLOCKSIZE;
+        }
+}
+
+/* Place VTE_BOA_BLOCKSIZE bytes at data */
+static gboolean
+_vte_boa_read (VteBoa *boa, gsize offset, char *data)
+{
+        unsigned char tag[VTE_CIPHER_TAG_SIZE];
+        guint8 faulty = 0;
+        unsigned int i, j;
+
+        g_assert_cmpuint (offset % VTE_BOA_BLOCKSIZE, ==, 0);
+
+        if (!_vte_snake_read (&boa->parent, OFFSET_BOA_TO_SNAKE(offset), boa->buf))
+                return FALSE;
+
+#ifndef VTESTREAM_MAIN
+        boa->iv.offset = offset;
+        gnutls_cipher_set_iv (boa->cipher_hd, &boa->iv, VTE_CIPHER_IV_SIZE);
+        gnutls_cipher_decrypt2 (boa->cipher_hd, boa->buf, VTE_BOA_BLOCKSIZE, data, VTE_BOA_BLOCKSIZE);
+        gnutls_cipher_tag (boa->cipher_hd, tag, VTE_CIPHER_TAG_SIZE);
+
+        /* Constant time comparison: 738601#c66 */
+        for (i = 0, j = VTE_BOA_BLOCKSIZE; i < VTE_CIPHER_TAG_SIZE; i++, j++) {
+                faulty |= tag[i] ^ boa->buf[j];
+        }
+        if (faulty) {
+                /*
+                 * Data integrity failure. What to do???
+                 * One possible reason is a previous disk full, leaving a hole in the file.
+                 * To make it more complicated, we're not sure that we're using the actual filesystem
+                 * block size, so a simple check for a fully zero block wouldn't suffice.
+                 * As of now, just return a normal failure, wiping out the data to be absolutely sure
+                 * that the caller won't use it.
+                 */
+                memset(data, 0, VTE_BOA_BLOCKSIZE);
+                return FALSE;
+        }
+#else
+        /* Fake decryption (lowercase<->uppercase) and fake integrity verification ('#') for unit testing */
+        data[0] = boa->buf[0] ^ 0x20;
+        data[1] = boa->buf[1] ^ 0x20;
+        data[2] = boa->buf[2] ^ 0x20;
+        g_assert_cmpuint(boa->buf[3], ==, '#');
+        (void) tag; (void) faulty; (void) i; (void) j;  /* silence gcc */
+#endif
+        return TRUE;
+}
+
+static void
+_vte_boa_advance_tail (VteBoa *boa, gsize offset)
+{
+        g_assert_cmpuint (offset, >=, boa->tail);
+        g_assert_cmpuint (offset, <=, boa->head);
+        g_assert_cmpuint (offset % VTE_BOA_BLOCKSIZE, ==, 0);
+
+        _vte_snake_advance_tail (&boa->parent, OFFSET_BOA_TO_SNAKE(offset));
+
+        boa->tail = offset;
+}
+
+static gsize
+_vte_boa_tail (VteBoa *boa)
+{
+        return boa->tail;
+}
+
+static gsize
+_vte_boa_head (VteBoa *boa)
+{
+        return boa->head;
+}
+
+static void
+_vte_boa_class_init (VteBoaClass *klass)
+{
+        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+        gobject_class->finalize = _vte_boa_finalize;
+
+        klass->reset = _vte_boa_reset;
+        klass->write = _vte_boa_write;
+        klass->read = _vte_boa_read;
+        klass->advance_tail = _vte_boa_advance_tail;
+        klass->tail = _vte_boa_tail;
+        klass->head = _vte_boa_head;
+}
+
+/******************************************************************************************/
+
+/*
+ * VteFileStream: Implement buffering/caching on top of VteBoa.
  */
 
 typedef struct _VteFileStream {
         GObject parent;
 
-        VteSnake *snake;
+        VteBoa *boa;
 
         char *rbuf;
         /* Offset of the cached record, always a multiple of block size.
@@ -524,11 +774,11 @@ _vte_file_stream_new (void)
 static void
 _vte_file_stream_init (VteFileStream *stream)
 {
-        stream->snake = g_object_new (VTE_TYPE_SNAKE, NULL);
-        _vte_snake_init (stream->snake);
+        stream->boa = g_object_new (VTE_TYPE_BOA, NULL);
+        _vte_boa_init (stream->boa);
 
-        stream->rbuf = g_malloc(VTE_SNAKE_BLOCKSIZE);
-        stream->wbuf = g_malloc(VTE_SNAKE_BLOCKSIZE);
+        stream->rbuf = g_malloc(VTE_BOA_BLOCKSIZE);
+        stream->wbuf = g_malloc(VTE_BOA_BLOCKSIZE);
         stream->rbuf_offset = 1;  /* Invalidate the read cache. */
 }
 
@@ -539,7 +789,7 @@ _vte_file_stream_finalize (GObject *object)
 
         g_free(stream->rbuf);
         g_free(stream->wbuf);
-        g_object_unref (stream->snake);
+        g_object_unref (stream->boa);
 
         G_OBJECT_CLASS (_vte_file_stream_parent_class)->finalize(object);
 }
@@ -548,9 +798,9 @@ static void
 _vte_file_stream_reset (VteStream *astream, gsize offset)
 {
 	VteFileStream *stream = (VteFileStream *) astream;
-        gsize offset_aligned = ALIGN_SNAKE(offset);
+        gsize offset_aligned = ALIGN_BOA(offset);
 
-        _vte_snake_reset (stream->snake, offset_aligned);
+        _vte_boa_reset (stream->boa, offset_aligned);
         stream->tail = stream->head = offset;
 
         /* When resetting at a non-aligned offset, initial bytes of the write buffer
@@ -559,12 +809,12 @@ _vte_file_stream_reset (VteStream *astream, gsize offset)
          * For unittesting, fill it with 1s for convenient testing. */
 
 #ifndef VTESTREAM_MAIN
-        memset(stream->wbuf, 0, MOD_SNAKE(offset));
+        memset(stream->wbuf, 0, MOD_BOA(offset));
 #else
-        memset(stream->wbuf, 1, MOD_SNAKE(offset));
+        memset(stream->wbuf, 1, MOD_BOA(offset));
 #endif
 
-        stream->wbuf_len = MOD_SNAKE(offset);
+        stream->wbuf_len = MOD_BOA(offset);
         stream->rbuf_offset = 1;  /* Invalidate the read cache. */
 }
 
@@ -576,11 +826,11 @@ _vte_file_stream_append (VteStream *astream, const char *data, gsize len)
         g_assert (len > 0);
 
         while (len) {
-                gsize l = MIN(VTE_SNAKE_BLOCKSIZE - stream->wbuf_len, len);
+                gsize l = MIN(VTE_BOA_BLOCKSIZE - stream->wbuf_len, len);
                 memcpy(stream->wbuf + stream->wbuf_len, data, l);
                 stream->wbuf_len += l; data += l; len -= l;
-                if (stream->wbuf_len == VTE_SNAKE_BLOCKSIZE) {
-                        _vte_snake_write (stream->snake, ALIGN_SNAKE(stream->head), stream->wbuf);
+                if (stream->wbuf_len == VTE_BOA_BLOCKSIZE) {
+                        _vte_boa_write (stream->boa, ALIGN_BOA(stream->head), stream->wbuf);
                         stream->wbuf_len = 0;
                 }
                 stream->head += l;
@@ -607,20 +857,20 @@ _vte_file_stream_read (VteStream *astream, gsize offset, char *data, gsize len)
                 g_assert_not_reached();
         }
 
-        while (len && offset < ALIGN_SNAKE(stream->head)) {
-                gsize l = MIN(VTE_SNAKE_BLOCKSIZE - MOD_SNAKE(offset), len);
-                gsize offset_aligned = ALIGN_SNAKE(offset);
+        while (len && offset < ALIGN_BOA(stream->head)) {
+                gsize l = MIN(VTE_BOA_BLOCKSIZE - MOD_BOA(offset), len);
+                gsize offset_aligned = ALIGN_BOA(offset);
                 if (offset_aligned != stream->rbuf_offset) {
-                        if (!_vte_snake_read (stream->snake, offset_aligned, stream->rbuf))
+                        if (!_vte_boa_read (stream->boa, offset_aligned, stream->rbuf))
                                 return FALSE;
                         stream->rbuf_offset = offset_aligned;
                 }
-                memcpy(data, stream->rbuf + MOD_SNAKE(offset), l);
+                memcpy(data, stream->rbuf + MOD_BOA(offset), l);
                 offset += l; data += l; len -= l;
         }
         if (len) {
-                g_assert_cmpuint(MOD_SNAKE(offset) + len, <=, stream->wbuf_len);
-                memcpy(data, stream->wbuf + MOD_SNAKE(offset), len);
+                g_assert_cmpuint(MOD_BOA(offset) + len, <=, stream->wbuf_len);
+                memcpy(data, stream->wbuf + MOD_BOA(offset), len);
         }
         return TRUE;
 }
@@ -633,24 +883,24 @@ _vte_file_stream_truncate (VteStream *astream, gsize offset)
         g_assert_cmpuint (offset, >=, stream->tail);
         g_assert_cmpuint (offset, <=, stream->head);
 
-        if (offset < ALIGN_SNAKE(stream->head)) {
+        if (offset < ALIGN_BOA(stream->head)) {
                 /* Truncating goes back to the part that we've written to the
                  * file. For simplicity (since this is a very rare, user
                  * initiated event - the window is made taller) go for the
                  * simplest local hack here that allows to leave the rest of
                  * the code intact, that is, read back the new partial last
                  * block to the write cache. */
-                gsize offset_aligned = ALIGN_SNAKE(offset);
-                if (!_vte_snake_read (stream->snake, offset_aligned, stream->wbuf)) {
+                gsize offset_aligned = ALIGN_BOA(offset);
+                if (!_vte_boa_read (stream->boa, offset_aligned, stream->wbuf)) {
                         /* what now? */
-                        memset(stream->wbuf, 0, VTE_SNAKE_BLOCKSIZE);
+                        memset(stream->wbuf, 0, VTE_BOA_BLOCKSIZE);
                 }
 
                 if (stream->rbuf_offset >= offset_aligned) {
                         stream->rbuf_offset = 1;  /* Invalidate the read cache. */
                 }
         }
-        stream->wbuf_len = MOD_SNAKE(offset);
+        stream->wbuf_len = MOD_BOA(offset);
 	stream->head = offset;
 }
 
@@ -662,8 +912,8 @@ _vte_file_stream_advance_tail (VteStream *astream, gsize offset)
         g_assert_cmpuint (offset, >=, stream->tail);
         g_assert_cmpuint (offset, <=, stream->head);
 
-        if (ALIGN_SNAKE(offset) > ALIGN_SNAKE(stream->tail))
-                _vte_snake_advance_tail (stream->snake, ALIGN_SNAKE(offset));
+        if (ALIGN_BOA(offset) > ALIGN_BOA(stream->tail))
+                _vte_boa_advance_tail (stream->boa, ALIGN_BOA(offset));
 
         stream->tail = offset;
 }
@@ -737,6 +987,19 @@ _vte_file_stream_class_init (VteFileStreamClass *klass)
         } \
 } while (0)
 
+/* Check for the boa's tail, head and contents */
+#define assert_boa(__boa, __tail, __head, __contents) do { \
+        char __buf[VTE_BOA_BLOCKSIZE]; \
+        int __i; \
+        g_assert_cmpuint (boa->tail, ==, __tail); \
+        g_assert_cmpuint (boa->head, ==, __head); \
+        g_assert_cmpuint (strlen(__contents), ==, __head - __tail); \
+        for (__i = __tail; __i < __head; __i += VTE_BOA_BLOCKSIZE) { \
+                g_assert (_vte_boa_read (boa, __i, __buf)); \
+                g_assert (memcmp(__buf, __contents + __i - __tail, VTE_BOA_BLOCKSIZE) == 0); \
+        } \
+} while (0)
+
 /* Check for the stream's tail, head and contents */
 #define assert_stream(__astream, __tail, __head, __contents) do { \
         char __buf[100]; \
@@ -836,158 +1099,224 @@ test_snake (void)
         g_object_unref (snake);
 }
 
+static void
+test_boa (void)
+{
+        VteBoa *boa = g_object_new (VTE_TYPE_BOA, NULL);
+        VteSnake *snake = (VteSnake *) &boa->parent;
+
+        /* State 1 */
+        _vte_boa_write (boa, 0, "aaa");
+        _vte_boa_write (boa, 3, "bbb");
+        assert_snake (snake, 1, 0, 8, "AAA#BBB#");
+        assert_boa (boa, 0, 6, "aaabbb");
+
+        /* Stay in state 1 */
+        _vte_boa_advance_tail (boa, 3);
+        _vte_boa_write (boa, 6, "ccc");
+        assert_snake (snake, 1, 4, 12, "BBB#CCC#");
+        assert_boa (boa, 3, 9, "bbbccc");
+
+        /* State 1 -> 2 */
+        _vte_boa_advance_tail (boa, 6);
+        _vte_boa_write (boa, 9, "ddd");
+        assert_file (snake->fd, "DDD#BBB#CCC#");
+        assert_snake (snake, 2, 8, 16, "CCC#DDD#");
+        assert_boa (boa, 6, 12, "cccddd");
+
+        /* Skip some state changes that we tested in test_snake_state_changes() */
+
+        /* Reset */
+        _vte_boa_reset (boa, 75);
+        assert_snake (snake, 1, 100, 100, "");
+        assert_boa (boa, 75, 75, "");
+
+        /* Stay in state 1 */
+        _vte_boa_write (boa, 75, "zzz");
+        assert_file_starts_with (snake->fd, "ZZZ#");
+        assert_snake (snake, 1, 100, 104, "ZZZ#");
+        assert_boa (boa, 75, 78, "zzz");
+
+        g_object_unref (boa);
+}
+
 #define append(as, str) _vte_stream_append((as), (str), strlen(str))
 
 static void
 test_stream (void)
 {
+        VteBoa *boa;
         VteSnake *snake;
-        char buf[5];
+        char buf[4];
 
         VteStream *astream = _vte_file_stream_new();
         VteFileStream *stream = (VteFileStream *) astream;
         _vte_file_stream_init (stream);
-        snake = stream->snake;
+        boa = stream->boa;
+        snake = (VteSnake *) &boa->parent;
 
         /* Append */
         append (astream, "ab");
         g_assert (snake->fd == -1);
+        assert_boa (boa, 0, 0, "");
         assert_stream (astream, 0, 2, "ab");
 
-        append (astream, "c#");
-        assert_file (snake->fd, "abc#");
-        assert_snake (snake, 1, 0, 4, "abc#");
-        assert_stream (astream, 0, 4, "abc#");
+        append (astream, "c");
+        assert_file (snake->fd, "ABC#");
+        assert_snake (snake, 1, 0, 4, "ABC#");
+        assert_boa (boa, 0, 3, "abc");
+        assert_stream (astream, 0, 3, "abc");
 
         append (astream, "d");
-        assert_file (snake->fd, "abc#");
-        assert_snake (snake, 1, 0, 4, "abc#");
-        assert_stream (astream, 0, 5, "abc#d");
+        assert_file (snake->fd, "ABC#");
+        assert_snake (snake, 1, 0, 4, "ABC#");
+        assert_boa (boa, 0, 3, "abc");
+        assert_stream (astream, 0, 4, "abcd");
 
-        append (astream, "ef#ghi#j");
-        assert_file (snake->fd, "abc#def#ghi#");
-        assert_snake (snake, 1, 0, 12, "abc#def#ghi#");
-        assert_stream (astream, 0, 13, "abc#def#ghi#j");
+        append (astream, "efghij");
+        assert_file (snake->fd, "ABC#DEF#GHI#");
+        assert_snake (snake, 1, 0, 12, "ABC#DEF#GHI#");
+        assert_boa (boa, 0, 9, "abcdefghi");
+        assert_stream (astream, 0, 10, "abcdefghij");
 
         /* Truncate */
-        _vte_stream_truncate (astream, 12);
-        assert_file (snake->fd, "abc#def#ghi#");
-        assert_snake (snake, 1, 0, 12, "abc#def#ghi#");
-        assert_stream (astream, 0, 12, "abc#def#ghi#");
-
-        _vte_stream_truncate (astream, 10);
-        assert_file (snake->fd, "abc#def#ghi#");
-        assert_snake (snake, 1, 0, 12, "abc#def#ghi#");
-        assert_stream (astream, 0, 10, "abc#def#gh");
+        _vte_stream_truncate (astream, 9);
+        assert_file (snake->fd, "ABC#DEF#GHI#");
+        assert_snake (snake, 1, 0, 12, "ABC#DEF#GHI#");
+        assert_boa (boa, 0, 9, "abcdefghi");
+        assert_stream (astream, 0, 9, "abcdefghi");
+
+        _vte_stream_truncate (astream, 8);
+        assert_file (snake->fd, "ABC#DEF#GHI#");
+        assert_snake (snake, 1, 0, 12, "ABC#DEF#GHI#");
+        assert_boa (boa, 0, 9, "abcdefghi");
+        assert_stream (astream, 0, 8, "abcdefgh");
 
         /* Test that the read cache is invalidated on truncate */
-        _vte_stream_read (astream, 6, buf, 3);
-        g_assert_cmpuint (stream->rbuf_offset, ==, 4);
-        _vte_stream_truncate (astream, 6);
+        _vte_stream_read (astream, 5, buf, 2);
+        g_assert_cmpuint (stream->rbuf_offset, ==, 3);
+        _vte_stream_truncate (astream, 5);
         g_assert_cmpuint (stream->rbuf_offset, ==, 1);
-        append (astream, "k#l");
-        _vte_stream_read (astream, 4, buf, 4);
-        g_assert_cmpuint (stream->rbuf_offset, ==, 4);
-        buf[4] = '\0';
-        g_assert_cmpstr (buf, ==, "dek#");
-        assert_file (snake->fd, "abc#dek#ghi#");
-        assert_snake (snake, 1, 0, 12, "abc#dek#ghi#");
-        assert_stream (astream, 0, 9, "abc#dek#l");
+        append (astream, "kl");
+        _vte_stream_read (astream, 3, buf, 3);
+        g_assert_cmpuint (stream->rbuf_offset, ==, 3);
+        buf[3] = '\0';
+        g_assert_cmpstr (buf, ==, "dek");
+        assert_file (snake->fd, "ABC#DEK#GHI#");
+        assert_snake (snake, 1, 0, 12, "ABC#DEK#GHI#");
+        assert_boa (boa, 0, 9, "abcdekghi");
+        assert_stream (astream, 0, 7, "abcdekl");
 
         /* Advance_tail */
         _vte_stream_advance_tail (astream, 2);
-        assert_file (snake->fd, "abc#dek#ghi#");
-        assert_snake (snake, 1, 0, 12, "abc#dek#ghi#");
-        assert_stream (astream, 2, 9, "c#dek#l");
+        assert_file (snake->fd, "ABC#DEK#GHI#");
+        assert_snake (snake, 1, 0, 12, "ABC#DEK#GHI#");
+        assert_boa (boa, 0, 9, "abcdekghi");
+        assert_stream (astream, 2, 7, "cdekl");
 
-        _vte_stream_advance_tail (astream, 4);
-        assert_file (snake->fd, "abc#dek#ghi#");
-        assert_snake (snake, 1, 4, 12, "dek#ghi#");
-        assert_stream (astream, 4, 9, "dek#l");
+        _vte_stream_advance_tail (astream, 3);
+        assert_file (snake->fd, "ABC#DEK#GHI#");
+        assert_snake (snake, 1, 4, 12, "DEK#GHI#");
+        assert_boa (boa, 3, 9, "dekghi");
+        assert_stream (astream, 3, 7, "dekl");
 
         append (astream, "m");
-        assert_file (snake->fd, "abc#dek#ghi#");
-        assert_snake (snake, 1, 4, 12, "dek#ghi#");
-        assert_stream (astream, 4, 10, "dek#lm");
+        assert_file (snake->fd, "ABC#DEK#GHI#");
+        assert_snake (snake, 1, 4, 12, "DEK#GHI#");
+        assert_boa (boa, 3, 9, "dekghi");
+        assert_stream (astream, 3, 8, "deklm");
 
         /* Tail and head within the same block in the stream,
          * but not in underlying layers (due to a previous truncate).
          * Nothing special. */
-        _vte_stream_advance_tail (astream, 9);
-        assert_file (snake->fd, "abc#dek#ghi#");
-        assert_snake (snake, 1, 8, 12, "ghi#");
-        assert_stream (astream, 9, 10, "m");
+        _vte_stream_advance_tail (astream, 7);
+        assert_file (snake->fd, "ABC#DEK#GHI#");
+        assert_snake (snake, 1, 8, 12, "GHI#");
+        assert_boa (boa, 6, 9, "ghi");
+        assert_stream (astream, 7, 8, "m");
 
         /* Tail reaches head. Still nothing special. */
-        _vte_stream_advance_tail (astream, 10);
-        assert_file (snake->fd, "abc#dek#ghi#");
-        assert_snake (snake, 1, 8, 12, "ghi#");
-        assert_stream (astream, 10, 10, "");
+        _vte_stream_advance_tail (astream, 8);
+        assert_file (snake->fd, "ABC#DEK#GHI#");
+        assert_snake (snake, 1, 8, 12, "GHI#");
+        assert_boa (boa, 6, 9, "ghi");
+        assert_stream (astream, 8, 8, "");
 
         /* Snake state 2 */
-        append (astream, "n#opq#rst#uv");
-        assert_file (snake->fd, "opq#rst#lmn#");
-        assert_snake (snake, 2, 8, 20, "lmn#opq#rst#");
-        assert_stream (astream, 10, 22, "n#opq#rst#uv");
+        append (astream, "nopqrstuv");
+        assert_file (snake->fd, "OPQ#RST#LMN#");
+        assert_snake (snake, 2, 8, 20, "LMN#OPQ#RST#");
+        assert_boa (boa, 6, 15, "lmnopqrst");
+        assert_stream (astream, 8, 17, "nopqrstuv");
 
         /* Tail and head within the same block.
          * The snake resets itself to state 1, ... */
-        _vte_stream_advance_tail (astream, 21);
+        _vte_stream_advance_tail (astream, 16);
         assert_snake (snake, 1, 20, 20, "");
-        assert_stream (astream, 21, 22, "v");
+        assert_boa (boa, 15, 15, "");
+        assert_stream (astream, 16, 17, "v");
 
         /* ... and the next write goes to beginning of the file */
-        append (astream, "w#x");
-        assert_file_starts_with (snake->fd, "uvw#");
-        assert_snake (snake, 1, 20, 24, "uvw#");
-        assert_stream (astream, 21, 25, "vw#x");
+        append (astream, "wx");
+        assert_file_starts_with (snake->fd, "UVW#");
+        assert_snake (snake, 1, 20, 24, "UVW#");
+        assert_boa (boa, 15, 18, "uvw");
+        assert_stream (astream, 16, 19, "vwx");
 
         /* Test a bit what happens when "accidentally" writing aligned blocks. */
-        append (astream, "yz#");
-        assert_file_starts_with (snake->fd, "uvw#xyz#");
-        assert_snake (snake, 1, 20, 28, "uvw#xyz#");
-        assert_stream (astream, 21, 28, "vw#xyz#");
+        append (astream, "yz");
+        assert_file_starts_with (snake->fd, "UVW#XYZ#");
+        assert_snake (snake, 1, 20, 28, "UVW#XYZ#");
+        assert_boa (boa, 15, 21, "uvwxyz");
+        assert_stream (astream, 16, 21, "vwxyz");
+
+        append (astream, "abc");
+        assert_file (snake->fd, "UVW#XYZ#ABC#");
+        assert_snake (snake, 1, 20, 32, "UVW#XYZ#ABC#");
+        assert_boa (boa, 15, 24, "uvwxyzabc");
+        assert_stream (astream, 16, 24, "vwxyzabc");
 
-        append (astream, "abc#");
-        assert_file (snake->fd, "uvw#xyz#abc#");
-        assert_snake (snake, 1, 20, 32, "uvw#xyz#abc#");
-        assert_stream (astream, 21, 32, "vw#xyz#abc#");
-
-        _vte_stream_advance_tail (astream, 28);
-        assert_file (snake->fd, "uvw#xyz#abc#");
-        assert_snake (snake, 1, 28, 32, "abc#");
-        assert_stream (astream, 28, 32, "abc#");
+        _vte_stream_advance_tail (astream, 21);
+        assert_file (snake->fd, "UVW#XYZ#ABC#");
+        assert_snake (snake, 1, 28, 32, "ABC#");
+        assert_boa (boa, 21, 24, "abc");
+        assert_stream (astream, 21, 24, "abc");
 
         /* State 2 */
-        append (astream, "def#");
-        assert_file (snake->fd, "def#xyz#abc#");
-        assert_snake (snake, 2, 28, 36, "abc#def#");
-        assert_stream (astream, 28, 36, "abc#def#");
-
-        append (astream, "ghi#");
-        assert_file (snake->fd, "def#ghi#abc#");
-        assert_snake (snake, 2, 28, 40, "abc#def#ghi#");
-        assert_stream (astream, 28, 40, "abc#def#ghi#");
+        append (astream, "def");
+        assert_file (snake->fd, "DEF#XYZ#ABC#");
+        assert_snake (snake, 2, 28, 36, "ABC#DEF#");
+        assert_boa (boa, 21, 27, "abcdef");
+        assert_stream (astream, 21, 27, "abcdef");
+
+        append (astream, "ghi");
+        assert_file (snake->fd, "DEF#GHI#ABC#");
+        assert_snake (snake, 2, 28, 40, "ABC#DEF#GHI#");
+        assert_boa (boa, 21, 30, "abcdefghi");
+        assert_stream (astream, 21, 30, "abcdefghi");
 
         /* State 3 */
-        append (astream, "jkl#");
-        assert_file (snake->fd, "def#ghi#abc#jkl#");
-        assert_snake (snake, 3, 28, 44, "abc#def#ghi#jkl#");
-        assert_stream (astream, 28, 44, "abc#def#ghi#jkl#");
+        append (astream, "jkl");
+        assert_file (snake->fd, "DEF#GHI#ABC#JKL#");
+        assert_snake (snake, 3, 28, 44, "ABC#DEF#GHI#JKL#");
+        assert_boa (boa, 21, 33, "abcdefghijkl");
+        assert_stream (astream, 21, 33, "abcdefghijkl");
 
         /* State 4 */
-        _vte_stream_advance_tail (astream, 32);
-        assert_file (snake->fd, "def#ghi#abc#jkl#");
-        assert_snake (snake, 4, 32, 44, "def#ghi#jkl#");
-        assert_stream (astream, 32, 44, "def#ghi#jkl#");
+        _vte_stream_advance_tail (astream, 24);
+        assert_file (snake->fd, "DEF#GHI#ABC#JKL#");
+        assert_snake (snake, 4, 32, 44, "DEF#GHI#JKL#");
+        assert_boa (boa, 24, 33, "defghijkl");
+        assert_stream (astream, 24, 33, "defghijkl");
 
         /* Explicit reset */
-        _vte_stream_reset (astream, 102);
-        append (astream, "y#z");
-        /* Unused leading blocks are filled with '\x01' */
-        assert_file_starts_with (snake->fd, "\x01\x01y#");
-        assert_snake (snake, 1, 100, 104, "\x01\x01y#");
-        assert_stream (astream, 102, 105, "y#z");
+        _vte_stream_reset (astream, 77);
+        append (astream, "yz");
+        /* Unused leading blocks are filled with '\x01', fake-encrypted to '!' */
+        assert_file_starts_with (snake->fd, "!!Y#");
+        assert_snake (snake, 1, 100, 104, "!!Y#");
+        assert_boa (boa, 75, 78, "\x01\x01y");
+        assert_stream (astream, 77, 79, "yz");
 
         g_object_unref (astream);
 }
@@ -996,6 +1325,7 @@ int
 main (int argc, char **argv)
 {
         test_snake();
+        test_boa();
         test_stream();
 
         printf("vtestream-file tests passed :)\n");
