revert prev commit modulo the introduction of encrypt_v
diff --git a/include/picotls.h b/include/picotls.h
index acd978d..b0290cb 100644
--- a/include/picotls.h
+++ b/include/picotls.h
@@ -343,12 +343,15 @@
     /* field above this line must not be altered by the crypto binding */
     void (*dispose_crypto)(struct st_ptls_aead_context_t *ctx);
     void (*do_xor_iv)(struct st_ptls_aead_context_t *ctx, const void *bytes, size_t len);
+    void (*do_encrypt_init)(struct st_ptls_aead_context_t *ctx, uint64_t seq, const void *aad, size_t aadlen);
+    size_t (*do_encrypt_update)(struct st_ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen);
+    size_t (*do_encrypt_final)(struct st_ptls_aead_context_t *ctx, void *output);
     void (*do_encrypt)(struct st_ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,
-                       ptls_iovec_t aad, ptls_aead_supplementary_encryption_t *supp);
+                       const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp);
     void (*do_encrypt_v)(struct st_ptls_aead_context_t *ctx, void *output, ptls_iovec_t *input, size_t incnt, uint64_t seq,
-                         ptls_iovec_t aad);
+                         const void *aad, size_t aadlen);
     size_t (*do_decrypt)(struct st_ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,
-                         ptls_iovec_t aad);
+                         const void *aad, size_t aadlen);
 } ptls_aead_context_t;
 
 /**
@@ -1309,7 +1312,21 @@
  * Encrypts one AEAD block, given a vector of vectors.
  */
 static void ptls_aead_encrypt_v(ptls_aead_context_t *ctx, void *output, ptls_iovec_t *input, size_t incnt, uint64_t seq,
-                                ptls_iovec_t aad);
+                                const void *aad, size_t aadlen);
+/**
+ * initializes the internal state of the encryptor
+ */
+static void ptls_aead_encrypt_init(ptls_aead_context_t *ctx, uint64_t seq, const void *aad, size_t aadlen);
+/**
+ * encrypts the input and updates the GCM state
+ * @return number of bytes emitted to output
+ */
+static size_t ptls_aead_encrypt_update(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen);
+/**
+ * emits buffered data (if any) and the GCM tag
+ * @return number of bytes emitted to output
+ */
+static size_t ptls_aead_encrypt_final(ptls_aead_context_t *ctx, void *output);
 /**
  * decrypts an AEAD record
  * @return number of bytes emitted to output if successful, or SIZE_MAX if the input is invalid (e.g. broken MAC)
@@ -1351,7 +1368,12 @@
  *
  */
 static void ptls_aead__do_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,
-                                  ptls_iovec_t aad, ptls_aead_supplementary_encryption_t *supp);
+                                  const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp);
+/**
+ *
+ */
+static void ptls_aead__do_encrypt_v(ptls_aead_context_t *ctx, void *_output, ptls_iovec_t *input, size_t incnt, uint64_t seq,
+                                    const void *aad, size_t aadlen);
 /**
  * internal
  */
@@ -1478,28 +1500,43 @@
 inline size_t ptls_aead_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,
                                 const void *aad, size_t aadlen)
 {
-    ctx->do_encrypt(ctx, output, input, inlen, seq, ptls_iovec_init(aad, aadlen), NULL);
+    ctx->do_encrypt(ctx, output, input, inlen, seq, aad, aadlen, NULL);
     return inlen + ctx->algo->tag_size;
 }
 
 inline void ptls_aead_encrypt_s(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,
                                 const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp)
 {
-    ctx->do_encrypt(ctx, output, input, inlen, seq, ptls_iovec_init(aad, aadlen), supp);
+    ctx->do_encrypt(ctx, output, input, inlen, seq, aad, aadlen, supp);
 }
 
 inline void ptls_aead_encrypt_v(ptls_aead_context_t *ctx, void *output, ptls_iovec_t *input, size_t incnt, uint64_t seq,
-                                ptls_iovec_t aad)
+                                const void *aad, size_t aadlen)
 {
-    ctx->do_encrypt_v(ctx, output, input, incnt, seq, aad);
+    ctx->do_encrypt_v(ctx, output, input, incnt, seq, aad, aadlen);
+}
+
+inline void ptls_aead_encrypt_init(ptls_aead_context_t *ctx, uint64_t seq, const void *aad, size_t aadlen)
+{
+    ctx->do_encrypt_init(ctx, seq, aad, aadlen);
+}
+
+inline size_t ptls_aead_encrypt_update(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen)
+{
+    return ctx->do_encrypt_update(ctx, output, input, inlen);
+}
+
+inline size_t ptls_aead_encrypt_final(ptls_aead_context_t *ctx, void *output)
+{
+    return ctx->do_encrypt_final(ctx, output);
 }
 
 inline void ptls_aead__do_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,
