chromium/chromecast/base/hash_util.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromecast/base/hash_util.h"

#include <limits.h>
#include <vector>

#include "base/check_op.h"
#include "base/hash/sha1.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "chromecast/base/legacy_app_id_mapper.h"
#include "chromecast/base/version.h"

namespace chromecast {

namespace {

const size_t kAppIdV2StrLen = 8;

}  // namespace

uint64_t HashToUInt64(const std::string& value) {
  uint64_t output;
  const std::string sha1hash = base::SHA1HashString(value);
  DCHECK_GE(sha1hash.size(), sizeof(output));
  memcpy(&output, sha1hash.data(), sizeof(output));
  return output;
}

uint32_t HashToUInt32(const std::string& value) {
  uint32_t output;
  const std::string sha1hash = base::SHA1HashString(value);
  DCHECK_GE(sha1hash.size(), sizeof(output));
  memcpy(&output, sha1hash.data(), sizeof(output));
  return output;
}

uint64_t HashGUID64(const std::string& guid) {
  std::string hex;
  base::RemoveChars(guid, "-", &hex);
  uint64_t output;
  DCHECK_EQ(hex.size(), 32u);
  if (base::HexStringToUInt64(hex.substr(0, 16), &output))
    return output;
  NOTREACHED_IN_MIGRATION();
  return 0;
}

uint32_t HashAppId32(const std::string& app_id) {
  uint32_t output;
  if (app_id.size() == kAppIdV2StrLen &&
      base::HexStringToUInt(app_id, &output)) {
    return output;
  }

  return MapLegacyAppId(app_id);
}

uint64_t HashCastBuildNumber64(const std::string& build_number) {
  uint64_t return_value = 0;
  std::vector<std::string> tokens(base::SplitString(
      build_number, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL));
  const size_t tsize = tokens.size();
  if (tsize < 1 || tsize > 4)
    return static_cast<uint64_t>(-1);

  int bits = 64 / tsize;
  // special case for 3-tuple to make hex look nicer.
  if (tsize == 3)
    bits = 16;

  for (size_t i = 0; i < tsize; ++i) {
    // special case for the last token of 3-tuple.
    if (tsize == 3 && i == 2)
      bits = 32;
    return_value <<= bits;
    unsigned value = 0;
    if (!base::StringToUint(tokens[i], &value))
      return static_cast<uint64_t>(-1);
    if (bits != 64)  // avoid overflow
      value &= (static_cast<uint64_t>(1) << bits) - 1;
    return_value |= value;
  }
  return return_value;
}

uint64_t HashSessionId64(const std::string& session_id) {
  return HashGUID64(session_id);
}

uint64_t HashSdkVersion64(const std::string& sdk_version) {
  if (sdk_version.empty())
    return 0;

  // Sdk version is usually in "X.Y.Z(.minor)" format.
  // Following code is a little bit relaxed to accept all sub-fields optional.
  uint64_t return_value = 0;
  std::vector<std::string> tokens(base::SplitString(
      sdk_version, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL));
  for (size_t i = 0; i < 4; ++i) {
    return_value <<= 16;
    if (tokens.size() > i) {
      unsigned value = 0;
      if (base::StringToUint(tokens[i], &value)) {
        return_value |= value & 0xFFFF;
      } else {
        LOG_IF(ERROR, !CAST_IS_DEBUG_BUILD())
            << "Sdk version " << sdk_version << " is not in correct format.";
        return static_cast<uint64_t>(-1);
      }
    }
  }
  return return_value;
}

uint32_t HashSocketId32(const std::string& socket_id) {
  uint32_t output;
  // socket id is usually just numerical.
  if (base::StringToUint(socket_id, &output)) {
    return output;
  } else {  // CastControlSocket and unittests.
    return HashToUInt32(socket_id);
  }
}

uint32_t HashConnectionId32(const std::string& connection_id) {
  return HashToUInt32(connection_id);
}

uint64_t HashAndroidBuildNumber64(const std::string& build_id) {
  if (build_id.length() == 0) {
    return static_cast<uint64_t>(-1);
  }

  uint64_t value = 0;
  for (const char& c : build_id.substr(0, sizeof(value) / sizeof(char))) {
    value <<= CHAR_BIT;
    value += c;
  }
  return value;
}

}  // namespace chromecast