Merge pull request #9742 from sameehj/pkcs7-rsa-pss

pkcs7: add RSA-PSS support for SignedData
This commit is contained in:
JacobBarthelmeh
2026-02-25 09:55:00 -07:00
committed by GitHub
15 changed files with 1093 additions and 134 deletions
+2
View File
@@ -53,6 +53,8 @@ jobs:
'--enable-opensslall --enable-opensslextra CPPFLAGS=-DWC_RNG_SEED_CB',
'--enable-opensslall --enable-opensslextra
CPPFLAGS=''-DWC_RNG_SEED_CB -DWOLFSSL_NO_GETPID'' ',
# PKCS#7 with RSA-PSS (CMS RSASSA-PSS signers)
'--enable-pkcs7 CPPFLAGS=-DWC_RSA_PSS',
'--enable-opensslextra CPPFLAGS=''-DWOLFSSL_NO_CA_NAMES'' ',
'--enable-opensslextra=x509small',
'CPPFLAGS=''-DWOLFSSL_EXTRA'' ',
+11
View File
@@ -52,6 +52,17 @@
}
}
#endif
#if defined(WC_RSA_PSS) && !defined(NO_RSA)
if (info->pk.type == WC_PK_TYPE_RSA_PSS) {
// RSA-PSS sign/verify
ret = wc_RsaPSS_Sign_ex(
info->pk.rsa.in, info->pk.rsa.inLen,
info->pk.rsa.out, *info->pk.rsa.outLen,
WC_HASH_TYPE_SHA256, WC_MGF1SHA256,
RSA_PSS_SALT_LEN_DEFAULT,
info->pk.rsa.key, info->pk.rsa.rng);
}
#endif
#ifdef HAVE_ECC
if (info->pk.type == WC_PK_TYPE_ECDSA_SIGN) {
// ECDSA
@@ -51,6 +51,7 @@
<li>\ref MD5</li>
<li>\ref Password</li>
<li>\ref PKCS7</li>
<li>\ref PKCS7_RSA_PSS</li>
<li>\ref PKCS11</li>
<li>\ref Poly1305</li>
<li>\ref RIPEMD</li>
@@ -97,4 +98,13 @@
\sa wc_CryptoCb_AesSetKey
\sa \ref Crypto Callbacks
*/
/*!
\page PKCS7_RSA_PSS PKCS#7 RSA-PSS (CMS)
PKCS#7 SignedData supports RSA-PSS signers (CMS RSASSA-PSS). When WC_RSA_PSS
is defined, use wc_PKCS7_InitWithCert with a signer certificate that has
RSA-PSS (id-RSASSA-PSS) and set hashOID and optional rng; encode produces
full RSASSA-PSS-params (hashAlgorithm, mgfAlgorithm, saltLength,
trailerField). Verify accepts NULL, empty, or absent parameters with
RFC defaults. See \ref PKCS7 for the main API.
*/
+1 -1
View File
@@ -147,7 +147,7 @@ int wc_PKCS7_EncodeData(wc_PKCS7* pkcs7, byte* output,
\brief This function builds the PKCS7 signed data content type, encoding
the PKCS7 structure into a buffer containing a parsable PKCS7
signed data packet.
signed data packet. For RSA-PSS signers (WC_RSA_PSS), see \ref PKCS7_RSA_PSS.
\return Success On successfully encoding the PKCS7 data into the buffer,
returns the index parsed up to in the PKCS7 structure. This index also
+1 -1
View File
@@ -23,7 +23,7 @@ Example wolfSSL configuration file templates for use when autoconf is not availa
* `user_settings_openssl_compat.h`: OpenSSL compatibility layer for drop-in replacement. Enables OPENSSL_ALL and related APIs.
* `user_settings_baremetal.h`: Bare metal configuration. No filesystem, static memory only, minimal footprint.
* `user_settings_rsa_only.h`: RSA-only configuration (no ECC). For legacy systems requiring RSA cipher suites.
* `user_settings_pkcs7.h`: PKCS#7/CMS configuration for signing and encryption. S/MIME, firmware signing.
* `user_settings_pkcs7.h`: PKCS#7/CMS configuration for signing and encryption. S/MIME, firmware signing. For RSA-PSS SignedData (CMS RSASSA-PSS), define `WC_RSA_PSS`; see doxygen \ref PKCS7_RSA_PSS.
* `user_settings_ca.h`: Certificate Authority / PKI operations. Certificate generation, signing, CRL, OCSP.
* `user_settings_wolfboot_keytools.h`: wolfBoot key generation and signing tool. Supports ECC, RSA, ED25519, ED448, and post-quantum (ML-DSA/Dilithium, LMS, XMSS).
* `user_settings_wolfssh.h`: Minimum options for building wolfSSH. See comment at top for ./configure used to generate.
+1
View File
@@ -115,6 +115,7 @@ extern "C" {
#undef NO_RSA
#define WOLFSSL_KEY_GEN
#define WC_RSA_NO_PADDING
#define WC_RSA_PSS /* RSA-PSS SignedData (id-RSASSA-PSS); see PKCS7_RSA_PSS */
#else
#define NO_RSA
#endif
+133
View File
@@ -24,6 +24,7 @@
#include <tests/api/test_asn.h>
#include <wolfssl/wolfcrypt/asn.h>
#include <wolfssl/wolfcrypt/rsa.h>
#if defined(WC_ENABLE_ASYM_KEY_EXPORT) && defined(HAVE_ED25519)
static int test_SetAsymKeyDer_once(byte* privKey, word32 privKeySz, byte* pubKey,
@@ -787,3 +788,135 @@ int test_wolfssl_local_MatchBaseName(void)
return EXPECT_RESULT();
}
/*
* Testing wc_DecodeRsaPssParams with known DER byte arrays.
* Exercises both WOLFSSL_ASN_TEMPLATE and non-template paths.
*/
int test_wc_DecodeRsaPssParams(void)
{
EXPECT_DECLS;
#if defined(WC_RSA_PSS) && !defined(NO_RSA) && !defined(NO_ASN)
enum wc_HashType hash;
int mgf;
int saltLen;
/* SHA-256 / MGF1-SHA-256 / saltLen=32 */
static const byte pssParamsSha256[] = {
0x30, 0x34,
0xA0, 0x0F,
0x30, 0x0D,
0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
0x04, 0x02, 0x01,
0x05, 0x00,
0xA1, 0x1C,
0x30, 0x1A,
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
0x01, 0x01, 0x08,
0x30, 0x0D,
0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
0x04, 0x02, 0x01,
0x05, 0x00,
0xA2, 0x03,
0x02, 0x01, 0x20,
};
/* Hash-only: SHA-256 hash, defaults for MGF and salt */
static const byte pssParamsHashOnly[] = {
0x30, 0x11,
0xA0, 0x0F,
0x30, 0x0D,
0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
0x04, 0x02, 0x01,
0x05, 0x00,
};
/* Salt-only: default hash/mgf, saltLen=48 */
static const byte pssParamsSaltOnly[] = {
0x30, 0x05,
0xA2, 0x03,
0x02, 0x01, 0x30,
};
/* NULL tag (05 00) means all defaults */
static const byte pssParamsNull[] = { 0x05, 0x00 };
/* Empty SEQUENCE means all non-default fields omitted => defaults */
static const byte pssParamsEmptySeq[] = { 0x30, 0x00 };
/* --- Test 1: sz=0 => all defaults --- */
hash = WC_HASH_TYPE_NONE;
mgf = 0;
saltLen = 0;
ExpectIntEQ(wc_DecodeRsaPssParams((const byte*)"", 0,
&hash, &mgf, &saltLen), 0);
ExpectIntEQ((int)hash, (int)WC_HASH_TYPE_SHA);
ExpectIntEQ(mgf, WC_MGF1SHA1);
ExpectIntEQ(saltLen, 20);
/* --- Test 2: NULL tag => all defaults --- */
hash = WC_HASH_TYPE_NONE;
mgf = 0;
saltLen = 0;
ExpectIntEQ(wc_DecodeRsaPssParams(pssParamsNull,
(word32)sizeof(pssParamsNull), &hash, &mgf, &saltLen), 0);
ExpectIntEQ((int)hash, (int)WC_HASH_TYPE_SHA);
ExpectIntEQ(mgf, WC_MGF1SHA1);
ExpectIntEQ(saltLen, 20);
/* --- Test 3: Empty SEQUENCE => all defaults --- */
hash = WC_HASH_TYPE_NONE;
mgf = 0;
saltLen = 0;
ExpectIntEQ(wc_DecodeRsaPssParams(pssParamsEmptySeq,
(word32)sizeof(pssParamsEmptySeq), &hash, &mgf, &saltLen), 0);
ExpectIntEQ((int)hash, (int)WC_HASH_TYPE_SHA);
ExpectIntEQ(mgf, WC_MGF1SHA1);
ExpectIntEQ(saltLen, 20);
#ifndef NO_SHA256
/* --- Test 4: SHA-256 / MGF1-SHA-256 / salt=32 --- */
hash = WC_HASH_TYPE_NONE;
mgf = 0;
saltLen = 0;
ExpectIntEQ(wc_DecodeRsaPssParams(pssParamsSha256,
(word32)sizeof(pssParamsSha256), &hash, &mgf, &saltLen), 0);
ExpectIntEQ((int)hash, (int)WC_HASH_TYPE_SHA256);
ExpectIntEQ(mgf, WC_MGF1SHA256);
ExpectIntEQ(saltLen, 32);
/* --- Test 5: Hash only => SHA-256, default MGF/salt --- */
hash = WC_HASH_TYPE_NONE;
mgf = 0;
saltLen = 0;
ExpectIntEQ(wc_DecodeRsaPssParams(pssParamsHashOnly,
(word32)sizeof(pssParamsHashOnly), &hash, &mgf, &saltLen), 0);
ExpectIntEQ((int)hash, (int)WC_HASH_TYPE_SHA256);
ExpectIntEQ(mgf, WC_MGF1SHA1);
ExpectIntEQ(saltLen, 20);
#endif
/* --- Test 6: Salt only => default hash/MGF, salt=48 --- */
hash = WC_HASH_TYPE_NONE;
mgf = 0;
saltLen = 0;
ExpectIntEQ(wc_DecodeRsaPssParams(pssParamsSaltOnly,
(word32)sizeof(pssParamsSaltOnly), &hash, &mgf, &saltLen), 0);
ExpectIntEQ((int)hash, (int)WC_HASH_TYPE_SHA);
ExpectIntEQ(mgf, WC_MGF1SHA1);
ExpectIntEQ(saltLen, 48);
/* --- Test 7: NULL pointer -> BAD_FUNC_ARG --- */
ExpectIntEQ(wc_DecodeRsaPssParams(NULL, 10, &hash, &mgf, &saltLen),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
/* --- Test 8: Bad leading tag => ASN_PARSE_E --- */
{
static const byte badTag[] = { 0x01, 0x00 };
ExpectIntEQ(wc_DecodeRsaPssParams(badTag, (word32)sizeof(badTag),
&hash, &mgf, &saltLen), WC_NO_ERR_TRACE(ASN_PARSE_E));
}
#endif /* WC_RSA_PSS && !NO_RSA && !NO_ASN */
return EXPECT_RESULT();
}
+3 -1
View File
@@ -28,11 +28,13 @@ int test_SetAsymKeyDer(void);
int test_GetSetShortInt(void);
int test_wc_IndexSequenceOf(void);
int test_wolfssl_local_MatchBaseName(void);
int test_wc_DecodeRsaPssParams(void);
#define TEST_ASN_DECLS \
TEST_DECL_GROUP("asn", test_SetAsymKeyDer), \
TEST_DECL_GROUP("asn", test_GetSetShortInt), \
TEST_DECL_GROUP("asn", test_wc_IndexSequenceOf), \
TEST_DECL_GROUP("asn", test_wolfssl_local_MatchBaseName)
TEST_DECL_GROUP("asn", test_wolfssl_local_MatchBaseName), \
TEST_DECL_GROUP("asn", test_wc_DecodeRsaPssParams)
#endif /* WOLFCRYPT_TEST_ASN_H */
+92
View File
@@ -947,6 +947,98 @@ int test_wc_PKCS7_EncodeSignedData(void)
} /* END test_wc_PKCS7_EncodeSignedData */
/*
* Testing wc_PKCS7_EncodeSignedData() with RSA-PSS signer certificate.
* Uses certs/rsapss/client-rsapss.der and client-rsapss-priv.der.
* Requires both encode and round-trip verify to succeed.
*/
#if defined(HAVE_PKCS7) && defined(WC_RSA_PSS) && !defined(NO_RSA) && \
!defined(NO_FILESYSTEM) && !defined(NO_SHA256)
int test_wc_PKCS7_EncodeSignedData_RSA_PSS(void)
{
EXPECT_DECLS;
PKCS7* pkcs7 = NULL;
WC_RNG rng;
byte output[FOURK_BUF];
byte cert[FOURK_BUF];
byte key[FOURK_BUF];
word32 outputSz = (word32)sizeof(output);
word32 certSz = 0;
word32 keySz = 0;
XFILE fp = XBADFILE;
byte data[] = "Test data for RSA-PSS SignedData.";
XMEMSET(&rng, 0, sizeof(WC_RNG));
XMEMSET(output, 0, outputSz);
XMEMSET(cert, 0, sizeof(cert));
XMEMSET(key, 0, sizeof(key));
ExpectIntEQ(wc_InitRng(&rng), 0);
ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
ExpectTrue((fp = XFOPEN("./certs/rsapss/client-rsapss.der", "rb")) != XBADFILE);
if (fp != XBADFILE) {
ExpectIntGT(certSz = (word32)XFREAD(cert, 1, sizeof(cert), fp), 0);
XFCLOSE(fp);
fp = XBADFILE;
}
ExpectTrue((fp = XFOPEN("./certs/rsapss/client-rsapss-priv.der", "rb")) != XBADFILE);
if (fp != XBADFILE) {
ExpectIntGT(keySz = (word32)XFREAD(key, 1, sizeof(key), fp), 0);
XFCLOSE(fp);
fp = XBADFILE;
}
ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, cert, certSz), 0);
if (pkcs7 != NULL) {
/* Force RSA-PSS so SignerInfo uses id-RSASSA-PSS (cert may use RSA
* in subjectPublicKeyInfo). WC_RSA_PSS is guaranteed by outer guard. */
pkcs7->publicKeyOID = RSAPSSk;
pkcs7->content = data;
pkcs7->contentSz = (word32)sizeof(data);
pkcs7->contentOID = DATA;
pkcs7->hashOID = SHA256h;
pkcs7->encryptOID = RSAk;
pkcs7->privateKey = key;
pkcs7->privateKeySz = keySz;
pkcs7->rng = &rng;
pkcs7->signedAttribs = NULL;
pkcs7->signedAttribsSz = 0;
}
/* EncodeSignedData with RSA-PSS cert: require encode and verify success */
{
int outLen = wc_PKCS7_EncodeSignedData(pkcs7, output, outputSz);
ExpectIntGT(outLen, 0);
if (outLen > 0) {
int verifyRet = wc_PKCS7_VerifySignedData(pkcs7, output,
(word32)outLen);
ExpectIntEQ(verifyRet, 0);
if (pkcs7 != NULL) {
/* Verify decoded RSASSA-PSS parameters match what we
* encoded:
* hashAlgorithm = SHA-256
* maskGenAlgorithm = MGF1-SHA-256
* saltLength = 32 (== SHA-256 digest length) */
ExpectIntEQ(pkcs7->pssHashType, (int)WC_HASH_TYPE_SHA256);
ExpectIntEQ(pkcs7->pssMgf, WC_MGF1SHA256);
ExpectIntEQ(pkcs7->pssSaltLen, 32);
}
}
}
wc_PKCS7_Free(pkcs7);
DoExpectIntEQ(wc_FreeRng(&rng), 0);
return EXPECT_RESULT();
} /* END test_wc_PKCS7_EncodeSignedData_RSA_PSS */
#endif
/*
* Testing wc_PKCS7_EncodeSignedData_ex() and wc_PKCS7_VerifySignedData_ex()
*/
+13
View File
@@ -29,6 +29,10 @@ int test_wc_PKCS7_Init(void);
int test_wc_PKCS7_InitWithCert(void);
int test_wc_PKCS7_EncodeData(void);
int test_wc_PKCS7_EncodeSignedData(void);
#if defined(HAVE_PKCS7) && defined(WC_RSA_PSS) && !defined(NO_RSA) && \
!defined(NO_FILESYSTEM) && !defined(NO_SHA256)
int test_wc_PKCS7_EncodeSignedData_RSA_PSS(void);
#endif
int test_wc_PKCS7_EncodeSignedData_ex(void);
int test_wc_PKCS7_VerifySignedData_RSA(void);
int test_wc_PKCS7_VerifySignedData_ECC(void);
@@ -55,10 +59,19 @@ int test_wc_PKCS7_VerifySignedData_PKCS7ContentSeq(void);
TEST_DECL_GROUP("pkcs7", test_wc_PKCS7_New), \
TEST_DECL_GROUP("pkcs7", test_wc_PKCS7_Init)
#if defined(HAVE_PKCS7) && defined(WC_RSA_PSS) && !defined(NO_RSA) && \
!defined(NO_FILESYSTEM) && !defined(NO_SHA256)
#define TEST_PKCS7_RSA_PSS_SD_DECL \
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_EncodeSignedData_RSA_PSS),
#else
#define TEST_PKCS7_RSA_PSS_SD_DECL
#endif
#define TEST_PKCS7_SIGNED_DATA_DECLS \
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_InitWithCert), \
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_EncodeData), \
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_EncodeSignedData), \
TEST_PKCS7_RSA_PSS_SD_DECL \
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_EncodeSignedData_ex), \
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_RSA), \
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_ECC), \
-1
View File
@@ -17147,5 +17147,4 @@ int wc_AesCtsDecryptFinal(Aes* aes, byte* out, word32* outSz)
#endif /* WOLFSSL_AES_CTS */
#endif /* !NO_AES */
+374 -112
View File
@@ -2539,7 +2539,7 @@ int GetASNHeader(const byte* input, byte tag, word32* inOutIdx, int* len,
return GetASNHeader_ex(input, tag, inOutIdx, len, maxIdx, 1);
}
#ifndef WOLFSSL_ASN_TEMPLATE
#if !defined(WOLFSSL_ASN_TEMPLATE)
static int GetHeader(const byte* input, byte* tag, word32* inOutIdx, int* len,
word32 maxIdx, int check)
{
@@ -2894,7 +2894,9 @@ static int GetInteger7Bit(const byte* input, word32* inOutIdx, word32 maxIdx)
return b;
}
#endif /* !NO_CERTS */
#endif /* !WOLFSSL_ASN_TEMPLATE */
#if !defined(WOLFSSL_ASN_TEMPLATE)
#if ((defined(WC_RSA_PSS) && !defined(NO_RSA)) || !defined(NO_CERTS))
/* Get the DER/BER encoding of an ASN.1 INTEGER that has a value of no more than
* 16 bits.
@@ -2932,7 +2934,7 @@ static int GetInteger16Bit(const byte* input, word32* inOutIdx, word32 maxIdx)
return ASN_PARSE_E;
}
n = input[idx++];
n = (n << 8) | input[idx++];
n = (word16)((n << 8) | input[idx++]);
}
else
return ASN_PARSE_E;
@@ -2940,7 +2942,7 @@ static int GetInteger16Bit(const byte* input, word32* inOutIdx, word32 maxIdx)
*inOutIdx = idx;
return n;
}
#endif /* WC_RSA_PSS && !NO_RSA */
#endif /* WC_RSA_PSS || !NO_CERTS */
#endif /* !WOLFSSL_ASN_TEMPLATE */
#if !defined(NO_DSA) && !defined(NO_SHA)
@@ -3132,10 +3134,12 @@ const char* GetSigName(int oid) {
#if !defined(WOLFSSL_ASN_TEMPLATE) || defined(HAVE_PKCS7) || \
defined(OPENSSL_EXTRA) || defined(WOLFSSL_CERT_GEN)
defined(OPENSSL_EXTRA) || defined(WOLFSSL_CERT_GEN) || defined(WC_RSA_PSS)
#if !defined(NO_DSA) || defined(HAVE_ECC) || !defined(NO_CERTS) || \
defined(WOLFSSL_CERT_GEN) || \
(!defined(NO_RSA) && (defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA)))
(!defined(NO_RSA) && \
(defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA))) || \
(defined(WC_RSA_PSS) && !defined(NO_RSA))
/* Set the DER/BER encoding of the ASN.1 INTEGER header.
*
* When output is NULL, calculate the header length only.
@@ -3146,7 +3150,7 @@ const char* GetSigName(int oid) {
* @param [out] output Buffer to write into.
* @return Number of bytes added to the buffer.
*/
int SetASNInt(int len, byte firstByte, byte* output)
WOLFSSL_LOCAL int SetASNInt(int len, byte firstByte, byte* output)
{
int idx = 0;
@@ -7452,6 +7456,9 @@ static int RsaPssHashOidToSigOid(word32 oid, word32* sigOid)
}
#endif
/* RSASSA-PSS-params context-specific tags.
* RFC 4055, 3.1
*/
#ifdef WOLFSSL_ASN_TEMPLATE
/* ASN tag for hashAlgorithm. */
#define ASN_TAG_RSA_PSS_HASH (ASN_CONTEXT_SPECIFIC | 0)
@@ -7498,7 +7505,7 @@ enum {
RSAPSSPARAMSASN_IDX_TRAILERINT
};
/* Number of items in ASN.1 template for an algorithm identifier. */
/* Number of items in ASN.1 template for RSA PSS parameters. */
#define rsaPssParamsASN_Length (sizeof(rsaPssParamsASN) / sizeof(ASNItem))
#else
/* ASN tag for hashAlgorithm. */
@@ -7525,122 +7532,44 @@ enum {
static int DecodeRsaPssParams(const byte* params, word32 sz,
enum wc_HashType* hash, int* mgf, int* saltLen)
{
#ifndef WOLFSSL_ASN_TEMPLATE
int ret = 0;
word32 idx = 0;
int len = 0;
word32 oid = 0;
byte tag;
int length;
if (params == NULL)
return BAD_FUNC_ARG;
if (params == NULL) {
ret = BAD_FUNC_ARG;
/* Empty or NULL-tag parameters mean all defaults per RFC 4055 */
if (sz == 0) {
*hash = WC_HASH_TYPE_SHA;
*mgf = WC_MGF1SHA1;
*saltLen = 20;
return 0;
}
if ((ret == 0) && (GetSequence_ex(params, &idx, &len, sz, 1) < 0)) {
ret = ASN_PARSE_E;
}
if (ret == 0) {
if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_HASH)) {
/* Hash algorithm to use on message. */
if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) {
ret = ASN_PARSE_E;
}
if (ret == 0) {
if (GetAlgoId(params, &idx, &oid, oidHashType, sz) < 0) {
ret = ASN_PARSE_E;
}
}
if (ret == 0) {
ret = RsaPssHashOidToType(oid, hash);
}
}
else {
/* Default hash algorithm. */
if (params[0] == ASN_TAG_NULL) {
if (sz >= 2 && params[1] == 0) {
*hash = WC_HASH_TYPE_SHA;
}
}
if (ret == 0) {
if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_MGF)) {
/* MGF and hash algorithm to use with padding. */
if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) {
ret = ASN_PARSE_E;
}
if (ret == 0) {
if (GetAlgoId(params, &idx, &oid, oidIgnoreType, sz) < 0) {
ret = ASN_PARSE_E;
}
}
if ((ret == 0) && (oid != MGF1_OID)) {
ret = ASN_PARSE_E;
}
if (ret == 0) {
ret = GetAlgoId(params, &idx, &oid, oidHashType, sz);
if (ret == 0) {
ret = RsaPssHashOidToMgf1(oid, mgf);
}
}
}
else {
/* Default MGF/Hash algorithm. */
*mgf = WC_MGF1SHA1;
}
}
if (ret == 0) {
if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_SALTLEN)) {
/* Salt length to use with padding. */
if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) {
ret = ASN_PARSE_E;
}
if (ret == 0) {
ret = GetInteger16Bit(params, &idx, sz);
if (ret >= 0) {
*saltLen = ret;
ret = 0;
}
}
}
else {
/* Default salt length. */
*saltLen = 20;
return 0;
}
return ASN_PARSE_E;
}
if (ret == 0) {
if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_TRAILER)) {
/* Unused - trialerField. */
if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) {
ret = ASN_PARSE_E;
}
if (ret == 0) {
ret = GetInteger16Bit(params, &idx, sz);
if (ret > 0) {
ret = 0;
}
}
}
}
if ((ret == 0) && (idx != sz)) {
ret = ASN_PARSE_E;
}
if (params[0] != (ASN_SEQUENCE | ASN_CONSTRUCTED))
return ASN_PARSE_E;
return ret;
#else
#ifdef WOLFSSL_ASN_TEMPLATE
{
DECL_ASNGETDATA(dataASN, rsaPssParamsASN_Length);
int ret = 0;
word16 sLen = 20;
if (params == NULL) {
ret = BAD_FUNC_ARG;
}
/* Default values. */
*hash = WC_HASH_TYPE_SHA;
*mgf = WC_MGF1SHA1;
CALLOC_ASNGETDATA(dataASN, rsaPssParamsASN_Length, ret, NULL);
if (ret == 0) {
word32 inOutIdx = 0;
/* Default values. */
*hash = WC_HASH_TYPE_SHA;
*mgf = WC_MGF1SHA1;
/* Set OID type expected. */
GetASN_OID(&dataASN[RSAPSSPARAMSASN_IDX_HASHOID], oidHashType);
GetASN_OID(&dataASN[RSAPSSPARAMSASN_IDX_MGFOID], oidIgnoreType);
GetASN_OID(&dataASN[RSAPSSPARAMSASN_IDX_MGFHOID], oidHashType);
/* Place the salt length into 16-bit var sLen. */
GetASN_Int16Bit(&dataASN[RSAPSSPARAMSASN_IDX_SALTLENINT], &sLen);
@@ -7652,6 +7581,10 @@ static int DecodeRsaPssParams(const byte* params, word32 sz,
word32 oid = dataASN[RSAPSSPARAMSASN_IDX_HASHOID].data.oid.sum;
ret = RsaPssHashOidToType(oid, hash);
}
if ((ret == 0) && (dataASN[RSAPSSPARAMSASN_IDX_MGFOID].tag != 0)) {
if (dataASN[RSAPSSPARAMSASN_IDX_MGFOID].data.oid.sum != MGF1_OID)
ret = ASN_PARSE_E;
}
if ((ret == 0) && (dataASN[RSAPSSPARAMSASN_IDX_MGFHOID].tag != 0)) {
word32 oid = dataASN[RSAPSSPARAMSASN_IDX_MGFHOID].data.oid.sum;
ret = RsaPssHashOidToMgf1(oid, mgf);
@@ -7662,8 +7595,317 @@ static int DecodeRsaPssParams(const byte* params, word32 sz,
FREE_ASNGETDATA(dataASN, NULL);
return ret;
}
#else /* !WOLFSSL_ASN_TEMPLATE */
{
int ret = 0;
word32 idx = 0;
int len = 0;
word32 oid = 0;
byte tag;
int length;
/* Decode RSASSA-PSS-params SEQUENCE content. */
if (GetSequence_ex(params, &idx, &len, sz, 1) < 0) {
WOLFSSL_MSG("DecodeRsaPssParams: fail at sequence");
return ASN_PARSE_E;
}
/* [0] hashAlgorithm */
if (ret == 0) {
if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_HASH)) {
/* Hash algorithm to use on message. */
if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) {
WOLFSSL_MSG("DecodeRsaPssParams: fail at hash_header");
ret = ASN_PARSE_E;
}
if (ret == 0) {
if (GetAlgoId(params, &idx, &oid, oidHashType, sz) < 0) {
WOLFSSL_MSG("DecodeRsaPssParams: fail at hash_algo");
ret = ASN_PARSE_E;
}
}
if (ret == 0) {
ret = RsaPssHashOidToType(oid, hash);
if (ret != 0) {
WOLFSSL_MSG("DecodeRsaPssParams: fail at hash_oid");
}
}
}
else {
/* Default hash algorithm. */
*hash = WC_HASH_TYPE_SHA;
}
}
/* [1] maskGenAlgorithm -- AlgorithmIdentifier { OID id-mgf1, hash AlgoId }
* Parse manually: read the MGF SEQUENCE + OID, then the hash AlgoId
* parameter, because GetAlgoId consumes the entire AlgorithmIdentifier
* including the hash parameter inside it. */
if (ret == 0) {
if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_MGF)) {
int mgfSeqLen = 0;
word32 mgfEnd;
if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) {
WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_header");
ret = ASN_PARSE_E;
}
/* Read MGF AlgorithmIdentifier SEQUENCE header */
if (ret == 0) {
if (GetSequence(params, &idx, &mgfSeqLen, sz) < 0) {
WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_seq");
ret = ASN_PARSE_E;
}
}
/* Bound subsequent reads to the MGF SEQUENCE content */
mgfEnd = idx + (word32)mgfSeqLen;
if (mgfEnd > sz) {
WOLFSSL_MSG("DecodeRsaPssParams: mgf_seq overflows buffer");
ret = ASN_PARSE_E;
}
/* Read MGF OID (id-mgf1) directly */
if (ret == 0) {
if (GetObjectId(params, &idx, &oid, oidIgnoreType,
mgfEnd) < 0) {
WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_oid");
ret = ASN_PARSE_E;
}
}
if ((ret == 0) && (oid != MGF1_OID)) {
WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_oid_value");
ret = ASN_PARSE_E;
}
/* Read hash AlgorithmIdentifier (parameter of MGF1) */
if (ret == 0) {
ret = GetAlgoId(params, &idx, &oid, oidHashType, mgfEnd);
if (ret == 0) {
ret = RsaPssHashOidToMgf1(oid, mgf);
if (ret != 0) {
WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_hash_oid");
}
}
else {
WOLFSSL_MSG("DecodeRsaPssParams: fail at mgf_hash_algo");
}
}
if ((ret == 0) && (idx != mgfEnd)) {
WOLFSSL_MSG("DecodeRsaPssParams: extra data in mgf_seq");
ret = ASN_PARSE_E;
}
}
else {
/* Default MGF/Hash algorithm. */
*mgf = WC_MGF1SHA1;
}
}
/* [2] saltLength */
if (ret == 0) {
if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_SALTLEN)) {
/* Salt length to use with padding. */
if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) {
WOLFSSL_MSG("DecodeRsaPssParams: fail at saltlen_header");
ret = ASN_PARSE_E;
}
if (ret == 0) {
ret = GetInteger16Bit(params, &idx, sz);
if (ret >= 0) {
*saltLen = ret;
ret = 0;
}
else {
WOLFSSL_MSG("DecodeRsaPssParams: fail at saltlen_value");
}
}
}
else {
/* Default salt length. */
*saltLen = 20;
}
}
/* [3] trailerField */
if (ret == 0) {
if ((idx < sz) && (params[idx] == ASN_TAG_RSA_PSS_TRAILER)) {
/* Unused - trailerField. */
if (GetHeader(params, &tag, &idx, &length, sz, 0) < 0) {
WOLFSSL_MSG("DecodeRsaPssParams: fail at trailer_header");
ret = ASN_PARSE_E;
}
if (ret == 0) {
ret = GetInteger16Bit(params, &idx, sz);
if (ret > 0) {
ret = 0;
}
else if (ret != 0) {
WOLFSSL_MSG("DecodeRsaPssParams: fail at trailer_value");
}
}
}
}
if ((ret == 0) && (idx != sz)) {
WOLFSSL_MSG("DecodeRsaPssParams: fail at extra_data");
ret = ASN_PARSE_E;
}
return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
}
WOLFSSL_TEST_VIS int wc_DecodeRsaPssParams(const byte* params, word32 sz,
enum wc_HashType* hash, int* mgf, int* saltLen)
{
return DecodeRsaPssParams(params, sz, hash, mgf, saltLen);
}
/* Encode AlgorithmIdentifier for id-RSASSA-PSS with full RSASSA-PSS-params.
* hashOID: e.g. SHA256h; saltLen: e.g. 32; trailerField is encoded as 1.
* When out is NULL returns required length only.
* Returns encoded length on success, 0 on error.
* Note: saltLength is encoded as a single-byte INTEGER; values >255 would
* require multi-byte encoding (not implemented; CMS profiles use hash-length
* salts, e.g. 20-64 bytes). */
/* Scratch buffer for building RSA-PSS AlgorithmIdentifier sub-encodings.
* Must hold the largest single sub-encoding (hash AlgoId, typically ~15 bytes)
* plus room for integer TLVs. 64 bytes is sufficient; 128 gives margin. */
#define RSA_PSS_ALGOID_TMPBUF_SZ 128
word32 wc_EncodeRsaPssAlgoId(int hashOID, int saltLen, byte* out, word32 outSz)
{
word32 idx = 0;
word32 hashAlgSz, mgf1ParamSz, tag0Sz, tag1Sz, tag2Sz, tag3Sz;
word32 paramsSz, outerSz;
const byte* rsapssOid = sigRsaSsaPssOid;
word32 rsapssOidSz = sizeof(sigRsaSsaPssOid);
/* MGF1 OID 1.2.840.113549.1.1.8 */
static const byte mgf1Oid[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08 };
int setIntRet;
#ifdef WOLFSSL_SMALL_STACK
byte* tmpBuf;
#else
byte tmpBuf[RSA_PSS_ALGOID_TMPBUF_SZ];
#endif
if (saltLen < 0 || saltLen > 255) {
WOLFSSL_MSG("Salt length must be 0-255 for single-byte encoding");
return 0;
}
#ifdef WOLFSSL_SMALL_STACK
tmpBuf = (byte*)XMALLOC(RSA_PSS_ALGOID_TMPBUF_SZ, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
if (tmpBuf == NULL)
return 0;
#endif
hashAlgSz = SetAlgoID(hashOID, out ? tmpBuf : NULL, oidHashType, 0);
if (hashAlgSz == 0) {
idx = 0; goto pss_algoid_done;
}
mgf1ParamSz = hashAlgSz;
tag0Sz = SetExplicit(0, hashAlgSz, NULL, 0) + hashAlgSz;
{
/* MGF AlgorithmIdentifier: SEQUENCE { OID id-mgf1, hash AlgoId }
* The hash AlgoId (from SetAlgoID) is the parameter of MGF1. */
word32 mgf1OidLen = (word32)SetObjectId((int)sizeof(mgf1Oid), NULL) + (word32)sizeof(mgf1Oid);
word32 mgf1SeqContent = mgf1OidLen + mgf1ParamSz;
word32 mgf1SeqSz = SetSequence(mgf1SeqContent, NULL) + mgf1SeqContent;
tag1Sz = SetExplicit(1, mgf1SeqSz, NULL, 0) + mgf1SeqSz;
}
/* SetASNInt writes only tag+length (+ optional leading 0); value byte(s) appended by caller. */
setIntRet = SetASNInt(1, (byte)(saltLen & 0xff), NULL);
if (setIntRet <= 0) {
idx = 0; goto pss_algoid_done;
}
{
word32 saltIntSz = (word32)setIntRet + 1; /* header + one value byte (two if high bit set) */
tag2Sz = SetExplicit(2, saltIntSz, NULL, 0) + saltIntSz;
}
setIntRet = SetASNInt(1, 0x01, NULL);
if (setIntRet <= 0) {
idx = 0; goto pss_algoid_done;
}
{
word32 trailerIntSz = (word32)setIntRet + 1;
tag3Sz = SetExplicit(3, trailerIntSz, NULL, 0) + trailerIntSz;
}
paramsSz = tag0Sz + tag1Sz + tag2Sz + tag3Sz;
{
word32 idPart = (word32)SetObjectId((int)rsapssOidSz, NULL) + rsapssOidSz;
word32 seqPart = SetSequence(paramsSz, NULL) + paramsSz;
outerSz = SetSequence(idPart + seqPart, NULL) + idPart + seqPart;
}
if (out == NULL) {
idx = outerSz; goto pss_algoid_done;
}
if (outSz < outerSz) {
idx = 0; goto pss_algoid_done;
}
{
word32 idPart = (word32)SetObjectId((int)rsapssOidSz, NULL) + rsapssOidSz;
word32 seqPart = SetSequence(paramsSz, NULL) + paramsSz;
idx += SetSequence(idPart + seqPart, out + idx);
}
idx += (word32)SetObjectId((int)rsapssOidSz, out + idx);
XMEMCPY(out + idx, rsapssOid, rsapssOidSz);
idx += rsapssOidSz;
idx += SetSequence(paramsSz, out + idx);
idx += SetExplicit(0, hashAlgSz, out + idx, 0);
XMEMCPY(out + idx, tmpBuf, hashAlgSz);
idx += hashAlgSz;
{
/* [1] EXPLICIT { SEQUENCE { OID id-mgf1, hash AlgoId } } */
word32 mgf1OidLen = (word32)SetObjectId((int)sizeof(mgf1Oid), NULL) + (word32)sizeof(mgf1Oid);
word32 mgf1SeqContent = mgf1OidLen + mgf1ParamSz;
word32 mgf1SeqSz = SetSequence(mgf1SeqContent, NULL) + mgf1SeqContent;
idx += SetExplicit(1, mgf1SeqSz, out + idx, 0);
idx += SetSequence(mgf1SeqContent, out + idx);
idx += (word32)SetObjectId((int)sizeof(mgf1Oid), out + idx);
XMEMCPY(out + idx, mgf1Oid, sizeof(mgf1Oid));
idx += (word32)sizeof(mgf1Oid);
XMEMCPY(out + idx, tmpBuf, hashAlgSz);
idx += hashAlgSz;
}
/* INTEGER saltLength (single byte; see function comment for >255 limitation). */
setIntRet = SetASNInt(1, (byte)(saltLen & 0xff), tmpBuf);
if (setIntRet > 0) {
tmpBuf[setIntRet] = (byte)(saltLen & 0xff);
}
{
word32 saltIntSz = (word32)setIntRet + 1;
idx += SetExplicit(2, saltIntSz, out + idx, 0);
XMEMCPY(out + idx, tmpBuf, saltIntSz);
idx += saltIntSz;
}
/* INTEGER trailerField (1): same pattern. */
setIntRet = SetASNInt(1, 0x01, tmpBuf);
if (setIntRet > 0) {
tmpBuf[setIntRet] = 0x01;
}
{
word32 trailerIntSz = (word32)setIntRet + 1;
idx += SetExplicit(3, trailerIntSz, out + idx, 0);
XMEMCPY(out + idx, tmpBuf, trailerIntSz);
idx += trailerIntSz;
}
pss_algoid_done:
#ifdef WOLFSSL_SMALL_STACK
XFREE(tmpBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return idx;
}
#endif /* WC_RSA_PSS */
#if defined(WOLFSSL_ASN_TEMPLATE) || (!defined(NO_CERTS) && \
@@ -11113,6 +11355,7 @@ int wc_RsaPublicKeyDecode_ex(const byte* input, word32* inOutIdx, word32 inSz,
#else
DECL_ASNGETDATA(dataASN, rsaPublicKeyASN_Length);
int ret = 0;
word32 startIdx = (inOutIdx != NULL) ? *inOutIdx : 0;
#ifdef WC_RSA_PSS
word32 oid = RSAk;
#endif
@@ -11131,7 +11374,10 @@ int wc_RsaPublicKeyDecode_ex(const byte* input, word32* inOutIdx, word32 inSz,
(int)(rsaPublicKeyASN_Length - RSAPUBLICKEYASN_IDX_PUBKEY_RSA_SEQ),
0, input, inOutIdx, inSz);
if (ret != 0) {
/* Didn't work - try whole SubjectKeyInfo instead. */
/* Didn't work - try whole SubjectKeyInfo instead. Reset index
* to caller's start since the previous attempt advanced it. */
if (inOutIdx != NULL)
*inOutIdx = startIdx;
#ifdef WC_RSA_PSS
/* Could be RSA or RSA PSS key. */
GetASN_OID(&dataASN[RSAPUBLICKEYASN_IDX_ALGOID_OID], oidKeyType);
@@ -17041,8 +17287,17 @@ static int GetSigAlg(DecodedCert* cert, word32* sigOid, word32 maxIdx)
if (cert->srcIdx != endSeqIdx) {
#ifdef WC_RSA_PSS
if (*sigOid == CTC_RSASSAPSS) {
cert->sigParamsIndex = cert->srcIdx;
cert->sigParamsLength = endSeqIdx - cert->srcIdx;
/* cert->srcIdx is at start of parameters TLV (NULL or SEQUENCE) */
word32 tmpIdx = cert->srcIdx;
byte tag;
int len;
WOLFSSL_MSG("Cert sigAlg is RSASSA-PSS; decoding params");
if (GetHeader(cert->source, &tag, &tmpIdx, &len, endSeqIdx, 0) < 0) {
return ASN_PARSE_E;
}
cert->sigParamsIndex = cert->srcIdx;
cert->sigParamsLength = (word32)((tmpIdx - cert->srcIdx) + len);
}
else
#endif
@@ -24082,10 +24337,17 @@ static int DecodeCertInternal(DecodedCert* cert, int verify, int* criticalExt,
}
}
if (ret == 0) {
/* Store parameters for use in signature verification. */
cert->sigParamsIndex =
dataASN[X509CERTASN_IDX_SIGALGO_PARAMS].offset;
cert->sigParamsLength = sigAlgParamsSz;
/* Store parameters for use in signature verification.
* Use full TLV length: tag (1) + length bytes + content.
* GetASNItem_Length can be content-only when buffer.data unset. */
word32 off = dataASN[X509CERTASN_IDX_SIGALGO_PARAMS].offset;
word32 contentLen =
(word32)dataASN[X509CERTASN_IDX_SIGALGO_PARAMS].length;
cert->sigParamsIndex = off;
cert->sigParamsLength = (contentLen <= 127)
? (2 + contentLen)
: GetASNItem_Length(
dataASN[X509CERTASN_IDX_SIGALGO_PARAMS], cert->source);
}
}
#endif
+439 -18
View File
@@ -28,6 +28,26 @@
#ifndef NO_RSA
#include <wolfssl/wolfcrypt/rsa.h>
#endif
#ifndef RSA_PSS_SALT_LEN_DEFAULT
#define RSA_PSS_SALT_LEN_DEFAULT (-1)
#endif
#if defined(WC_RSA_PSS) && !defined(NO_RSA)
#if (defined(HAVE_FIPS) && \
(!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2))) || \
(defined(HAVE_SELFTEST) && \
(!defined(HAVE_SELFTEST_VERSION) || (HAVE_SELFTEST_VERSION < 2)))
#ifndef WC_MGF1NONE
#define WC_MGF1NONE 0
#define WC_MGF1SHA1 26
#define WC_MGF1SHA224 4
#define WC_MGF1SHA256 1
#define WC_MGF1SHA384 2
#define WC_MGF1SHA512 3
#define WC_MGF1SHA512_224 5
#define WC_MGF1SHA512_256 6
#endif
#endif
#endif
#ifdef HAVE_ECC
#include <wolfssl/wolfcrypt/ecc.h>
#endif
@@ -1019,6 +1039,11 @@ static int wc_PKCS7_CheckPublicKeyDer(wc_PKCS7* pkcs7, int keyOID,
switch (keyOID) {
#ifndef NO_RSA
#ifdef WC_RSA_PSS
case RSAPSSk:
/* RSA-PSS cert: public key is same RSA format as RSAk */
FALL_THROUGH;
#endif
case RSAk:
ret = wc_InitRsaKey_ex(rsa, pkcs7->heap, pkcs7->devId);
if (ret != 0) {
@@ -1173,6 +1198,10 @@ int wc_PKCS7_InitWithCert(wc_PKCS7* pkcs7, byte* derCert, word32 derCertSz)
XMEMCPY(pkcs7->publicKey, dCert->publicKey, dCert->pubKeySize);
pkcs7->publicKeySz = dCert->pubKeySize;
pkcs7->publicKeyOID = dCert->keyOID;
/* Do not derive publicKeyOID from cert signatureOID: the cert's
* signature is how the cert was signed by its issuer; the signer
* chooses digestEncryptionAlgorithm (e.g. RSASSA-PSS vs PKCS#1 v1.5)
* via API / pkcs7->publicKeyOID set by the application. */
XMEMCPY(pkcs7->issuerHash, dCert->issuerHash, KEYID_SIZE);
pkcs7->issuer = dCert->issuerRaw;
pkcs7->issuerSz = (word32)dCert->issuerRawLen;
@@ -1533,7 +1562,11 @@ typedef struct ESD {
byte issuerSKIDSeq[MAX_SEQ_SZ];
byte issuerSKID[MAX_OCTET_STR_SZ];
byte signerDigAlgoId[MAX_ALGO_SZ];
#if defined(WC_RSA_PSS)
byte digEncAlgoId[128]; /* RSASSA-PSS needs full params */
#else
byte digEncAlgoId[MAX_ALGO_SZ];
#endif
byte signedAttribSet[MAX_SET_SZ];
EncodedAttrib signedAttribs[7];
byte signerDigest[MAX_OCTET_STR_SZ];
@@ -1945,6 +1978,131 @@ static int wc_PKCS7_EcdsaSign(wc_PKCS7* pkcs7, byte* in, word32 inSz, ESD* esd)
#endif /* HAVE_ECC */
#if defined(WC_RSA_PSS) && !defined(NO_RSA)
/* Map hash type to MGF1 identifier; local copy so PKCS7 does not depend on
* rsa.c when RSA is in a separate FIPS module (e.g. CAVP build). */
static int pkcs7_hash2mgf(enum wc_HashType hType)
{
switch (hType) {
case WC_HASH_TYPE_NONE:
return WC_MGF1NONE;
case WC_HASH_TYPE_SHA:
#ifndef NO_SHA
return WC_MGF1SHA1;
#else
break;
#endif
case WC_HASH_TYPE_SHA224:
#ifdef WOLFSSL_SHA224
return WC_MGF1SHA224;
#else
break;
#endif
case WC_HASH_TYPE_SHA256:
#ifndef NO_SHA256
return WC_MGF1SHA256;
#else
break;
#endif
case WC_HASH_TYPE_SHA384:
#ifdef WOLFSSL_SHA384
return WC_MGF1SHA384;
#else
break;
#endif
case WC_HASH_TYPE_SHA512:
#ifdef WOLFSSL_SHA512
return WC_MGF1SHA512;
#else
break;
#endif
/* MGF1 only supports SHA-1 and SHA-2; other hashes fall through to WC_MGF1NONE */
case WC_HASH_TYPE_MD2:
case WC_HASH_TYPE_MD4:
case WC_HASH_TYPE_MD5:
case WC_HASH_TYPE_MD5_SHA:
case WC_HASH_TYPE_SHA3_224:
case WC_HASH_TYPE_SHA3_256:
case WC_HASH_TYPE_SHA3_384:
case WC_HASH_TYPE_SHA3_512:
case WC_HASH_TYPE_BLAKE2B:
case WC_HASH_TYPE_BLAKE2S:
#ifndef WOLFSSL_NOSHA512_224
case WC_HASH_TYPE_SHA512_224:
#endif
#ifndef WOLFSSL_NOSHA512_256
case WC_HASH_TYPE_SHA512_256:
#endif
#ifdef WOLFSSL_SHAKE128
case WC_HASH_TYPE_SHAKE128:
#endif
#ifdef WOLFSSL_SHAKE256
case WC_HASH_TYPE_SHAKE256:
#endif
#ifdef WOLFSSL_SM3
case WC_HASH_TYPE_SM3:
#endif
default:
break;
}
return WC_MGF1NONE;
}
/* returns size of signature put into esd->encContentDigest, negative on error.
* Signs the digest (contentAttribsDigest) with RSA-PSS padding, like ECDSA. */
static int wc_PKCS7_RsaPssSign(wc_PKCS7* pkcs7, byte* digest, word32 digestSz,
ESD* esd)
{
int ret;
word32 outSz;
WC_DECLARE_VAR(privKey, RsaKey, 1, 0);
if (pkcs7 == NULL || pkcs7->rng == NULL || digest == NULL || esd == NULL) {
return BAD_FUNC_ARG;
}
WC_ALLOC_VAR_EX(privKey, RsaKey, 1, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER,
return MEMORY_E);
ret = wc_PKCS7_ImportRSA(pkcs7, privKey);
if (ret == 0) {
outSz = sizeof(esd->encContentDigest);
#ifdef WOLFSSL_ASYNC_CRYPT
do {
ret = wc_AsyncWait(ret, &privKey->asyncDev,
WC_ASYNC_FLAG_CALL_AGAIN);
if (ret >= 0)
#endif
{
/* Salt length policy: use hash digest length (RFC 4055 typical).
* RFC 3447 allows arbitrary salt lengths, but hash-length is the
* most interoperable choice and matches OpenSSL's default.
* Must agree with the saltLen encoded in
* SignerInfo.signatureAlgorithm params above. */
int saltLen = wc_HashGetDigestSize(wc_OidGetHash(pkcs7->hashOID));
if (saltLen < 0) {
ret = saltLen;
}
else {
ret = wc_RsaPSS_Sign_ex(digest, digestSz,
esd->encContentDigest, outSz,
esd->hashType, pkcs7_hash2mgf(esd->hashType),
saltLen, privKey, pkcs7->rng);
}
}
#ifdef WOLFSSL_ASYNC_CRYPT
} while (ret == WC_NO_ERR_TRACE(WC_PENDING_E));
#endif
/* wc_RsaPSS_Sign_ex returns signature length on success */
}
wc_FreeRsaKey(privKey);
WC_FREE_VAR_EX(privKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
#endif /* WC_RSA_PSS && !NO_RSA */
/* returns encContentDigestSz based on the signature set to be used */
static int wc_PKCS7_GetSignSize(wc_PKCS7* pkcs7)
{
@@ -1954,6 +2112,9 @@ static int wc_PKCS7_GetSignSize(wc_PKCS7* pkcs7)
#ifndef NO_RSA
case RSAk:
#ifdef WC_RSA_PSS
case RSAPSSk:
#endif
{
#ifndef WOLFSSL_SMALL_STACK
RsaKey privKey[1];
@@ -2244,6 +2405,20 @@ static int wc_PKCS7_SignedDataGetEncAlgoId(wc_PKCS7* pkcs7, int* digEncAlgoId,
}
#endif /* HAVE_ECC */
#ifdef WC_RSA_PSS
else if (pkcs7->publicKeyOID == RSAPSSk) {
algoType = oidSigType;
algoId = CTC_RSASSAPSS;
/* Hash/MGF/salt conveyed via PSS params in AlgorithmIdentifier */
}
#endif
#ifndef WC_RSA_PSS
else if (pkcs7->publicKeyOID == RSAPSSk) {
WOLFSSL_MSG("RSA-PSS requested but WC_RSA_PSS not compiled in");
return NOT_COMPILED_IN;
}
#endif
if (algoId == 0) {
WOLFSSL_MSG("Invalid signature algorithm type");
return BAD_FUNC_ARG;
@@ -2359,7 +2534,8 @@ static int wc_PKCS7_SignedDataBuildSignature(wc_PKCS7* pkcs7,
{
int ret = 0;
#if defined(HAVE_ECC) || \
(defined(HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK) && !defined(NO_RSA))
(defined(HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK) && !defined(NO_RSA)) || \
(defined(WC_RSA_PSS) && !defined(NO_RSA))
int hashSz = 0;
#endif
#if defined(HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK) && !defined(NO_RSA)
@@ -2384,7 +2560,8 @@ static int wc_PKCS7_SignedDataBuildSignature(wc_PKCS7* pkcs7,
}
#if defined(HAVE_ECC) || \
(defined(HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK) && !defined(NO_RSA))
(defined(HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK) && !defined(NO_RSA)) || \
(defined(WC_RSA_PSS) && !defined(NO_RSA))
/* get digest size from hash type */
hashSz = wc_HashGetDigestSize(esd->hashType);
if (hashSz < 0) {
@@ -2447,6 +2624,14 @@ static int wc_PKCS7_SignedDataBuildSignature(wc_PKCS7* pkcs7,
break;
#endif
#if defined(WC_RSA_PSS) && !defined(NO_RSA)
case RSAPSSk:
/* RSA-PSS signs the digest directly (no DigestInfo), like ECDSA */
ret = wc_PKCS7_RsaPssSign(pkcs7, esd->contentAttribsDigest,
(word32)hashSz, esd);
break;
#endif
default:
WOLFSSL_MSG("Unsupported public key type");
ret = BAD_FUNC_ARG;
@@ -2801,14 +2986,19 @@ static int PKCS7_EncodeSigned(wc_PKCS7* pkcs7,
return BAD_FUNC_ARG;
}
/* signature size varies with ECDSA, with a varying sign size the content
* hash must be known in order to create the surrounding ASN1 syntax
* properly before writing out the content and generating the hash on the
* fly and then creating the signature */
if (hashBuf == NULL && pkcs7->publicKeyOID == ECDSAk) {
/* signature size varies with ECDSA; RSA-PSS signs digest directly like
* ECDSA. For both, content hash must be known to build ASN.1 before signing */
#if defined(HAVE_ECC) || defined(WC_RSA_PSS)
if (hashBuf == NULL &&
(pkcs7->publicKeyOID == ECDSAk
#ifdef WC_RSA_PSS
|| pkcs7->publicKeyOID == RSAPSSk
#endif
)) {
WOLFSSL_MSG("Pre-calculated content hash is needed in this case");
return BAD_FUNC_ARG;
}
#endif
#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
keyIdSize = wc_HashGetDigestSize(wc_HashTypeConvert(HashIdAlg(
@@ -2965,8 +3155,31 @@ static int PKCS7_EncodeSigned(wc_PKCS7* pkcs7,
idx = ret;
goto out;
}
esd->digEncAlgoIdSz = SetAlgoIDEx(digEncAlgoId, esd->digEncAlgoId,
digEncAlgoType, 0, pkcs7->hashParamsAbsent);
#if defined(WC_RSA_PSS)
if (digEncAlgoId == CTC_RSASSAPSS) {
/* Salt length policy: always encode as hash digest length.
* This is the common CMS/RFC 4055 profile and matches OpenSSL
* defaults. The decoder (pssSaltLen) handles arbitrary values
* from external blobs. A future pkcs7->pssSaltLen override for
* encode could be added here if custom salt lengths are needed. */
int saltLen = wc_HashGetDigestSize(wc_OidGetHash(pkcs7->hashOID));
if (saltLen < 0) {
idx = saltLen;
goto out;
}
esd->digEncAlgoIdSz = wc_EncodeRsaPssAlgoId(pkcs7->hashOID,
(int)(word32)saltLen, esd->digEncAlgoId,
(word32)sizeof(esd->digEncAlgoId));
if (esd->digEncAlgoIdSz == 0) {
idx = ASN_PARSE_E;
goto out;
}
} else
#endif
{
esd->digEncAlgoIdSz = SetAlgoIDEx(digEncAlgoId, esd->digEncAlgoId,
digEncAlgoType, 0, pkcs7->hashParamsAbsent);
}
signerInfoSz += esd->digEncAlgoIdSz;
/* build up signed attributes, include contentType, signingTime, and
@@ -3567,8 +3780,12 @@ int wc_PKCS7_EncodeSignedData(wc_PKCS7* pkcs7, byte* output, word32 outputSz)
return BAD_FUNC_ARG;
}
/* pre-calculate hash for ECC signatures */
if (pkcs7->publicKeyOID == ECDSAk) {
/* pre-calculate content hash for ECDSA and RSA-PSS (both sign digest directly) */
if (pkcs7->publicKeyOID == ECDSAk
#ifdef WC_RSA_PSS
|| pkcs7->publicKeyOID == RSAPSSk
#endif
) {
int hashSz;
enum wc_HashType hashType;
byte hashBuf[WC_MAX_DIGEST_SIZE];
@@ -4071,8 +4288,7 @@ static int wc_PKCS7_RsaVerify(wc_PKCS7* pkcs7, byte* sig, int sigSz,
byte digest[MAX_PKCS7_DIGEST_SZ];
#endif
RsaKey key[1];
DecodedCert stack_dCert;
DecodedCert* dCert = &stack_dCert;
DecodedCert dCert[1];
#endif
if (pkcs7 == NULL || sig == NULL || hash == NULL) {
@@ -4175,6 +4391,144 @@ static int wc_PKCS7_RsaVerify(wc_PKCS7* pkcs7, byte* sig, int sigSz,
return ret;
}
#if defined(WC_RSA_PSS)
/* returns 0 when signature verifies, negative on error */
static int wc_PKCS7_RsaPssVerify(wc_PKCS7* pkcs7, byte* sig, int sigSz,
byte* hash, word32 hashSz)
{
int ret = 0, i;
word32 scratch = 0, verified = 0;
word32 pkSz;
int saltLen, verify;
enum wc_HashType hashType;
int hashDigSz, mgf;
/* wc_RsaPSS_Verify_ex output is PSS-encoded message (RSA_PSS_PAD_SZ +
* saltLen + 2*hLen bytes), which can exceed MAX_PKCS7_DIGEST_SZ.
* Use MAX_ENCRYPTED_KEY_SZ (512) to cover RSA keys up to 4096-bit. */
WC_DECLARE_VAR(digest, byte, MAX_ENCRYPTED_KEY_SZ, 0);
WC_DECLARE_VAR(key, RsaKey, 1, 0);
WC_DECLARE_VAR(dCert, DecodedCert, 1, 0);
if (pkcs7 == NULL || sig == NULL || hash == NULL)
return BAD_FUNC_ARG;
/* Use SignerInfo.signatureAlgorithm params when decoded; else digestAlgo */
if (pkcs7->pssParamsPresent)
hashType = (enum wc_HashType)pkcs7->pssHashType;
else
hashType = wc_OidGetHash(pkcs7->hashOID);
hashDigSz = wc_HashGetDigestSize(hashType);
if (hashDigSz < 0)
return ASN_PARSE_E;
if (hashSz != (word32)hashDigSz)
return ASN_PARSE_E;
mgf = pkcs7->pssParamsPresent
? pkcs7->pssMgf : pkcs7_hash2mgf(hashType);
if (mgf == WC_MGF1NONE)
return ASN_PARSE_E;
WC_ALLOC_VAR_EX(digest, byte, MAX_ENCRYPTED_KEY_SZ, pkcs7->heap,
DYNAMIC_TYPE_TMP_BUFFER, return MEMORY_E);
WC_ALLOC_VAR_EX(key, RsaKey, 1, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER,
{ WC_FREE_VAR_EX(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
return MEMORY_E; });
WC_ALLOC_VAR_EX(dCert, DecodedCert, 1, pkcs7->heap, DYNAMIC_TYPE_DCERT,
{ WC_FREE_VAR_EX(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
WC_FREE_VAR_EX(key, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
return MEMORY_E; });
XMEMSET(digest, 0, MAX_ENCRYPTED_KEY_SZ);
for (i = 0; i < MAX_PKCS7_CERTS; i++) {
if (pkcs7->certSz[i] == 0)
continue;
scratch = 0;
ret = wc_InitRsaKey_ex(key, pkcs7->heap, pkcs7->devId);
if (ret != 0)
break;
InitDecodedCert(dCert, pkcs7->cert[i], pkcs7->certSz[i], pkcs7->heap);
ret = ParseCert(dCert, CA_TYPE, NO_VERIFY, 0);
if (ret < 0) {
FreeDecodedCert(dCert);
wc_FreeRsaKey(key);
continue;
}
pkSz = dCert->pubKeySize;
if (pkSz > (MAX_RSA_INT_SZ + MAX_RSA_E_SZ))
pkSz = (MAX_RSA_INT_SZ + MAX_RSA_E_SZ);
if (wc_RsaPublicKeyDecode(dCert->publicKey, &scratch, key,
pkSz) < 0) {
FreeDecodedCert(dCert);
wc_FreeRsaKey(key);
continue;
}
saltLen = pkcs7->pssParamsPresent
? pkcs7->pssSaltLen : RSA_PSS_SALT_LEN_DEFAULT;
/* wc_RsaPSS_Verify_ex returns PSS encoded message length, not
* the recovered hash. Must then call wc_RsaPSS_CheckPadding_ex
* to verify the PSS padding against the expected hash. */
verify = wc_RsaPSS_Verify_ex(sig, (word32)sigSz, digest,
MAX_ENCRYPTED_KEY_SZ,
hashType, mgf, saltLen, key);
if (verify > 0) {
/* wc_RsaPSS_CheckPadding_ex signature varies across
* FIPS / selftest versions; match the pattern in asn.c */
#if (defined(HAVE_SELFTEST) && \
(!defined(HAVE_SELFTEST_VERSION) || \
(HAVE_SELFTEST_VERSION < 2))) || \
(defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && \
(HAVE_FIPS_VERSION < 2))
ret = wc_RsaPSS_CheckPadding_ex(hash, hashSz, digest,
(word32)verify, hashType,
saltLen);
#elif (defined(HAVE_SELFTEST) && \
(HAVE_SELFTEST_VERSION == 2)) || \
(defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && \
(HAVE_FIPS_VERSION == 2))
ret = wc_RsaPSS_CheckPadding_ex(hash, hashSz, digest,
(word32)verify, hashType,
saltLen, 0);
#else
ret = wc_RsaPSS_CheckPadding_ex2(hash, hashSz, digest,
(word32)verify, hashType,
saltLen,
mp_count_bits(&key->n),
pkcs7->heap);
#endif
}
else {
ret = verify;
}
FreeDecodedCert(dCert);
wc_FreeRsaKey(key);
if (ret == 0) {
verified = 1;
pkcs7->verifyCert = pkcs7->cert[i];
pkcs7->verifyCertSz = pkcs7->certSz[i];
break;
}
ret = 0;
}
if (verified == 0)
ret = SIG_VERIFY_E;
WC_FREE_VAR_EX(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
WC_FREE_VAR_EX(key, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
WC_FREE_VAR_EX(dCert, pkcs7->heap, DYNAMIC_TYPE_DCERT);
return ret;
}
#endif /* WC_RSA_PSS */
#endif /* NO_RSA */
@@ -4194,8 +4548,7 @@ static int wc_PKCS7_EcdsaVerify(wc_PKCS7* pkcs7, byte* sig, int sigSz,
#else
byte digest[MAX_PKCS7_DIGEST_SZ];
ecc_key key[1];
DecodedCert stack_dCert;
DecodedCert* dCert = &stack_dCert;
DecodedCert dCert[1];
#endif
word32 idx = 0;
@@ -4695,6 +5048,14 @@ static int wc_PKCS7_SignedDataVerifySignature(wc_PKCS7* pkcs7, byte* sig,
plainDigestSz);
}
break;
#ifdef WC_RSA_PSS
/* RSA-PSS signs the raw hash (plainDigest), not DigestInfo */
case RSAPSSk:
ret = wc_PKCS7_RsaPssVerify(pkcs7, sig, (int)sigSz, plainDigest,
plainDigestSz);
break;
#endif
#endif
#ifdef HAVE_ECC
@@ -4741,6 +5102,13 @@ static int wc_PKCS7_SetPublicKeyOID(wc_PKCS7* pkcs7, int sigOID)
pkcs7->publicKeyOID = RSAk;
break;
#ifdef WC_RSA_PSS
/* CTC_RSASSAPSS and RSAPSSk are the same OID value */
case CTC_RSASSAPSS:
pkcs7->publicKeyOID = RSAPSSk;
break;
#endif
/* if sigOID is already RSAk */
case RSAk:
pkcs7->publicKeyOID = (word32)sigOID;
@@ -5050,9 +5418,62 @@ static int wc_PKCS7_ParseSignerInfo(wc_PKCS7* pkcs7, byte* in, word32 inSz,
idx += (word32)length;
}
/* Get digestEncryptionAlgorithm - key type or signature type */
if (ret == 0 && GetAlgoId(in, &idx, &sigOID, oidIgnoreType, inSz) < 0) {
ret = ASN_PARSE_E;
/* Get digestEncryptionAlgorithm (signatureAlgorithm) - parse manually
* so we can decode id-RSASSA-PSS parameters in all builds. */
#if defined(WC_RSA_PSS) && !defined(NO_RSA)
pkcs7->pssParamsPresent = 0;
#endif
if (ret == 0) {
int algoSeqLen = 0;
word32 algoContentStart = 0;
if (GetSequence(in, &idx, &algoSeqLen, (word32)inSz) < 0) {
ret = ASN_PARSE_E;
}
else {
algoContentStart = idx; /* first byte of AlgorithmIdentifier content */
if (GetObjectId(in, &idx, &sigOID, oidSigType, inSz) < 0) {
ret = ASN_PARSE_E;
}
/* Only parse params when still inside the AlgorithmIdentifier;
* when optional params are absent, idx is already past the sequence. */
else if (algoContentStart + (word32)algoSeqLen > idx) {
word32 paramsStart = idx;
byte paramTag;
int paramLen = 0;
if (GetASNTag(in, &idx, &paramTag, inSz) != 0 ||
GetLength(in, &idx, &paramLen, inSz) < 0) {
ret = ASN_PARSE_E;
}
else {
#if defined(WC_RSA_PSS) && !defined(NO_RSA)
if ((word32)sigOID == (word32)CTC_RSASSAPSS &&
paramTag == (ASN_SEQUENCE | ASN_CONSTRUCTED)) {
word32 tlvLen = (idx - paramsStart) +
(word32)paramLen;
enum wc_HashType pssHash = WC_HASH_TYPE_SHA;
int pssMgfVal = 0, pssSalt = 0;
if (paramsStart + tlvLen > (word32)inSz) {
return ASN_PARSE_E;
}
ret = wc_DecodeRsaPssParams(in + paramsStart, tlvLen,
&pssHash, &pssMgfVal,
&pssSalt);
if (ret == 0) {
pkcs7->pssSaltLen = pssSalt;
pkcs7->pssHashType = (int)pssHash;
pkcs7->pssMgf = pssMgfVal;
pkcs7->pssParamsPresent = 1;
}
else {
WOLFSSL_MSG("RSASSA-PSS parameters invalid - failing parse");
return ASN_PARSE_E;
}
}
#endif
idx += (word32)paramLen;
}
}
}
}
/* store public key type based on digestEncryptionAlgorithm */
+6
View File
@@ -2506,6 +2506,12 @@ WOLFSSL_LOCAL word32 SetSet(word32 len, byte* output);
WOLFSSL_API word32 SetAlgoID(int algoOID, byte* output, int type, int curveSz);
WOLFSSL_LOCAL word32 SetAlgoIDEx(int algoOID, byte* output, int type, int curveSz,
byte absentParams);
#if defined(WC_RSA_PSS) && !defined(NO_RSA)
WOLFSSL_LOCAL word32 wc_EncodeRsaPssAlgoId(int hashOID, int saltLen, byte* out,
word32 outSz);
WOLFSSL_TEST_VIS int wc_DecodeRsaPssParams(const byte* params, word32 sz,
enum wc_HashType* hash, int* mgf, int* saltLen);
#endif
WOLFSSL_LOCAL int SetMyVersion(word32 version, byte* output, int header);
WOLFSSL_LOCAL int SetSerialNumber(const byte* sn, word32 snSz, byte* output,
word32 outputSz, int maxSnSz);
+7
View File
@@ -388,6 +388,13 @@ struct wc_PKCS7 {
CallbackEccSignRawDigest eccSignRawDigestCb;
#endif
#if defined(WC_RSA_PSS) && !defined(NO_RSA)
int pssSaltLen; /* RSASSA-PSS params from SignerInfo; valid when */
int pssHashType; /* pssParamsPresent == 1; else verify path uses */
int pssMgf; /* RSA_PSS_SALT_LEN_DEFAULT / digest algo defaults */
byte pssParamsPresent;
#endif
/* !! NEW DATA MEMBERS MUST BE ADDED AT END !! */
};