Skip to content

Commit 55aaf31

Browse files
authored
🔧 Replace RSA related deprecated functions (#189)
* RSA_size() --> EVP_PKEY_size() * getting rid of deprecated functions in rsa-pss signature creation * getting rid of deprecated functions in rsa-pss signature verification * generate_hash not used in pss
1 parent 66eea98 commit 55aaf31

File tree

3 files changed

+104
-82
lines changed

3 files changed

+104
-82
lines changed

include/jwt-cpp/jwt.h

Lines changed: 71 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ namespace jwt {
185185
verifyinit_failed,
186186
verifyupdate_failed,
187187
verifyfinal_failed,
188-
get_key_failed
188+
get_key_failed,
189+
set_rsa_pss_saltlen_failed
189190
};
190191
/**
191192
* \brief Error category for verification errors
@@ -208,6 +209,8 @@ namespace jwt {
208209
return "failed to verify signature: VerifyFinal failed";
209210
case signature_verification_error::get_key_failed:
210211
return "failed to verify signature: Could not get key";
212+
case signature_verification_error::set_rsa_pss_saltlen_failed:
213+
return "failed to verify signature: EVP_PKEY_CTX_set_rsa_pss_saltlen failed";
211214
default: return "unknown signature verification error";
212215
}
213216
}
@@ -236,7 +239,8 @@ namespace jwt {
236239
digestfinal_failed,
237240
rsa_padding_failed,
238241
rsa_private_encrypt_failed,
239-
get_key_failed
242+
get_key_failed,
243+
set_rsa_pss_saltlen_failed,
240244
};
241245
/**
242246
* \brief Error category for signature generation errors
@@ -265,11 +269,13 @@ namespace jwt {
265269
case signature_generation_error::digestfinal_failed:
266270
return "failed to create signature: DigestFinal failed";
267271
case signature_generation_error::rsa_padding_failed:
268-
return "failed to create signature: RSA_padding_add_PKCS1_PSS failed";
272+
return "failed to create signature: EVP_PKEY_CTX_set_rsa_padding failed";
269273
case signature_generation_error::rsa_private_encrypt_failed:
270274
return "failed to create signature: RSA_private_encrypt failed";
271275
case signature_generation_error::get_key_failed:
272276
return "failed to generate signature: Could not get key";
277+
case signature_generation_error::set_rsa_pss_saltlen_failed:
278+
return "failed to create signature: EVP_PKEY_CTX_set_rsa_pss_saltlen failed";
273279
default: return "unknown signature generation error";
274280
}
275281
}
@@ -1308,31 +1314,48 @@ namespace jwt {
13081314
*/
13091315
std::string sign(const std::string& data, std::error_code& ec) const {
13101316
ec.clear();
1311-
auto hash = this->generate_hash(data, ec);
1312-
if (ec) return {};
1313-
1314-
std::unique_ptr<RSA, decltype(&RSA_free)> key(EVP_PKEY_get1_RSA(pkey.get()), RSA_free);
1315-
if (!key) {
1316-
ec = error::signature_generation_error::get_key_failed;
1317+
#ifdef JWT_OPENSSL_1_0_0
1318+
std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_destroy)> md_ctx(EVP_MD_CTX_create(),
1319+
&EVP_MD_CTX_destroy);
1320+
#else
1321+
std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)> md_ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
1322+
#endif
1323+
if (!md_ctx) {
1324+
ec = error::signature_generation_error::create_context_failed;
13171325
return {};
13181326
}
1319-
const int size = RSA_size(key.get());
1320-
1321-
std::string padded(size, 0x00);
1322-
if (RSA_padding_add_PKCS1_PSS(key.get(), (unsigned char*)padded.data(),
1323-
reinterpret_cast<const unsigned char*>(hash.data()), md(),
1324-
-1) == 0) { // NOLINT(google-readability-casting) requires `const_cast`
1327+
EVP_PKEY_CTX* ctx = nullptr;
1328+
if (EVP_DigestSignInit(md_ctx.get(), &ctx, md(), nullptr, pkey.get()) != 1) {
1329+
ec = error::signature_generation_error::signinit_failed;
1330+
return {};
1331+
}
1332+
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0) {
13251333
ec = error::signature_generation_error::rsa_padding_failed;
13261334
return {};
13271335
}
1336+
// wolfSSL does not require EVP_PKEY_CTX_set_rsa_pss_saltlen. The default behavior
1337+
// sets the salt length to the hash length. Unlike OpenSSL which exposes this functionality.
1338+
#ifndef LIBWOLFSSL_VERSION_HEX
1339+
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, -1) <= 0) {
1340+
ec = error::signature_generation_error::set_rsa_pss_saltlen_failed;
1341+
return {};
1342+
}
1343+
#endif
1344+
if (EVP_DigestUpdate(md_ctx.get(), data.data(), data.size()) != 1) {
1345+
ec = error::signature_generation_error::digestupdate_failed;
1346+
return {};
1347+
}
13281348