-                                  ptls_iovec_t aad, ptls_aead_supplementary_encryption_t *supp)
+                                  const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp)
 {
-    ptls_iovec_t invec = ptls_iovec_init(input, inlen);
-
-    ptls_aead_encrypt_v(ctx, output, &invec, 1, seq, aad);
+    ctx->do_encrypt_init(ctx, seq, aad, aadlen);
+    ctx->do_encrypt_update(ctx, output, input, inlen);
+    ctx->do_encrypt_final(ctx, (uint8_t *)output + inlen);
 
     if (supp != NULL) {
         ptls_cipher_init(supp->ctx, supp->input);
@@ -1508,10 +1545,23 @@
     }
 }
 
+inline void ptls_aead__do_encrypt_v(ptls_aead_context_t *ctx, void *_output, ptls_iovec_t *input, size_t incnt, uint64_t seq,
+                                    const void *aad, size_t aadlen)
+{
+    uint8_t *output = _output;
+
+    ctx->do_encrypt_init(ctx, seq, aad, aadlen);
+    for (size_t i = 0; i < incnt; ++i) {
+        ctx->do_encrypt_update(ctx, output, input[i].base, input[i].len);
+        output += input[i].len;
+    }
+    ctx->do_encrypt_final(ctx, output);
+}
+
 inline size_t ptls_aead_decrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,
                                 const void *aad, size_t aadlen)
 {
-    return ctx->do_decrypt(ctx, output, input, inlen, seq, ptls_iovec_init(aad, aadlen));
+    return ctx->do_decrypt(ctx, output, input, inlen, seq, aad, aadlen);
 }
 
 #define ptls_define_hash(name, ctx_type, init_func, update_func, final_func)                                                       \
diff --git a/lib/cifra/aes-common.h b/lib/cifra/aes-common.h
index d638d2a..234e647 100644
--- a/lib/cifra/aes-common.h
+++ b/lib/cifra/aes-common.h
@@ -111,25 +111,33 @@
     ptls_clear_memory((uint8_t *)ctx + sizeof(ctx->super), sizeof(*ctx) - sizeof(ctx->super));
 }
 
-static void aesgcm_encrypt_v(struct st_ptls_aead_context_t *_ctx, void *output, ptls_iovec_t *input, size_t incnt, uint64_t seq,
-                             ptls_iovec_t aad)
+static inline void aesgcm_encrypt_init(ptls_aead_context_t *_ctx, uint64_t seq, const void *aad, size_t aadlen)
 {
     struct aesgcm_context_t *ctx = (struct aesgcm_context_t *)_ctx;
     uint8_t iv[PTLS_AES_BLOCK_SIZE];
 
     ptls_aead__build_iv(ctx->super.algo, iv, ctx->static_iv, seq);
-    cf_gcm_encrypt_init(&cf_aes, &ctx->aes, &ctx->gcm, aad.base, aad.len, iv, PTLS_AESGCM_IV_SIZE);
+    cf_gcm_encrypt_init(&cf_aes, &ctx->aes, &ctx->gcm, aad, aadlen, iv, PTLS_AESGCM_IV_SIZE);
+}
 
