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

#include "crypto/sign.h"

#include "base/check.h"
#include "base/check_op.h"
#include "crypto/openssl_util.h"
#include "third_party/boringssl/src/include/openssl/evp.h"
#include "third_party/boringssl/src/include/openssl/rsa.h"

namespace crypto::sign {

namespace {

enum SignatureMode {
  kOneShot,
  kStreaming,
};

bool CanUseKeyForSignatureKind(SignatureKind kind,
                               const EVP_PKEY* key,
                               SignatureMode mode) {
  // There is a separate EVP_PKEY_RSA_PSS value that this can return for
  // PSS-specific keys, but it's not used by PublicKey or PrivateKey.
  const int id = EVP_PKEY_id(key);
  switch (kind) {
    case RSA_PKCS1_SHA1:
    case RSA_PKCS1_SHA256:
    case RSA_PSS_SHA256:
      // There exists an EVP_PKEY_RSA_PSS key type for RSA-PSS-specific keys,
      // but BoringSSL doesn't implement it and Chromium doesn't use it.
      return id == EVP_PKEY_RSA;
    case ECDSA_SHA256:
      return id == EVP_PKEY_EC;
    case ED25519:
      return id == EVP_PKEY_ED25519 && mode == kOneShot;
  }

  return false;
}

const EVP_MD* DigestForSignatureKind(SignatureKind kind) {
  switch (kind) {
    case RSA_PKCS1_SHA1:
      return EVP_sha1();
    case RSA_PKCS1_SHA256:
      return EVP_sha256();
    case RSA_PSS_SHA256:
      return EVP_sha256();
    case ECDSA_SHA256:
      return EVP_sha256();
    case ED25519:
      return nullptr;
  }
}

void ConfigurePkeyCtx(EVP_PKEY_CTX* pkctx, SignatureKind kind) {
  if (kind == RSA_PSS_SHA256) {
    CHECK(EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_PSS_PADDING));
    CHECK(EVP_PKEY_CTX_set_rsa_mgf1_md(pkctx, DigestForSignatureKind(kind)));
    CHECK(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, RSA_PSS_SALTLEN_DIGEST));
  }
}

}  // namespace

std::vector<uint8_t> Sign(SignatureKind kind,
                          const crypto::keypair::PrivateKey& key,
                          base::span<const uint8_t> data) {
  CHECK(CanUseKeyForSignatureKind(kind, key.key(), kOneShot));

  const EVP_MD* const md = DigestForSignatureKind(kind);
  EVP_PKEY_CTX* pkctx;
  bssl::UniquePtr<EVP_MD_CTX> context(EVP_MD_CTX_new());
  CHECK(EVP_DigestSignInit(context.get(), &pkctx, md, nullptr,
                           const_cast<EVP_PKEY*>(key.key())));
  ConfigurePkeyCtx(pkctx, kind);

  size_t len = 0;
  CHECK(EVP_DigestSign(context.get(), nullptr, &len, data.data(), data.size()));
  std::vector<uint8_t> result(len);
  CHECK(EVP_DigestSign(context.get(), result.data(), &len, data.data(),
                       data.size()));
  result.resize(len);
  return result;
}

bool Verify(SignatureKind kind,
            const crypto::keypair::PublicKey& key,
            base::span<const uint8_t> data,
            base::span<const uint8_t> signature) {
  CHECK(CanUseKeyForSignatureKind(kind, key.key(), kOneShot));

  const EVP_MD* const md = DigestForSignatureKind(kind);
  EVP_PKEY_CTX* pkctx;
  bssl::UniquePtr<EVP_MD_CTX> context(EVP_MD_CTX_new());
  CHECK(EVP_DigestVerifyInit(context.get(), &pkctx, md, nullptr,
                             const_cast<EVP_PKEY*>(key.key())));
  ConfigurePkeyCtx(pkctx, kind);

  return EVP_DigestVerify(context.get(), signature.data(), signature.size(),
                          data.data(), data.size()) == 1;
}

Signer::Signer(SignatureKind kind, crypto::keypair::PrivateKey key)
    : key_(key), sign_context_(EVP_MD_CTX_new()) {
  CHECK(CanUseKeyForSignatureKind(kind, key.key(), kStreaming));
  OpenSSLErrStackTracer err_tracer(FROM_HERE);

  const EVP_MD* const md = DigestForSignatureKind(kind);
  EVP_PKEY_CTX* pkctx;
  CHECK(
      EVP_DigestSignInit(sign_context_.get(), &pkctx, md, nullptr, key.key()));
  ConfigurePkeyCtx(pkctx, kind);
}
Signer::~Signer() = default;

void Signer::Update(base::span<const uint8_t> data) {
  CHECK(EVP_DigestSignUpdate(sign_context_.get(), data.data(), data.size()));
}

std::vector<uint8_t> Signer::Finish() {
  OpenSSLErrStackTracer err_tracer(FROM_HERE);

  size_t len = 0;
  // Determine the maximum length of the signature.
  CHECK(EVP_DigestSignFinal(sign_context_.get(), nullptr, &len));

  std::vector<uint8_t> signature(len);
  CHECK(EVP_DigestSignFinal(sign_context_.get(), signature.data(), &len));
  signature.resize(len);
  return signature;
}

Verifier::Verifier(SignatureKind kind,
                   crypto::keypair::PublicKey key,
                   base::span<const uint8_t> signature)
    : key_(key), verify_context_(EVP_MD_CTX_new()) {
  OpenSSLErrStackTracer err_tracer(FROM_HERE);
  CHECK(CanUseKeyForSignatureKind(kind, key.key(), kStreaming));
  signature_.resize(signature.size());
  base::span(signature_).copy_from(signature);

  EVP_PKEY_CTX* pkctx;
  const EVP_MD* const md = DigestForSignatureKind(kind);
  CHECK(EVP_DigestVerifyInit(verify_context_.get(), &pkctx, md, nullptr,
                             key.key()));
  ConfigurePkeyCtx(pkctx, kind);
}
Verifier::~Verifier() = default;

void Verifier::Update(base::span<const uint8_t> data) {
  OpenSSLErrStackTracer err_tracer(FROM_HERE);
  CHECK_EQ(
      EVP_DigestVerifyUpdate(verify_context_.get(), data.data(), data.size()),
      1);
}

bool Verifier::Finish() {
  OpenSSLErrStackTracer err_tracer(FROM_HERE);
  int rv = EVP_DigestVerifyFinal(verify_context_.get(), signature_.data(),
                                 signature_.size());
  return rv == 1;
}

}  // namespace crypto::sign