1349+
size_t size = EVP_PKEY_size(pkey.get());
13291350
std::string res(size, 0x00);
1330-
if (RSA_private_encrypt(size, reinterpret_cast<const unsigned char*>(padded.data()),
1331-
(unsigned char*)res.data(), key.get(), RSA_NO_PADDING) <
1332-
0) { // NOLINT(google-readability-casting) requires `const_cast`
1333-
ec = error::signature_generation_error::rsa_private_encrypt_failed;
1351+
if (EVP_DigestSignFinal(
1352+
md_ctx.get(),
1353+
(unsigned char*)res.data(), // NOLINT(google-readability-casting) requires `const_cast`
1354+
&size) <= 0) {
1355+
ec = error::signature_generation_error::signfinal_failed;
13341356
return {};
13351357
}
1358+
13361359
return res;
13371360
}
13381361

@@ -1344,28 +1367,41 @@ namespace jwt {
13441367
*/
13451368
void verify(const std::string& data, const std::string& signature, std::error_code& ec) const {
13461369
ec.clear();
1347-
auto hash = this->generate_hash(data, ec);
1348-
if (ec) return;
13491370

1350-
std::unique_ptr<RSA, decltype(&RSA_free)> key(EVP_PKEY_get1_RSA(pkey.get()), RSA_free);
1351-
if (!key) {
1352-
ec = error::signature_verification_error::get_key_failed;
1371+
#ifdef JWT_OPENSSL_1_0_0
1372+
std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_destroy)> md_ctx(EVP_MD_CTX_create(),
1373+
&EVP_MD_CTX_destroy);
1374+
#else
1375+
std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)> md_ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
1376+
#endif
1377+
if (!md_ctx) {
1378+
ec = error::signature_verification_error::create_context_failed;
13531379
return;
13541380
}
1355-
const int size = RSA_size(key.get());
1356-
1357-
std::string sig(size, 0x00);
1358-
if (RSA_public_decrypt(
1359-
static_cast<int>(signature.size()), reinterpret_cast<const unsigned char*>(signature.data()),
1360-
(unsigned char*)sig.data(), // NOLINT(google-readability-casting) requires `const_cast`
1361-
key.get(), RSA_NO_PADDING) == 0) {
1362-
ec = error::signature_verification_error::invalid_signature;
1381+
EVP_PKEY_CTX* ctx = nullptr;
1382+
if (EVP_DigestVerifyInit(md_ctx.get(), &ctx, md(), nullptr, pkey.get()) != 1) {
1383+
ec = error::signature_verification_error::verifyinit_failed;
1384+
return;
1385+
}
1386+
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0) {
1387+
ec = error::signature_generation_error::rsa_padding_failed;
1388+
return;
1389+
}
1390+
// wolfSSL does not require EVP_PKEY_CTX_set_rsa_pss_saltlen. The default behavior
1391+
// sets the salt length to the hash length. Unlike OpenSSL which exposes this functionality.
1392+
#ifndef LIBWOLFSSL_VERSION_HEX
1393+
if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, -1) <= 0) {
1394+
ec = error::signature_verification_error::set_rsa_pss_saltlen_failed;
1395+
return;
1396+
}
1397+
#endif
1398+
if (EVP_DigestUpdate(md_ctx.get(), data.data(), data.size()) != 1) {
1399+
ec = error::signature_verification_error::verifyupdate_failed;
13631400
return;
13641401
}
13651402

