blob: 305ffe166d542e2e59f6fb5ee676e4b4be8c2e72 [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <rmad/utils/hwid_utils_impl.h>
#include <optional>
#include <string>
#include <vector>
#include <base/containers/span.h>
#include <base/logging.h>
#include <base/metrics/crc32.h>
#include <base/strings/stringprintf.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
namespace {
constexpr char kBase8Alphabet[] = "23456789";
constexpr char kBase32Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
constexpr uint32_t kChecksumBitMask = 0xFF;
constexpr int kBase32BitWidth = 5;
} // namespace
namespace rmad {
std::optional<std::string> HwidUtilsImpl::CalculateChecksum(
const std::string& hwid) const {
std::vector<std::string> parts =
base::SplitString(hwid, " ", base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);
if (parts.size() != 2) {
return std::nullopt;
}
base::RemoveChars(parts[1], "-", &parts[1]);
std::string stripped =
base::StringPrintf("%s %s", parts[0].c_str(), parts[1].c_str());
uint32_t crc32 =
~base::Crc32(0xFFFFFFFF, base::as_bytes(base::make_span(stripped))) &
kChecksumBitMask;
std::string checksum =
base::StringPrintf("%c%c", kBase8Alphabet[crc32 >> kBase32BitWidth],
kBase32Alphabet[crc32 & ((1 << kBase32BitWidth) - 1)]);
return checksum;
}
bool HwidUtilsImpl::VerifyChecksum(const std::string& hwid) {
if (hwid.size() <= 2) {
LOG(ERROR) << "The given HWID string has an invalid length.";
return false;
}
std::string raw_hwid = hwid.substr(0, hwid.size() - 2);
std::string original_checksum = hwid.substr(hwid.size() - 2);
std::optional<std::string> checksum = this->CalculateChecksum(raw_hwid);
return (checksum == original_checksum);
}
bool HwidUtilsImpl::VerifyHwidFormat(const std::string& hwid,
bool has_checksum) {
std::vector<std::string> parts =
base::SplitString(hwid, " ", base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);
if (parts.size() != 2) {
LOG(ERROR) << "HWID string should be split into exactly 2 parts.";
return false;
}
std::vector<std::string> product = base::SplitString(
parts[0], "-", base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);
if (product.size() != 1 && product.size() != 2) {
LOG(ERROR) << "The first part of HWID is not in a format of "
"<MODEL>[-<BRAND_CODE>].";
return false;
}
size_t expected_remainder = (has_checksum) ? 3 : 1;
if (parts[1].size() % 4 != expected_remainder) {
LOG(ERROR) << "The given HWID has unexpected length.";
return false;
}
return true;
}
std::optional<HwidElements> HwidUtilsImpl::DecomposeHwid(
const std::string& hwid) {
if (!VerifyHwidFormat(hwid, true)) {
LOG(ERROR) << "Failed to decompose HWID due to invalid format.";
return std::nullopt;
}
HwidElements elements;
std::vector<std::string> parts =
base::SplitString(hwid, " ", base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);
std::vector<std::string> product = base::SplitString(
parts[0], "-", base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);
// Parse <MODEL>-<BRAND_CODE>.
if (product.size() == 1) {
// Model name only.
elements.model_name = product[0];
} else if (product.size() == 2) {
// Model name + brand code.
elements.model_name = product[0];
elements.brand_code = product[1];
}
elements.encoded_components = parts[1].substr(0, parts[1].size() - 2);
elements.checksum = parts[1].substr(parts[1].size() - 2);
return elements;
}
} // namespace rmad