Develop a cryptographic provider library for picotls based on the Windows specifc bcrypt library.
diff --git a/include/picotls/ptlsbcrypt.h b/include/picotls/ptlsbcrypt.h
index 4685b76..890953a 100644
--- a/include/picotls/ptlsbcrypt.h
+++ b/include/picotls/ptlsbcrypt.h
@@ -19,8 +19,8 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  * IN THE SOFTWARE.
  */
-#ifndef picotls_openssl_h
-#define picotls_openssl_h
+#ifndef picotls_bcrypt_h
+#define picotls_bcrypt_h
 
 #ifdef __cplusplus
 extern "C" {
@@ -28,6 +28,9 @@
 
 #include "../picotls.h"
 
+#ifdef _WINDOWS
+#include <bcrypt.h>
+
 extern ptls_cipher_algorithm_t ptls_bcrypt_aes128ecb;
 extern ptls_cipher_algorithm_t ptls_bcrypt_aes256ecb;
 extern ptls_cipher_algorithm_t ptls_bcrypt_aes128ctr;
@@ -41,6 +44,7 @@
 
 extern ptls_cipher_suite_t ptls_bcrypt_aes128gcmsha256;
 extern ptls_cipher_suite_t ptls_bcrypt_aes256gcmsha384;
+#endif
 
 #if 0
 
@@ -84,4 +88,4 @@
 }
 #endif
 
-#endif
+#endif /* picotls_bcrypt_h */
diff --git a/lib/ptlsbcrypt.c b/lib/ptlsbcrypt.c
index 57cfd41..8b32da7 100644
--- a/lib/ptlsbcrypt.c
+++ b/lib/ptlsbcrypt.c
@@ -45,7 +45,7 @@
  * required common variables, etc. */
 int ptls_bcrypt_init()
 {
-    return -1;
+    return 0;
 }
 
 /**
@@ -63,30 +63,8 @@
     /* TODO: Crypto gen random */
 }
 
-/**
- * a symmetric cipher
- *
-typedef const struct st_ptls_cipher_algorithm_t {
-    const char *name;
-    size_t key_size;
-    size_t block_size;
-    size_t iv_size;
-    size_t context_size;
-    int (*setup_crypto)(ptls_cipher_context_t *ctx, int is_enc, const void *key);
-} ptls_cipher_algorithm_t;
-*/
-
-/**
- * context of a symmetric cipher
- * the "algo" field must not be altered by crypto bindings.
-
-typedef struct st_ptls_cipher_context_t {
-    const struct st_ptls_cipher_algorithm_t *algo;
-    void (*do_dispose)(struct st_ptls_cipher_context_t *ctx);
-    void (*do_init)(struct st_ptls_cipher_context_t *ctx, const void *iv);
-    void (*do_transform)(struct st_ptls_cipher_context_t *ctx, void *output, const void *input, size_t len);
-} ptls_cipher_context_t;
-
+/*
+ * Support for symmetric ciphers
 */
 
 struct ptls_bcrypt_symmetric_param_t {
@@ -103,11 +81,15 @@
     struct ptls_bcrypt_symmetric_param_t bctx;
 };
 
-static void ptls_bcrypt_cipher_init(ptls_cipher_context_t *_ctx, const void *iv)
+static void ptls_bcrypt_cipher_init_ctr(ptls_cipher_context_t *_ctx, const void *iv)
 {
     struct ptls_bcrypt_symmetric_context_t *ctx = (struct ptls_bcrypt_symmetric_context_t *)_ctx;
     /* Copy the IV to inside structure */
-    memcpy(ctx->bctx.iv, iv, ctx->super.algo->iv_size);
+    if (iv != NULL) {
+        memcpy(ctx->bctx.iv, iv, ctx->super.algo->block_size);
+    } else {
+        memset(ctx->bctx.iv, 0, ctx->super.algo->block_size);
+    }
 }
 
 static void ptls_bcrypt_cipher_dispose(ptls_cipher_context_t *_ctx)
@@ -141,6 +123,10 @@
     }
 
     assert(BCRYPT_SUCCESS(ret));
+
+    if (!BCRYPT_SUCCESS(ret)) {
+        memset(output, 0, cbResult);
+    }
 }
 
 static void ptls_bcrypt_cipher_transform_ctr(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len)
@@ -148,35 +134,36 @@
     struct ptls_bcrypt_symmetric_context_t *ctx = (struct ptls_bcrypt_symmetric_context_t *)_ctx;
     ULONG cbResult;
     NTSTATUS ret;
-    uint8_t iv[PTLS_MAX_IV_SIZE];
     uint8_t eiv[PTLS_MAX_IV_SIZE];
-    size_t iv_size = _ctx->algo->iv_size;
-    size_t i;
+    int i;
     uint64_t seq = 0;
     size_t processed = 0;
     uint8_t const *v_in = input;
     uint8_t *v_out = output;
 
