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

prince-chrismc / jwt-cpp / 22086903909

17 Feb 2026 05:19AM UTC coverage: 94.625% (+0.08%) from 94.549%
22086903909

Pull #38

github

web-flow
Merge 333a46024 into 179beb362
Pull Request #38: replace hmac impl to use BIGNUM

38 of 39 new or added lines in 1 file covered. (97.44%)

1 existing line in 1 file now uncovered.

1338 of 1414 relevant lines covered (94.63%)

258.18 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 >= 0x3050300fL
61
#define JWT_OPENSSL_1_1_0
62
#else
63
#define JWT_OPENSSL_1_0_0
64
#endif
65
#endif
66

67
#if defined(LIBWOLFSSL_VERSION_HEX)
68
#define JWT_OPENSSL_1_1_1
69
#endif
70

71
#ifndef JWT_CLAIM_EXPLICIT
72
#define JWT_CLAIM_EXPLICIT explicit
73
#endif
74

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

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

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

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

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

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

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

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

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

483
                        EVP_PKEY* get() const noexcept { return m_key; }
215✔
484
                        bool operator!() const noexcept { return m_key == nullptr; }
181✔
485
                        explicit operator bool() const noexcept { return m_key != nullptr; }
5✔
486

487
                private:
488
                        EVP_PKEY* m_key{nullptr};
489

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

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

517
                        /**
518
                         * \brief Construct a handle from a pointer. The handle takes ownership.
519
                         * \param bn The BIGNUM to store. Performs BN_dup() to create an independent copy.
520
                         * \throws std::runtime_error if BIGNUM duplication fails
521
                         */
522
                        explicit bn_handle(const BIGNUM* bn) {
138✔
523
                                if (bn == nullptr) throw std::runtime_error("BIGNUM pointer is null");
138✔
524
                                m_bn = BN_dup(bn);
137✔
525
                                if (m_bn == nullptr) throw std::runtime_error("BN_dup failed");
137✔
526
                        }
137✔
527

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

538
                        /**
539
                         * \brief Move constructor. Steals ownership from other.
540
                         */
541
                        bn_handle(bn_handle&& other) noexcept : m_bn{other.m_bn} { other.m_bn = nullptr; }
4✔
542

543
                        /**
544
                         * \brief Destructor. Frees the BIGNUM.
545
                         */
546
                        ~bn_handle() noexcept {
204✔
547
                                if (m_bn != nullptr) BN_free(m_bn);
204✔
548
                        }
204✔
549

550
                        /**
551
                         * \brief Copy assignment. Deleted to maintain immutability.
552
                         */
553
                        bn_handle& operator=(const bn_handle&) = delete;
554

555
                        /**
556
                         * \brief Move assignment. Deleted to maintain immutability.
557
                         */
558
                        bn_handle& operator=(bn_handle&&) = delete;
559

560
                        /**
561
                         * \brief Get the underlying BIGNUM pointer.
562
                         */
563
                        BIGNUM* get() const noexcept { return m_bn; }
160✔
564

565
                        /**
566
                         * \brief Take ownership of the underlying BIGNUM pointer.
567
                         */
568
                        BIGNUM* release() noexcept {
45✔
569
                                BIGNUM* temp = m_bn;
45✔
570
                                m_bn = nullptr;
45✔
571
                                return temp;
45✔
572
                        }
573

574
                        /**
575
                         * \brief Check if the handle is null.
576
                         */
577
                        bool operator!() const noexcept { return m_bn == nullptr; }
3✔
578

579
                        /**
580
                         * \brief Check if the handle is non-null.
581
                         */
582
                        explicit operator bool() const noexcept { return m_bn != nullptr; }
1✔
583

584
                private:
585
                        BIGNUM* m_bn{nullptr};
586
                };
587

588
                inline std::unique_ptr<BIO, decltype(&BIO_free_all)> make_mem_buf_bio() {
212✔
589
                        return std::unique_ptr<BIO, decltype(&BIO_free_all)>(BIO_new(BIO_s_mem()), BIO_free_all);
212✔
590
                }
591

592
                inline std::unique_ptr<BIO, decltype(&BIO_free_all)> make_mem_buf_bio(const std::string& data) {
33✔
593
                        return std::unique_ptr<BIO, decltype(&BIO_free_all)>(
594
#if OPENSSL_VERSION_NUMBER <= 0x10100003L
595
                                BIO_new_mem_buf(const_cast<char*>(data.data()), static_cast<int>(data.size())), BIO_free_all
596
#else
597
                                BIO_new_mem_buf(data.data(), static_cast<int>(data.size())), BIO_free_all
66✔
598
#endif
599
                        );
66✔
600
                }
601

602
                template<typename error_category = error::rsa_error>
603
                std::string write_bio_to_string(std::unique_ptr<BIO, decltype(&BIO_free_all)>& bio_out, std::error_code& ec) {
30✔
604
                        char* ptr = nullptr;
30✔
605
                        auto len = BIO_get_mem_data(bio_out.get(), &ptr);
30✔
606
                        if (len <= 0 || ptr == nullptr) {
30✔
607
                                ec = error_category::convert_to_pem_failed;
13✔
608
                                return {};
13✔
609
                        }
610
                        return {ptr, static_cast<size_t>(len)};
51✔
611
                }
612

613
                inline std::unique_ptr<EVP_MD_CTX, void (*)(EVP_MD_CTX*)> make_evp_md_ctx() {
94✔
614
                        return
615
#ifdef JWT_OPENSSL_1_0_0
616
                                std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_destroy)>(EVP_MD_CTX_create(), &EVP_MD_CTX_destroy);
617
#else
618
                                std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(EVP_MD_CTX_new(), &EVP_MD_CTX_free);
94✔
619
#endif
620
                }
621

622
                /**
623
                 * \brief Extract the public key of a pem certificate
624
                 *
625
                 * \tparam error_category        jwt::error enum category to match with the keys being used
626
                 * \param certstr                        String containing the certificate encoded as pem
627
                 * \param pw                                Password used to decrypt certificate (leave empty if not encrypted)
628
                 * \param ec                                error_code for error_detection (gets cleared if no error occurred)
629
                 */
630
                template<typename error_category = error::rsa_error>
631
                std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw, std::error_code& ec) {
33✔
632
                        ec.clear();
33✔
633
                        auto certbio = make_mem_buf_bio(certstr);
33✔
634
                        auto keybio = make_mem_buf_bio();
33✔
635
                        if (!certbio || !keybio) {
33✔
636
                                ec = error_category::create_mem_bio_failed;
4✔
637
                                return {};
4✔
638
                        }
639

640
                        std::unique_ptr<X509, decltype(&X509_free)> cert(
29✔
641
                                PEM_read_bio_X509(certbio.get(), nullptr, nullptr, const_cast<char*>(pw.c_str())), X509_free);
29✔
642
                        if (!cert) {
29✔
643
                                ec = error_category::cert_load_failed;
4✔
644
                                return {};
4✔
645
                        }
646
                        std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> key(X509_get_pubkey(cert.get()), EVP_PKEY_free);
25✔
647
                        if (!key) {
25✔
648
                                ec = error_category::get_key_failed;
4✔
649
                                return {};
4✔
650
                        }
651
                        if (PEM_write_bio_PUBKEY(keybio.get(), key.get()) == 0) {
21✔
652
                                ec = error_category::write_key_failed;
4✔
653
                                return {};
4✔
654
                        }
655

656
                        return write_bio_to_string<error_category>(keybio, ec);
17✔
657
                }
33✔
658

659
                /**
660
                 * \brief Extract the public key of a pem certificate
661
                 *
662
                 * \tparam error_category        jwt::error enum category to match with the keys being used
663
                 * \param certstr                        String containing the certificate encoded as pem
664
                 * \param pw                                Password used to decrypt certificate (leave empty if not encrypted)
665
                 * \throw                                        templated error_category's type exception if an error occurred
666
                 */
667
                template<typename error_category = error::rsa_error>
668
                std::string extract_pubkey_from_cert(const std::string& certstr, const std::string& pw = "") {
6✔
669
                        std::error_code ec;
6✔
670
                        auto res = extract_pubkey_from_cert<error_category>(certstr, pw, ec);
6✔
671
                        error::throw_if_error(ec);
6✔
672
                        return res;
2✔
673
                }
5✔
674

675
                /**
676
                 * \brief Convert the certificate provided as DER to PEM.
677
                 *
678
                 * \param cert_der_str         String containing the certificate encoded as base64 DER
679
                 * \param ec                        error_code for error_detection (gets cleared if no error occurs)
680
                 */
681
                inline std::string convert_der_to_pem(const std::string& cert_der_str, std::error_code& ec) {
13✔
682
                        ec.clear();
13✔
683

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

686
                        std::unique_ptr<X509, decltype(&X509_free)> cert(
687
                                d2i_X509(NULL, &c_str, static_cast<int>(cert_der_str.size())), X509_free);
13✔
688
                        auto certbio = make_mem_buf_bio();
13✔
689
                        if (!cert || !certbio) {
13✔
690
                                ec = error::rsa_error::create_mem_bio_failed;
3✔
691
                                return {};
3✔
692
                        }
693

694
                        if (!PEM_write_bio_X509(certbio.get(), cert.get())) {
10✔
695
                                ec = error::rsa_error::write_cert_failed;
3✔
696
                                return {};
3✔
697
                        }
698

699
                        return write_bio_to_string(certbio, ec);
7✔
700
                }
13✔
701

702
                /**
703
                 * \brief Convert the certificate provided as base64 DER to PEM.
704
                 *
705
                 * This is useful when using with JWKs as x5c claim is encoded as base64 DER. More info
706
                 * [here](https://tools.ietf.org/html/rfc7517#section-4.7).
707
                 *
708
                 * \tparam Decode is callable, taking a string_type and returns a string_type.
709
                 * It should ensure the padding of the input and then base64 decode and return
710
                 * the results.
711
                 *
712
                 * \param cert_base64_der_str         String containing the certificate encoded as base64 DER
713
                 * \param decode                                 The function to decode the cert
714
                 * \param ec                                        error_code for error_detection (gets cleared if no error occurs)
715
                 */
716
                template<typename Decode>
717
                std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str, Decode decode,
12✔
718
                                                                                          std::error_code& ec) {
719
                        ec.clear();
12✔
720
                        const auto decoded_str = decode(cert_base64_der_str);
12✔
721
                        return convert_der_to_pem(decoded_str, ec);
24✔
722
                }
12✔
723

724
                /**
725
                 * \brief Convert the certificate provided as base64 DER to PEM.
726
                 *
727
                 * This is useful when using with JWKs as x5c claim is encoded as base64 DER. More info
728
                 * [here](https://tools.ietf.org/html/rfc7517#section-4.7)
729
                 *
730
                 * \tparam Decode is callable, taking a string_type and returns a string_type.
731
                 * It should ensure the padding of the input and then base64 decode and return
732
                 * the results.
733
                 *
734
                 * \param cert_base64_der_str         String containing the certificate encoded as base64 DER
735
                 * \param decode                                 The function to decode the cert
736
                 * \throw                                                rsa_exception if an error occurred
737
                 */
738
                template<typename Decode>
739
                std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str, Decode decode) {
740
                        std::error_code ec;
741
                        auto res = convert_base64_der_to_pem(cert_base64_der_str, std::move(decode), ec);
742
                        error::throw_if_error(ec);
743
                        return res;
744
                }
745

746
                /**
747
                 * \brief Convert the certificate provided as DER to PEM.
748
                 *
749
                 * \param cert_der_str         String containing the DER certificate
750
                 * \throw                                rsa_exception if an error occurred
751
                 */
752
                inline std::string convert_der_to_pem(const std::string& cert_der_str) {
1✔
753
                        std::error_code ec;
1✔
754
                        auto res = convert_der_to_pem(cert_der_str, ec);
1✔
755
                        error::throw_if_error(ec);
1✔
756
                        return res;
2✔
757
                }
×
758

759
#ifndef JWT_DISABLE_BASE64
760
                /**
761
                 * \brief Convert the certificate provided as base64 DER to PEM.
762
                 *
763
                 * This is useful when using with JWKs as x5c claim is encoded as base64 DER. More info
764
                 * [here](https://tools.ietf.org/html/rfc7517#section-4.7)
765
                 *
766
                 * \param cert_base64_der_str         String containing the certificate encoded as base64 DER
767
                 * \param ec                                        error_code for error_detection (gets cleared if no error occurs)
768
                 */
769
                inline std::string convert_base64_der_to_pem(const std::string& cert_base64_der_str, std::error_code& ec) {
12✔
770
                        auto decode = [](const std::string& token) {
12✔
771
                                return base::decode<alphabet::base64>(base::pad<alphabet::base64>(token));
12✔
772
                        };
773
                        return convert_base64_der_to_pem(cert_base64_der_str, std::move(decode), ec);
24✔
774
                }
775

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

837
                        evp_pkey_handle pkey(PEM_read_bio_PUBKEY(
55✔
838
                                pubkey_bio.get(), nullptr, nullptr,
839
                                (void*)password.data())); // NOLINT(google-readability-casting) requires `const_cast`
55✔
840
                        if (!pkey) ec = error_category::load_key_bio_read;
55✔
841
                        return pkey;
55✔
842
                }
83✔
843

844
                /**
845
                 * \brief Load a public key from a string.
846
                 *
847
                 * The string should contain a pem encoded certificate or public key
848
                 *
849
                 * \tparam error_category        jwt::error enum category to match with the keys being used
850
                 * \param key                                String containing the certificate encoded as pem
851
                 * \param password                        Password used to decrypt certificate (leave empty if not encrypted)
852
                 * \throw                                        Templated error_category's type exception if an error occurred
853
                 */
854
                template<typename error_category = error::rsa_error>
855
                inline evp_pkey_handle load_public_key_from_string(const std::string& key, const std::string& password = "") {
38✔
856
                        std::error_code ec;
38✔
857
                        auto res = load_public_key_from_string<error_category>(key, password, ec);
38✔
858
                        error::throw_if_error(ec);
38✔
859
                        return res;
40✔
860
                }
18✔
861

862
                /**
863
                 * \brief Load a private key from a string.
864
                 *
865
                 * \tparam error_category        jwt::error enum category to match with the keys being used
866
                 * \param key                                String containing a private key as pem
867
                 * \param password                        Password used to decrypt key (leave empty if not encrypted)
868
                 * \param ec                                error_code for error_detection (gets cleared if no error occurs)
869
                 */
870
                template<typename error_category = error::rsa_error>
871
                inline evp_pkey_handle load_private_key_from_string(const std::string& key, const std::string& password,
69✔
872
                                                                                                                        std::error_code& ec) {
873
                        ec.clear();
69✔
874
                        auto private_key_bio = make_mem_buf_bio();
69✔
875
                        if (!private_key_bio) {
69✔
876
                                ec = error_category::create_mem_bio_failed;
5✔
877
                                return {};
5✔
878
                        }
879
                        const int len = static_cast<int>(key.size());
64✔
880
                        if (BIO_write(private_key_bio.get(), key.data(), len) != len) {
64✔
881
                                ec = error_category::load_key_bio_write;
5✔
882
                                return {};
5✔
883
                        }
884
                        evp_pkey_handle pkey(
59✔
885
                                PEM_read_bio_PrivateKey(private_key_bio.get(), nullptr, nullptr, const_cast<char*>(password.c_str())));
59✔
886
                        if (!pkey) ec = error_category::load_key_bio_read;
59✔
887
                        return pkey;
59✔
888
                }
69✔
889

890
                /**
891
                 * \brief Load a private key from a string.
892
                 *
893
                 * \tparam error_category        jwt::error enum category to match with the keys being used
894
                 * \param key                                String containing a private key as pem
895
                 * \param password                        Password used to decrypt key (leave empty if not encrypted)
896
                 * \throw                                        Templated error_category's type exception if an error occurred
897
                 */
898
                template<typename error_category = error::rsa_error>
899
                inline evp_pkey_handle load_private_key_from_string(const std::string& key, const std::string& password = "") {
33✔
900
                        std::error_code ec;
33✔
901
                        auto res = load_private_key_from_string<error_category>(key, password, ec);
33✔
902
                        error::throw_if_error(ec);
33✔
903
                        return res;
50✔
904
                }
8✔
905

906
                /**
907
                 * \brief Load a public key from a string.
908
                 *
909
                 * The string should contain a pem encoded certificate or public key
910
                 *
911
                 * \deprecated Use the templated version helper::load_private_key_from_string with error::ecdsa_error
912
                 *
913
                 * \param key                String containing the certificate encoded as pem
914
                 * \param password        Password used to decrypt certificate (leave empty if not encrypted)
915
                 * \param ec                error_code for error_detection (gets cleared if no error occurs)
916
                 */
917
                inline evp_pkey_handle load_public_ec_key_from_string(const std::string& key, const std::string& password,
918
                                                                                                                          std::error_code& ec) {
919
                        return load_public_key_from_string<error::ecdsa_error>(key, password, ec);
920
                }
921

922
                /**
923
                 * Convert a OpenSSL BIGNUM to a std::string
924
                 * \param bn BIGNUM to convert
925
                 * \return bignum as string
926
                 */
927
                inline
928
#ifdef JWT_OPENSSL_1_0_0
929
                        std::string
930
                        bn2raw(BIGNUM* bn)
931
#else
932
                        std::string
933
                        bn2raw(const BIGNUM* bn)
10✔
934
#endif
935
                {
936
                        std::string res(BN_num_bytes(bn), '\0');
10✔
937
                        BN_bn2bin(bn, (unsigned char*)res.data()); // NOLINT(google-readability-casting) requires `const_cast`
10✔
938
                        return res;
10✔
939
                }
×
940
                /**
941
                 * Convert an std::string to a OpenSSL BIGNUM
942
                 * \param raw String to convert
943
                 * \param ec  error_code for error_detection (gets cleared if no error occurs)
944
                 * \return BIGNUM representation
945
                 */
946
                inline bn_handle raw2bn(const std::string& raw, std::error_code& ec) {
129✔
947
                        auto bn =
948
                                BN_bin2bn(reinterpret_cast<const unsigned char*>(raw.data()), static_cast<int>(raw.size()), nullptr);
129✔
949
                        // https://www.openssl.org/docs/man1.1.1/man3/BN_bin2bn.html#RETURN-VALUES
950
                        if (!bn) {
129✔
951
                                ec = error::rsa_error::set_rsa_failed;
×
NEW
952
                                return bn_handle(nullptr);
×
953
                        }
954
                        return bn_handle(bn);
129✔
955
                }
956
                /**
957
                 * Convert an std::string to a OpenSSL BIGNUM
958
                 * \param raw String to convert
959
                 * \return BIGNUM representation
960
                 */
961
                inline bn_handle raw2bn(const std::string& raw) {
45✔
962
                        std::error_code ec;
45✔
963
                        auto res = raw2bn(raw, ec);
45✔
964
                        error::throw_if_error(ec);
45✔
965
                        return res;
90✔
UNCOV
966
                }
×
967

