Add support for xor_iv
diff --git a/include/picotls.h b/include/picotls.h
index 0ffe7d7..ca25ef3 100644
--- a/include/picotls.h
+++ b/include/picotls.h
@@ -324,6 +324,7 @@
     const struct st_ptls_aead_algorithm_t *algo;
     /* 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 * xor, size_t xorlen);
     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);
@@ -1229,6 +1230,11 @@
  */
 void ptls_aead_free(ptls_aead_context_t *ctx);
 /**
+ * Permutates the static IV by applying given bytes using bit-wise XOR. This API can be used for supplying nonces longer than 64-
+ * bits.
+ */
+static void ptls_aead_xor_iv(ptls_aead_context_t *ctx, const void *bytes, size_t len); 
+/**
  *
  */
 static size_t ptls_aead_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,
@@ -1413,6 +1419,11 @@
     ctx->do_transform(ctx, output, input, len);
 }
 
+inline void ptls_aead_xor_iv(ptls_aead_context_t *ctx, const void *bytes, size_t len)
+{
+    ctx->do_xor_iv(ctx, bytes, len);
+}
+
 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)
 {
diff --git a/lib/cifra/aes-common.h b/lib/cifra/aes-common.h
index 0c393c5..95a9463 100644
--- a/lib/cifra/aes-common.h
+++ b/lib/cifra/aes-common.h
@@ -154,11 +154,21 @@
     return tag_offset;
 }
 
+static inline void minicrypto_aesgcm_aead_xor_iv(ptls_aead_context_t *_ctx, const void *_bytes, size_t len)
+{
+    struct aesgcm_context_t *ctx = (struct aesgcm_context_t *)_ctx;
+    const uint8_t *bytes = _bytes;
+
+    for (size_t i = 0; i < len; ++i)
+        ctx->static_iv[i] ^= bytes[i];
+}
+
 static inline int aead_aesgcm_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv)
 {
     struct aesgcm_context_t *ctx = (struct aesgcm_context_t *)_ctx;
 
     ctx->super.dispose_crypto = aesgcm_dispose_crypto;
+    ctx->super.do_xor_iv = minicrypto_aesgcm_aead_xor_iv;
     if (is_enc) {
         ctx->super.do_encrypt_init = aesgcm_encrypt_init;
         ctx->super.do_encrypt_update = aesgcm_encrypt_update;
diff --git a/lib/cifra/chacha20.c b/lib/cifra/chacha20.c
index 57a6c39..e51a930 100644
--- a/lib/cifra/chacha20.c
+++ b/lib/cifra/chacha20.c
@@ -179,11 +179,21 @@
     return ret;
 }
 
+static void minicrypto_chacha20_aead_xor_iv(ptls_aead_context_t *_ctx, const void *_bytes, size_t len)
+{
+    struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
+    const uint8_t *bytes = _bytes;
+
+    for (size_t i = 0; i < len; ++i)
+        ctx->static_iv[i] ^= bytes[i];
+}
+
 static int aead_chacha20poly1305_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv)
 {
     struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
 
     ctx->super.dispose_crypto = chacha20poly1305_dispose_crypto;
+    ctx->super.do_xor_iv = minicrypto_chacha20_aead_xor_iv;
     if (is_enc) {
         ctx->super.do_encrypt_init = chacha20poly1305_init;
         ctx->super.do_encrypt_update = chacha20poly1305_encrypt_update;
diff --git a/lib/fusion.c b/lib/fusion.c
index 1610447..cbf70a5 100644
--- a/lib/fusion.c
+++ b/lib/fusion.c
@@ -924,6 +924,14 @@
     return enclen;
 }
 
+static inline void aesgcm_fusion_aead_xor_iv(ptls_aead_context_t *_ctx, const void *_bytes, size_t len)
+{
+    struct aesgcm_context *ctx = (struct aesgcm_context *)_ctx;
+    const uint8_t *bytes = _bytes;
+    __m128i xor_mask = loadn(_bytes, len);
+    ctx->static_iv = _mm_xor_si128(ctx->static_iv, xor_mask);
+}
+
 static int aesgcm_setup(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv, size_t key_size)
 {
     struct aesgcm_context *ctx = (struct aesgcm_context *)_ctx;
@@ -934,6 +942,7 @@
         return 0;
 
     ctx->super.dispose_crypto = aesgcm_dispose_crypto;
+    ctx->super.do_xor_iv = aesgcm_fusion_aead_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;
diff --git a/lib/openssl.c b/lib/openssl.c
index c7ba115..00c7777 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -779,6 +779,15 @@
         EVP_CIPHER_CTX_free(ctx->evp_ctx);
 }
 
+static void aead_xor_iv(ptls_aead_context_t *_ctx, const void *_bytes, size_t len)
+{
+    struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *)_ctx;
+    const uint8_t *bytes = _bytes;
+
+    for (size_t i = 0; i < len; ++i)
+        ctx->static_iv[i] ^= bytes[i];
+}
+
 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;
