First version of the bcrypt
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..4685b76
--- /dev/null
+++ b/include/picotls/ptlsbcrypt.h
@@ -0,0 +1,87 @@
+/*
+ * 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_openssl_h
+#define picotls_openssl_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "../picotls.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;
+
+#if 0
+
+void ptls_openssl_random_bytes(void *buf, size_t len);
+/**
+ * constructs a key exchange context. pkey's reference count is incremented.
+ */
+int ptls_openssl_create_key_exchange(ptls_key_exchange_context_t **ctx, EVP_PKEY *pkey);
+
+struct st_ptls_openssl_signature_scheme_t {
+    uint16_t scheme_id;
+    const EVP_MD *scheme_md;
+};
+
+typedef struct st_ptls_openssl_sign_certificate_t {
+    ptls_sign_certificate_t super;
+    EVP_PKEY *key;
+    struct st_ptls_openssl_signature_scheme_t schemes[4]; /* terminated by .scheme_id == UINT16_MAX */
+} ptls_openssl_sign_certificate_t;
+
+int ptls_openssl_init_sign_certificate(ptls_openssl_sign_certificate_t *self, EVP_PKEY *key);
+void ptls_openssl_dispose_sign_certificate(ptls_openssl_sign_certificate_t *self);
+int ptls_openssl_load_certificates(ptls_context_t *ctx, X509 *cert, STACK_OF(X509) * chain);
+
+typedef struct st_ptls_openssl_verify_certificate_t {
+    ptls_verify_certificate_t super;
+    X509_STORE *cert_store;
+} ptls_openssl_verify_certificate_t;
+
+int ptls_openssl_init_verify_certificate(ptls_openssl_verify_certificate_t *self, X509_STORE *store);
+void ptls_openssl_dispose_verify_certificate(ptls_openssl_verify_certificate_t *self);
+X509_STORE *ptls_openssl_create_default_certificate_store(void);
+
+int ptls_openssl_encrypt_ticket(ptls_buffer_t *dst, ptls_iovec_t src,
+                                int (*cb)(unsigned char *, unsigned char *, EVP_CIPHER_CTX *, HMAC_CTX *, int));
+int ptls_openssl_decrypt_ticket(ptls_buffer_t *dst, ptls_iovec_t src,
+                                int (*cb)(unsigned char *, unsigned char *, EVP_CIPHER_CTX *, HMAC_CTX *, int));
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/ptlsbcrypt.c b/lib/ptlsbcrypt.c
new file mode 100644
index 0000000..57cfd41
--- /dev/null
+++ b/lib/ptlsbcrypt.c
@@ -0,0 +1,906 @@
+/*
+ * 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 -1;
+}
+
+/**
+ * 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 */
+}
+
+/**
+ * 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;
+
+*/
+
+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(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);
+}
+
+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));
+}
+
+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 iv[PTLS_MAX_IV_SIZE];
+    uint8_t eiv[PTLS_MAX_IV_SIZE];
+    size_t iv_size = _ctx->algo->iv_size;
+    size_t 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 <= 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,
+                            (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];
+        }
+    }
+}
+
+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;
+        ctx->super.do_init = ptls_bcrypt_cipher_init;
+        if (is_ctr) {
+            ctx->super.do_transform = ptls_bcrypt_cipher_transform_ctr;
+        } else {
+            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);
+}
+
+struct ptls_bcrypt_aead_param_t {
+    HANDLE hKey;
+    ULONG cbKeyObject;
+    uint8_t *key_object;
+    uint8_t iv[PTLS_MAX_IV_SIZE];
+    uint8_t tag[PTLS_MAX_DIGEST_SIZE];
+    uint64_t nonce;
+    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));
+
+    /* 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);
+    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;
+}
+
+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;
+    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;
+}
+
+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;
+    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);
+    assert(BCRYPT_SUCCESS(ret));
+    return ctx->super.algo->tag_size;
+}
+
+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 out_len_max = 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 nonce */
+    ctx->bctx.nonce = 0;
+    ctx->bctx.aead_params.pbNonce = (PUCHAR)&ctx->bctx.nonce;
+    ctx->bctx.aead_params.cbNonce = (ULONG)sizeof(ctx->bctx.nonce);
+    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);
+
+    /* 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);
+
+    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)) {
+        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_mode, bcrypt_mode_size, 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 (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);
+    }
+
+    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;
+        }
+        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 */
+#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 {
+    wchar_t const *bcrypt_name;
+    BCRYPT_HASH_HANDLE hHash;
+    PUCHAR pbHashObject;
+    ULONG cbHashObject;
+    ULONG hash_size;
+};
+
+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));
+}
+
+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));
+        }
+
+        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));
+            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;
+
+        if (clone_ctx->ctx.pbHashObject == NULL) {
+            ret = STATUS_NO_MEMORY;
+        } else {
+            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_HASH_BLOCK_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()
+{
+    return ptls_bcrypt_hash_create(BCRYPT_SHA256_ALGORITHM, PTLS_SHA256_DIGEST_SIZE);
+}
+
+static ptls_hash_context_t *ptls_bcrypt_sha384_create()
+{
+    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 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 */
+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/picotls-bcrypt/picotls-bcrypt.vcxproj b/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj
new file mode 100644
index 0000000..9867c37
--- /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>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</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>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</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..8cfb619 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
@@ -43,6 +43,8 @@
 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
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|x64 = Debug|x64
@@ -115,6 +117,14 @@
 		{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
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE