diff --git a/crypto_libraries/inc/nx_crypto_const.h b/crypto_libraries/inc/nx_crypto_const.h index dc3ad4fa5..c7b5a6209 100644 --- a/crypto_libraries/inc/nx_crypto_const.h +++ b/crypto_libraries/inc/nx_crypto_const.h @@ -168,6 +168,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 cded4a411..450336e28 100644 --- a/crypto_libraries/inc/nx_crypto_rsa.h +++ b/crypto_libraries/inc/nx_crypto_rsa.h @@ -123,6 +123,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 e11b874b0..1ea69bba7 100644 --- a/crypto_libraries/src/nx_crypto_rsa.c +++ b/crypto_libraries/src/nx_crypto_rsa.c @@ -445,3 +445,341 @@ 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); + 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_tls_process_certificate_verify.c b/nx_secure/src/nx_secure_tls_process_certificate_verify.c index 052f79f5e..958193208 100644 --- a/nx_secure/src/nx_secure_tls_process_certificate_verify.c +++ b/nx_secure/src/nx_secure_tls_process_certificate_verify.c @@ -25,10 +25,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 _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}; @@ -183,7 +187,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: @@ -195,6 +199,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); } @@ -522,11 +535,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. */ @@ -637,6 +655,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_send_clienthello_extensions.c b/nx_secure/src/nx_secure_tls_send_clienthello_extensions.c index 2bcce88f7..26545fa9f 100644 --- a/nx_secure/src/nx_secure_tls_send_clienthello_extensions.c +++ b/nx_secure/src/nx_secure_tls_send_clienthello_extensions.c @@ -478,6 +478,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) &&