968
                /**
969
                 * \brief Load a public key from a string.
970
                 *
971
                 * The string should contain a pem encoded certificate or public key
972
                 *
973
                 * \deprecated Use the templated version helper::load_private_key_from_string with error::ecdsa_error
974
                 *
975
                 * \param key                String containing the certificate or key encoded as pem
976
                 * \param password        Password used to decrypt certificate or key (leave empty if not encrypted)
977
                 * \throw                        ecdsa_exception if an error occurred
978
                 */
979
                inline evp_pkey_handle load_public_ec_key_from_string(const std::string& key,
38✔
980
                                                                                                                          const std::string& password = "") {
981
                        std::error_code ec;
38✔
982
                        auto res = load_public_key_from_string<error::ecdsa_error>(key, password, ec);
38✔
983
                        error::throw_if_error(ec);
38✔
984
                        return res;
54✔
985
                }
11✔
986

987
                /**
988
                 * \brief Load a private key from a string.
989
                 *
990
                 * \deprecated Use the templated version helper::load_private_key_from_string with error::ecdsa_error
991
                 *
992
                 * \param key                String containing a private key as pem
993
                 * \param password        Password used to decrypt key (leave empty if not encrypted)
994
                 * \param ec                error_code for error_detection (gets cleared if no error occurs)
995
                 */
996
                inline evp_pkey_handle load_private_ec_key_from_string(const std::string& key, const std::string& password,
997
                                                                                                                           std::error_code& ec) {
998
                        return load_private_key_from_string<error::ecdsa_error>(key, password, ec);
999
                }
1000

1001
                /**
1002
                * \brief create public key from modulus and exponent. This is defined in
1003
                * [RFC 7518 Section 6.3](https://www.rfc-editor.org/rfc/rfc7518#section-6.3)
1004
                * Using the required "n" (Modulus) Parameter and "e" (Exponent) Parameter.
1005
                *
1006
                 * \tparam Decode is callable, taking a string_type and returns a string_type.
1007
                 * It should ensure the padding of the input and then base64url decode and
1008
                 * return the results.
1009
                * \param modulus        string containing base64url encoded modulus
1010
                * \param exponent        string containing base64url encoded exponent
1011
                * \param decode         The function to decode the RSA parameters
1012
                * \param ec                        error_code for error_detection (gets cleared if no error occur
1013
                * \return                         public key in PEM format
1014
                */
1015
                template<typename Decode>
1016
                std::string create_public_key_from_rsa_components(const std::string& modulus, const std::string& exponent,
19✔
1017
                                                                                                                  Decode decode, std::error_code& ec) {
1018
                        ec.clear();
19✔
1019
                        auto decoded_modulus = decode(modulus);
19✔
1020
                        auto decoded_exponent = decode(exponent);
19✔
1021

1022
                        auto n = helper::raw2bn(decoded_modulus, ec);
19✔
1023
                        if (ec) return {};
19✔
1024
                        auto e = helper::raw2bn(decoded_exponent, ec);
19✔
1025
                        if (ec) return {};
19✔
1026

1027
#if defined(JWT_OPENSSL_3_0)
1028
                        // OpenSSL deprecated mutable keys and there is a new way for making them
1029
                        // https://mta.openssl.org/pipermail/openssl-users/2021-July/013994.html
1030
                        // https://www.openssl.org/docs/man3.1/man3/OSSL_PARAM_BLD_new.html#Example-2
1031
                        std::unique_ptr<OSSL_PARAM_BLD, decltype(&OSSL_PARAM_BLD_free)> param_bld(OSSL_PARAM_BLD_new(),
19✔
1032
                                                                                                                                                                          OSSL_PARAM_BLD_free);
19✔
1033
                        if (!param_bld) {
19✔
1034
                                ec = error::rsa_error::create_context_failed;
2✔
1035
                                return {};
2✔
1036
                        }
1037

1038
                        if (OSSL_PARAM_BLD_push_BN(param_bld.get(), "n", n.get()) != 1 ||
32✔
1039
                                OSSL_PARAM_BLD_push_BN(param_bld.get(), "e", e.get()) != 1) {
15✔
1040
                                ec = error::rsa_error::set_rsa_failed;
2✔
1041
                                return {};
2✔
1042
                        }
1043

1044
                        std::unique_ptr<OSSL_PARAM, decltype(&OSSL_PARAM_free)> params(OSSL_PARAM_BLD_to_param(param_bld.get()),
15✔
1045
                                                                                                                                                   OSSL_PARAM_free);
15✔
1046
                        if (!params) {
15✔
1047
                                ec = error::rsa_error::set_rsa_failed;
2✔
1048
                                return {};
2✔
1049
                        }
1050

1051
                        std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> ctx(
13✔
1052
                                EVP_PKEY_CTX_new_from_name(nullptr, "RSA", nullptr), EVP_PKEY_CTX_free);
13✔
1053
                        if (!ctx) {
13✔
1054
                                ec = error::rsa_error::create_context_failed;
2✔
1055
                                return {};
2✔
1056
                        }
1057

1058
                        // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html#EXAMPLES
1059
                        // Error codes based on https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_fromdata_init.html#RETURN-VALUES
1060
                        EVP_PKEY* pkey = NULL;
11✔
1061
                        if (EVP_PKEY_fromdata_init(ctx.get()) <= 0 ||
20✔
1062
                                EVP_PKEY_fromdata(ctx.get(), &pkey, EVP_PKEY_KEYPAIR, params.get()) <= 0) {
9✔
1063
                                // It's unclear if this can fail after allocating but free it anyways
1064
                                // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html
1065
                                EVP_PKEY_free(pkey);
4✔
1066

1067
                                ec = error::rsa_error::cert_load_failed;
4✔
1068
                                return {};
4✔
1069
                        }
1070

1071
                        // Transfer ownership so we get ref counter and cleanup
1072
                        evp_pkey_handle rsa(pkey);
7✔
1073

1074
#else
1075
                        std::unique_ptr<RSA, decltype(&RSA_free)> rsa(RSA_new(), RSA_free);
1076

1077
#if defined(JWT_OPENSSL_1_1_1) || defined(JWT_OPENSSL_1_1_0)
1078
                        // After this RSA_free will also free the n and e big numbers
1079
                        // See https://github.com/Thalhammer/jwt-cpp/pull/298#discussion_r1282619186
1080
                        if (RSA_set0_key(rsa.get(), n.get(), e.get(), nullptr) == 1) {
1081
                                // This can only fail we passed in NULL for `n` or `e`
1082
                                // https://github.com/openssl/openssl/blob/d6e4056805f54bb1a0ef41fa3a6a35b70c94edba/crypto/rsa/rsa_lib.c#L396
1083
                                // So to make sure there is no memory leak, we hold the references
1084
                                n.release();
1085
                                e.release();
1086
                        } else {
1087
                                ec = error::rsa_error::set_rsa_failed;
1088
                                return {};
1089
                        }
1090
#elif defined(JWT_OPENSSL_1_0_0)
1091
                        rsa->e = e.release();
1092
                        rsa->n = n.release();
1093
                        rsa->d = nullptr;
1094
#endif
1095
#endif
1096

1097
                        auto pub_key_bio = make_mem_buf_bio();
7✔
1098
                        if (!pub_key_bio) {
7✔
1099
                                ec = error::rsa_error::create_mem_bio_failed;
2✔
1100
                                return {};
2✔
1101
                        }
1102

1103
                        auto write_pem_to_bio =
5✔
1104
#if defined(JWT_OPENSSL_3_0)
1105
                                // https://www.openssl.org/docs/man3.1/man3/PEM_write_bio_RSA_PUBKEY.html
1106
                                &PEM_write_bio_PUBKEY;
1107
#else
1108
                                &PEM_write_bio_RSA_PUBKEY;
1109
#endif
1110
                        if (write_pem_to_bio(pub_key_bio.get(), rsa.get()) != 1) {
5✔
1111
                                ec = error::rsa_error::load_key_bio_write;
2✔
1112
                                return {};
2✔
1113
                        }
1114

1115
                        return write_bio_to_string<error::rsa_error>(pub_key_bio, ec);
3✔
1116
                }
19✔
1117

1118
                /**
1119
                * Create public key from modulus and exponent. This is defined in
1120
                * [RFC 7518 Section 6.3](https://www.rfc-editor.org/rfc/rfc7518#section-6.3)
1121
                * Using the required "n" (Modulus) Parameter and "e" (Exponent) Parameter.
1122
                *
1123
                 * \tparam Decode is callable, taking a string_type and returns a string_type.
1124
                 * It should ensure the padding of the input and then base64url decode and
1125
                 * return the results.
1126
                * \param modulus        string containing base64url encoded modulus
1127
                * \param exponent        string containing base64url encoded exponent
1128
                * \param decode         The function to decode the RSA parameters
1129
                * \return public key in PEM format
1130
                */
1131
                template<typename Decode>
1132
                std::string create_public_key_from_rsa_components(const std::string& modulus, const std::string& exponent,
1133
                                                                                                                  Decode decode) {
1134
                        std::error_code ec;
1135
                        auto res = create_public_key_from_rsa_components(modulus, exponent, decode, ec);
1136
                        error::throw_if_error(ec);
1137
                        return res;
1138
                }
1139

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

1192
#if defined(JWT_OPENSSL_3_0)
1193

1194
                /**
1195
                 * \brief Convert a curve name to a group name.
1196
                 *
1197
                 * \param curve        string containing curve name
1198
                 * \param ec        error_code for error_detection
1199
                 * \return                 group name
1200
                 */
1201
                inline std::string curve2group(const std::string curve, std::error_code& ec) {
19✔
1202
                        if (curve == "P-256") {
19✔
1203
                                return "prime256v1";
×
1204
                        } else if (curve == "P-384") {
19✔
1205
                                return "secp384r1";
38✔
1206
                        } else if (curve == "P-521") {
×
1207
                                return "secp521r1";
×
1208
                        } else {
1209
                                ec = jwt::error::ecdsa_error::unknown_curve;
×
1210
                                return {};
×
1211
                        }
1212
                }
1213

1214
#else
1215

1216
                /**
1217
                 * \brief Convert a curve name to an ID.
1218
                 *
1219
                 * \param curve        string containing curve name
1220
                 * \param ec        error_code for error_detection
1221
                 * \return                 ID
1222
                 */
1223
                inline int curve2nid(const std::string curve, std::error_code& ec) {
1224
                        if (curve == "P-256") {
1225
                                return NID_X9_62_prime256v1;
1226
                        } else if (curve == "P-384") {
1227
                                return NID_secp384r1;
1228
                        } else if (curve == "P-521") {
1229
                                return NID_secp521r1;
1230
                        } else {
1231
                                ec = jwt::error::ecdsa_error::unknown_curve;
1232
                                return {};
1233
                        }
1234
                }
1235

1236
#endif
1237

1238
                /**
1239
                 * Create public key from curve name and coordinates. This is defined in
1240
                 * [RFC 7518 Section 6.2](https://www.rfc-editor.org/rfc/rfc7518#section-6.2)
1241
                 * Using the required "crv" (Curve), "x" (X Coordinate) and "y" (Y Coordinate) Parameters.
1242
                 *
1243
                 * \tparam Decode is callable, taking a string_type and returns a string_type.
1244
                 * It should ensure the padding of the input and then base64url decode and
1245
                 * return the results.
1246
                 * \param curve        string containing curve name
1247
                 * \param x                string containing base64url encoded x coordinate
1248
                 * \param y                string containing base64url encoded y coordinate
1249
                 * \param decode        The function to decode the RSA parameters
1250
                 * \param ec                error_code for error_detection (gets cleared if no error occur
1251
                 * \return                 public key in PEM format
1252
                 */
1253
                template<typename Decode>
1254
                std::string create_public_key_from_ec_components(const std::string& curve, const std::string& x,
21✔
1255
                                                                                                                 const std::string& y, Decode decode, std::error_code& ec) {
1256
                        ec.clear();
21✔
1257
                        auto decoded_x = decode(x);
21✔
1258
                        auto decoded_y = decode(y);
21✔
1259

1260
#if defined(JWT_OPENSSL_3_0)
1261
                        // OpenSSL deprecated mutable keys and there is a new way for making them
1262
                        // https://mta.openssl.org/pipermail/openssl-users/2021-July/013994.html
1263
                        // https://www.openssl.org/docs/man3.1/man3/OSSL_PARAM_BLD_new.html#Example-2
1264
                        std::unique_ptr<OSSL_PARAM_BLD, decltype(&OSSL_PARAM_BLD_free)> param_bld(OSSL_PARAM_BLD_new(),
21✔
1265
                                                                                                                                                                          OSSL_PARAM_BLD_free);
21✔
1266
                        if (!param_bld) {
21✔
1267
                                ec = error::ecdsa_error::create_context_failed;
2✔
1268
                                return {};
2✔
1269
                        }
1270

1271
                        std::string group = helper::curve2group(curve, ec);
19✔
1272
                        if (ec) return {};
19✔
1273

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

1277
                        if (OSSL_PARAM_BLD_push_utf8_string(param_bld.get(), "group", group.data(), group.size()) != 1 ||
36✔
1278
                                OSSL_PARAM_BLD_push_octet_string(param_bld.get(), "pub", pub.data(), pub.size()) != 1) {
17✔
1279
                                ec = error::ecdsa_error::set_ecdsa_failed;
4✔
1280
                                return {};
4✔
1281
                        }
1282

1283
                        std::unique_ptr<OSSL_PARAM, decltype(&OSSL_PARAM_free)> params(OSSL_PARAM_BLD_to_param(param_bld.get()),
15✔
1284
                                                                                                                                                   OSSL_PARAM_free);
15✔
1285
                        if (!params) {
15✔
1286
                                ec = error::ecdsa_error::set_ecdsa_failed;
2✔
1287
                                return {};
2✔
1288
                        }
1289

1290
                        std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> ctx(
13✔
1291
                                EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr), EVP_PKEY_CTX_free);
13✔
1292
                        if (!ctx) {
13✔
1293
                                ec = error::ecdsa_error::create_context_failed;
2✔
1294
                                return {};
2✔
1295
                        }
1296

1297
                        // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html#EXAMPLES
1298
                        // Error codes based on https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_fromdata_init.html#RETURN-VALUES
1299
                        EVP_PKEY* pkey = NULL;
11✔
1300
                        if (EVP_PKEY_fromdata_init(ctx.get()) <= 0 ||
20✔
1301
                                EVP_PKEY_fromdata(ctx.get(), &pkey, EVP_PKEY_KEYPAIR, params.get()) <= 0) {
9✔
1302
                                // It's unclear if this can fail after allocating but free it anyways
1303
                                // https://www.openssl.org/docs/man3.0/man3/EVP_PKEY_fromdata.html
1304
                                EVP_PKEY_free(pkey);
4✔
1305

1306
                                ec = error::ecdsa_error::cert_load_failed;
4✔
1307
                                return {};
4✔
1308
                        }
1309

1310
                        // Transfer ownership so we get ref counter and cleanup
1311
                        evp_pkey_handle ecdsa(pkey);
7✔
1312

1313
#else
1314
                        int nid = helper::curve2nid(curve, ec);
1315
                        if (ec) return {};
1316

1317
                        auto qx = helper::raw2bn(decoded_x, ec);
1318
                        if (ec) return {};
1319
                        auto qy = helper::raw2bn(decoded_y, ec);
1320
                        if (ec) return {};
1321

1322
                        std::unique_ptr<EC_GROUP, decltype(&EC_GROUP_free)> ecgroup(EC_GROUP_new_by_curve_name(nid), EC_GROUP_free);
1323
                        if (!ecgroup) {
1324
                                ec = error::ecdsa_error::set_ecdsa_failed;
1325
                                return {};
1326
                        }
1327

1328
                        EC_GROUP_set_asn1_flag(ecgroup.get(), OPENSSL_EC_NAMED_CURVE);
1329

1330
                        std::unique_ptr<EC_POINT, decltype(&EC_POINT_free)> ecpoint(EC_POINT_new(ecgroup.get()), EC_POINT_free);
1331
                        if (!ecpoint ||
1332
                                EC_POINT_set_affine_coordinates_GFp(ecgroup.get(), ecpoint.get(), qx.get(), qy.get(), nullptr) != 1) {
1333
                                ec = error::ecdsa_error::set_ecdsa_failed;
1334
                                return {};
1335
                        }
1336

1337
                        std::unique_ptr<EC_KEY, decltype(&EC_KEY_free)> ecdsa(EC_KEY_new(), EC_KEY_free);
1338
                        if (!ecdsa || EC_KEY_set_group(ecdsa.get(), ecgroup.get()) != 1 ||
1339
                                EC_KEY_set_public_key(ecdsa.get(), ecpoint.get()) != 1) {
1340
                                ec = error::ecdsa_error::set_ecdsa_failed;
1341
                                return {};
1342
                        }
1343

1344
#endif
1345

1346
                        auto pub_key_bio = make_mem_buf_bio();
7✔
1347
                        if (!pub_key_bio) {
7✔
1348
                                ec = error::ecdsa_error::create_mem_bio_failed;
2✔
1349
                                return {};
2✔
1350
                        }
1351

1352
                        auto write_pem_to_bio =
5✔
1353
#if defined(JWT_OPENSSL_3_0)
1354
                                // https://www.openssl.org/docs/man3.1/man3/PEM_write_bio_EC_PUBKEY.html
1355
                                &PEM_write_bio_PUBKEY;
1356
#else
1357
                                &PEM_write_bio_EC_PUBKEY;
1358
#endif
1359
                        if (write_pem_to_bio(pub_key_bio.get(), ecdsa.get()) != 1) {
5✔
1360
                                ec = error::ecdsa_error::load_key_bio_write;
2✔
1361
                                return {};
2✔
1362
                        }
1363

1364
                        return write_bio_to_string<error::ecdsa_error>(pub_key_bio, ec);
3✔
1365
                }
21✔
1366

1367
                /**
1368
                * Create public key from curve name and coordinates. This is defined in
1369
                * [RFC 7518 Section 6.2](https://www.rfc-editor.org/rfc/rfc7518#section-6.2)
1370
                * Using the required "crv" (Curve), "x" (X Coordinate) and "y" (Y Coordinate) Parameters.
1371
                *
1372
                 * \tparam Decode is callable, taking a string_type and returns a string_type.
1373
                 * It should ensure the padding of the input and then base64url decode and
1374
                 * return the results.
1375
                * \param curve        string containing curve name
1376
                * \param x                string containing base64url encoded x coordinate
1377
                * \param y                string containing base64url encoded y coordinate
1378
                * \param decode The function to decode the RSA parameters
1379
                * \return public key in PEM format
1380
                */
1381
                template<typename Decode>
1382
                std::string create_public_key_from_ec_components(const std::string& curve, const std::string& x,
1383
                                                                                                                 const std::string& y, Decode decode) {
1384
                        std::error_code ec;
1385
                        auto res = create_public_key_from_ec_components(curve, x, y, decode, ec);
1386
                        error::throw_if_error(ec);
1387
                        return res;
1388
                }
1389

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

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

1492
                        // Explicitly defaulted copy/move constructors (bn_handle provides RAII)
1493
                        hmacsha(const hmacsha&) = default;
50✔
1494
                        hmacsha(hmacsha&&) noexcept = default;
1495

1496
                        // Assignment operators deleted to maintain immutability
