Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
7 changes: 7 additions & 0 deletions source/common/common/base64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,13 @@ std::string Base64::encode(const char* input, uint64_t length, bool add_padding)
return ret;
}

void Base64::completePadding(std::string& encoded) {
if (encoded.length() % 4 != 0) {
std::string trailing_padding(4 - encoded.length() % 4, '=');
encoded.append(trailing_padding);
}
}

std::string Base64Url::decode(const std::string& input) {
if (input.empty()) {
return EMPTY_STRING;
Expand Down
6 changes: 6 additions & 0 deletions source/common/common/base64.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ class Base64 {
* bytes.
*/
static std::string decodeWithoutPadding(absl::string_view input);

/**
* Add the padding in the base64 encoded binary if the padding is missing.
* @param encoded is the target to complete the padding.
*/
static void completePadding(std::string& encoded);
};

/**
Expand Down
6 changes: 5 additions & 1 deletion source/extensions/filters/http/jwt_authn/authenticator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "envoy/http/async_client.h"

#include "common/common/assert.h"
#include "common/common/base64.h"
#include "common/common/enum_to_int.h"
#include "common/common/logger.h"
#include "common/http/message_impl.h"
Expand Down Expand Up @@ -247,9 +248,12 @@ void AuthenticatorImpl::verifyKey() {

// Forward the payload
const auto& provider = jwks_data_->getJwtProvider();
std::string payload_str_base64url_with_padding = jwt_->payload_str_base64url_;
Base64::completePadding(payload_str_base64url_with_padding);
Comment thread
TAOXUY marked this conversation as resolved.
Outdated

if (!provider.forward_payload_header().empty()) {
headers_->addCopy(Http::LowerCaseString(provider.forward_payload_header()),
jwt_->payload_str_base64url_);
payload_str_base64url_with_padding);
}

if (!provider.forward()) {
Expand Down
43 changes: 43 additions & 0 deletions test/common/common/base64_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,49 @@ TEST(Base64Test, BinaryBufferEncode) {
EXPECT_EQ("AAECAwgKCQCqvN4=", Base64::encode(buffer, 30));
}

TEST(Base64Test, CompletePadding) {
struct CompletePaddingBase64UrlTestCases {
std::string base64, base64_with_padding;
};

// For base64 encoding, there are only three length needed to test
// - 3n bytes => 4n bytes, no padding needed
// - 3n + 1 bytes => 4n + 2 bytes, 2 padding needed
// - 3n + 2 bytes => 4n + 3 bytes, 1 padding needed
CompletePaddingBase64UrlTestCases testCases[3] = {
// Payload text(3n bytes):
{"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG8iLCJpYXQiOjE1MTYyMzkwMjJ"
"9",
// No padding added.
"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG8iLCJpYXQiOjE1MTYyMzkwMjJ"
"9"},
// Payload text(3n + 1 bytes):
{"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2"
"MjM5MDIyfQ",
// 2 padding added.
"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2"
"MjM5MDIyfQ=="},
// Payload text(3n + 2 bytes):
{"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lZSIsImlhdCI6MTUx"
"NjIzOTAyMn0",
// 1 padding added.
"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lZSIsImlhdCI6MTUx"
"NjIzOTAyMn0="}};
for (int i = 0; i < 3; i++) {
struct CompletePaddingBase64UrlTestCases& tc = testCases[i];

// Ensure these two base64 binaries are equivalent after decoding.
EXPECT_EQ(Base64::decodeWithoutPadding(tc.base64),
Base64::decodeWithoutPadding(tc.base64_with_padding));
Comment thread
TAOXUY marked this conversation as resolved.
// Ensure the `base64_with_padding` is correctly padded.
EXPECT_NE(Base64::decode(tc.base64_with_padding), "");

std::string base64_padded = tc.base64;
Base64::completePadding(base64_padded);
EXPECT_EQ(base64_padded, tc.base64_with_padding);
}
}

TEST(Base64UrlTest, EncodeString) {
EXPECT_EQ("", Base64Url::encode("", 0));
EXPECT_EQ("AAA", Base64Url::encode("\0\0", 2));
Expand Down
1 change: 1 addition & 0 deletions test/extensions/filters/http/jwt_authn/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ envoy_extension_cc_test(
extension_name = "envoy.filters.http.jwt_authn",
deps = [
":mock_lib",
"//source/common/common:base64_lib",
"//source/extensions/filters/http/common:jwks_fetcher_lib",
"//source/extensions/filters/http/jwt_authn:authenticator_lib",
"//source/extensions/filters/http/jwt_authn:filter_config_interface",
Expand Down
2 changes: 1 addition & 1 deletion test/extensions/filters/http/jwt_authn/test_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ const char OtherGoodToken[] =
// Expected base64 payload value.
const char ExpectedPayloadValue[] = "eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwic3ViIjoidGVzdEBleGFtcG"
"xlLmNvbSIsImV4cCI6MjAwMTAwMTAwMSwiYXVkIjoiZXhhbXBsZV9zZXJ2"
"aWNlIn0";
"aWNlIn0=";

// Base64 decoded Payload JSON
const char ExpectedPayloadJSON[] = R"(
Expand Down