1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/prefs/tracked/pref_hash_calculator_helper.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/logging.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "chrome/browser/extensions/api/music_manager_private/device_id.h"
17 #include "content/public/test/test_browser_thread_bundle.h"
18 #include "content/public/test/test_utils.h"
19 #include "crypto/hmac.h"
20 #include "rlz/lib/machine_id.h"
21 #include "testing/gtest/include/gtest/gtest.h"
25 // extensions::api::DeviceId::GetDeviceId() signs the extension_id in
26 // GetRawDeviceIdCallback to get the final device_id, our code replicating this
27 // id needs to do the same thing.
28 const char kFakeExtensionId[] = "foo";
30 // Sets |media_device_id_out| to |media_device_id_in| and unblocks the original
31 // binder by calling |unblock_callback|.
32 void SetMediaDeviceIdAndUnblock(const base::Closure& unblock_callback,
33 std::string* media_device_id_out,
34 const std::string& media_device_id_in) {
35 if (media_device_id_in.empty())
36 LOG(WARNING) << "Media device ID is empty.";
37 *media_device_id_out = media_device_id_in;
38 unblock_callback.Run();
41 // Returns the device id from extensions::api::DeviceId::GetDeviceId()
43 std::string GetMediaDeviceIdSynchronously() {
44 std::string media_device_id;
45 const scoped_refptr<content::MessageLoopRunner> runner(
46 new content::MessageLoopRunner);
47 extensions::api::DeviceId::GetDeviceId(kFakeExtensionId,
48 base::Bind(&SetMediaDeviceIdAndUnblock,
49 runner->QuitClosure(),
50 base::Unretained(&media_device_id)));
51 // Run the message loop until SetMediaDeviceIdAndUnblock() calls the
52 // QuitClosure when done.
54 return media_device_id;
57 // Copy functionality from GetRawDeviceIdCallback() to get the final device id
58 // from the |raw_device_id|. Signing |kExtension| with an HMAC-SHA256 using
59 // |raw_device_id| as its key.
60 std::string GetDeviceIdFromRawDeviceId(const std::string& raw_device_id) {
61 crypto::HMAC hmac(crypto::HMAC::SHA256);
62 const size_t digest_length = hmac.DigestLength();
63 std::vector<uint8> digest(digest_length);
64 bool result = hmac.Init(raw_device_id) &&
65 hmac.Sign(kFakeExtensionId, &digest[0], digest.size());
70 return StringToLowerASCII(base::HexEncode(digest.data(), digest.size()));
73 std::string GetLegacyIdBasedOnRlzId() {
74 std::string rlz_machine_id;
75 rlz_lib::GetMachineId(&rlz_machine_id);
76 EXPECT_FALSE(rlz_machine_id.empty());
78 const std::string raw_legacy_device_id(GetLegacyDeviceId(rlz_machine_id));
80 if (raw_legacy_device_id.empty()) {
81 LOG(WARNING) << "Raw legacy device ID based on RLZ ID is empty.";
85 const std::string legacy_device_id(
86 GetDeviceIdFromRawDeviceId(raw_legacy_device_id));
87 EXPECT_FALSE(legacy_device_id.empty());
89 return legacy_device_id;
92 // Simulate browser threads (required by extensions::api::DeviceId) off of the
94 class PrefHashCalculatorHelperTest : public testing::Test {
96 content::TestBrowserThreadBundle test_browser_thread_bundle;
101 // The implementation for the legacy ID on Windows is copied from
102 // the M33 version of extensions::api::DeviceId::GetDeviceId(). We no longer
103 // depend on it as of M34, but should make sure that we are generating the same
104 // results in the mean time (it will be okay for the extension API's
105 // implementation to diverge on M34+ and this test can be removed once M34 ships
107 TEST_F(PrefHashCalculatorHelperTest, ResultMatchesMediaId) {
108 EXPECT_EQ(GetMediaDeviceIdSynchronously(), GetLegacyIdBasedOnRlzId());
111 TEST_F(PrefHashCalculatorHelperTest, MediaIdIsDeterministic) {
112 EXPECT_EQ(GetMediaDeviceIdSynchronously(), GetMediaDeviceIdSynchronously());
115 TEST_F(PrefHashCalculatorHelperTest, RlzBasedIdIsDeterministic) {
116 EXPECT_EQ(GetLegacyIdBasedOnRlzId(), GetLegacyIdBasedOnRlzId());