1// Copyright 2012 The Chromium Authors 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifdef UNSAFE_BUFFERS_BUILD 6// TODO(crbug.com/40284755): Remove this and spanify to fix the errors. 7#pragma allow_unsafe_buffers 8#endif 9 10#include "base/ios/device_util.h" 11 12#include <CommonCrypto/CommonDigest.h> 13#import <UIKit/UIKit.h> 14#include <ifaddrs.h> 15#include <net/if_dl.h> 16#include <stddef.h> 17#include <string.h> 18#include <sys/socket.h> 19#include <sys/sysctl.h> 20 21#include <memory> 22 23#include "base/apple/scoped_cftyperef.h" 24#include "base/check.h" 25#include "base/numerics/safe_conversions.h" 26#include "base/posix/sysctl.h" 27#include "base/strings/stringprintf.h" 28#include "base/strings/sys_string_conversions.h" 29#include "base/system/sys_info.h" 30 31namespace { 32 33// Client ID key in the user preferences. 34NSString* const kLegacyClientIdPreferenceKey = @"ChromiumClientID"; 35NSString* const kClientIdPreferenceKey = @"ChromeClientID"; 36// Current hardware type. This is used to detect that a device has been backed 37// up and restored to another device, and allows regenerating a new device id. 38NSString* const kHardwareTypePreferenceKey = @"ClientIDGenerationHardwareType"; 39// Default salt for device ids. 40const char kDefaultSalt[] = "Salt"; 41// Zero UUID returned on buggy iOS devices. 42NSString* const kZeroUUID = @"00000000-0000-0000-0000-000000000000"; 43 44NSString* GenerateClientId() { 45 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 46 47 // Try to migrate from legacy client id. 48 NSString* client_id = [defaults stringForKey:kLegacyClientIdPreferenceKey]; 49 50 // Some iOS6 devices return a buggy identifierForVendor: 51 // https://openradar.appspot.com/12377282. If this is the case, revert to 52 // generating a new one. 53 if (!client_id || [client_id isEqualToString:kZeroUUID]) { 54 client_id = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; 55 if ([client_id isEqualToString:kZeroUUID]) 56 client_id = base::SysUTF8ToNSString(ios::device_util::GetRandomId()); 57 } 58 return client_id; 59} 60 61} // namespace 62 63namespace ios::device_util { 64 65bool RamIsAtLeast512Mb() { 66 // 512MB devices report anywhere from 502-504 MB, use 450 MB just to be safe. 67 return RamIsAtLeast(450); 68} 69 70bool RamIsAtLeast1024Mb() { 71 // 1GB devices report anywhere from 975-999 MB, use 900 MB just to be safe. 72 return RamIsAtLeast(900); 73} 74 75bool RamIsAtLeast(uint64_t ram_in_mb) { 76 uint64_t memory_size = 0; 77 size_t size = sizeof(memory_size); 78 if (sysctlbyname("hw.memsize", &memory_size, &size, NULL, 0) == 0) { 79 // Anything >= 500M, call high ram. 80 return memory_size >= ram_in_mb * 1024 * 1024; 81 } 82 return false; 83} 84 85bool IsSingleCoreDevice() { 86 uint64_t cpu_number = 0; 87 size_t sizes = sizeof(cpu_number); 88 sysctlbyname("hw.physicalcpu", &cpu_number, &sizes, NULL, 0); 89 return cpu_number == 1; 90} 91 92std::string GetMacAddress(const std::string& interface_name) { 93 std::string mac_string; 94 struct ifaddrs* addresses; 95 if (getifaddrs(&addresses) == 0) { 96 for (struct ifaddrs* address = addresses; address; 97 address = address->ifa_next) { 98 if ((address->ifa_addr->sa_family == AF_LINK) && 99 strcmp(interface_name.c_str(), address->ifa_name) == 0) { 100 const struct sockaddr_dl* found_address_struct = 101 reinterpret_cast<const struct sockaddr_dl*>(address->ifa_addr); 102 103 // |found_address_struct->sdl_data| contains the interface name followed 104 // by the interface address. The address part can be accessed based on 105 // the length of the name, that is, |found_address_struct->sdl_nlen|. 106 const unsigned char* found_address = 107 reinterpret_cast<const unsigned char*>( 108 &found_address_struct->sdl_data[ 109 found_address_struct->sdl_nlen]); 110 111 int found_address_length = found_address_struct->sdl_alen; 112 for (int i = 0; i < found_address_length; ++i) { 113 if (i != 0) 114 mac_string.push_back(':'); 115 base::StringAppendF(&mac_string, "%02X", found_address[i]); 116 } 117 break; 118 } 119 } 120 freeifaddrs(addresses); 121 } 122 return mac_string; 123} 124 125std::string GetRandomId() { 126 base::apple::ScopedCFTypeRef<CFUUIDRef> uuid_object( 127 CFUUIDCreate(kCFAllocatorDefault)); 128 base::apple::ScopedCFTypeRef<CFStringRef> uuid_string( 129 CFUUIDCreateString(kCFAllocatorDefault, uuid_object.get())); 130 return base::SysCFStringRefToUTF8(uuid_string.get()); 131} 132 133std::string GetDeviceIdentifier(const char* salt) { 134 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 135 136 NSString* last_seen_hardware = 137 [defaults stringForKey:kHardwareTypePreferenceKey]; 138 NSString* current_hardware = 139 base::SysUTF8ToNSString(base::SysInfo::HardwareModelName()); 140 if (!last_seen_hardware) { 141 last_seen_hardware = current_hardware; 142 [defaults setObject:current_hardware forKey:kHardwareTypePreferenceKey]; 143 [defaults synchronize]; 144 } 145 146 NSString* client_id = [defaults stringForKey:kClientIdPreferenceKey]; 147 148 if (!client_id || ![last_seen_hardware isEqualToString:current_hardware]) { 149 client_id = GenerateClientId(); 150 [defaults setObject:client_id forKey:kClientIdPreferenceKey]; 151 [defaults setObject:current_hardware forKey:kHardwareTypePreferenceKey]; 152 [defaults synchronize]; 153 } 154 155 return GetSaltedString(base::SysNSStringToUTF8(client_id), 156 salt ? salt : kDefaultSalt); 157} 158 159std::string GetVendorId() { 160 return base::SysNSStringToUTF8( 161 [[[UIDevice currentDevice] identifierForVendor] UUIDString]); 162} 163 164std::string GetSaltedString(const std::string& in_string, 165 const std::string& salt) { 166 DCHECK(salt.length()); 167 NSData* hash_data = [base::SysUTF8ToNSString(in_string + salt) 168 dataUsingEncoding:NSUTF8StringEncoding]; 169 170 unsigned char hash[CC_SHA256_DIGEST_LENGTH]; 171 CC_SHA256([hash_data bytes], base::checked_cast<CC_LONG>([hash_data length]), 172 hash); 173 CFUUIDBytes* uuid_bytes = reinterpret_cast<CFUUIDBytes*>(hash); 174 175 base::apple::ScopedCFTypeRef<CFUUIDRef> uuid_object( 176 CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, *uuid_bytes)); 177 base::apple::ScopedCFTypeRef<CFStringRef> device_id( 178 CFUUIDCreateString(kCFAllocatorDefault, uuid_object.get())); 179 return base::SysCFStringRefToUTF8(device_id.get()); 180} 181 182} // namespace ios::device_util 183