From 76177e128e2670aef661dd83c97559e9964a5f07 Mon Sep 17 00:00:00 2001 From: isherman Date: Tue, 14 Oct 2014 14:21:40 -0700 Subject: [PATCH] [Easy Unlock] Implement base64url encode and decode functions. BUG=421277 TEST=components_unittests R=antrim@chromium.org, tengs@chromium.org Review URL: https://codereview.chromium.org/641783006 Cr-Commit-Position: refs/heads/master@{#299550} --- .../easy_unlock_create_keys_operation.cc | 12 +--- components/components_tests.gyp | 1 + components/proximity_auth.gypi | 2 + components/proximity_auth/BUILD.gn | 3 + components/proximity_auth/base64url.cc | 28 +++++++++ components/proximity_auth/base64url.h | 29 ++++++++++ components/proximity_auth/base64url_unittest.cc | 67 ++++++++++++++++++++++ components/proximity_auth/wire_message.cc | 4 +- components/proximity_auth/wire_message_unittest.cc | 17 +++++- 9 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 components/proximity_auth/base64url.cc create mode 100644 components/proximity_auth/base64url.h create mode 100644 components/proximity_auth/base64url_unittest.cc diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_create_keys_operation.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_create_keys_operation.cc index cc4378e672cf..673a26e62030 100644 --- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_create_keys_operation.cc +++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_create_keys_operation.cc @@ -6,7 +6,6 @@ #include -#include "base/base64.h" #include "base/bind.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -18,6 +17,7 @@ #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/easy_unlock_client.h" #include "chromeos/login/auth/key.h" +#include "components/proximity_auth/base64url.h" #include "crypto/encryptor.h" #include "crypto/random.h" #include "crypto/symmetric_key.h" @@ -62,14 +62,6 @@ const char kTpmPubKey[] = { 0xc1, 0x10, 0x81, 0x80, 0x04 }; -bool WebSafeBase64Decode(const std::string& encoded, std::string* decoded) { - std::string adjusted_encoded = encoded; - base::ReplaceChars(adjusted_encoded, "-", "+", &adjusted_encoded); - base::ReplaceChars(adjusted_encoded, "_", "/", &adjusted_encoded); - - return base::Base64Decode(adjusted_encoded, decoded); -} - } // namespace ///////////////////////////////////////////////////////////////////////////// @@ -152,7 +144,7 @@ void EasyUnlockCreateKeysOperation::ChallengeCreator::OnEcKeyPairGenerated( } std::string device_pub_key; - if (!WebSafeBase64Decode(device_->public_key, &device_pub_key)) { + if (!proximity_auth::Base64UrlDecode(device_->public_key, &device_pub_key)) { LOG(ERROR) << "Easy unlock failed to decode device public key."; callback_.Run(false); return; diff --git a/components/components_tests.gyp b/components/components_tests.gyp index b0bf4ed052b4..a8d833babeb1 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -667,6 +667,7 @@ 'copresence/rpc/http_post_unittest.cc', 'copresence/rpc/rpc_handler_unittest.cc', 'copresence/timed_map_unittest.cc', + 'proximity_auth/base64url_unittest.cc', 'proximity_auth/bluetooth_connection_unittest.cc', 'proximity_auth/connection_unittest.cc', 'proximity_auth/cryptauth/cryptauth_api_call_flow_unittest.cc', diff --git a/components/proximity_auth.gypi b/components/proximity_auth.gypi index e41855ef8424..27ffd2201c98 100644 --- a/components/proximity_auth.gypi +++ b/components/proximity_auth.gypi @@ -16,6 +16,8 @@ '../net/net.gyp:net', ], 'sources': [ + "proximity_auth/base64url.cc", + "proximity_auth/base64url.h", "proximity_auth/bluetooth_connection.cc", "proximity_auth/bluetooth_connection.h", "proximity_auth/bluetooth_util.cc", diff --git a/components/proximity_auth/BUILD.gn b/components/proximity_auth/BUILD.gn index caf35b416316..4730ea5d711c 100644 --- a/components/proximity_auth/BUILD.gn +++ b/components/proximity_auth/BUILD.gn @@ -4,6 +4,8 @@ source_set("proximity_auth") { sources = [ + "base64url.cc", + "base64url.h", "bluetooth_connection.cc", "bluetooth_connection.h", "bluetooth_util.cc", @@ -31,6 +33,7 @@ source_set("proximity_auth") { source_set("unit_tests") { testonly = true sources = [ + "base64url_unittest.cc", "bluetooth_connection_unittest.cc", "connection_unittest.cc", "proximity_auth_system_unittest.cc", diff --git a/components/proximity_auth/base64url.cc b/components/proximity_auth/base64url.cc new file mode 100644 index 000000000000..ae6ee9d87102 --- /dev/null +++ b/components/proximity_auth/base64url.cc @@ -0,0 +1,28 @@ +// Copyright 2014 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 "components/proximity_auth/base64url.h" + +#include "base/base64.h" +#include "base/strings/string_util.h" + +namespace proximity_auth { + +void Base64UrlEncode(const std::string& decoded_input, + std::string* encoded_output) { + base::Base64Encode(decoded_input, encoded_output); + base::ReplaceChars(*encoded_output, "+", "-", encoded_output); + base::ReplaceChars(*encoded_output, "/", "_", encoded_output); +} + +bool Base64UrlDecode(const std::string& encoded_input, + std::string* decoded_output) { + std::string adjusted_encoded_input = encoded_input; + base::ReplaceChars(adjusted_encoded_input, "-", "+", &adjusted_encoded_input); + base::ReplaceChars(adjusted_encoded_input, "_", "/", &adjusted_encoded_input); + + return base::Base64Decode(adjusted_encoded_input, decoded_output); +} + +} // namespace proximity_auth diff --git a/components/proximity_auth/base64url.h b/components/proximity_auth/base64url.h new file mode 100644 index 000000000000..63d4990d2dcd --- /dev/null +++ b/components/proximity_auth/base64url.h @@ -0,0 +1,29 @@ +// Copyright 2014 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 COMPONENTS_PROXIMITY_BASE64URL_H +#define COMPONENTS_PROXIMITY_BASE64URL_H + +#include + +namespace proximity_auth { + +// An implmentation of the base64url encoding. Escapes the unsafe '+' and '/' +// characters with the url-safe alternatives '-' and '_', respectively. For more +// info, see the section on "Base 64 Encoding with URL and Filename Safe +// Alphabet" in http://www.ietf.org/rfc/rfc4648.txt +// NOTE: Unlike many common web-safe encodings, this function does not escape +// the '=' character. This is to match the expectations set by Android. +// TODO(isherman): There are several (semantically slightly different) +// implementations of this within the Chromium codebase. Try to unify them. +void Base64UrlEncode(const std::string& decoded_input, + std::string* encoded_output); + +// The inverse operation for the base64url encoding above. +bool Base64UrlDecode(const std::string& encoded_input, + std::string* decoded_output); + +} // namespace proximity_auth + +#endif // COMPONENTS_PROXIMITY_BASE64URL_H diff --git a/components/proximity_auth/base64url_unittest.cc b/components/proximity_auth/base64url_unittest.cc new file mode 100644 index 000000000000..946185814d14 --- /dev/null +++ b/components/proximity_auth/base64url_unittest.cc @@ -0,0 +1,67 @@ +// Copyright 2014 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 "components/proximity_auth/base64url.h" + +#include "base/base64.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace proximity_auth { + +TEST(ProximityAuthBase64UrlTest, EncodeRegularString) { + const std::string input = "Hello world!"; + const std::string expected_output = "SGVsbG8gd29ybGQh"; + + std::string web_safe_output; + Base64UrlEncode(input, &web_safe_output); + EXPECT_EQ(expected_output, web_safe_output); + + // For good measure, make sure that the encoding matches the //base + // implemenation as well. + std::string non_web_safe_output; + base::Base64Encode(input, &non_web_safe_output); + EXPECT_EQ(web_safe_output, non_web_safe_output); +} + +TEST(ProximityAuthBase64UrlTest, DecodeRegularString) { + const std::string input = "SGVsbG8gd29ybGQh"; + const std::string expected_output = "Hello world!"; + + std::string web_safe_output; + EXPECT_TRUE(Base64UrlDecode(input, &web_safe_output)); + EXPECT_EQ(expected_output, web_safe_output); + + // For good measure, make sure that the encoding matches the //base + // implemenation as well. + std::string non_web_safe_output; + EXPECT_TRUE(base::Base64Decode(input, &non_web_safe_output)); + EXPECT_EQ(web_safe_output, non_web_safe_output); +} + +TEST(ProximityAuthBase64UrlTest, EncodeSpecialCharacters) { + // This happens to be a stable encoding, i.e. encode(decode(s)) gives back s. + const std::string encoded = "/+Y="; + std::string decoded; + ASSERT_TRUE(base::Base64Decode(encoded, &decoded)); + + // Decoded strings that encode to special characters are non-printable, so, + // for ease of testing, just compare the web-safe and non-web-safe encodings. + std::string web_safe_encoded; + Base64UrlEncode(decoded, &web_safe_encoded); + EXPECT_EQ("_-Y=", web_safe_encoded); +} + +TEST(ProximityAuthBase64UrlTest, DecodeSpecialCharacters) { + const std::string encoded = "_-Y="; + std::string decoded; + ASSERT_TRUE(Base64UrlDecode(encoded, &decoded)); + + // Decoded strings that encode to special characters are non-printable, so, + // for ease of testing, just compare the web-safe and non-web-safe encodings. + std::string non_web_safe_encoded; + base::Base64Encode(decoded, &non_web_safe_encoded); + EXPECT_EQ("/+Y=", non_web_safe_encoded); +} + +} // namespace proximity_auth diff --git a/components/proximity_auth/wire_message.cc b/components/proximity_auth/wire_message.cc index 62c8094ba63c..604c026566a7 100644 --- a/components/proximity_auth/wire_message.cc +++ b/components/proximity_auth/wire_message.cc @@ -4,11 +4,11 @@ #include "components/proximity_auth/wire_message.h" -#include "base/base64.h" #include "base/json/json_reader.h" #include "base/logging.h" #include "base/macros.h" #include "base/values.h" +#include "components/proximity_auth/base64url.h" // The wire messages have a simple format: // [ message version ] [ body length ] [ JSON body ] @@ -103,7 +103,7 @@ scoped_ptr WireMessage::Deserialize( } std::string payload; - if (!base::Base64Decode(payload_base64, &payload)) { + if (!Base64UrlDecode(payload_base64, &payload)) { VLOG(1) << "Error: Invalid base64 encoding for payload."; return scoped_ptr(); } diff --git a/components/proximity_auth/wire_message_unittest.cc b/components/proximity_auth/wire_message_unittest.cc index 8a124b3b9703..68ce0865ebfa 100644 --- a/components/proximity_auth/wire_message_unittest.cc +++ b/components/proximity_auth/wire_message_unittest.cc @@ -141,11 +141,24 @@ TEST(ProximityAuthWireMessage, Deserialize_ValidMessage) { scoped_ptr message = WireMessage::Deserialize(bytes, &is_incomplete); EXPECT_FALSE(is_incomplete); - EXPECT_TRUE(message); + ASSERT_TRUE(message); EXPECT_EQ("Hi!", message->permit_id()); EXPECT_EQ("a", message->payload()); } +TEST(ProximityAuthWireMessage, Deserialize_ValidMessageWithBase64UrlEncoding) { + bool is_incomplete; + std::string header("\3\0\x27", 3); + std::string bytes = + header + "{\"permit_id\": \"Hi!\", \"payload\": \"_-Y=\"}"; + scoped_ptr message = + WireMessage::Deserialize(bytes, &is_incomplete); + EXPECT_FALSE(is_incomplete); + ASSERT_TRUE(message); + EXPECT_EQ("Hi!", message->permit_id()); + EXPECT_EQ("\xFF\xE6", message->payload()); +} + TEST(ProximityAuthWireMessage, Deserialize_ValidMessageWithExtraUnknownFields) { bool is_incomplete; std::string header("\3\0\x46", 3); @@ -158,7 +171,7 @@ TEST(ProximityAuthWireMessage, Deserialize_ValidMessageWithExtraUnknownFields) { scoped_ptr message = WireMessage::Deserialize(bytes, &is_incomplete); EXPECT_FALSE(is_incomplete); - EXPECT_TRUE(message); + ASSERT_TRUE(message); EXPECT_EQ("Hi!", message->permit_id()); EXPECT_EQ("a", message->payload()); } -- 2.11.4.GIT