1497
                        hmacsha& operator=(const hmacsha&) = delete;
1498
                        hmacsha& operator=(hmacsha&&) = delete;
1499
                        std::string sign(const std::string& data, std::error_code& ec) const {
50✔
1500
                                ec.clear();
50✔
1501
                                std::string res(static_cast<size_t>(EVP_MAX_MD_SIZE), '\0');
50✔
1502
                                auto len = static_cast<unsigned int>(res.size());
50✔
1503

1504
                                std::vector<unsigned char> buffer(BN_num_bytes(secret.get()), '\0');
50✔
1505
                                const auto buffer_size = BN_bn2bin(secret.get(), buffer.data());
50✔
1506
                                buffer.resize(buffer_size);
50✔
1507

1508
                                if (HMAC(md(), buffer.data(), buffer_size, reinterpret_cast<const unsigned char*>(data.data()),
50✔
1509
                                                 static_cast<int>(data.size()),
50✔
1510
                                                 (unsigned char*)res.data(), // NOLINT(google-readability-casting) requires `const_cast`
50✔
1511
                                                 &len) == nullptr) {
50✔
1512
                                        ec = error::signature_generation_error::hmac_failed;
1✔
1513
                                        return {};
1✔
1514
                                }
1515
                                res.resize(len);
49✔
1516
                                return res;
49✔
1517
                        }
50✔
1518
                        /**
1519
                         * Check if signature is valid
1520
                         *
1521
                         * \param data The data to check signature against
1522
                         * \param signature Signature provided by the jwt
1523
                         * \param ec Filled with details about failure.
1524
                         */
1525
                        void verify(const std::string& data, const std::string& signature, std::error_code& ec) const {
29✔
1526
                                ec.clear();
29✔
1527
                                auto res = sign(data, ec);
29✔
1528
                                if (ec) return;
29✔
1529

1530
                                bool matched = true;
28✔
1531
                                for (size_t i = 0; i < std::min<size_t>(res.size(), signature.size()); i++)
923✔
1532
                                        if (res[i] != signature[i]) matched = false;
895✔
1533
                                if (res.size() != signature.size()) matched = false;
28✔
1534
                                if (!matched) {
28✔
1535
                                        ec = error::signature_verification_error::invalid_signature;
2✔
1536
                                        return;
2✔
1537
                                }
1538
                        }
29✔
1539
                        /**
1540
                         * Returns the algorithm name provided to the constructor
1541
                         *
1542
                         * \return algorithm's name
1543
                         */
1544
                        std::string name() const { return alg_name; }
46✔
1545

1546
                private:
1547
                        /// HMAC secret
1548
                        helper::bn_handle secret;
1549
                        /// HMAC hash generator
1550
                        const EVP_MD* (*md)();
1551
                        /// algorithm's name
1552
                        const std::string alg_name;
1553
                };
1554

1555
                /**
1556
                 * \brief Base class for RSA family of algorithms
1557
                 */
1558
                struct rsa {
1559
                        /**
1560
                         * Construct new rsa algorithm
1561
                         *
1562
                         * \param public_key RSA public key in PEM format
1563
                         * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
1564
                         * \param public_key_password Password to decrypt public key pem.
1565
                         * \param private_key_password Password to decrypt private key pem.
1566
                         * \param md Pointer to hash function
1567
                         * \param name Name of the algorithm
1568
                         */
1569
                        rsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password,
16✔
1570
                                const std::string& private_key_password, const EVP_MD* (*md)(), std::string name)
1571
                                : md(md), alg_name(std::move(name)) {
16✔
1572
                                if (!private_key.empty()) {
16✔
1573
                                        pkey = helper::load_private_key_from_string(private_key, private_key_password);
10✔
1574
                                } else if (!public_key.empty()) {
6✔
1575
                                        pkey = helper::load_public_key_from_string(public_key, public_key_password);
5✔
1576
                                } else
1577
                                        throw error::rsa_exception(error::rsa_error::no_key_provided);
1✔
1578
                        }
17✔
1579
                        /**
1580
                         * Construct new rsa algorithm
1581
                         *
1582
                         * \param key_pair openssl EVP_PKEY structure containing RSA key pair. The private part is optional.
1583
                         * \param md Pointer to hash function
1584
                         * \param name Name of the algorithm
1585
                         */
1586
                        rsa(helper::evp_pkey_handle key_pair, const EVP_MD* (*md)(), std::string name)
3✔
1587
                                : pkey(std::move(key_pair)), md(md), alg_name(std::move(name)) {
3✔
1588
                                if (!pkey) { throw error::rsa_exception(error::rsa_error::no_key_provided); }
3✔
1589
                        }
3✔
1590
                        /**
1591
                         * Sign jwt data
1592
                         * \param data The data to sign
1593
                         * \param ec error_code filled with details on error
1594
                         * \return RSA signature for the given data
1595
                         */
1596
                        std::string sign(const std::string& data, std::error_code& ec) const {
9✔
1597
                                ec.clear();
9✔
1598
                                auto ctx = helper::make_evp_md_ctx();
9✔
1599
                                if (!ctx) {
9✔
1600
                                        ec = error::signature_generation_error::create_context_failed;
1✔
1601
                                        return {};
1✔
1602
                                }
1603
                                if (!EVP_SignInit(ctx.get(), md())) {
8✔
1604
                                        ec = error::signature_generation_error::signinit_failed;
1✔
1605
                                        return {};
1✔
1606
                                }
1607

1608
                                std::string res(EVP_PKEY_size(pkey.get()), '\0');
7✔
1609
                                unsigned int len = 0;
7✔
1610

1611
                                if (!EVP_SignUpdate(ctx.get(), data.data(), data.size())) {
7✔
1612
                                        ec = error::signature_generation_error::signupdate_failed;
1✔
1613
                                        return {};
1✔
1614
                                }
1615
                                if (EVP_SignFinal(ctx.get(), (unsigned char*)res.data(), &len, pkey.get()) == 0) {
6✔
1616
                                        ec = error::signature_generation_error::signfinal_failed;
1✔
1617
                                        return {};
1✔
1618
                                }
1619

1620
                                res.resize(len);
5✔
1621
                                return res;
5✔
1622
                        }
9✔
1623
                        /**
1624
                         * Check if signature is valid
1625
                         *
1626
                         * \param data The data to check signature against
1627
                         * \param signature Signature provided by the jwt
1628
                         * \param ec Filled with details on failure
1629
                         */
1630
                        void verify(const std::string& data, const std::string& signature, std::error_code& ec) const {
16✔
1631
                                ec.clear();
16✔
1632
                                auto ctx = helper::make_evp_md_ctx();
16✔
1633
                                if (!ctx) {
16✔
1634
                                        ec = error::signature_verification_error::create_context_failed;
1✔
1635
                                        return;
1✔
1636
                                }
1637
                                if (!EVP_VerifyInit(ctx.get(), md())) {
15✔
1638
                                        ec = error::signature_verification_error::verifyinit_failed;
1✔
1639
                                        return;
1✔
1640
                                }
1641
                                if (!EVP_VerifyUpdate(ctx.get(), data.data(), data.size())) {
14✔
1642
                                        ec = error::signature_verification_error::verifyupdate_failed;
1✔
1643
                                        return;
1✔
1644
                                }
1645
                                auto res = EVP_VerifyFinal(ctx.get(), reinterpret_cast<const unsigned char*>(signature.data()),
26✔
1646
                                                                                   static_cast<unsigned int>(signature.size()), pkey.get());
13✔
1647
                                if (res != 1) {
13✔
1648
                                        ec = error::signature_verification_error::verifyfinal_failed;
3✔
1649
                                        return;
3✔
1650
                                }
1651
                        }
16✔
1652
                        /**
1653
                         * Returns the algorithm name provided to the constructor
1654
                         * \return algorithm's name
1655
                         */
1656
                        std::string name() const { return alg_name; }
15✔
1657

1658
                private:
1659
                        /// OpenSSL structure containing converted keys
1660
                        helper::evp_pkey_handle pkey;
1661
                        /// Hash generator
1662
                        const EVP_MD* (*md)();
1663
                        /// algorithm's name
1664
                        const std::string alg_name;
1665
                };
1666
                /**
1667
                 * \brief Base class for ECDSA family of algorithms
1668
                 */
1669
                struct ecdsa {
1670
                        /**
1671
                         * Construct new ecdsa algorithm
1672
                         *
1673
                         * \param public_key ECDSA public key in PEM format
1674
                         * \param private_key ECDSA private key or empty string if not available. If empty, signing will always fail
1675
                         * \param public_key_password Password to decrypt public key pem
1676
                         * \param private_key_password Password to decrypt private key pem
1677
                         * \param md Pointer to hash function
1678
                         * \param name Name of the algorithm
1679
                         * \param siglen The bit length of the signature
1680
                         */
1681
                        ecdsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password,
69✔
1682
                                  const std::string& private_key_password, const EVP_MD* (*md)(), std::string name, size_t siglen)
1683
                                : md(md), alg_name(std::move(name)), signature_length(siglen) {
69✔
1684
                                if (!private_key.empty()) {
69✔
1685
                                        pkey = helper::load_private_ec_key_from_string(private_key, private_key_password);
32✔
1686
                                        check_private_key(pkey.get());
29✔
1687
                                } else if (!public_key.empty()) {
37✔
1688
                                        pkey = helper::load_public_ec_key_from_string(public_key, public_key_password);
36✔
1689
                                        check_public_key(pkey.get());
25✔
1690
                                } else {
1691
                                        throw error::ecdsa_exception(error::ecdsa_error::no_key_provided);
1✔
1692
                                }
1693
                                if (!pkey) throw error::ecdsa_exception(error::ecdsa_error::invalid_key);
50✔
1694

1695
                                size_t keysize = EVP_PKEY_bits(pkey.get());
50✔
1696
                                if (keysize != signature_length * 4 && (signature_length != 132 || keysize != 521))
50✔
1697
                                        throw error::ecdsa_exception(error::ecdsa_error::invalid_key_size);
24✔
1698
                        }
112✔
1699

1700
                        /**
1701
                         * Construct new ecdsa algorithm
1702
                         *
1703
                         * \param key_pair openssl EVP_PKEY structure containing ECDSA key pair. The private part is optional.
1704
                         * \param md Pointer to hash function
1705
                         * \param name Name of the algorithm
1706
                         * \param siglen The bit length of the signature
1707
                         */
1708
                        ecdsa(helper::evp_pkey_handle key_pair, const EVP_MD* (*md)(), std::string name, size_t siglen)
4✔
1709
                                : pkey(std::move(key_pair)), md(md), alg_name(std::move(name)), signature_length(siglen) {
4✔
1710
                                if (!pkey) { throw error::ecdsa_exception(error::ecdsa_error::no_key_provided); }
4✔
1711
                                size_t keysize = EVP_PKEY_bits(pkey.get());
3✔
1712
                                if (keysize != signature_length * 4 && (signature_length != 132 || keysize != 521))
3✔
1713
                                        throw error::ecdsa_exception(error::ecdsa_error::invalid_key_size);
×
1714
                        }
5✔
1715

1716
                        /**
1717
                         * Sign jwt data
1718
                         * \param data The data to sign
1719
                         * \param ec error_code filled with details on error
1720
                         * \return ECDSA signature for the given data
1721
                         */
1722
                        std::string sign(const std::string& data, std::error_code& ec) const {
15✔
1723
                                ec.clear();
15✔
1724
                                auto ctx = helper::make_evp_md_ctx();
15✔
1725
                                if (!ctx) {
15✔
1726
                                        ec = error::signature_generation_error::create_context_failed;
1✔
1727
                                        return {};
1✔
1728
                                }
1729
                                if (!EVP_DigestSignInit(ctx.get(), nullptr, md(), nullptr, pkey.get())) {
14✔
1730
                                        ec = error::signature_generation_error::signinit_failed;
1✔
1731
                                        return {};
1✔
1732
                                }
1733
                                if (!EVP_DigestUpdate(ctx.get(), data.data(), static_cast<unsigned int>(data.size()))) {
13✔
1734
                                        ec = error::signature_generation_error::digestupdate_failed;
1✔
1735
                                        return {};
1✔
1736
                                }
1737

1738
                                size_t len = 0;
12✔
1739
                                if (!EVP_DigestSignFinal(ctx.get(), nullptr, &len)) {
12✔
1740
                                        ec = error::signature_generation_error::signfinal_failed;
1✔
1741
                                        return {};
1✔
1742
                                }
1743
                                std::string res(len, '\0');
11✔
1744
                                if (!EVP_DigestSignFinal(ctx.get(), (unsigned char*)res.data(), &len)) {
11✔
1745
                                        ec = error::signature_generation_error::signfinal_failed;
5✔
1746
                                        return {};
5✔
1747
                                }
1748

1749
                                res.resize(len);
6✔
1750
                                return der_to_p1363_signature(res, ec);
6✔
1751
                        }
15✔
1752

1753
                        /**
1754
                         * Check if signature is valid
1755
                         * \param data The data to check signature against
1756
                         * \param signature Signature provided by the jwt
1757
                         * \param ec Filled with details on error
1758
                         */
1759
                        void verify(const std::string& data, const std::string& signature, std::error_code& ec) const {
23✔
1760
                                ec.clear();
23✔
1761
                                std::string der_signature = p1363_to_der_signature(signature, ec);
23✔
1762
                                if (ec) { return; }
23✔
1763

1764
                                auto ctx = helper::make_evp_md_ctx();
20✔
1765
                                if (!ctx) {
20✔
1766
                                        ec = error::signature_verification_error::create_context_failed;
1✔
1767
                                        return;
1✔
1768
                                }
1769
                                if (!EVP_DigestVerifyInit(ctx.get(), nullptr, md(), nullptr, pkey.get())) {
19✔
1770
                                        ec = error::signature_verification_error::verifyinit_failed;
1✔
1771
                                        return;
1✔
1772
                                }
1773
                                if (!EVP_DigestUpdate(ctx.get(), data.data(), static_cast<unsigned int>(data.size()))) {
18✔
1774
                                        ec = error::signature_verification_error::verifyupdate_failed;
1✔
1775
                                        return;
1✔
1776
                                }
1777

1778
#if OPENSSL_VERSION_NUMBER < 0x10002000L
1779
                                unsigned char* der_sig_data = reinterpret_cast<unsigned char*>(const_cast<char*>(der_signature.data()));
1780
#else
1781
                                const unsigned char* der_sig_data = reinterpret_cast<const unsigned char*>(der_signature.data());
17✔
1782
#endif
1783
                                auto res =
1784
                                        EVP_DigestVerifyFinal(ctx.get(), der_sig_data, static_cast<unsigned int>(der_signature.length()));
17✔
1785
                                if (res == 0) {
17✔
1786
                                        ec = error::signature_verification_error::invalid_signature;
8✔
1787
                                        return;
8✔
1788
                                }
1789
                                if (res == -1) {
9✔
1790
                                        ec = error::signature_verification_error::verifyfinal_failed;
×
1791
                                        return;
×
1792
                                }
1793
                        }
34✔
1794
                        /**
1795
                         * Returns the algorithm name provided to the constructor
1796
                         * \return algorithm's name
1797
                         */
1798
                        std::string name() const { return alg_name; }
23✔
1799

1800
                private:
1801
                        static void check_public_key(EVP_PKEY* pkey) {
25✔
1802
#ifdef JWT_OPENSSL_3_0
1803
                                std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> ctx(
1804
                                        EVP_PKEY_CTX_new_from_pkey(nullptr, pkey, nullptr), EVP_PKEY_CTX_free);
25✔
1805
                                if (!ctx) { throw error::ecdsa_exception(error::ecdsa_error::create_context_failed); }
25✔
1806
                                if (EVP_PKEY_public_check(ctx.get()) != 1) {
24✔
1807
                                        throw error::ecdsa_exception(error::ecdsa_error::invalid_key);
1✔
1808
                                }
1809
#else
1810
                                std::unique_ptr<EC_KEY, decltype(&EC_KEY_free)> eckey(EVP_PKEY_get1_EC_KEY(pkey), EC_KEY_free);
1811
                                if (!eckey) { throw error::ecdsa_exception(error::ecdsa_error::invalid_key); }
1812
                                if (EC_KEY_check_key(eckey.get()) == 0) throw error::ecdsa_exception(error::ecdsa_error::invalid_key);
1813
#endif
1814
                        }
25✔
1815

1816
                        static void check_private_key(EVP_PKEY* pkey) {
29✔
1817
#ifdef JWT_OPENSSL_3_0
1818
                                std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> ctx(
1819
                                        EVP_PKEY_CTX_new_from_pkey(nullptr, pkey, nullptr), EVP_PKEY_CTX_free);
29✔
1820
                                if (!ctx) { throw error::ecdsa_exception(error::ecdsa_error::create_context_failed); }
29✔
1821
                                if (EVP_PKEY_private_check(ctx.get()) != 1) {
28✔
1822
                                        throw error::ecdsa_exception(error::ecdsa_error::invalid_key);
1✔
1823
                                }
1824
#else
1825
                                std::unique_ptr<EC_KEY, decltype(&EC_KEY_free)> eckey(EVP_PKEY_get1_EC_KEY(pkey), EC_KEY_free);
1826
                                if (!eckey) { throw error::ecdsa_exception(error::ecdsa_error::invalid_key); }
1827
                                if (EC_KEY_check_key(eckey.get()) == 0) throw error::ecdsa_exception(error::ecdsa_error::invalid_key);
1828
#endif
1829
                        }
29✔
1830

1831
                        std::string der_to_p1363_signature(const std::string& der_signature, std::error_code& ec) const {
6✔
1832
                                const unsigned char* possl_signature = reinterpret_cast<const unsigned char*>(der_signature.data());
6✔
1833
                                std::unique_ptr<ECDSA_SIG, decltype(&ECDSA_SIG_free)> sig(
1834
                                        d2i_ECDSA_SIG(nullptr, &possl_signature, static_cast<long>(der_signature.length())),
6✔
1835
                                        ECDSA_SIG_free);
6✔
1836
                                if (!sig) {
6✔
1837
                                        ec = error::signature_generation_error::signature_decoding_failed;
1✔
1838
                                        return {};
1✔
1839
                                }
1840

1841
#ifdef JWT_OPENSSL_1_0_0
1842
                                auto rr = helper::bn2raw(sig->r);
1843
                                auto rs = helper::bn2raw(sig->s);
1844
#else
1845
                                const BIGNUM* r;
1846
                                const BIGNUM* s;
1847
                                ECDSA_SIG_get0(sig.get(), &r, &s);
5✔
1848
                                auto rr = helper::bn2raw(r);
5✔
1849
                                auto rs = helper::bn2raw(s);
5✔
1850
#endif
1851
                                if (rr.size() > signature_length / 2 || rs.size() > signature_length / 2)
5✔
1852
                                        throw std::logic_error("bignum size exceeded expected length");
×
1853
                                rr.insert(0, signature_length / 2 - rr.size(), '\0');
5✔
1854
                                rs.insert(0, signature_length / 2 - rs.size(), '\0');
5✔
1855
                                return rr + rs;
5✔
1856
                        }
6✔
1857