+    assert(ctx->super.algo->block_size > 0);
     assert(ctx->super.algo->block_size <= PTLS_MAX_IV_SIZE);
 
     while (processed < len) {
-        /* Build the next iv block */
-        const uint8_t *s = ctx->bctx.iv;
-        uint8_t *d = iv;
-        for (i = iv_size - 8; i != 0; --i)
-            *d++ = *s++;
-        i = 64;
-        do {
-            i -= 8;
-            *d++ = *s++ ^ (uint8_t)(seq >> i);
-        } while (i != 0);
 
-        ret = BCryptEncrypt(ctx->bctx.hKey, (PUCHAR)iv, (ULONG)ctx->super.algo->block_size, NULL, NULL, 0, eiv,
+        ret = BCryptEncrypt(ctx->bctx.hKey, (PUCHAR)ctx->bctx.iv, (ULONG)ctx->super.algo->block_size, NULL, NULL, 0, eiv,
                             (ULONG)(ULONG)ctx->super.algo->block_size, &cbResult, 0);
         assert(BCRYPT_SUCCESS(ret));
 
-        for (i = 0; processed < len && i < ctx->super.algo->block_size; i++, processed++) {
-            v_out[processed] = v_in[processed] ^ eiv[i];
+        if (BCRYPT_SUCCESS(ret)) {
+            for (i = 0; processed < len && i < ctx->super.algo->block_size; i++, processed++) {
+                v_out[processed] = v_in[processed] ^ eiv[i];
+            }
+
+            /* Increment the iv block */
+            i = (int)ctx->super.algo->block_size - 1;
+            while (i >= 0) {
+                ctx->bctx.iv[i] += 1;
+                if (ctx->bctx.iv[i] > 0) {
+                    break;
+                }
+                i--;
+            }
         }
     }
 }
@@ -224,10 +211,11 @@
     if (BCRYPT_SUCCESS(ret)) {
 
         ctx->super.do_dispose = ptls_bcrypt_cipher_dispose;
-        ctx->super.do_init = ptls_bcrypt_cipher_init;
         if (is_ctr) {
+            ctx->super.do_init = ptls_bcrypt_cipher_init_ctr;
             ctx->super.do_transform = ptls_bcrypt_cipher_transform_ctr;
         } else {
+            ctx->super.do_init = NULL; 
             ctx->super.do_transform = ptls_bcrypt_cipher_transform_ecb;
         }
         ctx->bctx.is_enc = is_enc;
@@ -248,13 +236,68 @@
     return ptls_bcrypt_cipher_setup_crypto(_ctx, is_enc, key, BCRYPT_AES_ALGORITHM, 1);
 }
 
+
+/* Picotls assumes that AEAD encryption works as:
+ * - an "init" call that prepares the encryption context.
+ * - a series of "update" calls that encrypt segments of the message
+ * - a "final" call that completes the encryption.
+ *
+ * In Bcrypt, the update calls will be implemented as a series of calls
+ * to BCryptEncrypt. The state necessary to pass these calls is provided
+ * to the Bcrypt function in two parameters:
+ *  - the "padding info" points to a BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO
+ *    structure
+ *  - the "IV" parameter points to a buffer holding intermediate updates
+ *    of the IV. That buffer shall be initialize to zero before the 
+ *    first call.
+ * The documentation of the AEAD mode on MSDN is slightly obscure, and
+ * also slightly wrong. After trial and errors and web searches, we find
+ * that:
+ *  - the Nonce parameter (pbNonce, cbNonce) points to the initial
+ *    vector for the encryption, as passed by Picotls. Picotls combines
+ *    per session IV and sequence number in that nonce prior to the call.
+ *  - The Authdata parameter (pbAuthData, cbAuthData) points to the
+ *    authenticated data passed to the API as aad, aadlen.
+ *  - The cbAAd parameter contains the length of auth data that needs
+ *    to be processed. It is initialized before the first call.
+ *  - The tag parameter (pbTag, cbTag) points to a buffer that
+ *    holds intermediate tag values during chaining. The size must be
+ *    the size of the tag for the algorithm. It must be
+ *    initialized to zero before first call.
+ *  - The Mac Context parameter (pbMacContext, cbMacContext) contains
+ *    a working buffer for the computation of the tag. The size
+ *    must be the maxLength parameter returned retrieved in the 
+ *    BCRYPT_AUTH_TAG_LENGTH property of the algorithm. It must be
+ *    initialized to zero before first call.
+ *  - The dwflag parameters must be set to 
+ *    BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG on first call. (The
+ *    MSDN documentation says BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG,
+ *    but that's an error.)
+ *
+ * The members of the BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO struct
+ * should not be modified between calls, except for:
+ *  - the BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG should be cleared
+ *    before the final call.
+ *
+ * The Picotls API does not constrain the length of the segments
+ * passed in the "update" calls, but BCryptEncrypt will fail with
+ * error STATUS_INVALID_BUFFER_SIZE if the length passed in the
+ * chained calls is not an integer multiple of block size. This forces
+ * us to maintain an intermediate buffer of "extra bytes".
+ *    
+ */
+
 struct ptls_bcrypt_aead_param_t {
     HANDLE hKey;
     ULONG cbKeyObject;
+    ULONG maxTagLength;
+    ULONG nbExtraBytes;
     uint8_t *key_object;
+    uint8_t extraBytes[PTLS_MAX_DIGEST_SIZE];
     uint8_t iv[PTLS_MAX_IV_SIZE];
+    uint8_t ivbuf[PTLS_MAX_IV_SIZE];
     uint8_t tag[PTLS_MAX_DIGEST_SIZE];
-    uint64_t nonce;
+    uint8_t auth_tag[PTLS_MAX_DIGEST_SIZE];
     BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO aead_params;
 };
 
@@ -286,46 +329,101 @@
     memcpy(ctx->bctx.iv, iv, ctx->super.algo->iv_size);
     /* Auth tag to NULL */
     memset(ctx->bctx.tag, 0, sizeof(ctx->super.algo->tag_size));
-
-    /* pPaddingInfo must point to BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO structure. */
     BCRYPT_INIT_AUTH_MODE_INFO(ctx->bctx.aead_params);
-    /* TODO: find clarity on handling of nonce */
-    ctx->bctx.nonce = 0;
-    ctx->bctx.aead_params.pbNonce = (PUCHAR)&ctx->bctx.nonce;
-    ctx->bctx.aead_params.cbNonce = (ULONG)sizeof(ctx->bctx.nonce);
+
+    assert(ctx->super.algo->iv_size <= sizeof(ctx->bctx.ivbuf));
+    assert(ctx->super.algo->tag_size <= sizeof(ctx->bctx.tag));
+    assert(ctx->bctx.maxTagLength <= sizeof(ctx->bctx.auth_tag));
+
+    memset(ctx->bctx.ivbuf, 0, ctx->super.algo->iv_size);
+    memset(ctx->bctx.tag, 0, ctx->super.algo->tag_size);
+    memset(ctx->bctx.auth_tag, 0, sizeof(ctx->bctx.auth_tag));
+
+    ctx->bctx.nbExtraBytes = 0;
+
+    ctx->bctx.aead_params.pbNonce = (PUCHAR)&ctx->bctx.iv;
+    ctx->bctx.aead_params.cbNonce = (ULONG)ctx->super.algo->iv_size;
     ctx->bctx.aead_params.pbAuthData = (PUCHAR)aad;
     ctx->bctx.aead_params.cbAuthData = (ULONG)aadlen;
     ctx->bctx.aead_params.pbTag = (PUCHAR)ctx->bctx.tag;
-    ctx->bctx.aead_params.cbTag = (ULONG)ctx->super.algo->tag_size;
+    ctx->bctx.aead_params.cbTag = (ULONG) ctx->super.algo->tag_size;
+    // ctx->bctx.aead_params.cbAAD = (ULONG)aadlen;
+    ctx->bctx.aead_params.pbMacContext = (PUCHAR) ctx->bctx.auth_tag;
+    ctx->bctx.aead_params.cbMacContext = (ULONG)ctx->bctx.maxTagLength;
+    ctx->bctx.aead_params.dwFlags = BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG;
 }
 
 static size_t ptls_bcrypt_aead_do_encrypt_update(struct st_ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen)
 {
     struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx;
-    size_t outlenMax = inlen;
-    ULONG cbResult = 0;
+    size_t outlenMax = inlen + ctx->super.algo->tag_size + ctx->bctx.nbExtraBytes;
+    ULONG cbResult1 = 0;
+    ULONG cbResult2 = 0;
     NTSTATUS ret;
 
-    /* Call the decryption */
-    ret = BCryptEncrypt(ctx->bctx.hKey, (PUCHAR)input, (ULONG)inlen, (void *)&ctx->bctx.aead_params, ctx->bctx.iv,
-                        (ULONG)ctx->super.algo->iv_size, output, (ULONG)outlenMax, &cbResult,
-                        BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG | BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG);
-    assert(BCRYPT_SUCCESS(ret));
-    return inlen;
+    /* If there are extra bytes, complement and encrypt */
+    if (ctx->bctx.nbExtraBytes > 0) {
+        ULONG requiredBytes = (ULONG)(ctx->super.algo->ecb_cipher->block_size - ctx->bctx.nbExtraBytes);
+
+        if (inlen < requiredBytes) {
+            memcpy(&ctx->bctx.extraBytes[ctx->bctx.nbExtraBytes], input, inlen);
+            ctx->bctx.nbExtraBytes += (ULONG) inlen;
+            inlen = 0;
+        } else {
+            memcpy(&ctx->bctx.extraBytes[ctx->bctx.nbExtraBytes], input, requiredBytes);
+            inlen -= requiredBytes;
+            input = (void*)(((uint8_t *)input) + requiredBytes);
+            ret = BCryptEncrypt(ctx->bctx.hKey, (PUCHAR)ctx->bctx.extraBytes, (ULONG)ctx->super.algo->ecb_cipher->block_size,
+                                (void *)&ctx->bctx.aead_params, ctx->bctx.ivbuf, (ULONG)ctx->super.algo->iv_size, output, (ULONG)outlenMax, &cbResult1, 0);
+
+            assert(BCRYPT_SUCCESS(ret));
+            if (!BCRYPT_SUCCESS(ret)) {
+                memset(output, 0, cbResult1);
+            }
+            outlenMax -= cbResult1;
+            output = (void *)(((uint8_t *)output) + cbResult1);
+        }
+    }
+
+    /* If there are trailing bytes, store them in the extra bytes */
+    ctx->bctx.nbExtraBytes = (ULONG)(inlen % ctx->super.algo->ecb_cipher->block_size);
+    if (ctx->bctx.nbExtraBytes > 0) {
+        inlen -= ctx->bctx.nbExtraBytes;
+        memcpy(&ctx->bctx.extraBytes, (void *)(((uint8_t *)input) + inlen), ctx->bctx.nbExtraBytes);
+    }
+
+    if (inlen > 0) {
+        ret = BCryptEncrypt(ctx->bctx.hKey, (PUCHAR)input, (ULONG)inlen, (void *)&ctx->bctx.aead_params, ctx->bctx.ivbuf,
+                            (ULONG)ctx->super.algo->iv_size, output, (ULONG)outlenMax, &cbResult2, 0);
+        assert(BCRYPT_SUCCESS(ret));
+
+        if (!BCRYPT_SUCCESS(ret)) {
+            memset(output, 0, cbResult2);
+        }
+    }
+    return (size_t)cbResult1 + cbResult2;
 }
 
 static size_t ptls_bcrypt_aead_do_encrypt_final(struct st_ptls_aead_context_t *_ctx, void *output)
 {
     struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx;
-    size_t oulenMax = ctx->super.algo->tag_size;
+    size_t outlenMax = ctx->super.algo->tag_size + ctx->bctx.nbExtraBytes;
     ULONG cbResult = 0;
     NTSTATUS ret;
 
-    /* Call the decryption */
-    ret = BCryptDecrypt(ctx->bctx.hKey, (PUCHAR)NULL, (ULONG)0, (void *)&ctx->bctx.aead_params, ctx->bctx.iv,
-                        (ULONG)ctx->super.algo->iv_size, output, (ULONG)oulenMax, &cbResult, BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG);
+    ctx->bctx.aead_params.dwFlags &= ~BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG;
+
+    ret = BCryptEncrypt(ctx->bctx.hKey, (PUCHAR)ctx->bctx.extraBytes, (ULONG)ctx->bctx.nbExtraBytes, (void *)&ctx->bctx.aead_params, ctx->bctx.ivbuf,
+                        (ULONG)ctx->super.algo->iv_size, output, (ULONG)outlenMax, &cbResult, 0);
     assert(BCRYPT_SUCCESS(ret));
-    return ctx->super.algo->tag_size;
+
+    if (BCRYPT_SUCCESS(ret)) {
+        /* Find the tag in the aead parameters and append it to the output */
+        assert(cbResult + ctx->bctx.aead_params.cbTag <= outlenMax);
+        memcpy(((uint8_t *)output) + cbResult, ctx->bctx.aead_params.pbTag, ctx->bctx.aead_params.cbTag);
+        cbResult += ctx->bctx.aead_params.cbTag;
+    }
+    return cbResult;
 }
 
 static size_t ptls_bcrypt_aead_do_decrypt(struct st_ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen,
@@ -333,7 +431,7 @@
 {
     struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx;
     ULONG cbResult;
-    size_t out_len_max = inlen - ctx->super.algo->tag_size;
+    size_t textLen = inlen - ctx->super.algo->tag_size;
     NTSTATUS ret;
 
     /* Save a copy of the IV*/
@@ -341,20 +439,18 @@
 
     /* TODO: pPaddingInfo must point to BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO structure. */
     BCRYPT_INIT_AUTH_MODE_INFO(ctx->bctx.aead_params);
-    /* TODO: find clarity on handling of nonce */
-    ctx->bctx.nonce = 0;
-    ctx->bctx.aead_params.pbNonce = (PUCHAR)&ctx->bctx.nonce;
-    ctx->bctx.aead_params.cbNonce = (ULONG)sizeof(ctx->bctx.nonce);
+    /* TODO: find clarity on handling of ivbuf */
+    memset(ctx->bctx.tag, 0, sizeof(ctx->super.algo->tag_size));
+    ctx->bctx.aead_params.pbNonce = (PUCHAR)&ctx->bctx.iv;
+    ctx->bctx.aead_params.cbNonce = (ULONG)ctx->super.algo->iv_size;
     ctx->bctx.aead_params.pbAuthData = (PUCHAR)aad;
     ctx->bctx.aead_params.cbAuthData = (ULONG)aadlen;
-    memset(ctx->bctx.tag, 0, sizeof(ctx->bctx.tag));
-    /* TODO: check whether there is a need to set the precise tag size */
-    ctx->bctx.aead_params.pbTag = (PUCHAR)ctx->bctx.tag;
-    ctx->bctx.aead_params.cbTag = (ULONG)sizeof(ctx->bctx.tag);
+    ctx->bctx.aead_params.pbTag = (PUCHAR)(((uint8_t *)input) + textLen);
+    ctx->bctx.aead_params.cbTag = (ULONG)(ULONG)ctx->super.algo->tag_size;
 
     /* Call the decryption */
-    ret = BCryptDecrypt(ctx->bctx.hKey, (PUCHAR)input, (ULONG)inlen, (void *)&ctx->bctx.aead_params, ctx->bctx.iv,
-                        (ULONG)ctx->super.algo->iv_size, (PUCHAR)output, (ULONG)out_len_max, &cbResult, 0);
+    ret = BCryptDecrypt(ctx->bctx.hKey, (PUCHAR)input, (ULONG)textLen, (void *)&ctx->bctx.aead_params,
+                        NULL, 0, (PUCHAR)output, (ULONG)textLen, &cbResult, 0);
 
     if (BCRYPT_SUCCESS(ret)) {
         return (size_t)cbResult;
@@ -375,6 +471,10 @@
     ret = BCryptOpenAlgorithmProvider(&hAlgorithm, bcrypt_name, NULL, 0);
 
     if (BCRYPT_SUCCESS(ret)) {
+        ret = BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE, (PBYTE)bcrypt_mode, (ULONG)bcrypt_mode_size, 0);
+    }
+
+    if (BCRYPT_SUCCESS(ret)) {
         DWORD ko_size = 0;
         ULONG cbResult = 0;
 
@@ -391,7 +491,13 @@
     }
 
     if (BCRYPT_SUCCESS(ret)) {
-        ret = BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE, (PBYTE)bcrypt_mode, bcrypt_mode_size, 0);
+        BCRYPT_KEY_LENGTHS_STRUCT atl_st;
+        ULONG cbResult = 0;
+
+        ret = BCryptGetProperty(hAlgorithm, BCRYPT_AUTH_TAG_LENGTH, (PUCHAR)&atl_st, (ULONG)sizeof(atl_st), &cbResult, 0);
+        if (BCRYPT_SUCCESS(ret)) {
+            ctx->bctx.maxTagLength = atl_st.dwMaxLength;
+        }
     }
 
     if (BCRYPT_SUCCESS(ret)) {
@@ -399,11 +505,6 @@
                                          (ULONG)ctx->super.algo->key_size, 0);
     }
 
