• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prince-chrismc / jwt-cpp / 22837883566

09 Mar 2026 04:05AM UTC coverage: 92.471% (+0.1%) from 92.343%
22837883566

Pull #38

github

web-flow
Merge dc06f336b into b12ab284d
Pull Request #38: replace hmac impl to use BIGNUM

40 of 41 new or added lines in 1 file covered. (97.56%)

1 existing line in 1 file now uncovered.

1437 of 1554 relevant lines covered (92.47%)

620.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.0
/include/jwt-cpp/jwt.h
1
#ifndef JWT_CPP_JWT_H
2
#define JWT_CPP_JWT_H
3

4
#ifndef JWT_DISABLE_PICOJSON
5
#ifndef PICOJSON_USE_INT64
6
#define PICOJSON_USE_INT64
7
#endif
8
#include "picojson/picojson.h"
9
#endif
10

11
#ifndef JWT_DISABLE_BASE64
12
#include "base.h"
13
#endif
14

15
#include <openssl/ec.h>
16
#include <openssl/ecdsa.h>
17
#include <openssl/err.h>
18
#include <openssl/evp.h>
19
#include <openssl/hmac.h>
20
#include <openssl/pem.h>
21
#include <openssl/rsa.h>
22
#include <openssl/ssl.h>
23

24
#include <algorithm>
25
#include <chrono>
26
#include <climits>
27
#include <cmath>
28
#include <cstring>
29
#include <functional>
30
#include <iterator>
31
#include <locale>
32
#include <memory>
33
#include <set>
34
#include <system_error>
35
#include <type_traits>
36
#include <unordered_map>
37
#include <utility>
38
#include <vector>
39

40
#if __cplusplus >= 201402L
41
#ifdef __has_include
42
#if __has_include(<experimental/type_traits>)
43
#include <experimental/type_traits>
44
#endif
45
#endif
46
#endif
47

48
#if OPENSSL_VERSION_NUMBER >= 0x30000000L // 3.0.0
49
#define JWT_OPENSSL_3_0
50
#include <openssl/param_build.h>
51
#elif OPENSSL_VERSION_NUMBER >= 0x10101000L // 1.1.1
52
#define JWT_OPENSSL_1_1_1
53
#elif OPENSSL_VERSION_NUMBER >= 0x10100000L // 1.1.0
54
#define JWT_OPENSSL_1_1_0
55
#elif OPENSSL_VERSION_NUMBER >= 0x10000000L // 1.0.0
56
#define JWT_OPENSSL_1_0_0
57
#endif
58

59
#if defined(LIBRESSL_VERSION_NUMBER)
60
#if LIBRESSL_VERSION_NUMBER >= 0x3070100fL // 3.7.1 - EdDSA support
61
#define JWT_OPENSSL_1_1_1
62
#elif LIBRESSL_VERSION_NUMBER >= 0x3050300fL // 3.5.3
63
#define JWT_OPENSSL_1_1_0
64
#else
65
#define JWT_OPENSSL_1_0_0
66
#endif
67
#endif
68

69
#if defined(LIBWOLFSSL_VERSION_HEX)
70
#define JWT_OPENSSL_1_1_1
71
#endif
72

73
#ifndef JWT_CLAIM_EXPLICIT
74
#define JWT_CLAIM_EXPLICIT explicit
75
#endif
76

77
/**
78
 * \brief JSON Web Token.
79
 *
80
 * A namespace to contain everything related to handling JSON Web Tokens, JWT for short,
81
 * as a part of [RFC7519](https://tools.ietf.org/html/rfc7519), or alternatively for
82
 * JWS (JSON Web Signature) from [RFC7515](https://tools.ietf.org/html/rfc7515)
83
 */
84
namespace jwt {
85
        /**
86
         * Default system time point in UTC
87
         */
88
        using date = std::chrono::system_clock::time_point;
89

90
        /**
91
         * \brief Everything related to error codes issued by the library
92
         */
93
        namespace error {
94
                struct signature_verification_exception : public std::system_error {
95
                        using system_error::system_error;
96
                };
97
                struct signature_generation_exception : public std::system_error {
98
                        using system_error::system_error;
99
                };
100
                struct rsa_exception : public std::system_error {
101
                        using system_error::system_error;
102
                };
103
                struct ecdsa_exception : public std::system_error {
104
                        using system_error::system_error;
105
                };
106
                struct token_verification_exception : public std::system_error {
107
                        using system_error::system_error;
108
                };
109
                /**
110
                 * \brief Errors related to processing of RSA signatures
111
                 */
112
                enum class rsa_error {
113
                        ok = 0,
114
                        cert_load_failed = 10,
115
                        get_key_failed,
116
                        write_key_failed,
117
                        write_cert_failed,
118
                        convert_to_pem_failed,
119
                        load_key_bio_write,
120
                        load_key_bio_read,
121
                        create_mem_bio_failed,
122
                        no_key_provided,
123
                        set_rsa_failed,
124
                        create_context_failed
125
                };
126
                /**
127
                 * \brief Error category for RSA errors
128
                 */
129
                inline std::error_category& rsa_error_category() {
458✔
130
                        class rsa_error_cat : public std::error_category {
131
                        public:
132
                                const char* name() const noexcept override { return "rsa_error"; };
1✔
133
                                std::string message(int ev) const override {
81✔
134
                                        switch (static_cast<rsa_error>(ev)) {
81✔
135
                                        case rsa_error::ok: return "no error";
3✔
136
                                        case rsa_error::cert_load_failed: return "error loading cert into memory";
15✔
137
                                        case rsa_error::get_key_failed: return "error getting key from certificate";
9✔
138
                                        case rsa_error::write_key_failed: return "error writing key data in PEM format";
9✔
139
                                        case rsa_error::write_cert_failed: return "error writing cert data in PEM format";
9✔
140
                                        case rsa_error::convert_to_pem_failed: return "failed to convert key to pem";
21✔
141
                                        case rsa_error::load_key_bio_write: return "failed to load key: bio write failed";
24✔
142
                                        case rsa_error::load_key_bio_read: return "failed to load key: bio read failed";
18✔
143
                                        case rsa_error::create_mem_bio_failed: return "failed to create memory bio";
36✔
144
                                        case rsa_error::no_key_provided: return "at least one of public or private key need to be present";
39✔
145
                                        case rsa_error::set_rsa_failed: return "set modulus and exponent to RSA failed";
9✔
146
                                        case rsa_error::create_context_failed: return "failed to create context";
9✔
147
                                        default: return "unknown RSA error";
42✔
148
                                        }
149
                                }
150
                        };
151
                        static rsa_error_cat cat;
458✔
152
                        return cat;
458✔
153
                }
154
                /**
155
                 * \brief Converts JWT-CPP errors into generic STL error_codes
156
                 */
157
                inline std::error_code make_error_code(rsa_error e) { return {static_cast<int>(e), rsa_error_category()}; }
179✔
158
                /**
159
                 * \brief Errors related to processing of RSA signatures
160
                 */
161
                enum class ecdsa_error {
162
                        ok = 0,
163
                        load_key_bio_write = 10,
164
                        load_key_bio_read,
165
                        create_mem_bio_failed,
166
                        no_key_provided,
167
                        invalid_key_size,
168
                        invalid_key,
169
                        create_context_failed,
170
                        cert_load_failed,
171
                        get_key_failed,
172
                        write_key_failed,
173
                        write_cert_failed,
174
                        convert_to_pem_failed,
175
                        unknown_curve,
176
                        set_ecdsa_failed
177
                };
178
                /**
179
                 * \brief Error category for ECDSA errors
180
                 */
181
                inline std::error_category& ecdsa_error_category() {
388✔
182
                        class ecdsa_error_cat : public std::error_category {
183
                        public:
184
                                const char* name() const noexcept override { return "ecdsa_error"; };
1✔
185
                                std::string message(int ev) const override {
100✔
186
                                        switch (static_cast<ecdsa_error>(ev)) {
100✔
187
                                        case ecdsa_error::ok: return "no error";
3✔
188
                                        case ecdsa_error::load_key_bio_write: return "failed to load key: bio write failed";
18✔
189
                                        case ecdsa_error::load_key_bio_read: return "failed to load key: bio read failed";
18✔
190
                                        case ecdsa_error::create_mem_bio_failed: return "failed to create memory bio";
21✔
191
                                        case ecdsa_error::no_key_provided:
13✔
192
                                                return "at least one of public or private key need to be present";
26✔
193
                                        case ecdsa_error::invalid_key_size: return "invalid key size";
75✔
194
                                        case ecdsa_error::invalid_key: return "invalid key";
9✔
195
                                        case ecdsa_error::create_context_failed: return "failed to create context";
15✔
196
                                        case ecdsa_error::cert_load_failed: return "error loading cert into memory";
12✔
197
                                        case ecdsa_error::get_key_failed: return "error getting key from certificate";
6✔
198
                                        case ecdsa_error::write_key_failed: return "error writing key data in PEM format";
6✔
199
                                        case ecdsa_error::write_cert_failed: return "error writing cert data in PEM format";
3✔
200
                                        case ecdsa_error::convert_to_pem_failed: return "failed to convert key to pem";
9✔
201
                                        case ecdsa_error::unknown_curve: return "unknown curve";
3✔
202
                                        case ecdsa_error::set_ecdsa_failed: return "set parameters to ECDSA failed";
12✔
203
                                        default: return "unknown ECDSA error";
51✔
204
                                        }
205
                                }
206
                        };
207
                        static ecdsa_error_cat cat;
388✔
208
                        return cat;
388✔
209
                }
210
                /**
211
                 * \brief Converts JWT-CPP errors into generic STL error_codes
212
                 */
213
                inline std::error_code make_error_code(ecdsa_error e) { return {static_cast<int>(e), ecdsa_error_category()}; }
152✔
214

215
                /**
216
                 * \brief Errors related to verification of signatures
217
                 */
218
                enum class signature_verification_error {
219
                        ok = 0,
220
                        invalid_signature = 10,
221
                        create_context_failed,
222
                        verifyinit_failed,
223
                        verifyupdate_failed,
224
                        verifyfinal_failed,
225
                        get_key_failed,
226
                        set_rsa_pss_saltlen_failed,
227
                        signature_encoding_failed
228
                };
229
                /**
230
                 * \brief Error category for verification errors
231
                 */
232
                inline std::error_category& signature_verification_error_category() {
368✔
233
                        class verification_error_cat : public std::error_category {
234
                        public:
235
                                const char* name() const noexcept override { return "signature_verification_error"; };
1✔
236
                                std::string message(int ev) const override {
122✔
237
                                        switch (static_cast<signature_verification_error>(ev)) {
122✔
238
                                        case signature_verification_error::ok: return "no error";
3✔
239
                                        case signature_verification_error::invalid_signature: return "invalid signature";
183✔
240
                                        case signature_verification_error::create_context_failed:
1✔
241
                                                return "failed to verify signature: could not create context";
2✔
242
                                        case signature_verification_error::verifyinit_failed:
1✔
243
                                                return "failed to verify signature: VerifyInit failed";
2✔
244
                                        case signature_verification_error::verifyupdate_failed:
1✔
245
                                                return "failed to verify signature: VerifyUpdate failed";
2✔
246
                                        case signature_verification_error::verifyfinal_failed:
43✔
247
                                                return "failed to verify signature: VerifyFinal failed";
86✔
248
                                        case signature_verification_error::get_key_failed:
1✔
249
                                                return "failed to verify signature: Could not get key";
2✔
250
                                        case signature_verification_error::set_rsa_pss_saltlen_failed:
1✔
251
                                                return "failed to verify signature: EVP_PKEY_CTX_set_rsa_pss_saltlen failed";
2✔
252
                                        case signature_verification_error::signature_encoding_failed:
1✔
253
                                                return "failed to verify signature: i2d_ECDSA_SIG failed";
2✔
254
                                        default: return "unknown signature verification error";
33✔
255
                                        }
256
                                }
257
                        };
258
                        static verification_error_cat cat;
368✔
259
                        return cat;
368✔
260
                }
261
                /**
262
                 * \brief Converts JWT-CPP errors into generic STL error_codes
263
                 */
264
                inline std::error_code make_error_code(signature_verification_error e) {
159✔
265
                        return {static_cast<int>(e), signature_verification_error_category()};
159✔
266
                }
267

268
                /**
269
                 * \brief Errors related to signature generation errors
270
                 */
271
                enum class signature_generation_error {
272
                        ok = 0,
273
                        hmac_failed = 10,
274
                        create_context_failed,
275
                        signinit_failed,
276
                        signupdate_failed,
277
                        signfinal_failed,
278
                        ecdsa_do_sign_failed,
279
                        digestinit_failed,
280
                        digestupdate_failed,
281
                        digestfinal_failed,
282
                        rsa_padding_failed,
283
                        rsa_private_encrypt_failed,
284
                        get_key_failed,
285
                        set_rsa_pss_saltlen_failed,
286
                        signature_decoding_failed
287
                };
288
                /**
289
                 * \brief Error category for signature generation errors
290
                 */
291
                inline std::error_category& signature_generation_error_category() {
200✔
292
                        class signature_generation_error_cat : public std::error_category {
293
                        public:
294
                                const char* name() const noexcept override { return "signature_generation_error"; };
1✔
295
                                std::string message(int ev) const override {
56✔
296
                                        switch (static_cast<signature_generation_error>(ev)) {
56✔
297
                                        case signature_generation_error::ok: return "no error";
3✔
298
                                        case signature_generation_error::hmac_failed: return "hmac failed";
3✔
299
                                        case signature_generation_error::create_context_failed:
1✔
300
                                                return "failed to create signature: could not create context";
2✔
301
                                        case signature_generation_error::signinit_failed:
1✔
302
                                                return "failed to create signature: SignInit failed";
2✔
303
                                        case signature_generation_error::signupdate_failed:
1✔
304
                                                return "failed to create signature: SignUpdate failed";
2✔
305
                                        case signature_generation_error::signfinal_failed:
25✔
306
                                                return "failed to create signature: SignFinal failed";
50✔
307
                                        case signature_generation_error::ecdsa_do_sign_failed: return "failed to generate ecdsa signature";
3✔
308
                                        case signature_generation_error::digestinit_failed:
1✔
309
                                                return "failed to create signature: DigestInit failed";
2✔
310
                                        case signature_generation_error::digestupdate_failed:
1✔
311
                                                return "failed to create signature: DigestUpdate failed";
2✔
312
                                        case signature_generation_error::digestfinal_failed:
1✔
313
                                                return "failed to create signature: DigestFinal failed";
2✔
314
                                        case signature_generation_error::rsa_padding_failed:
1✔
315
                                                return "failed to create signature: EVP_PKEY_CTX_set_rsa_padding failed";
2✔
316
                                        case signature_generation_error::rsa_private_encrypt_failed:
1✔
317
                                                return "failed to create signature: RSA_private_encrypt failed";
2✔
318
                                        case signature_generation_error::get_key_failed:
1✔
319
                                                return "failed to generate signature: Could not get key";
2✔
320
                                        case signature_generation_error::set_rsa_pss_saltlen_failed:
1✔
321
                                                return "failed to create signature: EVP_PKEY_CTX_set_rsa_pss_saltlen failed";
2✔
322
                                        case signature_generation_error::signature_decoding_failed:
1✔
323
                                                return "failed to create signature: d2i_ECDSA_SIG failed";
2✔
324
                                        default: return "unknown signature generation error";
51✔
325
                                        }
326
                                }
327
                        };
328
                        static signature_generation_error_cat cat = {};
200✔
329
                        return cat;
200✔
330
                }
331
                /**
332
                 * \brief Converts JWT-CPP errors into generic STL error_codes
333
                 */
334
                inline std::error_code make_error_code(signature_generation_error e) {
93✔
335
                        return {static_cast<int>(e), signature_generation_error_category()};
93✔
336
                }
337

338
                /**
339
                 * \brief Errors related to token verification errors
340
                 */
341
                enum class token_verification_error {
342
                        ok = 0,
343
                        wrong_algorithm = 10,
344
                        missing_claim,
345
                        claim_type_missmatch,
346
                        claim_value_missmatch,
347
                        token_expired,
348
                        audience_missmatch
349
                };
350
                /**
351
                 * \brief Error category for token verification errors
352
                 */
353
                inline std::error_category& token_verification_error_category() {
229✔
354
                        class token_verification_error_cat : public std::error_category {
355
                        public:
356
                                const char* name() const noexcept override { return "token_verification_error"; };
1✔
357
                                std::string message(int ev) const override {
99✔
358
                                        switch (static_cast<token_verification_error>(ev)) {
99✔
359
                                        case token_verification_error::ok: return "no error";
3✔
360
                                        case token_verification_error::wrong_algorithm: return "wrong algorithm";
21✔
361
                                        case token_verification_error::missing_claim: return "decoded JWT is missing required claim(s)";
75✔
362
                                        case token_verification_error::claim_type_missmatch:
7✔
363
                                                return "claim type does not match expected type";
14✔
364
                                        case token_verification_error::claim_value_missmatch:
13✔
365
                                                return "claim value does not match expected value";
26✔
366
                                        case token_verification_error::token_expired: return "token expired";
72✔
367
                                        case token_verification_error::audience_missmatch:
13✔
368
                                                return "token doesn't contain the required audience";
26✔
369
                                        default: return "unknown token verification error";
27✔
370
                                        }
371
                                }
372
                        };
373
                        static token_verification_error_cat cat = {};
229✔
374
                        return cat;
229✔
375
                }
376
                /**
377
                 * \brief Converts JWT-CPP errors into generic STL error_codes
378
                 */
379
                inline std::error_code make_error_code(token_verification_error e) {
123✔
380
                        return {static_cast<int>(e), token_verification_error_category()};
123✔
381
                }
382
                /**
383
                 * \brief Raises an exception if any JWT-CPP error codes are active
384
                 */
385
                inline void throw_if_error(std::error_code ec) {
1,155✔
386
                        if (ec) {
1,155✔
387
                                if (ec.category() == rsa_error_category()) throw rsa_exception(ec);
279✔
388
                                if (ec.category() == ecdsa_error_category()) throw ecdsa_exception(ec);
236✔
389
                                if (ec.category() == signature_verification_error_category())
209✔
390
                                        throw signature_verification_exception(ec);
102✔
391
                                if (ec.category() == signature_generation_error_category()) throw signature_generation_exception(ec);
107✔
392
                                if (ec.category() == token_verification_error_category()) throw token_verification_exception(ec);
83✔
393
                        }
394
                }
876✔
395
        } // namespace error
396
} // namespace jwt
397

398
namespace std {
399
        template<>
400
        struct is_error_code_enum<jwt::error::rsa_error> : true_type {};
401
        template<>
402
        struct is_error_code_enum<jwt::error::ecdsa_error> : true_type {};
403
        template<>
404
        struct is_error_code_enum<jwt::error::signature_verification_error> : true_type {};
405
        template<>
406
        struct is_error_code_enum<jwt::error::signature_generation_error> : true_type {};
407
        template<>
408
        struct is_error_code_enum<jwt::error::token_verification_error> : true_type {};
409
} // namespace std
410

411
namespace jwt {
412
        /**
413
         * \brief A collection for working with certificates
414
         *
415
         * These _helpers_ are usefully when working with certificates OpenSSL APIs.
416
         * For example, when dealing with JWKS (JSON Web Key Set)[https://tools.ietf.org/html/rfc7517]
417
         * you maybe need to extract the modulus and exponent of an RSA Public Key.
418
         */
419
        namespace helper {
420
                /**
421
                 * \brief Handle class for EVP_PKEY structures
422
                 *
423
                 * Starting from OpenSSL 1.1.0, EVP_PKEY has internal reference counting. This handle class allows
424
                 * jwt-cpp to leverage that and thus safe an allocation for the control block in std::shared_ptr.
425
                 * The handle uses shared_ptr as a fallback on older versions. The behaviour should be identical between both.
426
                 */
427
                class evp_pkey_handle {
428
                public:
429
                        /**
430
                         * \brief Creates a null key pointer.
431
                         */
432
                        constexpr evp_pkey_handle() noexcept = default;
415✔
433
#ifdef JWT_OPENSSL_1_0_0
434
                        /**
435
                         * \brief Construct a new handle. The handle takes ownership of the key.
436
                         * \param key The key to store
437
                         */
438
                        explicit evp_pkey_handle(EVP_PKEY* key) { m_key = std::shared_ptr<EVP_PKEY>(key, EVP_PKEY_free); }
439

440
                        EVP_PKEY* get() const noexcept { return m_key.get(); }
441
                        bool operator!() const noexcept { return m_key == nullptr; }
442
                        explicit operator bool() const noexcept { return m_key != nullptr; }
443

444
                private:
445
                        std::shared_ptr<EVP_PKEY> m_key{nullptr};
446
#else
447
                        /**
448
                         * \brief Construct a new handle. The handle takes ownership of the key.
449
                         * \param key The key to store
450
                         */
451
                        explicit constexpr evp_pkey_handle(EVP_PKEY* key) noexcept : m_key{key} {}
404✔
452
                        evp_pkey_handle(const evp_pkey_handle& other) : m_key{other.m_key} {
444✔
453
                                if (m_key != nullptr && EVP_PKEY_up_ref(m_key) != 1) throw std::runtime_error("EVP_PKEY_up_ref failed");
444✔
454
                        }
444✔
455
// C++11 requires the body of a constexpr constructor to be empty
456
#if __cplusplus >= 201402L
457
                        constexpr
458
#endif
459
                                evp_pkey_handle(evp_pkey_handle&& other) noexcept
426✔
460
                                : m_key{other.m_key} {
426✔
461
                                other.m_key = nullptr;
426✔
462
                        }
426✔
463
                        evp_pkey_handle& operator=(const evp_pkey_handle& other) {
464
                                if (&other == this) return *this;
465
                                decrement_ref_count(m_key);
466
                                m_key = other.m_key;
467
                                increment_ref_count(m_key);
468
                                return *this;
469
                        }
470
                        evp_pkey_handle& operator=(evp_pkey_handle&& other) noexcept {
331✔
471
                                if (&other == this) return *this;
331✔
472
                                decrement_ref_count(m_key);
331✔
473
                                m_key = other.m_key;
331✔
474
                                other.m_key = nullptr;
331✔
475
                                return *this;
331✔
476
                        }
477
                        evp_pkey_handle& operator=(EVP_PKEY* key) {
478
                                decrement_ref_count(m_key);
479
                                m_key = key;
480
                                increment_ref_count(m_key);
481
                                return *this;
482
                        }
483
                        ~evp_pkey_handle() noexcept { decrement_ref_count(m_key); }
1,689✔
484

485
                        EVP_PKEY* get() const noexcept { return m_key; }
745✔
486
                        bool operator!() const noexcept { return m_key == nullptr; }
586✔
487
                        explicit operator bool() const noexcept { return m_key != nullptr; }
5✔
488

489
                private:
490
                        EVP_PKEY* m_key{nullptr};
491

492
                        static void increment_ref_count(EVP_PKEY* key) {
493
                                if (key != nullptr && EVP_PKEY_up_ref(key) != 1) throw std::runtime_error("EVP_PKEY_up_ref failed");
494
                        }
495
                        static void decrement_ref_count(EVP_PKEY* key) noexcept {
2,020✔
496
                                if (key != nullptr) EVP_PKEY_free(key);
2,020✔
497
                        }
2,020✔
498
#endif
499
                };
500

501
                /**
502
                 * \brief Handle class for BIGNUM structures
503
                 *
504
                 * This handle class wraps OpenSSL's BIGNUM type and manages its lifecycle.
505
                 * It provides copy semantics (via BN_dup) and move semantics for efficient
506
                 * passing between containers.
507
                 */
508
                class bignum_handle {
509
                public:
510
                        /**
511
                         * \brief Creates a new BIGNUM initialized to zero.
512
                         * \throws std::runtime_error if BIGNUM allocation fails
513
                         */
514
                        bignum_handle() {
11✔
515
                                m_bn = BN_new();
11✔
516
                                if (m_bn == nullptr) throw std::runtime_error("BN_new failed");
11✔
517
                        }
11✔
518

519
                        /**
520
                         * \brief Construct a handle from a pointer. The handle takes ownership.
521
                         * \param bn The BIGNUM to store. Performs BN_dup() to create an independent copy.
522
                         * \throws std::runtime_error if BIGNUM duplication fails
523
                         */
524
                        explicit bignum_handle(const BIGNUM* bn) {
347✔
525
                                if (bn == nullptr) throw std::runtime_error("BIGNUM pointer is null");
347✔
526
                                m_bn = bn;
346✔
527
                                if (m_bn == nullptr) throw std::runtime_error("BN_dup failed");
346✔
528
                                bn = nullptr; // Prevent the caller from accidentally using the original pointer after ownership transfer
346✔
529
                        }
346✔
530

531
                        /**
532
                         * \brief Copy constructor. Creates an independent copy of the BIGNUM.
533
                         * \throws std::runtime_error if BIGNUM duplication fails
534
                         */
535
                        bignum_handle(const bignum_handle& other) {
102✔
536
                                if (other.m_bn == nullptr) throw std::runtime_error("BIGNUM is null");
102✔
537
                                m_bn = BN_dup(other.m_bn);
102✔
538
                                if (m_bn == nullptr) throw std::runtime_error("BN_dup failed");
102✔
539
                        }
102✔
540

541
                        /**
542
                         * \brief Move constructor. Steals ownership from other.
543
                         */
544
                        bignum_handle(bignum_handle&& other) noexcept : m_bn{other.m_bn} { other.m_bn = nullptr; }
86✔
545

546
                        /**
547
                         * \brief Destructor. Frees the BIGNUM.
548
                         */
549
                        ~bignum_handle() noexcept {
545✔
550
                                if (m_bn != nullptr) BN_free(const_cast<BIGNUM*>(m_bn));
545✔
551
                        }
545✔
552

553
                        /**
554
                         * \brief Copy assignment. Creates an independent copy of the BIGNUM.
555
                         * \throws std::runtime_error if BIGNUM duplication fails
556
                         */
557
                        bignum_handle& operator=(const bignum_handle& other) {
558
                                if (m_bn != nullptr) BN_free(const_cast<BIGNUM*>(m_bn));
559
                                if (other.m_bn == nullptr) throw std::runtime_error("BIGNUM is null");
560
                                m_bn = BN_dup(other.m_bn);
561
                                if (m_bn == nullptr) throw std::runtime_error("BN_dup failed");
562
                                return *this;
563
                        }
564

565
                        /**
566
                         * \brief Move assignment. Steals ownership from other.
567
                         */
568
                        bignum_handle& operator=(bignum_handle&& other) noexcept {
569
                                if (m_bn != nullptr) BN_free(const_cast<BIGNUM*>(m_bn));
570
                                m_bn = other.m_bn;
571
                                other.m_bn = nullptr;
572
                                return *this;
573
                        }
574

575
                        /**
576
                         * \brief Get the underlying BIGNUM pointer.
577
                         */
578
                        const BIGNUM* get() const noexcept { return m_bn; }
270✔
579

580
                        /**
581
                         * \brief Take ownership of the underlying BIGNUM pointer.
582
                         */
583
                        const BIGNUM* release() noexcept {
201✔
584
                                const BIGNUM* temp = m_bn;
201✔
585
                                m_bn = nullptr;
201✔
586
                                return temp;
201✔
587
                        }
588

589
                        /**
590
                         * \brief Check if the handle is null.
591
                         */
592
                        bool operator!() const noexcept { return m_bn == nullptr; }
3✔
593

594
                        /**
595
                         * \brief Check if the handle is non-null.
596
                         */
597
                        explicit operator bool() const noexcept { return m_bn != nullptr; }
1✔
598

599
                private:
600
                        const BIGNUM* m_bn{nullptr};
601
                };
602

603
                inline std::unique_ptr<BIO, decltype(&BIO_free_all)> make_mem_buf_bio() {
482✔
604
                        return std::unique_ptr<BIO, decltype(&BIO_free_all)>(BIO_new(BIO_s_mem()), BIO_free_all);
482✔
605
                }
606

607
                inline std::unique_ptr<BIO, decltype(&BIO_free_all)> make_mem_buf_bio(const std::string& data) {
33✔
608
                        return std::unique_ptr<BIO, decltype(&BIO_free_all)>(
609
#if OPENSSL_VERSION_NUMBER <= 0x10100003L
610
                                BIO_new_mem_buf(const_cast<char*>(data.data()), static_cast<int>(data.size())), BIO_free_all
611
#else
612
                                BIO_new_mem_buf(data.data(), static_cast<int>(data.size())), BIO_free_all
66✔
613
#endif
614
                        );
66✔
615
                }
616

617
                template<typename error_category = error::rsa_error>
618
                std::string write_bio_to_string(std::unique_ptr<BIO, decltype(&BIO_free_all)>& bio_out, std::error_code& ec) {
30✔
619
                        char* ptr = nullptr;
30✔
620
                        auto len = BIO_get_mem_data(bio_out.get(), &ptr);
30✔
621
                        if (len <= 0 || ptr == nullptr) {
30✔
622
                                ec = error_category::convert_to_pem_failed;
13✔
623
                                return {};
13✔
624
                        }
625
                        return {ptr, static_cast<size_t>(len)};
51✔
626
                }
627

628
                inline std::unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX*)> make_evp_md_ctx() {
364✔
629
                        return
630
#ifdef JWT_OPENSSL_1_0_0
631
                                std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_destroy)>(EVP_MD_CTX_create(), &EVP_MD_CTX_destroy);
632
#else
633
                                std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(EVP_MD_CTX_new(), &EVP_MD_CTX_free);
364✔
634
#endif
635
                }
