// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

#include "internal.h"
#include "../cpucap/internal.h"

#if defined(CURVE25519_S2N_BIGNUM_CAPABLE)
#include "../../../third_party/s2n-bignum/include/s2n-bignum_aws-lc.h"

void x25519_scalar_mult_generic_s2n_bignum(
  uint8_t out_shared_key[X25519_SHARED_KEY_LEN],
  const uint8_t private_key[X25519_PRIVATE_KEY_LEN],
  const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN]) {

  uint8_t private_key_internal_demask[X25519_PRIVATE_KEY_LEN];
  OPENSSL_memcpy(private_key_internal_demask, private_key, X25519_PRIVATE_KEY_LEN);
  private_key_internal_demask[0] &= 248;
  private_key_internal_demask[31] &= 127;
  private_key_internal_demask[31] |= 64;

  curve25519_x25519_byte_selector(out_shared_key,
                                  private_key_internal_demask,
                                  peer_public_value);
}

void x25519_public_from_private_s2n_bignum(
  uint8_t out_public_value[X25519_PUBLIC_VALUE_LEN],
	const uint8_t private_key[X25519_PRIVATE_KEY_LEN]) {

  uint8_t private_key_internal_demask[X25519_PRIVATE_KEY_LEN];
  OPENSSL_memcpy(private_key_internal_demask, private_key, X25519_PRIVATE_KEY_LEN);
  private_key_internal_demask[0] &= 248;
  private_key_internal_demask[31] &= 127;
  private_key_internal_demask[31] |= 64;

  curve25519_x25519base_byte_selector(out_public_value, private_key_internal_demask);
}

void ed25519_public_key_from_hashed_seed_s2n_bignum(
  uint8_t out_public_key[ED25519_PUBLIC_KEY_LEN],
  uint8_t az[SHA512_DIGEST_LENGTH]) {

  uint64_t uint64_point[8] = {0};
  uint64_t uint64_hashed_seed[4] = {0};
  OPENSSL_memcpy(uint64_hashed_seed, az, 32);

  edwards25519_scalarmulbase_selector(uint64_point, uint64_hashed_seed);

  edwards25519_encode(out_public_key, uint64_point);
}

void ed25519_sign_s2n_bignum(uint8_t out_sig[ED25519_SIGNATURE_LEN],
  uint8_t r[SHA512_DIGEST_LENGTH], const uint8_t *s, const uint8_t *A,
  const void *message, size_t message_len) {

  uint8_t k[SHA512_DIGEST_LENGTH] = {0};
  uint64_t R[8] = {0};
  uint64_t S[4] = {0};
  uint64_t uint64_r[8] = {0};
  uint64_t uint64_k[8] = {0};
  uint64_t uint64_s[4] = {0};
  OPENSSL_memcpy(uint64_r, r, 64);
  OPENSSL_memcpy(uint64_s, s, 32);

  // Reduce r modulo the order of the base-point B.
  bignum_mod_n25519(uint64_r, 8, uint64_r);

  // Compute [r]B.
  edwards25519_scalarmulbase_selector(R, uint64_r);
  edwards25519_encode(out_sig, R);

  // Compute k = SHA512(R || A || message)
  // R is of length 32 octets
  ed25519_sha512(k, out_sig, 32, A, ED25519_PUBLIC_KEY_LEN, message,
    message_len);
  OPENSSL_memcpy(uint64_k, k, SHA512_DIGEST_LENGTH);
  bignum_mod_n25519(uint64_k, 8, uint64_k);

  // Compute S = r + k * s modulo the order of the base-point B.
  // out_sig = R || S
  bignum_madd_n25519_selector(S, uint64_k, uint64_s, uint64_r);
  OPENSSL_memcpy(out_sig + 32, S, 32);
}

int ed25519_verify_s2n_bignum(uint8_t R_computed_encoded[32],
  const uint8_t public_key[ED25519_PUBLIC_KEY_LEN], uint8_t R_expected[32],
  uint8_t S[32], const uint8_t *message, size_t message_len) {

  uint8_t k[SHA512_DIGEST_LENGTH] = {0};
  uint64_t uint64_k[8] = {0};
  uint64_t uint64_R[8] = {0};
  uint64_t uint64_S[4] = {0};
  uint64_t A[8] = {0};

  // Decode public key as A'.
  if (edwards25519_decode_selector(A, public_key) != 0) {
    return 0;
  }

  // Step: rfc8032 5.1.7.2
  // Compute k = SHA512(R_expected || public_key || message).
  ed25519_sha512(k, R_expected, 32, public_key, ED25519_PUBLIC_KEY_LEN, message,
    message_len);
  OPENSSL_memcpy(uint64_k, k, SHA512_DIGEST_LENGTH);
  bignum_mod_n25519(uint64_k, 8, uint64_k);

  // Step: rfc8032 5.1.7.3
  // Recall, we must compute [S]B - [k]A'.
  // First negate A'. Point negation for the twisted edwards curve when points
  // are represented in the extended coordinate system is simply:
  //   -(X,Y,Z,T) = (-X,Y,Z,-T).
  // See "Twisted Edwards curves revisited" https://ia.cr/2008/522.
  bignum_neg_p25519(A, A);

  // Compute R_have <- [S]B - [k]A'.
  OPENSSL_memcpy(uint64_S, S, 32);
  edwards25519_scalarmuldouble_selector(uint64_R, uint64_k, A, uint64_S);
  edwards25519_encode(R_computed_encoded, uint64_R);

  return 1;
}

int ed25519_check_public_key_s2n_bignum(const uint8_t public_key[ED25519_PUBLIC_KEY_LEN]) {
  uint64_t A[8] = {0};
  if (edwards25519_decode_selector(A, public_key) != 0) {
    return 0;
  }
  return 1;
}

#endif