1858
                        std::string p1363_to_der_signature(const std::string& signature, std::error_code& ec) const {
23✔
1859
                                ec.clear();
23✔
1860
                                auto r = helper::raw2bn(signature.substr(0, signature.size() / 2), ec);
23✔
1861
                                if (ec) return {};
23✔
1862
                                auto s = helper::raw2bn(signature.substr(signature.size() / 2), ec);
23✔
1863
                                if (ec) return {};
23✔
1864

1865
                                ECDSA_SIG* psig;
1866
#ifdef JWT_OPENSSL_1_0_0
1867
                                ECDSA_SIG sig;
1868
                                sig.r = r.get();
1869
                                sig.s = s.get();
1870
                                psig = &sig;
1871
#else
1872
                                std::unique_ptr<ECDSA_SIG, decltype(&ECDSA_SIG_free)> sig(ECDSA_SIG_new(), ECDSA_SIG_free);
23✔
1873
                                if (!sig) {
23✔
1874
                                        ec = error::signature_verification_error::create_context_failed;
1✔
1875
                                        return {};
1✔
1876
                                }
1877
                                ECDSA_SIG_set0(sig.get(), r.release(), s.release());
22✔
1878
                                psig = sig.get();
22✔
1879
#endif
1880

1881
                                int length = i2d_ECDSA_SIG(psig, nullptr);
22✔
1882
                                if (length < 0) {
22✔
1883
                                        ec = error::signature_verification_error::signature_encoding_failed;
1✔
1884
                                        return {};
1✔
1885
                                }
1886
                                std::string der_signature(length, '\0');
21✔
1887
                                unsigned char* psbuffer = (unsigned char*)der_signature.data();
21✔
1888
                                length = i2d_ECDSA_SIG(psig, &psbuffer);
21✔
1889
                                if (length < 0) {
21✔
1890
                                        ec = error::signature_verification_error::signature_encoding_failed;
1✔
1891
                                        return {};
1✔
1892
                                }
1893
                                der_signature.resize(length);
20✔
1894
                                return der_signature;
20✔
1895
                        }
23✔
1896

1897
                        /// OpenSSL struct containing keys
1898
                        helper::evp_pkey_handle pkey;
1899
                        /// Hash generator function
1900
                        const EVP_MD* (*md)();
1901
                        /// algorithm's name
1902
                        const std::string alg_name;
1903
                        /// Length of the resulting signature
1904
                        const size_t signature_length;
1905
                };
1906

1907
#if !defined(JWT_OPENSSL_1_0_0) && !defined(JWT_OPENSSL_1_1_0)
1908
                /**
1909
                 * \brief Base class for EdDSA family of algorithms
1910
                 *
1911
                 * https://tools.ietf.org/html/rfc8032
1912
                 *
1913
                 * The EdDSA algorithms were introduced in [OpenSSL v1.1.1](https://www.openssl.org/news/openssl-1.1.1-notes.html),
1914
                 * so these algorithms are only available when building against this version or higher.
1915
                 */
1916
                struct eddsa {
1917
                        /**
1918
                         * Construct new eddsa algorithm
1919
                         * \param public_key EdDSA public key in PEM format
1920
                         * \param private_key EdDSA private key or empty string if not available. If empty, signing will always
1921
                         * fail.
1922
                         * \param public_key_password Password to decrypt public key pem.
1923
                         * \param private_key_password Password
1924
                         * to decrypt private key pem.
1925
                         * \param name Name of the algorithm
1926
                         */
1927
                        eddsa(const std::string& public_key, const std::string& private_key, const std::string& public_key_password,
27✔
1928
                                  const std::string& private_key_password, std::string name)
1929
                                : alg_name(std::move(name)) {
27✔
1930
                                if (!private_key.empty()) {
27✔
1931
                                        pkey = helper::load_private_key_from_string(private_key, private_key_password);
10✔
1932
                                } else if (!public_key.empty()) {
17✔
1933
                                        pkey = helper::load_public_key_from_string(public_key, public_key_password);
16✔
1934
                                } else
1935
                                        throw error::ecdsa_exception(error::ecdsa_error::load_key_bio_read);
1✔
1936
                        }
41✔
1937
                        /**
1938
                         * Sign jwt data
1939
                         * \param data The data to sign
1940
                         * \param ec error_code filled with details on error
1941
                         * \return EdDSA signature for the given data
1942
                         */
1943
                        std::string sign(const std::string& data, std::error_code& ec) const {
6✔
1944
                                ec.clear();
6✔
1945
                                auto ctx = helper::make_evp_md_ctx();
6✔
1946
                                if (!ctx) {
6✔
1947
                                        ec = error::signature_generation_error::create_context_failed;
1✔
1948
                                        return {};
1✔
1949
                                }
1950
                                if (!EVP_DigestSignInit(ctx.get(), nullptr, nullptr, nullptr, pkey.get())) {
5✔
1951
                                        ec = error::signature_generation_error::signinit_failed;
1✔
1952
                                        return {};
1✔
1953
                                }
1954

1955
                                size_t len = EVP_PKEY_size(pkey.get());
4✔
1956
                                std::string res(len, '\0');
4✔
1957

1958
// LibreSSL is the special kid in the block, as it does not support EVP_DigestSign.
1959
// OpenSSL on the otherhand does not support using EVP_DigestSignUpdate for eddsa, which is why we end up with this
1960
// mess.
1961
#if defined(LIBRESSL_VERSION_NUMBER) || defined(LIBWOLFSSL_VERSION_HEX)
1962
                                ERR_clear_error();
1963
                                if (EVP_DigestSignUpdate(ctx.get(), reinterpret_cast<const unsigned char*>(data.data()), data.size()) !=
1964
                                        1) {
1965
                                        std::cout << ERR_error_string(ERR_get_error(), NULL) << '\n';
1966
                                        ec = error::signature_generation_error::signupdate_failed;
1967
                                        return {};
1968
                                }
1969
                                if (EVP_DigestSignFinal(ctx.get(), reinterpret_cast<unsigned char*>(&res[0]), &len) != 1) {
1970
                                        ec = error::signature_generation_error::signfinal_failed;
1971
                                        return {};
1972
                                }
1973
#else
1974
                                if (EVP_DigestSign(ctx.get(), reinterpret_cast<unsigned char*>(&res[0]), &len,
8✔
1975
                                                                   reinterpret_cast<const unsigned char*>(data.data()), data.size()) != 1) {
8✔
1976
                                        ec = error::signature_generation_error::signfinal_failed;
1✔
1977
                                        return {};
1✔
1978
                                }
1979
#endif
1980

1981
                                res.resize(len);
3✔
1982
                                return res;
3✔
1983
                        }
6✔
1984

1985
                        /**
1986
                         * Check if signature is valid
1987
                         * \param data The data to check signature against
1988
                         * \param signature Signature provided by the jwt
1989
                         * \param ec Filled with details on error
1990
                         */
1991
                        void verify(const std::string& data, const std::string& signature, std::error_code& ec) const {
12✔
1992
                                ec.clear();
12✔
1993
                                auto ctx = helper::make_evp_md_ctx();
12✔
1994
                                if (!ctx) {
12✔
1995
                                        ec = error::signature_verification_error::create_context_failed;
1✔
1996
                                        return;
1✔
1997
                                }
1998
                                if (!EVP_DigestVerifyInit(ctx.get(), nullptr, nullptr, nullptr, pkey.get())) {
11✔
1999
                                        ec = error::signature_verification_error::verifyinit_failed;
1✔
2000
                                        return;
1✔
2001
                                }
2002
// LibreSSL is the special kid in the block, as it does not support EVP_DigestVerify.
2003
// OpenSSL on the otherhand does not support using EVP_DigestVerifyUpdate for eddsa, which is why we end up with this
2004
// mess.
2005
#if defined(LIBRESSL_VERSION_NUMBER) || defined(LIBWOLFSSL_VERSION_HEX)
2006
                                if (EVP_DigestVerifyUpdate(ctx.get(), reinterpret_cast<const unsigned char*>(data.data()),
2007
                                                                                   data.size()) != 1) {
2008
                                        ec = error::signature_verification_error::verifyupdate_failed;
2009
                                        return;
2010
                                }
2011
                                if (EVP_DigestVerifyFinal(ctx.get(), reinterpret_cast<const unsigned char*>(signature.data()),
2012
                                                                                  signature.size()) != 1) {
2013
                                        ec = error::signature_verification_error::verifyfinal_failed;
2014
                                        return;
2015
                                }
2016
#else
2017
                                auto res = EVP_DigestVerify(ctx.get(), reinterpret_cast<const unsigned char*>(signature.data()),
20✔
2018
                                                                                        signature.size(), reinterpret_cast<const unsigned char*>(data.data()),
10✔
2019
                                                                                        data.size());
2020
                                if (res != 1) {
10✔
2021
                                        ec = error::signature_verification_error::verifyfinal_failed;
5✔
2022
                                        return;
5✔
2023
                                }
2024
#endif
2025
                        }
12✔
2026
                        /**
2027
                         * Returns the algorithm name provided to the constructor
2028
                         * \return algorithm's name
2029
                         */
2030
                        std::string name() const { return alg_name; }
10✔
2031

2032
                private:
2033
                        /// OpenSSL struct containing keys
2034
                        helper::evp_pkey_handle pkey;
2035
                        /// algorithm's name
2036
                        const std::string alg_name;
2037
                };
2038
#endif
2039
                /**
2040
                 * \brief Base class for PSS-RSA family of algorithms
2041
                 */
2042
                struct pss {
2043
                        /**
2044
                         * Construct new pss algorithm
2045
                         * \param public_key RSA public key in PEM format
2046
                         * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
2047
                         * \param public_key_password Password to decrypt public key pem.
2048
                         * \param private_key_password Password to decrypt private key pem.
2049
                         * \param md Pointer to hash function
2050
                         * \param name Name of the algorithm
2051
                         */
2052
                        pss(const std::string& public_key, const std::string& private_key, const std::string& public_key_password,
10✔
2053
                                const std::string& private_key_password, const EVP_MD* (*md)(), std::string name)
2054
                                : md(md), alg_name(std::move(name)) {
10✔
2055
                                if (!private_key.empty()) {
10✔
2056
                                        pkey = helper::load_private_key_from_string(private_key, private_key_password);
7✔
2057
                                } else if (!public_key.empty()) {
3✔
2058
                                        pkey = helper::load_public_key_from_string(public_key, public_key_password);
2✔
2059
                                } else
2060
                                        throw error::rsa_exception(error::rsa_error::no_key_provided);
1✔
2061
                        }
11✔
2062

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

2098
                                size_t size = EVP_PKEY_size(pkey.get());
5✔
2099
                                std::string res(size, 0x00);
5✔
2100
                                if (EVP_DigestSignFinal(
5✔
2101
                                                md_ctx.get(),
2102
                                                (unsigned char*)res.data(), // NOLINT(google-readability-casting) requires `const_cast`
5✔
2103
                                                &size) <= 0) {
5✔
2104
                                        ec = error::signature_generation_error::signfinal_failed;
1✔
2105
                                        return {};
1✔
2106
                                }
2107

2108
                                return res;
4✔
2109
                        }
8✔
2110

2111
                        /**
2112
                         * Check if signature is valid
2113
                         * \param data The data to check signature against
2114
                         * \param signature Signature provided by the jwt
2115
                         * \param ec Filled with error details
2116
                         */
2117
                        void verify(const std::string& data, const std::string& signature, std::error_code& ec) const {
8✔
2118
                                ec.clear();
8✔
2119

2120
                                auto md_ctx = helper::make_evp_md_ctx();
8✔
2121
                                if (!md_ctx) {
8✔
2122
                                        ec = error::signature_verification_error::create_context_failed;
1✔
2123
                                        return;
1✔
2124
                                }
2125
                                EVP_PKEY_CTX* ctx = nullptr;
7✔
2126
                                if (EVP_DigestVerifyInit(md_ctx.get(), &ctx, md(), nullptr, pkey.get()) != 1) {
7✔
2127
                                        ec = error::signature_verification_error::verifyinit_failed;
1✔
2128
                                        return;
1✔
2129
                                }
2130
                                if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0) {
6✔
2131
                                        ec = error::signature_generation_error::rsa_padding_failed;
×
2132
                                        return;
×
2133
                                }
2134
// wolfSSL does not require EVP_PKEY_CTX_set_rsa_pss_saltlen. The default behavior
2135
// sets the salt length to the hash length. Unlike OpenSSL which exposes this functionality.
2136
#ifndef LIBWOLFSSL_VERSION_HEX
2137
                                if (EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, -1) <= 0) {
6✔
2138
                                        ec = error::signature_verification_error::set_rsa_pss_saltlen_failed;
×
2139
                                        return;
×
2140
                                }
2141
#endif
2142
                                if (EVP_DigestUpdate(md_ctx.get(), data.data(), static_cast<unsigned int>(data.size())) != 1) {
6✔
2143
                                        ec = error::signature_verification_error::verifyupdate_failed;
1✔
2144
                                        return;
1✔
2145
                                }
2146

2147
                                if (EVP_DigestVerifyFinal(md_ctx.get(), (unsigned char*)signature.data(), signature.size()) <= 0) {
5✔
2148
                                        ec = error::signature_verification_error::verifyfinal_failed;
2✔
2149
                                        return;
2✔
2150
                                }
2151
                        }
8✔
2152
                        /**
2153
                         * Returns the algorithm name provided to the constructor
2154
                         * \return algorithm's name
2155
                         */
2156
                        std::string name() const { return alg_name; }
6✔
2157

2158
                private:
2159
                        /// OpenSSL structure containing keys
2160
                        helper::evp_pkey_handle pkey;
2161
                        /// Hash generator function
2162
                        const EVP_MD* (*md)();
2163
                        /// algorithm's name
2164
                        const std::string alg_name;
2165
                };
2166

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

2335
#if !defined(JWT_OPENSSL_1_0_0) && !defined(JWT_OPENSSL_1_1_0)
2336
                /**
2337
                 * Ed25519 algorithm
2338
                 *
2339
                 * https://en.wikipedia.org/wiki/EdDSA#Ed25519
2340
                 *
2341
                 * Requires at least OpenSSL 1.1.1.
2342
                 */
2343
                struct ed25519 : public eddsa {
2344
                        /**
2345
                         * Construct new instance of algorithm
2346
                         * \param public_key Ed25519 public key in PEM format
2347
                         * \param private_key Ed25519 private key or empty string if not available. If empty, signing will always
2348
                         * fail.
2349
                         * \param public_key_password Password to decrypt public key pem.
2350
                         * \param private_key_password Password
2351
                         * to decrypt private key pem.
2352
                         */
2353
                        explicit ed25519(const std::string& public_key, const std::string& private_key = "",
22✔
2354
                                                         const std::string& public_key_password = "", const std::string& private_key_password = "")
2355
                                : eddsa(public_key, private_key, public_key_password, private_key_password, "EdDSA") {}
66✔
2356
                };
2357

2358
                /**
2359
                 * Ed448 algorithm
2360
                 *
2361
                 * https://en.wikipedia.org/wiki/EdDSA#Ed448
2362
                 *
2363
                 * Requires at least OpenSSL 1.1.1.
2364
                 */
2365
                struct ed448 : public eddsa {
2366
                        /**
2367
                         * Construct new instance of algorithm
2368
                         * \param public_key Ed448 public key in PEM format
2369
                         * \param private_key Ed448 private key or empty string if not available. If empty, signing will always
2370
                         * fail.
2371
                         * \param public_key_password Password to decrypt public key pem.
2372
                         * \param private_key_password Password
2373
                         * to decrypt private key pem.
2374
                         */
2375
                        explicit ed448(const std::string& public_key, const std::string& private_key = "",
5✔
2376
                                                   const std::string& public_key_password = "", const std::string& private_key_password = "")
2377
                                : eddsa(public_key, private_key, public_key_password, private_key_password, "EdDSA") {}
15✔
2378
                };
2379
#endif
2380

2381
                /**
2382
                 * PS256 algorithm
2383
                 */
2384
                struct ps256 : public pss {
2385
                        /**
2386
                         * Construct new instance of algorithm
2387
                         * \param public_key RSA public key in PEM format
2388
                         * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
2389
                         * \param public_key_password Password to decrypt public key pem.
2390
                         * \param private_key_password Password to decrypt private key pem.
2391
                         */
2392
                        explicit ps256(const std::string& public_key, const std::string& private_key = "",
8✔
2393
                                                   const std::string& public_key_password = "", const std::string& private_key_password = "")
2394
                                : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha256, "PS256") {}
24✔
2395
                };
2396
                /**
2397
                 * PS384 algorithm
2398
                 */
2399
                struct ps384 : public pss {
2400
                        /**
2401
                         * Construct new instance of algorithm
2402
                         * \param public_key RSA public key in PEM format
2403
                         * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
2404
                         * \param public_key_password Password to decrypt public key pem.
2405
                         * \param private_key_password Password to decrypt private key pem.
2406
                         */
2407
                        explicit ps384(const std::string& public_key, const std::string& private_key = "",
1✔
2408
                                                   const std::string& public_key_password = "", const std::string& private_key_password = "")
2409
                                : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha384, "PS384") {}
3✔
2410
                };
2411
                /**
2412
                 * PS512 algorithm
2413
                 */
2414
                struct ps512 : public pss {
2415
                        /**
2416
                         * Construct new instance of algorithm
2417
                         * \param public_key RSA public key in PEM format
2418
                         * \param private_key RSA private key or empty string if not available. If empty, signing will always fail.
2419
                         * \param public_key_password Password to decrypt public key pem.
2420
                         * \param private_key_password Password to decrypt private key pem.
2421
                         */
2422
                        explicit ps512(const std::string& public_key, const std::string& private_key = "",
1✔
2423
                                                   const std::string& public_key_password = "", const std::string& private_key_password = "")
2424
                                : pss(public_key, private_key, public_key_password, private_key_password, EVP_sha512, "PS512") {}
3✔
2425
                };
2426
        } // namespace algorithm
2427

2428
        /**
2429
         * \brief JSON Abstractions for working with any library
2430
         */
2431
        namespace json {
2432
                /**
2433
                 * \brief Categories for the various JSON types used in JWTs
2434
                 *
2435
                 * This enum is to abstract the third party underlying types and allows the library
2436
                 * to identify the different structures and reason about them without needing a "concept"
2437
                 * to capture that defintion to compare against a concrete type.
2438
                 */
2439
                enum class type { boolean, integer, number, string, array, object };
2440
        } // namespace json
2441

