/*
 *  seccure  -  Copyright 2014 B. Poettering
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 3 of
 *  the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program. If not, see
 *  <http://www.gnu.org/licenses/>.
 */

/* 
 *   SECCURE Elliptic Curve Crypto Utility for Reliable Encryption
 *
 *              http://point-at-infinity.org/seccure/
 *
 *
 * seccure implements a selection of asymmetric algorithms based on  
 * elliptic curve cryptography (ECC). See the manpage or the project's  
 * homepage for further details.
 *
 * This code links against the GNU gcrypt library "libgcrypt" (which
 * is part of the GnuPG project). Use the included Makefile to build
 * the binary.
 * 
 * Report bugs to: seccure AT point-at-infinity.org
 *
 */

#include <gcrypt.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

#include "curves.h"
#include "ecc.h"
#include "serialize.h"

/******************************************************************************/

#define CURVE_NUM 15

struct curve {
  const char *name, *a, *b, *m, *base_x, *base_y, *order;
  int cofactor;
  size_t pk_len_compact;
};

static const struct curve curves[CURVE_NUM] = {
  { "secp112r1",
    "db7c2abf62e35e668076bead2088", 
    "659ef8ba043916eede8911702b22", 
    "db7c2abf62e35e668076bead208b",
    "09487239995a5ee76b55f9c2f098",
    "a89ce5af8724c0a23e0e0ff77500", 
    "db7c2abf62e35e7628dfac6561c5", 
    1, 18 },
  
  { "secp128r1",
    "fffffffdfffffffffffffffffffffffc", 
    "e87579c11079f43dd824993c2cee5ed3", 
    "fffffffdffffffffffffffffffffffff",
    "161ff7528b899b2d0c28607ca52c5b86", 
    "cf5ac8395bafeb13c02da292dded7a83",
    "fffffffe0000000075a30d1b9038a115", 
    1, 20 },

  { "secp160r1", 
    "ffffffffffffffffffffffffffffffff7ffffffc",
    "1c97befc54bd7a8b65acf89f81d4d4adc565fa45",
    "ffffffffffffffffffffffffffffffff7fffffff",
    "4a96b5688ef573284664698968c38bb913cbfc82",
    "23a628553168947d59dcc912042351377ac5fb32",
    "0100000000000000000001f4c8f927aed3ca752257", 
    1, 25 },

  { "secp192r1/nistp192",
    "fffffffffffffffffffffffffffffffefffffffffffffffc",
    "64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 
    "fffffffffffffffffffffffffffffffeffffffffffffffff",
    "188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012",
    "07192b95ffc8da78631011ed6b24cdd573f977a11e794811",
    "ffffffffffffffffffffffff99def836146bc9b1b4d22831", 
    1, 30 },

  { "secp224r1/nistp224",
    "fffffffffffffffffffffffffffffffefffffffffffffffffffffffe",
    "b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4",
    "ffffffffffffffffffffffffffffffff000000000000000000000001",
    "b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21",
    "bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34",
    "ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d", 
    1, 35 },

  { "secp256r1/nistp256",
    "ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 
    "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 
    "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff",
    "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 
    "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 
    "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 
    1, 40 },

  { "secp384r1/nistp384",
    "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc",
    "b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef", 
    "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff",
    "aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7",
    "3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f", 
    "ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973", 
    1, 60 },

  { "secp521r1/nistp521",
    "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc",
    "051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00",
    "1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
    "0c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66",
    "11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650",
    "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409", 
    1, 81 },
  
  { "brainpoolp160r1",
    "340e7be2a280eb74e2be61bada745d97e8f7c300",
    "1e589a8595423412134faa2dbdec95c8d8675e58",
    "e95e4a5f737059dc60dfc7ad95b3d8139515620f",
    "bed5af16ea3f6a4f62938c4631eb5af7bdbcdbc3",
    "1667cb477a1a8ec338f94741669c976316da6321",
    "e95e4a5f737059dc60df5991d45029409e60fc09",
    1, 25 },
  
  { "brainpoolp192r1",
    "6a91174076b1e0e19c39c031fe8685c1cae040e5c69a28ef",
    "469a28ef7c28cca3dc721d044f4496bcca7ef4146fbf25c9",
    "c302f41d932a36cda7a3463093d18db78fce476de1a86297",
    "c0a0647eaab6a48753b033c56cb0f0900a2f5c4853375fd6",
    "14b690866abd5bb88b5f4828c1490002e6773fa2fa299b8f",
    "c302f41d932a36cda7a3462f9e9e916b5be8f1029ac4acc1",
    1, 30 },
  
  { "brainpoolp224r1",
    "68a5e62ca9ce6c1c299803a6c1530b514e182ad8b0042a59cad29f43",
    "2580f63ccfe44138870713b1a92369e33e2135d266dbb372386c400b",
    "d7c134aa264366862a18302575d1d787b09f075797da89f57ec8c0ff",
    "0d9029ad2c7e5cf4340823b2a87dc68c9e4ce3174c1e6efdee12c07d",
    "58aa56f772c0726f24c6b89e4ecdac24354b9e99caa3f6d3761402cd",
    "d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939f",
    1, 35 },

  { "brainpoolp256r1",
    "7d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9",
    "26dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b6",
    "a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377",
    "8bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262",
    "547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997",
    "a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7",
    1, 40 },
  
  { "brainpoolp320r1",
    "3ee30b568fbab0f883ccebd46d3f3bb8a2a73513f5eb79da66190eb085ffa9f492f375a97d860eb4",
    "520883949dfdbc42d3ad198640688a6fe13f41349554b49acc31dccd884539816f5eb4ac8fb1f1a6",
    "d35e472036bc4fb7e13c785ed201e065f98fcfa6f6f40def4f92b9ec7893ec28fcd412b1f1b32e27",
    "43bd7e9afb53d8b85289bcc48ee5bfe6f20137d10a087eb6e7871e2a10a599c710af8d0d39e20611",
    "14fdd05545ec1cc8ab4093247f77275e0743ffed117182eaa9c77877aaac6ac7d35245d1692e8ee1",
    "d35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e98691555b44c59311",
    1, 50 },

  { "brainpoolp384r1",
    "7bc382c63d8c150c3c72080ace05afa0c2bea28e4fb22787139165efba91f90f8aa5814a503ad4eb04a8c7dd22ce2826",
    "04a8c7dd22ce28268b39b55416f0447c2fb77de107dcd2a62e880ea53eeb62d57cb4390295dbc9943ab78696fa504c11",
    "8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123acd3a729901d1a71874700133107ec53",
    "1d1c64f068cf45ffa2a63a81b7c13f6b8847a3e77ef14fe3db7fcafe0cbd10e8e826e03436d646aaef87b2e247d4af1e",
    "8abe1d7520f9c2a45cb1eb8e95cfd55262b70b29feec5864e19c054ff99129280e4646217791811142820341263c5315",
    "8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7cf3ab6af6b7fc3103b883202e9046565",
    1, 60 },

  { "brainpoolp512r1",
    "7830a3318b603b89e2327145ac234cc594cbdd8d3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94ca",
    "3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94cadc083e67984050b75ebae5dd2809bd638016f723",
    "aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca703308717d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3",
    "81aee4bdd82ed9645a21322e9c4c6a9385ed9f70b5d916c1b43b62eef4d0098eff3b1f78e2d0d48d50d1687b93b97d5f7c6d5047406a5e688b352209bcb9f822",
    "7dde385d566332ecc0eabfa9cf7822fdf209f70024a57b1aa000c55b881f8111b2dcde494a5f485e5bca4bd88a2763aed1ca2b2fa8f0540678cd1e0f3ad80892",
    "aadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90069",
    1, 79 },
};

