summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdouard Malot <[email protected]>2026-05-21 21:23:32 +0200
committerGitHub <[email protected]>2026-05-21 15:23:32 -0400
commit17d3abf227d9be925fd0559366e8ac91a7504e4c (patch)
treef712781aae841e27236038b98c0820eaf51aa631
parent8b6e03ac30ab688bec02c69d42f2304b7f72a202 (diff)
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.
-rw-r--r--crypto_libraries/inc/nx_crypto_const.h1
-rw-r--r--crypto_libraries/inc/nx_crypto_rsa.h6
-rw-r--r--crypto_libraries/src/nx_crypto_rsa.c345
-rw-r--r--nx_secure/src/nx_secure_process_server_key_exchange.c60
-rw-r--r--nx_secure/src/nx_secure_tls_process_certificate_verify.c73
-rw-r--r--nx_secure/src/nx_secure_tls_process_clienthello_extensions.c12
-rw-r--r--nx_secure/src/nx_secure_tls_send_certificate_verify.c21
-rw-r--r--nx_secure/src/nx_secure_tls_send_clienthello_extensions.c22
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) &&