2442
        namespace details {
2443
#ifdef __cpp_lib_void_t
2444
                template<typename... Ts>
2445
                using void_t = std::void_t<Ts...>;
2446
#else
2447
                // https://en.cppreference.com/w/cpp/types/void_t
2448
                template<typename... Ts>
2449
                struct make_void {
2450
                        using type = void;
2451
                };
2452

2453
                template<typename... Ts>
2454
                using void_t = typename make_void<Ts...>::type;
2455
#endif
2456

2457
#ifdef __cpp_lib_experimental_detect
2458
                template<template<typename...> class _Op, typename... _Args>
2459
                using is_detected = std::experimental::is_detected<_Op, _Args...>;
2460
#else
2461
                struct nonesuch {
2462
                        nonesuch() = delete;
2463
                        ~nonesuch() = delete;
2464
                        nonesuch(nonesuch const&) = delete;
2465
                        nonesuch(nonesuch const&&) = delete;
2466
                        void operator=(nonesuch const&) = delete;
2467
                        void operator=(nonesuch&&) = delete;
2468
                };
2469

2470
                // https://en.cppreference.com/w/cpp/experimental/is_detected
2471
                template<class Default, class AlwaysVoid, template<class...> class Op, class... Args>
2472
                struct detector {
2473
                        using value = std::false_type;
2474
                        using type = Default;
2475
                };
2476

2477
                template<class Default, template<class...> class Op, class... Args>
2478
                struct detector<Default, void_t<Op<Args...>>, Op, Args...> {
2479
                        using value = std::true_type;
2480
                        using type = Op<Args...>;
2481
                };
2482

2483
                template<template<class...> class Op, class... Args>
2484
                using is_detected = typename detector<nonesuch, void, Op, Args...>::value;
2485
#endif
2486

2487
                template<typename T, typename Signature>
2488
                using is_signature = typename std::is_same<T, Signature>;
2489

2490
                template<typename traits_type, template<typename...> class Op, typename Signature>
2491
                struct is_function_signature_detected {
2492
                        using type = Op<traits_type>;
2493
                        static constexpr auto value = is_detected<Op, traits_type>::value && std::is_function<type>::value &&
2494
                                                                                  is_signature<type, Signature>::value;
2495
                };
2496

2497
                template<typename traits_type, typename value_type>
2498
                struct supports_get_type {
2499
                        template<typename T>
2500
                        using get_type_t = decltype(T::get_type);
2501

2502
                        static constexpr auto value =
2503
                                is_function_signature_detected<traits_type, get_type_t, json::type(const value_type&)>::value;
2504

2505
                        // Internal assertions for better feedback
2506
                        static_assert(value, "traits implementation must provide `jwt::json::type get_type(const value_type&)`");
2507
                };
2508

2509
#define JWT_CPP_JSON_TYPE_TYPE(TYPE) json_##TYPE_type
2510
#define JWT_CPP_AS_TYPE_T(TYPE) as_##TYPE_t
2511
#define JWT_CPP_SUPPORTS_AS(TYPE)                                                                                      \
2512
        template<typename traits_type, typename value_type, typename JWT_CPP_JSON_TYPE_TYPE(TYPE)>                         \
2513
        struct supports_as_##TYPE {                                                                                        \
2514
                template<typename T>                                                                                           \
2515
                using JWT_CPP_AS_TYPE_T(TYPE) = decltype(T::as_##TYPE);                                                        \
2516
                                                                                                                       \
2517
                static constexpr auto value =                                                                                  \
2518
                        is_function_signature_detected<traits_type, JWT_CPP_AS_TYPE_T(TYPE),                                       \
2519
                                                                                   JWT_CPP_JSON_TYPE_TYPE(TYPE)(const value_type&)>::value;                    \
2520
                                                                                                                       \
2521
                static_assert(value, "traits implementation must provide `" #TYPE "_type as_" #TYPE "(const value_type&)`");   \
2522
        }
2523

2524
                JWT_CPP_SUPPORTS_AS(object);
2525
                JWT_CPP_SUPPORTS_AS(array);
2526
                JWT_CPP_SUPPORTS_AS(string);
2527
                JWT_CPP_SUPPORTS_AS(number);
2528
                JWT_CPP_SUPPORTS_AS(integer);
2529
                JWT_CPP_SUPPORTS_AS(boolean);
2530

2531
#undef JWT_CPP_JSON_TYPE_TYPE
2532
#undef JWT_CPP_AS_TYPE_T
2533
#undef JWT_CPP_SUPPORTS_AS
2534

2535
                template<typename traits>
2536
                struct is_valid_traits {
2537
                        static constexpr auto value =
2538
                                supports_get_type<traits, typename traits::value_type>::value &&
2539
                                supports_as_object<traits, typename traits::value_type, typename traits::object_type>::value &&
2540
                                supports_as_array<traits, typename traits::value_type, typename traits::array_type>::value &&
2541
                                supports_as_string<traits, typename traits::value_type, typename traits::string_type>::value &&
2542
                                supports_as_number<traits, typename traits::value_type, typename traits::number_type>::value &&
2543
                                supports_as_integer<traits, typename traits::value_type, typename traits::integer_type>::value &&
2544
                                supports_as_boolean<traits, typename traits::value_type, typename traits::boolean_type>::value;
2545
                };
2546

2547
                template<typename value_type>
2548
                struct is_valid_json_value {
2549
                        static constexpr auto value =
2550
                                std::is_default_constructible<value_type>::value &&
2551
                                std::is_constructible<value_type, const value_type&>::value && // a more generic is_copy_constructible
2552
                                std::is_move_constructible<value_type>::value && std::is_assignable<value_type, value_type>::value &&
2553
                                std::is_copy_assignable<value_type>::value && std::is_move_assignable<value_type>::value;
2554
                        // TODO(prince-chrismc): Stream operators
2555
                };
2556

2557
                // https://stackoverflow.com/a/53967057/8480874
2558
                template<typename T, typename = void>
2559
                struct is_iterable : std::false_type {};
2560

2561
                template<typename T>
2562
                struct is_iterable<T, void_t<decltype(std::begin(std::declval<T>())), decltype(std::end(std::declval<T>())),
2563
#if __cplusplus > 201402L
2564
                                                                         decltype(std::cbegin(std::declval<T>())), decltype(std::cend(std::declval<T>()))
2565
#else
2566
                                                                         decltype(std::begin(std::declval<const T>())),
2567
                                                                         decltype(std::end(std::declval<const T>()))
2568
#endif
2569
                                                                         >> : std::true_type {
2570
                };
2571

2572
#if __cplusplus > 201703L
2573
                template<typename T>
2574
                inline constexpr bool is_iterable_v = is_iterable<T>::value;
2575
#endif
2576

2577
                template<typename object_type, typename string_type>
2578
                using is_count_signature = typename std::is_integral<decltype(std::declval<const object_type>().count(
2579
                        std::declval<const string_type>()))>;
2580

2581
                template<typename object_type, typename string_type, typename = void>
2582
                struct is_subcription_operator_signature : std::false_type {};
2583

2584
                template<typename object_type, typename string_type>
2585
                struct is_subcription_operator_signature<
2586
                        object_type, string_type,
2587
                        void_t<decltype(std::declval<object_type>().operator[](std::declval<string_type>()))>> : std::true_type {
2588
                        // TODO(prince-chrismc): I am not convienced this is meaningful anymore
2589
                        static_assert(
2590
                                value,
2591
                                "object_type must implementate the subscription operator '[]' taking string_type as an argument");
2592
                };
2593

2594
                template<typename object_type, typename value_type, typename string_type>
2595
                using is_at_const_signature =
2596
                        typename std::is_same<decltype(std::declval<const object_type>().at(std::declval<const string_type>())),
2597
                                                                  const value_type&>;
2598

2599
                template<typename value_type, typename string_type, typename object_type>
2600
                struct is_valid_json_object {
2601
                        template<typename T>
2602
                        using mapped_type_t = typename T::mapped_type;
2603
                        template<typename T>
2604
                        using key_type_t = typename T::key_type;
2605
                        template<typename T>
2606
                        using iterator_t = typename T::iterator;
2607
                        template<typename T>
2608
                        using const_iterator_t = typename T::const_iterator;
2609

2610
                        static constexpr auto value =
2611
                                std::is_constructible<value_type, object_type>::value &&
2612
                                is_detected<mapped_type_t, object_type>::value &&
2613
                                std::is_same<typename object_type::mapped_type, value_type>::value &&
2614
                                is_detected<key_type_t, object_type>::value &&
2615
                                (std::is_same<typename object_type::key_type, string_type>::value ||
2616
                                 std::is_constructible<typename object_type::key_type, string_type>::value) &&
2617
                                is_detected<iterator_t, object_type>::value && is_detected<const_iterator_t, object_type>::value &&
2618
                                is_iterable<object_type>::value && is_count_signature<object_type, string_type>::value &&
2619
                                is_subcription_operator_signature<object_type, string_type>::value &&
2620
                                is_at_const_signature<object_type, value_type, string_type>::value;
2621
                };
2622

2623
                template<typename value_type, typename array_type>
2624
                struct is_valid_json_array {
2625
                        template<typename T>
2626
                        using value_type_t = typename T::value_type;
2627
                        using front_base_type = typename std::decay<decltype(std::declval<array_type>().front())>::type;
2628

2629
                        static constexpr auto value = std::is_constructible<value_type, array_type>::value &&
2630
                                                                                  is_iterable<array_type>::value &&
2631
                                                                                  is_detected<value_type_t, array_type>::value &&
2632
                                                                                  std::is_same<typename array_type::value_type, value_type>::value &&
2633
                                                                                  std::is_same<front_base_type, value_type>::value;
2634
                };
2635

2636
                template<typename string_type, typename integer_type>
2637
                using is_substr_start_end_index_signature =
2638
                        typename std::is_same<decltype(std::declval<string_type>().substr(
2639
                                                                          static_cast<size_t>(std::declval<integer_type>()),
2640
                                                                          static_cast<size_t>(std::declval<integer_type>()))),
2641
                                                                  string_type>;
2642

2643
                template<typename string_type, typename integer_type>
2644
                using is_substr_start_index_signature =
2645
                        typename std::is_same<decltype(std::declval<string_type>().substr(
2646
                                                                          static_cast<size_t>(std::declval<integer_type>()))),
2647
                                                                  string_type>;
2648

2649
                template<typename string_type>
2650
                using is_std_operate_plus_signature =
2651
                        typename std::is_same<decltype(std::operator+(std::declval<string_type>(), std::declval<string_type>())),
2652
                                                                  string_type>;
2653

2654
                template<typename value_type, typename string_type, typename integer_type>
2655
                struct is_valid_json_string {
2656
                        static constexpr auto substr = is_substr_start_end_index_signature<string_type, integer_type>::value &&
2657
                                                                                   is_substr_start_index_signature<string_type, integer_type>::value;
2658
                        static_assert(substr, "string_type must have a substr method taking only a start index and an overload "
2659
                                                                  "taking a start and end index, both must return a string_type");
2660

2661
                        static constexpr auto operator_plus = is_std_operate_plus_signature<string_type>::value;
2662
                        static_assert(operator_plus,
2663
                                                  "string_type must have a '+' operator implemented which returns the concatenated string");
2664

2665
                        static constexpr auto value =
2666
                                std::is_constructible<value_type, string_type>::value && substr && operator_plus;
2667
                };
2668

2669
                template<typename value_type, typename number_type>
2670
                struct is_valid_json_number {
2671
                        static constexpr auto value =
2672
                                std::is_floating_point<number_type>::value && std::is_constructible<value_type, number_type>::value;
2673
                };
2674

2675
                template<typename value_type, typename integer_type>
2676
                struct is_valid_json_integer {
2677
                        static constexpr auto value = std::is_signed<integer_type>::value &&
2678
                                                                                  !std::is_floating_point<integer_type>::value &&
2679
                                                                                  std::is_constructible<value_type, integer_type>::value;
2680
                };
2681
                template<typename value_type, typename boolean_type>
2682
                struct is_valid_json_boolean {
2683
                        static constexpr auto value = std::is_convertible<boolean_type, bool>::value &&
2684
                                                                                  std::is_constructible<value_type, boolean_type>::value;
2685
                };
2686

2687
                template<typename value_type, typename object_type, typename array_type, typename string_type,
2688
                                 typename number_type, typename integer_type, typename boolean_type>
2689
                struct is_valid_json_types {
2690
                        // Internal assertions for better feedback
2691
                        static_assert(is_valid_json_value<value_type>::value,
2692
                                                  "value_type must meet basic requirements, default constructor, copyable, moveable");
2693
                        static_assert(is_valid_json_object<value_type, string_type, object_type>::value,
2694
                                                  "object_type must be a string_type to value_type container");
2695
                        static_assert(is_valid_json_array<value_type, array_type>::value,
2696
                                                  "array_type must be a container of value_type");
2697

2698
                        static constexpr auto value = is_valid_json_value<value_type>::value &&
2699
                                                                                  is_valid_json_object<value_type, string_type, object_type>::value &&
2700
                                                                                  is_valid_json_array<value_type, array_type>::value &&
2701
                                                                                  is_valid_json_string<value_type, string_type, integer_type>::value &&
2702
                                                                                  is_valid_json_number<value_type, number_type>::value &&
2703
                                                                                  is_valid_json_integer<value_type, integer_type>::value &&
2704
                                                                                  is_valid_json_boolean<value_type, boolean_type>::value;
2705
                };
2706
        } // namespace details
2707

2708
        /**
2709
         * \brief a class to store a generic JSON value as claim
2710
         *
2711
         * \tparam json_traits : JSON implementation traits
2712
         *
2713
         * \see [RFC 7519: JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7519)
2714
         */
2715
        template<typename json_traits>
2716
        class basic_claim {
2717
                /**
2718
                 * The reason behind this is to provide an expressive abstraction without
2719
                 * over complicating the API. For more information take the time to read
2720
                 * https://github.com/nlohmann/json/issues/774. It maybe be expanded to
2721
                 * support custom string types.
2722
                 */
2723
                static_assert(std::is_same<typename json_traits::string_type, std::string>::value ||
2724
                                                  std::is_convertible<typename json_traits::string_type, std::string>::value ||
2725
                                                  std::is_constructible<typename json_traits::string_type, std::string>::value,
2726
                                          "string_type must be a std::string, convertible to a std::string, or construct a std::string.");
2727

2728
                static_assert(
2729
                        details::is_valid_json_types<typename json_traits::value_type, typename json_traits::object_type,
2730
                                                                                 typename json_traits::array_type, typename json_traits::string_type,
2731
                                                                                 typename json_traits::number_type, typename json_traits::integer_type,
2732
                                                                                 typename json_traits::boolean_type>::value,
2733
                        "must satisfy json container requirements");
2734
                static_assert(details::is_valid_traits<json_traits>::value, "traits must satisfy requirements");
2735

2736
                typename json_traits::value_type val;
2737

2738
        public:
2739
                /**
2740
                 * Order list of strings
2741
                 */
2742
                using set_t = std::set<typename json_traits::string_type>;
2743

2744
                basic_claim() = default;
16✔
2745
                basic_claim(const basic_claim&) = default;
238✔
2746
                basic_claim(basic_claim&&) = default;
47✔
2747
                basic_claim& operator=(const basic_claim&) = default;
2748
                basic_claim& operator=(basic_claim&&) = default;
2749
                ~basic_claim() = default;
652✔
2750

2751
                JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::string_type s) : val(std::move(s)) {}
50✔
2752
                JWT_CLAIM_EXPLICIT basic_claim(const date& d)
30✔
2753
                        : val(typename json_traits::integer_type(
36✔
2754
                                  std::chrono::duration_cast<std::chrono::seconds>(d.time_since_epoch()).count())) {}
60✔
2755
                JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::array_type a) : val(std::move(a)) {}
2756
                JWT_CLAIM_EXPLICIT basic_claim(typename json_traits::value_type v) : val(std::move(v)) {}
258✔
2757
                JWT_CLAIM_EXPLICIT basic_claim(const set_t& s) : val(typename json_traits::array_type(s.begin(), s.end())) {}
6✔
2758
                template<typename Iterator>
2759
                basic_claim(Iterator begin, Iterator end) : val(typename json_traits::array_type(begin, end)) {}
15✔
2760

2761
                /**
2762
                 * Get wrapped JSON value
2763
                 * \return Wrapped JSON value
2764
                 */
2765
                typename json_traits::value_type to_json() const { return val; }
56✔
2766

2767
                /**
2768
                 * Parse input stream into underlying JSON value
2769
                 * \return input stream
2770
                 */
2771
                std::istream& operator>>(std::istream& is) { return is >> val; }
8✔
2772

2773
                /**
2774
                 * Serialize claim to output stream from wrapped JSON value
2775
                 * \return output stream
2776
                 */
2777
                std::ostream& operator<<(std::ostream& os) { return os << val; }
2778

2779
                /**
2780
                 * Get type of contained JSON value
2781
                 * \return Type
2782
                 * \throw std::logic_error An internal error occurred
2783
                 */
2784
                json::type get_type() const { return json_traits::get_type(val); }
211✔
2785

2786
                /**
2787
                 * Get the contained JSON value as a string
2788
                 * \return content as string
2789
                 * \throw std::bad_cast Content was not a string
2790
                 */
2791
                typename json_traits::string_type as_string() const { return json_traits::as_string(val); }
220✔
2792

2793
                /**
2794
                 * \brief Get the contained JSON value as a date
2795
                 *
2796
                 * If the value is a decimal, it is rounded to the closest integer
2797
                 *
2798
                 * \return content as date
2799
                 * \throw std::bad_cast Content was not a date
2800
                 */
2801
                date as_date() const {
45✔
2802
                        using std::chrono::system_clock;
2803
                        if (get_type() == json::type::number)
45✔
2804
                                return date(std::chrono::seconds(static_cast<int64_t>(std::llround(as_number()))));
×
2805
                        return date(std::chrono::seconds(as_integer()));
45✔
2806
                }
2807

2808
                /**
2809
                 * Get the contained JSON value as an array
2810
                 * \return content as array
2811
                 * \throw std::bad_cast Content was not an array
2812
                 */
2813
                typename json_traits::array_type as_array() const { return json_traits::as_array(val); }
8✔
2814

2815
                /**
2816
                 * Get the contained JSON value as a set of strings
2817
                 * \return content as set of strings
2818
                 * \throw std::bad_cast Content was not an array of string
2819
                 */
2820
                set_t as_set() const {
1✔
2821
                        set_t res;
1✔
2822
                        for (const auto& e : json_traits::as_array(val)) {
3✔
2823
                                res.insert(json_traits::as_string(e));
2✔
2824
                        }
2825
                        return res;
1✔
2826
                }
×
2827

2828
                /**
2829
                 * Get the contained JSON value as an integer
2830
                 * \return content as int
2831
                 * \throw std::bad_cast Content was not an int
2832
                 */
2833
                typename json_traits::integer_type as_integer() const { return json_traits::as_integer(val); }
47✔
2834

2835
                /**
2836
                 * Get the contained JSON value as a bool
2837
                 * \return content as bool
2838
                 * \throw std::bad_cast Content was not a bool
2839
                 */
2840
                typename json_traits::boolean_type as_boolean() const { return json_traits::as_boolean(val); }
1✔
2841

2842
                /**
2843
                 * Get the contained JSON value as a number
2844
                 * \return content as double
2845
                 * \throw std::bad_cast Content was not a number
2846
                 */
2847
                typename json_traits::number_type as_number() const { return json_traits::as_number(val); }
1✔
2848
        };
2849

2850
        namespace error {
2851
                /**
2852
                 * Attempt to parse JSON was unsuccessful
2853
                 */
2854
                struct invalid_json_exception : public std::runtime_error {
2855
                        invalid_json_exception() : runtime_error("invalid json") {}
3✔
2856
                };
2857
                /**
2858
                 * Attempt to access claim was unsuccessful
2859
                 */
2860
                struct claim_not_present_exception : public std::out_of_range {
2861
                        claim_not_present_exception() : out_of_range("claim not found") {}
6✔
2862
                };
2863
        } // namespace error
