early-access version 2862

This commit is contained in:
pineappleEA
2022-07-26 03:25:01 +02:00
parent 1f02fcfc6e
commit ba84d02a09
211 changed files with 53330 additions and 31 deletions

View File

@@ -0,0 +1,312 @@
/*
Copyright (c) 2017 Arun Muralidharan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef CPP_JWT_ALGORITHM_IPP
#define CPP_JWT_ALGORITHM_IPP
namespace jwt {
template <typename Hasher>
verify_result_t HMACSign<Hasher>::verify(
const jwt::string_view key,
const jwt::string_view head,
const jwt::string_view jwt_sign)
{
std::error_code ec{};
unsigned char enc_buf[EVP_MAX_MD_SIZE];
uint32_t enc_buf_len = 0;
unsigned char* res = HMAC(Hasher{}(),
key.data(),
static_cast<int>(key.length()),
reinterpret_cast<const unsigned char*>(head.data()),
head.length(),
enc_buf,
&enc_buf_len);
if (!res) {
ec = AlgorithmErrc::VerificationErr;
return {false, ec};
}
if (enc_buf_len == 0) {
ec = AlgorithmErrc::VerificationErr;
return {false, ec};
}
std::string b64_enc_str = jwt::base64_encode((const char*)&enc_buf[0], enc_buf_len);
if (!b64_enc_str.length()) {
ec = AlgorithmErrc::VerificationErr;
return {false, ec};
}
// Make the base64 string url safe
auto new_len = jwt::base64_uri_encode(&b64_enc_str[0], b64_enc_str.length());
b64_enc_str.resize(new_len);
bool ret = (jwt::string_view{b64_enc_str} == jwt_sign);
return { ret, ec };
}
template <typename Hasher>
verify_result_t PEMSign<Hasher>::verify(
const jwt::string_view key,
const jwt::string_view head,
const jwt::string_view jwt_sign)
{
std::error_code ec{};
std::string dec_sig = base64_uri_decode(jwt_sign.data(), jwt_sign.length());
BIO_uptr bufkey{
BIO_new_mem_buf((void*)key.data(), static_cast<int>(key.length())),
bio_deletor};
if (!bufkey) {
throw MemoryAllocationException("BIO_new_mem_buf failed");
}
EC_PKEY_uptr pkey{
PEM_read_bio_PUBKEY(bufkey.get(), nullptr, nullptr, nullptr),
ev_pkey_deletor};
if (!pkey) {
ec = AlgorithmErrc::InvalidKeyErr;
return { false, ec };
}
int pkey_type = EVP_PKEY_id(pkey.get());
if (pkey_type != Hasher::type) {
ec = AlgorithmErrc::VerificationErr;
return { false, ec };
}
//Convert EC signature back to ASN1
if (Hasher::type == EVP_PKEY_EC) {
EC_SIG_uptr ec_sig{ECDSA_SIG_new(), ec_sig_deletor};
if (!ec_sig) {
throw MemoryAllocationException("ECDSA_SIG_new failed");
}
//Get the actual ec_key
EC_KEY_uptr ec_key{EVP_PKEY_get1_EC_KEY(pkey.get()), ec_key_deletor};
if (!ec_key) {
throw MemoryAllocationException("EVP_PKEY_get1_EC_KEY failed");
}
unsigned int degree = EC_GROUP_get_degree(
EC_KEY_get0_group(ec_key.get()));
unsigned int bn_len = (degree + 7) / 8;
if ((bn_len * 2) != dec_sig.length()) {
ec = AlgorithmErrc::VerificationErr;
return { false, ec };
}
BIGNUM* ec_sig_r = BN_bin2bn((unsigned char*)dec_sig.data(), bn_len, nullptr);
BIGNUM* ec_sig_s = BN_bin2bn((unsigned char*)dec_sig.data() + bn_len, bn_len, nullptr);
if (!ec_sig_r || !ec_sig_s) {
ec = AlgorithmErrc::VerificationErr;
return { false, ec };
}
ECDSA_SIG_set0(ec_sig.get(), ec_sig_r, ec_sig_s);
size_t nlen = i2d_ECDSA_SIG(ec_sig.get(), nullptr);
dec_sig.resize(nlen);
auto data = reinterpret_cast<unsigned char*>(&dec_sig[0]);
nlen = i2d_ECDSA_SIG(ec_sig.get(), &data);
if (nlen == 0) {
ec = AlgorithmErrc::VerificationErr;
return { false, ec };
}
}
EVP_MDCTX_uptr mdctx_ptr{EVP_MD_CTX_create(), evp_md_ctx_deletor};
if (!mdctx_ptr) {
throw MemoryAllocationException("EVP_MD_CTX_create failed");
}
if (EVP_DigestVerifyInit(
mdctx_ptr.get(), nullptr, Hasher{}(), nullptr, pkey.get()) != 1) {
ec = AlgorithmErrc::VerificationErr;
return { false, ec };
}
if (EVP_DigestVerifyUpdate(mdctx_ptr.get(), head.data(), head.length()) != 1) {
ec = AlgorithmErrc::VerificationErr;
return { false, ec };
}
if (EVP_DigestVerifyFinal(
mdctx_ptr.get(), (unsigned char*)&dec_sig[0], dec_sig.length()) != 1) {
ec = AlgorithmErrc::VerificationErr;
return { false, ec };
}
return { true, ec };
}
template <typename Hasher>
EVP_PKEY* PEMSign<Hasher>::load_key(
const jwt::string_view key,
std::error_code& ec)
{
ec.clear();
BIO_uptr bio_ptr{
BIO_new_mem_buf((void*)key.data(), static_cast<int>(key.length())),
bio_deletor};
if (!bio_ptr) {
throw MemoryAllocationException("BIO_new_mem_buf failed");
}
EVP_PKEY* pkey = PEM_read_bio_PrivateKey(
bio_ptr.get(), nullptr, nullptr, nullptr);
if (!pkey) {
ec = AlgorithmErrc::SigningErr;
return nullptr;
}
auto pkey_type = EVP_PKEY_id(pkey);
if (pkey_type != Hasher::type) {
ec = AlgorithmErrc::SigningErr;
return nullptr;
}
return pkey;
}
template <typename Hasher>
std::string PEMSign<Hasher>::evp_digest(
EVP_PKEY* pkey,
const jwt::string_view data,
std::error_code& ec)
{
ec.clear();
EVP_MDCTX_uptr mdctx_ptr{EVP_MD_CTX_create(), evp_md_ctx_deletor};
if (!mdctx_ptr) {
throw MemoryAllocationException("EVP_MD_CTX_create failed");
}
//Initialiaze the digest algorithm
if (EVP_DigestSignInit(
mdctx_ptr.get(), nullptr, Hasher{}(), nullptr, pkey) != 1) {
ec = AlgorithmErrc::SigningErr;
return {};
}
//Update the digest with the input data
if (EVP_DigestSignUpdate(mdctx_ptr.get(), data.data(), data.length()) != 1) {
ec = AlgorithmErrc::SigningErr;
return {};
}
size_t len = 0;
if (EVP_DigestSignFinal(mdctx_ptr.get(), nullptr, &len) != 1) {
ec = AlgorithmErrc::SigningErr;
return {};
}
std::string sign;
sign.resize(len);
//Get the signature
if (EVP_DigestSignFinal(mdctx_ptr.get(), (unsigned char*)&sign[0], &len) != 1) {
ec = AlgorithmErrc::SigningErr;
return {};
}
return sign;
}
template <typename Hasher>
std::string PEMSign<Hasher>::public_key_ser(
EVP_PKEY* pkey,
jwt::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;
ec.clear();
EC_KEY_uptr ec_key{EVP_PKEY_get1_EC_KEY(pkey), ec_key_deletor};
if (!ec_key) {
ec = AlgorithmErrc::SigningErr;
return {};
}
uint32_t degree = EC_GROUP_get_degree(EC_KEY_get0_group(ec_key.get()));
ec_key.reset(nullptr);
auto char_ptr = &sign[0];
EC_SIG_uptr ec_sig{d2i_ECDSA_SIG(nullptr,
(const unsigned char**)&char_ptr,
static_cast<long>(sign.length())),
ec_sig_deletor};
if (!ec_sig) {
ec = AlgorithmErrc::SigningErr;
return {};
}
const BIGNUM* ec_sig_r = nullptr;
const BIGNUM* ec_sig_s = nullptr;
ECDSA_SIG_get0(ec_sig.get(), &ec_sig_r, &ec_sig_s);
int r_len = BN_num_bytes(ec_sig_r);
int s_len = BN_num_bytes(ec_sig_s);
int bn_len = static_cast<int>((degree + 7) / 8);
if ((r_len > bn_len) || (s_len > bn_len)) {
ec = AlgorithmErrc::SigningErr;
return {};
}
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

View File

@@ -0,0 +1,160 @@
/*
Copyright (c) 2017 Arun Muralidharan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef CPP_JWT_ERROR_CODES_IPP
#define CPP_JWT_ERROR_CODES_IPP
namespace jwt {
// Anonymous namespace
namespace {
/**
*/
struct AlgorithmErrCategory: std::error_category
{
const char* name() const noexcept override
{
return "algorithms";
}
std::string message(int ev) const override
{
switch (static_cast<AlgorithmErrc>(ev))
{
case AlgorithmErrc::SigningErr:
return "signing failed";
case AlgorithmErrc::VerificationErr:
return "verification failed";
case AlgorithmErrc::KeyNotFoundErr:
return "key not provided";
case AlgorithmErrc::NoneAlgorithmUsed:
return "none algorithm used";
case AlgorithmErrc::InvalidKeyErr:
return "invalid key";
};
return "unknown algorithm error";
}
};
/**
*/
struct DecodeErrorCategory: std::error_category
{
const char* name() const noexcept override
{
return "decode";
}
std::string message(int ev) const override
{
switch (static_cast<DecodeErrc>(ev))
{
case DecodeErrc::EmptyAlgoList:
return "empty algorithm list";
case DecodeErrc::SignatureFormatError:
return "signature format is incorrect";
case DecodeErrc::AlgHeaderMiss:
return "missing algorithm header";
case DecodeErrc::TypHeaderMiss:
return "missing type header";
case DecodeErrc::TypMismatch:
return "type mismatch";
case DecodeErrc::JsonParseError:
return "json parse failed";
case DecodeErrc::DuplClaims:
return "duplicate claims";
case DecodeErrc::KeyNotPresent:
return "key not present";
case DecodeErrc::KeyNotRequiredForNoneAlg:
return "key not required for NONE algorithm";
};
return "unknown decode error";
}
};
/**
*/
struct VerificationErrorCategory: std::error_category
{
const char* name() const noexcept override
{
return "verification";
}
std::string message(int ev) const override
{
switch (static_cast<VerificationErrc>(ev))
{
case VerificationErrc::InvalidAlgorithm:
return "invalid algorithm";
case VerificationErrc::TokenExpired:
return "token expired";
case VerificationErrc::InvalidIssuer:
return "invalid issuer";
case VerificationErrc::InvalidSubject:
return "invalid subject";
case VerificationErrc::InvalidAudience:
return "invalid audience";
case VerificationErrc::InvalidIAT:
return "invalid iat";
case VerificationErrc::InvalidJTI:
return "invalid jti";
case VerificationErrc::ImmatureSignature:
return "immature signature";
case VerificationErrc::InvalidSignature:
return "invalid signature";
case VerificationErrc::TypeConversionError:
return "type conversion error";
};
return "unknown verification error";
}
};
// Create global object for the error categories
const AlgorithmErrCategory theAlgorithmErrCategory {};
const DecodeErrorCategory theDecodeErrorCategory {};
const VerificationErrorCategory theVerificationErrorCategory {};
}
// Create the AlgorithmErrc error code
inline std::error_code make_error_code(AlgorithmErrc err)
{
return { static_cast<int>(err), theAlgorithmErrCategory };
}
inline std::error_code make_error_code(DecodeErrc err)
{
return { static_cast<int>(err), theDecodeErrorCategory };
}
inline std::error_code make_error_code(VerificationErrc err)
{
return { static_cast<int>(err), theVerificationErrorCategory };
}
} // END namespace jwt
#endif

882
externals/cpp-jwt/include/jwt/impl/jwt.ipp vendored Executable file
View File

@@ -0,0 +1,882 @@
/*
Copyright (c) 2017 Arun Muralidharan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef JWT_IPP
#define JWT_IPP
#include "jwt/config.hpp"
#include "jwt/detail/meta.hpp"
#include <algorithm>
#include <iomanip>
namespace jwt {
/**
*/
static inline void jwt_throw_exception(const std::error_code& ec);
template <typename T, typename Cond>
std::string to_json_str(const T& obj, bool pretty)
{
return pretty ? obj.create_json_obj().dump(2)
: obj.create_json_obj().dump()
;
}
template <typename T>
std::ostream& write(std::ostream& os, const T& obj, bool pretty)
{
pretty ? (os << std::setw(2) << obj.create_json_obj())
: (os << obj.create_json_obj())
;
return os;
}
template <typename T, typename Cond>
std::ostream& operator<< (std::ostream& os, const T& obj)
{
os << obj.create_json_obj();
return os;
}
//========================================================================
inline void jwt_header::decode(const jwt::string_view enc_str, std::error_code& ec)
{
ec.clear();
std::string json_str = base64_decode(enc_str);
try {
payload_ = json_t::parse(std::move(json_str));
} catch(const std::exception&) {
ec = DecodeErrc::JsonParseError;
return;
}
//Look for the algorithm field
auto alg_itr = payload_.find("alg");
if (alg_itr == payload_.end()) {
ec = DecodeErrc::AlgHeaderMiss;
return;
}
alg_ = str_to_alg(alg_itr.value().get<std::string>());
if (alg_ != algorithm::NONE)
{
auto itr = payload_.find("typ");
if (itr != payload_.end()) {
const auto& typ = itr.value().get<std::string>();
if (strcasecmp(typ.c_str(), "JWT")) {
ec = DecodeErrc::TypMismatch;
return;
}
typ_ = str_to_type(typ);
}
} else {
//TODO:
}
// Populate header
for (auto it = payload_.begin(); it != payload_.end(); ++it) {
auto ret = headers_.insert(it.key());
if (!ret.second) {
ec = DecodeErrc::DuplClaims;
//ATTN: Dont stop the decode here
//Not a hard error.
}
}
return;
}
inline void jwt_header::decode(const jwt::string_view enc_str)
{
std::error_code ec;
decode(enc_str, ec);
if (ec) {
throw DecodeError(ec.message());
}
return;
}
inline void jwt_payload::decode(const jwt::string_view enc_str, std::error_code& ec)
{
ec.clear();
std::string json_str = base64_decode(enc_str);
try {
payload_ = json_t::parse(std::move(json_str));
} catch(const std::exception&) {
ec = DecodeErrc::JsonParseError;
return;
}
//populate the claims set
for (auto it = payload_.begin(); it != payload_.end(); ++it) {
auto ret = claim_names_.insert(it.key());
if (!ret.second) {
ec = DecodeErrc::DuplClaims;
break;
}
}
return;
}
inline void jwt_payload::decode(const jwt::string_view enc_str)
{
std::error_code ec;
decode(enc_str, ec);
if (ec) {
throw DecodeError(ec.message());
}
return;
}
inline std::string jwt_signature::encode(const jwt_header& header,
const jwt_payload& payload,
std::error_code& ec)
{
std::string jwt_msg;
ec.clear();
//TODO: Optimize allocations
sign_func_t sign_fn = get_sign_algorithm_impl(header);
std::string hdr_sign = header.base64_encode();
std::string pld_sign = payload.base64_encode();
std::string data = hdr_sign + '.' + pld_sign;
auto res = sign_fn(key_, data);
if (res.second && res.second != AlgorithmErrc::NoneAlgorithmUsed) {
ec = res.second;
return {};
}
std::string b64hash;
if (!res.second) {
b64hash = base64_encode(res.first.c_str(), res.first.length());
}
auto new_len = base64_uri_encode(&b64hash[0], b64hash.length());
b64hash.resize(new_len);
jwt_msg = data + '.' + b64hash;
return jwt_msg;
}
inline verify_result_t jwt_signature::verify(const jwt_header& header,
const jwt::string_view hdr_pld_sign,
const jwt::string_view jwt_sign)
{
verify_func_t verify_fn = get_verify_algorithm_impl(header);
return verify_fn(key_, hdr_pld_sign, jwt_sign);
}
inline sign_func_t
jwt_signature::get_sign_algorithm_impl(const jwt_header& hdr) const noexcept
{
sign_func_t ret = nullptr;
switch (hdr.algo()) {
case algorithm::HS256:
ret = HMACSign<algo::HS256>::sign;
break;
case algorithm::HS384:
ret = HMACSign<algo::HS384>::sign;
break;
case algorithm::HS512:
ret = HMACSign<algo::HS512>::sign;
break;
case algorithm::NONE:
ret = HMACSign<algo::NONE>::sign;
break;
case algorithm::RS256:
ret = PEMSign<algo::RS256>::sign;
break;
case algorithm::RS384:
ret = PEMSign<algo::RS384>::sign;
break;
case algorithm::RS512:
ret = PEMSign<algo::RS512>::sign;
break;
case algorithm::ES256:
ret = PEMSign<algo::ES256>::sign;
break;
case algorithm::ES384:
ret = PEMSign<algo::ES384>::sign;
break;
case algorithm::ES512:
ret = PEMSign<algo::ES512>::sign;
break;
default:
assert (0 && "Code not reached");
};
return ret;
}
inline verify_func_t
jwt_signature::get_verify_algorithm_impl(const jwt_header& hdr) const noexcept
{
verify_func_t ret = nullptr;
switch (hdr.algo()) {
case algorithm::HS256:
ret = HMACSign<algo::HS256>::verify;
break;
case algorithm::HS384:
ret = HMACSign<algo::HS384>::verify;
break;
case algorithm::HS512:
ret = HMACSign<algo::HS512>::verify;
break;
case algorithm::NONE:
ret = HMACSign<algo::NONE>::verify;
break;
case algorithm::RS256:
ret = PEMSign<algo::RS256>::verify;
break;
case algorithm::RS384:
ret = PEMSign<algo::RS384>::verify;
break;
case algorithm::RS512:
ret = PEMSign<algo::RS512>::verify;
break;
case algorithm::ES256:
ret = PEMSign<algo::ES256>::verify;
break;
case algorithm::ES384:
ret = PEMSign<algo::ES384>::verify;
break;
case algorithm::ES512:
ret = PEMSign<algo::ES512>::verify;
break;
default:
assert (0 && "Code not reached");
};
return ret;
}
//
template <typename First, typename... Rest,
typename SFINAE_COND>
jwt_object::jwt_object(
First&& first, Rest&&... rest)
{
static_assert (detail::meta::is_parameter_concept<First>::value &&
detail::meta::are_all_params<Rest...>::value,
"All constructor argument types must model ParameterConcept");
set_parameters(std::forward<First>(first), std::forward<Rest>(rest)...);
}
template <typename Map, typename... Rest>
void jwt_object::set_parameters(
params::detail::payload_param<Map>&& payload, Rest&&... rargs)
{
for (const auto& elem : payload.get()) {
payload_.add_claim(std::move(elem.first), std::move(elem.second));
}
set_parameters(std::forward<Rest>(rargs)...);
}
template <typename... Rest>
void jwt_object::set_parameters(
params::detail::secret_param secret, Rest&&... rargs)
{
secret_.assign(secret.get().data(), secret.get().length());
set_parameters(std::forward<Rest>(rargs)...);
}
template <typename... Rest>
void jwt_object::set_parameters(
params::detail::algorithm_param alg, Rest&&... rargs)
{
header_.algo(alg.get());
set_parameters(std::forward<Rest>(rargs)...);
}
template <typename Map, typename... Rest>
void jwt_object::set_parameters(
params::detail::headers_param<Map>&& header, Rest&&... rargs)
{
for (const auto& elem : header.get()) {
header_.add_header(std::move(elem.first), std::move(elem.second));
}
set_parameters(std::forward<Rest>(rargs)...);
}
inline void jwt_object::set_parameters()
{
//sentinel call
return;
}
inline jwt_object& jwt_object::add_claim(const jwt::string_view name, system_time_t tp)
{
return add_claim(
name,
std::chrono::duration_cast<
std::chrono::seconds>(tp.time_since_epoch()).count()
);
}
inline jwt_object& jwt_object::remove_claim(const jwt::string_view name)
{
payload_.remove_claim(name);
return *this;
}
inline std::string jwt_object::signature(std::error_code& ec) const
{
ec.clear();
//key/secret should be set for any algorithm except NONE
if (header().algo() != jwt::algorithm::NONE) {
if (secret_.length() == 0) {
ec = AlgorithmErrc::KeyNotFoundErr;
return {};
}
}
jwt_signature jws{secret_};
return jws.encode(header_, payload_, ec);
}
inline std::string jwt_object::signature() const
{
std::error_code ec;
std::string res = signature(ec);
if (ec) {
throw SigningError(ec.message());
}
return res;
}
template <typename Params, typename SequenceT>
std::error_code jwt_object::verify(
const Params& dparams,
const params::detail::algorithms_param<SequenceT>& algos) const
{
std::error_code ec{};
//Verify if the algorithm set in the header
//is any of the one expected by the client.
auto fitr = std::find_if(algos.get().begin(),
algos.get().end(),
[this](const auto& elem)
{
return jwt::str_to_alg(elem) == this->header().algo();
});
if (fitr == algos.get().end()) {
ec = VerificationErrc::InvalidAlgorithm;
return ec;
}
//Check for the expiry timings
if (has_claim(registered_claims::expiration)) {
auto curr_time =
std::chrono::duration_cast<
std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
auto p_exp = payload()
.get_claim_value<uint64_t>(registered_claims::expiration);
if (static_cast<uint64_t>(curr_time) > static_cast<uint64_t>(p_exp + dparams.leeway)) {
ec = VerificationErrc::TokenExpired;
return ec;
}
}
//Check for issuer
if (dparams.has_issuer)
{
if (has_claim(registered_claims::issuer))
{
const std::string& p_issuer = payload()
.get_claim_value<std::string>(registered_claims::issuer);
if (p_issuer != dparams.issuer) {
ec = VerificationErrc::InvalidIssuer;
return ec;
}
} else {
ec = VerificationErrc::InvalidIssuer;
return ec;
}
}
//Check for audience
if (dparams.has_aud)
{
if (has_claim(registered_claims::audience))
{
const std::string& p_aud = payload()
.get_claim_value<std::string>(registered_claims::audience);
if (p_aud != dparams.aud) {
ec = VerificationErrc::InvalidAudience;
return ec;
}
} else {
ec = VerificationErrc::InvalidAudience;
return ec;
}
}
//Check the subject
if (dparams.has_sub)
{
if (has_claim(registered_claims::subject))
{
const std::string& p_sub = payload()
.get_claim_value<std::string>(registered_claims::subject);
if (p_sub != dparams.sub) {
ec = VerificationErrc::InvalidSubject;
return ec;
}
} else {
ec = VerificationErrc::InvalidSubject;
return ec;
}
}
//Check for NBF
if (has_claim(registered_claims::not_before))
{
auto curr_time =
std::chrono::duration_cast<
std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
auto p_exp = payload()
.get_claim_value<uint64_t>(registered_claims::not_before);
if (static_cast<uint64_t>(p_exp - dparams.leeway) > static_cast<uint64_t>(curr_time)) {
ec = VerificationErrc::ImmatureSignature;
return ec;
}
}
//Check IAT validation
if (dparams.validate_iat) {
if (!has_claim(registered_claims::issued_at)) {
ec = VerificationErrc::InvalidIAT;
return ec;
} else {
// Will throw type conversion error
auto val = payload()
.get_claim_value<uint64_t>(registered_claims::issued_at);
(void)val;
}
}
//Check JTI validation
if (dparams.validate_jti) {
if (!has_claim("jti")) {
ec = VerificationErrc::InvalidJTI;
return ec;
}
}
return ec;
}
inline std::array<jwt::string_view, 3>
jwt_object::three_parts(const jwt::string_view enc_str)
{
std::array<jwt::string_view, 3> result;
size_t fpos = enc_str.find_first_of('.');
assert (fpos != jwt::string_view::npos);
result[0] = jwt::string_view{&enc_str[0], fpos};
size_t spos = enc_str.find_first_of('.', fpos + 1);
result[1] = jwt::string_view{&enc_str[fpos + 1], spos - fpos - 1};
if (spos + 1 != enc_str.length()) {
result[2] = jwt::string_view{&enc_str[spos + 1], enc_str.length() - spos - 1};
}
return result;
}
template <typename DecodeParams, typename... Rest>
void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::secret_param s, Rest&&... args)
{
dparams.secret.assign(s.get().data(), s.get().length());
dparams.has_secret = true;
jwt_object::set_decode_params(dparams, std::forward<Rest>(args)...);
}
template <typename DecodeParams, typename T, typename... Rest>
void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::secret_function_param<T>&& s, Rest&&... args)
{
dparams.secret = s.get(*dparams.payload_ptr);
dparams.has_secret = true;
jwt_object::set_decode_params(dparams, std::forward<Rest>(args)...);
}
template <typename DecodeParams, typename... Rest>
void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::leeway_param l, Rest&&... args)
{
dparams.leeway = l.get();
jwt_object::set_decode_params(dparams, std::forward<Rest>(args)...);
}
template <typename DecodeParams, typename... Rest>
void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::verify_param v, Rest&&... args)
{
dparams.verify = v.get();
jwt_object::set_decode_params(dparams, std::forward<Rest>(args)...);
}
template <typename DecodeParams, typename... Rest>
void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::issuer_param i, Rest&&... args)
{
dparams.issuer = std::move(i).get();
dparams.has_issuer = true;
jwt_object::set_decode_params(dparams, std::forward<Rest>(args)...);
}
template <typename DecodeParams, typename... Rest>
void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::audience_param a, Rest&&... args)
{
dparams.aud = std::move(a).get();
dparams.has_aud = true;
jwt_object::set_decode_params(dparams, std::forward<Rest>(args)...);
}
template <typename DecodeParams, typename... Rest>
void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::subject_param s, Rest&&... args)
{
dparams.sub = std::move(s).get();
dparams.has_sub = true;
jwt_object::set_decode_params(dparams, std::forward<Rest>(args)...);
}
template <typename DecodeParams, typename... Rest>
void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::validate_iat_param v, Rest&&... args)
{
dparams.validate_iat = v.get();
jwt_object::set_decode_params(dparams, std::forward<Rest>(args)...);
}
template <typename DecodeParams, typename... Rest>
void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::validate_jti_param v, Rest&&... args)
{
dparams.validate_jti = v.get();
jwt_object::set_decode_params(dparams, std::forward<Rest>(args)...);
}
template <typename DecodeParams>
void jwt_object::set_decode_params(DecodeParams& dparams)
{
(void) dparams; // prevent -Wunused-parameter with gcc
return;
}
//==================================================================
template <typename SequenceT, typename... Args>
jwt_object decode(const jwt::string_view enc_str,
const params::detail::algorithms_param<SequenceT>& algos,
std::error_code& ec,
Args&&... args)
{
ec.clear();
jwt_object obj;
if (algos.get().size() == 0) {
ec = DecodeErrc::EmptyAlgoList;
return obj;
}
struct decode_params
{
/// key to decode the JWS
bool has_secret = false;
std::string secret;
/// Verify parameter. Defaulted to true.
bool verify = true;
/// Leeway parameter. Defaulted to zero seconds.
uint32_t leeway = 0;
///The issuer
//TODO: optional type
bool has_issuer = false;
std::string issuer;
///The audience
//TODO: optional type
bool has_aud = false;
std::string aud;
//The subject
//TODO: optional type
bool has_sub = false;
std::string sub;
//Validate IAT
bool validate_iat = false;
//Validate JTI
bool validate_jti = false;
const jwt_payload* payload_ptr = 0;
};
decode_params dparams{};
//Signature must have atleast 2 dots
auto dot_cnt = std::count_if(std::begin(enc_str), std::end(enc_str),
[](char ch) { return ch == '.'; });
if (dot_cnt < 2) {
ec = DecodeErrc::SignatureFormatError;
return obj;
}
auto parts = jwt_object::three_parts(enc_str);
//throws decode error
jwt_header hdr{};
hdr.decode(parts[0], ec);
if (ec) {
return obj;
}
//obj.header(jwt_header{parts[0]});
obj.header(std::move(hdr));
//If the algorithm is not NONE, it must not
//have more than two dots ('.') and the split
//must result in three strings with some length.
if (obj.header().algo() != jwt::algorithm::NONE) {
if (dot_cnt > 2) {
ec = DecodeErrc::SignatureFormatError;
return obj;
}
if (parts[2].length() == 0) {
ec = DecodeErrc::SignatureFormatError;
return obj;
}
}
//throws decode error
jwt_payload payload{};
payload.decode(parts[1], ec);
if (ec) {
return obj;
}
obj.payload(std::move(payload));
dparams.payload_ptr = & obj.payload();
jwt_object::set_decode_params(dparams, std::forward<Args>(args)...);
if (dparams.verify) {
try {
ec = obj.verify(dparams, algos);
} catch (const json_ns::detail::type_error&) {
ec = VerificationErrc::TypeConversionError;
}
if (ec) return obj;
//Verify the signature only if some algorithm was used
if (obj.header().algo() != algorithm::NONE)
{
if (!dparams.has_secret) {
ec = DecodeErrc::KeyNotPresent;
return obj;
}
jwt_signature jsign{dparams.secret};
// Length of the encoded header and payload only.
// Addition of '1' to account for the '.' character.
auto l = parts[0].length() + 1 + parts[1].length();
//MemoryAllocationError is not caught
verify_result_t res = jsign.verify(obj.header(), enc_str.substr(0, l), parts[2]);
if (res.second) {
ec = res.second;
return obj;
}
if (!res.first) {
ec = VerificationErrc::InvalidSignature;
return obj;
}
} else {
ec = AlgorithmErrc::NoneAlgorithmUsed;
}
}
return obj;
}
template <typename SequenceT, typename... Args>
jwt_object decode(const jwt::string_view enc_str,
const params::detail::algorithms_param<SequenceT>& algos,
Args&&... args)
{
std::error_code ec{};
auto jwt_obj = decode(enc_str,
algos,
ec,
std::forward<Args>(args)...);
if (ec) {
jwt_throw_exception(ec);
}
return jwt_obj;
}
void jwt_throw_exception(const std::error_code& ec)
{
const auto& cat = ec.category();
if (&cat == &theVerificationErrorCategory ||
std::string(cat.name()) == std::string(theVerificationErrorCategory.name()))
{
switch (static_cast<VerificationErrc>(ec.value()))
{
case VerificationErrc::InvalidAlgorithm:
{
throw InvalidAlgorithmError(ec.message());
}
case VerificationErrc::TokenExpired:
{
throw TokenExpiredError(ec.message());
}
case VerificationErrc::InvalidIssuer:
{
throw InvalidIssuerError(ec.message());
}
case VerificationErrc::InvalidAudience:
{
throw InvalidAudienceError(ec.message());
}
case VerificationErrc::InvalidSubject:
{
throw InvalidSubjectError(ec.message());
}
case VerificationErrc::InvalidIAT:
{
throw InvalidIATError(ec.message());
}
case VerificationErrc::InvalidJTI:
{
throw InvalidJTIError(ec.message());
}
case VerificationErrc::ImmatureSignature:
{
throw ImmatureSignatureError(ec.message());
}
case VerificationErrc::InvalidSignature:
{
throw InvalidSignatureError(ec.message());
}
case VerificationErrc::TypeConversionError:
{
throw TypeConversionError(ec.message());
}
default:
assert (0 && "Unknown error code");
};
}
if (&cat == &theDecodeErrorCategory ||
std::string(cat.name()) == std::string(theDecodeErrorCategory.name()))
{
switch (static_cast<DecodeErrc>(ec.value()))
{
case DecodeErrc::SignatureFormatError:
{
throw SignatureFormatError(ec.message());
}
case DecodeErrc::KeyNotPresent:
{
throw KeyNotPresentError(ec.message());
}
case DecodeErrc::KeyNotRequiredForNoneAlg:
{
// Not an error. Just to be ignored.
break;
}
default:
{
throw DecodeError(ec.message());
}
};
assert (0 && "Unknown error code");
}
if (&cat == &theAlgorithmErrCategory ||
std::string(cat.name()) == std::string(theAlgorithmErrCategory.name()))
{
switch (static_cast<AlgorithmErrc>(ec.value()))
{
case AlgorithmErrc::InvalidKeyErr:
{
throw InvalidKeyError(ec.message());
}
case AlgorithmErrc::VerificationErr:
{
throw InvalidSignatureError(ec.message());
}
case AlgorithmErrc::NoneAlgorithmUsed:
{
//Not an error actually.
break;
}
default:
assert (0 && "Unknown error code or not to be treated as an error");
};
}
return;
}
} // END namespace jwt
#endif

