Merge pull request #275 from huitema/bcrypt-on-windows

Bcrypt on windows
diff --git a/.gitignore b/.gitignore
index 16a9c6e..8530cff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,5 @@
 deps/picotest
 /picotlsvs/cifra/cifra.vcxproj.user
 /picotlsvs/testopenssl/testopenssl.vcxproj.user
+/fuzz/fuzzer-asn1-type-and-length.c
+/fuzz/fuzzer-asn1-validation.c
diff --git a/include/picotls/ptlsbcrypt.h b/include/picotls/ptlsbcrypt.h
new file mode 100644
index 0000000..5317641
--- /dev/null
+++ b/include/picotls/ptlsbcrypt.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef picotls_bcrypt_h
+#define picotls_bcrypt_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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;
+extern ptls_cipher_algorithm_t ptls_bcrypt_aes256ctr;
+
+extern ptls_aead_algorithm_t ptls_bcrypt_aes128gcm;
+extern ptls_aead_algorithm_t ptls_bcrypt_aes256gcm;
+
+extern ptls_hash_algorithm_t ptls_bcrypt_sha256;
+extern ptls_hash_algorithm_t ptls_bcrypt_sha384;
+
+extern ptls_cipher_suite_t ptls_bcrypt_aes128gcmsha256;
+extern ptls_cipher_suite_t ptls_bcrypt_aes256gcmsha384;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* picotls_bcrypt_h */
diff --git a/lib/ptlsbcrypt.c b/lib/ptlsbcrypt.c
new file mode 100644
index 0000000..8b32da7
--- /dev/null
+++ b/lib/ptlsbcrypt.c
@@ -0,0 +1,796 @@
+/*
+ * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef _WINDOWS
+/* This module is only defined for windows.
+ * It is an implementation of the main crypto algorithms
+ * using windows crypto libraries */
+
+int ptls_bcrypt_init()
+{
+    return -1;
+}
+
+void ptlc_bcrypt_dispose()
+{
+}
+
+#else
+
+#include "wincompat.h"
+#include <bcrypt.h>
+#include "picotls.h"
+
+/**
+ * Initialize the brcrypt libraries, creates the
+ * required common variables, etc. */
+int ptls_bcrypt_init()
+{
+    return 0;
+}
+
+/**
+ * Clear the initialization of the bcrypt libraries */
+
+void ptlc_bcrypt_dispose()
+{
+}
+
+/**
+ * Random number generation */
+
+void ptls_bcrypt_random_bytes(void *buf, size_t len)
+{
+    /* TODO: Crypto gen random */
+}
+
+/*
+ * Support for symmetric ciphers
+*/
+
+struct ptls_bcrypt_symmetric_param_t {
+    HANDLE hKey;
+    DWORD dwFlags;
+    ULONG cbKeyObject;
+    uint8_t iv[PTLS_MAX_IV_SIZE];
+    uint8_t *key_object;
+    int is_enc;
+};
+
+struct ptls_bcrypt_symmetric_context_t {
+    ptls_cipher_context_t super;
+    struct ptls_bcrypt_symmetric_param_t bctx;
+};
+
+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 */
+    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)
+{
+    struct ptls_bcrypt_symmetric_context_t *ctx = (struct ptls_bcrypt_symmetric_context_t *)_ctx;
+
+    if (ctx->bctx.hKey != NULL) {
+        (void)BCryptDestroyKey(ctx->bctx.hKey);
+    }
+
+    if (ctx->bctx.key_object != NULL) {
+        free(ctx->bctx.key_object);
+    }
+
+    memset(&ctx->bctx, 0, sizeof(struct ptls_bcrypt_symmetric_param_t));
+}
+
+static void ptls_bcrypt_cipher_transform_ecb(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len)
+{
+    struct ptls_bcrypt_symmetric_context_t *ctx = (struct ptls_bcrypt_symmetric_context_t *)_ctx;
+    ULONG cbResult;
+    NTSTATUS ret;
+
+    assert((len % ctx->super.algo->block_size) == 0);
+
+    /* Call the encryption */
+    if (ctx->bctx.is_enc) {
+        ret = BCryptEncrypt(ctx->bctx.hKey, (PUCHAR)input, (ULONG)len, NULL, NULL, 0, output, (ULONG)len, &cbResult, 0);
+    } else {
+        ret = BCryptDecrypt(ctx->bctx.hKey, (PUCHAR)input, (ULONG)len, NULL, NULL, 0, output, (ULONG)len, &cbResult, 0);
+    }
+
+    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)
+{
+    struct ptls_bcrypt_symmetric_context_t *ctx = (struct ptls_bcrypt_symmetric_context_t *)_ctx;
+    ULONG cbResult;
+    NTSTATUS ret;
+    uint8_t eiv[PTLS_MAX_IV_SIZE];
+    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) {
+
+        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));
+
+        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--;
+            }
+        }
+    }
+}
+
+static int ptls_bcrypt_cipher_setup_crypto(ptls_cipher_context_t *_ctx, int is_enc, const void *key, wchar_t const *bcrypt_name,
+                                           int is_ctr)
+{
+    struct ptls_bcrypt_symmetric_context_t *ctx = (struct ptls_bcrypt_symmetric_context_t *)_ctx;
+    HANDLE hAlgorithm = NULL;
+    NTSTATUS ret;
+
+    memset(&ctx->bctx, 0, sizeof(struct ptls_bcrypt_symmetric_param_t));
+
+    ret = BCryptOpenAlgorithmProvider(&hAlgorithm, bcrypt_name, NULL, 0);
+
+    if (BCRYPT_SUCCESS(ret)) {
+        DWORD ko_size = 0;
+        ULONG cbResult = 0;
+
+        ret = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&ko_size, (ULONG)sizeof(ko_size), &cbResult, 0);
+
+        if (BCRYPT_SUCCESS(ret)) {
+            ctx->bctx.key_object = (uint8_t *)malloc(ko_size);
+            if (ctx->bctx.key_object == NULL) {
+                ret = STATUS_NO_MEMORY;
+            } else {
+                ctx->bctx.cbKeyObject = ko_size;
+            }
+        }
+    }
+
+    if (BCRYPT_SUCCESS(ret)) {
+        ret = BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0);
+    }
+
+    if (BCRYPT_SUCCESS(ret)) {
+        ret = BCryptGenerateSymmetricKey(hAlgorithm, &ctx->bctx.hKey, ctx->bctx.key_object, ctx->bctx.cbKeyObject, (PUCHAR)key,
+                                         (ULONG)ctx->super.algo->key_size, 0);
+    }
+
+    if (hAlgorithm != NULL) {
+        BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+    }
+
+    if (BCRYPT_SUCCESS(ret)) {
+
+        ctx->super.do_dispose = ptls_bcrypt_cipher_dispose;
+        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;
+        return 0;
+    } else {
+        ptls_bcrypt_cipher_dispose(_ctx);
+        return PTLS_ERROR_LIBRARY;
+    }
+}
+
+static int ptls_bcrypt_cipher_setup_crypto_aes_ecb(ptls_cipher_context_t *_ctx, int is_enc, const void *key)
+{
+    return ptls_bcrypt_cipher_setup_crypto(_ctx, is_enc, key, BCRYPT_AES_ALGORITHM, 0);
+}
+
+static int ptls_bcrypt_cipher_setup_crypto_aes_ctr(ptls_cipher_context_t *_ctx, int is_enc, const void *key)
+{
+    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];
+    uint8_t auth_tag[PTLS_MAX_DIGEST_SIZE];
+    BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO aead_params;
+};
+
+struct ptls_bcrypt_aead_context_t {
+    struct st_ptls_aead_context_t super;
+    struct ptls_bcrypt_aead_param_t bctx;
+};
+
+static void ptls_bcrypt_aead_dispose_crypto(struct st_ptls_aead_context_t *_ctx)
+{
+    struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx;
+
+    if (ctx->bctx.hKey != NULL) {
+        (void)BCryptDestroyKey(ctx->bctx.hKey);
+    }
+
+    if (ctx->bctx.key_object != NULL) {
+        free(ctx->bctx.key_object);
+    }
+
+    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)
+{
+    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);
+    /* Auth tag to NULL */
+    memset(ctx->bctx.tag, 0, sizeof(ctx->super.algo->tag_size));
+    BCRYPT_INIT_AUTH_MODE_INFO(ctx->bctx.aead_params);
+
+    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.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 + ctx->super.algo->tag_size + ctx->bctx.nbExtraBytes;
+    ULONG cbResult1 = 0;
+    ULONG cbResult2 = 0;
+    NTSTATUS ret;
+
+    /* 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 outlenMax = ctx->super.algo->tag_size + ctx->bctx.nbExtraBytes;
+    ULONG cbResult = 0;
+    NTSTATUS ret;
+
+    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));
+
+    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,
+                                          const void *iv, 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);
+
+    /* 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 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;
+    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)textLen, (void *)&ctx->bctx.aead_params,
+                        NULL, 0, (PUCHAR)output, (ULONG)textLen, &cbResult, 0);
+
+    if (BCRYPT_SUCCESS(ret)) {
+        return (size_t)cbResult;
+    } else {
+        return SIZE_MAX;
+    }
+}
+
+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)
+{
+    struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx;
+    HANDLE hAlgorithm = NULL;
+    NTSTATUS ret;
+
+    memset(&ctx->bctx, 0, sizeof(struct ptls_bcrypt_symmetric_param_t));
+
+    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;
+
+        ret = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&ko_size, (ULONG)sizeof(ko_size), &cbResult, 0);
+
+        if (BCRYPT_SUCCESS(ret)) {
+            ctx->bctx.key_object = (uint8_t *)malloc(ko_size);
+            if (ctx->bctx.key_object == NULL) {
+                ret = STATUS_NO_MEMORY;
+            } else {
+                ctx->bctx.cbKeyObject = ko_size;
+            }
+        }
+    }
+
+    if (BCRYPT_SUCCESS(ret)) {
+        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)) {
+        ret = BCryptGenerateSymmetricKey(hAlgorithm, &ctx->bctx.hKey, ctx->bctx.key_object, ctx->bctx.cbKeyObject, (PUCHAR)key,
+                                         (ULONG)ctx->super.algo->key_size, 0);
+    }
+
+    if (hAlgorithm != NULL) {
+        BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+    }
+
+    if (BCRYPT_SUCCESS(ret)) {
+        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;
+        } 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 {
+        ptls_bcrypt_aead_dispose_crypto(_ctx);
+        return PTLS_ERROR_LIBRARY;
+    }
+}
+
+static int ptls_bcrypt_aead_setup_crypto_aesgcm(ptls_aead_context_t *_ctx, int is_enc, const void *key)
+{
+    return ptls_bcrypt_aead_setup_crypto(_ctx, is_enc, key, BCRYPT_AES_ALGORITHM, BCRYPT_CHAIN_MODE_GCM,
+                                         sizeof(BCRYPT_CHAIN_MODE_GCM));
+}
+
+/* Hash algorithms */
+
+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 {
+    ptls_hash_context_t super;
+    struct st_ptls_bcrypt_hash_param_t ctx;
+};
+
+static void ptls_bcrypt_hash_update(struct st_ptls_hash_context_t *_ctx, const void *src, size_t len)
+{
+    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)
+{
+    if (ctx->ctx.pbHashObject != NULL) {
+        ptls_clear_memory(ctx->ctx.pbHashObject, ctx->ctx.cbHashObject);
+        free(ctx->ctx.pbHashObject);
+    }
+    ptls_clear_memory(&ctx->ctx, sizeof(ctx->ctx));
+
+    return NULL;
+}
+
+static ptls_hash_context_t *ptls_bcrypt_hash_clone(struct st_ptls_hash_context_t *_ctx);
+
+static void ptls_bcrypt_hash_final(struct st_ptls_hash_context_t *_ctx, void *md, ptls_hash_final_mode_t mode)
+{
+    if (mode == PTLS_HASH_FINAL_MODE_SNAPSHOT) {
+        /* TODO: Copying handle does not change the handle! */
+        struct st_ptls_hash_context_t *clone_ctx = ptls_bcrypt_hash_clone(_ctx);
+
+        if (clone_ctx != NULL) {
+            ptls_bcrypt_hash_final(clone_ctx, md, PTLS_HASH_FINAL_MODE_FREE);
+        } else {
+            assert(clone_ctx != NULL);
+        }
+    } else {
+        NTSTATUS ret;
+        struct st_ptls_bcrypt_hash_context_t *ctx = (struct st_ptls_bcrypt_hash_context_t *)_ctx;
+
+        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);
+        assert(BCRYPT_SUCCESS(ret));
+
+        switch (mode) {
+        case PTLS_HASH_FINAL_MODE_FREE:
+            ctx = ptls_bcrypt_hash_context_free(ctx);
+            break;
+        case PTLS_HASH_FINAL_MODE_RESET: {
+            BCRYPT_ALG_HANDLE hAlgorithm = NULL;
+            ret = BCryptOpenAlgorithmProvider(&hAlgorithm, ctx->ctx.bcrypt_name, NULL, 0);
+            if (BCRYPT_SUCCESS(ret)) {
+                ctx->ctx.hHash = NULL;
+                ret = BCryptCreateHash(hAlgorithm, &ctx->ctx.hHash, ctx->ctx.pbHashObject, ctx->ctx.cbHashObject, NULL, 0, 0);
+                BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+            }
+            assert(BCRYPT_SUCCESS(ret));
+            if (!BCRYPT_SUCCESS(ret)) {
+                ctx->ctx.hHash = NULL;   
+            }
+            break;
+        }
+        default:
+            assert(!"FIXME");
+            break;
+        }
+    }
+}
+
+static ptls_hash_context_t *ptls_bcrypt_hash_clone(struct st_ptls_hash_context_t *_ctx)
+{
+    struct st_ptls_bcrypt_hash_context_t *ctx = (struct st_ptls_bcrypt_hash_context_t *)_ctx;
+    struct st_ptls_bcrypt_hash_context_t *clone_ctx;
+
+    if ((clone_ctx = (struct st_ptls_bcrypt_hash_context_t *)malloc(sizeof(*ctx))) != NULL) {
+        NTSTATUS ret;
+
+        ptls_clear_memory(&clone_ctx->ctx, sizeof(clone_ctx->ctx));
+        clone_ctx->super = (ptls_hash_context_t){ptls_bcrypt_hash_update, ptls_bcrypt_hash_final, ptls_bcrypt_hash_clone};
+        clone_ctx->ctx.pbHashObject = (uint8_t *)malloc(ctx->ctx.cbHashObject);
+        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 {
+            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);
+        }
+
+        if (!BCRYPT_SUCCESS(ret)) {
+            clone_ctx = ptls_bcrypt_hash_context_free(clone_ctx);
+        }
+    }
+
+    return (ptls_hash_context_t *)clone_ctx;
+}
+
+static ptls_hash_context_t *ptls_bcrypt_hash_create(wchar_t const *bcrypt_name, ULONG hash_size)
+{
+    BCRYPT_ALG_HANDLE hAlgorithm = NULL;
+    NTSTATUS ret;
+    struct st_ptls_bcrypt_hash_context_t *ctx;
+
+    if ((ctx = (struct st_ptls_bcrypt_hash_context_t *)malloc(sizeof(*ctx))) != NULL) {
+        ctx->super = (ptls_hash_context_t){ptls_bcrypt_hash_update, ptls_bcrypt_hash_final, ptls_bcrypt_hash_clone};
+        memset(&ctx->ctx, 0, sizeof(struct st_ptls_bcrypt_hash_param_t));
+        ctx->ctx.hash_size = hash_size;
+        ctx->ctx.bcrypt_name = bcrypt_name;
+
+        ret = BCryptOpenAlgorithmProvider(&hAlgorithm, bcrypt_name, NULL, 0);
+
+        if (BCRYPT_SUCCESS(ret)) {
+            DWORD hb_length = 0;
+            ULONG 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);
+                if (ctx->ctx.pbHashObject == NULL) {
+                    ret = STATUS_NO_MEMORY;
+                } else {
+                    ctx->ctx.cbHashObject = hb_length;
+                }
+            }
+        }
+
+        if (BCRYPT_SUCCESS(ret)) {
+            ret = BCryptCreateHash(hAlgorithm, &ctx->ctx.hHash, ctx->ctx.pbHashObject, ctx->ctx.cbHashObject, NULL, 0, 0);
+        }
+
+        if (!BCRYPT_SUCCESS(ret)) {
+            ctx = ptls_bcrypt_hash_context_free(ctx);
+        }
+    }
+
+    if (hAlgorithm != NULL) {
+        BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+    }
+
+    return (ptls_hash_context_t *)ctx;
+}
+
+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(void)
+{
+    return ptls_bcrypt_hash_create(BCRYPT_SHA384_ALGORITHM, PTLS_SHA384_DIGEST_SIZE);
+}
+
+/* Declaration of algorithms
+ */
+
+ptls_cipher_algorithm_t ptls_bcrypt_aes128ecb = {"AES128-ECB",
+                                                 PTLS_AES128_KEY_SIZE,
+                                                 PTLS_AES_BLOCK_SIZE,
+                                                 0 /* iv size */,
+                                                 sizeof(struct ptls_bcrypt_symmetric_context_t),
+                                                 ptls_bcrypt_cipher_setup_crypto_aes_ecb};
+ptls_cipher_algorithm_t ptls_bcrypt_aes256ecb = {"AES256-ECB",
+                                                 PTLS_AES256_KEY_SIZE,
+                                                 PTLS_AES_BLOCK_SIZE,
+                                                 0 /* iv size */,
+                                                 sizeof(struct ptls_bcrypt_symmetric_context_t),
+                                                 ptls_bcrypt_cipher_setup_crypto_aes_ecb};
+
+ptls_cipher_algorithm_t ptls_bcrypt_aes128ctr = {"AES128-CTR",
+                                                 PTLS_AES128_KEY_SIZE,
+                                                 PTLS_AES_BLOCK_SIZE,
+                                                 0 /* iv size */,
+                                                 sizeof(struct ptls_bcrypt_symmetric_context_t),
+                                                 ptls_bcrypt_cipher_setup_crypto_aes_ctr};
+
+ptls_cipher_algorithm_t ptls_bcrypt_aes256ctr = {"AES256-CTR",
+                                                 PTLS_AES256_KEY_SIZE,
+                                                 PTLS_AES_BLOCK_SIZE,
+                                                 0 /* iv size */,
+                                                 sizeof(struct ptls_bcrypt_symmetric_context_t),
+                                                 ptls_bcrypt_cipher_setup_crypto_aes_ctr};
+
+ptls_aead_algorithm_t ptls_bcrypt_aes128gcm = {"AES128-GCM",
+                                               &ptls_bcrypt_aes128ecb,
+                                               &ptls_bcrypt_aes128ctr,
+                                               PTLS_AES128_KEY_SIZE,
+                                               PTLS_AESGCM_IV_SIZE,
+                                               PTLS_AESGCM_TAG_SIZE,
+                                               sizeof(struct ptls_bcrypt_aead_context_t),
+                                               ptls_bcrypt_aead_setup_crypto_aesgcm};
+
+ptls_aead_algorithm_t ptls_bcrypt_aes256gcm = {"AES256-GCM",
+                                               &ptls_bcrypt_aes256ecb,
+                                               &ptls_bcrypt_aes256ctr,
+                                               PTLS_AES256_KEY_SIZE,
+                                               PTLS_AESGCM_IV_SIZE,
+                                               PTLS_AESGCM_TAG_SIZE,
+                                               sizeof(struct ptls_bcrypt_aead_context_t),
+                                               ptls_bcrypt_aead_setup_crypto_aesgcm};
+
+ptls_hash_algorithm_t ptls_bcrypt_sha256 = {PTLS_SHA256_BLOCK_SIZE, PTLS_SHA256_DIGEST_SIZE, ptls_bcrypt_sha256_create,
+                                            PTLS_ZERO_DIGEST_SHA256};
+ptls_hash_algorithm_t ptls_bcrypt_sha384 = {PTLS_SHA384_BLOCK_SIZE, PTLS_SHA384_DIGEST_SIZE, ptls_bcrypt_sha384_create,
+                                            PTLS_ZERO_DIGEST_SHA384};
+
+ptls_cipher_suite_t ptls_bcrypt_aes128gcmsha256 = {PTLS_CIPHER_SUITE_AES_128_GCM_SHA256, &ptls_bcrypt_aes128gcm,
+                                                   &ptls_bcrypt_sha256};
+ptls_cipher_suite_t ptls_bcrypt_aes256gcmsha384 = {PTLS_CIPHER_SUITE_AES_256_GCM_SHA384, &ptls_bcrypt_aes256gcm,
+                                                   &ptls_bcrypt_sha384};
+
+#ifdef PRLS_BCRYPT_TODO
+/* 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
+ptls_key_exchange_algorithm_t ptls_bcrypt_secp384r1 = {PTLS_GROUP_SECP384R1, x9_62_create_key_exchange, secp_key_exchange,
+                                                       NID_secp384r1};
+#endif
+#if ptls_bcrypt_HAVE_SECP521R1
+ptls_key_exchange_algorithm_t ptls_bcrypt_secp521r1 = {PTLS_GROUP_SECP521R1, x9_62_create_key_exchange, secp_key_exchange,
+                                                       NID_secp521r1};
+#endif
+#if ptls_bcrypt_HAVE_X25519
+ptls_key_exchange_algorithm_t ptls_bcrypt_x25519 = {PTLS_GROUP_X25519, evp_keyex_create, evp_keyex_exchange, NID_X25519};
+#endif
+
+ptls_key_exchange_algorithm_t *ptls_bcrypt_key_exchanges[] = {&ptls_bcrypt_secp256r1, NULL};
+#endif
+
+#endif /* _WINDOWS */
\ No newline at end of file
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
new file mode 100644
index 0000000..973957a
--- /dev/null
+++ b/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj
@@ -0,0 +1,142 @@
+<?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>{0A0E7AF2-05C8-488B-867C-D83B776B8BF4}</ProjectGuid>
+    <RootNamespace>picotlsbcrypt</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>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</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 />
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+      <PreprocessorDefinitions>_DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+      <PreprocessorDefinitions>NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>$(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\lib\ptlsbcrypt.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\include\picotls\ptlsbcrypt.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj.filters b/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj.filters
new file mode 100644
index 0000000..2193df6
--- /dev/null
+++ b/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj.filters
@@ -0,0 +1,27 @@
+<?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\ptlsbcrypt.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\include\picotls\ptlsbcrypt.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj.user b/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj.user
new file mode 100644
index 0000000..88a5509
--- /dev/null
+++ b/picotlsvs/picotls-bcrypt/picotls-bcrypt.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 194eeac..ba57589 100644
--- a/picotlsvs/picotlsvs.sln
+++ b/picotlsvs/picotlsvs.sln
@@ -1,7 +1,7 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.26228.9
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29230.47
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "picotlsvs", "picotlsvs\picotlsvs.vcxproj", "{D0265367-FCCF-47A4-95FD-C33BECAB3486}"
 	ProjectSection(ProjectDependencies) = postProject
@@ -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
@@ -43,6 +44,16 @@
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "picotls-minicrypto-deps", "picotls-minicrypto-deps\picotls-minicrypto-deps.vcxproj", "{499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}"
 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
@@ -115,6 +126,22 @@
 		{499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}.Release|x64.Build.0 = Release|x64
 		{499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}.Release|x86.ActiveCfg = Release|Win32
 		{499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}.Release|x86.Build.0 = Release|Win32
+		{0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Debug|x64.ActiveCfg = Debug|x64
+		{0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Debug|x64.Build.0 = Debug|x64
+		{0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Debug|x86.ActiveCfg = Debug|Win32
+		{0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Debug|x86.Build.0 = Debug|Win32
+		{0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Release|x64.ActiveCfg = Release|x64
+		{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);
     }