1366-
if (RSA_verify_PKCS1_PSS(key.get(), reinterpret_cast<const unsigned char*>(hash.data()), md(),
1367-
reinterpret_cast<const unsigned char*>(sig.data()), -1) == 0) {
1368-
ec = error::signature_verification_error::invalid_signature;
1403+
if (EVP_DigestVerifyFinal(md_ctx.get(), (unsigned char*)signature.data(), signature.size()) <= 0) {
1404+
ec = error::signature_verification_error::verifyfinal_failed;
13691405
return;
13701406
}
13711407
}
@@ -1376,41 +1412,6 @@ namespace jwt {
13761412
std::string name() const { return alg_name; }
13771413

13781414
private:
1379-
/**
1380-
* Hash the provided data using the hash function specified in constructor
1381-
* \param data Data to hash
1382-
* \return Hash of data
1383-
*/
1384-
std::string generate_hash(const std::string& data, std::error_code& ec) const {
1385-
#ifdef JWT_OPENSSL_1_0_0
1386-
std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_destroy)> ctx(EVP_MD_CTX_create(),
1387-
&EVP_MD_CTX_destroy);
1388-
#else
1389-
std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)> ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
1390-
#endif
1391-
if (!ctx) {
1392-
ec = error::signature_generation_error::create_context_failed;
1393-
return {};
1394-
}
1395-
if (EVP_DigestInit(ctx.get(), md()) == 0) {
1396-
ec = error::signature_generation_error::digestinit_failed;
1397-
return {};
1398-
}
1399-
if (EVP_DigestUpdate(ctx.get(), data.data(), data.size()) == 0) {
1400-
ec = error::signature_generation_error::digestupdate_failed;
1401-
return {};
1402-
}
1403-
unsigned int len = 0;
1404-
std::string res(EVP_MD_CTX_size(ctx.get()), '\0');
1405-
if (EVP_DigestFinal(ctx.get(), (unsigned char*)res.data(), &len) ==
1406-
0) { // NOLINT(google-readability-casting) requires `const_cast`
1407-
ec = error::signature_generation_error::digestfinal_failed;
1408-
return {};
1409-
}
1410-
res.resize(len);
1411-
return res;
1412-
}
1413-
14141415
/// OpenSSL structure containing keys
14151416
std::shared_ptr<EVP_PKEY> pkey;
14161417
/// Hash generator function

