Merge pull request #316 from h2o/fusion-in-vs

Fusion in vs
diff --git a/include/picotls.h b/include/picotls.h
index 3be5278..e5c52ee 100644
--- a/include/picotls.h
+++ b/include/picotls.h
@@ -828,7 +828,10 @@
 #ifdef _WINDOWS
 #pragma warning(pop)
 #endif
-
+#ifdef _WINDOWS
+/* suppress warning C4293: >> shift count negative or too big */
+#pragma warning(disable : 4293)
+#endif
 /**
  * builds a new ptls_iovec_t instance using the supplied parameters
  */
diff --git a/lib/fusion.c b/lib/fusion.c
index 3af2bd1..b86205f 100644
--- a/lib/fusion.c
+++ b/lib/fusion.c
@@ -38,6 +38,7 @@
  * IN THE SOFTWARE.
  */
 #include <stdint.h>
+    
 #include <stdlib.h>
 #include <string.h>
 #include <immintrin.h>
@@ -293,7 +294,7 @@
 
     __m128i ek0, bits0, bits1, bits2, bits3, bits4, bits5 = _mm_setzero_si128();
     const __m128i *bits4keys = ctx->ecb.keys; /* is changed to supp->ctx.keys when calcurating suppout */
-    struct ptls_fusion_gfmul_state gstate = {};
+    struct ptls_fusion_gfmul_state gstate = { 0 };
     __m128i gdatabuf[6];
     __m128i ac = _mm_shuffle_epi8(_mm_set_epi32(0, (int)aadlen * 8, 0, (int)inlen * 8), bswap8);
 
@@ -491,7 +492,7 @@
 {
     __m128i ek0 = _mm_setzero_si128(), bits0, bits1 = _mm_setzero_si128(), bits2 = _mm_setzero_si128(), bits3 = _mm_setzero_si128(),
             bits4 = _mm_setzero_si128(), bits5 = _mm_setzero_si128();
-    struct ptls_fusion_gfmul_state gstate = {};
+    struct ptls_fusion_gfmul_state gstate = { 0 };
     __m128i gdatabuf[6];
     __m128i ac = _mm_shuffle_epi8(_mm_set_epi32(0, (int)aadlen * 8, 0, (int)inlen * 8), bswap8);
     struct ptls_fusion_aesgcm_ghash_precompute *ghash_precompute = ctx->ghash + (aadlen + 15) / 16 + (inlen + 15) / 16 + 1;
@@ -983,6 +984,40 @@
                                                sizeof(struct aesgcm_context),
                                                aes256gcm_setup};
 
+#ifdef _WINDOWS
+/**
+ * ptls_fusion_is_supported_by_cpu:
+ * Check that the CPU has extended instructions for PCMUL, AES and AVX2.
+ * This test assumes that the CPU is following the x86/x64 architecture.
+ * A slightly more refined test could check that the cpu_info spells out
+ * "genuineIntel" or "authenticAMD", but would fail in presence of
+ * little known CPU brands or some VM */
+int ptls_fusion_is_supported_by_cpu(void)
+{
+    uint32_t cpu_info[4];
+    uint32_t nb_ids;
+    int is_supported = 0;
+
+    __cpuid(cpu_info, 0);
+    nb_ids = cpu_info[0];
+
+    if (nb_ids >= 7) {
+        uint32_t leaf1_ecx;
+        __cpuid(cpu_info, 1);
+        leaf1_ecx = cpu_info[2];
+        
+        if (/* PCLMUL */ (leaf1_ecx & (1 << 5)) != 0 && /* AES */ (leaf1_ecx & (1 << 25)) != 0) {
+            uint32_t leaf7_ebx;
+            __cpuid(cpu_info, 7);
+            leaf7_ebx = cpu_info[1];
+
+            is_supported = /* AVX2 */ (leaf7_ebx & (1 << 5)) != 0;
+        }
+    }
+
+    return is_supported;
+}
+#else
 int ptls_fusion_is_supported_by_cpu(void)
 {
     unsigned leaf1_ecx, leaf7_ebx;
@@ -1008,3 +1043,4 @@
 
     return 1;
 }
