chromium/chromeos/ash/services/device_sync/cryptauth_key.cc

// Copyright 2019 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/services/device_sync/cryptauth_key.h"

#include "base/base64url.h"
#include "chromeos/ash/services/device_sync/value_string_encoding.h"
#include "crypto/sha2.h"

namespace ash::device_sync {

namespace {

// Strings used as the DictionaryValue keys in As*DictionaryValue().
const char kHandleDictKey[] = "handle";
const char kStatusDictKey[] = "status";
const char kTypeDictKey[] = "type";
const char kSymmetricKeyDictKey[] = "symmetric_key";
const char kPublicKeyDictKey[] = "public_key";
const char kPrivateKeyDictKey[] = "private_key";

// Returns the base64url-encoded SHA256 hash of the input string.
std::string CreateHandle(const std::string& string_to_hash) {
  std::string handle;
  base::Base64UrlEncode(crypto::SHA256HashString(string_to_hash),
                        base::Base64UrlEncodePolicy::INCLUDE_PADDING, &handle);
  return handle;
}

bool IsSymmetricKeyType(cryptauthv2::KeyType type) {
  return (type == cryptauthv2::KeyType::RAW128 ||
          type == cryptauthv2::KeyType::RAW256);
}

bool IsAsymmetricKeyType(cryptauthv2::KeyType type) {
  return (type == cryptauthv2::KeyType::P256 ||
          type == cryptauthv2::KeyType::CURVE25519);
}

}  // namespace

// static
std::optional<CryptAuthKey> CryptAuthKey::FromDictionary(
    const base::Value::Dict& dict) {
  std::optional<int> opt_status = dict.FindInt(kStatusDictKey);
  if (!opt_status) {
    return std::nullopt;
  }
  CryptAuthKey::Status status = static_cast<CryptAuthKey::Status>(*opt_status);

  std::optional<int> opt_type = dict.FindInt(kTypeDictKey);
  if (!opt_type || !cryptauthv2::KeyType_IsValid(*opt_type)) {
    return std::nullopt;
  }
  cryptauthv2::KeyType type = static_cast<cryptauthv2::KeyType>(*opt_type);

  const std::string* handle = dict.FindString(kHandleDictKey);
  if (!handle || handle->empty()) {
    return std::nullopt;
  }

  if (IsSymmetricKeyType(type)) {
    std::optional<std::string> symmetric_key =
        util::DecodeFromValueString(dict.Find(kSymmetricKeyDictKey));
    if (!symmetric_key || symmetric_key->empty()) {
      return std::nullopt;
    }

    return CryptAuthKey(*symmetric_key, status, type, *handle);
  }

  DCHECK(IsAsymmetricKeyType(type));

  std::optional<std::string> public_key =
      util::DecodeFromValueString(dict.Find(kPublicKeyDictKey));
  std::optional<std::string> private_key =
      util::DecodeFromValueString(dict.Find(kPrivateKeyDictKey));
  if (!public_key || !private_key || public_key->empty()) {
    return std::nullopt;
  }

  return CryptAuthKey(*public_key, *private_key, status, type, *handle);
}

CryptAuthKey::CryptAuthKey(const std::string& symmetric_key,
                           Status status,
                           cryptauthv2::KeyType type,
                           const std::optional<std::string>& handle)
    : symmetric_key_(symmetric_key),
      status_(status),
      type_(type),
      handle_(handle ? *handle : CreateHandle(symmetric_key)) {
  DCHECK(IsSymmetricKey());
  DCHECK(!symmetric_key_.empty());
  DCHECK(!handle_.empty());
}

CryptAuthKey::CryptAuthKey(const std::string& public_key,
                           const std::string& private_key,
                           Status status,
                           cryptauthv2::KeyType type,
                           const std::optional<std::string>& handle)
    : public_key_(public_key),
      private_key_(private_key),
      status_(status),
      type_(type),
      handle_(handle ? *handle : CreateHandle(public_key)) {
  DCHECK(IsAsymmetricKey());
  DCHECK(!public_key_.empty());
  DCHECK(!handle_.empty());
}

CryptAuthKey::CryptAuthKey(const CryptAuthKey&) = default;

CryptAuthKey::~CryptAuthKey() = default;

bool CryptAuthKey::IsSymmetricKey() const {
  return IsSymmetricKeyType(type_);
}

bool CryptAuthKey::IsAsymmetricKey() const {
  return IsAsymmetricKeyType(type_);
}

base::Value::Dict CryptAuthKey::AsSymmetricKeyDictionary() const {
  DCHECK(IsSymmetricKey());

  return base::Value::Dict()
      .Set(kHandleDictKey, handle_)
      .Set(kStatusDictKey, status_)
      .Set(kTypeDictKey, type_)
      .Set(kSymmetricKeyDictKey, util::EncodeAsValueString(symmetric_key_));
}

base::Value::Dict CryptAuthKey::AsAsymmetricKeyDictionary() const {
  DCHECK(IsAsymmetricKey());

  return base::Value::Dict()
      .Set(kHandleDictKey, handle_)
      .Set(kStatusDictKey, status_)
      .Set(kTypeDictKey, type_)
      .Set(kPublicKeyDictKey, util::EncodeAsValueString(public_key_))
      .Set(kPrivateKeyDictKey, util::EncodeAsValueString(private_key_));
}

bool CryptAuthKey::operator==(const CryptAuthKey& other) const {
  return handle_ == other.handle_ && status_ == other.status_ &&
         type_ == other.type_ && symmetric_key_ == other.symmetric_key_ &&
         public_key_ == other.public_key_ && private_key_ == other.private_key_;
}

bool CryptAuthKey::operator!=(const CryptAuthKey& other) const {
  return !(*this == other);
}

}  // namespace ash::device_sync