636

637
                /**
638
                 * \brief Extract the public key of a pem certificate
639
                 *
640
                 * \tparam error_category        jwt::error enum category to match with the keys being used
641
                 * \param certstr                        String containing the certificate encoded as pem
642
                 * \param pw                                Password used to decrypt certificate (leave empty if not encrypted)
643
                 * \param ec                                error_code for error_detection (gets cleared if no error occurred)
644
                 */
645
                template<typename error_category = error::rsa_error>
646
                std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw, std::error_code& ec) {
33✔
647
                        ec.clear();
33✔
648
                        auto certbio = make_mem_buf_bio(certstr);
33✔
649
                        auto keybio = make_mem_buf_bio();
33✔
650
                        if (!certbio || !keybio) {
33✔
651
                                ec = error_category::create_mem_bio_failed;
4✔
652
                                return {};
4✔
653
                        }
654

655
                        std::unique_ptr<X509, decltype(&X509_free)> cert(
29✔
656
                                PEM_read_bio_X509(certbio.get(), nullptr, nullptr, const_cast<char*>(pw.c_str())), X509_free);
29✔
657
                        if (!cert) {
29✔
658
                                ec = error_category::cert_load_failed;
4✔
659
                                return {};
4✔
660
                        }
661
                        std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> key(X509_get_pubkey(cert.get()), EVP_PKEY_free);
25✔
662
                        if (!key) {
25✔
663
                                ec = error_category::get_key_failed;
4✔
664
                                return {};
4✔
665
                        }
666
                        if (PEM_write_bio_PUBKEY(keybio.get(), key.get()) == 0) {
21✔
667
                                ec = error_category::write_key_failed;
4✔
668
                                return {};
4✔
669
                        }
670

671
                        return write_bio_to_string<error_category>(keybio, ec);
17✔
672
                }
33✔
673

674
                /**
675
                 * \brief Extract the public key of a pem certificate
676
                 *
677
                 * \tparam error_category        jwt::error enum category to match with the keys being used
678
                 * \param certstr                        String containing the certificate encoded as pem
679
                 * \param pw                                Password used to decrypt certificate (leave empty if not encrypted)
680
                 * \throw                                        templated error_category's type exception if an error occurred
681
                 */
682
                template<typename error_category = error::rsa_error>
683
                std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw = "") {
6✔
684
                        std::error_code ec;
6✔
685
                        auto res = extract_pubkey_from_cert<error_category>(certstr, pw, ec);
6✔
686
                        error::throw_if_error(ec);
6✔
687
                        return res;
2✔
688
                }
5✔
689

690
                /**
691
                 * \brief Convert the certificate provided as DER to PEM.
692
                 *
693
                 * \param cert_der_str         String containing the certificate encoded as base64 DER
694
                 * \param ec                        error_code for error_detection (gets cleared if no error occurs)
695
                 */
696
                inline std::string convert_der_to_pem(const std::string& cert_der_str, std::error_code& ec) {
13✔
697
                        ec.clear();
13✔
698

699
                        auto c_str = reinterpret_cast<const unsigned char*>(cert_der_str.c_str());
13✔
700

701
                        std::unique_ptr<X509, decltype(&X509_free)> cert(
702
                                d2i_X509(NULL, &c_str, static_cast<int>(cert_der_str.size())), X509_free);
13✔
703
                        auto certbio = make_mem_buf_bio();
13✔
704
                        if (!cert || !certbio) {
13✔
705
                                ec = error::rsa_error::create_mem_bio_failed;
3✔
706
                                return {};
3✔
707
                        }
708

709
                        if (!PEM_write_bio_X509(certbio.get(), cert.get())) {
10✔
710
                                ec = error::rsa_error::write_cert_failed;
3✔
711
                                return {};
3✔
712
                        }
713

714
                        return write_bio_to_string(certbio, ec);
7✔
715
                }
13✔
716

717
                /**
718
                 * \brief Convert the certificate provided as base64 DER to PEM.
719
                 *
720
                 * This is useful when using with JWKs as x5c claim is encoded as base64 DER. More info
721
                 * [here](https://tools.ietf.org/html/rfc7517#section-4.7).
722
                 *
723
                 * \tparam Decode is callable, taking a string_type and returns a string_type.
724
                 * It should ensure the padding of the input and then base64 decode and return
725
                 * the results.
726
                 *
727
                 * \param cert_base64_der_str         String containing the certificate encoded as base64 DER
728
                 * \param decode                                 The function to decode the cert
729
                 * \param ec                                        error_code for error_detection (gets cleared if no error occurs)
730
                 */
731
                template<typename Decode>
732
                std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str, Decode decode,
12✔
733
                                                                                          std::error_code& ec) {
734
                        ec.clear();
12✔
735
                        const auto decoded_str = decode(cert_base64_der_str);
12✔
736
                        return convert_der_to_pem(decoded_str, ec);
24✔
737
                }
12✔
738

739
                /**
740
                 * \brief Convert the certificate provided as base64 DER to PEM.
741
                 *
742
                 * This is useful when using with JWKs as x5c claim is encoded as base64 DER. More info
743
                 * [here](https://tools.ietf.org/html/rfc7517#section-4.7)
744
                 *
745
                 * \tparam Decode is callable, taking a string_type and returns a string_type.
746
                 * It should ensure the padding of the input and then base64 decode and return
747
                 * the results.
748
                 *
749
                 * \param cert_base64_der_str         String containing the certificate encoded as base64 DER
750
                 * \param decode                                 The function to decode the cert
751
                 * \throw                                                rsa_exception if an error occurred
752
                 */
753
                template<typename Decode>
754
                std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str, Decode decode) {
755
                        std::error_code ec;
756
                        auto res = convert_base64_der_to_pem(cert_base64_der_str, std::move(decode), ec);
757
                        error::throw_if_error(ec);
758
                        return res;
759
                }
760

761
                /**
762
                 * \brief Convert the certificate provided as DER to PEM.
763
                 *
764
                 * \param cert_der_str         String containing the DER certificate
765
                 * \throw                                rsa_exception if an error occurred
766
                 */
767
                inline std::string convert_der_to_pem(const std::string& cert_der_str) {
1✔
768
                        std::error_code ec;
1✔
769
                        auto res = convert_der_to_pem(cert_der_str, ec);
1✔
770
                        error::throw_if_error(ec);
1✔
771
                        return res;
2✔
772
                }
×
773

774
#ifndef JWT_DISABLE_BASE64
775
                /**
776
                 * \brief Convert the certificate provided as base64 DER to PEM.
777
                 *
778
                 * This is useful when using with JWKs as x5c claim is encoded as base64 DER. More info
779
                 * [here](https://tools.ietf.org/html/rfc7517#section-4.7)
780
                 *
781
                 * \param cert_base64_der_str         String containing the certificate encoded as base64 DER
782
                 * \param ec                                        error_code for error_detection (gets cleared if no error occurs)
783
                 */
784
                inline std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str, std::error_code& ec) {
12✔
785
                        auto decode = [](const std::string& token) {
12✔
786
                                return base::decode<alphabet::base64>(base::pad<alphabet::base64>(token));
12✔
787
                        };
788
                        return convert_base64_der_to_pem(cert_base64_der_str, std::move(decode), ec);
24✔
789
                }
790

791
                /**
792
                 * \brief Convert the certificate provided as base64 DER to PEM.
793
                 *
794
                 * This is useful when using with JWKs as x5c claim is encoded as base64 DER. More info
795
                 * [here](https://tools.ietf.org/html/rfc7517#section-4.7)
796
                 *
797
                 * \param cert_base64_der_str         String containing the certificate encoded as base64 DER
798
                 * \throw                                                rsa_exception if an error occurred
799
                 */
800
                inline std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str) {
7✔
801
                        std::error_code ec;
7✔
802
                        auto res = convert_base64_der_to_pem(cert_base64_der_str, ec);
7✔
803
                        error::throw_if_error(ec);
7✔
804
                        return res;
2✔
805
                }
6✔
806
#endif
807
                /**
808
                 * \brief Load a public key from a string.
809
                 *
810
                 * The string should contain a pem encoded certificate or public key
811
                 *
812
                 * \tparam error_category        jwt::error enum category to match with the keys being used
813
                 * \param key                String containing the certificate encoded as pem
814
                 * \param password        Password used to decrypt certificate (leave empty if not encrypted)
815
                 * \param ec                error_code for error_detection (gets cleared if no error occurs)
816
                 */
817
                template<typename error_category = error::rsa_error>
818
                evp_pkey_handle load_public_key_from_string(const std::string& key, const std::string& password,
258✔
819
                                                                                                        std::error_code& ec) {
820
                        ec.clear();
258✔
821
                        auto pubkey_bio = make_mem_buf_bio();
258✔
822
                        if (!pubkey_bio) {
258✔
823
                                ec = error_category::create_mem_bio_failed;
8✔
824
                                return {};
8✔
825
                        }
826
                        if (key.substr(0, 27) == "-----BEGIN CERTIFICATE-----") {
250✔
827
                                auto epkey = helper::extract_pubkey_from_cert<error_category>(key, password, ec);
21✔
828
                                if (ec) return {};
21✔
829
                                // Ensure the size fits into an int before casting
830
                                if (epkey.size() > static_cast<std::size_t>((std::numeric_limits<int>::max)())) {
9✔
831
                                        ec = error_category::load_key_bio_write; // Add an appropriate error here
×
832
                                        return {};
×
833
                                }
834
                                int len = static_cast<int>(epkey.size());
9✔
835
                                if (BIO_write(pubkey_bio.get(), epkey.data(), len) != len) {
9✔
836
                                        ec = error_category::load_key_bio_write;
4✔
837
                                        return {};
4✔
838
                                }
839
                        } else {
21✔
840
                                // Ensure the size fits into an int before casting
841
                                if (key.size() > static_cast<std::size_t>((std::numeric_limits<int>::max)())) {
229✔
842
                                        ec = error_category::load_key_bio_write; // Add an appropriate error here
×
843
                                        return {};
×
844
                                }
845
                                int len = static_cast<int>(key.size());
229✔
846
                                if (BIO_write(pubkey_bio.get(), key.data(), len) != len) {
229✔
847
                                        ec = error_category::load_key_bio_write;
4✔
848
                                        return {};
4✔
849
                                }
850
                        }
851

852
                        evp_pkey_handle pkey(PEM_read_bio_PUBKEY(
230✔
853
                                pubkey_bio.get(), nullptr, nullptr,
854
                                (void*)password.data())); // NOLINT(google-readability-casting) requires `const_cast`
230✔
855
                        if (!pkey) ec = error_category::load_key_bio_read;
230✔
856
                        return pkey;
230✔
857
                }
258✔
858

859
                /**
860
                 * \brief Load a public key from a string.
861
                 *
862
                 * The string should contain a pem encoded certificate or public key
863
                 *
864
                 * \tparam error_category        jwt::error enum category to match with the keys being used
865
                 * \param key                                String containing the certificate encoded as pem
866
                 * \param password                        Password used to decrypt certificate (leave empty if not encrypted)
867
                 * \throw                                        Templated error_category's type exception if an error occurred
868
                 */
869
                template<typename error_category = error::rsa_error>
870
                inline evp_pkey_handle load_public_key_from_string(const std::string& key, const std::string& password = "") {
118✔
871
                        std::error_code ec;
118✔
872
                        auto res = load_public_key_from_string<error_category>(key, password, ec);
118✔
873
                        error::throw_if_error(ec);
118✔
874
                        return res;
200✔
875
                }
18✔
876

877
                /**
878
                 * \brief Load a private key from a string.
879
                 *
880
                 * \tparam error_category        jwt::error enum category to match with the keys being used
881
                 * \param key                                String containing a private key as pem
882
                 * \param password                        Password used to decrypt key (leave empty if not encrypted)
883
                 * \param ec                                error_code for error_detection (gets cleared if no error occurs)
884
                 */
885
                template<typename error_category = error::rsa_error>
886
                inline evp_pkey_handle load_private_key_from_string(const std::string& key, const std::string& password,
164✔
887
                                                                                                                        std::error_code& ec) {
888
                        ec.clear();
164✔
889
                        auto private_key_bio = make_mem_buf_bio();
164✔
890
                        if (!private_key_bio) {
164✔
891
                                ec = error_category::create_mem_bio_failed;
5✔
892
                                return {};
5✔
893
                        }
894
                        const int len = static_cast<int>(key.size());
159✔
895
                        if (BIO_write(private_key_bio.get(), key.data(), len) != len) {
159✔
896
                                ec = error_category::load_key_bio_write;
5✔
897
                                return {};
5✔
898
                        }
899
                        evp_pkey_handle pkey(
154✔
900
                                PEM_read_bio_PrivateKey(private_key_bio.get(), nullptr, nullptr, const_cast<char*>(password.c_str())));
154✔
901
                        if (!pkey) ec = error_category::load_key_bio_read;
154✔
902
                        return pkey;
154✔
903
                }
164✔
904

905
                /**
906
                 * \brief Load a private key from a string.
907
                 *
908
                 * \tparam error_category        jwt::error enum category to match with the keys being used
909
                 * \param key                                String containing a private key as pem
910
                 * \param password                        Password used to decrypt key (leave empty if not encrypted)
911
                 * \throw                                        Templated error_category's type exception if an error occurred
912
                 */
913
                template<typename error_category = error::rsa_error>
914
                inline evp_pkey_handle load_private_key_from_string(const std::string& key, const std::string& password = "") {
108✔
915
                        std::error_code ec;
108✔
916
                        auto res = load_private_key_from_string<error_category>(key, password, ec);
108✔
917
                        error::throw_if_error(ec);
108✔
918
                        return res;
200✔
919
                }
8✔
920

921
                /**
922
                 * \brief Load a public key from a string.
923
                 *
924
                 * The string should contain a pem encoded certificate or public key
925
                 *
926
                 * \deprecated Use the templated version helper::load_private_key_from_string with error::ecdsa_error
927
                 *
928
                 * \param key                String containing the certificate encoded as pem
929
                 * \param password        Password used to decrypt certificate (leave empty if not encrypted)
930
                 * \param ec                error_code for error_detection (gets cleared if no error occurs)
931
                 */
932
                inline evp_pkey_handle load_public_ec_key_from_string(const std::string& key, const std::string& password,
933
                                                                                                                          std::error_code& ec) {
934
                        return load_public_key_from_string<error::ecdsa_error>(key, password, ec);
935
                }
936

937
                /**
938
                 * Convert a OpenSSL BIGNUM to a std::string
939
                 * \param bn BIGNUM to convert
940
                 * \return bignum as string
941
                 */
942
                inline
943
#ifdef JWT_OPENSSL_1_0_0
944
                        std::string
945
                        bn2raw(BIGNUM* bn)
946
#else
947
                        std::string
948
                        bn2raw(const BIGNUM* bn)
50✔
949
#endif
950
                {
951
                        std::string res(BN_num_bytes(bn), '\0');
50✔
952
                        BN_bn2bin(bn, (unsigned char*)res.data()); // NOLINT(google-readability-casting) requires `const_cast`
50✔
953
                        return res;
50✔
954
                }
×
955
                /**
956
                 * Convert an std::string to a OpenSSL BIGNUM
957
                 * \param raw String to convert
958
                 * \param ec  error_code for error_detection (gets cleared if no error occurs)
959
                 * \return BIGNUM representation
960
                 */
961
                inline bignum_handle raw2bn(const std::string& raw, std::error_code& ec) {
323✔
962
                        auto bn =
963
                                BN_bin2bn(reinterpret_cast<const unsigned char*>(raw.data()), static_cast<int>(raw.size()), nullptr);
323✔
964
                        // https://www.openssl.org/docs/man1.1.1/man3/BN_bin2bn.html#RETURN-VALUES
965
                        if (!bn) {
323✔
966
                                ec = error::rsa_error::set_rsa_failed;
×
NEW
967
                                return bignum_handle(nullptr);
×
968
                        }
969
                        return bignum_handle(bn);
323✔
970
                }
971
                /**
972
                 * Convert an std::string to a OpenSSL BIGNUM
973
                 * \param raw String to convert
974
                 * \return BIGNUM representation
975
                 */
976
                inline bignum_handle raw2bn(const std::string& raw) {
89✔
977
                        std::error_code ec;
89✔
978
                        auto res = raw2bn(raw, ec);
89✔
979
                        error::throw_if_error(ec);
89✔
980
                        return res;
178✔
UNCOV
981
                }
×
982

983
                /**
984
                 * \brief Load a public key from a string.
985
                 *
986
                 * The string should contain a pem encoded certificate or public key
987
                 *
988
                 * \deprecated Use the templated version helper::load_private_key_from_string with error::ecdsa_error
989
                 *
990
                 * \param key                String containing the certificate or key encoded as pem
991
                 * \param password        Password used to decrypt certificate or key (leave empty if not encrypted)
992
                 * \throw                        ecdsa_exception if an error occurred
993
                 */
994
                inline evp_pkey_handle load_public_ec_key_from_string(const std::string& key,
133✔
995
                                                                                                                          const std::string& password = "") {
996
                        std::error_code ec;
133✔
997
                        auto res = load_public_key_from_string<error::ecdsa_error>(key, password, ec);
133✔
998
                        error::throw_if_error(ec);
133✔
999
                        return res;
244✔
1000
                }
11✔
1001

1002
                /**
1003
                 * \brief Load a private key from a string.
1004
                 *
1005
                 * \deprecated Use the templated version helper::load_private_key_from_string with error::ecdsa_error
1006
                 *
1007
                 * \param key                String containing a private key as pem
1008
                 * \param password        Password used to decrypt key (leave empty if not encrypted)
1009
                 * \param ec                error_code for error_detection (gets cleared if no error occurs)
1010
                 */
1011
                inline evp_pkey_handle load_private_ec_key_from_string(const std::string& key, const std::string& password,
1012
                                                                                                                           std::error_code& ec) {
1013
                        return load_private_key_from_string<error::ecdsa_error>(key, password, ec);
1014
                }
1015

1016
                /**
1017
                * \brief create public key from modulus and exponent. This is defined in
1018
                * [RFC 7518 Section 6.3](https://www.rfc-editor.org/rfc/rfc7518#section-6.3)
1019
                * Using the required "n" (Modulus) Parameter and "e" (Exponent) Parameter.
1020
                *
1021
                 * \tparam Decode is callable, taking a string_type and returns a string_type.
1022
                 * It should ensure the padding of the input and then base64url decode and
1023
                 * return the results.
1024
                * \param modulus        string containing base64url encoded modulus
1025
                * \param exponent        string containing base64url encoded exponent
1026
                * \param decode         The function to decode the RSA parameters
1027
                * \param ec                        error_code for error_detection (gets cleared if no error occur
1028
                * \return                         public key in PEM format
1029
                */
1030
                template<typename Decode>
1031
                std::string create_public_key_from_rsa_components(const std::string& modulus, const std::string& exponent,
19✔
1032
                                                                                                                  Decode decode, std::error_code& ec) {
1033
                        ec.clear();
19✔
1034
                        auto decoded_modulus = decode(modulus);
19✔
1035
                        auto decoded_exponent = decode(exponent);
19✔
1036

1037
                        auto n = helper::raw2bn(decoded_modulus, ec);
19✔
1038
                        if (ec) return {};
19✔
1039
                        auto e = helper::raw2bn(decoded_exponent, ec);
19✔
1040
                        if (ec) return {};
19✔
1041

1042
#if defined(JWT_OPENSSL_3_0)
1043
                        // OpenSSL deprecated mutable keys and there is a new way for making them
1044
                        // https://mta.openssl.org/pipermail/openssl-users/2021-July/013994.html
1045
                        // https://www.openssl.org/docs/man3.1/man3/OSSL_PARAM_BLD_new.html#Example-2
1046
                        std::unique_ptr<OSSL_PARAM_BLD, decltype(&OSSL_PARAM_BLD_free)> param_bld(OSSL_PARAM_BLD_new(),
19✔
1047
                                                                                                                                                                          OSSL_PARAM_BLD_free);
19✔
1048
                        if (!param_bld) {
19✔
1049
                                ec = error::rsa_error::create_context_failed;
2✔
1050
                                return {};
2✔
1051
                        }
1052

1053
                        if (OSSL_PARAM_BLD_push_BN(param_bld.get(), "n", n.get()) != 1 ||
32✔
1054
                                OSSL_PARAM_BLD_push_BN(param_bld.get(), "e", e.get()) != 1) {
15✔
1055
                                ec = error::rsa_error::set_rsa_failed;
2✔
1056
                                return {};
2✔
1057
                        }
1058

1059
                        std::unique_ptr<OSSL_PARAM, decltype(&OSSL_PARAM_free)> params(OSSL_PARAM_BLD_to_param(param_bld.get()),
15✔
1060
                                                                                                                                                   OSSL_PARAM_free);
15✔
1061
                        if (!params) {
15✔
1062
                                ec = error::rsa_error::set_rsa_failed;
2✔
1063
                                return {};
2✔
1064
                        }
1065

1066
                        std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> ctx(
13✔
1067
                                EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr), EVP_PKEY_CTX_free);
13✔
1068
                        if (!ctx) {
13✔
1069
                                ec = error::rsa_error::create_context_failed;
2✔
1070
                                return {};
2✔
1071
                        }
1072

1073
                        // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html#EXAMPLES
1074
                        // Error codes based on https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_fromdata_init.html#RETURN-VALUES
1075
                        EVP_PKEY* pkey = NULL;
11✔
1076
                        if (EVP_PKEY_fromdata_init(ctx.get()) <= 0 ||
20✔
1077
                                EVP_PKEY_fromdata(ctx.get(), &pkey, EVP_PKEY_KEYPAIR, params.get()) <= 0) {
9✔
1078
                                // It's unclear if this can fail after allocating but free it anyways
1079
                                // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html
1080
                                EVP_PKEY_free(pkey);
4✔
1081

1082
                                ec = error::rsa_error::cert_load_failed;
4✔
1083
                                return {};
4✔
1084
                        }