/******************************************************************************/

#define SCAN(x, s) do {                                            \
    assert(! gcry_mpi_scan(x, GCRYMPI_FMT_HEX, s, 0, NULL));       \
    gcry_mpi_set_flag(*x, GCRYMPI_FLAG_SECURE);		           \
  } while(0)

static struct curve_params* load_curve(const struct curve *c)
{
  gcry_mpi_t h;
  struct curve_params *cp;
  struct domain_params *dp;

  if (! (cp = malloc(sizeof(struct curve_params))))
    return NULL;

  cp->name = c->name;

  dp = &cp->dp;
  SCAN(&dp->a, c->a);
  SCAN(&dp->b, c->b);
  SCAN(&dp->m, c->m);
  SCAN(&dp->order, c->order);
  SCAN(&dp->base.x, c->base_x);
  SCAN(&dp->base.y, c->base_y);
  dp->cofactor = c->cofactor;

  h = gcry_mpi_new(0);

  gcry_mpi_add(h, dp->m, dp->m);
  gcry_mpi_sub_ui(h, h, 1);
  cp->pk_len_bin = get_serialization_len(h, DF_BIN);
  cp->pk_len_compact = get_serialization_len(h, DF_COMPACT);

  gcry_mpi_mul(h, dp->order, dp->order);
  gcry_mpi_sub_ui(h, h, 1);
  cp->sig_len_bin = get_serialization_len(h, DF_BIN);
  cp->sig_len_compact = get_serialization_len(h, DF_COMPACT);

  cp->dh_len_bin = (gcry_mpi_get_nbits(dp->order) / 2 + 7) / 8;
  if (cp->dh_len_bin > 32)
    cp->dh_len_bin = 32;

  gcry_mpi_set_ui(h, 0);
  gcry_mpi_set_bit(h, 8 * cp->dh_len_bin);
  gcry_mpi_sub_ui(h, h, 1);
  cp->dh_len_compact = get_serialization_len(h, DF_COMPACT);

  cp->elem_len_bin = get_serialization_len(dp->m, DF_BIN);
  cp->order_len_bin = get_serialization_len(dp->order, DF_BIN);

#if 0   /* enable this when adding a new curve to do some sanity checks */
  do {
    struct affine_point p;
    if (! gcry_mpi_cmp_ui(dp->b, 0)) {
      fprintf(stderr, "FATAL: b == 0\n");
      exit(1);
    }
    if (cp->pk_len_compact != c->pk_len_compact) {
      fprintf(stderr, "FATAL: c->pk_len_compact != %zd\n", cp->pk_len_compact);
      exit(1);
    }
    if (! point_on_curve(&dp->base, dp)) {
      fprintf(stderr, "FATAL: base point not on curve!\n");
      exit(1);
    }
    p = pointmul(&dp->base, dp->order, dp);
    if (! point_is_zero(&p)) {
      fprintf(stderr, "FATAL: wrong point order!\n");
      exit(1);
    }
    point_release(&p);
    
    gcry_mpi_mul_ui(h, dp->order, dp->cofactor);
    gcry_mpi_sub(h, h, dp->m);
    gcry_mpi_sub_ui(h, h, 1);
    gcry_mpi_mul(h, h, h);
    gcry_mpi_rshift(h, h, 2);
    if (gcry_mpi_cmp(h, dp->m) > 0) {
      fprintf(stderr, "FATAL: invalid cofactor!\n");
      exit(1);
    }
  } while(0);
#endif

  assert(cp->pk_len_bin <= MAX_PK_LEN_BIN);
  assert(cp->pk_len_compact <= MAX_PK_LEN_COMPACT);
  assert(cp->sig_len_bin <= MAX_SIG_LEN_BIN);
  assert(cp->sig_len_compact <= MAX_SIG_LEN_COMPACT);
  assert(cp->dh_len_bin <= MAX_DH_LEN_BIN);
  assert(cp->dh_len_compact <= MAX_DH_LEN_COMPACT);
  assert(cp->elem_len_bin <= MAX_ELEM_LEN_BIN);
  assert(cp->order_len_bin <= MAX_ORDER_LEN_BIN);

  gcry_mpi_release(h);
  return cp;
}

struct curve_params* curve_by_name(const char *name)
{
  const struct curve *c = curves;
  int i;
  for(i = 0; i < CURVE_NUM; i++, c++)
    if (strstr(c->name, name))
      return load_curve(c);
  return NULL;
}

struct curve_params* curve_by_pk_len_compact(size_t len)
{
  const struct curve *c = curves;
  int i;
  for(i = 0; i < CURVE_NUM; i++, c++)
    if (c->pk_len_compact == len)
      return load_curve(c);
  return NULL;
}

void curve_release(struct curve_params *cp)
{
  struct domain_params *dp = &cp->dp;
  gcry_mpi_release(dp->a);
  gcry_mpi_release(dp->b);
  gcry_mpi_release(dp->m);
  gcry_mpi_release(dp->order);
  gcry_mpi_release(dp->base.x);
  gcry_mpi_release(dp->base.y);
  free(cp);
}