@@ -864,6 +873,7 @@
         return 0;
 
     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;
diff --git a/lib/ptlsbcrypt.c b/lib/ptlsbcrypt.c
index 9f46a47..43c442f 100644
--- a/lib/ptlsbcrypt.c
+++ b/lib/ptlsbcrypt.c
@@ -476,6 +476,15 @@
     }
 }
 
+static void ptls_bcrypt_aead_xor_iv(ptls_aead_context_t *_ctx, const void *_bytes, size_t len)
+{
+    struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx;
+    const uint8_t *bytes = _bytes;
+
+    for (size_t i = 0; i < len; ++i)
+        ctx->bctx.iv[i] ^= bytes[i];
+}
+
 static int ptls_bcrypt_aead_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv,
                                          wchar_t const *bcrypt_name, wchar_t const *bcrypt_mode, size_t bcrypt_mode_size)
 {
@@ -530,6 +539,7 @@
         memcpy(ctx->bctx.iv_static, iv, ctx->super.algo->iv_size);
         if (is_enc) {
             ctx->super.dispose_crypto = ptls_bcrypt_aead_dispose_crypto;
+            ctx->super.do_xor_iv = ptls_bcrypt_aead_xor_iv;
             ctx->super.do_decrypt = NULL;
             ctx->super.do_encrypt_init = ptls_bcrypt_aead_do_encrypt_init;
             ctx->super.do_encrypt_update = ptls_bcrypt_aead_do_encrypt_update;
diff --git a/t/fusion.c b/t/fusion.c
index a7a9685..cd30702 100644
--- a/t/fusion.c
+++ b/t/fusion.c
@@ -193,6 +193,31 @@
     ptls_fusion_aesgcm_free(aead);
 }
 
+static void gcm_iv96(void)
+{
+    static const uint8_t key[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
+                         aad[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19},
+                         iv[] = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
+                         plaintext[] =
+                             "hello world\nhello world\nhello world\nhello world\nhello world\nhello world\nhello world\n";
+
+    ptls_aead_context_t *aead = ptls_aead_new_direct(&ptls_fusion_aes128gcm, 0, key, iv);
+    uint8_t encrypted[sizeof(plaintext) + 16], decrypted[sizeof(plaintext)];
+    uint8_t seq32[4] = {0x11, 0x23, 0x45, 0x67};
+    uint8_t seq32_bad[4] = {0x89, 0xab, 0xcd, 0xef};
+    ptls_aead_xor_iv(aead, seq32, sizeof(seq32));
+    ptls_aead_encrypt(aead, encrypted, plaintext, sizeof(plaintext), 0, aad, sizeof(aad));
+    ok(ptls_aead_decrypt(aead, decrypted, encrypted, sizeof(encrypted), 0, aad, sizeof(aad)) == sizeof(plaintext));
+    ok(memcmp(decrypted, plaintext, sizeof(plaintext)) == 0);
+    ptls_aead_xor_iv(aead, seq32, sizeof(seq32));
+    ptls_aead_xor_iv(aead, seq32_bad, sizeof(seq32_bad));
+    ok(ptls_aead_decrypt(aead, decrypted, encrypted, sizeof(encrypted), 0, aad, sizeof(aad)) == SIZE_MAX);
+    ptls_aead_xor_iv(aead, seq32_bad, sizeof(seq32_bad));
+    ptls_aead_xor_iv(aead, seq32, sizeof(seq32));
+    ok(ptls_aead_decrypt(aead, decrypted, encrypted, sizeof(encrypted), 0, aad, sizeof(aad)) == sizeof(plaintext));
+    ptls_aead_free(aead);
+}
+
 static void test_generated(int aes256)
 {
     ptls_cipher_context_t *rand = ptls_cipher_new(&ptls_minicrypto_aes128ctr, 1, zero);
@@ -274,6 +299,7 @@
     subtest("gcm-basic", gcm_basic);
     subtest("gcm-capacity", gcm_capacity);
     subtest("gcm-test-vectors", gcm_test_vectors);
+    subtest("gcm-iv96", gcm_iv96);
     subtest("generated-128", test_generated_aes128);
     subtest("generated-256", test_generated_aes256);
 
diff --git a/t/picotls.c b/t/picotls.c
index ca07a90..d0e88ff 100644
--- a/t/picotls.c
+++ b/t/picotls.c
@@ -192,6 +192,46 @@
     ptls_aead_free(c);
 }
 
+static void test_aad96_ciphersuite(ptls_cipher_suite_t *cs1, ptls_cipher_suite_t *cs2)
+{
+    const char *traffic_secret = "012345678901234567890123456789012345678901234567", *src = "hello world", *aad = "my true aad";
+    ptls_aead_context_t *c;
+    char enc[256], dec[256];
+    uint8_t seq32[4] = {0xa1, 0xb2, 0xc3, 0xd4};
+    uint8_t seq32_bad[4] = {0xa2, 0xb3, 0xc4, 0xe5};
+    size_t enclen, declen;
+
+    /* encrypt */
+    c = ptls_aead_new(cs1->aead, cs1->hash, 1, traffic_secret, NULL);
+    assert(c != NULL);
+    ptls_aead_xor_iv(c, seq32, sizeof(seq32));
+    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 */
+    c = ptls_aead_new(cs2->aead, cs2->hash, 0, traffic_secret, NULL);
+    assert(c != NULL);
+    /* test first decryption */
+    ptls_aead_xor_iv(c, seq32, sizeof(seq32));
+    declen = ptls_aead_decrypt(c, dec, enc, enclen, 123, aad, strlen(aad));
+    ptls_aead_xor_iv(c, seq32, sizeof(seq32));
+    ok(declen == strlen(src));
+    ok(memcmp(src, dec, declen) == 0);
+    /* test that setting the wrong IV creates an error */
+    ptls_aead_xor_iv(c, seq32_bad, sizeof(seq32_bad));
+    declen = ptls_aead_decrypt(c, dec, enc, enclen, 123, aad, strlen(aad));
+    ptls_aead_xor_iv(c, seq32_bad, sizeof(seq32_bad));
+    ok(declen == SIZE_MAX);
+    /* test second decryption with correct IV to verify no side effect */
+    ptls_aead_xor_iv(c, seq32, sizeof(seq32));
+    declen = ptls_aead_decrypt(c, dec, enc, enclen, 123, aad, strlen(aad));
+    ok(declen == strlen(src));
+    ok(memcmp(src, dec, declen) == 0);
+    ptls_aead_free(c);
+}
+
 static void test_ecb(ptls_cipher_algorithm_t *algo, const void *expected, size_t expected_len)
 {
     static const uint8_t key[] = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,
@@ -289,6 +329,7 @@
 
     test_ciphersuite(cs, cs_peer);
     test_aad_ciphersuite(cs, cs_peer);
+    test_aad96_ciphersuite(cs, cs_peer);
 }
 
 static void test_aes256gcm(void)
@@ -299,6 +340,7 @@
     if (cs != NULL && cs_peer != NULL) {
         test_ciphersuite(cs, cs_peer);
         test_aad_ciphersuite(cs, cs_peer);
+        test_aad96_ciphersuite(cs, cs_peer);
     }
 }
 
@@ -310,6 +352,7 @@
     if (cs != NULL && cs_peer != NULL) {
         test_ciphersuite(cs, cs_peer);
         test_aad_ciphersuite(cs, cs_peer);
+        test_aad96_ciphersuite(cs, cs_peer);
     }
 }