This is an automated email from the ASF dual-hosted git repository.

wwbmmm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new ded7c225 Support url safe base64 (#2337)
ded7c225 is described below

commit ded7c2256fd8b73bedfca0421aec05c426ff44b2
Author: Bright Chen <[email protected]>
AuthorDate: Wed Oct 25 15:02:09 2023 +0800

    Support url safe base64 (#2337)
    
    * Support url safe base64
    
    * Update base64url license
    
    * Update base64url license
---
 .licenserc.yaml            |   1 +
 BUILD.bazel                |   1 +
 CMakeLists.txt             |   1 +
 LICENSE                    |  35 +++++++++++++++
 Makefile                   |   1 +
 src/butil/base64url.cc     |  96 +++++++++++++++++++++++++++++++++++++++
 src/butil/base64url.h      |  54 ++++++++++++++++++++++
 test/BUILD.bazel           |   1 +
 test/CMakeLists.txt        |   1 +
 test/Makefile              |   1 +
 test/base64url_unittest.cc | 110 +++++++++++++++++++++++++++++++++++++++++++++
 11 files changed, 302 insertions(+)

diff --git a/.licenserc.yaml b/.licenserc.yaml
index 042769f1..a189f6bc 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -59,6 +59,7 @@ header:
     - 'src/butil/atomic*'
     - 'src/butil/auto_reset.h'
     - 'src/butil/base64.*'
+    - 'src/butil/base64url.*'
     - 'src/butil/base_export.h'
     - 'src/butil/base_paths.cc'
     - 'src/butil/basictypes.h'
diff --git a/BUILD.bazel b/BUILD.bazel
index 5d317c90..03dcd3ec 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -117,6 +117,7 @@ BUTIL_SRCS = [
     "src/butil/at_exit.cc",
     "src/butil/atomicops_internals_x86_gcc.cc",
     "src/butil/base64.cc",
+    "src/butil/base64url.cc",
     "src/butil/big_endian.cc",
     "src/butil/cpu.cc",
     "src/butil/debug/alias.cc",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a01a0bf6..6dc1b5bf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -294,6 +294,7 @@ set(BUTIL_SOURCES
     ${PROJECT_SOURCE_DIR}/src/butil/at_exit.cc
     ${PROJECT_SOURCE_DIR}/src/butil/atomicops_internals_x86_gcc.cc
     ${PROJECT_SOURCE_DIR}/src/butil/base64.cc
+    ${PROJECT_SOURCE_DIR}/src/butil/base64url.cc
     ${PROJECT_SOURCE_DIR}/src/butil/big_endian.cc
     ${PROJECT_SOURCE_DIR}/src/butil/cpu.cc
     ${PROJECT_SOURCE_DIR}/src/butil/debug/alias.cc
diff --git a/LICENSE b/LICENSE
index feac1ef6..0efd09e4 100644
--- a/LICENSE
+++ b/LICENSE
@@ -924,3 +924,38 @@ copyright (c) Google inc and (c) The Chromium Authors and 
licensed under the
   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+src/butil/base64url.*
+test/base64url_unittest.cc
+
+Some portions of these files are derived from code in the Chromium project,
+copyright The Chromium Authors and licensed under the 3-clause BSD license:
+
+  Copyright 2015 The Chromium Authors
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are
+  met:
+
+     * Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+     * Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the following disclaimer
+  in the documentation and/or other materials provided with the
+  distribution.
+     * Neither the name of Google LLC nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
index 87ddc5a4..0ed8a352 100644
--- a/Makefile
+++ b/Makefile
@@ -68,6 +68,7 @@ BUTIL_SOURCES = \
     src/butil/at_exit.cc \
     src/butil/atomicops_internals_x86_gcc.cc \
     src/butil/base64.cc \
+    src/butil/base64url.cc \
     src/butil/big_endian.cc \
     src/butil/cpu.cc \
     src/butil/debug/alias.cc \
diff --git a/src/butil/base64url.cc b/src/butil/base64url.cc
new file mode 100644
index 00000000..f782f33d
--- /dev/null
+++ b/src/butil/base64url.cc
@@ -0,0 +1,96 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "butil/base64.h"
+#include "butil/base64url.h"
+
+#include "third_party/modp_b64/modp_b64_data.h"
+
+namespace butil {
+
+// Base64url maps {+, /} to {-, _} in order for the encoded content to be safe
+// to use in a URL. These characters will be translated by this implementation.
+#define BASE64_CHARS "+/"
+#define BASE64_URL_SAFE_CHARS "-_"
+#define URL_SAFE_CHAR62 '-'
+#define URL_SAFE_CHAR63 '_'
+
+void Base64UrlEncode(const StringPiece& input,
+                     Base64UrlEncodePolicy policy,
+                     std::string* output) {
+    Base64Encode(input, output);
+
+    std::replace(output->begin(), output->end(), CHAR62, URL_SAFE_CHAR62);
+    std::replace(output->begin(), output->end(), CHAR63, URL_SAFE_CHAR63);
+
+    switch (policy) {
+        case Base64UrlEncodePolicy::INCLUDE_PADDING:
+            // The padding included in |*output| will not be amended.
+            break;
+        case Base64UrlEncodePolicy::OMIT_PADDING:
+            // The padding included in |*output| will be removed.
+            const size_t last_non_padding_pos =
+                    output->find_last_not_of(CHARPAD);
+            if (last_non_padding_pos != std::string::npos) {
+                output->resize(last_non_padding_pos + 1);
+            }
+            break;
+    }
+}
+
+bool Base64UrlDecode(const StringPiece& input,
+                     Base64UrlDecodePolicy policy,
+                     std::string* output) {
+    // Characters outside of the base64url alphabet are disallowed, which 
includes
+    // the {+, /} characters found in the conventional base64 alphabet.
+    if (input.find_first_of(BASE64_CHARS) != std::string::npos)
+        return false;
+
+    const size_t required_padding_characters = input.size() % 4;
+    const bool needs_replacement =
+            input.find_first_of(BASE64_URL_SAFE_CHARS) != std::string::npos;
+
+    switch (policy) {
+        case Base64UrlDecodePolicy::REQUIRE_PADDING:
+            // Fail if the required padding is not included in |input|.
+            if (required_padding_characters > 0)
+                return false;
+            break;
+        case Base64UrlDecodePolicy::IGNORE_PADDING:
+            // Missing padding will be silently appended.
+            break;
+        case Base64UrlDecodePolicy::DISALLOW_PADDING:
+            // Fail if padding characters are included in |input|.
+            if (input.find_first_of(CHARPAD) != std::string::npos)
+                return false;
+            break;
+    }
+
+    // If the string either needs replacement of URL-safe characters to normal
+    // base64 ones, or additional padding, a copy of |input| needs to be made 
in
+    // order to make these adjustments without side effects.
+    if (required_padding_characters > 0 || needs_replacement) {
+        std::string base64_input;
+
+        size_t base64_input_size = input.size();
+        if (required_padding_characters > 0)
+            base64_input_size += 4 - required_padding_characters;
+
+        base64_input.reserve(base64_input_size);
+        input.AppendToString(&base64_input);
+
+        // Substitute the base64url URL-safe characters to their base64 
equivalents.
+        std::replace(base64_input.begin(), base64_input.end(), 
URL_SAFE_CHAR62, CHAR62);
+        std::replace(base64_input.begin(), base64_input.end(), 
URL_SAFE_CHAR63, CHAR63);
+
+        // Append the necessary padding characters.
+        base64_input.resize(base64_input_size, '=');
+
+        return Base64Decode(base64_input, output);
+    }
+
+    return Base64Decode(input, output);
+}
+
+}  // namespace butil
diff --git a/src/butil/base64url.h b/src/butil/base64url.h
new file mode 100644
index 00000000..438f6b7f
--- /dev/null
+++ b/src/butil/base64url.h
@@ -0,0 +1,54 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_BASE64URL_H_
+#define BASE_BASE64URL_H_
+
+#include <string>
+
+#include "butil/base_export.h"
+#include "butil/strings/string_piece.h"
+
+namespace butil {
+
+enum class Base64UrlEncodePolicy {
+    // Include the trailing padding in the output, when necessary.
+    INCLUDE_PADDING,
+
+    // Remove the trailing padding from the output.
+    OMIT_PADDING
+};
+
+// Encodes the |input| string in base64url, defined in RFC 4648:
+// https://tools.ietf.org/html/rfc4648#section-5
+//
+// The |policy| defines whether padding should be included or omitted from the
+// encoded |*output|. |input| and |*output| may reference the same storage.
+BUTIL_EXPORT void Base64UrlEncode(const StringPiece& input,
+                                 Base64UrlEncodePolicy policy,
+                                 std::string* output);
+
+enum class Base64UrlDecodePolicy {
+    // Require inputs to contain trailing padding if non-aligned.
+    REQUIRE_PADDING,
+
+    // Accept inputs regardless of whether they have the correct padding.
+    IGNORE_PADDING,
+
+    // Reject inputs if they contain any trailing padding.
+    DISALLOW_PADDING
+};
+
+// Decodes the |input| string in base64url, defined in RFC 4648:
+// https://tools.ietf.org/html/rfc4648#section-5
+//
+// The |policy| defines whether padding will be required, ignored or disallowed
+// altogether. |input| and |*output| may reference the same storage.
+BUTIL_EXPORT bool Base64UrlDecode(const StringPiece& input,
+                                 Base64UrlDecodePolicy policy,
+                                 std::string* output) WARN_UNUSED_RESULT;
+
+}  // namespace butil
+
+#endif  // BASE_BASE64URL_H_
diff --git a/test/BUILD.bazel b/test/BUILD.bazel
index 2c47aa10..8c57c10a 100644
--- a/test/BUILD.bazel
+++ b/test/BUILD.bazel
@@ -45,6 +45,7 @@ TEST_BUTIL_SOURCES = [
     "at_exit_unittest.cc",
     "atomicops_unittest.cc",
     "base64_unittest.cc",
+    "base64url_unittest.cc",
     "big_endian_unittest.cc",
     "bits_unittest.cc",
     "hash_tables_unittest.cc",
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index aa441d27..42cb647b 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -80,6 +80,7 @@ SET(TEST_BUTIL_SOURCES
     ${PROJECT_SOURCE_DIR}/test/at_exit_unittest.cc
     ${PROJECT_SOURCE_DIR}/test/atomicops_unittest.cc
     ${PROJECT_SOURCE_DIR}/test/base64_unittest.cc
+    ${PROJECT_SOURCE_DIR}/test/base64url_unittest.cc
     ${PROJECT_SOURCE_DIR}/test/big_endian_unittest.cc
     ${PROJECT_SOURCE_DIR}/test/bits_unittest.cc
     ${PROJECT_SOURCE_DIR}/test/hash_tables_unittest.cc
diff --git a/test/Makefile b/test/Makefile
index 0723d2df..6e0dbc97 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -50,6 +50,7 @@ TEST_BUTIL_SOURCES = \
     at_exit_unittest.cc \
     atomicops_unittest.cc \
     base64_unittest.cc \
+    base64url_unittest.cc \
     big_endian_unittest.cc \
     bits_unittest.cc \
     hash_tables_unittest.cc \
diff --git a/test/base64url_unittest.cc b/test/base64url_unittest.cc
new file mode 100644
index 00000000..4995b162
--- /dev/null
+++ b/test/base64url_unittest.cc
@@ -0,0 +1,110 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "butil/base64url.h"
+
+#include <gtest/gtest.h>
+
+namespace butil {
+
+TEST(Base64UrlTest, EncodeIncludePaddingPolicy) {
+  std::string output;
+  Base64UrlEncode("hello?world", Base64UrlEncodePolicy::INCLUDE_PADDING,
+                  &output);
+
+  // Base64 version: aGVsbG8/d29ybGQ=
+  EXPECT_EQ("aGVsbG8_d29ybGQ=", output);
+
+  // Test for behavior for very short and empty strings.
+  Base64UrlEncode("??", Base64UrlEncodePolicy::INCLUDE_PADDING, &output);
+  EXPECT_EQ("Pz8=", output);
+
+  Base64UrlEncode("", Base64UrlEncodePolicy::INCLUDE_PADDING, &output);
+  EXPECT_EQ("", output);
+}
+
+TEST(Base64UrlTest, EncodeOmitPaddingPolicy) {
+  std::string output;
+  Base64UrlEncode("hello?world", Base64UrlEncodePolicy::OMIT_PADDING, &output);
+
+  // base64 version: aGVsbG8/d29ybGQ=
+  EXPECT_EQ("aGVsbG8_d29ybGQ", output);
+
+  // Test for behavior for very short and empty strings.
+  Base64UrlEncode("??", Base64UrlEncodePolicy::OMIT_PADDING, &output);
+  EXPECT_EQ("Pz8", output);
+
+  Base64UrlEncode("", Base64UrlEncodePolicy::OMIT_PADDING, &output);
+  EXPECT_EQ("", output);
+}
+
+TEST(Base64UrlTest, DecodeRequirePaddingPolicy) {
+  std::string output;
+  ASSERT_TRUE(Base64UrlDecode("aGVsbG8_d29ybGQ=",
+                              Base64UrlDecodePolicy::REQUIRE_PADDING, 
&output));
+
+  EXPECT_EQ("hello?world", output);
+
+  ASSERT_FALSE(Base64UrlDecode(
+      "aGVsbG8_d29ybGQ", Base64UrlDecodePolicy::REQUIRE_PADDING, &output));
+
+  // Test for behavior for very short and empty strings.
+  ASSERT_TRUE(
+      Base64UrlDecode("Pz8=", Base64UrlDecodePolicy::REQUIRE_PADDING, 
&output));
+  EXPECT_EQ("??", output);
+
+  ASSERT_TRUE(
+      Base64UrlDecode("", Base64UrlDecodePolicy::REQUIRE_PADDING, &output));
+  EXPECT_EQ("", output);
+}
+
+TEST(Base64UrlTest, DecodeIgnorePaddingPolicy) {
+  std::string output;
+  ASSERT_TRUE(Base64UrlDecode("aGVsbG8_d29ybGQ",
+                              Base64UrlDecodePolicy::IGNORE_PADDING, &output));
+
+  EXPECT_EQ("hello?world", output);
+
+  // Including the padding is accepted as well.
+  ASSERT_TRUE(Base64UrlDecode("aGVsbG8_d29ybGQ=",
+                              Base64UrlDecodePolicy::IGNORE_PADDING, &output));
+
+  EXPECT_EQ("hello?world", output);
+}
+
+TEST(Base64UrlTest, DecodeDisallowPaddingPolicy) {
+  std::string output;
+  ASSERT_FALSE(Base64UrlDecode(
+      "aGVsbG8_d29ybGQ=", Base64UrlDecodePolicy::DISALLOW_PADDING, &output));
+
+  // The policy will allow the input when padding has been omitted.
+  ASSERT_TRUE(Base64UrlDecode(
+      "aGVsbG8_d29ybGQ", Base64UrlDecodePolicy::DISALLOW_PADDING, &output));
+
+  EXPECT_EQ("hello?world", output);
+}
+
+TEST(Base64UrlTest, DecodeDisallowsBase64Alphabet) {
+  std::string output;
+
+  // The "/" character is part of the conventional base64 alphabet, but has 
been
+  // substituted with "_" in the base64url alphabet.
+  ASSERT_FALSE(Base64UrlDecode(
+      "aGVsbG8/d29ybGQ=", Base64UrlDecodePolicy::REQUIRE_PADDING, &output));
+}
+
+TEST(Base64UrlTest, DecodeDisallowsPaddingOnly) {
+  std::string output;
+
+  ASSERT_FALSE(Base64UrlDecode(
+      "=", Base64UrlDecodePolicy::IGNORE_PADDING, &output));
+  ASSERT_FALSE(Base64UrlDecode(
+      "==", Base64UrlDecodePolicy::IGNORE_PADDING, &output));
+  ASSERT_FALSE(Base64UrlDecode(
+      "===", Base64UrlDecodePolicy::IGNORE_PADDING, &output));
+  ASSERT_FALSE(Base64UrlDecode(
+      "====", Base64UrlDecodePolicy::IGNORE_PADDING, &output));
+}
+
+}  // namespace butil


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to