Skip to content
Merged
48 changes: 45 additions & 3 deletions src/psa_aead.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
#include <wolfpsa/psa_chacha20_poly1305.h>
#include <wolfssl/wolfcrypt/aes.h>
#include <wolfssl/wolfcrypt/mem_track.h>
#ifdef HAVE_XCHACHA
#if defined(HAVE_XCHACHA) || (defined(HAVE_CHACHA) && defined(HAVE_POLY1305))
#include <wolfssl/wolfcrypt/chacha20_poly1305.h>
#endif
#ifdef HAVE_ASCON
Expand Down Expand Up @@ -157,7 +157,7 @@ static psa_status_t wolfpsa_aead_check_key(psa_key_id_t key,
}

key_usage = psa_get_key_usage_flags(attributes);
if ((key_usage & usage) == 0) {
if ((key_usage & usage) != usage) {
wolfpsa_forcezero_free_key_data(*key_data, *key_data_length);
*key_data = NULL;
*key_data_length = 0;
Expand Down Expand Up @@ -296,6 +296,17 @@ static psa_status_t wolfpsa_aead_setup(psa_aead_operation_t *operation,
return PSA_ERROR_INVALID_ARGUMENT;
}
#endif
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
/* ChaCha20-Poly1305 has no truncated-tag interface; only the native
* 16-byte tag is supported. Reject shortened-tag variants here rather than
* accepting them and failing the encrypt/decrypt roundtrip later. */
if (PSA_ALG_AEAD_EQUAL(alg, PSA_ALG_CHACHA20_POLY1305) &&
ctx->tag_length != CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE) {
wolfpsa_forcezero_free_key_data(key_data, key_data_length);
XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return PSA_ERROR_NOT_SUPPORTED;
}
#endif

ctx->key = (uint8_t *)XMALLOC(key_data_length, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
Expand Down Expand Up @@ -370,9 +381,16 @@ psa_status_t psa_aead_set_nonce(psa_aead_operation_t *operation,
}

if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_GCM)) {
if (nonce_length < 12 || nonce_length > PSA_AEAD_NONCE_MAX_SIZE) {
/* GCM (SP 800-38D) accepts any non-empty nonce. A zero-length nonce is
* invalid for the algorithm; other lengths outside the supported 12 to
* 24 byte range are valid for GCM but not supported by this
* implementation. */
if (nonce_length == 0) {
return PSA_ERROR_INVALID_ARGUMENT;
}
if (nonce_length < 12 || nonce_length > PSA_AEAD_NONCE_MAX_SIZE) {
return PSA_ERROR_NOT_SUPPORTED;
}
}
else if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_CCM)) {
if (nonce_length < 7 || nonce_length > 13) {
Expand Down Expand Up @@ -876,6 +894,12 @@ static psa_status_t wolfpsa_xchacha_oneshot_encrypt(
/* Encrypt produces plaintext_length bytes of ciphertext plus 16-byte tag. */
size_t out_len;

/* Only the native 16-byte tag is supported. A shortened-tag or
* at-least-this-length variant differs from the base algorithm and must be
* rejected rather than silently emitting a full-length tag. */
if (alg != PSA_ALG_XCHACHA20_POLY1305) {
return PSA_ERROR_NOT_SUPPORTED;
}
if (nonce_length != XCHACHA20_POLY1305_AEAD_NONCE_SIZE) {
return PSA_ERROR_INVALID_ARGUMENT;
}
Expand Down Expand Up @@ -955,6 +979,12 @@ static psa_status_t wolfpsa_xchacha_oneshot_decrypt(
int ret;
size_t pt_len;

/* Only the native 16-byte tag is supported. A shortened-tag or
* at-least-this-length variant differs from the base algorithm and must be
* rejected rather than mis-framing the trailing tag bytes. */
if (alg != PSA_ALG_XCHACHA20_POLY1305) {
return PSA_ERROR_NOT_SUPPORTED;
}
if (nonce_length != XCHACHA20_POLY1305_AEAD_NONCE_SIZE) {
return PSA_ERROR_INVALID_ARGUMENT;
}
Expand Down Expand Up @@ -1040,6 +1070,12 @@ static psa_status_t wolfpsa_ascon_oneshot_encrypt(
int ret;
size_t out_len;

/* Only the native 16-byte tag is supported. A shortened-tag or
* at-least-this-length variant differs from the base algorithm and must be
* rejected rather than silently emitting a full-length tag. */
if (alg != PSA_ALG_ASCON_AEAD128) {
return PSA_ERROR_NOT_SUPPORTED;
}
if (nonce_length != ASCON_AEAD128_NONCE_SZ) {
return PSA_ERROR_INVALID_ARGUMENT;
}
Expand Down Expand Up @@ -1140,6 +1176,12 @@ static psa_status_t wolfpsa_ascon_oneshot_decrypt(
int ret;
size_t ct_len; /* ciphertext body length (without tag) */

/* Only the native 16-byte tag is supported. A shortened-tag or
* at-least-this-length variant differs from the base algorithm and must be
* rejected rather than mis-framing the trailing tag bytes. */
if (alg != PSA_ALG_ASCON_AEAD128) {
return PSA_ERROR_NOT_SUPPORTED;
}
if (nonce_length != ASCON_AEAD128_NONCE_SZ) {
return PSA_ERROR_INVALID_ARGUMENT;
}
Expand Down
2 changes: 1 addition & 1 deletion src/psa_asymmetric_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ static psa_status_t wolfpsa_asymmetric_check_key(psa_key_id_t key,
}

key_usage = psa_get_key_usage_flags(attributes);
if ((key_usage & usage) == 0) {
if ((key_usage & usage) != usage) {
wolfpsa_forcezero_free_key_data(*key_data, *key_data_length);
*key_data = NULL;
*key_data_length = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/psa_cipher.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ static psa_status_t wolfpsa_cipher_check_key(
}

key_usage = psa_get_key_usage_flags(attributes);
if ((key_usage & usage) == 0) {
if ((key_usage & usage) != usage) {
wolfpsa_forcezero_free_key_data(*key_data, *key_data_length);
*key_data = NULL;
*key_data_length = 0;
Expand Down
1 change: 1 addition & 0 deletions src/psa_engine.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ psa_status_t wc_error_to_psa_status(int ret)
status = PSA_ERROR_INVALID_ARGUMENT;
break;
case BUFFER_E:
case RSA_BUFFER_E:
status = PSA_ERROR_BUFFER_TOO_SMALL;
break;
case MEMORY_E:
Expand Down
20 changes: 16 additions & 4 deletions src/psa_key_derivation.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ typedef struct wolfpsa_kdf_ctx {
#define WOLFPSA_KDF_STEP_COST (1u << 7)
#define WOLFPSA_KDF_STEP_CONTEXT (1u << 8)

/* Upper bound on how much of a bounded-capacity derivation is pre-computed
* and cached on the first output_bytes() call. HKDF's default capacity is at
* most 255 * hash_len (roughly 16 KB), which fits comfortably. SP800-108
* counter mode defaults its capacity to 2^29 - 1 bytes; caching that whole
* keystream up front would allocate about 512 MB and compute millions of MAC
* blocks just to return a few bytes, so above this bound the derivation is
* computed lazily per call instead. */
#define WOLFPSA_KDF_MAX_CACHE_BYTES (64u * 1024u)

static wolfpsa_kdf_ctx_t* wolfpsa_kdf_get_ctx(psa_key_derivation_operation_t *operation)
{
if (operation == NULL) {
Expand Down Expand Up @@ -1648,10 +1657,13 @@ psa_status_t psa_key_derivation_output_bytes(psa_key_derivation_operation_t *ope
if (ctx->output_cache == NULL
&& !ctx->is_raw_kdf
&& ctx->capacity != PSA_KEY_DERIVATION_UNLIMITED_CAPACITY
&& ctx->output_offset == 0) {
/* First output_bytes() call with a bounded capacity: compute the
* full derivation once and cache it. Subsequent calls serve slices
* from the cache, avoiding O(n^2) recomputation. */
&& ctx->output_offset == 0
&& (output_length + ctx->capacity) <= WOLFPSA_KDF_MAX_CACHE_BYTES) {
/* First output_bytes() call with a small bounded capacity: compute
* the full derivation once and cache it. Subsequent calls serve
* slices from the cache, avoiding O(n^2) recomputation. Capacities
* above the cache bound (for example the SP800-108 counter-mode
* default) skip this path and compute lazily per call. */
size_t cache_length = output_length + ctx->capacity;

ctx->output_cache = (uint8_t *)XMALLOC(cache_length, NULL,
Expand Down
25 changes: 25 additions & 0 deletions src/psa_key_storage.c
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,10 @@ static size_t psa_der_write_len(uint8_t* out, size_t len)
}
}

/* DER INTEGER encoders. These branch on the value being serialized (leading
* zero skip and high-bit pad), so they are variable-time and for public
* integers only, such as the RSA modulus and public exponent. Serialize a
* secret integer with a constant-time fixed-width encoder instead. */
static size_t psa_der_int_size(const uint8_t* val, size_t len)
{
size_t offset = 0;
Expand All @@ -624,6 +628,8 @@ static size_t psa_der_int_size(const uint8_t* val, size_t len)
value_len + (size_t)add_zero;
}

/* Public integers only; see the note on psa_der_int_size above. This encoder
* is variable-time with respect to the input bytes. */
static size_t psa_der_write_int(uint8_t* out, const uint8_t* val, size_t len)
{
size_t offset = 0;
Expand Down Expand Up @@ -950,6 +956,19 @@ psa_status_t psa_import_key(

attr = *attributes;

/* Local storage is the only location this build implements. A key whose
* lifetime names any other location (for example a secure element or
* vendor location) must be rejected rather than silently written to
* plaintext local storage. This choke point also covers psa_generate_key,
* psa_copy_key and psa_key_derivation_output_key, which all store through
* psa_import_key. */
if (PSA_KEY_LIFETIME_GET_LOCATION(attr.lifetime) !=
PSA_KEY_LOCATION_LOCAL_STORAGE) {
wolfpsa_debug_import_reason("unsupported key lifetime location", &attr,
data_length);
return PSA_ERROR_NOT_SUPPORTED;
}

if (attr.policy.alg2 != PSA_ALG_NONE) {
wolfpsa_debug_import_reason("unsupported secondary algorithm", &attr,
data_length);
Expand Down Expand Up @@ -1957,6 +1976,12 @@ psa_status_t psa_export_public_key(
status = PSA_SUCCESS;
}
}
else {
/* Key type admitted by the gate above but its backend is not
* compiled in (for example ML-DSA/ML-KEM when the corresponding
* WOLFSSL_HAVE_* macro is undefined). */
status = PSA_ERROR_NOT_SUPPORTED;
}
}

wolfpsa_forcezero_free_key_data(key_data, key_data_length);
Expand Down
2 changes: 1 addition & 1 deletion src/psa_mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ static psa_status_t wolfpsa_mac_check_key(psa_key_id_t key,
}

key_usage = psa_get_key_usage_flags(attributes);
if ((key_usage & usage) == 0) {
if ((key_usage & usage) != usage) {
wolfpsa_forcezero_free_key_data(*key_data, *key_data_length);
*key_data = NULL;
*key_data_length = 0;
Expand Down
Loading
Loading