mirror of
https://github.com/arun11299/cpp-jwt.git
synced 2025-05-14 16:58:34 +00:00
Implement verify. Part 1
This commit is contained in:
parent
8277e2cbd3
commit
796abd6e65
10 changed files with 473 additions and 160 deletions
BIN
include/jwt/.algorithm.hpp.swp
Normal file
BIN
include/jwt/.algorithm.hpp.swp
Normal file
Binary file not shown.
|
@ -19,9 +19,14 @@ namespace jwt {
|
||||||
/// The result type of the signing function
|
/// The result type of the signing function
|
||||||
using sign_result_t = std::pair<std::string, std::error_code>;
|
using sign_result_t = std::pair<std::string, std::error_code>;
|
||||||
/// The result type of verification function
|
/// The result type of verification function
|
||||||
using verify_result_t = std::pair<int, std::error_code>;
|
using verify_result_t = std::pair<bool, std::error_code>;
|
||||||
/// The function pointer type of the signing function
|
/// The function pointer type of the signing function
|
||||||
using sign_func_t = sign_result_t (*) (string_view key, string_view data);
|
using sign_func_t = sign_result_t (*) (const string_view key,
|
||||||
|
const string_view data);
|
||||||
|
///
|
||||||
|
using verify_func_t = verify_result_t (*) (const string_view key,
|
||||||
|
const string_view header,
|
||||||
|
const string_view jwt_sign);
|
||||||
|
|
||||||
namespace algo {
|
namespace algo {
|
||||||
|
|
||||||
|
@ -156,7 +161,7 @@ struct HMACSign
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
*/
|
*/
|
||||||
static sign_result_t sign(string_view key, string_view data)
|
static sign_result_t sign(const string_view key, const string_view data)
|
||||||
{
|
{
|
||||||
std::string sign;
|
std::string sign;
|
||||||
sign.resize(EVP_MAX_MD_SIZE);
|
sign.resize(EVP_MAX_MD_SIZE);
|
||||||
|
@ -183,13 +188,7 @@ struct HMACSign
|
||||||
/*!
|
/*!
|
||||||
*/
|
*/
|
||||||
static verify_result_t
|
static verify_result_t
|
||||||
verify(string_view key, string_view head, string_view sign)
|
verify(const string_view key, const string_view head, const string_view sign);
|
||||||
{
|
|
||||||
int compare_res = 0;
|
|
||||||
std::error_code ec{};
|
|
||||||
|
|
||||||
return {compare_res, ec};
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -202,7 +201,7 @@ struct HMACSign<algo::NONE>
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
*/
|
*/
|
||||||
static sign_result_t sign(string_view key, string_view data)
|
static sign_result_t sign(const string_view key, const string_view data)
|
||||||
{
|
{
|
||||||
std::string sign;
|
std::string sign;
|
||||||
std::error_code ec{};
|
std::error_code ec{};
|
||||||
|
@ -214,7 +213,7 @@ struct HMACSign<algo::NONE>
|
||||||
/*!
|
/*!
|
||||||
*/
|
*/
|
||||||
static verify_result_t
|
static verify_result_t
|
||||||
verify(string_view key, string_view head, string_view sign)
|
verify(const string_view key, const string_view head, const string_view sign)
|
||||||
{
|
{
|
||||||
int compare_res = 0;
|
int compare_res = 0;
|
||||||
std::error_code ec{};
|
std::error_code ec{};
|
||||||
|
@ -237,7 +236,7 @@ public:
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
*/
|
*/
|
||||||
static sign_result_t sign(string_view key, string_view data)
|
static sign_result_t sign(const string_view key, const string_view data)
|
||||||
{
|
{
|
||||||
std::error_code ec{};
|
std::error_code ec{};
|
||||||
|
|
||||||
|
@ -272,145 +271,15 @@ public:
|
||||||
private:
|
private:
|
||||||
/*!
|
/*!
|
||||||
*/
|
*/
|
||||||
static EVP_PKEY* load_key(const string_view key)
|
static EVP_PKEY* load_key(const string_view key);
|
||||||
{
|
|
||||||
auto bio_deletor = [](BIO* ptr) {
|
|
||||||
if (ptr) BIO_free(ptr);
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<BIO, decltype(bio_deletor)>
|
|
||||||
bio_ptr{BIO_new_mem_buf((void*)key.data(), key.length()), bio_deletor};
|
|
||||||
|
|
||||||
if (!bio_ptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio_ptr.get(), nullptr, nullptr, nullptr);
|
|
||||||
if (!pkey) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pkey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
*/
|
*/
|
||||||
static std::string evp_digest(EVP_PKEY* pkey, const string_view data, std::error_code& ec)
|
static std::string evp_digest(EVP_PKEY* pkey, const string_view data, std::error_code& ec);
|
||||||
{
|
|
||||||
auto md_deletor = [](EVP_MD_CTX* ptr) {
|
|
||||||
if (ptr) EVP_MD_CTX_destroy(ptr);
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<EVP_MD_CTX, decltype(md_deletor)>
|
|
||||||
mdctx_ptr{EVP_MD_CTX_create(), md_deletor};
|
|
||||||
|
|
||||||
if (!mdctx_ptr) {
|
|
||||||
//TODO: set appropriate error_code
|
|
||||||
return std::string{};
|
|
||||||
}
|
|
||||||
|
|
||||||
//Initialiaze the digest algorithm
|
|
||||||
if (EVP_DigestSignInit(
|
|
||||||
mdctx_ptr.get(), nullptr, Hasher{}(), nullptr, pkey) != 1) {
|
|
||||||
//TODO: set appropriate error_code
|
|
||||||
return std::string{};
|
|
||||||
}
|
|
||||||
|
|
||||||
//Update the digest with the input data
|
|
||||||
if (EVP_DigestSignUpdate(mdctx_ptr.get(), data.data(), data.length()) != 1) {
|
|
||||||
//TODO: set appropriate error_code
|
|
||||||
return std::string{};
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long len = 0;
|
|
||||||
|
|
||||||
if (EVP_DigestSignFinal(mdctx_ptr.get(), nullptr, &len) != 1) {
|
|
||||||
//TODO: set appropriate error_code
|
|
||||||
return std::string{};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string sign;
|
|
||||||
sign.resize(len);
|
|
||||||
|
|
||||||
//Get the signature
|
|
||||||
if (EVP_DigestSignFinal(mdctx_ptr.get(), (unsigned char*)&sign[0], &len) != 1) {
|
|
||||||
//TODO: set appropriate error_code
|
|
||||||
return std::string{};
|
|
||||||
}
|
|
||||||
|
|
||||||
return sign;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static std::string public_key_ser(EVP_PKEY* pkey, string_view sign, std::error_code& ec)
|
|
||||||
{
|
|
||||||
// Get the EC_KEY representing a public key and
|
|
||||||
// (optionaly) an associated private key
|
|
||||||
std::string new_sign;
|
|
||||||
|
|
||||||
static auto eckey_deletor = [](EC_KEY* ptr) {
|
|
||||||
if (ptr) EC_KEY_free(ptr);
|
|
||||||
};
|
|
||||||
|
|
||||||
static auto ecsig_deletor = [](ECDSA_SIG* ptr) {
|
|
||||||
if (ptr) ECDSA_SIG_free(ptr);
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<EC_KEY, decltype(eckey_deletor)>
|
|
||||||
ec_key{EVP_PKEY_get1_EC_KEY(pkey), eckey_deletor};
|
|
||||||
|
|
||||||
if (!ec_key) {
|
|
||||||
//TODO set a valid error code
|
|
||||||
return std::string{};
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t degree = EC_GROUP_get_degree(EC_KEY_get0_group(ec_key.get()));
|
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<ECDSA_SIG, decltype(ecsig_deletor)>
|
|
||||||
ec_sig{d2i_ECDSA_SIG(nullptr,
|
|
||||||
(const unsigned char**)&sign[0],
|
|
||||||
sign.length()),
|
|
||||||
ecsig_deletor};
|
|
||||||
|
|
||||||
if (!ec_sig) {
|
|
||||||
//TODO set a valid error code
|
|
||||||
return std::string{};
|
|
||||||
}
|
|
||||||
|
|
||||||
const BIGNUM* ec_sig_r = nullptr;
|
|
||||||
const BIGNUM* ec_sig_s = nullptr;
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
//Taken from https://github.com/nginnever/zogminer/issues/39
|
|
||||||
auto ECDSA_SIG_get0 = [](const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
|
|
||||||
{
|
|
||||||
if (pr != nullptr) *pr = sig->r;
|
|
||||||
if (ps != nullptr) *ps = sig->s;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ECDSA_SIG_get0(ec_sig.get(), &ec_sig_r, &ec_sig_s);
|
|
||||||
|
|
||||||
auto r_len = BN_num_bytes(ec_sig_r);
|
|
||||||
auto s_len = BN_num_bytes(ec_sig_s);
|
|
||||||
auto bn_len = (degree + 7) / 8;
|
|
||||||
|
|
||||||
if ((r_len > bn_len) || (s_len > bn_len)) {
|
|
||||||
//TODO set a valid error code
|
|
||||||
return std::string{};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto buf_len = 2 * bn_len;
|
|
||||||
new_sign.resize(buf_len);
|
|
||||||
|
|
||||||
BN_bn2bin(ec_sig_r, (unsigned char*)&new_sign[0] + bn_len - r_len);
|
|
||||||
BN_bn2bin(ec_sig_s, (unsigned char*)&new_sign[0] + buf_len - s_len);
|
|
||||||
|
|
||||||
return new_sign;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
*/
|
||||||
|
static std::string public_key_ser(EVP_PKEY* pkey, string_view sign, std::error_code& ec);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // END namespace jwt
|
} // END namespace jwt
|
||||||
|
|
|
@ -212,6 +212,36 @@ void base64_uri_encode(char* data, size_t len) noexcept
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
*/
|
||||||
|
std::string base64_uri_decode(const char* data, size_t len)
|
||||||
|
{
|
||||||
|
std::string uri_dec;
|
||||||
|
uri_dec.resize(len + 4);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; ++i) {
|
||||||
|
switch (data[i]) {
|
||||||
|
case '-':
|
||||||
|
uri_dec[i] = '+';
|
||||||
|
break;
|
||||||
|
case '_':
|
||||||
|
uri_dec[i] = '/';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
uri_dec[i] = data[i];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t trailer = 4 - (i % 4);
|
||||||
|
if (trailer && trailer < 4) {
|
||||||
|
while (trailer--) {
|
||||||
|
uri_dec[i++] = '=';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64_decode(uri_dec.c_str(), uri_dec.length());
|
||||||
|
}
|
||||||
|
|
||||||
} // END namespace jwt
|
} // END namespace jwt
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,42 @@
|
||||||
#ifndef JWT_META_HPP
|
#ifndef CPP_JWT_META_HPP
|
||||||
#define JWT_META_HPP
|
#define CPP_JWT_META_HPP
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
namespace jwt {
|
namespace jwt {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
namespace meta {
|
||||||
|
|
||||||
}
|
template <typename... T>
|
||||||
|
struct make_void
|
||||||
|
{
|
||||||
|
using type = void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
using void_t = typename make_void<T...>::type;
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename=void>
|
||||||
|
struct has_create_json_obj_member: std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct has_create_json_obj_member<T,
|
||||||
|
void_t<
|
||||||
|
decltype(
|
||||||
|
std::declval<T&&>.create_json_obj(),
|
||||||
|
(void)0
|
||||||
|
)
|
||||||
|
>
|
||||||
|
>: std::true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // END namespace meta
|
||||||
|
} // END namespace detail
|
||||||
} // END namespace jwt
|
} // END namespace jwt
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
194
include/jwt/impl/algorithm.ipp
Normal file
194
include/jwt/impl/algorithm.ipp
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
#ifndef CPP_JWT_ALGORITHM_IPP
|
||||||
|
#define CPP_JWT_ALGORITHM_IPP
|
||||||
|
|
||||||
|
namespace jwt {
|
||||||
|
|
||||||
|
template <typename Hasher>
|
||||||
|
verify_result_t HMACSign<Hasher>::verify(
|
||||||
|
const string_view key,
|
||||||
|
const string_view head,
|
||||||
|
const string_view jwt_sign)
|
||||||
|
{
|
||||||
|
std::error_code ec{};
|
||||||
|
static auto bio_deletor = [](BIO* ptr) {
|
||||||
|
if (ptr) BIO_free_all(ptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
using bio_deletor_t = decltype(bio_deletor);
|
||||||
|
using BIO_unique_ptr = std::unique_ptr<BIO_unique_ptr, bio_deletor_t>;
|
||||||
|
|
||||||
|
BIO_unique_ptr b64{BIO_new(BIO_f_base64())};
|
||||||
|
if (!b64) {
|
||||||
|
//TODO: set error code
|
||||||
|
return {false, ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
BIO* bmem = BIO_new(BIO_s_mem());
|
||||||
|
if (!bmem) {
|
||||||
|
//TODO: set error code
|
||||||
|
return {false, ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
BIO_push(b64, bmem);
|
||||||
|
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
|
||||||
|
|
||||||
|
unsigned char enc_buf[EVP_MAX_MD_SIZE];
|
||||||
|
|
||||||
|
unsigned char* res = HMAC(Hasher{}(),
|
||||||
|
key.data(),
|
||||||
|
key.length(),
|
||||||
|
reinterpret_cast<const unsigned char*>(head.data()),
|
||||||
|
head.length(),
|
||||||
|
enc_buf,
|
||||||
|
&enc_buf_len);
|
||||||
|
|
||||||
|
return {true, ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Hasher>
|
||||||
|
sign_result_t PEMSign<Hasher>::load_key(const string_view key)
|
||||||
|
{
|
||||||
|
auto bio_deletor = [](BIO* ptr) {
|
||||||
|
if (ptr) BIO_free(ptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<BIO, decltype(bio_deletor)>
|
||||||
|
bio_ptr{BIO_new_mem_buf((void*)key.data(), key.length()), bio_deletor};
|
||||||
|
|
||||||
|
if (!bio_ptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio_ptr.get(), nullptr, nullptr, nullptr);
|
||||||
|
if (!pkey) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Hasher>
|
||||||
|
std::string PEMSign<Hasher>::evp_digest(
|
||||||
|
EVP_PKEY* pkey,
|
||||||
|
const string_view data,
|
||||||
|
std::error_code& ec)
|
||||||
|
{
|
||||||
|
auto md_deletor = [](EVP_MD_CTX* ptr) {
|
||||||
|
if (ptr) EVP_MD_CTX_destroy(ptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<EVP_MD_CTX, decltype(md_deletor)>
|
||||||
|
mdctx_ptr{EVP_MD_CTX_create(), md_deletor};
|
||||||
|
|
||||||
|
if (!mdctx_ptr) {
|
||||||
|
//TODO: set appropriate error_code
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
//Initialiaze the digest algorithm
|
||||||
|
if (EVP_DigestSignInit(
|
||||||
|
mdctx_ptr.get(), nullptr, Hasher{}(), nullptr, pkey) != 1) {
|
||||||
|
//TODO: set appropriate error_code
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update the digest with the input data
|
||||||
|
if (EVP_DigestSignUpdate(mdctx_ptr.get(), data.data(), data.length()) != 1) {
|
||||||
|
//TODO: set appropriate error_code
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long len = 0;
|
||||||
|
|
||||||
|
if (EVP_DigestSignFinal(mdctx_ptr.get(), nullptr, &len) != 1) {
|
||||||
|
//TODO: set appropriate error_code
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sign;
|
||||||
|
sign.resize(len);
|
||||||
|
|
||||||
|
//Get the signature
|
||||||
|
if (EVP_DigestSignFinal(mdctx_ptr.get(), (unsigned char*)&sign[0], &len) != 1) {
|
||||||
|
//TODO: set appropriate error_code
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
return sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Hasher>
|
||||||
|
std::string PEMSign<Hasher>::public_key_ser(
|
||||||
|
EVP_PKEY* pkey,
|
||||||
|
string_view sign,
|
||||||
|
std::error_code& ec)
|
||||||
|
{
|
||||||
|
// Get the EC_KEY representing a public key and
|
||||||
|
// (optionaly) an associated private key
|
||||||
|
std::string new_sign;
|
||||||
|
|
||||||
|
static auto eckey_deletor = [](EC_KEY* ptr) {
|
||||||
|
if (ptr) EC_KEY_free(ptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto ecsig_deletor = [](ECDSA_SIG* ptr) {
|
||||||
|
if (ptr) ECDSA_SIG_free(ptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<EC_KEY, decltype(eckey_deletor)>
|
||||||
|
ec_key{EVP_PKEY_get1_EC_KEY(pkey), eckey_deletor};
|
||||||
|
|
||||||
|
if (!ec_key) {
|
||||||
|
//TODO set a valid error code
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t degree = EC_GROUP_get_degree(EC_KEY_get0_group(ec_key.get()));
|
||||||
|
|
||||||
|
std::unique_ptr<ECDSA_SIG, decltype(ecsig_deletor)>
|
||||||
|
ec_sig{d2i_ECDSA_SIG(nullptr,
|
||||||
|
(const unsigned char**)&sign[0],
|
||||||
|
sign.length()),
|
||||||
|
ecsig_deletor};
|
||||||
|
|
||||||
|
if (!ec_sig) {
|
||||||
|
//TODO set a valid error code
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
const BIGNUM* ec_sig_r = nullptr;
|
||||||
|
const BIGNUM* ec_sig_s = nullptr;
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
//Taken from https://github.com/nginnever/zogminer/issues/39
|
||||||
|
auto ECDSA_SIG_get0 = [](const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
|
||||||
|
{
|
||||||
|
if (pr != nullptr) *pr = sig->r;
|
||||||
|
if (ps != nullptr) *ps = sig->s;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ECDSA_SIG_get0(ec_sig.get(), &ec_sig_r, &ec_sig_s);
|
||||||
|
|
||||||
|
auto r_len = BN_num_bytes(ec_sig_r);
|
||||||
|
auto s_len = BN_num_bytes(ec_sig_s);
|
||||||
|
auto bn_len = (degree + 7) / 8;
|
||||||
|
|
||||||
|
if ((r_len > bn_len) || (s_len > bn_len)) {
|
||||||
|
//TODO set a valid error code
|
||||||
|
return std::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buf_len = 2 * bn_len;
|
||||||
|
new_sign.resize(buf_len);
|
||||||
|
|
||||||
|
BN_bn2bin(ec_sig_r, (unsigned char*)&new_sign[0] + bn_len - r_len);
|
||||||
|
BN_bn2bin(ec_sig_s, (unsigned char*)&new_sign[0] + buf_len - s_len);
|
||||||
|
|
||||||
|
return new_sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // END namespace jwt
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef JWT_IPP
|
#ifndef JWT_IPP
|
||||||
#define JWT_IPP
|
#define JWT_IPP
|
||||||
|
|
||||||
|
#include "jwt/detail/meta.hpp"
|
||||||
|
|
||||||
namespace jwt {
|
namespace jwt {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -23,13 +25,54 @@ std::ostream& write(std::ostream& os, const T& obj, bool pretty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T,
|
||||||
|
typename = typename std::enable_if<
|
||||||
|
detail::meta::has_create_json_obj_member<T>{}>::type>
|
||||||
std::ostream& operator<< (std::ostream& os, const T& obj)
|
std::ostream& operator<< (std::ostream& os, const T& obj)
|
||||||
{
|
{
|
||||||
os << obj.create_json_obj();
|
os << obj.create_json_obj();
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//========================================================================
|
||||||
|
|
||||||
|
void jwt_header::decode(const string_view enc_str)
|
||||||
|
{
|
||||||
|
std::string json_str = base64_decode(enc_str);
|
||||||
|
json_t obj = json_t::parse(std::move(json_str));
|
||||||
|
|
||||||
|
//Look for the algorithm field
|
||||||
|
auto alg_itr = obj.find("alg");
|
||||||
|
assert (alg_itr != obj.end() && "Algorithm header is missing");
|
||||||
|
std::error_code ec;
|
||||||
|
|
||||||
|
alg_ = str_to_alg(alg_itr.value().get<std::string>());
|
||||||
|
|
||||||
|
if (alg_ != algorithm::NONE) {
|
||||||
|
auto itr = obj.find("typ");
|
||||||
|
if (itr == obj.end()) {
|
||||||
|
//TODO: set error code
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto typ = itr.value().get<std::string>();
|
||||||
|
if (strcasecmp(typ.c_str(), "JWT")) {
|
||||||
|
//TODO: set error code
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
typ_ = str_to_type(typ);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jwt_payload::decode(const string_view enc_str)
|
||||||
|
{
|
||||||
|
std::string json_str = base64_decode(enc_str);
|
||||||
|
payload_ = json_t::parse(std::move(json_str));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string jwt_signature::encode(const jwt_header& header,
|
std::string jwt_signature::encode(const jwt_header& header,
|
||||||
const jwt_payload& payload)
|
const jwt_payload& payload)
|
||||||
{
|
{
|
||||||
|
@ -41,9 +84,6 @@ std::string jwt_signature::encode(const jwt_header& header,
|
||||||
std::string hdr_sign = header.base64_encode();
|
std::string hdr_sign = header.base64_encode();
|
||||||
std::string pld_sign = payload.base64_encode();
|
std::string pld_sign = payload.base64_encode();
|
||||||
|
|
||||||
base64_uri_encode(&hdr_sign[0], hdr_sign.length());
|
|
||||||
base64_uri_encode(&pld_sign[0], pld_sign.length());
|
|
||||||
|
|
||||||
std::string data = hdr_sign + '.' + pld_sign;
|
std::string data = hdr_sign + '.' + pld_sign;
|
||||||
auto res = sign_fn(key_, data);
|
auto res = sign_fn(key_, data);
|
||||||
|
|
||||||
|
@ -55,6 +95,15 @@ std::string jwt_signature::encode(const jwt_header& header,
|
||||||
return jwt_msg;
|
return jwt_msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool jwt_signature::verify(const jwt_header& header,
|
||||||
|
const string_view hdr_pld_sign,
|
||||||
|
const srting_view jwt_sign)
|
||||||
|
{
|
||||||
|
//TODO: is bool the right choice ?
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sign_func_t
|
sign_func_t
|
||||||
jwt_signature::get_algorithm_impl(const jwt_header& hdr) const noexcept
|
jwt_signature::get_algorithm_impl(const jwt_header& hdr) const noexcept
|
||||||
|
@ -99,6 +148,33 @@ jwt_signature::get_algorithm_impl(const jwt_header& hdr) const noexcept
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//====================================================================
|
||||||
|
|
||||||
|
void jwt_decode(string_view encoded_str, string_view key, bool validate)
|
||||||
|
{
|
||||||
|
//TODO: implement error_code
|
||||||
|
size_t fpos = encoded_str.find_first_of('.');
|
||||||
|
assert (fpos != string_view::npos);
|
||||||
|
|
||||||
|
string_view head{&encoded_str[0], fpos};
|
||||||
|
std::cout << "Head: " << head << std::endl;
|
||||||
|
jwt_header hdr;
|
||||||
|
hdr.decode(head);
|
||||||
|
|
||||||
|
size_t spos = encoded_str.find_first_of('.', fpos + 1);
|
||||||
|
if (spos == string_view::npos) {
|
||||||
|
//TODO: Check for none algorithm
|
||||||
|
}
|
||||||
|
string_view body{&encoded_str[fpos + 1], spos - fpos - 1};
|
||||||
|
std::cout << "Body: " << body << std::endl;
|
||||||
|
|
||||||
|
//Json objects or claims get set in the decode
|
||||||
|
jwt_payload pld;
|
||||||
|
pld.decode(body);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
} // END namespace jwt
|
} // END namespace jwt
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
#include "jwt/base64.hpp"
|
#include "jwt/base64.hpp"
|
||||||
#include "jwt/algorithm.hpp"
|
#include "jwt/algorithm.hpp"
|
||||||
#include "jwt/string_view.hpp"
|
#include "jwt/string_view.hpp"
|
||||||
#include "jwt/detail/meta.hpp"
|
|
||||||
#include "jwt/json/json.hpp"
|
#include "jwt/json/json.hpp"
|
||||||
|
|
||||||
// For convenience
|
// For convenience
|
||||||
|
@ -59,6 +58,25 @@ string_view alg_to_str(enum algorithm alg) noexcept
|
||||||
assert (0 && "Code not reached");
|
assert (0 && "Code not reached");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
*/
|
||||||
|
enum algorithm str_to_alg(const string_view alg) noexcept
|
||||||
|
{
|
||||||
|
if (!alg.length()) return algorithm::NONE;
|
||||||
|
|
||||||
|
if (!strcasecmp(alg.data(), "none")) return algorithm::NONE;
|
||||||
|
if (!strcasecmp(alg.data(), "hs256")) return algorithm::HS256;
|
||||||
|
if (!strcasecmp(alg.data(), "hs384")) return algorithm::HS384;
|
||||||
|
if (!strcasecmp(alg.data(), "hs512")) return algorithm::HS512;
|
||||||
|
if (!strcasecmp(alg.data(), "rs256")) return algorithm::RS256;
|
||||||
|
if (!strcasecmp(alg.data(), "rs384")) return algorithm::RS384;
|
||||||
|
if (!strcasecmp(alg.data(), "rs512")) return algorithm::RS512;
|
||||||
|
if (!strcasecmp(alg.data(), "es256")) return algorithm::ES256;
|
||||||
|
if (!strcasecmp(alg.data(), "es384")) return algorithm::ES384;
|
||||||
|
if (!strcasecmp(alg.data(), "es512")) return algorithm::ES512;
|
||||||
|
|
||||||
|
assert (0 && "Code not reached");
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
*/
|
*/
|
||||||
|
@ -67,6 +85,17 @@ enum class type
|
||||||
JWT = 0,
|
JWT = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
*/
|
||||||
|
enum type str_to_type(const string_view typ) noexcept
|
||||||
|
{
|
||||||
|
assert (typ.length() && "Empty type string");
|
||||||
|
|
||||||
|
if (!strcasecmp(typ.data(), "jwt")) return type::JWT;
|
||||||
|
|
||||||
|
assert (0 && "Code not reached");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
*/
|
*/
|
||||||
|
@ -160,18 +189,28 @@ template <typename Derived>
|
||||||
struct base64_enc_dec
|
struct base64_enc_dec
|
||||||
{
|
{
|
||||||
/*!
|
/*!
|
||||||
|
* Does URL safe base64 encoding
|
||||||
*/
|
*/
|
||||||
std::string base64_encode(bool with_pretty = false) const
|
std::string base64_encode(bool with_pretty = false) const
|
||||||
{
|
{
|
||||||
std::string jstr = to_json_str(*static_cast<const Derived*>(this), with_pretty);
|
std::string jstr = to_json_str(*static_cast<const Derived*>(this), with_pretty);
|
||||||
return jwt::base64_encode(jstr.c_str(), jstr.length());
|
std::string b64_str = jwt::base64_encode(jstr.c_str(), jstr.length());
|
||||||
|
size_t rpos = b64_str.length();
|
||||||
|
while(b64_str[rpos-1] == '=') rpos--;
|
||||||
|
// Remove the '=' characters
|
||||||
|
b64_str.resize(rpos);
|
||||||
|
// Do the URI safe encoding
|
||||||
|
jwt::base64_uri_encode(&b64_str[0], b64_str.length());
|
||||||
|
|
||||||
|
return b64_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
* Does URL safe base64 decoding.
|
||||||
*/
|
*/
|
||||||
static std::string base64_decode(const std::string& encoded_str)
|
std::string base64_decode(const string_view encoded_str)
|
||||||
{
|
{
|
||||||
return jwt::base64_decode(encoded_str.c_str(), encoded_str.length());
|
return jwt::base64_uri_decode(encoded_str.data(), encoded_str.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -235,6 +274,18 @@ public: // Exposed APIs
|
||||||
return typ_;
|
return typ_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
*/
|
||||||
|
//TODO: error code ?
|
||||||
|
std::string encode(bool pprint = false)
|
||||||
|
{
|
||||||
|
return base64_encode(pprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
*/
|
||||||
|
void decode(const string_view enc_str);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
*/
|
*/
|
||||||
json_t create_json_obj() const
|
json_t create_json_obj() const
|
||||||
|
@ -315,6 +366,18 @@ public: // Exposed APIs
|
||||||
return (cvalue == payload_[cname]);
|
return (cvalue == payload_[cname]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
*/
|
||||||
|
std::string encode(bool pprint = false)
|
||||||
|
{
|
||||||
|
return base64_encode(pprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
*/
|
||||||
|
//TODO: what about error_code ?
|
||||||
|
void decode(const string_view enc_str);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
*/
|
*/
|
||||||
const json_t& create_json_obj() const
|
const json_t& create_json_obj() const
|
||||||
|
@ -366,6 +429,12 @@ public: // Exposed APIs
|
||||||
std::string encode(const jwt_header& header,
|
std::string encode(const jwt_header& header,
|
||||||
const jwt_payload& payload);
|
const jwt_payload& payload);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
*/
|
||||||
|
bool verify(const jwt_header& header,
|
||||||
|
const string_view hdr_pld_sign,
|
||||||
|
const string_view jwt_sign);
|
||||||
|
|
||||||
private: // Private implementation
|
private: // Private implementation
|
||||||
/*!
|
/*!
|
||||||
*/
|
*/
|
||||||
|
@ -381,8 +450,24 @@ private: // Data members;
|
||||||
*/
|
*/
|
||||||
class jwt_object
|
class jwt_object
|
||||||
{
|
{
|
||||||
|
public: // 'tors
|
||||||
|
jwt_object() = default;
|
||||||
|
|
||||||
|
public: // Exposed APIs
|
||||||
|
|
||||||
|
private: // Data Members
|
||||||
|
|
||||||
|
/// JWT header section
|
||||||
|
jwt_header header_;
|
||||||
|
/// JWT payload section
|
||||||
|
jwt_payload payload_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
*/
|
||||||
|
void jwt_decode(string_view encoded_str, string_view key, bool validate=true);
|
||||||
|
|
||||||
|
|
||||||
} // END namespace jwt
|
} // END namespace jwt
|
||||||
|
|
||||||
|
|
BIN
include/jwt/test/test_jwt_decode
Executable file
BIN
include/jwt/test/test_jwt_decode
Executable file
Binary file not shown.
27
include/jwt/test/test_jwt_decode.cc
Normal file
27
include/jwt/test/test_jwt_decode.cc
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include "jwt/jwt.hpp"
|
||||||
|
|
||||||
|
void basic_decode_test()
|
||||||
|
{
|
||||||
|
// Create header
|
||||||
|
jwt::jwt_header hdr;
|
||||||
|
hdr = jwt::jwt_header{jwt::algorithm::HS256};
|
||||||
|
|
||||||
|
// Create payload
|
||||||
|
jwt::jwt_payload jp;
|
||||||
|
jp.add_claim("sub", "1234567890");
|
||||||
|
jp.add_claim("name", "John Doe");
|
||||||
|
jp.add_claim("admin", true);
|
||||||
|
|
||||||
|
jwt::jwt_signature sgn{"secret"};
|
||||||
|
auto res = sgn.encode(hdr, jp);
|
||||||
|
std::cout << res << std::endl;
|
||||||
|
|
||||||
|
std::cout << "DECODE: \n";
|
||||||
|
jwt::jwt_decode(res, "secret");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
basic_decode_test();
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ void test_basic_header()
|
||||||
|
|
||||||
std::string enc_str = hdr.base64_encode();
|
std::string enc_str = hdr.base64_encode();
|
||||||
std::cout << "Base64: " << enc_str << std::endl;
|
std::cout << "Base64: " << enc_str << std::endl;
|
||||||
std::cout << "Decoded: " << jwt::jwt_header::base64_decode(enc_str) << std::endl;
|
std::cout << "Decoded: " << hdr.base64_decode(enc_str) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue