/* * 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 #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]; 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 > 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 iv_static[PTLS_MAX_IV_SIZE]; uint8_t extraBytes[PTLS_MAX_DIGEST_SIZE]; uint8_t iv[PTLS_MAX_IV_SIZE]; uint8_t ivbuf[PTLS_MAX_IV_SIZE]; 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, uint64_t seq, const void *aad, size_t aadlen) { struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx; /* Build the IV for this encryption */ ptls_aead__build_iv(ctx->super.algo, ctx->bctx.iv, ctx->bctx.iv_static, seq); /* Auth tag to NULL */ memset(ctx->bctx.tag, 0, sizeof(ctx->super.algo->tag_size)); BCRYPT_INIT_AUTH_MODE_INFO(ctx->bctx.aead_params); 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; } void ptls_bcrypt_do_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq, const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp) { size_t after_update; ctx->do_encrypt_init(ctx, seq, aad, aadlen); after_update = ctx->do_encrypt_update(ctx, output, input, inlen); ctx->do_encrypt_final(ctx, (uint8_t *)output + after_update); if (supp != NULL) { ptls_cipher_init(supp->ctx, supp->input); memset(supp->output, 0, sizeof(supp->output)); ptls_cipher_encrypt(supp->ctx, supp->output, supp->output, sizeof(supp->output)); } } static size_t ptls_bcrypt_aead_do_decrypt(struct st_ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen, uint64_t seq, const void *aad, size_t aadlen) { struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx; ULONG cbResult; size_t textLen = inlen - ctx->super.algo->tag_size; NTSTATUS ret; /* Build the IV for this decryption */ ptls_aead__build_iv(ctx->super.algo, ctx->bctx.iv, ctx->bctx.iv_static, seq); /* TODO: pPaddingInfo must point to BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO structure. */ BCRYPT_INIT_AUTH_MODE_INFO(ctx->bctx.aead_params); /* 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 void ptls_bcrypt_aead_get_iv(ptls_aead_context_t *_ctx, void *iv) { struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx; memcpy(iv, ctx->bctx.iv, ctx->super.algo->iv_size); } static void ptls_bcrypt_aead_set_iv(ptls_aead_context_t *_ctx, const void *iv) { struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx; memcpy(ctx->bctx.iv, iv, ctx->super.algo->iv_size); } static int ptls_bcrypt_aead_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv, wchar_t const *bcrypt_name, wchar_t const *bcrypt_mode, size_t bcrypt_mode_size) { struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx; HANDLE hAlgorithm = NULL; 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)) { memcpy(ctx->bctx.iv_static, iv, ctx->super.algo->iv_size); if (is_enc) { ctx->super.dispose_crypto = ptls_bcrypt_aead_dispose_crypto; ctx->super.do_get_iv = ptls_bcrypt_aead_get_iv; ctx->super.do_set_iv = ptls_bcrypt_aead_set_iv; ctx->super.do_decrypt = NULL; ctx->super.do_encrypt_init = ptls_bcrypt_aead_do_encrypt_init; ctx->super.do_encrypt_update = ptls_bcrypt_aead_do_encrypt_update; ctx->super.do_encrypt_final = ptls_bcrypt_aead_do_encrypt_final; ctx->super.do_encrypt = ptls_bcrypt_do_encrypt; ctx->super.do_encrypt_v = ptls_aead__do_encrypt_v; } 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; ctx->super.do_encrypt = NULL; ctx->super.do_encrypt_v = 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, const void *iv) { return ptls_bcrypt_aead_setup_crypto(_ctx, is_enc, key, iv, 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_AESGCM_CONFIDENTIALITY_LIMIT, PTLS_AESGCM_INTEGRITY_LIMIT, &ptls_bcrypt_aes128ecb, &ptls_bcrypt_aes128ctr, PTLS_AES128_KEY_SIZE, PTLS_AESGCM_IV_SIZE, PTLS_AESGCM_TAG_SIZE, {PTLS_TLS12_AESGCM_FIXED_IV_SIZE, PTLS_TLS12_AESGCM_RECORD_IV_SIZE}, 0, 0, sizeof(struct ptls_bcrypt_aead_context_t), ptls_bcrypt_aead_setup_crypto_aesgcm}; ptls_aead_algorithm_t ptls_bcrypt_aes256gcm = {"AES256-GCM", PTLS_AESGCM_CONFIDENTIALITY_LIMIT, PTLS_AESGCM_INTEGRITY_LIMIT, &ptls_bcrypt_aes256ecb, &ptls_bcrypt_aes256ctr, PTLS_AES256_KEY_SIZE, PTLS_AESGCM_IV_SIZE, PTLS_AESGCM_TAG_SIZE, {PTLS_TLS12_AESGCM_FIXED_IV_SIZE, PTLS_TLS12_AESGCM_RECORD_IV_SIZE}, 0, 0, sizeof(struct ptls_bcrypt_aead_context_t), ptls_bcrypt_aead_setup_crypto_aesgcm}; ptls_hash_algorithm_t ptls_bcrypt_sha256 = {"sha256", PTLS_SHA256_BLOCK_SIZE, PTLS_SHA256_DIGEST_SIZE, ptls_bcrypt_sha256_create, PTLS_ZERO_DIGEST_SHA256}; ptls_hash_algorithm_t ptls_bcrypt_sha384 = {"sha384", PTLS_SHA384_BLOCK_SIZE, PTLS_SHA384_DIGEST_SIZE, ptls_bcrypt_sha384_create, PTLS_ZERO_DIGEST_SHA384}; ptls_cipher_suite_t ptls_bcrypt_aes128gcmsha256 = {.id = PTLS_CIPHER_SUITE_AES_128_GCM_SHA256, .name = PTLS_CIPHER_SUITE_NAME_AES_128_GCM_SHA256, .aead = &ptls_bcrypt_aes128gcm, .hash = &ptls_bcrypt_sha256}; ptls_cipher_suite_t ptls_bcrypt_aes256gcmsha384 = {.id = PTLS_CIPHER_SUITE_AES_256_GCM_SHA384, .name = PTLS_CIPHER_SUITE_NAME_AES_256_GCM_SHA384, .aead = &ptls_bcrypt_aes256gcm, .hash = &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 */