diff --git a/include/jwt/error_codes.hpp b/include/jwt/error_codes.hpp index 6c6aaa6..4f7f670 100644 --- a/include/jwt/error_codes.hpp +++ b/include/jwt/error_codes.hpp @@ -98,6 +98,8 @@ enum class VerificationErrc InvalidSignature, // Invalid value type used for known claims TypeConversionError, + // Algorithm confusion attack detected + AlgoConfusionAttack, }; /** diff --git a/include/jwt/impl/algorithm.ipp b/include/jwt/impl/algorithm.ipp index efc21de..c36fa1c 100644 --- a/include/jwt/impl/algorithm.ipp +++ b/include/jwt/impl/algorithm.ipp @@ -25,6 +25,31 @@ SOFTWARE. namespace jwt { +verify_result_t is_secret_a_public_key(const jwt::string_view secret) +{ + std::error_code ec{}; + + BIO_uptr bufkey{ + BIO_new_mem_buf((void*)secret.data(), static_cast(secret.length())), + bio_deletor + }; + if (!bufkey) { + throw MemoryAllocationException("BIO_new_mem_buf failed"); + } + + EC_PKEY_uptr pkey{ + PEM_read_bio_PUBKEY(bufkey.get(), nullptr, nullptr, nullptr), + ev_pkey_deletor + }; + + if (!pkey) { + ec = AlgorithmErrc::InvalidKeyErr; + return { false, ec }; + } + + return {true, ec}; +} + template verify_result_t HMACSign::verify( const jwt::string_view key, diff --git a/include/jwt/impl/error_codes.ipp b/include/jwt/impl/error_codes.ipp index 30906b6..c1fc6f3 100644 --- a/include/jwt/impl/error_codes.ipp +++ b/include/jwt/impl/error_codes.ipp @@ -124,6 +124,8 @@ struct VerificationErrorCategory: std::error_category return "invalid signature"; case VerificationErrc::TypeConversionError: return "type conversion error"; + case VerificationErrc::AlgoConfusionAttack: + return "algo confusion attack possibility"; }; return "unknown verification error"; } diff --git a/include/jwt/impl/jwt.ipp b/include/jwt/impl/jwt.ipp index 6173d2c..d516e4f 100644 --- a/include/jwt/impl/jwt.ipp +++ b/include/jwt/impl/jwt.ipp @@ -195,10 +195,30 @@ inline verify_result_t jwt_signature::verify(const jwt_header& header, const jwt::string_view hdr_pld_sign, const jwt::string_view jwt_sign) { + auto check_res = check_for_algo_confusion_attack(header); + if (check_res.first) { + return {false, VerificationErrc::AlgoConfusionAttack}; + } + verify_func_t verify_fn = get_verify_algorithm_impl(header); return verify_fn(key_, hdr_pld_sign, jwt_sign); } +inline verify_result_t jwt_signature::check_for_algo_confusion_attack( + const jwt_header& hdr) const +{ + switch (hdr.algo()) { + case algorithm::RS256: + case algorithm::RS384: + case algorithm::RS512: + return {false, std::error_code{}}; + default: + // For all other cases make sure that the secret provided + // is not the public key. + return is_secret_a_public_key(key_); + } +} + inline sign_func_t jwt_signature::get_sign_algorithm_impl(const jwt_header& hdr) const noexcept diff --git a/include/jwt/jwt.hpp b/include/jwt/jwt.hpp index f9fabd4..d459de4 100644 --- a/include/jwt/jwt.hpp +++ b/include/jwt/jwt.hpp @@ -829,6 +829,10 @@ private: // Private implementation */ verify_func_t get_verify_algorithm_impl(const jwt_header& hdr) const noexcept; + /*! + */ + verify_result_t check_for_algo_confusion_attack(const jwt_header& hdr) const; + private: // Data members; /// The key for creating the JWS diff --git a/tests/test_jwt_decode.cc b/tests/test_jwt_decode.cc index 215d312..2775843 100644 --- a/tests/test_jwt_decode.cc +++ b/tests/test_jwt_decode.cc @@ -186,3 +186,31 @@ TEST (DecodeTest, TypHeaderMiss) EXPECT_FALSE (ec); } +TEST (DecodeTest, AlgoConfusionAttack) +{ + using namespace jwt::params; + + using namespace jwt::params; + + const char* enc_str = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJpYXQiOjE1MTM4NjIzNzEsImlkIjoiYS1iLWMtZC1lLWYtMS0yLTMiLCJpc3MiOiJhcnVuLm11cmFsaWRoYXJhbiIsInN1YiI6ImFkbWluIn0." + "jk7bRQKTLvs1RcuvMc2B_rt6WBYPoVPirYi_QRBPiuk"; + + std::string pub_key = R"(-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwtpMAM4l1H995oqlqdMh +uqNuffp4+4aUCwuFE9B5s9MJr63gyf8jW0oDr7Mb1Xb8y9iGkWfhouZqNJbMFry+ +iBs+z2TtJF06vbHQZzajDsdux3XVfXv9v6dDIImyU24MsGNkpNt0GISaaiqv51NM +ZQX0miOXXWdkQvWTZFXhmsFCmJLE67oQFSar4hzfAaCulaMD+b3Mcsjlh0yvSq7g +6swiIasEU3qNLKaJAZEzfywroVYr3BwM1IiVbQeKgIkyPS/85M4Y6Ss/T+OWi1Oe +K49NdYBvFP+hNVEoeZzJz5K/nd6C35IX0t2bN5CVXchUFmaUMYk2iPdhXdsC720t +BwIDAQAB +-----END PUBLIC KEY-----)"; + + std::error_code ec; + auto obj = jwt::decode(enc_str, algorithms({"HS256"}), ec, verify(true), secret(pub_key)); + + ASSERT_TRUE (ec); + EXPECT_EQ (ec.value(), static_cast(jwt::VerificationErrc::AlgoConfusionAttack)); + +} \ No newline at end of file diff --git a/tests/test_jwt_rsa.cc b/tests/test_jwt_rsa.cc index de46f8c..7f5949a 100644 --- a/tests/test_jwt_rsa.cc +++ b/tests/test_jwt_rsa.cc @@ -141,5 +141,4 @@ TEST (RSAAlgo, NoSpecificAlgo) EXPECT_THROW (jwt::decode(enc_str, algorithms({"none", "HS384", "RS384"}), verify(true), secret(key)), jwt::InvalidAlgorithmError); -} - +} \ No newline at end of file