1085

1086
                        // Transfer ownership so we get ref counter and cleanup
1087
                        evp_pkey_handle rsa(pkey);
7✔
1088

1089
#else
1090
                        std::unique_ptr<RSA, decltype(&RSA_free)> rsa(RSA_new(), RSA_free);
1091

1092
#if defined(JWT_OPENSSL_1_1_1) || defined(JWT_OPENSSL_1_1_0)
1093
                        // After this RSA_free will also free the n and e big numbers
1094
                        // See https://github.com/Thalhammer/jwt-cpp/pull/298#discussion_r1282619186
1095
                        if (RSA_set0_key(rsa.get(), const_cast<BIGNUM*>(n.get()), const_cast<BIGNUM*>(e.get()), nullptr) == 1) {
1096
                                // This can only fail we passed in NULL for `n` or `e`
1097
                                // https://github.com/openssl/openssl/blob/d6e4056805f54bb1a0ef41fa3a6a35b70c94edba/crypto/rsa/rsa_lib.c#L396
1098
                                // So to make sure there is no memory leak, we hold the references
1099
                                n.release();
1100
                                e.release();
1101
                        } else {
1102
                                ec = error::rsa_error::set_rsa_failed;
1103
                                return {};
1104
                        }
1105
#elif defined(JWT_OPENSSL_1_0_0)
1106
                        rsa->e = e.release();
1107
                        rsa->n = n.release();
1108
                        rsa->d = nullptr;
1109
#endif
1110
#endif
1111

1112
                        auto pub_key_bio = make_mem_buf_bio();
7✔
1113
                        if (!pub_key_bio) {
7✔
1114
                                ec = error::rsa_error::create_mem_bio_failed;
2✔
1115
                                return {};
2✔
1116
                        }
1117

1118
                        auto write_pem_to_bio =
5✔
1119
#if defined(JWT_OPENSSL_3_0)
1120
                                // https://www.openssl.org/docs/man3.1/man3/PEM_write_bio_RSA_PUBKEY.html
1121
                                &PEM_write_bio_PUBKEY;
1122
#else
1123
                                &PEM_write_bio_RSA_PUBKEY;
1124
#endif
1125
                        if (write_pem_to_bio(pub_key_bio.get(), rsa.get()) != 1) {
5✔
1126
                                ec = error::rsa_error::load_key_bio_write;
2✔
1127
                                return {};
2✔
1128
                        }
1129

1130
                        return write_bio_to_string<error::rsa_error>(pub_key_bio, ec);
3✔
1131
                }
19✔
1132

1133
                /**
1134
                * Create public key from modulus and exponent. This is defined in
1135
                * [RFC 7518 Section 6.3](https://www.rfc-editor.org/rfc/rfc7518#section-6.3)
1136
                * Using the required "n" (Modulus) Parameter and "e" (Exponent) Parameter.
1137
                *
1138
                 * \tparam Decode is callable, taking a string_type and returns a string_type.
1139
                 * It should ensure the padding of the input and then base64url decode and
1140
                 * return the results.
1141
                * \param modulus        string containing base64url encoded modulus
1142
                * \param exponent        string containing base64url encoded exponent
1143
                * \param decode         The function to decode the RSA parameters
1144
                * \return public key in PEM format
1145
                */
1146
                template<typename Decode>
1147
                std::string create_public_key_from_rsa_components(const std::string& modulus, const std::string& exponent,
1148
                                                                                                                  Decode decode) {
1149
                        std::error_code ec;
1150
                        auto res = create_public_key_from_rsa_components(modulus, exponent, decode, ec);
1151
                        error::throw_if_error(ec);
1152
                        return res;
1153
                }
1154

1155
#ifndef JWT_DISABLE_BASE64
1156
                /**
1157
                * Create public key from modulus and exponent. This is defined in
1158
                * [RFC 7518 Section 6.3](https://www.rfc-editor.org/rfc/rfc7518#section-6.3)
1159
                * Using the required "n" (Modulus) Parameter and "e" (Exponent) Parameter.
1160
                *
1161
                * \param modulus        string containing base64 encoded modulus
1162
                * \param exponent        string containing base64 encoded exponent
1163
                * \param ec                        error_code for error_detection (gets cleared if no error occur
1164
                * \return public key in PEM format
1165
                */
1166
                inline std::string create_public_key_from_rsa_components(const std::string& modulus,
19✔
1167
                                                                                                                                 const std::string& exponent, std::error_code& ec) {
1168
                        auto decode = [](const std::string& token) {
38✔
1169
                                return base::decode<alphabet::base64url>(base::pad<alphabet::base64url>(token));
38✔
1170
                        };
1171
                        return create_public_key_from_rsa_components(modulus, exponent, std::move(decode), ec);
38✔
1172
                }
1173
                /**
1174
                * Create public key from modulus and exponent. This is defined in
1175
                * [RFC 7518 Section 6.3](https://www.rfc-editor.org/rfc/rfc7518#section-6.3)
1176
                * Using the required "n" (Modulus) Parameter and "e" (Exponent) Parameter.
1177
                *
1178
                * \param modulus        string containing base64url encoded modulus
1179
                * \param exponent        string containing base64url encoded exponent
1180
                * \return public key in PEM format
1181
                */
1182
                inline std::string create_public_key_from_rsa_components(const std::string& modulus,
10✔
1183
                                                                                                                                 const std::string& exponent) {
1184
                        std::error_code ec;
10✔
1185
                        auto res = create_public_key_from_rsa_components(modulus, exponent, ec);
10✔
1186
                        error::throw_if_error(ec);
10✔
1187
                        return res;
2✔
1188
                }
9✔
1189
#endif
1190
                /**
1191
                 * \brief Load a private key from a string.
1192
                 *
1193
                 * \deprecated Use the templated version helper::load_private_key_from_string with error::ecdsa_error
1194
                 *
1195
                 * \param key                String containing a private key as pem
1196
                 * \param password        Password used to decrypt key (leave empty if not encrypted)
1197
                 * \throw                        ecdsa_exception if an error occurred
1198
                 */
1199
                inline evp_pkey_handle load_private_ec_key_from_string(const std::string& key,
53✔
1200
                                                                                                                           const std::string& password = "") {
1201
                        std::error_code ec;
53✔
1202
                        auto res = load_private_key_from_string<error::ecdsa_error>(key, password, ec);
53✔
1203
                        error::throw_if_error(ec);
53✔
1204
                        return res;
100✔
1205
                }
3✔
1206

1207
#if defined(JWT_OPENSSL_3_0)
1208

1209
                /**
1210
                 * \brief Convert a curve name to a group name.
1211
                 *
1212
                 * \param curve        string containing curve name
1213
                 * \param ec        error_code for error_detection
1214
                 * \return                 group name
1215
                 */
1216
                inline std::string curve2group(const std::string curve, std::error_code& ec) {
19✔
1217
                        if (curve == "P-256") {
19✔
1218
                                return "prime256v1";
×
1219
                        } else if (curve == "P-384") {
19✔
1220
                                return "secp384r1";
38✔
1221
                        } else if (curve == "P-521") {
×
1222
                                return "secp521r1";
×
1223
                        } else {
1224
                                ec = jwt::error::ecdsa_error::unknown_curve;
×
1225
                                return {};
×
1226
                        }
1227
                }
1228

1229
#else
1230

1231
                /**
1232
                 * \brief Convert a curve name to an ID.
1233
                 *
1234
                 * \param curve        string containing curve name
1235
                 * \param ec        error_code for error_detection
1236
                 * \return                 ID
1237
                 */
1238
                inline int curve2nid(const std::string curve, std::error_code& ec) {
1239
                        if (curve == "P-256") {
1240
                                return NID_X9_62_prime256v1;
1241
                        } else if (curve == "P-384") {
1242
                                return NID_secp384r1;
1243
                        } else if (curve == "P-521") {
1244
                                return NID_secp521r1;
1245
                        } else {
1246
                                ec = jwt::error::ecdsa_error::unknown_curve;
1247
                                return {};
1248
                        }
1249
                }
1250

1251
#endif
1252

1253
                /**
1254
                 * Create public key from curve name and coordinates. This is defined in
1255
                 * [RFC 7518 Section 6.2](https://www.rfc-editor.org/rfc/rfc7518#section-6.2)
1256
                 * Using the required "crv" (Curve), "x" (X Coordinate) and "y" (Y Coordinate) Parameters.
1257
                 *
1258
                 * \tparam Decode is callable, taking a string_type and returns a string_type.
1259
                 * It should ensure the padding of the input and then base64url decode and
1260
                 * return the results.
1261
                 * \param curve        string containing curve name
1262
                 * \param x                string containing base64url encoded x coordinate
1263
                 * \param y                string containing base64url encoded y coordinate
1264
                 * \param decode        The function to decode the RSA parameters
1265
                 * \param ec                error_code for error_detection (gets cleared if no error occur
1266
                 * \return                 public key in PEM format
1267
                 */
1268
                template<typename Decode>
1269
                std::string create_public_key_from_ec_components(const std::string& curve, const std::string& x,
21✔
1270
                                                                                                                 const std::string& y, Decode decode, std::error_code& ec) {
1271
                        ec.clear();
21✔
1272
                        auto decoded_x = decode(x);
21✔
1273
                        auto decoded_y = decode(y);
21✔
1274

1275
#if defined(JWT_OPENSSL_3_0)
1276
                        // OpenSSL deprecated mutable keys and there is a new way for making them
1277
                        // https://mta.openssl.org/pipermail/openssl-users/2021-July/013994.html
1278
                        // https://www.openssl.org/docs/man3.1/man3/OSSL_PARAM_BLD_new.html#Example-2
1279
                        std::unique_ptr<OSSL_PARAM_BLD, decltype(&OSSL_PARAM_BLD_free)> param_bld(OSSL_PARAM_BLD_new(),
21✔
1280
                                                                                                                                                                          OSSL_PARAM_BLD_free);
21✔
1281
                        if (!param_bld) {
21✔
1282
                                ec = error::ecdsa_error::create_context_failed;
2✔
1283
                                return {};
2✔
1284
                        }
1285

1286
                        std::string group = helper::curve2group(curve, ec);
19✔
1287
                        if (ec) return {};
19✔
1288

1289
                        // https://github.com/openssl/openssl/issues/16270#issuecomment-895734092
1290
                        std::string pub = std::string("\x04").append(decoded_x).append(decoded_y);
19✔
1291

1292
                        if (OSSL_PARAM_BLD_push_utf8_string(param_bld.get(), "group", group.data(), group.size()) != 1 ||
36✔
1293
                                OSSL_PARAM_BLD_push_octet_string(param_bld.get(), "pub", pub.data(), pub.size()) != 1) {
17✔
1294
                                ec = error::ecdsa_error::set_ecdsa_failed;
4✔
1295
                                return {};
4✔
1296
                        }
1297

1298
                        std::unique_ptr<OSSL_PARAM, decltype(&OSSL_PARAM_free)> params(OSSL_PARAM_BLD_to_param(param_bld.get()),
15✔
1299
                                                                                                                                                   OSSL_PARAM_free);
15✔
1300
                        if (!params) {
15✔
1301
                                ec = error::ecdsa_error::set_ecdsa_failed;
2✔
1302
                                return {};
2✔
1303
                        }
1304

1305
                        std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> ctx(
13✔
1306
                                EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr), EVP_PKEY_CTX_free);
13✔
1307
                        if (!ctx) {
13✔
1308
                                ec = error::ecdsa_error::create_context_failed;
2✔
1309
                                return {};
2✔
1310
                        }
1311

1312
                        // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html#EXAMPLES
1313
                        // Error codes based on https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_fromdata_init.html#RETURN-VALUES
1314
                        EVP_PKEY* pkey = NULL;
11✔
1315
                        if (EVP_PKEY_fromdata_init(ctx.get()) <= 0 ||
20✔
1316
                                EVP_PKEY_fromdata(ctx.get(), &pkey, EVP_PKEY_KEYPAIR, params.get()) <= 0) {
9✔
1317
                                // It's unclear if this can fail after allocating but free it anyways
1318
                                // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html
1319
                                EVP_PKEY_free(pkey);
4✔
1320

1321
                                ec = error::ecdsa_error::cert_load_failed;
4✔
1322
                                return {};
4✔
1323
                        }
1324

1325
                        // Transfer ownership so we get ref counter and cleanup
1326
                        evp_pkey_handle ecdsa(pkey);
7✔
1327

1328
#else
1329
                        int nid = helper::curve2nid(curve, ec);
1330
                        if (ec) return {};
1331

1332
                        auto qx = helper::raw2bn(decoded_x, ec);
1333
                        if (ec) return {};
1334
                        auto qy = helper::raw2bn(decoded_y, ec);
1335
                        if (ec) return {};
1336

1337
                        std::unique_ptr<EC_GROUP, decltype(&EC_GROUP_free)> ecgroup(EC_GROUP_new_by_curve_name(nid), EC_GROUP_free);
1338
                        if (!ecgroup) {
1339
                                ec = error::ecdsa_error::set_ecdsa_failed;
1340
                                return {};
1341
                        }
1342

1343
                        EC_GROUP_set_asn1_flag(ecgroup.get(), OPENSSL_EC_NAMED_CURVE);
1344

1345
                        std::unique_ptr<EC_POINT, decltype(&EC_POINT_free)> ecpoint(EC_POINT_new(ecgroup.get()), EC_POINT_free);
1346
                        if (!ecpoint ||
1347
                                EC_POINT_set_affine_coordinates_GFp(ecgroup.get(), ecpoint.get(), qx.get(), qy.get(), nullptr) != 1) {
1348
                                ec = error::ecdsa_error::set_ecdsa_failed;
1349
                                return {};
1350
                        }
1351

1352
                        std::unique_ptr<EC_KEY, decltype(&EC_KEY_free)> ecdsa(EC_KEY_new(), EC_KEY_free);
1353
                        if (!ecdsa || EC_KEY_set_group(ecdsa.get(), ecgroup.get()) != 1 ||
1354
                                EC_KEY_set_public_key(ecdsa.get(), ecpoint.get()) != 1) {
1355
                                ec = error::ecdsa_error::set_ecdsa_failed;
1356
                                return {};
1357
                        }
1358

1359
#endif
1360

1361
                        auto pub_key_bio = make_mem_buf_bio();
7✔
1362
                        if (!pub_key_bio) {
7✔
1363
                                ec = error::ecdsa_error::create_mem_bio_failed;
2✔
1364
                                return {};
2✔
1365
                        }
1366

1367
                        auto write_pem_to_bio =
5✔
1368
#if defined(JWT_OPENSSL_3_0)
1369
                                // https://www.openssl.org/docs/man3.1/man3/PEM_write_bio_EC_PUBKEY.html
1370
                                &PEM_write_bio_PUBKEY;
1371
#else
1372
                                &PEM_write_bio_EC_PUBKEY;
1373
#endif
1374
                        if (write_pem_to_bio(pub_key_bio.get(), ecdsa.get()) != 1) {
5✔
1375
                                ec = error::ecdsa_error::load_key_bio_write;
2✔
1376
                                return {};
2✔
1377
                        }
1378

1379
                        return write_bio_to_string<error::ecdsa_error>(pub_key_bio, ec);
3✔
1380
                }
21✔
1381

1382
                /**
1383
                * Create public key from curve name and coordinates. This is defined in
1384
                * [RFC 7518 Section 6.2](https://www.rfc-editor.org/rfc/rfc7518#section-6.2)
1385
                * Using the required "crv" (Curve), "x" (X Coordinate) and "y" (Y Coordinate) Parameters.
1386
                *
1387
                 * \tparam Decode is callable, taking a string_type and returns a string_type.
1388
                 * It should ensure the padding of the input and then base64url decode and
1389
                 * return the results.
1390
                * \param curve        string containing curve name
1391
                * \param x                string containing base64url encoded x coordinate
1392
                * \param y                string containing base64url encoded y coordinate
1393
                * \param decode The function to decode the RSA parameters
1394
                * \return public key in PEM format
1395
                */
1396
                template<typename Decode>
1397
                std::string create_public_key_from_ec_components(const std::string& curve, const std::string& x,
1398
                                                                                                                 const std::string& y, Decode decode) {
1399
                        std::error_code ec;
1400
                        auto res = create_public_key_from_ec_components(curve, x, y, decode, ec);
1401
                        error::throw_if_error(ec);
1402
                        return res;
1403
                }
1404

1405
#ifndef JWT_DISABLE_BASE64
1406
                /**
1407
                * Create public key from curve name and coordinates. This is defined in
1408
                * [RFC 7518 Section 6.2](https://www.rfc-editor.org/rfc/rfc7518#section-6.2)
1409
                * Using the required "crv" (Curve), "x" (X Coordinate) and "y" (Y Coordinate) Parameters.
1410
                *
1411
                * \param curve        string containing curve name
1412
                * \param x                string containing base64url encoded x coordinate
1413
                * \param y                string containing base64url encoded y coordinate
1414
                * \param ec                error_code for error_detection (gets cleared if no error occur
1415
                * \return public key in PEM format
1416
                */
1417
                inline std::string create_public_key_from_ec_components(const std::string& curve, const std::string& x,
21✔
1418
                                                                                                                                const std::string& y, std::error_code& ec) {
1419
                        auto decode = [](const std::string& token) {
42✔
1420
                                return base::decode<alphabet::base64url>(base::pad<alphabet::base64url>(token));
42✔
1421
                        };
1422
                        return create_public_key_from_ec_components(curve, x, y, std::move(decode), ec);
42✔
1423
                }
1424
                /**
1425
                * Create public key from curve name and coordinates. This is defined in
1426
                * [RFC 7518 Section 6.2](https://www.rfc-editor.org/rfc/rfc7518#section-6.2)
1427
                * Using the required "crv" (Curve), "x" (X Coordinate) and "y" (Y Coordinate) Parameters.
1428
                *
1429
                * \param curve        string containing curve name
1430
                * \param x                string containing base64url encoded x coordinate
1431
                * \param y                string containing base64url encoded y coordinate
1432
                * \return public key in PEM format
1433
                */
1434
                inline std::string create_public_key_from_ec_components(const std::string& curve, const std::string& x,
11✔
1435
                                                                                                                                const std::string& y) {
1436
                        std::error_code ec;
11✔
1437
                        auto res = create_public_key_from_ec_components(curve, x, y, ec);
11✔
1438
                        error::throw_if_error(ec);
11✔
1439
                        return res;
2✔
1440
                }
10✔
1441
#endif
1442
        } // namespace helper
1443

1444
        /**
1445
         * \brief Various cryptographic algorithms when working with JWT
1446
         *
1447
         * JWT (JSON Web Tokens) signatures are typically used as the payload for a JWS (JSON Web Signature) or
1448
         * JWE (JSON Web Encryption). Both of these use various cryptographic as specified by
1449
         * [RFC7518](https://tools.ietf.org/html/rfc7518) and are exposed through the a [JOSE
1450
         * Header](https://tools.ietf.org/html/rfc7515#section-4) which points to one of the JWA [JSON Web
1451
         * Algorithms](https://tools.ietf.org/html/rfc7518#section-3.1)
1452
         */
1453
        namespace algorithm {
1454
                /**
1455
                 * \brief "none" algorithm.
1456
                 *
1457
                 * Returns and empty signature and checks if the given signature is empty.
1458
                 * See [RFC 7518 Section 3.6](https://datatracker.ietf.org/doc/html/rfc7518#section-3.6)
1459
                 * for more information.
1460
                 */
1461
                struct none {
1462
                        /**
1463
                         * \brief Return an empty string
1464
                         */
1465
                        std::string sign(const std::string& /*unused*/, std::error_code& ec) const {
83✔
1466
                                ec.clear();
83✔
1467
                                return {};
83✔
1468
                        }
1469
                        /**
1470
                         * \brief Check if the given signature is empty.
1471
                         *
1472
                         * JWT's with "none" algorithm should not contain a signature.
1473
                         * \param signature Signature data to verify
1474
                         * \param ec                error_code filled with details about the error
1475
                         */
1476
                        void verify(const std::string& /*unused*/, const std::string& signature, std::error_code& ec) const {
149✔
1477
                                ec.clear();
149✔
1478
                                if (!signature.empty()) { ec = error::signature_verification_error::invalid_signature; }
149✔
1479
                        }
149✔
1480
                        /// Get algorithm name
1481
                        std::string name() const { return "none"; }
570✔
1482
                };
1483
                /**
1484
                 * \brief Base class for HMAC family of algorithms
1485
                 */
1486
                struct hmacsha {
1487
                        /**
1488
                         * Construct new hmac algorithm
1489
                         *
1490
                         * \deprecated Using a character is not recommended and hardened applications should use BIGNUM
1491
                         * \param key Key to use for HMAC
1492
                         * \param md Pointer to hash function
1493
                         * \param name Name of the algorithm
1494
                         */
1495
                        hmacsha(std::string key, const EVP_MD* (*md)(), std::string name)
82✔
1496
                                : secret(std::move(helper::raw2bn(key))), md(md), alg_name(std::move(name)) {}
82✔
1497
                        /**
1498
                         * Construct new hmac algorithm
1499
                         *
1500
                         * \param key Key to use for HMAC
1501
                         * \param md Pointer to hash function
1502
                         * \param name Name of the algorithm
1503
                         */
1504
                        hmacsha(const BIGNUM* key, const EVP_MD* (*md)(), std::string name)
18✔
1505
                                : secret(key), md(md), alg_name(std::move(name)) {}
18✔
1506

1507
                        hmacsha(const hmacsha&) = default;
100✔
1508
                        hmacsha(hmacsha&&) noexcept = default;
1509
                        hmacsha& operator=(const hmacsha&) = delete;
1510
                        hmacsha& operator=(hmacsha&&) = delete;
1511
                        std::string sign(const std::string& data, std::error_code& ec) const {
105✔
1512
                                ec.clear();
105✔
1513
                                std::string res(static_cast<size_t>(EVP_MAX_MD_SIZE), '\0');
105✔
1514
                                auto len = static_cast<unsigned int>(res.size());
105✔
1515

1516
                                std::vector<unsigned char> buffer(BN_num_bytes(secret.get()), '\0');
105✔
1517
                                const auto buffer_size = BN_bn2bin(secret.get(), buffer.data());
105✔
1518
                                buffer.resize(buffer_size);
105✔
1519

1520
                                if (HMAC(md(), buffer.data(), buffer_size, reinterpret_cast<const unsigned char*>(data.data()),
105✔
1521
                                                 static_cast<int>(data.size()),
105✔
1522
                                                 (unsigned char*)res.data(), // NOLINT(google-readability-casting) requires `const_cast`
105✔
1523
                                                 &len) == nullptr) {
105✔
1524
                                        ec = error::signature_generation_error::hmac_failed;
1✔
1525
                                        return {};
1✔
1526
                                }
1527
                                res.resize(len);
104✔
1528
                                return res;
104✔
1529
                        }
105✔
1530
                        /**
1531
                         * Check if signature is valid
1532
                         *
1533
                         * \param data The data to check signature against
1534
                         * \param signature Signature provided by the jwt
1535
                         * \param ec Filled with details about failure.
1536
                         */
1537
                        void verify(const std::string& data, const std::string& signature, std::error_code& ec) const {
55✔
1538
                                ec.clear();
55✔
1539
                                auto res = sign(data, ec);
55✔
1540
                                if (ec) return;
55✔
1541

1542
                                bool matched = true;
54✔
1543
                                for (size_t i = 0; i < std::min<size_t>(res.size(), signature.size()); i++)
1,776✔
1544
                                        if (res[i] != signature[i]) matched = false;
1,722✔
1545
                                if (res.size() != signature.size()) matched = false;
54✔
1546
                                if (!matched) {
54✔
1547
                                        ec = error::signature_verification_error::invalid_signature;
12✔
1548
                                        return;
12✔
1549
                                }
1550
                        }
55✔
1551
                        /**
1552
                         * Returns the algorithm name provided to the constructor
1553
                         *
1554
                         * \return algorithm's name
1555
                         */
1556
                        std::string name() const { return alg_name; }
100✔
1557

1558
                private:
1559
                        /// HMAC secret
1560
                        const helper::bignum_handle secret;
1561
                        /// HMAC hash generator
1562
                        const EVP_MD* (*md)();
1563
                        /// algorithm's name
1564
                        const std::string alg_name;
1565
                };
1566

1567
                /**
1568
                 * \brief Base class for RSA family of algorithms
1569
                 */
1570
                struct rsa {
1571
                        /**
1572
                         * Construct new rsa algorithm
1573
                         *
1574
                         * \param public_key RSA public key in PEM format
1575
                         * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
1576
                         * \param public_key_password Password to decrypt public key pem.
1577
                         * \param private_key_password Password to decrypt private key pem.
1578
                         * \param md Pointer to hash function
1579
                         * \param name Name of the algorithm
1580
                         */
1581
                        rsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password,
81✔
1582
                                const std::string& private_key_password, const EVP_MD* (*md)(), std::string name)
1583
                                : md(md), alg_name(std::move(name)) {
81✔
1584
                                if (!private_key.empty()) {
81✔
1585
                                        pkey = helper::load_private_key_from_string(private_key, private_key_password);
45✔
1586
                                } else if (!public_key.empty()) {
36✔
1587
                                        pkey = helper::load_public_key_from_string(public_key, public_key_password);
30✔
1588
                                } else
1589
                                        throw error::rsa_exception(error::rsa_error::no_key_provided);
6✔
1590
                        }
87✔
1591
                        /**
1592
                         * Construct new rsa algorithm
1593
                         *
1594
                         * \param key_pair openssl EVP_PKEY structure containing RSA key pair. The private part is optional.
1595
                         * \param md Pointer to hash function
1596
                         * \param name Name of the algorithm
1597
                         */
1598
                        rsa(helper::evp_pkey_handle key_pair, const EVP_MD* (*md)(), std::string name)
18✔
1599
                                : pkey(std::move(key_pair)), md(md), alg_name(std::move(name)) {
18✔
1600
                                if (!pkey) { throw error::rsa_exception(error::rsa_error::no_key_provided); }
18✔
1601
                        }
18✔
1602
                        /**
1603
                         * Sign jwt data
1604
                         * \param data The data to sign
1605
                         * \param ec error_code filled with details on error
1606
                         * \return RSA signature for the given data
1607
                         */
1608
                        std::string sign(const std::string& data, std::error_code& ec) const {
29✔
1609
                                ec.clear();
29✔
1610
                                auto ctx = helper::make_evp_md_ctx();
29✔
1611
                                if (!ctx) {
29✔
1612
                                        ec = error::signature_generation_error::create_context_failed;
1✔
1613
                                        return {};
1✔
1614
                                }
1615
                                if (!EVP_SignInit(ctx.get(), md())) {
28✔
1616
                                        ec = error::signature_generation_error::signinit_failed;
1✔
1617
                                        return {};
1✔
1618
                                }
1619

1620
                                std::string res(EVP_PKEY_size(pkey.get()), '\0');
27✔
1621
                                unsigned int len = 0;
27✔
1622

1623
                                if (!EVP_SignUpdate(ctx.get(), data.data(), data.size())) {
27✔
1624
                                        ec = error::signature_generation_error::signupdate_failed;
1✔
1625
                                        return {};
1✔
1626
                                }
1627
                                if (EVP_SignFinal(ctx.get(), (unsigned char*)res.data(), &len, pkey.get()) == 0) {
26✔
1628
                                        ec = error::signature_generation_error::signfinal_failed;
1✔
1629
                                        return {};
1✔
1630
                                }
1631

1632
                                res.resize(len);
25✔
1633
                                return res;
25✔
1634
                        }
29✔
1635
                        /**
1636
                         * Check if signature is valid
1637
                         *
1638
                         * \param data The data to check signature against
1639
                         * \param signature Signature provided by the jwt
1640
                         * \param ec Filled with details on failure
1641
                         */
1642
                        void verify(const std::string& data, const std::string& signature, std::error_code& ec) const {
71✔
1643
                                ec.clear();
71✔
1644
                                auto ctx = helper::make_evp_md_ctx();
71✔
1645
                                if (!ctx) {
71✔
1646
                                        ec = error::signature_verification_error::create_context_failed;
1✔
1647
                                        return;
1✔
1648
                                }
1649
                                if (!EVP_VerifyInit(ctx.get(), md())) {
70✔
1650
                                        ec = error::signature_verification_error::verifyinit_failed;
1✔
1651
                                        return;
1✔
1652
                                }
1653
                                if (!EVP_VerifyUpdate(ctx.get(), data.data(), data.size())) {
69✔
1654
                                        ec = error::signature_verification_error::verifyupdate_failed;
1✔
1655
                                        return;
1✔
1656
                                }
1657
                                auto res = EVP_VerifyFinal(ctx.get(), reinterpret_cast<const unsigned char*>(signature.data()),
136✔
1658
                                                                                   static_cast<unsigned int>(signature.size()), pkey.get());
68✔
1659
                                if (res != 1) {
68✔
1660
                                        ec = error::signature_verification_error::verifyfinal_failed;
13✔
1661
                                        return;
13✔
1662
                                }
1663
                        }
71✔
1664
                        /**
1665
                         * Returns the algorithm name provided to the constructor
1666
                         * \return algorithm's name
1667
                         */
1668
                        std::string name() const { return alg_name; }
90✔
1669

1670
                private:
1671
                        /// OpenSSL structure containing converted keys
1672
                        helper::evp_pkey_handle pkey;
1673
                        /// Hash generator
1674
                        const EVP_MD* (*md)();
1675
                        /// algorithm's name
1676
                        const std::string alg_name;
1677
                };