2864

2865
        namespace details {
2866
                template<typename json_traits>
2867
                struct map_of_claims {
2868
                        typename json_traits::object_type claims;
2869
                        using basic_claim_t = basic_claim<json_traits>;
2870
                        using iterator = typename json_traits::object_type::iterator;
2871
                        using const_iterator = typename json_traits::object_type::const_iterator;
2872

2873
                        map_of_claims() = default;
176✔
2874
                        map_of_claims(const map_of_claims&) = default;
22✔
2875
                        map_of_claims(map_of_claims&&) = default;
2876
                        map_of_claims& operator=(const map_of_claims&) = default;
2877
                        map_of_claims& operator=(map_of_claims&&) = default;
168✔
2878

2879
                        map_of_claims(typename json_traits::object_type json) : claims(std::move(json)) {}
178✔
2880

2881
                        iterator begin() { return claims.begin(); }
2882
                        iterator end() { return claims.end(); }
2883
                        const_iterator cbegin() const { return claims.begin(); }
2884
                        const_iterator cend() const { return claims.end(); }
2885
                        const_iterator begin() const { return claims.begin(); }
2886
                        const_iterator end() const { return claims.end(); }
2887

2888
                        /**
2889
                         * \brief Parse a JSON string into a map of claims
2890
                         *
2891
                         * The implication is that a "map of claims" is identic to a JSON object
2892
                         *
2893
                         * \param str JSON data to be parse as an object
2894
                         * \return content as JSON object
2895
                         */
2896
                        static typename json_traits::object_type parse_claims(const typename json_traits::string_type& str) {
172✔
2897
                                typename json_traits::value_type val;
172✔
2898
                                if (!json_traits::parse(val, str)) throw error::invalid_json_exception();
172✔
2899

2900
                                return json_traits::as_object(val);
337✔
2901
                        };
172✔
2902

2903
                        /**
2904
                         * Check if a claim is present in the map
2905
                         * \return true if claim was present, false otherwise
2906
                         */
2907
                        bool has_claim(const typename json_traits::string_type& name) const noexcept {
625✔
2908
                                return claims.count(name) != 0;
625✔
2909
                        }
2910

2911
                        /**
2912
                         * Get a claim by name
2913
                         *
2914
                         * \param name the name of the desired claim
2915
                         * \return Requested claim
2916
                         * \throw jwt::error::claim_not_present_exception if the claim was not present
2917
                         */
2918
                        basic_claim_t get_claim(const typename json_traits::string_type& name) const {
252✔
2919
                                if (!has_claim(name)) throw error::claim_not_present_exception();
252✔
2920
                                return basic_claim_t{claims.at(name)};
248✔
2921
                        }
2922
                };
2923
        } // namespace details
2924

2925
        /**
2926
         * Base class that represents a token payload.
2927
         * Contains Convenience accessors for common claims.
2928
         */
2929
        template<typename json_traits>
2930
        class payload {
2931
        protected:
2932
                details::map_of_claims<json_traits> payload_claims;
2933

2934
        public:
2935
                using basic_claim_t = basic_claim<json_traits>;
2936

2937
                /**
2938
                 * Check if issuer is present ("iss")
2939
                 * \return true if present, false otherwise
2940
                 */
2941
                bool has_issuer() const noexcept { return has_payload_claim("iss"); }
27✔
2942
                /**
2943
                 * Check if subject is present ("sub")
2944
                 * \return true if present, false otherwise
2945
                 */
2946
                bool has_subject() const noexcept { return has_payload_claim("sub"); }
27✔
2947
                /**
2948
                 * Check if audience is present ("aud")
2949
                 * \return true if present, false otherwise
2950
                 */
2951
                bool has_audience() const noexcept { return has_payload_claim("aud"); }
24✔
2952
                /**
2953
                 * Check if expires is present ("exp")
2954
                 * \return true if present, false otherwise
2955
                 */
2956
                bool has_expires_at() const noexcept { return has_payload_claim("exp"); }
213✔
2957
                /**
2958
                 * Check if not before is present ("nbf")
2959
                 * \return true if present, false otherwise
2960
                 */
2961
                bool has_not_before() const noexcept { return has_payload_claim("nbf"); }
231✔
2962
                /**
2963
                 * Check if issued at is present ("iat")
2964
                 * \return true if present, false otherwise
2965
                 */
2966
                bool has_issued_at() const noexcept { return has_payload_claim("iat"); }
219✔
2967
                /**
2968
                 * Check if token id is present ("jti")
2969
                 * \return true if present, false otherwise
2970
                 */
2971
                bool has_id() const noexcept { return has_payload_claim("jti"); }
24✔
2972
                /**
2973
                 * Get issuer claim
2974
                 * \return issuer as string
2975
                 * \throw std::runtime_error If claim was not present
2976
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
2977
                 */
2978
                typename json_traits::string_type get_issuer() const { return get_payload_claim("iss").as_string(); }
9✔
2979
                /**
2980
                 * Get subject claim
2981
                 * \return subject as string
2982
                 * \throw std::runtime_error If claim was not present
2983
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
2984
                 */
2985
                typename json_traits::string_type get_subject() const { return get_payload_claim("sub").as_string(); }
3✔
2986
                /**
2987
                 * Get audience claim
2988
                 * \return audience as a set of strings
2989
                 * \throw std::runtime_error If claim was not present
2990
                 * \throw std::bad_cast Claim was present but not a set (Should not happen in a valid token)
2991
                 */
2992
                typename basic_claim_t::set_t get_audience() const {
6✔
2993
                        auto aud = get_payload_claim("aud");
6✔
2994
                        if (aud.get_type() == json::type::string) return {aud.as_string()};
21✔
2995

2996
                        return aud.as_set();
1✔
2997
                }
11✔
2998
                /**
2999
                 * Get expires claim
3000
                 * \return expires as a date in utc
3001
                 * \throw std::runtime_error If claim was not present
3002
                 * \throw std::bad_cast Claim was present but not a date (Should not happen in a valid token)
3003
                 */
3004
                date get_expires_at() const { return get_payload_claim("exp").as_date(); }
60✔
3005
                /**
3006
                 * Get not valid before claim
3007
                 * \return nbf date in utc
3008
                 * \throw std::runtime_error If claim was not present
3009
                 * \throw std::bad_cast Claim was present but not a date (Should not happen in a valid token)
3010
                 */
3011
                date get_not_before() const { return get_payload_claim("nbf").as_date(); }
12✔
3012
                /**
3013
                 * Get issued at claim
3014
                 * \return issued at as date in utc
3015
                 * \throw std::runtime_error If claim was not present
3016
                 * \throw std::bad_cast Claim was present but not a date (Should not happen in a valid token)
3017
                 */
3018
                date get_issued_at() const { return get_payload_claim("iat").as_date(); }
60✔
3019
                /**
3020
                 * Get id claim
3021
                 * \return id as string
3022
                 * \throw std::runtime_error If claim was not present
3023
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
3024
                 */
3025
                typename json_traits::string_type get_id() const { return get_payload_claim("jti").as_string(); }
3026
                /**
3027
                 * Check if a payload claim is present
3028
                 * \return true if claim was present, false otherwise
3029
                 */
3030
                bool has_payload_claim(const typename json_traits::string_type& name) const noexcept {
306✔
3031
                        return payload_claims.has_claim(name);
306✔
3032
                }
3033
                /**
3034
                 * Get payload claim
3035
                 * \return Requested claim
3036
                 * \throw std::runtime_error If claim was not present
3037
                 */
3038
                basic_claim_t get_payload_claim(const typename json_traits::string_type& name) const {
54✔
3039
                        return payload_claims.get_claim(name);
54✔
3040
                }
3041
        };
3042

3043
        /**
3044
         * Base class that represents a token header.
3045
         * Contains Convenience accessors for common claims.
3046
         */
3047
        template<typename json_traits>
3048
        class header {
3049
        protected:
3050
                details::map_of_claims<json_traits> header_claims;
3051

3052
        public:
3053
                using basic_claim_t = basic_claim<json_traits>;
3054
                /**
3055
                 * Check if algorithm is present ("alg")
3056
                 * \return true if present, false otherwise
3057
                 */
3058
                bool has_algorithm() const noexcept { return has_header_claim("alg"); }
27✔
3059
                /**
3060
                 * Check if type is present ("typ")
3061
                 * \return true if present, false otherwise
3062
                 */
3063
                bool has_type() const noexcept { return has_header_claim("typ"); }
27✔
3064
                /**
3065
                 * Check if content type is present ("cty")
3066
                 * \return true if present, false otherwise
3067
                 */
3068
                bool has_content_type() const noexcept { return has_header_claim("cty"); }
24✔
3069
                /**
3070
                 * Check if key id is present ("kid")
3071
                 * \return true if present, false otherwise
3072
                 */
3073
                bool has_key_id() const noexcept { return has_header_claim("kid"); }
24✔
3074
                /**
3075
                 * Get algorithm claim
3076
                 * \return algorithm as string
3077
                 * \throw std::runtime_error If claim was not present
3078
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
3079
                 */
3080
                typename json_traits::string_type get_algorithm() const { return get_header_claim("alg").as_string(); }
315✔
3081
                /**
3082
                 * Get type claim
3083
                 * \return type as a string
3084
                 * \throw std::runtime_error If claim was not present
3085
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
3086
                 */
3087
                typename json_traits::string_type get_type() const { return get_header_claim("typ").as_string(); }
27✔
3088
                /**
3089
                 * Get content type claim
3090
                 * \return content type as string
3091
                 * \throw std::runtime_error If claim was not present
3092
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
3093
                 */
3094
                typename json_traits::string_type get_content_type() const { return get_header_claim("cty").as_string(); }
3095
                /**
3096
                 * Get key id claim
3097
                 * \return key id as string
3098
                 * \throw std::runtime_error If claim was not present
3099
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
3100
                 */
3101
                typename json_traits::string_type get_key_id() const { return get_header_claim("kid").as_string(); }
3102
                /**
3103
                 * Check if a header claim is present
3104
                 * \return true if claim was present, false otherwise
3105
                 */
3106
                bool has_header_claim(const typename json_traits::string_type& name) const noexcept {
37✔
3107
                        return header_claims.has_claim(name);
37✔
3108
                }
3109
                /**
3110
                 * Get header claim
3111
                 * \return Requested claim
3112
                 * \throw std::runtime_error If claim was not present
3113
                 */
3114
                basic_claim_t get_header_claim(const typename json_traits::string_type& name) const {
114✔
3115
                        return header_claims.get_claim(name);
114✔
3116
                }
3117
        };
3118

3119
        /**
3120
         * Class containing all information about a decoded token
3121
         */
3122
        template<typename json_traits>
3123
        class decoded_jwt : public header<json_traits>, public payload<json_traits> {
3124
        protected:
3125
                /// Unmodified token, as passed to constructor
3126
                typename json_traits::string_type token;
3127
                /// Header part decoded from base64
3128
                typename json_traits::string_type header;
3129
                /// Unmodified header part in base64
3130
                typename json_traits::string_type header_base64;
3131
                /// Payload part decoded from base64
3132
                typename json_traits::string_type payload;
3133
                /// Unmodified payload part in base64
3134
                typename json_traits::string_type payload_base64;
3135
                /// Signature part decoded from base64
3136
                typename json_traits::string_type signature;
3137
                /// Unmodified signature part in base64
3138
                typename json_traits::string_type signature_base64;
3139

3140
        public:
3141
                using basic_claim_t = basic_claim<json_traits>;
3142
#ifndef JWT_DISABLE_BASE64
3143
                /**
3144
                 * \brief Parses a given token
3145
                 *
3146
                 * \note Decodes using the jwt::base64url which supports an std::string
3147
                 *
3148
                 * \param token The token to parse
3149
                 * \throw std::invalid_argument Token is not in correct format
3150
                 * \throw std::runtime_error Base64 decoding failed or invalid json
3151
                 */
3152
                JWT_CLAIM_EXPLICIT decoded_jwt(const typename json_traits::string_type& token)
88✔
3153
                        : decoded_jwt(token, [](const typename json_traits::string_type& str) {
253✔
3154
                                  return base::decode<alphabet::base64url>(base::pad<alphabet::base64url>(str));
253✔
3155
                          }) {}
88✔
3156
#endif
3157
                /**
3158
                 * \brief Parses a given token
3159
                 *
3160
                 * \tparam Decode is callable, taking a string_type and returns a string_type.
3161
                 * It should ensure the padding of the input and then base64url decode and
3162
                 * return the results.
3163
                 * \param token The token to parse
3164
                 * \param decode The function to decode the token
3165
                 * \throw std::invalid_argument Token is not in correct format
3166
                 * \throw std::runtime_error Base64 decoding failed or invalid json
3167
                 */
3168
                template<typename Decode>
3169
                decoded_jwt(const typename json_traits::string_type& token, Decode decode) : token(token) {
88✔
3170
                        auto hdr_end = token.find('.');
88✔
3171
                        if (hdr_end == json_traits::string_type::npos) throw std::invalid_argument("invalid token supplied");
88✔
3172
                        auto payload_end = token.find('.', hdr_end + 1);
87✔
3173
                        if (payload_end == json_traits::string_type::npos) throw std::invalid_argument("invalid token supplied");
87✔
3174
                        header_base64 = token.substr(0, hdr_end);
85✔
3175
                        payload_base64 = token.substr(hdr_end + 1, payload_end - hdr_end - 1);
85✔
3176
                        signature_base64 = token.substr(payload_end + 1);
85✔
3177

3178
                        header = decode(header_base64);
85✔
3179
                        payload = decode(payload_base64);
84✔
3180
                        signature = decode(signature_base64);
84✔
3181

3182
                        this->header_claims = details::map_of_claims<json_traits>::parse_claims(header);
84✔
3183
                        this->payload_claims = details::map_of_claims<json_traits>::parse_claims(payload);
83✔
3184
                }
128✔
3185

3186
                /**
3187
                 * Get token string, as passed to constructor
3188
                 * \return token as passed to constructor
3189
                 */
3190
                const typename json_traits::string_type& get_token() const noexcept { return token; }
1✔
3191
                /**
3192
                 * Get header part as json string
3193
                 * \return header part after base64 decoding
3194
                 */
3195
                const typename json_traits::string_type& get_header() const noexcept { return header; }
3196
                /**
3197
                 * Get payload part as json string
3198
                 * \return payload part after base64 decoding
3199
                 */
3200
                const typename json_traits::string_type& get_payload() const noexcept { return payload; }
3201
                /**
3202
                 * Get signature part as json string
3203
                 * \return signature part after base64 decoding
3204
                 */
3205
                const typename json_traits::string_type& get_signature() const noexcept { return signature; }
95✔
3206
                /**
3207
                 * Get header part as base64 string
3208
                 * \return header part before base64 decoding
3209
                 */
3210
                const typename json_traits::string_type& get_header_base64() const noexcept { return header_base64; }
95✔
3211
                /**
3212
                 * Get payload part as base64 string
3213
                 * \return payload part before base64 decoding
3214
                 */
3215
                const typename json_traits::string_type& get_payload_base64() const noexcept { return payload_base64; }
95✔
3216
                /**
3217
                 * Get signature part as base64 string
3218
                 * \return signature part before base64 decoding
3219
                 */
3220
                const typename json_traits::string_type& get_signature_base64() const noexcept { return signature_base64; }
3221
                /**
3222
                 * Get all payload as JSON object
3223
                 * \return map of claims
3224
                 */
3225
                typename json_traits::object_type get_payload_json() const { return this->payload_claims.claims; }
3226
                /**
3227
                 * Get all header as JSON object
3228
                 * \return map of claims
3229
                 */
3230
                typename json_traits::object_type get_header_json() const { return this->header_claims.claims; }
3231
                /**
3232
                 * Get a payload claim by name
3233
                 *
3234
                 * \param name the name of the desired claim
3235
                 * \return Requested claim
3236
                 * \throw jwt::error::claim_not_present_exception if the claim was not present
3237
                 */
3238
                basic_claim_t get_payload_claim(const typename json_traits::string_type& name) const {
48✔
3239
                        return this->payload_claims.get_claim(name);
48✔
3240
                }
3241
                /**
3242
                 * Get a header claim by name
3243
                 *
3244
                 * \param name the name of the desired claim
3245
                 * \return Requested claim
3246
                 * \throw jwt::error::claim_not_present_exception if the claim was not present
3247
                 */
3248
                basic_claim_t get_header_claim(const typename json_traits::string_type& name) const {
4✔
3249
                        return this->header_claims.get_claim(name);
4✔
3250
                }
3251
        };
3252

3253
        /**
3254
         * Builder class to build and sign a new token
3255
         * Use jwt::create() to get an instance of this class.
3256
         */
3257
        template<typename Clock, typename json_traits>