-    if (BCRYPT_SUCCESS(ret)) {
-        ret = BCryptSetProperty(ctx->bctx.hKey, BCRYPT_CHAINING_MODE, (PUCHAR)bcrypt_mode,
-                                (ULONG)(sizeof(wchar_t) * wcslen(bcrypt_mode)), 0);
-    }
-
     if (hAlgorithm != NULL) {
         BCryptCloseAlgorithmProvider(hAlgorithm, 0);
     }
@@ -411,16 +512,16 @@
     if (BCRYPT_SUCCESS(ret)) {
         if (is_enc) {
             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;
-        } else {
-            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;
+        } 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;
         }
         return 0;
     } else {
@@ -436,54 +537,14 @@
 }
 
 /* Hash algorithms */
-#if 0
-/**
- * A hash context.
- */
-typedef struct st_ptls_hash_context_t {
-    /**
-     * feeds additional data into the hash context
-     */
-    void (*update)(struct st_ptls_hash_context_t *ctx, const void *src, size_t len);
-    /**
-     * returns the digest and performs necessary operation specified by mode
-     */
-    void (*final)(struct st_ptls_hash_context_t *ctx, void *md, ptls_hash_final_mode_t mode);
-    /**
-     * creates a copy of the hash context
-     */
-    struct st_ptls_hash_context_t *(*clone_)(struct st_ptls_hash_context_t *src);
-} ptls_hash_context_t;
 
-/**
- * A hash algorithm and its properties.
- */
-typedef const struct st_ptls_hash_algorithm_t {
-    /**
-     * block size
-     */
-    size_t block_size;
-    /**
-     * digest size
-     */
-    size_t digest_size;
-    /**
-     * constructor that creates the hash context
-     */
-    ptls_hash_context_t *(*create)(void);
-    /**
-     * digest of zero-length octets
-     */
-    uint8_t empty_digest[PTLS_MAX_DIGEST_SIZE];
-} ptls_hash_algorithm_t;
-#endif
-
-typedef struct st_ptls_bcrypt_hash_param_t {
+struct st_ptls_bcrypt_hash_param_t {
     wchar_t const *bcrypt_name;
     BCRYPT_HASH_HANDLE hHash;
     PUCHAR pbHashObject;
     ULONG cbHashObject;
     ULONG hash_size;
+    int has_error;
 };
 
 struct st_ptls_bcrypt_hash_context_t {
@@ -496,6 +557,10 @@
     struct st_ptls_bcrypt_hash_context_t *ctx = (struct st_ptls_bcrypt_hash_context_t *)_ctx;
     NTSTATUS ret = BCryptHashData(ctx->ctx.hHash, (PUCHAR)src, (ULONG)len, 0);
     assert(BCRYPT_SUCCESS(ret));
+
+    if (!BCRYPT_SUCCESS(ret)) {
+        ctx->ctx.has_error = 1;
+    }
 }
 
 static struct st_ptls_bcrypt_hash_context_t *ptls_bcrypt_hash_context_free(struct st_ptls_bcrypt_hash_context_t *ctx)
@@ -529,6 +594,9 @@
         if (md != NULL) {
             ret = BCryptFinishHash(ctx->ctx.hHash, md, ctx->ctx.hash_size, 0);
             assert(BCRYPT_SUCCESS(ret));
+            if (!BCRYPT_SUCCESS(ret) || ctx->ctx.has_error) {
+                memset(md, 0, ctx->ctx.hash_size);
+            }
         }
 
         ret = BCryptDestroyHash(ctx->ctx.hHash);
@@ -547,6 +615,9 @@
                 BCryptCloseAlgorithmProvider(hAlgorithm, 0);
             }
             assert(BCRYPT_SUCCESS(ret));
+            if (!BCRYPT_SUCCESS(ret)) {
+                ctx->ctx.hHash = NULL;   
+            }
             break;
         }
         default:
