diff options
author | Andrey Jivsov <openpgp@brainhub.org> | 2011-01-05 23:37:26 -0800 |
---|---|---|
committer | Andrey Jivsov <openpgp@brainhub.org> | 2011-01-05 23:37:26 -0800 |
commit | 2c32b631acc1637c1d7826bcdcecf6c0ae9ce7fc (patch) | |
tree | 56b2909448de37cf3621bfa9a76e0e4c444eaa92 | |
parent | de7bd128995c9cf6e471d1bb16cf4a2f80262c96 (diff) | |
download | libgcrypt-2c32b631acc1637c1d7826bcdcecf6c0ae9ce7fc.tar.gz |
Integrating http://code.google.com/p/gnupg-ecc/source/detail?r=15 .
This merge went smoothly and didn't need any code written (as opposed to parallel gnupg integration). The checked in code was tested through gnupg with 'gnupg --gen-key (select ECC)'.
This change corresponds to earlier check in gnupg project. This branch, ECC-INTEGRATION-1-5, corresponds to ECC-INTEGRATION-2-1 in gnupg project.
-rw-r--r-- | cipher/ecc.c | 476 | ||||
-rw-r--r-- | cipher/pubkey.c | 12 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | doc/gcrypt.texi | 4 | ||||
-rw-r--r-- | src/cipher.h | 1 |
5 files changed, 369 insertions, 126 deletions
diff --git a/cipher/ecc.c b/cipher/ecc.c index bcfab058..fdcbb4c4 100644 --- a/cipher/ecc.c +++ b/cipher/ecc.c @@ -58,7 +58,9 @@ #include "g10lib.h" #include "mpi.h" #include "cipher.h" +#include <assert.h> +#define MAX_ECC_OID_LEN 16 /* Definition of a curve. */ typedef struct @@ -68,6 +70,10 @@ typedef struct gcry_mpi_t b; /* Second coefficient of the Weierstrass equation. */ mpi_point_t G; /* Base point (generator). */ gcry_mpi_t n; /* Order of G. */ + /* one byte length, followed by DER representation of curve OID: + * N byte OID is encoded as N+1 bytes as follows: N x0 x1 ... xN + */ + byte name_oid[MAX_ECC_OID_LEN]; } elliptic_curve_t; @@ -120,22 +126,26 @@ static const struct { NULL, NULL} }; +static const byte curve_oid_NISTP256[] = { 8, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 }; +static const byte curve_oid_NISTP384[] = { 5, 0x2B, 0x81, 0x04, 0x00, 0x22 }; +static const byte curve_oid_NISTP521[] = { 5, 0x2B, 0x81, 0x04, 0x00, 0x23 }; - -/* This static table defines all available curves. */ -static const struct -{ +typedef 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 byte *name_oid; /* points to LEN + curve DER OID, optional */ 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[] = +} ecc_domain_parms_t; + +/* This static table defines all available curves. */ +static const ecc_domain_parms_t domain_parms[] = { { - "NIST P-192", 192, 1, + "NIST P-192", 192, 1, NULL, "0xfffffffffffffffffffffffffffffffeffffffffffffffff", "0xfffffffffffffffffffffffffffffffefffffffffffffffc", "0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", @@ -145,7 +155,7 @@ static const struct "0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811" }, { - "NIST P-224", 224, 1, + "NIST P-224", 224, 1, NULL, "0xffffffffffffffffffffffffffffffff000000000000000000000001", "0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe", "0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4", @@ -155,7 +165,7 @@ static const struct "0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34" }, { - "NIST P-256", 256, 1, + "NIST P-256", 256, 1, curve_oid_NISTP256, "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff", "0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc", "0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", @@ -165,7 +175,7 @@ static const struct "0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5" }, { - "NIST P-384", 384, 1, + "NIST P-384", 384, 1, curve_oid_NISTP384, "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" "ffffffff0000000000000000ffffffff", "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" @@ -181,7 +191,7 @@ static const struct "0a60b1ce1d7e819d7a431d7c90ea0e5f" }, { - "NIST P-521", 521, 1, + "NIST P-521", 521, 1, curve_oid_NISTP521, "0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" @@ -197,7 +207,7 @@ static const struct "62c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650" }, - { "brainpoolP160r1", 160, 0, + { "brainpoolP160r1", 160, 0, NULL, "0xe95e4a5f737059dc60dfc7ad95b3d8139515620f", "0x340e7be2a280eb74e2be61bada745d97e8f7c300", "0x1e589a8595423412134faa2dbdec95c8d8675e58", @@ -206,7 +216,7 @@ static const struct "0x1667cb477a1a8ec338f94741669c976316da6321" }, - { "brainpoolP192r1", 192, 0, + { "brainpoolP192r1", 192, 0, NULL, "0xc302f41d932a36cda7a3463093d18db78fce476de1a86297", "0x6a91174076b1e0e19c39c031fe8685c1cae040e5c69a28ef", "0x469a28ef7c28cca3dc721d044f4496bcca7ef4146fbf25c9", @@ -215,7 +225,7 @@ static const struct "0x14b690866abd5bb88b5f4828c1490002e6773fa2fa299b8f" }, - { "brainpoolP224r1", 224, 0, + { "brainpoolP224r1", 224, 0, NULL, "0xd7c134aa264366862a18302575d1d787b09f075797da89f57ec8c0ff", "0x68a5e62ca9ce6c1c299803a6c1530b514e182ad8b0042a59cad29f43", "0x2580f63ccfe44138870713b1a92369e33e2135d266dbb372386c400b", @@ -224,7 +234,7 @@ static const struct "0x58aa56f772c0726f24c6b89e4ecdac24354b9e99caa3f6d3761402cd" }, - { "brainpoolP256r1", 256, 0, + { "brainpoolP256r1", 256, 0, NULL, "0xa9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377", "0x7d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9", "0x26dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b6", @@ -233,7 +243,7 @@ static const struct "0x547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997" }, - { "brainpoolP320r1", 320, 0, + { "brainpoolP320r1", 320, 0, NULL, "0xd35e472036bc4fb7e13c785ed201e065f98fcfa6f6f40def4f92b9ec7893ec28" "fcd412b1f1b32e27", "0x3ee30b568fbab0f883ccebd46d3f3bb8a2a73513f5eb79da66190eb085ffa9f4" @@ -248,7 +258,7 @@ static const struct "d35245d1692e8ee1" }, - { "brainpoolP384r1", 384, 0, + { "brainpoolP384r1", 384, 0, NULL, "0x8cb91e82a3386d280f5d6f7e50e641df152f7109ed5456b412b1da197fb71123" "acd3a729901d1a71874700133107ec53", "0x7bc382c63d8c150c3c72080ace05afa0c2bea28e4fb22787139165efba91f90f" @@ -263,7 +273,7 @@ static const struct "0e4646217791811142820341263c5315" }, - { "brainpoolP512r1", 512, 0, + { "brainpoolP512r1", 512, 0, NULL, "0xaadd9db8dbe9c48b3fd4e6ae33c9fc07cb308db3b3c9d20ed6639cca70330871" "7d4d9b009bc66842aecda12ae6a380e62881ff2f2d82c68528aa6056583a48f3", "0x7830a3318b603b89e2327145ac234cc594cbdd8d3df91610a83441caea9863bc" @@ -278,7 +288,7 @@ static const struct "b2dcde494a5f485e5bca4bd88a2763aed1ca2b2fa8f0540678cd1e0f3ad80892" }, - { NULL, 0, 0, NULL, NULL, NULL, NULL } + { NULL, 0, 0, NULL, NULL, NULL, NULL, NULL } }; @@ -349,6 +359,22 @@ curve_free (elliptic_curve_t *E) mpi_free (E->n); E->n = NULL; } +/* + * Release a PK object. + */ +static void ecc_pk_free( ECC_public_key *pk ) { + point_free (&pk->Q); + curve_free (&pk->E); +} + +/* + * Release a SK object. + */ +static void ecc_sk_free( ECC_secret_key *sk ) { + point_free (&sk->Q); + curve_free (&sk->E); + mpi_free (sk->d); sk->d = NULL; +} /* * Return a copy of a curve object. @@ -493,6 +519,9 @@ generate_curve (unsigned int nbits, const char *name, 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); + memset( curve->name_oid, 0, sizeof(curve->name_oid) ); + if( domain_parms[idx].name_oid != NULL ) + memcpy( curve->name_oid, domain_parms[idx].name_oid, domain_parms[idx].name_oid[0]+1 ); return 0; } @@ -521,13 +550,15 @@ generate_key (ECC_secret_key *sk, unsigned int nbits, const char *name, 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); + log_mpidump ("ecgen curve p", E.p); + log_mpidump ("ecgen curve a", E.a); + log_mpidump ("ecgen curve b", E.b); + log_mpidump ("ecgen curve n", E.n); + log_mpidump ("ecgen curve G.x", E.G.x); + /* log_mpidump ("ecc generation Gy", E.G.y); + log_mpidump ("ecc generation Gz", E.G.z); */ + + log_printf ("ecgen curve OID: [%d] ...%02X %02X\n", E.name_oid[0], E.name_oid[0]>0 ? E.name_oid[E.name_oid[0]-1] : 0, E.name_oid[E.name_oid[0]]); } random_level = transient_key ? GCRY_STRONG_RANDOM : GCRY_VERY_STRONG_RANDOM; @@ -545,6 +576,7 @@ generate_key (ECC_secret_key *sk, unsigned int nbits, const char *name, sk->E.p = mpi_copy (E.p); sk->E.a = mpi_copy (E.a); sk->E.b = mpi_copy (E.b); + memcpy(sk->E.name_oid, E.name_oid, sizeof(E.name_oid)); point_init (&sk->E.G); point_set (&sk->E.G, &E.G); sk->E.n = mpi_copy (E.n); @@ -556,12 +588,12 @@ generate_key (ECC_secret_key *sk, unsigned int nbits, const char *name, 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"); + log_fatal ("ecgen: 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"); + log_fatal ("ecgen: Failed to get affine coordinates\n"); } _gcry_mpi_ec_free (ctx); @@ -571,7 +603,7 @@ generate_key (ECC_secret_key *sk, unsigned int nbits, const char *name, /* Now we can test our keys (this should never fail!). */ test_keys (sk, nbits - 64); - + return 0; } @@ -615,8 +647,7 @@ test_keys (ECC_secret_key *sk, unsigned int nbits) if (DBG_CIPHER) log_debug ("ECDSA operation: sign, verify ok.\n"); - point_free (&pk.Q); - curve_free (&pk.E); + ecc_pk_free( &pk ); point_free (&R_); mpi_free (s); @@ -849,7 +880,45 @@ verify (gcry_mpi_t input, ECC_public_key *pkey, gcry_mpi_t r, gcry_mpi_t s) return err; } +/* lookup named curve and fill in internal curve parameters + * returns GPG_ERR_NOT_FOUND for an unknown OID + */ +static int +fill_in_curve( const byte name_oid[], elliptic_curve_t *curve ) { + int i; + const ecc_domain_parms_t *p; + if( name_oid == NULL || name_oid[0] == 0 ) { + log_debug ("ecc OID is malformed\n"); + return GPG_ERR_INV_ARG; + } + + for (i = 0; domain_parms[i].desc; i++) { + p = domain_parms + i; + if( p->name_oid == NULL || p->name_oid[0] != name_oid[0] ) + continue; + if ( memcmp( p->name_oid, name_oid, name_oid[0]+1 )==0 ) + break; + } + + if( ! p->desc ) { + log_debug ("ecc OID is not recognized\n"); + return GPG_ERR_NOT_FOUND; + } + + // TODO: there is no reason why these values are encoded as ASCII v.s. binary + curve->p = scanval (p->p); + curve->a = scanval (p->a); + curve->b = scanval (p->b); + curve->n = scanval (p->n); + curve->G.x = scanval (p->g_x); + curve->G.y = scanval (p->g_y); + curve->G.z = mpi_alloc_set_ui (1); + + if (DBG_CIPHER) + log_debug( "ec filled in curve %s\n", p->desc ); + return 0; +} /********************************************* ************** interface ****************** @@ -889,8 +958,20 @@ ec2os (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t p) log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err)); gcry_free (buf); - mpi_free (x); - mpi_free (y); + return result; +} + +static gcry_mpi_t +name_oid_to_mpi( const byte *name_oid ) { + gpg_error_t err; + gcry_mpi_t result; + + if( name_oid == NULL || name_oid[0] == 0 ) + return mpi_new (0); + + err = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, name_oid, name_oid[0]+1, NULL); + if (err) + log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err)); return result; } @@ -953,6 +1034,35 @@ os2ec (mpi_point_t *result, gcry_mpi_t value) return 0; } +static gcry_err_code_t +mpi_to_name_oid( gcry_mpi_t mpi_in, byte name_oid_out[MAX_ECC_OID_LEN] ) { + size_t nbytes; + unsigned char *buf; + gcry_error_t err; + + memset( name_oid_out, 0, MAX_ECC_OID_LEN ); + + nbytes = (mpi_get_nbits (mpi_in)+7)/8; + if( nbytes == 0 ) + return 0; + + buf = gcry_xmalloc (nbytes); + err = gcry_mpi_print (GCRYMPI_FMT_USG, buf, nbytes, &nbytes, mpi_in); + if (err) + { + gcry_free (buf); + return err; + } + if (buf[0]+1 != nbytes || nbytes >= MAX_ECC_OID_LEN) + { + gcry_free (buf); + return GPG_ERR_INV_OBJ; + } + memcpy( name_oid_out, buf, nbytes+1 ); + gcry_free (buf); + + return 0; +} /* Extended version of ecc_generate. */ static gcry_err_code_t @@ -964,6 +1074,7 @@ ecc_generate_ext (int algo, unsigned int nbits, unsigned long evalue, gpg_err_code_t ec; ECC_secret_key sk; gcry_mpi_t g_x, g_y, q_x, q_y; + gcry_mpi_t kek_params = NULL; char *curve_name = NULL; gcry_sexp_t l1; int transient_key = 0; @@ -974,6 +1085,7 @@ ecc_generate_ext (int algo, unsigned int nbits, unsigned long evalue, if (genparms) { + /* Parse the optional "curve" parameter. */ l1 = gcry_sexp_find_token (genparms, "curve", 0); if (l1) @@ -986,10 +1098,30 @@ ecc_generate_ext (int algo, unsigned int nbits, unsigned long evalue, /* Parse the optional transient-key flag. */ l1 = gcry_sexp_find_token (genparms, "transient-key", 0); + if( l1 ) { + const char *s; + s = _gcry_sexp_nth_string (l1, 1); + if( s && strcmp( s, "1" )==0 ) + transient_key = 1; + gcry_sexp_release (l1); + if (DBG_CIPHER) + log_debug( "ecgen 'transient-key' parameter supplied, value=%d\n", transient_key); + } + + /* Parse the "KEK parameters" parameter. */ + l1 = gcry_sexp_find_token (genparms, "kek-params", 0); if (l1) { - transient_key = 1; + kek_params = gcry_sexp_nth_mpi (l1, 1, 0); gcry_sexp_release (l1); + if (!kek_params) { + log_debug( "ecgen failed to parse 'kek-params'\n" ); + return GPG_ERR_INV_OBJ; /* No curve name or value too large. */ + } + if (DBG_CIPHER) { + log_debug( "ecgen 'kek-params' parameter supplied\n" ); + log_mpidump ("ecgen DH kek-param", kek_params); + } } } @@ -1006,16 +1138,20 @@ ecc_generate_ext (int algo, unsigned int nbits, unsigned long evalue, 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; - + skey[0] = name_oid_to_mpi( sk.E.name_oid ); // "c", name OID + //if( (ec=fill_in_curve( sk.E.name_oid, &sk.E )) ) + // return ec; + skey[1] = ec2os (q_x, q_y, sk.E.p); /* public key */ // "q", public key, the point + mpi_free (q_x); + mpi_free (q_y); + + if( algo == GCRY_PK_ECDSA ) { + skey[2] = sk.d; + } + else { + skey[2] = (kek_params ? kek_params : mpi_new (0)); // params, the last field in the public key portion + skey[3] = sk.d; + } point_free (&sk.E.G); point_free (&sk.Q); @@ -1024,6 +1160,21 @@ ecc_generate_ext (int algo, unsigned int nbits, unsigned long evalue, if (!*retfactors) return gpg_err_code_from_syserror (); + if (DBG_CIPHER) + { + if( algo == GCRY_PK_ECDSA ) { + log_mpidump ("ecgen DSA c ", skey[0]); + log_mpidump ("ecgen DSA Q ", skey[1]); + log_mpidump ("ecgen DSA d ", skey[2]); + } + else { + log_mpidump ("ecgen DH c ", skey[0]); + log_mpidump ("ecgen DH Q ", skey[1]); + log_mpidump ("ecgen DH p ", skey[2]); + log_mpidump ("ecgen DH d ", skey[3]); + } + } + return 0; } @@ -1036,7 +1187,12 @@ ecc_generate (int algo, unsigned int nbits, unsigned long evalue, return ecc_generate_ext (algo, nbits, 0, NULL, skey, retfactors, NULL); } - +#if 0 +/* Need to be implemented, if called neeeded. The issue is that the purpose of this function is to return the information about + * the curve that is beyond the information present in the public key. In particular, the pkey size is now just 2, + * while we may need to return E.a, E.b, E.p, E.n, E.g, type of the curve, at the minimum. + * This information is readily available for well-known named curves. + */ /* Return the parameters of the curve NAME. */ static gcry_err_code_t ecc_get_param (const char *name, gcry_mpi_t *pkey) @@ -1059,15 +1215,17 @@ ecc_get_param (const char *name, gcry_mpi_t *pkey) _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; + pkey[0] = name_oid_to_mpi( E.name_oid ); + pkey[1] = E.p; + pkey[2] = E.a; + pkey[3] = E.b; + pkey[4] = ec2os (g_x, g_y, E.p); + pkey[5] = E.n; + pkey[6] = NULL; return 0; } +#endif static gcry_err_code_t @@ -1078,23 +1236,15 @@ ecc_check_secret_key (int algo, gcry_mpi_t *skey) (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]) + if (!skey[0] || !skey[1] || !skey[2] ) 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]; + if( (err=mpi_to_name_oid( skey[0], sk.E.name_oid )) ) + return err; + if( (err=fill_in_curve( sk.E.name_oid, &sk.E )) ) + return err; point_init (&sk.Q); - err = os2ec (&sk.Q, skey[5]); + err = os2ec (&sk.Q, skey[1]); if (err) { point_free (&sk.E.G); @@ -1102,7 +1252,7 @@ ecc_check_secret_key (int algo, gcry_mpi_t *skey) return err; } - sk.d = skey[6]; + sk.d = skey[2]; if (check_secret_key (&sk)) { @@ -1124,30 +1274,22 @@ ecc_sign (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *skey) (void)algo; - if (!data || !skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4] - || !skey[5] || !skey[6] ) + if (!data || !skey[0] || !skey[1] || !skey[2] ) 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]; + if( (err=mpi_to_name_oid( skey[0], sk.E.name_oid )) ) + return err; + if( (err=fill_in_curve( sk.E.name_oid, &sk.E )) ) + return err; point_init (&sk.Q); - err = os2ec (&sk.Q, skey[5]); + err = os2ec (&sk.Q, skey[1]); if (err) { point_free (&sk.E.G); point_free (&sk.Q); return err; } - sk.d = skey[6]; + sk.d = gcry_mpi_copy( skey[2] ); resarr[0] = mpi_alloc (mpi_get_nlimbs (sk.E.p)); resarr[1] = mpi_alloc (mpi_get_nlimbs (sk.E.p)); @@ -1158,8 +1300,7 @@ ecc_sign (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *skey) mpi_free (resarr[1]); resarr[0] = NULL; /* Mark array as released. */ } - point_free (&sk.E.G); - point_free (&sk.Q); + ecc_sk_free( &sk ); return err; } @@ -1174,65 +1315,139 @@ ecc_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey, (void)cmp; (void)opaquev; - if (!data[0] || !data[1] || !hash || !pkey[0] || !pkey[1] || !pkey[2] - || !pkey[3] || !pkey[4] || !pkey[5] ) + if (!data[0] || !data[1] || !hash || !pkey[0] || !pkey[1] ) 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]; + if( (err=mpi_to_name_oid( pkey[0], pk.E.name_oid )) ) + return err; + if( (err=fill_in_curve( pk.E.name_oid, &pk.E )) ) + return err; point_init (&pk.Q); - err = os2ec (&pk.Q, pkey[5]); + err = os2ec (&pk.Q, pkey[1]); if (err) { - point_free (&pk.E.G); - point_free (&pk.Q); + ecc_pk_free( &pk ); return err; } err = verify (hash, &pk, data[0], data[1]); - point_free (&pk.E.G); - point_free (&pk.Q); + ecc_pk_free( &pk ); return err; } - -static unsigned int -ecc_get_nbits (int algo, gcry_mpi_t *pkey) +/* ecdh is very simple. It is basically the implementation of the primitive operation in the EC field. + * 'data' is scalar, 2-MPI array pkey uniquely defines a point on the curve. + * + * The complexity of ECDH encryption is shifted into OpenPGP layer to keep libgcrypt + * layer generic. ecc_encrypt is identical to ecc_decrypt due to the symmetry of DH protocol. + */ +static gcry_err_code_t +ecc_encrypt (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *pkey, int flags) { + ECC_secret_key sk; + mpi_point_t R; /* result that we return */ + mpi_ec_t ctx; + gcry_mpi_t result; + int err; + (void)algo; + (void)flags; + + if (DBG_CIPHER) + log_debug ("Called ecc_encrypt data size=%d bits, flags=%08x\n", gcry_mpi_get_nbits (data), flags); + + if ( !data || !pkey[0] || !pkey[1] ) + return GPG_ERR_BAD_MPI; - return mpi_get_nbits (pkey[0]); + if (DBG_CIPHER) + { + log_mpidump ("ecdh encrypt c ", pkey[0]); + log_mpidump ("ecdh encrypt q ", pkey[1]); + log_mpidump ("ecdh encrypt p ", pkey[2]); + log_mpidump ("ecdh encrypt data", data); + } + + if( (err=mpi_to_name_oid( pkey[0], sk.E.name_oid )) ) + return err; + if( (err=fill_in_curve( sk.E.name_oid, &sk.E )) ) + return err; + + point_init (&sk.Q); + err = os2ec (&sk.Q, pkey[1]); + sk.d = gcry_mpi_copy( data ); + if (err) + { + ecc_sk_free( &sk ); + return err; + } + + ctx = _gcry_mpi_ec_init (sk.E.p, sk.E.a); + + point_init (&R); + _gcry_mpi_ec_mul_point (&R, sk.d, &sk.Q, ctx); + + /* the following is false: assert( mpi_cmp_ui( R.x, 1 )==0 );, so */ + { + gcry_mpi_t x,y; + x = mpi_new (0); + y = mpi_new (0); + + if (_gcry_mpi_ec_get_affine (x, y, &R, ctx)) + log_fatal ("ecdh: Failed to get affine coordinates\n"); + + result = ec2os( x, y, sk.E.p ); + mpi_free(x); + mpi_free(y); + } + + point_free( &R ); + _gcry_mpi_ec_free (ctx); + ecc_sk_free( &sk ); + + if( result == NULL ) + return GPG_ERR_ENOMEM; + + /* success */ + + resarr[0] = result; + resarr[1] = mpi_new (0); /* not used; it is constructruced at OpenPGP layer */ + + return GPG_ERR_NO_ERROR; } +static gcry_err_code_t +ecc_decrypt (int algo, gcry_mpi_t *result, gcry_mpi_t *data, gcry_mpi_t *skey, int flags) +{ + return ecc_encrypt( algo, result, data[0], skey, flags ); +} +static unsigned int +ecc_get_nbits (int algo, gcry_mpi_t *pkey) +{ + (void)algo; + /* derive it from public key point Q, which is 1 byte + x + y */ + return (mpi_get_nbits (pkey[1]) / (8*2)) * 8; +} /* 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"; +#define N_ECC_PUBKEY_COMPONENETS 2 + static const char names[] = "cq"; gpg_err_code_t ec = 0; gcry_sexp_t l1; - gcry_mpi_t values[6]; + gcry_mpi_t values[N_ECC_PUBKEY_COMPONENETS]; int idx; /* Clear the values for easier error cleanup. */ - for (idx=0; idx < 6; idx++) + for (idx=0; idx < sizeof(values)/sizeof(values[0]); idx++) values[idx] = NULL; /* Fill values with all available parameters. */ - for (idx=0; idx < 6; idx++) + for (idx=0; idx < sizeof(values)/sizeof(values[0]); idx++) { l1 = gcry_sexp_find_token (keyparam, names+idx, 1); if (l1) @@ -1246,16 +1461,18 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam) } } } - + +#if 0 + /* Not used now: curve name (DER OID of the name, actually) is always hashed above */ /* 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]; + gcry_mpi_t tmpvalues[N_ECC_PUBKEY_COMPONENETS]; - for (idx = 0; idx < 6; idx++) + for (idx = 0; idx < sizeof(tmpvalues)/sizeof(tmpvalues[0]); idx++) tmpvalues[idx] = NULL; curve = _gcry_sexp_nth_string (l1, 1); @@ -1269,7 +1486,7 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam) if (ec) goto leave; - for (idx = 0; idx < 6; idx++) + for (idx = 0; idx < sizeof(values)/sizeof(values[0]); idx++) { if (!values[idx]) values[idx] = tmpvalues[idx]; @@ -1277,11 +1494,12 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam) mpi_free (tmpvalues[idx]); } } +#endif /* Check that all parameters are known and normalize all MPIs (that should not be required but we use an internal function later and thus we better make 100% sure that they are normalized). */ - for (idx = 0; idx < 6; idx++) + for (idx = 0; idx < sizeof(values)/sizeof(values[0]); idx++) if (!values[idx]) { ec = GPG_ERR_NO_OBJ; @@ -1291,7 +1509,7 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam) _gcry_mpi_normalize (values[idx]); /* Hash them all. */ - for (idx = 0; idx < 6; idx++) + for (idx = 0; idx < sizeof(values)/sizeof(values[0]); idx++) { char buf[30]; unsigned char *rawmpi; @@ -1311,10 +1529,11 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam) } leave: - for (idx = 0; idx < 6; idx++) + for (idx = 0; idx < sizeof(values)/sizeof(values[0]); idx++) _gcry_mpi_release (values[idx]); return ec; +#undef N_ECC_PUBKEY_COMPONENETS } @@ -1375,14 +1594,20 @@ run_selftests (int algo, int extended, selftest_report_func_t report) static const char *ecdsa_names[] = { "ecdsa", - "ecc", + "ecdh", + "ecc", // only here, for the minimum number of public parameters (= 2) + NULL, + }; +static const char *ecdh_names[] = + { + "ecdh", NULL, }; gcry_pk_spec_t _gcry_pubkey_spec_ecdsa = { "ECDSA", ecdsa_names, - "pabgnq", "pabgnqd", "", "rs", "pabgnq", + "cq", "cqd", "", "rs", "cq", GCRY_PK_USAGE_SIGN, ecc_generate, ecc_check_secret_key, @@ -1393,11 +1618,26 @@ gcry_pk_spec_t _gcry_pubkey_spec_ecdsa = ecc_get_nbits }; +gcry_pk_spec_t _gcry_pubkey_spec_ecdh = + { + "ECDH", ecdh_names, + "cqp", "cqpd", "ab", "", "cqp", + GCRY_PK_USAGE_ENCR, + ecc_generate, + ecc_check_secret_key, + ecc_encrypt, + ecc_decrypt, + NULL, + NULL, + ecc_get_nbits + }; + + pk_extra_spec_t _gcry_pubkey_extraspec_ecdsa = { run_selftests, ecc_generate_ext, compute_keygrip, - ecc_get_param + NULL // ecc_get_param }; diff --git a/cipher/pubkey.c b/cipher/pubkey.c index 86693e8e..6f0609c1 100644 --- a/cipher/pubkey.c +++ b/cipher/pubkey.c @@ -75,6 +75,8 @@ static struct pubkey_table_entry #if USE_ECC { &_gcry_pubkey_spec_ecdsa, &_gcry_pubkey_extraspec_ecdsa, GCRY_PK_ECDSA, 0 }, + { &_gcry_pubkey_spec_ecdh, + &_gcry_pubkey_extraspec_ecdsa, GCRY_PK_ECDH, 0 }, #endif { NULL, 0 }, }; @@ -981,7 +983,7 @@ sexp_to_key (gcry_sexp_t sexp, int want_private, gcry_mpi_t **retarray, gcry_module_t module; gcry_pk_spec_t *pubkey; pk_extra_spec_t *extraspec; - int is_ecc; +// int is_ecc; /* Check that the first element is valid. */ list = gcry_sexp_find_token (sexp, @@ -1009,7 +1011,7 @@ sexp_to_key (gcry_sexp_t sexp, int want_private, gcry_mpi_t **retarray, with a key is compatible with an application of the key (signing, encryption). For RSA this is easy, but ECC is the first algorithm which has many flavours. */ - is_ecc = ( !strcmp (name, "ecdsa") || !strcmp (name, "ecc") ); +// is_ecc = ( !strcmp (name, "ecdsa") || !strcmp (name, "ecc") ); gcry_free (name); if (!module) @@ -1029,9 +1031,9 @@ sexp_to_key (gcry_sexp_t sexp, int want_private, gcry_mpi_t **retarray, err = gpg_err_code_from_errno (errno); if (!err) { - if (is_ecc) - err = sexp_elements_extract_ecc (list, elems, array, extraspec); - else + // if (is_ecc) + // err = sexp_elements_extract_ecc (list, elems, array, extraspec); + // else err = sexp_elements_extract (list, elems, array, pubkey->name); } diff --git a/configure.ac b/configure.ac index c6bff373..b7fa853f 100644 --- a/configure.ac +++ b/configure.ac @@ -26,7 +26,7 @@ min_automake_version="1.10" # Remember to change the version number immediately *after* a release. # Set my_issvn to "yes" for non-released code. Remember to run an # "svn up" and "autogen.sh" right before creating a distribution. -m4_define([my_version], [1.5.0]) +m4_define([my_version], [1.6.0]) m4_define([my_issvn], [yes]) m4_define([svn_revision], m4_esyscmd([printf "%d" $(svn info 2>/dev/null \ diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi index 883de4a2..663ca5e4 100644 --- a/doc/gcrypt.texi +++ b/doc/gcrypt.texi @@ -2736,10 +2736,10 @@ are allowed. When specifying Q all values of N in the range 512 to 15680 are valid as long as they are multiples of 8. @item transient-key -This is only meaningful for RSA, DSA and ECDSA keys. This is a flag +This is only meaningful for RSA, DSA, ECDSA, and ECDH keys. This is a flag with no value. If given the key is created using a faster and a somewhat less secure random number generator. This flag may be used for -keys which are only used for a short time and do not require full +keys which are only used for a short time or per-message and do not require full cryptographic strength. @item domain diff --git a/src/cipher.h b/src/cipher.h index 48baab42..2be8787a 100644 --- a/src/cipher.h +++ b/src/cipher.h @@ -129,6 +129,7 @@ extern gcry_pk_spec_t _gcry_pubkey_spec_rsa; extern gcry_pk_spec_t _gcry_pubkey_spec_elg; extern gcry_pk_spec_t _gcry_pubkey_spec_dsa; extern gcry_pk_spec_t _gcry_pubkey_spec_ecdsa; +extern gcry_pk_spec_t _gcry_pubkey_spec_ecdh; extern pk_extra_spec_t _gcry_pubkey_extraspec_rsa; extern pk_extra_spec_t _gcry_pubkey_extraspec_dsa; |