3258
        class builder {
3259
                typename json_traits::object_type header_claims;
3260
                typename json_traits::object_type payload_claims;
3261

3262
                /// Instance of clock type
3263
                Clock clock;
3264

3265
        public:
3266
                /**
3267
                 * Constructor for building a new builder instance
3268
                 * \param c Clock instance
3269
                 */
3270
                JWT_CLAIM_EXPLICIT builder(Clock c) : clock(c) {}
55✔
3271
                /**
3272
                 * Set a header claim.
3273
                 * \param id Name of the claim
3274
                 * \param c Claim to add
3275
                 * \return *this to allow for method chaining
3276
                 */
3277
                builder& set_header_claim(const typename json_traits::string_type& id, typename json_traits::value_type c) {
27✔
3278
                        header_claims[id] = std::move(c);
27✔
3279
                        return *this;
27✔
3280
                }
3281

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

3420
                /**
3421
                 * Sign token and return result
3422
                 * \tparam Algo Callable method which takes a string_type and return the signed input as a string_type
3423
                 * \tparam Encode Callable method which takes a string_type and base64url safe encodes it,
3424
                 * MUST return the result with no padding; trim the result.
3425
                 * \param algo Instance of an algorithm to sign the token with
3426
                 * \param encode Callable to transform the serialized json to base64 with no padding
3427
                 * \return Final token as a string
3428
                 *
3429
                 * \note If the 'alg' header in not set in the token it will be set to `algo.name()`
3430
                 */
3431
                template<typename Algo, typename Encode>
3432
                typename json_traits::string_type sign(const Algo& algo, Encode encode) const {
3433
                        std::error_code ec;
3434
                        auto res = sign(algo, encode, ec);
3435
                        error::throw_if_error(ec);
3436
                        return res;
3437
                }
3438
#ifndef JWT_DISABLE_BASE64
3439
                /**
3440
                 * Sign token and return result
3441
                 *
3442
                 * using the `jwt::base` functions provided
3443
                 *
3444
                 * \param algo Instance of an algorithm to sign the token with
3445
                 * \return Final token as a string
3446
                 */
3447
                template<typename Algo>
3448
                typename json_traits::string_type sign(const Algo& algo) const {
55✔
3449
                        std::error_code ec;
55✔
3450
                        auto res = sign(algo, ec);
55✔
3451
                        error::throw_if_error(ec);
55✔
3452
                        return res;
102✔
3453
                }
4✔
3454
#endif
3455

3456
                /**
3457
                 * Sign token and return result
3458
                 * \tparam Algo Callable method which takes a string_type and return the signed input as a string_type
3459
                 * \tparam Encode Callable method which takes a string_type and base64url safe encodes it,
3460
                 * MUST return the result with no padding; trim the result.
3461
                 * \param algo Instance of an algorithm to sign the token with
3462
                 * \param encode Callable to transform the serialized json to base64 with no padding
3463
                 * \param ec error_code filled with details on error
3464
                 * \return Final token as a string
3465
                 *
3466
                 * \note If the 'alg' header in not set in the token it will be set to `algo.name()`
3467
                 */
3468
                template<typename Algo, typename Encode>
3469
                typename json_traits::string_type sign(const Algo& algo, Encode encode, std::error_code& ec) const {
55✔
3470
                        // make a copy such that a builder can be re-used
3471
                        typename json_traits::object_type obj_header = header_claims;
55✔
3472
                        if (header_claims.count("alg") == 0) obj_header["alg"] = typename json_traits::value_type(algo.name());
233✔
3473

3474
                        const auto header = encode(json_traits::serialize(typename json_traits::value_type(obj_header)));
55✔
3475
                        const auto payload = encode(json_traits::serialize(typename json_traits::value_type(payload_claims)));
55✔
3476
                        const auto token = header + "." + payload;
55✔
3477

3478
                        auto signature = algo.sign(token, ec);
55✔
3479
                        if (ec) return {};
55✔
3480

3481
                        return token + "." + encode(signature);
51✔
3482
                }
55✔
3483
#ifndef JWT_DISABLE_BASE64
3484
                /**
3485
                 * Sign token and return result
3486
                 *
3487
                 * using the `jwt::base` functions provided
3488
                 *
3489
                 * \param algo Instance of an algorithm to sign the token with
3490
                 * \param ec error_code filled with details on error
3491
                 * \return Final token as a string
3492
                 */
3493
                template<typename Algo>
3494
                typename json_traits::string_type sign(const Algo& algo, std::error_code& ec) const {
55✔
3495
                        return sign(
3496
                                algo,
3497
                                [](const typename json_traits::string_type& data) {
161✔
3498
                                        return base::trim<alphabet::base64url>(base::encode<alphabet::base64url>(data));
161✔
3499
                                },
3500
                                ec);
55✔
3501
                }
3502
#endif
3503
        };
3504

3505
        namespace verify_ops {
3506
                /**
3507
                 * This is the base container which holds the token that need to be verified
3508
                 */
3509
                template<typename json_traits>
3510
                struct verify_context {
3511
                        verify_context(date ctime, const decoded_jwt<json_traits>& j, size_t l)
76✔
3512
                                : current_time(ctime), jwt(j), default_leeway(l) {}
76✔
3513
                        /// Current time, retrieved from the verifiers clock and cached for performance and consistency
3514
                        date current_time;
3515
                        /// The jwt passed to the verifier
3516
                        const decoded_jwt<json_traits>& jwt;
3517
                        /// The configured default leeway for this verification
3518
                        size_t default_leeway{0};
3519

3520
                        /// The claim key to apply this comparison on
3521
                        typename json_traits::string_type claim_key{};
3522

3523
                        /**
3524
                         * \brief Helper method to get a claim from the jwt in this context
3525
                         * \param in_header check JWT header or payload sections
3526
                         * \param ec std::error_code which will indicate if any error occure
3527
                         * \return basic_claim if it was present otherwise empty
3528
                         */
3529
                        basic_claim<json_traits> get_claim(bool in_header, std::error_code& ec) const {
54✔
3530
                                if (in_header) {
54✔
3531
                                        if (!jwt.has_header_claim(claim_key)) {
3✔
3532
                                                ec = error::token_verification_error::missing_claim;
×
3533
                                                return {};
×
3534
                                        }
3535
                                        return jwt.get_header_claim(claim_key);
3✔
3536
                                } else {
3537
                                        if (!jwt.has_payload_claim(claim_key)) {
51✔
3538
                                                ec = error::token_verification_error::missing_claim;
4✔
3539
                                                return {};
4✔
3540
                                        }
3541
                                        return jwt.get_payload_claim(claim_key);
47✔
3542
                                }
3543
                        }
3544
                        /**
3545
                         * Helper method to get a claim of a specific type from the jwt in this context
3546
                         * \param in_header check JWT header or payload sections
3547
                         * \param t the expected type of the claim
3548
                         * \param ec std::error_code which will indicate if any error occure
3549
                         * \return basic_claim if it was present otherwise empty
3550
                          */
3551
                        basic_claim<json_traits> get_claim(bool in_header, json::type t, std::error_code& ec) const {
51✔
3552
                                auto c = get_claim(in_header, ec);
51✔
3553
                                if (ec) return {};
51✔
3554
                                if (c.get_type() != t) {
48✔
3555
                                        ec = error::token_verification_error::claim_type_missmatch;
1✔
3556
                                        return {};
1✔
3557
                                }
3558
                                return c;
47✔
3559
                        }
51✔
3560
                        /**
3561
                         * \brief Helper method to get a payload claim from the jwt
3562
                         * \param ec std::error_code which will indicate if any error occure
3563
                         * \return basic_claim if it was present otherwise empty
3564
                          */
3565
                        basic_claim<json_traits> get_claim(std::error_code& ec) const { return get_claim(false, ec); }
3566
                        /**
3567
                         * \brief Helper method to get a payload claim of a specific type from the jwt
3568
                         * \param t the expected type of the claim
3569
                         * \param ec std::error_code which will indicate if any error occure
3570
                         * \return basic_claim if it was present otherwise empty
3571
                          */
3572
                        basic_claim<json_traits> get_claim(json::type t, std::error_code& ec) const {
3573
                                return get_claim(false, t, ec);
3574
                        }
3575
                };
3576

3577
                /**
3578
                 * This is the default operation and does case sensitive matching
3579
                 */
3580
                template<typename json_traits, bool in_header = false>
3581
                struct equals_claim {
3582
                        const basic_claim<json_traits> expected;
3583
                        void operator()(const verify_context<json_traits>& ctx, std::error_code& ec) const {
48✔
3584
                                auto jc = ctx.get_claim(in_header, expected.get_type(), ec);
48✔
3585
                                if (ec) return;
48✔
3586
                                const bool matches = [&]() {
×
3587
                                        switch (expected.get_type()) {
44✔
3588
                                        case json::type::boolean: return expected.as_boolean() == jc.as_boolean();
×
3589
                                        case json::type::integer: return expected.as_integer() == jc.as_integer();
×
3590
                                        case json::type::number: return expected.as_number() == jc.as_number();
×
3591
                                        case json::type::string: return expected.as_string() == jc.as_string();
36✔
3592
                                        case json::type::array:
8✔
3593
                                        case json::type::object:
3594
                                                return json_traits::serialize(expected.to_json()) == json_traits::serialize(jc.to_json());
8✔
3595
                                        default: throw std::logic_error("internal error, should be unreachable");
×
3596
                                        }
3597
                                }();
44✔
3598
                                if (!matches) {
44✔
3599
                                        ec = error::token_verification_error::claim_value_missmatch;
1✔
3600
                                        return;
1✔
3601
                                }
3602
                        }
48✔
3603
                };
3604

3605
                /**
3606
                 * Checks that the current time is before the time specified in the given
3607
                 * claim. This is identical to how the "exp" check works.
3608
                 */
3609
                template<typename json_traits, bool in_header = false>
3610
                struct date_before_claim {
3611
                        const size_t leeway;
3612
                        void operator()(const verify_context<json_traits>& ctx, std::error_code& ec) const {
3613
                                auto jc = ctx.get_claim(in_header, json::type::integer, ec);
3614
                                if (ec) return;
3615
                                auto c = jc.as_date();
3616
                                if (ctx.current_time > c + std::chrono::seconds(leeway)) {
3617
                                        ec = error::token_verification_error::token_expired;
3618
                                }
3619
                        }
3620
                };
3621

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

3639
                /**
3640
                 * Checks if the given set is a subset of the set inside the token.
3641
                 * If the token value is a string it is treated as a set with a single element.
3642
                 * The comparison is case sensitive.
3643
                 */
3644
                template<typename json_traits, bool in_header = false>
3645
                struct is_subset_claim {
3646
                        const typename basic_claim<json_traits>::set_t expected;
3647
                        void operator()(const verify_context<json_traits>& ctx, std::error_code& ec) const {
3✔
3648
                                auto c = ctx.get_claim(in_header, ec);
3✔
3649
                                if (ec) return;
3✔
3650
                                if (c.get_type() == json::type::string) {
2✔
3651
                                        if (expected.size() != 1 || *expected.begin() != c.as_string()) {
2✔
3652
                                                ec = error::token_verification_error::audience_missmatch;
2✔
3653
                                                return;
2✔
3654
                                        }
3655
                                } else if (c.get_type() == json::type::array) {
×
3656
                                        auto jc = c.as_set();
×
3657
                                        for (auto& e : expected) {
×
3658
                                                if (jc.find(e) == jc.end()) {
×
3659
                                                        ec = error::token_verification_error::audience_missmatch;
×
3660
                                                        return;
×
3661
                                                }
3662
                                        }
3663
                                } else {
×
3664
                                        ec = error::token_verification_error::claim_type_missmatch;
×
3665
                                        return;
×
3666
                                }
3667
                        }
3✔
3668
                };
3669

3670
                /**
3671
                 * Checks if the claim is a string and does an case insensitive comparison.
3672
                 */
3673
                template<typename json_traits, bool in_header = false>
3674
                struct insensitive_string_claim {
3675
                        const typename json_traits::string_type expected;
3676
                        std::locale locale;
3677
                        insensitive_string_claim(const typename json_traits::string_type& e, std::locale loc)
2✔
3678
                                : expected(to_lower_unicode(e, loc)), locale(loc) {}
2✔
3679

3680
                        void operator()(const verify_context<json_traits>& ctx, std::error_code& ec) const {
3✔
3681
                                const auto c = ctx.get_claim(in_header, json::type::string, ec);
3✔
3682
                                if (ec) return;
3✔
3683
                                if (to_lower_unicode(c.as_string(), locale) != expected) {
3✔
3684
                                        ec = error::token_verification_error::claim_value_missmatch;
1✔
3685
                                }
3686
                        }
3✔
3687

3688
                        static std::string to_lower_unicode(const std::string& str, const std::locale& loc) {
5✔
3689
                                std::mbstate_t state = std::mbstate_t();
5✔
3690
                                const char* in_next = str.data();
5✔
3691
                                const char* in_end = str.data() + str.size();
5✔
3692
                                std::wstring wide;
5✔
3693
                                wide.reserve(str.size());
5✔
3694

3695
                                while (in_next != in_end) {
20✔
3696
                                        wchar_t wc;
3697
                                        std::size_t result = std::mbrtowc(&wc, in_next, in_end - in_next, &state);
15✔
3698
                                        if (result == static_cast<std::size_t>(-1)) {
15✔
3699
                                                throw std::runtime_error("encoding error: " + std::string(std::strerror(errno)));
×
3700
                                        } else if (result == static_cast<std::size_t>(-2)) {
15✔
3701
                                                throw std::runtime_error("conversion error: next bytes constitute an incomplete, but so far "
×
3702
                                                                                                 "valid, multibyte character.");
3703
                                        }
3704
                                        in_next += result;
15✔
3705
                                        wide.push_back(wc);
15✔
3706
                                }
3707

3708
                                auto& f = std::use_facet<std::ctype<wchar_t>>(loc);
5✔
3709
                                f.tolower(&wide[0], &wide[0] + wide.size());
5✔
3710

3711
                                std::string out;
5✔
3712
                                out.reserve(wide.size());
5✔
3713
                                for (wchar_t wc : wide) {
20✔
3714
                                        char mb[MB_LEN_MAX];
3715
                                        std::size_t n = std::wcrtomb(mb, wc, &state);
15✔
3716
                                        if (n != static_cast<std::size_t>(-1)) out.append(mb, n);
15✔
3717
                                }
3718

3719
                                return out;
10✔
3720
                        }
5✔
3721
                };
3722
        } // namespace verify_ops
3723

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

3744
        private:
3745
                struct algo_base {
3746
                        virtual ~algo_base() = default;
84✔
3747
                        virtual void verify(const std::string& data, const std::string& sig, std::error_code& ec) = 0;
3748
                };
3749
                template<typename T>
3750
                struct algo : public algo_base {
3751
                        T alg;
3752
                        explicit algo(T a) : alg(a) {}
84✔
3753
                        void verify(const std::string& data, const std::string& sig, std::error_code& ec) override {
94✔
3754
                                alg.verify(data, sig, ec);
94✔
3755
                        }
94✔
3756
                };
3757
                /// Required claims
3758
                std::unordered_map<typename json_traits::string_type, verify_check_fn_t> claims;
3759
                /// Leeway time for exp, nbf and iat
3760
                size_t default_leeway = 0;
3761
                /// Instance of clock type
3762
                Clock clock;
3763
                /// Supported algorithms
3764
                std::unordered_map<std::string, std::shared_ptr<algo_base>> algs;
3765

3766
        public:
3767
                /**
3768
                 * Constructor for building a new verifier instance
3769
                 * \param c Clock instance
3770
                 */
3771
                explicit verifier(Clock c) : clock(c) {
88✔
3772
                        claims["exp"] = [](const verify_ops::verify_context<json_traits>& ctx, std::error_code& ec) {
211✔
3773
                                if (!ctx.jwt.has_expires_at()) return;
63✔
3774
                                auto exp = ctx.jwt.get_expires_at();
20✔
3775
                                if (ctx.current_time > exp + std::chrono::seconds(ctx.default_leeway)) {
20✔
3776
                                        ec = error::token_verification_error::token_expired;
10✔
3777
                                }
3778
                        };
3779
                        claims["iat"] = [](const verify_ops::verify_context<json_traits>& ctx, std::error_code& ec) {
213✔
3780
                                if (!ctx.jwt.has_issued_at()) return;
65✔
3781
                                auto iat = ctx.jwt.get_issued_at();
20✔
3782
                                if (ctx.current_time < iat - std::chrono::seconds(ctx.default_leeway)) {
20✔
3783
                                        ec = error::token_verification_error::token_expired;
2✔
3784
                                }
3785
                        };
3786
                        claims["nbf"] = [](const verify_ops::verify_context<json_traits>& ctx, std::error_code& ec) {
217✔
3787
                                if (!ctx.jwt.has_not_before()) return;
69✔
3788
                                auto nbf = ctx.jwt.get_not_before();
4✔
3789
                                if (ctx.current_time < nbf - std::chrono::seconds(ctx.default_leeway)) {
4✔
3790
                                        ec = error::token_verification_error::token_expired;
2✔
3791
                                }
3792
                        };
3793
                }
88✔
3794

3795
                /**
3796
                 * Set default leeway to use.
3797
                 * \param leeway Default leeway to use if not specified otherwise
3798
                 * \return *this to allow chaining
3799
                 */
3800
                verifier& leeway(size_t leeway) {
3801
                        default_leeway = leeway;
3802
                        return *this;
3803
                }
3804
                /**
3805
                 * Set leeway for expires at.
3806
                 * If not specified the default leeway will be used.
3807
                 * \param leeway Set leeway to use for expires at.
3808
                 * \return *this to allow chaining
3809
                 */
3810
                verifier& expires_at_leeway(size_t leeway) {
3811
                        claims["exp"] = verify_ops::date_before_claim<json_traits>{leeway};
3812
                        return *this;
3813
                }
3814
                /**
3815
                 * Set leeway for not before.
3816
                 * If not specified the default leeway will be used.
3817
                 * \param leeway Set leeway to use for not before.
3818
                 * \return *this to allow chaining
3819
                 */
3820
                verifier& not_before_leeway(size_t leeway) {
3821
                        claims["nbf"] = verify_ops::date_after_claim<json_traits>{leeway};
3822
                        return *this;
3823
                }
3824
                /**
3825
                 * Set leeway for issued at.
3826
                 * If not specified the default leeway will be used.
3827
                 * \param leeway Set leeway to use for issued at.
3828
                 * \return *this to allow chaining
3829
                 */
3830
                verifier& issued_at_leeway(size_t leeway) {
3831
                        claims["iat"] = verify_ops::date_after_claim<json_traits>{leeway};
3832
                        return *this;
3833
                }
3834

3835
                /**
3836
                 * Set an type to check for.
3837
                 *
3838
                 * According to [RFC 7519 Section 5.1](https://datatracker.ietf.org/doc/html/rfc7519#section-5.1),
3839
                 * This parameter is ignored by JWT implementations; any processing of this parameter is performed by the JWT application.
3840
                 * Check is case sensitive.
3841
                 *
3842
                 * \param type Type Header Parameter to check for.
3843
                 * \param locale Localization functionality to use when comparing
3844
                 * \return *this to allow chaining
3845
                 */
3846
                verifier& with_type(const typename json_traits::string_type& type, std::locale locale = std::locale{}) {
2✔
3847
                        return with_claim("typ", verify_ops::insensitive_string_claim<json_traits, true>{type, std::move(locale)});
6✔
3848
                }
3849

3850
                /**
3851
                 * Set an issuer to check for.
3852
                 * Check is case sensitive.
3853
                 * \param iss Issuer to check for.
3854
                 * \return *this to allow chaining
3855
                 */
3856
                verifier& with_issuer(const typename json_traits::string_type& iss) {
44✔
3857
                        return with_claim("iss", basic_claim_t(iss));
132✔
3858
                }
3859

3860
                /**
3861
                 * Set a subject to check for.
3862
                 * Check is case sensitive.
3863
                 * \param sub Subject to check for.
3864
                 * \return *this to allow chaining
3865
                 */
3866
                verifier& with_subject(const typename json_traits::string_type& sub) {
1✔
3867
                        return with_claim("sub", basic_claim_t(sub));
3✔
3868
                }
3869
                /**
3870
                 * Set an audience to check for.
3871
                 * If any of the specified audiences is not present in the token the check fails.
3872
                 * \param aud Audience to check for.
3873
                 * \return *this to allow chaining
3874
                 */
3875
                verifier& with_audience(const typename basic_claim_t::set_t& aud) {
3✔
3876
                        claims["aud"] = verify_ops::is_subset_claim<json_traits>{aud};
9✔
3877
                        return *this;
3✔
3878
                }
3✔
3879
                /**
3880
                 * Set an audience to check for.
3881
                 * If the specified audiences is not present in the token the check fails.
3882
                 * \param aud Audience to check for.
3883
                 * \return *this to allow chaining
3884
                 */
3885
                verifier& with_audience(const typename json_traits::string_type& aud) {
2✔
3886
                        typename basic_claim_t::set_t s;
2✔
3887
                        s.insert(aud);
2✔
3888
                        return with_audience(s);
4✔
3889
                }
2✔
3890
                /**
3891
                 * Set an id to check for.
3892
                 * Check is case sensitive.
3893
                 * \param id ID to check for.
3894
                 * \return *this to allow chaining
3895
                 */