1678
                /**
1679
                 * \brief Base class for ECDSA family of algorithms
1680
                 */
1681
                struct ecdsa {
1682
                        /**
1683
                         * Construct new ecdsa algorithm
1684
                         *
1685
                         * \param public_key ECDSA public key in PEM format
1686
                         * \param private_key ECDSA private key or empty string if not available. If empty, signing will always fail
1687
                         * \param public_key_password Password to decrypt public key pem
1688
                         * \param private_key_password Password to decrypt private key pem
1689
                         * \param md Pointer to hash function
1690
                         * \param name Name of the algorithm
1691
                         * \param siglen The bit length of the signature
1692
                         */
1693
                        ecdsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password,
174✔
1694
                                  const std::string& private_key_password, const EVP_MD* (*md)(), std::string name, size_t siglen)
1695
                                : md(md), alg_name(std::move(name)), signature_length(siglen) {
174✔
1696
                                if (!private_key.empty()) {
174✔
1697
                                        pkey = helper::load_private_ec_key_from_string(private_key, private_key_password);
47✔
1698
                                        check_private_key(pkey.get());
44✔
1699
                                } else if (!public_key.empty()) {
127✔
1700
                                        pkey = helper::load_public_ec_key_from_string(public_key, public_key_password);
121✔
1701
                                        check_public_key(pkey.get());
110✔
1702
                                } else {
1703
                                        throw error::ecdsa_exception(error::ecdsa_error::no_key_provided);
6✔
1704
                                }
1705
                                if (!pkey) throw error::ecdsa_exception(error::ecdsa_error::invalid_key);
150✔
1706

1707
                                size_t keysize = EVP_PKEY_bits(pkey.get());
150✔
1708
                                if (keysize != signature_length * 4 && (signature_length != 132 || keysize != 521))
150✔
1709
                                        throw error::ecdsa_exception(error::ecdsa_error::invalid_key_size);
24✔
1710
                        }
222✔
1711

1712
                        /**
1713
                         * Construct new ecdsa algorithm
1714
                         *
1715
                         * \param key_pair openssl EVP_PKEY structure containing ECDSA key pair. The private part is optional.
1716
                         * \param md Pointer to hash function
1717
                         * \param name Name of the algorithm
1718
                         * \param siglen The bit length of the signature
1719
                         */
1720
                        ecdsa(helper::evp_pkey_handle key_pair, const EVP_MD* (*md)(), std::string name, size_t siglen)
24✔
1721
                                : pkey(std::move(key_pair)), md(md), alg_name(std::move(name)), signature_length(siglen) {
24✔
1722
                                if (!pkey) { throw error::ecdsa_exception(error::ecdsa_error::no_key_provided); }
24✔
1723
                                size_t keysize = EVP_PKEY_bits(pkey.get());
18✔
1724
                                if (keysize != signature_length * 4 && (signature_length != 132 || keysize != 521))
18✔
1725
                                        throw error::ecdsa_exception(error::ecdsa_error::invalid_key_size);
×
1726
                        }
30✔
1727

1728
                        /**
1729
                         * Sign jwt data
1730
                         * \param data The data to sign
1731
                         * \param ec error_code filled with details on error
1732
                         * \return ECDSA signature for the given data
1733
                         */
1734
                        std::string sign(const std::string& data, std::error_code& ec) const {
55✔
1735
                                ec.clear();
55✔
1736
                                auto ctx = helper::make_evp_md_ctx();
55✔
1737
                                if (!ctx) {
55✔
1738
                                        ec = error::signature_generation_error::create_context_failed;
1✔
1739
                                        return {};
1✔
1740
                                }
1741
                                if (!EVP_DigestSignInit(ctx.get(), nullptr, md(), nullptr, pkey.get())) {
54✔
1742
                                        ec = error::signature_generation_error::signinit_failed;
1✔
1743
                                        return {};
1✔
1744
                                }
1745
                                if (!EVP_DigestUpdate(ctx.get(), data.data(), static_cast<unsigned int>(data.size()))) {
53✔
1746
                                        ec = error::signature_generation_error::digestupdate_failed;
1✔
1747
                                        return {};
1✔
1748
                                }
1749

1750
                                size_t len = 0;
52✔
1751
                                if (!EVP_DigestSignFinal(ctx.get(), nullptr, &len)) {
52✔
1752
                                        ec = error::signature_generation_error::signfinal_failed;
1✔
1753
                                        return {};
1✔
1754
                                }
1755
                                std::string res(len, '\0');
51✔
1756
                                if (!EVP_DigestSignFinal(ctx.get(), (unsigned char*)res.data(), &len)) {
51✔
1757
                                        ec = error::signature_generation_error::signfinal_failed;
25✔
1758
                                        return {};
25✔
1759
                                }
1760

1761
                                res.resize(len);
26✔
1762
                                return der_to_p1363_signature(res, ec);
26✔
1763
                        }
55✔
1764

1765
                        /**
1766
                         * Check if signature is valid
1767
                         * \param data The data to check signature against
1768
                         * \param signature Signature provided by the jwt
1769
                         * \param ec Filled with details on error
1770
                         */
1771
                        void verify(const std::string& data, const std::string& signature, std::error_code& ec) const {
98✔
1772
                                ec.clear();
98✔
1773
                                std::string der_signature = p1363_to_der_signature(signature, ec);
98✔
1774
                                if (ec) { return; }
98✔
1775

1776
                                auto ctx = helper::make_evp_md_ctx();
95✔
1777
                                if (!ctx) {
95✔
1778
                                        ec = error::signature_verification_error::create_context_failed;
1✔
1779
                                        return;
1✔
1780
                                }
1781
                                if (!EVP_DigestVerifyInit(ctx.get(), nullptr, md(), nullptr, pkey.get())) {
94✔
1782
                                        ec = error::signature_verification_error::verifyinit_failed;
1✔
1783
                                        return;
1✔
1784
                                }
1785
                                if (!EVP_DigestUpdate(ctx.get(), data.data(), static_cast<unsigned int>(data.size()))) {
93✔
1786
                                        ec = error::signature_verification_error::verifyupdate_failed;
1✔
1787
                                        return;
1✔
1788
                                }
1789

1790
#if OPENSSL_VERSION_NUMBER < 0x10002000L
1791
                                unsigned char* der_sig_data = reinterpret_cast<unsigned char*>(const_cast<char*>(der_signature.data()));
1792
#else
1793
                                const unsigned char* der_sig_data = reinterpret_cast<const unsigned char*>(der_signature.data());
92✔
1794
#endif
1795
                                auto res =
1796
                                        EVP_DigestVerifyFinal(ctx.get(), der_sig_data, static_cast<unsigned int>(der_signature.length()));
92✔
1797
                                if (res == 0) {
92✔
1798
                                        ec = error::signature_verification_error::invalid_signature;
43✔
1799
                                        return;
43✔
1800
                                }
1801
                                if (res == -1) {
49✔
1802
                                        ec = error::signature_verification_error::verifyfinal_failed;
×
1803
                                        return;
×
1804
                                }
1805
                        }
144✔
1806
                        /**
1807
                         * Returns the algorithm name provided to the constructor
1808
                         * \return algorithm's name
1809
                         */
1810
                        std::string name() const { return alg_name; }
138✔
1811

1812
                private:
1813
                        static void check_public_key(EVP_PKEY* pkey) {
110✔
1814
#ifdef JWT_OPENSSL_3_0
1815
                                std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> ctx(
1816
                                        EVP_PKEY_CTX_new_from_pkey(nullptr, pkey, nullptr), EVP_PKEY_CTX_free);
110✔
1817
                                if (!ctx) { throw error::ecdsa_exception(error::ecdsa_error::create_context_failed); }
110✔
1818
                                if (EVP_PKEY_public_check(ctx.get()) != 1) {
109✔
1819
                                        throw error::ecdsa_exception(error::ecdsa_error::invalid_key);
1✔
1820
                                }
1821
#else
1822
                                std::unique_ptr<EC_KEY, decltype(&EC_KEY_free)> eckey(EVP_PKEY_get1_EC_KEY(pkey), EC_KEY_free);
1823
                                if (!eckey) { throw error::ecdsa_exception(error::ecdsa_error::invalid_key); }
1824
                                if (EC_KEY_check_key(eckey.get()) == 0) throw error::ecdsa_exception(error::ecdsa_error::invalid_key);
1825
#endif
1826
                        }
110✔
1827

1828
                        static void check_private_key(EVP_PKEY* pkey) {
44✔
1829
#ifdef JWT_OPENSSL_3_0
1830
                                std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> ctx(
1831
                                        EVP_PKEY_CTX_new_from_pkey(nullptr, pkey, nullptr), EVP_PKEY_CTX_free);
44✔
1832
                                if (!ctx) { throw error::ecdsa_exception(error::ecdsa_error::create_context_failed); }
44✔
1833
                                if (EVP_PKEY_private_check(ctx.get()) != 1) {
43✔
1834
                                        throw error::ecdsa_exception(error::ecdsa_error::invalid_key);
1✔
1835
                                }
1836
#else
1837
                                std::unique_ptr<EC_KEY, decltype(&EC_KEY_free)> eckey(EVP_PKEY_get1_EC_KEY(pkey), EC_KEY_free);
1838
                                if (!eckey) { throw error::ecdsa_exception(error::ecdsa_error::invalid_key); }
1839
                                if (EC_KEY_check_key(eckey.get()) == 0) throw error::ecdsa_exception(error::ecdsa_error::invalid_key);
1840
#endif
1841
                        }
44✔
1842

1843
                        std::string der_to_p1363_signature(const std::string& der_signature, std::error_code& ec) const {
26✔
1844
                                const unsigned char* possl_signature = reinterpret_cast<const unsigned char*>(der_signature.data());
26✔
1845
                                std::unique_ptr<ECDSA_SIG, decltype(&ECDSA_SIG_free)> sig(
1846
                                        d2i_ECDSA_SIG(nullptr, &possl_signature, static_cast<long>(der_signature.length())),
26✔
1847
                                        ECDSA_SIG_free);
26✔
1848
                                if (!sig) {
26✔
1849
                                        ec = error::signature_generation_error::signature_decoding_failed;
1✔
1850
                                        return {};
1✔
1851
                                }
1852

1853
#ifdef JWT_OPENSSL_1_0_0
1854
                                auto rr = helper::bn2raw(sig->r);
1855
                                auto rs = helper::bn2raw(sig->s);
1856
#else
1857
                                const BIGNUM* r;
1858
                                const BIGNUM* s;
1859
                                ECDSA_SIG_get0(sig.get(), &r, &s);
25✔
1860
                                auto rr = helper::bn2raw(r);
25✔
1861
                                auto rs = helper::bn2raw(s);
25✔
1862
#endif
1863
                                if (rr.size() > signature_length / 2 || rs.size() > signature_length / 2)
25✔
1864
                                        throw std::logic_error("bignum size exceeded expected length");
×
1865
                                rr.insert(0, signature_length / 2 - rr.size(), '\0');
25✔
1866
                                rs.insert(0, signature_length / 2 - rs.size(), '\0');
25✔
1867
                                return rr + rs;
25✔
1868
                        }
26✔
1869

1870
                        std::string p1363_to_der_signature(const std::string& signature, std::error_code& ec) const {
98✔
1871
                                ec.clear();
98✔
1872
                                auto r = helper::raw2bn(signature.substr(0, signature.size() / 2), ec);
98✔
1873
                                if (ec) return {};
98✔
1874
                                auto s = helper::raw2bn(signature.substr(signature.size() / 2), ec);
98✔
1875
                                if (ec) return {};
98✔
1876

1877
                                ECDSA_SIG* psig;
1878
#ifdef JWT_OPENSSL_1_0_0
1879
                                ECDSA_SIG sig;
1880
                                sig.r = r.get();
1881
                                sig.s = s.get();
1882
                                psig = &sig;
1883
#else
1884
                                std::unique_ptr<ECDSA_SIG, decltype(&ECDSA_SIG_free)> sig(ECDSA_SIG_new(), ECDSA_SIG_free);
98✔
1885
                                if (!sig) {
98✔
1886
                                        ec = error::signature_verification_error::create_context_failed;
1✔
1887
                                        return {};
1✔
1888
                                }
1889
                                ECDSA_SIG_set0(sig.get(), const_cast<BIGNUM*>(r.release()), const_cast<BIGNUM*>(s.release()));
97✔
1890
                                psig = sig.get();
97✔
1891
#endif
1892

1893
                                int length = i2d_ECDSA_SIG(psig, nullptr);
97✔
1894
                                if (length < 0) {
97✔
1895
                                        ec = error::signature_verification_error::signature_encoding_failed;
1✔
1896
                                        return {};
1✔
1897
                                }
1898
                                std::string der_signature(length, '\0');
96✔
1899
                                unsigned char* psbuffer = (unsigned char*)der_signature.data();
96✔
1900
                                length = i2d_ECDSA_SIG(psig, &psbuffer);
96✔
1901
                                if (length < 0) {
96✔
1902
                                        ec = error::signature_verification_error::signature_encoding_failed;
1✔
1903
                                        return {};
1✔
1904
                                }
1905
                                der_signature.resize(length);
95✔
1906
                                return der_signature;
95✔
1907
                        }
98✔
1908

1909
                        /// OpenSSL struct containing keys
1910
                        helper::evp_pkey_handle pkey;
1911
                        /// Hash generator function
1912
                        const EVP_MD* (*md)();
1913
                        /// algorithm's name
1914
                        const std::string alg_name;
1915
                        /// Length of the resulting signature
1916
                        const size_t signature_length;
1917
                };
1918

1919
#if !defined(JWT_OPENSSL_1_0_0) && !defined(JWT_OPENSSL_1_1_0)
1920
                /**
1921
                 * \brief Base class for EdDSA family of algorithms
1922
                 *
1923
                 * https://tools.ietf.org/html/rfc8032
1924
                 *
1925
                 * The EdDSA algorithms were introduced in [OpenSSL v1.1.1](https://www.openssl.org/news/openssl-1.1.1-notes.html),
1926
                 * so these algorithms are only available when building against this version or higher.
1927
                 * LibreSSL added EdDSA (Ed25519) functionality in [LibreSSL 3.7.1](https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-3.7.1-relnotes.txt)
1928
                 */
1929
                struct eddsa {
1930
                        /**
1931
                         * Construct new eddsa algorithm
1932
                         * \param public_key EdDSA public key in PEM format
1933
                         * \param private_key EdDSA private key or empty string if not available. If empty, signing will always
1934
                         * fail.
1935
                         * \param public_key_password Password to decrypt public key pem.
1936
                         * \param private_key_password Password
1937
                         * to decrypt private key pem.
1938
                         * \param name Name of the algorithm
1939
                         */
1940
                        eddsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password,
77✔
1941
                                  const std::string& private_key_password, std::string name)
1942
                                : alg_name(std::move(name)) {
77✔
1943
                                if (!private_key.empty()) {
77✔
1944
                                        pkey = helper::load_private_key_from_string(private_key, private_key_password);
20✔
1945
                                } else if (!public_key.empty()) {
57✔
1946
                                        pkey = helper::load_public_key_from_string(public_key, public_key_password);
56✔
1947
                                } else
1948
                                        throw error::ecdsa_exception(error::ecdsa_error::load_key_bio_read);
1✔
1949
                        }
91✔
1950
                        /**
1951
                         * Sign jwt data
1952
                         * \param data The data to sign
1953
                         * \param ec error_code filled with details on error
1954
                         * \return EdDSA signature for the given data
1955
                         */
1956
                        std::string sign(const std::string& data, std::error_code& ec) const {
16✔
1957
                                ec.clear();
16✔
1958
                                auto ctx = helper::make_evp_md_ctx();
16✔
1959
                                if (!ctx) {
16✔
1960
                                        ec = error::signature_generation_error::create_context_failed;
1✔
1961
                                        return {};
1✔
1962
                                }
1963
                                if (!EVP_DigestSignInit(ctx.get(), nullptr, nullptr, nullptr, pkey.get())) {
15✔
1964
                                        ec = error::signature_generation_error::signinit_failed;
1✔
1965
                                        return {};
1✔
1966
                                }
1967

1968
                                size_t len = EVP_PKEY_size(pkey.get());
14✔
1969
                                std::string res(len, '\0');
14✔
1970

1971
// LibreSSL and OpenSSL, require the oneshot EVP_DigestSign API.
1972
// wolfSSL uses the Update/Final pattern.
1973
#if defined(LIBWOLFSSL_VERSION_HEX)
1974
                                ERR_clear_error();
1975
                                if (EVP_DigestSignUpdate(ctx.get(), reinterpret_cast<const unsigned char*>(data.data()), data.size()) !=
1976
                                        1) {
1977
                                        std::cout << ERR_error_string(ERR_get_error(), NULL) << '\n';
1978
                                        ec = error::signature_generation_error::signupdate_failed;
1979
                                        return {};
1980
                                }
1981
                                if (EVP_DigestSignFinal(ctx.get(), reinterpret_cast<unsigned char*>(&res[0]), &len) != 1) {
1982
                                        ec = error::signature_generation_error::signfinal_failed;
1983
                                        return {};
1984
                                }
1985
#else
1986
                                if (EVP_DigestSign(ctx.get(), reinterpret_cast<unsigned char*>(&res[0]), &len,
28✔
1987
                                                                   reinterpret_cast<const unsigned char*>(data.data()), data.size()) != 1) {
28✔
1988
                                        ec = error::signature_generation_error::signfinal_failed;
1✔
1989
                                        return {};
1✔
1990
                                }
1991
#endif
1992

1993
                                res.resize(len);
13✔
1994
                                return res;
13✔
1995
                        }
16✔
1996

1997
                        /**
1998
                         * Check if signature is valid
1999
                         * \param data The data to check signature against
2000
                         * \param signature Signature provided by the jwt
2001
                         * \param ec Filled with details on error
2002
                         */
2003
                        void verify(const std::string& data, const std::string& signature, std::error_code& ec) const {
52✔
2004
                                ec.clear();
52✔
2005
                                auto ctx = helper::make_evp_md_ctx();
52✔
2006
                                if (!ctx) {
52✔
2007
                                        ec = error::signature_verification_error::create_context_failed;
1✔
2008
                                        return;
1✔
2009
                                }
2010
                                if (!EVP_DigestVerifyInit(ctx.get(), nullptr, nullptr, nullptr, pkey.get())) {
51✔
2011
                                        ec = error::signature_verification_error::verifyinit_failed;
1✔
2012
                                        return;
1✔
2013
                                }
2014
// LibreSSL and OpenSSL, require the oneshot EVP_DigestVerify API.
2015
// wolfSSL uses the Update/Final pattern.
2016
#if defined(LIBWOLFSSL_VERSION_HEX)
2017
                                if (EVP_DigestVerifyUpdate(ctx.get(), reinterpret_cast<const unsigned char*>(data.data()),
2018
                                                                                   data.size()) != 1) {
2019
                                        ec = error::signature_verification_error::verifyupdate_failed;
2020
                                        return;
2021
                                }
2022
                                if (EVP_DigestVerifyFinal(ctx.get(), reinterpret_cast<const unsigned char*>(signature.data()),
2023
                                                                                  signature.size()) != 1) {
2024
                                        ec = error::signature_verification_error::verifyfinal_failed;
2025
                                        return;
2026
                                }
2027
#else
2028
                                auto res = EVP_DigestVerify(ctx.get(), reinterpret_cast<const unsigned char*>(signature.data()),
100✔
2029
                                                                                        signature.size(), reinterpret_cast<const unsigned char*>(data.data()),
50✔
2030
                                                                                        data.size());
2031
                                if (res != 1) {
50✔
2032
                                        ec = error::signature_verification_error::verifyfinal_failed;
25✔
2033
                                        return;
25✔
2034
                                }
2035
#endif
2036
                        }
52✔
2037
                        /**
2038
                         * Returns the algorithm name provided to the constructor
2039
                         * \return algorithm's name
2040
                         */
2041
                        std::string name() const { return alg_name; }
60✔
2042

2043
                private:
2044
                        /// OpenSSL struct containing keys
2045
                        helper::evp_pkey_handle pkey;
2046
                        /// algorithm's name
2047
                        const std::string alg_name;
2048
                };
2049
#endif
2050
                /**
2051
                 * \brief Base class for PSS-RSA family of algorithms
2052
                 */
2053
                struct pss {
2054
                        /**
2055
                         * Construct new pss algorithm
2056
                         * \param public_key RSA public key in PEM format
2057
                         * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
2058
                         * \param public_key_password Password to decrypt public key pem.
2059
                         * \param private_key_password Password to decrypt private key pem.
2060
                         * \param md Pointer to hash function
2061
                         * \param name Name of the algorithm
2062
                         */
2063
                        pss(const std::string& public_key, const std::string& private_key, const std::string& public_key_password,
45✔
2064
                                const std::string& private_key_password, const EVP_MD* (*md)(), std::string name)
2065
                                : md(md), alg_name(std::move(name)) {
45✔
2066
                                if (!private_key.empty()) {
45✔
2067
                                        pkey = helper::load_private_key_from_string(private_key, private_key_password);
27✔
2068
                                } else if (!public_key.empty()) {
18✔
2069
                                        pkey = helper::load_public_key_from_string(public_key, public_key_password);
12✔
2070
                                } else
2071
                                        throw error::rsa_exception(error::rsa_error::no_key_provided);
6✔
2072
                        }
51✔
2073

2074
                        /**
2075
                         * Sign jwt data
2076
                         * \param data The data to sign
2077
                         * \param ec error_code filled with details on error
2078
                         * \return ECDSA signature for the given data
2079
                         */
2080
                        std::string sign(const std::string& data, std::error_code& ec) const {
23✔
2081
                                ec.clear();
23✔
2082
                                auto md_ctx = helper::make_evp_md_ctx();
23✔
2083
                                if (!md_ctx) {
23✔
2084
                                        ec = error::signature_generation_error::create_context_failed;
1✔
2085
                                        return {};
1✔
2086
                                }
2087
                                EVP_PKEY_CTX* ctx = nullptr;
22✔
2088
                                if (EVP_DigestSignInit(md_ctx.get(), &ctx, md(), nullptr, pkey.get()) != 1) {
22✔
2089
                                        ec = error::signature_generation_error::signinit_failed;
1✔
2090
                                        return {};
1✔
2091
                                }
2092
                                if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0) {
21✔
2093
                                        ec = error::signature_generation_error::rsa_padding_failed;
×
2094
                                        return {};
×
2095
                                }
2096
// wolfSSL does not require EVP_PKEY_CTX_set_rsa_pss_saltlen. The default behavior
2097
// sets the salt length to the hash length. Unlike OpenSSL which exposes this functionality.
2098
#ifndef LIBWOLFSSL_VERSION_HEX
2099
                                if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, -1) <= 0) {
21✔
2100
                                        ec = error::signature_generation_error::set_rsa_pss_saltlen_failed;
×
2101
                                        return {};
×
2102
                                }