tests/HelperTest.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,14 @@ TEST(HelperTest, ErrorCodeMessages) {
5959
ASSERT_EQ(std::error_code(static_cast<jwt::error::ecdsa_error>(i)).message(),
6060
std::error_code(static_cast<jwt::error::ecdsa_error>(-1)).message());
6161

62-
for (i = 10; i < 16; i++) {
62+
for (i = 10; i < 17; i++) {
6363
ASSERT_NE(std::error_code(static_cast<jwt::error::signature_verification_error>(i)).message(),
6464
std::error_code(static_cast<jwt::error::signature_verification_error>(-1)).message());
6565
}
6666
ASSERT_EQ(std::error_code(static_cast<jwt::error::signature_verification_error>(i)).message(),
6767
std::error_code(static_cast<jwt::error::signature_verification_error>(-1)).message());
6868

69-
for (i = 10; i < 22; i++) {
69+
for (i = 10; i < 23; i++) {
7070
ASSERT_NE(std::error_code(static_cast<jwt::error::signature_generation_error>(i)).message(),
7171
std::error_code(static_cast<jwt::error::signature_generation_error>(-1)).message());
7272
}

tests/OpenSSLErrorTest.cpp

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ static uint64_t fail_EVP_DigestSign = 0;
4343
static uint64_t fail_EVP_DigestVerifyInit = 0;
4444
static uint64_t fail_EVP_DigestVerify = 0;
4545
static uint64_t fail_EVP_PKEY_get1_EC_KEY = 0;
46+
static uint64_t fail_EVP_DigestSignFinal = 0;
47+
static uint64_t fail_EVP_DigestVerifyFinal = 0;
4648

4749
BIO* BIO_new(const BIO_METHOD* type) {
4850
static BIO* (*origMethod)(const BIO_METHOD*) = nullptr;
@@ -195,6 +197,17 @@ EVP_MD_CTX* EVP_MD_CTX_new(void) {
195197
return origMethod();
196198
}
197199

200+
int EVP_DigestSignFinal(EVP_MD_CTX* ctx, unsigned char* sigret, size_t* siglen) {
201+
static int (*origMethod)(EVP_MD_CTX * ctx, unsigned char* sigret, size_t* siglen) = nullptr;
202+
if (origMethod == nullptr) origMethod = (decltype(origMethod))dlsym(RTLD_NEXT, "EVP_DigestSignFinal");
203+
bool fail = fail_EVP_DigestSignFinal & 1;
204+
fail_EVP_DigestSignFinal = fail_EVP_DigestSignFinal >> 1;
205+
if (fail)
206+
return 0;
207+
else
208+
return origMethod(ctx, sigret, siglen);
209+
}
210+
198211
int EVP_DigestInit(EVP_MD_CTX* ctx, const EVP_MD* type) {
199212
static int (*origMethod)(EVP_MD_CTX * ctx, const EVP_MD* type) = nullptr;
200213
if (origMethod == nullptr) origMethod = (decltype(origMethod))dlsym(RTLD_NEXT, "EVP_DigestInit");
@@ -354,6 +367,17 @@ EC_KEY* EVP_PKEY_get1_EC_KEY(EVP_PKEY* pkey) {
354367
return origMethod(pkey);
355368
}
356369

370+
int EVP_DigestVerifyFinal(EVP_MD_CTX* ctx, const unsigned char* sigret, size_t siglen) {
371+
static int (*origMethod)(EVP_MD_CTX * ctx, const unsigned char* sigret, size_t siglen) = nullptr;
372+
if (origMethod == nullptr) origMethod = (decltype(origMethod))dlsym(RTLD_NEXT, "EVP_DigestVerifyFinal");
373+
bool fail = fail_EVP_DigestVerifyFinal & 1;
374+
fail_EVP_DigestVerifyFinal = fail_EVP_DigestVerifyFinal >> 1;
375+
if (fail)
376+
return 0;
377+
else
378+
return origMethod(ctx, sigret, siglen);
379+
}
380+
357381
/**
358382
* =========== End of black magic ============
359383
*/
@@ -728,11 +752,10 @@ TEST(OpenSSLErrorTest, PS256SignErrorCode) {
728752
jwt::algorithm::ps256 alg{rsa_pub_key, rsa_priv_key};
729753
std::vector<multitest_entry> mapping{
730754
{&fail_EVP_MD_CTX_new, 1, jwt::error::signature_generation_error::create_context_failed},
731-
{&fail_EVP_DigestInit, 1, jwt::error::signature_generation_error::digestinit_failed},
755+
{&fail_EVP_DigestSignInit, 1, jwt::error::signature_generation_error::signinit_failed},
732756
{&fail_EVP_DigestUpdate, 1, jwt::error::signature_generation_error::digestupdate_failed},
733-
{&fail_EVP_DigestFinal, 1, jwt::error::signature_generation_error::digestfinal_failed},
734-
{&fail_EVP_PKEY_get1_RSA, 1, jwt::error::signature_generation_error::get_key_failed}
735-
//TODO: RSA_padding_add_PKCS1_PSS, RSA_private_encrypt
757+
{&fail_EVP_DigestSignFinal, 1, jwt::error::signature_generation_error::signfinal_failed},
758+
//TODO: EVP_PKEY_CTX_set_rsa_padding, EVP_PKEY_CTX_set_rsa_pss_saltlen
736759
};
737760

738761
run_multitest(mapping, [&alg](std::error_code& ec) {
@@ -751,12 +774,10 @@ TEST(OpenSSLErrorTest, PS256VerifyErrorCode) {
751774
"B59uW3x1QUCKYKgZeqZOoqIP1YgLwvEpPtXYutQCFr4eBKgV7vdtE0wgHR43ka16fi5L4SyaZv53NCg==";
752775
signature = jwt::base::decode<jwt::alphabet::base64>(signature);
753776
std::vector<multitest_entry> mapping{
754-
{&fail_EVP_MD_CTX_new, 1, jwt::error::signature_generation_error::create_context_failed},
755-
{&fail_EVP_DigestInit, 1, jwt::error::signature_generation_error::digestinit_failed},
756-
{&fail_EVP_DigestUpdate, 1, jwt::error::signature_generation_error::digestupdate_failed},
757-
{&fail_EVP_DigestFinal, 1, jwt::error::signature_generation_error::digestfinal_failed},
758-
{&fail_EVP_PKEY_get1_RSA, 1, jwt::error::signature_verification_error::get_key_failed}
759-
//TODO: RSA_public_decrypt
777+
{&fail_EVP_MD_CTX_new, 1, jwt::error::signature_verification_error::create_context_failed},
778+
{&fail_EVP_DigestVerifyInit, 1, jwt::error::signature_verification_error::verifyinit_failed},
779+
{&fail_EVP_DigestUpdate, 1, jwt::error::signature_verification_error::verifyupdate_failed},
780+
{&fail_EVP_DigestVerifyFinal, 1, jwt::error::signature_verification_error::verifyfinal_failed},
760781
};
761782

762783
run_multitest(mapping, [&alg, &signature](std::error_code& ec) { alg.verify("testdata", signature, ec); });

0 commit comments

Comments
 (0)