3896
                verifier& with_id(const typename json_traits::string_type& id) { return with_claim("jti", basic_claim_t(id)); }
3897

3898
                /**
3899
                 * Specify a claim to check for using the specified operation.
3900
                 * This is helpful for implementating application specific authentication checks
3901
                 * such as the one seen in partial-claim-verifier.cpp
3902
                 *
3903
                 * \snippet{trimleft} partial-claim-verifier.cpp verifier check custom claim
3904
                 *
3905
                 * \param name Name of the claim to check for
3906
                 * \param fn Function to use for verifying the claim
3907
                 * \return *this to allow chaining
3908
                 */
3909
                verifier& with_claim(const typename json_traits::string_type& name, verify_check_fn_t fn) {
58✔
3910
                        claims[name] = fn;
58✔
3911
                        return *this;
58✔
3912
                }
3913

3914
                /**
3915
                 * Specify a claim to check for equality (both type & value).
3916
                 * See the private-claims.cpp example.
3917
                 *
3918
                 * \snippet{trimleft} private-claims.cpp verify exact claim
3919
                 *
3920
                 * \param name Name of the claim to check for
3921
                 * \param c Claim to check for
3922
                 * \return *this to allow chaining
3923
                 */
3924
                verifier& with_claim(const typename json_traits::string_type& name, basic_claim_t c) {
56✔
3925
                        return with_claim(name, verify_ops::equals_claim<json_traits>{c});
56✔
3926
                }
56✔
3927

3928
                /**
3929
                 * \brief Add an algorithm available for checking.
3930
                 *
3931
                 * This is used to handle incomming tokens for predefined algorithms
3932
                 * which the authorization server is provided. For example a small system
3933
                 * where only a single RSA key-pair is used to sign tokens
3934
                 *
3935
                 * \snippet{trimleft} example/rsa-verify.cpp allow rsa algorithm
3936
                 *
3937
                 * \tparam Algorithm any algorithm such as those provided by jwt::algorithm
3938
                 * \param alg Algorithm to allow
3939
                 * \return *this to allow chaining
3940
                 */
3941
                template<typename Algorithm>
3942
                verifier& allow_algorithm(Algorithm alg) {
84✔
3943
                        algs[alg.name()] = std::make_shared<algo<Algorithm>>(alg);
84✔
3944
                        return *this;
84✔
3945
                }
3946

3947
                /**
3948
                 * Verify the given token.
3949
                 * \param jwt Token to check
3950
                 * \throw token_verification_exception Verification failed
3951
                 */
3952
                void verify(const decoded_jwt<json_traits>& jwt) const {
83✔
3953
                        std::error_code ec;
83✔
3954
                        verify(jwt, ec);
83✔
3955
                        error::throw_if_error(ec);
83✔
3956
                }
49✔
3957
                /**
3958
                 * Verify the given token.
3959
                 * \param jwt Token to check
3960
                 * \param ec error_code filled with details on error
3961
                 */
3962
                void verify(const decoded_jwt<json_traits>& jwt, std::error_code& ec) const {
95✔
3963
                        ec.clear();
95✔
3964
                        const typename json_traits::string_type data = jwt.get_header_base64() + "." + jwt.get_payload_base64();
95✔
3965
                        const typename json_traits::string_type sig = jwt.get_signature();
95✔
3966
                        const std::string algo = jwt.get_algorithm();
95✔
3967
                        if (algs.count(algo) == 0) {
95✔
3968
                                ec = error::token_verification_error::wrong_algorithm;
1✔
3969
                                return;
1✔
3970
                        }
3971
                        algs.at(algo)->verify(data, sig, ec);
94✔
3972
                        if (ec) return;
94✔
3973

3974
                        verify_ops::verify_context<json_traits> ctx{clock.now(), jwt, default_leeway};
76✔
3975
                        for (auto& c : claims) {
304✔
3976
                                ctx.claim_key = c.first;
251✔
3977
                                c.second(ctx, ec);
251✔
3978
                                if (ec) return;
251✔
3979
                        }
3980
                }
202✔
3981
        };
3982

3983
        /**
3984
         * \brief JSON Web Key
3985
         *
3986
         * https://tools.ietf.org/html/rfc7517
3987
         *
3988
         * A JSON object that represents a cryptographic key.  The members of
3989
         * the object represent properties of the key, including its value.
3990
         */
3991
        template<typename json_traits>
3992
        class jwk {
3993
                using basic_claim_t = basic_claim<json_traits>;
3994
                const details::map_of_claims<json_traits> jwk_claims;
3995

3996
        public:
3997
                JWT_CLAIM_EXPLICIT jwk(const typename json_traits::string_type& str)
3✔
3998
                        : jwk_claims(details::map_of_claims<json_traits>::parse_claims(str)) {}
3✔
3999

4000
                JWT_CLAIM_EXPLICIT jwk(const typename json_traits::value_type& json)
7✔
4001
                        : jwk_claims(json_traits::as_object(json)) {}
7✔
4002

4003
                /**
4004
                 * Get key type claim
4005
                 *
4006
                 * This returns the general type (e.g. RSA or EC), not a specific algorithm value.
4007
                 * \return key type as string
4008
                 * \throw std::runtime_error If claim was not present
4009
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4010
                 */
4011
                typename json_traits::string_type get_key_type() const { return get_jwk_claim("kty").as_string(); }
4012

4013
                /**
4014
                 * Get public key usage claim
4015
                 * \return usage parameter as string
4016
                 * \throw std::runtime_error If claim was not present
4017
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4018
                 */
4019
                typename json_traits::string_type get_use() const { return get_jwk_claim("use").as_string(); }
4020

4021
                /**
4022
                 * Get key operation types claim
4023
                 * \return key operation types as a set of strings
4024
                 * \throw std::runtime_error If claim was not present
4025
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4026
                 */
4027
                typename basic_claim_t::set_t get_key_operations() const { return get_jwk_claim("key_ops").as_set(); }
4028

4029
                /**
4030
                 * Get algorithm claim
4031
                 * \return algorithm as string
4032
                 * \throw std::runtime_error If claim was not present
4033
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4034
                 */
4035
                typename json_traits::string_type get_algorithm() const { return get_jwk_claim("alg").as_string(); }
6✔
4036

4037
                /**
4038
                 * Get key id claim
4039
                 * \return key id as string
4040
                 * \throw std::runtime_error If claim was not present
4041
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4042
                 */
4043
                typename json_traits::string_type get_key_id() const { return get_jwk_claim("kid").as_string(); }
51✔
4044

4045
                /**
4046
                 * \brief Get curve claim
4047
                 *
4048
                 * https://www.rfc-editor.org/rfc/rfc7518.html#section-6.2.1.1
4049
                 * https://www.iana.org/assignments/jose/jose.xhtml#table-web-key-elliptic-curve
4050
                 *
4051
                 * \return curve as string
4052
                 * \throw std::runtime_error If claim was not present
4053
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4054
                 */
4055
                typename json_traits::string_type get_curve() const { return get_jwk_claim("crv").as_string(); }
4056

4057
                /**
4058
                 * Get x5c claim
4059
                 * \return x5c as an array
4060
                 * \throw std::runtime_error If claim was not present
4061
                 * \throw std::bad_cast Claim was present but not a array (Should not happen in a valid token)
4062
                 */
4063
                typename json_traits::array_type get_x5c() const { return get_jwk_claim("x5c").as_array(); };
9✔
4064

4065
                /**
4066
                 * Get X509 URL claim
4067
                 * \return x5u as string
4068
                 * \throw std::runtime_error If claim was not present
4069
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4070
                 */
4071
                typename json_traits::string_type get_x5u() const { return get_jwk_claim("x5u").as_string(); };
4072

4073
                /**
4074
                 * Get X509 thumbprint claim
4075
                 * \return x5t as string
4076
                 * \throw std::runtime_error If claim was not present
4077
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4078
                 */
4079
                typename json_traits::string_type get_x5t() const { return get_jwk_claim("x5t").as_string(); };
4080

4081
                /**
4082
                 * Get X509 SHA256 thumbprint claim
4083
                 * \return x5t#S256 as string
4084
                 * \throw std::runtime_error If claim was not present
4085
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4086
                 */
4087
                typename json_traits::string_type get_x5t_sha256() const { return get_jwk_claim("x5t#S256").as_string(); };
4088

4089
                /**
4090
                 * Get x5c claim as a string
4091
                 * \return x5c as an string
4092
                 * \throw std::runtime_error If claim was not present
4093
                 * \throw std::bad_cast Claim was present but not a string (Should not happen in a valid token)
4094
                 */
4095
                typename json_traits::string_type get_x5c_key_value() const {
2✔
4096
                        auto x5c_array = get_jwk_claim("x5c").as_array();
2✔
4097
                        if (x5c_array.size() == 0) throw error::claim_not_present_exception();
2✔
4098

4099
                        return json_traits::as_string(x5c_array.front());
2✔
4100
                };
2✔
4101

4102
                /**
4103
                 * Check if a key type is present ("kty")
4104
                 * \return true if present, false otherwise
4105
                 */
4106
                bool has_key_type() const noexcept { return has_jwk_claim("kty"); }
4107

4108
                /**
4109
                 * Check if a public key usage indication is present ("use")
4110
                 * \return true if present, false otherwise
4111
                 */
4112
                bool has_use() const noexcept { return has_jwk_claim("use"); }
4113

4114
                /**
4115
                 * Check if a key operations parameter is present ("key_ops")
4116
                 * \return true if present, false otherwise
4117
                 */
4118
                bool has_key_operations() const noexcept { return has_jwk_claim("key_ops"); }
4119

4120
                /**
4121
                 * Check if algorithm is present ("alg")
4122
                 * \return true if present, false otherwise
4123
                 */
4124
                bool has_algorithm() const noexcept { return has_jwk_claim("alg"); }
9✔
4125

4126
                /**
4127
                 * Check if curve is present ("crv")
4128
                 * \return true if present, false otherwise
4129
                 */
4130
                bool has_curve() const noexcept { return has_jwk_claim("crv"); }
4131

4132
                /**
4133
                 * Check if key id is present ("kid")
4134
                 * \return true if present, false otherwise
4135
                 */
4136
                bool has_key_id() const noexcept { return has_jwk_claim("kid"); }
45✔
4137

4138
                /**
4139
                 * Check if X509 URL is present ("x5u")
4140
                 * \return true if present, false otherwise
4141
                 */
4142
                bool has_x5u() const noexcept { return has_jwk_claim("x5u"); }
4143

4144
                /**
4145
                 * Check if X509 Chain is present ("x5c")
4146
                 * \return true if present, false otherwise
4147
                 */
4148
                bool has_x5c() const noexcept { return has_jwk_claim("x5c"); }
6✔
4149

4150
                /**
4151
                 * Check if a X509 thumbprint is present ("x5t")
4152
                 * \return true if present, false otherwise
4153
                 */
4154
                bool has_x5t() const noexcept { return has_jwk_claim("x5t"); }
4155

4156
                /**
4157
                 * Check if a X509 SHA256 thumbprint is present ("x5t#S256")
4158
                 * \return true if present, false otherwise
4159
                 */
4160
                bool has_x5t_sha256() const noexcept { return has_jwk_claim("x5t#S256"); }
4161

4162
                /**
4163
                 * Check if a jwk claim is present
4164
                 * \return true if claim was present, false otherwise
4165
                 */
4166
                bool has_jwk_claim(const typename json_traits::string_type& name) const noexcept {
22✔
4167
                        return jwk_claims.has_claim(name);
22✔
4168
                }
4169

4170
                /**
4171
                 * Get jwk claim by name
4172
                 * \return Requested claim
4173
                 * \throw std::runtime_error If claim was not present
4174
                 */
4175
                basic_claim_t get_jwk_claim(const typename json_traits::string_type& name) const {
24✔
4176
                        return jwk_claims.get_claim(name);
24✔
4177
                }
4178

4179
                /**
4180
                * Check if the jwk has any claims
4181
                * \return true is any claim is present
4182
                 */
4183
                bool empty() const noexcept { return jwk_claims.empty(); }
4184

4185
                /**
4186
                 * Get all jwk claims
4187
                 * \return Map of claims
4188
                 */
4189
                typename json_traits::object_type get_claims() const { return this->jwk_claims.claims; }
4190
        };
4191

4192
        /**
4193
         * \brief JWK Set
4194
         *
4195
         * https://tools.ietf.org/html/rfc7517
4196
         *
4197
         * A JSON object that represents a set of JWKs.  The JSON object MUST
4198
         * have a "keys" member, which is an array of JWKs.
4199
         *
4200
         * This container takes a JWKs and simplifies it to a vector of JWKs
4201
         */
4202
        template<typename json_traits>
4203
        class jwks {
4204
        public:
4205
                /// JWK instance template specialization
4206
                using jwks_t = jwk<json_traits>;
4207
                /// Type specialization for the vector of JWK
4208
                using jwks_vector_t = std::vector<jwks_t>;
4209
                using iterator = typename jwks_vector_t::iterator;
4210
                using const_iterator = typename jwks_vector_t::const_iterator;
4211

4212
                /**
4213
                 * Default constructor producing an empty object without any keys
4214
                 */
4215
                jwks() = default;
1✔
4216

4217
                /**
4218
                 * Parses a string buffer to extract the JWKS.
4219
                 * \param str buffer containing JSON object representing a JWKS
4220
                 * \throw error::invalid_json_exception or underlying JSON implation error if the JSON is
4221
                 *        invalid with regards to the JWKS specification
4222
                */
4223
                JWT_CLAIM_EXPLICIT jwks(const typename json_traits::string_type& str) {
3✔
4224
                        typename json_traits::value_type parsed_val;
3✔
4225
                        if (!json_traits::parse(parsed_val, str)) throw error::invalid_json_exception();
3✔
4226

4227
                        const details::map_of_claims<json_traits> jwks_json = json_traits::as_object(parsed_val);
3✔
4228
                        if (!jwks_json.has_claim("keys")) throw error::invalid_json_exception();
6✔
4229

4230
                        auto jwk_list = jwks_json.get_claim("keys").as_array();
3✔
4231
                        std::transform(jwk_list.begin(), jwk_list.end(), std::back_inserter(jwk_claims),
3✔
4232
                                                   [](const typename json_traits::value_type& val) { return jwks_t{val}; });
7✔
4233
                }
3✔
4234

4235
                iterator begin() { return jwk_claims.begin(); }
2✔
4236
                iterator end() { return jwk_claims.end(); }
2✔
4237
                const_iterator cbegin() const { return jwk_claims.begin(); }
9✔
4238
                const_iterator cend() const { return jwk_claims.end(); }
9✔
4239
                const_iterator begin() const { return jwk_claims.begin(); }
4240
                const_iterator end() const { return jwk_claims.end(); }
9✔
4241

4242
                /**
4243
                 * Check if a jwk with the kid is present
4244
                 * \return true if jwk was present, false otherwise
4245
                 */
4246
                bool has_jwk(const typename json_traits::string_type& key_id) const noexcept {
4✔
4247
                        return find_by_kid(key_id) != end();
4✔
4248
                }
4249

4250
                /**
4251
                 * Get jwk
4252
                 * \return Requested jwk by key_id
4253
                 * \throw std::runtime_error If jwk was not present
4254
                 */
4255
                jwks_t get_jwk(const typename json_traits::string_type& key_id) const {
5✔
4256
                        const auto maybe = find_by_kid(key_id);
5✔
4257
                        if (maybe == end()) throw error::claim_not_present_exception();
5✔
4258
                        return *maybe;
8✔
4259
                }
4260

4261
        private:
4262
                jwks_vector_t jwk_claims;
4263

4264
                const_iterator find_by_kid(const typename json_traits::string_type& key_id) const noexcept {
9✔
4265
                        return std::find_if(cbegin(), cend(), [key_id](const jwks_t& jwk) {
18✔
4266
                                if (!jwk.has_key_id()) { return false; }
13✔
4267
                                return jwk.get_key_id() == key_id;
13✔
4268
                        });
9✔
4269
                }
4270
        };
4271

4272
        /**
4273
         * Create a verifier using the given clock
4274
         * \param c Clock instance to use
4275
         * \return verifier instance
4276
         */
4277
        template<typename Clock, typename json_traits>
4278
        verifier<Clock, json_traits> verify(Clock c) {
64✔
4279
                return verifier<Clock, json_traits>(c);
64✔
4280
        }
4281

4282
        /**
4283
         * Create a builder using the given clock
4284
         * \param c Clock instance to use
4285
         * \return builder instance
4286
         */
4287
        template<typename Clock, typename json_traits>
4288
        builder<Clock, json_traits> create(Clock c) {
4289
                return builder<Clock, json_traits>(c);
4290
        }
4291

4292
        /**
4293
         * Default clock class using std::chrono::system_clock as a backend.
4294
         */
4295
        struct default_clock {
4296
                /**
4297
                 * Gets the current system time
4298
                 * \return time_point of the host system
4299
                 */
4300
                date now() const { return date::clock::now(); }
72✔
4301
        };
4302

4303
        /**
4304
         * Create a verifier using the default_clock.
4305
         *
4306
         *
4307
         *
4308
         * \param c Clock instance to use
4309
         * \return verifier instance
4310
         */
4311
        template<typename json_traits>
4312
        verifier<default_clock, json_traits> verify(default_clock c = {}) {
24✔
4313
                return verifier<default_clock, json_traits>(c);
24✔
4314
        }
4315

4316
        /**
4317
         * Return a builder instance to create a new token
4318
         */
4319
        template<typename json_traits>
4320
        builder<default_clock, json_traits> create(default_clock c = {}) {
20✔
4321
                return builder<default_clock, json_traits>(c);
20✔
4322
        }
4323

4324
        /**
4325
         * \brief Decode a token. This can be used to to help access important feild like 'x5c'
4326
         * for verifying tokens. See associated example rsa-verify.cpp for more details.
4327
         *
4328
         * \tparam json_traits JSON implementation traits
4329
         * \tparam Decode is callable, taking a string_type and returns a string_type.
4330
         *         It should ensure the padding of the input and then base64url decode and
4331
         *         return the results.
4332
         * \param token Token to decode
4333
         * \param decode function that will pad and base64url decode the token
4334
         * \return Decoded token
4335
         * \throw std::invalid_argument Token is not in correct format
4336
         * \throw std::runtime_error Base64 decoding failed or invalid json
4337
         */
4338
        template<typename json_traits, typename Decode>
4339
        decoded_jwt<json_traits> decode(const typename json_traits::string_type& token, Decode decode) {
4340
                return decoded_jwt<json_traits>(token, decode);
4341
        }
4342

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

4383
template<typename json_traits>
4384
std::istream& operator>>(std::istream& is, jwt::basic_claim<json_traits>& c) {
8✔
4385
        return c.operator>>(is);
8✔
4386
}
4387

4388
template<typename json_traits>
4389
std::ostream& operator<<(std::ostream& os, const jwt::basic_claim<json_traits>& c) {
4390
        return os << c.to_json();
4391
}
4392

4393
#ifndef JWT_DISABLE_PICOJSON
4394
#include "traits/kazuho-picojson/defaults.h"
4395
#endif
4396

4397
#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