2103
#endif
2104
                                if (EVP_DigestUpdate(md_ctx.get(), data.data(), static_cast<unsigned int>(data.size())) != 1) {
21✔
2105
                                        ec = error::signature_generation_error::digestupdate_failed;
1✔
2106
                                        return {};
1✔
2107
                                }
2108

2109
                                size_t size = EVP_PKEY_size(pkey.get());
20✔
2110
                                std::string res(size, 0x00);
20✔
2111
                                if (EVP_DigestSignFinal(
20✔
2112
                                                md_ctx.get(),
2113
                                                (unsigned char*)res.data(), // NOLINT(google-readability-casting) requires `const_cast`
20✔
2114
                                                &size) <= 0) {
20✔
2115
                                        ec = error::signature_generation_error::signfinal_failed;
1✔
2116
                                        return {};
1✔
2117
                                }
2118

2119
                                return res;
19✔
2120
                        }
23✔
2121

2122
                        /**
2123
                         * Check if signature is valid
2124
                         * \param data The data to check signature against
2125
                         * \param signature Signature provided by the jwt
2126
                         * \param ec Filled with error details
2127
                         */
2128
                        void verify(const std::string& data, const std::string& signature, std::error_code& ec) const {
23✔
2129
                                ec.clear();
23✔
2130

2131
                                auto md_ctx = helper::make_evp_md_ctx();
23✔
2132
                                if (!md_ctx) {
23✔
2133
                                        ec = error::signature_verification_error::create_context_failed;
1✔
2134
                                        return;
1✔
2135
                                }
2136
                                EVP_PKEY_CTX* ctx = nullptr;
22✔
2137
                                if (EVP_DigestVerifyInit(md_ctx.get(), &ctx, md(), nullptr, pkey.get()) != 1) {
22✔
2138
                                        ec = error::signature_verification_error::verifyinit_failed;
1✔
2139
                                        return;
1✔
2140
                                }
2141
                                if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0) {
21✔
2142
                                        ec = error::signature_generation_error::rsa_padding_failed;
×
2143
                                        return;
×
2144
                                }
2145
// wolfSSL does not require EVP_PKEY_CTX_set_rsa_pss_saltlen. The default behavior
2146
// sets the salt length to the hash length. Unlike OpenSSL which exposes this functionality.
2147
#ifndef LIBWOLFSSL_VERSION_HEX
2148
                                if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, -1) <= 0) {
21✔
2149
                                        ec = error::signature_verification_error::set_rsa_pss_saltlen_failed;
×
2150
                                        return;
×
2151
                                }
2152
#endif
2153
                                if (EVP_DigestUpdate(md_ctx.get(), data.data(), static_cast<unsigned int>(data.size())) != 1) {
21✔
2154
                                        ec = error::signature_verification_error::verifyupdate_failed;
1✔
2155
                                        return;
1✔
2156
                                }
2157

2158
                                if (EVP_DigestVerifyFinal(md_ctx.get(), (unsigned char*)signature.data(), signature.size()) <= 0) {
20✔
2159
                                        ec = error::signature_verification_error::verifyfinal_failed;
7✔
2160
                                        return;
7✔
2161
                                }
2162
                        }
23✔
2163
                        /**
2164
                         * Returns the algorithm name provided to the constructor
2165
                         * \return algorithm's name
2166
                         */
2167
                        std::string name() const { return alg_name; }
36✔
2168

2169
                private:
2170
                        /// OpenSSL structure containing keys
2171
                        helper::evp_pkey_handle pkey;
2172
                        /// Hash generator function
2173
                        const EVP_MD* (*md)();
2174
                        /// algorithm's name
2175
                        const std::string alg_name;
2176
                };
2177

2178
                /**
2179
                 * HS256 algorithm
2180
                 */
2181
                struct hs256 : public hmacsha {
2182
                        /**
2183
                         * Construct new instance of algorithm
2184
                         * \deprecated Using a character is not recommended and hardened applications should use BIGNUM
2185
                         * \param key HMAC signing key
2186
                         */
2187
                        explicit hs256(std::string key) : hmacsha(std::move(key), EVP_sha256, "HS256") {}
246✔
2188
                        /**
2189
                         * Construct new instance of algorithm
2190
                         * \param key HMAC signing key
2191
                         */
2192
                        explicit hs256(const BIGNUM* key) : hmacsha(key, EVP_sha256, "HS256") {}
54✔
2193
                };
2194
                /**
2195
                 * HS384 algorithm
2196
                 */
2197
                struct hs384 : public hmacsha {
2198
                        /**
2199
                         * Construct new instance of algorithm
2200
                         * \deprecated Using a character is not recommended and hardened applications should use BIGNUM
2201
                         * \param key HMAC signing key
2202
                         */
2203
                        explicit hs384(std::string key) : hmacsha(std::move(key), EVP_sha384, "HS384") {}
2204
                        /**
2205
                         * Construct new instance of algorithm
2206
                         * \param key HMAC signing key
2207
                         */
2208
                        explicit hs384(const BIGNUM* key) : hmacsha(key, EVP_sha384, "HS384") {}
2209
                };
2210
                /**
2211
                 * HS512 algorithm
2212
                 */
2213
                struct hs512 : public hmacsha {
2214
                        /**
2215
                         * Construct new instance of algorithm
2216
                         * \deprecated Using a character is not recommended and hardened applications should use BIGNUM
2217
                         * \param key HMAC signing key
2218
                         */
2219
                        explicit hs512(std::string key) : hmacsha(std::move(key), EVP_sha512, "HS512") {}
2220
                        /**
2221
                         * Construct new instance of algorithm
2222
                         *
2223
                         * This can be used to sign and verify tokens.
2224
                          * \snippet{trimleft} hs512.cpp use HMAC algo with BIGNUM
2225
                         *
2226
                         * \param key HMAC signing key
2227
                         */
2228
                        explicit hs512(const BIGNUM* key) : hmacsha(key, EVP_sha512, "HS512") {}
2229
                };
2230
                /**
2231
                 * RS256 algorithm.
2232
                 *
2233
                 * This data structure is used to describe the RSA256 and can be used to verify JWTs
2234
                 */
2235
                struct rs256 : public rsa {
2236
                        /**
2237
                         * \brief Construct new instance of algorithm
2238
                         *
2239
                         * \param public_key RSA public key in PEM format
2240
                         * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
2241
                         * \param public_key_password Password to decrypt public key pem.
2242
                         * \param private_key_password Password to decrypt private key pem.
2243
                         */
2244
                        explicit rs256(const std::string& public_key, const std::string& private_key = "",
51✔
2245
                                                   const std::string& public_key_password = "", const std::string& private_key_password = "")
2246
                                : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "RS256") {}
153✔
2247
                };
2248
                /**
2249
                 * RS384 algorithm
2250
                 */
2251
                struct rs384 : public rsa {
2252
                        /**
2253
                         * Construct new instance of algorithm
2254
                         * \param public_key RSA public key in PEM format
2255
                         * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
2256
                         * \param public_key_password Password to decrypt public key pem.
2257
                         * \param private_key_password Password to decrypt private key pem.
2258
                         */
2259
                        explicit rs384(const std::string& public_key, const std::string& private_key = "",
2260
                                                   const std::string& public_key_password = "", const std::string& private_key_password = "")
2261
                                : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "RS384") {}
2262
                };
2263
                /**
2264
                 * RS512 algorithm
2265
                 */
2266
                struct rs512 : public rsa {
2267
                        /**
2268
                         * Construct new instance of algorithm
2269
                         * \param public_key RSA public key in PEM format
2270
                         * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
2271
                         * \param public_key_password Password to decrypt public key pem.
2272
                         * \param private_key_password Password to decrypt private key pem.
2273
                         */
2274
                        explicit rs512(const std::string& public_key, const std::string& private_key = "",
30✔
2275
                                                   const std::string& public_key_password = "", const std::string& private_key_password = "")
2276
                                : rsa(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "RS512") {}
90✔
2277
                };
2278
                /**
2279
                 * ES256 algorithm
2280
                 */
2281
                struct es256 : public ecdsa {
2282
                        /**
2283
                         * Construct new instance of algorithm
2284
                         * \param public_key ECDSA public key in PEM format
2285
                         * \param private_key ECDSA private key or empty string if not available. If empty, signing will always
2286
                         * fail.
2287
                         * \param public_key_password Password to decrypt public key pem.
2288
                         * \param private_key_password Password
2289
                         * to decrypt private key pem.
2290
                         */
2291
                        explicit es256(const std::string& public_key, const std::string& private_key = "",
84✔
2292
                                                   const std::string& public_key_password = "", const std::string& private_key_password = "")
2293
                                : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "ES256", 64) {}
252✔
2294
                };
2295
                /**
2296
                 * ES384 algorithm
2297
                 */
2298
                struct es384 : public ecdsa {
2299
                        /**
2300
                         * Construct new instance of algorithm
2301
                         * \param public_key ECDSA public key in PEM format
2302
                         * \param private_key ECDSA private key or empty string if not available. If empty, signing will always
2303
                         * fail.
2304
                         * \param public_key_password Password to decrypt public key pem.
2305
                         * \param private_key_password Password
2306
                         * to decrypt private key pem.
2307
                         */
2308
                        explicit es384(const std::string& public_key, const std::string& private_key = "",
45✔
2309
                                                   const std::string& public_key_password = "", const std::string& private_key_password = "")
2310
                                : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "ES384", 96) {}
135✔
2311
                };
2312
                /**
2313
                 * ES512 algorithm
2314
                 */
2315
                struct es512 : public ecdsa {
2316
                        /**
2317
                         * Construct new instance of algorithm
2318
                         * \param public_key ECDSA public key in PEM format
2319
                         * \param private_key ECDSA private key or empty string if not available. If empty, signing will always
2320
                         * fail.
2321
                         * \param public_key_password Password to decrypt public key pem.
2322
                         * \param private_key_password Password
2323
                         * to decrypt private key pem.
2324
                         */
2325
                        explicit es512(const std::string& public_key, const std::string& private_key = "",
45✔
2326
                                                   const std::string& public_key_password = "", const std::string& private_key_password = "")
2327
                                : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "ES512", 132) {}
135✔
2328
                };
2329
                /**
2330
                 * ES256K algorithm
2331
                 */
2332
                struct es256k : public ecdsa {
2333
                        /**
2334
                         * Construct new instance of algorithm
2335
                         * \param public_key ECDSA public key in PEM format
2336
                         * \param private_key ECDSA private key or empty string if not available. If empty, signing will always
2337
                         * fail.
2338
                         * \param public_key_password Password to decrypt public key pem.
2339
                         * \param private_key_password Password to decrypt private key pem.
2340
                         */
2341
                        explicit es256k(const std::string& public_key, const std::string& private_key = "",
2342
                                                        const std::string& public_key_password = "", const std::string& private_key_password = "")
2343
                                : ecdsa(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "ES256K", 64) {}
2344
                };
2345

2346
#if !defined(JWT_OPENSSL_1_0_0) && !defined(JWT_OPENSSL_1_1_0)
2347
                /**
2348
                 * Ed25519 algorithm
2349
                 *
2350
                 * https://en.wikipedia.org/wiki/EdDSA#Ed25519
2351
                 *
2352
                 * Requires at least OpenSSL 1.1.1 or LibreSSL 3.7.1.
2353
                 */
2354
                struct ed25519 : public eddsa {
2355
                        /**
2356
                         * Construct new instance of algorithm
2357
                         * \param public_key Ed25519 public key in PEM format
2358
                         * \param private_key Ed25519 private key or empty string if not available. If empty, signing will always
2359
                         * fail.
2360
                         * \param public_key_password Password to decrypt public key pem.
2361
                         * \param private_key_password Password
2362
                         * to decrypt private key pem.
2363
                         */
2364
                        explicit ed25519(const std::string& public_key, const std::string& private_key = "",
47✔
2365
                                                         const std::string& public_key_password = "", const std::string& private_key_password = "")
2366
                                : eddsa(public_key, private_key, public_key_password, private_key_password, "EdDSA") {}
141✔
2367
                };
2368

2369
#if !defined(LIBRESSL_VERSION_NUMBER)
2370
                /**
2371
                 * Ed448 algorithm
2372
                 *
2373
                 * https://en.wikipedia.org/wiki/EdDSA#Ed448
2374
                 *
2375
                 * Requires at least OpenSSL 1.1.1. Note: Not supported by LibreSSL.
2376
                 */
2377
                struct ed448 : public eddsa {
2378
                        /**
2379
                         * Construct new instance of algorithm
2380
                         * \param public_key Ed448 public key in PEM format
2381
                         * \param private_key Ed448 private key or empty string if not available. If empty, signing will always
2382
                         * fail.
2383
                         * \param public_key_password Password to decrypt public key pem.
2384
                         * \param private_key_password Password
2385
                         * to decrypt private key pem.
2386
                         */
2387
                        explicit ed448(const std::string& public_key, const std::string& private_key = "",
30✔
2388
                                                   const std::string& public_key_password = "", const std::string& private_key_password = "")
2389
                                : eddsa(public_key, private_key, public_key_password, private_key_password, "EdDSA") {}
90✔
2390
                };
2391
#endif // !LIBRESSL_VERSION_NUMBER
2392
#endif // !JWT_OPENSSL_1_0_0 && !JWT_OPENSSL_1_1_0
2393

2394
                /**
2395
                 * PS256 algorithm
2396
                 */
2397
                struct ps256 : public pss {
2398
                        /**
2399
                         * Construct new instance of algorithm
2400
                         * \param public_key RSA public key in PEM format
2401
                         * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
2402
                         * \param public_key_password Password to decrypt public key pem.
2403
                         * \param private_key_password Password to decrypt private key pem.
2404
                         */
2405
                        explicit ps256(const std::string& public_key, const std::string& private_key = "",
33✔
2406
                                                   const std::string& public_key_password = "", const std::string& private_key_password = "")
2407
                                : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "PS256") {}
99✔
2408
                };
2409
                /**
2410
                 * PS384 algorithm
2411
                 */
2412
                struct ps384 : public pss {
2413
                        /**
2414
                         * Construct new instance of algorithm
2415
                         * \param public_key RSA public key in PEM format
2416
                         * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
2417
                         * \param public_key_password Password to decrypt public key pem.
2418
                         * \param private_key_password Password to decrypt private key pem.
2419
                         */
2420
                        explicit ps384(const std::string& public_key, const std::string& private_key = "",
6✔
2421
                                                   const std::string& public_key_password = "", const std::string& private_key_password = "")
2422
                                : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "PS384") {}
18✔
2423
                };
2424
                /**
2425
                 * PS512 algorithm
2426
                 */
2427
                struct ps512 : public pss {
2428
                        /**
2429
                         * Construct new instance of algorithm
2430
                         * \param public_key RSA public key in PEM format
2431
                         * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
2432
                         * \param public_key_password Password to decrypt public key pem.
2433
                         * \param private_key_password Password to decrypt private key pem.
2434
                         */
2435
                        explicit ps512(const std::string& public_key, const std::string& private_key = "",
6✔
2436
                                                   const std::string& public_key_password = "", const std::string& private_key_password = "")
2437
                                : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "PS512") {}
18✔
2438
                };
2439
        } // namespace algorithm
2440

2441
        /**
2442
         * \brief JSON Abstractions for working with any library
2443
         */
2444
        namespace json {
2445
                /**
2446
                 * \brief Categories for the various JSON types used in JWTs
2447
                 *
2448
                 * This enum is to abstract the third party underlying types and allows the library
2449
                 * to identify the different structures and reason about them without needing a "concept"
2450
                 * to capture that defintion to compare against a concrete type.
2451
                 */
2452
                enum class type { boolean, integer, number, string, array, object };
2453
        } // namespace json
2454

2455
        namespace details {
2456
#ifdef __cpp_lib_void_t
2457
                template<typename... Ts>
2458
                using void_t = std::void_t<Ts...>;
2459
#else
2460
                // https://en.cppreference.com/w/cpp/types/void_t
2461
                template<typename... Ts>
2462
                struct make_void {
2463
                        using type = void;
2464
                };
2465

2466
                template<typename... Ts>
2467
                using void_t = typename make_void<Ts...>::type;
2468
#endif
2469

2470
#ifdef __cpp_lib_experimental_detect
2471
                template<template<typename...> class _Op, typename... _Args>
2472
                using is_detected = std::experimental::is_detected<_Op, _Args...>;
2473
#else
2474
                struct nonesuch {
2475
                        nonesuch() = delete;
2476
                        ~nonesuch() = delete;
2477
                        nonesuch(nonesuch const&) = delete;
2478
                        nonesuch(nonesuch const&&) = delete;
2479
                        void operator=(nonesuch const&) = delete;
2480
                        void operator=(nonesuch&&) = delete;
2481
                };
2482

2483
                // https://en.cppreference.com/w/cpp/experimental/is_detected
2484
                template<class Default, class AlwaysVoid, template<class...> class Op, class... Args>
2485
                struct detector {
2486
                        using value = std::false_type;
2487
                        using type = Default;
2488
                };
2489

2490
                template<class Default, template<class...> class Op, class... Args>
2491
                struct detector<Default, void_t<Op<Args...>>, Op, Args...> {
2492
                        using value = std::true_type;
2493
                        using type = Op<Args...>;
2494
                };
2495

2496
                template<template<class...> class Op, class... Args>
2497
                using is_detected = typename detector<nonesuch, void, Op, Args...>::value;
2498
#endif
2499

2500
                template<typename T, typename Signature>
2501
                using is_signature = typename std::is_same<T, Signature>;
2502

2503
                template<typename traits_type, template<typename...> class Op, typename Signature>
2504
                struct is_function_signature_detected {
2505
                        using type = Op<traits_type>;
2506
                        static constexpr auto value = is_detected<Op, traits_type>::value && std::is_function<type>::value &&
2507
                                                                                  is_signature<type, Signature>::value;
2508
                };
2509

2510
                template<typename traits_type, typename value_type>
2511
                struct supports_get_type {
2512
                        template<typename T>
2513
                        using get_type_t = decltype(T::get_type);
2514

2515
                        static constexpr auto value =
2516
                                is_function_signature_detected<traits_type, get_type_t, json::type(const value_type&)>::value;
2517

2518
                        // Internal assertions for better feedback
2519
                        static_assert(value, "traits implementation must provide `jwt::json::type get_type(const value_type&)`");
2520
                };
2521

2522
#define JWT_CPP_JSON_TYPE_TYPE(TYPE) json_##TYPE_type
2523
#define JWT_CPP_AS_TYPE_T(TYPE) as_##TYPE_t
2524
#define JWT_CPP_SUPPORTS_AS(TYPE)                                                                                      \
2525
        template<typename traits_type, typename value_type, typename JWT_CPP_JSON_TYPE_TYPE(TYPE)>                         \
2526
        struct supports_as_##TYPE {                                                                                        \
2527
                template<typename T>                                                                                           \
2528
                using JWT_CPP_AS_TYPE_T(TYPE) = decltype(T::as_##TYPE);                                                        \
2529
                                                                                                                       \
2530
                static constexpr auto value =                                                                                  \
2531
                        is_function_signature_detected<traits_type, JWT_CPP_AS_TYPE_T(TYPE),                                       \
2532
                                                                                   JWT_CPP_JSON_TYPE_TYPE(TYPE)(const value_type&)>::value;                    \
2533
                                                                                                                       \
2534
                static_assert(value, "traits implementation must provide `" #TYPE "_type as_" #TYPE "(const value_type&)`");   \
2535
        }
2536

2537
                JWT_CPP_SUPPORTS_AS(object);
2538
                JWT_CPP_SUPPORTS_AS(array);
2539
                JWT_CPP_SUPPORTS_AS(string);
2540
                JWT_CPP_SUPPORTS_AS(number);
2541
                JWT_CPP_SUPPORTS_AS(integer);
2542
                JWT_CPP_SUPPORTS_AS(boolean);
2543

2544
#undef JWT_CPP_JSON_TYPE_TYPE
2545
#undef JWT_CPP_AS_TYPE_T
2546
#undef JWT_CPP_SUPPORTS_AS
2547

2548
                template<typename traits>
2549
                struct is_valid_traits {
2550
                        static constexpr auto value =
2551
                                supports_get_type<traits, typename traits::value_type>::value &&
2552
                                supports_as_object<traits, typename traits::value_type, typename traits::object_type>::value &&
2553
                                supports_as_array<traits, typename traits::value_type, typename traits::array_type>::value &&
2554
                                supports_as_string<traits, typename traits::value_type, typename traits::string_type>::value &&
2555
                                supports_as_number<traits, typename traits::value_type, typename traits::number_type>::value &&
2556
                                supports_as_integer<traits, typename traits::value_type, typename traits::integer_type>::value &&
2557
                                supports_as_boolean<traits, typename traits::value_type, typename traits::boolean_type>::value;
2558
                };
2559

2560
                template<typename value_type>
2561
                struct is_valid_json_value {
2562
                        static constexpr auto value =
2563
                                std::is_default_constructible<value_type>::value &&
2564
                                std::is_constructible<value_type, const value_type&>::value && // a more generic is_copy_constructible
2565
                                std::is_move_constructible<value_type>::value && std::is_assignable<value_type, value_type>::value &&
2566
                                std::is_copy_assignable<value_type>::value && std::is_move_assignable<value_type>::value;
2567
                        // TODO(prince-chrismc): Stream operators
2568
                };
2569

2570
                // https://stackoverflow.com/a/53967057/8480874
2571
                template<typename T, typename = void>
2572
                struct is_iterable : std::false_type {};
2573

2574
                template<typename T>
2575
                struct is_iterable<T, void_t<decltype(std::begin(std::declval<T>())), decltype(std::end(std::declval<T>())),
2576
#if __cplusplus > 201402L
2577
                                                                         decltype(std::cbegin(std::declval<T>())), decltype(std::cend(std::declval<T>()))
2578
#else
2579
                                                                         decltype(std::begin(std::declval<const T>())),
2580
                                                                         decltype(std::end(std::declval<const T>()))
2581
#endif
2582
                                                                         >> : std::true_type {
2583
                };
2584

2585
#if __cplusplus > 201703L
2586
                template<typename T>
2587
                inline constexpr bool is_iterable_v = is_iterable<T>::value;
2588
#endif
2589

2590
                template<typename object_type, typename string_type>
2591
                using is_count_signature = typename std::is_integral<decltype(std::declval<const object_type>().count(
2592
                        std::declval<const string_type>()))>;
2593

2594
                template<typename object_type, typename string_type, typename = void>
2595
                struct is_subcription_operator_signature : std::false_type {};
2596

2597
                template<typename object_type, typename string_type>
2598
                struct is_subcription_operator_signature<
2599
                        object_type, string_type,
2600
                        void_t<decltype(std::declval<object_type>().operator[](std::declval<string_type>()))>> : std::true_type {
2601
                        // TODO(prince-chrismc): I am not convienced this is meaningful anymore
2602
                        static_assert(
2603
                                value,
2604
                                "object_type must implementate the subscription operator '[]' taking string_type as an argument");
2605
                };
2606

2607
                template<typename object_type, typename value_type, typename string_type>
2608
                using is_at_const_signature =
2609
                        typename std::is_same<decltype(std::declval<const object_type>().at(std::declval<const string_type>())),
2610
                                                                  const value_type&>;
2611

2612
                template<typename value_type, typename string_type, typename object_type>
2613
                struct is_valid_json_object {
2614
                        template<typename T>
2615
                        using mapped_type_t = typename T::mapped_type;
2616
                        template<typename T>
2617
                        using key_type_t = typename T::key_type;
2618
                        template<typename T>
2619
                        using iterator_t = typename T::iterator;
2620
                        template<typename T>
2621
                        using const_iterator_t = typename T::const_iterator;
2622

2623
                        static constexpr auto value =
2624
                                std::is_constructible<value_type, object_type>::value &&
2625
                                is_detected<mapped_type_t, object_type>::value &&
2626
                                std::is_same<typename object_type::mapped_type, value_type>::value &&
2627
                                is_detected<key_type_t, object_type>::value &&
2628
                                (std::is_same<typename object_type::key_type, string_type>::value ||
2629
                                 std::is_constructible<typename object_type::key_type, string_type>::value) &&
2630
                                is_detected<iterator_t, object_type>::value && is_detected<const_iterator_t, object_type>::value &&
2631
                                is_iterable<object_type>::value && is_count_signature<object_type, string_type>::value &&
2632
                                is_subcription_operator_signature<object_type, string_type>::value &&
2633
                                is_at_const_signature<object_type, value_type, string_type>::value;
2634
                };
2635

2636
                template<typename value_type, typename array_type>
2637
                struct is_valid_json_array {
2638
                        template<typename T>
2639
                        using value_type_t = typename T::value_type;
2640
                        using front_base_type = typename std::decay<decltype(std::declval<array_type>().front())>::type;
2641

2642
                        static constexpr auto value = std::is_constructible<value_type, array_type>::value &&
2643
                                                                                  is_iterable<array_type>::value &&
2644
                                                                                  is_detected<value_type_t, array_type>::value &&
2645
                                                                                  std::is_same<typename array_type::value_type, value_type>::value &&
2646
                                                                                  std::is_same<front_base_type, value_type>::value;
2647
                };
2648

2649
                template<typename string_type, typename integer_type>
2650
                using is_substr_start_end_index_signature =
2651
                        typename std::is_same<decltype(std::declval<string_type>().substr(
2652
                                                                          static_cast<size_t>(std::declval<integer_type>()),
2653
                                                                          static_cast<size_t>(std::declval<integer_type>()))),
2654
                                                                  string_type>;
2655

2656
                template<typename string_type, typename integer_type>
2657
                using is_substr_start_index_signature =
2658
                        typename std::is_same<decltype(std::declval<string_type>().substr(
2659
                                                                          static_cast<size_t>(std::declval<integer_type>()))),
2660
                                                                  string_type>;
2661

2662
                template<typename string_type>
2663
                using is_std_operate_plus_signature =
2664
                        typename std::is_same<decltype(std::operator+(std::declval<string_type>(), std::declval<string_type>())),
2665
                                                                  string_type>;
2666

2667
                template<typename value_type, typename string_type, typename integer_type>
2668
                struct is_valid_json_string {
2669
                        static constexpr auto substr = is_substr_start_end_index_signature<string_type, integer_type>::value &&
2670
                                                                                   is_substr_start_index_signature<string_type, integer_type>::value;
2671
                        static_assert(substr, "string_type must have a substr method taking only a start index and an overload "
2672
                                                                  "taking a start and end index, both must return a string_type");
2673

2674
                        static constexpr auto operator_plus = is_std_operate_plus_signature<string_type>::value;
2675
                        static_assert(operator_plus,
2676
                                                  "string_type must have a '+' operator implemented which returns the concatenated string");
2677

2678
                        static constexpr auto value =
2679
                                std::is_constructible<value_type, string_type>::value && substr && operator_plus;
2680
                };
2681

2682
                template<typename value_type, typename number_type>
2683
                struct is_valid_json_number {
2684
                        static constexpr auto value =
2685
                                std::is_floating_point<number_type>::value && std::is_constructible<value_type, number_type>::value;
2686
                };
2687

2688
                template<typename value_type, typename integer_type>
2689
                struct is_valid_json_integer {
2690
                        static constexpr auto value = std::is_signed<integer_type>::value &&
2691
                                                                                  !std::is_floating_point<integer_type>::value &&
2692
                                                                                  std::is_constructible<value_type, integer_type>::value;
2693
                };
2694
                template<typename value_type, typename boolean_type>
2695
                struct is_valid_json_boolean {
2696
                        static constexpr auto value = std::is_convertible<boolean_type, bool>::value &&
2697
                                                                                  std::is_constructible<value_type, boolean_type>::value;
2698
                };
2699

2700
                template<typename value_type, typename object_type, typename array_type, typename string_type,
2701
                                 typename number_type, typename integer_type, typename boolean_type>
2702
                struct is_valid_json_types {
2703
                        // Internal assertions for better feedback
2704
                        static_assert(is_valid_json_value<value_type>::value,
2705
                                                  "value_type must meet basic requirements, default constructor, copyable, moveable");
2706
                        static_assert(is_valid_json_object<value_type, string_type, object_type>::value,
2707
                                                  "object_type must be a string_type to value_type container");
2708
                        static_assert(is_valid_json_array<value_type, array_type>::value,
2709
                                                  "array_type must be a container of value_type");
2710

2711
                        static constexpr auto value = is_valid_json_value<value_type>::value &&
2712
                                                                                  is_valid_json_object<value_type, string_type, object_type>::value &&
2713
                                                                                  is_valid_json_array<value_type, array_type>::value &&
2714
                                                                                  is_valid_json_string<value_type, string_type, integer_type>::value &&
2715
                                                                                  is_valid_json_number<value_type, number_type>::value &&
2716
                                                                                  is_valid_json_integer<value_type, integer_type>::value &&
2717
                                                                                  is_valid_json_boolean<value_type, boolean_type>::value;
2718
                };
2719
        } // namespace details
2720

2721
        /**
2722
         * \brief a class to store a generic JSON value as claim
2723
         *
2724
         * \tparam json_traits : JSON implementation traits
2725
         *
2726
         * \see [RFC 7519: JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7519)
2727
         */
2728
        template<typename json_traits>
2729
        class basic_claim {
2730
                /**
2731
                 * The reason behind this is to provide an expressive abstraction without
2732
                 * over complicating the API. For more information take the time to read
2733
                 * https://github.com/nlohmann/json/issues/774. It maybe be expanded to
2734
                 * support custom string types.
2735
                 */
2736
                static_assert(std::is_same<typename json_traits::string_type, std::string>::value ||
2737
                                                  std::is_convertible<typename json_traits::string_type, std::string>::value ||
2738
                                                  std::is_constructible<typename json_traits::string_type, std::string>::value,
2739
                                          "string_type must be a std::string, convertible to a std::string, or construct a std::string.");
2740

2741
                static_assert(
2742
                        details::is_valid_json_types<typename json_traits::value_type, typename json_traits::object_type,
2743
                                                                                 typename json_traits::array_type, typename json_traits::string_type,
2744
                                                                                 typename json_traits::number_type, typename json_traits::integer_type,
2745
                                                                                 typename json_traits::boolean_type>::value,
2746
                        "must satisfy json container requirements");
2747
                static_assert(details::is_valid_traits<json_traits>::value, "traits must satisfy requirements");
2748

2749
                typename json_traits::value_type val;
2750

2751
        public:
2752
                /**
2753
                 * Order list of strings
2754
                 */
2755
                using set_t = std::set<typename json_traits::string_type>;
2756

2757
                basic_claim() = default;
66✔
2758
                basic_claim(const basic_claim&) = default;
895✔
2759
                basic_claim(basic_claim&&) = default;
149✔
2760
                basic_claim& operator=(const basic_claim&) = default;
2761
                basic_claim& operator=(basic_claim&&) = default;
2762
                ~basic_claim() = default;
2,413✔
2763

2764
                JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::string_type s) : val(std::move(s)) {}
200✔
2765
                JWT_CLAIM_EXPLICIT basic_claim(const date& d)
66✔
2766
                        : val(typename json_traits::integer_type(
72✔
2767
                                  std::chrono::duration_cast<std::chrono::seconds>(d.time_since_epoch()).count())) {}
132✔
2768
                JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::array_type a) : val(std::move(a)) {}
