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. 4 5 #include "chrome/browser/prefs/tracked/pref_hash_calculator_helper.h" 6 7 #include <string> 8 #include <vector> 9 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" 22 23 namespace { 24 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"; 29 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(); 39 } 40 41 // Returns the device id from extensions::api::DeviceId::GetDeviceId() 42 // synchronously. 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. 53 runner->Run(); 54 return media_device_id; 55 } 56 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()); 66 if (!result) { 67 ADD_FAILURE(); 68 return std::string(); 69 } 70 return StringToLowerASCII(base::HexEncode(digest.data(), digest.size())); 71 } 72 73 std::string GetLegacyIdBasedOnRlzId() { 74 std::string rlz_machine_id; 75 rlz_lib::GetMachineId(&rlz_machine_id); 76 EXPECT_FALSE(rlz_machine_id.empty()); 77 78 const std::string raw_legacy_device_id(GetLegacyDeviceId(rlz_machine_id)); 79 80 if (raw_legacy_device_id.empty()) { 81 LOG(WARNING) << "Raw legacy device ID based on RLZ ID is empty."; 82 return std::string(); 83 } 84 85 const std::string legacy_device_id( 86 GetDeviceIdFromRawDeviceId(raw_legacy_device_id)); 87 EXPECT_FALSE(legacy_device_id.empty()); 88 89 return legacy_device_id; 90 } 91 92 // Simulate browser threads (required by extensions::api::DeviceId) off of the 93 // main message loop. 94 class PrefHashCalculatorHelperTest : public testing::Test { 95 private: 96 content::TestBrowserThreadBundle test_browser_thread_bundle; 97 }; 98 99 } // namespace 100 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 106 // to stable). 107 TEST_F(PrefHashCalculatorHelperTest, ResultMatchesMediaId) { 108 EXPECT_EQ(GetMediaDeviceIdSynchronously(), GetLegacyIdBasedOnRlzId()); 109 } 110 111 TEST_F(PrefHashCalculatorHelperTest, MediaIdIsDeterministic) { 112 EXPECT_EQ(GetMediaDeviceIdSynchronously(), GetMediaDeviceIdSynchronously()); 113 } 114 115 TEST_F(PrefHashCalculatorHelperTest, RlzBasedIdIsDeterministic) { 116 EXPECT_EQ(GetLegacyIdBasedOnRlzId(), GetLegacyIdBasedOnRlzId()); 117 } 118