View File

@@ -0,0 +1,87 @@
/*
// The MIT License (MIT)
//
// Copyright (c) 2015 Howard Hinnant
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
*/
#ifndef STACK_ALLOC_IPP
#define STACK_ALLOC_IPP
namespace jwt {
template <size_t N, size_t alignment>
template <size_t reqested_alignment>
char* Arena<N, alignment>::allocate(size_t n) noexcept
{
static_assert (reqested_alignment <= alignment,
"Requested alignment is too small for this arena");
assert (pointer_in_storage(ptr_) &&
"No more space in the arena or it has outgrown its capacity");
n = align_up(n);
if ((ptr_ + n) <= (buf_ + N)) {
char* ret = ptr_;
ptr_ += n;
return ret;
}
assert (0 && "Code should not reach here");
return nullptr;
}
template <size_t N, size_t alignment>
void Arena<N, alignment>::deallocate(char* p, size_t n) noexcept
{
assert (pointer_in_storage(p) &&
"The address to de deleted does not lie inside the storage");
n = align_up(n);
if ((p + n) == ptr_) {
ptr_ = p;
}
return;
}
template <typename T, size_t N, size_t alignment>
T* stack_alloc<T, N, alignment>::allocate(size_t n) noexcept
{
return reinterpret_cast<T*>(
arena_.template allocate<alignof(T)>(n * sizeof(T))
);
}
template <typename T, size_t N, size_t alignment>
void stack_alloc<T, N, alignment>::deallocate(T* p, size_t n) noexcept
{
arena_.deallocate(reinterpret_cast<char*>(p), n);
return;
}
}
#endif

