123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- /* ecc-ecdh.c - Elliptic Curve Diffie-Hellman key agreement
- * 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 <https://www.gnu.org/licenses/>.
- * SPDX-License-Identifier: LGPL-2.1+
- */
- #include <config.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include "g10lib.h"
- #include "mpi.h"
- #include "cipher.h"
- #include "context.h"
- #include "ec-context.h"
- #include "ecc-common.h"
- #define ECC_CURVE25519_BYTES 32
- #define ECC_CURVE448_BYTES 56
- static gpg_err_code_t
- prepare_ec (mpi_ec_t *r_ec, const char *name)
- {
- int flags = 0;
- if (!strcmp (name, "Curve25519"))
- flags = PUBKEY_FLAG_DJB_TWEAK;
- return _gcry_mpi_ec_internal_new (r_ec, &flags, "ecc_mul_point", NULL, name);
- }
- unsigned int
- _gcry_ecc_get_algo_keylen (int curveid)
- {
- unsigned int len = 0;
- if (curveid == GCRY_ECC_CURVE25519)
- len = ECC_CURVE25519_BYTES;
- else if (curveid == GCRY_ECC_CURVE448)
- len = ECC_CURVE448_BYTES;
- return len;
- }
- /* For Curve25519 and X448, we need to mask the bits and enable the MSB. */
- static void
- ecc_tweak_bits (unsigned char *seckey, size_t seckey_len)
- {
- if (seckey_len == 32)
- {
- seckey[0] &= 0xf8;
- seckey[31] &= 0x7f;
- seckey[31] |= 0x40;
- }
- else
- {
- seckey[0] &= 0xfc;
- seckey[55] |= 0x80;
- }
- }
- gpg_err_code_t
- _gcry_ecc_curve_keypair (const char *curve,
- unsigned char *pubkey, size_t pubkey_len,
- unsigned char *seckey, size_t seckey_len)
- {
- gpg_err_code_t err;
- unsigned int nbits;
- unsigned int nbytes;
- gcry_mpi_t mpi_k = NULL;
- mpi_ec_t ec = NULL;
- mpi_point_struct Q = { NULL, NULL, NULL };
- gcry_mpi_t x;
- unsigned int len;
- unsigned char *buf;
- err = prepare_ec (&ec, curve);
- if (err)
- return err;
- nbits = ec->nbits;
- nbytes = (nbits + 7)/8;
- if (seckey_len != nbytes)
- return GPG_ERR_INV_ARG;
- if (ec->model == MPI_EC_WEIERSTRASS)
- {
- if (pubkey_len != 1 + 2*nbytes)
- return GPG_ERR_INV_ARG;
- do
- {
- mpi_free (mpi_k);
- mpi_k = mpi_new (nbytes*8);
- _gcry_randomize (seckey, nbytes, GCRY_STRONG_RANDOM);
- _gcry_mpi_set_buffer (mpi_k, seckey, nbytes, 0);
- }
- while (mpi_cmp (mpi_k, ec->n) >= 0);
- }
- else if (ec->model == MPI_EC_MONTGOMERY)
- {
- if (pubkey_len != nbytes)
- return GPG_ERR_INV_ARG;
- _gcry_randomize (seckey, nbytes, GCRY_STRONG_RANDOM);
- /* Existing ECC applications with libgcrypt (like gpg-agent in
- GnuPG) assumes that scalar is tweaked at key generation time.
- For the possible use case where generated key with this routine
- may be used with those, we put compatibile behavior here. */
- ecc_tweak_bits (seckey, nbytes);
- mpi_k = _gcry_mpi_set_opaque_copy (NULL, seckey, nbytes*8);
- }
- else
- return GPG_ERR_UNKNOWN_CURVE;
- x = mpi_new (nbits);
- point_init (&Q);
- _gcry_mpi_ec_mul_point (&Q, mpi_k, ec->G, ec);
- if (ec->model == MPI_EC_WEIERSTRASS)
- {
- gcry_mpi_t y = mpi_new (nbits);
- gcry_mpi_t negative = mpi_new (nbits);
- _gcry_mpi_ec_get_affine (x, y, &Q, ec);
- /* For the backward compatibility, we check if it's a
- "compliant key". */
- mpi_sub (negative, ec->p, y);
- if (mpi_cmp (negative, y) < 0) /* p - y < p */
- {
- mpi_free (y);
- y = negative;
- mpi_sub (mpi_k, ec->n, mpi_k);
- buf = _gcry_mpi_get_buffer (mpi_k, 0, &len, NULL);
- memset (seckey, 0, nbytes - len);
- memcpy (seckey + nbytes - len, buf, len);
- }
- else /* p - y >= p */
- mpi_free (negative);
- buf = _gcry_ecc_ec2os_buf (x, y, ec->p, &len);
- if (!buf)
- {
- err = gpg_err_code_from_syserror ();
- mpi_free (y);
- }
- else
- {
- if (len != 1 + 2*nbytes)
- {
- err = GPG_ERR_INV_ARG;
- mpi_free (y);
- }
- else
- {
- /* (x,y) in SEC1 point encoding. */
- memcpy (pubkey, buf, len);
- xfree (buf);
- mpi_free (y);
- }
- }
- }
- else /* MPI_EC_MONTGOMERY */
- {
- _gcry_mpi_ec_get_affine (x, NULL, &Q, ec);
- buf = _gcry_mpi_get_buffer (x, nbytes, &len, NULL);
- if (!buf)
- err = gpg_err_code_from_syserror ();
- else
- {
- memcpy (pubkey, buf, nbytes);
- xfree (buf);
- }
- }
- mpi_free (x);
- point_free (&Q);
- mpi_free (mpi_k);
- _gcry_mpi_ec_free (ec);
- return err;
- }
- gpg_err_code_t
- _gcry_ecc_curve_mul_point (const char *curve,
- unsigned char *result, size_t result_len,
- const unsigned char *scalar, size_t scalar_len,
- const unsigned char *point, size_t point_len)
- {
- unsigned int nbits;
- unsigned int nbytes;
- gpg_err_code_t err;
- gcry_mpi_t mpi_k = NULL;
- mpi_ec_t ec = NULL;
- mpi_point_struct Q = { NULL, NULL, NULL };
- gcry_mpi_t x = NULL;
- unsigned int len;
- unsigned char *buf;
- err = prepare_ec (&ec, curve);
- if (err)
- return err;
- nbits = ec->nbits;
- nbytes = (nbits + 7)/8;
- if (ec->model == MPI_EC_WEIERSTRASS)
- {
- if (scalar_len != nbytes
- || result_len != 1 + 2*nbytes
- || point_len != 1 + 2*nbytes)
- {
- err = GPG_ERR_INV_ARG;
- goto leave;
- }
- mpi_k = mpi_new (nbytes*8);
- _gcry_mpi_set_buffer (mpi_k, scalar, nbytes, 0);
- }
- else if (ec->model == MPI_EC_MONTGOMERY)
- {
- if (scalar_len != nbytes
- || result_len != nbytes
- || point_len != nbytes)
- {
- err = GPG_ERR_INV_ARG;
- goto leave;
- }
- mpi_k = _gcry_mpi_set_opaque_copy (NULL, scalar, nbytes*8);
- }
- else
- {
- err = GPG_ERR_UNKNOWN_CURVE;
- goto leave;
- }
- point_init (&Q);
- if (point)
- {
- gcry_mpi_t mpi_u = _gcry_mpi_set_opaque_copy (NULL, point, point_len*8);
- mpi_point_struct P;
- point_init (&P);
- if (ec->model == MPI_EC_WEIERSTRASS)
- err = _gcry_ecc_sec_decodepoint (mpi_u, ec, &P);
- else /* MPI_EC_MONTGOMERY */
- err = _gcry_ecc_mont_decodepoint (mpi_u, ec, &P);
- mpi_free (mpi_u);
- if (err)
- goto leave;
- _gcry_mpi_ec_mul_point (&Q, mpi_k, &P, ec);
- point_free (&P);
- }
- else
- _gcry_mpi_ec_mul_point (&Q, mpi_k, ec->G, ec);
- x = mpi_new (nbits);
- if (ec->model == MPI_EC_WEIERSTRASS)
- {
- gcry_mpi_t y = mpi_new (nbits);
- _gcry_mpi_ec_get_affine (x, y, &Q, ec);
- buf = _gcry_ecc_ec2os_buf (x, y, ec->p, &len);
- if (!buf)
- {
- err = gpg_err_code_from_syserror ();
- mpi_free (y);
- }
- else
- {
- if (len != 1 + 2*nbytes)
- {
- err = GPG_ERR_INV_ARG;
- mpi_free (y);
- }
- else
- {
- /* (x,y) in SEC1 point encoding. */
- memcpy (result, buf, len);
- xfree (buf);
- mpi_free (y);
- }
- }
- }
- else /* MPI_EC_MONTGOMERY */
- {
- _gcry_mpi_ec_get_affine (x, NULL, &Q, ec);
- buf = _gcry_mpi_get_buffer (x, nbytes, &len, NULL);
- if (!buf)
- err = gpg_err_code_from_syserror ();
- else
- {
- if (len != nbytes)
- err = GPG_ERR_INV_ARG;
- else
- {
- /* x in little endian. */
- memcpy (result, buf, nbytes);
- xfree (buf);
- }
- }
- }
- mpi_free (x);
- leave:
- point_free (&Q);
- mpi_free (mpi_k);
- _gcry_mpi_ec_free (ec);
- return err;
- }
- gpg_err_code_t
- _gcry_ecc_mul_point (int curveid, unsigned char *result,
- const unsigned char *scalar, const unsigned char *point)
- {
- const char *curve;
- size_t pubkey_len, seckey_len;
- if (curveid == GCRY_ECC_CURVE25519)
- {
- curve = "Curve25519";
- pubkey_len = seckey_len = 32;
- }
- else if (curveid == GCRY_ECC_CURVE448)
- {
- curve = "X448";
- pubkey_len = seckey_len = 56;
- }
- else
- return gpg_error (GPG_ERR_UNKNOWN_CURVE);
- return _gcry_ecc_curve_mul_point (curve, result, pubkey_len,
- scalar, seckey_len,
- point, pubkey_len);
- }
|