more change on Curve25519 ECDH (was: [git] GCRYPT - branch, master, updated. libgcrypt-1.6.0-376-gee7e1a0)
NIIBE Yutaka
gniibe at fsij.org
Tue Apr 12 09:05:47 CEST 2016
On 04/12/2016 10:12 AM, by NIIBE Yutaka wrote:
> ecc: Fix X25519 computation on Curve25519.
>
> * cipher/ecc.c (ecc_encrypt_raw): Tweak of bits when
> PUBKEY_FLAG_DJB_TWEAK is enabled.
> (ecc_decrypt_raw): Return 0 when PUBKEY_FLAG_DJB_TWEAK is enabled.
> * tests/t-cv25519.c (test_cv): Update by using gcry_pk_encrypt.
>
> --
>
> X25519 function is not a plain scalar multiplication, but does
> two things; the scalar bits are tweaked before applying scalar
> multiplication and X0 function is applied to the result of
> scalar multiplication.
>
> In libgcrypt, _gcry_mpi_ec_mul_point is a plain scalar multiplication
> and those two things are done in functions for ECDH with X25519.
I thought this was the final change of mine, but trying to fix GnuPG,
I think that another patch is needed for libgcrypt.
Here is the change.
(1) For ecc_encrypt_raw, it won't abort but properly use X0 function
to map infinity to 0
(2) In ecc_decrypt_raw, avoiding validation (to be X25519 compatible) is
based on the flag not the name of the curve.
(3) In ecc_decrypt_raw, since our major use case is GnuPG, we handle
the case of infinity differently. While X25519 returns 0,
libgcrypt returns an error.
For (3), I don't implement this as input validation but as result
handling. If we do input validation, the values are:
0x0000000000000000000000000000000000000000000000000000000000000000
0x0000000000000000000000000000000000000000000000000000000000000001
0x00b8495f16056286fdb1329ceb8d09da6ac49ff1fae35616aeb8413b7c7aebe0
0x57119fd0dd4e22d8868e1c58c45c44045bef839c55b1d0b1248c50a3bc959c5f
0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec
0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed
0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee
Those other values (which is listed in https://cr.yp.to/ecdh.html )
0x80b8495f16056286fdb1329ceb8d09da6ac49ff1fae35616aeb8413b7c7aebcd
0xd7119fd0dd4e22d8868e1c58c45c44045bef839c55b1d0b1248c50a3bc959c4c
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd9
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffda
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdb
are not possible by the definition of X25519 where the MSB is cleared.
diff --git a/cipher/ecc.c b/cipher/ecc.c
index f6b2b69..663f4a8 100644
--- a/cipher/ecc.c
+++ b/cipher/ecc.c
@@ -1394,7 +1394,13 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t
s_data, gcry_sexp_t keyparms)
/* R = kQ <=> R = kdG */
_gcry_mpi_ec_mul_point (&R, data, &pk.Q, ec);
- if (_gcry_mpi_ec_get_affine (x, y, &R, ec))
+ /*
+ * Here, X is 0. In the X25519 computation on Curve25519, X0
+ * function maps infinity to zero. So, when PUBKEY_FLAG_DJB_TWEAK
+ * is enabled, skip the check to get the result of 0.
+ */
+ if (_gcry_mpi_ec_get_affine (x, y, &R, ec)
+ && !(flags & PUBKEY_FLAG_DJB_TWEAK))
log_fatal ("ecdh: Failed to get affine coordinates for kdG\n");
if (y)
mpi_s = _gcry_ecc_ec2os (x, y, pk.E.p);
@@ -1598,8 +1604,8 @@ 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 (!(curvename && !strcmp (curvename, "Curve25519"))
- /* For Curve25519, by its definition, validation should not be
done. */
+ if (!(flags & PUBKEY_FLAG_DJB_TWEAK)
+ /* For X25519, by its definition, validation should not be done. */
&& !_gcry_mpi_ec_curve_point (&kG, ec))
{
rc = GPG_ERR_INV_DATA;
@@ -1619,14 +1625,35 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain,
gcry_sexp_t s_data, gcry_sexp_t keyparms)
else
y = mpi_new (0);
- /*
- * Here, x is 0. In the X25519 computation on Curve25519, X0
- * function maps infinity to zero. So, when PUBKEY_FLAG_DJB_TWEAK
- * is enabled, we can just skip the check to get the result of 0.
- */
- if (_gcry_mpi_ec_get_affine (x, y, &R, ec)
- && !(flags & PUBKEY_FLAG_DJB_TWEAK))
- log_fatal ("ecdh: Failed to get affine coordinates\n");
+ if (_gcry_mpi_ec_get_affine (x, y, &R, ec))
+ {
+ if (!(flags & PUBKEY_FLAG_DJB_TWEAK))
+ log_fatal ("ecdh: Failed to get affine coordinates\n");
+ else
+ {
+ /*
+ * By the definition of X25519, this is the case where it
+ * returns 0, mapping infinity to zero. However, we
+ * deliberately let it return an error.
+ *
+ * Comming here means that it might be decrypted by anyone
+ * with the shared secret of 0 (the result of this
+ * function could be always 0 by other scalar values,
+ * other than the private key of SK.D).
+ *
+ * So, it looks like an encrypted message but it can be
+ * decrypted by anyone, or at least something wrong
+ * happens. Recipient should not proceed as if it were
+ * properly encrypted message.
+ *
+ * This handling is needed for our major usage of GnuPG,
+ * where it does the One-Pass Diffie-Hellman method,
+ * C(1, 1, ECC CDH), with an ephemeral key.
+ */
+ rc = GPG_ERR_INV_DATA;
+ goto leave;
+ }
+ }
if (y)
r = _gcry_ecc_ec2os (x, y, sk.E.p);
--
More information about the Gcrypt-devel
mailing list