]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw/sts: adding code for JWT signature validation
authorPritha Srivastava <prsrivas@redhat.com>
Thu, 26 Dec 2024 08:56:30 +0000 (14:26 +0530)
committerPritha Srivastava <prsrivas@redhat.com>
Sat, 26 Apr 2025 06:02:04 +0000 (11:32 +0530)
using modulus and exponent for RSA group of algorithms.

A couple of issues and a fix have been suggested by
Pupu Toivonen (pupu.toivonen@csc.fi) in signature calculation
using modulus and exponent.

Fixes: https://tracker.ceph.com/issues/51018
Signed-off-by: Pritha Srivastava <prsrivas@redhat.com>
src/rgw/jwt-cpp/jwt.h
src/rgw/rgw_rest_sts.cc
src/rgw/rgw_rest_sts.h

index 352b80010b866040742c080f4940780cf7c55941..995a6410ac154f058842976ea3ff2366efa43733 100644 (file)
 #include <openssl/pem.h>
 #include <openssl/ec.h>
 #include <openssl/err.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+
+#include "rgw/rgw_b64.h"
 
 //If openssl version less than 1.1
 #if OPENSSL_VERSION_NUMBER < 269484032
@@ -233,6 +237,17 @@ namespace jwt {
                                } else
                                        throw rsa_exception("at least one of public or private key need to be present");
                        }
