diff --git a/src/wp_rsa_kmgmt.c b/src/wp_rsa_kmgmt.c index 5163b97f..6e511cca 100644 --- a/src/wp_rsa_kmgmt.c +++ b/src/wp_rsa_kmgmt.c @@ -2407,10 +2407,16 @@ static int wp_rsa_enc_dec_set_ctx_params(wp_RsaEncDecCtx* ctx, return ok; } -/** Common base of RSA PKCS #1.5 and PSS OID. */ +/** First ten bytes shared by the RSA PKCS #1.5 and PSS OIDs. The ninth + * content byte is deliberately absent to match RSA_PKCS1_5_BYTE or + * RSA_PKCS1_PSS_BYTE. */ static const unsigned char rsa_pkcs1_oid[] = { 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01 }; +/** Full RSA PKCS #1 PSS OID. */ +static const unsigned char rsa_pss_oid[] = { + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a +}; /** Size of RSA PKCS OID. */ #define RSA_PKCS1_OID_SZ sizeof(rsa_pkcs1_oid) /** Last byte of RSA PKCS #1.5 OID. */ @@ -2501,45 +2507,45 @@ static int wp_rsa_determine_type(wp_Rsa* rsa, unsigned char* data, word32 len) static int wp_rsa_pss_get_params(wp_Rsa* rsa, unsigned char* data, word32 len) { int ok; + int haveParams = 0; word32 idx = 0; + word32 algIdEnd = 0; wp_RsaPssParams* pss = &rsa->pssParams; - ok = wp_rsa_pss_params_set_pss_defaults(pss); + ok = wp_rsa_find_oid(data, len, rsa_pkcs1_oid, RSA_PKCS1_OID_SZ, &idx); + if (ok && ((idx < 2) || (data[idx - 2] != 0x30))) { + /* OID is not inside an AlgorithmIdentifier SEQUENCE. */ + ok = 0; + } if (ok) { - ok = wp_rsa_find_oid(data, len, rsa_pkcs1_oid, RSA_PKCS1_OID_SZ, - &idx); + /* Length octet gives the end; a PSS AlgorithmIdentifier is + * always short form. */ + algIdEnd = idx + data[idx - 1]; + if (algIdEnd > len) { + ok = 0; + } } if (ok) { /* Step over PSS algorithm. */ - idx += 11; - - /* Step over BIT STRING field */ - if (data[idx] == 0x03) { - idx++; - if (data[idx] < 0x80) { - idx += 2; - } - else if (data[idx] == 0x81) { - idx += 3; - } - else if (data[idx] == 0x82) { - idx += 4; - } - else { - ok = 0; - } + idx += RSA_PKCS1_OID_SZ + 1; + if (idx > algIdEnd) { + ok = 0; } - - if (ok) { + /* Nothing after the OID means no restrictions. */ + else if (idx < algIdEnd) { if (data[idx] != 0x30) { ok = 0; } else { + haveParams = 1; idx += 2; } } } - if (ok && (data[idx] == 0xa0)) { + if (ok && haveParams) { + ok = wp_rsa_pss_params_set_pss_defaults(pss); + } + if (ok && haveParams && (idx < algIdEnd) && (data[idx] == 0xa0)) { /* Hash algorithm */ if (XMEMCMP(data + idx, sha224AlgId, sizeof(sha224AlgId)) == 0) { pss->hashType = WC_HASH_TYPE_SHA224; @@ -2590,7 +2596,7 @@ static int wp_rsa_pss_get_params(wp_Rsa* rsa, unsigned char* data, word32 len) ok = 0; } } - if (ok && (data[idx] == 0xa1)) { + if (ok && haveParams && (idx < algIdEnd) && (data[idx] == 0xa1)) { /* MGF algorithm */ if (XMEMCMP(data + idx, mgf1Sha224AlgId, sizeof(mgf1Sha224AlgId)) == 0) { @@ -2644,7 +2650,7 @@ static int wp_rsa_pss_get_params(wp_Rsa* rsa, unsigned char* data, word32 len) ok = 0; } } - if (ok && (data[idx] == 0xa2)) { + if (ok && haveParams && (idx < algIdEnd) && (data[idx] == 0xa2)) { /* Salt length. */ if (XMEMCMP(data + idx, saltLenDer, sizeof(saltLenDer)) == 0) { idx += sizeof(saltLenDer); @@ -2661,7 +2667,7 @@ static int wp_rsa_pss_get_params(wp_Rsa* rsa, unsigned char* data, word32 len) } } /* TODO: handle optional trailer: 0xa3. */ - if (ok) { + if (ok && haveParams) { /* PSS parameters have been seen and set. */ rsa->pssDefSet = 1; } @@ -2949,7 +2955,8 @@ static int wp_rsa_decode(wp_RsaEncDecCtx* ctx, OSSL_CORE_BIO* cBio, /** * Encode the RSA-PSS Algorithm ID in DER * - * @param [in] rsa RSA key object. + * @param [in] rsa RSA key object. May be NULL when saltLen is an + * explicit length. * @param [in] mdName Name of digest. * @param [in] mgf1Name Name of MGF algorithm's digest. * @param [in] saltLen Length of salt used. @@ -2965,10 +2972,6 @@ int wp_rsa_pss_encode_alg_id(const wp_Rsa* rsa, const char* mdName, int ok = 1; int i = 0; int hashLen = WP_RSA_DEFAULT_SALT_LEN; - const byte rsa_pss_oid[11] = { - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x0a - }; int seq1LenIdx; int seq2LenIdx; @@ -3139,6 +3142,39 @@ int wp_rsa_pss_encode_alg_id(const wp_Rsa* rsa, const char* mdName, return ok; } +/** + * Encode the AlgorithmIdentifier for an RSA-PSS key in DER. + * + * A key with no PSS restrictions encodes as the OID alone. + * + * @param [in] rsa RSA key object. + * @param [out] algId Buffer to hold encoded AlgorithmIdentifier. + * May be NULL and only length calculated. + * @param [out] len Length of encoding. + * @return 1 on success. + * @return 0 on failure. + */ +static int wp_rsa_pss_encode_key_alg_id(const wp_Rsa* rsa, byte* algId, + word32* len) +{ + int ok = 1; + + if (!rsa->pssDefSet) { + if (algId != NULL) { + algId[0] = 0x30; + algId[1] = (byte)sizeof(rsa_pss_oid); + XMEMCPY(algId + 2, rsa_pss_oid, sizeof(rsa_pss_oid)); + } + *len = (word32)(2 + sizeof(rsa_pss_oid)); + } + else { + ok = wp_rsa_pss_encode_alg_id(rsa, rsa->pssParams.mdName, + rsa->pssParams.mgfMdName, rsa->pssParams.saltLen, algId, len); + } + + return ok; +} + /** * Get the SubjectPublicKeyInfo encoding size for the key. * @@ -3175,8 +3211,7 @@ static int wp_rsa_encode_spki_size(const wp_Rsa* rsa, size_t* keyLen) #endif if (ok && (rsa->type == RSA_FLAG_TYPE_RSASSAPSS)) { word32 pssLen = 0; - ok = wp_rsa_pss_encode_alg_id(rsa, rsa->pssParams.mdName, - rsa->pssParams.mgfMdName, rsa->pssParams.saltLen, NULL, &pssLen); + ok = wp_rsa_pss_encode_key_alg_id(rsa, NULL, &pssLen); if (ok) { *keyLen += pssLen; } @@ -3220,21 +3255,17 @@ static int wp_rsa_encode_spki(const wp_Rsa* rsa, unsigned char* keyData, if (ok) { i += 13; /* Get length of encoded RSA-PSS Algorithm ID. */ - ok = wp_rsa_pss_encode_alg_id(rsa, rsa->pssParams.mdName, - rsa->pssParams.mgfMdName, rsa->pssParams.saltLen, NULL, - &pssLen); + ok = wp_rsa_pss_encode_key_alg_id(rsa, NULL, &pssLen); } if (ok) { /* Move rest of key to after RSA-PSS Algorithm ID. */ XMEMMOVE(keyData + 4 + pssLen, keyData + i, ret - i); /* Encode RSA-PSS Algorithm ID. */ - ok = wp_rsa_pss_encode_alg_id(rsa, rsa->pssParams.mdName, - rsa->pssParams.mgfMdName, rsa->pssParams.saltLen, - keyData + 4, &pssLen); + ok = wp_rsa_pss_encode_key_alg_id(rsa, keyData + 4, &pssLen); } if (ok) { /* Update return length. */ - ret += pssLen - i; + ret += (int)pssLen - (int)i; *keyLen = ret + 4; /* Update first sequence. */ keyData[2] = (byte)(ret >> 8); @@ -3359,8 +3390,7 @@ static int wp_rsa_encode_pki_size(const wp_Rsa* rsa, size_t* keyLen, int algoId) } if (ok && (rsa->type == RSA_FLAG_TYPE_RSASSAPSS)) { word32 pssLen = 0; - ok = wp_rsa_pss_encode_alg_id(rsa, rsa->pssParams.mdName, - rsa->pssParams.mgfMdName, rsa->pssParams.saltLen, NULL, &pssLen); + ok = wp_rsa_pss_encode_key_alg_id(rsa, NULL, &pssLen); if (ok) { *keyLen += pssLen; } @@ -3428,21 +3458,17 @@ static int wp_rsa_encode_pki(const wp_Rsa* rsa, unsigned char* keyData, if (ok) { i += 11; /* Get length of encoded RSA-PSS Algorithm ID. */ - ok = wp_rsa_pss_encode_alg_id(rsa, rsa->pssParams.mdName, - rsa->pssParams.mgfMdName, rsa->pssParams.saltLen, NULL, - &pssLen); + ok = wp_rsa_pss_encode_key_alg_id(rsa, NULL, &pssLen); } if (ok) { /* Move rest of key to after RSA-PSS Algorithm ID. */ XMEMMOVE(keyData + 7 + pssLen, keyData + i, ret - i); /* Encode RSA-PSS Algorithm ID. */ - ok = wp_rsa_pss_encode_alg_id(rsa, rsa->pssParams.mdName, - rsa->pssParams.mgfMdName, rsa->pssParams.saltLen, - keyData + 7, &pssLen); + ok = wp_rsa_pss_encode_key_alg_id(rsa, keyData + 7, &pssLen); } if (ok) { /* Update return length. */ - ret += pssLen - 13; + ret += (int)pssLen - 13; /* Update first sequence. */ keyData[2] = (byte)((ret - 4) >> 8); keyData[3] = (byte)((ret - 4) & 0xff); diff --git a/src/wp_rsa_sig.c b/src/wp_rsa_sig.c index 4568eb92..a9f81482 100644 --- a/src/wp_rsa_sig.c +++ b/src/wp_rsa_sig.c @@ -280,6 +280,8 @@ static wp_RsaSigCtx* wp_rsa_ctx_new(WOLFPROV_CTX* provCtx, ctx->propQuery = p; ctx->provCtx = provCtx; ctx->libCtx = provCtx->libCtx; + /* No PSS restrictions from a key yet. */ + ctx->minSaltLen = -1; } if (!ok) { @@ -349,6 +351,7 @@ static wp_RsaSigCtx* wp_rsa_ctx_dup(wp_RsaSigCtx* srcCtx) dstCtx->padMode = srcCtx->padMode; dstCtx->op = srcCtx->op; dstCtx->saltLen = srcCtx->saltLen; + dstCtx->minSaltLen = srcCtx->minSaltLen; XMEMCPY(dstCtx->mdName, srcCtx->mdName, sizeof(srcCtx->mdName)); XMEMCPY(dstCtx->mgf1MdName, srcCtx->mgf1MdName, sizeof(srcCtx->mgf1MdName)); @@ -512,9 +515,9 @@ static int wp_rsa_signverify_init(wp_RsaSigCtx* ctx, wp_Rsa* rsa, #ifdef RSA_PSS_SALTLEN_AUTO_DIGEST_MAX ctx->saltLen = RSA_PSS_SALTLEN_AUTO_DIGEST_MAX; #else - ctx->saltLen = WP_RSA_DEFAULT_SALT_LEN; + ctx->saltLen = RSA_PSS_SALTLEN_AUTO; #endif - ctx->minSaltLen = 0; + ctx->minSaltLen = -1; /* If we have already set PSS salt len, use that */ saltLen = wp_rsa_get_pss_salt_len(ctx->rsa); diff --git a/test/test_rsa.c b/test/test_rsa.c index 20c36a23..7a9b352a 100644 --- a/test/test_rsa.c +++ b/test/test_rsa.c @@ -478,6 +478,117 @@ static const unsigned char rsa_pss_key_der_2048_pkcs8_512[] = 0x7c, 0xf4, 0x75, 0x37, 0x70, 0x1f, 0x51, 0xe8, 0x55, 0x61, 0x32, }; +/* +* 2048 bit RSA-PSS private key in PKCS#8 format, no PSS parameters. +* openssl genpkey -algorithm RSA-PSS -outform der -pkeyopt rsa_keygen_bits:2048 +*/ +static const unsigned char rsa_pss_key_der_2048_pkcs8_noparams[] = +{ + 0x30, 0x82, 0x04, 0xbb, 0x02, 0x01, 0x00, 0x30, 0x0b, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, 0x04, 0x82, 0x04, 0xa7, + 0x30, 0x82, 0x04, 0xa3, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xd4, 0x7c, 0x92, 0x8a, 0xe4, 0x2d, 0xc3, 0x1f, 0x13, 0x20, 0x81, 0x3d, + 0x18, 0x03, 0x10, 0x3c, 0x6a, 0xc7, 0x4c, 0x77, 0xed, 0x65, 0xd7, 0x2f, + 0x94, 0xc8, 0x09, 0x71, 0xf5, 0x5e, 0xb1, 0xd8, 0x6f, 0x78, 0xea, 0x73, + 0xa5, 0x69, 0x3b, 0x90, 0x8f, 0x37, 0xe9, 0x82, 0x20, 0x07, 0x18, 0x21, + 0xe9, 0x61, 0xf4, 0x94, 0xef, 0x30, 0x81, 0x06, 0x0b, 0xcc, 0x3b, 0x5f, + 0x7c, 0xad, 0xf6, 0xf1, 0x77, 0x39, 0xd2, 0x49, 0x5e, 0x16, 0x0c, 0x04, + 0x72, 0xb8, 0xf2, 0x7d, 0x6d, 0xc0, 0x09, 0x70, 0x5a, 0x5e, 0x91, 0x3d, + 0xf5, 0xa4, 0x8b, 0x9f, 0x8d, 0x89, 0x66, 0x6c, 0x19, 0xb2, 0x51, 0x9c, + 0x9f, 0x32, 0xe7, 0x97, 0xc9, 0xca, 0xed, 0x65, 0x4e, 0x0f, 0x8c, 0x41, + 0x2d, 0xc9, 0xc8, 0xb5, 0x89, 0x4a, 0x9e, 0xf2, 0xf5, 0xaa, 0xd7, 0x9e, + 0xb0, 0x1c, 0x31, 0x45, 0x66, 0xf3, 0x96, 0x5a, 0x06, 0xa7, 0x90, 0x12, + 0x86, 0xe6, 0x86, 0x29, 0x89, 0xe1, 0x1d, 0xe8, 0x60, 0xc4, 0xa9, 0xc3, + 0xea, 0x30, 0x1e, 0x75, 0x5d, 0x76, 0xaa, 0x88, 0xa5, 0xc9, 0xce, 0xc5, + 0x7f, 0x7b, 0x4d, 0xc2, 0x64, 0x72, 0x33, 0xb3, 0x97, 0xce, 0xc8, 0x0d, + 0x2f, 0xf0, 0x7d, 0x10, 0xb9, 0xe8, 0x3d, 0xf8, 0x03, 0x30, 0x69, 0x60, + 0xfa, 0x90, 0x26, 0x56, 0x90, 0xa9, 0x62, 0x36, 0xf3, 0x23, 0x6d, 0x6b, + 0x95, 0x34, 0x34, 0x85, 0xcc, 0x56, 0x69, 0x09, 0xac, 0x54, 0xc7, 0x40, + 0x81, 0x8d, 0x59, 0xcd, 0x61, 0x46, 0x0b, 0x59, 0xe2, 0xac, 0x71, 0xe4, + 0xa3, 0x51, 0xc7, 0xa3, 0xbc, 0x45, 0x17, 0x63, 0x32, 0x84, 0x2d, 0x82, + 0x2f, 0x3a, 0x51, 0xfd, 0x06, 0xd2, 0xd3, 0xe6, 0x3c, 0x0d, 0x40, 0x79, + 0x5b, 0x6f, 0x81, 0x14, 0x50, 0x70, 0xf9, 0x31, 0x2c, 0xbd, 0x96, 0xc5, + 0xdb, 0x49, 0x7e, 0x19, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, + 0x00, 0x22, 0xa6, 0xc4, 0x01, 0x19, 0x6f, 0xeb, 0x0b, 0x84, 0xc3, 0x7e, + 0xbf, 0xf7, 0x4b, 0xfc, 0x98, 0x55, 0xb3, 0x75, 0xa1, 0x73, 0x19, 0xf2, + 0x0b, 0x24, 0xe0, 0xb0, 0x45, 0xa0, 0xc5, 0x96, 0x0c, 0x1d, 0x35, 0xa3, + 0x4a, 0x43, 0xb5, 0xd3, 0x0c, 0x62, 0x86, 0xb4, 0x32, 0x4f, 0xc5, 0xc8, + 0x66, 0x33, 0x58, 0x91, 0x99, 0xbe, 0x1d, 0xf3, 0x54, 0x88, 0x95, 0x00, + 0x33, 0xb4, 0xb1, 0xeb, 0xfc, 0xf9, 0xbb, 0xf8, 0xb2, 0x95, 0x40, 0xd4, + 0x29, 0xdf, 0x89, 0x90, 0xa5, 0xea, 0xc1, 0x6f, 0xe8, 0x9e, 0x1c, 0x20, + 0xe4, 0x71, 0x4c, 0x29, 0xa6, 0xd5, 0xde, 0x8b, 0xd0, 0xe2, 0x29, 0xe5, + 0xcc, 0x3c, 0x5c, 0x01, 0x4f, 0x8c, 0x0d, 0x67, 0x44, 0x67, 0x83, 0xfa, + 0x93, 0xe0, 0x83, 0x31, 0x13, 0x85, 0xb5, 0xa1, 0x11, 0x2f, 0x05, 0xf2, + 0xce, 0xc2, 0x9a, 0x7e, 0x60, 0x7a, 0x87, 0x0b, 0xc4, 0x0b, 0x52, 0x1c, + 0x04, 0xa2, 0x70, 0x1e, 0xa9, 0x8d, 0x10, 0xb2, 0x29, 0x95, 0x78, 0xd6, + 0xef, 0x0a, 0xa3, 0x97, 0xdf, 0xc7, 0xbf, 0xa4, 0x58, 0x1b, 0xe6, 0xe8, + 0xf3, 0xa5, 0x3d, 0x19, 0x44, 0xc8, 0xe8, 0xed, 0xdf, 0x16, 0x1b, 0xb0, + 0x38, 0x9c, 0x67, 0xa7, 0xb3, 0x12, 0xec, 0xab, 0xd9, 0xa3, 0x76, 0xe6, + 0x8b, 0x9c, 0xbb, 0x87, 0xde, 0x8b, 0xc8, 0xae, 0xe5, 0x65, 0xa2, 0x28, + 0x96, 0xa7, 0xee, 0xed, 0x37, 0x2c, 0x3f, 0x42, 0x82, 0xc0, 0x36, 0x8b, + 0xc9, 0xcc, 0xae, 0xd3, 0xae, 0x76, 0x67, 0xd2, 0xb2, 0x3e, 0x4c, 0x87, + 0x40, 0x5b, 0x50, 0x61, 0x64, 0x1b, 0xd8, 0xfd, 0x69, 0x68, 0xd8, 0x0d, + 0x47, 0xec, 0x4b, 0xa3, 0xc6, 0x6d, 0xfc, 0xaf, 0x0c, 0x8e, 0xdd, 0x83, + 0xd0, 0x18, 0xba, 0x41, 0xb9, 0xd7, 0x6e, 0x31, 0xa6, 0x04, 0xf1, 0x0a, + 0x98, 0x56, 0x58, 0x7d, 0x99, 0x02, 0x81, 0x81, 0x00, 0xfd, 0x0c, 0xd9, + 0xb9, 0x85, 0x66, 0x53, 0x9b, 0xaf, 0xae, 0x54, 0x54, 0xff, 0x83, 0x7f, + 0x5e, 0xe4, 0x23, 0x31, 0xe8, 0x37, 0x39, 0x19, 0x0f, 0x01, 0x45, 0xe8, + 0x8d, 0x5f, 0x7f, 0xf5, 0xb0, 0x48, 0xd9, 0x12, 0xed, 0xce, 0x3b, 0xca, + 0x43, 0x30, 0xd4, 0x70, 0xd3, 0x6d, 0x4d, 0x1c, 0x93, 0x5b, 0xcc, 0x42, + 0xec, 0x16, 0x08, 0x77, 0x22, 0x7b, 0xed, 0x6a, 0xdf, 0xa1, 0x5f, 0x2c, + 0x07, 0xd5, 0x43, 0x55, 0x49, 0x4a, 0x19, 0xe6, 0xfd, 0x0b, 0xd2, 0x48, + 0xfe, 0xbf, 0xa4, 0x0a, 0x24, 0xe3, 0x69, 0x59, 0x59, 0xcf, 0xa1, 0x46, + 0xdc, 0xa7, 0x70, 0xfd, 0x37, 0x7d, 0x58, 0x35, 0x6a, 0x5d, 0xcf, 0x08, + 0xb8, 0xf6, 0xc6, 0xe6, 0xf0, 0xd2, 0x6f, 0x81, 0x24, 0x68, 0x5c, 0x8a, + 0x33, 0x1a, 0xa6, 0xaf, 0xfc, 0xb6, 0x8b, 0xbd, 0x2b, 0x6c, 0x41, 0x1e, + 0x1e, 0x6d, 0x2b, 0x90, 0xdd, 0x02, 0x81, 0x81, 0x00, 0xd6, 0xf6, 0xac, + 0x2c, 0x77, 0x45, 0xcc, 0x10, 0xf1, 0x5e, 0x01, 0x6a, 0x7a, 0x00, 0x03, + 0xde, 0x64, 0x91, 0x0f, 0xc7, 0x10, 0xb8, 0x6c, 0x0d, 0xe2, 0x19, 0x37, + 0x15, 0x7e, 0xf9, 0x73, 0x98, 0x84, 0x5a, 0x38, 0x5a, 0xab, 0x4a, 0x6d, + 0xde, 0xf6, 0xec, 0x54, 0x9b, 0xcd, 0xbe, 0x6e, 0x4f, 0xbf, 0x45, 0x89, + 0xab, 0xa7, 0xb2, 0x88, 0xc4, 0xc7, 0x59, 0x0f, 0x1c, 0xdc, 0x61, 0xef, + 0x23, 0x84, 0xc9, 0x55, 0x6b, 0x4d, 0x5f, 0x8b, 0x9e, 0x4e, 0x1a, 0x83, + 0x22, 0x92, 0x31, 0x8b, 0x9b, 0x2b, 0xec, 0x5b, 0xb8, 0x1e, 0x79, 0x8b, + 0x16, 0x1a, 0x4e, 0x12, 0x85, 0xb3, 0xcd, 0x7c, 0x5d, 0x67, 0x81, 0x7c, + 0x4f, 0xc9, 0xb0, 0x8e, 0x9a, 0xc4, 0x95, 0x68, 0x98, 0xbf, 0xcb, 0x9e, + 0x82, 0x9a, 0x2b, 0x5c, 0x85, 0x81, 0xb9, 0xce, 0x2e, 0x0d, 0x10, 0x2d, + 0x8d, 0x36, 0xfb, 0x10, 0x6d, 0x02, 0x81, 0x81, 0x00, 0x93, 0x7a, 0x50, + 0xce, 0x09, 0xcc, 0x18, 0x40, 0x2d, 0x04, 0x49, 0xcb, 0xfb, 0xda, 0x74, + 0x7a, 0x26, 0x35, 0xc8, 0xd9, 0x50, 0x8d, 0xeb, 0x67, 0xce, 0x53, 0x22, + 0xf4, 0x79, 0xcb, 0x06, 0xb2, 0x85, 0xd2, 0x55, 0x70, 0xd8, 0xf8, 0x09, + 0xaa, 0x5d, 0x24, 0x02, 0xd9, 0x85, 0xe3, 0x22, 0x1b, 0x53, 0xad, 0xd8, + 0xf6, 0xe4, 0xe4, 0x6c, 0x22, 0x61, 0xdd, 0x7d, 0xa2, 0xf5, 0xd6, 0x94, + 0xbf, 0xbf, 0xef, 0xed, 0x1d, 0xb8, 0x9d, 0x1d, 0xfd, 0x2e, 0x4d, 0x91, + 0x13, 0x3f, 0x37, 0x68, 0x5e, 0x15, 0x57, 0x89, 0xa6, 0x89, 0x16, 0xb2, + 0x48, 0xd9, 0x21, 0x45, 0xc2, 0x78, 0x3f, 0xd7, 0x69, 0xb5, 0x60, 0x7a, + 0xf1, 0xe9, 0x89, 0x04, 0x81, 0x27, 0xf3, 0xaa, 0x6d, 0x40, 0x91, 0x66, + 0x4d, 0xe0, 0x63, 0x73, 0x3f, 0xb2, 0x58, 0x00, 0x98, 0xf5, 0x0f, 0x82, + 0x71, 0x33, 0x28, 0xe7, 0xd5, 0x02, 0x81, 0x80, 0x45, 0x3c, 0xfc, 0xa5, + 0xf1, 0xe0, 0xec, 0x62, 0xc4, 0xab, 0xa7, 0xbf, 0x18, 0xfe, 0xaa, 0x40, + 0x56, 0x9a, 0x16, 0xbf, 0xe2, 0x95, 0xd4, 0x50, 0x00, 0x74, 0x68, 0xb6, + 0xa6, 0xc8, 0x48, 0x40, 0x66, 0xd8, 0xfc, 0x6f, 0xcf, 0xde, 0x50, 0x57, + 0x51, 0xa7, 0x3b, 0x75, 0x57, 0xa6, 0xaf, 0xb7, 0x45, 0xff, 0x6a, 0xe1, + 0x7c, 0x49, 0x58, 0x1e, 0x31, 0xab, 0x70, 0xdf, 0xb5, 0xe8, 0x61, 0x73, + 0x4a, 0x50, 0xe3, 0x0f, 0x85, 0xc3, 0x71, 0xa0, 0x12, 0xbe, 0x1d, 0x60, + 0xe4, 0x3f, 0xcb, 0x1c, 0xb6, 0xe0, 0x11, 0x86, 0xd3, 0x62, 0x08, 0xb7, + 0x9c, 0xcf, 0x6a, 0xcd, 0xdf, 0xe5, 0x1e, 0xda, 0xa2, 0xfd, 0x25, 0x02, + 0x3a, 0x0f, 0x81, 0x4f, 0x3e, 0x64, 0x75, 0x65, 0x45, 0xd9, 0x9a, 0xfb, + 0xac, 0x16, 0xad, 0x5d, 0x5f, 0x34, 0x6e, 0xc1, 0x8c, 0x79, 0x9a, 0x82, + 0x72, 0xa4, 0x19, 0x5d, 0x02, 0x81, 0x80, 0x6b, 0xbb, 0x48, 0x0e, 0x94, + 0xbd, 0x4e, 0xed, 0xcb, 0x2a, 0x6f, 0x91, 0xb2, 0x92, 0x05, 0x0c, 0xcc, + 0xf2, 0xbb, 0x58, 0x5c, 0x22, 0xd2, 0x87, 0x85, 0xc1, 0xa6, 0xa1, 0x39, + 0xca, 0x89, 0x3f, 0xe0, 0x0f, 0x04, 0x5d, 0x0a, 0xc1, 0x1f, 0xb6, 0x0a, + 0xf9, 0xc7, 0xa1, 0xc0, 0xb2, 0x15, 0x1c, 0xdc, 0xee, 0x73, 0x9d, 0x61, + 0x2d, 0x88, 0x38, 0x04, 0x93, 0x3a, 0x58, 0x98, 0x38, 0x0c, 0x81, 0x35, + 0x40, 0xd5, 0xc1, 0x2e, 0xd4, 0xf8, 0xd4, 0x10, 0xe6, 0x39, 0x79, 0xad, + 0x96, 0x6b, 0xf8, 0x76, 0xb0, 0x11, 0x72, 0x9b, 0xde, 0xeb, 0x5f, 0x97, + 0x98, 0x29, 0x9a, 0x60, 0x45, 0xa2, 0x6b, 0x56, 0x42, 0x63, 0x59, 0x4a, + 0xe6, 0xd8, 0x92, 0x8b, 0x75, 0xcf, 0x69, 0x71, 0x82, 0xad, 0xdb, 0x88, + 0xa6, 0x39, 0xee, 0x45, 0xc1, 0x7c, 0x0d, 0x43, 0xec, 0xe2, 0x59, 0xc7, + 0xed, 0x7e, 0x4d, +}; + + static int test_rsa_sign_verify_pad(int padMode, const EVP_MD *md, const EVP_MD *mgf1Md, int saltlen) { @@ -1033,6 +1144,334 @@ int test_rsa_pss_restrictions(void *data) return err; } +/* + * Sign a message with mdName and verify it back, each side using its own + * library context so a key decoded by one provider is checked by the other. + */ +static int test_rsa_pss_np_sign_verify(EVP_PKEY* signKey, OSSL_LIB_CTX* signCtx, + EVP_PKEY* verifyKey, OSSL_LIB_CTX* verifyCtx, const char* mdName) +{ + int err = 0; + EVP_MD_CTX* mdCtx = NULL; + unsigned char sig[512]; + size_t sigLen = sizeof(sig); + static const unsigned char msg[] = "RSA-PSS key with no restrictions"; + + mdCtx = EVP_MD_CTX_new(); + err = mdCtx == NULL; + if (err == 0) { + err = EVP_DigestSignInit_ex(mdCtx, NULL, mdName, signCtx, NULL, signKey, + NULL) != 1; + if (err != 0) { + PRINT_ERR_MSG("Sign init rejected digest %s", mdName); + } + } + if (err == 0) { + err = EVP_DigestSign(mdCtx, sig, &sigLen, msg, sizeof(msg)) != 1; + } + EVP_MD_CTX_free(mdCtx); + mdCtx = NULL; + + if (err == 0) { + mdCtx = EVP_MD_CTX_new(); + err = mdCtx == NULL; + } + if (err == 0) { + err = EVP_DigestVerifyInit_ex(mdCtx, NULL, mdName, verifyCtx, NULL, + verifyKey, NULL) != 1; + } + if (err == 0) { + err = EVP_DigestVerify(mdCtx, sig, sigLen, msg, sizeof(msg)) != 1; + if (err != 0) { + PRINT_ERR_MSG("Verify failed for digest %s", mdName); + } + } + EVP_MD_CTX_free(mdCtx); + + return err; +} + +/* + * Re-encode the key and compare against the DER it was decoded from. + */ +static int test_rsa_pss_np_encode(EVP_PKEY* pkey, int pub, + const unsigned char* expected, int expectedLen) +{ + int err = 0; + unsigned char* der = NULL; + int derLen; + + if (pub) { + derLen = i2d_PUBKEY(pkey, &der); + } + else { + derLen = i2d_PrivateKey(pkey, &der); + } + err = derLen <= 0; + if ((err == 0) && ((derLen != expectedLen) || + (memcmp(der, expected, (size_t)expectedLen) != 0))) { + PRINT_ERR_MSG("Re-encoded %s key is %d bytes, expected %d", + pub ? "public" : "private", derLen, expectedLen); + err = 1; + } + OPENSSL_free(der); + + return err; +} + +/* + * A restricted key must still pin its digest. + */ +static int test_rsa_pss_np_restricted(void) +{ + int err; + EVP_PKEY* pkey = NULL; + EVP_MD_CTX* mdCtx = NULL; + const unsigned char* p = rsa_pss_key_der_2048_pkcs8_512; + + pkey = d2i_AutoPrivateKey_ex(NULL, &p, + (long)sizeof(rsa_pss_key_der_2048_pkcs8_512), wpLibCtx, NULL); + err = pkey == NULL; + if (err != 0) { + PRINT_ERR_MSG("Failed to decode restricted RSA-PSS key"); + } + if (err == 0) { + err = test_rsa_pss_np_sign_verify(pkey, wpLibCtx, pkey, wpLibCtx, + "SHA512"); + } + if (err == 0) { + mdCtx = EVP_MD_CTX_new(); + err = mdCtx == NULL; + } + if (err == 0) { + if (EVP_DigestSignInit_ex(mdCtx, NULL, "SHA256", wpLibCtx, NULL, pkey, + NULL) == 1) { + PRINT_ERR_MSG("SHA256 accepted for a SHA512 restricted key"); + err = 1; + } + } + + EVP_MD_CTX_free(mdCtx); + EVP_PKEY_free(pkey); + + return err; +} + +/* + * A duplicated context must keep the key's salt length restriction. + */ +static int test_rsa_pss_np_dupctx(void) +{ + int err; + EVP_PKEY* pkey = NULL; + EVP_MD_CTX* mdCtx = NULL; + EVP_MD_CTX* dupCtx = NULL; + EVP_PKEY_CTX* pkeyCtx = NULL; + const unsigned char* p = rsa_pss_key_der_2048_pkcs8_512; + + pkey = d2i_AutoPrivateKey_ex(NULL, &p, + (long)sizeof(rsa_pss_key_der_2048_pkcs8_512), wpLibCtx, NULL); + err = pkey == NULL; + if (err == 0) { + mdCtx = EVP_MD_CTX_new(); + err = mdCtx == NULL; + } + if (err == 0) { + err = EVP_DigestSignInit_ex(mdCtx, NULL, "SHA512", wpLibCtx, NULL, + pkey, NULL) != 1; + } + if (err == 0) { + dupCtx = EVP_MD_CTX_new(); + err = dupCtx == NULL; + } + if (err == 0) { + err = EVP_MD_CTX_copy_ex(dupCtx, mdCtx) != 1; + } + if (err == 0) { + pkeyCtx = EVP_MD_CTX_get_pkey_ctx(dupCtx); + err = pkeyCtx == NULL; + } + if (err == 0) { + /* Below the key's 64 byte minimum: the duplicate must refuse it. */ + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkeyCtx, 20) > 0) { + PRINT_ERR_MSG("Duplicated context lost the salt length minimum"); + err = 1; + } + } + + EVP_MD_CTX_free(dupCtx); + EVP_MD_CTX_free(mdCtx); + EVP_PKEY_free(pkey); + + return err; +} + +/* + * Rebuild the SPKI with the given RSASSA-PSS-params bytes and decode it. + */ +static int test_rsa_pss_np_with_params(const unsigned char* spki, int spkiLen, + const unsigned char* params, int paramsLen, EVP_PKEY** pkey) +{ + int err = 0; + unsigned char der[512]; + const unsigned char* p; + + *pkey = NULL; + if ((spkiLen <= 17) || ((size_t)(spkiLen + paramsLen) > sizeof(der))) { + PRINT_ERR_MSG("SPKI of %d bytes does not fit the test buffer", + spkiLen); + err = 1; + } + if (err == 0) { + /* In 30 82 LL LL 30 0b <11 byte OID>: 3 is the low byte of the outer + * length, 5 the AlgorithmIdentifier length, 17 the end of the OID. */ + XMEMCPY(der, spki, 17); + der[3] = (unsigned char)(der[3] + paramsLen); + der[5] = (unsigned char)(der[5] + paramsLen); + XMEMCPY(der + 17, params, (size_t)paramsLen); + XMEMCPY(der + 17 + paramsLen, spki + 17, (size_t)(spkiLen - 17)); + p = der; + *pkey = d2i_PUBKEY_ex(NULL, &p, (long)(spkiLen + paramsLen), wpLibCtx, + NULL); + } + + return err; +} + +int test_rsa_pss_no_params(void *data) +{ + int err = 0; + EVP_PKEY* wpKey = NULL; + EVP_PKEY* osslKey = NULL; + EVP_PKEY* wpPub = NULL; + EVP_PKEY* varKey = NULL; + const unsigned char* p; + unsigned char* spki = NULL; + static const unsigned char emptyParams[] = { 0x30, 0x00 }; + int spkiLen = 0; + OSSL_PARAM params[2]; + char mdName[64] = ""; + int saltLen = 0; + const char* mds[] = { "SHA256", "SHA384", "SHA512" }; + size_t i; + + (void)data; + + PRINT_MSG("Decode PKCS#8 RSA-PSS key with no PSS parameters"); + p = rsa_pss_key_der_2048_pkcs8_noparams; + wpKey = d2i_AutoPrivateKey_ex(NULL, &p, + (long)sizeof(rsa_pss_key_der_2048_pkcs8_noparams), wpLibCtx, NULL); + err = wpKey == NULL; + if (err != 0) { + PRINT_ERR_MSG("Failed to decode key with no PSS parameters"); + } + if (err == 0) { + p = rsa_pss_key_der_2048_pkcs8_noparams; + osslKey = d2i_AutoPrivateKey_ex(NULL, &p, + (long)sizeof(rsa_pss_key_der_2048_pkcs8_noparams), osslLibCtx, + NULL); + err = osslKey == NULL; + } + + if (err == 0) { + /* OpenSSL's own SubjectPublicKeyInfo for this key is the reference + * encoding to decode and to compare against. */ + spkiLen = i2d_PUBKEY(osslKey, &spki); + err = spkiLen <= 0; + } + if (err == 0) { + PRINT_MSG("Decode SubjectPublicKeyInfo with no PSS parameters"); + p = spki; + wpPub = d2i_PUBKEY_ex(NULL, &p, (long)spkiLen, wpLibCtx, NULL); + err = wpPub == NULL; + } + + if (err == 0) { + PRINT_MSG("Unrestricted key reports the default digest"); + err = EVP_PKEY_get_utf8_string_param(wpPub, + OSSL_PKEY_PARAM_DEFAULT_DIGEST, mdName, sizeof(mdName), + NULL) != 1; + } + if ((err == 0) && (strcmp(mdName, "SHA256") != 0)) { + PRINT_ERR_MSG("Default digest is %s, expected SHA256", mdName); + err = 1; + } + + if (err == 0) { + PRINT_MSG("Unrestricted key reports no salt length"); + params[0] = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_RSA_PSS_SALTLEN, + &saltLen); + params[1] = OSSL_PARAM_construct_end(); + if ((EVP_PKEY_get_params(wpPub, params) == 1) && + OSSL_PARAM_modified(¶ms[0])) { + PRINT_ERR_MSG("Salt length %d reported for unrestricted key", + saltLen); + err = 1; + } + } + + for (i = 0; (err == 0) && (i < sizeof(mds) / sizeof(mds[0])); i++) { + PRINT_MSG("Sign and verify with an unrestricted key"); + err = test_rsa_pss_np_sign_verify(wpKey, wpLibCtx, wpKey, wpLibCtx, + mds[i]); + if (err == 0) { + err = test_rsa_pss_np_sign_verify(wpKey, wpLibCtx, osslKey, + osslLibCtx, mds[i]); + } + if (err == 0) { + err = test_rsa_pss_np_sign_verify(osslKey, osslLibCtx, wpKey, + wpLibCtx, mds[i]); + } + } + + if (err == 0) { + PRINT_MSG("Re-encode leaves the AlgorithmIdentifier unchanged"); + err = test_rsa_pss_np_encode(wpPub, 1, spki, spkiLen); + } + if (err == 0) { + err = test_rsa_pss_np_encode(wpKey, 0, + rsa_pss_key_der_2048_pkcs8_noparams, + (int)sizeof(rsa_pss_key_der_2048_pkcs8_noparams)); + } + + if (err == 0) { + PRINT_MSG("Empty PSS parameters restrict the key"); + err = test_rsa_pss_np_with_params(spki, spkiLen, emptyParams, + (int)sizeof(emptyParams), &varKey); + } + if ((err == 0) && (varKey == NULL)) { + PRINT_ERR_MSG("Key with empty PSS parameters was rejected"); + err = 1; + } + if (err == 0) { + /* Restricted to the ASN.1 defaults: no provider default digest. */ + if (EVP_PKEY_get_utf8_string_param(varKey, + OSSL_PKEY_PARAM_DEFAULT_DIGEST, mdName, sizeof(mdName), + NULL) == 1) { + PRINT_ERR_MSG("Empty PSS parameters left the key unrestricted"); + err = 1; + } + } + + if (err == 0) { + PRINT_MSG("Restricted key still pins its digest"); + err = test_rsa_pss_np_restricted(); + } + + if (err == 0) { + PRINT_MSG("Duplicated context keeps the salt length restriction"); + err = test_rsa_pss_np_dupctx(); + } + + OPENSSL_free(spki); + EVP_PKEY_free(varKey); + EVP_PKEY_free(wpPub); + EVP_PKEY_free(osslKey); + EVP_PKEY_free(wpKey); + + return err; +} + int test_rsa_sign_verify_x931(void *data) { int err = 0; @@ -3415,6 +3854,46 @@ int test_rsa_key_integrity(void* data) return err; } +/* + * Get the PSS signature AlgorithmIdentifier for a signature made with mdName. + */ +static int test_rsa_pss_alg_id_get(EVP_PKEY* pkey, OSSL_LIB_CTX* libCtx, + const char* mdName, unsigned char* aid, size_t aidSize, size_t* aidLen) +{ + int err; + EVP_MD_CTX* mdCtx = NULL; + EVP_PKEY_CTX* pkeyCtx = NULL; + OSSL_PARAM params[2]; + + *aidLen = 0; + + err = (mdCtx = EVP_MD_CTX_new()) == NULL; + if (err == 0) { + err = EVP_DigestSignInit_ex(mdCtx, &pkeyCtx, mdName, libCtx, NULL, + pkey, NULL) != 1; + } + if (err == 0) { + err = EVP_PKEY_CTX_set_rsa_padding(pkeyCtx, + RSA_PKCS1_PSS_PADDING) <= 0; + } + if (err == 0) { + err = EVP_PKEY_CTX_set_rsa_pss_saltlen(pkeyCtx, + RSA_PSS_SALTLEN_DIGEST) <= 0; + } + if (err == 0) { + params[0] = OSSL_PARAM_construct_octet_string( + OSSL_SIGNATURE_PARAM_ALGORITHM_ID, aid, aidSize); + params[1] = OSSL_PARAM_construct_end(); + err = EVP_PKEY_CTX_get_params(pkeyCtx, params) != 1; + } + if (err == 0) { + *aidLen = params[0].return_size; + } + + EVP_MD_CTX_free(mdCtx); + return err; +} + static int test_rsa_alg_id_get(EVP_PKEY* pkey, OSSL_LIB_CTX* libCtx, const char* mdName, unsigned char* aid, size_t aidSize, size_t* aidLen, int* initRet, int* getRet) @@ -3453,8 +3932,8 @@ int test_rsa_sig_alg_id(void *data) EVP_PKEY *pkey = NULL; const unsigned char *p = rsa_key_der_2048; const char* mds[] = { "SHA256", "SHA384", "SHA512" }; - unsigned char wpAid[64]; - unsigned char osslAid[64]; + unsigned char wpAid[128]; + unsigned char osslAid[128]; size_t wpLen = 0; size_t osslLen = 0; int wpInit = 0; @@ -3499,6 +3978,25 @@ int test_rsa_sig_alg_id(void *data) } } + /* The PSS signature alg-id must carry the digest, MGF and salt the + * signature was made with, whatever restrictions the key does or does + * not have. */ + for (i = 0; (err == 0) && (i < sizeof(mds) / sizeof(mds[0])); i++) { + PRINT_MSG("RSA-PSS alg-id A/B against OpenSSL"); + err = test_rsa_pss_alg_id_get(pkey, wpLibCtx, mds[i], wpAid, + sizeof(wpAid), &wpLen); + if (err == 0) { + err = test_rsa_pss_alg_id_get(pkey, osslLibCtx, mds[i], osslAid, + sizeof(osslAid), &osslLen); + } + if ((err == 0) && ((wpLen == 0) || (wpLen != osslLen) || + (memcmp(wpAid, osslAid, wpLen) != 0))) { + PRINT_ERR_MSG("RSA-PSS alg-id mismatch for %s: %d vs %d bytes", + mds[i], (int)wpLen, (int)osslLen); + err = 1; + } + } + EVP_PKEY_free(pkey); return err; diff --git a/test/unit.c b/test/unit.c index 7832cb71..7f7f7330 100644 --- a/test/unit.c +++ b/test/unit.c @@ -391,6 +391,7 @@ TEST_CASE test_case[] = { #endif TEST_DECL(test_rsa_pss_salt, NULL), TEST_DECL(test_rsa_pss_restrictions, NULL), + TEST_DECL(test_rsa_pss_no_params, NULL), TEST_DECL(test_rsa_load_key, NULL), TEST_DECL(test_rsa_load_cert, NULL), TEST_DECL(test_rsa_load_key_prop_query, NULL), diff --git a/test/unit.h b/test/unit.h index 45cc146c..b9da01fd 100644 --- a/test/unit.h +++ b/test/unit.h @@ -362,6 +362,7 @@ int test_rsa_sha512_224_dupctx(void *data); int test_rsa_sha512_256_dupctx(void *data); int test_rsa_pss_salt(void *date); int test_rsa_pss_restrictions(void *data); +int test_rsa_pss_no_params(void *data); int test_rsa_load_key(void* data); int test_rsa_load_cert(void* data);