From 17d3abf227d9be925fd0559366e8ac91a7504e4c Mon Sep 17 00:00:00 2001 From: Edouard Malot Date: Thu, 21 May 2026 21:23:32 +0200 Subject: Added RSA-PSS signature verification support (TLS 1.3 with RSA cert) (#377) * Add RSA-PSS * Fix SHA-384/512 transcript hash truncation and add MGF1 bounds check Address review feedback on RSA-PSS PR: - Resize handshake_hash buffer from 130 to 162 bytes (64+34+64) in both nx_secure_tls_process_certificate_verify.c and nx_secure_tls_send_certificate_verify.c to fit SHA-512 transcript. - Replace hardcoded 32-byte transcript hash copy with dynamic length derived from hash_method->nx_crypto_ICV_size_in_bits. - Add bounds check in _nx_crypto_rsa_pss_mgf1 to reject hash_method whose output exceeds the local hash_buf size. * Fix RSA-PSS verify in TLS 1.2 ServerKeyExchange Extend RSA-PSS support to TLS 1.2 ServerKeyExchange signature verify: - nx_secure_process_server_key_exchange.c: detect RSA-PSS wire codes (0x0804/0805/0806) and dispatch to _nx_crypto_rsa_pss_verify after RSA decrypt; PKCS#1 v1.5 path kept for non-PSS signatures. - nx_secure_tls_process_clienthello_extensions.c: map RSA-PSS wire codes to the existing RSA SHA-256/384/512 internal IDs so the certificate cipher table lookup finds the right hash method. --- crypto_libraries/inc/nx_crypto_const.h | 1 + crypto_libraries/inc/nx_crypto_rsa.h | 6 + crypto_libraries/src/nx_crypto_rsa.c | 345 +++++++++++++++++++++ .../src/nx_secure_process_server_key_exchange.c | 60 ++++ .../src/nx_secure_tls_process_certificate_verify.c | 73 ++++- .../nx_secure_tls_process_clienthello_extensions.c | 12 + .../src/nx_secure_tls_send_certificate_verify.c | 21 +- .../nx_secure_tls_send_clienthello_extensions.c | 22 ++ 8 files changed, 517 insertions(+), 23 deletions(-) diff --git a/crypto_libraries/inc/nx_crypto_const.h b/crypto_libraries/inc/nx_crypto_const.h index 55f30b39..5c5fa851 100644 --- a/crypto_libraries/inc/nx_crypto_const.h +++ b/crypto_libraries/inc/nx_crypto_const.h @@ -150,6 +150,7 @@ extern "C" { #define NX_CRYPTO_DIGITAL_SIGNATURE_RSA 0x00050001 #define NX_CRYPTO_DIGITAL_SIGNATURE_DSA 0x00050002 #define NX_CRYPTO_DIGITAL_SIGNATURE_ECDSA 0x00050003 +#define NX_CRYPTO_DIGITAL_SIGNATURE_RSAPSS 0x00050004 /*Define the elliptic curve algorithm */ /* Values of 16 least significant bits are the same as named curve defined in RFC 4492, section 5.1.1 */ diff --git a/crypto_libraries/inc/nx_crypto_rsa.h b/crypto_libraries/inc/nx_crypto_rsa.h index 609e9198..587d9858 100644 --- a/crypto_libraries/inc/nx_crypto_rsa.h +++ b/crypto_libraries/inc/nx_crypto_rsa.h @@ -116,6 +116,12 @@ UINT _nx_crypto_method_rsa_init(struct NX_CRYPTO_METHOD_STRUCT *method, VOID **handle, VOID *crypto_metadata, ULONG crypto_metadata_size); +UINT _nx_crypto_rsa_pss_verify(const UCHAR *message_hash, UINT hash_length, + const UCHAR *em, UINT em_bits, + const NX_CRYPTO_METHOD *hash_method, + VOID *hash_metadata, ULONG hash_metadata_size, + UCHAR *scratch, UINT scratch_length); + #ifdef __cplusplus } #endif diff --git a/crypto_libraries/src/nx_crypto_rsa.c b/crypto_libraries/src/nx_crypto_rsa.c index 0017c741..550fc66e 100644 --- a/crypto_libraries/src/nx_crypto_rsa.c +++ b/crypto_libraries/src/nx_crypto_rsa.c @@ -407,3 +407,348 @@ UINT return_value = NX_CRYPTO_SUCCESS; return(return_value); } + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _nx_crypto_rsa_pss_mgf1 PORTABLE C */ +/* 6.4.3 */ +/* DESCRIPTION */ +/* */ +/* Mask Generation Function 1 (MGF1) as defined in RFC 8017 §B.2.1. */ +/* Generates a pseudo-random octet string of length mask_length from */ +/* a seed, using the supplied hash function. */ +/* */ +/* INPUT */ +/* */ +/* hash_method Hash function (e.g. SHA-256/384/512) */ +/* hash_metadata Scratch memory for hash operations */ +/* hash_metadata_size Size of hash_metadata in bytes */ +/* seed MGF seed (typically the PSS H value) */ +/* seed_length Length of seed in bytes */ +/* mask Output buffer for generated mask */ +/* mask_length Desired mask length in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* status NX_CRYPTO_SUCCESS or error code */ +/* */ +/**************************************************************************/ +static UINT _nx_crypto_rsa_pss_mgf1(const NX_CRYPTO_METHOD *hash_method, + VOID *hash_metadata, ULONG hash_metadata_size, + const UCHAR *seed, UINT seed_length, + UCHAR *mask, UINT mask_length) +{ +UINT counter; +UINT offset; +UINT copy_len; +UINT hash_len; +UINT status; +UCHAR counter_bytes[4]; +UCHAR hash_buf[64]; /* large enough for SHA-512 */ +VOID *handler = NX_CRYPTO_NULL; + + hash_len = (UINT)(hash_method -> nx_crypto_ICV_size_in_bits >> 3); + + /* Sanity check: hash_buf must be large enough to hold one hash output. */ + if (hash_len == 0u || hash_len > sizeof(hash_buf)) + { + return(NX_CRYPTO_INVALID_BUFFER_SIZE); + } + + offset = 0; + + for (counter = 0; offset < mask_length; counter++) + { + counter_bytes[0] = (UCHAR)((counter >> 24) & 0xFFu); + counter_bytes[1] = (UCHAR)((counter >> 16) & 0xFFu); + counter_bytes[2] = (UCHAR)((counter >> 8) & 0xFFu); + counter_bytes[3] = (UCHAR)( counter & 0xFFu); + + if (hash_method -> nx_crypto_init) + { + status = hash_method -> nx_crypto_init((NX_CRYPTO_METHOD *)hash_method, + NX_CRYPTO_NULL, 0, + &handler, + hash_metadata, hash_metadata_size); + if (status != NX_CRYPTO_SUCCESS) + { + return(status); + } + } + + status = hash_method -> nx_crypto_operation(NX_CRYPTO_HASH_INITIALIZE, + handler, (NX_CRYPTO_METHOD *)hash_method, + NX_CRYPTO_NULL, 0, + NX_CRYPTO_NULL, 0, NX_CRYPTO_NULL, + NX_CRYPTO_NULL, 0, + hash_metadata, hash_metadata_size, + NX_CRYPTO_NULL, NX_CRYPTO_NULL); + if (status != NX_CRYPTO_SUCCESS) + { + return(status); + } + + status = hash_method -> nx_crypto_operation(NX_CRYPTO_HASH_UPDATE, + handler, (NX_CRYPTO_METHOD *)hash_method, + NX_CRYPTO_NULL, 0, + (UCHAR *)seed, (ULONG)seed_length, + NX_CRYPTO_NULL, + NX_CRYPTO_NULL, 0, + hash_metadata, hash_metadata_size, + NX_CRYPTO_NULL, NX_CRYPTO_NULL); + if (status != NX_CRYPTO_SUCCESS) + { + return(status); + } + + status = hash_method -> nx_crypto_operation(NX_CRYPTO_HASH_UPDATE, + handler, (NX_CRYPTO_METHOD *)hash_method, + NX_CRYPTO_NULL, 0, + counter_bytes, 4, + NX_CRYPTO_NULL, + NX_CRYPTO_NULL, 0, + hash_metadata, hash_metadata_size, + NX_CRYPTO_NULL, NX_CRYPTO_NULL); + if (status != NX_CRYPTO_SUCCESS) + { + return(status); + } + + status = hash_method -> nx_crypto_operation(NX_CRYPTO_HASH_CALCULATE, + handler, (NX_CRYPTO_METHOD *)hash_method, + NX_CRYPTO_NULL, 0, + NX_CRYPTO_NULL, 0, + NX_CRYPTO_NULL, + hash_buf, (ULONG)sizeof(hash_buf), + hash_metadata, hash_metadata_size, + NX_CRYPTO_NULL, NX_CRYPTO_NULL); + if (status != NX_CRYPTO_SUCCESS) + { + return(status); + } + + copy_len = mask_length - offset; + if (copy_len > hash_len) + { + copy_len = hash_len; + } + NX_CRYPTO_MEMCPY(&mask[offset], hash_buf, copy_len); /* Use case of memcpy is verified. */ + offset += copy_len; + } + + return(NX_CRYPTO_SUCCESS); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _nx_crypto_rsa_pss_verify PORTABLE C */ +/* 6.4.3 */ +/* DESCRIPTION */ +/* */ +/* Verifies an RSA-PSS signature encoding (RFC 8017 §9.1.2). */ +/* Used by TLS 1.3 CertificateVerify processing. */ +/* Assumes salt length == hash length (required by RFC 8446 §4.2.3). */ +/* */ +/* INPUT */ +/* */ +/* message_hash Pre-computed mHash over the signed content */ +/* hash_length hLen = byte length of mHash */ +/* em Encoded message from RSA public-key operation */ +/* em_bits emBits = modulus_bits - 1 */ +/* hash_method Same hash used to build the PSS encoding */ +/* hash_metadata Scratch memory for hash operations */ +/* hash_metadata_size Size of hash_metadata in bytes */ +/* scratch Work buffer; must be >= ceil(emBits/8) bytes */ +/* scratch_length Size of scratch in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* NX_CRYPTO_SUCCESS Signature is valid */ +/* NX_CRYPTO_NOT_SUCCESSFUL Signature is invalid */ +/* NX_CRYPTO_INVALID_BUFFER_SIZE Buffers too small */ +/* */ +/**************************************************************************/ +UINT _nx_crypto_rsa_pss_verify(const UCHAR *message_hash, UINT hash_length, + const UCHAR *em, UINT em_bits, + const NX_CRYPTO_METHOD *hash_method, + VOID *hash_metadata, ULONG hash_metadata_size, + UCHAR *scratch, UINT scratch_length) +{ +UINT em_len; +UINT db_len; +UINT s_len; +UINT i; +UINT status; +UCHAR zero_bits; +UCHAR *db; +UCHAR *h_prime; +const UCHAR *h; +const UCHAR *masked_db; +VOID *handler = NX_CRYPTO_NULL; +static const UCHAR _pss_zero8[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + /* emLen = ceil(emBits / 8). */ + em_len = (em_bits + 7u) >> 3; + + /* TLS 1.3 mandates salt length == hash length (RFC 8446 §4.2.3). */ + s_len = hash_length; + + if (em_len < (hash_length + s_len + 2u)) + { + return(NX_CRYPTO_NOT_SUCCESSFUL); + } + + db_len = em_len - hash_length - 1u; + + /* scratch layout: db[db_len] | h_prime[hash_length]. */ + if (scratch_length < (db_len + hash_length)) + { + return(NX_CRYPTO_INVALID_BUFFER_SIZE); + } + + db = scratch; + h_prime = scratch + db_len; + + /* Step 4 – last byte must be 0xBC. */ + if (em[em_len - 1u] != 0xBCu) + { + return(NX_CRYPTO_NOT_SUCCESSFUL); + } + + /* maskedDB = em[0..db_len-1], H = em[db_len..em_len-2]. */ + masked_db = em; + h = em + db_len; + + /* Step 6 – top (8*emLen - emBits) bits of em[0] must be zero. */ + zero_bits = (UCHAR)(8u * em_len - em_bits); + if (zero_bits && (em[0] & (UCHAR)(0xFFu << (8u - zero_bits)))) + { + return(NX_CRYPTO_NOT_SUCCESSFUL); + } + + /* Step 7 – dbMask = MGF1(H, db_len). */ + status = _nx_crypto_rsa_pss_mgf1(hash_method, hash_metadata, hash_metadata_size, + h, hash_length, db, db_len); + if (status != NX_CRYPTO_SUCCESS) + { + return(status); + } + + /* Step 8 – DB = maskedDB XOR dbMask. */ + for (i = 0u; i < db_len; i++) + { + db[i] ^= masked_db[i]; + } + + /* Step 9 – zero the top bits of DB[0]. */ + if (zero_bits) + { + db[0] &= (UCHAR)(0xFFu >> zero_bits); + } + + /* Steps 10-11 – PS (all zeros) then 0x01 separator. */ + for (i = 0u; i < db_len - s_len - 1u; i++) + { + if (db[i] != 0x00u) + { + return(NX_CRYPTO_NOT_SUCCESSFUL); + } + } + if (db[db_len - s_len - 1u] != 0x01u) + { + return(NX_CRYPTO_NOT_SUCCESSFUL); + } + + /* Steps 13-14 – H' = Hash(0x00^8 || mHash || salt). */ + if (hash_method -> nx_crypto_init) + { + status = hash_method -> nx_crypto_init((NX_CRYPTO_METHOD *)hash_method, + NX_CRYPTO_NULL, 0, + &handler, + hash_metadata, hash_metadata_size); + if (status != NX_CRYPTO_SUCCESS) + { + return(status); + } + } + + status = hash_method -> nx_crypto_operation(NX_CRYPTO_HASH_INITIALIZE, + handler, (NX_CRYPTO_METHOD *)hash_method, + NX_CRYPTO_NULL, 0, + NX_CRYPTO_NULL, 0, NX_CRYPTO_NULL, + NX_CRYPTO_NULL, 0, + hash_metadata, hash_metadata_size, + NX_CRYPTO_NULL, NX_CRYPTO_NULL); + if (status != NX_CRYPTO_SUCCESS) + { + return(status); + } + + /* Hash 8 zero bytes. */ + status = hash_method -> nx_crypto_operation(NX_CRYPTO_HASH_UPDATE, + handler, (NX_CRYPTO_METHOD *)hash_method, + NX_CRYPTO_NULL, 0, + (UCHAR *)_pss_zero8, 8, + NX_CRYPTO_NULL, + NX_CRYPTO_NULL, 0, + hash_metadata, hash_metadata_size, + NX_CRYPTO_NULL, NX_CRYPTO_NULL); + if (status != NX_CRYPTO_SUCCESS) + { + return(status); + } + + /* Hash mHash. */ + status = hash_method -> nx_crypto_operation(NX_CRYPTO_HASH_UPDATE, + handler, (NX_CRYPTO_METHOD *)hash_method, + NX_CRYPTO_NULL, 0, + (UCHAR *)message_hash, (ULONG)hash_length, + NX_CRYPTO_NULL, + NX_CRYPTO_NULL, 0, + hash_metadata, hash_metadata_size, + NX_CRYPTO_NULL, NX_CRYPTO_NULL); + if (status != NX_CRYPTO_SUCCESS) + { + return(status); + } + + /* Hash salt = DB[db_len - s_len .. db_len - 1]. */ + status = hash_method -> nx_crypto_operation(NX_CRYPTO_HASH_UPDATE, + handler, (NX_CRYPTO_METHOD *)hash_method, + NX_CRYPTO_NULL, 0, + &db[db_len - s_len], (ULONG)s_len, + NX_CRYPTO_NULL, + NX_CRYPTO_NULL, 0, + hash_metadata, hash_metadata_size, + NX_CRYPTO_NULL, NX_CRYPTO_NULL); + if (status != NX_CRYPTO_SUCCESS) + { + return(status); + } + + status = hash_method -> nx_crypto_operation(NX_CRYPTO_HASH_CALCULATE, + handler, (NX_CRYPTO_METHOD *)hash_method, + NX_CRYPTO_NULL, 0, + NX_CRYPTO_NULL, 0, NX_CRYPTO_NULL, + h_prime, (ULONG)hash_length, + hash_metadata, hash_metadata_size, + NX_CRYPTO_NULL, NX_CRYPTO_NULL); + if (status != NX_CRYPTO_SUCCESS) + { + return(status); + } + + /* Step 15 – compare H == H'. */ + if (NX_CRYPTO_MEMCMP(h, h_prime, hash_length) != 0) + { + return(NX_CRYPTO_NOT_SUCCESSFUL); + } + + return(NX_CRYPTO_SUCCESS); +} + diff --git a/nx_secure/src/nx_secure_process_server_key_exchange.c b/nx_secure/src/nx_secure_process_server_key_exchange.c index 32ace90c..e7b9f807 100644 --- a/nx_secure/src/nx_secure_process_server_key_exchange.c +++ b/nx_secure/src/nx_secure_process_server_key_exchange.c @@ -27,12 +27,14 @@ #ifdef NX_SECURE_ENABLE_DTLS #include "nx_secure_dtls.h" #endif /* NX_SECURE_ENABLE_DTLS */ +#include "nx_crypto_rsa.h" #ifndef NX_SECURE_TLS_CLIENT_DISABLED #if defined(NX_SECURE_ENABLE_ECC_CIPHERSUITE) && !defined(NX_SECURE_DISABLE_X509) static UCHAR hash[64]; /* We concatenate MD5 and SHA-1 hashes into this buffer, OR SHA-256, SHA-384, SHA512. */ static UCHAR decrypted_signature[512]; +static UCHAR _nx_secure_ske_pss_scratch[600]; /* PSS verify scratch: db[<=511 B] + h_prime[<=64 B] for RSA-4096+SHA-512 */ #endif /* NX_SECURE_ENABLE_ECC_CIPHERSUITE && !NX_SECURE_DISABLE_X509 */ /**************************************************************************/ @@ -129,6 +131,7 @@ UCHAR *current_buffer; UCHAR hash_algorithm; UCHAR signature_algorithm; USHORT signature_algorithm_id; +UINT is_pss = NX_FALSE; #if (NX_SECURE_TLS_TLS_1_0_ENABLED || NX_SECURE_TLS_TLS_1_1_ENABLED) UINT i; #endif /* NX_SECURE_TLS_TLS_1_0_ENABLED || NX_SECURE_TLS_TLS_1_1_ENABLED */ @@ -330,6 +333,32 @@ UINT i; hash_algorithm = current_buffer[0]; signature_algorithm = current_buffer[1]; current_buffer += 2; + + /* Detect RSASSA-PSS signature schemes (RFC 8446 §4.2.3). The wire + tuple (0x08, 0x04/05/06) is not a valid TLS 1.2 (hash, sig) pair; + it encodes the 16-bit SignatureScheme code. Normalize it back to + (RSA, SHA-x) so the existing hash computation and RSA decrypt + path is reused; PSS-specific verify is selected via is_pss. */ + switch (((UINT)hash_algorithm << 8) + (UINT)signature_algorithm) + { + case 0x0804u: /* rsa_pss_rsae_sha256 */ + hash_algorithm = NX_SECURE_TLS_HASH_ALGORITHM_SHA256; + signature_algorithm = NX_SECURE_TLS_SIGNATURE_ALGORITHM_RSA; + is_pss = NX_TRUE; + break; + case 0x0805u: /* rsa_pss_rsae_sha384 */ + hash_algorithm = NX_SECURE_TLS_HASH_ALGORITHM_SHA384; + signature_algorithm = NX_SECURE_TLS_SIGNATURE_ALGORITHM_RSA; + is_pss = NX_TRUE; + break; + case 0x0806u: /* rsa_pss_rsae_sha512 */ + hash_algorithm = NX_SECURE_TLS_HASH_ALGORITHM_SHA512; + signature_algorithm = NX_SECURE_TLS_SIGNATURE_ALGORITHM_RSA; + is_pss = NX_TRUE; + break; + default: + break; + } } } @@ -740,6 +769,36 @@ UINT i; } handler = NX_NULL; + if (is_pss) + { + /* RSASSA-PSS verification (RFC 8017 §9.1.2). The RSA "decrypt" + above gave us the encoded message EM; the hash to verify is + the SKE handshake hash already computed in hash[]. */ + UINT hash_len = (UINT)(hash_method -> nx_crypto_ICV_size_in_bits >> 3); + UINT em_bits = ((UINT)signature_length << 3) - 1u; /* emBits = modBits - 1 */ + + status = _nx_crypto_rsa_pss_verify( + hash, hash_len, + decrypted_signature, em_bits, + hash_method, + tls_handshake_hash -> nx_secure_tls_handshake_hash_scratch, + tls_handshake_hash -> nx_secure_tls_handshake_hash_scratch_size, + _nx_secure_ske_pss_scratch, sizeof(_nx_secure_ske_pss_scratch)); + +#ifdef NX_SECURE_KEY_CLEAR + NX_SECURE_MEMSET(hash, 0, sizeof(hash)); + NX_SECURE_MEMSET(decrypted_signature, 0, sizeof(decrypted_signature)); + NX_SECURE_MEMSET(_nx_secure_ske_pss_scratch, 0, sizeof(_nx_secure_ske_pss_scratch)); +#endif /* NX_SECURE_KEY_CLEAR */ + + if (status != NX_CRYPTO_SUCCESS) + { + return(NX_SECURE_TLS_SIGNATURE_VERIFICATION_ERROR); + } + } + else + { + #if (NX_SECURE_TLS_TLS_1_0_ENABLED || NX_SECURE_TLS_TLS_1_1_ENABLED) #ifdef NX_SECURE_ENABLE_DTLS if (protocol_version == NX_SECURE_TLS_VERSION_TLS_1_0 || @@ -810,6 +869,7 @@ UINT i; { return(NX_SECURE_TLS_SIGNATURE_VERIFICATION_ERROR); } + } /* end of !is_pss PKCS#1 v1.5 path */ } else if (signature_algorithm == NX_SECURE_TLS_SIGNATURE_ALGORITHM_ECDSA && auth_method -> nx_crypto_algorithm == NX_CRYPTO_DIGITAL_SIGNATURE_ECDSA) diff --git a/nx_secure/src/nx_secure_tls_process_certificate_verify.c b/nx_secure/src/nx_secure_tls_process_certificate_verify.c index 0d5bcd0d..e817767f 100644 --- a/nx_secure/src/nx_secure_tls_process_certificate_verify.c +++ b/nx_secure/src/nx_secure_tls_process_certificate_verify.c @@ -26,10 +26,14 @@ #ifdef NX_SECURE_ENABLE_DTLS #include "nx_secure_dtls.h" #endif /* NX_SECURE_ENABLE_DTLS */ +#include "nx_crypto_rsa.h" #ifndef NX_SECURE_DISABLE_X509 -static UCHAR handshake_hash[64 + 34 + 32]; /* We concatenate MD5 and SHA-1 hashes into this buffer, OR SHA-256. */ +static UCHAR handshake_hash[64 + 34 + 64]; /* We concatenate MD5 and SHA-1 hashes into this buffer, OR SHA-256/384/512. */ static UCHAR _nx_secure_decrypted_signature[600]; +#if (NX_SECURE_TLS_TLS_1_3_ENABLED) +static UCHAR _nx_secure_pss_scratch[600]; /* PSS verify: db[<=511 B] + h_prime[<=64 B] for RSA-4096+SHA-512 */ +#endif #if (NX_SECURE_TLS_TLS_1_2_ENABLED) static const UCHAR _NX_SECURE_OID_SHA256[] = {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; @@ -158,7 +162,7 @@ NX_SECURE_EC_PUBLIC_KEY *ec_pubkey; #if (NX_SECURE_TLS_TLS_1_3_ENABLED) if (tls_session -> nx_secure_tls_1_3) { - /* TLS1.3 uses RSASSA-PSS instead of RSASSA-PKCS. RSASSA-PSS is not supported now. */ + /* Select crypto methods based on the wire signature algorithm code. */ switch ((UINT)((packet_buffer[0] << 8) + packet_buffer[1])) { case NX_SECURE_TLS_SIGNATURE_ECDSA_SHA256: @@ -170,6 +174,15 @@ NX_SECURE_EC_PUBLIC_KEY *ec_pubkey; case NX_SECURE_TLS_SIGNATURE_ECDSA_SHA512: signature_algorithm = NX_SECURE_TLS_X509_TYPE_ECDSA_SHA_512; break; + case 0x0804u: /* rsa_pss_rsae_sha256 */ + signature_algorithm = NX_SECURE_TLS_X509_TYPE_RSA_SHA_256; + break; + case 0x0805u: /* rsa_pss_rsae_sha384 */ + signature_algorithm = NX_SECURE_TLS_X509_TYPE_RSA_SHA_384; + break; + case 0x0806u: /* rsa_pss_rsae_sha512 */ + signature_algorithm = NX_SECURE_TLS_X509_TYPE_RSA_SHA_512; + break; default: return(NX_SECURE_TLS_UNSUPPORTED_CERT_SIGN_ALG); } @@ -222,19 +235,22 @@ NX_SECURE_EC_PUBLIC_KEY *ec_pubkey; NX_SECURE_MEMCPY(&handshake_hash[64], client_context, 34); /* Use case of memcpy is verified. */ } - /* Copy in transcript hash. */ - NX_SECURE_MEMCPY(&handshake_hash[64 + 34], transcript_hash, 32); /* Use case of memcpy is verified. */ - - handshake_hash_length = 130; - - - /* Generate a hash of the data we just produced. */ - /* Use SHA-256 for now... */ + /* Determine hash method and transcript hash length before copying. + hash_method drives the transcript hash size: 32 (SHA-256), 48 (SHA-384), 64 (SHA-512). */ hash_method = crypto_methods -> nx_secure_x509_hash_method; metadata = tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_scratch; metadata_size = tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_scratch_size; + { + UINT transcript_hash_len = (UINT)(hash_method -> nx_crypto_ICV_size_in_bits >> 3); + + /* Copy in transcript hash — length depends on negotiated hash algorithm. */ + NX_SECURE_MEMCPY(&handshake_hash[64 + 34], transcript_hash, transcript_hash_len); /* Use case of memcpy is verified. */ + + handshake_hash_length = 64u + 34u + transcript_hash_len; + } + /* Hash the data using the chosen hash method. */ if (hash_method -> nx_crypto_init) @@ -497,11 +513,16 @@ NX_SECURE_EC_PUBLIC_KEY *ec_pubkey; if (tls_session -> nx_secure_tls_protocol_version == NX_SECURE_TLS_VERSION_TLS_1_2) #endif /* NX_SECURE_ENABLE_DTLS */ { - /* Check the signature method. */ - if (packet_buffer[0] != NX_SECURE_TLS_HASH_ALGORITHM_SHA256 || - packet_buffer[1] != NX_SECURE_TLS_SIGNATURE_ALGORITHM_RSA) + /* Check the signature method (skipped for TLS 1.3: already validated above). */ +#if (NX_SECURE_TLS_TLS_1_3_ENABLED) + if (!tls_session -> nx_secure_tls_1_3) +#endif { - return(NX_SECURE_TLS_UNKNOWN_CERT_SIG_ALGORITHM); + if (packet_buffer[0] != NX_SECURE_TLS_HASH_ALGORITHM_SHA256 || + packet_buffer[1] != NX_SECURE_TLS_SIGNATURE_ALGORITHM_RSA) + { + return(NX_SECURE_TLS_UNKNOWN_CERT_SIG_ALGORITHM); + } } /* Get the length of the encrypted signature data. */ @@ -612,6 +633,30 @@ NX_SECURE_EC_PUBLIC_KEY *ec_pubkey; } } +#if (NX_SECURE_TLS_TLS_1_3_ENABLED) + if (tls_session -> nx_secure_tls_1_3) + { + /* RSA-PSS verification for TLS 1.3 (RFC 8017 §9.1.2, RFC 8446 §4.4.3). */ + UINT hash_len = (UINT)(hash_method -> nx_crypto_ICV_size_in_bits >> 3); + UINT em_bits = (data_size << 3) - 1u; /* emBits = modBits - 1 */ + + status = _nx_crypto_rsa_pss_verify( + handshake_hash, hash_len, + _nx_secure_decrypted_signature, em_bits, + hash_method, + tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_scratch, + tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_scratch_size, + _nx_secure_pss_scratch, sizeof(_nx_secure_pss_scratch)); + +#ifdef NX_SECURE_KEY_CLEAR + NX_SECURE_MEMSET(handshake_hash, 0, sizeof(handshake_hash)); + NX_SECURE_MEMSET(_nx_secure_decrypted_signature, 0, sizeof(_nx_secure_decrypted_signature)); + NX_SECURE_MEMSET(_nx_secure_pss_scratch, 0, sizeof(_nx_secure_pss_scratch)); +#endif /* NX_SECURE_KEY_CLEAR */ + return((status == NX_CRYPTO_SUCCESS) ? NX_SUCCESS : (UINT)NX_SECURE_TLS_CERTIFICATE_VERIFY_FAILURE); + } +#endif /* NX_SECURE_TLS_TLS_1_3_ENABLED */ + /* Check PKCS-1 Signature padding. The scheme is to start with the block type (0x00, 0x01 for signing) then pad with 0xFF bytes (for signing) followed with a single 0 byte right before the payload, which comes at the end of the RSA block. */ diff --git a/nx_secure/src/nx_secure_tls_process_clienthello_extensions.c b/nx_secure/src/nx_secure_tls_process_clienthello_extensions.c index 34a551b2..ef5e70fa 100644 --- a/nx_secure/src/nx_secure_tls_process_clienthello_extensions.c +++ b/nx_secure/src/nx_secure_tls_process_clienthello_extensions.c @@ -1216,6 +1216,18 @@ VOID _nx_secure_tls_get_signature_algorithm_id(UINT signature_algorithm, USHORT case NX_SECURE_TLS_SIGNATURE_ECDSA_SHA512: *signature_algorithm_id = NX_SECURE_TLS_X509_TYPE_ECDSA_SHA_512; break; + /* RSASSA-PSS schemes (RFC 8446 §4.2.3). Mapped to the same internal IDs + as PKCS#1 v1.5 so the certificate cipher table lookup finds the (RSA, + SHA-x) pair. PSS-vs-PKCS1 dispatch is done at signature-verify time. */ + case 0x0804u: /* rsa_pss_rsae_sha256 */ + *signature_algorithm_id = NX_SECURE_TLS_X509_TYPE_RSA_SHA_256; + break; + case 0x0805u: /* rsa_pss_rsae_sha384 */ + *signature_algorithm_id = NX_SECURE_TLS_X509_TYPE_RSA_SHA_384; + break; + case 0x0806u: /* rsa_pss_rsae_sha512 */ + *signature_algorithm_id = NX_SECURE_TLS_X509_TYPE_RSA_SHA_512; + break; default: return; } diff --git a/nx_secure/src/nx_secure_tls_send_certificate_verify.c b/nx_secure/src/nx_secure_tls_send_certificate_verify.c index c022250a..35c10355 100644 --- a/nx_secure/src/nx_secure_tls_send_certificate_verify.c +++ b/nx_secure/src/nx_secure_tls_send_certificate_verify.c @@ -28,7 +28,7 @@ #endif /* NX_SECURE_ENABLE_DTLS */ #ifndef NX_SECURE_DISABLE_X509 -static UCHAR handshake_hash[64 + 34 + 32]; /* We concatenate MD5 and SHA-1 hashes into this buffer, OR SHA-256. */ +static UCHAR handshake_hash[64 + 34 + 64]; /* We concatenate MD5 and SHA-1 hashes into this buffer, OR SHA-256/384/512. */ static UCHAR _nx_secure_padded_signature[600]; #if (NX_SECURE_TLS_TLS_1_2_ENABLED) @@ -241,19 +241,22 @@ NX_CRYPTO_EXTENDED_OUTPUT extended_output; NX_SECURE_MEMCPY(&handshake_hash[64], server_context, 34); /* Use case of memcpy is verified. */ } - /* Copy in transcript hash. */ - NX_SECURE_MEMCPY(&handshake_hash[64 + 34], transcript_hash, 32); /* Use case of memcpy is verified. */ - - handshake_hash_length = 130; - - - /* Generate a hash of the data we just produced. */ - /* Use SHA-256 for now... */ + /* Determine hash method and transcript hash length before copying. + hash_method drives the transcript hash size: 32 (SHA-256), 48 (SHA-384), 64 (SHA-512). */ hash_method = crypto_methods -> nx_secure_x509_hash_method; metadata = tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_scratch; metadata_size = tls_session -> nx_secure_tls_handshake_hash.nx_secure_tls_handshake_hash_scratch_size; + { + UINT transcript_hash_len = (UINT)(hash_method -> nx_crypto_ICV_size_in_bits >> 3); + + /* Copy in transcript hash — length depends on negotiated hash algorithm. */ + NX_SECURE_MEMCPY(&handshake_hash[64 + 34], transcript_hash, transcript_hash_len); /* Use case of memcpy is verified. */ + + handshake_hash_length = 64u + 34u + transcript_hash_len; + } + if (hash_method -> nx_crypto_init) { diff --git a/nx_secure/src/nx_secure_tls_send_clienthello_extensions.c b/nx_secure/src/nx_secure_tls_send_clienthello_extensions.c index 0c9ce53b..08364122 100644 --- a/nx_secure/src/nx_secure_tls_send_clienthello_extensions.c +++ b/nx_secure/src/nx_secure_tls_send_clienthello_extensions.c @@ -451,6 +451,28 @@ UCHAR sig_algo = 0; } } + /* TLS 1.3 requires RSA-PSS instead of PKCS#1 v1.5 (RFC 8446 §4.2.3). + Map RSA + SHA-256/384/512 to rsa_pss_rsae_sha256/384/512 (0x0804/0805/0806). */ + if (tls_session -> nx_secure_tls_1_3 && + sig_algo == NX_SECURE_TLS_SIGNATURE_ALGORITHM_RSA) + { + switch (hash_algo) + { + case NX_SECURE_TLS_HASH_ALGORITHM_SHA256: + *signature_algorithm = 0x0804u; /* rsa_pss_rsae_sha256 */ + break; + case NX_SECURE_TLS_HASH_ALGORITHM_SHA384: + *signature_algorithm = 0x0805u; /* rsa_pss_rsae_sha384 */ + break; + case NX_SECURE_TLS_HASH_ALGORITHM_SHA512: + *signature_algorithm = 0x0806u; /* rsa_pss_rsae_sha512 */ + break; + default: + *signature_algorithm = 0; + } + return; + } + /* In TLS 1.3, the signing curve is constrained. */ if (tls_session -> nx_secure_tls_1_3 && (named_curve != 0) && -- cgit v1.3.1