@@ -570,11 +641,12 @@
         clone_ctx->ctx.cbHashObject = ctx->ctx.cbHashObject;
         clone_ctx->ctx.bcrypt_name = ctx->ctx.bcrypt_name;
         clone_ctx->ctx.hash_size = ctx->ctx.hash_size;
+        clone_ctx->ctx.has_error = ctx->ctx.has_error;
 
         if (clone_ctx->ctx.pbHashObject == NULL) {
             ret = STATUS_NO_MEMORY;
         } else {
-            ctx->ctx.hHash = NULL;
+            clone_ctx->ctx.hHash = NULL;
             ptls_clear_memory(&clone_ctx->ctx.pbHashObject, clone_ctx->ctx.cbHashObject);
             ret = BCryptDuplicateHash(ctx->ctx.hHash, &clone_ctx->ctx.hHash, clone_ctx->ctx.pbHashObject,
                                       clone_ctx->ctx.cbHashObject, 0);
@@ -606,8 +678,7 @@
             DWORD hb_length = 0;
             ULONG cbResult = 0;
 
-            ret =
-                BCryptGetProperty(hAlgorithm, BCRYPT_HASH_BLOCK_LENGTH, (PUCHAR)&hb_length, (ULONG)sizeof(hb_length), &cbResult, 0);
+            ret = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&hb_length, (ULONG)sizeof(hb_length), &cbResult, 0);
 
             if (BCRYPT_SUCCESS(ret)) {
                 ctx->ctx.pbHashObject = (uint8_t *)malloc(hb_length);
@@ -635,12 +706,12 @@
     return (ptls_hash_context_t *)ctx;
 }
 
-static ptls_hash_context_t *ptls_bcrypt_sha256_create()
+static ptls_hash_context_t *ptls_bcrypt_sha256_create(void)
 {
     return ptls_bcrypt_hash_create(BCRYPT_SHA256_ALGORITHM, PTLS_SHA256_DIGEST_SIZE);
 }
 
-static ptls_hash_context_t *ptls_bcrypt_sha384_create()
+static ptls_hash_context_t *ptls_bcrypt_sha384_create(void)
 {
     return ptls_bcrypt_hash_create(BCRYPT_SHA384_ALGORITHM, PTLS_SHA384_DIGEST_SIZE);
 }
@@ -703,189 +774,8 @@
 ptls_cipher_suite_t ptls_bcrypt_aes256gcmsha384 = {PTLS_CIPHER_SUITE_AES_256_GCM_SHA384, &ptls_bcrypt_aes256gcm,
                                                    &ptls_bcrypt_sha384};
 
-#ifdef PTLS_BCRYPT_TODO
-int ptls_bcrypt_init_verify_certificate(ptls_bcrypt_verify_certificate_t *self, X509_STORE *store)
-{
-    /* TODO: Replace with bcrypt library */
-    *self = (ptls_bcrypt_verify_certificate_t){{verify_cert}};
-
-    if (store != NULL) {
-        X509_STORE_up_ref(store);
-        self->cert_store = store;
-    } else {
-        /* use default store */
-        if ((self->cert_store = ptls_bcrypt_create_default_certificate_store()) == NULL)
-            return -1;
-    }
-
-    return 0;
-}
-
-void ptls_bcrypt_dispose_verify_certificate(ptls_bcrypt_verify_certificate_t *self)
-{
-    X509_STORE_free(self->cert_store);
-    free(self);
-}
-
-X509_STORE *ptls_bcrypt_create_default_certificate_store(void)
-{
-    X509_STORE *store;
-    X509_LOOKUP *lookup;
-
-    if ((store = X509_STORE_new()) == NULL)
-        goto Error;
-    if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL)
-        goto Error;
-    X509_LOOKUP_load_file(lookup, NULL, X509_FILETYPE_DEFAULT);
-    if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir())) == NULL)
-        goto Error;
-    X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT);
-
-    return store;
-Error:
-    if (store != NULL)
-        X509_STORE_free(store);
-    return NULL;
-}
-
-#define TICKET_LABEL_SIZE 16
-#define TICKET_IV_SIZE EVP_MAX_IV_LENGTH
-
-int ptls_bcrypt_encrypt_ticket(ptls_buffer_t *buf, ptls_iovec_t src,
-                               int (*cb)(unsigned char *key_name, unsigned char *iv, EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc))
-{
-    /* TODO: rewrite with bcrypt functions */
-    EVP_CIPHER_CTX *cctx = NULL;
-    HMAC_CTX *hctx = NULL;
-    uint8_t *dst;
-    int clen, ret;
-
-    if ((cctx = EVP_CIPHER_CTX_new()) == NULL) {
-        ret = PTLS_ERROR_NO_MEMORY;
-        goto Exit;
-    }
-    if ((hctx = HMAC_CTX_new()) == NULL) {
-        ret = PTLS_ERROR_NO_MEMORY;
-        goto Exit;
-    }
-
-    if ((ret = ptls_buffer_reserve(buf, TICKET_LABEL_SIZE + TICKET_IV_SIZE + src.len + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE)) !=
-        0)
-        goto Exit;
-    dst = buf->base + buf->off;
-
-    /* fill label and iv, as well as obtaining the keys */
-    if (!(*cb)(dst, dst + TICKET_LABEL_SIZE, cctx, hctx, 1)) {
-        ret = PTLS_ERROR_LIBRARY;
-        goto Exit;
-    }
-    dst += TICKET_LABEL_SIZE + TICKET_IV_SIZE;
-
-    /* encrypt */
-    if (!EVP_EncryptUpdate(cctx, dst, &clen, src.base, (int)src.len)) {
-        ret = PTLS_ERROR_LIBRARY;
-        goto Exit;
-    }
-    dst += clen;
-    if (!EVP_EncryptFinal_ex(cctx, dst, &clen)) {
-        ret = PTLS_ERROR_LIBRARY;
-        goto Exit;
-    }
-    dst += clen;
-
-    /* append hmac */
-    if (!HMAC_Update(hctx, buf->base + buf->off, dst - (buf->base + buf->off)) || !HMAC_Final(hctx, dst, NULL)) {
-        ret = PTLS_ERROR_LIBRARY;
-        goto Exit;
-    }
-    dst += HMAC_size(hctx);
-
-    assert(dst <= buf->base + buf->capacity);
-    buf->off += dst - (buf->base + buf->off);
-    ret = 0;
-
-Exit:
-    if (cctx != NULL)
-        cleanup_cipher_ctx(cctx);
-    if (hctx != NULL)
-        HMAC_CTX_free(hctx);
-    return ret;
-}
-
-int ptls_bcrypt_decrypt_ticket(ptls_buffer_t *buf, ptls_iovec_t src,
-                               int (*cb)(unsigned char *key_name, unsigned char *iv, EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc))
-{
-    /* TODO: replace with bcrypt functions */
-    EVP_CIPHER_CTX *cctx = NULL;
-    HMAC_CTX *hctx = NULL;
-    int clen, ret;
-
-    if ((cctx = EVP_CIPHER_CTX_new()) == NULL) {
-        ret = PTLS_ERROR_NO_MEMORY;
-        goto Exit;
-    }
-    if ((hctx = HMAC_CTX_new()) == NULL) {
-        ret = PTLS_ERROR_NO_MEMORY;
-        goto Exit;
-    }
-
-    /* obtain cipher and hash context.
-     * Note: no need to handle renew, since in picotls we always send a new ticket to minimize the chance of ticket reuse */
-    if (src.len < TICKET_LABEL_SIZE + TICKET_IV_SIZE) {
-        ret = PTLS_ALERT_DECODE_ERROR;
-        goto Exit;
-    }
-    if (!(*cb)(src.base, src.base + TICKET_LABEL_SIZE, cctx, hctx, 0)) {
-        ret = PTLS_ERROR_LIBRARY;
-        goto Exit;
-    }
-
-    /* check hmac, and exclude label, iv, hmac */
-    size_t hmac_size = HMAC_size(hctx);
-    if (src.len < TICKET_LABEL_SIZE + TICKET_IV_SIZE + hmac_size) {
-        ret = PTLS_ALERT_DECODE_ERROR;
-        goto Exit;
-    }
-    src.len -= hmac_size;
-    uint8_t hmac[EVP_MAX_MD_SIZE];
-    if (!HMAC_Update(hctx, src.base, src.len) || !HMAC_Final(hctx, hmac, NULL)) {
-        ret = PTLS_ERROR_LIBRARY;
-        goto Exit;
-    }
-    if (!ptls_mem_equal(src.base + src.len, hmac, hmac_size)) {
-        ret = PTLS_ALERT_HANDSHAKE_FAILURE;
-        goto Exit;
-    }
-    src.base += TICKET_LABEL_SIZE + TICKET_IV_SIZE;
-    src.len -= TICKET_LABEL_SIZE + TICKET_IV_SIZE;
-
-    /* decrypt */
-    if ((ret = ptls_buffer_reserve(buf, src.len)) != 0)
-        goto Exit;
-    if (!EVP_DecryptUpdate(cctx, buf->base + buf->off, &clen, src.base, (int)src.len)) {
-        ret = PTLS_ERROR_LIBRARY;
-        goto Exit;
-    }
-    buf->off += clen;
-    if (!EVP_DecryptFinal_ex(cctx, buf->base + buf->off, &clen)) {
-        ret = PTLS_ERROR_LIBRARY;
-        goto Exit;
-    }
-    buf->off += clen;
-
-    ret = 0;
-
-Exit:
-    if (cctx != NULL)
-        cleanup_cipher_ctx(cctx);
-    if (hctx != NULL)
-        HMAC_CTX_free(hctx);
-    return ret;
-}
-#endif
-
 #ifdef PRLS_BCRYPT_TODO
