Skip to content

Commit

Permalink
feat: implement EdDSA
Browse files Browse the repository at this point in the history
Signed-off-by: Artur Troian <[email protected]>
  • Loading branch information
troian committed Mar 20, 2020
1 parent e784574 commit 27508a3
Show file tree
Hide file tree
Showing 14 changed files with 377 additions and 105 deletions.
66 changes: 50 additions & 16 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ if (NOT WIN32 AND NOT JsonCPP_FOUND)
pkg_check_modules(JsonCPP REQUIRED jsoncpp)
endif ()

set(eddsa_support OFF)

if (OPENSSL_VERSION VERSION_GREATER_EQUAL 1.1.1)
set(eddsa_support ON)
endif()

include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR})
include_directories(SYSTEM ${JsonCPP_INCLUDE_DIRS})
link_directories(${JsonCPP_LIBRARY_DIRS})
Expand All @@ -56,6 +62,7 @@ if (NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated")
endif ()


set(LIB_SOURCES
src/b64.cpp
src/claims.cpp
Expand All @@ -65,15 +72,27 @@ set(LIB_SOURCES
src/header.cpp
src/hmac.cpp
src/jwtpp.cpp
src/rsa.cpp
src/tools.cpp
src/pss.cpp
src/rsa.cpp
src/statics.cpp
src/tools.cpp

include/export/jwtpp/jwtpp.hh
include/local/jwtpp/statics.hh
)

add_library(${PROJECT_NAME}-static STATIC ${LIB_SOURCES})
add_library(
${PROJECT_NAME}-static
STATIC ${LIB_SOURCES}
)
if (eddsa_support)
target_sources(
${PROJECT_NAME}-static
PRIVATE
src/eddsa.cpp
)
endif()

set_target_properties(${PROJECT_NAME}-static PROPERTIES OUTPUT_NAME ${PROJECT_NAME} CLEAN_DIRECT_OUTPUT 1)

target_include_directories(
Expand Down Expand Up @@ -103,6 +122,14 @@ endif ()

if (BUILD_SHARED_LIBS)
add_library(${PROJECT_NAME}-shared SHARED ${LIB_SOURCES})
if (eddsa_support)
target_sources(
${PROJECT_NAME}-shared
PRIVATE
src/eddsa.cpp
)
endif()

set_target_properties(${PROJECT_NAME}-shared PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
set_target_properties(${PROJECT_NAME}-shared PROPERTIES OUTPUT_NAME ${PROJECT_NAME} CLEAN_DIRECT_OUTPUT 1)
target_include_directories(
Expand Down Expand Up @@ -159,18 +186,24 @@ if (WITH_TESTS)

include_directories(${PROJECT_SOURCE_DIR}/gtest/googletest/include)

set(JOSEPP_TEST_SRS
add_executable(jwtpp_test
tests/b64.cpp
tests/claims.cpp
tests/digest.cpp
tests/ecdsa.cpp
tests/header.cpp
tests/hmac.cpp
tests/rsa.cpp
tests/pss.cpp
tests/digest.cpp
tests/header.cpp
tests/rsa.cpp
)

add_executable(jwtpp_test ${JOSEPP_TEST_SRS})
if (eddsa_support)
target_sources(
jwtpp_test
PRIVATE
tests/eddsa.cpp
)
endif()

if (WIN32)
set(WIN32_DEP_LIBS crypt32.lib ws2_32.lib)
Expand All @@ -190,12 +223,13 @@ if (WITH_TESTS)
PRIVATE
-DTEST_RSA_KEY_PATH=\"${CMAKE_SOURCE_DIR}/tests/rsa.pem\"
)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
append_coverage_compiler_flags()

setup_target_for_coverage_lcov(
NAME coverage
EXECUTABLE jwtpp_test
)
endif ()
#
# if (CMAKE_BUILD_TYPE STREQUAL "Debug")
# append_coverage_compiler_flags()
#
# setup_target_for_coverage_lcov(
# NAME coverage
# EXECUTABLE jwtpp_test
# )
# endif ()
endif ()
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ JSON Object Signing and Encryption library for C++
| PS256 | **Supported** |
| PS384 | **Supported** |
| PS512 | **Supported** |
| EdDSA | **Supported** |

#### Claims
|Claim|Options|Status|
Expand Down
59 changes: 47 additions & 12 deletions include/export/jwtpp/jwtpp.hh
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ enum class alg_t {
PS256,
PS384,
PS512,
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
EdDSA,
#endif // OPENSSL_VERSION_NUMBER >= 0x10101000L
UNKNOWN
};

Expand All @@ -92,14 +95,18 @@ enum class alg_t {
typedef std::shared_ptr<RSA> sp_rsa_key;
typedef std::shared_ptr<EC_KEY> sp_ecdsa_key;
#else
using sp_claims = typename std::shared_ptr<class claims>;
using up_claims = typename std::unique_ptr<class claims>;
using sp_crypto = typename std::shared_ptr<class crypto>;
using sp_hmac = typename std::shared_ptr<class hmac>;
using sp_rsa = typename std::shared_ptr<class rsa>;
using sp_ecdsa = typename std::shared_ptr<class ecdsa>;
using sp_rsa_key = typename std::shared_ptr<RSA>;
using sp_ecdsa_key = typename std::shared_ptr<EC_KEY>;
using sp_claims = typename std::shared_ptr<class claims>;
using up_claims = typename std::unique_ptr<class claims>;
using sp_crypto = typename std::shared_ptr<class crypto>;
using sp_hmac = typename std::shared_ptr<class hmac>;
using sp_rsa = typename std::shared_ptr<class rsa>;
using sp_ecdsa = typename std::shared_ptr<class ecdsa>;
using sp_rsa_key = typename std::shared_ptr<RSA>;
using sp_ecdsa_key = typename std::shared_ptr<EC_KEY>;
using sp_evp_key = typename std::shared_ptr<EVP_PKEY>;

using sp_evp_md_ctx = typename std::shared_ptr<EVP_MD_CTX>;
using sp_evp_pkey_ctx = typename std::shared_ptr<EVP_PKEY_CTX>;
#endif // defined(_MSC_VER) && (_MSC_VER < 1700)

template <class T>
Expand Down Expand Up @@ -559,7 +566,7 @@ protected:

class hmac : public crypto {
public:
explicit hmac(alg_t a, const secure_string &secret);
explicit hmac(const secure_string &secret, alg_t a = alg_t::HS256);

~hmac() override = default;

Expand All @@ -581,7 +588,7 @@ private:

class rsa : public crypto {
public:
explicit rsa(alg_t a, sp_rsa_key key);
explicit rsa(sp_rsa_key key, alg_t a = alg_t::RS256);

~rsa() override;

Expand Down Expand Up @@ -617,7 +624,7 @@ private:

class ecdsa : public crypto {
public:
explicit ecdsa(alg_t a, sp_ecdsa_key key);
explicit ecdsa(sp_ecdsa_key key, alg_t a = alg_t::ES256);

~ecdsa() override = default;

Expand All @@ -640,9 +647,37 @@ private:
sp_ecdsa_key _e;
};

#if OPENSSL_VERSION_NUMBER >= 0x10101000L
class eddsa : public crypto {
public:
explicit eddsa(sp_evp_key key, alg_t a = alg_t::EdDSA);

~eddsa() override = default;

public:
std::string sign(const std::string &data) override;
bool verify(const std::string &data, const std::string &sig) override;

public:

#if !(defined(_MSC_VER) && (_MSC_VER < 1700))
template <typename... _Args>
static sp_ecdsa make_shared(_Args&&... __args) {
return std::make_shared<class ecdsa>(__args...);
}
#endif // !(defined(_MSC_VER) && (_MSC_VER < 1700))

static sp_evp_key gen();
static sp_evp_key get_pub(sp_evp_key priv);

private:
sp_evp_key _e;
};
#endif // OPENSSL_VERSION_NUMBER >= 0x10101000L

class pss : public crypto {
public:
explicit pss(alg_t a, sp_rsa_key key);
explicit pss(sp_rsa_key key, alg_t a = alg_t::PS256);

~pss() override = default;

Expand Down
12 changes: 12 additions & 0 deletions src/crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ crypto::crypto(alg_t a)
_hash_type = digest::type::SHA384;
} else if (a == alg_t::HS512 || a == alg_t::RS512 || a == alg_t::ES512 || a == alg_t::PS512) {
_hash_type = digest::type::SHA512;
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
} else if (a == alg_t::EdDSA) {
// ED25519 does not support digests
#endif // OPENSSL_VERSION_NUMBER >= 0x10101000L
} else {
throw std::runtime_error("invalid algorithm");
}
Expand Down Expand Up @@ -70,6 +74,10 @@ const char *crypto::alg2str(alg_t a) {
return "PS384";
case alg_t::PS512:
return "PS512";
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
case alg_t::EdDSA:
return "EdDSA";
#endif // OPENSSL_VERSION_NUMBER >= 0x10101000L
default:
return nullptr;
}
Expand Down Expand Up @@ -102,6 +110,10 @@ alg_t crypto::str2alg(const std::string &a) {
return alg_t::PS384;
} else if (a == "PS512") {
return alg_t::PS512;
} else if (a == "EdDSA") {
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
return alg_t::EdDSA;
#endif // OPENSSL_VERSION_NUMBER >= 0x10101000L
} else {
return alg_t::UNKNOWN;
}
Expand Down
2 changes: 1 addition & 1 deletion src/ecdsa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

namespace jwtpp {

ecdsa::ecdsa(alg_t a, sp_ecdsa_key key)
ecdsa::ecdsa(sp_ecdsa_key key, alg_t a)
: crypto(a)
, _e(key)
{
Expand Down
105 changes: 105 additions & 0 deletions src/eddsa.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// The MIT License (MIT)
//
// Copyright (c) 2016-2020 Artur Troian
//
// 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.

#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/crypto.h>

#include <jwtpp/jwtpp.hh>

namespace jwtpp {

eddsa::eddsa(sp_evp_key key, alg_t a)
: crypto(a)
, _e(key)
{
if (a != alg_t::EdDSA) {
throw std::invalid_argument("Invalid algorithm");
}
}

std::string eddsa::sign(const std::string &data) {
auto md = sp_evp_md_ctx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);

EVP_MD_CTX_init(md.get());

if (EVP_DigestSignInit(md.get(), nullptr, nullptr, nullptr, _e.get()) != 1) {
throw std::runtime_error("eddsa: digest sign init");
}

size_t sig_len = EVP_PKEY_size(_e.get());

auto sig = std::shared_ptr<uint8_t>(new uint8_t[sig_len], std::default_delete<uint8_t[]>());

if (EVP_DigestSign(md.get(), sig.get(), &sig_len, (const uint8_t *)data.data(), data.size()) != 1) {
throw std::runtime_error("eddsa: digest sign");
}

return b64::encode_uri(sig.get(), sig_len);
}

bool eddsa::verify(const std::string &data, const std::string &sig) {
auto s = b64::decode_uri(sig.data(), sig.length());

auto md = sp_evp_md_ctx(EVP_MD_CTX_new(), ::EVP_MD_CTX_free);

EVP_MD_CTX_init(md.get());

if (EVP_DigestVerifyInit(md.get(), nullptr, nullptr, nullptr, _e.get()) != 1) {
throw std::runtime_error("eddsa: digest verify init");
}

return EVP_DigestVerify(md.get(), s.data(), s.size(), (const uint8_t *)data.data(), data.size()) == 1;
}

sp_evp_key eddsa::gen() {
auto ctx = sp_evp_pkey_ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, nullptr), ::EVP_PKEY_CTX_free);
if (EVP_PKEY_keygen_init(ctx.get()) != 1) {
throw std::runtime_error("eddsa: couldn't init evp keygen");
}

EVP_PKEY *key = nullptr;

if (EVP_PKEY_keygen(ctx.get(), &key) != 1) {
throw std::runtime_error("eddsa: couldn't generate ED25519 key");
}

return sp_evp_key(key, ::EVP_PKEY_free);
}

sp_evp_key eddsa::get_pub(sp_evp_key priv) {
size_t key_len;

if (EVP_PKEY_get_raw_public_key(priv.get(), nullptr, &key_len) != 1) {
throw std::runtime_error("eddsa: couldn't read size of public key");
}

auto k = std::shared_ptr<uint8_t>(new uint8_t[key_len], std::default_delete<uint8_t[]>());

if (EVP_PKEY_get_raw_public_key(priv.get(), k.get(), &key_len) != 1) {
throw std::runtime_error("eddsa: couldn't extract public key");
}

return sp_evp_key(EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, k.get(), key_len), ::EVP_PKEY_free);
}

} // namespace jwtpp
2 changes: 1 addition & 1 deletion src/hmac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

namespace jwtpp {

hmac::hmac(alg_t a, const secure_string &secret)
hmac::hmac(const secure_string &secret, alg_t a)
: crypto(a)
, _secret(secret)
{
Expand Down
2 changes: 1 addition & 1 deletion src/pss.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace jwtpp {

pss::pss(alg_t a, sp_rsa_key key)
pss::pss(sp_rsa_key key, alg_t a)
: crypto(a)
, _r(key)
{
Expand Down
Loading

0 comments on commit 27508a3

Please sign in to comment.