Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions crypto_libraries/inc/nx_crypto_rsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ UINT _nx_crypto_rsa_pss_verify(const UCHAR *message_hash, UINT hash_length,
VOID *hash_metadata, ULONG hash_metadata_size,
UCHAR *scratch, UINT scratch_length);

UINT _nx_crypto_rsa_pss_sign(const UCHAR *message_hash, UINT hash_length,
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
Expand Down
205 changes: 205 additions & 0 deletions crypto_libraries/src/nx_crypto_rsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -752,3 +752,208 @@ static const UCHAR _pss_zero8[8] = {0, 0, 0, 0, 0, 0, 0, 0};
return(NX_CRYPTO_SUCCESS);
}


/**************************************************************************/
/* */
/* FUNCTION RELEASE */
/* */
/* _nx_crypto_rsa_pss_sign PORTABLE C */
/* 6.4.3 */
/* DESCRIPTION */
/* */
/* Builds an EMSA-PSS-ENCODE octet string (RFC 8017 §9.1.1). */
/* Caller is expected to run the RSA private-key operation on the */
/* returned EM to produce the wire signature. */
/* Assumes salt length == hash length (required by RFC 8446 §4.2.3). */
/* Salt bytes are produced internally via NX_CRYPTO_RAND(). */
/* */
/* INPUT */
/* */
/* message_hash Pre-computed mHash over the signed content */
/* hash_length hLen = byte length of mHash */
/* em Output buffer; receives the encoded message */
/* 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 >= db_len bytes */
/* scratch_length Size of scratch in bytes */
/* */
/* OUTPUT */
/* */
/* NX_CRYPTO_SUCCESS EM built successfully */
/* NX_CRYPTO_NOT_SUCCESSFUL em_len smaller than minimum */
/* NX_CRYPTO_INVALID_BUFFER_SIZE Scratch too small */
/* */
/**************************************************************************/
UINT _nx_crypto_rsa_pss_sign(const UCHAR *message_hash, UINT hash_length,
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 *salt;
UCHAR *h;
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] | salt[s_len]. The salt is also copied
* into DB at offset (db_len - s_len), so keeping a separate copy lets
* us hash it BEFORE building maskedDB. */
if (scratch_length < (db_len + s_len))
{
return(NX_CRYPTO_INVALID_BUFFER_SIZE);
}

db = scratch;
salt = scratch + db_len;
h = em + db_len; /* H sits at em[db_len..em_len-2]. */

/* Step 4 – generate the random salt. NX_CRYPTO_RAND() is the same
* source ECDSA uses for its nonce, so the entropy is at least as good
* as the existing signature paths on this device. */
for (i = 0u; i < s_len; i++)
{
salt[i] = (UCHAR)((UINT)NX_CRYPTO_RAND() & 0xFFu);
}

/* Steps 5-6 – H = Hash(0x00^8 || mHash || salt). Write directly into
* em[db_len..] so we don't have to copy H around later. */
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. */
status = hash_method -> nx_crypto_operation(NX_CRYPTO_HASH_UPDATE,
handler, (NX_CRYPTO_METHOD *)hash_method,
NX_CRYPTO_NULL, 0,
salt, (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, (ULONG)hash_length,
hash_metadata, hash_metadata_size,
NX_CRYPTO_NULL, NX_CRYPTO_NULL);
if (status != NX_CRYPTO_SUCCESS)
{
return(status);
}

/* Steps 7-8 – DB = PS (zeros) || 0x01 || salt. */
for (i = 0u; i < db_len - s_len - 1u; i++)
{
db[i] = 0x00u;
}
db[db_len - s_len - 1u] = 0x01u;
NX_CRYPTO_MEMCPY(&db[db_len - s_len], salt, s_len); /* Use case of memcpy is verified. */

/* Step 9 – dbMask = MGF1(H, db_len). Write directly into em[0..db_len-1]. */
status = _nx_crypto_rsa_pss_mgf1(hash_method, hash_metadata, hash_metadata_size,
h, hash_length, em, db_len);
if (status != NX_CRYPTO_SUCCESS)
{
return(status);
}

/* Step 10 – maskedDB = DB XOR dbMask (em[0..db_len-1] currently holds dbMask). */
for (i = 0u; i < db_len; i++)
{
em[i] ^= db[i];
}

/* Step 11 – zero the top (8*emLen - emBits) bits of maskedDB[0]. */
zero_bits = (UCHAR)(8u * em_len - em_bits);
if (zero_bits)
{
em[0] &= (UCHAR)(0xFFu >> zero_bits);
}

/* Step 12 – EM = maskedDB || H || 0xBC. H is already in place; trailer last. */
em[em_len - 1u] = 0xBCu;

#ifdef NX_SECURE_KEY_CLEAR
NX_CRYPTO_MEMSET(db, 0, db_len);
NX_CRYPTO_MEMSET(salt, 0, s_len);
#endif

return(NX_CRYPTO_SUCCESS);
}