-/* TODO: replace with bcrypt functions */
+/* TODO: develp these bcrypt functions */
 ptls_key_exchange_algorithm_t ptls_bcrypt_secp256r1 = {PTLS_GROUP_SECP256R1, x9_62_create_key_exchange, secp_key_exchange,
                                                        NID_X9_62_prime256v1};
 #if ptls_bcrypt_HAVE_SECP384R1
diff --git a/picotlsvs/bcrypt-test/bcrypt-test.c b/picotlsvs/bcrypt-test/bcrypt-test.c
new file mode 100644
index 0000000..324d425
--- /dev/null
+++ b/picotlsvs/bcrypt-test/bcrypt-test.c
@@ -0,0 +1,614 @@
+// bcrypt-test.cpp : This file contains the 'main' function. Program execution begins and ends there.
+//
+#include <wincompat.h>
+#include <bcrypt.h>
+#include <stdio.h>
+#include "picotls/ptlsbcrypt.h"
+#include "picotls/minicrypto.h"
+
+int KeyInit(BCRYPT_KEY_HANDLE *hKey, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz, const BYTE *proposedKey,
+            DWORD proposedKeyLength, BYTE **ko, ULONG *ko_length)
+{
+    DWORD cbData = 0;
+    HANDLE hAlgo = NULL;
+
+    // Open an algorithm handle.
+    NTSTATUS ret = BCryptOpenAlgorithmProvider(&hAlgo, name, NULL, 0);
+
+    if (BCRYPT_SUCCESS(ret)) {
+        // Set the properties to define the chaining mode
+        ret = BCryptSetProperty(hAlgo, BCRYPT_CHAINING_MODE, (PBYTE)chain_mode, (ULONG)chain_mode_sz, 0);
+    }
+
+    *ko = NULL;
+    *ko_length = 0;
+
+    if (BCRYPT_SUCCESS(ret)) {
+        DWORD ko_size = 0;
+        ULONG cbResult = 0;
+
+        ret = BCryptGetProperty(hAlgo, BCRYPT_OBJECT_LENGTH, (PUCHAR)&ko_size, (ULONG)sizeof(ko_size), &cbResult, 0);
+
+        if (BCRYPT_SUCCESS(ret)) {
+            *ko = (uint8_t *)malloc(ko_size);
+            if (*ko == NULL) {
+                ret = STATUS_NO_MEMORY;
+            } else {
+                *ko_length = ko_size;
+                memset(*ko, 0, *ko_length);
+            }
+        }
+    }
+
+    if (BCRYPT_SUCCESS(ret)) {
+        // Generate the key from supplied input key bytes.
+        ret = BCryptGenerateSymmetricKey(hAlgo, hKey, *ko, *ko_length, (PBYTE)proposedKey, proposedKeyLength, 0);
+    } else {
+        if (*ko != NULL) {
+            free(*ko);
+            *ko = NULL;
+            *ko_length = 0;
+        }
+    }
+
+    if (hAlgo != NULL) {
+        BCryptCloseAlgorithmProvider(hAlgo, 0);
+    }
+
+    return BCRYPT_SUCCESS(ret) ? 0 : -1;
+}
+
+void KeyRelease(BCRYPT_KEY_HANDLE *hKey, BYTE **ko, ULONG *ko_length)
+{
+    BCryptDestroyKey(*hKey);
+    *hKey = NULL;
+    if (*ko) {
+        free(*ko);
+    }
+    *ko = NULL;
+    *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)
+{
+
+    BCRYPT_KEY_HANDLE hKey = NULL;
+    BYTE *authTag = encrypted + dataLength;
+    BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO bacmi;
+    BYTE *ko = NULL;
+    ULONG ko_length = 0;
+    int ret = 0;
+
+    *encryptedLength = 0;
+
+    if (KeyInit(&hKey, name, chain_mode, chain_mode_sz, key, key_length, &ko, &ko_length) != 0) {
+        return -1;
+    }
+
+    memset(authTag, 0, authTagLength);
+    // Set the auth mode info for AEAD
+    BCRYPT_INIT_AUTH_MODE_INFO(bacmi);
+    bacmi.pbNonce = nonceValue;
+    bacmi.cbNonce = nonceLength;
+    bacmi.pbAuthData = authData;
+    bacmi.cbAuthData = authDataLength;
+    bacmi.pbTag = authTag;
+    bacmi.cbTag = authTagLength;
+    /* All other fields are set to NULL by the INIT macro. */
+
+    /* If called with a NULL pointer for the data block, we will merely compute the block size. */
+    DWORD cbCipherText = 0;
+    NTSTATUS status = BCryptEncrypt(hKey, data, dataLength, &bacmi, NULL, 0, encrypted, encryptedLengthMax, &cbCipherText, 0);
+
+    KeyRelease(&hKey, &ko, &ko_length);
+
+    if (BCRYPT_SUCCESS(status)) {
+        *encryptedLength = cbCipherText + authTagLength;
+    } else {
+        ret = -1;
+    }
+
+    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,
+                  ULONG authTagLength, BYTE *decrypted, ULONG decryptedLengthMax, ULONG *decryptedLength)
+{
+
+    BCRYPT_KEY_HANDLE hKey = NULL;
+    BYTE *authTag = encrypted + (encryptedLength - authTagLength);
+    BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO bacmi;
+    BYTE *ko = NULL;
+    ULONG ko_length = 0;
+    int ret = 0;
+
+    *decryptedLength = 0;
+
+    if (KeyInit(&hKey, name, chain_mode, chain_mode_sz, key, key_length, &ko, &ko_length) != 0) {
+        return -1;
+    }
+
+    // Set the auth mode info for AEAD
+    BCRYPT_INIT_AUTH_MODE_INFO(bacmi);
+    bacmi.pbNonce = nonceValue;
+    bacmi.cbNonce = nonceLength;
+    bacmi.pbAuthData = authData;
+    bacmi.cbAuthData = authDataLength;
+    bacmi.pbTag = authTag;
+    bacmi.cbTag = authTagLength;
+    /* All other fields are set to NULL by the INIT macro. */
+
+    /* If called with a NULL pointer for the data block, we will merely compute the block size. */
+    DWORD cbCipherText = 0;
+    NTSTATUS status = BCryptDecrypt(hKey, encrypted, encryptedLength - authTagLength, &bacmi, NULL, 0, decrypted,
+                                    decryptedLengthMax, &cbCipherText, 0);
+
+    KeyRelease(&hKey, &ko, &ko_length);
+
+    if (BCRYPT_SUCCESS(status)) {
+        *decryptedLength = cbCipherText;
+    } else {
+        ret = -1;
+    }
+
+    return ret;
+}
+
+int test_oneshot(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz)
+{
+    BYTE key[32];
+    BYTE data[123];
+    BYTE nonce[12];
+    BYTE authData[9];
+    BYTE encrypted[256];
+    ULONG encryptedLength;
+    BYTE decrypted[256];
+    ULONG decryptedLength;
+    ULONG authTagLength = (ULONG)aead->tag_size;
+    int ret = 0;
+
+    assert(sizeof(key) >= aead->key_size);
+    assert(sizeof(nonce) >= 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(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);
+
+    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);
+
+        printf("Decrypt one shot returns %d, l=%d\n", ret, decryptedLength);
+
+        if (ret == 0) {
+            if (decryptedLength != 123) {
+                printf("Wrong length, not %d\n", 123);
+                ret = -1;
+            } else if (memcmp(data, decrypted, 123) != 0) {
+                printf("Data and decrypted don't match\n");
+                ret = -1;
+            } else {
+                printf("One shot matches.\n");
+            }
+        }
+    }
+
+    return ret;
+}
+
+void delete_test_aead_context(ptls_aead_context_t *ctx)
+{
+    if (ctx != NULL) {
+        ctx->dispose_crypto(ctx);
+        free(ctx);
+    }
+}
+
+ptls_aead_context_t *new_test_aead_context(ptls_aead_algorithm_t *aead, int is_enc, BYTE *key)
+{
+    int ret = 0;
+    ptls_aead_context_t *ctx = (ptls_aead_context_t *)malloc(aead->context_size);
+
+    if (ctx != NULL) {
+        memset(ctx, 0, aead->context_size);
+        *ctx = (ptls_aead_context_t){aead};
+        if (aead->setup_crypto(ctx, is_enc, key) != 0) {
+            printf("For %s, setup returns %d\n", aead->name, ret);
+            delete_test_aead_context(ctx);
+            ctx = NULL;
+        }
+    } else {
+        printf("For %s, memory error during setup\n", aead->name);
+    }
+
+    return (ctx);
+}
+
+int test_decrypt(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz)
+{
+    BYTE key[32];
+    BYTE data[123];
+    BYTE nonce[12];
+    BYTE authData[9];
+    BYTE encrypted[256];
+    ULONG encryptedLength;
+    BYTE decrypted[256];
+    size_t decryptedLength;
+    ULONG authTagLength = (ULONG)aead->tag_size;
+    ptls_aead_context_t *ctx = NULL;
+    int ret = 0;
+
+    assert(sizeof(key) >= aead->key_size);
+    assert(sizeof(nonce) >= 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(authData, 'a', sizeof(authData));
+
+    /* Create a decryption context */
+    ctx = new_test_aead_context(aead, 0, key);
+    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);
+    }
+
+    /* Try decrypt with library procedure */
+    if (ret == 0) {
+        decryptedLength = ctx->do_decrypt(ctx, decrypted, encrypted, encryptedLength, nonce, authData, 9);
+        if (decryptedLength >= encryptedLength) {
+            printf("For %s, decrypt returns %d\n", aead->name, (int)decryptedLength);
+            ret = -1;
+        } else if (decryptedLength != 123) {
+            printf("For %s, decrypt returns %d instead of %d\n", aead->name, (int)decryptedLength, 123);
+            ret = -1;
+        } else if (memcmp(data, decrypted, decryptedLength) != 0) {
+            printf("For %s, decrypted does not match clear text\n", aead->name);
+            ret = -1;
+        } else {
+            printf("For %s, decrypting test passes.\n", aead->name);
+        }
+    }
+
+    delete_test_aead_context(ctx);
+
+    return ret;
+}
+
+int test_encrypt(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz)
+{
+    BYTE key[32];
+    BYTE data[123];
+    BYTE nonce[12];
+    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(nonce) >= aead->iv_size);
+    assert(sizeof(data) + authTagLength <= sizeof(encrypted));
+    assert(sizeof(data) + authTagLength <= sizeof(encryptedRef));
+
+    memset(key, 'k', sizeof(key));
+    memset(data, 'd', sizeof(data));
+    memset(nonce, 'n', sizeof(nonce));
+    memset(authData, 'a', sizeof(authData));
+
+    /* Create an encryption context */
+    ctx = new_test_aead_context(aead, 1, key);
+    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);
+    }
+
+    /* Try encrypt with library procedure */
+    if (ret == 0) {
+        ctx->do_encrypt_init(ctx, nonce, authData, 9);
+        encryptedLength = ctx->do_encrypt_update(ctx, encrypted, data, 123);
+        encryptedLength += ctx->do_encrypt_final(ctx, &encrypted[encryptedLength]);
+
+        if (encryptedLength != encryptedRefLength) {
+            printf("For %s, encrypt returns %d instead of %d\n", aead->name, (int)encryptedLength, encryptedRefLength);
+            ret = -1;
+        } else if (memcmp(encryptedRef, encrypted, encryptedRefLength) != 0) {
+            printf("For %s, encrypted does not match ref\n", aead->name);
+            for (ULONG i = 0; i < encryptedRefLength; i++) {
+                if (encryptedRef[i] != encrypted[i]) {
+                    printf("For %s, 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, 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];
+    BYTE nonce[12];
+    BYTE authData[9];
+    BYTE *data = NULL;
+    BYTE *encrypted = NULL;
+    BYTE *decrypted = NULL;
+    size_t encryptedLength;
+    size_t decryptedLength;
+    ULONG authTagLength = (ULONG)aead->tag_size;
+    ptls_aead_context_t *ctx_e = NULL;
+    ptls_aead_context_t *ctx_d = NULL;
+    ULONG packet_size[] = {1500, 128, 3, 0};
+    ULONG nb_packet_size = (ULONG)(sizeof(packet_size) / sizeof(ULONG));
+    int ret = 0;
+
+    assert(sizeof(key) >= aead->key_size);
+    assert(sizeof(nonce) >= aead->iv_size);
+
+    memset(key, 'k', sizeof(key));
+    memset(nonce, 'n', sizeof(nonce));
+    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);
+
+    if (ctx_e == NULL || ctx_d == NULL) {
+        ret = -1;
+    }
+
+    /* Test a variety of packet sizes */
+    for (ULONG i = 0; ret == 0 && i < nb_packet_size; i++) {
+        ULONG data_size = (packet_size[i] > 0) ? packet_size[i] : 128;
+        ULONG encrypted_size = packet_size[i] + authTagLength;
+
+        data = (BYTE *)malloc(data_size);
+        encrypted = (BYTE *)malloc(encrypted_size);
+        decrypted = (BYTE *)malloc(data_size);
+
+        if (data == NULL || encrypted == NULL || decrypted == NULL) {
+            printf("For %s: cannot allocate memory for packet size[%d] = %d\n", aead->name, i, packet_size[i]);
+        } else {
+            memset(data, 'd', data_size);
+
+            ctx_e->do_encrypt_init(ctx_e, nonce, authData, 9);
+            encryptedLength = ctx_e->do_encrypt_update(ctx_e, encrypted, data, packet_size[i]);
+            encryptedLength += ctx_e->do_encrypt_final(ctx_e, &encrypted[encryptedLength]);
+            decryptedLength = ctx_d->do_decrypt(ctx_d, decrypted, encrypted, encryptedLength, nonce, authData, 9);
+
+            if (decryptedLength >= encryptedLength) {
+                printf("For %s, decrypt returns %d\n", aead->name, (int)decryptedLength);
+                ret = -1;
+            } else if (decryptedLength != packet_size[i]) {
+                printf("For %s, decrypt returns %d instead of %d\n", aead->name, (int)decryptedLength, packet_size[i]);
+                ret = -1;
+            } else if (memcmp(data, decrypted, decryptedLength) != 0) {
+                printf("For %s, decrypted does not match clear text\n", aead->name);
+                ret = -1;
+            } else {
+                printf("For %s, test packet size[%d] = %d passes.\n", aead->name, i, packet_size[i]);
+            }
+        }
+
+        if (data != NULL) {
+            free(data);
+            data = NULL;
+        }
+
+        if (encrypted != NULL) {
+            free(encrypted);
+            encrypted = NULL;
+        }
+
+        if (decrypted != NULL) {
+            free(decrypted);
+            decrypted = NULL;
+        }
+    }
+
+    delete_test_aead_context(ctx_e);
+    delete_test_aead_context(ctx_d);
+
+    return ret;
+}
+
+int test_one_aead(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz)
+{
+    int ret = test_oneshot(aead, name, chain_mode, chain_mode_sz);
+
+    printf("For %s, test one shot returns %d\n", aead->name, ret);
+
+    if (ret == 0) {
+        ret = test_decrypt(aead, name, chain_mode, chain_mode_sz);
+
+        printf("For %s, test decrypt returns %d\n", aead->name, ret);
+    }
+
+    if (ret == 0) {
+        ret = test_encrypt(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);
+    }
+
+    return ret;
+}
+
+/* Test of cipher functions.
+ * The test verifies that a message encode with a bcrypt function can be
+ * decoded with a minicrypto function, and vice versa.
+ */
+
+int test_cipher_one_way(char const *name1, char const *name2, ptls_cipher_algorithm_t *b1, ptls_cipher_algorithm_t *b2,
+                        unsigned int nb_blocks)
+{
+    BYTE key[32];
+    BYTE nonce[16];
+    BYTE data[128];
+    BYTE encrypted[128];
+    BYTE decrypted[128];
+    size_t data_size = b1->block_size * nb_blocks;
+    ptls_cipher_context_t *ctx1 = NULL;
+    ptls_cipher_context_t *ctx2 = NULL;
+    int ret = 0;
+
+    assert(sizeof(key) >= b1->key_size);
+    assert(sizeof(data) >= data_size);
+    assert(sizeof(nonce) >= b1->iv_size);
+
+    memset(key, 'k', sizeof(key));
+    memset(data, 'd', data_size);
+
+    ctx1 = ptls_cipher_new(b1, 1, key);
+    ctx2 = ptls_cipher_new(b2, 0, key);
+
+    if (ctx1 == NULL || ctx2 == NULL) {
+        ret = -1;
+    } else {
+        memset(nonce, 0, sizeof(nonce));
+        if (ctx1->do_init != NULL) {
+            ctx1->do_init(ctx1, nonce);
+        }
+
+        if (ctx2->do_init != NULL) {
+            ctx2->do_init(ctx2, nonce);
+        }
+
+        ctx1->do_transform(ctx1, encrypted, data, data_size);
+        ctx2->do_transform(ctx2, decrypted, encrypted, data_size);
+
+        if (memcmp(data, decrypted, data_size) != 0) {
+            printf("For %s -> %s, decrypted does not match clear text\n", name1, name2);
+            ret = -1;
+        } else {
+            printf("For %s -> %s, test passes.\n", name1, name2);
+        }
+    }
+
+    if (ctx1 != NULL) {
+        ptls_cipher_free(ctx1);
+    }
+
+    if (ctx2 != NULL) {
+        ptls_cipher_free(ctx2);
+    }
+
+    return ret;
+}
+
+int test_cipher_pair(char const *name1, char const *name2, ptls_cipher_algorithm_t *b1, ptls_cipher_algorithm_t *b2,
+                     unsigned int nb_blocks)
+{
+    int ret = test_cipher_one_way(name1, name2, b1, b2, nb_blocks);
+
+    if (ret == 0) {
+        ret = test_cipher_one_way(name2, name1, b2, b1, nb_blocks);
+    }
+
+    return ret;
+}
+
+/* Test of the hash functions
+ */
+
+int test_hash_calc(char const *name1, char const *name2, ptls_hash_algorithm_t *h1, ptls_hash_algorithm_t *h2)
+{
+    BYTE data[123];
+    BYTE tag1[128];
+    BYTE tag2[128];
+    ptls_hash_context_t *ctx1 = NULL;
+    ptls_hash_context_t *ctx2 = NULL;
+    int ret = 0;
+
+    assert(sizeof(tag1) >= h1->digest_size);
+    assert(sizeof(tag2) >= h2->digest_size);
+    assert(h1->digest_size == h2->digest_size);
+
+    memset(data, 'd', sizeof(data));
+    memset(tag1, '1', sizeof(tag1));
+    memset(tag2, '2', sizeof(tag2));
+
+    if (h1->digest_size != h2->digest_size) {
+        ret = -1;
+    }
+    if (ret == 0) {
+        ret = ptls_calc_hash(h1, tag1, data, sizeof(data));
+    }
+
+    if (ret == 0) {
+        ret = ptls_calc_hash(h2, tag2, data, sizeof(data));
+    }
+
+    if (ret == 0){
+        if (memcmp(tag1, tag2, h1->digest_size) != 0) {
+            printf("For %s -> %s, hash1 does not match hash2\n", name1, name2);
+            ret = -1;
+        } else {
+            printf("For %s -> %s, hash test passes.\n", name1, name2);
+        }
+    }
+
+    return ret;
+}
+
+/* Minimal test program for the bcrypt functions.
+ * Need to add tests for the SHA256 and SHA384 implementations.
+ */
+
+int main()
+{
+    int ret = 0;
+
+    ret |= test_cipher_pair("bcrypt aes128ecb", "minicrypto aes128ecb", &ptls_bcrypt_aes128ecb, &ptls_minicrypto_aes128ecb, 1);
+    ret |= test_cipher_pair("bcrypt aes256ecb", "minicrypto aes256ecb", &ptls_bcrypt_aes256ecb, &ptls_minicrypto_aes256ecb, 1);
+
+    ret |= test_cipher_pair("bcrypt aes128ctr", "minicrypto aes128ctr", &ptls_bcrypt_aes128ctr, &ptls_minicrypto_aes128ctr, 4);
+    ret |= test_cipher_pair("bcrypt aes256ctr", "minicrypto aes256ctr", &ptls_bcrypt_aes256ctr, &ptls_minicrypto_aes256ctr, 4);
+        
+    ret |= test_one_aead(&ptls_bcrypt_aes128gcm, BCRYPT_AES_ALGORITHM, BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM));
+
+    ret |= test_one_aead(&ptls_bcrypt_aes256gcm, BCRYPT_AES_ALGORITHM, BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM));
+
+    ret |= test_hash_calc("bcrypt sha256", "minicrypto sha256", &ptls_bcrypt_sha256, &ptls_minicrypto_sha256);
+    ret |= test_hash_calc("bcrypt sha384", "minicrypto sha384", &ptls_bcrypt_sha384, &ptls_minicrypto_sha384);
+
+    exit(ret);
+}
diff --git a/picotlsvs/bcrypt-test/bcrypt-test.vcxproj b/picotlsvs/bcrypt-test/bcrypt-test.vcxproj
new file mode 100644
index 0000000..4c71134
--- /dev/null
+++ b/picotlsvs/bcrypt-test/bcrypt-test.vcxproj
@@ -0,0 +1,171 @@
+<?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>{7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>bcrypttest</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>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>_DEBUG;_CONSOLE;_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>
+      <AdditionalDependencies>picotls-core.lib;picotls-bcrypt.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_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>
+      <AdditionalDependencies>picotls-core.lib;picotls-bcrypt.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <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;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>picotls-core.lib;picotls-bcrypt.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <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;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>picotls-core.lib;picotls-bcrypt.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="bcrypt-test.c" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/picotlsvs/bcrypt-test/bcrypt-test.vcxproj.filters b/picotlsvs/bcrypt-test/bcrypt-test.vcxproj.filters
new file mode 100644
index 0000000..6d21ff8
--- /dev/null
+++ b/picotlsvs/bcrypt-test/bcrypt-test.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="bcrypt-test.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/picotlsvs/bcrypt-test/bcrypt-test.vcxproj.user b/picotlsvs/bcrypt-test/bcrypt-test.vcxproj.user
new file mode 100644
index 0000000..88a5509
--- /dev/null
+++ b/picotlsvs/bcrypt-test/bcrypt-test.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 9867c37..973957a 100644
--- a/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj
+++ b/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj
@@ -26,13 +26,13 @@
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
+    <ConfigurationType>StaticLibrary</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <PlatformToolset>v142</PlatformToolset>
     <CharacterSet>MultiByte</CharacterSet>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
+    <ConfigurationType>StaticLibrary</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
     <PlatformToolset>v142</PlatformToolset>
     <WholeProgramOptimization>true</WholeProgramOptimization>
@@ -45,9 +45,9 @@
     <CharacterSet>MultiByte</CharacterSet>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
-    <ConfigurationType>Application</ConfigurationType>
+    <ConfigurationType>StaticLibrary</ConfigurationType>
     <UseDebugLibraries>false</UseDebugLibraries>
-    <PlatformToolset>v142</PlatformToolset>
+    <PlatformToolset>v141</PlatformToolset>
     <WholeProgramOptimization>true</WholeProgramOptimization>
     <CharacterSet>MultiByte</CharacterSet>
   </PropertyGroup>
diff --git a/picotlsvs/picotlsvs.sln b/picotlsvs/picotlsvs.sln
index 8cfb619..ba57589 100644
--- a/picotlsvs/picotlsvs.sln
+++ b/picotlsvs/picotlsvs.sln
@@ -24,6 +24,7 @@
 		{559AC085-1BEF-450A-A62D-0D370561D596} = {559AC085-1BEF-450A-A62D-0D370561D596}
 		{499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} = {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}
 		{56C264BF-822B-4F29-B512-5B26157CA2EC} = {56C264BF-822B-4F29-B512-5B26157CA2EC}
+		{0A0E7AF2-05C8-488B-867C-D83B776B8BF4} = {0A0E7AF2-05C8-488B-867C-D83B776B8BF4}
 		{497433FE-B252-4985-A504-54EB791F57F4} = {497433FE-B252-4985-A504-54EB791F57F4}
 	EndProjectSection
 EndProject
@@ -45,6 +46,14 @@
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "picotls-bcrypt", "picotls-bcrypt\picotls-bcrypt.vcxproj", "{0A0E7AF2-05C8-488B-867C-D83B776B8BF4}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bcrypt-test", "bcrypt-test\bcrypt-test.vcxproj", "{7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}"
+	ProjectSection(ProjectDependencies) = postProject
+		{559AC085-1BEF-450A-A62D-0D370561D596} = {559AC085-1BEF-450A-A62D-0D370561D596}
+		{499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} = {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}
+		{0A0E7AF2-05C8-488B-867C-D83B776B8BF4} = {0A0E7AF2-05C8-488B-867C-D83B776B8BF4}
+		{497433FE-B252-4985-A504-54EB791F57F4} = {497433FE-B252-4985-A504-54EB791F57F4}
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|x64 = Debug|x64
@@ -125,6 +134,14 @@
 		{0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Release|x64.Build.0 = Release|x64
 		{0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Release|x86.ActiveCfg = Release|Win32
 		{0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Release|x86.Build.0 = Release|Win32
+		{7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Debug|x64.ActiveCfg = Debug|x64
+		{7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Debug|x64.Build.0 = Debug|x64
+		{7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Debug|x86.ActiveCfg = Debug|Win32
+		{7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Debug|x86.Build.0 = Debug|Win32
+		{7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Release|x64.ActiveCfg = Release|x64
+		{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
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/picotlsvs/picotlsvs/picotlsvs.vcxproj b/picotlsvs/picotlsvs/picotlsvs.vcxproj
index f1190d1..35810aa 100644
--- a/picotlsvs/picotlsvs/picotlsvs.vcxproj
+++ b/picotlsvs/picotlsvs/picotlsvs.vcxproj
@@ -23,7 +23,7 @@
     <ProjectGuid>{D0265367-FCCF-47A4-95FD-C33BECAB3486}</ProjectGuid>
     <Keyword>Win32Proj</Keyword>
     <RootNamespace>picotlsvs</RootNamespace>
-    <WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion>
+    <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
diff --git a/picotlsvs/ptlsbench/ptlsbench.vcxproj b/picotlsvs/ptlsbench/ptlsbench.vcxproj
index 7c4525b..4e06766 100644
--- a/picotlsvs/ptlsbench/ptlsbench.vcxproj
+++ b/picotlsvs/ptlsbench/ptlsbench.vcxproj
@@ -100,7 +100,7 @@
       <SubSystem>Console</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalLibraryDirectories>$(OPENSSL64DIR);$(OPENSSL64DIR)\lib;$(OutDir)</AdditionalLibraryDirectories>
-      <AdditionalDependencies>picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>bcrypt.lib;picotls-core.lib;picotls-openssl.lib;picotls-bcrypt.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
@@ -118,7 +118,7 @@
       <SubSystem>Console</SubSystem>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <AdditionalLibraryDirectories>$(OPENSSLDIR);$(OutDir)</AdditionalLibraryDirectories>
-      <AdditionalDependencies>picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>bcrypt.lib;picotls-core.lib;picotls-openssl.lib;picotls-bcrypt.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
     </Link>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
diff --git a/picotlsvs/testopenssl/testopenssl.vcxproj b/picotlsvs/testopenssl/testopenssl.vcxproj
index f1bb883..6c7b156 100644
--- a/picotlsvs/testopenssl/testopenssl.vcxproj
+++ b/picotlsvs/testopenssl/testopenssl.vcxproj
@@ -23,7 +23,7 @@
     <ProjectGuid>{8750EE3B-9440-48BF-8D83-7274E94B06A7}</ProjectGuid>
     <Keyword>Win32Proj</Keyword>
     <RootNamespace>testopenssl</RootNamespace>
-    <WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion>
+    <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
   </PropertyGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
diff --git a/t/ptlsbench.c b/t/ptlsbench.c
index fd8cfd0..09292e1 100644
--- a/t/ptlsbench.c
+++ b/t/ptlsbench.c
@@ -38,11 +38,14 @@
 #include <openssl/opensslv.h>
 
 #ifdef _WINDOWS
+#include <bcrypt.h>
+#include "picotls/ptlsbcrypt.h"
 #ifdef _DEBUG
 #define BENCH_MODE "check"
 #else
 #define BENCH_MODE "release"
 #endif
+#include "../lib/ptlsbcrypt.c"
 #else
 #ifdef PTLS_DEBUG
 #define BENCH_MODE "debug"
@@ -242,17 +245,23 @@
     const char *algo_name;
     ptls_aead_algorithm_t *aead;
     ptls_hash_algorithm_t *hash;
+    int enabled_by_defaut;
 } ptls_bench_entry_t;
 
 static ptls_bench_entry_t aead_list[] = {
-    {"minicrypto", "aes128gcm", &ptls_minicrypto_aes128gcm, &ptls_minicrypto_sha256},
-    {"minicrypto", "aes256gcm", &ptls_minicrypto_aes256gcm, &ptls_minicrypto_sha384},
-    {"minicrypto", "chacha20poly1305", &ptls_minicrypto_chacha20poly1305, &ptls_minicrypto_sha256},
-#if PTLS_OPENSSL_HAVE_CHACHA20_POLY1305
-    {"openssl", "chacha20poly1305", &ptls_openssl_chacha20poly1305, &ptls_minicrypto_sha256},
+    /* Minicrypto AES disabled by defaut because of atrocious perf */
+    {"minicrypto", "aes128gcm", &ptls_minicrypto_aes128gcm, &ptls_minicrypto_sha256, 0},
+    {"minicrypto", "aes256gcm", &ptls_minicrypto_aes256gcm, &ptls_minicrypto_sha384, 0},
+    {"minicrypto", "chacha20poly1305", &ptls_minicrypto_chacha20poly1305, &ptls_minicrypto_sha256, 1},
+#ifdef _WINDOWS
+    {"ptlsbcrypt", "aes128gcm", &ptls_bcrypt_aes128gcm, &ptls_bcrypt_sha256, 1},
+    {"ptlsbcrypt", "aes256gcm", &ptls_bcrypt_aes256gcm, &ptls_bcrypt_sha384, 1},
 #endif
-    {"openssl", "aes128gcm", &ptls_openssl_aes128gcm, &ptls_minicrypto_sha256},
-    {"openssl", "aes256gcm", &ptls_openssl_aes256gcm, &ptls_minicrypto_sha384}};
+#if PTLS_OPENSSL_HAVE_CHACHA20_POLY1305
+    {"openssl", "chacha20poly1305", &ptls_openssl_chacha20poly1305, &ptls_minicrypto_sha256, 1},
+#endif
+    {"openssl", "aes128gcm", &ptls_openssl_aes128gcm, &ptls_minicrypto_sha256, 1},
+    {"openssl", "aes256gcm", &ptls_openssl_aes256gcm, &ptls_minicrypto_sha384, 1}};
 
 static size_t nb_aead_list = sizeof(aead_list) / sizeof(ptls_bench_entry_t);
 
@@ -280,6 +289,7 @@
 int main(int argc, char **argv)
 {
     int ret = 0;
+    int force_all_tests = 0;
     uint64_t x = 0xdeadbeef;
     uint64_t s = 0;
     int basic_ref = bench_basic(&x);
@@ -305,17 +315,24 @@
     }
 #endif
 
-    
-    printf("OS, HW, bits, mode, 10M ops, provider, version, algorithm, N, L, encrypt us, decrypt us, encrypt mbps, decrypt mbps,\n");
-    
+    if (argc == 2 && strcmp(argv[1], "-f") == 0) {
+        force_all_tests = 1;
+    } else if (argc > 1) {
+        fprintf(stderr, "Usage: %s [-f]\n   Use option \"-f\" to force execution of the slower tests.\n", argv[0]);
+        exit (-1);
+    }
 
+    printf("OS, HW, bits, mode, 10M ops, provider, version, algorithm, N, L, encrypt us, decrypt us, encrypt mbps, decrypt mbps,\n");
+ 
     for (size_t i = 0; ret == 0 && i < nb_aead_list; i++) {
-        ret = bench_run_aead(OS, HW, basic_ref, x, aead_list[i].provider, aead_list[i].algo_name, aead_list[i].aead, aead_list[i].hash, 1000,
-                             1500, &s);
+        if (aead_list[i].enabled_by_defaut || force_all_tests) {
+            ret = bench_run_aead(OS, HW, basic_ref, x, aead_list[i].provider, aead_list[i].algo_name, aead_list[i].aead,
+                                 aead_list[i].hash, 1000, 1500, &s);
+        }
     }
 
     /* Gratuitous test, designed to ensure that the initial computation
-     * of the basic reference benchmark is not optimised away. */
+     * of the basic reference benchmark is not optimized away. */
     if (s == 0){
        printf("Unexpected value of test sum s = %llx\n", (unsigned long long)s);
     }