mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-06 20:10:53 +02:00
Merge pull request #9742 from sameehj/pkcs7-rsa-pss
pkcs7: add RSA-PSS support for SignedData
This commit is contained in:
@@ -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'' ',
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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()
|
||||
*/
|
||||
|
||||
@@ -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), \
|
||||
|
||||
@@ -17147,5 +17147,4 @@ int wc_AesCtsDecryptFinal(Aes* aes, byte* out, word32* outSz)
|
||||
|
||||
#endif /* WOLFSSL_AES_CTS */
|
||||
|
||||
|
||||
#endif /* !NO_AES */
|
||||
|
||||
+374
-112
@@ -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
@@ -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, ¶mTag, inSz) != 0 ||
|
||||
GetLength(in, &idx, ¶mLen, 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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 !! */
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user