1 /*
2 Copyright (c) 2017 Arun Muralidharan
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 */
22
23 #ifndef CPP_JWT_ALGORITHM_HPP
24 #define CPP_JWT_ALGORITHM_HPP
25
26 /*!
27 * Most of the signing and verification code has been taken
28 * and modified for C++ specific use from the C implementation
29 * JWT library, libjwt.
30 * https://github.com/benmcollins/libjwt/tree/master/libjwt
31 */
32
33 #include <cassert>
34 #include <memory>
35 #include <system_error>
36
37 #include <openssl/bn.h>
38 #include <openssl/bio.h>
39 #include <openssl/pem.h>
40 #include <openssl/evp.h>
41 #include <openssl/hmac.h>
42 #include <openssl/ecdsa.h>
43 #include <openssl/buffer.h>
44 #include <openssl/opensslv.h>
45
46 #include "jwt/exceptions.hpp"
47 #include "jwt/string_view.hpp"
48 #include "jwt/error_codes.hpp"
49 #include "jwt/base64.hpp"
50 #include "jwt/config.hpp"
51
52 namespace jwt {
53
54 /// The result type of the signing function
55 using sign_result_t = std::pair<std::string, std::error_code>;
56 /// The result type of verification function
57 using verify_result_t = std::pair<bool, std::error_code>;
58 /// The function pointer type for the signing function
59 using sign_func_t = sign_result_t (*) (const jwt::string_view key,
60 const jwt::string_view data);
61 /// The function pointer type for the verifying function
62 using verify_func_t = verify_result_t (*) (const jwt::string_view key,
63 const jwt::string_view head,
64 const jwt::string_view jwt_sign);
65
66 namespace algo {
67
68 //Me: TODO: All these can be done using code generaion.
69 //Me: NO. NEVER. I hate Macros.
70 //Me: You can use templates too.
71 //Me: No. I would rather prefer explicit.
72 //Me: Ok. You win.
73 //Me: Same to you.
74
75 /**
76 * HS256 algorithm.
77 */
78 struct HS256
79 {
operator ()jwt::algo::HS25680 const EVP_MD* operator()() noexcept
81 {
82 return EVP_sha256();
83 }
84 };
85
86 /**
87 * HS384 algorithm.
88 */
89 struct HS384
90 {
operator ()jwt::algo::HS38491 const EVP_MD* operator()() noexcept
92 {
93 return EVP_sha384();
94 }
95 };
96
97 /**
98 * HS512 algorithm.
99 */
100 struct HS512
101 {
operator ()jwt::algo::HS512102 const EVP_MD* operator()() noexcept
103 {
104 return EVP_sha512();
105 }
106 };
107
108 /**
109 * NONE algorithm.
110 */
111 struct NONE
112 {
operator ()jwt::algo::NONE113 void operator()() noexcept
114 {
115 return;
116 }
117 };
118
119 /**
120 * RS256 algorithm.
121 */
122 struct RS256
123 {
124 static const int type = EVP_PKEY_RSA;
125
operator ()jwt::algo::RS256126 const EVP_MD* operator()() noexcept
127 {
128 return EVP_sha256();
129 }
130 };
131
132 /**
133 * RS384 algorithm.
134 */
135 struct RS384
136 {
137 static const int type = EVP_PKEY_RSA;
138
operator ()jwt::algo::RS384139 const EVP_MD* operator()() noexcept
140 {
141 return EVP_sha384();
142 }
143 };
144
145 /**
146 * RS512 algorithm.
147 */
148 struct RS512
149 {
150 static const int type = EVP_PKEY_RSA;
151
operator ()jwt::algo::RS512152 const EVP_MD* operator()() noexcept
153 {
154 return EVP_sha512();
155 }
156 };
157
158 /**
159 * ES256 algorithm.
160 */
161 struct ES256
162 {
163 static const int type = EVP_PKEY_EC;
164
operator ()jwt::algo::ES256165 const EVP_MD* operator()() noexcept
166 {
167 return EVP_sha256();
168 }
169 };
170
171 /**
172 * ES384 algorithm.
173 */
174 struct ES384
175 {
176 static const int type = EVP_PKEY_EC;
177
operator ()jwt::algo::ES384178 const EVP_MD* operator()() noexcept
179 {
180 return EVP_sha384();
181 }
182 };
183
184 /**
185 * ES512 algorithm.
186 */
187 struct ES512
188 {
189 static const int type = EVP_PKEY_EC;
190
operator ()jwt::algo::ES512191 const EVP_MD* operator()() noexcept
192 {
193 return EVP_sha512();
194 }
195 };
196
197 } //END Namespace algo
198
199
200 /**
201 * JWT signing algorithm types.
202 */
203 enum class algorithm
204 {
205 NONE = 0,
206 HS256,
207 HS384,
208 HS512,
209 RS256,
210 RS384,
211 RS512,
212 ES256,
213 ES384,
214 ES512,
215 UNKN,
216 TERM,
217 };
218
219
220 /**
221 * Convert the algorithm enum class type to
222 * its stringified form.
223 */
alg_to_str(SCOPED_ENUM algorithm alg)224 inline jwt::string_view alg_to_str(SCOPED_ENUM algorithm alg) noexcept
225 {
226 switch (alg) {
227 case algorithm::HS256: return "HS256";
228 case algorithm::HS384: return "HS384";
229 case algorithm::HS512: return "HS512";
230 case algorithm::RS256: return "RS256";
231 case algorithm::RS384: return "RS384";
232 case algorithm::RS512: return "RS512";
233 case algorithm::ES256: return "ES256";
234 case algorithm::ES384: return "ES384";
235 case algorithm::ES512: return "ES512";
236 case algorithm::TERM: return "TERM";
237 case algorithm::NONE: return "NONE";
238 case algorithm::UNKN: return "UNKN";
239 default: assert (0 && "Unknown Algorithm");
240 };
241 return "UNKN";
242 assert (0 && "Code not reached");
243 }
244
245 /**
246 * Convert stringified algorithm to enum class.
247 * The string comparison is case insesitive.
248 */
str_to_alg(const jwt::string_view alg)249 inline SCOPED_ENUM algorithm str_to_alg(const jwt::string_view alg) noexcept
250 {
251 if (!alg.length()) return algorithm::NONE;
252
253 if (!strcasecmp(alg.data(), "none")) return algorithm::NONE;
254 if (!strcasecmp(alg.data(), "hs256")) return algorithm::HS256;
255 if (!strcasecmp(alg.data(), "hs384")) return algorithm::HS384;
256 if (!strcasecmp(alg.data(), "hs512")) return algorithm::HS512;
257 if (!strcasecmp(alg.data(), "rs256")) return algorithm::RS256;
258 if (!strcasecmp(alg.data(), "rs384")) return algorithm::RS384;
259 if (!strcasecmp(alg.data(), "rs512")) return algorithm::RS512;
260 if (!strcasecmp(alg.data(), "es256")) return algorithm::ES256;
261 if (!strcasecmp(alg.data(), "es384")) return algorithm::ES384;
262 if (!strcasecmp(alg.data(), "es512")) return algorithm::ES512;
263
264 return algorithm::UNKN;
265
266 assert (0 && "Code not reached");
267 }
268
269 /**
270 */
bio_deletor(BIO * ptr)271 inline void bio_deletor(BIO* ptr)
272 {
273 if (ptr) BIO_free_all(ptr);
274 }
275
276 /**
277 */
evp_md_ctx_deletor(EVP_MD_CTX * ptr)278 inline void evp_md_ctx_deletor(EVP_MD_CTX* ptr)
279 {
280 if (ptr) EVP_MD_CTX_destroy(ptr);
281 }
282
283 /**
284 */
ec_key_deletor(EC_KEY * ptr)285 inline void ec_key_deletor(EC_KEY* ptr)
286 {
287 if (ptr) EC_KEY_free(ptr);
288 }
289
290 /**
291 */
ec_sig_deletor(ECDSA_SIG * ptr)292 inline void ec_sig_deletor(ECDSA_SIG* ptr)
293 {
294 if (ptr) ECDSA_SIG_free(ptr);
295 }
296
297 /**
298 */
ev_pkey_deletor(EVP_PKEY * ptr)299 inline void ev_pkey_deletor(EVP_PKEY* ptr)
300 {
301 if (ptr) EVP_PKEY_free(ptr);
302 }
303
304 /// Useful typedefs
305 using bio_deletor_t = decltype(&bio_deletor);
306 using BIO_uptr = std::unique_ptr<BIO, bio_deletor_t>;
307
308 using evp_mdctx_deletor_t = decltype(&evp_md_ctx_deletor);
309 using EVP_MDCTX_uptr = std::unique_ptr<EVP_MD_CTX, evp_mdctx_deletor_t>;
310
311 using eckey_deletor_t = decltype(&ec_key_deletor);
312 using EC_KEY_uptr = std::unique_ptr<EC_KEY, eckey_deletor_t>;
313
314 using ecsig_deletor_t = decltype(&ec_sig_deletor);
315 using EC_SIG_uptr = std::unique_ptr<ECDSA_SIG, ecsig_deletor_t>;
316
317 using evpkey_deletor_t = decltype(&ev_pkey_deletor);
318 using EC_PKEY_uptr = std::unique_ptr<EVP_PKEY, evpkey_deletor_t>;
319
320
321
322 /**
323 * OpenSSL HMAC based signature and verfication.
324 *
325 * The template type `Hasher` takes the type representing
326 * the HMAC algorithm type from the `jwt::algo` namespace.
327 *
328 * The struct is specialized for NONE algorithm. See the
329 * details of that class as well.
330 */
331 template <typename Hasher>
332 struct HMACSign
333 {
334 /// The type of Hashing algorithm
335 using hasher_type = Hasher;
336
337 /**
338 * Signs the input using the HMAC algorithm using the
339 * provided key.
340 *
341 * Arguments:
342 * @key : The secret/key to use for the signing.
343 * Cannot be empty string.
344 * @data : The data to be signed.
345 *
346 * Exceptions:
347 * Any allocation failure will result in jwt::MemoryAllocationException
348 * being thrown.
349 */
signjwt::HMACSign350 static sign_result_t sign(const jwt::string_view key, const jwt::string_view data)
351 {
352 std::string sign;
353 sign.resize(EVP_MAX_MD_SIZE);
354 std::error_code ec{};
355
356 uint32_t len = 0;
357
358 unsigned char* res = HMAC(Hasher{}(),
359 key.data(),
360 key.length(),
361 reinterpret_cast<const unsigned char*>(data.data()),
362 data.length(),
363 reinterpret_cast<unsigned char*>(&sign[0]),
364 &len);
365 if (!res) {
366 ec = AlgorithmErrc::SigningErr;
367 }
368
369 sign.resize(len);
370 return { std::move(sign), ec };
371 }
372
373 /**
374 * Verifies the JWT string against the signature using
375 * the provided key.
376 *
377 * Arguments:
378 * @key : The secret/key to use for the signing.
379 * Cannot be empty string.
380 * @head : The part of JWT encoded string representing header
381 * and the payload claims.
382 * @sign : The signature part of the JWT encoded string.
383 *
384 * Returns:
385 * verify_result_t
386 * verify_result_t::first set to true if verification succeeds.
387 * false otherwise.
388 * verify_result_t::second set to relevant error if verification fails.
389 *
390 * Exceptions:
391 * Any allocation failure will result in jwt::MemoryAllocationException
392 * being thrown.
393 */
394 static verify_result_t
395 verify(const jwt::string_view key, const jwt::string_view head, const jwt::string_view sign);
396
397 };
398
399 /**
400 * Specialization of `HMACSign` class
401 * for NONE algorithm.
402 *
403 * This specialization is selected for even
404 * PEM based algorithms.
405 *
406 * The signing and verification APIs are
407 * basically no-op except that they would
408 * set the relevant error code.
409 *
410 * NOTE: error_code would be set in the case
411 * of usage of NONE algorithm.
412 * Users of this API are expected to check for
413 * the case explicitly.
414 */
415 template <>
416 struct HMACSign<algo::NONE>
417 {
418 using hasher_type = algo::NONE;
419
420 /**
421 * Basically a no-op. Sets the error code to NoneAlgorithmUsed.
422 */
signjwt::HMACSign423 static sign_result_t sign(const jwt::string_view key, const jwt::string_view data)
424 {
425 (void)key;
426 (void)data;
427 std::error_code ec{};
428 ec = AlgorithmErrc::NoneAlgorithmUsed;
429
430 return { std::string{}, ec };
431 }
432
433 /**
434 * Basically a no-op. Sets the error code to NoneAlgorithmUsed.
435 */
436 static verify_result_t
verifyjwt::HMACSign437 verify(const jwt::string_view key, const jwt::string_view head, const jwt::string_view sign)
438 {
439 (void)key;
440 (void)head;
441 (void)sign;
442 std::error_code ec{};
443 ec = AlgorithmErrc::NoneAlgorithmUsed;
444
445 return { true, ec };
446 }
447
448 };
449
450
451
452 /**
453 * OpenSSL PEM based signature and verfication.
454 *
455 * The template type `Hasher` takes the type representing
456 * the PEM algorithm type from the `jwt::algo` namespace.
457 *
458 * For NONE algorithm, HMACSign<> specialization is used.
459 * See that for more details.
460 */
461 template <typename Hasher>
462 struct PEMSign
463 {
464 public:
465 /// The type of Hashing algorithm
466 using hasher_type = Hasher;
467
468 /**
469 * Signs the input data using PEM encryption algorithm.
470 *
471 * Arguments:
472 * @key : The key/secret to be used for signing.
473 * Cannot be an empty string.
474 * @data: The data to be signed.
475 *
476 * Exceptions:
477 * Any allocation failure would be thrown out as
478 * jwt::MemoryAllocationException.
479 */
signjwt::PEMSign480 static sign_result_t sign(const jwt::string_view key, const jwt::string_view data)
481 {
482 std::error_code ec{};
483
484 std::string ii{data.data(), data.length()};
485
486 EC_PKEY_uptr pkey{load_key(key, ec), ev_pkey_deletor};
487 if (ec) return { std::string{}, ec };
488
489 //TODO: Use stack string here ?
490 std::string sign = evp_digest(pkey.get(), data, ec);
491
492 if (ec) return { std::string{}, ec };
493
494 if (Hasher::type == EVP_PKEY_EC) {
495 sign = public_key_ser(pkey.get(), sign, ec);
496 }
497
498 return { std::move(sign), ec };
499 }
500
501 /**
502 */
503 static verify_result_t
504 verify(const jwt::string_view key, const jwt::string_view head, const jwt::string_view sign);
505
506 private:
507
508 /*!
509 */
510 static EVP_PKEY* load_key(const jwt::string_view key, std::error_code& ec);
511
512 /*!
513 */
514 static std::string evp_digest(EVP_PKEY* pkey, const jwt::string_view data, std::error_code& ec);
515
516 /*!
517 */
518 static std::string public_key_ser(EVP_PKEY* pkey, jwt::string_view sign, std::error_code& ec);
519
520 #if OPENSSL_VERSION_NUMBER < 0x10100000L
521
522 //ATTN: Below 2 functions
523 //are Taken from https://github.com/nginnever/zogminer/issues/39
524
525 /**
526 */
ECDSA_SIG_get0jwt::PEMSign527 static void ECDSA_SIG_get0(const ECDSA_SIG* sig, const BIGNUM** pr, const BIGNUM** ps)
528 {
529 if (pr != nullptr) *pr = sig->r;
530 if (ps != nullptr) *ps = sig->s;
531 };
532
533 /**
534 */
ECDSA_SIG_set0jwt::PEMSign535 static int ECDSA_SIG_set0(ECDSA_SIG* sig, BIGNUM* r, BIGNUM* s)
536 {
537 if (r == nullptr || s == nullptr) return 0;
538
539 BN_clear_free(sig->r);
540 BN_clear_free(sig->s);
541
542 sig->r = r;
543 sig->s = s;
544 return 1;
545 }
546
547 #endif
548 };
549
550 } // END namespace jwt
551
552 #include "jwt/impl/algorithm.ipp"
553
554
555 #endif
556