-    for (size_t i = 0; i < incnt; ++i) {
-        cf_gcm_encrypt_update(&ctx->gcm, input[i].base, input[i].len, output);
-        output += input[i].len;
-    }
+static inline size_t aesgcm_encrypt_update(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen)
+{
+    struct aesgcm_context_t *ctx = (struct aesgcm_context_t *)_ctx;
+
+    cf_gcm_encrypt_update(&ctx->gcm, input, inlen, output);
+    return inlen;
+}
+
+static inline size_t aesgcm_encrypt_final(ptls_aead_context_t *_ctx, void *output)
+{
+    struct aesgcm_context_t *ctx = (struct aesgcm_context_t *)_ctx;
 
     cf_gcm_encrypt_final(&ctx->gcm, output, PTLS_AESGCM_TAG_SIZE);
+    return PTLS_AESGCM_TAG_SIZE;
 }
 
 static inline size_t aesgcm_decrypt(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen, uint64_t seq,
-                                    ptls_iovec_t aad)
+                                    const void *aad, size_t aadlen)
 {
     struct aesgcm_context_t *ctx = (struct aesgcm_context_t *)_ctx;
     uint8_t iv[PTLS_AES_BLOCK_SIZE];
@@ -139,8 +147,8 @@
     size_t tag_offset = inlen - PTLS_AESGCM_TAG_SIZE;
 
     ptls_aead__build_iv(ctx->super.algo, iv, ctx->static_iv, seq);
-    if (cf_gcm_decrypt(&cf_aes, &ctx->aes, input, tag_offset, aad.base, aad.len, iv, PTLS_AESGCM_IV_SIZE,
-                       (uint8_t *)input + tag_offset, PTLS_AESGCM_TAG_SIZE, output) != 0)
+    if (cf_gcm_decrypt(&cf_aes, &ctx->aes, input, tag_offset, aad, aadlen, iv, PTLS_AESGCM_IV_SIZE, (uint8_t *)input + tag_offset,
+                       PTLS_AESGCM_TAG_SIZE, output) != 0)
         return SIZE_MAX;
 
     return tag_offset;
@@ -162,10 +170,16 @@
     ctx->super.dispose_crypto = aesgcm_dispose_crypto;
     ctx->super.do_xor_iv = aesgcm_xor_iv;
     if (is_enc) {
+        ctx->super.do_encrypt_init = aesgcm_encrypt_init;
+        ctx->super.do_encrypt_update = aesgcm_encrypt_update;
+        ctx->super.do_encrypt_final = aesgcm_encrypt_final;
         ctx->super.do_encrypt = ptls_aead__do_encrypt;
-        ctx->super.do_encrypt_v = aesgcm_encrypt_v;
+        ctx->super.do_encrypt_v = ptls_aead__do_encrypt_v;
         ctx->super.do_decrypt = NULL;
     } else {
+        ctx->super.do_encrypt_init = NULL;
+        ctx->super.do_encrypt_update = NULL;
+        ctx->super.do_encrypt_final = NULL;
         ctx->super.do_encrypt = NULL;
         ctx->super.do_encrypt_v = NULL;
         ctx->super.do_decrypt = aesgcm_decrypt;
diff --git a/lib/cifra/chacha20.c b/lib/cifra/chacha20.c
index 66c8d9c..2e35843 100644
--- a/lib/cifra/chacha20.c
+++ b/lib/cifra/chacha20.c
@@ -103,8 +103,9 @@
     cf_poly1305_finish(&ctx->poly, tag);
 }
 
-static void chacha20poly1305_initialize(struct chacha20poly1305_context_t *ctx, uint64_t seq, ptls_iovec_t aad)
+static void chacha20poly1305_init(ptls_aead_context_t *_ctx, uint64_t seq, const void *aad, size_t aadlen)
 {
+    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
     uint8_t tmpbuf[64];
 
     /* init chacha */
@@ -119,36 +120,38 @@
     ptls_clear_memory(tmpbuf, sizeof(tmpbuf));
 
     /* aad */
-    if (aad.len != 0) {
-        cf_poly1305_update(&ctx->poly, aad.base, aad.len);
-        chacha20poly1305_encrypt_pad(&ctx->poly, aad.len);
+    if (aadlen != 0) {
+        cf_poly1305_update(&ctx->poly, aad, aadlen);
+        chacha20poly1305_encrypt_pad(&ctx->poly, aadlen);
     }
 
-    ctx->aadlen = aad.len;
+    ctx->aadlen = aadlen;
     ctx->textlen = 0;
 }
 
-static void chacha20poly1305_encrypt_v(struct st_ptls_aead_context_t *_ctx, void *output, ptls_iovec_t *input, size_t incnt,
-                                       uint64_t seq, ptls_iovec_t aad)
+static size_t chacha20poly1305_encrypt_update(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen)
 {
     struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
 
-    chacha20poly1305_initialize(ctx, seq, aad);
+    cf_chacha20_cipher(&ctx->chacha, input, output, inlen);
+    cf_poly1305_update(&ctx->poly, output, inlen);
+    ctx->textlen += inlen;
 
-    for (size_t i = 0; i < incnt; ++i) {
-        cf_chacha20_cipher(&ctx->chacha, input[i].base, output, input[i].len);
-        cf_poly1305_update(&ctx->poly, output, input[i].len);
-        output += input[i].len;
-        ctx->textlen += input[i].len;
-    }
+    return inlen;
+}
+
+static size_t chacha20poly1305_encrypt_final(ptls_aead_context_t *_ctx, void *output)
+{
+    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
 
     chacha20poly1305_finalize(ctx, output);
 
     ptls_clear_memory(&ctx->chacha, sizeof(ctx->chacha));
+    return PTLS_CHACHA20POLY1305_TAG_SIZE;
 }
 
 static size_t chacha20poly1305_decrypt(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen, uint64_t seq,
-                                       ptls_iovec_t aad)
+                                       const void *aad, size_t aadlen)
 {
     struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
     uint8_t tag[PTLS_CHACHA20POLY1305_TAG_SIZE];
@@ -157,7 +160,7 @@
     if (inlen < sizeof(tag))
         return SIZE_MAX;
 
-    chacha20poly1305_initialize(ctx, seq, aad);
+    chacha20poly1305_init(&ctx->super, seq, aad, aadlen);
 
     cf_poly1305_update(&ctx->poly, input, inlen - sizeof(tag));
     ctx->textlen = inlen - sizeof(tag);
@@ -192,10 +195,16 @@
     ctx->super.dispose_crypto = chacha20poly1305_dispose_crypto;
     ctx->super.do_xor_iv = chacha20poly1305_xor_iv;
     if (is_enc) {
+        ctx->super.do_encrypt_init = chacha20poly1305_init;
+        ctx->super.do_encrypt_update = chacha20poly1305_encrypt_update;
+        ctx->super.do_encrypt_final = chacha20poly1305_encrypt_final;
         ctx->super.do_encrypt = ptls_aead__do_encrypt;
-        ctx->super.do_encrypt_v = chacha20poly1305_encrypt_v;
+        ctx->super.do_encrypt_v = ptls_aead__do_encrypt_v;
         ctx->super.do_decrypt = NULL;
     } else {
+        ctx->super.do_encrypt_init = NULL;
+        ctx->super.do_encrypt_update = NULL;
+        ctx->super.do_encrypt_final = NULL;
         ctx->super.do_encrypt = NULL;
         ctx->super.do_encrypt_v = NULL;
         ctx->super.do_decrypt = chacha20poly1305_decrypt;
diff --git a/lib/fusion.c b/lib/fusion.c
index 642de8f..e0d977c 100644
--- a/lib/fusion.c
+++ b/lib/fusion.c
@@ -878,6 +878,23 @@
     ptls_fusion_aesgcm_free(ctx->aesgcm);
 }
 
+static void aead_do_encrypt_init(ptls_aead_context_t *_ctx, uint64_t seq, const void *aad, size_t aadlen)
+{
+    assert(!"FIXME");
+}
+
+static size_t aead_do_encrypt_update(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen)
+{
+    assert(!"FIXME");
+    return SIZE_MAX;
+}
+
+static size_t aead_do_encrypt_final(ptls_aead_context_t *_ctx, void *_output)
+{
+    assert(!"FIXME");
+    return SIZE_MAX;
+}
+
 static inline __m128i calc_counter(struct aesgcm_context *ctx, uint64_t seq)
 {
     __m128i ctr = _mm_setzero_si128();
@@ -888,23 +905,23 @@
 }
 
 void aead_do_encrypt(struct st_ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen, uint64_t seq,
-                     ptls_iovec_t aad, ptls_aead_supplementary_encryption_t *supp)
+                     const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp)
 {
     struct aesgcm_context *ctx = (void *)_ctx;
 
-    if (inlen + aad.len > ctx->aesgcm->capacity)
-        ctx->aesgcm = ptls_fusion_aesgcm_set_capacity(ctx->aesgcm, inlen + aad.len);
-    ptls_fusion_aesgcm_encrypt(ctx->aesgcm, output, input, inlen, calc_counter(ctx, seq), aad.base, aad.len, supp);
+    if (inlen + aadlen > ctx->aesgcm->capacity)
+        ctx->aesgcm = ptls_fusion_aesgcm_set_capacity(ctx->aesgcm, inlen + aadlen);
+    ptls_fusion_aesgcm_encrypt(ctx->aesgcm, output, input, inlen, calc_counter(ctx, seq), aad, aadlen, supp);
 }
 
 static void aead_do_encrypt_v(struct st_ptls_aead_context_t *ctx, void *output, ptls_iovec_t *input, size_t incnt, uint64_t seq,
-                              ptls_iovec_t aad)
+                              const void *aad, size_t aadlen)
 {
     assert(!"FIXME");
 }
 
 static size_t aead_do_decrypt(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen, uint64_t seq,
-                              ptls_iovec_t aad)
+                              const void *aad, size_t aadlen)
 {
     struct aesgcm_context *ctx = (void *)_ctx;
 
@@ -912,9 +929,9 @@
         return SIZE_MAX;
 
     size_t enclen = inlen - 16;
-    if (enclen + aad.len > ctx->aesgcm->capacity)
-        ctx->aesgcm = ptls_fusion_aesgcm_set_capacity(ctx->aesgcm, enclen + aad.len);
-    if (!ptls_fusion_aesgcm_decrypt(ctx->aesgcm, output, input, enclen, calc_counter(ctx, seq), aad.base, aad.len,
+    if (enclen + aadlen > ctx->aesgcm->capacity)
+        ctx->aesgcm = ptls_fusion_aesgcm_set_capacity(ctx->aesgcm, enclen + aadlen);
+    if (!ptls_fusion_aesgcm_decrypt(ctx->aesgcm, output, input, enclen, calc_counter(ctx, seq), aad, aadlen,
                                     (const uint8_t *)input + enclen))
         return SIZE_MAX;
     return enclen;
@@ -939,6 +956,9 @@
 
     ctx->super.dispose_crypto = aesgcm_dispose_crypto;
     ctx->super.do_xor_iv = aesgcm_xor_iv;
+    ctx->super.do_encrypt_init = aead_do_encrypt_init;
+    ctx->super.do_encrypt_update = aead_do_encrypt_update;
+    ctx->super.do_encrypt_final = aead_do_encrypt_final;
     ctx->super.do_encrypt = aead_do_encrypt;
     ctx->super.do_encrypt_v = aead_do_encrypt_v;
     ctx->super.do_decrypt = aead_do_decrypt;
diff --git a/lib/openssl.c b/lib/openssl.c
index 47c0e1b..95356a1 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -889,38 +889,53 @@
         ctx->static_iv[i] ^= bytes[i];
 }
 
-static void aead_do_encrypt_v(struct st_ptls_aead_context_t *_ctx, void *output, ptls_iovec_t *input, size_t incnt, uint64_t seq,
-                              ptls_iovec_t aad)
+static void aead_do_encrypt_init(ptls_aead_context_t *_ctx, uint64_t seq, const void *aad, size_t aadlen)
 {
     struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *)_ctx;
     uint8_t iv[PTLS_MAX_IV_SIZE];
-    int blocklen, ret;
+    int ret;
 
     ptls_aead__build_iv(ctx->super.algo, iv, ctx->static_iv, seq);
     ret = EVP_EncryptInit_ex(ctx->evp_ctx, NULL, NULL, NULL, iv);
     assert(ret);
 
-    if (aad.len != 0) {
+    if (aadlen != 0) {
         int blocklen;
-        ret = EVP_EncryptUpdate(ctx->evp_ctx, NULL, &blocklen, aad.base, (int)aad.len);
+        ret = EVP_EncryptUpdate(ctx->evp_ctx, NULL, &blocklen, aad, (int)aadlen);
         assert(ret);
     }
+}
 
-    for (size_t i = 0; i < incnt; ++i) {
-        ret = EVP_EncryptUpdate(ctx->evp_ctx, output, &blocklen, input[i].base, (int)input[i].len);
-        assert(ret);
-        output += blocklen;
-    }
+static size_t aead_do_encrypt_update(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen)
+{
+    struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *)_ctx;
+    int blocklen, ret;
 
-    ret = EVP_EncryptFinal_ex(ctx->evp_ctx, output, &blocklen);
+    ret = EVP_EncryptUpdate(ctx->evp_ctx, output, &blocklen, input, (int)inlen);
     assert(ret);
-    output += blocklen;
-    ret = EVP_CIPHER_CTX_ctrl(ctx->evp_ctx, EVP_CTRL_GCM_GET_TAG, (int)ctx->super.algo->tag_size, output);
+
+    return blocklen;
+}
+
+static size_t aead_do_encrypt_final(ptls_aead_context_t *_ctx, void *_output)
+{
+    struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *)_ctx;
+    uint8_t *output = _output;
+    size_t off = 0, tag_size = ctx->super.algo->tag_size;
+    int blocklen, ret;
+
+    ret = EVP_EncryptFinal_ex(ctx->evp_ctx, output + off, &blocklen);
     assert(ret);
+    off += blocklen;
+    ret = EVP_CIPHER_CTX_ctrl(ctx->evp_ctx, EVP_CTRL_GCM_GET_TAG, (int)tag_size, output + off);
+    assert(ret);
+    off += tag_size;
+
+    return off;
 }
 
 static size_t aead_do_decrypt(ptls_aead_context_t *_ctx, void *_output, const void *input, size_t inlen, uint64_t seq,
-                              ptls_iovec_t aad)
+                              const void *aad, size_t aadlen)
 {
     struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *)_ctx;
     uint8_t *output = _output, iv[PTLS_MAX_IV_SIZE];
