Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/root/configuration/http_filters/lua_filter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,32 @@ Returns the current request's underlying :repo:`connection <include/envoy/networ

Returns a :ref:`connection object <config_http_filters_lua_connection_wrapper>`.

importPublicKey()
^^^^^^^^^^^^^^^^^

.. code-block:: lua

pubkey = handle:importPublicKey(keyder, keyderLength)

Returns public key which is used by :ref:`verifySignature <verify_signature>` to verify digital signature.

.. _verify_signature:

verifySignature()
^^^^^^^^^^^^^^^^^

.. code-block:: lua

ok, error = verifySignature(hashFunction, pubkey, signature, signatureLength, data, dataLength)

Verify signature using provided parameters. *hashFunction* is the variable for hash function which be used
for verifying signature. *SHA1*, *SHA224*, *SHA256*, *SHA384* and *SHA512* are supported.
*pubkey* is the public key. *signature* is the signature to be verified. *signatureLength* is
the length of the signature. *data* is the content which will be hashed. *dataLength* is the length of data.

The function returns a pair. If the first element is *true*, the second element will be empty
which means signature is verified; otherwise, the second element will store the error message.

.. _config_http_filters_lua_header_wrapper:

Header object API
Expand Down
6 changes: 6 additions & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ Version history
* http: fixed a crashing bug where gRPC local replies would cause segfaults when upstream access logging was on.
* http: mitigated a race condition with the :ref:`delayed_close_timeout<envoy_api_field_config.filter.network.http_connection_manager.v2.HttpConnectionManager.delayed_close_timeout>` where it could trigger while actively flushing a pending write buffer for a downstream connection.
* jwt_authn: make filter's parsing of JWT more flexible, allowing syntax like ``jwt=eyJhbGciOiJS...ZFnFIw,extra=7,realm=123``
* listener: added :ref:`source IP <envoy_api_field_listener.FilterChainMatch.source_prefix_ranges>`
and :ref:`source port <envoy_api_field_listener.FilterChainMatch.source_ports>` filter
chain matching.
* lua: exposed functions to Lua to verify digital signature.
* original_src filter: added the :ref:`filter<config_http_filters_original_src>`.
* rbac: migrated from v2alpha to v2.
* redis: add support for Redis cluster custom cluster type.
* redis: added :ref:`prefix routing <envoy_api_field_config.filter.network.redis_proxy.v2.RedisProxy.prefix_routes>` to enable routing commands based on their key's prefix to different upstream.
* redis: add support for zpopmax and zpopmin commands.
Expand Down
59 changes: 58 additions & 1 deletion source/common/crypto/utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
#include "common/common/assert.h"
#include "common/common/stack_array.h"

#include "openssl/evp.h"
#include "absl/strings/ascii.h"
#include "absl/strings/str_cat.h"
#include "openssl/bytestring.h"
#include "openssl/hmac.h"
#include "openssl/sha.h"

Expand Down Expand Up @@ -41,6 +43,61 @@ std::vector<uint8_t> Utility::getSha256Hmac(const std::vector<uint8_t>& key,
return hmac;
}

const VerificationOutput Utility::verifySignature(absl::string_view hash, EVP_PKEY* pubKey,
const std::vector<uint8_t>& signature,
const std::vector<uint8_t>& text) {
// Step 1: initialize EVP_MD_CTX
bssl::ScopedEVP_MD_CTX ctx;

// Step 2: initialize EVP_MD
const EVP_MD* md = Utility::getHashFunction(hash);

if (md == nullptr) {
return {false, absl::StrCat(hash, " is not supported.")};
}

// Step 3: initialize EVP_DigestVerify
int ok = EVP_DigestVerifyInit(ctx.get(), nullptr, md, nullptr, pubKey);
if (!ok) {
return {false, "Failed to initialize digest verify."};
}

// Step 4: verify signature
ok = EVP_DigestVerify(ctx.get(), signature.data(), signature.size(), text.data(), text.size());

// Step 5: check result
if (ok == 1) {
return {true, ""};
}

return {false, absl::StrCat("Failed to verify digest. Error code: ", ok)};
}

