From be603cd74ed869980f1b6a0897c70bcc77960ba4 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Tue, 31 Mar 2020 10:48:28 -0400 Subject: [PATCH 01/20] add utility for adding padding useful in when decoding ( padding is optional ) --- include/jwt-cpp/base.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/jwt-cpp/base.h b/include/jwt-cpp/base.h index dfca7fc08..f20462dbd 100644 --- a/include/jwt-cpp/base.h +++ b/include/jwt-cpp/base.h @@ -44,6 +44,10 @@ namespace jwt { static std::string decode(const std::string& base) { return decode(base, T::data(), T::fill()); } + template + static std::string pad(const std::string& base) { + return pad(base, T::fill()); + } private: static std::string encode(const std::string& bin, const std::array& alphabet, const std::string& fill) { @@ -164,5 +168,14 @@ namespace jwt { return res; } + + static std::string pad(const std::string& bin, const std::string& fill) + { + const auto missingPadding = input.length() % 4; + for (size_t i = 0; i < missingPadding; i++) + { + input += fill; + } + } }; } From 33c40b023f852d3dc5f99891a251666c779d4aae Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Wed, 1 Apr 2020 09:48:11 -0400 Subject: [PATCH 02/20] fixing padding + adding a trim --- include/jwt-cpp/base.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/include/jwt-cpp/base.h b/include/jwt-cpp/base.h index f20462dbd..0a3707628 100644 --- a/include/jwt-cpp/base.h +++ b/include/jwt-cpp/base.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include namespace jwt { namespace alphabet { @@ -48,6 +49,10 @@ namespace jwt { static std::string pad(const std::string& base) { return pad(base, T::fill()); } + template + static std::string trim(const std::string& base) { + return trim(base, T::fill()); + } private: static std::string encode(const std::string& bin, const std::array& alphabet, const std::string& fill) { @@ -169,13 +174,20 @@ namespace jwt { return res; } - static std::string pad(const std::string& bin, const std::string& fill) - { + static std::string pad(const std::string& bin, const std::string& base) + { + auto input = base; const auto missingPadding = input.length() % 4; for (size_t i = 0; i < missingPadding; i++) { input += fill; } + return input; + } + + static std::string trim(const std::string& base, const std::string& fill) + { + return std::regex_replace(base, std::regex{ fill }, ""); } }; } From 27673b058d6a16d569d34e87cbeb432cc1cea0a3 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Wed, 1 Apr 2020 09:57:29 -0400 Subject: [PATCH 03/20] adding test code fro pad and trim --- tests/BaseTest.cpp | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/BaseTest.cpp b/tests/BaseTest.cpp index fd3171ca6..3ef186e1f 100644 --- a/tests/BaseTest.cpp +++ b/tests/BaseTest.cpp @@ -27,4 +27,32 @@ TEST(BaseTest, Base64EncodeURL) { ASSERT_EQ("MTI%3d", jwt::base::encode("12")); ASSERT_EQ("MTIz", jwt::base::encode("123")); ASSERT_EQ("MTIzNA%3d%3d", jwt::base::encode("1234")); -} \ No newline at end of file +} + +TEST(BaseTest, Base64Pad) { + ASSERT_EQ("MQ==", jwt::base::pad("MQ")); + ASSERT_EQ("MTI=", jwt::base::pad("MTI")); + ASSERT_EQ("MTIz", jwt::base::pad("MTIz")); + ASSERT_EQ("MTIzNA==", jwt::base::pad("MTIzNA")); +} + +TEST(BaseTest, Base64PadURL) { + ASSERT_EQ("MQ%3d%3d", jwt::base::pad("MQ")); + ASSERT_EQ("MTI%3d", jwt::base::pad("MTI")); + ASSERT_EQ("MTIz", jwt::base::pad("MTIz")); + ASSERT_EQ("MTIzNA%3d%3d", jwt::base::pad("MTIzNA")); +} + +TEST(BaseTest, Base64Trim) { + ASSERT_EQ("MQ", jwt::base::trim("MQ==")); + ASSERT_EQ("MTI", jwt::base::trim("MTI=")); + ASSERT_EQ("MTIz", jwt::base::trim("MTIz")); + ASSERT_EQ("MTIzNA", jwt::base::trim("MTIzNA==")); +} + +TEST(BaseTest, Base64TrimURL) { + ASSERT_EQ("MQ", jwt::base::trim("MQ%3d%3d")); + ASSERT_EQ("MTI", jwt::base::trim("MTI%3d")); + ASSERT_EQ("MTIz", jwt::base::trim("MTIz")); + ASSERT_EQ("MTIzNA", jwt::base::trim("MTIzNA%3d%3d")); +} From 0b0ca4c34591ce1bc30763b9ae25381120902dd2 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Sat, 11 Apr 2020 09:14:52 -0400 Subject: [PATCH 04/20] using original source for pad/trim --- include/jwt-cpp/base.h | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/include/jwt-cpp/base.h b/include/jwt-cpp/base.h index 0a3707628..ad93cd123 100644 --- a/include/jwt-cpp/base.h +++ b/include/jwt-cpp/base.h @@ -1,7 +1,6 @@ #pragma once #include #include -#include namespace jwt { namespace alphabet { @@ -174,20 +173,37 @@ namespace jwt { return res; } - static std::string pad(const std::string& bin, const std::string& base) - { - auto input = base; - const auto missingPadding = input.length() % 4; - for (size_t i = 0; i < missingPadding; i++) - { - input += fill; - } - return input; + static std::string pad(const std::string& base, const std::string& fill) + { switch (base.size() % 4) { + case 1: + base += fill; +#ifdef __has_cpp_attribute +#if __has_cpp_attribute(fallthrough) + [[fallthrough]]; +#endif +#endif + case 2: + base += fill; +#ifdef __has_cpp_attribute +#if __has_cpp_attribute(fallthrough) + [[fallthrough]]; +#endif +#endif + case 3: + base += fill; +#ifdef __has_cpp_attribute +#if __has_cpp_attribute(fallthrough) + [[fallthrough]]; +#endif +#endif + default: + break; } static std::string trim(const std::string& base, const std::string& fill) - { - return std::regex_replace(base, std::regex{ fill }, ""); + { + auto pos = base.find(fill); + return base.substr(0, pos); } }; } From 56b6c8e5311ee2c64808b6b424be91968fe21e6f Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Sat, 11 Apr 2020 09:15:03 -0400 Subject: [PATCH 05/20] removing duplication --- include/jwt-cpp/jwt.h | 38 ++++---------------------------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h index f9f29ccc8..ba46720e2 100644 --- a/include/jwt-cpp/jwt.h +++ b/include/jwt-cpp/jwt.h @@ -1139,36 +1139,9 @@ namespace jwt { signature = signature_base64 = token.substr(payload_end + 1); // Fix padding: JWT requires padding to get removed - auto fix_padding = [](std::string& str) { - switch (str.size() % 4) { - case 1: - str += alphabet::base64url::fill(); -#ifdef __has_cpp_attribute -#if __has_cpp_attribute(fallthrough) - [[fallthrough]]; -#endif -#endif - case 2: - str += alphabet::base64url::fill(); -#ifdef __has_cpp_attribute -#if __has_cpp_attribute(fallthrough) - [[fallthrough]]; -#endif -#endif - case 3: - str += alphabet::base64url::fill(); -#ifdef __has_cpp_attribute -#if __has_cpp_attribute(fallthrough) - [[fallthrough]]; -#endif -#endif - default: - break; - } - }; - fix_padding(header); - fix_padding(payload); - fix_padding(signature); + header = base::pad(header); + payload = base::pad(payload); + signature = base::pad(signature); header = base::decode(header); payload = base::decode(payload); @@ -1344,10 +1317,7 @@ namespace jwt { } auto encode = [](const std::string& data) { - auto base = base::encode(data); - auto pos = base.find(alphabet::base64url::fill()); - base = base.substr(0, pos); - return base; + return base::trim(base::encode(data)); }; std::string header = encode(picojson::value(obj_header).serialize()); From 103532688d6b845c7b8eec8b221e57679e1c63dd Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Sat, 11 Apr 2020 09:19:33 -0400 Subject: [PATCH 06/20] exposing private functions as helpers this is to help with other JOSE tasks --- include/jwt-cpp/jwt.h | 60 ++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h index ba46720e2..8f10822fa 100644 --- a/include/jwt-cpp/jwt.h +++ b/include/jwt-cpp/jwt.h @@ -122,6 +122,33 @@ namespace jwt { throw rsa_exception("failed to load private key: PEM_read_bio_PrivateKey failed"); return pkey; } + + /** + * Convert a OpenSSL BIGNUM to a std::string + * \param bn BIGNUM to convert + * \return bignum as string + */ + inline +#ifdef OPENSSL10 + static std::string bn2raw(BIGNUM* bn) +#else + static std::string bn2raw(const BIGNUM* bn) +#endif + { + std::string res; + res.resize(BN_num_bytes(bn)); + BN_bn2bin(bn, (unsigned char*)res.data()); + return res; + } + /** + * Convert an std::string to a OpenSSL BIGNUM + * \param raw String to convert + * \return BIGNUM representation + */ + inline + static std::unique_ptr raw2bn(const std::string& raw) { + return std::unique_ptr(BN_bin2bn((const unsigned char*)raw.data(), raw.size(), nullptr), BN_free); + } } namespace algorithm { @@ -372,8 +399,8 @@ namespace jwt { const BIGNUM *r; const BIGNUM *s; ECDSA_SIG_get0(sig.get(), &r, &s); - auto rr = bn2raw(r); - auto rs = bn2raw(s); + auto rr = helper::bn2raw(r); + auto rs = helper::bn2raw(s); #endif if(rr.size() > signature_length/2 || rs.size() > signature_length/2) throw std::logic_error("bignum size exceeded expected length"); @@ -390,8 +417,8 @@ namespace jwt { */ void verify(const std::string& data, const std::string& signature) const { const std::string hash = generate_hash(data); - auto r = raw2bn(signature.substr(0, signature.size() / 2)); - auto s = raw2bn(signature.substr(signature.size() / 2)); + auto r = helper::raw2bn(signature.substr(0, signature.size() / 2)); + auto s = helper::raw2bn(signature.substr(signature.size() / 2)); #ifdef OPENSSL10 ECDSA_SIG sig; @@ -417,31 +444,6 @@ namespace jwt { return alg_name; } private: - /** - * Convert a OpenSSL BIGNUM to a std::string - * \param bn BIGNUM to convert - * \return bignum as string - */ -#ifdef OPENSSL10 - static std::string bn2raw(BIGNUM* bn) -#else - static std::string bn2raw(const BIGNUM* bn) -#endif - { - std::string res; - res.resize(BN_num_bytes(bn)); - BN_bn2bin(bn, (unsigned char*)res.data()); - return res; - } - /** - * Convert an std::string to a OpenSSL BIGNUM - * \param raw String to convert - * \return BIGNUM representation - */ - static std::unique_ptr raw2bn(const std::string& raw) { - return std::unique_ptr(BN_bin2bn((const unsigned char*)raw.data(), raw.size(), nullptr), BN_free); - } - /** * Hash the provided data using the hash function specified in constructor * \param data Data to hash From bc932e8ab40b423af7b69f32a50317dff0722d96 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Sat, 11 Apr 2020 09:23:55 -0400 Subject: [PATCH 07/20] adding macro for fall-through removing code duplication --- include/jwt-cpp/base.h | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/include/jwt-cpp/base.h b/include/jwt-cpp/base.h index ad93cd123..bbcd49914 100644 --- a/include/jwt-cpp/base.h +++ b/include/jwt-cpp/base.h @@ -2,6 +2,16 @@ #include #include +#ifdef __has_cpp_attribute +#if __has_cpp_attribute(fallthrough) +#define JWT_FALLTHROUGH [[fallthrough]] +#endif +#endif + +#ifdef JWT_FALLTHROUGH +#define JWT_FALLTHROUGH +#endif + namespace jwt { namespace alphabet { struct base64 { @@ -177,25 +187,13 @@ namespace jwt { { switch (base.size() % 4) { case 1: base += fill; -#ifdef __has_cpp_attribute -#if __has_cpp_attribute(fallthrough) - [[fallthrough]]; -#endif -#endif + JWT_FALLTHROUGH; case 2: base += fill; -#ifdef __has_cpp_attribute -#if __has_cpp_attribute(fallthrough) - [[fallthrough]]; -#endif -#endif + JWT_FALLTHROUGH; case 3: base += fill; -#ifdef __has_cpp_attribute -#if __has_cpp_attribute(fallthrough) - [[fallthrough]]; -#endif -#endif + JWT_FALLTHROUGH; default: break; } From 141ae3cac0bc24049162af5f3a5d714a2dda66bb Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Sat, 11 Apr 2020 09:41:34 -0400 Subject: [PATCH 08/20] replacing decimal for hex to be consistent with other places in the code to follow the numbering style of openssl --- include/jwt-cpp/jwt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h index 8f10822fa..10fe01174 100644 --- a/include/jwt-cpp/jwt.h +++ b/include/jwt-cpp/jwt.h @@ -13,7 +13,7 @@ #include //If openssl version less than 1.1 -#if OPENSSL_VERSION_NUMBER < 269484032 +#if OPENSSL_VERSION_NUMBER < 0x10100000L #define OPENSSL10 #endif From 1c1447387534ea945c2434cebe01abe356727d03 Mon Sep 17 00:00:00 2001 From: Christopher McArthur Date: Sat, 11 Apr 2020 12:28:24 -0400 Subject: [PATCH 09/20] correcting copy paste errors --- include/jwt-cpp/base.h | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/include/jwt-cpp/base.h b/include/jwt-cpp/base.h index bbcd49914..7f07be1ee 100644 --- a/include/jwt-cpp/base.h +++ b/include/jwt-cpp/base.h @@ -8,7 +8,7 @@ #endif #endif -#ifdef JWT_FALLTHROUGH +#ifndef JWT_FALLTHROUGH #define JWT_FALLTHROUGH #endif @@ -183,23 +183,26 @@ namespace jwt { return res; } - static std::string pad(const std::string& base, const std::string& fill) - { switch (base.size() % 4) { + static std::string pad(const std::string& base, const std::string& fill) { + std::string padding; + switch (base.size() % 4) { case 1: - base += fill; + padding += fill; JWT_FALLTHROUGH; case 2: - base += fill; + padding += fill; JWT_FALLTHROUGH; case 3: - base += fill; + padding += fill; JWT_FALLTHROUGH; default: break; + } + + return base + padding; } - - static std::string trim(const std::string& base, const std::string& fill) - { + + static std::string trim(const std::string& base, const std::string& fill) { auto pos = base.find(fill); return base.substr(0, pos); } From 79370dcf1e6fd48ca793a90a1ae255d95de29a82 Mon Sep 17 00:00:00 2001 From: Christopher McArthur Date: Sat, 11 Apr 2020 12:43:59 -0400 Subject: [PATCH 10/20] making sure there warnings --- tests/CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 42979cf6b..7a17b07b1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,7 +10,10 @@ set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/TokenTest.cpp) add_executable(jwt-cpp-test ${TEST_SOURCES}) - +target_compile_options(jwt-cpp-test PRIVATE + $<$:/W4> + $<$:-Wall -Wextra -Wpedantic> + $<$:-Weverything>) target_link_libraries(jwt-cpp-test PRIVATE jwt-cpp::jwt-cpp gtest gtest_main pthread) -gtest_add_tests(TARGET jwt-cpp-test) +gtest_add_tests(TARGET jwt-cpp-test) From fa806e7be9222e7ee385bbfc83696297d4e17e57 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Sat, 11 Apr 2020 14:05:50 -0400 Subject: [PATCH 11/20] fixing conversion warnings on windows where sizeof(size_t) != sizeof(int) since x86 still exists --- include/jwt-cpp/base.h | 2 +- include/jwt-cpp/jwt.h | 36 +++++++++++++++++++++--------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/include/jwt-cpp/base.h b/include/jwt-cpp/base.h index 7f07be1ee..4ff7b200c 100644 --- a/include/jwt-cpp/base.h +++ b/include/jwt-cpp/base.h @@ -138,7 +138,7 @@ namespace jwt { auto get_sextet = [&](size_t offset) { for (size_t i = 0; i < alphabet.size(); i++) { if (alphabet[i] == base[offset]) - return i; + return static_cast(i); } throw std::runtime_error("Invalid input"); }; diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h index 10fe01174..b7692029c 100644 --- a/include/jwt-cpp/jwt.h +++ b/include/jwt-cpp/jwt.h @@ -78,7 +78,7 @@ namespace jwt { #if OPENSSL_VERSION_NUMBER <= 0x10100003L std::unique_ptr certbio(BIO_new_mem_buf(const_cast(certstr.data()), certstr.size()), BIO_free_all); #else - std::unique_ptr certbio(BIO_new_mem_buf(certstr.data(), certstr.size()), BIO_free_all); + std::unique_ptr certbio(BIO_new_mem_buf(certstr.data(), static_cast(certstr.size())), BIO_free_all); #endif std::unique_ptr keybio(BIO_new(BIO_s_mem()), BIO_free_all); @@ -99,10 +99,12 @@ namespace jwt { std::unique_ptr pubkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); if(key.substr(0, 27) == "-----BEGIN CERTIFICATE-----") { auto epkey = helper::extract_pubkey_from_cert(key, password); - if ((size_t)BIO_write(pubkey_bio.get(), epkey.data(), epkey.size()) != epkey.size()) + const int len = static_cast(epkey.size()); + if (BIO_write(pubkey_bio.get(), epkey.data(), len) != len) throw rsa_exception("failed to load public key: bio_write failed"); } else { - if ((size_t)BIO_write(pubkey_bio.get(), key.data(), key.size()) != key.size()) + const int len = static_cast(key.size()); + if (BIO_write(pubkey_bio.get(), key.data(), len) != len) throw rsa_exception("failed to load public key: bio_write failed"); } @@ -115,7 +117,8 @@ namespace jwt { inline std::shared_ptr load_private_key_from_string(const std::string& key, const std::string& password = "") { std::unique_ptr privkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); - if ((size_t)BIO_write(privkey_bio.get(), key.data(), key.size()) != key.size()) + const int len = static_cast(key.size()); + if ((size_t)BIO_write(privkey_bio.get(), key.data(), len) != len) throw rsa_exception("failed to load private key: bio_write failed"); std::shared_ptr pkey(PEM_read_bio_PrivateKey(privkey_bio.get(), nullptr, nullptr, const_cast(password.c_str())), EVP_PKEY_free); if (!pkey) @@ -147,7 +150,7 @@ namespace jwt { */ inline static std::unique_ptr raw2bn(const std::string& raw) { - return std::unique_ptr(BN_bin2bn((const unsigned char*)raw.data(), raw.size(), nullptr), BN_free); + return std::unique_ptr(BN_bin2bn((const unsigned char*)raw.data(), static_cast(raw.size()), nullptr), BN_free); } } @@ -193,9 +196,9 @@ namespace jwt { */ std::string sign(const std::string& data) const { std::string res; - res.resize(EVP_MAX_MD_SIZE); - unsigned int len = res.size(); - if (HMAC(md(), secret.data(), secret.size(), (const unsigned char*)data.data(), data.size(), (unsigned char*)res.data(), &len) == nullptr) + res.resize(static_cast(EVP_MAX_MD_SIZE)); + unsigned int len = static_cast(res.size()); + if (HMAC(md(), secret.data(), static_cast(secret.size()), (const unsigned char*)data.data(), static_cast(data.size()), (unsigned char*)res.data(), &len) == nullptr) throw signature_generation_exception(); res.resize(len); return res; @@ -307,7 +310,7 @@ namespace jwt { throw signature_verification_exception("failed to verify signature: VerifyInit failed"); if (!EVP_VerifyUpdate(ctx.get(), data.data(), data.size())) throw signature_verification_exception("failed to verify signature: VerifyUpdate failed"); - auto res = EVP_VerifyFinal(ctx.get(), (const unsigned char*)signature.data(), signature.size(), pkey.get()); + auto res = EVP_VerifyFinal(ctx.get(), (const unsigned char*)signature.data(), static_cast(signature.size()), pkey.get()); if (res != 1) throw signature_verification_exception("evp verify final failed: " + std::to_string(res) + " " + ERR_error_string(ERR_get_error(), NULL)); } @@ -346,10 +349,12 @@ namespace jwt { std::unique_ptr pubkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); if(public_key.substr(0, 27) == "-----BEGIN CERTIFICATE-----") { auto epkey = helper::extract_pubkey_from_cert(public_key, public_key_password); - if ((size_t)BIO_write(pubkey_bio.get(), epkey.data(), epkey.size()) != epkey.size()) + const int len = static_cast(epkey.size()); + if (BIO_write(pubkey_bio.get(), epkey.data(), len) != len) throw ecdsa_exception("failed to load public key: bio_write failed"); } else { - if ((size_t)BIO_write(pubkey_bio.get(), public_key.data(), public_key.size()) != public_key.size()) + const int len = static_cast(public_key.size()); + if (BIO_write(pubkey_bio.get(), public_key.data(), len) != len) throw ecdsa_exception("failed to load public key: bio_write failed"); } @@ -363,7 +368,8 @@ namespace jwt { if (!private_key.empty()) { std::unique_ptr privkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); - if ((size_t)BIO_write(privkey_bio.get(), private_key.data(), private_key.size()) != private_key.size()) + const int len = static_cast(private_key.size()); + if (BIO_write(privkey_bio.get(), private_key.data(), len) != len) throw ecdsa_exception("failed to load private key: bio_write failed"); pkey.reset(PEM_read_bio_ECPrivateKey(privkey_bio.get(), nullptr, nullptr, const_cast(private_key_password.c_str())), EC_KEY_free); if (!pkey) @@ -388,7 +394,7 @@ namespace jwt { const std::string hash = generate_hash(data); std::unique_ptr - sig(ECDSA_do_sign((const unsigned char*)hash.data(), hash.size(), pkey.get()), ECDSA_SIG_free); + sig(ECDSA_do_sign((const unsigned char*)hash.data(), static_cast(hash.size()), pkey.get()), ECDSA_SIG_free); if(!sig) throw signature_generation_exception(); #ifdef OPENSSL10 @@ -432,7 +438,7 @@ namespace jwt { ECDSA_SIG_set0(sig.get(), r.release(), s.release()); - if(ECDSA_do_verify((const unsigned char*)hash.data(), hash.size(), sig.get(), pkey.get()) != 1) + if(ECDSA_do_verify((const unsigned char*)hash.data(), static_cast(hash.size()), sig.get(), pkey.get()) != 1) throw signature_verification_exception("Invalid signature"); #endif } @@ -535,7 +541,7 @@ namespace jwt { const int size = RSA_size(key.get()); std::string sig(size, 0x00); - if(!RSA_public_decrypt(signature.size(), (const unsigned char*)signature.data(), (unsigned char*)sig.data(), key.get(), RSA_NO_PADDING)) + if(!RSA_public_decrypt(static_cast(signature.size()), (const unsigned char*)signature.data(), (unsigned char*)sig.data(), key.get(), RSA_NO_PADDING)) throw signature_verification_exception("Invalid signature"); if(!RSA_verify_PKCS1_PSS_mgf1(key.get(), (const unsigned char*)hash.data(), md(), md(), (const unsigned char*)sig.data(), -1)) From 20a992fff37f14df279803a5499c2d723c63ff83 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Wed, 15 Apr 2020 22:34:52 -0400 Subject: [PATCH 12/20] changing whitespace just to see if CI will pass today --- include/jwt-cpp/base.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/jwt-cpp/base.h b/include/jwt-cpp/base.h index 4ff7b200c..3ca399b42 100644 --- a/include/jwt-cpp/base.h +++ b/include/jwt-cpp/base.h @@ -17,10 +17,10 @@ namespace jwt { struct base64 { static const std::array& data() { static std::array data = { - {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}}; + {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}}; return data; }; static const std::string& fill() { @@ -31,10 +31,10 @@ namespace jwt { struct base64url { static const std::array& data() { static std::array data = { - {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}}; + {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}}; return data; }; static const std::string& fill() { From 5477d3f749db82853d572d31a4f9579332e0b988 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Wed, 15 Apr 2020 22:52:48 -0400 Subject: [PATCH 13/20] missed namespace for helper only tested with openssl 1.1.1d RIP --- include/jwt-cpp/base.h | 24 ++++++++++++------------ include/jwt-cpp/jwt.h | 5 ++--- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/include/jwt-cpp/base.h b/include/jwt-cpp/base.h index 3ca399b42..375e0eb08 100644 --- a/include/jwt-cpp/base.h +++ b/include/jwt-cpp/base.h @@ -16,12 +16,12 @@ namespace jwt { namespace alphabet { struct base64 { static const std::array& data() { - static std::array data = { - {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}}; - return data; + static std::array data = { + {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}}; + return data; }; static const std::string& fill() { static std::string fill = "="; @@ -30,12 +30,12 @@ namespace jwt { }; struct base64url { static const std::array& data() { - static std::array data = { - {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}}; - return data; + static std::array data = { + {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}}; + return data; }; static const std::string& fill() { static std::string fill = "%3d"; diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h index b7692029c..5d8392e64 100644 --- a/include/jwt-cpp/jwt.h +++ b/include/jwt-cpp/jwt.h @@ -74,7 +74,6 @@ namespace jwt { namespace helper { inline std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw = "") { - // TODO: Cannot find the exact version this change happended #if OPENSSL_VERSION_NUMBER <= 0x10100003L std::unique_ptr certbio(BIO_new_mem_buf(const_cast(certstr.data()), certstr.size()), BIO_free_all); #else @@ -399,8 +398,8 @@ namespace jwt { throw signature_generation_exception(); #ifdef OPENSSL10 - auto rr = bn2raw(sig->r); - auto rs = bn2raw(sig->s); + auto rr = helper::bn2raw(sig->r); + auto rs = helper::bn2raw(sig->s); #else const BIGNUM *r; const BIGNUM *s; From a4bb9dc79670bd6a9118b8668ffc1ed31362994a Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Wed, 15 Apr 2020 22:55:17 -0400 Subject: [PATCH 14/20] fixing warning --- include/jwt-cpp/jwt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h index 5d8392e64..51df8af80 100644 --- a/include/jwt-cpp/jwt.h +++ b/include/jwt-cpp/jwt.h @@ -117,7 +117,7 @@ namespace jwt { std::shared_ptr load_private_key_from_string(const std::string& key, const std::string& password = "") { std::unique_ptr privkey_bio(BIO_new(BIO_s_mem()), BIO_free_all); const int len = static_cast(key.size()); - if ((size_t)BIO_write(privkey_bio.get(), key.data(), len) != len) + if (BIO_write(privkey_bio.get(), key.data(), len) != len) throw rsa_exception("failed to load private key: bio_write failed"); std::shared_ptr pkey(PEM_read_bio_PrivateKey(privkey_bio.get(), nullptr, nullptr, const_cast(password.c_str())), EVP_PKEY_free); if (!pkey) From c9d04be14aab3087319d01160f9af9e8fc02e231 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Wed, 15 Apr 2020 23:33:20 -0400 Subject: [PATCH 15/20] adding support for aud as string making sure to compare complex objects in verifier inspired by https://github.com/lindobyte/jwt-cpp/commit/dcbf0ddbfd29680dfa4d1ad7433af7ddcc52ade6 --- include/jwt-cpp/jwt.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h index f9f29ccc8..9a4bd2f54 100644 --- a/include/jwt-cpp/jwt.h +++ b/include/jwt-cpp/jwt.h @@ -1440,6 +1440,13 @@ namespace jwt { * \return *this to allow chaining */ verifier& with_audience(const std::set& aud) { return with_claim("aud", claim(aud)); } + /** + * Set an audience to check for. + * If the specified audiences is not present in the token the check fails. + * \param aud Audience to check for. + * \return *this to allow chaining + */ + verifier& with_audience(const std::string& aud) { return with_claim("aud", claim(aud)); } /** * Set an id to check for. * Check is casesensitive. @@ -1501,6 +1508,18 @@ namespace jwt { throw token_verification_exception("claim " + key + " does not match expected"); } } + else if (c.get_type() == claim::type::object) { + auto s1 = c.serialize(); + auto s2 = jc.serialize(); + if (s1.size() != s2.size()) + throw token_verification_exception("claim " + key + " does not match expected"); + auto it1 = s1.cbegin(); + auto it2 = s2.cbegin(); + while (it1 != s1.cend() && it2 != s2.cend()) { + if (*it1++ != *it2++) + throw token_verification_exception("claim " + key + " does not match expected"); + } + } else if (c.get_type() == claim::type::string) { if (c.as_string() != jc.as_string()) throw token_verification_exception("claim " + key + " does not match expected"); From 9dec02d5710b4451f122d45dcb37f36a196f0732 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Wed, 15 Apr 2020 23:37:55 -0400 Subject: [PATCH 16/20] adding tests for new functionality --- tests/TokenTest.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/TokenTest.cpp b/tests/TokenTest.cpp index c388508dc..348bd5b85 100644 --- a/tests/TokenTest.cpp +++ b/tests/TokenTest.cpp @@ -328,6 +328,13 @@ TEST(TokenTest, VerifyFail) { .with_audience({ "test" }); ASSERT_THROW(verify.verify(decoded_token), jwt::token_verification_exception); } + { + auto verify = jwt::verify() + .allow_algorithm(jwt::algorithm::none{}) + .with_issuer("auth0") + .with_audience("test"); + ASSERT_THROW(verify.verify(decoded_token), jwt::token_verification_exception); + } { auto verify = jwt::verify() .allow_algorithm(jwt::algorithm::none{}) @@ -342,6 +349,15 @@ TEST(TokenTest, VerifyFail) { .with_claim("myclaim", jwt::claim(std::string("test"))); ASSERT_THROW(verify.verify(decoded_token), jwt::token_verification_exception); } + { + jwt::claim object; + std::stringstream{R"{ "test": null }"} >> object; + auto verify = jwt::verify() + .allow_algorithm(jwt::algorithm::none{}) + .with_issuer("auth0") + .with_claim("myclaim", object); + ASSERT_THROW(verify.verify(decoded_token), jwt::token_verification_exception); + } } TEST(TokenTest, VerifyTokenES256) { From d8f11708262ae1a0c904e851a8e85710d274d34b Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Wed, 15 Apr 2020 23:44:45 -0400 Subject: [PATCH 17/20] simplified object compare --- include/jwt-cpp/jwt.h | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/include/jwt-cpp/jwt.h b/include/jwt-cpp/jwt.h index 9a4bd2f54..f4119fede 100644 --- a/include/jwt-cpp/jwt.h +++ b/include/jwt-cpp/jwt.h @@ -1509,16 +1509,8 @@ namespace jwt { } } else if (c.get_type() == claim::type::object) { - auto s1 = c.serialize(); - auto s2 = jc.serialize(); - if (s1.size() != s2.size()) + if( c.to_json().serialize() != jc.to_json().serialize()) throw token_verification_exception("claim " + key + " does not match expected"); - auto it1 = s1.cbegin(); - auto it2 = s2.cbegin(); - while (it1 != s1.cend() && it2 != s2.cend()) { - if (*it1++ != *it2++) - throw token_verification_exception("claim " + key + " does not match expected"); - } } else if (c.get_type() == claim::type::string) { if (c.as_string() != jc.as_string()) From 385afc3bd7c8f0361d690aa0bae676ad1efd1019 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Wed, 15 Apr 2020 23:48:47 -0400 Subject: [PATCH 18/20] fixing string literal --- tests/TokenTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TokenTest.cpp b/tests/TokenTest.cpp index 348bd5b85..0126501fd 100644 --- a/tests/TokenTest.cpp +++ b/tests/TokenTest.cpp @@ -351,7 +351,7 @@ TEST(TokenTest, VerifyFail) { } { jwt::claim object; - std::stringstream{R"{ "test": null }"} >> object; + std::stringstream{R"({ "test": null })"} >> object; auto verify = jwt::verify() .allow_algorithm(jwt::algorithm::none{}) .with_issuer("auth0") From 5df90e9c11f312f1c77725947681fc55cd36d8ff Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Wed, 15 Apr 2020 23:52:17 -0400 Subject: [PATCH 19/20] fixing ambiguous --- tests/TokenTest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TokenTest.cpp b/tests/TokenTest.cpp index 0126501fd..209f18cf2 100644 --- a/tests/TokenTest.cpp +++ b/tests/TokenTest.cpp @@ -325,7 +325,7 @@ TEST(TokenTest, VerifyFail) { auto verify = jwt::verify() .allow_algorithm(jwt::algorithm::none{}) .with_issuer("auth0") - .with_audience({ "test" }); + .with_audience(std::set{ "test" }); ASSERT_THROW(verify.verify(decoded_token), jwt::token_verification_exception); } { From aa001544e2f0364dbc556b88cd61989ee3de0f55 Mon Sep 17 00:00:00 2001 From: Chris Mc Date: Wed, 15 Apr 2020 23:53:26 -0400 Subject: [PATCH 20/20] correcting usage of stream operator --- tests/TokenTest.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/TokenTest.cpp b/tests/TokenTest.cpp index 209f18cf2..65915a167 100644 --- a/tests/TokenTest.cpp +++ b/tests/TokenTest.cpp @@ -351,7 +351,10 @@ TEST(TokenTest, VerifyFail) { } { jwt::claim object; - std::stringstream{R"({ "test": null })"} >> object; + std::istringstream iss{R"({ "test": null })"}; + iss >> object; + ASSERT_EQ(object.get_type() , jwt::claim::type::object); + auto verify = jwt::verify() .allow_algorithm(jwt::algorithm::none{}) .with_issuer("auth0")