@@ -933,8 +948,8 @@
     ptls_aead__build_iv(ctx->super.algo, iv, ctx->static_iv, seq);
     ret = EVP_DecryptInit_ex(ctx->evp_ctx, NULL, NULL, NULL, iv);
     assert(ret);
-    if (aad.len != 0) {
-        ret = EVP_DecryptUpdate(ctx->evp_ctx, NULL, &blocklen, aad.base, (int)aad.len);
+    if (aadlen != 0) {
+        ret = EVP_DecryptUpdate(ctx->evp_ctx, NULL, &blocklen, aad, (int)aadlen);
         assert(ret);
     }
     ret = EVP_DecryptUpdate(ctx->evp_ctx, output + off, &blocklen, input, (int)(inlen - tag_size));
@@ -961,10 +976,16 @@
     ctx->super.dispose_crypto = aead_dispose_crypto;
     ctx->super.do_xor_iv = aead_xor_iv;
     if (is_enc) {
+        ctx->super.do_encrypt_init = aead_do_encrypt_init;
+        ctx->super.do_encrypt_update = aead_do_encrypt_update;
+        ctx->super.do_encrypt_final = aead_do_encrypt_final;
         ctx->super.do_encrypt = ptls_aead__do_encrypt;
-        ctx->super.do_encrypt_v = aead_do_encrypt_v;
+        ctx->super.do_encrypt_v = ptls_aead__do_encrypt_v;
         ctx->super.do_decrypt = NULL;
     } else {
+        ctx->super.do_encrypt_init = NULL;
+        ctx->super.do_encrypt_update = NULL;
+        ctx->super.do_encrypt_final = NULL;
         ctx->super.do_encrypt = NULL;
         ctx->super.do_encrypt_v = NULL;
         ctx->super.do_decrypt = aead_do_decrypt;
diff --git a/lib/picotls.c b/lib/picotls.c
index e6e9eea..90b013b 100644
--- a/lib/picotls.c
+++ b/lib/picotls.c
@@ -642,7 +642,7 @@
     uint8_t aad[5];
 
     build_aad(aad, inlen + 1 + ctx->aead->algo->tag_size);
-    ptls_aead_encrypt_v(ctx->aead, output, invec, PTLS_ELEMENTSOF(invec), ctx->seq++, ptls_iovec_init(aad, sizeof(aad)));
+    ptls_aead_encrypt_v(ctx->aead, output, invec, PTLS_ELEMENTSOF(invec), ctx->seq++, aad, sizeof(aad));
 
     return inlen + 1 + ctx->aead->algo->tag_size;
 }
diff --git a/t/picotls.c b/t/picotls.c
index 92bffae..b1f1bc6 100644
--- a/t/picotls.c
+++ b/t/picotls.c
@@ -168,8 +168,12 @@
     /* encrypt */
     c = ptls_aead_new(cs1->aead, cs1->hash, 1, traffic_secret, NULL);
     assert(c != NULL);
-    enc1len = ptls_aead_encrypt(c, enc1, src1, strlen(src1), 0, NULL, 0);
-    enc2len = ptls_aead_encrypt(c, enc2, src2, strlen(src2), 1, NULL, 0);
+    ptls_aead_encrypt_init(c, 0, NULL, 0);
+    enc1len = ptls_aead_encrypt_update(c, enc1, src1, strlen(src1));
+    enc1len += ptls_aead_encrypt_final(c, enc1 + enc1len);
+    ptls_aead_encrypt_init(c, 1, NULL, 0);
+    enc2len = ptls_aead_encrypt_update(c, enc2, src2, strlen(src2));
+    enc2len += ptls_aead_encrypt_final(c, enc2 + enc2len);
     ptls_aead_free(c);
 
     c = ptls_aead_new(cs2->aead, cs2->hash, 0, traffic_secret, NULL);
@@ -203,7 +207,9 @@
     /* encrypt */
     c = ptls_aead_new(cs1->aead, cs1->hash, 1, traffic_secret, NULL);
     assert(c != NULL);
-    enclen = ptls_aead_encrypt(c, enc, src, strlen(src), 123, aad, strlen(aad));
+    ptls_aead_encrypt_init(c, 123, aad, strlen(aad));
+    enclen = ptls_aead_encrypt_update(c, enc, src, strlen(src));
+    enclen += ptls_aead_encrypt_final(c, enc + enclen);
     ptls_aead_free(c);
 
     /* decrypt */
@@ -230,7 +236,9 @@
     c = ptls_aead_new(cs1->aead, cs1->hash, 1, traffic_secret, NULL);
     assert(c != NULL);
     ptls_aead_xor_iv(c, seq32, sizeof(seq32));
-    enclen = ptls_aead_encrypt(c, enc, src, strlen(src), 123, aad, strlen(aad));
+    ptls_aead_encrypt_init(c, 123, aad, strlen(aad));
+    enclen = ptls_aead_encrypt_update(c, enc, src, strlen(src));
+    enclen += ptls_aead_encrypt_final(c, enc + enclen);
     ptls_aead_free(c);
 
     /* decrypt */