diff options
Diffstat (limited to 'comm/third_party/libgcrypt/cipher/ecc-eddsa.c')
-rw-r--r-- | comm/third_party/libgcrypt/cipher/ecc-eddsa.c | 1182 |
1 files changed, 1182 insertions, 0 deletions
diff --git a/comm/third_party/libgcrypt/cipher/ecc-eddsa.c b/comm/third_party/libgcrypt/cipher/ecc-eddsa.c new file mode 100644 index 0000000000..2a1a89073c --- /dev/null +++ b/comm/third_party/libgcrypt/cipher/ecc-eddsa.c @@ -0,0 +1,1182 @@ +/* ecc-eddsa.c - Elliptic Curve EdDSA signatures + * Copyright (C) 2013, 2014 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 <http://www.gnu.org/licenses/>. + */ + +#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" + + + +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; + } +} + + +/* Helper to scan a hex string. */ +static gcry_mpi_t +scanval (const char *string) +{ + gpg_err_code_t rc; + gcry_mpi_t val; + + rc = _gcry_mpi_scan (&val, GCRYMPI_FMT_HEX, string, 0, NULL); + if (rc) + log_fatal ("scanning ECC parameter failed: %s\n", gpg_strerror (rc)); + return val; +} + + + +/* Encode MPI using the EdDSA scheme. MINLEN specifies the required + length of the buffer in bytes. On success 0 is returned an a + malloced buffer with the encoded point is stored at R_BUFFER; the + length of this buffer is stored at R_BUFLEN. */ +static gpg_err_code_t +eddsa_encodempi (gcry_mpi_t mpi, unsigned int nbits, + unsigned char **r_buffer, unsigned int *r_buflen) +{ + unsigned char *rawmpi; + unsigned int rawmpilen; + unsigned int minlen = (nbits%8) == 0 ? (nbits/8 + 1): (nbits+7)/8; + + rawmpi = _gcry_mpi_get_buffer (mpi, minlen, &rawmpilen, NULL); + if (!rawmpi) + return gpg_err_code_from_syserror (); + + *r_buffer = rawmpi; + *r_buflen = rawmpilen; + return 0; +} + + +/* Encode (X,Y) using the EdDSA scheme. NBITS is the number of bits + of the field of the curve. If WITH_PREFIX is set the returned + buffer is prefixed with a 0x40 byte. On success 0 is returned and + a malloced buffer with the encoded point is stored at R_BUFFER; the + length of this buffer is stored at R_BUFLEN. */ +static gpg_err_code_t +eddsa_encode_x_y (gcry_mpi_t x, gcry_mpi_t y, unsigned int nbits, + int with_prefix, + unsigned char **r_buffer, unsigned int *r_buflen) +{ + unsigned char *rawmpi; + unsigned int rawmpilen; + int off = with_prefix? 1:0; + unsigned int minlen = (nbits%8) == 0 ? (nbits/8 + 1): (nbits+7)/8; + + rawmpi = _gcry_mpi_get_buffer_extra (y, minlen, off?-1:0, &rawmpilen, NULL); + if (!rawmpi) + return gpg_err_code_from_syserror (); + if (mpi_test_bit (x, 0) && rawmpilen) + rawmpi[off + rawmpilen - 1] |= 0x80; /* Set sign bit. */ + if (off) + rawmpi[0] = 0x40; + + *r_buffer = rawmpi; + *r_buflen = rawmpilen + off; + return 0; +} + +/* Encode POINT using the EdDSA scheme. X and Y are either scratch + variables supplied by the caller or NULL. CTX is the usual + context. If WITH_PREFIX is set the returned buffer is prefixed + with a 0x40 byte. On success 0 is returned and a malloced buffer + with the encoded point is stored at R_BUFFER; the length of this + buffer is stored at R_BUFLEN. */ +gpg_err_code_t +_gcry_ecc_eddsa_encodepoint (mpi_point_t point, mpi_ec_t ec, + gcry_mpi_t x_in, gcry_mpi_t y_in, + int with_prefix, + unsigned char **r_buffer, unsigned int *r_buflen) +{ + gpg_err_code_t rc; + gcry_mpi_t x, y; + + x = x_in? x_in : mpi_new (0); + y = y_in? y_in : mpi_new (0); + + if (_gcry_mpi_ec_get_affine (x, y, point, ec)) + { + log_error ("eddsa_encodepoint: Failed to get affine coordinates\n"); + rc = GPG_ERR_INTERNAL; + } + else + rc = eddsa_encode_x_y (x, y, ec->nbits, with_prefix, r_buffer, r_buflen); + + if (!x_in) + mpi_free (x); + if (!y_in) + mpi_free (y); + return rc; +} + + +/* Make sure that the opaque MPI VALUE is in compact EdDSA format. + This function updates MPI if needed. */ +gpg_err_code_t +_gcry_ecc_eddsa_ensure_compact (gcry_mpi_t value, unsigned int nbits) +{ + gpg_err_code_t rc; + const unsigned char *buf; + unsigned int rawmpilen; + gcry_mpi_t x, y; + unsigned char *enc; + unsigned int enclen; + + if (!mpi_is_opaque (value)) + return GPG_ERR_INV_OBJ; + buf = mpi_get_opaque (value, &rawmpilen); + if (!buf) + return GPG_ERR_INV_OBJ; + rawmpilen = (rawmpilen + 7)/8; + + if (rawmpilen > 1 && (rawmpilen%2)) + { + if (buf[0] == 0x04) + { + /* Buffer is in SEC1 uncompressed format. Extract y and + compress. */ + rc = _gcry_mpi_scan (&x, GCRYMPI_FMT_USG, + buf+1, (rawmpilen-1)/2, NULL); + if (rc) + return rc; + rc = _gcry_mpi_scan (&y, GCRYMPI_FMT_USG, + buf+1+(rawmpilen-1)/2, (rawmpilen-1)/2, NULL); + if (rc) + { + mpi_free (x); + return rc; + } + + rc = eddsa_encode_x_y (x, y, nbits, 0, &enc, &enclen); + mpi_free (x); + mpi_free (y); + if (rc) + return rc; + + mpi_set_opaque (value, enc, 8*enclen); + } + else if (buf[0] == 0x40) + { + /* Buffer is compressed but with our SEC1 alike compression + indicator. Remove that byte. FIXME: We should write and + use a function to manipulate an opaque MPI in place. */ + if (!_gcry_mpi_set_opaque_copy (value, buf + 1, (rawmpilen - 1)*8)) + return gpg_err_code_from_syserror (); + } + } + + return 0; +} + + +static gpg_err_code_t +ecc_ed448_recover_x (gcry_mpi_t x, gcry_mpi_t y, int x_0, mpi_ec_t ec) +{ + gpg_err_code_t rc = 0; + gcry_mpi_t u, v, u3, v3, t; + static gcry_mpi_t p34; /* Hard coded (P-3)/4 */ + + if (mpi_cmp (y, ec->p) >= 0) + rc = GPG_ERR_INV_OBJ; + + if (!p34) + p34 = scanval ("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + + u = mpi_new (0); + v = mpi_new (0); + u3 = mpi_new (0); + v3 = mpi_new (0); + t = mpi_new (0); + + /* Compute u and v */ + /* u = y^2 */ + mpi_mulm (u, y, y, ec->p); + /* v = b*y^2 */ + mpi_mulm (v, ec->b, u, ec->p); + /* u = y^2-1 */ + mpi_sub_ui (u, u, 1); + /* v = b*y^2-1 */ + mpi_sub_ui (v, v, 1); + + /* Compute sqrt(u/v) */ + /* u3 = u^3 */ + mpi_powm (u3, u, mpi_const (MPI_C_THREE), ec->p); + mpi_powm (v3, v, mpi_const (MPI_C_THREE), ec->p); + /* t = u^4 * u * v3 = u^5 * v^3 */ + mpi_powm (t, u, mpi_const (MPI_C_FOUR), ec->p); + mpi_mulm (t, t, u, ec->p); + mpi_mulm (t, t, v3, ec->p); + /* t = t^((p-3)/4) = (u^5 * v^3)^((p-3)/4) */ + mpi_powm (t, t, p34, ec->p); + /* x = t * u^3 * v = (u^3 * v) * (u^5 * v^3)^((p-3)/4) */ + mpi_mulm (t, t, u3, ec->p); + mpi_mulm (x, t, v, ec->p); + + /* t = v * x^2 */ + mpi_mulm (t, x, x, ec->p); + mpi_mulm (t, t, v, ec->p); + + if (mpi_cmp (t, u) != 0) + rc = GPG_ERR_INV_OBJ; + else + { + if (!mpi_cmp_ui (x, 0) && x_0) + rc = GPG_ERR_INV_OBJ; + + /* Choose the desired square root according to parity */ + if (mpi_test_bit (x, 0) != !!x_0) + mpi_sub (x, ec->p, x); + } + + mpi_free (t); + mpi_free (u3); + mpi_free (v3); + mpi_free (v); + mpi_free (u); + + return rc; +} + + +/* Recover X from Y and SIGN (which actually is a parity bit). */ +gpg_err_code_t +_gcry_ecc_eddsa_recover_x (gcry_mpi_t x, gcry_mpi_t y, int sign, mpi_ec_t ec) +{ + gpg_err_code_t rc = 0; + gcry_mpi_t u, v, v3, t; + static gcry_mpi_t p58, seven; + + /* + * This routine is actually curve specific. Now, only supports + * Ed25519 and Ed448. + */ + + if (ec->dialect != ECC_DIALECT_ED25519) + /* For now, it's only Ed448. */ + return ecc_ed448_recover_x (x, y, sign, ec); + + /* It's Ed25519. */ + + if (!p58) + p58 = scanval ("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD"); + if (!seven) + seven = mpi_set_ui (NULL, 7); + + u = mpi_new (0); + v = mpi_new (0); + v3 = mpi_new (0); + t = mpi_new (0); + + /* Compute u and v */ + /* u = y^2 */ + mpi_mulm (u, y, y, ec->p); + /* v = b*y^2 */ + mpi_mulm (v, ec->b, u, ec->p); + /* u = y^2-1 */ + mpi_sub_ui (u, u, 1); + /* v = b*y^2+1 */ + mpi_add_ui (v, v, 1); + + /* Compute sqrt(u/v) */ + /* v3 = v^3 */ + mpi_powm (v3, v, mpi_const (MPI_C_THREE), ec->p); + /* t = v3 * v3 * u * v = u * v^7 */ + mpi_powm (t, v, seven, ec->p); + mpi_mulm (t, t, u, ec->p); + /* t = t^((p-5)/8) = (u * v^7)^((p-5)/8) */ + mpi_powm (t, t, p58, ec->p); + /* x = t * u * v^3 = (u * v^3) * (u * v^7)^((p-5)/8) */ + mpi_mulm (t, t, u, ec->p); + mpi_mulm (x, t, v3, ec->p); + + /* Adjust if needed. */ + /* t = v * x^2 */ + mpi_mulm (t, x, x, ec->p); + mpi_mulm (t, t, v, ec->p); + /* -t == u ? x = x * sqrt(-1) */ + mpi_sub (t, ec->p, t); + if (!mpi_cmp (t, u)) + { + static gcry_mpi_t m1; /* Fixme: this is not thread-safe. */ + if (!m1) + m1 = scanval ("2B8324804FC1DF0B2B4D00993DFBD7A7" + "2F431806AD2FE478C4EE1B274A0EA0B0"); + mpi_mulm (x, x, m1, ec->p); + /* t = v * x^2 */ + mpi_mulm (t, x, x, ec->p); + mpi_mulm (t, t, v, ec->p); + /* -t == u ? x = x * sqrt(-1) */ + mpi_sub (t, ec->p, t); + if (!mpi_cmp (t, u)) + rc = GPG_ERR_INV_OBJ; + } + + /* Choose the desired square root according to parity */ + if (mpi_test_bit (x, 0) != !!sign) + mpi_sub (x, ec->p, x); + + mpi_free (t); + mpi_free (v3); + mpi_free (v); + mpi_free (u); + + return rc; +} + + +/* Decode the EdDSA style encoded PK and set it into RESULT. CTX is + the usual curve context. If R_ENCPK is not NULL, the encoded PK is + stored at that address; this is a new copy to be released by the + caller. In contrast to the supplied PK, this is not an MPI and + thus guaranteed to be properly padded. R_ENCPKLEN receives the + length of that encoded key. */ +gpg_err_code_t +_gcry_ecc_eddsa_decodepoint (gcry_mpi_t pk, mpi_ec_t ctx, mpi_point_t result, + unsigned char **r_encpk, unsigned int *r_encpklen) +{ + gpg_err_code_t rc; + unsigned char *rawmpi; + unsigned int rawmpilen; + int sign; + + if (mpi_is_opaque (pk)) + { + const unsigned char *buf; + unsigned int len; + + len = (ctx->nbits%8) == 0 ? (ctx->nbits/8 + 1): (ctx->nbits+7)/8; + + buf = mpi_get_opaque (pk, &rawmpilen); + if (!buf) + return GPG_ERR_INV_OBJ; + rawmpilen = (rawmpilen + 7)/8; + + if (!(rawmpilen == len + || rawmpilen == len + 1 + || rawmpilen == len * 2 + 1)) + return GPG_ERR_INV_OBJ; + + /* Handle compression prefixes. The size of the buffer will be + odd in this case. */ + if (rawmpilen > 1 && (rawmpilen == len + 1 || rawmpilen == len * 2 + 1)) + { + /* First check whether the public key has been given in + standard uncompressed format (SEC1). No need to recover + x in this case. */ + if (buf[0] == 0x04) + { + gcry_mpi_t x, y; + + rc = _gcry_mpi_scan (&x, GCRYMPI_FMT_USG, + buf+1, (rawmpilen-1)/2, NULL); + if (rc) + return rc; + rc = _gcry_mpi_scan (&y, GCRYMPI_FMT_USG, + buf+1+(rawmpilen-1)/2, (rawmpilen-1)/2,NULL); + if (rc) + { + mpi_free (x); + return rc; + } + + if (r_encpk) + { + rc = eddsa_encode_x_y (x, y, ctx->nbits, 0, + r_encpk, r_encpklen); + if (rc) + { + mpi_free (x); + mpi_free (y); + return rc; + } + } + mpi_snatch (result->x, x); + mpi_snatch (result->y, y); + mpi_set_ui (result->z, 1); + return 0; + } + + /* Check whether the public key has been prefixed with a 0x40 + byte to explicitly indicate compressed format using a SEC1 + alike prefix byte. This is a Libgcrypt extension. */ + if (buf[0] == 0x40) + { + rawmpilen--; + buf++; + } + } + + /* EdDSA compressed point. */ + rawmpi = xtrymalloc (rawmpilen); + if (!rawmpi) + return gpg_err_code_from_syserror (); + memcpy (rawmpi, buf, rawmpilen); + reverse_buffer (rawmpi, rawmpilen); + } + else + { + /* Note: Without using an opaque MPI it is not reliable possible + to find out whether the public key has been given in + uncompressed format. Thus we expect native EdDSA format. */ + rawmpi = _gcry_mpi_get_buffer (pk, (ctx->nbits+7)/8, &rawmpilen, NULL); + if (!rawmpi) + return gpg_err_code_from_syserror (); + } + + if (rawmpilen) + { + sign = !!(rawmpi[0] & 0x80); + rawmpi[0] &= 0x7f; + } + else + sign = 0; + _gcry_mpi_set_buffer (result->y, rawmpi, rawmpilen, 0); + if (r_encpk) + { + /* Revert to little endian. */ + if (sign && rawmpilen) + rawmpi[0] |= 0x80; + reverse_buffer (rawmpi, rawmpilen); + *r_encpk = rawmpi; + if (r_encpklen) + *r_encpklen = rawmpilen; + } + else + xfree (rawmpi); + + rc = _gcry_ecc_eddsa_recover_x (result->x, result->y, sign, ctx); + mpi_set_ui (result->z, 1); + + return rc; +} + + +/* Compute the A value as used by EdDSA. The caller needs to provide + the context EC and the actual secret D as an MPI. The function + returns a newly allocated 64 byte buffer at r_digest; the first 32 + bytes represent the A value. NULL is returned on error and NULL + stored at R_DIGEST. */ +gpg_err_code_t +_gcry_ecc_eddsa_compute_h_d (unsigned char **r_digest, mpi_ec_t ec) +{ + gpg_err_code_t rc; + unsigned char *rawmpi = NULL; + unsigned int rawmpilen; + unsigned char *digest; + int hashalgo, b; + + *r_digest = NULL; + + b = (ec->nbits+7)/8; + + /* + * Choice of hashalgo is curve specific. + * For now, it's determine by the bit size of the field. + */ + if (ec->nbits == 255) + hashalgo = GCRY_MD_SHA512; + else if (ec->nbits == 448) + { + b++; + hashalgo = GCRY_MD_SHAKE256; + } + else + return GPG_ERR_NOT_IMPLEMENTED; + + /* Note that we clear DIGEST so we can use it as input to left pad + the key with zeroes for hashing. */ + digest = xtrycalloc_secure (2, b); + if (!digest) + return gpg_err_code_from_syserror (); + + rawmpi = _gcry_mpi_get_buffer (ec->d, 0, &rawmpilen, NULL); + if (!rawmpi) + { + xfree (digest); + return gpg_err_code_from_syserror (); + } + + if (hashalgo == GCRY_MD_SHAKE256) + { + gcry_error_t err; + gcry_md_hd_t hd; + + err = _gcry_md_open (&hd, hashalgo, 0); + if (err) + rc = gcry_err_code (err); + else + { + _gcry_md_write (hd, rawmpi, rawmpilen); + _gcry_md_ctl (hd, GCRYCTL_FINALIZE, NULL, 0); + _gcry_md_extract (hd, GCRY_MD_SHAKE256, digest, 2*b); + _gcry_md_close (hd); + rc = 0; + } + } + else + { + gcry_buffer_t hvec[2]; + + memset (hvec, 0, sizeof hvec); + + hvec[0].data = digest; + hvec[0].len = b > rawmpilen? b - rawmpilen : 0; + hvec[1].data = rawmpi; + hvec[1].len = rawmpilen; + rc = _gcry_md_hash_buffers (hashalgo, 0, digest, hvec, 2); + } + + xfree (rawmpi); + if (rc) + { + xfree (digest); + return rc; + } + + /* Compute the A value. */ + reverse_buffer (digest, b); /* Only the first half of the hash. */ + + /* Field specific handling of clearing/setting bits. */ + if (ec->nbits == 255) + { + digest[0] = (digest[0] & 0x7f) | 0x40; + digest[31] &= 0xf8; + } + else + { + digest[0] = 0; + digest[1] |= 0x80; + digest[56] &= 0xfc; + } + + *r_digest = digest; + return 0; +} + + +/** + * _gcry_ecc_eddsa_genkey - EdDSA version of the key generation. + * + * @ec: Elliptic curve computation context. + * @flags: Flags controlling aspects of the creation. + * + * Return: An error code. + * + * The only @flags bit used by this function is %PUBKEY_FLAG_TRANSIENT + * to use a faster RNG. + */ +gpg_err_code_t +_gcry_ecc_eddsa_genkey (mpi_ec_t ec, int flags) +{ + gpg_err_code_t rc; + int b; + gcry_mpi_t a, x, y; + mpi_point_struct Q; + gcry_random_level_t random_level; + char *dbuf; + size_t dlen; + unsigned char *hash_d = NULL; + + point_init (&Q); + + if ((flags & PUBKEY_FLAG_TRANSIENT_KEY)) + random_level = GCRY_STRONG_RANDOM; + else + random_level = GCRY_VERY_STRONG_RANDOM; + + b = (ec->nbits+7)/8; + + if (ec->nbits == 255) + ; + else if (ec->nbits == 448) + b++; + else + return GPG_ERR_NOT_IMPLEMENTED; + + dlen = b; + + a = mpi_snew (0); + x = mpi_new (0); + y = mpi_new (0); + + /* Generate a secret. */ + dbuf = _gcry_random_bytes_secure (dlen, random_level); + ec->d = _gcry_mpi_set_opaque (NULL, dbuf, dlen*8); + rc = _gcry_ecc_eddsa_compute_h_d (&hash_d, ec); + if (rc) + goto leave; + + _gcry_mpi_set_buffer (a, hash_d, b, 0); + xfree (hash_d); + /* log_printmpi ("ecgen a", a); */ + + /* Compute Q. */ + _gcry_mpi_ec_mul_point (&Q, a, ec->G, ec); + if (DBG_CIPHER) + log_printpnt ("ecgen pk", &Q, ec); + + ec->Q = mpi_point_snatch_set (NULL, Q.x, Q.y, Q.z); + Q.x = NULL; + Q.y = NULL; + Q.x = NULL; + + leave: + _gcry_mpi_release (a); + _gcry_mpi_release (x); + _gcry_mpi_release (y); + return rc; +} + + +/* Compute an EdDSA signature. See: + * [ed25519] 23pp. (PDF) Daniel J. Bernstein, Niels Duif, Tanja + * Lange, Peter Schwabe, Bo-Yin Yang. High-speed high-security + * signatures. Journal of Cryptographic Engineering 2 (2012), 77-89. + * Document ID: a1a62a2f76d23f65d622484ddd09caf8. + * URL: http://cr.yp.to/papers.html#ed25519. Date: 2011.09.26. + * + * Despite that this function requires the specification of a hash + * algorithm, we only support what has been specified by the paper. + * This may change in the future. + * + * Return the signature struct (r,s) from the message hash. The caller + * must have allocated R_R and S. + */ + +/* String to be used with Ed448 */ +#define DOM25519 "SigEd25519 no Ed25519 collisions" +#define DOM25519_LEN 32 +#define DOM448 "SigEd448" +#define DOM448_LEN 8 + +gpg_err_code_t +_gcry_ecc_eddsa_sign (gcry_mpi_t input, mpi_ec_t ec, + gcry_mpi_t r_r, gcry_mpi_t s, + struct pk_encoding_ctx *ctx) +{ + int rc; + unsigned int tmp; + unsigned char *digest = NULL; + const void *mbuf; + size_t mlen; + unsigned char *rawmpi = NULL; + unsigned int rawmpilen; + unsigned char *encpk = NULL; /* Encoded public key. */ + unsigned int encpklen; + mpi_point_struct I; /* Intermediate value. */ + gcry_mpi_t a, x, y, r; + int b; + unsigned char x_olen[2]; + unsigned char prehashed_msg[64]; + + b = (ec->nbits+7)/8; + + if (ec->nbits == 255) + ; + else if (ec->nbits == 448) + b++; + else + return GPG_ERR_NOT_IMPLEMENTED; + + if (!mpi_is_opaque (input)) + return GPG_ERR_INV_DATA; + + /* Initialize some helpers. */ + point_init (&I); + a = mpi_snew (0); + x = mpi_new (0); + y = mpi_new (0); + r = mpi_snew (0); + + rc = _gcry_ecc_eddsa_compute_h_d (&digest, ec); + if (rc) + goto leave; + _gcry_mpi_set_buffer (a, digest, b, 0); + + /* Compute the public key if it's not available (only secret part). */ + if (ec->Q == NULL) + { + mpi_point_struct Q; + + point_init (&Q); + _gcry_mpi_ec_mul_point (&Q, a, ec->G, ec); + ec->Q = mpi_point_snatch_set (NULL, Q.x, Q.y, Q.z); + } + rc = _gcry_ecc_eddsa_encodepoint (ec->Q, ec, x, y, 0, &encpk, &encpklen); + if (rc) + goto leave; + if (DBG_CIPHER) + log_printhex (" e_pk", encpk, encpklen); + + /* Compute R. */ + mbuf = mpi_get_opaque (input, &tmp); + mlen = (tmp +7)/8; + if (DBG_CIPHER) + log_printhex (" m", mbuf, mlen); + + if (ctx->hash_algo == GCRY_MD_SHAKE256) + { + gcry_error_t err; + gcry_md_hd_t hd; + + err = _gcry_md_open (&hd, ctx->hash_algo, 0); + if (err) + rc = gcry_err_code (err); + else + { + _gcry_md_write (hd, DOM448, DOM448_LEN); + x_olen[0] = !!(ctx->flags & PUBKEY_FLAG_PREHASH); + x_olen[1] = ctx->labellen; + _gcry_md_write (hd, x_olen, 2); + if (ctx->labellen) + _gcry_md_write (hd, ctx->label, ctx->labellen); + _gcry_md_write (hd, digest+b, b); + if ((ctx->flags & PUBKEY_FLAG_PREHASH)) + { + gcry_md_hd_t hd2; + + err = _gcry_md_open (&hd2, ctx->hash_algo, 0); + if (err) + { + rc = gcry_err_code (err); + _gcry_md_close (hd); + goto leave; + } + _gcry_md_write (hd2, mbuf, mlen); + _gcry_md_ctl (hd2, GCRYCTL_FINALIZE, NULL, 0); + _gcry_md_extract (hd2, GCRY_MD_SHAKE256, prehashed_msg, 64); + _gcry_md_close (hd2); + _gcry_md_write (hd, prehashed_msg, 64); + } + else + _gcry_md_write (hd, mbuf, mlen); + _gcry_md_ctl (hd, GCRYCTL_FINALIZE, NULL, 0); + _gcry_md_extract (hd, GCRY_MD_SHAKE256, digest, 2*b); + _gcry_md_close (hd); + rc = 0; + } + } + else + { + gcry_buffer_t hvec[6]; + int i = 0; + + memset (hvec, 0, sizeof hvec); + + if ((ctx->flags & PUBKEY_FLAG_PREHASH) || ctx->labellen) + { + hvec[i].data = (void *)DOM25519; + hvec[i].len = DOM25519_LEN; + i++; + x_olen[0] = !!(ctx->flags & PUBKEY_FLAG_PREHASH); + x_olen[1] = ctx->labellen; + hvec[i].data = x_olen; + hvec[i].len = 2; + i++; + if (ctx->labellen) + { + hvec[i].data = ctx->label; + hvec[i].len = ctx->labellen; + i++; + } + } + + hvec[i].data = digest; + hvec[i].off = b; + hvec[i].len = b; + i++; + if ((ctx->flags & PUBKEY_FLAG_PREHASH)) + { + _gcry_md_hash_buffer (ctx->hash_algo, prehashed_msg, mbuf, mlen); + hvec[i].data = (char*)prehashed_msg; + hvec[i].len = 64; + } + else + { + hvec[i].data = (char*)mbuf; + hvec[i].len = mlen; + } + i++; + rc = _gcry_md_hash_buffers (ctx->hash_algo, 0, digest, hvec, i); + } + + if (rc) + goto leave; + reverse_buffer (digest, 2*b); + if (DBG_CIPHER) + log_printhex (" r", digest, 2*b); + _gcry_mpi_set_buffer (r, digest, 2*b, 0); + mpi_mod (r, r, ec->n); + _gcry_mpi_ec_mul_point (&I, r, ec->G, ec); + if (DBG_CIPHER) + log_printpnt (" r", &I, ec); + + /* Convert R into affine coordinates and apply encoding. */ + rc = _gcry_ecc_eddsa_encodepoint (&I, ec, x, y, 0, &rawmpi, &rawmpilen); + if (rc) + goto leave; + if (DBG_CIPHER) + log_printhex (" e_r", rawmpi, rawmpilen); + + if (ctx->hash_algo == GCRY_MD_SHAKE256) + { + gcry_error_t err; + gcry_md_hd_t hd; + + err = _gcry_md_open (&hd, ctx->hash_algo, 0); + if (err) + rc = gcry_err_code (err); + else + { + _gcry_md_write (hd, DOM448, DOM448_LEN); + x_olen[0] = !!(ctx->flags & PUBKEY_FLAG_PREHASH); + x_olen[1] = ctx->labellen; + _gcry_md_write (hd, x_olen, 2); + if (ctx->labellen) + _gcry_md_write (hd, ctx->label, ctx->labellen); + _gcry_md_write (hd, rawmpi, rawmpilen); + _gcry_md_write (hd, encpk, encpklen); + if ((ctx->flags & PUBKEY_FLAG_PREHASH)) + _gcry_md_write (hd, prehashed_msg, 64); + else + _gcry_md_write (hd, mbuf, mlen); + _gcry_md_ctl (hd, GCRYCTL_FINALIZE, NULL, 0); + _gcry_md_extract (hd, GCRY_MD_SHAKE256, digest, 2*b); + _gcry_md_close (hd); + rc = 0; + } + } + else + { + gcry_buffer_t hvec[6]; + int i = 0; + + memset (hvec, 0, sizeof hvec); + + if ((ctx->flags & PUBKEY_FLAG_PREHASH) || ctx->labellen) + { + hvec[i].data = (void *)DOM25519; + hvec[i].len = DOM25519_LEN; + i++; + x_olen[0] = !!(ctx->flags & PUBKEY_FLAG_PREHASH); + x_olen[1] = ctx->labellen; + hvec[i].data = x_olen; + hvec[i].len = 2; + i++; + if (ctx->labellen) + { + hvec[i].data = ctx->label; + hvec[i].len = ctx->labellen; + i++; + } + } + + /* S = r + a * H(dom2(F,C)+encodepoint(R)+encodepoint(pk)+m) mod n */ + hvec[i].data = rawmpi; /* (this is R) */ + hvec[i].len = rawmpilen; + i++; + hvec[i].data = encpk; + hvec[i].len = encpklen; + i++; + if ((ctx->flags & PUBKEY_FLAG_PREHASH)) + { + hvec[i].data = (char*)prehashed_msg; + hvec[i].len = 64; + } + else + { + hvec[i].data = (char*)mbuf; + hvec[i].len = mlen; + } + i++; + rc = _gcry_md_hash_buffers (ctx->hash_algo, 0, digest, hvec, i); + } + + if (rc) + goto leave; + + /* No more need for RAWMPI thus we now transfer it to R_R. */ + mpi_set_opaque (r_r, rawmpi, rawmpilen*8); + rawmpi = NULL; + + reverse_buffer (digest, 2*b); + if (DBG_CIPHER) + log_printhex (" H(R+)", digest, 2*b); + _gcry_mpi_set_buffer (s, digest, 2*b, 0); + mpi_mulm (s, s, a, ec->n); + mpi_addm (s, s, r, ec->n); + rc = eddsa_encodempi (s, ec->nbits, &rawmpi, &rawmpilen); + if (rc) + goto leave; + if (DBG_CIPHER) + log_printhex (" e_s", rawmpi, rawmpilen); + mpi_set_opaque (s, rawmpi, rawmpilen*8); + rawmpi = NULL; + + rc = 0; + + leave: + _gcry_mpi_release (a); + _gcry_mpi_release (x); + _gcry_mpi_release (y); + _gcry_mpi_release (r); + xfree (digest); + point_free (&I); + xfree (encpk); + xfree (rawmpi); + return rc; +} + + +/* Verify an EdDSA signature. See sign_eddsa for the reference. + * Check if R_IN and S_IN verifies INPUT. + */ +gpg_err_code_t +_gcry_ecc_eddsa_verify (gcry_mpi_t input, mpi_ec_t ec, + gcry_mpi_t r_in, gcry_mpi_t s_in, + struct pk_encoding_ctx *ctx) +{ + int rc; + int b; + unsigned int tmp; + unsigned char *encpk = NULL; /* Encoded public key. */ + unsigned int encpklen; + const void *mbuf, *rbuf; + unsigned char *tbuf = NULL; + size_t mlen, rlen; + unsigned int tlen; + unsigned char digest[114]; + gcry_mpi_t h, s; + mpi_point_struct Ia, Ib; + unsigned char x_olen[2]; + unsigned char prehashed_msg[64]; + + if (!mpi_is_opaque (input) || !mpi_is_opaque (r_in) || !mpi_is_opaque (s_in)) + return GPG_ERR_INV_DATA; + + point_init (&Ia); + point_init (&Ib); + h = mpi_new (0); + s = mpi_new (0); + + b = (ec->nbits+7)/8; + + if (ec->nbits == 255) + ; + else if (ec->nbits == 448) + b++; + else + return GPG_ERR_NOT_IMPLEMENTED; + + /* Encode and check the public key. */ + rc = _gcry_ecc_eddsa_encodepoint (ec->Q, ec, NULL, NULL, 0, + &encpk, &encpklen); + if (rc) + goto leave; + if (!_gcry_mpi_ec_curve_point (ec->Q, ec)) + { + rc = GPG_ERR_BROKEN_PUBKEY; + goto leave; + } + if (DBG_CIPHER) + log_printhex (" e_pk", encpk, encpklen); + if (encpklen != b) + { + rc = GPG_ERR_INV_LENGTH; + goto leave; + } + + /* Convert the other input parameters. */ + mbuf = mpi_get_opaque (input, &tmp); + mlen = (tmp +7)/8; + if (DBG_CIPHER) + log_printhex (" m", mbuf, mlen); + rbuf = mpi_get_opaque (r_in, &tmp); + rlen = (tmp +7)/8; + if (DBG_CIPHER) + log_printhex (" r", rbuf, rlen); + if (rlen != b) + { + rc = GPG_ERR_INV_LENGTH; + goto leave; + } + + if (ctx->hash_algo == GCRY_MD_SHAKE256) + { + gcry_error_t err; + gcry_md_hd_t hd; + + err = _gcry_md_open (&hd, ctx->hash_algo, 0); + if (err) + rc = gcry_err_code (err); + else + { + _gcry_md_write (hd, DOM448, DOM448_LEN); + x_olen[0] = !!(ctx->flags & PUBKEY_FLAG_PREHASH); + x_olen[1] = ctx->labellen; + _gcry_md_write (hd, x_olen, 2); + if (ctx->labellen) + _gcry_md_write (hd, ctx->label, ctx->labellen); + _gcry_md_write (hd, rbuf, rlen); + _gcry_md_write (hd, encpk, encpklen); + if ((ctx->flags & PUBKEY_FLAG_PREHASH)) + { + gcry_md_hd_t hd2; + + err = _gcry_md_open (&hd2, ctx->hash_algo, 0); + if (err) + { + rc = gcry_err_code (err); + _gcry_md_close (hd); + goto leave; + } + _gcry_md_write (hd2, mbuf, mlen); + _gcry_md_ctl (hd2, GCRYCTL_FINALIZE, NULL, 0); + _gcry_md_extract (hd2, GCRY_MD_SHAKE256, prehashed_msg, 64); + _gcry_md_close (hd2); + _gcry_md_write (hd, prehashed_msg, 64); + } + else + _gcry_md_write (hd, mbuf, mlen); + _gcry_md_ctl (hd, GCRYCTL_FINALIZE, NULL, 0); + _gcry_md_extract (hd, GCRY_MD_SHAKE256, digest, 2*b); + _gcry_md_close (hd); + rc = 0; + } + } + else + { + gcry_buffer_t hvec[6]; + int i = 0; + + memset (hvec, 0, sizeof hvec); + + /* h = H(dom2(F,C)+encodepoint(R)+encodepoint(pk)+m) */ + if ((ctx->flags & PUBKEY_FLAG_PREHASH) || ctx->labellen) + { + hvec[i].data = (void *)DOM25519; + hvec[i].len = DOM25519_LEN; + i++; + x_olen[0] = !!(ctx->flags & PUBKEY_FLAG_PREHASH); + x_olen[1] = ctx->labellen; + hvec[i].data = x_olen; + hvec[i].len = 2; + i++; + if (ctx->labellen) + { + hvec[i].data = ctx->label; + hvec[i].len = ctx->labellen; + i++; + } + } + + hvec[i].data = (char*)rbuf; + hvec[i].len = rlen; + i++; + hvec[i].data = encpk; + hvec[i].len = encpklen; + i++; + if ((ctx->flags & PUBKEY_FLAG_PREHASH)) + { + _gcry_md_hash_buffer (ctx->hash_algo, prehashed_msg, mbuf, mlen); + hvec[i].data = (char*)prehashed_msg; + hvec[i].len = 64; + } + else + { + hvec[i].data = (char*)mbuf; + hvec[i].len = mlen; + } + i++; + rc = _gcry_md_hash_buffers (ctx->hash_algo, 0, digest, hvec, i); + } + + if (rc) + goto leave; + reverse_buffer (digest, 2*b); + if (DBG_CIPHER) + log_printhex (" H(R+)", digest, 2*b); + _gcry_mpi_set_buffer (h, digest, 2*b, 0); + + /* According to the paper the best way for verification is: + encodepoint(sG - h·Q) = encodepoint(r) + because we don't need to decode R. */ + { + void *sbuf; + unsigned int slen; + + sbuf = _gcry_mpi_get_opaque_copy (s_in, &tmp); + slen = (tmp +7)/8; + reverse_buffer (sbuf, slen); + if (DBG_CIPHER) + log_printhex (" s", sbuf, slen); + _gcry_mpi_set_buffer (s, sbuf, slen, 0); + xfree (sbuf); + if (slen != b) + { + rc = GPG_ERR_INV_LENGTH; + goto leave; + } + } + + _gcry_mpi_ec_mul_point (&Ia, s, ec->G, ec); + _gcry_mpi_ec_mul_point (&Ib, h, ec->Q, ec); + _gcry_mpi_sub (Ib.x, ec->p, Ib.x); + _gcry_mpi_ec_add_points (&Ia, &Ia, &Ib, ec); + rc = _gcry_ecc_eddsa_encodepoint (&Ia, ec, s, h, 0, &tbuf, &tlen); + if (rc) + goto leave; + if (tlen != rlen || memcmp (tbuf, rbuf, tlen)) + { + rc = GPG_ERR_BAD_SIGNATURE; + goto leave; + } + + rc = 0; + + leave: + xfree (encpk); + xfree (tbuf); + _gcry_mpi_release (s); + _gcry_mpi_release (h); + point_free (&Ia); + point_free (&Ib); + return rc; +} |