diff --git a/src/psa_aead.c b/src/psa_aead.c index a1f49ee..6d90eb0 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -33,7 +33,7 @@ #include #include #include -#ifdef HAVE_XCHACHA +#if defined(HAVE_XCHACHA) || (defined(HAVE_CHACHA) && defined(HAVE_POLY1305)) #include #endif #ifdef HAVE_ASCON @@ -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; @@ -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); @@ -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) { @@ -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; } @@ -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; } @@ -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; } @@ -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; } diff --git a/src/psa_asymmetric_api.c b/src/psa_asymmetric_api.c index 799812d..e926dc7 100644 --- a/src/psa_asymmetric_api.c +++ b/src/psa_asymmetric_api.c @@ -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; diff --git a/src/psa_cipher.c b/src/psa_cipher.c index 6830c50..5bc442b 100644 --- a/src/psa_cipher.c +++ b/src/psa_cipher.c @@ -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; diff --git a/src/psa_engine.c b/src/psa_engine.c index 70c3546..cb6fbee 100644 --- a/src/psa_engine.c +++ b/src/psa_engine.c @@ -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: diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 568e180..d119b1c 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -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) { @@ -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, diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index 8555269..2edb0c0 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -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; @@ -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; @@ -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); @@ -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); diff --git a/src/psa_mac.c b/src/psa_mac.c index ea102c4..91b84ec 100644 --- a/src/psa_mac.c +++ b/src/psa_mac.c @@ -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; diff --git a/test/psa_server/psa_14_misc_test.c b/test/psa_server/psa_14_misc_test.c index c3c9ec0..2a69c62 100644 --- a/test/psa_server/psa_14_misc_test.c +++ b/test/psa_server/psa_14_misc_test.c @@ -484,6 +484,353 @@ static int test_size_macros(void) return fail; } +/* Case 9: a key whose lifetime names a storage location this build does not + * implement (for example a secure element or vendor location) must be rejected + * rather than silently written to plaintext local storage. Covers both the + * import and generate entry points. */ +static int test_unsupported_lifetime_location(void) +{ + /* A non-local vendor / secure-element storage location. */ + psa_key_location_t se_location = + (psa_key_location_t)(PSA_KEY_LOCATION_VENDOR_FLAG | 0x01u); + psa_key_lifetime_t se_lifetime = + PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION( + PSA_KEY_PERSISTENCE_DEFAULT, se_location); + static const uint8_t aes_key[16] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t key = 0; + psa_status_t st; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attrs, 128); + psa_set_key_usage_flags(&attrs, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_GCM); + psa_set_key_lifetime(&attrs, se_lifetime); + + /* Import path */ + st = psa_import_key(&attrs, aes_key, sizeof(aes_key), &key); + if (st == PSA_SUCCESS) { + (void)psa_destroy_key(key); + } + if (expect_status("import_key unsupported location", st, + PSA_ERROR_NOT_SUPPORTED) != 0) { + return 1; + } + + /* Generate path (funnels through psa_import_key) */ + key = 0; + st = psa_generate_key(&attrs, &key); + if (st == PSA_SUCCESS) { + (void)psa_destroy_key(key); + } + if (expect_status("generate_key unsupported location", st, + PSA_ERROR_NOT_SUPPORTED) != 0) { + return 1; + } + + return 0; +} + +/* Case 10: deterministic ECDSA (RFC 6979) must produce identical signatures + * for the same key and hash, and those signatures must verify. This exercises + * the deterministic nonce setup on the sign path; a randomized ECDSA nonce + * (deterministic setup deleted or disabled) yields two different signatures + * and fails the identical-signature assertion. */ +static int test_deterministic_ecdsa(void) +{ + psa_algorithm_t alg = PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256); + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t key = 0; + static const uint8_t hash[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }; + uint8_t sig1[PSA_SIGNATURE_MAX_SIZE]; + uint8_t sig2[PSA_SIGNATURE_MAX_SIZE]; + size_t sig1_len = 0; + size_t sig2_len = 0; + psa_status_t st; + + psa_set_key_type(&attrs, + PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attrs, 256); + psa_set_key_usage_flags(&attrs, + PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH); + psa_set_key_algorithm(&attrs, alg); + + st = psa_generate_key(&attrs, &key); + if (expect_status("det_ecdsa: generate", st, PSA_SUCCESS) != 0) + return 1; + + st = psa_sign_hash(key, alg, hash, sizeof(hash), sig1, sizeof(sig1), + &sig1_len); + if (st == PSA_ERROR_NOT_SUPPORTED) { + printf("SKIP test_deterministic_ecdsa (deterministic ECDSA not" + " supported by this build)\n"); + (void)psa_destroy_key(key); + return 0; + } + if (expect_status("det_ecdsa: sign #1", st, PSA_SUCCESS) != 0) { + (void)psa_destroy_key(key); + return 1; + } + + st = psa_sign_hash(key, alg, hash, sizeof(hash), sig2, sizeof(sig2), + &sig2_len); + if (expect_status("det_ecdsa: sign #2", st, PSA_SUCCESS) != 0) { + (void)psa_destroy_key(key); + return 1; + } + + if (sig1_len != sig2_len || memcmp(sig1, sig2, sig1_len) != 0) { + printf("FAIL det_ecdsa: signatures differ, deterministic nonce not" + " applied\n"); + (void)psa_destroy_key(key); + return 1; + } + + st = psa_verify_hash(key, alg, hash, sizeof(hash), sig1, sig1_len); + if (expect_status("det_ecdsa: verify", st, PSA_SUCCESS) != 0) { + (void)psa_destroy_key(key); + return 1; + } + + (void)psa_destroy_key(key); + printf("PASS: deterministic ECDSA identical signatures\n"); + return 0; +} + +/* Case 11: multipart ChaCha20-Poly1305 with a shortened tag must be rejected + * at setup with PSA_ERROR_NOT_SUPPORTED. Truncated Poly1305 tags are not + * implemented, so a shortened-tag algorithm must not be accepted and then fail + * the encrypt/decrypt roundtrip. */ +static int test_chacha20_poly1305_shortened_tag(void) +{ + psa_algorithm_t alg = + PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305, 8); + static const uint8_t key_bytes[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }; + static const uint8_t nonce[12] = { 0 }; + static const uint8_t pt[16] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f + }; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t key = 0; + psa_aead_operation_t op = PSA_AEAD_OPERATION_INIT; + uint8_t ct[sizeof(pt) + 16]; + size_t ct_len = 0; + psa_status_t st; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_CHACHA20); + psa_set_key_bits(&attrs, 256u); + psa_set_key_usage_flags(&attrs, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + /* Bind the key policy to the shortened-tag algorithm so the key-policy + * check passes and setup reaches the tag-length validation. */ + psa_set_key_algorithm(&attrs, alg); + + st = psa_import_key(&attrs, key_bytes, sizeof(key_bytes), &key); + if (st == PSA_ERROR_NOT_SUPPORTED) { + printf("SKIP chacha20_poly1305_shortened_tag (not supported by this" + " build)\n"); + return 0; + } + if (expect_status("chacha short-tag import", st, PSA_SUCCESS) != 0) + return 1; + + /* Multipart setup must reject the shortened tag. */ + st = psa_aead_encrypt_setup(&op, key, alg); + if (expect_status("chacha short-tag encrypt_setup", st, + PSA_ERROR_NOT_SUPPORTED) != 0) { + (void)psa_aead_abort(&op); + (void)psa_destroy_key(key); + return 1; + } + (void)psa_aead_abort(&op); + + /* One-shot path (routes through setup) must reject it too. */ + ct_len = 0; + st = psa_aead_encrypt(key, alg, nonce, sizeof(nonce), NULL, 0, + pt, sizeof(pt), ct, sizeof(ct), &ct_len); + if (expect_status("chacha short-tag encrypt", st, + PSA_ERROR_NOT_SUPPORTED) != 0) { + (void)psa_destroy_key(key); + return 1; + } + + (void)psa_destroy_key(key); + printf("PASS: ChaCha20-Poly1305 shortened tag rejected\n"); + return 0; +} + +/* Case 12: an undersized RSA signature buffer must be reported as + * PSA_ERROR_BUFFER_TOO_SMALL, not PSA_ERROR_GENERIC_ERROR. wc_RsaSSL_Sign + * returns RSA_BUFFER_E when the output buffer is smaller than the modulus, and + * the error translator must map that to BUFFER_TOO_SMALL so callers can retry + * with a larger buffer. */ +static int test_rsa_sign_buffer_too_small(void) +{ + psa_algorithm_t alg = PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256); + static const uint8_t hash[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t key = 0; + uint8_t sig[16]; /* far smaller than a 2048-bit (256-byte) signature */ + size_t sig_len = 0; + psa_status_t st; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_RSA_KEY_PAIR); + psa_set_key_bits(&attrs, 2048u); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_HASH); + psa_set_key_algorithm(&attrs, alg); + + st = psa_generate_key(&attrs, &key); + if (st == PSA_ERROR_NOT_SUPPORTED) { + printf("SKIP rsa_sign_buffer_too_small (RSA not supported by this" + " build)\n"); + return 0; + } + if (expect_status("rsa buffer-too-small: generate", st, PSA_SUCCESS) != 0) + return 1; + + st = psa_sign_hash(key, alg, hash, sizeof(hash), sig, sizeof(sig), + &sig_len); + if (expect_status("rsa sign undersized buffer", st, + PSA_ERROR_BUFFER_TOO_SMALL) != 0) { + (void)psa_destroy_key(key); + return 1; + } + + (void)psa_destroy_key(key); + printf("PASS: RSA sign undersized buffer -> BUFFER_TOO_SMALL\n"); + return 0; +} + +/* Case 13: GCM nonce-length error codes. GCM (SP 800-38D) accepts any + * non-empty nonce; a zero-length nonce is invalid for the algorithm + * (INVALID_ARGUMENT), while non-empty lengths outside the supported 12-24 byte + * range are valid for GCM but not supported by this implementation + * (NOT_SUPPORTED). A 12-byte nonce is accepted. */ +static int test_gcm_nonce_lengths(void) +{ + static const uint8_t key_bytes[16] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + static const uint8_t nonce[25] = { 0 }; + static const uint8_t pt[16] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f + }; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t key = 0; + uint8_t ct[sizeof(pt) + 16]; + uint8_t ptout[sizeof(pt)]; + size_t ct_len = 0; + size_t pt_len = 0; + psa_status_t st; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attrs, 128u); + psa_set_key_usage_flags(&attrs, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_GCM); + + st = psa_import_key(&attrs, key_bytes, sizeof(key_bytes), &key); + if (st == PSA_ERROR_NOT_SUPPORTED) { + printf("SKIP gcm_nonce_lengths (GCM not supported by this build)\n"); + return 0; + } + if (expect_status("gcm nonce import", st, PSA_SUCCESS) != 0) + return 1; + + /* 0 bytes: invalid for GCM */ + st = psa_aead_encrypt(key, PSA_ALG_GCM, nonce, 0, NULL, 0, + pt, sizeof(pt), ct, sizeof(ct), &ct_len); + if (expect_status("gcm nonce 0", st, PSA_ERROR_INVALID_ARGUMENT) != 0) { + (void)psa_destroy_key(key); + return 1; + } + + /* 8 bytes: valid GCM nonce, unsupported here */ + st = psa_aead_encrypt(key, PSA_ALG_GCM, nonce, 8, NULL, 0, + pt, sizeof(pt), ct, sizeof(ct), &ct_len); + if (expect_status("gcm nonce 8", st, PSA_ERROR_NOT_SUPPORTED) != 0) { + (void)psa_destroy_key(key); + return 1; + } + + /* 25 bytes: valid GCM nonce, exceeds supported max */ + st = psa_aead_encrypt(key, PSA_ALG_GCM, nonce, 25, NULL, 0, + pt, sizeof(pt), ct, sizeof(ct), &ct_len); + if (expect_status("gcm nonce 25", st, PSA_ERROR_NOT_SUPPORTED) != 0) { + (void)psa_destroy_key(key); + return 1; + } + + /* 12 bytes: supported (lower boundary) */ + st = psa_aead_encrypt(key, PSA_ALG_GCM, nonce, 12, NULL, 0, + pt, sizeof(pt), ct, sizeof(ct), &ct_len); + if (expect_status("gcm nonce 12", st, PSA_SUCCESS) != 0) { + (void)psa_destroy_key(key); + return 1; + } + + /* 24 bytes: supported (upper boundary, PSA_AEAD_NONCE_MAX_SIZE) */ + st = psa_aead_encrypt(key, PSA_ALG_GCM, nonce, 24, NULL, 0, + pt, sizeof(pt), ct, sizeof(ct), &ct_len); + if (expect_status("gcm nonce 24", st, PSA_SUCCESS) != 0) { + (void)psa_destroy_key(key); + return 1; + } + + /* The decrypt direction reaches the same nonce validation. ct holds the + * 24-byte-nonce ciphertext from above; only the nonce length is exercised + * here, so the tag never verifies. */ + st = psa_aead_decrypt(key, PSA_ALG_GCM, nonce, 0, NULL, 0, + ct, ct_len, ptout, sizeof(ptout), &pt_len); + if (expect_status("gcm decrypt nonce 0", st, PSA_ERROR_INVALID_ARGUMENT) + != 0) { + (void)psa_destroy_key(key); + return 1; + } + + st = psa_aead_decrypt(key, PSA_ALG_GCM, nonce, 8, NULL, 0, + ct, ct_len, ptout, sizeof(ptout), &pt_len); + if (expect_status("gcm decrypt nonce 8", st, PSA_ERROR_NOT_SUPPORTED) + != 0) { + (void)psa_destroy_key(key); + return 1; + } + + st = psa_aead_decrypt(key, PSA_ALG_GCM, nonce, 25, NULL, 0, + ct, ct_len, ptout, sizeof(ptout), &pt_len); + if (expect_status("gcm decrypt nonce 25", st, PSA_ERROR_NOT_SUPPORTED) + != 0) { + (void)psa_destroy_key(key); + return 1; + } + + (void)psa_destroy_key(key); + printf("PASS: GCM nonce length handling\n"); + return 0; +} + int main(void) { psa_status_t st; @@ -526,6 +873,26 @@ int main(void) if (test_size_macros() != 0) return 1; + /* Case 9: unsupported key lifetime location rejected */ + if (test_unsupported_lifetime_location() != 0) + return 1; + + /* Case 10: deterministic ECDSA identical-signature check */ + if (test_deterministic_ecdsa() != 0) + return 1; + + /* Case 11: ChaCha20-Poly1305 shortened tag rejected at setup */ + if (test_chacha20_poly1305_shortened_tag() != 0) + return 1; + + /* Case 12: undersized RSA signature buffer -> BUFFER_TOO_SMALL */ + if (test_rsa_sign_buffer_too_small() != 0) + return 1; + + /* Case 13: GCM nonce-length error codes */ + if (test_gcm_nonce_lengths() != 0) + return 1; + printf("PSA 1.4 misc test: OK\n"); return 0; } diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index aeeff8e..fe6029d 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -3770,8 +3770,9 @@ static int test_aead_gcm_rejects_short_nonce(void) } st = psa_aead_set_nonce(&op, short_nonce, sizeof(short_nonce)); - if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, - "psa_aead_set_nonce rejects 11-byte GCM nonce") != TEST_OK) { + if (check_true(st == PSA_ERROR_NOT_SUPPORTED, + "psa_aead_set_nonce reports 11-byte GCM nonce unsupported") + != TEST_OK) { goto cleanup; } diff --git a/test/psa_server/psa_ascon_xchacha_test.c b/test/psa_server/psa_ascon_xchacha_test.c index 10c065a..877ccde 100644 --- a/test/psa_server/psa_ascon_xchacha_test.c +++ b/test/psa_server/psa_ascon_xchacha_test.c @@ -631,6 +631,132 @@ static int test_xchacha_key_policy(void) return 0; } +/* ========================================================================= + * Test 5 — Shortened / at-least-this-length tag variants must be rejected + * + * The one-shot XChaCha20-Poly1305 and Ascon-AEAD128 paths only implement the + * native 16-byte tag. A shortened-tag (or at-least-this-length) algorithm must + * be rejected with PSA_ERROR_NOT_SUPPORTED rather than silently producing or + * requiring a 16-byte tag while the caller expects a shorter one. + * ====================================================================== */ + +static int test_aead_shortened_tag_rejected(void) +{ + psa_key_attributes_t attrs; + psa_key_id_t key_id = PSA_KEY_ID_NULL; + uint8_t enc_out[130]; + uint8_t dec_out[130]; + size_t out_len = 0; + psa_status_t st; + psa_algorithm_t xch_short = + PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_XCHACHA20_POLY1305, 12); + psa_algorithm_t xch_atleast = + PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_XCHACHA20_POLY1305, + 16); + psa_algorithm_t asc_short = + PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_ASCON_AEAD128, 12); + + /* --- XChaCha20-Poly1305 --------------------------------------------- */ + attrs = psa_key_attributes_init(); + psa_set_key_type(&attrs, PSA_KEY_TYPE_XCHACHA20); + psa_set_key_bits(&attrs, 256u); + psa_set_key_usage_flags(&attrs, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_XCHACHA20_POLY1305); + + st = psa_import_key(&attrs, xchacha_key, sizeof(xchacha_key), &key_id); + if (st == PSA_ERROR_NOT_SUPPORTED) { + printf("SKIP aead_shortened_tag xchacha (not supported by this build)\n"); + } + else { + if (expect_status("shortened xchacha import", st, PSA_SUCCESS) != 0) + return 1; + + out_len = 0; + st = psa_aead_encrypt(key_id, xch_short, + xchacha_nonce, sizeof(xchacha_nonce), + NULL, 0, xchacha_pt, sizeof(xchacha_pt), + enc_out, sizeof(enc_out), &out_len); + if (expect_status("xchacha shortened-tag encrypt rejected", + st, PSA_ERROR_NOT_SUPPORTED) != 0) { + (void)psa_destroy_key(key_id); + return 1; + } + + out_len = 0; + st = psa_aead_encrypt(key_id, xch_atleast, + xchacha_nonce, sizeof(xchacha_nonce), + NULL, 0, xchacha_pt, sizeof(xchacha_pt), + enc_out, sizeof(enc_out), &out_len); + if (expect_status("xchacha at-least-tag encrypt rejected", + st, PSA_ERROR_NOT_SUPPORTED) != 0) { + (void)psa_destroy_key(key_id); + return 1; + } + + out_len = 0; + st = psa_aead_decrypt(key_id, xch_short, + xchacha_nonce, sizeof(xchacha_nonce), + NULL, 0, enc_out, sizeof(xchacha_pt) + 12u, + dec_out, sizeof(dec_out), &out_len); + if (expect_status("xchacha shortened-tag decrypt rejected", + st, PSA_ERROR_NOT_SUPPORTED) != 0) { + (void)psa_destroy_key(key_id); + return 1; + } + + (void)psa_destroy_key(key_id); + } + + /* --- Ascon-AEAD128 -------------------------------------------------- */ + key_id = PSA_KEY_ID_NULL; + attrs = psa_key_attributes_init(); + psa_set_key_type(&attrs, PSA_KEY_TYPE_ASCON); + psa_set_key_bits(&attrs, 128u); + psa_set_key_usage_flags(&attrs, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_ASCON_AEAD128); + + st = psa_import_key(&attrs, ascon_aead128_key, sizeof(ascon_aead128_key), + &key_id); + if (st == PSA_ERROR_NOT_SUPPORTED) { + printf("SKIP aead_shortened_tag ascon (not supported by this build)\n"); + } + else { + if (expect_status("shortened ascon import", st, PSA_SUCCESS) != 0) + return 1; + + out_len = 0; + st = psa_aead_encrypt(key_id, asc_short, + ascon_aead128_nonce, sizeof(ascon_aead128_nonce), + NULL, 0, ascon_aead128_pt, + sizeof(ascon_aead128_pt), + enc_out, sizeof(enc_out), &out_len); + if (expect_status("ascon shortened-tag encrypt rejected", + st, PSA_ERROR_NOT_SUPPORTED) != 0) { + (void)psa_destroy_key(key_id); + return 1; + } + + out_len = 0; + st = psa_aead_decrypt(key_id, asc_short, + ascon_aead128_nonce, sizeof(ascon_aead128_nonce), + NULL, 0, ascon_aead128_ct_tag, + sizeof(ascon_aead128_pt) + 12u, + dec_out, sizeof(dec_out), &out_len); + if (expect_status("ascon shortened-tag decrypt rejected", + st, PSA_ERROR_NOT_SUPPORTED) != 0) { + (void)psa_destroy_key(key_id); + return 1; + } + + (void)psa_destroy_key(key_id); + } + + printf("aead_shortened_tag_rejected: OK\n"); + return 0; +} + /* ========================================================================= * main * ====================================================================== */ @@ -657,6 +783,9 @@ int main(void) if (test_xchacha_key_policy() != 0) return 1; + if (test_aead_shortened_tag_rejected() != 0) + return 1; + printf("psa_ascon_xchacha_test: all tests passed\n"); return 0; } diff --git a/test/psa_server/psa_sp800_108_test.c b/test/psa_server/psa_sp800_108_test.c index 3d219e9..c9fc0d4 100644 --- a/test/psa_server/psa_sp800_108_test.c +++ b/test/psa_server/psa_sp800_108_test.c @@ -933,6 +933,116 @@ static int test_hmac_duplicate_step_rejected(void) /* ------------------------------------------------------------------------- * main * -------------------------------------------------------------------------*/ +/* Setup an SP800-108 HMAC-SHA256 operation with the given capacity and the + * standard secret/label/context inputs. Returns 0 on success, 1 on failure + * (op aborted). */ +static int sp800_hmac_setup(psa_key_derivation_operation_t *op, + psa_algorithm_t alg, size_t capacity) +{ + psa_status_t st; + + st = psa_key_derivation_setup(op, alg); + if (expect_status("large-cap setup", st, PSA_SUCCESS) != 0) + return 1; + st = psa_key_derivation_set_capacity(op, capacity); + if (expect_status("large-cap set_capacity", st, PSA_SUCCESS) != 0) { + psa_key_derivation_abort(op); + return 1; + } + st = psa_key_derivation_input_bytes(op, PSA_KEY_DERIVATION_INPUT_SECRET, + g_hmac_secret, sizeof(g_hmac_secret)); + if (expect_status("large-cap secret", st, PSA_SUCCESS) != 0) { + psa_key_derivation_abort(op); + return 1; + } + st = psa_key_derivation_input_bytes(op, PSA_KEY_DERIVATION_INPUT_LABEL, + g_label, sizeof(g_label)); + if (expect_status("large-cap label", st, PSA_SUCCESS) != 0) { + psa_key_derivation_abort(op); + return 1; + } + st = psa_key_derivation_input_bytes(op, PSA_KEY_DERIVATION_INPUT_CONTEXT, + g_context, sizeof(g_context)); + if (expect_status("large-cap context", st, PSA_SUCCESS) != 0) { + psa_key_derivation_abort(op); + return 1; + } + return 0; +} + +/* Test case: large-capacity lazy path. + * + * A capacity above the output-cache bound forces the derivation onto the + * lazy, non-cached path. Verify prefix-consistency across the offset==0 + * direct-compute branch and the offset>0 recompute-and-slice branch: two + * consecutive output_bytes() calls, a single output_bytes(64) call on a fresh + * identical derivation, and the independent K(1)||K(2) reconstruction must all + * agree. Existing SP800-108 tests only use small capacities that stay on the + * cached path, so this exercises the otherwise-untested lazy branches. */ +static int test_hmac_large_capacity_lazy(void) +{ + psa_algorithm_t alg = PSA_ALG_SP800_108_COUNTER_HMAC(PSA_ALG_SHA_256); + size_t cap = 100000u; /* above the 64 KB output-cache bound */ + psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); + uint8_t out_a[32]; + uint8_t out_b[32]; + uint8_t out_single[64]; + uint8_t ref[64]; + psa_status_t st; + + /* Two consecutive 32-byte calls: offset 0 (direct compute) then offset 32 + * (recompute-and-slice). */ + if (sp800_hmac_setup(&op, alg, cap) != 0) + return 1; + st = psa_key_derivation_output_bytes(&op, out_a, sizeof(out_a)); + if (expect_status("large-cap out_a", st, PSA_SUCCESS) != 0) { + psa_key_derivation_abort(&op); + return 1; + } + st = psa_key_derivation_output_bytes(&op, out_b, sizeof(out_b)); + if (expect_status("large-cap out_b", st, PSA_SUCCESS) != 0) { + psa_key_derivation_abort(&op); + return 1; + } + psa_key_derivation_abort(&op); + + /* Single 64-byte call on a fresh identical derivation. */ + op = psa_key_derivation_operation_init(); + if (sp800_hmac_setup(&op, alg, cap) != 0) + return 1; + st = psa_key_derivation_output_bytes(&op, out_single, sizeof(out_single)); + if (expect_status("large-cap single", st, PSA_SUCCESS) != 0) { + psa_key_derivation_abort(&op); + return 1; + } + psa_key_derivation_abort(&op); + + /* Independent reconstruction (L = capacity in bits). */ + if (reconstruct_hmac(g_hmac_secret, sizeof(g_hmac_secret), + g_label, sizeof(g_label), + g_context, sizeof(g_context), + cap, ref, sizeof(ref)) != 0) { + printf("FAIL large-cap: reconstruction failed\n"); + return 1; + } + + if (memcmp(out_a, ref, 32u) != 0) { + printf("FAIL large-cap: K(1) mismatch (lazy direct-compute)\n"); + return 1; + } + if (memcmp(out_b, ref + 32, 32u) != 0) { + printf("FAIL large-cap: K(2) mismatch (lazy recompute-and-slice)\n"); + return 1; + } + if (memcmp(out_single, ref, 64u) != 0) { + printf("FAIL large-cap: single-call mismatch\n"); + return 1; + } + + printf("PASS large-cap: SP800-108 lazy path prefix-consistent\n"); + return 0; +} + int main(void) { psa_status_t st; @@ -952,6 +1062,7 @@ int main(void) if (test_cmac_invalid_key_len() != 0) return 1; if (test_hmac_salt_rejected() != 0) return 1; if (test_hmac_duplicate_step_rejected() != 0) return 1; + if (test_hmac_large_capacity_lazy() != 0) return 1; printf("SP800-108 counter-mode KDF test: OK\n"); return 0;