2769
                JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::value_type v) : val(std::move(v)) {}
1,016✔
2770
                JWT_CLAIM_EXPLICIT basic_claim(const set_t& s) : val(typename json_traits::array_type(s.begin(), s.end())) {}
9✔
2771
                template<typename Iterator>
2772
                basic_claim(Iterator begin, Iterator end) : val(typename json_traits::array_type(begin, end)) {}
36✔
2773

2774
                /**
2775
                 * Get wrapped JSON value
2776
                 * \return Wrapped JSON value
2777
                 */
2778
                typename json_traits::value_type to_json() const { return val; }
108✔
2779

2780
                /**
2781
                 * Parse input stream into underlying JSON value
2782
                 * \return input stream
2783
                 */
2784
                std::istream& operator>>(std::istream& is) { return is >> val; }
18✔
2785

2786
                /**
2787
                 * Serialize claim to output stream from wrapped JSON value
2788
                 * \return output stream
2789
                 */
2790
                std::ostream& operator<<(std::ostream& os) { return os << val; }
2791

2792
                /**
2793
                 * Get type of contained JSON value
2794
                 * \return Type
2795
                 * \throw std::logic_error An internal error occurred
2796
                 */
2797
                json::type get_type() const { return json_traits::get_type(val); }
620✔
2798

2799
                /**
2800
                 * Get the contained JSON value as a string
2801
                 * \return content as string
2802
                 * \throw std::bad_cast Content was not a string
2803
                 */
2804
                typename json_traits::string_type as_string() const { return json_traits::as_string(val); }
930✔
2805

2806
                /**
2807
                 * \brief Get the contained JSON value as a date
2808
                 *
2809
                 * If the value is a decimal, it is rounded to the closest integer
2810
                 *
2811
                 * \return content as date
2812
                 * \throw std::bad_cast Content was not a date
2813
                 */
2814
                date as_date() const {
118✔
2815
                        using std::chrono::system_clock;
2816
                        if (get_type() == json::type::number)
118✔
2817
                                return date(std::chrono::seconds(static_cast<int64_t>(std::llround(as_number()))));
×
2818
                        return date(std::chrono::seconds(as_integer()));
118✔
2819
                }
2820

2821
                /**
2822
                 * Get the contained JSON value as an array
2823
                 * \return content as array
2824
                 * \throw std::bad_cast Content was not an array
2825
                 */
2826
                typename json_traits::array_type as_array() const { return json_traits::as_array(val); }
43✔
2827

2828
                /**
2829
                 * Get the contained JSON value as a set of strings
2830
                 * \return content as set of strings
2831
                 * \throw std::bad_cast Content was not an array of string
2832
                 */
2833
                set_t as_set() const {
6✔
2834
                        set_t res;
6✔
2835
                        for (const auto& e : json_traits::as_array(val)) {
18✔
2836
                                res.insert(json_traits::as_string(e));
12✔
2837
                        }
2838
                        return res;
6✔
2839
                }
×
2840

2841
                /**
2842
                 * Get the contained JSON value as an integer
2843
                 * \return content as int
2844
                 * \throw std::bad_cast Content was not an int
2845
                 */
2846
                typename json_traits::integer_type as_integer() const { return json_traits::as_integer(val); }
125✔
2847

2848
                /**
2849
                 * Get the contained JSON value as a bool
2850
                 * \return content as bool
2851
                 * \throw std::bad_cast Content was not a bool
2852
                 */
2853
                typename json_traits::boolean_type as_boolean() const { return json_traits::as_boolean(val); }
1✔
2854

2855
                /**
2856
                 * Get the contained JSON value as a number
2857
                 * \return content as double
2858
                 * \throw std::bad_cast Content was not a number
2859
                 */
2860
                typename json_traits::number_type as_number() const { return json_traits::as_number(val); }
1✔
2861
        };
2862

2863
        namespace error {
2864
                /**
2865
                 * Attempt to parse JSON was unsuccessful
2866
                 */
2867
                struct invalid_json_exception : public std::runtime_error {
2868
                        invalid_json_exception() : runtime_error("invalid json") {}
13✔
2869
                };
2870
                /**
2871
                 * Attempt to access claim was unsuccessful
2872
                 */
2873
                struct claim_not_present_exception : public std::out_of_range {
2874
                        claim_not_present_exception() : out_of_range("claim not found") {}
31✔
2875
                };
2876
        } // namespace error
2877

2878
        namespace details {
2879
                template<typename json_traits>
2880
                struct map_of_claims {
2881
                        typename json_traits::object_type claims;
2882
                        using basic_claim_t = basic_claim<json_traits>;
2883
                        using iterator = typename json_traits::object_type::iterator;
2884
                        using const_iterator = typename json_traits::object_type::const_iterator;
2885

2886
                        map_of_claims() = default;
780✔
2887
                        map_of_claims(const map_of_claims&) = default;
132✔
2888
                        map_of_claims(map_of_claims&&) = default;
2889
                        map_of_claims& operator=(const map_of_claims&) = default;
2890
                        map_of_claims& operator=(map_of_claims&&) = default;
732✔
2891

2892
                        map_of_claims(typename json_traits::object_type json) : claims(std::move(json)) {}
787✔
2893

2894
                        iterator begin() { return claims.begin(); }
2895
                        iterator end() { return claims.end(); }
2896
                        const_iterator cbegin() const { return claims.begin(); }
2897
                        const_iterator cend() const { return claims.end(); }
2898
                        const_iterator begin() const { return claims.begin(); }
2899
                        const_iterator end() const { return claims.end(); }
2900

2901
                        /**
2902
                         * \brief Parse a JSON string into a map of claims
2903
                         *
2904
                         * The implication is that a "map of claims" is identic to a JSON object
2905
                         *
2906
                         * \param str JSON data to be parse as an object
2907
                         * \return content as JSON object
2908
                         */
2909
                        static typename json_traits::object_type parse_claims(const typename json_traits::string_type& str) {
746✔
2910
                                typename json_traits::value_type val;
746✔
2911
                                if (!json_traits::parse(val, str)) throw error::invalid_json_exception();
746✔
2912

2913
                                return json_traits::as_object(val);
1,460✔
2914
                        };
746✔
2915

2916
                        /**
2917
                         * Check if a claim is present in the map
2918
                         * \return true if claim was present, false otherwise
2919
                         */
2920
                        bool has_claim(const typename json_traits::string_type& name) const noexcept {
2,479✔
2921
                                return claims.count(name) != 0;
2,479✔
2922
                        }
2923

2924
                        /**
2925
                         * Get a claim by name
2926
                         *
2927
                         * \param name the name of the desired claim
2928
                         * \return Requested claim
2929
                         * \throw jwt::error::claim_not_present_exception if the claim was not present
2930
                         */
2931
                        basic_claim_t get_claim(const typename json_traits::string_type& name) const {
1,007✔
2932
                                if (!has_claim(name)) throw error::claim_not_present_exception();
1,007✔
2933
                                return basic_claim_t{claims.at(name)};
988✔
2934
                        }
2935
                };
2936
        } // namespace details
2937

2938
        /**
2939
         * Base class that represents a token payload.
2940
         * Contains Convenience accessors for common claims.
2941
         */
2942
        template<typename json_traits>
2943
        class payload {
2944
        protected:
2945
                details::map_of_claims<json_traits> payload_claims;
2946

2947
        public:
2948
                using basic_claim_t = basic_claim<json_traits>;
2949

2950
                /**
2951
                 * Check if issuer is present ("iss")
2952
                 * \return true if present, false otherwise
2953
                 */
2954
                bool has_issuer() const noexcept { return has_payload_claim("iss"); }
105✔
2955
                /**
2956
                 * Check if subject is present ("sub")
2957
                 * \return true if present, false otherwise
2958
                 */
2959
                bool has_subject() const noexcept { return has_payload_claim("sub"); }
105✔
2960
                /**
2961
                 * Check if audience is present ("aud")
2962
                 * \return true if present, false otherwise
2963
                 */
2964
                bool has_audience() const noexcept { return has_payload_claim("aud"); }
87✔
2965
                /**
2966
                 * Check if expires is present ("exp")
2967
                 * \return true if present, false otherwise
2968
                 */
2969
                bool has_expires_at() const noexcept { return has_payload_claim("exp"); }
822✔
2970
                /**
2971
                 * Check if not before is present ("nbf")
2972
                 * \return true if present, false otherwise
2973
                 */
2974
                bool has_not_before() const noexcept { return has_payload_claim("nbf"); }
930✔
2975
                /**
2976
                 * Check if issued at is present ("iat")
2977
                 * \return true if present, false otherwise
2978
                 */
2979
                bool has_issued_at() const noexcept { return has_payload_claim("iat"); }
858✔
2980
                /**
2981
                 * Check if token id is present ("jti")
2982
                 * \return true if present, false otherwise
2983
                 */
2984
                bool has_id() const noexcept { return has_payload_claim("jti"); }
87✔
2985
                /**
2986
                 * Get issuer claim
2987
                 * \return issuer as string
2988
                 * \throw std::runtime_error If claim was not present
2989
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
2990
                 */
2991
                typename json_traits::string_type get_issuer() const { return get_payload_claim("iss").as_string(); }
54✔
2992
                /**
2993
                 * Get subject claim
2994
                 * \return subject as string
2995
                 * \throw std::runtime_error If claim was not present
2996
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
2997
                 */
2998
                typename json_traits::string_type get_subject() const { return get_payload_claim("sub").as_string(); }
18✔
2999
                /**
3000
                 * Get audience claim
3001
                 * \return audience as a set of strings
3002
                 * \throw std::runtime_error If claim was not present
3003
                 * \throw std::bad_cast Claim was present but not a set (Should not happen in a valid token)
3004
                 */
3005
                typename basic_claim_t::set_t get_audience() const {
17✔
3006
                        auto aud = get_payload_claim("aud");
17✔
3007
                        if (aud.get_type() == json::type::string) return {aud.as_string()};
50✔
3008

3009
                        return aud.as_set();
6✔
3010
                }
28✔
3011
                /**
3012
                 * Get expires claim
3013
                 * \return expires as a date in utc
3014
                 * \throw std::runtime_error If claim was not present
3015
                 * \throw std::bad_cast Claim was present but not a date (Should not happen in a valid token)
3016
                 */
3017
                date get_expires_at() const { return get_payload_claim("exp").as_date(); }
132✔
3018
                /**
3019
                 * Get not valid before claim
3020
                 * \return nbf date in utc
3021
                 * \throw std::runtime_error If claim was not present
3022
                 * \throw std::bad_cast Claim was present but not a date (Should not happen in a valid token)
3023
                 */
3024
                date get_not_before() const { return get_payload_claim("nbf").as_date(); }
72✔
3025
                /**
3026
                 * Get issued at claim
3027
                 * \return issued at as date in utc
3028
                 * \throw std::runtime_error If claim was not present
3029
                 * \throw std::bad_cast Claim was present but not a date (Should not happen in a valid token)
3030
                 */
3031
                date get_issued_at() const { return get_payload_claim("iat").as_date(); }
132✔
3032
                /**
3033
                 * Get id claim
3034
                 * \return id as string
3035
                 * \throw std::runtime_error If claim was not present
3036
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
3037
                 */
3038
                typename json_traits::string_type get_id() const { return get_payload_claim("jti").as_string(); }
3039
                /**
3040
                 * Check if a payload claim is present
3041
                 * \return true if claim was present, false otherwise
3042
                 */
3043
                bool has_payload_claim(const typename json_traits::string_type& name) const noexcept {
1,171✔
3044
                        return payload_claims.has_claim(name);
1,171✔
3045
                }
3046
                /**
3047
                 * Get payload claim
3048
                 * \return Requested claim
3049
                 * \throw std::runtime_error If claim was not present
3050
                 */
3051
                basic_claim_t get_payload_claim(const typename json_traits::string_type& name) const {
153✔
3052
                        return payload_claims.get_claim(name);
153✔
3053
                }
3054
        };
3055

3056
        /**
3057
         * Base class that represents a token header.
3058
         * Contains Convenience accessors for common claims.
3059
         */
3060
        template<typename json_traits>
3061
        class header {
3062
        protected:
3063
                details::map_of_claims<json_traits> header_claims;
3064

3065
        public:
3066
                using basic_claim_t = basic_claim<json_traits>;
3067
                /**
3068
                 * Check if algorithm is present ("alg")
3069
                 * \return true if present, false otherwise
3070
                 */
3071
                bool has_algorithm() const noexcept { return has_header_claim("alg"); }
105✔
3072
                /**
3073
                 * Check if type is present ("typ")
3074
                 * \return true if present, false otherwise
3075
                 */
3076
                bool has_type() const noexcept { return has_header_claim("typ"); }
105✔
3077
                /**
3078
                 * Check if content type is present ("cty")
3079
                 * \return true if present, false otherwise
3080
                 */
3081
                bool has_content_type() const noexcept { return has_header_claim("cty"); }
87✔
3082
                /**
3083
                 * Check if key id is present ("kid")
3084
                 * \return true if present, false otherwise
3085
                 */
3086
                bool has_key_id() const noexcept { return has_header_claim("kid"); }
87✔
3087
                /**
3088
                 * Get algorithm claim
3089
                 * \return algorithm as string
3090
                 * \throw std::runtime_error If claim was not present
3091
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
3092
                 */
3093
                typename json_traits::string_type get_algorithm() const { return get_header_claim("alg").as_string(); }
1,419✔
3094
                /**
3095
                 * Get type claim
3096
                 * \return type as a string
3097
                 * \throw std::runtime_error If claim was not present
3098
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
3099
                 */
3100
                typename json_traits::string_type get_type() const { return get_header_claim("typ").as_string(); }
105✔
3101
                /**
3102
                 * Get content type claim
3103
                 * \return content type as string
3104
                 * \throw std::runtime_error If claim was not present
3105
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
3106
                 */
3107
                typename json_traits::string_type get_content_type() const { return get_header_claim("cty").as_string(); }
3108
                /**
3109
                 * Get key id claim
3110
                 * \return key id as string
3111
                 * \throw std::runtime_error If claim was not present
3112
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
3113
                 */
3114
                typename json_traits::string_type get_key_id() const { return get_header_claim("kid").as_string(); }
3115
                /**
3116
                 * Check if a header claim is present
3117
                 * \return true if claim was present, false otherwise
3118
                 */
3119
                bool has_header_claim(const typename json_traits::string_type& name) const noexcept {
146✔
3120
                        return header_claims.has_claim(name);
146✔
3121
                }
3122
                /**
3123
                 * Get header claim
3124
                 * \return Requested claim
3125
                 * \throw std::runtime_error If claim was not present
3126
                 */
3127
                basic_claim_t get_header_claim(const typename json_traits::string_type& name) const {
508✔
3128
                        return header_claims.get_claim(name);
508✔
3129
                }
3130
        };
3131

3132
        /**
3133
         * Class containing all information about a decoded token
3134
         */
3135
        template<typename json_traits>
3136
        class decoded_jwt : public header<json_traits>, public payload<json_traits> {
3137
        protected:
3138
                /// Unmodified token, as passed to constructor
3139
                typename json_traits::string_type token;
3140
                /// Header part decoded from base64
3141
                typename json_traits::string_type header;
3142
                /// Unmodified header part in base64
3143
                typename json_traits::string_type header_base64;
3144
                /// Payload part decoded from base64
3145
                typename json_traits::string_type payload;
3146
                /// Unmodified payload part in base64
3147
                typename json_traits::string_type payload_base64;
3148
                /// Signature part decoded from base64
3149
                typename json_traits::string_type signature;
3150
                /// Unmodified signature part in base64
3151
                typename json_traits::string_type signature_base64;
3152

3153
        public:
3154
                using basic_claim_t = basic_claim<json_traits>;
3155
#ifndef JWT_DISABLE_BASE64
3156
                /**
3157
                 * \brief Parses a given token
3158
                 *
3159
                 * \note Decodes using the jwt::base64url which supports an std::string
3160
                 *
3161
                 * \param token The token to parse
3162
                 * \throw std::invalid_argument Token is not in correct format
3163
                 * \throw std::runtime_error Base64 decoding failed or invalid json
3164
                 */
3165
                JWT_CLAIM_EXPLICIT decoded_jwt(const typename json_traits::string_type& token)
390✔
3166
                        : decoded_jwt(token, [](const typename json_traits::string_type& str) {
1,104✔
3167
                                  return base::decode<alphabet::base64url>(base::pad<alphabet::base64url>(str));
1,104✔
3168
                          }) {}
390✔
3169
#endif
3170
                /**
3171
                 * \brief Parses a given token
3172
                 *
3173
                 * \tparam Decode is callable, taking a string_type and returns a string_type.
3174
                 * It should ensure the padding of the input and then base64url decode and
3175
                 * return the results.
3176
                 * \param token The token to parse
3177
                 * \param decode The function to decode the token
3178
                 * \throw std::invalid_argument Token is not in correct format
3179
                 * \throw std::runtime_error Base64 decoding failed or invalid json
3180
                 */
3181
                template<typename Decode>
3182
                decoded_jwt(const typename json_traits::string_type& token, Decode decode) : token(token) {
390✔
3183
                        auto hdr_end = token.find('.');
390✔
3184
                        if (hdr_end == json_traits::string_type::npos) throw std::invalid_argument("invalid token supplied");
390✔
3185
                        auto payload_end = token.find('.', hdr_end + 1);
384✔
3186
                        if (payload_end == json_traits::string_type::npos) throw std::invalid_argument("invalid token supplied");
384✔
3187
                        header_base64 = token.substr(0, hdr_end);
372✔
3188
                        payload_base64 = token.substr(hdr_end + 1, payload_end - hdr_end - 1);
372✔
3189
                        signature_base64 = token.substr(payload_end + 1);
372✔
3190

3191
                        header = decode(header_base64);
372✔
3192
                        payload = decode(payload_base64);
366✔
3193
                        signature = decode(signature_base64);
366✔
3194

3195
                        this->header_claims = details::map_of_claims<json_traits>::parse_claims(header);
366✔
3196
                        this->payload_claims = details::map_of_claims<json_traits>::parse_claims(payload);
360✔
3197
                }
630✔
3198

3199
                /**
3200
                 * Get token string, as passed to constructor
3201
                 * \return token as passed to constructor
3202
                 */
3203
                const typename json_traits::string_type& get_token() const noexcept { return token; }
6✔
3204
                /**
3205
                 * Get header part as json string
3206
                 * \return header part after base64 decoding
3207
                 */
3208
                const typename json_traits::string_type& get_header() const noexcept { return header; }
3209
                /**
3210
                 * Get payload part as json string
3211
                 * \return payload part after base64 decoding
3212
                 */
3213
                const typename json_traits::string_type& get_payload() const noexcept { return payload; }
3214
                /**
3215
                 * Get signature part as json string
3216
                 * \return signature part after base64 decoding
3217
                 */
3218
                const typename json_traits::string_type& get_signature() const noexcept { return signature; }
432✔
3219
                /**
3220
                 * Get header part as base64 string
3221
                 * \return header part before base64 decoding
3222
                 */
3223
                const typename json_traits::string_type& get_header_base64() const noexcept { return header_base64; }
432✔
3224
                /**
3225
                 * Get payload part as base64 string
3226
                 * \return payload part before base64 decoding
3227
                 */
3228
                const typename json_traits::string_type& get_payload_base64() const noexcept { return payload_base64; }
432✔
3229
                /**
3230
                 * Get signature part as base64 string
3231
                 * \return signature part before base64 decoding
3232
                 */
3233
                const typename json_traits::string_type& get_signature_base64() const noexcept { return signature_base64; }
3234
                /**
3235
                 * Get all payload as JSON object
3236
                 * \return map of claims
3237
                 */
3238
                typename json_traits::object_type get_payload_json() const { return this->payload_claims.claims; }
3239
                /**
3240
                 * Get all header as JSON object
3241
                 * \return map of claims
3242
                 */
3243
                typename json_traits::object_type get_header_json() const { return this->header_claims.claims; }
3244
                /**
3245
                 * Get a payload claim by name
3246
                 *
3247
                 * \param name the name of the desired claim
3248
                 * \return Requested claim
3249
                 * \throw jwt::error::claim_not_present_exception if the claim was not present
3250
                 */
3251
                basic_claim_t get_payload_claim(const typename json_traits::string_type& name) const {
155✔
3252
                        return this->payload_claims.get_claim(name);
155✔
3253
                }
3254
                /**
3255
                 * Get a header claim by name
3256
                 *
3257
                 * \param name the name of the desired claim
3258
                 * \return Requested claim
3259
                 * \throw jwt::error::claim_not_present_exception if the claim was not present
3260
                 */
3261
                basic_claim_t get_header_claim(const typename json_traits::string_type& name) const {
24✔
3262
                        return this->header_claims.get_claim(name);
24✔
3263
                }
3264
        };
