blob: fcbd8e3a9d6d7ee695f0361fef3b2b17aaa33241 [file] [log] [blame]
/* ecc.c - Elliptic Curve Cryptography
Copyright (C) 2007, 2008 Free Software Foundation, Inc.
This file is part of Libgcrypt.
Libgcrypt 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 2.1 of
the License, or (at your option) any later version.
Libgcrypt 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
USA. */
/* This code is originally based on the Patch 0.1.6 for the gnupg
1.4.x branch as retrieved on 2007-03-21 from
http://www.calcurco.cat/eccGnuPG/src/gnupg-1.4.6-ecc0.2.0beta1.diff.bz2
The original authors are:
Written by
Sergi Blanch i Torne <d4372211 at alumnes.eup.udl.es>,
Ramiro Moreno Chiral <ramiro at eup.udl.es>
Maintainers
Sergi Blanch i Torne
Ramiro Moreno Chiral
Mikael Mylnikov (mmr)
For use in Libgcrypt the code has been heavily modified and cleaned
up. In fact there is not much left of the orginally code except for
some variable names and the text book implementaion of the sign and
verification algorithms. The arithmetic functions have entirely
been rewritten and moved to mpi/ec.c. */
/* TODO:
- If we support point compression we need to decide how to compute
the keygrip - it should not change due to compression.
- In mpi/ec.c we use mpi_powm for x^2 mod p: Either implement a
special case in mpi_powm or check whether mpi_mulm is faster.
- Decide whether we should hide the mpi_point_t definition.
- Support more than just ECDSA.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "g10lib.h"
#include "mpi.h"
#include "cipher.h"
/* Definition of a curve. */
typedef struct
{
gcry_mpi_t p; /* Prime specifying the field GF(p). */
gcry_mpi_t a; /* First coefficient of the Weierstrass equation. */
gcry_mpi_t b; /* Second coefficient of the Weierstrass equation. */
mpi_point_t G; /* Base point (generator). */
gcry_mpi_t n; /* Order of G. */
} elliptic_curve_t;
typedef struct
{
elliptic_curve_t E;
mpi_point_t Q; /* Q = [d]G */
} ECC_public_key;
typedef struct
{
elliptic_curve_t E;
mpi_point_t Q;
gcry_mpi_t d;
} ECC_secret_key;
/* This tables defines aliases for curve names. */
static const struct
{
const char *name; /* Our name. */
const char *other; /* Other name. */
} curve_aliases[] =
{
{ "NIST P-192", "1.2.840.10045.3.1.1" }, /* X9.62 OID */
{ "NIST P-192", "prime192v1" }, /* X9.62 name. */
{ "NIST P-192", "secp192r1" }, /* SECP name. */
{ "NIST P-224", "secp224r1" },
{ "NIST P-224", "1.3.132.0.33" }, /* SECP OID. */
{ "NIST P-256", "1.2.840.10045.3.1.7" }, /* From NIST SP 800-78-1. */
{ "NIST P-256", "prime256v1" },
{ "NIST P-256", "secp256r1" },
{ "NIST P-384", "secp384r1" },
{ "NIST P-384", "1.3.132.0.34" },
{ "NIST P-521", "secp521r1" },
{ "NIST P-521", "1.3.132.0.35" },
{ "brainpoolP160r1", "1.3.36.3.3.2.8.1.1.1" },
{ "brainpoolP192r1", "1.3.36.3.3.2.8.1.1.3" },
{ "brainpoolP224r1", "1.3.36.3.3.2.8.1.1.5" },
{ "brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7" },
{ "brainpoolP320r1", "1.3.36.3.3.2.8.1.1.9" },
{ "brainpoolP384r1", "1.3.36.3.3.2.8.1.1.11"},
{ "brainpoolP512r1", "1.3.36.3.3.2.8.1.1.13"},
{ NULL, NULL}
};
/* This static table defines all available curves. */
static const struct
{
const char *desc; /* Description of the curve. */
unsigned int nbits; /* Number of bits. */
unsigned int fips:1; /* True if this is a FIPS140-2 approved curve. */
const char *p; /* Order of the prime field. */
const char *a, *b; /* The coefficients. */
const char *n; /* The order of the base point. */
const char *g_x, *g_y; /* Base point. */
} domain_parms[] =
{
{
"NIST P-192", 192, 1,
"0xfffffffffffffffffffffffffffffffeffffffffffffffff",
"0xfffffffffffffffffffffffffffffffefffffffffffffffc",
"0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1",
"0xffffffffffffffffffffffff99def836146bc9b1b4d22831",
"0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012",
"0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811"
},
{
"NIST P-224", 224, 1,
"0xffffffffffffffffffffffffffffffff000000000000000000000001",
"0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe",
"0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4",
"0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d" ,
"0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21",
"0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"
},
{
"NIST P-256", 256, 1,
"0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff",
"0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc",
"0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
"0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
"0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
"0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5"
},
{
"NIST P-384", 384, 1,
"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
"ffffffff0000000000000000ffffffff",
"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
"ffffffff0000000000000000fffffffc",
"0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875a"
"c656398d8a2ed19d2a85c8edd3ec2aef",
"0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf"
"581a0db248b0a77aecec196accc52973",
"0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38"
"5502f25dbf55296c3a545e3872760ab7",
"0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c0"
"0a60b1ce1d7e819d7a431d7c90ea0e5f"
},
{
"NIST P-521", 521, 1,
"0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc",
"0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef10"
"9e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00",
"0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"ffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409",
"0xc6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3d"
"baa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66",
"0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e6"
"62c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650"
},
{ "brainpoolP160r1", 160, 0,
"0xe95e4a5f737059dc60dfc7ad95b3d8139515620f",
"0x340e7be2a280eb74e2be61bada745d97e8f7c300",
"0x1e589a8595423412134faa2dbdec95c8d8675e58",
"0xe95e4a5f737059dc60df5991d45029409e60fc09",
"0xbed5af16ea3f6a4f62938c4631eb5af7bdbcdbc3",
"0x1667cb477a1a8ec338f94741669c976316da6321"
},
{ "brainpoolP192r1", 192, 0,
"0xc302f41d932a36cda7a3463093d18db78fce476de1a86297",
"0x6a91174076b1e0e19c39c031fe8685c1cae040e5c69a28ef",
"0x469a28ef7c28cca3dc721d044f4496bcca7ef4146fbf25c9",
"0xc302f41d932a36cda7a3462f9e9e916b5be8f1029ac4acc1",
"0xc0a0647eaab6a48753b033c56cb0f0900a2f5c4853375fd6",
"0x14b690866abd5bb88b5f4828c1490002e6773fa2fa299b8f"
},
{ "brainpoolP224r1", 224, 0,
"0xd7c134aa264366862a18302575d1d787b09f075797da89f57ec8c0ff",
"0x68a5e62ca9ce6c1c299803a6c1530b514e182ad8b0042a59cad29f43",
"0x2580f63ccfe44138870713b1a92369e33e2135d266dbb372386c400b",
"0xd7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939f",
"0x0d9029ad2c7e5cf4340823b2a87dc68c9e4ce3174c1e6efdee12c07d",
"0x58aa56f772c0726f24c6b89e4ecdac24354b9e99caa3f6d3761402cd"
},
{ "brainpoolP256r1", 256, 0,
"0xa9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377",
"0x7d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9",
"0x26dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b6",
"0xa9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7",
"0x8bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262",
"0x547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997"
},
{ "brainpoolP320r1", 320, 0,
"0xd35e472036bc4fb7e13c785ed201e065f98fcfa6f6f40def4f92b9ec7893ec28"
"fcd412b1f1b32e27",
"0x3ee30b568fbab0f883ccebd46d3f3bb8a2a73513f5eb79da66190eb085ffa9f4"
"92f375a97d860eb4",
"0x520883949dfdbc42d3ad198640688a6fe13f41349554b49acc31dccd88453981"
"6f5eb4ac8fb1f1a6",
"0xd35e472036bc4fb7e13c785ed201e065f98fcfa5b68f12a32d482ec7ee8658e9"
"8691555b44c59311",
"0x43bd7e9afb53d8b85289bcc48ee5bfe6f20137d10a087eb6e7871e2a10a599c7"
"10af8d0d39e20611",
"0x14fdd05545ec1cc8ab4093247f77275e0743ffed117182eaa9c77877aaac6ac7"
"d35245d1692e8ee1"
},
{ "brainpoolP384r1", 384, 0,
"0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123"
"acd3a729901d1a71874700133107ec53",
"0x7bc382c63d8c150c3c72080ace05afa0c2bea28e4fb22787139165efba91f90f"
"8aa5814a503ad4eb04a8c7dd22ce2826",
"0x04a8c7dd22ce28268b39b55416f0447c2fb77de107dcd2a62e880ea53eeb62d5"
"7cb4390295dbc9943ab78696fa504c11",
"0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b31f166e6cac0425a7"
"cf3ab6af6b7fc3103b883202e9046565",
"0x1d1c64f068cf45ffa2a63a81b7c13f6b8847a3e77ef14fe3db7fcafe0cbd10e8"
"e826e03436d646aaef87b2e247d4af1e",
"0x8abe1d7520f9c2a45cb1eb8e95cfd55262b70b29feec5864e19c054ff9912928"
"0e4646217791811142820341263c5315"
},
{ "brainpoolP512r1", 512, 0,
"0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330871"
"7d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3",
"0x7830a3318b603b89e2327145ac234cc594cbdd8d3df91610a83441caea9863bc"
"2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a72bf2c7b9e7c1ac4d77fc94ca",
"0x3df91610a83441caea9863bc2ded5d5aa8253aa10a2ef1c98b9ac8b57f1117a7"
"2bf2c7b9e7c1ac4d77fc94cadc083e67984050b75ebae5dd2809bd638016f723",
"0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330870"
"553e5c414ca92619418661197fac10471db1d381085ddaddb58796829ca90069",
"0x81aee4bdd82ed9645a21322e9c4c6a9385ed9f70b5d916c1b43b62eef4d0098e"
"ff3b1f78e2d0d48d50d1687b93b97d5f7c6d5047406a5e688b352209bcb9f822",
"0x7dde385d566332ecc0eabfa9cf7822fdf209f70024a57b1aa000c55b881f8111"
"b2dcde494a5f485e5bca4bd88a2763aed1ca2b2fa8f0540678cd1e0f3ad80892"
},
{ NULL, 0, 0, NULL, NULL, NULL, NULL }
};
/* Registered progress function and its callback value. */
static void (*progress_cb) (void *, const char*, int, int, int);
static void *progress_cb_data;
#define point_init(a) _gcry_mpi_ec_point_init ((a))
#define point_free(a) _gcry_mpi_ec_point_free ((a))
/* Local prototypes. */
static gcry_mpi_t gen_k (gcry_mpi_t p, int security_level);
static void test_keys (ECC_secret_key * sk, unsigned int nbits);
static int check_secret_key (ECC_secret_key * sk);
static gpg_err_code_t sign (gcry_mpi_t input, ECC_secret_key *skey,
gcry_mpi_t r, gcry_mpi_t s);
static gpg_err_code_t verify (gcry_mpi_t input, ECC_public_key *pkey,
gcry_mpi_t r, gcry_mpi_t s);
static gcry_mpi_t gen_y_2 (gcry_mpi_t x, elliptic_curve_t * base);
void
_gcry_register_pk_ecc_progress (void (*cb) (void *, const char *,
int, int, int),
void *cb_data)
{
progress_cb = cb;
progress_cb_data = cb_data;
}
/* static void */
/* progress (int c) */
/* { */
/* if (progress_cb) */
/* progress_cb (progress_cb_data, "pk_ecc", c, 0, 0); */
/* } */
/* Set the value from S into D. */
static void
point_set (mpi_point_t *d, mpi_point_t *s)
{
mpi_set (d->x, s->x);
mpi_set (d->y, s->y);
mpi_set (d->z, s->z);
}
/*
* Release a curve object.
*/
static void
curve_free (elliptic_curve_t *E)
{
mpi_free (E->p); E->p = NULL;
mpi_free (E->a); E->a = NULL;
mpi_free (E->b); E->b = NULL;
point_free (&E->G);
mpi_free (E->n); E->n = NULL;
}
/*
* Return a copy of a curve object.
*/
static elliptic_curve_t
curve_copy (elliptic_curve_t E)
{
elliptic_curve_t R;
R.p = mpi_copy (E.p);
R.a = mpi_copy (E.a);
R.b = mpi_copy (E.b);
point_init (&R.G);
point_set (&R.G, &E.G);
R.n = mpi_copy (E.n);
return R;
}
/* Helper to scan a hex string. */
static gcry_mpi_t
scanval (const char *string)
{
gpg_error_t err;
gcry_mpi_t val;
err = gcry_mpi_scan (&val, GCRYMPI_FMT_HEX, string, 0, NULL);
if (err)
log_fatal ("scanning ECC parameter failed: %s\n", gpg_strerror (err));
return val;
}
/****************
* Solve the right side of the equation that defines a curve.
*/
static gcry_mpi_t
gen_y_2 (gcry_mpi_t x, elliptic_curve_t *base)
{
gcry_mpi_t three, x_3, axb, y;
three = mpi_alloc_set_ui (3);
x_3 = mpi_new (0);
axb = mpi_new (0);
y = mpi_new (0);
mpi_powm (x_3, x, three, base->p);
mpi_mulm (axb, base->a, x, base->p);
mpi_addm (axb, axb, base->b, base->p);
mpi_addm (y, x_3, axb, base->p);
mpi_free (x_3);
mpi_free (axb);
mpi_free (three);
return y; /* The quadratic value of the coordinate if it exist. */
}
/* Generate a random secret scalar k with an order of p
At the beginning this was identical to the code is in elgamal.c.
Later imporved by mmr. Further simplified by wk. */
static gcry_mpi_t
gen_k (gcry_mpi_t p, int security_level)
{
gcry_mpi_t k;
unsigned int nbits;
nbits = mpi_get_nbits (p);
k = mpi_snew (nbits);
if (DBG_CIPHER)
log_debug ("choosing a random k of %u bits\n", nbits);
gcry_mpi_randomize (k, nbits, security_level);
mpi_mod (k, k, p); /* k = k mod p */
return k;
}
/****************
* Generate the crypto system setup.
* As of now the fix NIST recommended values are used.
* The subgroup generator point is in another function: gen_big_point.
*/
static gpg_err_code_t
generate_curve (unsigned int nbits, const char *name,
elliptic_curve_t *curve, unsigned int *r_nbits)
{
int idx, aliasno;
if (name)
{
/* First check nor native curves. */
for (idx = 0; domain_parms[idx].desc; idx++)
if (!strcmp (name, domain_parms[idx].desc))
break;
/* If not found consult the alias table. */
if (!domain_parms[idx].desc)
{
for (aliasno = 0; curve_aliases[aliasno].name; aliasno++)
if (!strcmp (name, curve_aliases[aliasno].other))
break;
if (curve_aliases[aliasno].name)
{
for (idx = 0; domain_parms[idx].desc; idx++)
if (!strcmp (curve_aliases[aliasno].name,
domain_parms[idx].desc))
break;
}
}
}
else
{
for (idx = 0; domain_parms[idx].desc; idx++)
if (nbits == domain_parms[idx].nbits)
break;
}
if (!domain_parms[idx].desc)
return GPG_ERR_INV_VALUE;
/* In fips mode we only support NIST curves. Note that it is
possible to bypass this check by specifying the curve parameters
directly. */
if (fips_mode () && !domain_parms[idx].fips )
return GPG_ERR_NOT_SUPPORTED;
*r_nbits = domain_parms[idx].nbits;
curve->p = scanval (domain_parms[idx].p);
curve->a = scanval (domain_parms[idx].a);
curve->b = scanval (domain_parms[idx].b);
curve->n = scanval (domain_parms[idx].n);
curve->G.x = scanval (domain_parms[idx].g_x);
curve->G.y = scanval (domain_parms[idx].g_y);
curve->G.z = mpi_alloc_set_ui (1);
return 0;
}
/*
* First obtain the setup. Over the finite field randomize an scalar
* secret value, and calculate the public point.
*/
static gpg_err_code_t
generate_key (ECC_secret_key *sk, unsigned int nbits, const char *name,
gcry_mpi_t g_x, gcry_mpi_t g_y,
gcry_mpi_t q_x, gcry_mpi_t q_y)
{
gpg_err_code_t err;
elliptic_curve_t E;
gcry_mpi_t d;
mpi_point_t Q;
mpi_ec_t ctx;
err = generate_curve (nbits, name, &E, &nbits);
if (err)
return err;
if (DBG_CIPHER)
{
log_mpidump ("ecc generation p", E.p);
log_mpidump ("ecc generation a", E.a);
log_mpidump ("ecc generation b", E.b);
log_mpidump ("ecc generation n", E.n);
log_mpidump ("ecc generation Gx", E.G.x);
log_mpidump ("ecc generation Gy", E.G.y);
log_mpidump ("ecc generation Gz", E.G.z);
}
if (DBG_CIPHER)
log_debug ("choosing a random x of size %u\n", nbits);
d = gen_k (E.n, GCRY_VERY_STRONG_RANDOM);
/* Compute Q. */
point_init (&Q);
ctx = _gcry_mpi_ec_init (E.p, E.a);
_gcry_mpi_ec_mul_point (&Q, d, &E.G, ctx);
/* Copy the stuff to the key structures. */
sk->E.p = mpi_copy (E.p);
sk->E.a = mpi_copy (E.a);
sk->E.b = mpi_copy (E.b);
point_init (&sk->E.G);
point_set (&sk->E.G, &E.G);
sk->E.n = mpi_copy (E.n);
point_init (&sk->Q);
point_set (&sk->Q, &Q);
sk->d = mpi_copy (d);
/* We also return copies of G and Q in affine coordinates if
requested. */
if (g_x && g_y)
{
if (_gcry_mpi_ec_get_affine (g_x, g_y, &sk->E.G, ctx))
log_fatal ("ecc generate: Failed to get affine coordinates\n");
}
if (q_x && q_y)
{
if (_gcry_mpi_ec_get_affine (q_x, q_y, &sk->Q, ctx))
log_fatal ("ecc generate: Failed to get affine coordinates\n");
}
_gcry_mpi_ec_free (ctx);
point_free (&Q);
mpi_free (d);
curve_free (&E);
/* Now we can test our keys (this should never fail!). */
test_keys (sk, nbits - 64);
return 0;
}
/****************
* To verify correct skey it use a random information.
* First, encrypt and decrypt this dummy value,
* test if the information is recuperated.
* Second, test with the sign and verify functions.
*/
static void
test_keys (ECC_secret_key *sk, unsigned int nbits)
{
ECC_public_key pk;
gcry_mpi_t test = mpi_new (nbits);
mpi_point_t R_;
gcry_mpi_t c = mpi_new (nbits);
gcry_mpi_t out = mpi_new (nbits);
gcry_mpi_t r = mpi_new (nbits);
gcry_mpi_t s = mpi_new (nbits);
if (DBG_CIPHER)
log_debug ("Testing key.\n");
point_init (&R_);
pk.E = curve_copy (sk->E);
point_init (&pk.Q);
point_set (&pk.Q, &sk->Q);
gcry_mpi_randomize (test, nbits, GCRY_WEAK_RANDOM);
if (sign (test, sk, r, s) )
log_fatal ("ECDSA operation: sign failed\n");
if (verify (test, &pk, r, s))
{
log_fatal ("ECDSA operation: sign, verify failed\n");
}
if (DBG_CIPHER)
log_debug ("ECDSA operation: sign, verify ok.\n");
point_free (&pk.Q);
curve_free (&pk.E);
point_free (&R_);
mpi_free (s);
mpi_free (r);
mpi_free (out);
mpi_free (c);
mpi_free (test);
}
/****************
* To check the validity of the value, recalculate the correspondence
* between the public value and the secret one.
*/
static int
check_secret_key (ECC_secret_key * sk)
{
mpi_point_t Q;
gcry_mpi_t y_2, y2 = mpi_alloc (0);
mpi_ec_t ctx;
/* ?primarity test of 'p' */
/* (...) //!! */
/* G in E(F_p) */
y_2 = gen_y_2 (sk->E.G.x, &sk->E); /* y^2=x^3+a*x+b */
mpi_mulm (y2, sk->E.G.y, sk->E.G.y, sk->E.p); /* y^2=y*y */
if (mpi_cmp (y_2, y2))
{
if (DBG_CIPHER)
log_debug ("Bad check: Point 'G' does not belong to curve 'E'!\n");
return (1);
}
/* G != PaI */
if (!mpi_cmp_ui (sk->E.G.z, 0))
{
if (DBG_CIPHER)
log_debug ("Bad check: 'G' cannot be Point at Infinity!\n");
return (1);
}
point_init (&Q);
ctx = _gcry_mpi_ec_init (sk->E.p, sk->E.a);
_gcry_mpi_ec_mul_point (&Q, sk->E.n, &sk->E.G, ctx);
if (mpi_cmp_ui (Q.z, 0))
{
if (DBG_CIPHER)
log_debug ("check_secret_key: E is not a curve of order n\n");
point_free (&Q);
_gcry_mpi_ec_free (ctx);
return 1;
}
/* pubkey cannot be PaI */
if (!mpi_cmp_ui (sk->Q.z, 0))
{
if (DBG_CIPHER)
log_debug ("Bad check: Q can not be a Point at Infinity!\n");
_gcry_mpi_ec_free (ctx);
return (1);
}
/* pubkey = [d]G over E */
_gcry_mpi_ec_mul_point (&Q, sk->d, &sk->E.G, ctx);
if ((Q.x == sk->Q.x) && (Q.y == sk->Q.y) && (Q.z == sk->Q.z))
{
if (DBG_CIPHER)
log_debug
("Bad check: There is NO correspondence between 'd' and 'Q'!\n");
_gcry_mpi_ec_free (ctx);
return (1);
}
_gcry_mpi_ec_free (ctx);
point_free (&Q);
return 0;
}
/*
* Return the signature struct (r,s) from the message hash. The caller
* must have allocated R and S.
*/
static gpg_err_code_t
sign (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s)
{
gpg_err_code_t err = 0;
gcry_mpi_t k, dr, sum, k_1, x;
mpi_point_t I;
mpi_ec_t ctx;
k = NULL;
dr = mpi_alloc (0);
sum = mpi_alloc (0);
k_1 = mpi_alloc (0);
x = mpi_alloc (0);
point_init (&I);
mpi_set_ui (s, 0);
mpi_set_ui (r, 0);
ctx = _gcry_mpi_ec_init (skey->E.p, skey->E.a);
while (!mpi_cmp_ui (s, 0)) /* s == 0 */
{
while (!mpi_cmp_ui (r, 0)) /* r == 0 */
{
/* Note, that we are guaranteed to enter this loop at least
once because r has been intialized to 0. We can't use a
do_while because we want to keep the value of R even if S
has to be recomputed. */
mpi_free (k);
k = gen_k (skey->E.n, GCRY_STRONG_RANDOM);
_gcry_mpi_ec_mul_point (&I, k, &skey->E.G, ctx);
if (_gcry_mpi_ec_get_affine (x, NULL, &I, ctx))
{
if (DBG_CIPHER)
log_debug ("ecc sign: Failed to get affine coordinates\n");
err = GPG_ERR_BAD_SIGNATURE;
goto leave;
}
mpi_mod (r, x, skey->E.n); /* r = x mod n */
}
mpi_mulm (dr, skey->d, r, skey->E.n); /* dr = d*r mod n */
mpi_addm (sum, input, dr, skey->E.n); /* sum = hash + (d*r) mod n */
mpi_invm (k_1, k, skey->E.n); /* k_1 = k^(-1) mod n */
mpi_mulm (s, k_1, sum, skey->E.n); /* s = k^(-1)*(hash+(d*r)) mod n */
}
leave:
_gcry_mpi_ec_free (ctx);
point_free (&I);
mpi_free (x);
mpi_free (k_1);
mpi_free (sum);
mpi_free (dr);
mpi_free (k);
return err;
}
/*
* Check if R and S verifies INPUT.
*/
static gpg_err_code_t
verify (gcry_mpi_t input, ECC_public_key *pkey, gcry_mpi_t r, gcry_mpi_t s)
{
gpg_err_code_t err = 0;
gcry_mpi_t h, h1, h2, x, y;
mpi_point_t Q, Q1, Q2;
mpi_ec_t ctx;
if( !(mpi_cmp_ui (r, 0) > 0 && mpi_cmp (r, pkey->E.n) < 0) )
return GPG_ERR_BAD_SIGNATURE; /* Assertion 0 < r < n failed. */
if( !(mpi_cmp_ui (s, 0) > 0 && mpi_cmp (s, pkey->E.n) < 0) )
return GPG_ERR_BAD_SIGNATURE; /* Assertion 0 < s < n failed. */
h = mpi_alloc (0);
h1 = mpi_alloc (0);
h2 = mpi_alloc (0);
x = mpi_alloc (0);
y = mpi_alloc (0);
point_init (&Q);
point_init (&Q1);
point_init (&Q2);
ctx = _gcry_mpi_ec_init (pkey->E.p, pkey->E.a);
/* h = s^(-1) (mod n) */
mpi_invm (h, s, pkey->E.n);
/* log_mpidump (" h", h); */
/* h1 = hash * s^(-1) (mod n) */
mpi_mulm (h1, input, h, pkey->E.n);
/* log_mpidump (" h1", h1); */
/* Q1 = [ hash * s^(-1) ]G */
_gcry_mpi_ec_mul_point (&Q1, h1, &pkey->E.G, ctx);
/* log_mpidump ("Q1.x", Q1.x); */
/* log_mpidump ("Q1.y", Q1.y); */
/* log_mpidump ("Q1.z", Q1.z); */
/* h2 = r * s^(-1) (mod n) */
mpi_mulm (h2, r, h, pkey->E.n);
/* log_mpidump (" h2", h2); */
/* Q2 = [ r * s^(-1) ]Q */
_gcry_mpi_ec_mul_point (&Q2, h2, &pkey->Q, ctx);
/* log_mpidump ("Q2.x", Q2.x); */
/* log_mpidump ("Q2.y", Q2.y); */
/* log_mpidump ("Q2.z", Q2.z); */
/* Q = ([hash * s^(-1)]G) + ([r * s^(-1)]Q) */
_gcry_mpi_ec_add_points (&Q, &Q1, &Q2, ctx);
/* log_mpidump (" Q.x", Q.x); */
/* log_mpidump (" Q.y", Q.y); */
/* log_mpidump (" Q.z", Q.z); */
if (!mpi_cmp_ui (Q.z, 0))
{
if (DBG_CIPHER)
log_debug ("ecc verify: Rejected\n");
err = GPG_ERR_BAD_SIGNATURE;
goto leave;
}
if (_gcry_mpi_ec_get_affine (x, y, &Q, ctx))
{
if (DBG_CIPHER)
log_debug ("ecc verify: Failed to get affine coordinates\n");
err = GPG_ERR_BAD_SIGNATURE;
goto leave;
}
mpi_mod (x, x, pkey->E.n); /* x = x mod E_n */
if (mpi_cmp (x, r)) /* x != r */
{
if (DBG_CIPHER)
{
log_mpidump (" x", x);
log_mpidump (" y", y);
log_mpidump (" r", r);
log_mpidump (" s", s);
log_debug ("ecc verify: Not verified\n");
}
err = GPG_ERR_BAD_SIGNATURE;
goto leave;
}
if (DBG_CIPHER)
log_debug ("ecc verify: Accepted\n");
leave:
_gcry_mpi_ec_free (ctx);
point_free (&Q2);
point_free (&Q1);
point_free (&Q);
mpi_free (y);
mpi_free (x);
mpi_free (h2);
mpi_free (h1);
mpi_free (h);
return err;
}
/*********************************************
************** interface ******************
*********************************************/
static gcry_mpi_t
ec2os (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t p)
{
gpg_error_t err;
int pbytes = (mpi_get_nbits (p)+7)/8;
size_t n;
unsigned char *buf, *ptr;
gcry_mpi_t result;
buf = gcry_xmalloc ( 1 + 2*pbytes );
*buf = 04; /* Uncompressed point. */
ptr = buf+1;
err = gcry_mpi_print (GCRYMPI_FMT_USG, ptr, pbytes, &n, x);
if (err)
log_fatal ("mpi_print failed: %s\n", gpg_strerror (err));
if (n < pbytes)
{
memmove (ptr+(pbytes-n), ptr, n);
memset (ptr, 0, (pbytes-n));
}
ptr += pbytes;
err = gcry_mpi_print (GCRYMPI_FMT_USG, ptr, pbytes, &n, y);
if (err)
log_fatal ("mpi_print failed: %s\n", gpg_strerror (err));
if (n < pbytes)
{
memmove (ptr+(pbytes-n), ptr, n);
memset (ptr, 0, (pbytes-n));
}
err = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, buf, 1+2*pbytes, NULL);
if (err)
log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err));
gcry_free (buf);
mpi_free (x);
mpi_free (y);
return result;
}
/* RESULT must have been initialized and is set on success to the
point given by VALUE. */
static gcry_error_t
os2ec (mpi_point_t *result, gcry_mpi_t value)
{
gcry_error_t err;
size_t n;
unsigned char *buf;
gcry_mpi_t x, y;
n = (mpi_get_nbits (value)+7)/8;
buf = gcry_xmalloc (n);
err = gcry_mpi_print (GCRYMPI_FMT_USG, buf, n, &n, value);
if (err)
{
gcry_free (buf);
return err;
}
if (n < 1)
{
gcry_free (buf);
return GPG_ERR_INV_OBJ;
}
if (*buf != 4)
{
gcry_free (buf);
return GPG_ERR_NOT_IMPLEMENTED; /* No support for point compression. */
}
if ( ((n-1)%2) )
{
gcry_free (buf);
return GPG_ERR_INV_OBJ;
}
n = (n-1)/2;
err = gcry_mpi_scan (&x, GCRYMPI_FMT_USG, buf+1, n, NULL);
if (err)
{
gcry_free (buf);
return err;
}
err = gcry_mpi_scan (&y, GCRYMPI_FMT_USG, buf+1+n, n, NULL);
gcry_free (buf);
if (err)
{
mpi_free (x);
return err;
}
mpi_set (result->x, x);
mpi_set (result->y, y);
mpi_set_ui (result->z, 1);
mpi_free (x);
mpi_free (y);
return 0;
}
/* Extended version of ecc_generate. */
static gcry_err_code_t
ecc_generate_ext (int algo, unsigned int nbits, unsigned long evalue,
const gcry_sexp_t genparms,
gcry_mpi_t *skey, gcry_mpi_t **retfactors,
gcry_sexp_t *r_extrainfo)
{
gpg_err_code_t ec;
ECC_secret_key sk;
gcry_mpi_t g_x, g_y, q_x, q_y;
char *curve_name = NULL;
gcry_sexp_t l1;
(void)algo;
(void)evalue;
(void)r_extrainfo;
if (genparms)
{
/* Parse the optional "curve" parameter. */
l1 = gcry_sexp_find_token (genparms, "curve", 0);
if (l1)
{
curve_name = _gcry_sexp_nth_string (l1, 1);
gcry_sexp_release (l1);
if (!curve_name)
return GPG_ERR_INV_OBJ; /* No curve name or value too large. */
}
}
/* NBITS is required if no curve name has been given. */
if (!nbits && !curve_name)
return GPG_ERR_NO_OBJ; /* No NBITS parameter. */
g_x = mpi_new (0);
g_y = mpi_new (0);
q_x = mpi_new (0);
q_y = mpi_new (0);
ec = generate_key (&sk, nbits, curve_name, g_x, g_y, q_x, q_y);
gcry_free (curve_name);
if (ec)
return ec;
skey[0] = sk.E.p;
skey[1] = sk.E.a;
skey[2] = sk.E.b;
/* The function ec2os releases g_x and g_y. */
skey[3] = ec2os (g_x, g_y, sk.E.p);
skey[4] = sk.E.n;
/* The function ec2os releases g_x and g_y. */
skey[5] = ec2os (q_x, q_y, sk.E.p);
skey[6] = sk.d;
point_free (&sk.E.G);
point_free (&sk.Q);
/* Make an empty list of factors. */
*retfactors = gcry_calloc ( 1, sizeof **retfactors );
if (!*retfactors)
return gpg_err_code_from_syserror ();
return 0;
}
static gcry_err_code_t
ecc_generate (int algo, unsigned int nbits, unsigned long evalue,
gcry_mpi_t *skey, gcry_mpi_t **retfactors)
{
(void)evalue;
return ecc_generate_ext (algo, nbits, 0, NULL, skey, retfactors, NULL);
}
/* Return the parameters of the curve NAME. */
static gcry_err_code_t
ecc_get_param (const char *name, gcry_mpi_t *pkey)
{
gpg_err_code_t err;
unsigned int nbits;
elliptic_curve_t E;
mpi_ec_t ctx;
gcry_mpi_t g_x, g_y;
err = generate_curve (0, name, &E, &nbits);
if (err)
return err;
g_x = mpi_new (0);
g_y = mpi_new (0);
ctx = _gcry_mpi_ec_init (E.p, E.a);
if (_gcry_mpi_ec_get_affine (g_x, g_y, &E.G, ctx))
log_fatal ("ecc get param: Failed to get affine coordinates\n");
_gcry_mpi_ec_free (ctx);
point_free (&E.G);
pkey[0] = E.p;
pkey[1] = E.a;
pkey[2] = E.b;
pkey[3] = ec2os (g_x, g_y, E.p);
pkey[4] = E.n;
pkey[5] = NULL;
return 0;
}
static gcry_err_code_t
ecc_check_secret_key (int algo, gcry_mpi_t *skey)
{
gpg_err_code_t err;
ECC_secret_key sk;
(void)algo;
if (!skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4] || !skey[5]
|| !skey[6] || !skey[7] || !skey[8] || !skey[9] || !skey[10])
return GPG_ERR_BAD_MPI;
sk.E.p = skey[0];
sk.E.a = skey[1];
sk.E.b = skey[2];
point_init (&sk.E.G);
err = os2ec (&sk.E.G, skey[3]);
if (err)
{
point_free (&sk.E.G);
return err;
}
sk.E.n = skey[4];
point_init (&sk.Q);
err = os2ec (&sk.Q, skey[5]);
if (err)
{
point_free (&sk.E.G);
point_free (&sk.Q);
return err;
}
sk.d = skey[6];
if (check_secret_key (&sk))
{
point_free (&sk.E.G);
point_free (&sk.Q);
return GPG_ERR_BAD_SECKEY;
}
point_free (&sk.E.G);
point_free (&sk.Q);
return 0;
}
static gcry_err_code_t
ecc_sign (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *skey)
{
gpg_err_code_t err;
ECC_secret_key sk;
(void)algo;
if (!data || !skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4]
|| !skey[5] || !skey[6] )
return GPG_ERR_BAD_MPI;
sk.E.p = skey[0];
sk.E.a = skey[1];
sk.E.b = skey[2];
point_init (&sk.E.G);
err = os2ec (&sk.E.G, skey[3]);
if (err)
{
point_free (&sk.E.G);
return err;
}
sk.E.n = skey[4];
point_init (&sk.Q);
err = os2ec (&sk.Q, skey[5]);
if (err)
{
point_free (&sk.E.G);
point_free (&sk.Q);
return err;
}
sk.d = skey[6];
resarr[0] = mpi_alloc (mpi_get_nlimbs (sk.E.p));
resarr[1] = mpi_alloc (mpi_get_nlimbs (sk.E.p));
err = sign (data, &sk, resarr[0], resarr[1]);
if (err)
{
mpi_free (resarr[0]);
mpi_free (resarr[1]);
resarr[0] = NULL; /* Mark array as released. */
}
point_free (&sk.E.G);
point_free (&sk.Q);
return err;
}
static gcry_err_code_t
ecc_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey,
int (*cmp)(void *, gcry_mpi_t), void *opaquev)
{
gpg_err_code_t err;
ECC_public_key pk;
(void)algo;
(void)cmp;
(void)opaquev;
if (!data[0] || !data[1] || !hash || !pkey[0] || !pkey[1] || !pkey[2]
|| !pkey[3] || !pkey[4] || !pkey[5] )
return GPG_ERR_BAD_MPI;
pk.E.p = pkey[0];
pk.E.a = pkey[1];
pk.E.b = pkey[2];
point_init (&pk.E.G);
err = os2ec (&pk.E.G, pkey[3]);
if (err)
{
point_free (&pk.E.G);
return err;
}
pk.E.n = pkey[4];
point_init (&pk.Q);
err = os2ec (&pk.Q, pkey[5]);
if (err)
{
point_free (&pk.E.G);
point_free (&pk.Q);
return err;
}
err = verify (hash, &pk, data[0], data[1]);
point_free (&pk.E.G);
point_free (&pk.Q);
return err;
}
static unsigned int
ecc_get_nbits (int algo, gcry_mpi_t *pkey)
{
(void)algo;
return mpi_get_nbits (pkey[0]);
}
/* See rsa.c for a description of this function. */
static gpg_err_code_t
compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam)
{
static const char names[] = "pabgnq";
gpg_err_code_t ec = 0;
gcry_sexp_t l1;
gcry_mpi_t values[6];
int idx;
/* Clear the values for easier error cleanup. */
for (idx=0; idx < 6; idx++)
values[idx] = NULL;
/* Fill values with all available parameters. */
for (idx=0; idx < 6; idx++)
{
l1 = gcry_sexp_find_token (keyparam, names+idx, 1);
if (l1)
{
values[idx] = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
gcry_sexp_release (l1);
if (!values[idx])
{
ec = GPG_ERR_INV_OBJ;
goto leave;
}
}
}
/* Check whether a curve parameter is available and use that to fill
in missing values. */
l1 = gcry_sexp_find_token (keyparam, "curve", 5);
if (l1)
{
char *curve;
gcry_mpi_t tmpvalues[6];
for (idx = 0; idx < 6; idx++)
tmpvalues[idx] = NULL;
curve = _gcry_sexp_nth_string (l1, 1);
if (!curve)
{
ec = GPG_ERR_INV_OBJ; /* Name missing or out of core. */
goto leave;
}
ec = ecc_get_param (curve, tmpvalues);
gcry_free (curve);
if (ec)
goto leave;
for (idx = 0; idx < 6; idx++)
{
if (!values[idx])
values[idx] = tmpvalues[idx];
else
mpi_free (tmpvalues[idx]);
}
}
/* Check that all parameters are known and normalize all MPIs (that
should not be required but we use an internal fucntion later and
thus we better make 100% sure that they are normalized). */
for (idx = 0; idx < 6; idx++)
if (!values[idx])
{
ec = GPG_ERR_NO_OBJ;
goto leave;
}
else
_gcry_mpi_normalize (values[idx]);
/* Hash them all. */
for (idx = 0; idx < 6; idx++)
{
char buf[30];
unsigned char *rawmpi;
unsigned int rawmpilen;
rawmpi = _gcry_mpi_get_buffer (values[idx], &rawmpilen, NULL);
if (!rawmpi)
{
ec = gpg_err_code_from_syserror ();
goto leave;
}
snprintf (buf, sizeof buf, "(1:%c%u:", names[idx], rawmpilen);
gcry_md_write (md, buf, strlen (buf));
gcry_md_write (md, rawmpi, rawmpilen);
gcry_md_write (md, ")", 1);
gcry_free (rawmpi);
}
leave:
for (idx = 0; idx < 6; idx++)
_gcry_mpi_release (values[idx]);
return ec;
}
/*
Self-test section.
*/
static gpg_err_code_t
selftests_ecdsa (selftest_report_func_t report)
{
const char *what;
const char *errtxt;
what = "low-level";
errtxt = NULL; /*selftest ();*/
if (errtxt)
goto failed;
/* FIXME: need more tests. */
return 0; /* Succeeded. */
failed:
if (report)
report ("pubkey", GCRY_PK_ECDSA, what, errtxt);
return GPG_ERR_SELFTEST_FAILED;
}
/* Run a full self-test for ALGO and return 0 on success. */
static gpg_err_code_t
run_selftests (int algo, int extended, selftest_report_func_t report)
{
gpg_err_code_t ec;
(void)extended;
switch (algo)
{
case GCRY_PK_ECDSA:
ec = selftests_ecdsa (report);
break;
default:
ec = GPG_ERR_PUBKEY_ALGO;
break;
}
return ec;
}
static const char *ecdsa_names[] =
{
"ecdsa",
"ecc",
NULL,
};
gcry_pk_spec_t _gcry_pubkey_spec_ecdsa =
{
"ECDSA", ecdsa_names,
"pabgnq", "pabgnqd", "", "rs", "pabgnq",
GCRY_PK_USAGE_SIGN,
ecc_generate,
ecc_check_secret_key,
NULL,
NULL,
ecc_sign,
ecc_verify,
ecc_get_nbits
};
pk_extra_spec_t _gcry_pubkey_extraspec_ecdsa =
{
run_selftests,
ecc_generate_ext,
compute_keygrip,
ecc_get_param
};