blob: f7386d3976f6111dc2e65c0155e94d5fae8241b0 [file] [log] [blame]
/*
* Copyright (c) 2023, Christian Huitema
*
* 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.
*/
#ifdef _WINDOWS
#include "wincompat.h"
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <psa/crypto.h>
#include <mbedtls/chacha20.h>
#include <mbedtls/ecdh.h>
#include "picotls.h"
void ptls_mbedtls_free(void)
{
mbedtls_psa_crypto_free();
}
int ptls_mbedtls_init(void)
{
int ret = 0;
psa_status_t status;
if ((status = psa_crypto_init()) != PSA_SUCCESS) {
ret = -1;
}
return ret;
}
void ptls_mbedtls_random_bytes(void *buf, size_t len)
{
psa_generate_random((uint8_t *)buf, len);
}
/* Definitions for hash algorithms.
* In Picotls, these are described by the stucture
* ptls_hash_algorithm_t, which include the function
* pointer for creation of the hash context.
*
* The structure contains a function pointer to the
* "create" function that creates a hash operation,
* which itself contains three function pointers:
*
* void (*update)(struct st_ptls_hash_context_t *ctx, const void *src, size_t len);
* void (*final)(struct st_ptls_hash_context_t *ctx, void *md, ptls_hash_final_mode_t mode);
* struct st_ptls_hash_context_t *(*clone_)(struct st_ptls_hash_context_t *src);
*
*/
typedef struct st_ptls_mbedtls_hash_ctx_t {
ptls_hash_context_t super;
psa_algorithm_t alg;
size_t hash_size;
psa_hash_operation_t operation;
} ptls_mbedtls_hash_ctx_t;
static void ptls_mbedtls_hash_update(struct st_ptls_hash_context_t *_ctx, const void *src, size_t len)
{
ptls_mbedtls_hash_ctx_t *ctx = (ptls_mbedtls_hash_ctx_t *)_ctx;
(void)psa_hash_update(&ctx->operation, (const uint8_t *)src, len);
}
static void ptls_mbedtls_hash_final(struct st_ptls_hash_context_t *_ctx, void *md, ptls_hash_final_mode_t mode);
static struct st_ptls_hash_context_t *ptls_mbedtls_hash_clone(struct st_ptls_hash_context_t *_src)
{
ptls_mbedtls_hash_ctx_t *ctx = (ptls_mbedtls_hash_ctx_t *)malloc(sizeof(ptls_mbedtls_hash_ctx_t));
const ptls_mbedtls_hash_ctx_t *src = (const ptls_mbedtls_hash_ctx_t *)_src;
if (ctx != NULL) {
ptls_mbedtls_hash_ctx_t *src = (ptls_mbedtls_hash_ctx_t *)_src;
memset(&ctx->operation, 0, sizeof(mbedtls_sha256_context));
ctx->super.clone_ = ptls_mbedtls_hash_clone;
ctx->super.update = ptls_mbedtls_hash_update;
ctx->super.final = ptls_mbedtls_hash_final;
ctx->alg = src->alg;
ctx->hash_size = src->hash_size;
if (psa_hash_clone(&src->operation, &ctx->operation) != 0) {
free(ctx);
ctx = NULL;
}
}
return (ptls_hash_context_t *)ctx;
}
static void ptls_mbedtls_hash_final(struct st_ptls_hash_context_t *_ctx, void *md, ptls_hash_final_mode_t mode)
{
ptls_mbedtls_hash_ctx_t *ctx = (ptls_mbedtls_hash_ctx_t *)_ctx;
if (mode == PTLS_HASH_FINAL_MODE_SNAPSHOT) {
struct st_ptls_hash_context_t *cloned = ptls_mbedtls_hash_clone(_ctx);
if (cloned != NULL) {
ptls_mbedtls_hash_final(cloned, md, PTLS_HASH_FINAL_MODE_FREE);
}
} else {
if (md != NULL) {
size_t hash_length = 0;
if (psa_hash_finish(&ctx->operation, md, ctx->hash_size, &hash_length) != 0) {
memset(md, 0, ctx->hash_size);
}
}
if (mode == PTLS_HASH_FINAL_MODE_FREE) {
(void)psa_hash_abort(&ctx->operation);
free(ctx);
} else {
/* if mode = reset, reset the context */
memset(&ctx->operation, 0, sizeof(ctx->operation));
(void)psa_hash_setup(&ctx->operation, ctx->alg);
}
}
}
ptls_hash_context_t *ptls_mbedtls_hash_create(psa_algorithm_t alg, size_t hash_size)
{
ptls_mbedtls_hash_ctx_t *ctx = (ptls_mbedtls_hash_ctx_t *)malloc(sizeof(ptls_mbedtls_hash_ctx_t));
if (ctx != NULL) {
memset(&ctx->operation, 0, sizeof(ctx->operation));
ctx->alg = alg;
ctx->hash_size = hash_size;
ctx->super.clone_ = ptls_mbedtls_hash_clone;
ctx->super.update = ptls_mbedtls_hash_update;
ctx->super.final = ptls_mbedtls_hash_final;
if (psa_hash_setup(&ctx->operation, alg) != 0) {
free(ctx);
ctx = NULL;
}
}
return (ptls_hash_context_t *)ctx;
}
ptls_hash_context_t *ptls_mbedtls_sha256_create(void)
{
return ptls_mbedtls_hash_create(PSA_ALG_SHA_256, PTLS_SHA256_DIGEST_SIZE);
}
ptls_hash_context_t *ptls_mbedtls_sha512_create(void)
{
return ptls_mbedtls_hash_create(PSA_ALG_SHA_512, PTLS_SHA512_DIGEST_SIZE);
}
ptls_hash_algorithm_t ptls_mbedtls_sha256 = {"sha256", PTLS_SHA256_BLOCK_SIZE, PTLS_SHA256_DIGEST_SIZE, ptls_mbedtls_sha256_create,
PTLS_ZERO_DIGEST_SHA256};
ptls_hash_algorithm_t ptls_mbedtls_sha512 = {"SHA512", PTLS_SHA512_BLOCK_SIZE, PTLS_SHA512_DIGEST_SIZE, ptls_mbedtls_sha512_create,
PTLS_ZERO_DIGEST_SHA512};
#if defined(MBEDTLS_SHA384_C)
ptls_hash_context_t *ptls_mbedtls_sha384_create(void)
{
return ptls_mbedtls_hash_create(PSA_ALG_SHA_384, PTLS_SHA384_DIGEST_SIZE);
}
ptls_hash_algorithm_t ptls_mbedtls_sha384 = {"SHA384", PTLS_SHA384_BLOCK_SIZE, PTLS_SHA384_DIGEST_SIZE, ptls_mbedtls_sha384_create,
PTLS_ZERO_DIGEST_SHA384};
#endif /* MBEDTLS_SHA384_C */
/*
* Generic implementation of a cipher using the PSA API
*/
struct st_ptls_mbedtls_cipher_context_t {
ptls_cipher_context_t super;
psa_algorithm_t alg;
size_t iv_length;
int is_enc;
int is_op_in_progress;
mbedtls_svc_key_id_t key;
psa_cipher_operation_t operation;
};
static void ptls_mbedtls_cipher_init(ptls_cipher_context_t *_ctx, const void *iv)
{
struct st_ptls_mbedtls_cipher_context_t *ctx = (struct st_ptls_mbedtls_cipher_context_t *)_ctx;
if (ctx->is_op_in_progress) {
psa_cipher_abort(&ctx->operation);
ctx->is_op_in_progress = 0;
}
memset(&ctx->operation, 0, sizeof(ctx->operation));
if (ctx->is_enc) {
(void)psa_cipher_encrypt_setup(&ctx->operation, ctx->key, ctx->alg);
} else {
(void)psa_cipher_decrypt_setup(&ctx->operation, ctx->key, ctx->alg);
}
if (ctx->iv_length > 0) {
(void)psa_cipher_set_iv(&ctx->operation, (const uint8_t *)iv, ctx->iv_length);
}
ctx->is_op_in_progress = 1;
}
static void ptls_mbedtls_cipher_transform(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len)
{
struct st_ptls_mbedtls_cipher_context_t *ctx = (struct st_ptls_mbedtls_cipher_context_t *)_ctx;
size_t outlen = 0;
(void)psa_cipher_update(&ctx->operation, (const uint8_t *)input, len, (uint8_t *)output, len, &outlen);
}
static void ptls_mbedtls_cipher_dispose(ptls_cipher_context_t *_ctx)
{
struct st_ptls_mbedtls_cipher_context_t *ctx = (struct st_ptls_mbedtls_cipher_context_t *)_ctx;
if (ctx->is_op_in_progress) {
psa_cipher_abort(&ctx->operation);
ctx->is_op_in_progress = 0;
}
psa_destroy_key(ctx->key);
}
static int ptls_mbedtls_cipher_setup_key(mbedtls_svc_key_id_t *key_id, int is_enc, psa_algorithm_t alg, psa_key_type_t key_type,
size_t key_bits, const uint8_t *key_bytes)
{
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
int ret = 0;
psa_set_key_usage_flags(&attributes, (is_enc) ? PSA_KEY_USAGE_ENCRYPT : PSA_KEY_USAGE_DECRYPT);
psa_set_key_algorithm(&attributes, alg);
psa_set_key_type(&attributes, key_type);
psa_set_key_bits(&attributes, key_bits);
/* Import key */
if (psa_import_key(&attributes, key_bytes, key_bits / 8, key_id) != PSA_SUCCESS) {
ret = PTLS_ERROR_LIBRARY;
}
return ret;
}
static int ptls_mbedtls_cipher_setup_crypto(ptls_cipher_context_t *_ctx, int is_enc, const void *key_bytes, psa_algorithm_t alg,
size_t iv_length, psa_key_type_t key_type, size_t key_bits)
{
struct st_ptls_mbedtls_cipher_context_t *ctx = (struct st_ptls_mbedtls_cipher_context_t *)_ctx;
int ret = 0;
ctx->alg = alg;
ctx->is_enc = is_enc;
ctx->iv_length = iv_length;
/* Initialize the key attributes */
ret = ptls_mbedtls_cipher_setup_key(&ctx->key, is_enc, alg, key_type, key_bits, (const uint8_t *)key_bytes);
/* Finish initializing the context */
ctx->super.do_dispose = ptls_mbedtls_cipher_dispose;
ctx->super.do_init = ptls_mbedtls_cipher_init;
ctx->super.do_transform = NULL;
if (ret == 0) {
ctx->super.do_transform = ptls_mbedtls_cipher_transform;
}
return ret;
}
/*
* Implementation of AES128_ECB using the PSA API:
*/
static int ptls_mbedtls_cipher_setup_aes128_ecb(ptls_cipher_context_t *_ctx, int is_enc, const void *key_bytes)
{
return ptls_mbedtls_cipher_setup_crypto(_ctx, is_enc, key_bytes, PSA_ALG_ECB_NO_PADDING, 0, PSA_KEY_TYPE_AES, 128);
}
ptls_cipher_algorithm_t ptls_mbedtls_aes128ecb = {"AES128-ECB",
PTLS_AES128_KEY_SIZE,
PTLS_AES_BLOCK_SIZE,
0 /* iv size */,
sizeof(struct st_ptls_mbedtls_cipher_context_t),
ptls_mbedtls_cipher_setup_aes128_ecb};
/*
* Implementation of AES256_ECB using the PSA API:
*/
static int ptls_mbedtls_cipher_setup_aes256_ecb(ptls_cipher_context_t *_ctx, int is_enc, const void *key_bytes)
{
return ptls_mbedtls_cipher_setup_crypto(_ctx, is_enc, key_bytes, PSA_ALG_ECB_NO_PADDING, 0, PSA_KEY_TYPE_AES, 256);
}
ptls_cipher_algorithm_t ptls_mbedtls_aes256ecb = {"AES256-ECB",
PTLS_AES128_KEY_SIZE,
PTLS_AES_BLOCK_SIZE,
0 /* iv size */,
sizeof(struct st_ptls_mbedtls_cipher_context_t),
ptls_mbedtls_cipher_setup_aes256_ecb};
/*
* Implementation of AES128_CTR using the PSA API:
*/
static int ptls_mbedtls_cipher_setup_aes128_ctr(ptls_cipher_context_t *_ctx, int is_enc, const void *key_bytes)
{
return ptls_mbedtls_cipher_setup_crypto(_ctx, is_enc, key_bytes, PSA_ALG_CTR, 16, PSA_KEY_TYPE_AES, 128);
}
ptls_cipher_algorithm_t ptls_mbedtls_aes128ctr = {"AES128-CTR",
PTLS_AES128_KEY_SIZE,
PTLS_AES_BLOCK_SIZE,
16 /* iv size */,
sizeof(struct st_ptls_mbedtls_cipher_context_t),
ptls_mbedtls_cipher_setup_aes128_ctr};
/*
* Implementation of AES128_CTR using the PSA API:
*/
static int ptls_mbedtls_cipher_setup_aes256_ctr(ptls_cipher_context_t *_ctx, int is_enc, const void *key_bytes)
{
return ptls_mbedtls_cipher_setup_crypto(_ctx, is_enc, key_bytes, PSA_ALG_CTR, 16, PSA_KEY_TYPE_AES, 256);
}
ptls_cipher_algorithm_t ptls_mbedtls_aes256ctr = {"AES128-CTR",
PTLS_AES256_KEY_SIZE,
PTLS_AES_BLOCK_SIZE,
16 /* iv size */,
sizeof(struct st_ptls_mbedtls_cipher_context_t),
ptls_mbedtls_cipher_setup_aes256_ctr};
#if 0
/*
* Implementation of CHACHA20 using the PSA API.
* This is disabled for now, as there seems to be an issue when
* setting the 16 bytes long IV that we need.
*/
static int ptls_mbedtls_cipher_setup_crypto_chacha20(ptls_cipher_context_t *_ctx, int is_enc, const void *key_bytes)
{
return ptls_mbedtls_cipher_setup_crypto(_ctx, is_enc, key_bytes,
PSA_ALG_STREAM_CIPHER, 16, PSA_KEY_TYPE_CHACHA20, 256);
}
ptls_cipher_algorithm_t ptls_mbedtls_chacha20 = {
"CHACHA20", PTLS_CHACHA20_KEY_SIZE, 1 /* block size */, PTLS_CHACHA20_IV_SIZE, sizeof(struct st_ptls_mbedtls_cipher_context_t),
ptls_mbedtls_cipher_setup_crypto_chacha20};
#else
/* Implementation of ChaCha20 using the low level ChaCha20 API.
* TODO: remove this and the reference to chacha20.h as soon as
* the IV bug in the generic implementation is fixed.
*/
struct st_ptls_mbedtls_chacha20_context_t {
ptls_cipher_context_t super;
mbedtls_chacha20_context mctx;
};
static void ptls_mbedtls_chacha20_init(ptls_cipher_context_t *_ctx, const void *v_iv)
{
struct st_ptls_mbedtls_chacha20_context_t *ctx = (struct st_ptls_mbedtls_chacha20_context_t *)_ctx;
const uint8_t *iv = (const uint8_t *)v_iv;
uint32_t ctr = iv[0] | ((uint32_t)iv[1] << 8) | ((uint32_t)iv[2] << 16) | ((uint32_t)iv[3] << 24);
(void)mbedtls_chacha20_starts(&ctx->mctx, (const uint8_t *)(iv + 4), ctr);
}
static void ptls_mbedtls_chacha20_transform(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len)
{
struct st_ptls_mbedtls_chacha20_context_t *ctx = (struct st_ptls_mbedtls_chacha20_context_t *)_ctx;
if (mbedtls_chacha20_update(&ctx->mctx, len, (const uint8_t *)input, (uint8_t *)output) != 0) {
memset(output, 0, len);
}
}
static void ptls_mbedtls_chacha20_dispose(ptls_cipher_context_t *_ctx)
{
struct st_ptls_mbedtls_chacha20_context_t *ctx = (struct st_ptls_mbedtls_chacha20_context_t *)_ctx;
mbedtls_chacha20_free(&ctx->mctx);
}
static int ptls_mbedtls_cipher_setup_crypto_chacha20(ptls_cipher_context_t *_ctx, int is_enc, const void *key)
{
struct st_ptls_mbedtls_chacha20_context_t *ctx = (struct st_ptls_mbedtls_chacha20_context_t *)_ctx;
int ret = 0;
mbedtls_chacha20_init(&ctx->mctx);
ret = mbedtls_chacha20_setkey(&ctx->mctx, (const uint8_t *)key);
ctx->super.do_dispose = ptls_mbedtls_chacha20_dispose;
ctx->super.do_init = ptls_mbedtls_chacha20_init;
ctx->super.do_transform = NULL;
if (ret == 0) {
ctx->super.do_transform = ptls_mbedtls_chacha20_transform;
}
return ret;
}
ptls_cipher_algorithm_t ptls_mbedtls_chacha20 = {"CHACHA20",
PTLS_CHACHA20_KEY_SIZE,
1 /* block size */,
PTLS_CHACHA20_IV_SIZE,
sizeof(struct st_ptls_mbedtls_chacha20_context_t),
ptls_mbedtls_cipher_setup_crypto_chacha20};
#endif
/* Definitions of AEAD algorithms.
*
* For the picotls API, AEAD algorithms are created by calling:
*
* ptls_aead_context_t *ptls_aead_new(ptls_aead_algorithm_t *aead,
* ptls_hash_algorithm_t *hash, int is_enc, const void *secret,
* const char *label_prefix)
* That procedure will allocate memory and create keys, and then call
* a provider specific function:
*
* if (aead->setup_crypto(ctx, is_enc, key, iv) != 0) {
* free(ctx);
* return NULL;
* }
*
* The function will finish completing the aead structure, perform
* initialization, and then document the function pointers:
*
* ctx->super.dispose_crypto: release all resourc
* ctx->super.do_get_iv: return IV
* ctx->super.do_set_iv: set IV value
* ctx->super.do_decrypt: decrypt function
* ctx->super.do_encrypt_init: start encrypting one message
* ctx->super.do_encrypt_update: feed more ciphertext to descriptor
* ctx->super.do_encrypt_final: finalize encryption, including AEAD checksum
* ctx->super.do_encrypt: single shot variant of init/update/final
* ctx->super.do_encrypt_v: scatter gather version of do encrypt
*
* The aead context also documents the underlying "ECB" and "CTR" modes.
* In QUIC, these are used for PN encryption.
*
* TODO: declare other algorithms besides AES128_GCM
*/
struct ptls_mbedtls_aead_param_t {
uint8_t static_iv[PTLS_MAX_IV_SIZE];
psa_algorithm_t alg;
psa_key_id_t key;
psa_aead_operation_t op;
size_t extra_bytes;
int is_op_in_progress;
};
struct ptls_mbedtls_aead_context_t {
struct st_ptls_aead_context_t super;
struct ptls_mbedtls_aead_param_t mctx;
};
void ptls_mbedtls_aead_dispose_crypto(struct st_ptls_aead_context_t *_ctx)
{
struct ptls_mbedtls_aead_context_t *ctx = (struct ptls_mbedtls_aead_context_t *)_ctx;
if (ctx->mctx.is_op_in_progress) {
psa_aead_abort(&ctx->mctx.op);
ctx->mctx.is_op_in_progress = 0;
}
psa_destroy_key(ctx->mctx.key);
}
static void ptls_mbedtls_aead_get_iv(ptls_aead_context_t *_ctx, void *iv)
{
struct ptls_mbedtls_aead_context_t *ctx = (struct ptls_mbedtls_aead_context_t *)_ctx;
memcpy(iv, ctx->mctx.static_iv, ctx->super.algo->iv_size);
}
static void ptls_mbedtls_aead_set_iv(ptls_aead_context_t *_ctx, const void *iv)
{
struct ptls_mbedtls_aead_context_t *ctx = (struct ptls_mbedtls_aead_context_t *)_ctx;
memcpy(ctx->mctx.static_iv, iv, ctx->super.algo->iv_size);
}
void ptls_mbedtls_aead_do_encrypt_init(struct st_ptls_aead_context_t *_ctx, uint64_t seq, const void *aad, size_t aadlen)
{
struct ptls_mbedtls_aead_context_t *ctx = (struct ptls_mbedtls_aead_context_t *)_ctx;
psa_status_t status;
if (ctx->mctx.is_op_in_progress) {
psa_aead_abort(&ctx->mctx.op); /* required on errors, harmless on success */
ctx->mctx.is_op_in_progress = 0;
}
ctx->mctx.is_op_in_progress = 1;
memset(&ctx->mctx.op, 0, sizeof(ctx->mctx.op));
status = psa_aead_encrypt_setup(&ctx->mctx.op, ctx->mctx.key, ctx->mctx.alg);
if (status == PSA_SUCCESS) {
/* set the nonce. */
uint8_t iv[PTLS_MAX_IV_SIZE];
ptls_aead__build_iv(ctx->super.algo, iv, ctx->mctx.static_iv, seq);
status = psa_aead_set_nonce(&ctx->mctx.op, iv, ctx->super.algo->iv_size);
}
if (status == PSA_SUCCESS) {
status = psa_aead_update_ad(&ctx->mctx.op, aad, aadlen);
}
if (status != PSA_SUCCESS) {
psa_aead_abort(&ctx->mctx.op); /* required on errors, harmless on success */
ctx->mctx.is_op_in_progress = 0;
}
}
size_t ptls_mbedtls_aead_do_encrypt_update(struct st_ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen)
{
size_t olen = 0;
struct ptls_mbedtls_aead_context_t *ctx = (struct ptls_mbedtls_aead_context_t *)_ctx;
if (ctx->mctx.is_op_in_progress) {
size_t available = inlen + ctx->mctx.extra_bytes;
psa_status_t status =
psa_aead_update(&ctx->mctx.op, input, inlen, (uint8_t *)output, available + ctx->super.algo->tag_size, &olen);
if (status == PSA_SUCCESS) {
if (olen < available) {
ctx->mctx.extra_bytes = available - olen;
} else {
ctx->mctx.extra_bytes = 0;
}
} else {
psa_aead_abort(&ctx->mctx.op); /* required on errors */
ctx->mctx.is_op_in_progress = 0;
}
}
return olen;
}
size_t ptls_mbedtls_aead_do_encrypt_final(struct st_ptls_aead_context_t *_ctx, void *output)
{
size_t olen = 0;
struct ptls_mbedtls_aead_context_t *ctx = (struct ptls_mbedtls_aead_context_t *)_ctx;
if (ctx->mctx.is_op_in_progress) {
unsigned char tag[PSA_AEAD_TAG_MAX_SIZE];
size_t olen_tag = 0;
size_t available = ctx->mctx.extra_bytes;
uint8_t *p = (uint8_t *)output;
psa_status_t status =
psa_aead_finish(&ctx->mctx.op, p, available + ctx->super.algo->tag_size, &olen, tag, sizeof(tag), &olen_tag);
if (status == PSA_SUCCESS) {
p += olen;
memcpy(p, tag, ctx->super.algo->tag_size);
olen += ctx->super.algo->tag_size;
} else {
psa_aead_abort(&ctx->mctx.op); /* required on errors */
}
ctx->mctx.is_op_in_progress = 0;
}
return (olen);
}
void ptls_mbedtls_aead_do_encrypt_v(struct st_ptls_aead_context_t *_ctx, void *output, ptls_iovec_t *input, size_t incnt,
uint64_t seq, const void *aad, size_t aadlen)
{
unsigned char *p = (uint8_t *)output;
ptls_mbedtls_aead_do_encrypt_init(_ctx, seq, aad, aadlen);
for (size_t i = 0; i < incnt; i++) {
p += ptls_mbedtls_aead_do_encrypt_update(_ctx, p, input[i].base, input[i].len);
}
(void)ptls_mbedtls_aead_do_encrypt_final(_ctx, p);
}
void ptls_mbedtls_aead_do_encrypt(struct st_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)
{
ptls_iovec_t in_v;
in_v.base = (uint8_t *)input;
in_v.len = inlen;
ptls_mbedtls_aead_do_encrypt_v(_ctx, output, &in_v, 1, seq, aad, aadlen);
}
size_t ptls_mbedtls_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)
{
size_t o_len = 0;
uint8_t iv[PTLS_MAX_IV_SIZE];
struct ptls_mbedtls_aead_context_t *ctx = (struct ptls_mbedtls_aead_context_t *)_ctx;
psa_status_t status;
/* set the nonce. */
ptls_aead__build_iv(ctx->super.algo, iv, ctx->mctx.static_iv, seq);
status = psa_aead_decrypt(ctx->mctx.key, ctx->mctx.alg, iv, ctx->super.algo->iv_size, (uint8_t *)aad, aadlen, (uint8_t *)input,
inlen, (uint8_t *)output, inlen, &o_len);
if (status != PSA_SUCCESS) {
o_len = inlen + 1;
}
return o_len;
}
static int ptls_mbedtls_aead_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key_bytes, const void *iv,
psa_algorithm_t psa_alg, size_t key_bits, psa_key_type_t key_type)
{
int ret = 0;
struct ptls_mbedtls_aead_context_t *ctx = (struct ptls_mbedtls_aead_context_t *)_ctx;
/* set mbed specific context to NULL, just to be sure */
memset(&ctx->mctx, 0, sizeof(struct ptls_mbedtls_aead_param_t));
ctx->mctx.alg = psa_alg;
/* Initialize the key attributes */
if (ret == 0) {
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_usage_flags(&attributes, (is_enc) ? PSA_KEY_USAGE_ENCRYPT : PSA_KEY_USAGE_DECRYPT);
psa_set_key_algorithm(&attributes, ctx->mctx.alg);
psa_set_key_type(&attributes, key_type);
psa_set_key_bits(&attributes, key_bits);
/* Import key */
if (psa_import_key(&attributes, key_bytes, key_bits / 8, &ctx->mctx.key) != PSA_SUCCESS) {
ret = PTLS_ERROR_LIBRARY;
}
}
if (ret == 0) {
/* Store the static IV */
if (ctx->super.algo->iv_size > PTLS_MAX_IV_SIZE) {
ret = PTLS_ERROR_LIBRARY;
} else {
memcpy(ctx->mctx.static_iv, iv, ctx->super.algo->iv_size);
ctx->mctx.is_op_in_progress = 0;
}
}
/* set the pointers to the individual functions */
if (ret == 0) {
if (is_enc) {
ctx->super.do_encrypt_init = ptls_mbedtls_aead_do_encrypt_init;
ctx->super.do_encrypt_update = ptls_mbedtls_aead_do_encrypt_update;
ctx->super.do_encrypt_final = ptls_mbedtls_aead_do_encrypt_final;
ctx->super.do_encrypt = ptls_mbedtls_aead_do_encrypt;
ctx->super.do_encrypt_v = ptls_mbedtls_aead_do_encrypt_v;
} else {
ctx->super.do_decrypt = ptls_mbedtls_aead_do_decrypt;
}
ctx->super.dispose_crypto = ptls_mbedtls_aead_dispose_crypto;
ctx->super.do_get_iv = ptls_mbedtls_aead_get_iv;
ctx->super.do_set_iv = ptls_mbedtls_aead_set_iv;
}
return ret;
}
static int ptls_mbedtls_aead_setup_aes128gcm(ptls_aead_context_t *_ctx, int is_enc, const void *key_bytes, const void *iv)
{
return ptls_mbedtls_aead_setup_crypto(_ctx, is_enc, key_bytes, iv, PSA_ALG_GCM, 128, PSA_KEY_TYPE_AES);
}
ptls_aead_algorithm_t ptls_mbedtls_aes128gcm = {"AES128-GCM",
PTLS_AESGCM_CONFIDENTIALITY_LIMIT,
PTLS_AESGCM_INTEGRITY_LIMIT,
&ptls_mbedtls_aes128ctr,
&ptls_mbedtls_aes128ecb,
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_mbedtls_aead_context_t),
ptls_mbedtls_aead_setup_aes128gcm};
ptls_cipher_suite_t ptls_mbedtls_aes128gcmsha256 = {.id = PTLS_CIPHER_SUITE_AES_128_GCM_SHA256,
.name = PTLS_CIPHER_SUITE_NAME_AES_128_GCM_SHA256,
.aead = &ptls_mbedtls_aes128gcm,
.hash = &ptls_mbedtls_sha256};
static int ptls_mbedtls_aead_setup_aes256gcm(ptls_aead_context_t *_ctx, int is_enc, const void *key_bytes, const void *iv)
{
return ptls_mbedtls_aead_setup_crypto(_ctx, is_enc, key_bytes, iv, PSA_ALG_GCM, 256, PSA_KEY_TYPE_AES);
}
ptls_aead_algorithm_t ptls_mbedtls_aes256gcm = {"AES256-GCM",
PTLS_AESGCM_CONFIDENTIALITY_LIMIT,
PTLS_AESGCM_INTEGRITY_LIMIT,
&ptls_mbedtls_aes256ctr,
&ptls_mbedtls_aes256ecb,
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_mbedtls_aead_context_t),
ptls_mbedtls_aead_setup_aes256gcm};
ptls_cipher_suite_t ptls_mbedtls_aes256gcmsha384 = {.id = PTLS_CIPHER_SUITE_AES_256_GCM_SHA384,
.name = PTLS_CIPHER_SUITE_NAME_AES_256_GCM_SHA384,
.aead = &ptls_mbedtls_aes256gcm,
.hash = &ptls_mbedtls_sha384};
static int ptls_mbedtls_aead_setup_chacha20poly1305(ptls_aead_context_t *_ctx, int is_enc, const void *key_bytes, const void *iv)
{
return ptls_mbedtls_aead_setup_crypto(_ctx, is_enc, key_bytes, iv, PSA_ALG_CHACHA20_POLY1305, 256, PSA_KEY_TYPE_CHACHA20);
}
ptls_aead_algorithm_t ptls_mbedtls_chacha20poly1305 = {"CHACHA20-POLY1305",
PTLS_CHACHA20POLY1305_CONFIDENTIALITY_LIMIT,
PTLS_CHACHA20POLY1305_INTEGRITY_LIMIT,
&ptls_mbedtls_chacha20,
NULL,
PTLS_CHACHA20_KEY_SIZE,
PTLS_CHACHA20POLY1305_IV_SIZE,
PTLS_CHACHA20POLY1305_TAG_SIZE,
{PTLS_TLS12_CHACHAPOLY_FIXED_IV_SIZE, PTLS_TLS12_CHACHAPOLY_RECORD_IV_SIZE},
0,
0,
sizeof(struct ptls_mbedtls_aead_context_t),
ptls_mbedtls_aead_setup_chacha20poly1305};
ptls_cipher_suite_t ptls_mbedtls_chacha20poly1305sha256 = {.id = PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256,
.name = PTLS_CIPHER_SUITE_NAME_CHACHA20_POLY1305_SHA256,
.aead = &ptls_mbedtls_chacha20poly1305,
.hash = &ptls_mbedtls_sha256};
/* Key exchange algorithms.
* The Picotls framework defines these algorithms as ptls_key_exchange_algorithm_t,
* a structure containing two function pointers:
*
* int (*create)(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **ctx);
* int (*exchange)(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_iovec_t *pubkey, ptls_iovec_t *secret,
* ptls_iovec_t peerkey);
* The "create" call is used on the client. It documents the ptls_key_exchange_context_t, which contains
* the public key prepared by the client, as an iovec, and a function pointer:
*
* int (*on_exchange)(struct st_ptls_key_exchange_context_t **keyex, int release, ptls_iovec_t *secret, ptls_iovec_t peerkey);
*
* The public key of the client is passed to the server an ends up as "peerkey" argument to the (exchange) function.
* That function documents the server's public key, and the secret computed by combining server and client key.
*
* When the client receives the server hello, the stack calls the "on_exchange" callback, passing the context
* previously created by the client and the public key of the peer, so the client can compute its own
* version of the secret.
*
* The following code uses the MbedTLS PSA API to create the "create", "exchange" and "on_exchange" functions.
*/
#define PTLS_MBEDTLS_ECDH_PUBKEY_MAX 129
struct ptls_mbedtls_key_exchange_context_t {
ptls_key_exchange_context_t super;
psa_algorithm_t psa_alg;
psa_ecc_family_t curve;
size_t curve_bits;
size_t secret_size;
psa_key_id_t private_key;
uint8_t pub[PTLS_MBEDTLS_ECDH_PUBKEY_MAX];
};
/* Set a private key for key exchange. For now, we only support ECC
*/
static int ptls_mbedtls_key_exchange_set_private_key(psa_key_id_t *private_key, psa_algorithm_t psa_alg, psa_ecc_family_t curve,
size_t curve_bits)
{
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
int ret = 0;
psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE);
psa_set_key_algorithm(&attributes, psa_alg);
psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(curve));
psa_set_key_bits(&attributes, curve_bits);
if (psa_generate_key(&attributes, private_key) != 0) {
ret = -1;
}
return ret;
}
/*
* The key agreement is done by calling psa_raw_key_agreement
psa_status_t psa_raw_key_agreement(psa_algorithm_t alg,
psa_key_id_t private_key,
const uint8_t * peer_key,
size_t peer_key_length,
uint8_t * output,
size_t output_size,
size_t * output_length);
*/
int ptls_mbedtls_key_exchange_on_exchange(struct st_ptls_key_exchange_context_t **_pctx, int release, ptls_iovec_t *secret,
ptls_iovec_t peerkey)
{
int ret = 0;
struct ptls_mbedtls_key_exchange_context_t *keyex = (struct ptls_mbedtls_key_exchange_context_t *)*_pctx;
if (secret != NULL) {
uint8_t *secbytes = (uint8_t *)malloc(keyex->secret_size);
if (secbytes == NULL) {
ret = PTLS_ERROR_NO_MEMORY;
} else {
size_t olen;
if (psa_raw_key_agreement(keyex->psa_alg, keyex->private_key, (const uint8_t *)peerkey.base, peerkey.len, secbytes,
keyex->secret_size, &olen) == 0) {
*secret = ptls_iovec_init(secbytes, keyex->secret_size);
} else {
free(secbytes);
ret = PTLS_ERROR_LIBRARY;
}
}
}
if (release) {
/* Clear the private key */
psa_destroy_key(keyex->private_key);
/* Set context to NULL */
*_pctx = NULL;
/* TODO: check whether allocated memory should be freed */
}
return ret;
}
int ptls_mbedtls_key_exchange_create(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **ctx,
psa_algorithm_t psa_alg, psa_ecc_family_t curve, size_t curve_bits, size_t secret_size)
{
struct ptls_mbedtls_key_exchange_context_t *keyex;
size_t olen = 0;
if ((keyex = (struct ptls_mbedtls_key_exchange_context_t *)malloc(sizeof(struct ptls_mbedtls_key_exchange_context_t))) == NULL)
return PTLS_ERROR_NO_MEMORY;
/* Initialize the exchange context based on the algorithm definition */
keyex->psa_alg = psa_alg;
keyex->curve = curve;
keyex->curve_bits = curve_bits;
keyex->secret_size = secret_size;
/* Initialize the private key and format the public key */
if (ptls_mbedtls_key_exchange_set_private_key(&keyex->private_key, psa_alg, curve, curve_bits) != 0) {
free(keyex);
*ctx = NULL;
return PTLS_ERROR_LIBRARY;
}
/* According to the doc, format of public key is same as picotls */
if (psa_export_public_key(keyex->private_key, keyex->pub, sizeof(keyex->pub), &olen) != 0) {
psa_destroy_key(keyex->private_key);
free(keyex);
*ctx = NULL;
return PTLS_ERROR_LIBRARY;
}
keyex->super.pubkey = ptls_iovec_init(keyex->pub, olen);
/* Initialize the ptls exchange context */
keyex->super.algo = algo;
keyex->super.on_exchange = ptls_mbedtls_key_exchange_on_exchange;
*ctx = (ptls_key_exchange_context_t *)keyex;
return 0;
}
static int ptls_mbedtls_key_exchange_exchange(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_iovec_t *pubkey,
ptls_iovec_t *secret, ptls_iovec_t peerkey, psa_algorithm_t psa_alg,
psa_ecc_family_t curve, size_t curve_bits, size_t secret_size)
{
/* generate a local private key for the selected algorithm */
psa_key_id_t private_key;
size_t pubkey_len;
uint8_t *pubkey_bytes = NULL;
size_t secret_len;
uint8_t *secret_bytes = (uint8_t *)malloc(secret_size);
int ret = 0;
if (secret_bytes == NULL) {
return PTLS_ERROR_NO_MEMORY;
}
pubkey_bytes = (uint8_t *)malloc(PTLS_MBEDTLS_ECDH_PUBKEY_MAX);
if (pubkey_bytes == NULL) {
free(secret_bytes);
return PTLS_ERROR_NO_MEMORY;
}
if (ptls_mbedtls_key_exchange_set_private_key(&private_key, psa_alg, curve, curve_bits) != 0) {
free(secret_bytes);
free(pubkey_bytes);
return PTLS_ERROR_LIBRARY;
}
/* Export public key and call key agrement function */
if (psa_export_public_key(private_key, pubkey_bytes, PTLS_MBEDTLS_ECDH_PUBKEY_MAX, &pubkey_len) == 0 &&
psa_raw_key_agreement(psa_alg, private_key, (const uint8_t *)peerkey.base, peerkey.len, secret_bytes, secret_size,
&secret_len) == 0) {
*secret = ptls_iovec_init(secret_bytes, secret_len);
*pubkey = ptls_iovec_init(pubkey_bytes, pubkey_len);
} else {
free(secret_bytes);
free(pubkey_bytes);
return PTLS_ERROR_LIBRARY;
}
psa_destroy_key(private_key);
return ret;
}
/* Instantiation of the generic key exchange API with secp256r1
*/
static int ptls_mbedtls_secp256r1_create(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **ctx)
{
return ptls_mbedtls_key_exchange_create(algo, ctx, PSA_ALG_ECDH, PSA_ECC_FAMILY_SECP_R1, 256, 32);
}
static int ptls_mbedtls_secp256r1_exchange(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_iovec_t *pubkey,
ptls_iovec_t *secret, ptls_iovec_t peerkey)
{
return ptls_mbedtls_key_exchange_exchange(algo, pubkey, secret, peerkey, PSA_ALG_ECDH, PSA_ECC_FAMILY_SECP_R1, 256, 32);
}
ptls_key_exchange_algorithm_t ptls_mbedtls_secp256r1 = {.id = PTLS_GROUP_SECP256R1,
.name = PTLS_GROUP_NAME_SECP256R1,
.create = ptls_mbedtls_secp256r1_create,
.exchange = ptls_mbedtls_secp256r1_exchange};
/* Instantiation of the generic key exchange API with x25519
*/
static int ptls_mbedtls_x25519_create(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **ctx)
{
return ptls_mbedtls_key_exchange_create(algo, ctx, PSA_ALG_ECDH, PSA_ECC_FAMILY_MONTGOMERY, 255, 32);
}
static int ptls_mbedtls_x25519_exchange(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_iovec_t *pubkey,
ptls_iovec_t *secret, ptls_iovec_t peerkey)
{
return ptls_mbedtls_key_exchange_exchange(algo, pubkey, secret, peerkey, PSA_ALG_ECDH, PSA_ECC_FAMILY_MONTGOMERY, 255, 32);
}
ptls_key_exchange_algorithm_t ptls_mbedtls_x25519 = {.id = PTLS_GROUP_X25519,
.name = PTLS_GROUP_NAME_X25519,
.create = ptls_mbedtls_x25519_create,
.exchange = ptls_mbedtls_x25519_exchange};