From wk at gnupg.org Wed Oct 2 15:42:39 2019 From: wk at gnupg.org (Werner Koch) Date: Wed, 02 Oct 2019 15:42:39 +0200 Subject: [PATCH] ecc: Add Curve448. In-Reply-To: <87eezxzodv.fsf@fifthhorseman.net> (Daniel Kahn Gillmor via Gcrypt-devel's message of "Mon, 30 Sep 2019 19:26:36 +0200") References: <87sgoky6cb.fsf@jumper.gniibe.org> <87muerwkmr.fsf@wheatstone.g10code.de> <87zhirs9d0.fsf@jumper.gniibe.org> <87eezxzodv.fsf@fifthhorseman.net> Message-ID: <87eezvi7qo.fsf@wheatstone.g10code.de> On Mon, 30 Sep 2019 19:26, Daniel Kahn Gillmor said: > I lean toward using the "native" format where possible, even if that > leaves 25519 as an outlier. I tend to agree. Salam-Shalom, Werner -- Die Gedanken sind frei. Ausnahmen regelt ein Bundesgesetz. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 227 bytes Desc: not available URL: From gniibe at fsij.org Thu Oct 3 08:39:46 2019 From: gniibe at fsij.org (NIIBE Yutaka) Date: Thu, 03 Oct 2019 15:39:46 +0900 Subject: [PATCH] ecc: Add Curve448. In-Reply-To: <87eezvi7qo.fsf@wheatstone.g10code.de> References: <87sgoky6cb.fsf@jumper.gniibe.org> <87muerwkmr.fsf@wheatstone.g10code.de> <87zhirs9d0.fsf@jumper.gniibe.org> <87eezxzodv.fsf@fifthhorseman.net> <87eezvi7qo.fsf@wheatstone.g10code.de> Message-ID: <87k19mqqml.fsf@iwagami.gniibe.org> Hello, again, Thanks for your suggestions. Daniel Kahn Gillmor wrote: > I'm not convinced that this kind of consistency is the right way to go. I see your point. If I could allow to make an excuse, my initial motivation was minimizing the patch. Anyway, I concur. Thus, considering again, I'm going to modify my patch to use native format of X448 to represent point (with the prefix 0x40), using OID of X448. Probably, for libgcrypt, I'll introduce new internal flag, ECC_DIALECT_LITTLE_ENDIAN to express that the curve prefers/requires non-default serialization format (from the view point of existing curves of libgcrypt). Then, code paths will be more cleaner. Well, it reminds me about a virtue of good baseball umpire; Don't keep further misjudgments for your own consistency. Or, I whould quote a phrase in Chapter I of the first book of Confucius. Thank you, as always. -- From dkg at fifthhorseman.net Thu Oct 3 12:33:54 2019 From: dkg at fifthhorseman.net (Daniel Kahn Gillmor) Date: Thu, 03 Oct 2019 06:33:54 -0400 Subject: [PATCH] ecc: Add Curve448. In-Reply-To: <87k19mqqml.fsf@iwagami.gniibe.org> References: <87sgoky6cb.fsf@jumper.gniibe.org> <87muerwkmr.fsf@wheatstone.g10code.de> <87zhirs9d0.fsf@jumper.gniibe.org> <87eezxzodv.fsf@fifthhorseman.net> <87eezvi7qo.fsf@wheatstone.g10code.de> <87k19mqqml.fsf@iwagami.gniibe.org> Message-ID: <87sgoaunhp.fsf@fifthhorseman.net> On Thu 2019-10-03 15:39:46 +0900, NIIBE Yutaka wrote: > Hello, again, > > Thanks for your suggestions. > > Daniel Kahn Gillmor wrote: >> I'm not convinced that this kind of consistency is the right way to go. > > I see your point. If I could allow to make an excuse, my initial > motivation was minimizing the patch. > > Anyway, I concur. > > Thus, considering again, I'm going to modify my patch to use native > format of X448 to represent point (with the prefix 0x40), using OID of > X448. > > Probably, for libgcrypt, I'll introduce new internal flag, > > ECC_DIALECT_LITTLE_ENDIAN > > to express that the curve prefers/requires non-default serialization > format (from the view point of existing curves of libgcrypt). Then, > code paths will be more cleaner. It occurs to me that if we want consistency within some future version of gcrypt, we could introduce the same codepath for 25519 in its native representation -- so that the invoker could use either representation. Then we could somehow deprecate the older representation, and finally remove it on the next major API break. I'm not sure how to do that deprecation and removal effectively and safely, given gcrypt's string-driven interface, but if anyone with deeper understanding of gcrypt's API design and evolution wants to suggest a deprecation mechanism, i'd be happy to review it. > Well, it reminds me about a virtue of good baseball umpire; > Don't keep further misjudgments for your own consistency. > Or, I whould quote a phrase in Chapter I of the first book of Confucius. :) --dkg -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 227 bytes Desc: not available URL: From wk at gnupg.org Fri Oct 4 08:49:32 2019 From: wk at gnupg.org (Werner Koch) Date: Fri, 04 Oct 2019 08:49:32 +0200 Subject: [PATCH] ecc: Add Curve448. In-Reply-To: <87sgoaunhp.fsf@fifthhorseman.net> (Daniel Kahn Gillmor's message of "Thu, 03 Oct 2019 06:33:54 -0400") References: <87sgoky6cb.fsf@jumper.gniibe.org> <87muerwkmr.fsf@wheatstone.g10code.de> <87zhirs9d0.fsf@jumper.gniibe.org> <87eezxzodv.fsf@fifthhorseman.net> <87eezvi7qo.fsf@wheatstone.g10code.de> <87k19mqqml.fsf@iwagami.gniibe.org> <87sgoaunhp.fsf@fifthhorseman.net> Message-ID: <87k19lgg3n.fsf@wheatstone.g10code.de> On Thu, 3 Oct 2019 06:33, Daniel Kahn Gillmor said: > I'm not sure how to do that deprecation and removal effectively and There won't be any deprecation of an already Libgcrypt provided curve; if something else is required a new curve needs to be added. Shalom-Salam, Werner -- Die Gedanken sind frei. Ausnahmen regelt ein Bundesgesetz. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 227 bytes Desc: not available URL: From gniibe at fsij.org Fri Oct 11 06:18:56 2019 From: gniibe at fsij.org (NIIBE Yutaka) Date: Fri, 11 Oct 2019 13:18:56 +0900 Subject: An octet sequence as an MPI Message-ID: <87wodbkj7z.fsf@iwagami.gniibe.org> Hello, Current libgcrypt + GnuPG implementation have an issue around handling of an octet sequence as big-endian number in an MPI. IIUC, this issue originates in RFC 6637, and we can find same problem in the draft of rfc4880bis. I will ask and discuss in OpenPGP WG, if needed, but for now, I'd like to improve the implementation at first. The issue can be found in the draft of rfc4880bis, quoting: ========================== 5.6.5. Algorithm-Specific Part for EdDSA Keys The public key is this series of values: o a variable-length field containing a curve OID, formatted as follows: * a one-octet size of the following field; values 0 and 0xFF are reserved for future extensions, * the octets representing a curve OID, defined in Section 9.2; 1? o a MPI of an EC point representing a public key Q as described 1? under EdDSA Point Format below. The secret key is this single multiprecision integer: 2? o MPI of an integer representing the secret key, which is a scalar 2? of the public EC point. 5.6.6. Algorithm-Specific Part for ECDH Keys The public key is this series of values: o a variable-length field containing a curve OID, formatted as follows: * a one-octet size of the following field; values 0 and 0xFF are reserved for future extensions, * the octets representing a curve OID, defined in Section 9.2; 3? o a MPI of an EC point representing a public key; o a variable-length field containing KDF parameters, formatted as follows: * a one-octet size of the following fields; values 0 and 0xff are reserved for future extensions; * a one-octet value 1, reserved for future extensions; * a one-octet hash function ID used with a KDF; * a one-octet algorithm ID for the symmetric algorithm used to wrap the symmetric key used for the message encryption; see Section 13.5 for details. Observe that an ECDH public key is composed of the same sequence of fields that define an ECDSA key, plus the KDF parameters field. The secret key is this single multiprecision integer: 4? o MPI of an integer representing the secret key, which is a scalar 4? of the public EC point. ========================== I put ? in the left column. Here I enumerated four problems. (Same for ECDSA, but this is omitted, right now, because my focus is introducing new curves.) It is described as "MPI", but I think that it is better to be described as "size + octets". MPI has semantics than mere octet string; It is (may be) interpreted as a big-endian number, and its representation can be changed where minimum size representation is assumed (removing zero octets at the beginning). For #1 and #3, putting the point representation in an MPI is questionable, as the point is not a big-endian number. Because of its prefix (the first octet, 0x04 or 0x40), it is safe against removing-zero handling. But, by defining as "size + octets", it allows any point representation. For #2, the description is not accuate for EdDSA, since it's not "a scalar of the public EC point". It is just a fixed-size octets. When it is handled as an MPI, it is unsafe against removing-zero handling. So, our implementation of libgcrypt has special care for that, namely, detecting removed zeros to recover them. By defining as "size + octets", it allows curve implementation to interpret octets. For #4, current implementation assumes an MPI, but it is more natural for other curves to define it as "size + octets". For Curve25519, we do use this field as an MPI (as a big-endian number), just like other NIST curves. For X448, it is more friendly to the X448 API to use this field as a native fixed-size little endian octets. By defining as "size + octets", it allows curve implementation to interpret octets. (We can define X25519, using X25519 native format.) -- From gniibe at fsij.org Fri Oct 11 05:23:30 2019 From: gniibe at fsij.org (NIIBE Yutaka) Date: Fri, 11 Oct 2019 12:23:30 +0900 Subject: [PATCH] ecc: Add Curve448. In-Reply-To: <87k19lgg3n.fsf@wheatstone.g10code.de> References: <87sgoky6cb.fsf@jumper.gniibe.org> <87muerwkmr.fsf@wheatstone.g10code.de> <87zhirs9d0.fsf@jumper.gniibe.org> <87eezxzodv.fsf@fifthhorseman.net> <87eezvi7qo.fsf@wheatstone.g10code.de> <87k19mqqml.fsf@iwagami.gniibe.org> <87sgoaunhp.fsf@fifthhorseman.net> <87k19lgg3n.fsf@wheatstone.g10code.de> Message-ID: <871rvkklsd.fsf@iwagami.gniibe.org> Hello, It's not yet finished, but I post update of my work. I modified my patch so that it's more friendly to X488 API. That is: * A scalar is fixed-size little-endian. * A point is represented in fixed-size little-endian. (with no prefix) I think that current libgcrypt + GnuPG implementation have an issue around handling of those scalar and point as an MPI. I'll describe this issue in another mail. -------------------------- diff --git a/cipher/ecc-curves.c b/cipher/ecc-curves.c index 4a3139c3..1e0d3e55 100644 --- a/cipher/ecc-curves.c +++ b/cipher/ecc-curves.c @@ -54,9 +54,9 @@ static const struct { "Ed448", "1.3.101.113" }, /* rfc8410 */ { "X22519", "1.3.101.110" }, /* rfc8410 */ +#endif { "X448", "1.3.101.111" }, /* rfc8410 */ -#endif { "NIST P-192", "1.2.840.10045.3.1.1" }, /* X9.62 OID */ { "NIST P-192", "prime192v1" }, /* X9.62 name. */ @@ -162,6 +162,22 @@ static const ecc_domain_parms_t domain_parms[] = * the function _gcry_ecc_fill_in_curve. */ }, + { + /* (y^2 = x^3 + 156326*x^2 + x) */ + "X448", 448, 0, + MPI_EC_MONTGOMERY, ECC_DIALECT_SAFECURVE, + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "0x98A9", + "0x01", + "0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "7CCA23E9C44EDB49AED63690216CC2728DC58F552378C292AB5844F3", + "0x00000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000005", + "0x7D235D1295F5B1F66C98AB6E58326FCECBAE5D34F55545D060F75DC2" + "8DF3F6EDB8027E2346430D211312C4B150677AF76FD7223D457B5B1A", + "0x04" + }, #if 0 /* No real specs yet found. */ { /* x^2 + y^2 = 1 + 3617x^2y^2 mod 2^414 - 17 */ diff --git a/cipher/ecc-ecdh.c b/cipher/ecc-ecdh.c index bfd07d40..dbf0491e 100644 --- a/cipher/ecc-ecdh.c +++ b/cipher/ecc-ecdh.c @@ -38,13 +38,18 @@ static mpi_ec_t prepare_ec (const char *curve_name, elliptic_curve_t *E) { mpi_ec_t ec; + int flags = 0; memset (E, 0, sizeof *E); if (_gcry_ecc_fill_in_curve (0, curve_name, E, NULL)) return NULL; + if (E->dialect != ECC_DIALECT_SAFECURVE) + flags = PUBKEY_FLAG_DJB_TWEAK; + ec = _gcry_mpi_ec_p_internal_new (E->model, E->dialect, - PUBKEY_FLAG_DJB_TWEAK, E->p, E->a, E->b); + flags, E->p, E->a, E->b); + ec->h = mpi_copy (E->h); return ec; } @@ -89,26 +94,30 @@ _gcry_ecc_mul_point (int algo, unsigned char *result, { nbits = ECC_CURVE448_BITS; curve = "X448"; - return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); } else return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); nbytes = nbits / 8; - mpi_k = mpi_new (nbits); ec = prepare_ec (curve, &E); mpi_u = mpi_new (nbits); Q = mpi_point_new (nbits); x = mpi_new (nbits); memcpy (buffer, scalar, nbytes); - reverse_buffer (buffer, nbytes); - _gcry_mpi_set_buffer (mpi_k, buffer, nbytes, 0); + if (algo == GCRY_ECC_CURVE25519) + { + mpi_k = mpi_new (nbits); + reverse_buffer (buffer, nbytes); + _gcry_mpi_set_buffer (mpi_k, buffer, nbytes, 0); - for (i = 0; i < mpi_get_nbits (E.h) - 1; i++) - mpi_clear_bit (mpi_k, i); - mpi_set_highbit (mpi_k, mpi_get_nbits (E.p) - 1); + for (i = 0; i < mpi_get_nbits (E.h) - 1; i++) + mpi_clear_bit (mpi_k, i); + mpi_set_highbit (mpi_k, mpi_get_nbits (E.p) - 1); + } + else + mpi_k = _gcry_mpi_set_opaque_copy (NULL, buffer, nbytes*8); if (point) { diff --git a/cipher/ecc-misc.c b/cipher/ecc-misc.c index 8cdfec62..39a6581d 100644 --- a/cipher/ecc-misc.c +++ b/cipher/ecc-misc.c @@ -98,6 +98,7 @@ _gcry_ecc_dialect2str (enum ecc_dialects dialect) { case ECC_DIALECT_STANDARD: str = "Standard"; break; case ECC_DIALECT_ED25519: str = "Ed25519"; break; + case ECC_DIALECT_SAFECURVE: str = "SafeCurve"; break; } return str; } diff --git a/cipher/ecc.c b/cipher/ecc.c index 96e267fb..cedcbe8f 100644 --- a/cipher/ecc.c +++ b/cipher/ecc.c @@ -161,21 +161,29 @@ nist_generate_key (ECC_secret_key *sk, elliptic_curve_t *E, mpi_ec_t ctx, random_level = GCRY_VERY_STRONG_RANDOM; /* Generate a secret. */ - if (ctx->dialect == ECC_DIALECT_ED25519 || (flags & PUBKEY_FLAG_DJB_TWEAK)) + if (ctx->dialect == ECC_DIALECT_ED25519 + || ctx->dialect == ECC_DIALECT_SAFECURVE + || (flags & PUBKEY_FLAG_DJB_TWEAK)) { char *rndbuf; int len = (pbits+7)/8; - unsigned int h; - mpi_get_ui (&h, E->h); - sk->d = mpi_snew (pbits); rndbuf = _gcry_random_bytes_secure (len, random_level); - if ((pbits % 8)) - rndbuf[0] &= (1 << (pbits % 8)) - 1; - rndbuf[0] |= (1 << ((pbits + 7) % 8)); - rndbuf[(pbits-1)/8] &= (256 - h); - _gcry_mpi_set_buffer (sk->d, rndbuf, len, 0); - xfree (rndbuf); + if (ctx->dialect == ECC_DIALECT_SAFECURVE) + sk->d = mpi_set_opaque (NULL, rndbuf, len*8); + else + { + unsigned int h; + + mpi_get_ui (&h, E->h); + sk->d = mpi_snew (pbits); + if ((pbits % 8)) + rndbuf[0] &= (1 << (pbits % 8)) - 1; + rndbuf[0] |= (1 << ((pbits + 7) % 8)); + rndbuf[(pbits-1)/8] &= (256 - h); + _gcry_mpi_set_buffer (sk->d, rndbuf, len, 0); + xfree (rndbuf); + } } else sk->d = _gcry_dsa_gen_k (E->n, random_level); @@ -342,22 +350,29 @@ test_ecdh_only_keys (ECC_secret_key *sk, unsigned int nbits, int flags) point_init (&pk.Q); point_set (&pk.Q, &sk->Q); - if ((flags & PUBKEY_FLAG_DJB_TWEAK)) + if (sk->E.dialect == ECC_DIALECT_SAFECURVE + || (flags & PUBKEY_FLAG_DJB_TWEAK)) { char *rndbuf; const unsigned int pbits = mpi_get_nbits (sk->E.p); int len = (pbits+7)/8; - unsigned int h; - mpi_get_ui (&h, sk->E.h); - test = mpi_new (pbits); rndbuf = _gcry_random_bytes (len, GCRY_WEAK_RANDOM); - if ((pbits % 8)) - rndbuf[0] &= (1 << (pbits % 8)) - 1; - rndbuf[0] |= (1 << ((pbits + 7) % 8)); - rndbuf[(pbits-1)/8] &= (256 - h); - _gcry_mpi_set_buffer (test, rndbuf, len, 0); - xfree (rndbuf); + if (sk->E.dialect == ECC_DIALECT_SAFECURVE) + test = mpi_set_opaque (NULL, rndbuf, len*8); + else + { + unsigned int h; + + mpi_get_ui (&h, sk->E.h); + test = mpi_new (pbits); + if ((pbits % 8)) + rndbuf[0] &= (1 << (pbits % 8)) - 1; + rndbuf[0] |= (1 << ((pbits + 7) % 8)); + rndbuf[(pbits-1)/8] &= (256 - h); + _gcry_mpi_set_buffer (test, rndbuf, len, 0); + xfree (rndbuf); + } } else { @@ -372,7 +387,7 @@ test_ecdh_only_keys (ECC_secret_key *sk, unsigned int nbits, int flags) /* R_ = hkQ <=> R_ = hkdG */ _gcry_mpi_ec_mul_point (&R_, test, &pk.Q, ec); - if (!(flags & PUBKEY_FLAG_DJB_TWEAK)) + if (pk.E.dialect == ECC_DIALECT_STANDARD && !(flags & PUBKEY_FLAG_DJB_TWEAK)) _gcry_mpi_ec_mul_point (&R_, ec->h, &R_, ec); if (_gcry_mpi_ec_get_affine (x0, NULL, &R_, ec)) log_fatal ("ecdh: Failed to get affine coordinates for hkQ\n"); @@ -380,7 +395,7 @@ test_ecdh_only_keys (ECC_secret_key *sk, unsigned int nbits, int flags) _gcry_mpi_ec_mul_point (&R_, test, &pk.E.G, ec); _gcry_mpi_ec_mul_point (&R_, sk->d, &R_, ec); /* R_ = hdkG */ - if (!(flags & PUBKEY_FLAG_DJB_TWEAK)) + if (pk.E.dialect == ECC_DIALECT_STANDARD && !(flags & PUBKEY_FLAG_DJB_TWEAK)) _gcry_mpi_ec_mul_point (&R_, ec->h, &R_, ec); if (_gcry_mpi_ec_get_affine (x1, NULL, &R_, ec)) @@ -602,6 +617,7 @@ ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey) } ctx = _gcry_mpi_ec_p_internal_new (E.model, E.dialect, flags, E.p, E.a, E.b); + ctx->h = mpi_copy (E.h); if (E.model == MPI_EC_MONTGOMERY) rc = nist_generate_key (&sk, &E, ctx, flags, nbits, &Qx, NULL); @@ -628,7 +644,9 @@ ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey) unsigned int encpklen; if (E.model == MPI_EC_MONTGOMERY) - rc = _gcry_ecc_mont_encodepoint (Qx, nbits, 1, &encpk, &encpklen); + rc = _gcry_ecc_mont_encodepoint (Qx, nbits, + sk.E.dialect != ECC_DIALECT_SAFECURVE, + &encpk, &encpklen); else /* (Gx and Gy are used as scratch variables) */ rc = _gcry_ecc_eddsa_encodepoint (&sk.Q, ctx, Gx, Gy, @@ -765,12 +783,11 @@ ecc_check_secret_key (gcry_sexp_t keyparms) /* Extract the parameters. */ if ((flags & PUBKEY_FLAG_PARAM)) - rc = sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?h?/q?+d", + rc = sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?h?/q?", &sk.E.p, &sk.E.a, &sk.E.b, &mpi_g, &sk.E.n, - &sk.E.h, &mpi_q, &sk.d, NULL); + &sk.E.h, &mpi_q, NULL); else - rc = sexp_extract_param (keyparms, NULL, "/q?+d", - &mpi_q, &sk.d, NULL); + rc = sexp_extract_param (keyparms, NULL, "/q?", &mpi_q, NULL); if (rc) goto leave; @@ -807,8 +824,16 @@ ecc_check_secret_key (gcry_sexp_t keyparms) ? ECC_DIALECT_ED25519 : ECC_DIALECT_STANDARD); if (!sk.E.h) - sk.E.h = mpi_const (MPI_C_ONE); + sk.E.h = mpi_const (MPI_C_ONE); } + + if (sk.E.dialect == ECC_DIALECT_SAFECURVE) + rc = sexp_extract_param (keyparms, NULL, "/d", &sk.d, NULL); + else + rc = sexp_extract_param (keyparms, NULL, "+d", &sk.d, NULL); + if (rc) + goto leave; + if (DBG_CIPHER) { log_debug ("ecc_testkey info: %s/%s\n", @@ -834,6 +859,7 @@ ecc_check_secret_key (gcry_sexp_t keyparms) ec = _gcry_mpi_ec_p_internal_new (sk.E.model, sk.E.dialect, flags, sk.E.p, sk.E.a, sk.E.b); + ec->h = mpi_copy (sk.E.h); if (mpi_q) { @@ -939,7 +965,7 @@ ecc_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms) ? ECC_DIALECT_ED25519 : ECC_DIALECT_STANDARD); if (!sk.E.h) - sk.E.h = mpi_const (MPI_C_ONE); + sk.E.h = mpi_const (MPI_C_ONE); } if (DBG_CIPHER) { @@ -1107,7 +1133,7 @@ ecc_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms) ? ECC_DIALECT_ED25519 : ECC_DIALECT_STANDARD); if (!pk.E.h) - pk.E.h = mpi_const (MPI_C_ONE); + pk.E.h = mpi_const (MPI_C_ONE); } if (DBG_CIPHER) @@ -1261,6 +1287,7 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms) ECC_public_key pk; mpi_ec_t ec = NULL; int flags = 0; + int safecurve; memset (&pk, 0, sizeof pk); _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_ENCRYPT, @@ -1277,18 +1304,6 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms) sexp_release (l1); l1 = NULL; - /* - * Extract the data. - */ - rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx); - if (rc) - goto leave; - if (mpi_is_opaque (data)) - { - rc = GPG_ERR_INV_DATA; - goto leave; - } - /* * Extract the key. */ @@ -1307,22 +1322,42 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms) /* Add missing parameters using the optional curve parameter. */ l1 = sexp_find_token (keyparms, "curve", 5); if (l1) + curvename = sexp_nth_string (l1, 1); + /* Guess required fields if a curve parameter has not been given. */ + if (curvename) { - curvename = sexp_nth_string (l1, 1); - if (curvename) - { - rc = _gcry_ecc_fill_in_curve (0, curvename, &pk.E, NULL); - if (rc) - goto leave; - } + rc = _gcry_ecc_fill_in_curve (0, curvename, &pk.E, NULL); + if (rc) + goto leave; } - /* Guess required fields if a curve parameter has not been given. */ - if (!curvename) + else { pk.E.model = MPI_EC_WEIERSTRASS; pk.E.dialect = ECC_DIALECT_STANDARD; if (!pk.E.h) - pk.E.h = mpi_const (MPI_C_ONE); + pk.E.h = mpi_const (MPI_C_ONE); + } + + if (pk.E.dialect == ECC_DIALECT_SAFECURVE) + { + ctx.flags |= PUBKEY_FLAG_RAW_FLAG; + safecurve = 1; + } + else if ((flags & PUBKEY_FLAG_DJB_TWEAK)) + safecurve = 1; + else + safecurve = 0; + + /* + * Extract the data. + */ + rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx); + if (rc) + goto leave; + if (pk.E.dialect != ECC_DIALECT_SAFECURVE && mpi_is_opaque (data)) + { + rc = GPG_ERR_INV_DATA; + goto leave; } /* @@ -1364,6 +1399,7 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms) /* Compute the encrypted value. */ ec = _gcry_mpi_ec_p_internal_new (pk.E.model, pk.E.dialect, flags, pk.E.p, pk.E.a, pk.E.b); + ec->h = mpi_copy (pk.E.h); /* Convert the public key. */ if (mpi_q) @@ -1408,7 +1444,7 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms) * imported public key which might not follow the key * generation procedure. */ - if (!(flags & PUBKEY_FLAG_DJB_TWEAK)) + if (!safecurve) { /* It's not for X25519, then, the input data was simply wrong. */ rc = GPG_ERR_INV_DATA; goto leave_main; @@ -1418,7 +1454,9 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms) mpi_s = _gcry_ecc_ec2os (x, y, pk.E.p); else { - rc = _gcry_ecc_mont_encodepoint (x, nbits, 1, &rawmpi, &rawmpilen); + rc = _gcry_ecc_mont_encodepoint (x, nbits, + pk.E.dialect != ECC_DIALECT_SAFECURVE, + &rawmpi, &rawmpilen); if (rc) goto leave_main; mpi_s = mpi_new (0); @@ -1437,7 +1475,9 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms) mpi_e = _gcry_ecc_ec2os (x, y, pk.E.p); else { - rc = _gcry_ecc_mont_encodepoint (x, nbits, 1, &rawmpi, &rawmpilen); + rc = _gcry_ecc_mont_encodepoint (x, nbits, + pk.E.dialect != ECC_DIALECT_SAFECURVE, + &rawmpi, &rawmpilen); if (!rc) { mpi_e = mpi_new (0); @@ -1502,6 +1542,7 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) mpi_point_struct R; gcry_mpi_t r = NULL; int flags = 0; + int safecurve; memset (&sk, 0, sizeof sk); point_init (&kG); @@ -1527,23 +1568,18 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) rc = _gcry_pk_util_preparse_encval (s_data, ecc_names, &l1, &ctx); if (rc) goto leave; - rc = sexp_extract_param (l1, NULL, "e", &data_e, NULL); + rc = sexp_extract_param (l1, NULL, "/e", &data_e, NULL); if (rc) goto leave; if (DBG_CIPHER) log_printmpi ("ecc_decrypt d_e", data_e); - if (mpi_is_opaque (data_e)) - { - rc = GPG_ERR_INV_DATA; - goto leave; - } /* * Extract the key. */ - rc = sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?h?+d", + rc = sexp_extract_param (keyparms, NULL, "-p?a?b?g?n?h?", &sk.E.p, &sk.E.a, &sk.E.b, &mpi_g, &sk.E.n, - &sk.E.h, &sk.d, NULL); + &sk.E.h, NULL); if (rc) goto leave; if (mpi_g) @@ -1557,23 +1593,29 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) sexp_release (l1); l1 = sexp_find_token (keyparms, "curve", 5); if (l1) + curvename = sexp_nth_string (l1, 1); + /* Guess required fields if a curve parameter has not been given. */ + if (curvename) { - curvename = sexp_nth_string (l1, 1); - if (curvename) - { - rc = _gcry_ecc_fill_in_curve (0, curvename, &sk.E, NULL); - if (rc) - goto leave; - } + rc = _gcry_ecc_fill_in_curve (0, curvename, &sk.E, NULL); + if (rc) + goto leave; } - /* Guess required fields if a curve parameter has not been given. */ - if (!curvename) + else { sk.E.model = MPI_EC_WEIERSTRASS; sk.E.dialect = ECC_DIALECT_STANDARD; if (!sk.E.h) - sk.E.h = mpi_const (MPI_C_ONE); + sk.E.h = mpi_const (MPI_C_ONE); } + + if (sk.E.dialect == ECC_DIALECT_SAFECURVE) + rc = sexp_extract_param (keyparms, NULL, "/d", &sk.d, NULL); + else + rc = sexp_extract_param (keyparms, NULL, "+d", &sk.d, NULL); + if (rc) + goto leave; + if (DBG_CIPHER) { log_debug ("ecc_decrypt info: %s/%s\n", @@ -1596,9 +1638,14 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) goto leave; } + if (sk.E.dialect == ECC_DIALECT_SAFECURVE || (flags & PUBKEY_FLAG_DJB_TWEAK)) + safecurve = 1; + else + safecurve = 0; ec = _gcry_mpi_ec_p_internal_new (sk.E.model, sk.E.dialect, flags, sk.E.p, sk.E.a, sk.E.b); + ec->h = mpi_copy (sk.E.h); /* * Compute the plaintext. @@ -1613,7 +1660,7 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) if (DBG_CIPHER) log_printpnt ("ecc_decrypt kG", &kG, NULL); - if ((flags & PUBKEY_FLAG_DJB_TWEAK)) + if (safecurve) { /* For X25519, by its definition, validation should not be done. */ /* (Instead, we do output check.) @@ -1682,7 +1729,9 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) unsigned char *rawmpi; unsigned int rawmpilen; - rc = _gcry_ecc_mont_encodepoint (x, nbits, 1, &rawmpi, &rawmpilen); + rc = _gcry_ecc_mont_encodepoint (x, nbits, + sk.E.dialect != ECC_DIALECT_SAFECURVE, + &rawmpi, &rawmpilen); if (rc) goto leave; @@ -1877,7 +1926,7 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparms) if (rc) goto leave; } - else if ((flags & PUBKEY_FLAG_DJB_TWEAK)) + else if (dialect == ECC_DIALECT_SAFECURVE || (flags & PUBKEY_FLAG_DJB_TWEAK)) { /* Remove the prefix 0x40 for keygrip computation. */ raw = mpi_get_opaque (values[6], &n); @@ -1998,7 +2047,8 @@ _gcry_pk_ecc_get_sexp (gcry_sexp_t *r_sexp, int mode, mpi_ec_t ec) unsigned char *encpk; unsigned int encpklen; - rc = _gcry_ecc_mont_encodepoint (ec->Q->x, ec->nbits, 1, + rc = _gcry_ecc_mont_encodepoint (ec->Q->x, ec->nbits, + ec->dialect != ECC_DIALECT_SAFECURVE, &encpk, &encpklen); if (rc) goto leave; diff --git a/cipher/pubkey-util.c b/cipher/pubkey-util.c index ae0e1c46..859b54a1 100644 --- a/cipher/pubkey-util.c +++ b/cipher/pubkey-util.c @@ -685,7 +685,10 @@ _gcry_pk_util_data_to_mpi (gcry_sexp_t input, gcry_mpi_t *ret_mpi, ldata = sexp_find_token (input, "data", 0); if (!ldata) { /* assume old style */ - *ret_mpi = sexp_nth_mpi (input, 0, 0); + if ((ctx->flags & PUBKEY_FLAG_RAW_FLAG)) + *ret_mpi = sexp_nth_mpi (input, 0, GCRYMPI_FMT_OPAQUE); + else + *ret_mpi = sexp_nth_mpi (input, 0, 0); return *ret_mpi ? GPG_ERR_NO_ERROR : GPG_ERR_INV_OBJ; } diff --git a/mpi/ec.c b/mpi/ec.c index ed936d74..8d3d7284 100644 --- a/mpi/ec.c +++ b/mpi/ec.c @@ -30,6 +30,7 @@ #include "ec-context.h" #include "ec-internal.h" +extern void reverse_buffer (unsigned char *buffer, unsigned int length); #define point_init(a) _gcry_mpi_point_init ((a)) #define point_free(a) _gcry_mpi_point_free_parts ((a)) @@ -366,7 +367,7 @@ mpih_set_cond (mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned long set) wp[i] = wp[i] ^ x; } } - + /* Routines for 2^255 - 19. */ #define LIMB_SIZE_25519 ((256+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB) @@ -477,7 +478,167 @@ ec_pow2_25519 (gcry_mpi_t w, const gcry_mpi_t b, mpi_ec_t ctx) { ec_mulm_25519 (w, b, b, ctx); } + +/* Routines for 2^448 - 2^224 - 1. */ + +#define LIMB_SIZE_448 ((448+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB) +#define LIMB_SIZE_HALF_448 ((LIMB_SIZE_448+1)/2) + +static void +ec_addm_448 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx) +{ + mpi_ptr_t wp, up, vp; + mpi_size_t wsize = LIMB_SIZE_448; + mpi_limb_t n[LIMB_SIZE_448]; + mpi_limb_t cy; + + if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize) + log_bug ("addm_448: different sizes\n"); + + memset (n, 0, sizeof n); + up = u->d; + vp = v->d; + wp = w->d; + + cy = _gcry_mpih_add_n (wp, up, vp, wsize); + mpih_set_cond (n, ctx->p->d, wsize, (cy != 0UL)); + _gcry_mpih_sub_n (wp, wp, n, wsize); +} + +static void +ec_subm_448 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx) +{ + mpi_ptr_t wp, up, vp; + mpi_size_t wsize = LIMB_SIZE_448; + mpi_limb_t n[LIMB_SIZE_448]; + mpi_limb_t borrow; + if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize) + log_bug ("subm_448: different sizes\n"); + + memset (n, 0, sizeof n); + up = u->d; + vp = v->d; + wp = w->d; + + borrow = _gcry_mpih_sub_n (wp, up, vp, wsize); + mpih_set_cond (n, ctx->p->d, wsize, (borrow != 0UL)); + _gcry_mpih_add_n (wp, wp, n, wsize); +} + +static void +ec_mulm_448 (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, mpi_ec_t ctx) +{ + mpi_ptr_t wp, up, vp; + mpi_size_t wsize = LIMB_SIZE_448; + mpi_limb_t n[LIMB_SIZE_448*2]; + mpi_limb_t a2[LIMB_SIZE_HALF_448]; + mpi_limb_t a3[LIMB_SIZE_HALF_448]; + mpi_limb_t b0[LIMB_SIZE_HALF_448]; + mpi_limb_t b1[LIMB_SIZE_HALF_448]; + mpi_limb_t cy; + int i; +#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2) + mpi_limb_t b1_rest, a3_rest; +#endif + + if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize) + log_bug ("mulm_448: different sizes\n"); + + up = u->d; + vp = v->d; + wp = w->d; + + _gcry_mpih_mul_n (n, up, vp, wsize); + + for (i = 0; i < (wsize + 1)/ 2; i++) + { + b0[i] = n[i]; + b1[i] = n[i+wsize/2]; + a2[i] = n[i+wsize]; + a3[i] = n[i+wsize+wsize/2]; + } + +#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2) + b0[LIMB_SIZE_HALF_448-1] &= (1UL<<32)-1; + a2[LIMB_SIZE_HALF_448-1] &= (1UL<<32)-1; + + b1_rest = 0; + a3_rest = 0; + + for (i = (wsize + 1)/ 2 -1; i >= 0; i--) + { + mpi_limb_t b1v, a3v; + b1v = b1[i]; + a3v = a3[i]; + b1[i] = (b1_rest<<32) | (b1v >> 32); + a3[i] = (a3_rest<<32) | (a3v >> 32); + b1_rest = b1v & ((1UL <<32)-1); + a3_rest = a3v & ((1UL <<32)-1); + } +#endif + + cy = _gcry_mpih_add_n (b0, b0, a2, LIMB_SIZE_HALF_448); + cy += _gcry_mpih_add_n (b0, b0, a3, LIMB_SIZE_HALF_448); + for (i = 0; i < (wsize + 1)/ 2; i++) + wp[i] = b0[i]; +#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2) + wp[LIMB_SIZE_HALF_448-1] &= ((1UL <<32)-1); +#endif + +#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2) + cy = b0[LIMB_SIZE_HALF_448-1] >> 32; +#endif + + cy = _gcry_mpih_add_1 (b1, b1, LIMB_SIZE_HALF_448, cy); + cy += _gcry_mpih_add_n (b1, b1, a2, LIMB_SIZE_HALF_448); + cy += _gcry_mpih_add_n (b1, b1, a3, LIMB_SIZE_HALF_448); + cy += _gcry_mpih_add_n (b1, b1, a3, LIMB_SIZE_HALF_448); +#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2) + b1_rest = 0; + for (i = (wsize + 1)/ 2 -1; i >= 0; i--) + { + mpi_limb_t b1v = b1[i]; + b1[i] = (b1_rest<<32) | (b1v >> 32); + b1_rest = b1v & ((1UL <<32)-1); + } + wp[LIMB_SIZE_HALF_448-1] |= (b1_rest << 32); +#endif + for (i = 0; i < wsize / 2; i++) + wp[i+(wsize + 1) / 2] = b1[i]; + +#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2) + cy = b1[LIMB_SIZE_HALF_448-1]; +#endif + + memset (n, 0, wsize * BYTES_PER_MPI_LIMB); + +#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2) + n[LIMB_SIZE_HALF_448-1] = cy << 32; +#else + n[LIMB_SIZE_HALF_448] = cy; +#endif + n[0] = cy; + _gcry_mpih_add_n (wp, wp, n, wsize); + + memset (n, 0, wsize * BYTES_PER_MPI_LIMB); + cy = _gcry_mpih_sub_n (wp, wp, ctx->p->d, wsize); + mpih_set_cond (n, ctx->p->d, wsize, (cy != 0UL)); + _gcry_mpih_add_n (wp, wp, n, wsize); +} + +static void +ec_mul2_448 (gcry_mpi_t w, gcry_mpi_t u, mpi_ec_t ctx) +{ + ec_addm_448 (w, u, u, ctx); +} + +static void +ec_pow2_448 (gcry_mpi_t w, const gcry_mpi_t b, mpi_ec_t ctx) +{ + ec_mulm_448 (w, b, b, ctx); +} + struct field_table { const char *p; @@ -498,6 +659,15 @@ static const struct field_table field_table[] = { ec_mul2_25519, ec_pow2_25519 }, + { + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + ec_addm_448, + ec_subm_448, + ec_mulm_448, + ec_mul2_448, + ec_pow2_448 + }, { NULL, NULL, NULL, NULL, NULL, NULL }, }; @@ -544,17 +714,37 @@ ec_get_two_inv_p (mpi_ec_t ec) } -static const char *curve25519_bad_points[] = { +static const char *const curve25519_bad_points[] = { + "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000001", "0x00b8495f16056286fdb1329ceb8d09da6ac49ff1fae35616aeb8413b7c7aebe0", "0x57119fd0dd4e22d8868e1c58c45c44045bef839c55b1d0b1248c50a3bc959c5f", "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec", - "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed", "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee", NULL }; + +static const char *const curve448_bad_points[] = { + "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0x00000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000", + "0x00000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000001", + "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "00000000000000000000000000000000000000000000000000000000", + NULL +}; + +static const char *const *bad_points_table[] = { + curve25519_bad_points, + curve448_bad_points, +}; + static gcry_mpi_t scanval (const char *string) { @@ -607,8 +797,19 @@ ec_p_init (mpi_ec_t ctx, enum gcry_mpi_ec_models model, if (model == MPI_EC_MONTGOMERY) { - for (i=0; i< DIM(ctx->t.scratch) && curve25519_bad_points[i]; i++) - ctx->t.scratch[i] = scanval (curve25519_bad_points[i]); + for (i=0; i< DIM(bad_points_table); i++) + { + gcry_mpi_t p_candidate = scanval (bad_points_table[i][0]); + int match_p = !mpi_cmp (ctx->p, p_candidate); + int j; + + mpi_free (p_candidate); + if (!match_p) + continue; + + for (j=0; i< DIM(ctx->t.scratch) && bad_points_table[i][j]; j++) + ctx->t.scratch[j] = scanval (bad_points_table[i][j]); + } } else { @@ -1570,6 +1771,7 @@ _gcry_mpi_ec_mul_point (mpi_point_t result, mpi_point_t q1, q2, prd, sum; unsigned long sw; mpi_size_t rsize; + int scalar_copied = 0; /* Compute scalar point multiplication with Montgomery Ladder. Note that we don't use Y-coordinate in the points at all. @@ -1585,6 +1787,29 @@ _gcry_mpi_ec_mul_point (mpi_point_t result, p2.x = mpi_copy (point->x); mpi_set_ui (p2.z, 1); + if (mpi_is_opaque (scalar)) + { + gcry_mpi_t a; + unsigned int cofactor, n; + const unsigned int pbits = mpi_get_nbits (ctx->p); + unsigned char *raw; + + scalar_copied = 1; + + raw = _gcry_mpi_get_opaque_copy (scalar, &n); + mpi_get_ui (&cofactor, ctx->h); + reverse_buffer (raw, (n+7)/8); + if ((pbits % 8)) + raw[0] &= (1 << (pbits % 8)) - 1; + raw[0] |= (1 << ((pbits + 7) % 8)); + raw[(pbits-1)/8] &= (256 - cofactor); + a = mpi_is_secure (scalar) ? mpi_snew (pbits): mpi_new (pbits); + _gcry_mpi_set_buffer (a, raw, (n+7)/8, 0); + xfree (raw); + + scalar = a; + } + point_resize (&p1, ctx); point_resize (&p2, ctx); point_resize (&p1_, ctx); @@ -1634,6 +1859,8 @@ _gcry_mpi_ec_mul_point (mpi_point_t result, point_free (&p2); point_free (&p1_); point_free (&p2_); + if (scalar_copied) + _gcry_mpi_release (scalar); return; } diff --git a/src/mpi.h b/src/mpi.h index aeba7f8b..ee6be2f7 100644 --- a/src/mpi.h +++ b/src/mpi.h @@ -266,7 +266,8 @@ enum gcry_mpi_ec_models enum ecc_dialects { ECC_DIALECT_STANDARD = 0, - ECC_DIALECT_ED25519 + ECC_DIALECT_ED25519, + ECC_DIALECT_SAFECURVE }; diff --git a/tests/Makefile.am b/tests/Makefile.am index 9e117970..e463d8c6 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -22,7 +22,8 @@ tests_bin = \ version t-secmem mpitests t-sexp t-convert \ t-mpi-bit t-mpi-point curves t-lock \ prime basic keygen pubkey hmac hashtest t-kdf keygrip \ - fips186-dsa aeswrap pkcs1v2 random dsa-rfc6979 t-ed25519 t-cv25519 + fips186-dsa aeswrap pkcs1v2 random dsa-rfc6979 \ + t-ed25519 t-cv25519 t-x448 tests_bin_last = benchmark bench-slope diff --git a/tests/curves.c b/tests/curves.c index dc5ebe77..bacc1302 100644 --- a/tests/curves.c +++ b/tests/curves.c @@ -33,7 +33,7 @@ #include "t-common.h" /* Number of curves defined in ../cipger/ecc.c */ -#define N_CURVES 22 +#define N_CURVES 23 /* A real world sample public key. */ static char const sample_key_1[] = diff --git a/tests/t-x448.c b/tests/t-x448.c new file mode 100644 index 00000000..5c3cbeb9 --- /dev/null +++ b/tests/t-x448.c @@ -0,0 +1,593 @@ +/* t-x448.c - Check the X488 computation + * Copyright (C) 2019 g10 Code GmbH + * + * 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, see . + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "stopwatch.h" + +#define PGM "t-x448" +#include "t-common.h" +#define N_TESTS 9 + + +static void +print_mpi (const char *text, gcry_mpi_t a) +{ + gcry_error_t err; + char *buf; + void *bufaddr = &buf; + + err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, bufaddr, NULL, a); + if (err) + fprintf (stderr, "%s: [error printing number: %s]\n", + text, gpg_strerror (err)); + else + { + fprintf (stderr, "%s: %s\n", text, buf); + gcry_free (buf); + } +} + + +static void +show_note (const char *format, ...) +{ + va_list arg_ptr; + + if (!verbose && getenv ("srcdir")) + fputs (" ", stderr); /* To align above "PASS: ". */ + else + fprintf (stderr, "%s: ", PGM); + va_start (arg_ptr, format); + vfprintf (stderr, format, arg_ptr); + if (*format && format[strlen(format)-1] != '\n') + putc ('\n', stderr); + va_end (arg_ptr); +} + + +/* Convert STRING consisting of hex characters into its binary + representation and return it as an allocated buffer. The valid + length of the buffer is returned at R_LENGTH. The string is + delimited by end of string. The function returns NULL on + error. */ +static void * +hex2buffer (const char *string, size_t *r_length) +{ + const char *s; + unsigned char *buffer; + size_t length; + + buffer = xmalloc (strlen(string)/2+1); + length = 0; + for (s=string; *s; s +=2 ) + { + if (!hexdigitp (s) || !hexdigitp (s+1)) + return NULL; /* Invalid hex digits. */ + ((unsigned char*)buffer)[length++] = xtoi_2 (s); + } + *r_length = length; + return buffer; +} + +static void +reverse_buffer (unsigned char *buffer, unsigned int length) +{ + unsigned int tmp, i; + + for (i=0; i < length/2; i++) + { + tmp = buffer[i]; + buffer[i] = buffer[length-1-i]; + buffer[length-1-i] = tmp; + } +} + + +/* + * Test X448 functionality through higher layer crypto routines. + * + * Input: K (as hex string), U (as hex string), R (as hex string) + * + * where R is expected result of X448 (K, U). + * + */ +static void +test_cv_hl (int testno, const char *k_str, const char *u_str, + const char *result_str) +{ + gpg_error_t err; + void *buffer = NULL; + size_t buflen; + gcry_sexp_t s_pk = NULL; + gcry_mpi_t mpi_k = NULL; + gcry_sexp_t s_data = NULL; + gcry_sexp_t s_result = NULL; + gcry_sexp_t s_tmp = NULL; + unsigned char *res = NULL; + size_t res_len; + + if (verbose > 1) + info ("Running test %d\n", testno); + + if (!(buffer = hex2buffer (k_str, &buflen)) || buflen != 56) + { + fail ("error building s-exp for test %d, %s: %s", + testno, "k", "invalid hex string"); + goto leave; + } + + mpi_k = gcry_mpi_set_opaque (NULL, buffer, buflen*8); + if ((err = gcry_sexp_build (&s_data, NULL, "%m", mpi_k))) + { + fail ("error building s-exp for test %d, %s: %s", + testno, "data", gpg_strerror (err)); + goto leave; + } + + if (!(buffer = hex2buffer (u_str, &buflen)) || buflen != 56) + { + fail ("error building s-exp for test %d, %s: %s", + testno, "u", "invalid hex string"); + goto leave; + } + + /* + * The procedure of decodeUCoordinate will be done internally + * by _gcry_ecc_mont_decodepoint. So, we just put the little-endian + * binary to build S-exp. + * + * We could add the prefix 0x40, but libgcrypt also supports + * format with no prefix. So, it is OK not to put the prefix. + */ + if ((err = gcry_sexp_build (&s_pk, NULL, + "(public-key" + " (ecc" + " (curve \"X448\")" + " (q%b)))", (int)buflen, buffer))) + { + fail ("error building s-exp for test %d, %s: %s", + testno, "pk", gpg_strerror (err)); + goto leave; + } + + xfree (buffer); + buffer = NULL; + + if ((err = gcry_pk_encrypt (&s_result, s_data, s_pk))) + fail ("gcry_pk_encrypt failed for test %d: %s", testno, + gpg_strerror (err)); + + s_tmp = gcry_sexp_find_token (s_result, "s", 0); + if (!s_tmp || !(res = gcry_sexp_nth_buffer (s_tmp, 1, &res_len))) + fail ("gcry_pk_encrypt failed for test %d: %s", testno, "missing value"); + else + { + char *r, *r0; + int i; + + r0 = r = xmalloc (2*(res_len)+1); + if (!r0) + { + fail ("memory allocation for test %d", testno); + goto leave; + } + + for (i=0; i < res_len; i++, r += 2) + snprintf (r, 3, "%02x", res[i]); + if (strcmp (result_str, r0)) + { + fail ("gcry_pk_encrypt failed for test %d: %s", + testno, "wrong value returned"); + info (" expected: '%s'", result_str); + info (" got: '%s'", r0); + } + xfree (r0); + } + + leave: + xfree (res); + gcry_mpi_release (mpi_k); + gcry_sexp_release (s_tmp); + gcry_sexp_release (s_result); + gcry_sexp_release (s_data); + gcry_sexp_release (s_pk); + xfree (buffer); +} + +/* + * Test X448 functionality through the API for X448. + * + * Input: K (as hex string), U (as hex string), R (as hex string) + * + * where R is expected result of X448 (K, U). + * + */ +static void +test_cv_x448 (int testno, const char *k_str, const char *u_str, + const char *result_str) +{ + gpg_error_t err; + void *scalar; + void *point = NULL; + size_t buflen; + unsigned char result[56]; + char result_hex[113]; + int i; + + if (verbose > 1) + info ("Running test %d\n", testno); + + if (!(scalar = hex2buffer (k_str, &buflen)) || buflen != 56) + { + fail ("error building s-exp for test %d, %s: %s", + testno, "k", "invalid hex string"); + goto leave; + } + + if (!(point = hex2buffer (u_str, &buflen)) || buflen != 56) + { + fail ("error building s-exp for test %d, %s: %s", + testno, "u", "invalid hex string"); + goto leave; + } + + if ((err = gcry_ecc_mul_point (GCRY_ECC_CURVE448, result, scalar, point))) + fail ("gcry_ecc_mul_point failed for test %d: %s", testno, + gpg_strerror (err)); + + for (i=0; i < 56; i++) + snprintf (&result_hex[i*2], 3, "%02x", result[i]); + + if (strcmp (result_str, result_hex)) + { + fail ("gcry_ecc_mul_point failed for test %d: %s", + testno, "wrong value returned"); + info (" expected: '%s'", result_str); + info (" got: '%s'", result_hex); + } + + leave: + xfree (scalar); + xfree (point); +} + +static void +test_cv (int testno, const char *k_str, const char *u_str, + const char *result_str) +{ + test_cv_hl (testno, k_str, u_str, result_str); + test_cv_x448 (testno, k_str, u_str, result_str); +} + +/* + * Test iterative X448 computation through lower layer MPI routines. + * + * Input: K (as hex string), ITER, R (as hex string) + * + * where R is expected result of iterating X448 by ITER times. + * + */ +static void +test_it (int testno, const char *k_str, int iter, const char *result_str) +{ + gcry_ctx_t ctx; + gpg_error_t err; + void *buffer = NULL; + size_t buflen; + gcry_mpi_t mpi_k = NULL; + gcry_mpi_t mpi_x = NULL; + gcry_mpi_point_t P = NULL; + gcry_mpi_point_t Q; + int i; + gcry_mpi_t mpi_kk = NULL; + + if (verbose > 1) + info ("Running test %d: iteration=%d\n", testno, iter); + + gcry_mpi_ec_new (&ctx, NULL, "X448"); + Q = gcry_mpi_point_new (0); + + if (!(buffer = hex2buffer (k_str, &buflen)) || buflen != 56) + { + fail ("error scanning MPI for test %d, %s: %s", + testno, "k", "invalid hex string"); + goto leave; + } + reverse_buffer (buffer, buflen); + if ((err = gcry_mpi_scan (&mpi_x, GCRYMPI_FMT_USG, buffer, buflen, NULL))) + { + fail ("error scanning MPI for test %d, %s: %s", + testno, "x", gpg_strerror (err)); + goto leave; + } + + xfree (buffer); + buffer = NULL; + + P = gcry_mpi_point_set (NULL, mpi_x, NULL, GCRYMPI_CONST_ONE); + + mpi_k = gcry_mpi_copy (mpi_x); + if (debug) + print_mpi ("k", mpi_k); + + for (i = 0; i < iter; i++) + { + /* + * Another variant of decodeScalar448 thing. + */ + mpi_kk = gcry_mpi_set (mpi_kk, mpi_k); + gcry_mpi_set_bit (mpi_kk, 447); + gcry_mpi_clear_bit (mpi_kk, 0); + gcry_mpi_clear_bit (mpi_kk, 1); + + gcry_mpi_ec_mul (Q, mpi_kk, P, ctx); + + P = gcry_mpi_point_set (P, mpi_k, NULL, GCRYMPI_CONST_ONE); + gcry_mpi_ec_get_affine (mpi_k, NULL, Q, ctx); + + if (debug) + print_mpi ("k", mpi_k); + } + + { + unsigned char res[56]; + char *r, *r0; + + gcry_mpi_print (GCRYMPI_FMT_USG, res, 56, NULL, mpi_k); + reverse_buffer (res, 56); + + r0 = r = xmalloc (113); + if (!r0) + { + fail ("memory allocation for test %d", testno); + goto leave; + } + + for (i=0; i < 56; i++, r += 2) + snprintf (r, 3, "%02x", res[i]); + + if (strcmp (result_str, r0)) + { + fail ("X448 failed for test %d: %s", + testno, "wrong value returned"); + info (" expected: '%s'", result_str); + info (" got: '%s'", r0); + } + xfree (r0); + } + + leave: + gcry_mpi_release (mpi_kk); + gcry_mpi_release (mpi_k); + gcry_mpi_point_release (P); + gcry_mpi_release (mpi_x); + xfree (buffer); + gcry_mpi_point_release (Q); + gcry_ctx_release (ctx); +} + +/* + * X-coordinate of generator of the X448. + */ +#define G_X ("0500000000000000000000000000000000000000000000000000000000000000" \ + "000000000000000000000000000000000000000000000000") + +/* + * Test Diffie-Hellman in RFC-7748. + * + * Note that it's not like the ECDH of OpenPGP, where we use + * ephemeral public key. + */ +static void +test_dh (int testno, const char *a_priv_str, const char *a_pub_str, + const char *b_priv_str, const char *b_pub_str, + const char *result_str) +{ + /* Test A for private key corresponds to public key. */ + test_cv (testno, a_priv_str, G_X, a_pub_str); + /* Test B for private key corresponds to public key. */ + test_cv (testno, b_priv_str, G_X, b_pub_str); + /* Test DH with A's private key and B's public key. */ + test_cv (testno, a_priv_str, b_pub_str, result_str); + /* Test DH with B's private key and A's public key. */ + test_cv (testno, b_priv_str, a_pub_str, result_str); +} + + +static void +check_x448 (void) +{ + int ntests; + + info ("Checking X448.\n"); + + ntests = 0; + + /* + * Values are cited from RFC-7748: 5.2. Test Vectors. + * Following two tests are for the first type test. + */ + test_cv (1, + "3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121" + "700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3", + "06fce640fa3487bfda5f6cf2d5263f8aad88334cbd07437f020f08f9" + "814dc031ddbdc38c19c6da2583fa5429db94ada18aa7a7fb4ef8a086", + "ce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239f" + "e14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f"); + ntests++; + test_cv (2, + "203d494428b8399352665ddca42f9de8fef600908e0d461cb021f8c5" + "38345dd77c3e4806e25f46d3315c44e0a5b4371282dd2c8d5be3095f", + "0fbcc2f993cd56d3305b0b7d9e55d4c1a8fb5dbb52f8e9a1e9b6201b" + "165d015894e56c4d3570bee52fe205e28a78b91cdfbde71ce8d157db", + "884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7" + "ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d"); + ntests++; + + /* + * Additional test. Value is from second type test. + */ + test_cv (3, + G_X, + G_X, + "3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a" + "4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113"); + ntests++; + + /* + * Following two tests are for the second type test, + * with one iteration and 1,000 iterations. (1,000,000 iterations + * takes too long.) + */ + test_it (4, + G_X, + 1, + "3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a" + "4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113"); + ntests++; + + test_it (5, + G_X, + 1000, + "aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4" + "af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38"); + ntests++; + + /* + * Last test is from: 6. Diffie-Hellman, 6.2. Curve448 + */ + test_dh (6, + /* Alice's private key, a */ + "9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28d" + "d9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b", + /* Alice's public key, X448(a, 5) */ + "9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c" + "22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0", + /* Bob's private key, b */ + "1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d" + "6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d", + /* Bob's public key, X448(b, 5) */ + "3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b430" + "27d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609", + /* Their shared secret, K */ + "07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282b" + "b60c0b56fd2464c335543936521c24403085d59a449a5037514a879d"); + ntests++; + + /* three tests which results 0. */ + test_cv (7, + "3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121" + "700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3", + "00000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000", + "00000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000"); + ntests++; + + test_cv (8, + "3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121" + "700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3", + "01000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000", + "00000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000"); + ntests++; + + test_cv (9, + "3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121" + "700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3", + "feffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "feffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "00000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000"); + ntests++; + + if (ntests != N_TESTS) + fail ("did %d tests but expected %d", ntests, N_TESTS); + else if ((ntests % 256)) + show_note ("%d tests done\n", ntests); +} + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + + if (argc) + { argc--; argv++; } + + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + fputs ("usage: " PGM " [options]\n" + "Options:\n" + " --verbose print timings etc.\n" + " --debug flyswatter\n", + stdout); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose += 2; + debug++; + argc--; argv++; + } + else if (!strncmp (*argv, "--", 2)) + die ("unknown option '%s'", *argv); + } + + xgcry_control ((GCRYCTL_DISABLE_SECMEM, 0)); + if (!gcry_check_version (GCRYPT_VERSION)) + die ("version mismatch\n"); + if (debug) + xgcry_control ((GCRYCTL_SET_DEBUG_FLAGS, 1u , 0)); + xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0)); + xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0)); + + start_timer (); + check_x448 (); + stop_timer (); + + info ("All tests completed in %s. Errors: %d\n", + elapsed_time (1), error_count); + return !!error_count; +} From gniibe at fsij.org Mon Oct 14 06:47:58 2019 From: gniibe at fsij.org (Niibe Yutaka) Date: Mon, 14 Oct 2019 13:47:58 +0900 Subject: An octet sequence as an MPI In-Reply-To: <87wodbkj7z.fsf@iwagami.gniibe.org> References: <87wodbkj7z.fsf@iwagami.gniibe.org> Message-ID: <87blukvsox.fsf@jumper.gniibe.org> Hello, again, NIIBE Yutaka wrote: > Current libgcrypt + GnuPG implementation have an issue around handling > of an octet sequence as big-endian number in an MPI. More specifically, for S-expression, here is an example program to show the problem. The point is that the reader side should know how to interpret binary in S-expression. It is not defined by the data itself. -- -------------- next part -------------- A non-text attachment was scrubbed... Name: test-sexp-mpi-octet.c Type: text/x-csrc Size: 2391 bytes Desc: not available URL: From jussi.kivilinna at iki.fi Mon Oct 14 21:48:17 2019 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Mon, 14 Oct 2019 22:48:17 +0300 Subject: [PATCH] hash-common: avoid integer division to reduce call overhead Message-ID: <157108249763.32177.8039741921960436846.stgit@localhost.localdomain> * cipher/hash-common.h (gcry_md_block_ctx): Replace 'blocksize' with 'blocksize_shift'. * cipher/hash-common.c (_gcry_md_block_write): Use bit-level operations instead of division to get number of blocks. * cipher/gostr2411-94.c (gost3411_init): Initialize 'blocksize_shift' instead of 'blocksize'. * cipher/md2.c (md2_init): Ditto. * cipher/md4.c (md4_init): Ditto. * cipher/md5.c (md5_init): Ditto. * cipher/rmd160.c (rmd160_init): Ditto. * cipher/sha1.c (sha1_init): Ditto. * cipher/sha256.c (sha256_common_init): Ditto. * cipher/sha512.c (sha512_init_common): Ditto. * cipher/sm3.c (sm3_init): Ditto. * cipher/stribog.c (stribog_init_512): Ditto. * cipher/tiger.c (do_init): Ditto. * cipher/whirlpool.c (whirlpool_init): Ditto. -- Signed-off-by: Jussi Kivilinna --- cipher/gostr3411-94.c | 2 +- cipher/hash-common.c | 9 +++++---- cipher/hash-common.h | 2 +- cipher/md2.c | 2 +- cipher/md4.c | 2 +- cipher/md5.c | 2 +- cipher/rmd160.c | 2 +- cipher/sha1.c | 2 +- cipher/sha256.c | 2 +- cipher/sha512.c | 2 +- cipher/sm3.c | 2 +- cipher/stribog.c | 2 +- cipher/tiger.c | 2 +- cipher/whirlpool.c | 2 +- 14 files changed, 18 insertions(+), 17 deletions(-) diff --git a/cipher/gostr3411-94.c b/cipher/gostr3411-94.c index d9746275e..ebf9e0a24 100644 --- a/cipher/gostr3411-94.c +++ b/cipher/gostr3411-94.c @@ -61,7 +61,7 @@ gost3411_init (void *context, unsigned int flags) hd->bctx.nblocks = 0; hd->bctx.count = 0; - hd->bctx.blocksize = 32; + hd->bctx.blocksize_shift = _gcry_ctz(32); hd->bctx.bwrite = transform; hd->cryptopro = 0; } diff --git a/cipher/hash-common.c b/cipher/hash-common.c index 74675d49f..ab486f06e 100644 --- a/cipher/hash-common.c +++ b/cipher/hash-common.c @@ -123,7 +123,8 @@ _gcry_md_block_write (void *context, const void *inbuf_arg, size_t inlen) gcry_md_block_ctx_t *hd = context; unsigned int stack_burn = 0; unsigned int nburn; - const unsigned int blocksize = hd->blocksize; + const unsigned int blocksize_shift = hd->blocksize_shift; + const unsigned int blocksize = 1 << blocksize_shift; size_t inblocks; size_t copylen; @@ -164,14 +165,14 @@ _gcry_md_block_write (void *context, const void *inbuf_arg, size_t inlen) if (inlen >= blocksize) { - inblocks = inlen / blocksize; + inblocks = inlen >> blocksize_shift; nburn = hd->bwrite (hd, inbuf, inblocks); stack_burn = nburn > stack_burn ? nburn : stack_burn; hd->count = 0; hd->nblocks_high += (hd->nblocks + inblocks < inblocks); hd->nblocks += inblocks; - inlen -= inblocks * blocksize; - inbuf += inblocks * blocksize; + inlen -= inblocks << blocksize_shift; + inbuf += inblocks << blocksize_shift; } if (inlen) diff --git a/cipher/hash-common.h b/cipher/hash-common.h index 0b3ade11e..561e77a7e 100644 --- a/cipher/hash-common.h +++ b/cipher/hash-common.h @@ -51,7 +51,7 @@ typedef struct gcry_md_block_ctx MD_NBLOCKS_TYPE nblocks; MD_NBLOCKS_TYPE nblocks_high; int count; - size_t blocksize; + unsigned int blocksize_shift; _gcry_md_block_write_t bwrite; } gcry_md_block_ctx_t; diff --git a/cipher/md2.c b/cipher/md2.c index bf2fbee4c..dc4deac3b 100644 --- a/cipher/md2.c +++ b/cipher/md2.c @@ -135,7 +135,7 @@ md2_init (void *context, unsigned int flags) (void)flags; memset (ctx, 0, sizeof(*ctx)); - ctx->bctx.blocksize = 16; + ctx->bctx.blocksize_shift = _gcry_ctz(16); ctx->bctx.bwrite = transform; } diff --git a/cipher/md4.c b/cipher/md4.c index b75bc5e69..24986c27a 100644 --- a/cipher/md4.c +++ b/cipher/md4.c @@ -83,7 +83,7 @@ md4_init (void *context, unsigned int flags) ctx->bctx.nblocks = 0; ctx->bctx.nblocks_high = 0; ctx->bctx.count = 0; - ctx->bctx.blocksize = 64; + ctx->bctx.blocksize_shift = _gcry_ctz(64); ctx->bctx.bwrite = transform; } diff --git a/cipher/md5.c b/cipher/md5.c index 94fcdf033..6859d5664 100644 --- a/cipher/md5.c +++ b/cipher/md5.c @@ -67,7 +67,7 @@ md5_init( void *context, unsigned int flags) ctx->bctx.nblocks = 0; ctx->bctx.nblocks_high = 0; ctx->bctx.count = 0; - ctx->bctx.blocksize = 64; + ctx->bctx.blocksize_shift = _gcry_ctz(64); ctx->bctx.bwrite = transform; } diff --git a/cipher/rmd160.c b/cipher/rmd160.c index 24210a077..0608f74cd 100644 --- a/cipher/rmd160.c +++ b/cipher/rmd160.c @@ -166,7 +166,7 @@ rmd160_init (void *context, unsigned int flags) hd->bctx.nblocks = 0; hd->bctx.nblocks_high = 0; hd->bctx.count = 0; - hd->bctx.blocksize = 64; + hd->bctx.blocksize_shift = _gcry_ctz(64); hd->bctx.bwrite = transform; } diff --git a/cipher/sha1.c b/cipher/sha1.c index 23aceef32..d3ee982b0 100644 --- a/cipher/sha1.c +++ b/cipher/sha1.c @@ -274,7 +274,7 @@ sha1_init (void *context, unsigned int flags) hd->bctx.nblocks = 0; hd->bctx.nblocks_high = 0; hd->bctx.count = 0; - hd->bctx.blocksize = 64; + hd->bctx.blocksize_shift = _gcry_ctz(64); /* Order of feature checks is important here; last match will be * selected. Keep slower implementations at the top and faster at diff --git a/cipher/sha256.c b/cipher/sha256.c index 562dee9af..175da909d 100644 --- a/cipher/sha256.c +++ b/cipher/sha256.c @@ -246,7 +246,7 @@ sha256_common_init (SHA256_CONTEXT *hd) hd->bctx.nblocks = 0; hd->bctx.nblocks_high = 0; hd->bctx.count = 0; - hd->bctx.blocksize = 64; + hd->bctx.blocksize_shift = _gcry_ctz(64); /* Order of feature checks is important here; last match will be * selected. Keep slower implementations at the top and faster at diff --git a/cipher/sha512.c b/cipher/sha512.c index b05157aa9..df9a449c3 100644 --- a/cipher/sha512.c +++ b/cipher/sha512.c @@ -302,7 +302,7 @@ sha512_init_common (SHA512_CONTEXT *ctx, unsigned int flags) ctx->bctx.nblocks = 0; ctx->bctx.nblocks_high = 0; ctx->bctx.count = 0; - ctx->bctx.blocksize = 128; + ctx->bctx.blocksize_shift = _gcry_ctz(128); /* Order of feature checks is important here; last match will be * selected. Keep slower implementations at the top and faster at diff --git a/cipher/sm3.c b/cipher/sm3.c index b6f0ab28c..aee949874 100644 --- a/cipher/sm3.c +++ b/cipher/sm3.c @@ -77,7 +77,7 @@ sm3_init (void *context, unsigned int flags) hd->bctx.nblocks = 0; hd->bctx.nblocks_high = 0; hd->bctx.count = 0; - hd->bctx.blocksize = 64; + hd->bctx.blocksize_shift = _gcry_ctz(64); hd->bctx.bwrite = transform; (void)features; diff --git a/cipher/stribog.c b/cipher/stribog.c index 267872474..c919182ad 100644 --- a/cipher/stribog.c +++ b/cipher/stribog.c @@ -1206,7 +1206,7 @@ stribog_init_512 (void *context, unsigned int flags) memset (hd, 0, sizeof (*hd)); - hd->bctx.blocksize = 64; + hd->bctx.blocksize_shift = _gcry_ctz(64); hd->bctx.bwrite = transform; } diff --git a/cipher/tiger.c b/cipher/tiger.c index c78e3ac35..b2f166773 100644 --- a/cipher/tiger.c +++ b/cipher/tiger.c @@ -601,7 +601,7 @@ do_init (void *context, int variant) hd->bctx.nblocks = 0; hd->bctx.nblocks_high = 0; hd->bctx.count = 0; - hd->bctx.blocksize = 64; + hd->bctx.blocksize_shift = _gcry_ctz(64); hd->bctx.bwrite = transform; hd->variant = variant; } diff --git a/cipher/whirlpool.c b/cipher/whirlpool.c index d9b79cf1a..79b2026b5 100644 --- a/cipher/whirlpool.c +++ b/cipher/whirlpool.c @@ -1179,7 +1179,7 @@ whirlpool_init (void *ctx, unsigned int flags) memset (context, 0, sizeof (*context)); - context->bctx.blocksize = BLOCK_SIZE; + context->bctx.blocksize_shift = _gcry_ctz(BLOCK_SIZE); context->bctx.bwrite = whirlpool_transform; if ((flags & GCRY_MD_FLAG_BUGEMU1)) { From jussi.kivilinna at iki.fi Mon Oct 14 21:48:42 2019 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Mon, 14 Oct 2019 22:48:42 +0300 Subject: [PATCH] Fix building t-lock for WIN32 Message-ID: <157108252226.32386.9479935439055258264.stgit@localhost.localdomain> * tests/t-lock.c (external_lock_test_init, external_lock_test_lock) (externel_lock_test_unlock, external_lock_test_destroy) (nonce_thread, get_rand, pick_account, pick_value, revision_thread) (accountant_thread): Build also if _WIN32 defined in addition to HAVE_PTHREAD. -- Signed-off-by: Jussi Kivilinna --- tests/t-lock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/t-lock.c b/tests/t-lock.c index e09b782e4..e263aff2e 100644 --- a/tests/t-lock.c +++ b/tests/t-lock.c @@ -90,7 +90,7 @@ struct thread_arg_s -#ifdef HAVE_PTHREAD +#if defined(HAVE_PTHREAD) || defined(_WIN32) /* Wrapper functions to access Libgcrypt's internal test lock. */ static void external_lock_test_init (int line) @@ -136,7 +136,7 @@ external_lock_test_destroy (int line) -#ifdef HAVE_PTHREAD +#if defined(HAVE_PTHREAD) || defined(_WIN32) /* The nonce thread. We simply request a couple of nonces and return. */ static THREAD_RET_TYPE @@ -253,7 +253,7 @@ print_accounts (void) } -#ifdef HAVE_PTHREAD +#if defined(HAVE_PTHREAD) || defined(_WIN32) /* Get a a random integer value in the range 0 to HIGH. */ static unsigned int get_rand (int high) From clay at cs.georgetown.edu Tue Oct 22 00:42:27 2019 From: clay at cs.georgetown.edu (Clay Shields) Date: Mon, 21 Oct 2019 18:42:27 -0400 Subject: Oddity in AES encryption? Message-ID: <24E5C487-4943-4CB7-90B7-6BD65889C883@cs.georgetown.edu> Hi, I am working on a C programming project that involves encrypting files with AES-256-CBC. I was testing libgcrypt and was comparing it to other libraries when I noticed that the the output of libgcrypt seems to reverse the order of the final two blocks compared to the openssl library. I then used test values from NIST 800-38a and found that the final two blocks produced by libgcrypt are swapped from what the standards recommend. Libgcrypt is able to decrypt its own output but fails to decrypt the output of openssl. Using the test vectors from F.2.5 of NIST Special Publication 800-38A (https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf), I get: SSl Ciphertext : 0000 - f5 8c 4c 04 d6 e5 f1 ba-77 9e ab fb 5f 7b fb d6 ..L.....w..._{.. 0010 - 9c fc 4e 96 7e db 80 8d-67 9f 77 7b c6 70 2c 7d ..N.~...g.w{.p,} 0020 - 39 f2 33 69 a9 d9 ba cf-a5 30 e2 63 04 23 14 61 9.3i.....0.c.#.a 0030 - b2 eb 05 e2 c3 9b e9 fc-da 6c 19 07 8c 6a 9d 1b .........l...j.. gcrypt Ciphertext is: 0000 - f5 8c 4c 04 d6 e5 f1 ba-77 9e ab fb 5f 7b fb d6 ..L.....w..._{.. 0010 - 9c fc 4e 96 7e db 80 8d-67 9f 77 7b c6 70 2c 7d ..N.~...g.w{.p,} 0020 - b2 eb 05 e2 c3 9b e9 fc-da 6c 19 07 8c 6a 9d 1b .........l...j.. 0030 - 39 f2 33 69 a9 d9 ba cf-a5 30 e2 63 04 23 14 61 9.3i.....0.c.#.a I believe that according to the NIST specs, libgcrypt has swapped the blocks labelled 0020 and 0030. Perhaps I have done something wrong or unusual in my use of the library? Thanks, Clay ?? Code // uses SSL library to dump hax values to screen // Replace print functions or compile with ssl: // gcc gcrypt-test.c -o gcrypt-test `/usr/bin/libgcrypt-config --libs --cflags` -lcrypto // code from: https://www.tnichols.org/2015/09/27/Encrypting-and-Signing-Using-libgcrypt/ #include #include #include #include // size_t #include #define AES256_KEY_SIZE 32 #define AES256_BLOCK_SIZE 16 typedef struct { unsigned char * ct; int ct_len; } encryption; typedef struct { unsigned char * pt; int pt_len; } decryption; void handlegcrypterr(int err){ fprintf(stderr, "cipher_open: %s/%s\n", gcry_strsource(err), gcry_strerror(err)); abort(); } void print_encryption(encryption * e){ BIO_dump_fp (stdout, (const char *)e->ct, e->ct_len); } encryption ** encrypt_gcrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *iv){ gcry_cipher_hd_t handle; gcry_error_t err; // 256-bit AES using cipher-block chaining; with ciphertext stealing, no manual padding is required err = gcry_cipher_open(&handle, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_CBC_CTS); if (err) { handlegcrypterr(err); } err = gcry_cipher_setkey(handle, key, AES256_KEY_SIZE); if (err) { handlegcrypterr(err); } err = gcry_cipher_setiv(handle, iv, AES256_BLOCK_SIZE); if (err) { handlegcrypterr(err); } // Find number of blocks required for data int blocks_required = plaintext_len / AES256_BLOCK_SIZE; if (plaintext_len % AES256_BLOCK_SIZE != 0) { blocks_required++; } // Make new buffer of size blocks_required * AES256_BLOCK_SIZE for in-place encryption unsigned char * ciphertext = malloc(blocks_required * AES256_BLOCK_SIZE); memcpy(ciphertext, plaintext, plaintext_len); // Encryption is performed in-place err = gcry_cipher_encrypt(handle, ciphertext, AES256_BLOCK_SIZE * blocks_required, NULL, 0); if (err) { handlegcrypterr(err); } free(handle); encryption ** result = malloc(sizeof(encryption *)); *result = (encryption *) malloc(sizeof(encryption)); (*result)->ct = ciphertext; (*result)->ct_len = blocks_required * AES256_BLOCK_SIZE; return result; } decryption ** decrypt_gcrypt(encryption *ciphertext, unsigned char *key, unsigned char *iv){ gcry_cipher_hd_t handle; gcry_error_t err; // 256-bit AES using cipher-block chaining; with ciphertext stealing, no manual padding is required err = gcry_cipher_open(&handle, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_CBC_CTS); if (err) { handlegcrypterr(err); } err = gcry_cipher_setkey(handle, key, AES256_KEY_SIZE); if (err) { handlegcrypterr(err); } err = gcry_cipher_setiv(handle, iv, AES256_BLOCK_SIZE); if (err) { handlegcrypterr(err); } // Find number of blocks required for data int blocks_required = ciphertext->ct_len / AES256_BLOCK_SIZE; if (ciphertext->ct_len % AES256_BLOCK_SIZE != 0) { blocks_required++; } // Make new buffer of size blocks_required * AES256_BLOCK_SIZE for in-place encryption unsigned char * plaintext = malloc(blocks_required * AES256_BLOCK_SIZE); // Encryption is performed in-place // put the plaintext where we need it memcpy(plaintext, ciphertext->ct, ciphertext->ct_len); err = gcry_cipher_decrypt(handle, plaintext, AES256_BLOCK_SIZE * blocks_required, NULL, 0); if (err) { handlegcrypterr(err); } free(handle); decryption ** result = malloc(sizeof(decryption *)); *result = (decryption *) malloc(sizeof(decryption)); (*result)->pt = plaintext; (*result)->pt_len = blocks_required * AES256_BLOCK_SIZE; return result; } int main (void) { // Test values taken from NIST 800-38a, section F.2.5 // https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf /* A 256 bit key */ unsigned char key[32] = {0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4}; /* A 128 bit IV */ unsigned char iv[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; /* Message to be encrypted */ const unsigned int plaintext_size = 64; unsigned char plaintext[64] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10}; encryption ** gcrypt_encrypted = encrypt_gcrypt (plaintext, plaintext_size, key, iv); printf("\ngcrypt Ciphertext is:\n"); print_encryption(*gcrypt_encrypted); /* Decrypt the ciphertext */ decryption ** gcrypt_decrypted = decrypt_gcrypt(*gcrypt_encrypted, key, iv); printf("\ngcrypt Plaintext is:\n"); print_decryption(*gcrypt_decrypted); return 0; } From jussi.kivilinna at iki.fi Wed Oct 23 21:47:43 2019 From: jussi.kivilinna at iki.fi (Jussi Kivilinna) Date: Wed, 23 Oct 2019 22:47:43 +0300 Subject: Oddity in AES encryption? In-Reply-To: <24E5C487-4943-4CB7-90B7-6BD65889C883@cs.georgetown.edu> References: <24E5C487-4943-4CB7-90B7-6BD65889C883@cs.georgetown.edu> Message-ID: <63278bb0-daff-18ba-33cf-668797f9b138@iki.fi> Hello, On 22.10.2019 1.42, Clay Shields wrote: > > Hi, > > I am working on a C programming project that involves encrypting files with AES-256-CBC. I was testing libgcrypt and was comparing it to other libraries when I noticed that the the output of libgcrypt seems to reverse the order of the final two blocks compared to the openssl library. I then used test values from NIST 800-38a and found that the final two blocks produced by libgcrypt are swapped from what the standards recommend. Libgcrypt is able to decrypt its own output but fails to decrypt the output of openssl. Oddity is actually caused by ciphertext stealing. With CTS enabled, the last two blocks of CBC ciphertext get swapped when input length is multiply of cipher's blocksize. Running your example without "GCRY_CIPHER_CBC_CTS", I get: gcrypt Ciphertext is: 0000 - f5 8c 4c 04 d6 e5 f1 ba-77 9e ab fb 5f 7b fb d6 ..L.....w..._{.. 0010 - 9c fc 4e 96 7e db 80 8d-67 9f 77 7b c6 70 2c 7d ..N.~...g.w{.p,} 0020 - 39 f2 33 69 a9 d9 ba cf-a5 30 e2 63 04 23 14 61 9.3i.....0.c.#.a 0030 - b2 eb 05 e2 c3 9b e9 fc-da 6c 19 07 8c 6a 9d 1b .........l...j.. -Jussi > > Using the test vectors from F.2.5 of NIST Special Publication 800-38A (https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf), I get: > > SSl Ciphertext : > 0000 - f5 8c 4c 04 d6 e5 f1 ba-77 9e ab fb 5f 7b fb d6 ..L.....w..._{.. > 0010 - 9c fc 4e 96 7e db 80 8d-67 9f 77 7b c6 70 2c 7d ..N.~...g.w{.p,} > 0020 - 39 f2 33 69 a9 d9 ba cf-a5 30 e2 63 04 23 14 61 9.3i.....0.c.#.a > 0030 - b2 eb 05 e2 c3 9b e9 fc-da 6c 19 07 8c 6a 9d 1b .........l...j.. > > gcrypt Ciphertext is: > 0000 - f5 8c 4c 04 d6 e5 f1 ba-77 9e ab fb 5f 7b fb d6 ..L.....w..._{.. > 0010 - 9c fc 4e 96 7e db 80 8d-67 9f 77 7b c6 70 2c 7d ..N.~...g.w{.p,} > 0020 - b2 eb 05 e2 c3 9b e9 fc-da 6c 19 07 8c 6a 9d 1b .........l...j.. > 0030 - 39 f2 33 69 a9 d9 ba cf-a5 30 e2 63 04 23 14 61 9.3i.....0.c.#.a > > I believe that according to the NIST specs, libgcrypt has swapped the blocks labelled 0020 and 0030. > > Perhaps I have done something wrong or unusual in my use of the library? > > Thanks, > > Clay > From gniibe at fsij.org Fri Oct 25 06:13:14 2019 From: gniibe at fsij.org (Niibe Yutaka) Date: Fri, 25 Oct 2019 13:13:14 +0900 Subject: ecc: Use opaque bytes by MPI (was: An octet sequence as an MPI) In-Reply-To: <87blukvsox.fsf@jumper.gniibe.org> References: <87wodbkj7z.fsf@iwagami.gniibe.org> <87blukvsox.fsf@jumper.gniibe.org> Message-ID: <87pnilea3p.fsf@jumper.gniibe.org> Hello, In master, I clean up the code of ECC. Now, for point representation (in public key, in secret key, and in ephemeral key), it uses an opaque bytes internally. We have public API: gcry_mpi_ec_get_mpi gcry_mpi_ec_set_mpi which is questionable these days (it's useful when we do experiment on ECC domain parameters). I don't touch here. For the public API gcry_mpi_ec_decode_point, it supports both cases of an opaque bytes and normal MPI. I found that for EdDSA, while the 0x40 prefix is used in public key and secret key, in r of signature, there is no prefix. The places where we still have problems are: EdDSA r of signature: It's native point representation in little endian with no prefix. Zero-byte at the beginnings may be removed, thus we have left-pad-zero handling for input. For input, no prefix is allowed. EdDSA s of signature: It's native little endian. Zero-byte at the beginnings may be removed, thus we have left-pad-zero handling for input. EdDSA private key bytes: It's fixed-length bytes. Zero-byte at the beginnings may be removed, thus we have left-pad-zero handling, for input. I don't change those, for now. -- From gniibe at fsij.org Fri Oct 25 06:35:51 2019 From: gniibe at fsij.org (Niibe Yutaka) Date: Fri, 25 Oct 2019 13:35:51 +0900 Subject: An octet sequence as an MPI In-Reply-To: <87wodbkj7z.fsf@iwagami.gniibe.org> References: <87wodbkj7z.fsf@iwagami.gniibe.org> Message-ID: <87o8y5e920.fsf@jumper.gniibe.org> NIIBE Yutaka wrote: > IIUC, this issue originates in RFC 6637, and we can find same problem in > the draft of rfc4880bis. I will ask and discuss in OpenPGP WG, if > needed, but for now, I'd like to improve the implementation at first. [...] > It is described as "MPI", but I think that it is better to be described > as "size + octets". > > MPI has semantics than mere octet string; It is (may be) interpreted as > a big-endian number, and its representation can be changed where minimum > size representation is assumed (removing zero octets at the beginning). > > For #1 and #3, putting the point representation in an MPI is > questionable, as the point is not a big-endian number. Because of its > prefix (the first octet, 0x04 or 0x40), it is safe against removing-zero > handling. But, by defining as "size + octets", it allows any point > representation. > > For #2, the description is not accuate for EdDSA, since it's not "a > scalar of the public EC point". It is just a fixed-size octets. When > it is handled as an MPI, it is unsafe against removing-zero handling. > So, our implementation of libgcrypt has special care for that, namely, > detecting removed zeros to recover them. By defining as "size + > octets", it allows curve implementation to interpret octets. > > For #4, current implementation assumes an MPI, but it is more natural > for other curves to define it as "size + octets". For Curve25519, we do > use this field as an MPI (as a big-endian number), just like other NIST > curves. For X448, it is more friendly to the X448 API to use this field > as a native fixed-size little endian octets. By defining as "size + > octets", it allows curve implementation to interpret octets. > (We can define X25519, using X25519 native format.) To be friendly to existing data of Ed25519, I think that it's good to define native string format as: a two-octet scalar for (8*size of octet string) octet string It could be interpreted as an MPI, but generally it's ill-formed MPI, because the first bits can be zeros. I realized that GnuPG implementation handles those things as an MPI of OpenPGP format. We need to change to support native bytes. --