/*
 *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "rtc_base/ssl_stream_adapter.h"

#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <set>
#include <span>
#include <string>
#include <utility>
#include <vector>

#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "api/environment/environment.h"
#include "api/field_trials_view.h"
#include "rtc_base/openssl_stream_adapter.h"
#include "rtc_base/ssl_identity.h"
#include "rtc_base/stream.h"

namespace webrtc {

// Deprecated, prefer SrtpCryptoSuiteToName.
// https://www.iana.org/assignments/sdp-security-descriptions/sdp-security-descriptions.xhtml
const char kCsAesCm128HmacSha1_80[] = "AES_CM_128_HMAC_SHA1_80";
const char kCsAesCm128HmacSha1_32[] = "AES_CM_128_HMAC_SHA1_32";
const char kCsAeadAes128Gcm[] = "AEAD_AES_128_GCM";
const char kCsAeadAes256Gcm[] = "AEAD_AES_256_GCM";

// https://www.iana.org/assignments/srtp-protection/srtp-protection.xhtml
std::string SrtpCryptoSuiteToName(int crypto_suite) {
  switch (crypto_suite) {
    case kSrtpAes128CmSha1_80:
      return "SRTP_AES128_CM_HMAC_SHA1_80";
    case kSrtpAes128CmSha1_32:
      return "SRTP_AES128_CM_HMAC_SHA1_32";
    case kSrtpAeadAes128Gcm:
      return "SRTP_AEAD_AES_128_GCM";
    case kSrtpAeadAes256Gcm:
      return "SRTP_AEAD_AES_256_GCM";
    default:
      return std::string();
  }
}

bool GetSrtpKeyAndSaltLengths(int crypto_suite,
                              int* key_length,
                              int* salt_length) {
  switch (crypto_suite) {
    case kSrtpAes128CmSha1_32:
    case kSrtpAes128CmSha1_80:
      // SRTP_AES128_CM_HMAC_SHA1_32 and SRTP_AES128_CM_HMAC_SHA1_80 are defined
      // in RFC 5764 to use a 128 bits key and 112 bits salt for the cipher.
      *key_length = 16;
      *salt_length = 14;
      break;
    case kSrtpAeadAes128Gcm:
      // kSrtpAeadAes128Gcm is defined in RFC 7714 to use a 128 bits key and
      // a 96 bits salt for the cipher.
      *key_length = 16;
      *salt_length = 12;
      break;
    case kSrtpAeadAes256Gcm:
      // kSrtpAeadAes256Gcm is defined in RFC 7714 to use a 256 bits key and
      // a 96 bits salt for the cipher.
      *key_length = 32;
      *salt_length = 12;
      break;
    default:
      return false;
  }
  return true;
}

bool IsGcmCryptoSuite(int crypto_suite) {
  return (crypto_suite == kSrtpAeadAes256Gcm ||
          crypto_suite == kSrtpAeadAes128Gcm);
}

std::unique_ptr<SSLStreamAdapter> SSLStreamAdapter::Create(
    std::unique_ptr<StreamInterface> stream,
    absl::AnyInvocable<void(SSLHandshakeError)> handshake_error) {
  return std::make_unique<OpenSSLStreamAdapter>(std::nullopt, std::move(stream),
                                                std::move(handshake_error));
}

std::unique_ptr<SSLStreamAdapter> SSLStreamAdapter::Create(
    const Environment& env,
    std::unique_ptr<StreamInterface> stream,
    absl::AnyInvocable<void(SSLHandshakeError)> handshake_error) {
  return std::make_unique<OpenSSLStreamAdapter>(env, std::move(stream),
                                                std::move(handshake_error));
}

bool SSLStreamAdapter::IsBoringSsl() {
  return OpenSSLStreamAdapter::IsBoringSsl();
}
bool SSLStreamAdapter::IsAcceptableCipher(int cipher, KeyType key_type) {
  return OpenSSLStreamAdapter::IsAcceptableCipher(cipher, key_type);
}
bool SSLStreamAdapter::IsAcceptableCipher(absl::string_view cipher,
                                          KeyType key_type) {
  return OpenSSLStreamAdapter::IsAcceptableCipher(cipher, key_type);
}

std::optional<std::string>
SSLStreamAdapter::GetEphemeralKeyExchangeCipherGroupName(uint16_t group_id) {
#if defined(OPENSSL_IS_BORINGSSL)
  auto val = SSL_get_group_name(group_id);
  if (val != nullptr) {
    return std::string(val);
  }
#endif
  return std::nullopt;
}

std::set<uint16_t>
SSLStreamAdapter::GetSupportedEphemeralKeyExchangeCipherGroups() {
  return {
  // It would be nice if BoringSSL had a function like this!
#ifdef SSL_GROUP_SECP224R1
      SSL_GROUP_SECP224R1,
#endif
#ifdef SSL_GROUP_SECP256R1
      SSL_GROUP_SECP256R1,
#endif
#ifdef SSL_GROUP_SECP384R1
      SSL_GROUP_SECP384R1,
#endif
#ifdef SSL_GROUP_SECP521R1
      SSL_GROUP_SECP521R1,
#endif
#ifdef SSL_GROUP_X25519
      SSL_GROUP_X25519,
#endif
#ifdef SSL_GROUP_X25519_MLKEM768
      SSL_GROUP_X25519_MLKEM768,
#endif
  };
}

std::vector<uint16_t>
SSLStreamAdapter::GetDefaultEphemeralKeyExchangeCipherGroups(
    const FieldTrialsView* field_trials) {
  // It would be nice if BoringSSL had a function like this!
  // from boringssl/src/ssl/extensions.cc kDefaultGroups.
  if (field_trials && field_trials->IsEnabled("WebRTC-EnableDtlsPqc")) {
    return {
#ifdef SSL_GROUP_X25519_MLKEM768
        SSL_GROUP_X25519_MLKEM768,
#endif
#ifdef SSL_GROUP_X25519
        SSL_GROUP_X25519,
#endif
#ifdef SSL_GROUP_SECP256R1
        SSL_GROUP_SECP256R1,
#endif
#ifdef SSL_GROUP_SECP384R1
        SSL_GROUP_SECP384R1,
#endif
    };
  }
  return {
#ifdef SSL_GROUP_X25519
      SSL_GROUP_X25519,
#endif
#ifdef SSL_GROUP_SECP256R1
      SSL_GROUP_SECP256R1,
#endif
#ifdef SSL_GROUP_SECP384R1
      SSL_GROUP_SECP384R1,
#endif
  };
}

// Default shim for backward compat.
bool SSLStreamAdapter::SetPeerCertificateDigest(
    absl::string_view digest_alg,
    const unsigned char* digest_val,
    size_t digest_len,
    SSLPeerCertificateDigestError* error) {
  unsigned char* nonconst_val = const_cast<unsigned char*>(digest_val);
  SSLPeerCertificateDigestError ret = SetPeerCertificateDigest(
      digest_alg, std::span<uint8_t>(nonconst_val, digest_len));
  if (error)
    *error = ret;
  return ret == SSLPeerCertificateDigestError::NONE;
}

///////////////////////////////////////////////////////////////////////////////
// Test only settings
///////////////////////////////////////////////////////////////////////////////

void SSLStreamAdapter::EnableTimeCallbackForTesting() {
  OpenSSLStreamAdapter::EnableTimeCallbackForTesting();
}

SSLProtocolVersion SSLStreamAdapter::GetMaxSupportedDTLSProtocolVersion() {
  return OpenSSLStreamAdapter::GetMaxSupportedDTLSProtocolVersion();
}

///////////////////////////////////////////////////////////////////////////////

}  // namespace webrtc