View File

@@ -0,0 +1,339 @@
/*
Copyright (c) 2017 Arun Muralidharan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef JWT_STRING_VIEW_IPP
#define JWT_STRING_VIEW_IPP
namespace jwt {
template <typename CharT, typename Traits>
auto basic_string_view<CharT, Traits>::find(
const CharT* str,
size_type pos,
size_type n) const noexcept -> size_type
{
assert (str);
assert (n < (len_ - pos) && "Comparison size out of bounds");
if (n == 0) {
return pos <= len_ ? pos : npos;
}
if (n <= len_) {
for (; pos <= (len_ - n); ++pos) {
if (traits_type::eq(data_[pos], str[0]) &&
traits_type::compare(data_ + pos + 1, str + 1, n - 1) == 0) {
return pos;
}
}
}
return npos;
}
template <typename CharT, typename Traits>
auto basic_string_view<CharT, Traits>::rfind(
const CharT* str,
size_type pos,
size_type n) const noexcept -> size_type
{
assert (str);
assert (pos < len_ && "Position out of bounds");
if (n <= len_) {
pos = std::min(len_ - n, pos);
do {
if (traits_type::eq(data_[pos], str[0]) &&
traits_type::compare(data_ + pos + 1, str + 1, n - 1) == 0) {
return pos;
}
} while (pos-- != 0);
}
return npos;
}
template <typename CharT, typename Traits>
auto basic_string_view<CharT, Traits>::find(
const CharT ch,
size_type pos) const noexcept -> size_type
{
if (pos < len_) {
for (size_type i = pos; i < len_; ++i) {
if (traits_type::eq(data_[i], ch)) return i;
}
}
return npos;
}
template <typename CharT, typename Traits>
auto basic_string_view<CharT, Traits>::rfind(
const CharT ch,
size_type pos) const noexcept -> size_type
{
if (pos < len_) {
do {
if (traits_type::eq(data_[pos], ch)) {
return pos;
}
} while (pos-- != 0);
}
return npos;
}
template <typename CharT, typename Traits>
auto basic_string_view<CharT, Traits>::find_first_of(
const CharT* str,
size_type pos,
size_type count) const noexcept -> size_type
{
assert (str);
for (size_type i = pos; i < len_; ++i) {
auto p = traits_type::find(str, count, data_[i]);
if (p) {
return i;
}
}
return npos;
}
template <typename CharT, typename Traits>
auto basic_string_view<CharT, Traits>::find_last_of(
const CharT* str,
size_type pos,
size_type count) const noexcept -> size_type
{
assert (str);
assert (pos < len_ && "Position must be within the bounds of the view");
size_type siz = len_;
if (siz && count) {
siz = std::min(pos, siz);
do {
auto p = traits_type::find(str, count, data_[siz]);
if (p) {
return siz;
}
} while (siz-- != 0);
}
return npos;
}
template <typename CharT, typename Traits>
auto basic_string_view<CharT, Traits>::find_first_not_of(
const CharT* str,
size_type pos,
size_type n) const noexcept -> size_type
{
assert (str);
assert (pos < len_&& "Position must be within the bounds of the view");
for (size_type i = pos; i < len_; ++i)
{
auto p = traits_type::find(str, n, data_[i]);
if (!p) return i;
}
return npos;
}
template <typename CharT, typename Traits>
auto basic_string_view<CharT, Traits>::find_last_not_of(
const CharT* str,
size_type pos,
size_type n) const noexcept -> size_type
{
assert (str);
assert (pos < len_ && "Position must be within the bounds of the view");
do {
for (size_type i = 0; i < n; ++i) {
if (!traits_type::eq(data_[pos], str[i])) return pos;
}
} while (pos-- != 0);
return npos;
}
template <typename CharT, typename Traits>
auto basic_string_view<CharT, Traits>::find_first_not_of(
CharT ch,
size_type pos) const noexcept -> size_type
{
assert (pos < len_&& "Position must be within the bounds of the view");
for (size_type i = pos; i < len_; ++i) {
if (!traits_type::eq(data_[i], ch)) return i;
}
return npos;
}
template <typename CharT, typename Traits>
auto basic_string_view<CharT, Traits>::find_last_not_of(
CharT ch,
size_type pos) const noexcept -> size_type
{
assert (pos < len_ && "Position must be within the bounds of the view");
do {
if (!traits_type::eq(data_[pos], ch)) return pos;
} while (pos-- != 0);
return npos;
}
// Comparison Operators
template <typename CharT, typename Traits>
bool operator== (basic_string_view<CharT, Traits> a,
basic_string_view<CharT, Traits> b) noexcept
{
if (a.length() != b.length()) return false;
using traits_type = typename basic_string_view<CharT, Traits>::traits_type;
using size_type = typename basic_string_view<CharT, Traits>::size_type;
for (size_type i = 0; i < a.length(); ++i) {
if (!traits_type::eq(a[i], b[i])) return false;
}
return true;
}
template <typename CharT, typename Traits>
bool operator!= (basic_string_view<CharT, Traits> a,
basic_string_view<CharT, Traits> b) noexcept
{
return !( a == b );
}
template <typename CharT, typename Traits>
bool operator< (basic_string_view<CharT, Traits> a,
basic_string_view<CharT, Traits> b) noexcept
{
return a.compare(b) < 0;
}
template <typename CharT, typename Traits>
bool operator> (basic_string_view<CharT, Traits> a,
basic_string_view<CharT, Traits> b) noexcept
{
return a.compare(b) > 0;
}
template <typename CharT, typename Traits>
bool operator<= (basic_string_view<CharT, Traits> a,
basic_string_view<CharT, Traits> b) noexcept
{
return a.compare(b) <= 0;
}
template <typename CharT, typename Traits>
bool operator>= (basic_string_view<CharT, Traits> a,
basic_string_view<CharT, Traits> b) noexcept
{
return a.compare(b) >= 0;
}
template <typename CharT, typename Traits>
std::ostream& operator<< (std::ostream& os, basic_string_view<CharT, Traits> sv)
{
os.write(sv.data(), sv.length());
return os;
}
namespace {
/*
* Copy of gcc implementation of murmurhash
* hash_bytes.cc
*/
inline size_t
unaligned_load(const char* p) noexcept
{
std::size_t result;
std::memcpy(&result, p, sizeof(result));
return result;
}
inline size_t
hash_bytes(const void* ptr, size_t len, size_t seed) noexcept
{
const size_t m = 0x5bd1e995;
size_t hash = seed ^ len;
const char* buf = static_cast<const char*>(ptr);
// Mix 4 bytes at a time into the hash.
while(len >= 4)
{
size_t k = unaligned_load(buf);
k *= m;
k ^= k >> 24;
k *= m;
hash *= m;
hash ^= k;
buf += 4;
len -= 4;
}
// Handle the last few bytes of the input array.
switch(len)
{
case 3:
hash ^= static_cast<unsigned char>(buf[2]) << 16;
//FALLTHROUGH
case 2:
hash ^= static_cast<unsigned char>(buf[1]) << 8;
//FALLTHROUGH
case 1:
hash ^= static_cast<unsigned char>(buf[0]);
hash *= m;
};
// Do a few final mixes of the hash.
hash ^= hash >> 13;
hash *= m;
hash ^= hash >> 15;
return hash;
}
}
} // END namespace jwt
/// Provide a hash specialization
namespace std {
template <>
struct hash<jwt::string_view>
{
size_t operator()(const jwt::string_view& sv) const noexcept
{
return jwt::hash_bytes((void*)sv.data(), sv.length(), static_cast<size_t>(0xc70f6907UL));
}
};
}
#endif