Commit 3746ed85 authored by Mikel Astiz's avatar Mikel Astiz Committed by Chromium LUCI CQ

[Fake Gaia] Provide sync trusted vault keys when requested

The fake server leverages the recently used authenticator API,
syncTrustedVaultKeys, that allows the server to provide sync encryption
keys during authentication.

This server-side logic triggers only if the client expressed the desire
to receive sync trusted vault keys, achieved via newly-introduced URL
parameters.

Change-Id: I1538d83fd9716fbdc7989d299911368ba346e39b
Bug: 1081651
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2587171Reviewed-by: default avatarDavid Roger <droger@chromium.org>
Reviewed-by: default avatarRoman Sorokin [CET] <rsorokin@chromium.org>
Commit-Queue: Mikel Astiz <mastiz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#846255}
parent 02f738b6
......@@ -7,14 +7,17 @@
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/base_paths.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/containers/contains.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
......@@ -104,7 +107,7 @@ void SetCookies(BasicHttpResponse* http_response,
}
std::string FormatCookieForMultilogin(std::string name, std::string value) {
std::string format = R"(
const char format[] = R"(
{
"name":"%s",
"value":"%s",
......@@ -116,7 +119,39 @@ std::string FormatCookieForMultilogin(std::string name, std::string value) {
"maxAge":63070000
}
)";
return base::StringPrintf(format.c_str(), name.c_str(), value.c_str());
return base::StringPrintf(format, name.c_str(), value.c_str());
}
std::string FormatSyncTrustedPublicKeys(
const std::vector<std::vector<uint8_t>>& public_keys) {
std::string result;
for (const std::vector<uint8_t>& public_key : public_keys) {
if (!result.empty()) {
base::StrAppend(&result, {","});
}
base::StrAppend(&result, {"\"", base::Base64Encode(public_key), "\""});
}
return result;
}
std::string FormatSyncTrustedVaultKeysHeader(
const FakeGaia::SyncTrustedVaultKeys& sync_trusted_vault_keys) {
// Single line used because this string populates HTTP headers. Similarly,
// base64 encoding is used to avoid line breaks and meanwhile adopt JSON
// format (which doesn't support binary blobs). This base64 encoding is undone
// embedded_setup_chromeos.html.
const char format[] =
"{"
"\"fakeEncryptionKeyMaterial\":\"%s\","
"\"fakeEncryptionKeyVersion\":%d,"
"\"fakeTrustedPublicKeys\":[%s]"
"}";
return base::StringPrintf(
format,
base::Base64Encode(sync_trusted_vault_keys.encryption_key).c_str(),
sync_trusted_vault_keys.encryption_key_version,
FormatSyncTrustedPublicKeys(sync_trusted_vault_keys.trusted_public_keys)
.c_str());
}
} // namespace
......@@ -152,6 +187,10 @@ void FakeGaia::MergeSessionParams::Update(const MergeSessionParams& update) {
maybe_update_field(&MergeSessionParams::email);
}
FakeGaia::SyncTrustedVaultKeys::SyncTrustedVaultKeys() = default;
FakeGaia::SyncTrustedVaultKeys::~SyncTrustedVaultKeys() = default;
FakeGaia::FakeGaia() : issue_oauth_code_cookie_(false) {
base::FilePath source_root_dir;
base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir);
......@@ -195,6 +234,13 @@ void FakeGaia::MapEmailToGaiaId(const std::string& email,
email_to_gaia_id_map_[email] = gaia_id;
}
void FakeGaia::SetSyncTrustedVaultKeys(
const std::string& email,
const SyncTrustedVaultKeys& sync_trusted_vault_keys) {
DCHECK(!email.empty());
email_to_sync_trusted_vault_keys_map_[email] = sync_trusted_vault_keys;
}
std::string FakeGaia::GetGaiaIdOfEmail(const std::string& email) const {
const auto it = email_to_gaia_id_map_.find(email);
return it == email_to_gaia_id_map_.end() ? std::string(kDefaultGaiaId) :
......@@ -203,6 +249,7 @@ std::string FakeGaia::GetGaiaIdOfEmail(const std::string& email) const {
void FakeGaia::AddGoogleAccountsSigninHeader(BasicHttpResponse* http_response,
const std::string& email) const {
DCHECK(http_response);
http_response->AddCustomHeader("google-accounts-signin",
base::StringPrintf(
"email=\"%s\", obfuscatedid=\"%s\", sessionindex=0",
......@@ -210,12 +257,23 @@ void FakeGaia::AddGoogleAccountsSigninHeader(BasicHttpResponse* http_response,
}
void FakeGaia::SetOAuthCodeCookie(BasicHttpResponse* http_response) const {
DCHECK(http_response);
http_response->AddCustomHeader(
"Set-Cookie", base::StringPrintf("oauth_code=%s%s",
merge_session_params_.auth_code.c_str(),
kTestCookieAttributes));
}
void FakeGaia::AddSyncTrustedKeysHeader(BasicHttpResponse* http_response,
const std::string& email) const {
DCHECK(http_response);
DCHECK(base::Contains(email_to_sync_trusted_vault_keys_map_, email));
http_response->AddCustomHeader(
"fake-sync-trusted-vault-keys",
FormatSyncTrustedVaultKeysHeader(
email_to_sync_trusted_vault_keys_map_.at(email)));
}
void FakeGaia::Initialize() {
GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
// Handles /MergeSession GAIA call.
......@@ -644,6 +702,10 @@ void FakeGaia::HandleEmbeddedSigninChallenge(const HttpRequest& request,
if (issue_oauth_code_cookie_)
SetOAuthCodeCookie(http_response);
if (base::Contains(email_to_sync_trusted_vault_keys_map_, email)) {
AddSyncTrustedKeysHeader(http_response, email);
}
}
void FakeGaia::HandleSSO(const HttpRequest& request,
......
......@@ -87,6 +87,15 @@ class FakeGaia {
std::string email;
};
struct SyncTrustedVaultKeys {
SyncTrustedVaultKeys();
~SyncTrustedVaultKeys();
std::vector<uint8_t> encryption_key;
int encryption_key_version = 0;
std::vector<std::vector<uint8_t>> trusted_public_keys;
};
FakeGaia();
virtual ~FakeGaia();
......@@ -105,6 +114,11 @@ class FakeGaia {
// an email address, a default GAIA Id is used.
void MapEmailToGaiaId(const std::string& email, const std::string& gaia_id);
// Adds sync trusted vault keys for |email|.
void SetSyncTrustedVaultKeys(
const std::string& email,
const SyncTrustedVaultKeys& sync_trusted_vault_keys);
// Initializes HTTP request handlers. Should be called after switches
// for tweaking GaiaUrls are in place.
void Initialize();
......@@ -191,6 +205,8 @@ class FakeGaia {
using EmailToGaiaIdMap = std::map<std::string, std::string>;
using SamlAccountIdpMap = std::map<std::string, GURL>;
using SamlDomainRedirectUrlMap = std::map<std::string, GURL>;
using EmailToSyncTrustedVaultKeysMap =
std::map<std::string, SyncTrustedVaultKeys>;
std::string GetGaiaIdOfEmail(const std::string& email) const;
......@@ -201,6 +217,10 @@ class FakeGaia {
void SetOAuthCodeCookie(
net::test_server::BasicHttpResponse* http_response) const;
void AddSyncTrustedKeysHeader(
net::test_server::BasicHttpResponse* http_response,
const std::string& email) const;
// Formats a JSON response with the data in |value|, setting the http status
// to |status|.
void FormatJSONResponse(const base::Value& value,
......@@ -306,6 +326,7 @@ class FakeGaia {
SamlDomainRedirectUrlMap saml_domain_url_map_;
bool issue_oauth_code_cookie_;
RefreshTokenToDeviceIdMap refresh_token_to_device_id_map_;
EmailToSyncTrustedVaultKeysMap email_to_sync_trusted_vault_keys_map_;
std::string prefilled_email_;
std::string is_supervised_;
std::string is_device_owner_;
......
......@@ -10,6 +10,8 @@ gaia.chromeOSLogin.parent_webview_ = undefined;
gaia.chromeOSLogin.parent_webview_url_ = undefined;
gaia.chromeOSLogin.initialized_ = false;
const urlParams = new URLSearchParams(window.location.search);
const syncTrustedVaultKeysRequestedByClient = !!urlParams.get('szkr');
function goFirstPage() {
console.error('On first page');
......@@ -131,6 +133,13 @@ function goNext() {
request.open('POST', '/_/embedded/signin/challenge', true);
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
if (request.getResponseHeader("fake-sync-trusted-vault-keys") &&
syncTrustedVaultKeysRequestedByClient) {
// TODO(crbug.com/1081651): Revisit if this message should be sent
// *after* sending userInfo.
sendSyncTrustedVaultKeys(JSON.parse(
request.getResponseHeader("fake-sync-trusted-vault-keys")));
}
gaia.chromeOSLogin.sendUserInfo(services);
history.pushState({}, "", window.location.pathname + "#close");
}
......@@ -184,6 +193,42 @@ function sendSetAllActionsEnabled(enabled) {
},
gaia.chromeOSLogin.parent_webview_url_);
}
function base64DecodeToArrayBuffer(encoded) {
// atob() decodes base64 strings into binary string.
var decoded = atob(encoded);
var buffer = new ArrayBuffer(decoded.length);
var view = new Uint8Array(buffer);
for (var i = 0; i < decoded.length; i++) {
view[i] = decoded.charCodeAt(i);
}
return buffer;
}
// FakeGaia sends the keys in a JSON format that requires some conversion before
// posting to authenticator.js, due to the use of binary blobs in the API.
function convertSyncTrustedVaultKeys(fakeKeys) {
var keys = {
encryptionKeys: [
{keyMaterial: base64DecodeToArrayBuffer(fakeKeys.fakeEncryptionKeyMaterial),
version: fakeKeys.fakeEncryptionKeyVersion}
],
trustedPublicKeys: []
};
for (var i = 0; i < fakeKeys.fakeTrustedPublicKeys.length; i++) {
keys.trustedPublicKeys.push(
{keyMaterial: base64DecodeToArrayBuffer(fakeKeys.fakeTrustedPublicKeys[i])});
}
return keys;
}
function sendSyncTrustedVaultKeys(fakeKeys) {
gaia.chromeOSLogin.parent_webview_.postMessage({
method: 'syncTrustedVaultKeys',
value: convertSyncTrustedVaultKeys(fakeKeys)
},
gaia.chromeOSLogin.parent_webview_url_);
}
</script>
</head>
<body onload='onLoad();'>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment