summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authoriclelland <iclelland@chromium.org>2016-02-02 07:00:36 -0800
committerCommit bot <commit-bot@chromium.org>2016-02-02 15:01:56 +0000
commit5b27fa016f2736e2186984e5f737e805c2836d9c (patch)
treef2c48ffafec4d3d376a4df3cb013c3059251ab83 /content
parente72c97cfbd4829b5fbec934e67460886be345882 (diff)
downloadchromium_src-5b27fa016f2736e2186984e5f737e805c2836d9c.zip
chromium_src-5b27fa016f2736e2186984e5f737e805c2836d9c.tar.gz
chromium_src-5b27fa016f2736e2186984e5f737e805c2836d9c.tar.bz2
Rename Experimental Framework classes and concepts
"Experiments" is far too generic a term to use for the trials enabled by the experimental framework, so this patch renames everything to (hopefully) more precise terms. These changes are most prominent: core::ExperimentalFeatures class => OriginTrials core::Experiments class => OriginTrialContext API Key => Trial Token (or "Token" where unambiguous) API => Feature /experiments/ directories => /origin_trials/ api_name (RuntimeEnabledFeatures) => origin_trial_feature_name BUG=575808 Review URL: https://codereview.chromium.org/1635593004 Cr-Commit-Position: refs/heads/master@{#372943}
Diffstat (limited to 'content')
-rw-r--r--content/child/blink_platform_impl.cc4
-rw-r--r--content/child/blink_platform_impl.h6
-rw-r--r--content/child/experiments/api_key_validator.cc27
-rw-r--r--content/child/experiments/api_key_validator.h33
-rw-r--r--content/child/origin_trials/trial_token_validator.cc24
-rw-r--r--content/child/origin_trials/trial_token_validator.h33
-rw-r--r--content/common/experiments/api_key.h95
-rw-r--r--content/common/experiments/api_key_unittest.cc191
-rw-r--r--content/common/origin_trials/trial_token.cc (renamed from content/common/experiments/api_key.cc)67
-rw-r--r--content/common/origin_trials/trial_token.h96
-rw-r--r--content/common/origin_trials/trial_token_unittest.cc192
-rw-r--r--content/content_child.gypi4
-rw-r--r--content/content_common.gypi4
-rw-r--r--content/content_tests.gypi2
14 files changed, 388 insertions, 390 deletions
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc
index d798e5c..7671d05 100644
--- a/content/child/blink_platform_impl.cc
+++ b/content/child/blink_platform_impl.cc
@@ -1062,8 +1062,8 @@ blink::WebSyncProvider* BlinkPlatformImpl::backgroundSyncProvider() {
main_thread_task_runner_.get());
}
-blink::WebApiKeyValidator* BlinkPlatformImpl::apiKeyValidator() {
- return &api_key_validator_;
+blink::WebTrialTokenValidator* BlinkPlatformImpl::trialTokenValidator() {
+ return &trial_token_validator_;
}
WebThemeEngine* BlinkPlatformImpl::themeEngine() {
diff --git a/content/child/blink_platform_impl.h b/content/child/blink_platform_impl.h
index 47787d5..1418689 100644
--- a/content/child/blink_platform_impl.h
+++ b/content/child/blink_platform_impl.h
@@ -15,7 +15,7 @@
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/webcrypto/webcrypto_impl.h"
-#include "content/child/experiments/api_key_validator.h"
+#include "content/child/origin_trials/trial_token_validator.h"
#include "content/child/webfallbackthemeengine_impl.h"
#include "content/common/content_export.h"
#include "third_party/WebKit/public/platform/Platform.h"
@@ -159,7 +159,7 @@ class CONTENT_EXPORT BlinkPlatformImpl
blink::WebServicePortProviderClient*) override;
blink::WebPermissionClient* permissionClient() override;
blink::WebSyncProvider* backgroundSyncProvider() override;
- blink::WebApiKeyValidator* apiKeyValidator() override;
+ blink::WebTrialTokenValidator* trialTokenValidator() override;
blink::WebString domCodeStringFromEnum(int dom_code) override;
int domEnumFromCodeString(const blink::WebString& codeString) override;
@@ -197,7 +197,7 @@ class CONTENT_EXPORT BlinkPlatformImpl
scoped_ptr<PermissionDispatcher> permission_client_;
scoped_ptr<BackgroundSyncProvider> main_thread_sync_provider_;
- ApiKeyValidator api_key_validator_;
+ TrialTokenValidator trial_token_validator_;
scheduler::WebThreadBase* compositor_thread_;
};
diff --git a/content/child/experiments/api_key_validator.cc b/content/child/experiments/api_key_validator.cc
deleted file mode 100644
index 3d87ecfb..0000000
--- a/content/child/experiments/api_key_validator.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2016 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 "content/child/experiments/api_key_validator.h"
-
-#include "base/time/time.h"
-#include "content/common/experiments/api_key.h"
-
-namespace content {
-
-ApiKeyValidator::ApiKeyValidator() {}
-ApiKeyValidator::~ApiKeyValidator() {}
-
-bool ApiKeyValidator::validateApiKey(const blink::WebString& apiKey,
- const blink::WebString& origin,
- const blink::WebString& apiName) {
- bool result = false;
- scoped_ptr<ApiKey> key = ApiKey::Parse(apiKey.utf8());
- if (key) {
- result = (key->IsAppropriate(origin.utf8(), apiName.utf8()) &&
- key->IsValid(base::Time::Now()));
- }
- return result;
-}
-
-} // namespace content
diff --git a/content/child/experiments/api_key_validator.h b/content/child/experiments/api_key_validator.h
deleted file mode 100644
index 1be8434..0000000
--- a/content/child/experiments/api_key_validator.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2016 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 CONTENT_CHILD_EXPERIMENTS_API_KEY_VALIDATOR_H_
-#define CONTENT_CHILD_EXPERIMENTS_API_KEY_VALIDATOR_H_
-
-#include <string>
-#include "third_party/WebKit/public/platform/WebApiKeyValidator.h"
-
-namespace content {
-
-// The ApiKeyValidator is called by the Experimental Framework code in Blink to
-// validate tokens to enable experimental APIs.
-//
-// This class is thread-safe.
-class ApiKeyValidator : public blink::WebApiKeyValidator {
- public:
- ApiKeyValidator();
- ~ApiKeyValidator() override;
-
- // blink::WebApiKeyValidator implementation
- bool validateApiKey(const blink::WebString& apiKey,
- const blink::WebString& origin,
- const blink::WebString& apiName) override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ApiKeyValidator);
-};
-
-} // namespace content
-
-#endif // CONTENT_CHILD_EXPERIMENTS_API_KEY_VALIDATOR_H_
diff --git a/content/child/origin_trials/trial_token_validator.cc b/content/child/origin_trials/trial_token_validator.cc
new file mode 100644
index 0000000..b1015c5
--- /dev/null
+++ b/content/child/origin_trials/trial_token_validator.cc
@@ -0,0 +1,24 @@
+// Copyright 2016 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 "content/child/origin_trials/trial_token_validator.h"
+
+#include "base/time/time.h"
+#include "content/common/origin_trials/trial_token.h"
+
+namespace content {
+
+TrialTokenValidator::TrialTokenValidator() {}
+TrialTokenValidator::~TrialTokenValidator() {}
+
+bool TrialTokenValidator::validateToken(const blink::WebString& token,
+ const blink::WebString& origin,
+ const blink::WebString& featureName) {
+ scoped_ptr<TrialToken> trialToken = TrialToken::Parse(token.utf8());
+ return trialToken &&
+ trialToken->IsAppropriate(origin.utf8(), featureName.utf8()) &&
+ trialToken->IsValid(base::Time::Now());
+}
+
+} // namespace content
diff --git a/content/child/origin_trials/trial_token_validator.h b/content/child/origin_trials/trial_token_validator.h
new file mode 100644
index 0000000..2013bc1
--- /dev/null
+++ b/content/child/origin_trials/trial_token_validator.h
@@ -0,0 +1,33 @@
+// Copyright 2016 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 CONTENT_CHILD_ORIGIN_TRIALS_TRIAL_TOKEN_VALIDATOR_H_
+#define CONTENT_CHILD_ORIGIN_TRIALS_TRIAL_TOKEN_VALIDATOR_H_
+
+#include <string>
+#include "third_party/WebKit/public/platform/WebTrialTokenValidator.h"
+
+namespace content {
+
+// The TrialTokenValidator is called by the Experimental Framework code in Blink
+// to validate tokens to enable experimental features.
+//
+// This class is thread-safe.
+class TrialTokenValidator : public blink::WebTrialTokenValidator {
+ public:
+ TrialTokenValidator();
+ ~TrialTokenValidator() override;
+
+ // blink::WebTrialTokenValidator implementation
+ bool validateToken(const blink::WebString& token,
+ const blink::WebString& origin,
+ const blink::WebString& featureName) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TrialTokenValidator);
+};
+
+} // namespace content
+
+#endif // CONTENT_CHILD_ORIGIN_TRIALS_TRIAL_TOKEN_VALIDATOR_H_
diff --git a/content/common/experiments/api_key.h b/content/common/experiments/api_key.h
deleted file mode 100644
index 1ad575a1..0000000
--- a/content/common/experiments/api_key.h
+++ /dev/null
@@ -1,95 +0,0 @@
-// 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 CONTENT_COMMON_EXPERIMENTS_API_KEY_H_
-#define CONTENT_COMMON_EXPERIMENTS_API_KEY_H_
-
-#include <string>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/strings/string_piece.h"
-#include "base/time/time.h"
-#include "content/common/content_export.h"
-#include "url/gurl.h"
-
-namespace content {
-
-// The Experimental Framework (EF) provides limited access to experimental APIs,
-// on a per-origin basis. This class defines the API key data structure, used
-// to securely provide access to an experimental API.
-//
-// Experimental APIs are defined by string names, provided by the implementers.
-// The EF code does not maintain an enum or constant list for experiment names.
-// Instead, the EF validates the name provided by the API implementation against
-// any provided API keys.
-//
-// More documentation on the key format can be found at
-// https://docs.google.com/document/d/1v5fi0EUV_QHckVHVF2K4P72iNywnrJtNhNZ6i2NPt0M
-
-class CONTENT_EXPORT ApiKey {
- public:
- ~ApiKey();
-
- // Returns a key object if the string represents a well-formed key, or
- // nullptr otherwise. (This does not mean that the key is valid, just that it
- // can be parsed.)
- static scoped_ptr<ApiKey> Parse(const std::string& key_text);
-
- // Returns true if this API is appropriate for use by the given origin, for
- // the given API name. This does not check whether the signature is valid, or
- // whether the key itself has expired.
- bool IsAppropriate(const std::string& origin,
- const std::string& apiName) const;
-
- // Returns true if this API key has a valid signature, and has not expired.
- bool IsValid(const base::Time& now) const;
-
- std::string signature() { return signature_; }
- std::string data() { return data_; }
- GURL origin() { return origin_; }
- std::string api_name() { return api_name_; }
- uint64_t expiry_timestamp() { return expiry_timestamp_; }
-
- protected:
- friend class ApiKeyTest;
-
- bool ValidateOrigin(const std::string& origin) const;
- bool ValidateApiName(const std::string& api_name) const;
- bool ValidateDate(const base::Time& now) const;
- bool ValidateSignature(const base::StringPiece& public_key) const;
-
- static bool ValidateSignature(const std::string& signature_text,
- const std::string& data,
- const base::StringPiece& public_key);
-
- private:
- ApiKey(const std::string& signature,
- const std::string& data,
- const GURL& origin,
- const std::string& api_name,
- uint64_t expiry_timestamp);
-
- // The base64-encoded-signature portion of the key. For the key to be valid,
- // this must be a valid signature for the data portion of the key, as verified
- // by the public key in api_key.cc.
- std::string signature_;
-
- // The portion of the key string which is signed, and whose signature is
- // contained in the signature_ member.
- std::string data_;
-
- // The origin for which this key is valid. Must be a secure origin.
- GURL origin_;
-
- // The name of the API experiment which this key enables.
- std::string api_name_;
-
- // The time until which this key should be considered valid, in UTC, as
- // seconds since the Unix epoch.
- uint64_t expiry_timestamp_;
-};
-
-} // namespace content
-
-#endif // CONTENT_COMMON_EXPERIMENTS_API_KEY_H_
diff --git a/content/common/experiments/api_key_unittest.cc b/content/common/experiments/api_key_unittest.cc
deleted file mode 100644
index a70ffcc..0000000
--- a/content/common/experiments/api_key_unittest.cc
+++ /dev/null
@@ -1,191 +0,0 @@
-// 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 "content/common/experiments/api_key.h"
-
-#include "base/macros.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/strings/string_util.h"
-#include "base/test/simple_test_clock.h"
-#include "base/time/time.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace content {
-
-namespace {
-
-// This is a sample public key for testing the API. The corresponding private
-// key (use this to generate new samples for this test file) is:
-//
-// 0x83, 0x67, 0xf4, 0xcd, 0x2a, 0x1f, 0x0e, 0x04, 0x0d, 0x43, 0x13,
-// 0x4c, 0x67, 0xc4, 0xf4, 0x28, 0xc9, 0x90, 0x15, 0x02, 0xe2, 0xba,
-// 0xfd, 0xbb, 0xfa, 0xbc, 0x92, 0x76, 0x8a, 0x2c, 0x4b, 0xc7, 0x75,
-// 0x10, 0xac, 0xf9, 0x3a, 0x1c, 0xb8, 0xa9, 0x28, 0x70, 0xd2, 0x9a,
-// 0xd0, 0x0b, 0x59, 0xe1, 0xac, 0x2b, 0xb7, 0xd5, 0xca, 0x1f, 0x64,
-// 0x90, 0x08, 0x8e, 0xa8, 0xe0, 0x56, 0x3a, 0x04, 0xd0
-const uint8_t kTestPublicKey[] = {
- 0x75, 0x10, 0xac, 0xf9, 0x3a, 0x1c, 0xb8, 0xa9, 0x28, 0x70, 0xd2,
- 0x9a, 0xd0, 0x0b, 0x59, 0xe1, 0xac, 0x2b, 0xb7, 0xd5, 0xca, 0x1f,
- 0x64, 0x90, 0x08, 0x8e, 0xa8, 0xe0, 0x56, 0x3a, 0x04, 0xd0,
-};
-
-// This is a good key, signed with the above test private key.
-const char* kSampleAPIKey =
- "UsEO0cNxoUtBnHDJdGPWTlXuLENjXcEIPL7Bs7sbvicPCcvAtyqhQuTJ9h/u1R3VZpWigtI+S"
- "dUwk7Dyk/qbDw==|https://valid.example.com|Frobulate|1458766277";
-const char* kExpectedAPIKeySignature =
- "UsEO0cNxoUtBnHDJdGPWTlXuLENjXcEIPL7Bs7sbvicPCcvAtyqhQuTJ9h/u1R3VZpWigtI+S"
- "dUwk7Dyk/qbDw==";
-const char* kExpectedAPIKeyData =
- "https://valid.example.com|Frobulate|1458766277";
-const char* kExpectedAPIName = "Frobulate";
-const char* kExpectedOrigin = "https://valid.example.com";
-const uint64_t kExpectedExpiry = 1458766277;
-
-// The key should not be valid for this origin, or for this API.
-const char* kInvalidOrigin = "https://invalid.example.com";
-const char* kInsecureOrigin = "http://valid.example.com";
-const char* kInvalidAPIName = "Grokalyze";
-
-// The key should be valid if the current time is kValidTimestamp or earlier.
-double kValidTimestamp = 1458766276.0;
-
-// The key should be invalid if the current time is kInvalidTimestamp or later.
-double kInvalidTimestamp = 1458766278.0;
-
-// Well-formed API key with an invalid signature.
-const char* kInvalidSignatureAPIKey =
- "CO8hDne98QeFeOJ0DbRZCBN3uE0nyaPgaLlkYhSWnbRoDfEAg+TXELaYfQPfEvKYFauBg/hnx"
- "mba765hz0mXMc==|https://valid.example.com|Frobulate|1458766277";
-
-// Various ill-formed API keys. These should all fail to parse.
-const char* kInvalidAPIKeys[] = {
- // Invalid - only one part
- "abcde",
- // Not enough parts
- "https://valid.example.com|APIName|1458766277",
- // Delimiter in API Name
- "Signature|https://valid.example.com|API|Name|1458766277",
- // Extra string field
- "Signature|https://valid.example.com|APIName|1458766277|SomethingElse",
- // Extra numeric field
- "Signature|https://valid.example.com|APIName|1458766277|1458766277",
- // Non-numeric expiry timestamp
- "Signature|https://valid.example.com|APIName|abcdefghij",
- "Signature|https://valid.example.com|APIName|1458766277x",
- // Negative expiry timestamp
- "Signature|https://valid.example.com|APIName|-1458766277",
- // Origin not a proper origin URL
- "Signature|abcdef|APIName|1458766277",
- "Signature|data:text/plain,abcdef|APIName|1458766277",
- "Signature|javascript:alert(1)|APIName|1458766277"};
-const size_t kNumInvalidAPIKeys = arraysize(kInvalidAPIKeys);
-
-} // namespace
-
-class ApiKeyTest : public testing::Test {
- public:
- ApiKeyTest()
- : public_key_(
- base::StringPiece(reinterpret_cast<const char*>(kTestPublicKey),
- arraysize(kTestPublicKey))) {}
-
- protected:
- bool ValidateOrigin(ApiKey* api_key, const char* origin) {
- return api_key->ValidateOrigin(origin);
- }
-
- bool ValidateApiName(ApiKey* api_key, const char* api_name) {
- return api_key->ValidateApiName(api_name);
- }
-
- bool ValidateDate(ApiKey* api_key, const base::Time& now) {
- return api_key->ValidateDate(now);
- }
-
- bool ValidateSignature(ApiKey* api_key, const base::StringPiece& public_key) {
- return api_key->ValidateSignature(public_key);
- }
-
- const base::StringPiece& public_key() { return public_key_; };
-
- private:
- base::StringPiece public_key_;
-};
-
-TEST_F(ApiKeyTest, ParseEmptyString) {
- scoped_ptr<ApiKey> empty_key = ApiKey::Parse("");
- EXPECT_FALSE(empty_key);
-}
-
-TEST_F(ApiKeyTest, ParseInvalidStrings) {
- for (size_t i = 0; i < kNumInvalidAPIKeys; ++i) {
- scoped_ptr<ApiKey> empty_key = ApiKey::Parse(kInvalidAPIKeys[i]);
- EXPECT_FALSE(empty_key) << "Invalid API Key should not parse: "
- << kInvalidAPIKeys[i];
- }
-}
-
-TEST_F(ApiKeyTest, ParseValidKeyString) {
- scoped_ptr<ApiKey> key = ApiKey::Parse(kSampleAPIKey);
- ASSERT_TRUE(key);
- EXPECT_EQ(kExpectedAPIName, key->api_name());
- EXPECT_EQ(kExpectedAPIKeySignature, key->signature());
- EXPECT_EQ(kExpectedAPIKeyData, key->data());
- EXPECT_EQ(GURL(kExpectedOrigin), key->origin());
- EXPECT_EQ(kExpectedExpiry, key->expiry_timestamp());
-}
-
-TEST_F(ApiKeyTest, ValidateValidKey) {
- scoped_ptr<ApiKey> key = ApiKey::Parse(kSampleAPIKey);
- ASSERT_TRUE(key);
- EXPECT_TRUE(ValidateOrigin(key.get(), kExpectedOrigin));
- EXPECT_FALSE(ValidateOrigin(key.get(), kInvalidOrigin));
- EXPECT_FALSE(ValidateOrigin(key.get(), kInsecureOrigin));
- EXPECT_TRUE(ValidateApiName(key.get(), kExpectedAPIName));
- EXPECT_FALSE(ValidateApiName(key.get(), kInvalidAPIName));
- EXPECT_TRUE(
- ValidateDate(key.get(), base::Time::FromDoubleT(kValidTimestamp)));
- EXPECT_FALSE(
- ValidateDate(key.get(), base::Time::FromDoubleT(kInvalidTimestamp)));
-}
-
-TEST_F(ApiKeyTest, KeyIsAppropriateForOriginAndAPI) {
- scoped_ptr<ApiKey> key = ApiKey::Parse(kSampleAPIKey);
- ASSERT_TRUE(key);
- EXPECT_TRUE(key->IsAppropriate(kExpectedOrigin, kExpectedAPIName));
- EXPECT_TRUE(key->IsAppropriate(kExpectedOrigin,
- base::ToUpperASCII(kExpectedAPIName)));
- EXPECT_TRUE(key->IsAppropriate(kExpectedOrigin,
- base::ToLowerASCII(kExpectedAPIName)));
- EXPECT_FALSE(key->IsAppropriate(kInvalidOrigin, kExpectedAPIName));
- EXPECT_FALSE(key->IsAppropriate(kInsecureOrigin, kExpectedAPIName));
- EXPECT_FALSE(key->IsAppropriate(kExpectedOrigin, kInvalidAPIName));
-}
-
-TEST_F(ApiKeyTest, ValidateValidSignature) {
- scoped_ptr<ApiKey> key = ApiKey::Parse(kSampleAPIKey);
- ASSERT_TRUE(key);
- EXPECT_TRUE(ValidateSignature(key.get(), public_key()));
-}
-
-TEST_F(ApiKeyTest, ValidateInvalidSignature) {
- scoped_ptr<ApiKey> key = ApiKey::Parse(kInvalidSignatureAPIKey);
- ASSERT_TRUE(key);
- EXPECT_FALSE(ValidateSignature(key.get(), public_key()));
-}
-
-TEST_F(ApiKeyTest, ValidateSignatureOnWrongKey) {
- scoped_ptr<ApiKey> key = ApiKey::Parse(kSampleAPIKey);
- ASSERT_TRUE(key);
- // Signature will be invalid if tested against the real public key
- EXPECT_FALSE(key->IsValid(base::Time::FromDoubleT(kValidTimestamp)));
-}
-
-TEST_F(ApiKeyTest, ValidateWhenNotExpired) {
- scoped_ptr<ApiKey> key = ApiKey::Parse(kSampleAPIKey);
- ASSERT_TRUE(key);
-}
-
-} // namespace content
diff --git a/content/common/experiments/api_key.cc b/content/common/origin_trials/trial_token.cc
index 9199d5d..5c64bcb 100644
--- a/content/common/experiments/api_key.cc
+++ b/content/common/origin_trials/trial_token.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/common/experiments/api_key.h"
+#include "content/common/origin_trials/trial_token.h"
#include <openssl/curve25519.h>
@@ -30,31 +30,30 @@ static const uint8_t kPublicKey[] = {
0x51, 0x14, 0x66, 0xaa, 0x02, 0x53, 0x4e, 0x33, 0xa1, 0x15,
};
-const char* kApiKeyFieldSeparator = "|";
+const char* kFieldSeparator = "|";
} // namespace
-ApiKey::~ApiKey() {}
+TrialToken::~TrialToken() {}
-scoped_ptr<ApiKey> ApiKey::Parse(const std::string& key_text) {
- if (key_text.empty()) {
+scoped_ptr<TrialToken> TrialToken::Parse(const std::string& token_text) {
+ if (token_text.empty()) {
return nullptr;
}
- // API Key should resemble:
- // signature|origin|api_name|expiry_timestamp
- // TODO(iclelland): Add version code to API key format to identify key algo
+ // A valid token should resemble:
+ // signature|origin|feature_name|expiry_timestamp
+ // TODO(iclelland): Add version code to token format to identify key algo
// https://crbug.com/570684
- std::vector<std::string> parts =
- SplitString(key_text, kApiKeyFieldSeparator, base::KEEP_WHITESPACE,
- base::SPLIT_WANT_ALL);
+ std::vector<std::string> parts = SplitString(
+ token_text, kFieldSeparator, base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
if (parts.size() != 4) {
return nullptr;
}
const std::string& signature = parts[0];
const std::string& origin_string = parts[1];
- const std::string& api_name = parts[2];
+ const std::string& feature_name = parts[2];
const std::string& expiry_string = parts[3];
uint64_t expiry_timestamp;
@@ -68,30 +67,30 @@ scoped_ptr<ApiKey> ApiKey::Parse(const std::string& key_text) {
return nullptr;
}
- // signed data is (origin + "|" + api_name + "|" + expiry).
- std::string data = key_text.substr(signature.length() + 1);
+ // signed data is (origin + "|" + feature_name + "|" + expiry).
+ std::string data = token_text.substr(signature.length() + 1);
- return make_scoped_ptr(
- new ApiKey(signature, data, origin_url, api_name, expiry_timestamp));
+ return make_scoped_ptr(new TrialToken(signature, data, origin_url,
+ feature_name, expiry_timestamp));
}
-ApiKey::ApiKey(const std::string& signature,
- const std::string& data,
- const GURL& origin,
- const std::string& api_name,
- uint64_t expiry_timestamp)
+TrialToken::TrialToken(const std::string& signature,
+ const std::string& data,
+ const GURL& origin,
+ const std::string& feature_name,
+ uint64_t expiry_timestamp)
: signature_(signature),
data_(data),
origin_(origin),
- api_name_(api_name),
+ feature_name_(feature_name),
expiry_timestamp_(expiry_timestamp) {}
-bool ApiKey::IsAppropriate(const std::string& origin,
- const std::string& api_name) const {
- return ValidateOrigin(origin) && ValidateApiName(api_name);
+bool TrialToken::IsAppropriate(const std::string& origin,
+ const std::string& feature_name) const {
+ return ValidateOrigin(origin) && ValidateFeatureName(feature_name);
}
-bool ApiKey::IsValid(const base::Time& now) const {
+bool TrialToken::IsValid(const base::Time& now) const {
// TODO(iclelland): Allow for multiple signing keys, and iterate over all
// active keys here. https://crbug.com/543220
return ValidateDate(now) &&
@@ -99,27 +98,27 @@ bool ApiKey::IsValid(const base::Time& now) const {
reinterpret_cast<const char*>(kPublicKey), arraysize(kPublicKey)));
}
-bool ApiKey::ValidateOrigin(const std::string& origin) const {
+bool TrialToken::ValidateOrigin(const std::string& origin) const {
return GURL(origin) == origin_;
}
-bool ApiKey::ValidateApiName(const std::string& api_name) const {
- return base::EqualsCaseInsensitiveASCII(api_name, api_name_);
+bool TrialToken::ValidateFeatureName(const std::string& feature_name) const {
+ return base::EqualsCaseInsensitiveASCII(feature_name, feature_name_);
}
-bool ApiKey::ValidateDate(const base::Time& now) const {
+bool TrialToken::ValidateDate(const base::Time& now) const {
base::Time expiry_time = base::Time::FromDoubleT((double)expiry_timestamp_);
return expiry_time > now;
}
-bool ApiKey::ValidateSignature(const base::StringPiece& public_key) const {
+bool TrialToken::ValidateSignature(const base::StringPiece& public_key) const {
return ValidateSignature(signature_, data_, public_key);
}
// static
-bool ApiKey::ValidateSignature(const std::string& signature_text,
- const std::string& data,
- const base::StringPiece& public_key) {
+bool TrialToken::ValidateSignature(const std::string& signature_text,
+ const std::string& data,
+ const base::StringPiece& public_key) {
// Public key must be 32 bytes long for Ed25519.
CHECK_EQ(public_key.length(), 32UL);
diff --git a/content/common/origin_trials/trial_token.h b/content/common/origin_trials/trial_token.h
new file mode 100644
index 0000000..bdb964b
--- /dev/null
+++ b/content/common/origin_trials/trial_token.h
@@ -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.
+
+#ifndef CONTENT_COMMON_ORIGIN_TRIALS_TRIAL_TOKEN_H_
+#define CONTENT_COMMON_ORIGIN_TRIALS_TRIAL_TOKEN_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "content/common/content_export.h"
+#include "url/gurl.h"
+
+namespace content {
+
+// The Experimental Framework (EF) provides limited access to experimental
+// features, on a per-origin basis (origin trials). This class defines the trial
+// token data structure, used to securely provide access to an experimental
+// feature.
+//
+// Features are defined by string names, provided by the implementers. The EF
+// code does not maintain an enum or constant list for feature names. Instead,
+// the EF validates the name provided by the feature implementation against any
+// provided tokens.
+//
+// More documentation on the token format can be found at
+// https://docs.google.com/document/d/1v5fi0EUV_QHckVHVF2K4P72iNywnrJtNhNZ6i2NPt0M
+
+class CONTENT_EXPORT TrialToken {
+ public:
+ ~TrialToken();
+
+ // Returns a token object if the string represents a well-formed token, or
+ // nullptr otherwise. (This does not mean that the token is valid, just that
+ // it can be parsed.)
+ static scoped_ptr<TrialToken> Parse(const std::string& token_text);
+
+ // Returns true if this feature is appropriate for use by the given origin,
+ // for the given feature name. This does not check whether the signature is
+ // valid, or whether the token itself has expired.
+ bool IsAppropriate(const std::string& origin,
+ const std::string& featureName) const;
+
+ // Returns true if this token has a valid signature, and has not expired.
+ bool IsValid(const base::Time& now) const;
+
+ std::string signature() { return signature_; }
+ std::string data() { return data_; }
+ GURL origin() { return origin_; }
+ std::string feature_name() { return feature_name_; }
+ uint64_t expiry_timestamp() { return expiry_timestamp_; }
+
+ protected:
+ friend class TrialTokenTest;
+
+ bool ValidateOrigin(const std::string& origin) const;
+ bool ValidateFeatureName(const std::string& feature_name) const;
+ bool ValidateDate(const base::Time& now) const;
+ bool ValidateSignature(const base::StringPiece& public_key) const;
+
+ static bool ValidateSignature(const std::string& signature_text,
+ const std::string& data,
+ const base::StringPiece& public_key);
+
+ private:
+ TrialToken(const std::string& signature,
+ const std::string& data,
+ const GURL& origin,
+ const std::string& feature_name,
+ uint64_t expiry_timestamp);
+
+ // The base64-encoded-signature portion of the token. For the token to be
+ // valid, this must be a valid signature for the data portion of the token, as
+ // verified by the public key in trial_token.cc.
+ std::string signature_;
+
+ // The portion of the token string which is signed, and whose signature is
+ // contained in the signature_ member.
+ std::string data_;
+
+ // The origin for which this token is valid. Must be a secure origin.
+ GURL origin_;
+
+ // The name of the experimental feature which this token enables.
+ std::string feature_name_;
+
+ // The time until which this token should be considered valid, in UTC, as
+ // seconds since the Unix epoch.
+ uint64_t expiry_timestamp_;
+};
+
+} // namespace content
+
+#endif // CONTENT_COMMON_ORIGIN_TRIALS_TRIAL_TOKEN_H_
diff --git a/content/common/origin_trials/trial_token_unittest.cc b/content/common/origin_trials/trial_token_unittest.cc
new file mode 100644
index 0000000..c4426fd
--- /dev/null
+++ b/content/common/origin_trials/trial_token_unittest.cc
@@ -0,0 +1,192 @@
+// 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 "content/common/origin_trials/trial_token.h"
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+#include "base/test/simple_test_clock.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+namespace {
+
+// This is a sample public key for testing the API. The corresponding private
+// key (use this to generate new samples for this test file) is:
+//
+// 0x83, 0x67, 0xf4, 0xcd, 0x2a, 0x1f, 0x0e, 0x04, 0x0d, 0x43, 0x13,
+// 0x4c, 0x67, 0xc4, 0xf4, 0x28, 0xc9, 0x90, 0x15, 0x02, 0xe2, 0xba,
+// 0xfd, 0xbb, 0xfa, 0xbc, 0x92, 0x76, 0x8a, 0x2c, 0x4b, 0xc7, 0x75,
+// 0x10, 0xac, 0xf9, 0x3a, 0x1c, 0xb8, 0xa9, 0x28, 0x70, 0xd2, 0x9a,
+// 0xd0, 0x0b, 0x59, 0xe1, 0xac, 0x2b, 0xb7, 0xd5, 0xca, 0x1f, 0x64,
+// 0x90, 0x08, 0x8e, 0xa8, 0xe0, 0x56, 0x3a, 0x04, 0xd0
+const uint8_t kTestPublicKey[] = {
+ 0x75, 0x10, 0xac, 0xf9, 0x3a, 0x1c, 0xb8, 0xa9, 0x28, 0x70, 0xd2,
+ 0x9a, 0xd0, 0x0b, 0x59, 0xe1, 0xac, 0x2b, 0xb7, 0xd5, 0xca, 0x1f,
+ 0x64, 0x90, 0x08, 0x8e, 0xa8, 0xe0, 0x56, 0x3a, 0x04, 0xd0,
+};
+
+// This is a good trial token, signed with the above test private key.
+const char* kSampleToken =
+ "UsEO0cNxoUtBnHDJdGPWTlXuLENjXcEIPL7Bs7sbvicPCcvAtyqhQuTJ9h/u1R3VZpWigtI+S"
+ "dUwk7Dyk/qbDw==|https://valid.example.com|Frobulate|1458766277";
+const char* kExpectedSignature =
+ "UsEO0cNxoUtBnHDJdGPWTlXuLENjXcEIPL7Bs7sbvicPCcvAtyqhQuTJ9h/u1R3VZpWigtI+S"
+ "dUwk7Dyk/qbDw==";
+const char* kExpectedData = "https://valid.example.com|Frobulate|1458766277";
+const char* kExpectedFeatureName = "Frobulate";
+const char* kExpectedOrigin = "https://valid.example.com";
+const uint64_t kExpectedExpiry = 1458766277;
+
+// The token should not be valid for this origin, or for this feature.
+const char* kInvalidOrigin = "https://invalid.example.com";
+const char* kInsecureOrigin = "http://valid.example.com";
+const char* kInvalidFeatureName = "Grokalyze";
+
+// The token should be valid if the current time is kValidTimestamp or earlier.
+double kValidTimestamp = 1458766276.0;
+
+// The token should be invalid if the current time is kInvalidTimestamp or
+// later.
+double kInvalidTimestamp = 1458766278.0;
+
+// Well-formed trial token with an invalid signature.
+const char* kInvalidSignatureToken =
+ "CO8hDne98QeFeOJ0DbRZCBN3uE0nyaPgaLlkYhSWnbRoDfEAg+TXELaYfQPfEvKYFauBg/hnx"
+ "mba765hz0mXMc==|https://valid.example.com|Frobulate|1458766277";
+
+// Various ill-formed trial tokens. These should all fail to parse.
+const char* kInvalidTokens[] = {
+ // Invalid - only one part
+ "abcde",
+ // Not enough parts
+ "https://valid.example.com|FeatuerName|1458766277",
+ // Delimiter in feature name
+ "Signature|https://valid.example.com|Feature|Name|1458766277",
+ // Extra string field
+ "Signature|https://valid.example.com|FeatureName|1458766277|SomethingElse",
+ // Extra numeric field
+ "Signature|https://valid.example.com|FeatureName|1458766277|1458766277",
+ // Non-numeric expiry timestamp
+ "Signature|https://valid.example.com|FeatureName|abcdefghij",
+ "Signature|https://valid.example.com|FeatureName|1458766277x",
+ // Negative expiry timestamp
+ "Signature|https://valid.example.com|FeatureName|-1458766277",
+ // Origin not a proper origin URL
+ "Signature|abcdef|FeatureName|1458766277",
+ "Signature|data:text/plain,abcdef|FeatureName|1458766277",
+ "Signature|javascript:alert(1)|FeatureName|1458766277"};
+const size_t kNumInvalidTokens = arraysize(kInvalidTokens);
+
+} // namespace
+
+class TrialTokenTest : public testing::Test {
+ public:
+ TrialTokenTest()
+ : public_key_(
+ base::StringPiece(reinterpret_cast<const char*>(kTestPublicKey),
+ arraysize(kTestPublicKey))) {}
+
+ protected:
+ bool ValidateOrigin(TrialToken* token, const char* origin) {
+ return token->ValidateOrigin(origin);
+ }
+
+ bool ValidateFeatureName(TrialToken* token, const char* feature_name) {
+ return token->ValidateFeatureName(feature_name);
+ }
+
+ bool ValidateDate(TrialToken* token, const base::Time& now) {
+ return token->ValidateDate(now);
+ }
+
+ bool ValidateSignature(TrialToken* token,
+ const base::StringPiece& public_key) {
+ return token->ValidateSignature(public_key);
+ }
+
+ const base::StringPiece& public_key() { return public_key_; };
+
+ private:
+ base::StringPiece public_key_;
+};
+
+TEST_F(TrialTokenTest, ParseEmptyString) {
+ scoped_ptr<TrialToken> empty_token = TrialToken::Parse("");
+ EXPECT_FALSE(empty_token);
+}
+
+TEST_F(TrialTokenTest, ParseInvalidStrings) {
+ for (size_t i = 0; i < kNumInvalidTokens; ++i) {
+ scoped_ptr<TrialToken> empty_token = TrialToken::Parse(kInvalidTokens[i]);
+ EXPECT_FALSE(empty_token) << "Invalid trial token should not parse: "
+ << kInvalidTokens[i];
+ }
+}
+
+TEST_F(TrialTokenTest, ParseValidToken) {
+ scoped_ptr<TrialToken> token = TrialToken::Parse(kSampleToken);
+ ASSERT_TRUE(token);
+ EXPECT_EQ(kExpectedFeatureName, token->feature_name());
+ EXPECT_EQ(kExpectedSignature, token->signature());
+ EXPECT_EQ(kExpectedData, token->data());
+ EXPECT_EQ(GURL(kExpectedOrigin), token->origin());
+ EXPECT_EQ(kExpectedExpiry, token->expiry_timestamp());
+}
+
+TEST_F(TrialTokenTest, ValidateValidToken) {
+ scoped_ptr<TrialToken> token = TrialToken::Parse(kSampleToken);
+ ASSERT_TRUE(token);
+ EXPECT_TRUE(ValidateOrigin(token.get(), kExpectedOrigin));
+ EXPECT_FALSE(ValidateOrigin(token.get(), kInvalidOrigin));
+ EXPECT_FALSE(ValidateOrigin(token.get(), kInsecureOrigin));
+ EXPECT_TRUE(ValidateFeatureName(token.get(), kExpectedFeatureName));
+ EXPECT_FALSE(ValidateFeatureName(token.get(), kInvalidFeatureName));
+ EXPECT_TRUE(
+ ValidateDate(token.get(), base::Time::FromDoubleT(kValidTimestamp)));
+ EXPECT_FALSE(
+ ValidateDate(token.get(), base::Time::FromDoubleT(kInvalidTimestamp)));
+}
+
+TEST_F(TrialTokenTest, TokenIsAppropriateForOriginAndFeature) {
+ scoped_ptr<TrialToken> token = TrialToken::Parse(kSampleToken);
+ ASSERT_TRUE(token);
+ EXPECT_TRUE(token->IsAppropriate(kExpectedOrigin, kExpectedFeatureName));
+ EXPECT_TRUE(token->IsAppropriate(kExpectedOrigin,
+ base::ToUpperASCII(kExpectedFeatureName)));
+ EXPECT_TRUE(token->IsAppropriate(kExpectedOrigin,
+ base::ToLowerASCII(kExpectedFeatureName)));
+ EXPECT_FALSE(token->IsAppropriate(kInvalidOrigin, kExpectedFeatureName));
+ EXPECT_FALSE(token->IsAppropriate(kInsecureOrigin, kExpectedFeatureName));
+ EXPECT_FALSE(token->IsAppropriate(kExpectedOrigin, kInvalidFeatureName));
+}
+
+TEST_F(TrialTokenTest, ValidateValidSignature) {
+ scoped_ptr<TrialToken> token = TrialToken::Parse(kSampleToken);
+ ASSERT_TRUE(token);
+ EXPECT_TRUE(ValidateSignature(token.get(), public_key()));
+}
+
+TEST_F(TrialTokenTest, ValidateInvalidSignature) {
+ scoped_ptr<TrialToken> token = TrialToken::Parse(kInvalidSignatureToken);
+ ASSERT_TRUE(token);
+ EXPECT_FALSE(ValidateSignature(token.get(), public_key()));
+}
+
+TEST_F(TrialTokenTest, ValidateSignatureOnWrongKey) {
+ scoped_ptr<TrialToken> token = TrialToken::Parse(kSampleToken);
+ ASSERT_TRUE(token);
+ // Signature will be invalid if tested against the real public key
+ EXPECT_FALSE(token->IsValid(base::Time::FromDoubleT(kValidTimestamp)));
+}
+
+TEST_F(TrialTokenTest, ValidateWhenNotExpired) {
+ scoped_ptr<TrialToken> token = TrialToken::Parse(kSampleToken);
+ ASSERT_TRUE(token);
+}
+
+} // namespace content
diff --git a/content/content_child.gypi b/content/content_child.gypi
index 62d84ec..4718a75 100644
--- a/content/content_child.gypi
+++ b/content/content_child.gypi
@@ -86,8 +86,6 @@
'child/dwrite_font_proxy/dwrite_font_proxy_win.h',
'child/dwrite_font_proxy/dwrite_localized_strings_win.cc',
'child/dwrite_font_proxy/dwrite_localized_strings_win.h',
- 'child/experiments/api_key_validator.cc',
- 'child/experiments/api_key_validator.h',
'child/file_info_util.cc',
'child/file_info_util.h',
'child/fileapi/file_system_dispatcher.cc',
@@ -175,6 +173,8 @@
'child/npapi/webplugin_ime_win.cc',
'child/npapi/webplugin_ime_win.h',
'child/npapi/webplugin_resource_client.h',
+ 'child/origin_trials/trial_token_validator.cc',
+ 'child/origin_trials/trial_token_validator.h',
'child/permissions/permission_dispatcher.cc',
'child/permissions/permission_dispatcher.h',
'child/permissions/permission_dispatcher_thread_proxy.cc',
diff --git a/content/content_common.gypi b/content/content_common.gypi
index 2330466..0c04506 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -257,8 +257,6 @@
'common/dwrite_font_platform_win.cc',
'common/dwrite_font_proxy_messages.h',
'common/edit_command.h',
- 'common/experiments/api_key.cc',
- 'common/experiments/api_key.h',
'common/file_utilities_messages.h',
'common/fileapi/file_system_messages.h',
'common/fileapi/webblob_messages.h',
@@ -469,6 +467,8 @@
'common/notification_constants.h',
'common/one_writer_seqlock.cc',
'common/one_writer_seqlock.h',
+ 'common/origin_trials/trial_token.cc',
+ 'common/origin_trials/trial_token.h',
'common/origin_util.cc',
'common/p2p_messages.h',
'common/page_state_serialization.cc',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 848231a..8cf840a 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -658,7 +658,6 @@
'common/discardable_shared_memory_heap_unittest.cc',
'common/dom_storage/dom_storage_map_unittest.cc',
'common/dwrite_font_platform_win_unittest.cc',
- 'common/experiments/api_key_unittest.cc',
'common/fileapi/file_system_util_unittest.cc',
'common/gpu/client/gpu_memory_buffer_impl_shared_memory_unittest.cc',
'common/gpu/gpu_channel_manager_unittest.cc',
@@ -678,6 +677,7 @@
'common/mac/attributed_string_coder_unittest.mm',
'common/mac/font_descriptor_unittest.mm',
'common/one_writer_seqlock_unittest.cc',
+ 'common/origin_trials/trial_token_unittest.cc',
'common/origin_util_unittest.cc',
'common/page_state_serialization_unittest.cc',
'common/page_zoom_unittest.cc',