| // 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 |