3265

3266
        /**
3267
         * Builder class to build and sign a new token
3268
         * Use jwt::create() to get an instance of this class.
3269
         */
3270
        template<typename Clock, typename json_traits>
3271
        class builder {
3272
                typename json_traits::object_type header_claims;
3273
                typename json_traits::object_type payload_claims;
3274

3275
                /// Instance of clock type
3276
                Clock clock;
3277

3278
        public:
3279
                /**
3280
                 * Constructor for building a new builder instance
3281
                 * \param c Clock instance
3282
                 */
3283
                JWT_CLAIM_EXPLICIT builder(Clock c) : clock(c) {}
235✔
3284
                /**
3285
                 * Set a header claim.
3286
                 * \param id Name of the claim
3287
                 * \param c Claim to add
3288
                 * \return *this to allow for method chaining
3289
                 */
3290
                builder& set_header_claim(const typename json_traits::string_type& id, typename json_traits::value_type c) {
162✔
3291
                        header_claims[id] = std::move(c);
162✔
3292
                        return *this;
162✔
3293
                }
3294

3295
                /**
3296
                 * Set a header claim.
3297
                 * \param id Name of the claim
3298
                 * \param c Claim to add
3299
                 * \return *this to allow for method chaining
3300
                 */
3301
                builder& set_header_claim(const typename json_traits::string_type& id, basic_claim<json_traits> c) {
3302
                        header_claims[id] = c.to_json();
3303
                        return *this;
3304
                }
3305
                /**
3306
                 * Set a payload claim.
3307
                 * \param id Name of the claim
3308
                 * \param c Claim to add
3309
                 * \return *this to allow for method chaining
3310
                 */
3311
                builder& set_payload_claim(const typename json_traits::string_type& id, typename json_traits::value_type c) {
177✔
3312
                        payload_claims[id] = std::move(c);
177✔
3313
                        return *this;
177✔
3314
                }
3315
                /**
3316
                 * Set a payload claim.
3317
                 * \param id Name of the claim
3318
                 * \param c Claim to add
3319
                 * \return *this to allow for method chaining
3320
                 */
3321
                builder& set_payload_claim(const typename json_traits::string_type& id, basic_claim<json_traits> c) {
88✔
3322
                        payload_claims[id] = c.to_json();
88✔
3323
                        return *this;
88✔
3324
                }
3325
                /**
3326
                 * \brief Set algorithm claim
3327
                 * You normally don't need to do this, as the algorithm is automatically set if you don't change it.
3328
                 *
3329
                 * \param str Name of algorithm
3330
                 * \return *this to allow for method chaining
3331
                 */
3332
                builder& set_algorithm(typename json_traits::string_type str) {
6✔
3333
                        return set_header_claim("alg", typename json_traits::value_type(str));
18✔
3334
                }
3335
                /**
3336
                 * Set type claim
3337
                 * \param str Type to set
3338
                 * \return *this to allow for method chaining
3339
                 */
3340
                builder& set_type(typename json_traits::string_type str) {
156✔
3341
                        return set_header_claim("typ", typename json_traits::value_type(str));
468✔
3342
                }
3343
                /**
3344
                 * Set content type claim
3345
                 * \param str Type to set
3346
                 * \return *this to allow for method chaining
3347
                 */
3348
                builder& set_content_type(typename json_traits::string_type str) {
3349
                        return set_header_claim("cty", typename json_traits::value_type(str));
3350
                }
3351
                /**
3352
                 * \brief Set key id claim
3353
                 *
3354
                 * \param str Key id to set
3355
                 * \return *this to allow for method chaining
3356
                 */
3357
                builder& set_key_id(typename json_traits::string_type str) {
3358
                        return set_header_claim("kid", typename json_traits::value_type(str));
3359
                }
3360
                /**
3361
                 * Set issuer claim
3362
                 * \param str Issuer to set
3363
                 * \return *this to allow for method chaining
3364
                 */
3365
                builder& set_issuer(typename json_traits::string_type str) {
147✔
3366
                        return set_payload_claim("iss", typename json_traits::value_type(str));
441✔
3367
                }
3368
                /**
3369
                 * Set subject claim
3370
                 * \param str Subject to set
3371
                 * \return *this to allow for method chaining
3372
                 */
3373
                builder& set_subject(typename json_traits::string_type str) {
3374
                        return set_payload_claim("sub", typename json_traits::value_type(str));
3375
                }
3376
                /**
3377
                 * Set audience claim
3378
                 * \param a Audience set
3379
                 * \return *this to allow for method chaining
3380
                 */
3381
                builder& set_audience(typename json_traits::array_type a) {
6✔
3382
                        return set_payload_claim("aud", typename json_traits::value_type(a));
18✔
3383
                }
3384
                /**
3385
                 * Set audience claim
3386
                 * \param aud Single audience
3387
                 * \return *this to allow for method chaining
3388
                 */
3389
                builder& set_audience(typename json_traits::string_type aud) {
18✔
3390
                        return set_payload_claim("aud", typename json_traits::value_type(aud));
54✔
3391
                }
3392
                /**
3393
                 * Set expires at claim
3394
                 * \param d Expires time
3395
                 * \return *this to allow for method chaining
3396
                 */
3397
                builder& set_expires_at(const date& d) { return set_payload_claim("exp", basic_claim<json_traits>(d)); }
66✔
3398
                /**
3399
                 * Set expires at claim to @p d from the current moment
3400
                 * \param d token expiration timeout
3401
                 * \return *this to allow for method chaining
3402
                 */
3403
                template<class Rep, class Period>
3404
                builder& set_expires_in(const std::chrono::duration<Rep, Period>& d) {
5✔
3405
                        return set_payload_claim("exp", basic_claim<json_traits>(clock.now() + d));
15✔
3406
                }
3407
                /**
3408
                 * Set not before claim
3409
                 * \param d First valid time
3410
                 * \return *this to allow for method chaining
3411
                 */
3412
                builder& set_not_before(const date& d) { return set_payload_claim("nbf", basic_claim<json_traits>(d)); }
36✔
3413
                /**
3414
                 * Set issued at claim
3415
                 * \param d Issued at time, should be current time
3416
                 * \return *this to allow for method chaining
3417
                 */
3418
                builder& set_issued_at(const date& d) { return set_payload_claim("iat", basic_claim<json_traits>(d)); }
81✔
3419
                /**
3420
                 * Set issued at claim to the current moment
3421
                 * \return *this to allow for method chaining
3422
                 */
3423
                builder& set_issued_now() { return set_issued_at(clock.now()); }
5✔
3424
                /**
3425
                 * Set id claim
3426
                 * \param str ID to set
3427
                 * \return *this to allow for method chaining
3428
                 */
3429
                builder& set_id(const typename json_traits::string_type& str) {
3430
                        return set_payload_claim("jti", typename json_traits::value_type(str));
3431
                }
3432

3433
                /**
3434
                 * Sign token and return result
3435
                 * \tparam Algo Callable method which takes a string_type and return the signed input as a string_type
3436
                 * \tparam Encode Callable method which takes a string_type and base64url safe encodes it,
3437
                 * MUST return the result with no padding; trim the result.
3438
                 * \param algo Instance of an algorithm to sign the token with
3439
                 * \param encode Callable to transform the serialized json to base64 with no padding
3440
                 * \return Final token as a string
3441
                 *
3442
                 * \note If the 'alg' header in not set in the token it will be set to `algo.name()`
3443
                 */
3444
                template<typename Algo, typename Encode>
3445
                typename json_traits::string_type sign(const Algo& algo, Encode encode) const {
3446
                        std::error_code ec;
3447
                        auto res = sign(algo, encode, ec);
3448
                        error::throw_if_error(ec);
3449
                        return res;
3450
                }
3451
#ifndef JWT_DISABLE_BASE64
3452
                /**
3453
                 * Sign token and return result
3454
                 *
3455
                 * using the `jwt::base` functions provided
3456
                 *
3457
                 * \param algo Instance of an algorithm to sign the token with
3458
                 * \return Final token as a string
3459
                 */
3460
                template<typename Algo>
3461
                typename json_traits::string_type sign(const Algo& algo) const {
235✔
3462
                        std::error_code ec;
235✔
3463
                        auto res = sign(algo, ec);
235✔
3464
                        error::throw_if_error(ec);
235✔
3465
                        return res;
422✔
3466
                }
24✔
3467
#endif
3468

3469
                /**
3470
                 * Sign token and return result
3471
                 * \tparam Algo Callable method which takes a string_type and return the signed input as a string_type
3472
                 * \tparam Encode Callable method which takes a string_type and base64url safe encodes it,
3473
                 * MUST return the result with no padding; trim the result.
3474
                 * \param algo Instance of an algorithm to sign the token with
3475
                 * \param encode Callable to transform the serialized json to base64 with no padding
3476
                 * \param ec error_code filled with details on error
3477
                 * \return Final token as a string
3478
                 *
3479
                 * \note If the 'alg' header in not set in the token it will be set to `algo.name()`
3480
                 */
3481
                template<typename Algo, typename Encode>
3482
                typename json_traits::string_type sign(const Algo& algo, Encode encode, std::error_code& ec) const {
235✔
3483
                        // make a copy such that a builder can be re-used
3484
                        typename json_traits::object_type obj_header = header_claims;
235✔
3485
                        if (header_claims.count("alg") == 0) obj_header["alg"] = typename json_traits::value_type(algo.name());
1,113✔
3486

3487
                        const auto header = encode(json_traits::serialize(typename json_traits::value_type(obj_header)));
235✔
3488
                        const auto payload = encode(json_traits::serialize(typename json_traits::value_type(payload_claims)));
235✔
3489
                        const auto token = header + "." + payload;
235✔
3490

3491
                        auto signature = algo.sign(token, ec);
235✔
3492
                        if (ec) return {};
235✔
3493

3494
                        return token + "." + encode(signature);
211✔
3495
                }
235✔
3496
#ifndef JWT_DISABLE_BASE64
3497
                /**
3498
                 * Sign token and return result
3499
                 *
3500
                 * using the `jwt::base` functions provided
3501
                 *
3502
                 * \param algo Instance of an algorithm to sign the token with
3503
                 * \param ec error_code filled with details on error
3504
                 * \return Final token as a string
3505
                 */
3506
                template<typename Algo>
3507
                typename json_traits::string_type sign(const Algo& algo, std::error_code& ec) const {
235✔
3508
                        return sign(
3509
                                algo,
3510
                                [](const typename json_traits::string_type& data) {
681✔
3511
                                        return base::trim<alphabet::base64url>(base::encode<alphabet::base64url>(data));
681✔
3512
                                },
3513
                                ec);
235✔
3514
                }
3515
#endif
3516
        };
3517

3518
        namespace verify_ops {
3519
                /**
3520
                 * This is the base container which holds the token that need to be verified
3521
                 */
3522
                template<typename json_traits>
3523
                struct verify_context {
3524
                        verify_context(date ctime, const decoded_jwt<json_traits>& j, size_t l)
323✔
3525
                                : current_time(ctime), jwt(j), default_leeway(l) {}
323✔
3526
                        /// Current time, retrieved from the verifiers clock and cached for performance and consistency
3527
                        date current_time;
3528
                        /// The jwt passed to the verifier
3529
                        const decoded_jwt<json_traits>& jwt;
3530
                        /// The configured default leeway for this verification
3531
                        size_t default_leeway{0};
3532

3533
                        /// The claim key to apply this comparison on
3534
                        typename json_traits::string_type claim_key{};
3535

3536
                        /**
3537
                         * \brief Helper method to get a claim from the jwt in this context
3538
                         * \param in_header check JWT header or payload sections
3539
                         * \param ec std::error_code which will indicate if any error occure
3540
                         * \return basic_claim if it was present otherwise empty
3541
                         */
3542
                        basic_claim<json_traits> get_claim(bool in_header, std::error_code& ec) const {
191✔
3543
                                if (in_header) {
191✔
3544
                                        if (!jwt.has_header_claim(claim_key)) {
18✔
3545
                                                ec = error::token_verification_error::missing_claim;
×
3546
                                                return {};
×
3547
                                        }
3548
                                        return jwt.get_header_claim(claim_key);
18✔
3549
                                } else {
3550
                                        if (!jwt.has_payload_claim(claim_key)) {
173✔
3551
                                                ec = error::token_verification_error::missing_claim;
24✔
3552
                                                return {};
24✔
3553
                                        }
3554
                                        return jwt.get_payload_claim(claim_key);
149✔
3555
                                }
3556
                        }
3557
                        /**
3558
                         * Helper method to get a claim of a specific type from the jwt in this context
3559
                         * \param in_header check JWT header or payload sections
3560
                         * \param t the expected type of the claim
3561
                         * \param ec std::error_code which will indicate if any error occure
3562
                         * \return basic_claim if it was present otherwise empty
3563
                          */
3564
                        basic_claim<json_traits> get_claim(bool in_header, json::type t, std::error_code& ec) const {
173✔
3565
                                auto c = get_claim(in_header, ec);
173✔
3566
                                if (ec) return {};
173✔
3567
                                if (c.get_type() != t) {
155✔
3568
                                        ec = error::token_verification_error::claim_type_missmatch;
6✔
3569
                                        return {};
6✔
3570
                                }
3571
                                return c;
149✔
3572
                        }
173✔
3573
                        /**
3574
                         * \brief Helper method to get a payload claim from the jwt
3575
                         * \param ec std::error_code which will indicate if any error occure
3576
                         * \return basic_claim if it was present otherwise empty
3577
                          */
3578
                        basic_claim<json_traits> get_claim(std::error_code& ec) const { return get_claim(false, ec); }
3579
                        /**
3580
                         * \brief Helper method to get a payload claim of a specific type from the jwt
3581
                         * \param t the expected type of the claim
3582
                         * \param ec std::error_code which will indicate if any error occure
3583
                         * \return basic_claim if it was present otherwise empty
3584
                          */
3585
                        basic_claim<json_traits> get_claim(json::type t, std::error_code& ec) const {
3586
                                return get_claim(false, t, ec);
3587
                        }
3588
                };
3589

3590
                /**
3591
                 * This is the default operation and does case sensitive matching
3592
                 */
3593
                template<typename json_traits, bool in_header = false>
3594
                struct equals_claim {
3595
                        const basic_claim<json_traits> expected;
3596
                        void operator()(const verify_context<json_traits>& ctx, std::error_code& ec) const {
155✔
3597
                                auto jc = ctx.get_claim(in_header, expected.get_type(), ec);
155✔
3598
                                if (ec) return;
155✔
3599
                                const bool matches = [&]() {
×
3600
                                        switch (expected.get_type()) {
131✔
3601
                                        case json::type::boolean: return expected.as_boolean() == jc.as_boolean();
×
3602
                                        case json::type::integer: return expected.as_integer() == jc.as_integer();
×
3603
                                        case json::type::number: return expected.as_number() == jc.as_number();
×
3604
                                        case json::type::string: return expected.as_string() == jc.as_string();
121✔
3605
                                        case json::type::array:
10✔
3606
                                        case json::type::object:
3607
                                                return json_traits::serialize(expected.to_json()) == json_traits::serialize(jc.to_json());
10✔
3608
                                        default: throw std::logic_error("internal error, should be unreachable");
×
3609
                                        }
3610
                                }();
131✔
3611
                                if (!matches) {
131✔
3612
                                        ec = error::token_verification_error::claim_value_missmatch;
6✔
3613
                                        return;
6✔
3614
                                }
3615
                        }
155✔
3616
                };
3617

3618
                /**
3619
                 * Checks that the current time is before the time specified in the given
3620
                 * claim. This is identical to how the "exp" check works.
3621
                 */
3622
                template<typename json_traits, bool in_header = false>
3623
                struct date_before_claim {
3624
                        const size_t leeway;
3625
                        void operator()(const verify_context<json_traits>& ctx, std::error_code& ec) const {
3626
                                auto jc = ctx.get_claim(in_header, json::type::integer, ec);
3627
                                if (ec) return;
3628
                                auto c = jc.as_date();
3629
                                if (ctx.current_time > c + std::chrono::seconds(leeway)) {
3630
                                        ec = error::token_verification_error::token_expired;
3631
                                }
3632
                        }
3633
                };
3634

3635
                /**
3636
                 * Checks that the current time is after the time specified in the given
3637
                 * claim. This is identical to how the "nbf" and "iat" check works.
3638
                 */
3639
                template<typename json_traits, bool in_header = false>
3640
                struct date_after_claim {
3641
                        const size_t leeway;
3642
                        void operator()(const verify_context<json_traits>& ctx, std::error_code& ec) const {
3643
                                auto jc = ctx.get_claim(in_header, json::type::integer, ec);
3644
                                if (ec) return;
3645
                                auto c = jc.as_date();
3646
                                if (ctx.current_time < c - std::chrono::seconds(leeway)) {
3647
                                        ec = error::token_verification_error::token_expired;
3648
                                }
3649
                        }
3650
                };
3651

3652
                /**
3653
                 * Checks if the given set is a subset of the set inside the token.
3654
                 * If the token value is a string it is treated as a set with a single element.
3655
                 * The comparison is case sensitive.
3656
                 */
3657
                template<typename json_traits, bool in_header = false>
3658
                struct is_subset_claim {
3659
                        const typename basic_claim<json_traits>::set_t expected;
3660
                        void operator()(const verify_context<json_traits>& ctx, std::error_code& ec) const {
18✔
3661
                                auto c = ctx.get_claim(in_header, ec);
18✔
3662
                                if (ec) return;
18✔
3663
                                if (c.get_type() == json::type::string) {
12✔
3664
                                        if (expected.size() != 1 || *expected.begin() != c.as_string()) {
12✔
3665
                                                ec = error::token_verification_error::audience_missmatch;
12✔
3666
                                                return;
12✔
3667
                                        }
3668
                                } else if (c.get_type() == json::type::array) {
×
3669
                                        auto jc = c.as_set();
×
3670
                                        for (auto& e : expected) {
×
3671
                                                if (jc.find(e) == jc.end()) {
×
3672
                                                        ec = error::token_verification_error::audience_missmatch;
×
3673
                                                        return;
×
3674
                                                }
3675
                                        }
3676
                                } else {
×
3677
                                        ec = error::token_verification_error::claim_type_missmatch;
×
3678
                                        return;
×
3679
                                }
3680
                        }
18✔
3681
                };
3682

3683
                /**
3684
                 * Checks if the claim is a string and does an case insensitive comparison.
3685
                 */
3686
                template<typename json_traits, bool in_header = false>
3687
                struct insensitive_string_claim {
3688
                        const typename json_traits::string_type expected;
3689
                        std::locale locale;
3690
                        insensitive_string_claim(const typename json_traits::string_type& e, std::locale loc)
12✔
3691
                                : expected(to_lower_unicode(e, loc)), locale(loc) {}
12✔
3692

3693
                        void operator()(const verify_context<json_traits>& ctx, std::error_code& ec) const {
18✔
3694
                                const auto c = ctx.get_claim(in_header, json::type::string, ec);
18✔
3695
                                if (ec) return;
18✔
3696
                                if (to_lower_unicode(c.as_string(), locale) != expected) {
18✔
3697
                                        ec = error::token_verification_error::claim_value_missmatch;
6✔
3698
                                }
3699
                        }
18✔
3700

3701
                        static std::string to_lower_unicode(const std::string& str, const std::locale& loc) {
30✔
3702
                                std::mbstate_t state = std::mbstate_t();
30✔
3703
                                const char* in_next = str.data();
30✔
3704
                                const char* in_end = str.data() + str.size();
30✔
3705
                                std::wstring wide;
30✔
3706
                                wide.reserve(str.size());
30✔
3707

3708
                                while (in_next != in_end) {
120✔
3709
                                        wchar_t wc;
3710
                                        std::size_t result = std::mbrtowc(&wc, in_next, in_end - in_next, &state);
90✔
3711
                                        if (result == static_cast<std::size_t>(-1)) {
90✔
3712
                                                throw std::runtime_error("encoding error: " + std::string(std::strerror(errno)));
×
3713
                                        } else if (result == static_cast<std::size_t>(-2)) {
90✔
3714
                                                throw std::runtime_error("conversion error: next bytes constitute an incomplete, but so far "
×
3715
                                                                                                 "valid, multibyte character.");
3716
                                        }
3717
                                        in_next += result;
90✔
3718
                                        wide.push_back(wc);
90✔
3719
                                }
3720

3721
                                auto& f = std::use_facet<std::ctype<wchar_t>>(loc);
30✔
3722
                                f.tolower(&wide[0], &wide[0] + wide.size());
30✔
3723

3724
                                std::string out;
30✔
3725
                                out.reserve(wide.size());
30✔
3726
                                for (wchar_t wc : wide) {
120✔
3727
                                        char mb[MB_LEN_MAX];
3728
                                        std::size_t n = std::wcrtomb(mb, wc, &state);
90✔
3729
                                        if (n != static_cast<std::size_t>(-1)) out.append(mb, n);
90✔
3730
                                }
3731

3732
                                return out;
60✔
3733
                        }
30✔
3734
                };
3735
        } // namespace verify_ops
3736

3737
        /**
3738
         * Verifier class used to check if a decoded token contains all claims required by your application and has a valid
3739
         * signature.
3740
         */
3741
        template<typename Clock, typename json_traits>