+#endif
diff --git a/lib/picotls.c b/lib/picotls.c
index e6bd528..9f84120 100644
--- a/lib/picotls.c
+++ b/lib/picotls.c
@@ -5140,7 +5140,7 @@
                               ptls_iovec_t hash_value, const char *label_prefix)
 {
     ptls_aead_context_t *ctx = NULL;
-    uint8_t key_iv[aead->key_size + aead->iv_size];
+    uint8_t key_iv[PTLS_MAX_SECRET_SIZE + PTLS_MAX_IV_SIZE];
     int ret;
 
     if ((ret = get_traffic_key(hash, key_iv, aead->key_size, 0, secret, hash_value, label_prefix)) != 0)
diff --git a/lib/ptlsbcrypt.c b/lib/ptlsbcrypt.c
index 8b32da7..1035c69 100644
--- a/lib/ptlsbcrypt.c
+++ b/lib/ptlsbcrypt.c
@@ -293,6 +293,7 @@
     ULONG maxTagLength;
     ULONG nbExtraBytes;
     uint8_t *key_object;
+    uint8_t iv_static[PTLS_MAX_IV_SIZE];
     uint8_t extraBytes[PTLS_MAX_DIGEST_SIZE];
     uint8_t iv[PTLS_MAX_IV_SIZE];
     uint8_t ivbuf[PTLS_MAX_IV_SIZE];
@@ -321,12 +322,12 @@
     memset(&ctx->bctx, 0, sizeof(struct ptls_bcrypt_aead_param_t));
 }
 
-static void ptls_bcrypt_aead_do_encrypt_init(struct st_ptls_aead_context_t *_ctx, const void *iv, const void *aad, size_t aadlen)
+static void ptls_bcrypt_aead_do_encrypt_init(struct st_ptls_aead_context_t *_ctx, uint64_t seq, const void *aad, size_t aadlen)
 {
     struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx;
 
-    /* Save a copy of the IV*/
-    memcpy(ctx->bctx.iv, iv, ctx->super.algo->iv_size);
+    /* Build the IV for this encryption */
+    ptls_aead__build_iv(ctx->super.algo, ctx->bctx.iv, ctx->bctx.iv_static, seq);
     /* Auth tag to NULL */
     memset(ctx->bctx.tag, 0, sizeof(ctx->super.algo->tag_size));
     BCRYPT_INIT_AUTH_MODE_INFO(ctx->bctx.aead_params);
@@ -427,15 +428,15 @@
 }
 
 static size_t ptls_bcrypt_aead_do_decrypt(struct st_ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen,
-                                          const void *iv, const void *aad, size_t aadlen)
+                                          uint64_t seq, const void *aad, size_t aadlen)
 {
     struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx;
     ULONG cbResult;
     size_t textLen = inlen - ctx->super.algo->tag_size;
     NTSTATUS ret;
 
-    /* Save a copy of the IV*/
-    memcpy(ctx->bctx.iv, iv, ctx->super.algo->iv_size);
+    /* Build the IV for this decryption */
+    ptls_aead__build_iv(ctx->super.algo, ctx->bctx.iv, ctx->bctx.iv_static, seq);
 
     /* TODO: pPaddingInfo must point to BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO structure. */
     BCRYPT_INIT_AUTH_MODE_INFO(ctx->bctx.aead_params);
@@ -459,8 +460,8 @@
     }
 }
 
-static int ptls_bcrypt_aead_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key, wchar_t const *bcrypt_name,
-                                         wchar_t const *bcrypt_mode, size_t bcrypt_mode_size)
+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)
 {
     struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx;
     HANDLE hAlgorithm = NULL;
@@ -510,12 +511,14 @@
     }
 
     if (BCRYPT_SUCCESS(ret)) {
+        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_decrypt = NULL;
             ctx->super.do_encrypt_init = ptls_bcrypt_aead_do_encrypt_init;
             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_aead__do_encrypt;
         } else {
             ctx->super.dispose_crypto = ptls_bcrypt_aead_dispose_crypto;
             ctx->super.do_decrypt = ptls_bcrypt_aead_do_decrypt;
@@ -530,9 +533,9 @@
     }
 }
 
-static int ptls_bcrypt_aead_setup_crypto_aesgcm(ptls_aead_context_t *_ctx, int is_enc, const void *key)
+static int ptls_bcrypt_aead_setup_crypto_aesgcm(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void * iv)
 {
-    return ptls_bcrypt_aead_setup_crypto(_ctx, is_enc, key, BCRYPT_AES_ALGORITHM, BCRYPT_CHAIN_MODE_GCM,
+    return ptls_bcrypt_aead_setup_crypto(_ctx, is_enc, key, iv, BCRYPT_AES_ALGORITHM, BCRYPT_CHAIN_MODE_GCM,
                                          sizeof(BCRYPT_CHAIN_MODE_GCM));
 }
 
diff --git a/picotlsvs/bcrypt-test/bcrypt-test.c b/picotlsvs/bcrypt-test/bcrypt-test.c
index 324d425..513a344 100644
--- a/picotlsvs/bcrypt-test/bcrypt-test.c
+++ b/picotlsvs/bcrypt-test/bcrypt-test.c
@@ -69,14 +69,17 @@
     *ko_length = 0;
 }
 
-int EncodeOneShot(wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz, BYTE *key, ULONG key_length, BYTE *data,
-                  ULONG dataLength, BYTE *nonceValue, ULONG nonceLength, BYTE *authData, ULONG authDataLength, ULONG authTagLength,
-                  BYTE *encrypted, ULONG encryptedLengthMax, ULONG *encryptedLength)
+int EncodeOneShot(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz, BYTE *key,
+                  ULONG key_length,
+    BYTE* iv, ULONG iv_length,
+    BYTE *data, ULONG dataLength, uint64_t seq, BYTE *authData, ULONG authDataLength, ULONG authTagLength,
+    BYTE *encrypted, ULONG encryptedLengthMax, ULONG *encryptedLength)
 {
 
     BCRYPT_KEY_HANDLE hKey = NULL;
     BYTE *authTag = encrypted + dataLength;
     BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO bacmi;
+    BYTE iv_nonce[PTLS_MAX_IV_SIZE];
     BYTE *ko = NULL;
     ULONG ko_length = 0;
     int ret = 0;
@@ -90,8 +93,9 @@
     memset(authTag, 0, authTagLength);
     // Set the auth mode info for AEAD
     BCRYPT_INIT_AUTH_MODE_INFO(bacmi);
-    bacmi.pbNonce = nonceValue;
-    bacmi.cbNonce = nonceLength;
+    ptls_aead__build_iv(aead, iv_nonce, iv, seq);
+    bacmi.pbNonce = iv_nonce;
+    bacmi.cbNonce = iv_length;
     bacmi.pbAuthData = authData;
     bacmi.cbAuthData = authDataLength;
     bacmi.pbTag = authTag;
@@ -113,13 +117,15 @@
     return ret;
 }
 
