6#include <openssl/ecdsa.h>
7#include <openssl/evp.h>
22namespace mcp::auth::openssl {
27 void operator()(ECDSA_SIG* value)
const noexcept { ECDSA_SIG_free(value); }
31 void operator()(EVP_MD_CTX* value)
const noexcept { EVP_MD_CTX_free(value); }
34using EcdsaSigPtr = std::unique_ptr<ECDSA_SIG, EcdsaSigDeleter>;
35using EvpMdCtxPtr = std::unique_ptr<EVP_MD_CTX, EvpMdCtxDeleter>;
37inline const EVP_MD* digest_for_jose_algorithm(std::string_view algorithm) {
38 if (algorithm ==
"RS256" || algorithm ==
"ES256") {
41 if (algorithm ==
"ES384") {
44 if (algorithm ==
"ES512") {
50inline core::Result<std::vector<unsigned char>> ecdsa_raw_signature_to_der(
51 const std::vector<unsigned char>& raw_signature,
52 std::string_view algorithm) {
53 const std::size_t coordinate_size =
54 ec_coordinate_size_for_jose_algorithm(algorithm);
55 if (coordinate_size == 0 || raw_signature.size() != coordinate_size * 2) {
56 return core::unexpected(
57 make_jose_error(JoseErrorCode::kSignatureVerificationFailed,
58 "JWS ECDSA signature has an invalid size"));
61 BignumPtr r(BN_bin2bn(raw_signature.data(),
static_cast<int>(coordinate_size),
63 BignumPtr s(BN_bin2bn(raw_signature.data() + coordinate_size,
64 static_cast<int>(coordinate_size),
nullptr));
66 return core::unexpected(
67 make_jose_error(JoseErrorCode::kSignatureVerificationFailed,
68 "failed to decode JWS ECDSA signature"));
71 EcdsaSigPtr signature(ECDSA_SIG_new());
73 ECDSA_SIG_set0(signature.get(), r.release(), s.release()) != 1) {
74 return core::unexpected(
75 make_jose_error(JoseErrorCode::kSignatureVerificationFailed,
76 "failed to build JWS ECDSA signature"));
79 const int der_size = i2d_ECDSA_SIG(signature.get(),
nullptr);
81 return core::unexpected(
82 make_jose_error(JoseErrorCode::kSignatureVerificationFailed,
83 "failed to size JWS ECDSA signature"));
86 std::vector<unsigned char> der(
static_cast<std::size_t
>(der_size));
87 unsigned char* cursor = der.data();
88 if (i2d_ECDSA_SIG(signature.get(), &cursor) != der_size) {
89 return core::unexpected(
90 make_jose_error(JoseErrorCode::kSignatureVerificationFailed,
91 "failed to encode JWS ECDSA signature"));
96inline core::Result<std::vector<unsigned char>> ecdsa_der_signature_to_raw(
97 const std::vector<unsigned char>& der_signature,
98 std::string_view algorithm) {
99 const std::size_t coordinate_size =
100 ec_coordinate_size_for_jose_algorithm(algorithm);
101 if (coordinate_size == 0) {
102 return core::unexpected(
103 make_jose_error(JoseErrorCode::kUnsupportedJoseAlgorithm,
104 "unsupported JWS ECDSA signature algorithm"));
107 const unsigned char* cursor = der_signature.data();
108 EcdsaSigPtr signature(d2i_ECDSA_SIG(
110 static_cast<long>(der_signature.size())));
112 return core::unexpected(
113 make_jose_error(JoseErrorCode::kSignatureVerificationFailed,
114 "failed to decode JWS ECDSA DER signature"));
117 const BIGNUM* r =
nullptr;
118 const BIGNUM* s =
nullptr;
119 ECDSA_SIG_get0(signature.get(), &r, &s);
120 if (r ==
nullptr || s ==
nullptr) {
121 return core::unexpected(
122 make_jose_error(JoseErrorCode::kSignatureVerificationFailed,
123 "JWS ECDSA DER signature is missing r/s"));
126 std::vector<unsigned char> raw;
127 auto r_bytes = bignum_to_fixed_bytes(r, coordinate_size);
128 if (!r_bytes.has_value()) {
129 return core::unexpected(r_bytes.error());
131 auto s_bytes = bignum_to_fixed_bytes(s, coordinate_size);
132 if (!s_bytes.has_value()) {
133 return core::unexpected(s_bytes.error());
135 raw.reserve(r_bytes->size() + s_bytes->size());
136 raw.insert(raw.end(), r_bytes->begin(), r_bytes->end());
137 raw.insert(raw.end(), s_bytes->begin(), s_bytes->end());
141inline bool is_ecdsa_jose_algorithm(std::string_view algorithm) {
142 return algorithm ==
"ES256" || algorithm ==
"ES384" || algorithm ==
"ES512";
148 std::optional<std::string> required_algorithm;
149 std::optional<std::string> required_key_id;
153 std::string_view compact_jws,
const JsonWebKey& jwk,
155 auto decoded = decode_compact_jws(compact_jws);
156 if (!decoded.has_value()) {
157 return core::unexpected(decoded.error());
160 const auto& header = decoded->protected_header;
161 if (options.required_algorithm.has_value() &&
162 header.algorithm != *options.required_algorithm) {
163 return core::unexpected(
164 make_jose_error(JoseErrorCode::kSignatureVerificationFailed,
165 "JWS alg does not match required algorithm"));
167 if (jwk.algorithm.has_value() && header.algorithm != *jwk.algorithm) {
168 return core::unexpected(
169 make_jose_error(JoseErrorCode::kSignatureVerificationFailed,
170 "JWS alg does not match JWK alg"));
172 if (options.required_key_id.has_value() &&
173 (!header.key_id.has_value() ||
174 *header.key_id != *options.required_key_id)) {
175 return core::unexpected(
176 make_jose_error(JoseErrorCode::kSignatureVerificationFailed,
177 "JWS kid does not match required key id"));
179 if (jwk.key_id.has_value() &&
180 (!header.key_id.has_value() || *header.key_id != *jwk.key_id)) {
181 return core::unexpected(
182 make_jose_error(JoseErrorCode::kSignatureVerificationFailed,
183 "JWS kid does not match JWK kid"));
186 auto public_key = public_key_from_jwk(jwk, header.algorithm);
187 if (!public_key.has_value()) {
188 return core::unexpected(public_key.error());
191 const EVP_MD* digest = detail::digest_for_jose_algorithm(header.algorithm);
192 if (digest ==
nullptr) {
193 return core::unexpected(
194 make_jose_error(JoseErrorCode::kUnsupportedJoseAlgorithm,
195 "unsupported JWS signature algorithm"));
198 std::vector<unsigned char> signature = decoded->signature;
199 if (detail::is_ecdsa_jose_algorithm(header.algorithm)) {
200 auto der = detail::ecdsa_raw_signature_to_der(signature, header.algorithm);
201 if (!der.has_value()) {
202 return core::unexpected(der.error());
204 signature = std::move(*der);
207 detail::EvpMdCtxPtr context(EVP_MD_CTX_new());
208 const std::string signing_input = decoded->parts.signing_input();
210 EVP_DigestVerifyInit(context.get(),
nullptr, digest,
nullptr,
211 public_key->key.get()) <= 0 ||
212 EVP_DigestVerifyUpdate(context.get(), signing_input.data(),
213 signing_input.size()) <= 0) {
214 return core::unexpected(
215 make_jose_error(JoseErrorCode::kSignatureVerificationFailed,
216 "failed to initialize JWS signature verification"));
220 EVP_DigestVerifyFinal(context.get(), signature.data(), signature.size());
225 return core::unexpected(
226 make_jose_error(JoseErrorCode::kSignatureVerificationFailed,
227 "JWS signature verification failed"));
229 return core::unexpected(
230 make_jose_error(JoseErrorCode::kSignatureVerificationFailed,
231 "OpenSSL failed while verifying JWS signature"));
OpenSSL conversion helpers for public JSON Web Keys.
Safe parsing boundaries for JOSE compact JWS values.
Shared result and error primitives used by the public cxxmcp SDK.
tl::expected< T, Error > Result
Alias for the SDK result type.
Definition result.hpp:64
Public JSON Web Key value model.
Definition jwks.hpp:24
Definition jws_verify.hpp:147
Definition jws_verify.hpp:26
Definition jws_verify.hpp:30