PublicKeyPtr Utility::importPublicKey(const std::vector<uint8_t>& key) {
CBS cbs({key.data(), key.size()});
return PublicKeyPtr(EVP_parse_public_key(&cbs));
}

const EVP_MD* Utility::getHashFunction(absl::string_view name) {
const std::string hash = absl::AsciiStrToLower(name);

// Hash algorithms set refers
// https://github.com/google/boringssl/blob/master/include/openssl/digest.h
if (hash == "sha1") {
return EVP_sha1();
} else if (hash == "sha224") {
return EVP_sha224();
} else if (hash == "sha256") {
return EVP_sha256();
} else if (hash == "sha384") {
return EVP_sha384();
} else if (hash == "sha512") {
return EVP_sha512();
} else {
return nullptr;
}
}

} // namespace Crypto
} // namespace Common
} // namespace Envoy
39 changes: 39 additions & 0 deletions source/common/crypto/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,27 @@
#include "envoy/buffer/buffer.h"

#include "absl/strings/string_view.h"
#include "openssl/evp.h"

namespace Envoy {
namespace Common {
namespace Crypto {

struct VerificationOutput {
/**
* Verification result. If result_ is true, error_message_ is empty.
*/
bool result_;

/**
* Error message when verification failed.
* TODO(crazyxy): switch to absl::StatusOr when available
*/
std::string error_message_;
};

typedef bssl::UniquePtr<EVP_PKEY> PublicKeyPtr;

class Utility {
public:
/**
Expand All @@ -28,6 +44,29 @@ class Utility {
*/
static std::vector<uint8_t> getSha256Hmac(const std::vector<uint8_t>& key,
absl::string_view message);

/**
* Verify cryptographic signatures.
* @param hash hash function(including SHA1, SHA224, SHA256, SHA384, SHA512)
* @param key pointer to public key
* @param signature signature
* @param text clear text
* @return If the result_ is true, the error_message_ is empty; otherwise,
* the error_message_ stores the error message
*/
static const VerificationOutput verifySignature(absl::string_view hash, EVP_PKEY* key,
const std::vector<uint8_t>& signature,
const std::vector<uint8_t>& text);

/**
* Import public key.
* @param key key string
* @return pointer to public key
*/
static PublicKeyPtr importPublicKey(const std::vector<uint8_t>& key);

private:
static const EVP_MD* getHashFunction(absl::string_view name);
};

} // namespace Crypto
Expand Down
2 changes: 2 additions & 0 deletions source/extensions/filters/http/lua/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ envoy_cc_library(
"//include/envoy/upstream:cluster_manager_interface",
"//source/common/buffer:buffer_lib",
"//source/common/common:enum_to_int",
"//source/common/crypto:utility_lib",
"//source/common/http:message_lib",
"//source/extensions/filters/common/lua:lua_lib",
"//source/extensions/filters/common/lua:wrappers_lib",
Expand All @@ -36,6 +37,7 @@ envoy_cc_library(
deps = [
"//include/envoy/http:header_map_interface",
"//include/envoy/stream_info:stream_info_interface",
"//source/common/crypto:utility_lib",
"//source/common/http:utility_lib",
"//source/extensions/filters/common/lua:lua_lib",
"//source/extensions/filters/common/lua:wrappers_lib",
Expand Down
48 changes: 48 additions & 0 deletions source/extensions/filters/http/lua/lua_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "common/buffer/buffer_impl.h"
#include "common/common/assert.h"
#include "common/common/enum_to_int.h"
#include "common/crypto/utility.h"
#include "common/http/message_impl.h"

namespace Envoy {
Expand Down Expand Up @@ -428,6 +429,52 @@ int StreamHandleWrapper::luaLogCritical(lua_State* state) {
return 0;
}

int StreamHandleWrapper::luaVerifySignature(lua_State* state) {
// Step 1: get hash function
absl::string_view hash = luaL_checkstring(state, 2);

// Step 2: get key pointer
auto ptr = lua_touserdata(state, 3);

// Step 3: get signature
const char* signature = luaL_checkstring(state, 4);
int sig_len = luaL_checknumber(state, 5);
const std::vector<uint8_t> sig_vec(signature, signature + sig_len);

// Step 4: get clear text
const char* clear_text = luaL_checkstring(state, 6);
int text_len = luaL_checknumber(state, 7);
const std::vector<uint8_t> text_vec(clear_text, clear_text + text_len);

// Step 5: verify signature
auto output = Common::Crypto::Utility::verifySignature(hash, reinterpret_cast<EVP_PKEY*>(ptr),
sig_vec, text_vec);

lua_pushboolean(state, output.result_);
if (output.result_) {
lua_pushnil(state);
} else {
lua_pushlstring(state, output.error_message_.data(), output.error_message_.length());
}
return 2;
}

int StreamHandleWrapper::luaImportPublicKey(lua_State* state) {
// Get byte array and the length.
const char* str = luaL_checkstring(state, 2);
int n = luaL_checknumber(state, 3);
std::vector<uint8_t> key(str, str + n);

if (public_key_wrapper_.get() != nullptr) {
public_key_wrapper_.pushStack();
} else {
public_key_wrapper_.reset(
PublicKeyWrapper::create(state, Common::Crypto::Utility::importPublicKey(key)), true);
}

return 1;
}

FilterConfig::FilterConfig(const std::string& lua_code, ThreadLocal::SlotAllocator& tls,
Upstream::ClusterManager& cluster_manager)
: cluster_manager_(cluster_manager), lua_state_(lua_code, tls) {
Expand All @@ -442,6 +489,7 @@ FilterConfig::FilterConfig(const std::string& lua_code, ThreadLocal::SlotAllocat
lua_state_.registerType<DynamicMetadataMapWrapper>();
lua_state_.registerType<DynamicMetadataMapIterator>();
lua_state_.registerType<StreamHandleWrapper>();
lua_state_.registerType<PublicKeyWrapper>();

request_function_slot_ = lua_state_.registerGlobal("envoy_on_request");
if (lua_state_.getGlobalRef(request_function_slot_) == LUA_REFNIL) {
Expand Down
48 changes: 40 additions & 8 deletions source/extensions/filters/http/lua/lua_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,23 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject<StreamHan
}

static ExportedFunctions exportedFunctions() {
return {{"headers", static_luaHeaders}, {"body", static_luaBody},
{"bodyChunks", static_luaBodyChunks}, {"trailers", static_luaTrailers},
{"metadata", static_luaMetadata}, {"logTrace", static_luaLogTrace},
{"logDebug", static_luaLogDebug}, {"logInfo", static_luaLogInfo},
{"logWarn", static_luaLogWarn}, {"logErr", static_luaLogErr},
{"logCritical", static_luaLogCritical}, {"httpCall", static_luaHttpCall},
{"respond", static_luaRespond}, {"streamInfo", static_luaStreamInfo},
{"connection", static_luaConnection}};
return {{"headers", static_luaHeaders},
{"body", static_luaBody},
{"bodyChunks", static_luaBodyChunks},
{"trailers", static_luaTrailers},
{"metadata", static_luaMetadata},
{"logTrace", static_luaLogTrace},
{"logDebug", static_luaLogDebug},
{"logInfo", static_luaLogInfo},
{"logWarn", static_luaLogWarn},
{"logErr", static_luaLogErr},
{"logCritical", static_luaLogCritical},
{"httpCall", static_luaHttpCall},
{"respond", static_luaRespond},
{"streamInfo", static_luaStreamInfo},
{"connection", static_luaConnection},
{"importPublicKey", static_luaImportPublicKey},
{"verifySignature", static_luaVerifySignature}};
}

private:
Expand Down Expand Up @@ -209,6 +218,27 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject<StreamHan
DECLARE_LUA_FUNCTION(StreamHandleWrapper, luaLogErr);
DECLARE_LUA_FUNCTION(StreamHandleWrapper, luaLogCritical);

/**
* Verify cryptographic signatures.
* @param 1 (string) hash function(including SHA1, SHA224, SHA256, SHA384, SHA512)
* @param 2 (void*) pointer to public key
* @param 3 (string) signature
* @param 4 (int) length of signature
* @param 5 (string) clear text
* @param 6 (int) length of clear text
* @return (bool, string) If the first element is true, the second element is empty; otherwise,
* the second element stores the error message
*/
DECLARE_LUA_FUNCTION(StreamHandleWrapper, luaVerifySignature);

/**
* Import public key.
* @param 1 (string) keyder string
* @param 2 (int) length of keyder string
* @return pointer to public key
*/
DECLARE_LUA_FUNCTION(StreamHandleWrapper, luaImportPublicKey);

/**
* This is the closure/iterator returned by luaBodyChunks() above.
*/
Expand All @@ -226,6 +256,7 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject<StreamHan
metadata_wrapper_.reset();
stream_info_wrapper_.reset();
connection_wrapper_.reset();
public_key_wrapper_.reset();
}

// Http::AsyncClient::Callbacks
Expand All @@ -247,6 +278,7 @@ class StreamHandleWrapper : public Filters::Common::Lua::BaseLuaObject<StreamHan
Filters::Common::Lua::LuaDeathRef<Filters::Common::Lua::MetadataMapWrapper> metadata_wrapper_;
Filters::Common::Lua::LuaDeathRef<StreamInfoWrapper> stream_info_wrapper_;
Filters::Common::Lua::LuaDeathRef<Filters::Common::Lua::ConnectionWrapper> connection_wrapper_;
Filters::Common::Lua::LuaDeathRef<PublicKeyWrapper> public_key_wrapper_;
State state_{State::Running};
std::function<void()> yield_callback_;
Http::AsyncClient::Request* http_request_{};
Expand Down
9 changes: 9 additions & 0 deletions source/extensions/filters/http/lua/wrappers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,15 @@ int DynamicMetadataMapWrapper::luaPairs(lua_State* state) {
return 1;
}

int PublicKeyWrapper::luaGet(lua_State* state) {
if (public_key_ == nullptr || public_key_.get() == nullptr) {
lua_pushnil(state);
} else {
lua_pushlightuserdata(state, public_key_.get());
}
return 1;
}

} // namespace Lua
} // namespace HttpFilters
} // namespace Extensions
Expand Down
20 changes: 20 additions & 0 deletions source/extensions/filters/http/lua/wrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "envoy/http/header_map.h"
#include "envoy/stream_info/stream_info.h"

#include "common/crypto/utility.h"

#include "extensions/filters/common/lua/lua.h"

namespace Envoy {
Expand Down Expand Up @@ -201,6 +203,24 @@ class StreamInfoWrapper : public Filters::Common::Lua::BaseLuaObject<StreamInfoW
friend class DynamicMetadataMapWrapper;
};

/**
* Lua wrapper for EVP_PKEY.
*/
class PublicKeyWrapper : public Filters::Common::Lua::BaseLuaObject<PublicKeyWrapper> {
public:
PublicKeyWrapper(Envoy::Common::Crypto::PublicKeyPtr key) : public_key_(std::move(key)) {}
static ExportedFunctions exportedFunctions() { return {{"get", static_luaGet}}; }

private:
/**
* Get a pointer to public key.
* @return pointer to public key.
*/
DECLARE_LUA_FUNCTION(PublicKeyWrapper, luaGet);

Envoy::Common::Crypto::PublicKeyPtr public_key_;
};

} // namespace Lua
} // namespace HttpFilters
} // namespace Extensions
Expand Down
Loading