13 changes: 10 additions & 3 deletions nx_secure/src/nx_secure_tls_process_certificate_request.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,18 @@ UINT extension_type;
{
tls_session -> nx_secure_tls_signature_algorithm = 0;

/* TLS1.3 uses RSASSA-PSS instead of RSASSA-PKCS. RSASSA-PSS is not supported now. */
/* TLS 1.3 + RSA cert: announce RSASSA-PSS (RFC 8446 §4.2.3 — PKCS#1 v1.5 is banned for
* CertificateVerify in TLS 1.3). The send_certificate_verify path builds the EMSA-PSS-ENCODE
* via _nx_crypto_rsa_pss_sign. We pick rsa_pss_rsae_sha256 (0x0804) unconditionally — it's
* the only PSS sigalg our nx_crypto_rsa.c currently supports for signing, and any modern
* broker (mosquitto/OpenSSL) advertises it in CertificateRequest.sig_algs. */
if (local_certificate -> nx_secure_x509_public_algorithm == NX_SECURE_TLS_X509_TYPE_RSA)
{
return(NX_SECURE_TLS_UNSUPPORTED_CERT_SIGN_TYPE);
expected_cert_type = NX_SECURE_TLS_CERT_TYPE_RSA_SIGN;
expected_sign_alg = 0x0804u; /* rsa_pss_rsae_sha256 */
}

else
{
/* In TLS 1.3, the signing curve is constrained. */
switch (local_certificate -> nx_secure_x509_private_key.ec_private_key.nx_secure_ec_named_curve)
{
Expand All @@ -141,6 +147,7 @@ UINT extension_type;
default:
return(NX_SECURE_TLS_UNSUPPORTED_CERT_SIGN_ALG);
}
}
}
else
#endif
Expand Down
68 changes: 64 additions & 4 deletions nx_secure/src/nx_secure_tls_send_certificate_verify.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@
#define NX_SECURE_SOURCE_CODE

#include "nx_secure_tls.h"
#include "nx_crypto_rsa.h"
#ifdef NX_SECURE_ENABLE_DTLS
#include "nx_secure_dtls.h"
#endif /* NX_SECURE_ENABLE_DTLS */

#ifndef NX_SECURE_DISABLE_X509
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_3_ENABLED)
static UCHAR _nx_secure_pss_scratch[600]; /* PSS encode: db[<=511 B] + salt[<=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};
Expand Down Expand Up @@ -174,7 +178,10 @@ NX_CRYPTO_EXTENDED_OUTPUT extended_output;
#if (NX_SECURE_TLS_TLS_1_3_ENABLED)
if (tls_session -> nx_secure_tls_1_3)
{
/* Signature algorithm is set when processing CertificateRequest or SignatureAlgorithm extension. */
/* Signature algorithm is set when processing CertificateRequest or SignatureAlgorithm extension.
* For RSA in TLS 1.3 the wire codes are rsa_pss_rsae_sha256/384/512 (0x0804/0805/0806). We reuse
* the existing RSA_SHA_* x509 cert types because the cipher/hash table lookup is shared with
* PKCS#1 — only the EMSA encoding step downstream switches to PSS. */
switch (tls_session -> nx_secure_tls_signature_algorithm)
{
case NX_SECURE_TLS_SIGNATURE_ECDSA_SHA256:
Expand All @@ -186,6 +193,15 @@ NX_CRYPTO_EXTENDED_OUTPUT extended_output;
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);
}
Expand Down Expand Up @@ -528,6 +544,43 @@ NX_CRYPTO_EXTENDED_OUTPUT extended_output;

