mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
957c61cbbf
This change upgrades to GCC 12.3 and GNU binutils 2.42. The GNU linker appears to have changed things so that only a single de-duplicated str table is present in the binary, and it gets placed wherever the linker wants, regardless of what the linker script says. To cope with that we need to stop using .ident to embed licenses. As such, this change does significant work to revamp how third party licenses are defined in the codebase, using `.section .notice,"aR",@progbits`. This new GCC 12.3 toolchain has support for GNU indirect functions. It lets us support __target_clones__ for the first time. This is used for optimizing the performance of libc string functions such as strlen and friends so far on x86, by ensuring AVX systems favor a second codepath that uses VEX encoding. It shaves some latency off certain operations. It's a useful feature to have for scientific computing for the reasons explained by the test/libcxx/openmp_test.cc example which compiles for fifteen different microarchitectures. Thanks to the upgrades, it's now also possible to use newer instruction sets, such as AVX512FP16, VNNI. Cosmo now uses the %gs register on x86 by default for TLS. Doing it is helpful for any program that links `cosmo_dlopen()`. Such programs had to recompile their binaries at startup to change the TLS instructions. That's not great, since it means every page in the executable needs to be faulted. The work of rewriting TLS-related x86 opcodes, is moved to fixupobj.com instead. This is great news for MacOS x86 users, since we previously needed to morph the binary every time for that platform but now that's no longer necessary. The only platforms where we need fixup of TLS x86 opcodes at runtime are now Windows, OpenBSD, and NetBSD. On Windows we morph TLS to point deeper into the TIB, based on a TlsAlloc assignment, and on OpenBSD/NetBSD we morph %gs back into %fs since the kernels do not allow us to specify a value for the %gs register. OpenBSD users are now required to use APE Loader to run Cosmo binaries and assimilation is no longer possible. OpenBSD kernel needs to change to allow programs to specify a value for the %gs register, or it needs to stop marking executable pages loaded by the kernel as mimmutable(). This release fixes __constructor__, .ctor, .init_array, and lastly the .preinit_array so they behave the exact same way as glibc. We no longer use hex constants to define math.h symbols like M_PI.
1559 lines
51 KiB
C
1559 lines
51 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
|
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright The Mbed TLS Contributors │
|
|
│ │
|
|
│ Licensed under the Apache License, Version 2.0 (the "License"); │
|
|
│ you may not use this file except in compliance with the License. │
|
|
│ You may obtain a copy of the License at │
|
|
│ │
|
|
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
|
│ │
|
|
│ Unless required by applicable law or agreed to in writing, software │
|
|
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
|
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
|
│ See the License for the specific language governing permissions and │
|
|
│ limitations under the License. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/str/str.h"
|
|
#include "third_party/mbedtls/ccm.h"
|
|
#include "third_party/mbedtls/chacha20.h"
|
|
#include "third_party/mbedtls/chachapoly.h"
|
|
#include "third_party/mbedtls/cipher.h"
|
|
#include "third_party/mbedtls/cipher_internal.h"
|
|
#include "third_party/mbedtls/common.h"
|
|
#include "third_party/mbedtls/error.h"
|
|
#include "third_party/mbedtls/gcm.h"
|
|
#include "third_party/mbedtls/nist_kw.h"
|
|
#include "third_party/mbedtls/platform.h"
|
|
__static_yoink("mbedtls_notice");
|
|
|
|
#if defined(MBEDTLS_CIPHER_C)
|
|
|
|
#define CIPHER_VALIDATE_RET( cond ) \
|
|
MBEDTLS_INTERNAL_VALIDATE_RET( cond, MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA )
|
|
#define CIPHER_VALIDATE( cond ) \
|
|
MBEDTLS_INTERNAL_VALIDATE( cond )
|
|
|
|
static int supported_init = 0;
|
|
|
|
const int *mbedtls_cipher_list( void )
|
|
{
|
|
const mbedtls_cipher_definition_t *def;
|
|
int *type;
|
|
|
|
if( ! supported_init )
|
|
{
|
|
def = mbedtls_cipher_definitions;
|
|
type = mbedtls_cipher_supported;
|
|
|
|
while( def->type != 0 )
|
|
*type++ = (*def++).type;
|
|
|
|
*type = 0;
|
|
|
|
supported_init = 1;
|
|
}
|
|
|
|
return( mbedtls_cipher_supported );
|
|
}
|
|
|
|
const mbedtls_cipher_info_t *mbedtls_cipher_info_from_type(
|
|
const mbedtls_cipher_type_t cipher_type )
|
|
{
|
|
const mbedtls_cipher_definition_t *def;
|
|
|
|
for( def = mbedtls_cipher_definitions; def->info != NULL; def++ )
|
|
if( def->type == cipher_type )
|
|
return( def->info );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
const mbedtls_cipher_info_t *mbedtls_cipher_info_from_string(
|
|
const char *cipher_name )
|
|
{
|
|
const mbedtls_cipher_definition_t *def;
|
|
|
|
if( NULL == cipher_name )
|
|
return( NULL );
|
|
|
|
for( def = mbedtls_cipher_definitions; def->info != NULL; def++ )
|
|
if( ! strcmp( def->info->name, cipher_name ) )
|
|
return( def->info );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
const mbedtls_cipher_info_t *mbedtls_cipher_info_from_values(
|
|
const mbedtls_cipher_id_t cipher_id,
|
|
int key_bitlen,
|
|
const mbedtls_cipher_mode_t mode )
|
|
{
|
|
const mbedtls_cipher_definition_t *def;
|
|
|
|
for( def = mbedtls_cipher_definitions; def->info != NULL; def++ )
|
|
if( def->info->base->cipher == cipher_id &&
|
|
def->info->key_bitlen == (unsigned) key_bitlen &&
|
|
def->info->mode == mode )
|
|
return( def->info );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
void mbedtls_cipher_init( mbedtls_cipher_context_t *ctx )
|
|
{
|
|
CIPHER_VALIDATE( ctx != NULL );
|
|
mbedtls_platform_zeroize( ctx, sizeof( mbedtls_cipher_context_t ) );
|
|
}
|
|
|
|
void mbedtls_cipher_free( mbedtls_cipher_context_t *ctx )
|
|
{
|
|
if( ctx == NULL )
|
|
return;
|
|
|
|
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
|
if( ctx->psa_enabled == 1 )
|
|
{
|
|
if( ctx->cipher_ctx != NULL )
|
|
{
|
|
mbedtls_cipher_context_psa * const cipher_psa =
|
|
(mbedtls_cipher_context_psa *) ctx->cipher_ctx;
|
|
|
|
if( cipher_psa->slot_state == MBEDTLS_CIPHER_PSA_KEY_OWNED )
|
|
{
|
|
/* xxx_free() doesn't allow to return failures. */
|
|
(void) psa_destroy_key( cipher_psa->slot );
|
|
}
|
|
|
|
mbedtls_platform_zeroize( cipher_psa, sizeof( *cipher_psa ) );
|
|
mbedtls_free( cipher_psa );
|
|
}
|
|
|
|
mbedtls_platform_zeroize( ctx, sizeof(mbedtls_cipher_context_t) );
|
|
return;
|
|
}
|
|
#endif /* MBEDTLS_USE_PSA_CRYPTO */
|
|
|
|
#if defined(MBEDTLS_CMAC_C)
|
|
if( ctx->cmac_ctx )
|
|
{
|
|
mbedtls_platform_zeroize( ctx->cmac_ctx,
|
|
sizeof( mbedtls_cmac_context_t ) );
|
|
mbedtls_free( ctx->cmac_ctx );
|
|
}
|
|
#endif
|
|
|
|
if( ctx->cipher_ctx )
|
|
ctx->cipher_info->base->ctx_free_func( ctx->cipher_ctx );
|
|
|
|
mbedtls_platform_zeroize( ctx, sizeof(mbedtls_cipher_context_t) );
|
|
}
|
|
|
|
int mbedtls_cipher_setup( mbedtls_cipher_context_t *ctx,
|
|
const mbedtls_cipher_info_t *cipher_info )
|
|
{
|
|
CIPHER_VALIDATE_RET( ctx != NULL );
|
|
if( cipher_info == NULL )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
mbedtls_platform_zeroize( ctx, sizeof( mbedtls_cipher_context_t ) );
|
|
|
|
if( NULL == ( ctx->cipher_ctx = cipher_info->base->ctx_alloc_func() ) )
|
|
return( MBEDTLS_ERR_CIPHER_ALLOC_FAILED );
|
|
|
|
ctx->cipher_info = cipher_info;
|
|
|
|
#if defined(MBEDTLS_CIPHER_MODE_WITH_PADDING)
|
|
/*
|
|
* Ignore possible errors caused by a cipher mode that doesn't use padding
|
|
*/
|
|
#if defined(MBEDTLS_CIPHER_PADDING_PKCS7)
|
|
(void) mbedtls_cipher_set_padding_mode( ctx, MBEDTLS_PADDING_PKCS7 );
|
|
#else
|
|
(void) mbedtls_cipher_set_padding_mode( ctx, MBEDTLS_PADDING_NONE );
|
|
#endif
|
|
#endif /* MBEDTLS_CIPHER_MODE_WITH_PADDING */
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
|
int mbedtls_cipher_setup_psa( mbedtls_cipher_context_t *ctx,
|
|
const mbedtls_cipher_info_t *cipher_info,
|
|
size_t taglen )
|
|
{
|
|
psa_algorithm_t alg;
|
|
mbedtls_cipher_context_psa *cipher_psa;
|
|
|
|
if( NULL == cipher_info || NULL == ctx )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
/* Check that the underlying cipher mode and cipher type are
|
|
* supported by the underlying PSA Crypto implementation. */
|
|
alg = mbedtls_psa_translate_cipher_mode( cipher_info->mode, taglen );
|
|
if( alg == 0 )
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
if( mbedtls_psa_translate_cipher_type( cipher_info->type ) == 0 )
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
|
|
mbedtls_platform_zeroize( ctx, sizeof( mbedtls_cipher_context_t ) );
|
|
|
|
cipher_psa = mbedtls_calloc( 1, sizeof(mbedtls_cipher_context_psa ) );
|
|
if( cipher_psa == NULL )
|
|
return( MBEDTLS_ERR_CIPHER_ALLOC_FAILED );
|
|
cipher_psa->alg = alg;
|
|
ctx->cipher_ctx = cipher_psa;
|
|
ctx->cipher_info = cipher_info;
|
|
ctx->psa_enabled = 1;
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_USE_PSA_CRYPTO */
|
|
|
|
int mbedtls_cipher_setkey( mbedtls_cipher_context_t *ctx,
|
|
const unsigned char *key,
|
|
int key_bitlen,
|
|
const mbedtls_operation_t operation )
|
|
{
|
|
CIPHER_VALIDATE_RET( ctx != NULL );
|
|
CIPHER_VALIDATE_RET( key != NULL );
|
|
CIPHER_VALIDATE_RET( operation == MBEDTLS_ENCRYPT ||
|
|
operation == MBEDTLS_DECRYPT );
|
|
if( ctx->cipher_info == NULL )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
|
if( ctx->psa_enabled == 1 )
|
|
{
|
|
mbedtls_cipher_context_psa * const cipher_psa =
|
|
(mbedtls_cipher_context_psa *) ctx->cipher_ctx;
|
|
|
|
size_t const key_bytelen = ( (size_t) key_bitlen + 7 ) / 8;
|
|
|
|
psa_status_t status;
|
|
psa_key_type_t key_type;
|
|
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
|
|
|
|
/* PSA Crypto API only accepts byte-aligned keys. */
|
|
if( key_bitlen % 8 != 0 )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
/* Don't allow keys to be set multiple times. */
|
|
if( cipher_psa->slot_state != MBEDTLS_CIPHER_PSA_KEY_UNSET )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
key_type = mbedtls_psa_translate_cipher_type(
|
|
ctx->cipher_info->type );
|
|
if( key_type == 0 )
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
psa_set_key_type( &attributes, key_type );
|
|
|
|
/* Mbed TLS' cipher layer doesn't enforce the mode of operation
|
|
* (encrypt vs. decrypt): it is possible to setup a key for encryption
|
|
* and use it for AEAD decryption. Until tests relying on this
|
|
* are changed, allow any usage in PSA. */
|
|
psa_set_key_usage_flags( &attributes,
|
|
/* mbedtls_psa_translate_cipher_operation( operation ); */
|
|
PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT );
|
|
psa_set_key_algorithm( &attributes, cipher_psa->alg );
|
|
|
|
status = psa_import_key( &attributes, key, key_bytelen,
|
|
&cipher_psa->slot );
|
|
switch( status )
|
|
{
|
|
case PSA_SUCCESS:
|
|
break;
|
|
case PSA_ERROR_INSUFFICIENT_MEMORY:
|
|
return( MBEDTLS_ERR_CIPHER_ALLOC_FAILED );
|
|
case PSA_ERROR_NOT_SUPPORTED:
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
default:
|
|
return( MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED );
|
|
}
|
|
/* Indicate that we own the key slot and need to
|
|
* destroy it in mbedtls_cipher_free(). */
|
|
cipher_psa->slot_state = MBEDTLS_CIPHER_PSA_KEY_OWNED;
|
|
|
|
ctx->key_bitlen = key_bitlen;
|
|
ctx->operation = operation;
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_USE_PSA_CRYPTO */
|
|
|
|
if( ( ctx->cipher_info->flags & MBEDTLS_CIPHER_VARIABLE_KEY_LEN ) == 0 &&
|
|
(int) ctx->cipher_info->key_bitlen != key_bitlen )
|
|
{
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
}
|
|
|
|
ctx->key_bitlen = key_bitlen;
|
|
ctx->operation = operation;
|
|
|
|
/*
|
|
* For OFB, CFB and CTR mode always use the encryption key schedule
|
|
*/
|
|
if( MBEDTLS_ENCRYPT == operation ||
|
|
MBEDTLS_MODE_CFB == ctx->cipher_info->mode ||
|
|
MBEDTLS_MODE_OFB == ctx->cipher_info->mode ||
|
|
MBEDTLS_MODE_CTR == ctx->cipher_info->mode )
|
|
{
|
|
return( ctx->cipher_info->base->setkey_enc_func( ctx->cipher_ctx, key,
|
|
ctx->key_bitlen ) );
|
|
}
|
|
|
|
if( MBEDTLS_DECRYPT == operation )
|
|
return( ctx->cipher_info->base->setkey_dec_func( ctx->cipher_ctx, key,
|
|
ctx->key_bitlen ) );
|
|
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
}
|
|
|
|
int mbedtls_cipher_set_iv( mbedtls_cipher_context_t *ctx,
|
|
const unsigned char *iv,
|
|
size_t iv_len )
|
|
{
|
|
size_t actual_iv_size;
|
|
|
|
CIPHER_VALIDATE_RET( ctx != NULL );
|
|
CIPHER_VALIDATE_RET( iv_len == 0 || iv != NULL );
|
|
if( ctx->cipher_info == NULL )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
|
if( ctx->psa_enabled == 1 )
|
|
{
|
|
/* While PSA Crypto has an API for multipart
|
|
* operations, we currently don't make it
|
|
* accessible through the cipher layer. */
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
}
|
|
#endif /* MBEDTLS_USE_PSA_CRYPTO */
|
|
|
|
/* avoid buffer overflow in ctx->iv */
|
|
if( iv_len > MBEDTLS_MAX_IV_LENGTH )
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
|
|
if( ( ctx->cipher_info->flags & MBEDTLS_CIPHER_VARIABLE_IV_LEN ) != 0 )
|
|
actual_iv_size = iv_len;
|
|
else
|
|
{
|
|
actual_iv_size = ctx->cipher_info->iv_size;
|
|
|
|
/* avoid reading past the end of input buffer */
|
|
if( actual_iv_size > iv_len )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
}
|
|
|
|
#if defined(MBEDTLS_CHACHA20_C)
|
|
if ( ctx->cipher_info->type == MBEDTLS_CIPHER_CHACHA20 )
|
|
{
|
|
if ( 0 != mbedtls_chacha20_starts( (mbedtls_chacha20_context*)ctx->cipher_ctx,
|
|
iv,
|
|
0U ) ) /* Initial counter value */
|
|
{
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ( actual_iv_size != 0 )
|
|
{
|
|
memcpy( ctx->iv, iv, actual_iv_size );
|
|
ctx->iv_size = actual_iv_size;
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
int mbedtls_cipher_reset( mbedtls_cipher_context_t *ctx )
|
|
{
|
|
CIPHER_VALIDATE_RET( ctx != NULL );
|
|
if( ctx->cipher_info == NULL )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
|
if( ctx->psa_enabled == 1 )
|
|
{
|
|
/* We don't support resetting PSA-based
|
|
* cipher contexts, yet. */
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
}
|
|
#endif /* MBEDTLS_USE_PSA_CRYPTO */
|
|
|
|
ctx->unprocessed_len = 0;
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C)
|
|
int mbedtls_cipher_update_ad( mbedtls_cipher_context_t *ctx,
|
|
const unsigned char *ad, size_t ad_len )
|
|
{
|
|
CIPHER_VALIDATE_RET( ctx != NULL );
|
|
CIPHER_VALIDATE_RET( ad_len == 0 || ad != NULL );
|
|
if( ctx->cipher_info == NULL )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
|
if( ctx->psa_enabled == 1 )
|
|
{
|
|
/* While PSA Crypto has an API for multipart
|
|
* operations, we currently don't make it
|
|
* accessible through the cipher layer. */
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
}
|
|
#endif /* MBEDTLS_USE_PSA_CRYPTO */
|
|
|
|
#if defined(MBEDTLS_GCM_C)
|
|
if( MBEDTLS_MODE_GCM == ctx->cipher_info->mode )
|
|
{
|
|
return( mbedtls_gcm_starts( (mbedtls_gcm_context *) ctx->cipher_ctx, ctx->operation,
|
|
ctx->iv, ctx->iv_size, ad, ad_len ) );
|
|
}
|
|
#endif
|
|
|
|
#if defined(MBEDTLS_CHACHAPOLY_C)
|
|
if (MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type )
|
|
{
|
|
int result;
|
|
mbedtls_chachapoly_mode_t mode;
|
|
|
|
mode = ( ctx->operation == MBEDTLS_ENCRYPT )
|
|
? MBEDTLS_CHACHAPOLY_ENCRYPT
|
|
: MBEDTLS_CHACHAPOLY_DECRYPT;
|
|
|
|
result = mbedtls_chachapoly_starts( (mbedtls_chachapoly_context*) ctx->cipher_ctx,
|
|
ctx->iv,
|
|
mode );
|
|
if ( result != 0 )
|
|
return( result );
|
|
|
|
return( mbedtls_chachapoly_update_aad( (mbedtls_chachapoly_context*) ctx->cipher_ctx,
|
|
ad, ad_len ) );
|
|
}
|
|
#endif
|
|
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */
|
|
|
|
int mbedtls_cipher_update( mbedtls_cipher_context_t *ctx, const unsigned char *input,
|
|
size_t ilen, unsigned char *output, size_t *olen )
|
|
{
|
|
int ret = MBEDTLS_ERR_THIS_CORRUPTION;
|
|
size_t block_size;
|
|
|
|
CIPHER_VALIDATE_RET( ctx != NULL );
|
|
CIPHER_VALIDATE_RET( ilen == 0 || input != NULL );
|
|
CIPHER_VALIDATE_RET( output != NULL );
|
|
CIPHER_VALIDATE_RET( olen != NULL );
|
|
if( ctx->cipher_info == NULL )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
*olen = 0;
|
|
block_size = mbedtls_cipher_get_block_size( ctx );
|
|
if ( 0 == block_size )
|
|
{
|
|
return( MBEDTLS_ERR_CIPHER_INVALID_CONTEXT );
|
|
}
|
|
|
|
if( ctx->cipher_info->mode == MBEDTLS_MODE_ECB )
|
|
{
|
|
if( ilen != block_size )
|
|
return( MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED );
|
|
|
|
*olen = ilen;
|
|
|
|
if( ( ret = ctx->cipher_info->base->ecb_func( ctx->cipher_ctx,
|
|
ctx->operation,
|
|
input, output ) ) )
|
|
{
|
|
return( ret );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
#if defined(MBEDTLS_GCM_C)
|
|
if( ctx->cipher_info->mode == MBEDTLS_MODE_GCM )
|
|
{
|
|
*olen = ilen;
|
|
return( mbedtls_gcm_update( (mbedtls_gcm_context *) ctx->cipher_ctx, ilen, input,
|
|
output ) );
|
|
}
|
|
#endif
|
|
|
|
#if defined(MBEDTLS_CHACHAPOLY_C)
|
|
if ( ctx->cipher_info->type == MBEDTLS_CIPHER_CHACHA20_POLY1305 )
|
|
{
|
|
*olen = ilen;
|
|
return( mbedtls_chachapoly_update( (mbedtls_chachapoly_context*) ctx->cipher_ctx,
|
|
ilen, input, output ) );
|
|
}
|
|
#endif
|
|
|
|
if( input == output &&
|
|
( ctx->unprocessed_len != 0 || ilen % block_size ) )
|
|
{
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
}
|
|
|
|
#if defined(MBEDTLS_CIPHER_MODE_CBC)
|
|
if( ctx->cipher_info->mode == MBEDTLS_MODE_CBC )
|
|
{
|
|
size_t copy_len = 0;
|
|
|
|
/*
|
|
* If there is not enough data for a full block, cache it.
|
|
*/
|
|
if( ( ctx->operation == MBEDTLS_DECRYPT && NULL != ctx->add_padding &&
|
|
ilen <= block_size - ctx->unprocessed_len ) ||
|
|
( ctx->operation == MBEDTLS_DECRYPT && NULL == ctx->add_padding &&
|
|
ilen < block_size - ctx->unprocessed_len ) ||
|
|
( ctx->operation == MBEDTLS_ENCRYPT &&
|
|
ilen < block_size - ctx->unprocessed_len ) )
|
|
{
|
|
memcpy( &( ctx->unprocessed_data[ctx->unprocessed_len] ), input,
|
|
ilen );
|
|
|
|
ctx->unprocessed_len += ilen;
|
|
return( 0 );
|
|
}
|
|
|
|
/*
|
|
* Process cached data first
|
|
*/
|
|
if( 0 != ctx->unprocessed_len )
|
|
{
|
|
copy_len = block_size - ctx->unprocessed_len;
|
|
|
|
memcpy( &( ctx->unprocessed_data[ctx->unprocessed_len] ), input,
|
|
copy_len );
|
|
|
|
if( 0 != ( ret = ctx->cipher_info->base->cbc_func( ctx->cipher_ctx,
|
|
ctx->operation, block_size, ctx->iv,
|
|
ctx->unprocessed_data, output ) ) )
|
|
{
|
|
return( ret );
|
|
}
|
|
|
|
*olen += block_size;
|
|
output += block_size;
|
|
ctx->unprocessed_len = 0;
|
|
|
|
input += copy_len;
|
|
ilen -= copy_len;
|
|
}
|
|
|
|
/*
|
|
* Cache final, incomplete block
|
|
*/
|
|
if( 0 != ilen )
|
|
{
|
|
/* Encryption: only cache partial blocks
|
|
* Decryption w/ padding: always keep at least one whole block
|
|
* Decryption w/o padding: only cache partial blocks
|
|
*/
|
|
copy_len = ilen % block_size;
|
|
if( copy_len == 0 &&
|
|
ctx->operation == MBEDTLS_DECRYPT &&
|
|
NULL != ctx->add_padding)
|
|
{
|
|
copy_len = block_size;
|
|
}
|
|
|
|
memcpy( ctx->unprocessed_data, &( input[ilen - copy_len] ),
|
|
copy_len );
|
|
|
|
ctx->unprocessed_len += copy_len;
|
|
ilen -= copy_len;
|
|
}
|
|
|
|
/*
|
|
* Process remaining full blocks
|
|
*/
|
|
if( ilen )
|
|
{
|
|
if( 0 != ( ret = ctx->cipher_info->base->cbc_func( ctx->cipher_ctx,
|
|
ctx->operation, ilen, ctx->iv, input, output ) ) )
|
|
{
|
|
return( ret );
|
|
}
|
|
|
|
*olen += ilen;
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_CIPHER_MODE_CBC */
|
|
|
|
#if defined(MBEDTLS_CIPHER_MODE_CFB)
|
|
if( ctx->cipher_info->mode == MBEDTLS_MODE_CFB )
|
|
{
|
|
if( 0 != ( ret = ctx->cipher_info->base->cfb_func( ctx->cipher_ctx,
|
|
ctx->operation, ilen, &ctx->unprocessed_len, ctx->iv,
|
|
input, output ) ) )
|
|
{
|
|
return( ret );
|
|
}
|
|
|
|
*olen = ilen;
|
|
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_CIPHER_MODE_CFB */
|
|
|
|
#if defined(MBEDTLS_CIPHER_MODE_OFB)
|
|
if( ctx->cipher_info->mode == MBEDTLS_MODE_OFB )
|
|
{
|
|
if( 0 != ( ret = ctx->cipher_info->base->ofb_func( ctx->cipher_ctx,
|
|
ilen, &ctx->unprocessed_len, ctx->iv, input, output ) ) )
|
|
{
|
|
return( ret );
|
|
}
|
|
|
|
*olen = ilen;
|
|
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_CIPHER_MODE_OFB */
|
|
|
|
#if defined(MBEDTLS_CIPHER_MODE_CTR)
|
|
if( ctx->cipher_info->mode == MBEDTLS_MODE_CTR )
|
|
{
|
|
if( 0 != ( ret = ctx->cipher_info->base->ctr_func( ctx->cipher_ctx,
|
|
ilen, &ctx->unprocessed_len, ctx->iv,
|
|
ctx->unprocessed_data, input, output ) ) )
|
|
{
|
|
return( ret );
|
|
}
|
|
|
|
*olen = ilen;
|
|
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_CIPHER_MODE_CTR */
|
|
|
|
#if defined(MBEDTLS_CIPHER_MODE_XTS)
|
|
if( ctx->cipher_info->mode == MBEDTLS_MODE_XTS )
|
|
{
|
|
if( ctx->unprocessed_len > 0 ) {
|
|
/* We can only process an entire data unit at a time. */
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
}
|
|
|
|
ret = ctx->cipher_info->base->xts_func( ctx->cipher_ctx,
|
|
ctx->operation, ilen, ctx->iv, input, output );
|
|
if( ret != 0 )
|
|
{
|
|
return( ret );
|
|
}
|
|
|
|
*olen = ilen;
|
|
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_CIPHER_MODE_XTS */
|
|
|
|
#if defined(MBEDTLS_CIPHER_MODE_STREAM)
|
|
if( ctx->cipher_info->mode == MBEDTLS_MODE_STREAM )
|
|
{
|
|
if( 0 != ( ret = ctx->cipher_info->base->stream_func( ctx->cipher_ctx,
|
|
ilen, input, output ) ) )
|
|
{
|
|
return( ret );
|
|
}
|
|
|
|
*olen = ilen;
|
|
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_CIPHER_MODE_STREAM */
|
|
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
}
|
|
|
|
#if defined(MBEDTLS_CIPHER_MODE_WITH_PADDING)
|
|
#if defined(MBEDTLS_CIPHER_PADDING_PKCS7)
|
|
/*
|
|
* PKCS7 (and PKCS5) padding: fill with ll bytes, with ll = padding_len
|
|
*/
|
|
static void add_pkcs_padding( unsigned char *output, size_t output_len,
|
|
size_t data_len )
|
|
{
|
|
size_t padding_len = output_len - data_len;
|
|
unsigned char i;
|
|
|
|
for( i = 0; i < padding_len; i++ )
|
|
output[data_len + i] = (unsigned char) padding_len;
|
|
}
|
|
|
|
static int get_pkcs_padding( unsigned char *input, size_t input_len,
|
|
size_t *data_len )
|
|
{
|
|
size_t i, pad_idx;
|
|
unsigned char padding_len, bad = 0;
|
|
|
|
if( NULL == input || NULL == data_len )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
padding_len = input[input_len - 1];
|
|
*data_len = input_len - padding_len;
|
|
|
|
/* Avoid logical || since it results in a branch */
|
|
bad |= padding_len > input_len;
|
|
bad |= padding_len == 0;
|
|
|
|
/* The number of bytes checked must be independent of padding_len,
|
|
* so pick input_len, which is usually 8 or 16 (one block) */
|
|
pad_idx = input_len - padding_len;
|
|
for( i = 0; i < input_len; i++ )
|
|
bad |= ( input[i] ^ padding_len ) * ( i >= pad_idx );
|
|
|
|
return( MBEDTLS_ERR_CIPHER_INVALID_PADDING * ( bad != 0 ) );
|
|
}
|
|
#endif /* MBEDTLS_CIPHER_PADDING_PKCS7 */
|
|
|
|
#if defined(MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS)
|
|
/*
|
|
* One and zeros padding: fill with 80 00 ... 00
|
|
*/
|
|
static void add_one_and_zeros_padding( unsigned char *output,
|
|
size_t output_len, size_t data_len )
|
|
{
|
|
size_t padding_len = output_len - data_len;
|
|
unsigned char i = 0;
|
|
|
|
output[data_len] = 0x80;
|
|
for( i = 1; i < padding_len; i++ )
|
|
output[data_len + i] = 0x00;
|
|
}
|
|
|
|
static int get_one_and_zeros_padding( unsigned char *input, size_t input_len,
|
|
size_t *data_len )
|
|
{
|
|
size_t i;
|
|
unsigned char done = 0, prev_done, bad;
|
|
|
|
if( NULL == input || NULL == data_len )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
bad = 0x80;
|
|
*data_len = 0;
|
|
for( i = input_len; i > 0; i-- )
|
|
{
|
|
prev_done = done;
|
|
done |= ( input[i - 1] != 0 );
|
|
*data_len |= ( i - 1 ) * ( done != prev_done );
|
|
bad ^= input[i - 1] * ( done != prev_done );
|
|
}
|
|
|
|
return( MBEDTLS_ERR_CIPHER_INVALID_PADDING * ( bad != 0 ) );
|
|
|
|
}
|
|
#endif /* MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS */
|
|
|
|
#if defined(MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN)
|
|
/*
|
|
* Zeros and len padding: fill with 00 ... 00 ll, where ll is padding length
|
|
*/
|
|
static void add_zeros_and_len_padding( unsigned char *output,
|
|
size_t output_len, size_t data_len )
|
|
{
|
|
size_t padding_len = output_len - data_len;
|
|
unsigned char i = 0;
|
|
|
|
for( i = 1; i < padding_len; i++ )
|
|
output[data_len + i - 1] = 0x00;
|
|
output[output_len - 1] = (unsigned char) padding_len;
|
|
}
|
|
|
|
static int get_zeros_and_len_padding( unsigned char *input, size_t input_len,
|
|
size_t *data_len )
|
|
{
|
|
size_t i, pad_idx;
|
|
unsigned char padding_len, bad = 0;
|
|
|
|
if( NULL == input || NULL == data_len )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
padding_len = input[input_len - 1];
|
|
*data_len = input_len - padding_len;
|
|
|
|
/* Avoid logical || since it results in a branch */
|
|
bad |= padding_len > input_len;
|
|
bad |= padding_len == 0;
|
|
|
|
/* The number of bytes checked must be independent of padding_len */
|
|
pad_idx = input_len - padding_len;
|
|
for( i = 0; i < input_len - 1; i++ )
|
|
bad |= input[i] * ( i >= pad_idx );
|
|
|
|
return( MBEDTLS_ERR_CIPHER_INVALID_PADDING * ( bad != 0 ) );
|
|
}
|
|
#endif /* MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN */
|
|
|
|
#if defined(MBEDTLS_CIPHER_PADDING_ZEROS)
|
|
/*
|
|
* Zero padding: fill with 00 ... 00
|
|
*/
|
|
static void add_zeros_padding( unsigned char *output,
|
|
size_t output_len, size_t data_len )
|
|
{
|
|
size_t i;
|
|
|
|
for( i = data_len; i < output_len; i++ )
|
|
output[i] = 0x00;
|
|
}
|
|
|
|
static int get_zeros_padding( unsigned char *input, size_t input_len,
|
|
size_t *data_len )
|
|
{
|
|
size_t i;
|
|
unsigned char done = 0, prev_done;
|
|
|
|
if( NULL == input || NULL == data_len )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
*data_len = 0;
|
|
for( i = input_len; i > 0; i-- )
|
|
{
|
|
prev_done = done;
|
|
done |= ( input[i-1] != 0 );
|
|
*data_len |= i * ( done != prev_done );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_CIPHER_PADDING_ZEROS */
|
|
|
|
/*
|
|
* No padding: don't pad :)
|
|
*
|
|
* There is no add_padding function (check for NULL in mbedtls_cipher_finish)
|
|
* but a trivial get_padding function
|
|
*/
|
|
static int get_no_padding( unsigned char *input, size_t input_len,
|
|
size_t *data_len )
|
|
{
|
|
if( NULL == input || NULL == data_len )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
*data_len = input_len;
|
|
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_CIPHER_MODE_WITH_PADDING */
|
|
|
|
int mbedtls_cipher_finish( mbedtls_cipher_context_t *ctx,
|
|
unsigned char *output, size_t *olen )
|
|
{
|
|
CIPHER_VALIDATE_RET( ctx != NULL );
|
|
CIPHER_VALIDATE_RET( output != NULL );
|
|
CIPHER_VALIDATE_RET( olen != NULL );
|
|
if( ctx->cipher_info == NULL )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
|
if( ctx->psa_enabled == 1 )
|
|
{
|
|
/* While PSA Crypto has an API for multipart
|
|
* operations, we currently don't make it
|
|
* accessible through the cipher layer. */
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
}
|
|
#endif /* MBEDTLS_USE_PSA_CRYPTO */
|
|
|
|
*olen = 0;
|
|
|
|
if( MBEDTLS_MODE_CFB == ctx->cipher_info->mode ||
|
|
MBEDTLS_MODE_OFB == ctx->cipher_info->mode ||
|
|
MBEDTLS_MODE_CTR == ctx->cipher_info->mode ||
|
|
MBEDTLS_MODE_GCM == ctx->cipher_info->mode ||
|
|
MBEDTLS_MODE_XTS == ctx->cipher_info->mode ||
|
|
MBEDTLS_MODE_STREAM == ctx->cipher_info->mode )
|
|
{
|
|
return( 0 );
|
|
}
|
|
|
|
if ( ( MBEDTLS_CIPHER_CHACHA20 == ctx->cipher_info->type ) ||
|
|
( MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type ) )
|
|
{
|
|
return( 0 );
|
|
}
|
|
|
|
if( MBEDTLS_MODE_ECB == ctx->cipher_info->mode )
|
|
{
|
|
if( ctx->unprocessed_len != 0 )
|
|
return( MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
#if defined(MBEDTLS_CIPHER_MODE_CBC)
|
|
if( MBEDTLS_MODE_CBC == ctx->cipher_info->mode )
|
|
{
|
|
int ret = 0;
|
|
|
|
if( MBEDTLS_ENCRYPT == ctx->operation )
|
|
{
|
|
/* check for 'no padding' mode */
|
|
if( NULL == ctx->add_padding )
|
|
{
|
|
if( 0 != ctx->unprocessed_len )
|
|
return( MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
ctx->add_padding( ctx->unprocessed_data, mbedtls_cipher_get_iv_size( ctx ),
|
|
ctx->unprocessed_len );
|
|
}
|
|
else if( mbedtls_cipher_get_block_size( ctx ) != ctx->unprocessed_len )
|
|
{
|
|
/*
|
|
* For decrypt operations, expect a full block,
|
|
* or an empty block if no padding
|
|
*/
|
|
if( NULL == ctx->add_padding && 0 == ctx->unprocessed_len )
|
|
return( 0 );
|
|
|
|
return( MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED );
|
|
}
|
|
|
|
/* cipher block */
|
|
if( 0 != ( ret = ctx->cipher_info->base->cbc_func( ctx->cipher_ctx,
|
|
ctx->operation, mbedtls_cipher_get_block_size( ctx ), ctx->iv,
|
|
ctx->unprocessed_data, output ) ) )
|
|
{
|
|
return( ret );
|
|
}
|
|
|
|
/* Set output size for decryption */
|
|
if( MBEDTLS_DECRYPT == ctx->operation )
|
|
return( ctx->get_padding( output, mbedtls_cipher_get_block_size( ctx ),
|
|
olen ) );
|
|
|
|
/* Set output size for encryption */
|
|
*olen = mbedtls_cipher_get_block_size( ctx );
|
|
return( 0 );
|
|
}
|
|
#else
|
|
((void) output);
|
|
#endif /* MBEDTLS_CIPHER_MODE_CBC */
|
|
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
}
|
|
|
|
#if defined(MBEDTLS_CIPHER_MODE_WITH_PADDING)
|
|
int mbedtls_cipher_set_padding_mode( mbedtls_cipher_context_t *ctx,
|
|
mbedtls_cipher_padding_t mode )
|
|
{
|
|
CIPHER_VALIDATE_RET( ctx != NULL );
|
|
|
|
if( NULL == ctx->cipher_info || MBEDTLS_MODE_CBC != ctx->cipher_info->mode )
|
|
{
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
}
|
|
|
|
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
|
if( ctx->psa_enabled == 1 )
|
|
{
|
|
/* While PSA Crypto knows about CBC padding
|
|
* schemes, we currently don't make them
|
|
* accessible through the cipher layer. */
|
|
if( mode != MBEDTLS_PADDING_NONE )
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_USE_PSA_CRYPTO */
|
|
|
|
switch( mode )
|
|
{
|
|
#if defined(MBEDTLS_CIPHER_PADDING_PKCS7)
|
|
case MBEDTLS_PADDING_PKCS7:
|
|
ctx->add_padding = add_pkcs_padding;
|
|
ctx->get_padding = get_pkcs_padding;
|
|
break;
|
|
#endif
|
|
#if defined(MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS)
|
|
case MBEDTLS_PADDING_ONE_AND_ZEROS:
|
|
ctx->add_padding = add_one_and_zeros_padding;
|
|
ctx->get_padding = get_one_and_zeros_padding;
|
|
break;
|
|
#endif
|
|
#if defined(MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN)
|
|
case MBEDTLS_PADDING_ZEROS_AND_LEN:
|
|
ctx->add_padding = add_zeros_and_len_padding;
|
|
ctx->get_padding = get_zeros_and_len_padding;
|
|
break;
|
|
#endif
|
|
#if defined(MBEDTLS_CIPHER_PADDING_ZEROS)
|
|
case MBEDTLS_PADDING_ZEROS:
|
|
ctx->add_padding = add_zeros_padding;
|
|
ctx->get_padding = get_zeros_padding;
|
|
break;
|
|
#endif
|
|
case MBEDTLS_PADDING_NONE:
|
|
ctx->add_padding = NULL;
|
|
ctx->get_padding = get_no_padding;
|
|
break;
|
|
|
|
default:
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_CIPHER_MODE_WITH_PADDING */
|
|
|
|
#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C)
|
|
int mbedtls_cipher_write_tag( mbedtls_cipher_context_t *ctx,
|
|
unsigned char *tag, size_t tag_len )
|
|
{
|
|
CIPHER_VALIDATE_RET( ctx != NULL );
|
|
CIPHER_VALIDATE_RET( tag_len == 0 || tag != NULL );
|
|
if( ctx->cipher_info == NULL )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
if( MBEDTLS_ENCRYPT != ctx->operation )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
|
if( ctx->psa_enabled == 1 )
|
|
{
|
|
/* While PSA Crypto has an API for multipart
|
|
* operations, we currently don't make it
|
|
* accessible through the cipher layer. */
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
}
|
|
#endif /* MBEDTLS_USE_PSA_CRYPTO */
|
|
|
|
#if defined(MBEDTLS_GCM_C)
|
|
if( MBEDTLS_MODE_GCM == ctx->cipher_info->mode )
|
|
return( mbedtls_gcm_finish( (mbedtls_gcm_context *) ctx->cipher_ctx,
|
|
tag, tag_len ) );
|
|
#endif
|
|
|
|
#if defined(MBEDTLS_CHACHAPOLY_C)
|
|
if ( MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type )
|
|
{
|
|
/* Don't allow truncated MAC for Poly1305 */
|
|
if ( tag_len != 16U )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
return( mbedtls_chachapoly_finish(
|
|
(mbedtls_chachapoly_context*) ctx->cipher_ctx, tag ) );
|
|
}
|
|
#endif
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
int mbedtls_cipher_check_tag( mbedtls_cipher_context_t *ctx,
|
|
const unsigned char *tag, size_t tag_len )
|
|
{
|
|
unsigned char check_tag[16];
|
|
int ret = MBEDTLS_ERR_THIS_CORRUPTION;
|
|
|
|
CIPHER_VALIDATE_RET( ctx != NULL );
|
|
CIPHER_VALIDATE_RET( tag_len == 0 || tag != NULL );
|
|
if( ctx->cipher_info == NULL )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
if( MBEDTLS_DECRYPT != ctx->operation )
|
|
{
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
}
|
|
|
|
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
|
if( ctx->psa_enabled == 1 )
|
|
{
|
|
/* While PSA Crypto has an API for multipart
|
|
* operations, we currently don't make it
|
|
* accessible through the cipher layer. */
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
}
|
|
#endif /* MBEDTLS_USE_PSA_CRYPTO */
|
|
|
|
#if defined(MBEDTLS_GCM_C)
|
|
if( MBEDTLS_MODE_GCM == ctx->cipher_info->mode )
|
|
{
|
|
if( tag_len > sizeof( check_tag ) )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
if( 0 != ( ret = mbedtls_gcm_finish(
|
|
(mbedtls_gcm_context *) ctx->cipher_ctx,
|
|
check_tag, tag_len ) ) )
|
|
{
|
|
return( ret );
|
|
}
|
|
|
|
/* Check the tag in "constant-time" */
|
|
if( timingsafe_bcmp( tag, check_tag, tag_len ) != 0 )
|
|
return( MBEDTLS_ERR_CIPHER_AUTH_FAILED );
|
|
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_GCM_C */
|
|
|
|
#if defined(MBEDTLS_CHACHAPOLY_C)
|
|
if ( MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type )
|
|
{
|
|
/* Don't allow truncated MAC for Poly1305 */
|
|
if ( tag_len != sizeof( check_tag ) )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
ret = mbedtls_chachapoly_finish(
|
|
(mbedtls_chachapoly_context*) ctx->cipher_ctx, check_tag );
|
|
if ( ret != 0 )
|
|
{
|
|
return( ret );
|
|
}
|
|
|
|
/* Check the tag in "constant-time" */
|
|
if( timingsafe_bcmp( tag, check_tag, tag_len ) != 0 )
|
|
return( MBEDTLS_ERR_CIPHER_AUTH_FAILED );
|
|
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_CHACHAPOLY_C */
|
|
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */
|
|
|
|
/*
|
|
* Packet-oriented wrapper for non-AEAD modes
|
|
*/
|
|
int mbedtls_cipher_crypt( mbedtls_cipher_context_t *ctx,
|
|
const unsigned char *iv, size_t iv_len,
|
|
const unsigned char *input, size_t ilen,
|
|
unsigned char *output, size_t *olen )
|
|
{
|
|
int ret = MBEDTLS_ERR_THIS_CORRUPTION;
|
|
size_t finish_olen;
|
|
|
|
CIPHER_VALIDATE_RET( ctx != NULL );
|
|
CIPHER_VALIDATE_RET( iv_len == 0 || iv != NULL );
|
|
CIPHER_VALIDATE_RET( ilen == 0 || input != NULL );
|
|
CIPHER_VALIDATE_RET( output != NULL );
|
|
CIPHER_VALIDATE_RET( olen != NULL );
|
|
|
|
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
|
if( ctx->psa_enabled == 1 )
|
|
{
|
|
/* As in the non-PSA case, we don't check that
|
|
* a key has been set. If not, the key slot will
|
|
* still be in its default state of 0, which is
|
|
* guaranteed to be invalid, hence the PSA-call
|
|
* below will gracefully fail. */
|
|
mbedtls_cipher_context_psa * const cipher_psa =
|
|
(mbedtls_cipher_context_psa *) ctx->cipher_ctx;
|
|
|
|
psa_status_t status;
|
|
psa_cipher_operation_t cipher_op = PSA_CIPHER_OPERATION_INIT;
|
|
size_t part_len;
|
|
|
|
if( ctx->operation == MBEDTLS_DECRYPT )
|
|
{
|
|
status = psa_cipher_decrypt_setup( &cipher_op,
|
|
cipher_psa->slot,
|
|
cipher_psa->alg );
|
|
}
|
|
else if( ctx->operation == MBEDTLS_ENCRYPT )
|
|
{
|
|
status = psa_cipher_encrypt_setup( &cipher_op,
|
|
cipher_psa->slot,
|
|
cipher_psa->alg );
|
|
}
|
|
else
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
/* In the following, we can immediately return on an error,
|
|
* because the PSA Crypto API guarantees that cipher operations
|
|
* are terminated by unsuccessful calls to psa_cipher_update(),
|
|
* and by any call to psa_cipher_finish(). */
|
|
if( status != PSA_SUCCESS )
|
|
return( MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED );
|
|
|
|
status = psa_cipher_set_iv( &cipher_op, iv, iv_len );
|
|
if( status != PSA_SUCCESS )
|
|
return( MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED );
|
|
|
|
status = psa_cipher_update( &cipher_op,
|
|
input, ilen,
|
|
output, ilen, olen );
|
|
if( status != PSA_SUCCESS )
|
|
return( MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED );
|
|
|
|
status = psa_cipher_finish( &cipher_op,
|
|
output + *olen, ilen - *olen,
|
|
&part_len );
|
|
if( status != PSA_SUCCESS )
|
|
return( MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED );
|
|
|
|
*olen += part_len;
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_USE_PSA_CRYPTO */
|
|
|
|
if( ( ret = mbedtls_cipher_set_iv( ctx, iv, iv_len ) ) != 0 )
|
|
return( ret );
|
|
|
|
if( ( ret = mbedtls_cipher_reset( ctx ) ) != 0 )
|
|
return( ret );
|
|
|
|
if( ( ret = mbedtls_cipher_update( ctx, input, ilen,
|
|
output, olen ) ) != 0 )
|
|
return( ret );
|
|
|
|
if( ( ret = mbedtls_cipher_finish( ctx, output + *olen,
|
|
&finish_olen ) ) != 0 )
|
|
return( ret );
|
|
|
|
*olen += finish_olen;
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
#if defined(MBEDTLS_CIPHER_MODE_AEAD)
|
|
/*
|
|
* Packet-oriented encryption for AEAD modes: internal function shared by
|
|
* mbedtls_cipher_auth_encrypt() and mbedtls_cipher_auth_encrypt_ext().
|
|
*/
|
|
static int mbedtls_cipher_aead_encrypt( mbedtls_cipher_context_t *ctx,
|
|
const unsigned char *iv, size_t iv_len,
|
|
const unsigned char *ad, size_t ad_len,
|
|
const unsigned char *input, size_t ilen,
|
|
unsigned char *output, size_t *olen,
|
|
unsigned char *tag, size_t tag_len )
|
|
{
|
|
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
|
if( ctx->psa_enabled == 1 )
|
|
{
|
|
/* As in the non-PSA case, we don't check that
|
|
* a key has been set. If not, the key slot will
|
|
* still be in its default state of 0, which is
|
|
* guaranteed to be invalid, hence the PSA-call
|
|
* below will gracefully fail. */
|
|
mbedtls_cipher_context_psa * const cipher_psa =
|
|
(mbedtls_cipher_context_psa *) ctx->cipher_ctx;
|
|
psa_status_t status;
|
|
/* PSA Crypto API always writes the authentication tag
|
|
* at the end of the encrypted message. */
|
|
if( output == NULL || tag != output + ilen )
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
status = psa_aead_encrypt( cipher_psa->slot,
|
|
cipher_psa->alg,
|
|
iv, iv_len,
|
|
ad, ad_len,
|
|
input, ilen,
|
|
output, ilen + tag_len, olen );
|
|
if( status != PSA_SUCCESS )
|
|
return( MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED );
|
|
*olen -= tag_len;
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_USE_PSA_CRYPTO */
|
|
|
|
#if defined(MBEDTLS_GCM_C)
|
|
if( MBEDTLS_MODE_GCM == ctx->cipher_info->mode )
|
|
{
|
|
*olen = ilen;
|
|
return( mbedtls_gcm_crypt_and_tag( ctx->cipher_ctx, MBEDTLS_GCM_ENCRYPT,
|
|
ilen, iv, iv_len, ad, ad_len,
|
|
input, output, tag_len, tag ) );
|
|
}
|
|
#endif /* MBEDTLS_GCM_C */
|
|
#if defined(MBEDTLS_CCM_C)
|
|
if( MBEDTLS_MODE_CCM == ctx->cipher_info->mode )
|
|
{
|
|
*olen = ilen;
|
|
return( mbedtls_ccm_encrypt_and_tag( ctx->cipher_ctx, ilen,
|
|
iv, iv_len, ad, ad_len, input, output,
|
|
tag, tag_len ) );
|
|
}
|
|
#endif /* MBEDTLS_CCM_C */
|
|
#if defined(MBEDTLS_CHACHAPOLY_C)
|
|
if ( MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type )
|
|
{
|
|
/* ChachaPoly has fixed length nonce and MAC (tag) */
|
|
if ( ( iv_len != ctx->cipher_info->iv_size ) ||
|
|
( tag_len != 16U ) )
|
|
{
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
}
|
|
|
|
*olen = ilen;
|
|
return( mbedtls_chachapoly_encrypt_and_tag( ctx->cipher_ctx,
|
|
ilen, iv, ad, ad_len, input, output, tag ) );
|
|
}
|
|
#endif /* MBEDTLS_CHACHAPOLY_C */
|
|
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
}
|
|
|
|
/*
|
|
* Packet-oriented encryption for AEAD modes: internal function shared by
|
|
* mbedtls_cipher_auth_encrypt() and mbedtls_cipher_auth_encrypt_ext().
|
|
*/
|
|
static int mbedtls_cipher_aead_decrypt( mbedtls_cipher_context_t *ctx,
|
|
const unsigned char *iv, size_t iv_len,
|
|
const unsigned char *ad, size_t ad_len,
|
|
const unsigned char *input, size_t ilen,
|
|
unsigned char *output, size_t *olen,
|
|
const unsigned char *tag, size_t tag_len )
|
|
{
|
|
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
|
if( ctx->psa_enabled == 1 )
|
|
{
|
|
/* As in the non-PSA case, we don't check that
|
|
* a key has been set. If not, the key slot will
|
|
* still be in its default state of 0, which is
|
|
* guaranteed to be invalid, hence the PSA-call
|
|
* below will gracefully fail. */
|
|
mbedtls_cipher_context_psa * const cipher_psa =
|
|
(mbedtls_cipher_context_psa *) ctx->cipher_ctx;
|
|
|
|
psa_status_t status;
|
|
|
|
/* PSA Crypto API always writes the authentication tag
|
|
* at the end of the encrypted message. */
|
|
if( input == NULL || tag != input + ilen )
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
|
|
status = psa_aead_decrypt( cipher_psa->slot,
|
|
cipher_psa->alg,
|
|
iv, iv_len,
|
|
ad, ad_len,
|
|
input, ilen + tag_len,
|
|
output, ilen, olen );
|
|
if( status == PSA_ERROR_INVALID_SIGNATURE )
|
|
return( MBEDTLS_ERR_CIPHER_AUTH_FAILED );
|
|
else if( status != PSA_SUCCESS )
|
|
return( MBEDTLS_ERR_CIPHER_HW_ACCEL_FAILED );
|
|
|
|
return( 0 );
|
|
}
|
|
#endif /* MBEDTLS_USE_PSA_CRYPTO */
|
|
|
|
#if defined(MBEDTLS_GCM_C)
|
|
if( MBEDTLS_MODE_GCM == ctx->cipher_info->mode )
|
|
{
|
|
int ret = MBEDTLS_ERR_THIS_CORRUPTION;
|
|
|
|
*olen = ilen;
|
|
ret = mbedtls_gcm_auth_decrypt( ctx->cipher_ctx, ilen,
|
|
iv, iv_len, ad, ad_len,
|
|
tag, tag_len, input, output );
|
|
|
|
if( ret == MBEDTLS_ERR_GCM_AUTH_FAILED )
|
|
ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED;
|
|
|
|
return( ret );
|
|
}
|
|
#endif /* MBEDTLS_GCM_C */
|
|
#if defined(MBEDTLS_CCM_C)
|
|
if( MBEDTLS_MODE_CCM == ctx->cipher_info->mode )
|
|
{
|
|
int ret = MBEDTLS_ERR_THIS_CORRUPTION;
|
|
|
|
*olen = ilen;
|
|
ret = mbedtls_ccm_auth_decrypt( ctx->cipher_ctx, ilen,
|
|
iv, iv_len, ad, ad_len,
|
|
input, output, tag, tag_len );
|
|
|
|
if( ret == MBEDTLS_ERR_CCM_AUTH_FAILED )
|
|
ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED;
|
|
|
|
return( ret );
|
|
}
|
|
#endif /* MBEDTLS_CCM_C */
|
|
#if defined(MBEDTLS_CHACHAPOLY_C)
|
|
if ( MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type )
|
|
{
|
|
int ret = MBEDTLS_ERR_THIS_CORRUPTION;
|
|
|
|
/* ChachaPoly has fixed length nonce and MAC (tag) */
|
|
if ( ( iv_len != ctx->cipher_info->iv_size ) ||
|
|
( tag_len != 16U ) )
|
|
{
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
}
|
|
|
|
*olen = ilen;
|
|
ret = mbedtls_chachapoly_auth_decrypt( ctx->cipher_ctx, ilen,
|
|
iv, ad, ad_len, tag, input, output );
|
|
|
|
if( ret == MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED )
|
|
ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED;
|
|
|
|
return( ret );
|
|
}
|
|
#endif /* MBEDTLS_CHACHAPOLY_C */
|
|
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
}
|
|
|
|
/*
|
|
* Packet-oriented encryption for AEAD modes: public legacy function.
|
|
*/
|
|
int mbedtls_cipher_auth_encrypt( mbedtls_cipher_context_t *ctx,
|
|
const unsigned char *iv, size_t iv_len,
|
|
const unsigned char *ad, size_t ad_len,
|
|
const unsigned char *input, size_t ilen,
|
|
unsigned char *output, size_t *olen,
|
|
unsigned char *tag, size_t tag_len )
|
|
{
|
|
CIPHER_VALIDATE_RET( ctx != NULL );
|
|
CIPHER_VALIDATE_RET( iv_len == 0 || iv != NULL );
|
|
CIPHER_VALIDATE_RET( ad_len == 0 || ad != NULL );
|
|
CIPHER_VALIDATE_RET( ilen == 0 || input != NULL );
|
|
CIPHER_VALIDATE_RET( ilen == 0 || output != NULL );
|
|
CIPHER_VALIDATE_RET( olen != NULL );
|
|
CIPHER_VALIDATE_RET( tag_len == 0 || tag != NULL );
|
|
|
|
return( mbedtls_cipher_aead_encrypt( ctx, iv, iv_len, ad, ad_len,
|
|
input, ilen, output, olen,
|
|
tag, tag_len ) );
|
|
}
|
|
|
|
/*
|
|
* Packet-oriented decryption for AEAD modes: public legacy function.
|
|
*/
|
|
int mbedtls_cipher_auth_decrypt( mbedtls_cipher_context_t *ctx,
|
|
const unsigned char *iv, size_t iv_len,
|
|
const unsigned char *ad, size_t ad_len,
|
|
const unsigned char *input, size_t ilen,
|
|
unsigned char *output, size_t *olen,
|
|
const unsigned char *tag, size_t tag_len )
|
|
{
|
|
CIPHER_VALIDATE_RET( ctx != NULL );
|
|
CIPHER_VALIDATE_RET( iv_len == 0 || iv != NULL );
|
|
CIPHER_VALIDATE_RET( ad_len == 0 || ad != NULL );
|
|
CIPHER_VALIDATE_RET( ilen == 0 || input != NULL );
|
|
CIPHER_VALIDATE_RET( ilen == 0 || output != NULL );
|
|
CIPHER_VALIDATE_RET( olen != NULL );
|
|
CIPHER_VALIDATE_RET( tag_len == 0 || tag != NULL );
|
|
|
|
return( mbedtls_cipher_aead_decrypt( ctx, iv, iv_len, ad, ad_len,
|
|
input, ilen, output, olen,
|
|
tag, tag_len ) );
|
|
}
|
|
|
|
#endif /* MBEDTLS_CIPHER_MODE_AEAD */
|
|
|
|
#if defined(MBEDTLS_CIPHER_MODE_AEAD) || defined(MBEDTLS_NIST_KW_C)
|
|
/*
|
|
* Packet-oriented encryption for AEAD/NIST_KW: public function.
|
|
*/
|
|
int mbedtls_cipher_auth_encrypt_ext( mbedtls_cipher_context_t *ctx,
|
|
const unsigned char *iv, size_t iv_len,
|
|
const unsigned char *ad, size_t ad_len,
|
|
const unsigned char *input, size_t ilen,
|
|
unsigned char *output, size_t output_len,
|
|
size_t *olen, size_t tag_len )
|
|
{
|
|
CIPHER_VALIDATE_RET( ctx != NULL );
|
|
CIPHER_VALIDATE_RET( iv_len == 0 || iv != NULL );
|
|
CIPHER_VALIDATE_RET( ad_len == 0 || ad != NULL );
|
|
CIPHER_VALIDATE_RET( ilen == 0 || input != NULL );
|
|
CIPHER_VALIDATE_RET( output != NULL );
|
|
CIPHER_VALIDATE_RET( olen != NULL );
|
|
|
|
#if defined(MBEDTLS_NIST_KW_C)
|
|
if(
|
|
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
|
ctx->psa_enabled == 0 &&
|
|
#endif
|
|
( MBEDTLS_MODE_KW == ctx->cipher_info->mode ||
|
|
MBEDTLS_MODE_KWP == ctx->cipher_info->mode ) )
|
|
{
|
|
mbedtls_nist_kw_mode_t mode = ( MBEDTLS_MODE_KW == ctx->cipher_info->mode ) ?
|
|
MBEDTLS_KW_MODE_KW : MBEDTLS_KW_MODE_KWP;
|
|
|
|
/* There is no iv, tag or ad associated with KW and KWP,
|
|
* so these length should be 0 as documented. */
|
|
if( iv_len != 0 || tag_len != 0 || ad_len != 0 )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
(void) iv;
|
|
(void) ad;
|
|
|
|
return( mbedtls_nist_kw_wrap( ctx->cipher_ctx, mode, input, ilen,
|
|
output, olen, output_len ) );
|
|
}
|
|
#endif /* MBEDTLS_NIST_KW_C */
|
|
|
|
#if defined(MBEDTLS_CIPHER_MODE_AEAD)
|
|
/* AEAD case: check length before passing on to shared function */
|
|
if( output_len < ilen + tag_len )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
int ret = mbedtls_cipher_aead_encrypt( ctx, iv, iv_len, ad, ad_len,
|
|
input, ilen, output, olen,
|
|
output + ilen, tag_len );
|
|
*olen += tag_len;
|
|
return( ret );
|
|
#else
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
#endif /* MBEDTLS_CIPHER_MODE_AEAD */
|
|
}
|
|
|
|
/*
|
|
* Packet-oriented decryption for AEAD/NIST_KW: public function.
|
|
*/
|
|
int mbedtls_cipher_auth_decrypt_ext( mbedtls_cipher_context_t *ctx,
|
|
const unsigned char *iv, size_t iv_len,
|
|
const unsigned char *ad, size_t ad_len,
|
|
const unsigned char *input, size_t ilen,
|
|
unsigned char *output, size_t output_len,
|
|
size_t *olen, size_t tag_len )
|
|
{
|
|
CIPHER_VALIDATE_RET( ctx != NULL );
|
|
CIPHER_VALIDATE_RET( iv_len == 0 || iv != NULL );
|
|
CIPHER_VALIDATE_RET( ad_len == 0 || ad != NULL );
|
|
CIPHER_VALIDATE_RET( ilen == 0 || input != NULL );
|
|
CIPHER_VALIDATE_RET( output_len == 0 || output != NULL );
|
|
CIPHER_VALIDATE_RET( olen != NULL );
|
|
|
|
#if defined(MBEDTLS_NIST_KW_C)
|
|
if(
|
|
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
|
ctx->psa_enabled == 0 &&
|
|
#endif
|
|
( MBEDTLS_MODE_KW == ctx->cipher_info->mode ||
|
|
MBEDTLS_MODE_KWP == ctx->cipher_info->mode ) )
|
|
{
|
|
mbedtls_nist_kw_mode_t mode = ( MBEDTLS_MODE_KW == ctx->cipher_info->mode ) ?
|
|
MBEDTLS_KW_MODE_KW : MBEDTLS_KW_MODE_KWP;
|
|
|
|
/* There is no iv, tag or ad associated with KW and KWP,
|
|
* so these length should be 0 as documented. */
|
|
if( iv_len != 0 || tag_len != 0 || ad_len != 0 )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
(void) iv;
|
|
(void) ad;
|
|
|
|
return( mbedtls_nist_kw_unwrap( ctx->cipher_ctx, mode, input, ilen,
|
|
output, olen, output_len ) );
|
|
}
|
|
#endif /* MBEDTLS_NIST_KW_C */
|
|
|
|
#if defined(MBEDTLS_CIPHER_MODE_AEAD)
|
|
/* AEAD case: check length before passing on to shared function */
|
|
if( ilen < tag_len || output_len < ilen - tag_len )
|
|
return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA );
|
|
|
|
return( mbedtls_cipher_aead_decrypt( ctx, iv, iv_len, ad, ad_len,
|
|
input, ilen - tag_len, output, olen,
|
|
input + ilen - tag_len, tag_len ) );
|
|
#else
|
|
return( MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE );
|
|
#endif /* MBEDTLS_CIPHER_MODE_AEAD */
|
|
}
|
|
#endif /* MBEDTLS_CIPHER_MODE_AEAD || MBEDTLS_NIST_KW_C */
|
|
|
|
#endif /* MBEDTLS_CIPHER_C */
|