diff --git a/cipher/ecc-eddsa.c b/cipher/ecc-eddsa.c
index 777694ca..2a1a8907 100644
--- a/cipher/ecc-eddsa.c
+++ b/cipher/ecc-eddsa.c
@@ -1,1039 +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 .
*/
#include
#include
#include
#include
#include
#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].off = 0;
hvec[0].len = b > rawmpilen? b - rawmpilen : 0;
hvec[1].data = rawmpi;
- hvec[1].off = 0;
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 DOM4_0_NONE "SigEd448\0\0"
-#define DOM4_0_NONE_LEN 10
+#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, DOM4_0_NONE, DOM4_0_NONE_LEN);
+ _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);
- _gcry_md_write (hd, mbuf, mlen);
+ 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[3];
+ gcry_buffer_t hvec[6];
+ int i = 0;
memset (hvec, 0, sizeof hvec);
- hvec[0].data = digest;
- hvec[0].off = b;
- hvec[0].len = b;
- hvec[1].data = (char*)mbuf;
- hvec[1].len = mlen;
- rc = _gcry_md_hash_buffers (ctx->hash_algo, 0, digest, hvec, 2);
+ 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, DOM4_0_NONE, DOM4_0_NONE_LEN);
+ _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);
- _gcry_md_write (hd, mbuf, mlen);
+ 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[3];
+ gcry_buffer_t hvec[6];
+ int i = 0;
memset (hvec, 0, sizeof hvec);
- /* S = r + a * H(encodepoint(R) + encodepoint(pk) + m) mod n */
- hvec[0].data = rawmpi; /* (this is R) */
- hvec[0].off = 0;
- hvec[0].len = rawmpilen;
- hvec[1].data = encpk;
- hvec[1].off = 0;
- hvec[1].len = encpklen;
- hvec[2].data = (char*)mbuf;
- hvec[2].off = 0;
- hvec[2].len = mlen;
- rc = _gcry_md_hash_buffers (ctx->hash_algo, 0, digest, hvec, 3);
+ 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, DOM4_0_NONE, DOM4_0_NONE_LEN);
+ _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);
- _gcry_md_write (hd, mbuf, mlen);
+ 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[3];
+ gcry_buffer_t hvec[6];
+ int i = 0;
memset (hvec, 0, sizeof hvec);
- /* h = H(encodepoint(R) + encodepoint(pk) + m) */
- hvec[0].data = (char*)rbuf;
- hvec[0].off = 0;
- hvec[0].len = rlen;
- hvec[1].data = encpk;
- hvec[1].off = 0;
- hvec[1].len = encpklen;
- hvec[2].data = (char*)mbuf;
- hvec[2].off = 0;
- hvec[2].len = mlen;
- rc = _gcry_md_hash_buffers (ctx->hash_algo, 0, digest, hvec, 3);
+ /* 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;
}
diff --git a/tests/t-ed448.c b/tests/t-ed448.c
index 02096c1f..6380143b 100644
--- a/tests/t-ed448.c
+++ b/tests/t-ed448.c
@@ -1,494 +1,543 @@
/* t-ed448.c - Check the Ed448 crypto
* Copyright (C) 2020 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 .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#include
#include "stopwatch.h"
#define PGM "t-ed448"
#include "t-common.h"
-#define N_TESTS 8
+#define N_TESTS 11
static int sign_with_pk;
static int no_verify;
static int custom_data_file;
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);
}
static void
show_sexp (const char *prefix, gcry_sexp_t a)
{
char *buf;
size_t size;
fprintf (stderr, "%s: ", PGM);
if (prefix)
fputs (prefix, stderr);
size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0);
buf = xmalloc (size);
gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size);
fprintf (stderr, "%.*s", (int)size, buf);
gcry_free (buf);
}
/* Prepend FNAME with the srcdir environment variable's value and
* return an allocated filename. */
char *
prepend_srcdir (const char *fname)
{
static const char *srcdir;
char *result;
if (!srcdir && !(srcdir = getenv ("srcdir")))
srcdir = ".";
result = xmalloc (strlen (srcdir) + 1 + strlen (fname) + 1);
strcpy (result, srcdir);
strcat (result, "/");
strcat (result, fname);
return result;
}
/* Read next line but skip over empty and comment lines. Caller must
xfree the result. */
static char *
read_textline (FILE *fp, int *lineno)
{
char line[4096];
char *p;
do
{
if (!fgets (line, sizeof line, fp))
{
if (feof (fp))
return NULL;
die ("error reading input line: %s\n", strerror (errno));
}
++*lineno;
p = strchr (line, '\n');
if (!p)
die ("input line %d not terminated or too long\n", *lineno);
*p = 0;
for (p--;p > line && my_isascii (*p) && isspace (*p); p--)
*p = 0;
}
while (!*line || *line == '#');
/* if (debug) */
/* info ("read line: '%s'\n", line); */
return xstrdup (line);
}
/* Copy the data after the tag to BUFFER. BUFFER will be allocated as
needed. */
static void
copy_data (char **buffer, const char *line, int lineno)
{
const char *s;
xfree (*buffer);
*buffer = NULL;
s = strchr (line, ':');
if (!s)
{
fail ("syntax error at input line %d", lineno);
return;
}
for (s++; my_isascii (*s) && isspace (*s); s++)
;
*buffer = xstrdup (s);
}
/* 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
hexdowncase (char *string)
{
char *p;
for (p=string; *p; p++)
if (my_isascii (*p))
*p = tolower (*p);
}
static void
-one_test (int testno, const char *sk, const char *pk,
- const char *msg, const char *sig)
+one_test (int testno, int ph, const char *sk, const char *pk,
+ const char *msg, const char *ctx, const char *sig)
{
gpg_error_t err;
int i;
char *p;
void *buffer = NULL;
void *buffer2 = NULL;
size_t buflen, buflen2;
gcry_sexp_t s_tmp, s_tmp2;
gcry_sexp_t s_sk = NULL;
gcry_sexp_t s_pk = NULL;
gcry_sexp_t s_msg= NULL;
gcry_sexp_t s_sig= NULL;
unsigned char *sig_r = NULL;
unsigned char *sig_s = NULL;
char *sig_rs_string = NULL;
size_t sig_r_len, sig_s_len;
if (verbose > 1)
- info ("Running test %d\n", testno);
+ info ("Running test %d %d\n", testno, ph);
if (!(buffer = hex2buffer (sk, &buflen)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "sk", "invalid hex string");
goto leave;
}
if (!(buffer2 = hex2buffer (pk, &buflen2)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "pk", "invalid hex string");
goto leave;
}
if (sign_with_pk)
err = gcry_sexp_build (&s_sk, NULL,
"(private-key"
" (ecc"
" (curve \"Ed448\")"
" (q %b)"
" (d %b)))",
(int)buflen2, buffer2,
(int)buflen, buffer);
else
err = gcry_sexp_build (&s_sk, NULL,
"(private-key"
" (ecc"
" (curve \"Ed448\")"
" (d %b)))",
(int)buflen, buffer);
if (err)
{
fail ("error building s-exp for test %d, %s: %s",
testno, "sk", gpg_strerror (err));
goto leave;
}
if ((err = gcry_sexp_build (&s_pk, NULL,
"(public-key"
" (ecc"
" (curve \"Ed448\")"
" (q %b)))", (int)buflen2, buffer2)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "pk", gpg_strerror (err));
goto leave;
}
xfree (buffer);
if (!(buffer = hex2buffer (msg, &buflen)))
{
fail ("error building s-exp for test %d, %s: %s",
testno, "msg", "invalid hex string");
goto leave;
}
- if ((err = gcry_sexp_build (&s_msg, NULL,
- "(data"
- " (flags eddsa)"
- " (hash-algo shake256)"
- " (value %b))", (int)buflen, buffer)))
+ if (ctx)
{
- fail ("error building s-exp for test %d, %s: %s",
- testno, "msg", gpg_strerror (err));
- goto leave;
+ xfree (buffer2);
+ if (!(buffer2 = hex2buffer (ctx, &buflen2)))
+ {
+ fail ("error building s-exp for test %d, %s: %s",
+ testno, "ctx", "invalid hex string");
+ goto leave;
+ }
+
+ if ((err = gcry_sexp_build (&s_msg, NULL,
+ ph ?
+ "(data"
+ " (flags prehash eddsa)"
+ " (hash-algo shake256)"
+ " (label %b)"
+ " (value %b))"
+ :
+ "(data"
+ " (flags eddsa)"
+ " (hash-algo shake256)"
+ " (label %b)"
+ " (value %b))",
+ (int)buflen2, buffer2,
+ (int)buflen, buffer)))
+ {
+ fail ("error building s-exp for test %d, %s: %s",
+ testno, "msg", gpg_strerror (err));
+ goto leave;
+ }
+ }
+ else
+ {
+ if ((err = gcry_sexp_build (&s_msg, NULL,
+ ph ?
+ "(data"
+ " (flags prehash eddsa)"
+ " (hash-algo shake256)"
+ " (value %b))"
+ :
+ "(data"
+ " (flags eddsa)"
+ " (hash-algo shake256)"
+ " (value %b))", (int)buflen, buffer)))
+ {
+ fail ("error building s-exp for test %d, %s: %s",
+ testno, "msg", gpg_strerror (err));
+ goto leave;
+ }
}
if ((err = gcry_pk_sign (&s_sig, s_msg, s_sk)))
fail ("gcry_pk_sign failed for test %d: %s", testno, gpg_strerror (err));
if (debug)
show_sexp ("sig=", s_sig);
s_tmp2 = NULL;
s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0);
if (s_tmp)
{
s_tmp2 = s_tmp;
s_tmp = gcry_sexp_find_token (s_tmp2, "eddsa", 0);
if (s_tmp)
{
gcry_sexp_release (s_tmp2);
s_tmp2 = s_tmp;
s_tmp = gcry_sexp_find_token (s_tmp2, "r", 0);
if (s_tmp)
{
sig_r = gcry_sexp_nth_buffer (s_tmp, 1, &sig_r_len);
gcry_sexp_release (s_tmp);
}
s_tmp = gcry_sexp_find_token (s_tmp2, "s", 0);
if (s_tmp)
{
sig_s = gcry_sexp_nth_buffer (s_tmp, 1, &sig_s_len);
gcry_sexp_release (s_tmp);
}
}
}
gcry_sexp_release (s_tmp2); s_tmp2 = NULL;
if (!sig_r || !sig_s)
fail ("gcry_pk_sign failed for test %d: %s", testno, "r or s missing");
else
{
sig_rs_string = xmalloc (2*(sig_r_len + sig_s_len)+1);
p = sig_rs_string;
*p = 0;
for (i=0; i < sig_r_len; i++, p += 2)
snprintf (p, 3, "%02x", sig_r[i]);
for (i=0; i < sig_s_len; i++, p += 2)
snprintf (p, 3, "%02x", sig_s[i]);
if (strcmp (sig_rs_string, sig))
{
fail ("gcry_pk_sign failed for test %d: %s",
testno, "wrong value returned");
info (" expected: '%s'", sig);
info (" got: '%s'", sig_rs_string);
}
}
if (!no_verify)
if ((err = gcry_pk_verify (s_sig, s_msg, s_pk)))
fail ("gcry_pk_verify failed for test %d: %s",
testno, gpg_strerror (err));
leave:
gcry_sexp_release (s_sig);
gcry_sexp_release (s_sk);
gcry_sexp_release (s_pk);
gcry_sexp_release (s_msg);
xfree (buffer);
xfree (buffer2);
xfree (sig_r);
xfree (sig_s);
xfree (sig_rs_string);
}
static void
check_ed448 (const char *fname)
{
FILE *fp;
int lineno, ntests;
char *line;
int testno;
- char *sk, *pk, *msg, *sig;
+ int ph;
+ char *sk, *pk, *msg, *ctx, *sig;
info ("Checking Ed448.\n");
fp = fopen (fname, "r");
if (!fp)
die ("error opening '%s': %s\n", fname, strerror (errno));
testno = 0;
- sk = pk = msg = sig = NULL;
+ ph = 0;
+ sk = pk = msg = ctx = sig = NULL;
lineno = ntests = 0;
while ((line = read_textline (fp, &lineno)))
{
if (!strncmp (line, "TST:", 4))
testno = atoi (line+4);
+ else if (!strncmp (line, "PH:", 3))
+ ph = atoi (line+3);
else if (!strncmp (line, "SK:", 3))
copy_data (&sk, line, lineno);
else if (!strncmp (line, "PK:", 3))
copy_data (&pk, line, lineno);
else if (!strncmp (line, "MSG:", 4))
copy_data (&msg, line, lineno);
+ else if (!strncmp (line, "CTX:", 4))
+ copy_data (&ctx, line, lineno);
else if (!strncmp (line, "SIG:", 4))
copy_data (&sig, line, lineno);
else
fail ("unknown tag at input line %d", lineno);
xfree (line);
if (testno && sk && pk && msg && sig)
{
hexdowncase (sig);
- one_test (testno, sk, pk, msg, sig);
+ one_test (testno, ph, sk, pk, msg, ctx, sig);
ntests++;
if (!(ntests % 256))
show_note ("%d of %d tests done\n", ntests, N_TESTS);
+ ph = 0;
xfree (pk); pk = NULL;
xfree (sk); sk = NULL;
xfree (msg); msg = NULL;
+ xfree (ctx); ctx = NULL;
xfree (sig); sig = NULL;
}
}
xfree (pk);
xfree (sk);
xfree (msg);
+ xfree (ctx);
xfree (sig);
if (ntests != N_TESTS && !custom_data_file)
fail ("did %d tests but expected %d", ntests, N_TESTS);
else if ((ntests % 256))
show_note ("%d tests done\n", ntests);
fclose (fp);
}
int
main (int argc, char **argv)
{
int last_argc = -1;
char *fname = NULL;
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"
" --sign-with-pk also use the public key for signing\n"
" --no-verify skip the verify test\n"
" --data FNAME take test data from file FNAME\n",
stdout);
exit (0);
}
else if (!strcmp (*argv, "--verbose"))
{
verbose++;
argc--; argv++;
}
else if (!strcmp (*argv, "--debug"))
{
verbose += 2;
debug++;
argc--; argv++;
}
else if (!strcmp (*argv, "--sign-with-pk"))
{
sign_with_pk = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--no-verify"))
{
no_verify = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--data"))
{
argc--; argv++;
if (argc)
{
xfree (fname);
fname = xstrdup (*argv);
argc--; argv++;
}
}
else if (!strncmp (*argv, "--", 2))
die ("unknown option '%s'", *argv);
}
if (!fname)
fname = prepend_srcdir ("t-ed448.inp");
else
custom_data_file = 1;
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));
/* Ed448 isn't supported in fips mode */
if (gcry_fips_mode_active())
return 77;
start_timer ();
check_ed448 (fname);
stop_timer ();
xfree (fname);
info ("All tests completed in %s. Errors: %d\n",
elapsed_time (1), error_count);
return !!error_count;
}
diff --git a/tests/t-ed448.inp b/tests/t-ed448.inp
index 85daa377..a2766f95 100644
--- a/tests/t-ed448.inp
+++ b/tests/t-ed448.inp
@@ -1,53 +1,75 @@
# t-ed448.inp
# This is from RFC 8032, the section
#
# 7.4. Test Vectors for Ed448
#
TST: 1
SK: 6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b
PK: 5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180
MSG:
SIG: 533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980ff0d2028d4b18a9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4dbb61149f05a7363268c71d95808ff2e652600
TST: 2
SK: c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e
PK: 43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480
MSG: 03
SIG: 26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f4352541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd779805e0dbcc0aae1cbcee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0ff3348ab21aa4adafd1d234441cf807c03a00
TST: 3
+SK: c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e
+PK: 43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480
+MSG: 03
+CTX: 666f6f
+SIG: d4f8f6131770dd46f40867d6fd5d5055de43541f8c5e35abbcd001b32a89f7d2151f7647f11d8ca2ae279fb842d607217fce6e042f6815ea000c85741de5c8da1144a6a1aba7f96de42505d7a7298524fda538fccbbb754f578c1cad10d54d0d5428407e85dcbc98a49155c13764e66c3c00
+
+TST: 4
SK: cd23d24f714274e744343237b93290f511f6425f98e64459ff203e8985083ffdf60500553abc0e05cd02184bdb89c4ccd67e187951267eb328
PK: dcea9e78f35a1bf3499a831b10b86c90aac01cd84b67a0109b55a36e9328b1e365fce161d71ce7131a543ea4cb5f7e9f1d8b00696447001400
MSG: 0c3e544074ec63b0265e0c
SIG: 1f0a8888ce25e8d458a21130879b840a9089d999aaba039eaf3e3afa090a09d389dba82c4ff2ae8ac5cdfb7c55e94d5d961a29fe0109941e00b8dbdeea6d3b051068df7254c0cdc129cbe62db2dc957dbb47b51fd3f213fb8698f064774250a5028961c9bf8ffd973fe5d5c206492b140e00
-TST: 4
+TST: 5
SK: 258cdd4ada32ed9c9ff54e63756ae582fb8fab2ac721f2c8e676a72768513d939f63dddb55609133f29adf86ec9929dccb52c1c5fd2ff7e21b
PK: 3ba16da0c6f2cc1f30187740756f5e798d6bc5fc015d7c63cc9510ee3fd44adc24d8e968b6e46e6f94d19b945361726bd75e149ef09817f580
MSG: 64a65f3cdedcdd66811e2915
SIG: 7eeeab7c4e50fb799b418ee5e3197ff6bf15d43a14c34389b59dd1a7b1b85b4ae90438aca634bea45e3a2695f1270f07fdcdf7c62b8efeaf00b45c2c96ba457eb1a8bf075a3db28e5c24f6b923ed4ad747c3c9e03c7079efb87cb110d3a99861e72003cbae6d6b8b827e4e6c143064ff3c00
-TST: 5
+TST: 6
SK: 7ef4e84544236752fbb56b8f31a23a10e42814f5f55ca037cdcc11c64c9a3b2949c1bb60700314611732a6c2fea98eebc0266a11a93970100e
PK: b3da079b0aa493a5772029f0467baebee5a8112d9d3a22532361da294f7bb3815c5dc59e176b4d9f381ca0938e13c6c07b174be65dfa578e80
MSG: 64a65f3cdedcdd66811e2915e7
SIG: 6a12066f55331b6c22acd5d5bfc5d71228fbda80ae8dec26bdd306743c5027cb4890810c162c027468675ecf645a83176c0d7323a2ccde2d80efe5a1268e8aca1d6fbc194d3f77c44986eb4ab4177919ad8bec33eb47bbb5fc6e28196fd1caf56b4e7e0ba5519234d047155ac727a1053100
-TST: 6
+TST: 7
SK: d65df341ad13e008567688baedda8e9dcdc17dc024974ea5b4227b6530e339bff21f99e68ca6968f3cca6dfe0fb9f4fab4fa135d5542ea3f01
PK: df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00
MSG: bd0f6a3747cd561bdddf4640a332461a4a30a12a434cd0bf40d766d9c6d458e5512204a30c17d1f50b5079631f64eb3112182da3005835461113718d1a5ef944
SIG: 554bc2480860b49eab8532d2a533b7d578ef473eeb58c98bb2d0e1ce488a98b18dfde9b9b90775e67f47d4a1c3482058efc9f40d2ca033a0801b63d45b3b722ef552bad3b4ccb667da350192b61c508cf7b6b5adadc2c8d9a446ef003fb05cba5f30e88e36ec2703b349ca229c2670833900
-TST: 7
+TST: 8
SK: 2ec5fe3c17045abdb136a5e6a913e32ab75ae68b53d2fc149b77e504132d37569b7e766ba74a19bd6162343a21c8590aa9cebca9014c636df5
PK: 79756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9bfe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00
MSG: 15777532b0bdd0d1389f636c5f6b9ba734c90af572877e2d272dd078aa1e567cfa80e12928bb542330e8409f3174504107ecd5efac61ae7504dabe2a602ede89e5cca6257a7c77e27a702b3ae39fc769fc54f2395ae6a1178cab4738e543072fc1c177fe71e92e25bf03e4ecb72f47b64d0465aaea4c7fad372536c8ba516a6039c3c2a39f0e4d832be432dfa9a706a6e5c7e19f397964ca4258002f7c0541b590316dbc5622b6b2a6fe7a4abffd96105eca76ea7b98816af0748c10df048ce012d901015a51f189f3888145c03650aa23ce894c3bd889e030d565071c59f409a9981b51878fd6fc110624dcbcde0bf7a69ccce38fabdf86f3bef6044819de11
SIG: c650ddbb0601c19ca11439e1640dd931f43c518ea5bea70d3dcde5f4191fe53f00cf966546b72bcc7d58be2b9badef28743954e3a44a23f880e8d4f1cfce2d7a61452d26da05896f0a50da66a239a8a188b6d825b3305ad77b73fbac0836ecc60987fd08527c1a8e80d5823e65cafe2a3d00
-TST: 8
+TST: 9
SK: 872d093780f5d3730df7c212664b37b8a0f24f56810daa8382cd4fa3f77634ec44dc54f1c2ed9bea86fafb7632d8be199ea165f5ad55dd9ce8
PK: a81b2e8a70a5ac94ffdbcc9badfc3feb0801f258578bb114ad44ece1ec0e799da08effb81c5d685c0c56f64eecaef8cdf11cc38737838cf400
MSG: 6ddf802e1aae4986935f7f981ba3f0351d6273c0a0c22c9c0e8339168e675412a3debfaf435ed651558007db4384b650fcc07e3b586a27a4f7a00ac8a6fec2cd86ae4bf1570c41e6a40c931db27b2faa15a8cedd52cff7362c4e6e23daec0fbc3a79b6806e316efcc7b68119bf46bc76a26067a53f296dafdbdc11c77f7777e972660cf4b6a9b369a6665f02e0cc9b6edfad136b4fabe723d2813db3136cfde9b6d044322fee2947952e031b73ab5c603349b307bdc27bc6cb8b8bbd7bd323219b8033a581b59eadebb09b3c4f3d2277d4f0343624acc817804728b25ab797172b4c5c21a22f9c7839d64300232eb66e53f31c723fa37fe387c7d3e50bdf9813a30e5bb12cf4cd930c40cfb4e1fc622592a49588794494d56d24ea4b40c89fc0596cc9ebb961c8cb10adde976a5d602b1c3f85b9b9a001ed3c6a4d3b1437f52096cd1956d042a597d561a596ecd3d1735a8d570ea0ec27225a2c4aaff26306d1526c1af3ca6d9cf5a2c98f47e1c46db9a33234cfd4d81f2c98538a09ebe76998d0d8fd25997c7d255c6d66ece6fa56f11144950f027795e653008f4bd7ca2dee85d8e90f3dc315130ce2a00375a318c7c3d97be2c8ce5b6db41a6254ff264fa6155baee3b0773c0f497c573f19bb4f4240281f0b1f4f7be857a4e59d416c06b4c50fa09e1810ddc6b1467baeac5a3668d11b6ecaa901440016f389f80acc4db977025e7f5924388c7e340a732e554440e76570f8dd71b7d640b3450d1fd5f0410a18f9a3494f707c717b79b4bf75c98400b096b21653b5d217cf3565c9597456f70703497a078763829bc01bb1cbc8fa04eadc9a6e3f6699587a9e75c94e5bab0036e0b2e711392cff0047d0d6b05bd2a588bc109718954259f1d86678a579a3120f19cfb2963f177aeb70f2d4844826262e51b80271272068ef5b3856fa8535aa2a88b2d41f2a0e2fda7624c2850272ac4a2f561f8f2f7a318bfd5caf9696149e4ac824ad3460538fdc25421beec2cc6818162d06bbed0c40a387192349db67a118bada6cd5ab0140ee273204f628aad1c135f770279a651e24d8c14d75a6059d76b96a6fd857def5e0b354b27ab937a5815d16b5fae407ff18222c6d1ed263be68c95f32d908bd895cd76207ae726487567f9a67dad79abec316f683b17f2d02bf07e0ac8b5bc6162cf94697b3c27cd1fea49b27f23ba2901871962506520c392da8b6ad0d99f7013fbc06c2c17a569500c8a7696481c1cd33e9b14e40b82e79a5f5db82571ba97bae3ad3e0479515bb0e2b0f3bfcd1fd33034efc6245eddd7ee2086ddae2600d8ca73e214e8c2b0bdb2b047c6a464a562ed77b73d2d841c4b34973551257713b753632efba348169abc90a68f42611a40126d7cb21b58695568186f7e569d2ff0f9e745d0487dd2eb997cafc5abf9dd102e62ff66cba87
SIG: e301345a41a39a4d72fff8df69c98075a0cc082b802fc9b2b6bc503f926b65bddf7f4c8f1cb49f6396afc8a70abe6d8aef0db478d4c6b2970076c6a0484fe76d76b3a97625d79f1ce240e7c576750d295528286f719b413de9ada3e8eb78ed573603ce30d8bb761785dc30dbc320869e1a00
+
+TST: 10
+PH: 1
+SK: 833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49
+PK: 259b71c19f83ef77a7abd26524cbdb3161b590a48f7d17de3ee0ba9c52beb743c09428a131d6b1b57303d90d8132c276d5ed3d5d01c0f53880
+MSG: 616263
+SIG: 822f6901f7480f3d5f562c592994d9693602875614483256505600bbc281ae381f54d6bce2ea911574932f52a4e6cadd78769375ec3ffd1b801a0d9b3f4030cd433964b6457ea39476511214f97469b57dd32dbc560a9a94d00bff07620464a3ad203df7dc7ce360c3cd3696d9d9fab90f00
+
+TST: 11
+PH: 1
+SK: 833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49
+PK: 259b71c19f83ef77a7abd26524cbdb3161b590a48f7d17de3ee0ba9c52beb743c09428a131d6b1b57303d90d8132c276d5ed3d5d01c0f53880
+MSG: 616263
+CTX: 666f6f
+SIG: c32299d46ec8ff02b54540982814dce9a05812f81962b649d528095916a2aa481065b1580423ef927ecf0af5888f90da0f6a9a85ad5dc3f280d91224ba9911a3653d00e484e2ce232521481c8658df304bb7745a73514cdb9bf3e15784ab71284f8d0704a608c54a6b62d97beb511d132100