3742
        class verifier {
3743
        public:
3744
                using basic_claim_t = basic_claim<json_traits>;
3745
                /**
3746
                 * \brief Verification function data structure.
3747
                 *
3748
                 * This gets passed the current verifier, a reference to the decoded jwt, a reference to the key of this claim,
3749
                 * as well as a reference to an error_code.
3750
                 * The function checks if the actual value matches certain rules (e.g. equality to value x) and sets the error_code if
3751
                 * it does not. Once a non zero error_code is encountered the verification stops and this error_code becomes the result
3752
                 * returned from verify
3753
                 */
3754
                using verify_check_fn_t =
3755
                        std::function<void(const verify_ops::verify_context<json_traits>&, std::error_code& ec)>;
3756

3757
        private:
3758
                struct algo_base {
3759
                        virtual ~algo_base() = default;
385✔
3760
                        virtual void verify(const std::string& data, const std::string& sig, std::error_code& ec) = 0;
3761
                };
3762
                template<typename T>
3763
                struct algo : public algo_base {
3764
                        T alg;
3765
                        explicit algo(T a) : alg(a) {}
385✔
3766
                        void verify(const std::string& data, const std::string& sig, std::error_code& ec) override {
426✔
3767
                                alg.verify(data, sig, ec);
426✔
3768
                        }
426✔
3769
                };
3770
                /// Required claims
3771
                std::unordered_map<typename json_traits::string_type, verify_check_fn_t> claims;
3772
                /// Leeway time for exp, nbf and iat
3773
                size_t default_leeway = 0;
3774
                /// Instance of clock type
3775
                Clock clock;
3776
                /// Supported algorithms
3777
                std::unordered_map<std::string, std::shared_ptr<algo_base>> algs;
3778

3779
        public:
3780
                /**
3781
                 * Constructor for building a new verifier instance
3782
                 * \param c Clock instance
3783
                 */
3784
                explicit verifier(Clock c) : clock(c) {
409✔
3785
                        claims["exp"] = [](const verify_ops::verify_context<json_traits>& ctx, std::error_code& ec) {
1,028✔
3786
                                if (!ctx.jwt.has_expires_at()) return;
245✔
3787
                                auto exp = ctx.jwt.get_expires_at();
44✔
3788
                                if (ctx.current_time > exp + std::chrono::seconds(ctx.default_leeway)) {
44✔
3789
                                        ec = error::token_verification_error::token_expired;
22✔
3790
                                }
3791
                        };
3792
                        claims["iat"] = [](const verify_ops::verify_context<json_traits>& ctx, std::error_code& ec) {
1,040✔
3793
                                if (!ctx.jwt.has_issued_at()) return;
257✔
3794
                                auto iat = ctx.jwt.get_issued_at();
44✔
3795
                                if (ctx.current_time < iat - std::chrono::seconds(ctx.default_leeway)) {
44✔
3796
                                        ec = error::token_verification_error::token_expired;
12✔
3797
                                }
3798
                        };
3799
                        claims["nbf"] = [](const verify_ops::verify_context<json_traits>& ctx, std::error_code& ec) {
1,064✔
3800
                                if (!ctx.jwt.has_not_before()) return;
281✔
3801
                                auto nbf = ctx.jwt.get_not_before();
24✔
3802
                                if (ctx.current_time < nbf - std::chrono::seconds(ctx.default_leeway)) {
24✔
3803
                                        ec = error::token_verification_error::token_expired;
12✔
3804
                                }
3805
                        };
3806
                }
409✔
3807

3808
                /**
3809
                 * Set default leeway to use.
3810
                 * \param leeway Default leeway to use if not specified otherwise
3811
                 * \return *this to allow chaining
3812
                 */
3813
                verifier& leeway(size_t leeway) {
3814
                        default_leeway = leeway;
3815
                        return *this;
3816
                }
3817
                /**
3818
                 * Set leeway for expires at.
3819
                 * If not specified the default leeway will be used.
3820
                 * \param leeway Set leeway to use for expires at.
3821
                 * \return *this to allow chaining
3822
                 */
3823
                verifier& expires_at_leeway(size_t leeway) {
3824
                        claims["exp"] = verify_ops::date_before_claim<json_traits>{leeway};
3825
                        return *this;
3826
                }
3827
                /**
3828
                 * Set leeway for not before.
3829
                 * If not specified the default leeway will be used.
3830
                 * \param leeway Set leeway to use for not before.
3831
                 * \return *this to allow chaining
3832
                 */
3833
                verifier& not_before_leeway(size_t leeway) {
3834
                        claims["nbf"] = verify_ops::date_after_claim<json_traits>{leeway};
3835
                        return *this;
3836
                }
3837
                /**
3838
                 * Set leeway for issued at.
3839
                 * If not specified the default leeway will be used.
3840
                 * \param leeway Set leeway to use for issued at.
3841
                 * \return *this to allow chaining
3842
                 */
3843
                verifier& issued_at_leeway(size_t leeway) {
3844
                        claims["iat"] = verify_ops::date_after_claim<json_traits>{leeway};
3845
                        return *this;
3846
                }
3847

3848
                /**
3849
                 * Set an type to check for.
3850
                 *
3851
                 * According to [RFC 7519 Section 5.1](https://datatracker.ietf.org/doc/html/rfc7519#section-5.1),
3852
                 * This parameter is ignored by JWT implementations; any processing of this parameter is performed by the JWT application.
3853
                 * Check is case sensitive.
3854
                 *
3855
                 * \param type Type Header Parameter to check for.
3856
                 * \param locale Localization functionality to use when comparing
3857
                 * \return *this to allow chaining
3858
                 */
3859
                verifier& with_type(const typename json_traits::string_type& type, std::locale locale = std::locale{}) {
12✔
3860
                        return with_claim("typ", verify_ops::insensitive_string_claim<json_traits, true>{type, std::move(locale)});
36✔
3861
                }
3862

3863
                /**
3864
                 * Set an issuer to check for.
3865
                 * Check is case sensitive.
3866
                 * \param iss Issuer to check for.
3867
                 * \return *this to allow chaining
3868
                 */
3869
                verifier& with_issuer(const typename json_traits::string_type& iss) {
183✔
3870
                        return with_claim("iss", basic_claim_t(iss));
549✔
3871
                }
3872

3873
                /**
3874
                 * Set a subject to check for.
3875
                 * Check is case sensitive.
3876
                 * \param sub Subject to check for.
3877
                 * \return *this to allow chaining
3878
                 */
3879
                verifier& with_subject(const typename json_traits::string_type& sub) {
6✔
3880
                        return with_claim("sub", basic_claim_t(sub));
18✔
3881
                }
3882
                /**
3883
                 * Set an audience to check for.
3884
                 * If any of the specified audiences is not present in the token the check fails.
3885
                 * \param aud Audience to check for.
3886
                 * \return *this to allow chaining
3887
                 */
3888
                verifier& with_audience(const typename basic_claim_t::set_t& aud) {
18✔
3889
                        claims["aud"] = verify_ops::is_subset_claim<json_traits>{aud};
54✔
3890
                        return *this;
18✔
3891
                }
18✔
3892
                /**
3893
                 * Set an audience to check for.
3894
                 * If the specified audiences is not present in the token the check fails.
3895
                 * \param aud Audience to check for.
3896
                 * \return *this to allow chaining
3897
                 */
3898
                verifier& with_audience(const typename json_traits::string_type& aud) {
12✔
3899
                        typename basic_claim_t::set_t s;
12✔
3900
                        s.insert(aud);
12✔
3901
                        return with_audience(s);
24✔
3902
                }
12✔
3903
                /**
3904
                 * Set an id to check for.
3905
                 * Check is case sensitive.
3906
                 * \param id ID to check for.
3907
                 * \return *this to allow chaining
3908
                 */
3909
                verifier& with_id(const typename json_traits::string_type& id) { return with_claim("jti", basic_claim_t(id)); }
3910

3911
                /**
3912
                 * Specify a claim to check for using the specified operation.
3913
                 * This is helpful for implementating application specific authentication checks
3914
                 * such as the one seen in partial-claim-verifier.cpp
3915
                 *
3916
                 * \snippet{trimleft} partial-claim-verifier.cpp verifier check custom claim
3917
                 *
3918
                 * \param name Name of the claim to check for
3919
                 * \param fn Function to use for verifying the claim
3920
                 * \return *this to allow chaining
3921
                 */
3922
                verifier& with_claim(const typename json_traits::string_type& name, verify_check_fn_t fn) {
229✔
3923
                        claims[name] = fn;
229✔
3924
                        return *this;
229✔
3925
                }
3926

3927
                /**
3928
                 * Specify a claim to check for equality (both type & value).
3929
                 * See the private-claims.cpp example.
3930
                 *
3931
                 * \snippet{trimleft} private-claims.cpp verify exact claim
3932
                 *
3933
                 * \param name Name of the claim to check for
3934
                 * \param c Claim to check for
3935
                 * \return *this to allow chaining
3936
                 */
3937
                verifier& with_claim(const typename json_traits::string_type& name, basic_claim_t c) {
217✔
3938
                        return with_claim(name, verify_ops::equals_claim<json_traits>{c});
217✔
3939
                }
217✔
3940

3941
                /**
3942
                 * \brief Add an algorithm available for checking.
3943
                 *
3944
                 * This is used to handle incomming tokens for predefined algorithms
3945
                 * which the authorization server is provided. For example a small system
3946
                 * where only a single RSA key-pair is used to sign tokens
3947
                 *
3948
                 * \snippet{trimleft} example/rsa-verify.cpp allow rsa algorithm
3949
                 *
3950
                 * \tparam Algorithm any algorithm such as those provided by jwt::algorithm
3951
                 * \param alg Algorithm to allow
3952
                 * \return *this to allow chaining
3953
                 */
3954
                template<typename Algorithm>
3955
                verifier& allow_algorithm(Algorithm alg) {
385✔
3956
                        algs[alg.name()] = std::make_shared<algo<Algorithm>>(alg);
385✔
3957
                        return *this;
385✔
3958
                }
3959

3960
                /**
3961
                 * Verify the given token.
3962
                 * \param jwt Token to check
3963
                 * \throw token_verification_exception Verification failed
3964
                 */
3965
                void verify(const decoded_jwt<json_traits>& jwt) const {
384✔
3966
                        std::error_code ec;
384✔
3967
                        verify(jwt, ec);
384✔
3968
                        error::throw_if_error(ec);
384✔
3969
                }
199✔
3970
                /**
3971
                 * Verify the given token.
3972
                 * \param jwt Token to check
3973
                 * \param ec error_code filled with details on error
3974
                 */
3975
                void verify(const decoded_jwt<json_traits>& jwt, std::error_code& ec) const {
432✔
3976
                        ec.clear();
432✔
3977
                        const typename json_traits::string_type data = jwt.get_header_base64() + "." + jwt.get_payload_base64();
432✔
3978
                        const typename json_traits::string_type sig = jwt.get_signature();
432✔
3979
                        const std::string algo = jwt.get_algorithm();
432✔
3980
                        if (algs.count(algo) == 0) {
432✔
3981
                                ec = error::token_verification_error::wrong_algorithm;
6✔
3982
                                return;
6✔
3983
                        }
3984
                        algs.at(algo)->verify(data, sig, ec);
426✔
3985
                        if (ec) return;
426✔
3986

3987
                        verify_ops::verify_context<json_traits> ctx{clock.now(), jwt, default_leeway};
323✔
3988
                        for (auto& c : claims) {
1,197✔
3989
                                ctx.claim_key = c.first;
974✔
3990
                                c.second(ctx, ec);
974✔
3991
                                if (ec) return;
974✔
3992
                        }
3993
                }
950✔
3994
        };
3995

3996
        /**
3997
         * \brief JSON Web Key
3998
         *
3999
         * https://tools.ietf.org/html/rfc7517
4000
         *
4001
         * A JSON object that represents a cryptographic key.  The members of
4002
         * the object represent properties of the key, including its value.
4003
         */
4004
        template<typename json_traits>
4005
        class jwk {
4006
                using basic_claim_t = basic_claim<json_traits>;
4007
                const details::map_of_claims<json_traits> jwk_claims;
4008

4009
        public:
4010
                JWT_CLAIM_EXPLICIT jwk(const typename json_traits::string_type& str)
18✔
4011
                        : jwk_claims(details::map_of_claims<json_traits>::parse_claims(str)) {}
18✔
4012

4013
                JWT_CLAIM_EXPLICIT jwk(const typename json_traits::value_type& json)
42✔
4014
                        : jwk_claims(json_traits::as_object(json)) {}
42✔
4015

4016
                /**
4017
                 * Get key type claim
4018
                 *
4019
                 * This returns the general type (e.g. RSA or EC), not a specific algorithm value.
4020
                 * \return key type as string
4021
                 * \throw std::runtime_error If claim was not present
4022
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4023
                 */
4024
                typename json_traits::string_type get_key_type() const { return get_jwk_claim("kty").as_string(); }
4025

4026
                /**
4027
                 * Get public key usage claim
4028
                 * \return usage parameter as string
4029
                 * \throw std::runtime_error If claim was not present
4030
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4031
                 */
4032
                typename json_traits::string_type get_use() const { return get_jwk_claim("use").as_string(); }
4033

4034
                /**
4035
                 * Get key operation types claim
4036
                 * \return key operation types as a set of strings
4037
                 * \throw std::runtime_error If claim was not present
4038
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4039
                 */
4040
                typename basic_claim_t::set_t get_key_operations() const { return get_jwk_claim("key_ops").as_set(); }
4041

4042
                /**
4043
                 * Get algorithm claim
4044
                 * \return algorithm as string
4045
                 * \throw std::runtime_error If claim was not present
4046
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4047
                 */
4048
                typename json_traits::string_type get_algorithm() const { return get_jwk_claim("alg").as_string(); }
36✔
4049

4050
                /**
4051
                 * Get key id claim
4052
                 * \return key id as string
4053
                 * \throw std::runtime_error If claim was not present
4054
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4055
                 */
4056
                typename json_traits::string_type get_key_id() const { return get_jwk_claim("kid").as_string(); }
306✔
4057

4058
                /**
4059
                 * \brief Get curve claim
4060
                 *
4061
                 * https://www.rfc-editor.org/rfc/rfc7518.html#section-6.2.1.1
4062
                 * https://www.iana.org/assignments/jose/jose.xhtml#table-web-key-elliptic-curve
4063
                 *
4064
                 * \return curve as string
4065
                 * \throw std::runtime_error If claim was not present
4066
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4067
                 */
4068
                typename json_traits::string_type get_curve() const { return get_jwk_claim("crv").as_string(); }
4069

4070
                /**
4071
                 * Get x5c claim
4072
                 * \return x5c as an array
4073
                 * \throw std::runtime_error If claim was not present
4074
                 * \throw std::bad_cast Claim was present but not a array (Should not happen in a valid token)
4075
                 */
4076
                typename json_traits::array_type get_x5c() const { return get_jwk_claim("x5c").as_array(); };
54✔
4077

4078
                /**
4079
                 * Get X509 URL claim
4080
                 * \return x5u as string
4081
                 * \throw std::runtime_error If claim was not present
4082
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4083
                 */
4084
                typename json_traits::string_type get_x5u() const { return get_jwk_claim("x5u").as_string(); };
4085

4086
                /**
4087
                 * Get X509 thumbprint claim
4088
                 * \return x5t as string
4089
                 * \throw std::runtime_error If claim was not present
4090
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4091
                 */
4092
                typename json_traits::string_type get_x5t() const { return get_jwk_claim("x5t").as_string(); };
4093

4094
                /**
4095
                 * Get X509 SHA256 thumbprint claim
4096
                 * \return x5t#S256 as string
4097
                 * \throw std::runtime_error If claim was not present
4098
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4099
                 */
4100
                typename json_traits::string_type get_x5t_sha256() const { return get_jwk_claim("x5t#S256").as_string(); };
4101

4102
                /**
4103
                 * Get x5c claim as a string
4104
                 * \return x5c as an string
4105
                 * \throw std::runtime_error If claim was not present
4106
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4107
                 */
4108
                typename json_traits::string_type get_x5c_key_value() const {
12✔
4109
                        auto x5c_array = get_jwk_claim("x5c").as_array();
12✔
4110
                        if (x5c_array.size() == 0) throw error::claim_not_present_exception();
12✔
4111

4112
                        return json_traits::as_string(x5c_array.front());
12✔
4113
                };
12✔
4114

4115
                /**
4116
                 * Check if a key type is present ("kty")
4117
                 * \return true if present, false otherwise
4118
                 */
4119
                bool has_key_type() const noexcept { return has_jwk_claim("kty"); }
4120

4121
                /**
4122
                 * Check if a public key usage indication is present ("use")
4123
                 * \return true if present, false otherwise
4124
                 */
4125
                bool has_use() const noexcept { return has_jwk_claim("use"); }
4126

4127
                /**
4128
                 * Check if a key operations parameter is present ("key_ops")
4129
                 * \return true if present, false otherwise
4130
                 */
4131
                bool has_key_operations() const noexcept { return has_jwk_claim("key_ops"); }
4132

4133
                /**
4134
                 * Check if algorithm is present ("alg")
4135
                 * \return true if present, false otherwise
4136
                 */
4137
                bool has_algorithm() const noexcept { return has_jwk_claim("alg"); }
54✔
4138

4139
                /**
4140
                 * Check if curve is present ("crv")
4141
                 * \return true if present, false otherwise
4142
                 */
4143
                bool has_curve() const noexcept { return has_jwk_claim("crv"); }
4144

4145
                /**
4146
                 * Check if key id is present ("kid")
4147
                 * \return true if present, false otherwise
4148
                 */
4149
                bool has_key_id() const noexcept { return has_jwk_claim("kid"); }
270✔
4150

4151
                /**
4152
                 * Check if X509 URL is present ("x5u")
4153
                 * \return true if present, false otherwise
4154
                 */
4155
                bool has_x5u() const noexcept { return has_jwk_claim("x5u"); }
4156

4157
                /**
4158
                 * Check if X509 Chain is present ("x5c")
4159
                 * \return true if present, false otherwise
4160
                 */
4161
                bool has_x5c() const noexcept { return has_jwk_claim("x5c"); }
36✔
4162

4163
                /**
4164
                 * Check if a X509 thumbprint is present ("x5t")
4165
                 * \return true if present, false otherwise
4166
                 */
4167
                bool has_x5t() const noexcept { return has_jwk_claim("x5t"); }
4168

4169
                /**
4170
                 * Check if a X509 SHA256 thumbprint is present ("x5t#S256")
4171
                 * \return true if present, false otherwise
4172
                 */
4173
                bool has_x5t_sha256() const noexcept { return has_jwk_claim("x5t#S256"); }
4174

4175
                /**
4176
                 * Check if a jwk claim is present
4177
                 * \return true if claim was present, false otherwise
4178
                 */
4179
                bool has_jwk_claim(const typename json_traits::string_type& name) const noexcept {
132✔
4180
                        return jwk_claims.has_claim(name);
132✔
4181
                }
4182

4183
                /**
4184
                 * Get jwk claim by name
4185
                 * \return Requested claim
4186
                 * \throw std::runtime_error If claim was not present
4187
                 */
4188
                basic_claim_t get_jwk_claim(const typename json_traits::string_type& name) const {
144✔
4189
                        return jwk_claims.get_claim(name);
144✔
4190
                }
4191

4192
                /**
4193
                * Check if the jwk has any claims
4194
                * \return true is any claim is present
4195
                 */
4196
                bool empty() const noexcept { return jwk_claims.empty(); }
4197

4198
                /**
4199
                 * Get all jwk claims
4200
                 * \return Map of claims
4201
                 */
4202
                typename json_traits::object_type get_claims() const { return this->jwk_claims.claims; }
4203
        };
4204

4205
        /**
4206
         * \brief JWK Set
4207
         *
4208
         * https://tools.ietf.org/html/rfc7517
4209
         *
4210
         * A JSON object that represents a set of JWKs.  The JSON object MUST
4211
         * have a "keys" member, which is an array of JWKs.
4212
         *
4213
         * This container takes a JWKs and simplifies it to a vector of JWKs
4214
         */
4215
        template<typename json_traits>
4216
        class jwks {
4217
        public:
4218
                /// JWK instance template specialization
4219
                using jwks_t = jwk<json_traits>;
4220
                /// Type specialization for the vector of JWK
4221
                using jwks_vector_t = std::vector<jwks_t>;
4222
                using iterator = typename jwks_vector_t::iterator;
4223
                using const_iterator = typename jwks_vector_t::const_iterator;
4224

4225
                /**
4226
                 * Default constructor producing an empty object without any keys
4227
                 */
4228
                jwks() = default;
4229

4230
                /**
4231
                 * Parses a string buffer to extract the JWKS.
4232
                 * \param str buffer containing JSON object representing a JWKS
4233
                 * \throw error::invalid_json_exception or underlying JSON implation error if the JSON is
4234
                 *        invalid with regards to the JWKS specification
4235
                */
4236
                JWT_CLAIM_EXPLICIT jwks(const typename json_traits::string_type& str) {
18✔
4237
                        typename json_traits::value_type parsed_val;
18✔
4238
                        if (!json_traits::parse(parsed_val, str)) throw error::invalid_json_exception();
18✔
4239

4240
                        const details::map_of_claims<json_traits> jwks_json = json_traits::as_object(parsed_val);
18✔
4241
                        if (!jwks_json.has_claim("keys")) throw error::invalid_json_exception();
36✔
4242

4243
                        auto jwk_list = jwks_json.get_claim("keys").as_array();
18✔
4244
                        std::transform(jwk_list.begin(), jwk_list.end(), std::back_inserter(jwk_claims),
18✔
4245
                                                   [](const typename json_traits::value_type& val) { return jwks_t{val}; });
42✔
4246
                }
18✔
4247

4248
                iterator begin() { return jwk_claims.begin(); }
12✔
4249
                iterator end() { return jwk_claims.end(); }
12✔
4250
                const_iterator cbegin() const { return jwk_claims.begin(); }
54✔
4251
                const_iterator cend() const { return jwk_claims.end(); }
54✔
4252
                const_iterator begin() const { return jwk_claims.begin(); }
4253
                const_iterator end() const { return jwk_claims.end(); }
54✔
4254

4255
                /**
4256
                 * Check if a jwk with the kid is present
4257
                 * \return true if jwk was present, false otherwise
4258
                 */
4259
                bool has_jwk(const typename json_traits::string_type& key_id) const noexcept {
24✔
4260
                        return find_by_kid(key_id) != end();
24✔
4261
                }
4262

4263
                /**
4264
                 * Get jwk
4265
                 * \return Requested jwk by key_id
4266
                 * \throw std::runtime_error If jwk was not present
4267
                 */
4268
                jwks_t get_jwk(const typename json_traits::string_type& key_id) const {
30✔
4269
                        const auto maybe = find_by_kid(key_id);
30✔
4270
                        if (maybe == end()) throw error::claim_not_present_exception();
30✔
4271
                        return *maybe;
48✔
4272
                }
4273

4274
        private:
4275
                jwks_vector_t jwk_claims;
4276

4277
                const_iterator find_by_kid(const typename json_traits::string_type& key_id) const noexcept {
54✔
4278
                        return std::find_if(cbegin(), cend(), [key_id](const jwks_t& jwk) {
108✔
4279
                                if (!jwk.has_key_id()) { return false; }
78✔
4280
                                return jwk.get_key_id() == key_id;
78✔
4281
                        });
54✔
4282
                }
4283
        };
4284

4285
        /**
4286
         * Create a verifier using the given clock
4287
         * \param c Clock instance to use
4288
         * \return verifier instance
4289
         */
4290
        template<typename Clock, typename json_traits>
4291
        verifier<Clock, json_traits> verify(Clock c) {
379✔
4292
                return verifier<Clock, json_traits>(c);
379✔
4293
        }
4294

4295
        /**
4296
         * Create a builder using the given clock
4297
         * \param c Clock instance to use
4298
         * \return builder instance
4299
         */
4300
        template<typename Clock, typename json_traits>
4301
        builder<Clock, json_traits> create(Clock c) {
4302
                return builder<Clock, json_traits>(c);
4303
        }
4304

4305
        /**
4306
         * Default clock class using std::chrono::system_clock as a backend.
4307
         */
4308
        struct default_clock {
4309
                /**
4310
                 * Gets the current system time
4311
                 * \return time_point of the host system
4312
                 */
4313
                date now() const { return date::clock::now(); }
261✔
4314
        };
4315

4316
        /**
4317
         * Create a verifier using the default_clock.
4318
         *
4319
         *
4320
         *
4321
         * \param c Clock instance to use
4322
         * \return verifier instance
4323
         */
4324
        template<typename json_traits>
4325
        verifier<default_clock, json_traits> verify(default_clock c = {}) {
30✔
4326
                return verifier<default_clock, json_traits>(c);
30✔
4327
        }
4328

4329
        /**
4330
         * Return a builder instance to create a new token
4331
         */
4332
        template<typename json_traits>
4333
        builder<default_clock, json_traits> create(default_clock c = {}) {
25✔
4334
                return builder<default_clock, json_traits>(c);
25✔
4335
        }
4336

4337
        /**
4338
         * \brief Decode a token. This can be used to to help access important feild like 'x5c'
4339
         * for verifying tokens. See associated example rsa-verify.cpp for more details.
4340
         *
4341
         * \tparam json_traits JSON implementation traits
4342
         * \tparam Decode is callable, taking a string_type and returns a string_type.
4343
         *         It should ensure the padding of the input and then base64url decode and
4344
         *         return the results.
4345
         * \param token Token to decode
4346
         * \param decode function that will pad and base64url decode the token
4347
         * \return Decoded token
4348
         * \throw std::invalid_argument Token is not in correct format
4349
         * \throw std::runtime_error Base64 decoding failed or invalid json
4350
         */
4351
        template<typename json_traits, typename Decode>
4352
        decoded_jwt<json_traits> decode(const typename json_traits::string_type& token, Decode decode) {
4353
                return decoded_jwt<json_traits>(token, decode);
4354
        }
4355

4356
        /**
4357
         * Decode a token. This can be used to to help access important feild like 'x5c'
4358
         * for verifying tokens. See associated example rsa-verify.cpp for more details.
4359
         *
4360
         * \tparam json_traits JSON implementation traits
4361
         * \param token Token to decode
4362
         * \return Decoded token
4363
         * \throw std::invalid_argument Token is not in correct format
4364
         * \throw std::runtime_error Base64 decoding failed or invalid json
4365
         */
4366
        template<typename json_traits>
4367
        decoded_jwt<json_traits> decode(const typename json_traits::string_type& token) {
35✔
4368
                return decoded_jwt<json_traits>(token);
35✔
4369
        }
4370
        /**
4371
         * Parse a single JSON Web Key
4372
         * \tparam json_traits JSON implementation traits
4373
         * \param jwk_ string buffer containing the JSON object
4374
         * \return Decoded jwk
4375
         */
4376
        template<typename json_traits>
4377
        jwk<json_traits> parse_jwk(const typename json_traits::string_type& jwk_) {
4378
                return jwk<json_traits>(jwk_);
4379
        }
4380
        /**
4381
         * Parse a JSON Web Key Set. This can be used to to help access
4382
         * important feild like 'x5c' for verifying tokens. See example
4383
         * jwks-verify.cpp for more information.
4384
         *
4385
         * \tparam json_traits JSON implementation traits
4386
         * \param jwks_ string buffer containing the JSON object
4387
         * \return Parsed JSON object containing the data of the JWK SET string
4388
         * \throw std::runtime_error Token is not in correct format
4389
         */
4390
        template<typename json_traits>
4391
        jwks<json_traits> parse_jwks(const typename json_traits::string_type& jwks_) {
4392
                return jwks<json_traits>(jwks_);
4393
        }
4394
} // namespace jwt
4395

4396
template<typename json_traits>
4397
std::istream& operator>>(std::istream& is, jwt::basic_claim<json_traits>& c) {
18✔
4398
        return c.operator>>(is);
18✔
4399
}
4400

4401
template<typename json_traits>
4402
std::ostream& operator<<(std::ostream& os, const jwt::basic_claim<json_traits>& c) {
4403
        return os << c.to_json();
4404
}
4405

4406
#ifndef JWT_DISABLE_PICOJSON
4407
#include "traits/kazuho-picojson/defaults.h"
4408
#endif
4409

4410
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc