Merge branch 'master' into kazuho/offload
diff --git a/include/picotls.h b/include/picotls.h
index 2396852..f63b311 100644
--- a/include/picotls.h
+++ b/include/picotls.h
@@ -337,6 +337,8 @@
 /**
  * AEAD context. AEAD implementations are allowed to stuff data at the end of the struct. The size of the memory allocated for the
  * struct is governed by ptls_aead_algorithm_t::context_size.
+ * Ciphers for TLS over TCP MUST implement `do_encrypt`, `do_encrypt_v`, `do_decrypt`. `do_encrypt_init`, `~update`, `~final` are
+ * obsolete, and therefore may not be available.
  */
 typedef struct st_ptls_aead_context_t {
     const struct st_ptls_aead_algorithm_t *algo;
@@ -348,6 +350,8 @@
     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,
                        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,
+                         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,
                          const void *aad, size_t aadlen);
 } ptls_aead_context_t;
@@ -1301,24 +1305,31 @@
  */
 static void ptls_aead_xor_iv(ptls_aead_context_t *ctx, const void *bytes, size_t len);
 /**
- *
+ * Encrypts one AEAD block, given input and output vectors.
  */
 static 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);
+/**
+ * Encrypts one AEAD block, as well as one block of ECB (for QUIC / DTLS packet number encryption). Depending on the AEAD engine
+ * being used, the two operations might run simultaneously.
+ */
 static 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);
 /**
- * initializes the internal state of the encryptor
+ * 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,
+                                const void *aad, size_t aadlen);
+/**
+ * Obsolete; new applications should use one of: `ptls_aead_encrypt`, `ptls_aead_encrypt_s`, `ptls_aead_encrypt_v`.
  */
 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
+ * Obsolete; see `ptls_aead_encrypt_init`.
  */
 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
+ * Obsolete; see `ptls_aead_encrypt_init`.
  */
 static size_t ptls_aead_encrypt_final(ptls_aead_context_t *ctx, void *output);
 /**
@@ -1364,6 +1375,11 @@
 static void ptls_aead__do_encrypt(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);
 /**
+ *
+ */
+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
  */
 void ptls__key_schedule_update_hash(ptls_key_schedule_t *sched, const uint8_t *msg, size_t msglen);
@@ -1499,6 +1515,12 @@
     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,
+                                const void *aad, size_t aadlen)
+{
+    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);
@@ -1517,9 +1539,8 @@
 inline void ptls_aead__do_encrypt(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_init(ctx, seq, aad, aadlen);
-    ctx->do_encrypt_update(ctx, output, input, inlen);
-    ctx->do_encrypt_final(ctx, (uint8_t *)output + inlen);
+    ptls_iovec_t invec = ptls_iovec_init(input, inlen);
+    ctx->do_encrypt_v(ctx, output, &invec, 1, seq, aad, aadlen);
 
     if (supp != NULL) {
         ptls_cipher_init(supp->ctx, supp->input);
@@ -1528,6 +1549,17 @@
     }
 }
 
+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)
+        output += ctx->do_encrypt_update(ctx, output, input[i].base, 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)
 {
diff --git a/lib/cifra/aes-common.h b/lib/cifra/aes-common.h
index dfb620a..234e647 100644
--- a/lib/cifra/aes-common.h
+++ b/lib/cifra/aes-common.h
@@ -174,11 +174,14 @@
         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 = 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 8db3a18..2e35843 100644
--- a/lib/cifra/chacha20.c
+++ b/lib/cifra/chacha20.c
@@ -199,11 +199,14 @@
         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 = 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 e77e8ba..e0d977c 100644
--- a/lib/fusion.c
+++ b/lib/fusion.c
@@ -914,6 +914,12 @@
     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,
+                              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,
                               const void *aad, size_t aadlen)
 {
@@ -954,6 +960,7 @@
     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;
 
     ctx->aesgcm = ptls_fusion_aesgcm_new(key, key_size, 1500 /* assume ordinary packet size */);
diff --git a/lib/openssl.c b/lib/openssl.c
index 85327a2..95356a1 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -980,11 +980,14 @@
         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 = 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;
     }
     ctx->evp_ctx = NULL;
diff --git a/lib/picotls.c b/lib/picotls.c
index b9c74e9..8bb5b13 100644
--- a/lib/picotls.c
+++ b/lib/picotls.c
@@ -638,16 +638,13 @@
 static size_t aead_encrypt(struct st_ptls_traffic_protection_t *ctx, void *output, const void *input, size_t inlen,
                            uint8_t content_type)
 {
+    ptls_iovec_t invec[2] = {ptls_iovec_init(input, inlen), ptls_iovec_init(&content_type, 1)};
     uint8_t aad[5];
-    size_t off = 0;
 
     build_aad(aad, inlen + 1 + ctx->aead->algo->tag_size);
-    ptls_aead_encrypt_init(ctx->aead, ctx->seq++, aad, sizeof(aad));
-    off += ptls_aead_encrypt_update(ctx->aead, ((uint8_t *)output) + off, input, inlen);
-    off += ptls_aead_encrypt_update(ctx->aead, ((uint8_t *)output) + off, &content_type, 1);
-    off += ptls_aead_encrypt_final(ctx->aead, ((uint8_t *)output) + off);
+    ptls_aead_encrypt_v(ctx->aead, output, invec, PTLS_ELEMENTSOF(invec), ctx->seq++, aad, sizeof(aad));
 
-    return off;
+    return inlen + 1 + ctx->aead->algo->tag_size;
 }
 
 static int aead_decrypt(struct st_ptls_traffic_protection_t *ctx, void *output, size_t *outlen, const void *input, size_t inlen)
diff --git a/lib/ptlsbcrypt.c b/lib/ptlsbcrypt.c
index 92e26fd..41e6a73 100644
--- a/lib/ptlsbcrypt.c
+++ b/lib/ptlsbcrypt.c
@@ -545,12 +545,15 @@
             ctx->super.do_encrypt_update = ptls_bcrypt_aead_do_encrypt_update;
             ctx->super.do_encrypt_final = ptls_bcrypt_aead_do_encrypt_final;
             ctx->super.do_encrypt = ptls_bcrypt_do_encrypt;
+            ctx->super.do_encrypt_v = ptls_aead__do_encrypt_v;
         } else {
             ctx->super.dispose_crypto = ptls_bcrypt_aead_dispose_crypto;
             ctx->super.do_decrypt = ptls_bcrypt_aead_do_decrypt;
             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;
         }
         return 0;
     } else {
@@ -830,4 +833,4 @@
 ptls_key_exchange_algorithm_t *ptls_bcrypt_key_exchanges[] = {&ptls_bcrypt_secp256r1, NULL};
 #endif
 
-#endif /* _WINDOWS */
\ No newline at end of file
+#endif /* _WINDOWS */
diff --git a/picotlsvs/bcrypt-test/bcrypt-test.c b/picotlsvs/bcrypt-test/bcrypt-test.c
index 513a344..f45c497 100644
--- a/picotlsvs/bcrypt-test/bcrypt-test.c
+++ b/picotlsvs/bcrypt-test/bcrypt-test.c
@@ -374,6 +374,78 @@
     return ret;
 }
 
+int test_encrypt_vector(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz)
+{
+    BYTE key[32];
+    BYTE iv[PTLS_MAX_IV_SIZE];
+    BYTE data[123];
+    uint64_t nonce;
+    BYTE authData[9];
+    BYTE encryptedRef[256];
+    ULONG encryptedRefLength;
+    BYTE encrypted[256];
+    size_t encryptedLength;
+    ULONG authTagLength = (ULONG)aead->tag_size;
+    ptls_aead_context_t *ctx = NULL;
+    int ret = 0;
+
+    assert(sizeof(key) >= aead->key_size);
+    assert(sizeof(iv) >= aead->iv_size);
+    assert(sizeof(data) + authTagLength <= sizeof(encrypted));
+    assert(sizeof(data) + authTagLength <= sizeof(encryptedRef));
+
+    memset(key, 'k', sizeof(key));
+    memset(iv, 'n', sizeof(iv));
+    memset(data, 'd', sizeof(data));
+    nonce = 0;
+    memset(authData, 'a', sizeof(authData));
+
+    /* Create an encryption context */
+    ctx = new_test_aead_context(aead, 1, key, iv);
+    if (ctx == NULL) {
+        ret = -1;
+    }
+
+    if (ret == 0) {
+        ret = EncodeOneShot(aead, name, chain_mode, chain_mode_sz, key, (ULONG)aead->key_size, iv, (ULONG)aead->iv_size, data, 123,
+                            nonce, authData, 9, authTagLength, encryptedRef, 256, &encryptedRefLength);
+    }
+
+    /* Try encrypt with vector procedure */
+    if (ret == 0) {
+        ptls_iovec_t input_vec[2];
+
+        input_vec[0].base = data;
+        input_vec[0].len = 23;
+        input_vec[1].base = data + 23;
+        input_vec[1].len = 100;
+
+        ptls_aead_encrypt_v(ctx, encrypted, input_vec, 2, nonce, authData, 9);
+        encryptedLength = 123 + authTagLength;
+
+        if (encryptedLength != encryptedRefLength) {
+            printf("For %s, encrypt_v returns %d instead of %d\n", aead->name, (int)encryptedLength, encryptedRefLength);
+            ret = -1;
+        } else if (memcmp(encryptedRef, encrypted, encryptedRefLength) != 0) {
+            printf("For %s, vector encrypted does not match ref\n", aead->name);
+            for (ULONG i = 0; i < encryptedRefLength; i++) {
+                if (encryptedRef[i] != encrypted[i]) {
+                    printf("For %s, vector encrypted[%d] = 0x%02x vs encryptedRef[%d] = 0x%02x\n", aead->name, i, encrypted[i], i,
+                           encryptedRef[i]);
+                    break;
+                }
+            }
+            ret = -1;
+        } else {
+            printf("For %s, vector encrypting test passes.\n", aead->name);
+        }
+    }
+
+    delete_test_aead_context(ctx);
+
+    return ret;
+}
+
 int test_for_size(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz)
 {
     BYTE key[32];
@@ -482,6 +554,13 @@
     }
 
     if (ret == 0) {
+        ret = test_encrypt_vector(aead, name, chain_mode, chain_mode_sz);
+
+        printf("For %s, test encrypt returns %d\n", aead->name, ret);
+    }
+
+
+    if (ret == 0) {
         ret = test_for_size(aead, name, chain_mode, chain_mode_sz);
 
         printf("For %s, test packet sizes returns %d\n", aead->name, ret);