#endif /* NX_SECURE_ENABLE_DTLS */
{
#if (NX_SECURE_TLS_TLS_1_3_ENABLED)
if (tls_session -> nx_secure_tls_1_3)
{
/* TLS 1.3 CertificateVerify wire format: SignatureScheme (2 bytes, big-endian) || length (2 bytes) || signature.
* nx_secure_tls_signature_algorithm carries the wire code (rsa_pss_rsae_sha256/384/512 = 0x0804/0805/0806)
* stored by process_certificate_request. The PSS-encoded EM is built directly into _nx_secure_padded_signature
* at full size — the PKCS#1 v1.5 padding loop further down is gated to skip when nx_secure_tls_1_3 is set. */
current_buffer[length] = (UCHAR)((tls_session -> nx_secure_tls_signature_algorithm) >> 8);
current_buffer[length + 1] = (UCHAR)((tls_session -> nx_secure_tls_signature_algorithm) & 0xFFu);
current_buffer[length + 2] = (UCHAR)(data_size >> 8);
current_buffer[length + 3] = (UCHAR)(data_size);
length += 4;

/* Sentinel: signature_length == data_size makes the PKCS#1 loop body below run zero iterations
* (i < data_size - data_size - 1 underflows; we explicitly skip it via the nx_secure_tls_1_3 guard). */
signature_length = data_size;

status = _nx_crypto_rsa_pss_sign(handshake_hash, handshake_hash_length,
_nx_secure_padded_signature,
(data_size << 3) - 1u,
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));
if (status != NX_CRYPTO_SUCCESS)
{
#ifdef NX_SECURE_KEY_CLEAR
NX_SECURE_MEMSET(handshake_hash, 0, sizeof(handshake_hash));
NX_SECURE_MEMSET(_nx_secure_padded_signature, 0, sizeof(_nx_secure_padded_signature));
NX_SECURE_MEMSET(_nx_secure_pss_scratch, 0, sizeof(_nx_secure_pss_scratch));
#endif /* NX_SECURE_KEY_CLEAR */
return(status);
}
}
else
#endif /* NX_SECURE_TLS_TLS_1_3_ENABLED */
{
/* Signature algorithm used. */
current_buffer[length] = NX_SECURE_TLS_HASH_ALGORITHM_SHA256; /* We only support SHA-256 right now. */
current_buffer[length + 1] = NX_SECURE_TLS_SIGNATURE_ALGORITHM_RSA; /* RSA */
Expand Down Expand Up @@ -560,6 +613,7 @@ NX_CRYPTO_EXTENDED_OUTPUT extended_output;

/* Now put the data into the padded buffer - must be at the end. */
NX_SECURE_MEMCPY(&working_ptr[19], handshake_hash, 32); /* Use case of memcpy is verified. lgtm[cpp/banned-api-usage-required-any] */
}
}
#endif

Expand Down Expand Up @@ -609,10 +663,16 @@ NX_CRYPTO_EXTENDED_OUTPUT extended_output;
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. */

_nx_secure_padded_signature[1] = 0x1; /* Block type is 0x00, 0x01 for signatures */
for (i = 2; i < (data_size - signature_length - 1); ++i)
#if (NX_SECURE_TLS_TLS_1_3_ENABLED)
/* TLS 1.3 uses RSA-PSS, which already wrote the full EM into _nx_secure_padded_signature above. */
if (!tls_session -> nx_secure_tls_1_3)
#endif
{
_nx_secure_padded_signature[i] = (UCHAR)0xFF;
_nx_secure_padded_signature[1] = 0x1; /* Block type is 0x00, 0x01 for signatures */
for (i = 2; i < (data_size - signature_length - 1); ++i)
{
_nx_secure_padded_signature[i] = (UCHAR)0xFF;
}
}

/* Check for user-defined keys. */
Expand Down
Loading