-int DecodeOneShot(wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz, BYTE *key, ULONG key_length, BYTE *encrypted,
-                  ULONG encryptedLength, BYTE *nonceValue, ULONG nonceLength, BYTE *authData, ULONG authDataLength,
+int DecodeOneShot(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz, 
+    BYTE *key, ULONG key_length, BYTE * iv, ULONG iv_length, BYTE *encrypted,
+                  ULONG encryptedLength, uint64_t seq, BYTE *authData, ULONG authDataLength,
                   ULONG authTagLength, BYTE *decrypted, ULONG decryptedLengthMax, ULONG *decryptedLength)
 {
 
     BCRYPT_KEY_HANDLE hKey = NULL;
     BYTE *authTag = encrypted + (encryptedLength - authTagLength);
+    BYTE iv_nonce[PTLS_MAX_IV_SIZE];
     BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO bacmi;
     BYTE *ko = NULL;
     ULONG ko_length = 0;
@@ -133,8 +139,9 @@
 
     // Set the auth mode info for AEAD
     BCRYPT_INIT_AUTH_MODE_INFO(bacmi);
-    bacmi.pbNonce = nonceValue;
-    bacmi.cbNonce = nonceLength;
+    ptls_aead__build_iv(aead, iv_nonce, iv, seq);
+    bacmi.pbNonce = iv_nonce;
+    bacmi.cbNonce = iv_length;
     bacmi.pbAuthData = authData;
     bacmi.cbAuthData = authDataLength;
     bacmi.pbTag = authTag;
@@ -161,7 +168,8 @@
 {
     BYTE key[32];
     BYTE data[123];
-    BYTE nonce[12];
+    BYTE iv[PTLS_MAX_IV_SIZE];
+    uint64_t nonce;
     BYTE authData[9];
     BYTE encrypted[256];
     ULONG encryptedLength;
@@ -171,23 +179,25 @@
     int ret = 0;
 
     assert(sizeof(key) >= aead->key_size);
-    assert(sizeof(nonce) >= aead->iv_size);
+    assert(sizeof(iv) >= aead->iv_size);
     assert(sizeof(data) + authTagLength <= sizeof(encrypted));
     assert(sizeof(decrypted) >= sizeof(encrypted));
 
     memset(key, 'k', sizeof(key));
     memset(data, 'd', sizeof(data));
-    memset(nonce, 'n', sizeof(nonce));
+    memset(iv, 'n', sizeof(iv));
+    nonce = 0;
     memset(authData, 'a', sizeof(authData));
 
-    ret = EncodeOneShot(name, chain_mode, chain_mode_sz, key, (ULONG)aead->key_size, data, 123, nonce, (ULONG)aead->iv_size,
-                        authData, 9, authTagLength, encrypted, 256, &encryptedLength);
+    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, encrypted, 256, &encryptedLength);
 
     printf("Encrypt one shot returns %d, l=%d\n", ret, encryptedLength);
 
     if (ret == 0) {
-        ret = DecodeOneShot(name, chain_mode, chain_mode_sz, key, (ULONG)aead->key_size, encrypted, encryptedLength, nonce,
-                            (ULONG)aead->iv_size, authData, 9, authTagLength, decrypted, 256, &decryptedLength);
+        ret = DecodeOneShot(aead, name, chain_mode, chain_mode_sz, key, (ULONG)aead->key_size, 
+            iv, (ULONG)aead->iv_size, encrypted, encryptedLength, nonce, authData, 9, 
+            authTagLength, decrypted, 256, &decryptedLength);
 
         printf("Decrypt one shot returns %d, l=%d\n", ret, decryptedLength);
 
@@ -215,7 +225,7 @@
     }
 }
 
-ptls_aead_context_t *new_test_aead_context(ptls_aead_algorithm_t *aead, int is_enc, BYTE *key)
+ptls_aead_context_t *new_test_aead_context(ptls_aead_algorithm_t *aead, int is_enc, BYTE *key, BYTE *iv)
 {
     int ret = 0;
     ptls_aead_context_t *ctx = (ptls_aead_context_t *)malloc(aead->context_size);
@@ -223,7 +233,7 @@
     if (ctx != NULL) {
         memset(ctx, 0, aead->context_size);
         *ctx = (ptls_aead_context_t){aead};
-        if (aead->setup_crypto(ctx, is_enc, key) != 0) {
+        if (aead->setup_crypto(ctx, is_enc, key, iv) != 0) {
             printf("For %s, setup returns %d\n", aead->name, ret);
             delete_test_aead_context(ctx);
             ctx = NULL;
@@ -238,8 +248,9 @@
 int test_decrypt(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];
-    BYTE nonce[12];
+    uint64_t nonce;
     BYTE authData[9];
     BYTE encrypted[256];
     ULONG encryptedLength;
@@ -250,25 +261,27 @@
     int ret = 0;
 
     assert(sizeof(key) >= aead->key_size);
-    assert(sizeof(nonce) >= aead->iv_size);
+    assert(sizeof(iv) >= aead->iv_size);
     assert(sizeof(data) + authTagLength <= sizeof(encrypted));
     assert(sizeof(decrypted) >= sizeof(encrypted));
 
     memset(key, 'k', sizeof(key));
+    memset(iv, 'n', sizeof(iv));
     memset(data, 'd', sizeof(data));
-    memset(nonce, 'n', sizeof(nonce));
+    nonce = 0;
     memset(authData, 'a', sizeof(authData));
 
     /* Create a decryption context */
-    ctx = new_test_aead_context(aead, 0, key);
+    ctx = new_test_aead_context(aead, 0, key, iv);
     if (ctx == NULL) {
         ret = -1;
     }
 
     /* Do a simple encrypt using one shot bcrypt */
     if (ret == 0) {
-        ret = EncodeOneShot(name, chain_mode, chain_mode_sz, key, (ULONG)aead->key_size, data, 123, nonce, (ULONG)aead->iv_size,
-                            authData, 9, authTagLength, encrypted, 256, &encryptedLength);
+        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, encrypted, 256, &encryptedLength);
     }
 
     /* Try decrypt with library procedure */
@@ -296,8 +309,9 @@
 int test_encrypt(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];
-    BYTE nonce[12];
+    uint64_t nonce;
     BYTE authData[9];
     BYTE encryptedRef[256];
     ULONG encryptedRefLength;
@@ -308,25 +322,27 @@
     int ret = 0;
 
     assert(sizeof(key) >= aead->key_size);
-    assert(sizeof(nonce) >= aead->iv_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));
-    memset(nonce, 'n', sizeof(nonce));
+    nonce = 0;
     memset(authData, 'a', sizeof(authData));
 
     /* Create an encryption context */
-    ctx = new_test_aead_context(aead, 1, key);
+    ctx = new_test_aead_context(aead, 1, key, iv);
     if (ctx == NULL) {
         ret = -1;
     }
 
     /* Do a simple encrypt using one shot bcrypt */
     if (ret == 0) {
-        ret = EncodeOneShot(name, chain_mode, chain_mode_sz, key, (ULONG)aead->key_size, data, 123, nonce, (ULONG)aead->iv_size,
-                            authData, 9, authTagLength, encryptedRef, 256, &encryptedRefLength);
+        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 library procedure */
@@ -361,7 +377,8 @@
 int test_for_size(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz)
 {
     BYTE key[32];
-    BYTE nonce[12];
+    BYTE iv[PTLS_MAX_IV_SIZE];
+    uint64_t nonce;
     BYTE authData[9];
     BYTE *data = NULL;
     BYTE *encrypted = NULL;
@@ -376,15 +393,16 @@
     int ret = 0;
 
     assert(sizeof(key) >= aead->key_size);
-    assert(sizeof(nonce) >= aead->iv_size);
+    assert(sizeof(iv) >= aead->iv_size);
 
     memset(key, 'k', sizeof(key));
-    memset(nonce, 'n', sizeof(nonce));
+    memset(key, 'n', sizeof(iv));
+    nonce = 0;
     memset(authData, 'a', sizeof(authData));
 
     /* Create the encryption contexts */
-    ctx_e = new_test_aead_context(aead, 1, key);
-    ctx_d = new_test_aead_context(aead, 0, key);
+    ctx_e = new_test_aead_context(aead, 1, key, iv);
+    ctx_d = new_test_aead_context(aead, 0, key, iv);
 
     if (ctx_e == NULL || ctx_d == NULL) {
         ret = -1;
diff --git a/picotlsvs/fusiontest/fusiontest.vcxproj b/picotlsvs/fusiontest/fusiontest.vcxproj
new file mode 100644
index 0000000..6e55e9e
--- /dev/null
+++ b/picotlsvs/fusiontest/fusiontest.vcxproj
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\deps\picotest\picotest.c" />
+    <ClCompile Include="..\..\t\fusion.c" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>16.0</VCProjectVersion>
+    <ProjectGuid>{513DC1C9-2CD4-437C-B8EC-168924EB5AAC}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>fusiontest</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>_DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(OPENSSL64DIR);$(OPENSSL64DIR)\lib;$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>picotls-core.lib;picotls-fusion.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(OPENSSLDIR);$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>picotls-core.lib;picotls-fusion.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>picotls-core.lib;picotls-fusion.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OPENSSLDIR);$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>picotls-core.lib;picotls-fusion.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OPENSSL64DIR);$(OPENSSL64DIR)\lib;$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/picotlsvs/fusiontest/fusiontest.vcxproj.filters b/picotlsvs/fusiontest/fusiontest.vcxproj.filters
new file mode 100644
index 0000000..6869114
--- /dev/null
+++ b/picotlsvs/fusiontest/fusiontest.vcxproj.filters
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\t\fusion.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\deps\picotest\picotest.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/picotlsvs/fusiontest/fusiontest.vcxproj.user b/picotlsvs/fusiontest/fusiontest.vcxproj.user
new file mode 100644
index 0000000..88a5509
--- /dev/null
+++ b/picotlsvs/fusiontest/fusiontest.vcxproj.user
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup />
+</Project>
\ No newline at end of file
diff --git a/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj b/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj
index 8444ca3..7b5fb25 100644
--- a/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj
+++ b/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj
@@ -76,7 +76,7 @@
       <Optimization>Disabled</Optimization>
       <SDLCheck>true</SDLCheck>
       <ConformanceMode>true</ConformanceMode>
-      <PreprocessorDefinitions>_DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>_DEBUG;_LIB;_WINDOWS;_WINDOWS64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
@@ -121,7 +121,7 @@
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <SDLCheck>true</SDLCheck>
       <ConformanceMode>true</ConformanceMode>
-      <PreprocessorDefinitions>NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>NDEBUG;_LIB;_WINDOWS;_WINDOWS64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
diff --git a/picotlsvs/picotls-fusion/picotls-fusion.vcxproj b/picotlsvs/picotls-fusion/picotls-fusion.vcxproj
new file mode 100644
index 0000000..06213f0
--- /dev/null
+++ b/picotlsvs/picotls-fusion/picotls-fusion.vcxproj
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>16.0</VCProjectVersion>
+    <ProjectGuid>{55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}</ProjectGuid>
+    <RootNamespace>picotlsfusion</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>_DEBUG;_LIB;_WINDOWS;_WINDOWS64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>NDEBUG;_LIB;_WINDOWS;_WINDOWS64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\lib\fusion.c" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/picotlsvs/picotls-fusion/picotls-fusion.vcxproj.filters b/picotlsvs/picotls-fusion/picotls-fusion.vcxproj.filters
new file mode 100644
index 0000000..66181a5
--- /dev/null
+++ b/picotlsvs/picotls-fusion/picotls-fusion.vcxproj.filters
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\lib\fusion.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/picotlsvs/picotls-fusion/picotls-fusion.vcxproj.user b/picotlsvs/picotls-fusion/picotls-fusion.vcxproj.user
new file mode 100644
index 0000000..88a5509
--- /dev/null
+++ b/picotlsvs/picotls-fusion/picotls-fusion.vcxproj.user
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup />
+</Project>
\ No newline at end of file
diff --git a/picotlsvs/picotlsvs.sln b/picotlsvs/picotlsvs.sln
index ba57589..2b6a843 100644
--- a/picotlsvs/picotlsvs.sln
+++ b/picotlsvs/picotlsvs.sln
@@ -54,6 +54,16 @@
 		{497433FE-B252-4985-A504-54EB791F57F4} = {497433FE-B252-4985-A504-54EB791F57F4}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "picotls-fusion", "picotls-fusion\picotls-fusion.vcxproj", "{55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fusiontest", "fusiontest\fusiontest.vcxproj", "{513DC1C9-2CD4-437C-B8EC-168924EB5AAC}"
+	ProjectSection(ProjectDependencies) = postProject
+		{559AC085-1BEF-450A-A62D-0D370561D596} = {559AC085-1BEF-450A-A62D-0D370561D596}
+		{499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} = {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}
+		{55F22DE6-EAAE-4279-97B7-84FAAB7F29BB} = {55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}
+		{497433FE-B252-4985-A504-54EB791F57F4} = {497433FE-B252-4985-A504-54EB791F57F4}
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|x64 = Debug|x64
@@ -142,6 +152,18 @@
 		{7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Release|x64.Build.0 = Release|x64
 		{7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Release|x86.ActiveCfg = Release|Win32
 		{7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Release|x86.Build.0 = Release|Win32
+		{55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}.Debug|x64.ActiveCfg = Debug|x64
+		{55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}.Debug|x64.Build.0 = Debug|x64
+		{55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}.Debug|x86.ActiveCfg = Debug|Win32
+		{55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}.Release|x64.ActiveCfg = Release|x64
+		{55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}.Release|x64.Build.0 = Release|x64
+		{55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}.Release|x86.ActiveCfg = Release|Win32
+		{513DC1C9-2CD4-437C-B8EC-168924EB5AAC}.Debug|x64.ActiveCfg = Debug|x64
+		{513DC1C9-2CD4-437C-B8EC-168924EB5AAC}.Debug|x64.Build.0 = Debug|x64
+		{513DC1C9-2CD4-437C-B8EC-168924EB5AAC}.Debug|x86.ActiveCfg = Debug|Win32
+		{513DC1C9-2CD4-437C-B8EC-168924EB5AAC}.Release|x64.ActiveCfg = Release|x64
+		{513DC1C9-2CD4-437C-B8EC-168924EB5AAC}.Release|x64.Build.0 = Release|x64
+		{513DC1C9-2CD4-437C-B8EC-168924EB5AAC}.Release|x86.ActiveCfg = Release|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/t/fusion.c b/t/fusion.c
index c51e88e..a7a9685 100644
--- a/t/fusion.c
+++ b/t/fusion.c
@@ -50,7 +50,7 @@
 
 static void test_loadn(void)
 {
-    uint8_t buf[8192] = {};
+    uint8_t buf[8192] = { 0 };
 
     for (size_t off = 0; off < 8192 - 15; ++off) {
         uint8_t *src = buf + off;
@@ -65,7 +65,7 @@
     ok(!!"success");
 }
 
-static const uint8_t zero[16384] = {};
+static const uint8_t zero[16384] = { 0 };
 
 static void test_ecb(void)
 {
@@ -208,11 +208,14 @@
         ptls_cipher_encrypt(rand, &aadlen, zero, sizeof(aadlen));
         ptls_cipher_encrypt(rand, &textlen, zero, sizeof(textlen));
         ptls_cipher_encrypt(rand, &seq, zero, sizeof(seq));
-        uint8_t aad[aadlen], text[textlen];
+
+        uint8_t aad[256], text[256];
+
         ptls_cipher_encrypt(rand, aad, zero, sizeof(aad));
         ptls_cipher_encrypt(rand, text, zero, sizeof(text));
 
-        uint8_t encrypted[textlen + 16], decrypted[textlen];
+        uint8_t encrypted[272], decrypted[256];
+
         memset(encrypted, 0x55, sizeof(encrypted));
         memset(decrypted, 0xcc, sizeof(decrypted));