/*
 * Copyright (c) 2021, [Ribose Inc](https://www.ribose.com).
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <string.h>
#include "ec.h"
#include "types.h"
#include "utils.h"
#include "str-utils.h"

/**
 * EC Curves definition used by implementation
 *
 * \see RFC4880 bis01 - 9.2. ECC Curve OID
 *
 * Order of the elements in this array corresponds to
 * values in pgp_curve_t enum.
 */
static const ec_curve_desc_t ec_curves[] = {
  {PGP_CURVE_UNKNOWN, 0, {0}, 0, NULL, NULL},

  {PGP_CURVE_NIST_P_256,
   256,
   {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07},
   8,
#if defined(CRYPTO_BACKEND_BOTAN)
   "secp256r1",
#elif defined(CRYPTO_BACKEND_OPENSSL)
   "prime256v1",
#endif
   "NIST P-256",
   true,
   "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff",
   "0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc",
   "0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
   "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
   "0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
   "0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5",
   "0x01"},
  {PGP_CURVE_NIST_P_384,
   384,
   {0x2B, 0x81, 0x04, 0x00, 0x22},
   5,
#if defined(CRYPTO_BACKEND_BOTAN)
   "secp384r1",
#elif defined(CRYPTO_BACKEND_OPENSSL)
   "secp384r1",
#endif
   "NIST P-384",
   true,
   "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000"
   "ffffffff",
   "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000"
   "fffffffc",
   "0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8ed"
   "d3ec2aef",
   "0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196a"
   "ccc52973",
   "0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e38"
   "72760ab7",
   "0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c"
   "90ea0e5f",
   "0x01"},
  {PGP_CURVE_NIST_P_521,
   521,
   {0x2B, 0x81, 0x04, 0x00, 0x23},
   5,
#if defined(CRYPTO_BACKEND_BOTAN)
   "secp521r1",
#elif defined(CRYPTO_BACKEND_OPENSSL)
   "secp521r1",
#endif
   "NIST P-521",
   true,
   "0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
   "ffffffffffffffffffffffffffffffffffffffffffff",
   "0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
   "fffffffffffffffffffffffffffffffffffffffffffc",
   "0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652"
   "c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00",
   "0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc"
   "0148f709a5d03bb5c9b8899c47aebb6fb71e91386409",
   "0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1d"
   "c127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66",
   "0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550"
   "b9013fad0761353c7086a272c24088be94769fd16650",
   "0x01"},
  {PGP_CURVE_ED25519,
   255,
   {0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01},
   9,
#if defined(CRYPTO_BACKEND_BOTAN)
   "Ed25519",
#elif defined(CRYPTO_BACKEND_OPENSSL)
   "ED25519",
#endif
   "Ed25519",
   true,
   "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
   /* two below are actually negative */
   "0x01",
   "0x2dfc9311d490018c7338bf8688861767ff8ff5b2bebe27548a14b235eca6874a",
   "0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed",
   "0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a",
   "0x6666666666666666666666666666666666666666666666666666666666666658",
   "0x08"},
  {PGP_CURVE_25519,
   255,
   {0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01},
   10,
#if defined(CRYPTO_BACKEND_BOTAN)
   "curve25519",
#elif defined(CRYPTO_BACKEND_OPENSSL)
   "X25519",
#endif
   "Curve25519",
   true,
   "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
   "0x01db41",
   "0x01",
   "0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed",
   "0x0000000000000000000000000000000000000000000000000000000000000009",
   "0x20ae19a1b8a086b4e01edd2c7748d14c923d4d7e6d7c61b229e9c5a27eced3d9",
   "0x08"},
  {PGP_CURVE_BP256,
   256,
   {0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07},
   9,
#if defined(CRYPTO_BACKEND_BOTAN)
   "brainpool256r1",
#elif defined(CRYPTO_BACKEND_OPENSSL)
   "brainpoolP256r1",
#endif
   "brainpoolP256r1",
#if defined(ENABLE_BRAINPOOL)
   true,
#else
   false,
#endif
   "0xa9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377",
   "0x7d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9",
   "0x26dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b6",
   "0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7",
   "0x8bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262",
   "0x547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997",
   "0x01"},
  {PGP_CURVE_BP384,
   384,
   {0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B},
   9,
#if defined(CRYPTO_BACKEND_BOTAN)
   "brainpool384r1",
#elif defined(CRYPTO_BACKEND_OPENSSL)
   "brainpoolP384r1",
#endif
   "brainpoolP384r1",
#if defined(ENABLE_BRAINPOOL)
   true,
#else
   false,
#endif
   "0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a7187470013"
   "3107ec53",
   "0x7bc382c63d8c150c3c72080ace05afa0c2bea28e4fb22787139165efba91f90f8aa5814a503ad4eb04a8c7dd"
   "22ce2826",
   "0x04a8c7dd22ce28268b39b55416f0447c2fb77de107dcd2a62e880ea53eeb62d57cb4390295dbc9943ab78696"
   "fa504c11",
   "0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202"
   "e9046565",
   "0x1d1c64f068cf45ffa2a63a81b7c13f6b8847a3e77ef14fe3db7fcafe0cbd10e8e826e03436d646aaef87b2e2"
   "47d4af1e",
   "0x8abe1d7520f9c2a45cb1eb8e95cfd55262b70b29feec5864e19c054ff99129280e4646217791811142820341"
   "263c5315",
   "0x01"},
  {PGP_CURVE_BP512,
   512,
   {0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D},
   9,
#if defined(CRYPTO_BACKEND_BOTAN)
   "brainpool512r1",
#elif defined(CRYPTO_BACKEND_OPENSSL)
   "brainpoolP512r1",
#endif
   "brainpoolP512r1",
#if defined(ENABLE_BRAINPOOL)
   true,
#else
   false,
#endif
   "0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12a"
   "e6a380e62881ff2f2d82c68528aa6056583a48f3",
   "0x7830a3318b603b89e2327145ac234cc594cbdd8d3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c9"
   "8b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94ca",
   "0x3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94ca"
   "dc083e67984050b75ebae5dd2809bd638016f723",
   "0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca9261941866119"
   "7fac10471db1d381085ddaddb58796829ca90069",
   "0x81aee4bdd82ed9645a21322e9c4c6a9385ed9f70b5d916c1b43b62eef4d0098eff3b1f78e2d0d48d50d1687b"
   "93b97d5f7c6d5047406a5e688b352209bcb9f822",
   "0x7dde385d566332ecc0eabfa9cf7822fdf209f70024a57b1aa000c55b881f8111b2dcde494a5f485e5bca4bd8"
   "8a2763aed1ca2b2fa8f0540678cd1e0f3ad80892",
   "0x01"},
  {PGP_CURVE_P256K1,
   256,
   {0x2B, 0x81, 0x04, 0x00, 0x0A},
   5,
#if defined(CRYPTO_BACKEND_BOTAN)
   "secp256k1",
#elif defined(CRYPTO_BACKEND_OPENSSL)
   "secp256k1",
#endif
   "secp256k1",
   true,
   "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
   "0x0000000000000000000000000000000000000000000000000000000000000000",
   "0x0000000000000000000000000000000000000000000000000000000000000007",
   "0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
   "0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
   "0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
   "0x01"},
  {
    PGP_CURVE_SM2_P_256,
    256,
    {0x2A, 0x81, 0x1C, 0xCF, 0x55, 0x01, 0x82, 0x2D},
    8,
#if defined(CRYPTO_BACKEND_BOTAN)
    "sm2p256v1",
#elif defined(CRYPTO_BACKEND_OPENSSL)
    "sm2",
#endif
    "SM2 P-256",
#if defined(ENABLE_SM2)
    true,
#else
    false,
#endif
    "0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",
    "0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",
    "0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",
    "0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",
    "0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",
    "0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0",
  },
};

pgp_curve_t
find_curve_by_OID(const uint8_t *oid, size_t oid_len)
{
    for (size_t i = 0; i < PGP_CURVE_MAX; i++) {
        if ((oid_len == ec_curves[i].OIDhex_len) &&
            (!memcmp(oid, ec_curves[i].OIDhex, oid_len))) {
            return static_cast<pgp_curve_t>(i);
        }
    }

    return PGP_CURVE_MAX;
}

pgp_curve_t
find_curve_by_name(const char *name)
{
    for (size_t i = 1; i < PGP_CURVE_MAX; i++) {
        if (rnp::str_case_eq(ec_curves[i].pgp_name, name)) {
            return ec_curves[i].rnp_curve_id;
        }
    }

    return PGP_CURVE_MAX;
}

const ec_curve_desc_t *
get_curve_desc(const pgp_curve_t curve_id)
{
    return (curve_id < PGP_CURVE_MAX && curve_id > 0) ? &ec_curves[curve_id] : NULL;
}

bool
alg_allows_curve(pgp_pubkey_alg_t alg, pgp_curve_t curve)
{
    /* SM2 curve is only for SM2 algo */
    if ((alg == PGP_PKA_SM2) || (curve == PGP_CURVE_SM2_P_256)) {
        return (alg == PGP_PKA_SM2) && (curve == PGP_CURVE_SM2_P_256);
    }
    /* EDDSA and PGP_CURVE_ED25519 */
    if ((alg == PGP_PKA_EDDSA) || (curve == PGP_CURVE_ED25519)) {
        return (alg == PGP_PKA_EDDSA) && (curve == PGP_CURVE_ED25519);
    }
    /* Curve x25519 is only for ECDH */
    if (curve == PGP_CURVE_25519) {
        return alg == PGP_PKA_ECDH;
    }
    /* Other curves are good for both ECDH and ECDSA */
    return true;
}

bool
curve_supported(pgp_curve_t curve)
{
    const ec_curve_desc_t *info = get_curve_desc(curve);
    return info && info->supported;
}
