| /* |
| * 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]; |
| 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_xor_iv(ptls_aead_context_t *_ctx, const void *_bytes, size_t len) |
| { |
| struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx; |
| const uint8_t *bytes = _bytes; |
| |
| for (size_t i = 0; i < len; ++i) |
| ctx->bctx.iv[i] ^= bytes[i]; |
| } |
| |
| 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_xor_iv = ptls_bcrypt_aead_xor_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; |
| } 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, 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, |
| 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, |
| 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 = {.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 */ |