blob: 8f4561f490045d612527750a86e034fac992b33a [file] [log] [blame]
/*
* 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 = {PTLS_CIPHER_SUITE_AES_128_GCM_SHA256, &ptls_bcrypt_aes128gcm,
&ptls_bcrypt_sha256};
ptls_cipher_suite_t ptls_bcrypt_aes256gcmsha384 = {PTLS_CIPHER_SUITE_AES_256_GCM_SHA384, &ptls_bcrypt_aes256gcm,
&ptls_bcrypt_sha384};
#ifdef PRLS_BCRYPT_TODO
/* TODO: develp these bcrypt functions */
ptls_key_exchange_algorithm_t ptls_bcrypt_secp256r1 = {PTLS_GROUP_SECP256R1, x9_62_create_key_exchange, secp_key_exchange,
NID_X9_62_prime256v1};
#if ptls_bcrypt_HAVE_SECP384R1
ptls_key_exchange_algorithm_t ptls_bcrypt_secp384r1 = {PTLS_GROUP_SECP384R1, x9_62_create_key_exchange, secp_key_exchange,
NID_secp384r1};
#endif
#if ptls_bcrypt_HAVE_SECP521R1
ptls_key_exchange_algorithm_t ptls_bcrypt_secp521r1 = {PTLS_GROUP_SECP521R1, x9_62_create_key_exchange, secp_key_exchange,
NID_secp521r1};
#endif
#if ptls_bcrypt_HAVE_X25519
ptls_key_exchange_algorithm_t ptls_bcrypt_x25519 = {PTLS_GROUP_X25519, evp_keyex_create, evp_keyex_exchange, NID_X25519};
#endif
ptls_key_exchange_algorithm_t *ptls_bcrypt_key_exchanges[] = {&ptls_bcrypt_secp256r1, NULL};
#endif
#endif /* _WINDOWS */