chromium/chromeos/ash/components/report/utils/psm_utils.cc

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

#include "chromeos/ash/components/report/utils/psm_utils.h"

#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "crypto/hmac.h"
#include "third_party/private_membership/src/private_membership_rlwe.pb.h"

namespace psm_rlwe = private_membership::rlwe;

namespace ash::report::utils {

namespace {

std::string GetDigestString(const std::string& key,
                            const std::string& message) {
  crypto::HMAC hmac(crypto::HMAC::SHA256);
  std::vector<uint8_t> digest(hmac.DigestLength());
  if (!hmac.Init(key) || !hmac.Sign(message, &digest[0], digest.size())) {
    return std::string();
  }
  return base::HexEncode(digest);
}

}  // namespace

std::optional<psm_rlwe::RlwePlaintextId> GeneratePsmIdentifier(
    const std::string& high_entropy_seed,
    const std::string& psm_use_case_str,
    const std::string& window_id) {
  if (high_entropy_seed.empty() || psm_use_case_str.empty() ||
      window_id.empty()) {
    LOG(ERROR)
        << "Failed to generate PSM id without the high entropy seed, use "
        << "case, and window id being defined.";
    return std::nullopt;
  }

  std::string unhashed_psm_id =
      base::JoinString({psm_use_case_str, window_id}, "|");

  // Represents a 64 byte hex encoded value by default.
  std::string psm_id_str = GetDigestString(high_entropy_seed, unhashed_psm_id);

  if (!psm_id_str.empty()) {
    psm_rlwe::RlwePlaintextId psm_rlwe_id;
    psm_rlwe_id.set_sensitive_id(psm_id_str);

    return psm_rlwe_id;
  }

  // Failed HMAC-SHA256 hash on PSM id.
  LOG(ERROR) << "Failed to calculate HMAC-256 has on PSM id.";
  return std::nullopt;
}

}  // namespace ash::report::utils