| /* 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 |
| }; |
| |