+
+                       rsa(const EVP_MD*(*md)(), const std::string& name) : md(md), alg_name(name)
+                       {}
+
+                       void setModulusExponentCalcPublicKey(const std::string& modulus, const std::string& exponent)
+                       {
+                               this->modulus = modulus;
+                               this->exponent = exponent;
+                               calculatePublicKey();
+                       }
+
                        /**
                         * Sign jwt data
                         * \param data The data to sign
@@ -296,8 +311,43 @@ namespace jwt {
                        std::shared_ptr<EVP_PKEY> pkey;
                        /// Hash generator
                        const EVP_MD*(*md)();
+                       /// Modulus
+                       std::string modulus;
+                       /// Exponent
+                       std::string exponent;
                        /// Algorithmname
                        const std::string alg_name;
+
+                       void calculatePublicKey()
+                       {
+                               std::string n_str = base64_decode_url(modulus);
+                               std::string e_str = base64_decode_url(exponent);
+                               unsigned char* u_n = (unsigned char *)n_str.c_str();
+                               unsigned char* u_e = (unsigned char *)e_str.c_str();
+                               BIGNUM *n = BN_bin2bn(u_n, n_str.size(), NULL);
+                               BIGNUM *e = BN_bin2bn(u_e, e_str.size(), NULL);
+
+                               if (e && n) {
+                                       EVP_PKEY* pRsaKey = EVP_PKEY_new();
+                                       RSA* rsa = RSA_new();
+                                       RSA_set0_key(rsa, n, e, nullptr);
+                                       EVP_PKEY_assign_RSA(pRsaKey, rsa);
+                                       std::shared_ptr<EVP_PKEY> p(pRsaKey, EVP_PKEY_free);
+                                       pkey = p;
+                               } else {
+                                       if (n) BN_free(n);
+                                       if (e) BN_free(e);
+                                       throw rsa_exception("Invalid encoding for modulus or exponent\n");
+                               }
+                       }
+
+                       std::string base64_decode_url(const std::string& str) const {
+                               std::string s = "====";
+                               std::string padded_str = str.length() % 4 == 0 ? str : str + s.substr(0, str.length() % 4);
+                               std::replace(padded_str.begin(), padded_str.end(), '_', '/');
+                               std::replace(padded_str.begin(), padded_str.end(), '-', '+');
+                               return rgw::from_base64(padded_str);
+                       }
                };
                /**
                 * Base class for ECDSA family of algorithms
@@ -629,6 +679,15 @@ namespace jwt {
                        explicit rs256(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "")
                                : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "RS256")
                        {}
+
+                       rs256() : rsa(EVP_sha256, "RS256")
+                       {}
+
+                       rs256& setModulusAndExponent(const std::string& modulus, const std::string& exponent)
+                       {
+                               rsa::setModulusExponentCalcPublicKey(modulus, exponent);
+                               return *this;
+                       }
                };
                /**
                 * RS384 algorithm
@@ -644,6 +703,15 @@ namespace jwt {
                        explicit rs384(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "")
                                : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "RS384")
                        {}
+
+                       rs384() : rsa(EVP_sha384, "RS384")
+                       {}
+
+                       rs384& setModulusAndExponent(const std::string& modulus, const std::string& exponent)
+                       {
+                               rsa::setModulusExponentCalcPublicKey(modulus, exponent);
+                               return *this;
+                       }
                };
                /**
                 * RS512 algorithm
@@ -659,6 +727,15 @@ namespace jwt {
                        explicit rs512(const std::string& public_key, const std::string& private_key = "", const std::string& public_key_password = "", const std::string& private_key_password = "")
                                : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "RS512")
                        {}
+
+                       rs512() : rsa(EVP_sha512, "RS512")
+                       {}
+
+                       rs512& setModulusAndExponent(const std::string& modulus, const std::string& exponent)
+                       {
+                               rsa::setModulusExponentCalcPublicKey(modulus, exponent);
+                               return *this;
+                       }
                };
                /**
                 * ES256 algorithm
index c0c6b5e89acd2c873f41b926ac896f93796ef9ff..b9308b5cf941bf91b42ac794b9d32cfd7f0445b9 100644 (file)
@@ -339,6 +339,30 @@ WebTokenEngine::get_cert_url(const string& iss, const DoutPrefixProvider *dpp, o
   return cert_url;
 }
 
+void
+WebTokenEngine::validate_signature_using_n_e(const DoutPrefixProvider* dpp, const jwt::decoded_jwt& decoded, const std::string &algorithm, const std::string& n, const std::string& e) const
+{
+  try {
+    if (algorithm == "RS256") {
+      auto verifier = jwt::verify()
+                  .allow_algorithm(jwt::algorithm::rs256().setModulusAndExponent(n,e));
+      verifier.verify(decoded);
+    } else if (algorithm == "RS384") {
+      auto verifier = jwt::verify()
+                  .allow_algorithm(jwt::algorithm::rs384().setModulusAndExponent(n,e));
+      verifier.verify(decoded);
+    } else if (algorithm == "RS512") {
+      auto verifier = jwt::verify()
+                  .allow_algorithm(jwt::algorithm::rs512().setModulusAndExponent(n,e));
+      verifier.verify(decoded);
+    }
+  } catch (const std::exception& e) {
+    throw std::system_error(EACCES, std::system_category(), std::string("Signature validation using n, e failed: ") + e.what());
+  }
+  ldpp_dout(dpp, 10) << "Verified signature using n and e"<< dendl;
+  return;
+}
+
 void
 WebTokenEngine::validate_signature(const DoutPrefixProvider* dpp, const jwt::decoded_jwt& decoded, const string& algorithm, const string& iss, const vector<string>& thumbprints, optional_yield y) const
 {
@@ -450,8 +474,17 @@ WebTokenEngine::validate_signature(const DoutPrefixProvider* dpp, const jwt::dec
               throw;
             }
           } else {
-            ldpp_dout(dpp, 0) << "x5c not present" << dendl;
-            throw -EINVAL;
+            if (algorithm == "RS256" || algorithm == "RS384" || algorithm == "RS512") {
+              string n, e; //modulus and exponent
+              if (JSONDecoder::decode_json("n", n, &parser) && JSONDecoder::decode_json("e", e, &parser)) {
+                validate_signature_using_n_e(dpp, decoded, algorithm, n, e);
+                return;
+              }
+              ldpp_dout(dpp, 0) << "x5c not present or n, e not present" << dendl;
+              throw std::system_error(EINVAL, std::system_category());
+            } else {
+              throw std::system_error(EINVAL, std::system_category(), "Invalid algorithm: " + algorithm);
+            }
           }
         } else {
           ldpp_dout(dpp, 0) << "Malformed JSON object for keys" << dendl;
index 432244009202f7b32683b74a67b518051362db3c..8d25f6784329e165643c4987ff2cc40b44f9ba2a 100644 (file)
@@ -53,6 +53,8 @@ class WebTokenEngine : public rgw::auth::Engine {
   std::tuple<boost::optional<WebTokenEngine::token_t>, boost::optional<WebTokenEngine::principal_tags_t>>
   get_from_jwt(const DoutPrefixProvider* dpp, const std::string& token, const req_state* const s, optional_yield y) const;
 
+ void validate_signature_using_n_e(const DoutPrefixProvider* dpp, const jwt::decoded_jwt& decoded, const std::string &algorithm, const std::string& n, const std::string& e) const;
+
   void validate_signature (const DoutPrefixProvider* dpp, const jwt::decoded_jwt& decoded, const std::string& algorithm, const std::string& iss, const std::vector<std::string>& thumbprints, optional_yield y) const;
 
   result_t authenticate(const DoutPrefixProvider* dpp,