blob: ed5eb4402487b0714b97a07821c6dbc6ff688f6a [file] [log] [blame]
// Copyright (c) 2013 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 "chrome/browser/chromeos/login/hwid_checker.h"
#include <cstdio>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "chrome/common/chrome_switches.h"
#include "chromeos/constants/chromeos_switches.h"
#include "chromeos/system/statistics_provider.h"
#include "content/public/common/content_switches.h"
#include "third_party/re2/src/re2/re2.h"
#include "third_party/zlib/zlib.h"
namespace {
unsigned CalculateCRC32(const std::string& data) {
return static_cast<unsigned>(
crc32(0, reinterpret_cast<const Bytef*>(data.c_str()), data.length()));
}
std::string CalculateHWIDv2Checksum(const std::string& data) {
unsigned crc32 = CalculateCRC32(data);
// We take four least significant decimal digits of CRC-32.
char checksum[5];
int snprintf_result = snprintf(checksum, 5, "%04u", crc32 % 10000);
LOG_ASSERT(snprintf_result == 4);
return checksum;
}
bool IsCorrectHWIDv2(const std::string& hwid) {
std::string body;
std::string checksum;
if (!RE2::FullMatch(hwid, "([\\s\\S]*) (\\d{4})", &body, &checksum))
return false;
return CalculateHWIDv2Checksum(body) == checksum;
}
bool IsExceptionalHWID(const std::string& hwid) {
return RE2::PartialMatch(hwid, "^(SPRING [A-D])|(FALCO A)");
}
std::string CalculateExceptionalHWIDChecksum(const std::string& data) {
static const char base32_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
unsigned crc32 = CalculateCRC32(data);
// We take 10 least significant bits of CRC-32 and encode them in 2 characters
// using Base32 alphabet.
std::string checksum;
checksum += base32_alphabet[(crc32 >> 5) & 0x1f];
checksum += base32_alphabet[crc32 & 0x1f];
return checksum;
}
bool IsCorrectExceptionalHWID(const std::string& hwid) {
if (!IsExceptionalHWID(hwid))
return false;
std::string bom;
if (!RE2::FullMatch(hwid, "[A-Z0-9]+ ((?:[A-Z2-7]{4}-)*[A-Z2-7]{1,4})", &bom))
return false;
if (bom.length() < 2)
return false;
std::string hwid_without_dashes;
base::RemoveChars(hwid, "-", &hwid_without_dashes);
LOG_ASSERT(hwid_without_dashes.length() >= 2);
std::string not_checksum =
hwid_without_dashes.substr(0, hwid_without_dashes.length() - 2);
std::string checksum =
hwid_without_dashes.substr(hwid_without_dashes.length() - 2);
return CalculateExceptionalHWIDChecksum(not_checksum) == checksum;
}
std::string CalculateHWIDv3Checksum(const std::string& data) {
static const char base8_alphabet[] = "23456789";
static const char base32_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
unsigned crc32 = CalculateCRC32(data);
// We take 8 least significant bits of CRC-32 and encode them in 2 characters.
std::string checksum;
checksum += base8_alphabet[(crc32 >> 5) & 0x7];
checksum += base32_alphabet[crc32 & 0x1f];
return checksum;
}
bool IsCorrectHWIDv3(const std::string& hwid) {
if (IsExceptionalHWID(hwid))
return false;
// HWIDv3 format:
// Regular: "<MODEL> <COMPONENT><CHECKSUM>"
// Extended: "<MODEL>-<RLZ> <CONFIGLESS> <COMPONENT><CHECKSUM>"
std::vector<std::string> parts =
base::SplitString(hwid, " ", base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_ALL);
std::string not_checksum, checksum;
constexpr char model[] = "[-A-Z0-9]+";
constexpr char configless_field[] = "(?:[[:xdigit:]]+-){3}[[:xdigit:]]+";
constexpr char component_and_checksum[] =
"(?:[A-Z2-7][2-9][A-Z2-7]-)*[A-Z2-7][2-9][A-Z2-7]";
int component_field_index;
if (parts.size() == 2) {
// Regular: "<MODEL> <COMPONENT><CHECKSUM>"
if (!RE2::FullMatch(parts[0], model) ||
!RE2::FullMatch(parts[1], component_and_checksum)) {
return false;
}
component_field_index = 1;
} else if (parts.size() == 3) {
// Extended: "<MODEL>-<RLZ> <CONFIGLESS> <COMPONENT><CHECKSUM>"
if (!RE2::FullMatch(parts[0], model) ||
!RE2::FullMatch(parts[1], configless_field) ||
!RE2::FullMatch(parts[2], component_and_checksum)) {
return false;
}
component_field_index = 2;
} else {
return false;
}
// Modify component_field before computing checksum.
std::string& component_field = parts[component_field_index];
// Last 2 characters are checksum.
checksum = component_field.substr(component_field.size() - 2);
component_field = component_field.substr(0, component_field.size() - 2);
// When computing checksum, "-" is removed from component field.
base::RemoveChars(component_field, "-", &component_field);
// Construct not_checksum to compute checksum.
not_checksum = base::JoinString(parts, " ");
return CalculateHWIDv3Checksum(not_checksum) == checksum;
}
} // anonymous namespace
namespace chromeos {
bool IsHWIDCorrect(const std::string& hwid) {
return IsCorrectHWIDv2(hwid) || IsCorrectExceptionalHWID(hwid) ||
IsCorrectHWIDv3(hwid);
}
bool IsMachineHWIDCorrect() {
#if !defined(GOOGLE_CHROME_BUILD)
return true;
#endif
base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
if (cmd_line->HasSwitch(::switches::kTestType))
return true;
if (!base::SysInfo::IsRunningOnChromeOS())
return true;
chromeos::system::StatisticsProvider* stats =
chromeos::system::StatisticsProvider::GetInstance();
if (stats->IsRunningOnVm())
return true;
std::string hwid;
if (!stats->GetMachineStatistic(chromeos::system::kHardwareClassKey, &hwid)) {
LOG(ERROR) << "Couldn't get machine statistic 'hardware_class'.";
return false;
}
if (!chromeos::IsHWIDCorrect(hwid)) {
LOG(ERROR) << "Machine has malformed HWID '" << hwid << "'. ";
return false;
}
return true;
}
} // namespace chromeos