Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F35444298
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
194 KB
Subscribers
None
View Options
diff --git a/cipher/dsa.c b/cipher/dsa.c
index e559f9f5..564edf8d 100644
--- a/cipher/dsa.c
+++ b/cipher/dsa.c
@@ -1,1456 +1,1461 @@
/* dsa.c - DSA signature algorithm
* Copyright (C) 1998, 2000, 2001, 2002, 2003,
* 2006, 2008 Free Software Foundation, Inc.
* Copyright (C) 2013 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 "g10lib.h"
#include "mpi.h"
#include "cipher.h"
#include "pubkey-internal.h"
typedef struct
{
gcry_mpi_t p; /* prime */
gcry_mpi_t q; /* group order */
gcry_mpi_t g; /* group generator */
gcry_mpi_t y; /* g^x mod p */
} DSA_public_key;
typedef struct
{
gcry_mpi_t p; /* prime */
gcry_mpi_t q; /* group order */
gcry_mpi_t g; /* group generator */
gcry_mpi_t y; /* g^x mod p */
gcry_mpi_t x; /* secret exponent */
} DSA_secret_key;
/* A structure used to hold domain parameters. */
typedef struct
{
gcry_mpi_t p; /* prime */
gcry_mpi_t q; /* group order */
gcry_mpi_t g; /* group generator */
} dsa_domain_t;
static const char *dsa_names[] =
{
"dsa",
"openpgp-dsa",
NULL,
};
/* A sample 1024 bit DSA key used for the selftests. Not anymore
* used, kept only for reference. */
#if 0
static const char sample_secret_key_1024[] =
"(private-key"
" (dsa"
" (p #00AD7C0025BA1A15F775F3F2D673718391D00456978D347B33D7B49E7F32EDAB"
" 96273899DD8B2BB46CD6ECA263FAF04A28903503D59062A8865D2AE8ADFB5191"
" CF36FFB562D0E2F5809801A1F675DAE59698A9E01EFE8D7DCFCA084F4C6F5A44"
" 44D499A06FFAEA5E8EF5E01F2FD20A7B7EF3F6968AFBA1FB8D91F1559D52D8777B#)"
" (q #00EB7B5751D25EBBB7BD59D920315FD840E19AEBF9#)"
" (g #1574363387FDFD1DDF38F4FBE135BB20C7EE4772FB94C337AF86EA8E49666503"
" AE04B6BE81A2F8DD095311E0217ACA698A11E6C5D33CCDAE71498ED35D13991E"
" B02F09AB40BD8F4C5ED8C75DA779D0AE104BC34C960B002377068AB4B5A1F984"
" 3FBA91F537F1B7CAC4D8DD6D89B0D863AF7025D549F9C765D2FC07EE208F8D15#)"
" (y #64B11EF8871BE4AB572AA810D5D3CA11A6CDBC637A8014602C72960DB135BF46"
" A1816A724C34F87330FC9E187C5D66897A04535CC2AC9164A7150ABFA8179827"
" 6E45831AB811EEE848EBB24D9F5F2883B6E5DDC4C659DEF944DCFD80BF4D0A20"
" 42CAA7DC289F0C5A9D155F02D3D551DB741A81695B74D4C8F477F9C7838EB0FB#)"
" (x #11D54E4ADBD3034160F2CED4B7CD292A4EBF3EC0#)))";
/* A sample 1024 bit DSA key used for the selftests (public only). */
static const char sample_public_key_1024[] =
"(public-key"
" (dsa"
" (p #00AD7C0025BA1A15F775F3F2D673718391D00456978D347B33D7B49E7F32EDAB"
" 96273899DD8B2BB46CD6ECA263FAF04A28903503D59062A8865D2AE8ADFB5191"
" CF36FFB562D0E2F5809801A1F675DAE59698A9E01EFE8D7DCFCA084F4C6F5A44"
" 44D499A06FFAEA5E8EF5E01F2FD20A7B7EF3F6968AFBA1FB8D91F1559D52D8777B#)"
" (q #00EB7B5751D25EBBB7BD59D920315FD840E19AEBF9#)"
" (g #1574363387FDFD1DDF38F4FBE135BB20C7EE4772FB94C337AF86EA8E49666503"
" AE04B6BE81A2F8DD095311E0217ACA698A11E6C5D33CCDAE71498ED35D13991E"
" B02F09AB40BD8F4C5ED8C75DA779D0AE104BC34C960B002377068AB4B5A1F984"
" 3FBA91F537F1B7CAC4D8DD6D89B0D863AF7025D549F9C765D2FC07EE208F8D15#)"
" (y #64B11EF8871BE4AB572AA810D5D3CA11A6CDBC637A8014602C72960DB135BF46"
" A1816A724C34F87330FC9E187C5D66897A04535CC2AC9164A7150ABFA8179827"
" 6E45831AB811EEE848EBB24D9F5F2883B6E5DDC4C659DEF944DCFD80BF4D0A20"
" 42CAA7DC289F0C5A9D155F02D3D551DB741A81695B74D4C8F477F9C7838EB0FB#)))";
#endif /*0*/
/* 2048 DSA key from RFC 6979 A.2.2 */
static const char sample_public_key_2048[] =
"(public-key"
" (dsa"
" (p #9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44FFE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE235567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA153E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B#)"
" (q #F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F#)"
" (g #5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C46A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7#)"
" (y #667098C654426C78D7F8201EAC6C203EF030D43605032C2F1FA937E5237DBD949F34A0A2564FE126DC8B715C5141802CE0979C8246463C40E6B6BDAA2513FA611728716C2E4FD53BC95B89E69949D96512E873B9C8F8DFD499CC312882561ADECB31F658E934C0C197F2C4D96B05CBAD67381E7B768891E4DA3843D24D94CDFB5126E9B8BF21E8358EE0E0A30EF13FD6A664C0DCE3731F7FB49A4845A4FD8254687972A2D382599C9BAC4E0ED7998193078913032558134976410B89D2C171D123AC35FD977219597AA7D15C1A9A428E59194F75C721EBCBCFAE44696A499AFA74E04299F132026601638CB87AB79190D4A0986315DA8EEC6561C938996BEADF#)))";
static const char sample_secret_key_2048[] =
"(private-key"
" (dsa"
" (p #9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44FFE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE235567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA153E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B#)"
" (q #F2C3119374CE76C9356990B465374A17F23F9ED35089BD969F61C6DDE9998C1F#)"
" (g #5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C46A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7#)"
" (y #667098C654426C78D7F8201EAC6C203EF030D43605032C2F1FA937E5237DBD949F34A0A2564FE126DC8B715C5141802CE0979C8246463C40E6B6BDAA2513FA611728716C2E4FD53BC95B89E69949D96512E873B9C8F8DFD499CC312882561ADECB31F658E934C0C197F2C4D96B05CBAD67381E7B768891E4DA3843D24D94CDFB5126E9B8BF21E8358EE0E0A30EF13FD6A664C0DCE3731F7FB49A4845A4FD8254687972A2D382599C9BAC4E0ED7998193078913032558134976410B89D2C171D123AC35FD977219597AA7D15C1A9A428E59194F75C721EBCBCFAE44696A499AFA74E04299F132026601638CB87AB79190D4A0986315DA8EEC6561C938996BEADF#)"
" (x #69C7548C21D0DFEA6B9A51C9EAD4E27C33D3B3F180316E5BCAB92C933F0E4DBC#)))";
static int test_keys (DSA_secret_key *sk, unsigned int qbits);
static int check_secret_key (DSA_secret_key *sk);
static gpg_err_code_t generate (DSA_secret_key *sk,
unsigned int nbits,
unsigned int qbits,
int transient_key,
dsa_domain_t *domain,
gcry_mpi_t **ret_factors);
static gpg_err_code_t sign (gcry_mpi_t r, gcry_mpi_t s,
gcry_mpi_t input, gcry_mpi_t k,
DSA_secret_key *skey, int flags, int hashalgo);
static gpg_err_code_t verify (gcry_mpi_t r, gcry_mpi_t s, gcry_mpi_t input,
DSA_public_key *pkey, int flags, int hashalgo);
static unsigned int dsa_get_nbits (gcry_sexp_t parms);
static void (*progress_cb) (void *,const char *, int, int, int );
static void *progress_cb_data;
/* Check the DSA key length is acceptable for key generation or usage */
static gpg_err_code_t
dsa_check_keysize (unsigned int nbits)
{
if (fips_mode () && nbits < 2048)
- return GPG_ERR_INV_VALUE;
+ {
+ if (fips_check_rejection (GCRY_FIPS_FLAG_REJECT_PK))
+ return GPG_ERR_INV_VALUE;
+ else
+ fips_service_indicator_mark_non_compliant ();
+ }
return 0;
}
void
_gcry_register_pk_dsa_progress (void (*cb) (void *, const char *,
int, int, int),
void *cb_data)
{
progress_cb = cb;
progress_cb_data = cb_data;
}
static void
progress (int c)
{
if (progress_cb)
progress_cb (progress_cb_data, "pk_dsa", c, 0, 0);
}
/* Check that a freshly generated key actually works. Returns 0 on success. */
static int
test_keys (DSA_secret_key *sk, unsigned int qbits)
{
int result = -1; /* Default to failure. */
DSA_public_key pk;
gcry_mpi_t data = mpi_new (qbits);
gcry_mpi_t sig_a = mpi_new (qbits);
gcry_mpi_t sig_b = mpi_new (qbits);
/* Put the relevant parameters into a public key structure. */
pk.p = sk->p;
pk.q = sk->q;
pk.g = sk->g;
pk.y = sk->y;
/* Create a random plaintext. */
_gcry_mpi_randomize (data, qbits, GCRY_WEAK_RANDOM);
/* Sign DATA using the secret key. */
sign (sig_a, sig_b, data, NULL, sk, 0, 0);
/* Verify the signature using the public key. */
if ( verify (sig_a, sig_b, data, &pk, 0, 0) )
goto leave; /* Signature does not match. */
/* Modify the data and check that the signing fails. */
mpi_add_ui (data, data, 1);
if ( !verify (sig_a, sig_b, data, &pk, 0, 0) )
goto leave; /* Signature matches but should not. */
result = 0; /* The test succeeded. */
leave:
_gcry_mpi_release (sig_b);
_gcry_mpi_release (sig_a);
_gcry_mpi_release (data);
return result;
}
/*
Generate a DSA key pair with a key of size NBITS. If transient_key
is true the key is generated using the standard RNG and not the
very secure one.
Returns: 2 structures filled with all needed values
and an array with the n-1 factors of (p-1)
*/
static gpg_err_code_t
generate (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits,
int transient_key, dsa_domain_t *domain, gcry_mpi_t **ret_factors )
{
gpg_err_code_t rc;
gcry_mpi_t p; /* the prime */
gcry_mpi_t q; /* the 160 bit prime factor */
gcry_mpi_t g; /* the generator */
gcry_mpi_t y; /* g^x mod p */
gcry_mpi_t x; /* the secret exponent */
gcry_mpi_t h, e; /* helper */
unsigned char *rndbuf;
gcry_random_level_t random_level;
if (qbits)
; /* Caller supplied qbits. Use this value. */
else if ( nbits >= 512 && nbits <= 1024 )
qbits = 160;
else if ( nbits == 2048 )
qbits = 224;
else if ( nbits == 3072 )
qbits = 256;
else if ( nbits == 7680 )
qbits = 384;
else if ( nbits == 15360 )
qbits = 512;
else
return GPG_ERR_INV_VALUE;
if (qbits < 160 || qbits > 512 || (qbits%8) )
return GPG_ERR_INV_VALUE;
if (nbits < 2*qbits || nbits > 15360)
return GPG_ERR_INV_VALUE;
if (domain->p && domain->q && domain->g)
{
/* Domain parameters are given; use them. */
p = mpi_copy (domain->p);
q = mpi_copy (domain->q);
g = mpi_copy (domain->g);
gcry_assert (mpi_get_nbits (p) == nbits);
gcry_assert (mpi_get_nbits (q) == qbits);
h = mpi_alloc (0);
e = NULL;
}
else
{
/* Generate new domain parameters. */
rc = _gcry_generate_elg_prime (1, nbits, qbits, NULL, &p, ret_factors);
if (rc)
return rc;
/* Get q out of factors. */
q = mpi_copy ((*ret_factors)[0]);
gcry_assert (mpi_get_nbits (q) == qbits);
/* Find a generator g (h and e are helpers).
e = (p-1)/q */
e = mpi_alloc (mpi_get_nlimbs (p));
mpi_sub_ui (e, p, 1);
mpi_fdiv_q (e, e, q);
g = mpi_alloc (mpi_get_nlimbs (p));
h = mpi_alloc_set_ui (1); /* (We start with 2.) */
do
{
mpi_add_ui (h, h, 1);
/* g = h^e mod p */
mpi_powm (g, h, e, p);
}
while (!mpi_cmp_ui (g, 1)); /* Continue until g != 1. */
}
/* Select a random number X with the property:
* 0 < x < q-1
*
* FIXME: Why do we use the requirement x < q-1 ? It should be
* sufficient to test for x < q. FIPS-186-3 check x < q-1 but it
* does not check for 0 < x because it makes sure that Q is unsigned
* and finally adds one to the result so that 0 will never be
* returned. We should replace the code below with _gcry_dsa_gen_k.
*
* This must be a very good random number because this is the secret
* part. The random quality depends on the transient_key flag. */
random_level = transient_key ? GCRY_STRONG_RANDOM : GCRY_VERY_STRONG_RANDOM;
if (DBG_CIPHER)
log_debug("choosing a random x%s\n", transient_key? " (transient-key)":"");
gcry_assert( qbits >= 160 );
x = mpi_alloc_secure( mpi_get_nlimbs(q) );
mpi_sub_ui( h, q, 1 ); /* put q-1 into h */
rndbuf = NULL;
do
{
if( DBG_CIPHER )
progress('.');
if( !rndbuf )
rndbuf = _gcry_random_bytes_secure ((qbits+7)/8, random_level);
else
{ /* Change only some of the higher bits (= 2 bytes)*/
char *r = _gcry_random_bytes_secure (2, random_level);
memcpy(rndbuf, r, 2 );
xfree(r);
}
_gcry_mpi_set_buffer( x, rndbuf, (qbits+7)/8, 0 );
mpi_clear_highbit( x, qbits+1 );
}
while ( !( mpi_cmp_ui( x, 0 )>0 && mpi_cmp( x, h )<0 ) );
xfree(rndbuf);
mpi_free( e );
mpi_free( h );
/* y = g^x mod p */
y = mpi_alloc( mpi_get_nlimbs(p) );
mpi_powm (y, g, x, p);
if( DBG_CIPHER )
{
progress('\n');
log_mpidump("dsa p", p );
log_mpidump("dsa q", q );
log_mpidump("dsa g", g );
log_mpidump("dsa y", y );
log_mpidump("dsa x", x );
}
/* Copy the stuff to the key structures. */
sk->p = p;
sk->q = q;
sk->g = g;
sk->y = y;
sk->x = x;
/* Now we can test our keys (this should never fail!). */
if ( test_keys (sk, qbits) )
{
_gcry_mpi_release (sk->p); sk->p = NULL;
_gcry_mpi_release (sk->q); sk->q = NULL;
_gcry_mpi_release (sk->g); sk->g = NULL;
_gcry_mpi_release (sk->y); sk->y = NULL;
_gcry_mpi_release (sk->x); sk->x = NULL;
fips_signal_error ("self-test after key generation failed");
return GPG_ERR_SELFTEST_FAILED;
}
return 0;
}
/* Generate a DSA key pair with a key of size NBITS using the
algorithm given in FIPS-186-3. If USE_FIPS186_2 is true,
FIPS-186-2 is used and thus the length is restricted to 1024/160.
If DERIVEPARMS is not NULL it may contain a seed value. If domain
parameters are specified in DOMAIN, DERIVEPARMS may not be given
and NBITS and QBITS must match the specified domain parameters. */
static gpg_err_code_t
generate_fips186 (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits,
gcry_sexp_t deriveparms, int use_fips186_2,
dsa_domain_t *domain,
int *r_counter, void **r_seed, size_t *r_seedlen,
gcry_mpi_t *r_h)
{
gpg_err_code_t ec;
struct {
gcry_sexp_t sexp;
const void *seed;
size_t seedlen;
} initial_seed = { NULL, NULL, 0 };
gcry_mpi_t prime_q = NULL;
gcry_mpi_t prime_p = NULL;
gcry_mpi_t value_g = NULL; /* The generator. */
gcry_mpi_t value_y = NULL; /* g^x mod p */
gcry_mpi_t value_x = NULL; /* The secret exponent. */
gcry_mpi_t value_h = NULL; /* Helper. */
gcry_mpi_t value_e = NULL; /* Helper. */
gcry_mpi_t value_c = NULL; /* helper for x */
gcry_mpi_t value_qm2 = NULL; /* q - 2 */
/* Preset return values. */
*r_counter = 0;
*r_seed = NULL;
*r_seedlen = 0;
*r_h = NULL;
/* Derive QBITS from NBITS if requested */
if (!qbits)
{
if (nbits == 1024)
qbits = 160;
else if (nbits == 2048)
qbits = 224;
else if (nbits == 3072)
qbits = 256;
}
/* Check that QBITS and NBITS match the standard. Note that FIPS
186-3 uses N for QBITS and L for NBITS. */
if (nbits == 1024 && qbits == 160 && use_fips186_2)
; /* Allowed in FIPS 186-2 mode. */
else if (nbits == 2048 && qbits == 224)
;
else if (nbits == 2048 && qbits == 256)
;
else if (nbits == 3072 && qbits == 256)
;
else
return GPG_ERR_INV_VALUE;
ec = dsa_check_keysize (nbits);
if (ec)
return ec;
if (domain->p && domain->q && domain->g)
{
/* Domain parameters are given; use them. */
prime_p = mpi_copy (domain->p);
prime_q = mpi_copy (domain->q);
value_g = mpi_copy (domain->g);
gcry_assert (mpi_get_nbits (prime_p) == nbits);
gcry_assert (mpi_get_nbits (prime_q) == qbits);
gcry_assert (!deriveparms);
ec = 0;
}
else
{
/* Generate new domain parameters. */
/* Get an initial seed value. */
if (deriveparms)
{
initial_seed.sexp = sexp_find_token (deriveparms, "seed", 0);
if (initial_seed.sexp)
initial_seed.seed = sexp_nth_data (initial_seed.sexp, 1,
&initial_seed.seedlen);
}
if (use_fips186_2)
ec = _gcry_generate_fips186_2_prime (nbits, qbits,
initial_seed.seed,
initial_seed.seedlen,
&prime_q, &prime_p,
r_counter,
r_seed, r_seedlen);
else
ec = _gcry_generate_fips186_3_prime (nbits, qbits,
initial_seed.seed,
initial_seed.seedlen,
&prime_q, &prime_p,
r_counter,
r_seed, r_seedlen, NULL);
sexp_release (initial_seed.sexp);
if (ec)
goto leave;
/* Find a generator g (h and e are helpers).
* e = (p-1)/q
*/
value_e = mpi_alloc_like (prime_p);
mpi_sub_ui (value_e, prime_p, 1);
mpi_fdiv_q (value_e, value_e, prime_q );
value_g = mpi_alloc_like (prime_p);
value_h = mpi_alloc_set_ui (1);
do
{
mpi_add_ui (value_h, value_h, 1);
/* g = h^e mod p */
mpi_powm (value_g, value_h, value_e, prime_p);
}
while (!mpi_cmp_ui (value_g, 1)); /* Continue until g != 1. */
}
value_c = mpi_snew (qbits);
value_x = mpi_snew (qbits);
value_qm2 = mpi_snew (qbits);
mpi_sub_ui (value_qm2, prime_q, 2);
/* FIPS 186-4 B.1.2 steps 4-6 */
do
{
if( DBG_CIPHER )
progress('.');
_gcry_mpi_randomize (value_c, qbits, GCRY_VERY_STRONG_RANDOM);
mpi_clear_highbit (value_c, qbits+1);
}
while (!(mpi_cmp_ui (value_c, 0) > 0 && mpi_cmp (value_c, value_qm2) < 0));
/* while (mpi_cmp (value_c, value_qm2) > 0); */
/* x = c + 1 */
mpi_add_ui(value_x, value_c, 1);
/* y = g^x mod p */
value_y = mpi_alloc_like (prime_p);
mpi_powm (value_y, value_g, value_x, prime_p);
if (DBG_CIPHER)
{
progress('\n');
log_mpidump("dsa p", prime_p );
log_mpidump("dsa q", prime_q );
log_mpidump("dsa g", value_g );
log_mpidump("dsa y", value_y );
log_mpidump("dsa x", value_x );
log_mpidump("dsa h", value_h );
}
/* Copy the stuff to the key structures. */
sk->p = prime_p; prime_p = NULL;
sk->q = prime_q; prime_q = NULL;
sk->g = value_g; value_g = NULL;
sk->y = value_y; value_y = NULL;
sk->x = value_x; value_x = NULL;
*r_h = value_h; value_h = NULL;
leave:
_gcry_mpi_release (prime_p);
_gcry_mpi_release (prime_q);
_gcry_mpi_release (value_g);
_gcry_mpi_release (value_y);
_gcry_mpi_release (value_x);
_gcry_mpi_release (value_h);
_gcry_mpi_release (value_e);
_gcry_mpi_release (value_c);
_gcry_mpi_release (value_qm2);
/* As a last step test this keys (this should never fail of course). */
if (!ec && test_keys (sk, qbits) )
{
_gcry_mpi_release (sk->p); sk->p = NULL;
_gcry_mpi_release (sk->q); sk->q = NULL;
_gcry_mpi_release (sk->g); sk->g = NULL;
_gcry_mpi_release (sk->y); sk->y = NULL;
_gcry_mpi_release (sk->x); sk->x = NULL;
fips_signal_error ("self-test after key generation failed");
ec = GPG_ERR_SELFTEST_FAILED;
}
if (ec)
{
*r_counter = 0;
xfree (*r_seed); *r_seed = NULL;
*r_seedlen = 0;
_gcry_mpi_release (*r_h); *r_h = NULL;
}
return ec;
}
/*
Test whether the secret key is valid.
Returns: if this is a valid key.
*/
static int
check_secret_key( DSA_secret_key *sk )
{
int rc;
gcry_mpi_t y = mpi_alloc( mpi_get_nlimbs(sk->y) );
mpi_powm( y, sk->g, sk->x, sk->p );
rc = !mpi_cmp( y, sk->y );
mpi_free( y );
return rc;
}
/*
Make a DSA signature from INPUT and put it into r and s.
INPUT may either be a plain MPI or an opaque MPI which is then
internally converted to a plain MPI. FLAGS and HASHALGO may both
be 0 for standard operation mode.
The random value, K_SUPPLIED, may be supplied externally. If not,
it is generated internally.
The return value is 0 on success or an error code. Note that for
backward compatibility the function will not return any error if
FLAGS and HASHALGO are both 0 and INPUT is a plain MPI.
*/
static gpg_err_code_t
sign (gcry_mpi_t r, gcry_mpi_t s, gcry_mpi_t input, gcry_mpi_t k_supplied,
DSA_secret_key *skey, int flags, int hashalgo)
{
gpg_err_code_t rc;
gcry_mpi_t hash;
gcry_mpi_t k;
gcry_mpi_t kinv;
gcry_mpi_t tmp;
const void *abuf;
unsigned int abits, qbits;
int extraloops = 0;
gcry_mpi_t hash_computed_internally = NULL;
qbits = mpi_get_nbits (skey->q);
if ((flags & PUBKEY_FLAG_PREHASH))
{
rc = _gcry_dsa_compute_hash (&hash_computed_internally, input, hashalgo);
if (rc)
return rc;
input = hash_computed_internally;
}
/* Convert the INPUT into an MPI. */
rc = _gcry_dsa_normalize_hash (input, &hash, qbits);
if (rc)
{
mpi_free (hash_computed_internally);
return rc;
}
again:
if (k_supplied)
k = k_supplied;
/* Create the K value. */
else if ((flags & PUBKEY_FLAG_RFC6979) && hashalgo)
{
/* Use Pornin's method for deterministic DSA. If this flag is
set, it is expected that HASH is an opaque MPI with the to be
signed hash. That hash is also used as h1 from 3.2.a. */
if (!mpi_is_opaque (input))
{
rc = GPG_ERR_CONFLICT;
goto leave;
}
abuf = mpi_get_opaque (input, &abits);
rc = _gcry_dsa_gen_rfc6979_k (&k, skey->q, skey->x,
abuf, (abits+7)/8, hashalgo, extraloops);
if (rc)
goto leave;
}
else
{
/* Select a random k with 0 < k < q */
k = _gcry_dsa_gen_k (skey->q, GCRY_STRONG_RANDOM);
}
/* kinv = k^(-1) mod q */
kinv = mpi_alloc( mpi_get_nlimbs(k) );
mpi_invm(kinv, k, skey->q );
_gcry_dsa_modify_k (k, skey->q, qbits);
/* r = (a^k mod p) mod q */
mpi_powm( r, skey->g, k, skey->p );
mpi_fdiv_r( r, r, skey->q );
/* s = (kinv * ( hash + x * r)) mod q */
tmp = mpi_alloc( mpi_get_nlimbs(skey->p) );
mpi_mul( tmp, skey->x, r );
mpi_add( tmp, tmp, hash );
mpi_mulm( s , kinv, tmp, skey->q );
if (!k_supplied)
mpi_free(k);
mpi_free(kinv);
mpi_free(tmp);
if (!mpi_cmp_ui (r, 0))
{
if (k_supplied)
{
rc = GPG_ERR_INV_VALUE;
goto leave;
}
/* This is a highly unlikely code path. */
extraloops++;
goto again;
}
rc = 0;
leave:
if (hash != input)
mpi_free (hash);
mpi_free (hash_computed_internally);
return rc;
}
/*
Returns true if the signature composed from R and S is valid.
*/
static gpg_err_code_t
verify (gcry_mpi_t r, gcry_mpi_t s, gcry_mpi_t input, DSA_public_key *pkey,
int flags, int hashalgo)
{
gpg_err_code_t rc = 0;
gcry_mpi_t w, u1, u2, v;
gcry_mpi_t base[3];
gcry_mpi_t ex[3];
gcry_mpi_t hash;
unsigned int nbits;
gcry_mpi_t hash_computed_internally = NULL;
if( !(mpi_cmp_ui( r, 0 ) > 0 && mpi_cmp( r, pkey->q ) < 0) )
return GPG_ERR_BAD_SIGNATURE; /* Assertion 0 < r < n failed. */
if( !(mpi_cmp_ui( s, 0 ) > 0 && mpi_cmp( s, pkey->q ) < 0) )
return GPG_ERR_BAD_SIGNATURE; /* Assertion 0 < s < n failed. */
nbits = mpi_get_nbits (pkey->q);
if ((flags & PUBKEY_FLAG_PREHASH))
{
rc = _gcry_dsa_compute_hash (&hash_computed_internally, input, hashalgo);
if (rc)
return rc;
input = hash_computed_internally;
}
rc = _gcry_dsa_normalize_hash (input, &hash, nbits);
if (rc)
{
mpi_free (hash_computed_internally);
return rc;
}
w = mpi_alloc( mpi_get_nlimbs(pkey->q) );
u1 = mpi_alloc( mpi_get_nlimbs(pkey->q) );
u2 = mpi_alloc( mpi_get_nlimbs(pkey->q) );
v = mpi_alloc( mpi_get_nlimbs(pkey->p) );
/* w = s^(-1) mod q */
mpi_invm( w, s, pkey->q );
/* u1 = (hash * w) mod q */
mpi_mulm( u1, hash, w, pkey->q );
/* u2 = r * w mod q */
mpi_mulm( u2, r, w, pkey->q );
/* v = g^u1 * y^u2 mod p mod q */
base[0] = pkey->g; ex[0] = u1;
base[1] = pkey->y; ex[1] = u2;
base[2] = NULL; ex[2] = NULL;
mpi_mulpowm( v, base, ex, pkey->p );
mpi_fdiv_r( v, v, pkey->q );
if (mpi_cmp( v, r ))
{
if (DBG_CIPHER)
{
log_mpidump (" i", input);
log_mpidump (" h", hash);
log_mpidump (" v", v);
log_mpidump (" r", r);
log_mpidump (" s", s);
}
rc = GPG_ERR_BAD_SIGNATURE;
}
mpi_free(w);
mpi_free(u1);
mpi_free(u2);
mpi_free(v);
if (hash != input)
mpi_free (hash);
mpi_free (hash_computed_internally);
return rc;
}
/*********************************************
************** interface ******************
*********************************************/
static gcry_err_code_t
dsa_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
{
gpg_err_code_t rc;
unsigned int nbits;
gcry_sexp_t domainsexp;
DSA_secret_key sk;
gcry_sexp_t l1;
unsigned int qbits = 0;
gcry_sexp_t deriveparms = NULL;
gcry_sexp_t seedinfo = NULL;
gcry_sexp_t misc_info = NULL;
int flags = 0;
dsa_domain_t domain;
gcry_mpi_t *factors = NULL;
memset (&sk, 0, sizeof sk);
memset (&domain, 0, sizeof domain);
rc = _gcry_pk_util_get_nbits (genparms, &nbits);
if (rc)
return rc;
/* Parse the optional flags list. */
l1 = sexp_find_token (genparms, "flags", 0);
if (l1)
{
rc = _gcry_pk_util_parse_flaglist (l1, &flags, NULL);
sexp_release (l1);
if (rc)
return rc;\
}
/* Parse the optional qbits element. */
l1 = sexp_find_token (genparms, "qbits", 0);
if (l1)
{
char buf[50];
const char *s;
size_t n;
s = sexp_nth_data (l1, 1, &n);
if (!s || n >= DIM (buf) - 1 )
{
sexp_release (l1);
return GPG_ERR_INV_OBJ; /* No value or value too large. */
}
memcpy (buf, s, n);
buf[n] = 0;
qbits = (unsigned int)strtoul (buf, NULL, 0);
sexp_release (l1);
}
/* Parse the optional transient-key flag. */
if (!(flags & PUBKEY_FLAG_TRANSIENT_KEY))
{
l1 = sexp_find_token (genparms, "transient-key", 0);
if (l1)
{
flags |= PUBKEY_FLAG_TRANSIENT_KEY;
sexp_release (l1);
}
}
/* Get the optional derive parameters. */
deriveparms = sexp_find_token (genparms, "derive-parms", 0);
/* Parse the optional "use-fips186" flags. */
if (!(flags & PUBKEY_FLAG_USE_FIPS186))
{
l1 = sexp_find_token (genparms, "use-fips186", 0);
if (l1)
{
flags |= PUBKEY_FLAG_USE_FIPS186;
sexp_release (l1);
}
}
if (!(flags & PUBKEY_FLAG_USE_FIPS186_2))
{
l1 = sexp_find_token (genparms, "use-fips186-2", 0);
if (l1)
{
flags |= PUBKEY_FLAG_USE_FIPS186_2;
sexp_release (l1);
}
}
/* Check whether domain parameters are given. */
domainsexp = sexp_find_token (genparms, "domain", 0);
if (domainsexp)
{
/* DERIVEPARMS can't be used together with domain parameters.
NBITS abnd QBITS may not be specified because there values
are derived from the domain parameters. */
if (deriveparms || qbits || nbits)
{
sexp_release (domainsexp);
sexp_release (deriveparms);
return GPG_ERR_INV_VALUE;
}
/* Put all domain parameters into the domain object. */
l1 = sexp_find_token (domainsexp, "p", 0);
domain.p = sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
sexp_release (l1);
l1 = sexp_find_token (domainsexp, "q", 0);
domain.q = sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
sexp_release (l1);
l1 = sexp_find_token (domainsexp, "g", 0);
domain.g = sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
sexp_release (l1);
sexp_release (domainsexp);
/* Check that all domain parameters are available. */
if (!domain.p || !domain.q || !domain.g)
{
_gcry_mpi_release (domain.p);
_gcry_mpi_release (domain.q);
_gcry_mpi_release (domain.g);
sexp_release (deriveparms);
return GPG_ERR_MISSING_VALUE;
}
/* Get NBITS and QBITS from the domain parameters. */
nbits = mpi_get_nbits (domain.p);
qbits = mpi_get_nbits (domain.q);
}
if (deriveparms
|| (flags & PUBKEY_FLAG_USE_FIPS186)
|| (flags & PUBKEY_FLAG_USE_FIPS186_2)
|| fips_mode ())
{
int counter;
void *seed;
size_t seedlen;
gcry_mpi_t h_value;
rc = generate_fips186 (&sk, nbits, qbits, deriveparms,
!!(flags & PUBKEY_FLAG_USE_FIPS186_2),
&domain,
&counter, &seed, &seedlen, &h_value);
if (!rc && h_value)
{
/* Format the seed-values unless domain parameters are used
for which a H_VALUE of NULL is an indication. */
rc = sexp_build (&seedinfo, NULL,
"(seed-values(counter %d)(seed %b)(h %m))",
counter, (int)seedlen, seed, h_value);
xfree (seed);
_gcry_mpi_release (h_value);
}
}
else
{
rc = generate (&sk, nbits, qbits,
!!(flags & PUBKEY_FLAG_TRANSIENT_KEY),
&domain, &factors);
}
if (!rc)
{
/* Put the factors into MISC_INFO. Note that the factors are
not confidential thus we can store them in standard memory. */
int nfactors, i, j;
char *p;
char *format = NULL;
void **arg_list = NULL;
for (nfactors=0; factors && factors[nfactors]; nfactors++)
;
/* Allocate space for the format string:
"(misc-key-info%S(pm1-factors%m))"
with one "%m" for each factor and construct it. */
format = xtrymalloc (50 + 2*nfactors);
if (!format)
rc = gpg_err_code_from_syserror ();
else
{
p = stpcpy (format, "(misc-key-info");
if (seedinfo)
p = stpcpy (p, "%S");
if (nfactors)
{
p = stpcpy (p, "(pm1-factors");
for (i=0; i < nfactors; i++)
p = stpcpy (p, "%m");
p = stpcpy (p, ")");
}
p = stpcpy (p, ")");
/* Allocate space for the list of factors plus one for the
seedinfo s-exp plus an extra NULL entry for safety and
fill it with the factors. */
arg_list = xtrycalloc (nfactors+1+1, sizeof *arg_list);
if (!arg_list)
rc = gpg_err_code_from_syserror ();
else
{
i = 0;
if (seedinfo)
arg_list[i++] = &seedinfo;
for (j=0; j < nfactors; j++)
arg_list[i++] = factors + j;
arg_list[i] = NULL;
rc = sexp_build_array (&misc_info, NULL, format, arg_list);
}
}
xfree (arg_list);
xfree (format);
}
if (!rc)
rc = sexp_build (r_skey, NULL,
"(key-data"
" (public-key"
" (dsa(p%m)(q%m)(g%m)(y%m)))"
" (private-key"
" (dsa(p%m)(q%m)(g%m)(y%m)(x%m)))"
" %S)",
sk.p, sk.q, sk.g, sk.y,
sk.p, sk.q, sk.g, sk.y, sk.x,
misc_info);
_gcry_mpi_release (sk.p);
_gcry_mpi_release (sk.q);
_gcry_mpi_release (sk.g);
_gcry_mpi_release (sk.y);
_gcry_mpi_release (sk.x);
_gcry_mpi_release (domain.p);
_gcry_mpi_release (domain.q);
_gcry_mpi_release (domain.g);
sexp_release (seedinfo);
sexp_release (misc_info);
sexp_release (deriveparms);
if (factors)
{
gcry_mpi_t *mp;
for (mp = factors; *mp; mp++)
mpi_free (*mp);
xfree (factors);
}
return rc;
}
static gcry_err_code_t
dsa_check_secret_key (gcry_sexp_t keyparms)
{
gcry_err_code_t rc;
DSA_secret_key sk = {NULL, NULL, NULL, NULL, NULL};
rc = _gcry_sexp_extract_param (keyparms, NULL, "pqgyx",
&sk.p, &sk.q, &sk.g, &sk.y, &sk.x,
NULL);
if (rc)
goto leave;
if (!check_secret_key (&sk))
rc = GPG_ERR_BAD_SECKEY;
leave:
_gcry_mpi_release (sk.p);
_gcry_mpi_release (sk.q);
_gcry_mpi_release (sk.g);
_gcry_mpi_release (sk.y);
_gcry_mpi_release (sk.x);
if (DBG_CIPHER)
log_debug ("dsa_testkey => %s\n", gpg_strerror (rc));
return rc;
}
static gcry_err_code_t
dsa_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
{
gcry_err_code_t rc;
struct pk_encoding_ctx ctx;
gcry_mpi_t data = NULL;
gcry_mpi_t k = NULL;
DSA_secret_key sk = {NULL, NULL, NULL, NULL, NULL};
gcry_mpi_t sig_r = NULL;
gcry_mpi_t sig_s = NULL;
unsigned int nbits = dsa_get_nbits (keyparms);
rc = dsa_check_keysize (nbits);
if (rc)
return rc;
_gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_SIGN, nbits);
/* Extract the data. */
rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
if (rc)
goto leave;
if (DBG_CIPHER)
log_mpidump ("dsa_sign data", data);
if (ctx.label)
rc = _gcry_mpi_scan (&k, GCRYMPI_FMT_USG, ctx.label, ctx.labellen, NULL);
if (rc)
goto leave;
/* Extract the key. */
rc = _gcry_sexp_extract_param (keyparms, NULL, "pqgyx",
&sk.p, &sk.q, &sk.g, &sk.y, &sk.x, NULL);
if (rc)
goto leave;
if (DBG_CIPHER)
{
log_mpidump ("dsa_sign p", sk.p);
log_mpidump ("dsa_sign q", sk.q);
log_mpidump ("dsa_sign g", sk.g);
log_mpidump ("dsa_sign y", sk.y);
if (!fips_mode ())
log_mpidump ("dsa_sign x", sk.x);
}
sig_r = mpi_new (0);
sig_s = mpi_new (0);
rc = sign (sig_r, sig_s, data, k, &sk, ctx.flags, ctx.hash_algo);
if (rc)
goto leave;
if (DBG_CIPHER)
{
log_mpidump ("dsa_sign sig_r", sig_r);
log_mpidump ("dsa_sign sig_s", sig_s);
}
rc = sexp_build (r_sig, NULL, "(sig-val(dsa(r%M)(s%M)))", sig_r, sig_s);
leave:
_gcry_mpi_release (sig_r);
_gcry_mpi_release (sig_s);
_gcry_mpi_release (sk.p);
_gcry_mpi_release (sk.q);
_gcry_mpi_release (sk.g);
_gcry_mpi_release (sk.y);
_gcry_mpi_release (sk.x);
_gcry_mpi_release (data);
_gcry_mpi_release (k);
_gcry_pk_util_free_encoding_ctx (&ctx);
if (DBG_CIPHER)
log_debug ("dsa_sign => %s\n", gpg_strerror (rc));
return rc;
}
static gcry_err_code_t
dsa_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
{
gcry_err_code_t rc;
struct pk_encoding_ctx ctx;
gcry_sexp_t l1 = NULL;
gcry_mpi_t sig_r = NULL;
gcry_mpi_t sig_s = NULL;
gcry_mpi_t data = NULL;
DSA_public_key pk = { NULL, NULL, NULL, NULL };
unsigned int nbits = dsa_get_nbits (s_keyparms);
rc = dsa_check_keysize (nbits);
if (rc)
return rc;
_gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_VERIFY, nbits);
/* Extract the data. */
rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
if (rc)
goto leave;
if (DBG_CIPHER)
log_mpidump ("dsa_verify data", data);
/* Extract the signature value. */
rc = _gcry_pk_util_preparse_sigval (s_sig, dsa_names, &l1, NULL);
if (rc)
goto leave;
rc = _gcry_sexp_extract_param (l1, NULL, "rs", &sig_r, &sig_s, NULL);
if (rc)
goto leave;
if (DBG_CIPHER)
{
log_mpidump ("dsa_verify s_r", sig_r);
log_mpidump ("dsa_verify s_s", sig_s);
}
/* Extract the key. */
rc = _gcry_sexp_extract_param (s_keyparms, NULL, "pqgy",
&pk.p, &pk.q, &pk.g, &pk.y, NULL);
if (rc)
goto leave;
if (DBG_CIPHER)
{
log_mpidump ("dsa_verify p", pk.p);
log_mpidump ("dsa_verify q", pk.q);
log_mpidump ("dsa_verify g", pk.g);
log_mpidump ("dsa_verify y", pk.y);
}
/* Verify the signature. */
rc = verify (sig_r, sig_s, data, &pk, ctx.flags, ctx.hash_algo);
leave:
_gcry_mpi_release (pk.p);
_gcry_mpi_release (pk.q);
_gcry_mpi_release (pk.g);
_gcry_mpi_release (pk.y);
_gcry_mpi_release (data);
_gcry_mpi_release (sig_r);
_gcry_mpi_release (sig_s);
sexp_release (l1);
_gcry_pk_util_free_encoding_ctx (&ctx);
if (DBG_CIPHER)
log_debug ("dsa_verify => %s\n", rc?gpg_strerror (rc):"Good");
return rc;
}
/* Return the number of bits for the key described by PARMS. On error
* 0 is returned. The format of PARMS starts with the algorithm name;
* for example:
*
* (dsa
* (p <mpi>)
* (q <mpi>)
* (g <mpi>)
* (y <mpi>))
*
* More parameters may be given but we only need P here.
*/
static unsigned int
dsa_get_nbits (gcry_sexp_t parms)
{
gcry_sexp_t l1;
gcry_mpi_t p;
unsigned int nbits;
l1 = sexp_find_token (parms, "p", 1);
if (!l1)
return 0; /* Parameter P not found. */
p = sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
sexp_release (l1);
nbits = p? mpi_get_nbits (p) : 0;
_gcry_mpi_release (p);
return nbits;
}
/*
Self-test section.
*/
static const char *
selftest_sign (gcry_sexp_t pkey, gcry_sexp_t skey)
{
/* Sample data from RFC 6979 section A.2.2, hash is of message "sample" */
static const char sample_data[] =
"(data (flags rfc6979 prehash)"
" (hash-algo sha256)"
" (value 6:sample))";
static const char sample_data_bad[] =
"(data (flags rfc6979)"
" (hash sha256 #bf2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf#))";
static const char signature_r[] =
"eace8bdbbe353c432a795d9ec556c6d021f7a03f42c36e9bc87e4ac7932cc809";
static const char signature_s[] =
"7081e175455f9247b812b74583e9e94f9ea79bd640dc962533b0680793a38d53";
const char *errtxt = NULL;
gcry_error_t err;
gcry_sexp_t data = NULL;
gcry_sexp_t data_bad = NULL;
gcry_sexp_t sig = NULL;
gcry_sexp_t l1 = NULL;
gcry_sexp_t l2 = NULL;
gcry_mpi_t r = NULL;
gcry_mpi_t s = NULL;
gcry_mpi_t calculated_r = NULL;
gcry_mpi_t calculated_s = NULL;
int cmp;
err = sexp_sscan (&data, NULL, sample_data, strlen (sample_data));
if (!err)
err = sexp_sscan (&data_bad, NULL,
sample_data_bad, strlen (sample_data_bad));
if (!err)
err = _gcry_mpi_scan (&r, GCRYMPI_FMT_HEX, signature_r, 0, NULL);
if (!err)
err = _gcry_mpi_scan (&s, GCRYMPI_FMT_HEX, signature_s, 0, NULL);
if (err)
{
errtxt = "converting data failed";
goto leave;
}
err = _gcry_pk_sign (&sig, data, skey);
if (err)
{
errtxt = "signing failed";
goto leave;
}
/* check against known signature */
errtxt = "signature validity failed";
l1 = _gcry_sexp_find_token (sig, "sig-val", 0);
if (!l1)
goto leave;
l2 = _gcry_sexp_find_token (l1, "dsa", 0);
if (!l2)
goto leave;
sexp_release (l1);
l1 = l2;
l2 = _gcry_sexp_find_token (l1, "r", 0);
if (!l2)
goto leave;
calculated_r = _gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
if (!calculated_r)
goto leave;
sexp_release (l2);
l2 = _gcry_sexp_find_token (l1, "s", 0);
if (!l2)
goto leave;
calculated_s = _gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
if (!calculated_s)
goto leave;
errtxt = "known sig check failed";
cmp = _gcry_mpi_cmp (r, calculated_r);
if (cmp)
goto leave;
cmp = _gcry_mpi_cmp (s, calculated_s);
if (cmp)
goto leave;
errtxt = NULL;
err = _gcry_pk_verify (sig, data, pkey);
if (err)
{
errtxt = "verify failed";
goto leave;
}
err = _gcry_pk_verify (sig, data_bad, pkey);
if (gcry_err_code (err) != GPG_ERR_BAD_SIGNATURE)
{
errtxt = "bad signature not detected";
goto leave;
}
leave:
_gcry_mpi_release (calculated_s);
_gcry_mpi_release (calculated_r);
_gcry_mpi_release (s);
_gcry_mpi_release (r);
sexp_release (l2);
sexp_release (l1);
sexp_release (sig);
sexp_release (data_bad);
sexp_release (data);
return errtxt;
}
static gpg_err_code_t
selftests_dsa_2048 (selftest_report_func_t report)
{
const char *what;
const char *errtxt;
gcry_error_t err;
gcry_sexp_t skey = NULL;
gcry_sexp_t pkey = NULL;
/* Convert the S-expressions into the internal representation. */
what = "convert";
err = sexp_sscan (&skey, NULL, sample_secret_key_2048, strlen (sample_secret_key_2048));
if (!err)
err = sexp_sscan (&pkey, NULL,
sample_public_key_2048, strlen (sample_public_key_2048));
if (err)
{
errtxt = _gcry_strerror (err);
goto failed;
}
what = "key consistency";
err = _gcry_pk_testkey (skey);
if (err)
{
errtxt = _gcry_strerror (err);
goto failed;
}
what = "sign";
errtxt = selftest_sign (pkey, skey);
if (errtxt)
goto failed;
sexp_release (pkey);
sexp_release (skey);
return 0; /* Succeeded. */
failed:
sexp_release (pkey);
sexp_release (skey);
if (report)
report ("pubkey", GCRY_PK_DSA, what, errtxt);
return GPG_ERR_SELFTEST_FAILED;
}
/* Run a full self-test for ALGO and return 0 on success. */
static gpg_err_code_t
run_selftests (int algo, int extended, selftest_report_func_t report)
{
gpg_err_code_t ec;
(void)extended;
switch (algo)
{
case GCRY_PK_DSA:
ec = selftests_dsa_2048 (report);
break;
default:
ec = GPG_ERR_PUBKEY_ALGO;
break;
}
return ec;
}
gcry_pk_spec_t _gcry_pubkey_spec_dsa =
{
GCRY_PK_DSA, { 0, 0 },
GCRY_PK_USAGE_SIGN,
"DSA", dsa_names,
"pqgy", "pqgyx", "", "rs", "pqgy",
dsa_generate,
dsa_check_secret_key,
NULL,
NULL,
dsa_sign,
dsa_verify,
dsa_get_nbits,
run_selftests
};
diff --git a/cipher/ecc-ecdsa.c b/cipher/ecc-ecdsa.c
index 871b0371..cb9a001c 100644
--- a/cipher/ecc-ecdsa.c
+++ b/cipher/ecc-ecdsa.c
@@ -1,305 +1,308 @@
/* ecc-ecdsa.c - Elliptic Curve ECDSA signatures
* Copyright (C) 2007, 2008, 2010, 2011 Free Software Foundation, Inc.
* Copyright (C) 2013 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 "pubkey-internal.h"
#include "ecc-common.h"
/* Compute an ECDSA signature.
* Return the signature struct (r,s) from the message hash. The caller
* must have allocated R and S.
*/
gpg_err_code_t
_gcry_ecc_ecdsa_sign (gcry_mpi_t input, gcry_mpi_t k_supplied, mpi_ec_t ec,
gcry_mpi_t r, gcry_mpi_t s,
int flags, int hashalgo)
{
gpg_err_code_t rc = 0;
int extraloops = 0;
gcry_mpi_t k, dr, sum, k_1, x;
mpi_point_struct I;
gcry_mpi_t hash;
const void *abuf;
unsigned int abits, qbits;
gcry_mpi_t b; /* Random number needed for blinding. */
gcry_mpi_t bi; /* multiplicative inverse of B. */
gcry_mpi_t hash_computed_internally = NULL;
if (DBG_CIPHER)
log_mpidump ("ecdsa sign hash ", input );
qbits = mpi_get_nbits (ec->n);
if ((flags & PUBKEY_FLAG_PREHASH))
{
rc = _gcry_dsa_compute_hash (&hash_computed_internally, input, hashalgo);
if (rc)
return rc;
input = hash_computed_internally;
}
/* Convert the INPUT into an MPI if needed. */
rc = _gcry_dsa_normalize_hash (input, &hash, qbits);
if (rc)
{
mpi_free (hash_computed_internally);
return rc;
}
b = mpi_snew (qbits);
bi = mpi_snew (qbits);
do
{
_gcry_mpi_randomize (b, qbits, GCRY_WEAK_RANDOM);
mpi_mod (b, b, ec->n);
}
while (!mpi_invm (bi, b, ec->n));
k = NULL;
dr = mpi_alloc (0);
sum = mpi_alloc (0);
k_1 = mpi_alloc (0);
x = mpi_alloc (0);
point_init (&I);
/* Two loops to avoid R or S are zero. This is more of a joke than
a real demand because the probability of them being zero is less
than any hardware failure. Some specs however require it. */
while (1)
{
while (1)
{
if (k_supplied)
k = k_supplied;
else
{
mpi_free (k);
k = NULL;
if ((flags & PUBKEY_FLAG_RFC6979) && hashalgo)
{
if (fips_mode () &&
(hashalgo == GCRY_MD_SHAKE128
|| hashalgo == GCRY_MD_SHAKE256))
{
- rc = GPG_ERR_DIGEST_ALGO;
+ if (fips_check_rejection (GCRY_FIPS_FLAG_REJECT_PK))
+ rc = GPG_ERR_DIGEST_ALGO;
+ else
+ fips_service_indicator_mark_non_compliant ();
goto leave;
}
/* Use Pornin's method for deterministic DSA. If this
flag is set, it is expected that HASH is an opaque
MPI with the to be signed hash. That hash is also
used as h1 from 3.2.a. */
if (!mpi_is_opaque (input))
{
rc = GPG_ERR_CONFLICT;
goto leave;
}
abuf = mpi_get_opaque (input, &abits);
rc = _gcry_dsa_gen_rfc6979_k (&k, ec->n, ec->d,
abuf, (abits+7)/8,
hashalgo, extraloops);
if (rc)
goto leave;
extraloops++;
}
else
k = _gcry_dsa_gen_k (ec->n, GCRY_STRONG_RANDOM);
}
mpi_invm (k_1, k, ec->n); /* k_1 = k^(-1) mod n */
_gcry_dsa_modify_k (k, ec->n, qbits);
_gcry_mpi_ec_mul_point (&I, k, ec->G, ec);
if (_gcry_mpi_ec_get_affine (x, NULL, &I, ec))
{
if (DBG_CIPHER)
log_debug ("ecc sign: Failed to get affine coordinates\n");
rc = GPG_ERR_BAD_SIGNATURE;
goto leave;
}
mpi_mod (r, x, ec->n); /* r = x mod n */
if (mpi_cmp_ui (r, 0))
break;
if (k_supplied)
{
rc = GPG_ERR_INV_VALUE;
goto leave;
}
}
/* Computation of dr, sum, and s are blinded with b. */
mpi_mulm (dr, b, ec->d, ec->n);
mpi_mulm (dr, dr, r, ec->n); /* dr = d*r mod n */
mpi_mulm (sum, b, hash, ec->n);
mpi_addm (sum, sum, dr, ec->n); /* sum = hash + (d*r) mod n */
mpi_mulm (s, k_1, sum, ec->n); /* s = k^(-1)*(hash+(d*r)) mod n */
/* Undo blinding by b^-1 */
mpi_mulm (s, bi, s, ec->n);
if (mpi_cmp_ui (s, 0))
break;
if (k_supplied)
{
rc = GPG_ERR_INV_VALUE;
break;
}
}
if (DBG_CIPHER)
{
log_mpidump ("ecdsa sign result r ", r);
log_mpidump ("ecdsa sign result s ", s);
}
leave:
mpi_free (b);
mpi_free (bi);
point_free (&I);
mpi_free (x);
mpi_free (k_1);
mpi_free (sum);
mpi_free (dr);
if (!k_supplied)
mpi_free (k);
if (hash != input)
mpi_free (hash);
mpi_free (hash_computed_internally);
return rc;
}
/* Verify an ECDSA signature.
* Check if R and S verifies INPUT.
*/
gpg_err_code_t
_gcry_ecc_ecdsa_verify (gcry_mpi_t input, mpi_ec_t ec,
gcry_mpi_t r, gcry_mpi_t s, int flags, int hashalgo)
{
gpg_err_code_t err = 0;
gcry_mpi_t hash, h, h1, h2, x;
mpi_point_struct Q, Q1, Q2;
unsigned int nbits;
gcry_mpi_t hash_computed_internally = NULL;
if (!_gcry_mpi_ec_curve_point (ec->Q, ec))
return GPG_ERR_BROKEN_PUBKEY;
if( !(mpi_cmp_ui (r, 0) > 0 && mpi_cmp (r, ec->n) < 0) )
return GPG_ERR_BAD_SIGNATURE; /* Assertion 0 < r < n failed. */
if( !(mpi_cmp_ui (s, 0) > 0 && mpi_cmp (s, ec->n) < 0) )
return GPG_ERR_BAD_SIGNATURE; /* Assertion 0 < s < n failed. */
nbits = mpi_get_nbits (ec->n);
if ((flags & PUBKEY_FLAG_PREHASH))
{
err = _gcry_dsa_compute_hash (&hash_computed_internally, input,
hashalgo);
if (err)
return err;
input = hash_computed_internally;
}
err = _gcry_dsa_normalize_hash (input, &hash, nbits);
if (err)
{
mpi_free (hash_computed_internally);
return err;
}
h = mpi_alloc (0);
h1 = mpi_alloc (0);
h2 = mpi_alloc (0);
x = mpi_alloc (0);
point_init (&Q);
point_init (&Q1);
point_init (&Q2);
/* h = s^(-1) (mod n) */
mpi_invm (h, s, ec->n);
/* h1 = hash * s^(-1) (mod n) */
mpi_mulm (h1, hash, h, ec->n);
/* Q1 = [ hash * s^(-1) ]G */
_gcry_mpi_ec_mul_point (&Q1, h1, ec->G, ec);
/* h2 = r * s^(-1) (mod n) */
mpi_mulm (h2, r, h, ec->n);
/* Q2 = [ r * s^(-1) ]Q */
_gcry_mpi_ec_mul_point (&Q2, h2, ec->Q, ec);
/* Q = ([hash * s^(-1)]G) + ([r * s^(-1)]Q) */
_gcry_mpi_ec_add_points (&Q, &Q1, &Q2, ec);
if (!mpi_cmp_ui (Q.z, 0))
{
if (DBG_CIPHER)
log_debug ("ecc verify: Rejected\n");
err = GPG_ERR_BAD_SIGNATURE;
goto leave;
}
if (_gcry_mpi_ec_get_affine (x, NULL, &Q, ec))
{
if (DBG_CIPHER)
log_debug ("ecc verify: Failed to get affine coordinates\n");
err = GPG_ERR_BAD_SIGNATURE;
goto leave;
}
mpi_mod (x, x, ec->n); /* x = x mod E_n */
if (mpi_cmp (x, r)) /* x != r */
{
if (DBG_CIPHER)
{
log_mpidump (" x", x);
log_mpidump (" r", r);
log_mpidump (" s", s);
}
err = GPG_ERR_BAD_SIGNATURE;
goto leave;
}
leave:
point_free (&Q2);
point_free (&Q1);
point_free (&Q);
mpi_free (x);
mpi_free (h2);
mpi_free (h1);
mpi_free (h);
if (hash != input)
mpi_free (hash);
mpi_free (hash_computed_internally);
return err;
}
diff --git a/cipher/ecc.c b/cipher/ecc.c
index 65525207..8896afd0 100644
--- a/cipher/ecc.c
+++ b/cipher/ecc.c
@@ -1,2381 +1,2387 @@
/* ecc.c - Elliptic Curve Cryptography
* Copyright (C) 2007, 2008, 2010, 2011 Free Software Foundation, Inc.
* Copyright (C) 2013, 2015 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/>.
*/
/* This code is originally based on the Patch 0.1.6 for the gnupg
1.4.x branch as retrieved on 2007-03-21 from
http://www.calcurco.cat/eccGnuPG/src/gnupg-1.4.6-ecc0.2.0beta1.diff.bz2
The original authors are:
Written by
Sergi Blanch i Torne <d4372211 at alumnes.eup.udl.es>,
Ramiro Moreno Chiral <ramiro at eup.udl.es>
Maintainers
Sergi Blanch i Torne
Ramiro Moreno Chiral
Mikael Mylnikov (mmr)
For use in Libgcrypt the code has been heavily modified and cleaned
up. In fact there is not much left of the originally code except for
some variable names and the text book implementaion of the sign and
verification algorithms. The arithmetic functions have entirely
been rewritten and moved to mpi/ec.c.
ECDH encrypt and decrypt code written by Andrey Jivsov.
*/
/* TODO:
- In mpi/ec.c we use mpi_powm for x^2 mod p: Either implement a
special case in mpi_powm or check whether mpi_mulm is faster.
*/
#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 "pubkey-internal.h"
#include "ecc-common.h"
static const char *ecc_names[] =
{
"ecc",
"ecdsa",
"ecdh",
"eddsa",
"gost",
"sm2",
NULL,
};
/* Sample NIST P-256 key from RFC 6979 A.2.5 */
static const char ecdsa_sample_public_key_secp256[] =
"(public-key"
" (ecc"
" (curve secp256r1)"
" (q #04"
/**/ "60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6"
/**/ "7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299#)))";
static const char ecdsa_sample_secret_key_secp256[] =
"(private-key"
" (ecc"
" (curve secp256r1)"
" (d #C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721#)"
" (q #04"
/**/ "60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6"
/**/ "7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299#)))";
/* Sample data from RFC 6979 section A.2.5, hash is of message "sample" */
static const char ecdsa_sample_data[] =
"(data (flags rfc6979 prehash)"
" (hash-algo sha256)"
" (value 6:sample))";
static const char ecdsa_sample_data_bad[] =
"(data (flags rfc6979)"
" (hash sha256 #bf2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e98915"
/**/ "62113d8a62add1bf#))";
static const char ecdsa_signature_r[] =
"efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716";
static const char ecdsa_signature_s[] =
"f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8";
static const char *ecdsa_data_tmpl = "(data (flags rfc6979) (hash %s %b))";
/* Sample data from RFC 6979 section A.2.5, hash is of message "sample" */
static const char ecdsa_sample_data_string[] = "sample";
static const char ecdsa_sample_data_bad_string[] = "sbmple";
/* Ed25519 test vector from RFC 8032 7.1. */
static const char ed25519_sample_public_key[] =
"(public-key"
" (ecc"
" (curve Ed25519)"
" (flags eddsa)"
" (q #3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c#)))";
static const char ed25519_sample_secret_key[] =
"(private-key"
" (ecc"
" (curve Ed25519)"
" (flags eddsa)"
" (d #4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb#)"
" (q #3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c#)))";
static const char ed25519_sample_data[] =
"(data (value #72#))";
static const char ed25519_sample_data_bad[] =
"(data (value #72727272#))";
static const char ed25519_signature_r[] =
"92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da";
static const char ed25519_signature_s[] =
"085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00";
static const char *ed25519_data_tmpl = "(data (value %b))";
/* Sample data from RFC 6979 section A.2.5, hash is of message "sample" */
static const char ed25519_sample_data_string[] = "\x72";
static const char ed25519_sample_data_bad_string[] = "\x72\x72\x72\x72";
/* Ed448 test vector from RFC 8032 7.4. */
static const char ed448_sample_public_key[] =
"(public-key"
" (ecc"
" (curve Ed448)"
" (q #43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c086"
/**/ "6aea01eb00742802b8438ea4cb82169c235160627b4c3a9480#)))";
static const char ed448_sample_secret_key[] =
"(private-key"
" (ecc"
" (curve Ed448)"
" (d #c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463a"
/**/ "fbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e#)"
" (q #43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c086"
/**/ "6aea01eb00742802b8438ea4cb82169c235160627b4c3a9480#)))";
static const char ed448_sample_data[] =
"(data (value #03#))";
static const char ed448_sample_data_bad[] =
"(data (value #030303#))";
static const char ed448_signature_r[] =
"26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f435"
"2541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd77980";
static const char ed448_signature_s[] =
"5e0dbcc0aae1cbcee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905"
"e799f1953d2a0ff3348ab21aa4adafd1d234441cf807c03a00";
static const char *ed448_data_tmpl = "(data (value %b))";
/* Sample data from RFC 6979 section A.2.5, hash is of message "sample" */
static const char ed448_sample_data_string[] = "\x03";
static const char ed448_sample_data_bad_string[] = "\x03\x03\x03";
/* Registered progress function and its callback value. */
static void (*progress_cb) (void *, const char*, int, int, int);
static void *progress_cb_data;
/* Local prototypes. */
static void test_keys (mpi_ec_t ec, unsigned int nbits);
static int test_keys_fips (gcry_sexp_t skey);
static void test_ecdh_only_keys (mpi_ec_t ec, unsigned int nbits, int flags);
static unsigned int ecc_get_nbits (gcry_sexp_t parms);
void
_gcry_register_pk_ecc_progress (void (*cb) (void *, const char *,
int, int, int),
void *cb_data)
{
progress_cb = cb;
progress_cb_data = cb_data;
}
/* static void */
/* progress (int c) */
/* { */
/* if (progress_cb) */
/* progress_cb (progress_cb_data, "pk_ecc", c, 0, 0); */
/* } */
/**
* nist_generate_key - Standard version of the ECC key generation.
* @ec: Elliptic curve computation context.
* @flags: Flags controlling aspects of the creation.
* @r_x: On success this receives an allocated MPI with the affine
* x-coordinate of the poblic key. On error NULL is stored.
* @r_y: Ditto for the y-coordinate.
*
* Return: An error code.
*
* The @flags bits used by this function are %PUBKEY_FLAG_TRANSIENT to
* use a faster RNG, and %PUBKEY_FLAG_NO_KEYTEST to skip the assertion
* that the key works as expected.
*
* FIXME: Check whether N is needed.
*/
static gpg_err_code_t
nist_generate_key (mpi_ec_t ec, int flags,
gcry_mpi_t *r_x, gcry_mpi_t *r_y)
{
mpi_point_struct Q;
gcry_random_level_t random_level;
gcry_mpi_t x, y;
const unsigned int pbits = ec->nbits;
point_init (&Q);
if ((flags & PUBKEY_FLAG_TRANSIENT_KEY))
random_level = GCRY_STRONG_RANDOM;
else
random_level = GCRY_VERY_STRONG_RANDOM;
/* Generate a secret. */
if (ec->dialect == ECC_DIALECT_ED25519
|| ec->dialect == ECC_DIALECT_SAFECURVE
|| (flags & PUBKEY_FLAG_DJB_TWEAK))
{
char *rndbuf;
int len = (pbits+7)/8;
rndbuf = _gcry_random_bytes_secure (len, random_level);
if (ec->dialect == ECC_DIALECT_SAFECURVE)
ec->d = mpi_set_opaque (NULL, rndbuf, len*8);
else
{
ec->d = mpi_snew (pbits);
if ((pbits % 8))
rndbuf[0] &= (1 << (pbits % 8)) - 1;
rndbuf[0] |= (1 << ((pbits + 7) % 8));
rndbuf[len-1] &= (256 - ec->h);
_gcry_mpi_set_buffer (ec->d, rndbuf, len, 0);
xfree (rndbuf);
}
}
else
ec->d = _gcry_dsa_gen_k (ec->n, random_level);
/* Compute Q. */
_gcry_mpi_ec_mul_point (&Q, ec->d, ec->G, ec);
x = mpi_new (pbits);
if (r_y == NULL)
y = NULL;
else
y = mpi_new (pbits);
if (_gcry_mpi_ec_get_affine (x, y, &Q, ec))
log_fatal ("ecgen: Failed to get affine coordinates for %s\n", "Q");
/* We want the Q=(x,y) be a "compliant key" in terms of the
* http://tools.ietf.org/html/draft-jivsov-ecc-compact, which simply
* means that we choose either Q=(x,y) or -Q=(x,p-y) such that we
* end up with the min(y,p-y) as the y coordinate. Such a public
* key allows the most efficient compression: y can simply be
* dropped because we know that it's a minimum of the two
* possibilities without any loss of security. Note that we don't
* do that for Ed25519 so that we do not violate the special
* construction of the secret key. */
if (r_y == NULL || ec->dialect == ECC_DIALECT_ED25519)
ec->Q = mpi_point_set (NULL, Q.x, Q.y, Q.z);
else
{
gcry_mpi_t negative;
negative = mpi_new (pbits);
if (ec->model == MPI_EC_WEIERSTRASS)
mpi_sub (negative, ec->p, y); /* negative = p - y */
else
mpi_sub (negative, ec->p, x); /* negative = p - x */
if (mpi_cmp (negative, y) < 0) /* p - y < p */
{
/* We need to end up with -Q; this assures that new Q's y is
the smallest one */
if (ec->model == MPI_EC_WEIERSTRASS)
{
mpi_free (y);
y = negative;
}
else
{
mpi_free (x);
x = negative;
}
mpi_sub (ec->d, ec->n, ec->d); /* d = order - d */
ec->Q = mpi_point_set (NULL, x, y, mpi_const (MPI_C_ONE));
if (DBG_CIPHER)
log_debug ("ecgen converted Q to a compliant point\n");
}
else /* p - y >= p */
{
/* No change is needed exactly 50% of the time: just copy. */
mpi_free (negative);
ec->Q = mpi_point_set (NULL, Q.x, Q.y, Q.z);
if (DBG_CIPHER)
log_debug ("ecgen didn't need to convert Q to a compliant point\n");
}
}
*r_x = x;
if (r_y)
*r_y = y;
point_free (&Q);
/* Now we can test our keys (this should never fail!). */
if ((flags & PUBKEY_FLAG_NO_KEYTEST))
; /* User requested to skip the test. */
else if (ec->model == MPI_EC_MONTGOMERY)
test_ecdh_only_keys (ec, ec->nbits - 63, flags);
else if (!fips_mode ())
test_keys (ec, ec->nbits - 64);
return 0;
}
/*
* To verify correct skey it use a random information.
* First, encrypt and decrypt this dummy value,
* test if the information is recuperated.
* Second, test with the sign and verify functions.
*/
static void
test_keys (mpi_ec_t ec, unsigned int nbits)
{
gcry_mpi_t test = mpi_new (nbits);
mpi_point_struct R_;
gcry_mpi_t c = mpi_new (nbits);
gcry_mpi_t out = mpi_new (nbits);
gcry_mpi_t r = mpi_new (nbits);
gcry_mpi_t s = mpi_new (nbits);
if (DBG_CIPHER)
log_debug ("Testing key.\n");
point_init (&R_);
_gcry_mpi_randomize (test, nbits, GCRY_WEAK_RANDOM);
if (_gcry_ecc_ecdsa_sign (test, NULL, ec, r, s, 0, 0) )
log_fatal ("ECDSA operation: sign failed\n");
if (_gcry_ecc_ecdsa_verify (test, ec, r, s, 0, 0))
{
log_fatal ("ECDSA operation: sign, verify failed\n");
}
if (DBG_CIPHER)
log_debug ("ECDSA operation: sign, verify ok.\n");
point_free (&R_);
mpi_free (s);
mpi_free (r);
mpi_free (out);
mpi_free (c);
mpi_free (test);
}
/* We should get here only with the NIST curves as they are the only ones
* having the fips bit set in ecc_domain_parms_t struct so this is slightly
* simpler than the whole ecc_generate function */
static int
test_keys_fips (gcry_sexp_t skey)
{
int result = -1; /* Default to failure */
gcry_md_hd_t hd = NULL;
const char *data_tmpl = "(data (flags rfc6979) (hash %s %b))";
gcry_sexp_t sig = NULL;
char plaintext[128];
int rc;
/* Create a random plaintext. */
_gcry_randomize (plaintext, sizeof plaintext, GCRY_WEAK_RANDOM);
/* Open MD context and feed the random data in */
rc = _gcry_md_open (&hd, GCRY_MD_SHA256, 0);
if (rc)
{
log_error ("ECDSA operation: failed to initialize MD context: %s\n", gpg_strerror (rc));
goto leave;
}
_gcry_md_write (hd, plaintext, sizeof(plaintext));
/* Sign the data */
rc = _gcry_pk_sign_md (&sig, data_tmpl, hd, skey, NULL);
if (rc)
{
log_error ("ECDSA operation: signing failed: %s\n", gpg_strerror (rc));
goto leave;
}
/* Verify this signature. */
rc = _gcry_pk_verify_md (sig, data_tmpl, hd, skey, NULL);
if (rc)
{
log_error ("ECDSA operation: verification failed: %s\n", gpg_strerror (rc));
goto leave;
}
/* Modify the data and check that the signing fails. */
_gcry_md_reset(hd);
plaintext[sizeof plaintext / 2] ^= 1;
_gcry_md_write (hd, plaintext, sizeof(plaintext));
rc = _gcry_pk_verify_md (sig, data_tmpl, hd, skey, NULL);
if (rc != GPG_ERR_BAD_SIGNATURE)
{
log_error ("ECDSA operation: signature verification worked on modified data\n");
goto leave;
}
result = 0;
leave:
_gcry_md_close (hd);
sexp_release (sig);
return result;
}
static int
test_keys_eddsa_fips (gcry_sexp_t skey)
{
int result = -1; /* Default to failure */
gcry_ctx_t ctx = NULL;
const char *data_tmpl = "(data (value %b))";
gcry_sexp_t sig = NULL;
char plaintext[128];
int rc;
/* Create a random plaintext. */
_gcry_randomize (plaintext, sizeof plaintext, GCRY_WEAK_RANDOM);
rc = _gcry_pk_single_data_push (&ctx, (void *)plaintext, sizeof(plaintext));
if (rc)
{
log_error ("EdDSA operation: failed to push input data: %s\n",
gpg_strerror (rc));
goto leave;
}
/* Sign the data */
rc = _gcry_pk_sign_md (&sig, data_tmpl, NULL, skey, ctx);
if (rc)
{
log_error ("EdDSA operation: signing failed: %s\n", gpg_strerror (rc));
goto leave;
}
/* Verify this signature. */
rc = _gcry_pk_verify_md (sig, data_tmpl, NULL, skey, ctx);
if (rc)
{
log_error ("EdDSA operation: verification failed: %s\n", gpg_strerror (rc));
goto leave;
}
_gcry_ctx_release (ctx);
ctx = NULL;
/* Modify the data and check that the signing fails. */
plaintext[sizeof plaintext / 2] ^= 1;
rc = _gcry_pk_single_data_push (&ctx, (void *)plaintext, sizeof(plaintext));
if (rc)
{
log_error ("EdDSA operation: failed to push input data: %s\n",
gpg_strerror (rc));
goto leave;
}
rc = _gcry_pk_verify_md (sig, data_tmpl, NULL, skey, ctx);
if (rc != GPG_ERR_BAD_SIGNATURE)
{
log_error ("EdDSA operation: signature verification worked on modified data\n");
goto leave;
}
result = 0;
leave:
_gcry_ctx_release (ctx);
sexp_release (sig);
return result;
}
static void
test_ecdh_only_keys (mpi_ec_t ec, unsigned int nbits, int flags)
{
gcry_mpi_t test;
mpi_point_struct R_;
gcry_mpi_t x0, x1;
if (DBG_CIPHER)
log_debug ("Testing ECDH only key.\n");
point_init (&R_);
if (ec->dialect == ECC_DIALECT_SAFECURVE || (flags & PUBKEY_FLAG_DJB_TWEAK))
{
char *rndbuf;
const unsigned int pbits = ec->nbits;
int len = (pbits+7)/8;
rndbuf = _gcry_random_bytes (len, GCRY_WEAK_RANDOM);
if (ec->dialect == ECC_DIALECT_SAFECURVE)
test = mpi_set_opaque (NULL, rndbuf, len*8);
else
{
test = mpi_new (pbits);
if ((pbits % 8))
rndbuf[0] &= (1 << (pbits % 8)) - 1;
rndbuf[0] |= (1 << ((pbits + 7) % 8));
rndbuf[len-1] &= (256 - ec->h);
_gcry_mpi_set_buffer (test, rndbuf, len, 0);
xfree (rndbuf);
}
}
else
{
test = mpi_new (nbits);
_gcry_mpi_randomize (test, nbits, GCRY_WEAK_RANDOM);
}
x0 = mpi_new (0);
x1 = mpi_new (0);
/* R_ = hkQ <=> R_ = hkdG */
_gcry_mpi_ec_mul_point (&R_, test, ec->Q, ec);
if (ec->dialect == ECC_DIALECT_STANDARD && !(flags & PUBKEY_FLAG_DJB_TWEAK))
_gcry_mpi_ec_mul_point (&R_, _gcry_mpi_get_const (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");
_gcry_mpi_ec_mul_point (&R_, test, ec->G, ec);
_gcry_mpi_ec_mul_point (&R_, ec->d, &R_, ec);
/* R_ = hdkG */
if (ec->dialect == ECC_DIALECT_STANDARD && !(flags & PUBKEY_FLAG_DJB_TWEAK))
_gcry_mpi_ec_mul_point (&R_, _gcry_mpi_get_const (ec->h), &R_, ec);
if (_gcry_mpi_ec_get_affine (x1, NULL, &R_, ec))
log_fatal ("ecdh: Failed to get affine coordinates for hdkG\n");
if (mpi_cmp (x0, x1))
{
log_fatal ("ECDH test failed.\n");
}
mpi_free (x0);
mpi_free (x1);
point_free (&R_);
mpi_free (test);
}
/*
* To check the validity of the value, recalculate the correspondence
* between the public value and the secret one.
*/
static int
check_secret_key (mpi_ec_t ec, int flags)
{
int rc = 1;
mpi_point_struct Q;
gcry_mpi_t x1, y1;
gcry_mpi_t x2 = NULL;
gcry_mpi_t y2 = NULL;
point_init (&Q);
x1 = mpi_new (0);
if (ec->model == MPI_EC_MONTGOMERY)
y1 = NULL;
else
y1 = mpi_new (0);
/* G in E(F_p) */
if (!_gcry_mpi_ec_curve_point (ec->G, ec))
{
if (DBG_CIPHER)
log_debug ("Bad check: Point 'G' does not belong to curve 'E'!\n");
goto leave;
}
/* G != PaI */
if (!mpi_cmp_ui (ec->G->z, 0))
{
if (DBG_CIPHER)
log_debug ("Bad check: 'G' cannot be Point at Infinity!\n");
goto leave;
}
/* Check order of curve. */
if (ec->dialect == ECC_DIALECT_STANDARD && !(flags & PUBKEY_FLAG_DJB_TWEAK))
{
_gcry_mpi_ec_mul_point (&Q, ec->n, ec->G, ec);
if (mpi_cmp_ui (Q.z, 0))
{
if (DBG_CIPHER)
log_debug ("check_secret_key: E is not a curve of order n\n");
goto leave;
}
}
/* Pubkey cannot be PaI */
if (!mpi_cmp_ui (ec->Q->z, 0))
{
if (DBG_CIPHER)
log_debug ("Bad check: Q can not be a Point at Infinity!\n");
goto leave;
}
/* pubkey = [d]G over E */
if (!_gcry_ecc_compute_public (&Q, ec))
{
if (DBG_CIPHER)
log_debug ("Bad check: computation of dG failed\n");
goto leave;
}
if (_gcry_mpi_ec_get_affine (x1, y1, &Q, ec))
{
if (DBG_CIPHER)
log_debug ("Bad check: Q can not be a Point at Infinity!\n");
goto leave;
}
if (!mpi_cmp_ui (ec->Q->z, 1))
{
/* Fast path if Q is already in affine coordinates. */
if (mpi_cmp (x1, ec->Q->x) || (y1 && mpi_cmp (y1, ec->Q->y)))
{
if (DBG_CIPHER)
log_debug
("Bad check: There is NO correspondence between 'd' and 'Q'!\n");
goto leave;
}
}
else
{
x2 = mpi_new (0);
y2 = mpi_new (0);
if (_gcry_mpi_ec_get_affine (x2, y2, ec->Q, ec))
{
if (DBG_CIPHER)
log_debug ("Bad check: Q can not be a Point at Infinity!\n");
goto leave;
}
if (mpi_cmp (x1, x2) || mpi_cmp (y1, y2))
{
if (DBG_CIPHER)
log_debug
("Bad check: There is NO correspondence between 'd' and 'Q'!\n");
goto leave;
}
}
rc = 0; /* Okay. */
leave:
mpi_free (x2);
mpi_free (x1);
mpi_free (y1);
mpi_free (y2);
point_free (&Q);
return rc;
}
/*********************************************
************** interface ******************
*********************************************/
static gcry_err_code_t
ecc_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
{
gpg_err_code_t rc;
gcry_mpi_t Gx = NULL;
gcry_mpi_t Gy = NULL;
gcry_mpi_t Qx = NULL;
gcry_mpi_t Qy = NULL;
mpi_ec_t ec = NULL;
gcry_sexp_t curve_info = NULL;
gcry_sexp_t curve_flags = NULL;
gcry_mpi_t base = NULL;
gcry_mpi_t public = NULL;
int flags = 0;
rc = _gcry_mpi_ec_internal_new (&ec, &flags, "ecgen curve", genparms, NULL);
if (rc)
goto leave;
if ((flags & PUBKEY_FLAG_EDDSA)
|| (ec->model == MPI_EC_EDWARDS && ec->dialect == ECC_DIALECT_SAFECURVE))
rc = _gcry_ecc_eddsa_genkey (ec, flags);
else if (ec->model == MPI_EC_MONTGOMERY)
rc = nist_generate_key (ec, flags, &Qx, NULL);
else
rc = nist_generate_key (ec, flags, &Qx, &Qy);
if (rc)
goto leave;
/* Copy data to the result. */
Gx = mpi_new (0);
Gy = mpi_new (0);
if (ec->model != MPI_EC_MONTGOMERY)
{
if (_gcry_mpi_ec_get_affine (Gx, Gy, ec->G, ec))
log_fatal ("ecgen: Failed to get affine coordinates for %s\n", "G");
base = _gcry_ecc_ec2os (Gx, Gy, ec->p);
}
if (((ec->dialect == ECC_DIALECT_SAFECURVE && ec->model == MPI_EC_EDWARDS)
|| ec->dialect == ECC_DIALECT_ED25519 || ec->model == MPI_EC_MONTGOMERY)
&& !(flags & PUBKEY_FLAG_NOCOMP))
{
unsigned char *encpk;
unsigned int encpklen;
if (ec->model == MPI_EC_MONTGOMERY)
rc = _gcry_ecc_mont_encodepoint (Qx, ec->nbits,
ec->dialect != ECC_DIALECT_SAFECURVE,
&encpk, &encpklen);
else
/* (Gx and Gy are used as scratch variables) */
rc = _gcry_ecc_eddsa_encodepoint (ec->Q, ec, Gx, Gy,
(ec->dialect != ECC_DIALECT_SAFECURVE
&& !!(flags & PUBKEY_FLAG_COMP)),
&encpk, &encpklen);
if (rc)
goto leave;
public = mpi_new (0);
mpi_set_opaque (public, encpk, encpklen*8);
}
else
{
if (!Qx)
{
/* This is the case for a key from _gcry_ecc_eddsa_generate
with no compression. */
Qx = mpi_new (0);
Qy = mpi_new (0);
if (_gcry_mpi_ec_get_affine (Qx, Qy, ec->Q, ec))
log_fatal ("ecgen: Failed to get affine coordinates for %s\n", "Q");
}
public = _gcry_ecc_ec2os (Qx, Qy, ec->p);
}
if (ec->name)
{
rc = sexp_build (&curve_info, NULL, "(curve %s)", ec->name);
if (rc)
goto leave;
}
if ((flags & PUBKEY_FLAG_PARAM) || (flags & PUBKEY_FLAG_EDDSA)
|| (flags & PUBKEY_FLAG_DJB_TWEAK))
{
rc = sexp_build
(&curve_flags, NULL,
((flags & PUBKEY_FLAG_PARAM) && (flags & PUBKEY_FLAG_EDDSA))?
"(flags param eddsa)" :
((flags & PUBKEY_FLAG_PARAM) && (flags & PUBKEY_FLAG_DJB_TWEAK))?
"(flags param djb-tweak)" :
((flags & PUBKEY_FLAG_PARAM))?
"(flags param)" : ((flags & PUBKEY_FLAG_EDDSA))?
"(flags eddsa)" : "(flags djb-tweak)" );
if (rc)
goto leave;
}
if ((flags & PUBKEY_FLAG_PARAM) && ec->name)
rc = sexp_build (r_skey, NULL,
"(key-data"
" (public-key"
" (ecc%S%S(p%m)(a%m)(b%m)(g%m)(n%m)(h%u)(q%m)))"
" (private-key"
" (ecc%S%S(p%m)(a%m)(b%m)(g%m)(n%m)(h%u)(q%m)(d%m)))"
" )",
curve_info, curve_flags,
ec->p, ec->a, ec->b, base, ec->n, ec->h, public,
curve_info, curve_flags,
ec->p, ec->a, ec->b, base, ec->n, ec->h, public,
ec->d);
else
rc = sexp_build (r_skey, NULL,
"(key-data"
" (public-key"
" (ecc%S%S(q%m)))"
" (private-key"
" (ecc%S%S(q%m)(d%m)))"
" )",
curve_info, curve_flags,
public,
curve_info, curve_flags,
public, ec->d);
if (rc)
goto leave;
if (DBG_CIPHER)
{
log_printmpi ("ecgen result p", ec->p);
log_printmpi ("ecgen result a", ec->a);
log_printmpi ("ecgen result b", ec->b);
log_printmpi ("ecgen result G", base);
log_printmpi ("ecgen result n", ec->n);
log_debug ("ecgen result h:+%02x\n", ec->h);
log_printmpi ("ecgen result Q", public);
log_printmpi ("ecgen result d", ec->d);
if ((flags & PUBKEY_FLAG_EDDSA))
log_debug ("ecgen result using Ed25519+EdDSA\n");
}
if (fips_mode ())
{
int result;
if (ec->model == MPI_EC_EDWARDS)
result = test_keys_eddsa_fips (*r_skey);
else
result = test_keys_fips (*r_skey);
if (result)
{
sexp_release (*r_skey);
*r_skey = NULL;
fips_signal_error ("self-test after key generation failed");
rc = GPG_ERR_SELFTEST_FAILED;
}
}
leave:
mpi_free (public);
mpi_free (base);
mpi_free (Gx);
mpi_free (Gy);
mpi_free (Qx);
mpi_free (Qy);
_gcry_mpi_ec_free (ec);
sexp_release (curve_flags);
sexp_release (curve_info);
return rc;
}
static gcry_err_code_t
ecc_check_secret_key (gcry_sexp_t keyparms)
{
gcry_err_code_t rc;
int flags = 0;
mpi_ec_t ec = NULL;
/*
* Extract the key.
*/
rc = _gcry_mpi_ec_internal_new (&ec, &flags, "ecc_testkey", keyparms, NULL);
if (rc)
goto leave;
if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n || !ec->Q || !ec->d)
{
rc = GPG_ERR_NO_OBJ;
goto leave;
}
if (check_secret_key (ec, flags))
rc = GPG_ERR_BAD_SECKEY;
leave:
_gcry_mpi_ec_free (ec);
if (DBG_CIPHER)
log_debug ("ecc_testkey => %s\n", gpg_strerror (rc));
return rc;
}
static gcry_err_code_t
ecc_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
{
gcry_err_code_t rc;
struct pk_encoding_ctx ctx;
gcry_mpi_t data = NULL;
gcry_mpi_t k = NULL;
gcry_mpi_t sig_r = NULL;
gcry_mpi_t sig_s = NULL;
mpi_ec_t ec = NULL;
int flags = 0;
_gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_SIGN, 0);
/*
* Extract the key.
*/
rc = _gcry_mpi_ec_internal_new (&ec, &flags, "ecc_sign", keyparms, NULL);
if (rc)
goto leave;
if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n || !ec->d)
{
rc = GPG_ERR_NO_OBJ;
goto leave;
}
ctx.flags |= flags;
if (ec->model == MPI_EC_EDWARDS && ec->dialect == ECC_DIALECT_SAFECURVE)
ctx.flags |= PUBKEY_FLAG_EDDSA;
/* Clear hash algo for EdDSA. */
if ((ctx.flags & PUBKEY_FLAG_EDDSA))
ctx.hash_algo = GCRY_MD_NONE;
/* Extract the data. */
rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
if (rc)
goto leave;
if (DBG_CIPHER)
log_mpidump ("ecc_sign data", data);
if (ctx.label)
rc = _gcry_mpi_scan (&k, GCRYMPI_FMT_USG, ctx.label, ctx.labellen, NULL);
if (rc)
goto leave;
/* Hash algo is determined by curve in EdDSA. */
if ((ctx.flags & PUBKEY_FLAG_EDDSA))
{
if (ctx.hash_algo)
{
if (fips_mode ()
&& ((ec->dialect == ECC_DIALECT_ED25519
&&ctx.hash_algo != GCRY_MD_SHA512)
|| (ec->dialect == ECC_DIALECT_SAFECURVE
&& ctx.hash_algo != GCRY_MD_SHAKE256)))
{
- rc = GPG_ERR_DIGEST_ALGO;
+ if (fips_check_rejection (GCRY_FIPS_FLAG_REJECT_PK))
+ rc = GPG_ERR_DIGEST_ALGO;
+ else
+ fips_service_indicator_mark_non_compliant ();
goto leave;
}
}
else
{
if (ec->dialect == ECC_DIALECT_ED25519)
ctx.hash_algo = GCRY_MD_SHA512;
else if (ec->dialect == ECC_DIALECT_SAFECURVE)
ctx.hash_algo = GCRY_MD_SHAKE256;
}
}
sig_r = mpi_new (0);
sig_s = mpi_new (0);
if ((ctx.flags & PUBKEY_FLAG_EDDSA))
{
/* EdDSA requires the public key. */
rc = _gcry_ecc_eddsa_sign (data, ec, sig_r, sig_s, &ctx);
if (!rc)
rc = sexp_build (r_sig, NULL,
"(sig-val(eddsa(r%M)(s%M)))", sig_r, sig_s);
}
else if ((ctx.flags & PUBKEY_FLAG_GOST))
{
rc = _gcry_ecc_gost_sign (data, ec, sig_r, sig_s);
if (!rc)
rc = sexp_build (r_sig, NULL,
"(sig-val(gost(r%M)(s%M)))", sig_r, sig_s);
}
else if ((ctx.flags & PUBKEY_FLAG_SM2))
{
rc = _gcry_ecc_sm2_sign (data, ec, sig_r, sig_s,
ctx.flags, ctx.hash_algo);
if (!rc)
rc = sexp_build (r_sig, NULL,
"(sig-val(sm2(r%M)(s%M)))", sig_r, sig_s);
}
else
{
rc = _gcry_ecc_ecdsa_sign (data, k, ec, sig_r, sig_s,
ctx.flags, ctx.hash_algo);
if (!rc)
rc = sexp_build (r_sig, NULL,
"(sig-val(ecdsa(r%M)(s%M)))", sig_r, sig_s);
}
leave:
_gcry_mpi_release (sig_r);
_gcry_mpi_release (sig_s);
_gcry_mpi_release (data);
_gcry_mpi_release (k);
_gcry_mpi_ec_free (ec);
_gcry_pk_util_free_encoding_ctx (&ctx);
if (DBG_CIPHER)
log_debug ("ecc_sign => %s\n", gpg_strerror (rc));
return rc;
}
static gcry_err_code_t
ecc_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
{
gcry_err_code_t rc;
struct pk_encoding_ctx ctx;
gcry_sexp_t l1 = NULL;
gcry_mpi_t sig_r = NULL;
gcry_mpi_t sig_s = NULL;
gcry_mpi_t data = NULL;
int sigflags;
mpi_ec_t ec = NULL;
int flags = 0;
_gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_VERIFY,
ecc_get_nbits (s_keyparms));
/*
* Extract the key.
*/
rc = _gcry_mpi_ec_internal_new (&ec, &flags, "ecc_verify",
s_keyparms, NULL);
if (rc)
goto leave;
if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n || !ec->Q)
{
rc = GPG_ERR_NO_OBJ;
goto leave;
}
if (ec->model == MPI_EC_MONTGOMERY)
{
if (DBG_CIPHER)
log_debug ("ecc_verify: Can't use a Montgomery curve\n");
rc = GPG_ERR_INTERNAL;
goto leave;
}
ctx.flags |= flags;
if (ec->model == MPI_EC_EDWARDS && ec->dialect == ECC_DIALECT_SAFECURVE)
ctx.flags |= PUBKEY_FLAG_EDDSA;
/* Clear hash algo for EdDSA. */
if ((ctx.flags & PUBKEY_FLAG_EDDSA))
ctx.hash_algo = GCRY_MD_NONE;
/* Extract the data. */
rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
if (rc)
goto leave;
if (DBG_CIPHER)
log_mpidump ("ecc_verify data", data);
/* Hash algo is determined by curve in EdDSA. */
if ((ctx.flags & PUBKEY_FLAG_EDDSA))
{
if (ctx.hash_algo)
{
if (fips_mode ()
&& ((ec->dialect == ECC_DIALECT_ED25519
&&ctx.hash_algo != GCRY_MD_SHA512)
|| (ec->dialect == ECC_DIALECT_SAFECURVE
&& ctx.hash_algo != GCRY_MD_SHAKE256)))
{
- rc = GPG_ERR_DIGEST_ALGO;
+ if (fips_check_rejection (GCRY_FIPS_FLAG_REJECT_PK))
+ rc = GPG_ERR_DIGEST_ALGO;
+ else
+ fips_service_indicator_mark_non_compliant ();
goto leave;
}
}
else
{
if (ec->dialect == ECC_DIALECT_ED25519)
ctx.hash_algo = GCRY_MD_SHA512;
else if (ec->dialect == ECC_DIALECT_SAFECURVE)
ctx.hash_algo = GCRY_MD_SHAKE256;
}
}
/*
* Extract the signature value.
*/
rc = _gcry_pk_util_preparse_sigval (s_sig, ecc_names, &l1, &sigflags);
if (rc)
goto leave;
rc = sexp_extract_param (l1, NULL, (sigflags & PUBKEY_FLAG_EDDSA)? "/rs":"rs",
&sig_r, &sig_s, NULL);
if (rc)
goto leave;
if (DBG_CIPHER)
{
log_mpidump ("ecc_verify s_r", sig_r);
log_mpidump ("ecc_verify s_s", sig_s);
}
if ((ctx.flags & PUBKEY_FLAG_EDDSA) ^ (sigflags & PUBKEY_FLAG_EDDSA))
{
rc = GPG_ERR_CONFLICT; /* Inconsistent use of flag/algoname. */
goto leave;
}
/*
* Verify the signature.
*/
if ((sigflags & PUBKEY_FLAG_EDDSA))
{
rc = _gcry_ecc_eddsa_verify (data, ec, sig_r, sig_s, &ctx);
}
else if ((sigflags & PUBKEY_FLAG_GOST))
{
rc = _gcry_ecc_gost_verify (data, ec, sig_r, sig_s);
}
else if ((sigflags & PUBKEY_FLAG_SM2))
{
rc = _gcry_ecc_sm2_verify (data, ec, sig_r, sig_s);
}
else
{
rc = _gcry_ecc_ecdsa_verify (data, ec, sig_r, sig_s,
ctx.flags, ctx.hash_algo);
}
leave:
_gcry_mpi_release (data);
_gcry_mpi_release (sig_r);
_gcry_mpi_release (sig_s);
_gcry_mpi_ec_free (ec);
sexp_release (l1);
_gcry_pk_util_free_encoding_ctx (&ctx);
if (DBG_CIPHER)
log_debug ("ecc_verify => %s\n", rc?gpg_strerror (rc):"Good");
return rc;
}
/* ecdh raw is classic 2-round DH protocol published in 1976.
*
* Overview of ecc_encrypt_raw and ecc_decrypt_raw.
*
* As with any PK operation, encrypt version uses a public key and
* decrypt -- private.
*
* Symbols used below:
* G - field generator point
* d - private long-term scalar
* dG - public long-term key
* k - ephemeral scalar
* kG - ephemeral public key
* dkG - shared secret
*
* ecc_encrypt_raw description:
* input:
* data[0] : private scalar (k)
* output: A new S-expression with the parameters:
* s : shared point (kdG)
* e : generated ephemeral public key (kG)
*
* ecc_decrypt_raw description:
* input:
* data[0] : a point kG (ephemeral public key)
* output:
* result[0] : shared point (kdG)
*/
static gcry_err_code_t
ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
{
unsigned int nbits;
gcry_err_code_t rc;
struct pk_encoding_ctx ctx;
gcry_mpi_t mpi_s = NULL;
gcry_mpi_t mpi_e = NULL;
gcry_mpi_t data = NULL;
mpi_ec_t ec = NULL;
int flags = 0;
int no_error_on_infinity;
_gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_ENCRYPT,
(nbits = ecc_get_nbits (keyparms)));
/*
* Extract the key.
*/
rc = _gcry_mpi_ec_internal_new (&ec, &flags, "ecc_encrypt", keyparms, NULL);
if (rc)
goto leave;
if (ec->dialect == ECC_DIALECT_SAFECURVE)
{
ctx.flags |= PUBKEY_FLAG_RAW_FLAG;
no_error_on_infinity = 1;
}
else if ((flags & PUBKEY_FLAG_DJB_TWEAK))
no_error_on_infinity = 1;
else
no_error_on_infinity = 0;
/*
* Extract the data.
*/
rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
if (rc)
goto leave;
/*
* Tweak the scalar bits by cofactor and number of bits of the field.
* It assumes the cofactor is a power of 2.
*/
if ((flags & PUBKEY_FLAG_DJB_TWEAK))
{
int i;
for (i = 0; (ec->h & (1 << i)) == 0; i++)
mpi_clear_bit (data, i);
mpi_set_highbit (data, ec->nbits - 1);
}
if (DBG_CIPHER)
log_mpidump ("ecc_encrypt data", data);
if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n || !ec->Q)
{
rc = GPG_ERR_NO_OBJ;
goto leave;
}
if ((ctx.flags & PUBKEY_FLAG_SM2))
{
/* All encryption will be done, return it. */
rc = _gcry_ecc_sm2_encrypt (r_ciph, data, ec);
goto leave;
}
/* The following is false: assert( mpi_cmp_ui( R.x, 1 )==0 );, so */
{
mpi_point_struct R; /* Result that we return. */
gcry_mpi_t x, y;
unsigned char *rawmpi;
unsigned int rawmpilen;
rc = 0;
x = mpi_new (0);
if (ec->model == MPI_EC_MONTGOMERY)
y = NULL;
else
y = mpi_new (0);
point_init (&R);
/* R = kQ <=> R = kdG */
_gcry_mpi_ec_mul_point (&R, data, ec->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, return the result of 0 not raising an error.
*
* This is a corner case. It never occurs with properly
* generated public keys, but it might happen with blindly
* imported public key which might not follow the key
* generation procedure.
*/
if (!no_error_on_infinity)
{ /* It's not for X25519, then, the input data was simply wrong. */
rc = GPG_ERR_INV_DATA;
goto leave_main;
}
}
if (y)
mpi_s = _gcry_ecc_ec2os (x, y, ec->p);
else
{
rc = _gcry_ecc_mont_encodepoint (x, nbits,
ec->dialect != ECC_DIALECT_SAFECURVE,
&rawmpi, &rawmpilen);
if (rc)
goto leave_main;
mpi_s = mpi_new (0);
mpi_set_opaque (mpi_s, rawmpi, rawmpilen*8);
}
/* R = kG */
_gcry_mpi_ec_mul_point (&R, data, ec->G, ec);
if (_gcry_mpi_ec_get_affine (x, y, &R, ec))
{
rc = GPG_ERR_INV_DATA;
goto leave_main;
}
if (y)
mpi_e = _gcry_ecc_ec2os (x, y, ec->p);
else
{
rc = _gcry_ecc_mont_encodepoint (x, nbits,
ec->dialect != ECC_DIALECT_SAFECURVE,
&rawmpi, &rawmpilen);
if (!rc)
{
mpi_e = mpi_new (0);
mpi_set_opaque (mpi_e, rawmpi, rawmpilen*8);
}
}
leave_main:
mpi_free (x);
mpi_free (y);
point_free (&R);
if (rc)
goto leave;
}
if (!rc)
rc = sexp_build (r_ciph, NULL, "(enc-val(ecdh(s%m)(e%m)))", mpi_s, mpi_e);
leave:
_gcry_mpi_release (data);
_gcry_mpi_release (mpi_s);
_gcry_mpi_release (mpi_e);
_gcry_mpi_ec_free (ec);
_gcry_pk_util_free_encoding_ctx (&ctx);
if (DBG_CIPHER)
log_debug ("ecc_encrypt => %s\n", gpg_strerror (rc));
return rc;
}
/* input:
* data[0] : a point kG (ephemeral public key)
* output:
* resaddr[0] : shared point kdG
*
* see ecc_encrypt_raw for details.
*/
static gcry_err_code_t
ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
{
unsigned int nbits;
gpg_err_code_t rc;
struct pk_encoding_ctx ctx;
gcry_sexp_t l1 = NULL;
gcry_mpi_t data_e = NULL;
mpi_ec_t ec = NULL;
mpi_point_struct kG;
mpi_point_struct R;
gcry_mpi_t r = NULL;
int flags = 0;
int enable_specific_point_validation;
point_init (&kG);
point_init (&R);
_gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_DECRYPT,
(nbits = ecc_get_nbits (keyparms)));
/*
* Extract the key.
*/
rc = _gcry_mpi_ec_internal_new (&ec, &flags, "ecc_decrypt", keyparms, NULL);
if (rc)
goto leave;
if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n || !ec->d)
{
rc = GPG_ERR_NO_OBJ;
goto leave;
}
/*
* Extract the data.
*/
rc = _gcry_pk_util_preparse_encval (s_data, ecc_names, &l1, &ctx);
if (rc)
goto leave;
if ((ctx.flags & PUBKEY_FLAG_SM2))
{
/* All decryption will be done, return it. */
rc = _gcry_ecc_sm2_decrypt (r_plain, l1, ec);
goto leave;
}
else
{
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 (ec->dialect == ECC_DIALECT_SAFECURVE || (flags & PUBKEY_FLAG_DJB_TWEAK))
enable_specific_point_validation = 1;
else
enable_specific_point_validation = 0;
/*
* Compute the plaintext.
*/
if (ec->model == MPI_EC_MONTGOMERY)
rc = _gcry_ecc_mont_decodepoint (data_e, ec, &kG);
else
rc = _gcry_ecc_sec_decodepoint (data_e, ec, &kG);
if (rc)
goto leave;
if (DBG_CIPHER)
log_printpnt ("ecc_decrypt kG", &kG, NULL);
if (enable_specific_point_validation)
{
/* For X25519, by its definition, validation should not be done. */
/* (Instead, we do output check.)
*
* However, to mitigate secret key leak from our implementation,
* we also do input validation here. For constant-time
* implementation, we can remove this input validation.
*/
if (_gcry_mpi_ec_bad_point (&kG, ec))
{
rc = GPG_ERR_INV_DATA;
goto leave;
}
}
else if (!_gcry_mpi_ec_curve_point (&kG, ec))
{
rc = GPG_ERR_INV_DATA;
goto leave;
}
/* R = dkG */
_gcry_mpi_ec_mul_point (&R, ec->d, &kG, ec);
/* The following is false: assert( mpi_cmp_ui( R.x, 1 )==0 );, so: */
{
gcry_mpi_t x, y;
x = mpi_new (0);
if (ec->model == MPI_EC_MONTGOMERY)
y = NULL;
else
y = mpi_new (0);
if (_gcry_mpi_ec_get_affine (x, y, &R, ec))
{
rc = GPG_ERR_INV_DATA;
goto leave;
/*
* Note for X25519.
*
* By the definition of X25519, this is the case where X25519
* returns 0, mapping infinity to zero. However, we
* deliberately let it return an error.
*
* For X25519 ECDH, 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 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.
*/
}
if (y)
r = _gcry_ecc_ec2os (x, y, ec->p);
else
{
unsigned char *rawmpi;
unsigned int rawmpilen;
rc = _gcry_ecc_mont_encodepoint (x, nbits,
ec->dialect != ECC_DIALECT_SAFECURVE,
&rawmpi, &rawmpilen);
if (rc)
goto leave;
r = mpi_new (0);
mpi_set_opaque (r, rawmpi, rawmpilen*8);
}
if (!r)
rc = gpg_err_code_from_syserror ();
else
rc = 0;
mpi_free (x);
mpi_free (y);
}
if (DBG_CIPHER)
log_printmpi ("ecc_decrypt res", r);
if (!rc)
rc = sexp_build (r_plain, NULL, "(value %m)", r);
leave:
point_free (&R);
point_free (&kG);
_gcry_mpi_release (r);
_gcry_mpi_release (data_e);
sexp_release (l1);
_gcry_mpi_ec_free (ec);
_gcry_pk_util_free_encoding_ctx (&ctx);
if (DBG_CIPHER)
log_debug ("ecc_decrypt => %s\n", gpg_strerror (rc));
return rc;
}
/* Return the number of bits for the key described by PARMS. On error
* 0 is returned. The format of PARMS starts with the algorithm name;
* for example:
*
* (ecc
* (curve <name>)
* (p <mpi>)
* (a <mpi>)
* (b <mpi>)
* (g <mpi>)
* (n <mpi>)
* (q <mpi>))
*
* More parameters may be given. Either P or CURVE is needed.
*/
static unsigned int
ecc_get_nbits (gcry_sexp_t parms)
{
gcry_sexp_t l1;
gcry_mpi_t p;
unsigned int nbits = 0;
char *curve;
l1 = sexp_find_token (parms, "p", 1);
if (!l1)
{ /* Parameter P not found - check whether we have "curve". */
l1 = sexp_find_token (parms, "curve", 5);
if (!l1)
return 0; /* Neither P nor CURVE found. */
curve = sexp_nth_string (l1, 1);
sexp_release (l1);
if (!curve)
return 0; /* No curve name given (or out of core). */
if (_gcry_ecc_fill_in_curve (0, curve, NULL, &nbits))
nbits = 0;
xfree (curve);
}
else
{
p = sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
sexp_release (l1);
if (p)
{
nbits = mpi_get_nbits (p);
_gcry_mpi_release (p);
}
}
return nbits;
}
/* See rsa.c for a description of this function. */
static gpg_err_code_t
compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparms)
{
#define N_COMPONENTS 6
static const char names[N_COMPONENTS] = "pabgnq";
gpg_err_code_t rc;
gcry_sexp_t l1;
gcry_mpi_t values[N_COMPONENTS];
int idx;
char *curvename = NULL;
int flags = 0;
enum gcry_mpi_ec_models model = 0;
enum ecc_dialects dialect = 0;
const unsigned char *raw;
unsigned int n;
int maybe_uncompress;
/* Clear the values first. */
for (idx=0; idx < N_COMPONENTS; idx++)
values[idx] = NULL;
/* Look for flags. */
l1 = sexp_find_token (keyparms, "flags", 0);
if (l1)
{
rc = _gcry_pk_util_parse_flaglist (l1, &flags, NULL);
if (rc)
goto leave;
}
/* Extract the parameters. */
if ((flags & PUBKEY_FLAG_PARAM))
rc = sexp_extract_param (keyparms, NULL, "p?a?b?g?n?/q",
&values[0], &values[1], &values[2],
&values[3], &values[4], &values[5],
NULL);
else
rc = sexp_extract_param (keyparms, NULL, "/q", &values[5], NULL);
if (rc)
goto leave;
/* Check whether a curve parameter is available and use that to fill
in missing values. */
sexp_release (l1);
l1 = sexp_find_token (keyparms, "curve", 5);
if (l1)
{
curvename = sexp_nth_string (l1, 1);
if (curvename)
{
rc = _gcry_ecc_update_curve_param (curvename,
&model, &dialect,
&values[0], &values[1], &values[2],
&values[3], &values[4]);
if (rc)
goto leave;
}
}
/* Guess required fields if a curve parameter has not been given.
FIXME: This is a crude hacks. We need to fix that. */
if (!curvename)
{
model = ((flags & PUBKEY_FLAG_EDDSA)
? MPI_EC_EDWARDS
: MPI_EC_WEIERSTRASS);
dialect = ((flags & PUBKEY_FLAG_EDDSA)
? ECC_DIALECT_ED25519
: ECC_DIALECT_STANDARD);
}
/* Check that all parameters are known and normalize all MPIs (that
should not be required but we use an internal function later and
thus we better make 100% sure that they are normalized). */
for (idx = 0; idx < N_COMPONENTS; idx++)
if (!values[idx])
{
rc = GPG_ERR_NO_OBJ;
goto leave;
}
else
_gcry_mpi_normalize (values[idx]);
/* Uncompress the public key with the exception of EdDSA where
compression is the default and we thus compute the keygrip using
the compressed version. Because we don't support any non-eddsa
compression, the only thing we need to do is to compress
EdDSA. */
if ((flags & PUBKEY_FLAG_EDDSA) && dialect == ECC_DIALECT_ED25519)
{
const unsigned int pbits = mpi_get_nbits (values[0]);
rc = _gcry_ecc_eddsa_ensure_compact (values[5], pbits);
if (rc)
goto leave;
maybe_uncompress = 0;
}
else if ((flags & PUBKEY_FLAG_DJB_TWEAK))
{
/* Remove the prefix 0x40 for keygrip computation. */
raw = mpi_get_opaque (values[5], &n);
if (raw)
{
n = (n + 7)/8;
if (n > 1 && (n%2) && raw[0] == 0x40)
if (!_gcry_mpi_set_opaque_copy (values[5], raw + 1, (n - 1)*8))
rc = gpg_err_code_from_syserror ();
}
else
{
rc = GPG_ERR_INV_OBJ;
goto leave;
}
maybe_uncompress = 0;
}
else
maybe_uncompress = 1;
/* Hash them all. */
for (idx = 0; idx < N_COMPONENTS; idx++)
{
char buf[30];
unsigned char *rawbuffer;
unsigned int rawlen;
if (mpi_is_opaque (values[idx]))
{
rawbuffer = NULL;
raw = mpi_get_opaque (values[idx], &rawlen);
rawlen = (rawlen + 7)/8;
}
else
{
rawbuffer = _gcry_mpi_get_buffer (values[idx], 0, &rawlen, NULL);
if (!rawbuffer)
{
rc = gpg_err_code_from_syserror ();
goto leave;
}
raw = rawbuffer;
}
if (maybe_uncompress && idx == 5 && rawlen > 1
&& (*raw == 0x02 || *raw == 0x03))
{
/* This is a compressed Q - uncompress. */
mpi_ec_t ec = NULL;
gcry_mpi_t x, y;
gcry_mpi_t x3;
gcry_mpi_t t;
gcry_mpi_t p1_4;
int y_bit = (*raw == 0x03);
/* We need to get the curve parameters as MPIs so that we
* can do computations. We have them in VALUES but it is
* possible that the caller provided them as opaque MPIs. */
rc = _gcry_mpi_ec_internal_new (&ec, &flags, "ecc_keygrip",
keyparms, NULL);
if (rc)
goto leave;
if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n)
{
rc = GPG_ERR_NO_OBJ;
_gcry_mpi_ec_free (ec);
goto leave;
}
if (!mpi_test_bit (ec->p, 1))
{
/* No support for point compression for this curve. */
rc = GPG_ERR_NOT_IMPLEMENTED;
_gcry_mpi_ec_free (ec);
xfree (rawbuffer);
goto leave;
}
raw++;
rawlen--;
rc = _gcry_mpi_scan (&x, GCRYMPI_FMT_USG, raw, rawlen, NULL);
if (rc)
{
_gcry_mpi_ec_free (ec);
xfree (rawbuffer);
goto leave;
}
/*
* Recover Y. The Weierstrass curve: y^2 = x^3 + a*x + b
*/
x3 = mpi_new (0);
t = mpi_new (0);
p1_4 = mpi_new (0);
y = mpi_new (0);
/* Compute right hand side. */
mpi_powm (x3, x, mpi_const (MPI_C_THREE), ec->p);
mpi_mul (t, ec->a, x);
mpi_mod (t, t, ec->p);
mpi_add (t, t, ec->b);
mpi_mod (t, t, ec->p);
mpi_add (t, t, x3);
mpi_mod (t, t, ec->p);
/*
* When p mod 4 = 3, modular square root of A can be computed by
* A^((p+1)/4) mod p
*/
/* Compute (p+1)/4 into p1_4 */
mpi_rshift (p1_4, ec->p, 2);
_gcry_mpi_add_ui (p1_4, p1_4, 1);
mpi_powm (y, t, p1_4, ec->p);
if (y_bit != mpi_test_bit (y, 0))
mpi_sub (y, ec->p, y);
mpi_free (p1_4);
mpi_free (t);
mpi_free (x3);
xfree (rawbuffer);
rawbuffer = _gcry_ecc_ec2os_buf (x, y, ec->p, &rawlen);
raw = rawbuffer;
mpi_free (x);
mpi_free (y);
_gcry_mpi_ec_free (ec);
}
snprintf (buf, sizeof buf, "(1:%c%u:", names[idx], rawlen);
_gcry_md_write (md, buf, strlen (buf));
_gcry_md_write (md, raw, rawlen);
_gcry_md_write (md, ")", 1);
xfree (rawbuffer);
}
leave:
xfree (curvename);
sexp_release (l1);
for (idx = 0; idx < N_COMPONENTS; idx++)
_gcry_mpi_release (values[idx]);
return rc;
#undef N_COMPONENTS
}
/*
Low-level API helper functions.
*/
/* This is the worker function for gcry_pubkey_get_sexp for ECC
algorithms. Note that the caller has already stored NULL at
R_SEXP. */
gpg_err_code_t
_gcry_pk_ecc_get_sexp (gcry_sexp_t *r_sexp, int mode, mpi_ec_t ec)
{
gpg_err_code_t rc;
gcry_mpi_t mpi_G = NULL;
gcry_mpi_t mpi_Q = NULL;
if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n)
return GPG_ERR_BAD_CRYPT_CTX;
if (mode == GCRY_PK_GET_SECKEY && !ec->d)
return GPG_ERR_NO_SECKEY;
/* Compute the public point if it is missing. */
if (!ec->Q && ec->d)
ec->Q = _gcry_ecc_compute_public (NULL, ec);
/* Encode G and Q. */
mpi_G = _gcry_mpi_ec_ec2os (ec->G, ec);
if (!mpi_G)
{
rc = GPG_ERR_BROKEN_PUBKEY;
goto leave;
}
if (!ec->Q)
{
rc = GPG_ERR_BAD_CRYPT_CTX;
goto leave;
}
if (ec->dialect == ECC_DIALECT_ED25519)
{
unsigned char *encpk;
unsigned int encpklen;
rc = _gcry_ecc_eddsa_encodepoint (ec->Q, ec, NULL, NULL, 0,
&encpk, &encpklen);
if (rc)
goto leave;
mpi_Q = mpi_set_opaque (NULL, encpk, encpklen*8);
encpk = NULL;
}
else if (ec->model == MPI_EC_MONTGOMERY)
{
unsigned char *encpk;
unsigned int encpklen;
rc = _gcry_ecc_mont_encodepoint (ec->Q->x, ec->nbits,
ec->dialect != ECC_DIALECT_SAFECURVE,
&encpk, &encpklen);
if (rc)
goto leave;
mpi_Q = mpi_set_opaque (NULL, encpk, encpklen*8);
}
else
{
mpi_Q = _gcry_mpi_ec_ec2os (ec->Q, ec);
}
if (!mpi_Q)
{
rc = GPG_ERR_BROKEN_PUBKEY;
goto leave;
}
/* Fixme: We should return a curve name instead of the parameters if
if know that they match a curve. */
if (ec->d && (!mode || mode == GCRY_PK_GET_SECKEY))
{
/* Let's return a private key. */
rc = sexp_build (r_sexp, NULL,
"(private-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(h%u)(q%m)(d%m)))",
ec->p, ec->a, ec->b, mpi_G, ec->n, ec->h, mpi_Q, ec->d);
}
else if (ec->Q)
{
/* Let's return a public key. */
rc = sexp_build (r_sexp, NULL,
"(public-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)(h%u)(q%m)))",
ec->p, ec->a, ec->b, mpi_G, ec->n, ec->h, mpi_Q);
}
else
rc = GPG_ERR_BAD_CRYPT_CTX;
leave:
mpi_free (mpi_Q);
mpi_free (mpi_G);
return rc;
}
/*
Self-test section.
*/
static const char *
selftest_hash_sign (gcry_sexp_t pkey, gcry_sexp_t skey, const char *tmpl,
const char *input_str, const char *input_bad_str,
const char *signature_r, const char *signature_s)
{
int md_algo = GCRY_MD_SHA256;
gcry_md_hd_t hd = NULL;
const char *errtxt = NULL;
gcry_error_t err;
gcry_sexp_t sig = NULL;
gcry_sexp_t l1 = NULL;
gcry_sexp_t l2 = NULL;
gcry_mpi_t r = NULL;
gcry_mpi_t s = NULL;
gcry_mpi_t calculated_r = NULL;
gcry_mpi_t calculated_s = NULL;
int cmp;
err = _gcry_md_open (&hd, md_algo, 0);
if (err)
{
errtxt = "gcry_md_open failed";
goto leave;
}
_gcry_md_write (hd, input_str, strlen (input_str));
err = _gcry_mpi_scan (&r, GCRYMPI_FMT_HEX, signature_r, 0, NULL);
if (!err)
err = _gcry_mpi_scan (&s, GCRYMPI_FMT_HEX, signature_s, 0, NULL);
if (err)
{
errtxt = "converting data failed";
goto leave;
}
err = _gcry_pk_sign_md (&sig, tmpl, hd, skey, NULL);
if (err)
{
errtxt = "signing failed";
goto leave;
}
/* check against known signature */
errtxt = "signature validity failed";
l1 = _gcry_sexp_find_token (sig, "sig-val", 0);
if (!l1)
goto leave;
/* Here, we have the ECC name like: "ecdsa", "eddsa"...,
But we skip parsing the name. */
l2 = _gcry_sexp_find_token (l1, "r", 0);
if (!l2)
goto leave;
calculated_r = _gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
if (!calculated_r)
goto leave;
sexp_release (l2);
l2 = _gcry_sexp_find_token (l1, "s", 0);
if (!l2)
goto leave;
calculated_s = _gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
if (!calculated_s)
goto leave;
errtxt = "known sig check failed";
cmp = _gcry_mpi_cmp (r, calculated_r);
if (cmp)
goto leave;
cmp = _gcry_mpi_cmp (s, calculated_s);
if (cmp)
goto leave;
errtxt = NULL;
/* verify generated signature */
err = _gcry_pk_verify_md (sig, tmpl, hd, pkey, NULL);
if (err)
{
errtxt = "verify failed";
goto leave;
}
_gcry_md_reset(hd);
_gcry_md_write (hd, input_bad_str, strlen (input_bad_str));
err = _gcry_pk_verify_md (sig, tmpl, hd, pkey, NULL);
if (gcry_err_code (err) != GPG_ERR_BAD_SIGNATURE)
{
errtxt = "bad signature not detected";
goto leave;
}
leave:
_gcry_md_close (hd);
sexp_release (sig);
sexp_release (l1);
sexp_release (l2);
mpi_release (r);
mpi_release (s);
mpi_release (calculated_r);
mpi_release (calculated_s);
return errtxt;
}
static const char *
selftest_hash_sign_eddsa (gcry_sexp_t pkey, gcry_sexp_t skey, const char *tmpl,
const char *input_str, const char *input_bad_str,
const char *signature_r, const char *signature_s)
{
gcry_ctx_t ctx = NULL;
const char *errtxt = NULL;
gcry_error_t err;
gcry_sexp_t sig = NULL;
gcry_sexp_t l1 = NULL;
gcry_sexp_t l2 = NULL;
unsigned char *r = NULL;
unsigned char *s = NULL;
size_t r_len, s_len;
unsigned char *calculated_r = NULL;
unsigned char *calculated_s = NULL;
size_t calculated_r_len, calculated_s_len;
err = _gcry_pk_single_data_push (&ctx, (void *)input_str, strlen (input_str));
if (err)
{
errtxt ="error setting input data";
goto leave;
}
r = _gcry_hex2buffer (signature_r, &r_len);
s = _gcry_hex2buffer (signature_s, &s_len);
if (!r || !s)
{
errtxt = "converting data failed";
goto leave;
}
err = _gcry_pk_sign_md (&sig, tmpl, NULL, skey, ctx);
if (err)
{
errtxt = "signing failed";
goto leave;
}
/* check against known signature */
errtxt = "signature validity failed";
l1 = _gcry_sexp_find_token (sig, "sig-val", 0);
if (!l1)
goto leave;
/* Here, we have the ECC name like: "ecdsa", "eddsa"...,
But we skip parsing the name. */
l2 = _gcry_sexp_find_token (l1, "r", 0);
if (!l2)
goto leave;
calculated_r = _gcry_sexp_nth_buffer (l2, 1, &calculated_r_len);
if (!calculated_r)
goto leave;
sexp_release (l2);
l2 = _gcry_sexp_find_token (l1, "s", 0);
if (!l2)
goto leave;
calculated_s = _gcry_sexp_nth_buffer (l2, 1, &calculated_s_len);
if (!calculated_s)
goto leave;
errtxt = "known sig check failed";
if (r_len != calculated_r_len)
goto leave;
if (s_len != calculated_s_len)
goto leave;
if (memcmp (r, calculated_r, r_len))
goto leave;
if (memcmp (s, calculated_s, s_len))
goto leave;
errtxt = NULL;
/* verify generated signature */
err = _gcry_pk_verify_md (sig, tmpl, NULL, pkey, ctx);
if (err)
{
errtxt = "verify failed";
goto leave;
}
_gcry_ctx_release (ctx);
ctx = NULL;
err = _gcry_pk_single_data_push (&ctx, (void *)input_bad_str,
strlen (input_bad_str));
if (err)
{
errtxt ="error setting input data";
goto leave;
}
err = _gcry_pk_verify_md (sig, tmpl, NULL, pkey, ctx);
if (gcry_err_code (err) != GPG_ERR_BAD_SIGNATURE)
{
errtxt = "bad signature not detected";
goto leave;
}
leave:
_gcry_ctx_release (ctx);
sexp_release (sig);
sexp_release (l1);
sexp_release (l2);
xfree (r);
xfree (s);
xfree (calculated_r);
xfree (calculated_s);
return errtxt;
}
static const char *
selftest_sign (gcry_sexp_t pkey, gcry_sexp_t skey,
const char *input, const char *input_bad,
const char *signature_r, const char *signature_s)
{
const char *errtxt = NULL;
gcry_error_t err;
gcry_sexp_t data = NULL;
gcry_sexp_t data_bad = NULL;
gcry_sexp_t sig = NULL;
gcry_sexp_t l1 = NULL;
gcry_sexp_t l2 = NULL;
gcry_mpi_t r = NULL;
gcry_mpi_t s = NULL;
gcry_mpi_t calculated_r = NULL;
gcry_mpi_t calculated_s = NULL;
int cmp;
err = sexp_sscan (&data, NULL, input, strlen (input));
if (!err)
err = sexp_sscan (&data_bad, NULL,
input_bad, strlen (input_bad));
if (!err)
err = _gcry_mpi_scan (&r, GCRYMPI_FMT_HEX, signature_r, 0, NULL);
if (!err)
err = _gcry_mpi_scan (&s, GCRYMPI_FMT_HEX, signature_s, 0, NULL);
if (err)
{
errtxt = "converting data failed";
goto leave;
}
err = _gcry_pk_sign (&sig, data, skey);
if (err)
{
errtxt = "signing failed";
goto leave;
}
/* check against known signature */
errtxt = "signature validity failed";
l1 = _gcry_sexp_find_token (sig, "sig-val", 0);
if (!l1)
goto leave;
/* Here, we have the ECC name like: "ecdsa", "eddsa"...,
But we skip parsing the name. */
l2 = _gcry_sexp_find_token (l1, "r", 0);
if (!l2)
goto leave;
calculated_r = _gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
if (!calculated_r)
goto leave;
sexp_release (l2);
l2 = _gcry_sexp_find_token (l1, "s", 0);
if (!l2)
goto leave;
calculated_s = _gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
if (!calculated_s)
goto leave;
errtxt = "known sig check failed";
cmp = _gcry_mpi_cmp (r, calculated_r);
if (cmp)
goto leave;
cmp = _gcry_mpi_cmp (s, calculated_s);
if (cmp)
goto leave;
errtxt = NULL;
/* verify generated signature */
err = _gcry_pk_verify (sig, data, pkey);
if (err)
{
errtxt = "verify failed";
goto leave;
}
err = _gcry_pk_verify (sig, data_bad, pkey);
if (gcry_err_code (err) != GPG_ERR_BAD_SIGNATURE)
{
errtxt = "bad signature not detected";
goto leave;
}
leave:
sexp_release (sig);
sexp_release (data_bad);
sexp_release (data);
sexp_release (l1);
sexp_release (l2);
mpi_release (r);
mpi_release (s);
mpi_release (calculated_r);
mpi_release (calculated_s);
return errtxt;
}
static gpg_err_code_t
selftests_ecc (selftest_report_func_t report, int extended, int is_eddsa,
const char *secret_key, const char *public_key,
const char *input, const char *input_bad,
const char *tmpl,
const char *input_str, const char *input_bad_str,
const char *signature_r, const char *signature_s)
{
const char *what;
const char *errtxt;
gcry_error_t err;
gcry_sexp_t skey = NULL;
gcry_sexp_t pkey = NULL;
what = "convert";
err = sexp_sscan (&skey, NULL, secret_key, strlen (secret_key));
if (!err)
err = sexp_sscan (&pkey, NULL, public_key, strlen (public_key));
if (err)
{
errtxt = _gcry_strerror (err);
goto failed;
}
what = "key consistency";
err = ecc_check_secret_key (skey);
if (err)
{
errtxt = _gcry_strerror (err);
goto failed;
}
if (extended)
{
what = "sign";
errtxt = selftest_sign (pkey, skey, input, input_bad,
signature_r, signature_s);
if (errtxt)
goto failed;
}
what = "digest sign";
if (is_eddsa)
errtxt = selftest_hash_sign_eddsa (pkey, skey, tmpl,
input_str, input_bad_str,
signature_r, signature_s);
else
errtxt = selftest_hash_sign (pkey, skey, tmpl,
input_str, input_bad_str,
signature_r, signature_s);
if (errtxt)
goto failed;
sexp_release(pkey);
sexp_release(skey);
return 0; /* Succeeded. */
failed:
sexp_release(pkey);
sexp_release(skey);
if (report)
report ("pubkey", GCRY_PK_ECC, what, errtxt);
return GPG_ERR_SELFTEST_FAILED;
}
/* Run a full self-test for ALGO and return 0 on success. */
static gpg_err_code_t
run_selftests (int algo, int extended, selftest_report_func_t report)
{
int r;
if (algo != GCRY_PK_ECC)
return GPG_ERR_PUBKEY_ALGO;
r = selftests_ecc (report, extended, 0,
ecdsa_sample_secret_key_secp256,
ecdsa_sample_public_key_secp256,
ecdsa_sample_data, ecdsa_sample_data_bad,
ecdsa_data_tmpl,
ecdsa_sample_data_string, ecdsa_sample_data_bad_string,
ecdsa_signature_r, ecdsa_signature_s);
if (r)
return r;
r = selftests_ecc (report, extended, 1,
ed25519_sample_secret_key,
ed25519_sample_public_key,
ed25519_sample_data, ed25519_sample_data_bad,
ed25519_data_tmpl,
ed25519_sample_data_string, ed25519_sample_data_bad_string,
ed25519_signature_r, ed25519_signature_s);
if (r)
return r;
r = selftests_ecc (report, extended, 1,
ed448_sample_secret_key,
ed448_sample_public_key,
ed448_sample_data, ed448_sample_data_bad,
ed448_data_tmpl,
ed448_sample_data_string, ed448_sample_data_bad_string,
ed448_signature_r, ed448_signature_s);
return r;
}
gcry_pk_spec_t _gcry_pubkey_spec_ecc =
{
GCRY_PK_ECC, { 0, 1 },
(GCRY_PK_USAGE_SIGN | GCRY_PK_USAGE_ENCR),
"ECC", ecc_names,
"pabgnhq", "pabgnhqd", "se", "rs", "pabgnhq",
ecc_generate,
ecc_check_secret_key,
ecc_encrypt_raw,
ecc_decrypt_raw,
ecc_sign,
ecc_verify,
ecc_get_nbits,
run_selftests,
compute_keygrip,
_gcry_ecc_get_curve,
_gcry_ecc_get_param_sexp
};
diff --git a/cipher/pubkey.c b/cipher/pubkey.c
index 4d7743cc..aacf9f5a 100644
--- a/cipher/pubkey.c
+++ b/cipher/pubkey.c
@@ -1,1277 +1,1325 @@
/* pubkey.c - pubkey dispatcher
* Copyright (C) 1998, 1999, 2000, 2002, 2003, 2005,
* 2007, 2008, 2011 Free Software Foundation, Inc.
* Copyright (C) 2013 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 "pubkey-internal.h"
/* This is the list of the public-key algorithms included in
Libgcrypt. */
static gcry_pk_spec_t * const pubkey_list[] =
{
#if USE_ECC
&_gcry_pubkey_spec_ecc,
#endif
#if USE_RSA
&_gcry_pubkey_spec_rsa,
#endif
#if USE_DSA
&_gcry_pubkey_spec_dsa,
#endif
#if USE_ELGAMAL
&_gcry_pubkey_spec_elg,
#endif
&_gcry_pubkey_spec_kem,
NULL
};
static int
map_algo (int algo)
{
switch (algo)
{
case GCRY_PK_RSA_E: return GCRY_PK_RSA;
case GCRY_PK_RSA_S: return GCRY_PK_RSA;
case GCRY_PK_ELG_E: return GCRY_PK_ELG;
case GCRY_PK_ECDSA: return GCRY_PK_ECC;
case GCRY_PK_EDDSA: return GCRY_PK_ECC;
case GCRY_PK_ECDH: return GCRY_PK_ECC;
default: return algo;
}
}
/* Return the spec structure for the public key algorithm ALGO. For
an unknown algorithm NULL is returned. */
static gcry_pk_spec_t *
spec_from_algo (int algo)
{
int idx;
gcry_pk_spec_t *spec;
algo = map_algo (algo);
for (idx = 0; (spec = pubkey_list[idx]); idx++)
if (algo == spec->algo)
return spec;
return NULL;
}
/* Return the spec structure for the public key algorithm with NAME.
For an unknown name NULL is returned. */
static gcry_pk_spec_t *
spec_from_name (const char *name)
{
gcry_pk_spec_t *spec;
int idx;
const char **aliases;
for (idx=0; (spec = pubkey_list[idx]); idx++)
{
if (!stricmp (name, spec->name))
return spec;
for (aliases = spec->aliases; *aliases; aliases++)
if (!stricmp (name, *aliases))
return spec;
}
return NULL;
}
/* Given the s-expression SEXP with the first element be either
* "private-key" or "public-key" return the spec structure for it. We
* look through the list to find a list beginning with "private-key"
* or "public-key" - the first one found is used. If WANT_PRIVATE is
* set the function will only succeed if a private key has been given.
* On success the spec is stored at R_SPEC. On error NULL is stored
* at R_SPEC and an error code returned. If R_PARMS is not NULL and
* the function returns success, the parameter list below
* "private-key" or "public-key" is stored there and the caller must
* call gcry_sexp_release on it.
*/
static gcry_err_code_t
spec_from_sexp (gcry_sexp_t sexp, int want_private,
gcry_pk_spec_t **r_spec, gcry_sexp_t *r_parms)
{
gcry_sexp_t list, l2;
char *name;
gcry_pk_spec_t *spec;
*r_spec = NULL;
if (r_parms)
*r_parms = NULL;
/* Check that the first element is valid. If we are looking for a
public key but a private key was supplied, we allow the use of
the private key anyway. The rationale for this is that the
private key is a superset of the public key. */
list = sexp_find_token (sexp, want_private? "private-key":"public-key", 0);
if (!list && !want_private)
list = sexp_find_token (sexp, "private-key", 0);
if (!list)
return GPG_ERR_INV_OBJ; /* Does not contain a key object. */
l2 = sexp_cadr (list);
sexp_release (list);
list = l2;
name = sexp_nth_string (list, 0);
if (!name)
{
sexp_release ( list );
return GPG_ERR_INV_OBJ; /* Invalid structure of object. */
}
spec = spec_from_name (name);
xfree (name);
if (!spec)
{
sexp_release (list);
return GPG_ERR_PUBKEY_ALGO; /* Unknown algorithm. */
}
*r_spec = spec;
if (r_parms)
*r_parms = list;
else
sexp_release (list);
return 0;
}
/* Disable the use of the algorithm ALGO. This is not thread safe and
should thus be called early. */
static void
disable_pubkey_algo (int algo)
{
gcry_pk_spec_t *spec = spec_from_algo (algo);
if (spec)
spec->flags.disabled = 1;
}
/*
* Map a string to the pubkey algo
*/
int
_gcry_pk_map_name (const char *string)
{
gcry_pk_spec_t *spec;
if (!string)
return 0;
spec = spec_from_name (string);
if (!spec)
return 0;
if (spec->flags.disabled)
return 0;
if (!spec->flags.fips && fips_mode ())
return 0;
return spec->algo;
}
/* Map the public key algorithm whose ID is contained in ALGORITHM to
a string representation of the algorithm name. For unknown
algorithm IDs this functions returns "?". */
const char *
_gcry_pk_algo_name (int algo)
{
gcry_pk_spec_t *spec;
spec = spec_from_algo (algo);
if (spec)
return spec->name;
return "?";
}
/****************
* A USE of 0 means: don't care.
*/
static gcry_err_code_t
check_pubkey_algo (int algo, unsigned use)
{
gcry_err_code_t err = 0;
gcry_pk_spec_t *spec;
spec = spec_from_algo (algo);
if (spec && !spec->flags.disabled && (spec->flags.fips || !fips_mode ()))
{
if (((use & GCRY_PK_USAGE_SIGN)
&& (! (spec->use & GCRY_PK_USAGE_SIGN)))
|| ((use & GCRY_PK_USAGE_ENCR)
&& (! (spec->use & GCRY_PK_USAGE_ENCR))))
err = GPG_ERR_WRONG_PUBKEY_ALGO;
}
else
err = GPG_ERR_PUBKEY_ALGO;
return err;
}
/****************
* Return the number of public key material numbers
*/
static int
pubkey_get_npkey (int algo)
{
gcry_pk_spec_t *spec = spec_from_algo (algo);
return spec? strlen (spec->elements_pkey) : 0;
}
/****************
* Return the number of secret key material numbers
*/
static int
pubkey_get_nskey (int algo)
{
gcry_pk_spec_t *spec = spec_from_algo (algo);
return spec? strlen (spec->elements_skey) : 0;
}
/****************
* Return the number of signature material numbers
*/
static int
pubkey_get_nsig (int algo)
{
gcry_pk_spec_t *spec = spec_from_algo (algo);
return spec? strlen (spec->elements_sig) : 0;
}
/****************
* Return the number of encryption material numbers
*/
static int
pubkey_get_nenc (int algo)
{
gcry_pk_spec_t *spec = spec_from_algo (algo);
return spec? strlen (spec->elements_enc) : 0;
}
/*
Do a PK encrypt operation
Caller has to provide a public key as the SEXP pkey and data as a
SEXP with just one MPI in it. Alternatively S_DATA might be a
complex S-Expression, similar to the one used for signature
verification. This provides a flag which allows to handle PKCS#1
block type 2 padding. The function returns a sexp which may be
passed to to pk_decrypt.
Returns: 0 or an errorcode.
s_data = See comment for _gcry_pk_util_data_to_mpi
s_pkey = <key-as-defined-in-sexp_to_key>
r_ciph = (enc-val
(<algo>
(<param_name1> <mpi>)
...
(<param_namen> <mpi>)
))
*/
gcry_err_code_t
_gcry_pk_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t s_pkey)
{
gcry_err_code_t rc;
gcry_pk_spec_t *spec;
gcry_sexp_t keyparms;
*r_ciph = NULL;
rc = spec_from_sexp (s_pkey, 0, &spec, &keyparms);
if (rc)
goto leave;
if (spec->flags.disabled)
rc = GPG_ERR_PUBKEY_ALGO;
else if (!spec->flags.fips && fips_mode ())
- rc = GPG_ERR_PUBKEY_ALGO;
+ {
+ if (fips_check_rejection (GCRY_FIPS_FLAG_REJECT_PK))
+ rc = GPG_ERR_PUBKEY_ALGO;
+ else
+ fips_service_indicator_mark_non_compliant ();
+ }
else if (spec->encrypt)
rc = spec->encrypt (r_ciph, s_data, keyparms);
else
rc = GPG_ERR_NOT_IMPLEMENTED;
leave:
sexp_release (keyparms);
return rc;
}
/*
Do a PK decrypt operation
Caller has to provide a secret key as the SEXP skey and data in a
format as created by gcry_pk_encrypt. For historic reasons the
function returns simply an MPI as an S-expression part; this is
deprecated and the new method should be used which returns a real
S-expressionl this is selected by adding at least an empty flags
list to S_DATA.
Returns: 0 or an errorcode.
s_data = (enc-val
[(flags [raw, pkcs1, oaep])]
(<algo>
(<param_name1> <mpi>)
...
(<param_namen> <mpi>)
))
s_skey = <key-as-defined-in-sexp_to_key>
r_plain= Either an incomplete S-expression without the parentheses
or if the flags list is used (even if empty) a real S-expression:
(value PLAIN). In raw mode (or no flags given) the returned value
is to be interpreted as a signed MPI, thus it may have an extra
leading zero octet even if not included in the original data.
With pkcs1 or oaep decoding enabled the returned value is a
verbatim octet string.
*/
gcry_err_code_t
_gcry_pk_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t s_skey)
{
gcry_err_code_t rc;
gcry_pk_spec_t *spec;
gcry_sexp_t keyparms;
*r_plain = NULL;
rc = spec_from_sexp (s_skey, 1, &spec, &keyparms);
if (rc)
goto leave;
if (spec->flags.disabled)
rc = GPG_ERR_PUBKEY_ALGO;
else if (!spec->flags.fips && fips_mode ())
rc = GPG_ERR_PUBKEY_ALGO;
else if (spec->decrypt)
rc = spec->decrypt (r_plain, s_data, keyparms);
else
rc = GPG_ERR_NOT_IMPLEMENTED;
leave:
sexp_release (keyparms);
return rc;
}
/*
Create a signature.
Caller has to provide a secret key as the SEXP skey and data
expressed as a SEXP list hash with only one element which should
instantly be available as a MPI. Alternatively the structure given
below may be used for S_HASH, it provides the abiliy to pass flags
to the operation; the flags defined by now are "pkcs1" which does
PKCS#1 block type 1 style padding and "pss" for PSS encoding.
Returns: 0 or an errorcode.
In case of 0 the function returns a new SEXP with the
signature value; the structure of this signature depends on the
other arguments but is always suitable to be passed to
gcry_pk_verify
s_hash = See comment for _gcry-pk_util_data_to_mpi
s_skey = <key-as-defined-in-sexp_to_key>
r_sig = (sig-val
(<algo>
(<param_name1> <mpi>)
...
(<param_namen> <mpi>))
[(hash algo)])
Note that (hash algo) in R_SIG is not used.
*/
gcry_err_code_t
_gcry_pk_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_hash, gcry_sexp_t s_skey)
{
gcry_err_code_t rc;
gcry_pk_spec_t *spec;
gcry_sexp_t keyparms;
*r_sig = NULL;
rc = spec_from_sexp (s_skey, 1, &spec, &keyparms);
if (rc)
goto leave;
if (spec->flags.disabled)
rc = GPG_ERR_PUBKEY_ALGO;
else if (!spec->flags.fips && fips_mode ())
- rc = GPG_ERR_PUBKEY_ALGO;
+ {
+ if (fips_check_rejection (GCRY_FIPS_FLAG_REJECT_PK))
+ rc = GPG_ERR_PUBKEY_ALGO;
+ else
+ fips_service_indicator_mark_non_compliant ();
+ }
else if (spec->sign)
rc = spec->sign (r_sig, s_hash, keyparms);
else
rc = GPG_ERR_NOT_IMPLEMENTED;
leave:
sexp_release (keyparms);
return rc;
}
#define MAX_CONTEXTS 2 /* Currently, input data and random_override */
static gcry_err_code_t
prepare_datasexp_to_be_signed (const char *tmpl, gcry_md_hd_t hd,
gcry_ctx_t ctx, gcry_sexp_t *s_data_p)
{
const char *s;
const char *digest_name = NULL;
const unsigned char *digest;
int digest_size;
int algo;
gcry_err_code_t rc;
if (hd == NULL)
{
const unsigned char *data[MAX_CONTEXTS];
int data_size[MAX_CONTEXTS];
int i = 0;
void *argv[MAX_CONTEXTS*2];
while (1)
{
size_t len;
rc = _gcry_pk_get_single_data (&ctx, &data[i/2], &len);
if (rc)
return rc;
data_size[i/2] = (int)len;
argv[i] = (void *)&data_size[i/2];
argv[i+1] = (void *)&data[i/2];
i += 2;
if (!ctx)
break;
if (i >= MAX_CONTEXTS*2)
return GPG_ERR_EINVAL;
}
rc = _gcry_sexp_build_array (s_data_p, NULL, tmpl, argv);
return rc;
}
/* Check if it has fixed hash name or %s */
s = strstr (tmpl, "(hash ");
if (s == NULL)
return GPG_ERR_DIGEST_ALGO;
s += 6;
if (!strncmp (s, "%s", 2))
{
algo = _gcry_md_get_algo (hd);
if (fips_mode () && algo == GCRY_MD_SHA1)
{
if (fips_check_rejection (GCRY_FIPS_FLAG_REJECT_PK))
return GPG_ERR_DIGEST_ALGO;
else
fips_service_indicator_mark_non_compliant ();
}
digest_name = _gcry_md_algo_name (algo);
digest_size = (int)_gcry_md_get_algo_dlen (algo);
digest = _gcry_md_read (hd, 0);
}
else
{
const char *p;
char *digest_name_supplied;
for (p = s; *p && *p != ' '; p++)
;
digest_name_supplied = xtrymalloc (p - s + 1);
if (!digest_name_supplied)
return gpg_error_from_syserror ();
memcpy (digest_name_supplied, s, p - s);
digest_name_supplied[p - s] = 0;
algo = _gcry_md_map_name (digest_name_supplied);
xfree (digest_name_supplied);
if (algo == 0)
{
_gcry_md_close (hd);
return GPG_ERR_DIGEST_ALGO;
}
else if (fips_mode () && algo == GCRY_MD_SHA1)
{
if (fips_check_rejection (GCRY_FIPS_FLAG_REJECT_PK))
return GPG_ERR_DIGEST_ALGO;
else
fips_service_indicator_mark_non_compliant ();
}
digest_size = (int)_gcry_md_get_algo_dlen (algo);
digest = _gcry_md_read (hd, algo);
}
if (!digest)
{
_gcry_md_close (hd);
return GPG_ERR_NOT_IMPLEMENTED;
}
if (!ctx)
{
if (!digest_name)
rc = _gcry_sexp_build (s_data_p, NULL, tmpl,
digest_size, digest);
else
rc = _gcry_sexp_build (s_data_p, NULL, tmpl, digest_name,
digest_size, digest);
}
else
{
const unsigned char *p;
size_t len;
rc = _gcry_pk_get_single_data (&ctx, &p, &len);
if (rc)
return rc;
if (!digest_name)
rc = _gcry_sexp_build (s_data_p, NULL, tmpl,
digest_size, digest, (int) len, p);
else
rc = _gcry_sexp_build (s_data_p, NULL, tmpl, digest_name,
digest_size, digest, (int) len, p);
}
_gcry_md_close (hd);
return rc;
}
gcry_err_code_t
_gcry_pk_sign_md (gcry_sexp_t *r_sig, const char *tmpl, gcry_md_hd_t hd_orig,
gcry_sexp_t s_skey, gcry_ctx_t ctx)
{
gcry_err_code_t rc;
gcry_pk_spec_t *spec;
gcry_sexp_t keyparms = NULL;
gcry_sexp_t s_data = NULL;
gcry_error_t err;
gcry_md_hd_t hd;
*r_sig = NULL;
if (!hd_orig)
hd = NULL;
else
{
err = _gcry_md_copy (&hd, hd_orig);
if (err)
return gpg_err_code (err);
}
rc = prepare_datasexp_to_be_signed (tmpl, hd, ctx, &s_data);
if (rc)
return rc;
rc = spec_from_sexp (s_skey, 1, &spec, &keyparms);
if (rc)
goto leave;
if (spec->flags.disabled)
rc = GPG_ERR_PUBKEY_ALGO;
else if (!spec->flags.fips && fips_mode ())
{
if (fips_check_rejection (GCRY_FIPS_FLAG_REJECT_PK))
rc = GPG_ERR_PUBKEY_ALGO;
else
fips_service_indicator_mark_non_compliant ();
}
else if (spec->sign)
rc = spec->sign (r_sig, s_data, keyparms);
else
rc = GPG_ERR_NOT_IMPLEMENTED;
leave:
sexp_release (s_data);
sexp_release (keyparms);
return rc;
}
/*
Verify a signature.
Caller has to supply the public key pkey, the signature sig and his
hashvalue data. Public key has to be a standard public key given
as an S-Exp, sig is a S-Exp as returned from gcry_pk_sign and data
must be an S-Exp like the one in sign too. */
gcry_err_code_t
_gcry_pk_verify (gcry_sexp_t s_sig, gcry_sexp_t s_hash, gcry_sexp_t s_pkey)
{
gcry_err_code_t rc;
gcry_pk_spec_t *spec;
gcry_sexp_t keyparms;
rc = spec_from_sexp (s_pkey, 0, &spec, &keyparms);
if (rc)
goto leave;
if (spec->flags.disabled)
rc = GPG_ERR_PUBKEY_ALGO;
else if (!spec->flags.fips && fips_mode ())
- rc = GPG_ERR_PUBKEY_ALGO;
+ {
+ if (fips_check_rejection (GCRY_FIPS_FLAG_REJECT_PK))
+ rc = GPG_ERR_PUBKEY_ALGO;
+ else
+ fips_service_indicator_mark_non_compliant ();
+ }
else if (spec->verify)
rc = spec->verify (s_sig, s_hash, keyparms);
else
rc = GPG_ERR_NOT_IMPLEMENTED;
leave:
sexp_release (keyparms);
return rc;
}
gcry_err_code_t
_gcry_pk_verify_md (gcry_sexp_t s_sig, const char *tmpl, gcry_md_hd_t hd_orig,
gcry_sexp_t s_pkey, gcry_ctx_t ctx)
{
gcry_err_code_t rc;
gcry_pk_spec_t *spec;
gcry_sexp_t keyparms = NULL;
gcry_sexp_t s_data = NULL;
gcry_error_t err;
gcry_md_hd_t hd;
if (!hd_orig)
hd = NULL;
else
{
err = _gcry_md_copy (&hd, hd_orig);
if (err)
return gpg_err_code (err);
}
rc = prepare_datasexp_to_be_signed (tmpl, hd, ctx, &s_data);
if (rc)
return rc;
rc = spec_from_sexp (s_pkey, 0, &spec, &keyparms);
if (rc)
goto leave;
if (spec->flags.disabled)
rc = GPG_ERR_PUBKEY_ALGO;
else if (!spec->flags.fips && fips_mode ())
{
if (fips_check_rejection (GCRY_FIPS_FLAG_REJECT_PK))
rc = GPG_ERR_PUBKEY_ALGO;
else
fips_service_indicator_mark_non_compliant ();
}
else if (spec->verify)
rc = spec->verify (s_sig, s_data, keyparms);
else
rc = GPG_ERR_NOT_IMPLEMENTED;
leave:
sexp_release (s_data);
sexp_release (keyparms);
return rc;
}
/*
Test a key.
This may be used either for a public or a secret key to see whether
the internal structure is okay.
Returns: 0 or an errorcode.
NOTE: We currently support only secret key checking. */
gcry_err_code_t
_gcry_pk_testkey (gcry_sexp_t s_key)
{
gcry_err_code_t rc;
gcry_pk_spec_t *spec;
gcry_sexp_t keyparms;
rc = spec_from_sexp (s_key, 1, &spec, &keyparms);
if (rc)
goto leave;
if (spec->flags.disabled)
rc = GPG_ERR_PUBKEY_ALGO;
else if (!spec->flags.fips && fips_mode ())
- rc = GPG_ERR_PUBKEY_ALGO;
+ {
+ if (fips_check_rejection (GCRY_FIPS_FLAG_REJECT_PK))
+ rc = GPG_ERR_PUBKEY_ALGO;
+ else
+ fips_service_indicator_mark_non_compliant ();
+ }
else if (spec->check_secret_key)
rc = spec->check_secret_key (keyparms);
else
rc = GPG_ERR_NOT_IMPLEMENTED;
leave:
sexp_release (keyparms);
return rc;
}
/* Create a public key pair and return it as R_KEY.
* How the key is created depends on s_parms:
*
* (genkey
* (algo
* (parameter_name_1 ....)
* ....
* (parameter_name_n ....)))
*
* The key is returned in a format depending on the algorithm. Both,
* private and secret keys are returned and optionally some additional
* information. For example for Elgamal this structure is returned:
*
* (key-data
* (public-key
* (elg
* (p <mpi>)
* (g <mpi>)
* (y <mpi>)))
* (private-key
* (elg
* (p <mpi>)
* (g <mpi>)
* (y <mpi>)
* (x <mpi>)))
* (misc-key-info
* (pm1-factors n1 n2 ... nn)))
*/
gcry_err_code_t
_gcry_pk_genkey (gcry_sexp_t *r_key, gcry_sexp_t s_parms)
{
gcry_pk_spec_t *spec = NULL;
gcry_sexp_t list = NULL;
gcry_sexp_t l2 = NULL;
char *name = NULL;
gcry_err_code_t rc;
*r_key = NULL;
list = sexp_find_token (s_parms, "genkey", 0);
if (!list)
{
rc = GPG_ERR_INV_OBJ; /* Does not contain genkey data. */
goto leave;
}
l2 = sexp_cadr (list);
sexp_release (list);
list = l2;
l2 = NULL;
if (! list)
{
rc = GPG_ERR_NO_OBJ; /* No cdr for the genkey. */
goto leave;
}
name = _gcry_sexp_nth_string (list, 0);
if (!name)
{
rc = GPG_ERR_INV_OBJ; /* Algo string missing. */
goto leave;
}
spec = spec_from_name (name);
xfree (name);
name = NULL;
- if (!spec || spec->flags.disabled || (!spec->flags.fips && fips_mode ()))
+ if (!spec || spec->flags.disabled)
{
rc = GPG_ERR_PUBKEY_ALGO; /* Unknown algorithm. */
goto leave;
}
+ else if (!spec->flags.fips && fips_mode ())
+ {
+ if (fips_check_rejection (GCRY_FIPS_FLAG_REJECT_PK))
+ {
+ rc = GPG_ERR_PUBKEY_ALGO;
+ goto leave;
+ }
+ else
+ fips_service_indicator_mark_non_compliant ();
+ }
if (spec->generate)
rc = spec->generate (list, r_key);
else
rc = GPG_ERR_NOT_IMPLEMENTED;
leave:
sexp_release (list);
xfree (name);
sexp_release (l2);
return rc;
}
/*
Get the number of nbits from the public key.
Hmmm: Should we have really this function or is it better to have a
more general function to retrieve different properties of the key? */
unsigned int
_gcry_pk_get_nbits (gcry_sexp_t key)
{
gcry_pk_spec_t *spec;
gcry_sexp_t parms;
unsigned int nbits;
/* Parsing KEY might be considered too much overhead. For example
for RSA we would only need to look at P and stop parsing right
away. However, with ECC things are more complicate in that only
a curve name might be specified. Thus we need to tear the sexp
apart. */
if (spec_from_sexp (key, 0, &spec, &parms))
return 0; /* Error - 0 is a suitable indication for that. */
+
if (spec->flags.disabled)
- return 0;
- if (!spec->flags.fips && fips_mode ())
- return 0;
+ nbits = 0; /* Error */
+ else if (!spec->flags.fips && fips_mode ())
+ {
+ if (fips_check_rejection (GCRY_FIPS_FLAG_REJECT_PK))
+ nbits = 0; /* Error */
+ else
+ {
+ fips_service_indicator_mark_non_compliant ();
+ nbits = spec->get_nbits (parms);
+ }
+ }
+ else
+ nbits = spec->get_nbits (parms);
- nbits = spec->get_nbits (parms);
sexp_release (parms);
return nbits;
}
/* Return the so called KEYGRIP which is the SHA-1 hash of the public
key parameters expressed in a way depending on the algorithm.
ARRAY must either be 20 bytes long or NULL; in the latter case a
newly allocated array of that size is returned, otherwise ARRAY or
NULL is returned to indicate an error which is most likely an
unknown algorithm. The function accepts public or secret keys. */
unsigned char *
_gcry_pk_get_keygrip (gcry_sexp_t key, unsigned char *array)
{
gcry_sexp_t list = NULL;
gcry_sexp_t l2 = NULL;
gcry_pk_spec_t *spec = NULL;
const char *s;
char *name = NULL;
int idx;
const char *elems;
gcry_md_hd_t md = NULL;
int okay = 0;
/* Check that the first element is valid. */
list = sexp_find_token (key, "public-key", 0);
if (! list)
list = sexp_find_token (key, "private-key", 0);
if (! list)
list = sexp_find_token (key, "protected-private-key", 0);
if (! list)
list = sexp_find_token (key, "shadowed-private-key", 0);
if (! list)
return NULL; /* No public- or private-key object. */
l2 = sexp_cadr (list);
sexp_release (list);
list = l2;
l2 = NULL;
name = _gcry_sexp_nth_string (list, 0);
if (!name)
goto fail; /* Invalid structure of object. */
spec = spec_from_name (name);
if (!spec)
goto fail; /* Unknown algorithm. */
elems = spec->elements_grip;
if (!elems)
goto fail; /* No grip parameter. */
if (_gcry_md_open (&md, GCRY_MD_SHA1, 0))
goto fail;
if (spec->comp_keygrip)
{
/* Module specific method to compute a keygrip. */
if (spec->comp_keygrip (md, list))
goto fail;
}
else
{
/* Generic method to compute a keygrip. */
for (idx = 0, s = elems; *s; s++, idx++)
{
const char *data;
size_t datalen;
char buf[30];
l2 = sexp_find_token (list, s, 1);
if (! l2)
goto fail;
data = sexp_nth_data (l2, 1, &datalen);
if (! data)
goto fail;
snprintf (buf, sizeof buf, "(1:%c%u:", *s, (unsigned int)datalen);
_gcry_md_write (md, buf, strlen (buf));
_gcry_md_write (md, data, datalen);
sexp_release (l2);
l2 = NULL;
_gcry_md_write (md, ")", 1);
}
}
if (!array)
{
array = xtrymalloc (20);
if (! array)
goto fail;
}
memcpy (array, _gcry_md_read (md, GCRY_MD_SHA1), 20);
okay = 1;
fail:
xfree (name);
sexp_release (l2);
_gcry_md_close (md);
sexp_release (list);
return okay? array : NULL;
}
const char *
_gcry_pk_get_curve (gcry_sexp_t key, int iterator, unsigned int *r_nbits)
{
const char *result = NULL;
gcry_pk_spec_t *spec;
gcry_sexp_t keyparms = NULL;
if (r_nbits)
*r_nbits = 0;
if (key)
{
iterator = 0;
if (spec_from_sexp (key, 0, &spec, &keyparms))
return NULL;
}
else
{
spec = spec_from_name ("ecc");
if (!spec)
return NULL;
}
if (spec->flags.disabled)
- return NULL;
- if (!spec->flags.fips && fips_mode ())
- return NULL;
- if (spec->get_curve)
+ result = NULL;
+ else if (!spec->flags.fips && fips_mode ())
+ {
+ if (fips_check_rejection (GCRY_FIPS_FLAG_REJECT_PK))
+ result = NULL;
+ else
+ {
+ fips_service_indicator_mark_non_compliant ();
+ result = spec->get_curve (keyparms, iterator, r_nbits);
+ }
+ }
+ else if (spec->get_curve)
result = spec->get_curve (keyparms, iterator, r_nbits);
sexp_release (keyparms);
return result;
}
gcry_sexp_t
_gcry_pk_get_param (int algo, const char *name)
{
gcry_sexp_t result = NULL;
gcry_pk_spec_t *spec = NULL;
algo = map_algo (algo);
if (algo != GCRY_PK_ECC)
return NULL;
spec = spec_from_name ("ecc");
if (spec)
{
if (spec && spec->get_curve_param)
result = spec->get_curve_param (name);
}
return result;
}
gcry_err_code_t
_gcry_pk_ctl (int cmd, void *buffer, size_t buflen)
{
gcry_err_code_t rc = 0;
switch (cmd)
{
case GCRYCTL_DISABLE_ALGO:
/* This one expects a buffer pointing to an integer with the
algo number. */
if ((! buffer) || (buflen != sizeof (int)))
rc = GPG_ERR_INV_ARG;
else
disable_pubkey_algo (*((int *) buffer));
break;
default:
rc = GPG_ERR_INV_OP;
}
return rc;
}
/* Return information about the given algorithm
WHAT selects the kind of information returned:
GCRYCTL_TEST_ALGO:
Returns 0 when the specified algorithm is available for use.
Buffer must be NULL, nbytes may have the address of a variable
with the required usage of the algorithm. It may be 0 for don't
care or a combination of the GCRY_PK_USAGE_xxx flags;
GCRYCTL_GET_ALGO_USAGE:
Return the usage flags for the given algo. An invalid algo
returns 0. Disabled algos are ignored here because we
only want to know whether the algo is at all capable of
the usage.
Note: Because this function is in most cases used to return an
integer value, we can make it easier for the caller to just look at
the return value. The caller will in all cases consult the value
and thereby detecting whether a error occurred or not (i.e. while
checking the block size) */
gcry_err_code_t
_gcry_pk_algo_info (int algorithm, int what, void *buffer, size_t *nbytes)
{
gcry_err_code_t rc = 0;
switch (what)
{
case GCRYCTL_TEST_ALGO:
{
int use = nbytes ? *nbytes : 0;
if (buffer)
rc = GPG_ERR_INV_ARG;
else if (check_pubkey_algo (algorithm, use))
rc = GPG_ERR_PUBKEY_ALGO;
break;
}
case GCRYCTL_GET_ALGO_USAGE:
{
gcry_pk_spec_t *spec;
spec = spec_from_algo (algorithm);
*nbytes = spec? spec->use : 0;
break;
}
case GCRYCTL_GET_ALGO_NPKEY:
{
/* FIXME? */
int npkey = pubkey_get_npkey (algorithm);
*nbytes = npkey;
break;
}
case GCRYCTL_GET_ALGO_NSKEY:
{
/* FIXME? */
int nskey = pubkey_get_nskey (algorithm);
*nbytes = nskey;
break;
}
case GCRYCTL_GET_ALGO_NSIGN:
{
/* FIXME? */
int nsign = pubkey_get_nsig (algorithm);
*nbytes = nsign;
break;
}
case GCRYCTL_GET_ALGO_NENCR:
{
/* FIXME? */
int nencr = pubkey_get_nenc (algorithm);
*nbytes = nencr;
break;
}
default:
rc = GPG_ERR_INV_OP;
}
return rc;
}
/* Return an S-expression representing the context CTX. Depending on
the state of that context, the S-expression may either be a public
key, a private key or any other object used with public key
operations. On success a new S-expression is stored at R_SEXP and
0 is returned, on error NULL is store there and an error code is
returned. MODE is either 0 or one of the GCRY_PK_GET_xxx values.
As of now it only support certain ECC operations because a context
object is right now only defined for ECC. Over time this function
will be extended to cover more algorithms. Note also that the name
of the function is gcry_pubkey_xxx and not gcry_pk_xxx. The idea
is that we will eventually provide variants of the existing
gcry_pk_xxx functions which will take a context parameter. */
gcry_err_code_t
_gcry_pubkey_get_sexp (gcry_sexp_t *r_sexp, int mode, gcry_ctx_t ctx)
{
mpi_ec_t ec;
if (!r_sexp)
return GPG_ERR_INV_VALUE;
*r_sexp = NULL;
switch (mode)
{
case 0:
case GCRY_PK_GET_PUBKEY:
case GCRY_PK_GET_SECKEY:
break;
default:
return GPG_ERR_INV_VALUE;
}
if (!ctx)
return GPG_ERR_NO_CRYPT_CTX;
ec = _gcry_ctx_find_pointer (ctx, CONTEXT_TYPE_EC);
if (ec)
return _gcry_pk_ecc_get_sexp (r_sexp, mode, ec);
return GPG_ERR_WRONG_CRYPT_CTX;
}
/* Explicitly initialize this module. */
gcry_err_code_t
_gcry_pk_init (void)
{
return 0;
}
/* Run the selftests for pubkey algorithm ALGO with optional reporting
function REPORT. */
gpg_error_t
_gcry_pk_selftest (int algo, int extended, selftest_report_func_t report)
{
gcry_err_code_t ec;
gcry_pk_spec_t *spec;
algo = map_algo (algo);
spec = spec_from_algo (algo);
if (spec && !spec->flags.disabled
&& (spec->flags.fips || !fips_mode ())
&& spec->selftest)
ec = spec->selftest (algo, extended, report);
else
{
ec = GPG_ERR_PUBKEY_ALGO;
/* Fixme: We need to change the report function to allow passing
of an encryption mode (e.g. pkcs1, ecdsa, or ecdh). */
if (report)
report ("pubkey", algo, "module",
spec && !spec->flags.disabled
&& (spec->flags.fips || !fips_mode ())?
"no selftest available" :
spec? "algorithm disabled" :
"algorithm not found");
}
return gpg_error (ec);
}
struct pk_single_data {
size_t len;
unsigned char area[1]; /* In future, we may use flexible array member. */
};
gpg_err_code_t
_gcry_pk_single_data_push (gcry_ctx_t *r_ctx,
const unsigned char *p, size_t len)
{
gcry_ctx_t ctx;
struct pk_single_data *psd;
int data_type = CONTEXT_TYPE_SINGLE_DATA;
if (!p)
return GPG_ERR_EINVAL;
ctx = _gcry_ctx_alloc (data_type,
offsetof (struct pk_single_data, area) + len,
NULL, *r_ctx);
if (!ctx)
return gpg_err_code_from_syserror ();
psd = _gcry_ctx_get_pointer (ctx, data_type);
psd->len = len;
memcpy (psd->area, p, len);
*r_ctx = ctx;
return 0;
}
gpg_err_code_t
_gcry_pk_get_single_data (gcry_ctx_t *r_ctx,
const unsigned char **r_p, size_t *r_len)
{
struct pk_single_data *psd;
int data_type = CONTEXT_TYPE_SINGLE_DATA;
gcry_ctx_t ctx = *r_ctx;
psd = _gcry_ctx_find_pointer (ctx, data_type);
if (!psd)
return GPG_ERR_EINVAL;
*r_p = psd->area;
*r_len = psd->len;
*r_ctx = _gcry_ctx_get_pointer (ctx, 0);
return 0;
}
diff --git a/src/visibility.c b/src/visibility.c
index d22c8b59..e02d6cfe 100644
--- a/src/visibility.c
+++ b/src/visibility.c
@@ -1,1788 +1,1795 @@
/* visibility.c - Wrapper for all public functions.
* Copyright (C) 2007, 2008, 2011 Free Software Foundation, Inc.
* Copyright (C) 2013 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 <stdarg.h>
#define _GCRY_INCLUDED_BY_VISIBILITY_C
#include "g10lib.h"
#include "cipher-proto.h"
#include "context.h"
#include "mpi.h"
#include "ec-context.h"
const char *
gcry_strerror (gcry_error_t err)
{
return _gcry_strerror (err);
}
const char *
gcry_strsource (gcry_error_t err)
{
return _gcry_strsource (err);
}
gcry_err_code_t
gcry_err_code_from_errno (int err)
{
return _gcry_err_code_from_errno (err);
}
int
gcry_err_code_to_errno (gcry_err_code_t code)
{
return _gcry_err_code_to_errno (code);
}
gcry_error_t
gcry_err_make_from_errno (gcry_err_source_t source, int err)
{
return _gcry_err_make_from_errno (source, err);
}
gcry_error_t
gcry_error_from_errno (int err)
{
return _gcry_error_from_errno (err);
}
const char *
gcry_check_version (const char *req_version)
{
return _gcry_check_version (req_version);
}
gcry_error_t
gcry_control (enum gcry_ctl_cmds cmd, ...)
{
gcry_error_t err;
va_list arg_ptr;
va_start (arg_ptr, cmd);
err = gpg_error (_gcry_vcontrol (cmd, arg_ptr));
va_end(arg_ptr);
return err;
}
gcry_error_t
gcry_sexp_new (gcry_sexp_t *retsexp,
const void *buffer, size_t length,
int autodetect)
{
return gpg_error (_gcry_sexp_new (retsexp, buffer, length, autodetect));
}
gcry_error_t
gcry_sexp_create (gcry_sexp_t *retsexp,
void *buffer, size_t length,
int autodetect, void (*freefnc) (void *))
{
return gpg_error (_gcry_sexp_create (retsexp, buffer, length,
autodetect, freefnc));
}
gcry_error_t
gcry_sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
const char *buffer, size_t length)
{
return gpg_error (_gcry_sexp_sscan (retsexp, erroff, buffer, length));
}
gcry_error_t
gcry_sexp_build (gcry_sexp_t *retsexp, size_t *erroff,
const char *format, ...)
{
gcry_err_code_t rc;
va_list arg_ptr;
va_start (arg_ptr, format);
rc = _gcry_sexp_vbuild (retsexp, erroff, format, arg_ptr);
va_end (arg_ptr);
return gpg_error (rc);
}
gcry_error_t
gcry_sexp_build_array (gcry_sexp_t *retsexp, size_t *erroff,
const char *format, void **arg_list)
{
return gpg_error (_gcry_sexp_build_array (retsexp, erroff, format, arg_list));
}
void
gcry_sexp_release (gcry_sexp_t sexp)
{
_gcry_sexp_release (sexp);
}
size_t
gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
size_t *erroff, gcry_error_t *errcode)
{
size_t n;
gpg_err_code_t rc;
n = _gcry_sexp_canon_len (buffer, length, erroff, &rc);
if (errcode)
*errcode = gpg_error (rc);
return n;
}
size_t
gcry_sexp_sprint (gcry_sexp_t sexp, int mode, void *buffer, size_t maxlength)
{
return _gcry_sexp_sprint (sexp, mode, buffer, maxlength);
}
void
gcry_sexp_dump (const gcry_sexp_t a)
{
_gcry_sexp_dump (a);
}
gcry_sexp_t
gcry_sexp_cons (const gcry_sexp_t a, const gcry_sexp_t b)
{
return _gcry_sexp_cons (a, b);
}
gcry_sexp_t
gcry_sexp_alist (const gcry_sexp_t *array)
{
return _gcry_sexp_alist (array);
}
gcry_sexp_t
gcry_sexp_vlist (const gcry_sexp_t a, ...)
{
/* This is not yet implemented in sexp.c. */
(void)a;
BUG ();
return NULL;
}
gcry_sexp_t
gcry_sexp_append (const gcry_sexp_t a, const gcry_sexp_t n)
{
return _gcry_sexp_append (a, n);
}
gcry_sexp_t
gcry_sexp_prepend (const gcry_sexp_t a, const gcry_sexp_t n)
{
return _gcry_sexp_prepend (a, n);
}
gcry_sexp_t
gcry_sexp_find_token (gcry_sexp_t list, const char *tok, size_t toklen)
{
return _gcry_sexp_find_token (list, tok, toklen);
}
int
gcry_sexp_length (const gcry_sexp_t list)
{
return _gcry_sexp_length (list);
}
gcry_sexp_t
gcry_sexp_nth (const gcry_sexp_t list, int number)
{
return _gcry_sexp_nth (list, number);
}
gcry_sexp_t
gcry_sexp_car (const gcry_sexp_t list)
{
return _gcry_sexp_car (list);
}
gcry_sexp_t
gcry_sexp_cdr (const gcry_sexp_t list)
{
return _gcry_sexp_cdr (list);
}
gcry_sexp_t
gcry_sexp_cadr (const gcry_sexp_t list)
{
return _gcry_sexp_cadr (list);
}
const char *
gcry_sexp_nth_data (const gcry_sexp_t list, int number, size_t *datalen)
{
return _gcry_sexp_nth_data (list, number, datalen);
}
void *
gcry_sexp_nth_buffer (const gcry_sexp_t list, int number, size_t *rlength)
{
return _gcry_sexp_nth_buffer (list, number, rlength);
}
char *
gcry_sexp_nth_string (gcry_sexp_t list, int number)
{
return _gcry_sexp_nth_string (list, number);
}
gcry_mpi_t
gcry_sexp_nth_mpi (gcry_sexp_t list, int number, int mpifmt)
{
return _gcry_sexp_nth_mpi (list, number, mpifmt);
}
gpg_error_t
gcry_sexp_extract_param (gcry_sexp_t sexp, const char *path,
const char *list, ...)
{
gcry_err_code_t rc;
va_list arg_ptr;
va_start (arg_ptr, list);
rc = _gcry_sexp_vextract_param (sexp, path, list, arg_ptr);
va_end (arg_ptr);
return gpg_error (rc);
}
gcry_mpi_t
gcry_mpi_new (unsigned int nbits)
{
return _gcry_mpi_new (nbits);
}
gcry_mpi_t
gcry_mpi_snew (unsigned int nbits)
{
return _gcry_mpi_snew (nbits);
}
void
gcry_mpi_release (gcry_mpi_t a)
{
_gcry_mpi_release (a);
}
gcry_mpi_t
gcry_mpi_copy (const gcry_mpi_t a)
{
return _gcry_mpi_copy (a);
}
void
gcry_mpi_snatch (gcry_mpi_t w, const gcry_mpi_t u)
{
_gcry_mpi_snatch (w, u);
}
gcry_mpi_t
gcry_mpi_set (gcry_mpi_t w, const gcry_mpi_t u)
{
return _gcry_mpi_set (w, u);
}
gcry_mpi_t
gcry_mpi_set_ui (gcry_mpi_t w, unsigned long u)
{
return _gcry_mpi_set_ui (w, u);
}
gcry_error_t
gcry_mpi_get_ui (unsigned int *w, gcry_mpi_t u)
{
return gpg_error (_gcry_mpi_get_ui (w, u));
}
void
gcry_mpi_swap (gcry_mpi_t a, gcry_mpi_t b)
{
_gcry_mpi_swap (a, b);
}
int
gcry_mpi_is_neg (gcry_mpi_t a)
{
return _gcry_mpi_is_neg (a);
}
void
gcry_mpi_neg (gcry_mpi_t w, gcry_mpi_t u)
{
_gcry_mpi_neg (w, u);
}
void
gcry_mpi_abs (gcry_mpi_t w)
{
_gcry_mpi_abs (w);
}
int
gcry_mpi_cmp (const gcry_mpi_t u, const gcry_mpi_t v)
{
return _gcry_mpi_cmp (u, v);
}
int
gcry_mpi_cmp_ui (const gcry_mpi_t u, unsigned long v)
{
return _gcry_mpi_cmp_ui (u, v);
}
gcry_error_t
gcry_mpi_scan (gcry_mpi_t *ret_mpi, enum gcry_mpi_format format,
const void *buffer, size_t buflen,
size_t *nscanned)
{
return gpg_error (_gcry_mpi_scan (ret_mpi, format, buffer, buflen, nscanned));
}
gcry_error_t
gcry_mpi_print (enum gcry_mpi_format format,
unsigned char *buffer, size_t buflen,
size_t *nwritten,
const gcry_mpi_t a)
{
return gpg_error (_gcry_mpi_print (format, buffer, buflen, nwritten, a));
}
gcry_error_t
gcry_mpi_aprint (enum gcry_mpi_format format,
unsigned char **buffer, size_t *nwritten,
const gcry_mpi_t a)
{
return gpg_error (_gcry_mpi_aprint (format, buffer, nwritten, a));
}
void
gcry_mpi_dump (const gcry_mpi_t a)
{
_gcry_log_printmpi (NULL, a);
}
void
gcry_mpi_add (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v)
{
_gcry_mpi_add (w, u, v);
}
void
gcry_mpi_add_ui (gcry_mpi_t w, gcry_mpi_t u, unsigned long v)
{
_gcry_mpi_add_ui (w, u, v);
}
void
gcry_mpi_addm (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, gcry_mpi_t m)
{
_gcry_mpi_addm (w, u, v, m);
}
void
gcry_mpi_sub (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v)
{
_gcry_mpi_sub (w, u, v);
}
void
gcry_mpi_sub_ui (gcry_mpi_t w, gcry_mpi_t u, unsigned long v )
{
_gcry_mpi_sub_ui (w, u, v);
}
void
gcry_mpi_subm (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, gcry_mpi_t m)
{
_gcry_mpi_subm (w, u, v, m);
}
void
gcry_mpi_mul (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v)
{
_gcry_mpi_mul (w, u, v);
}
void
gcry_mpi_mul_ui (gcry_mpi_t w, gcry_mpi_t u, unsigned long v )
{
_gcry_mpi_mul_ui (w, u, v);
}
void
gcry_mpi_mulm (gcry_mpi_t w, gcry_mpi_t u, gcry_mpi_t v, gcry_mpi_t m)
{
_gcry_mpi_mulm (w, u, v, m);
}
void
gcry_mpi_mul_2exp (gcry_mpi_t w, gcry_mpi_t u, unsigned long cnt)
{
_gcry_mpi_mul_2exp (w, u, cnt);
}
void
gcry_mpi_div (gcry_mpi_t q, gcry_mpi_t r,
gcry_mpi_t dividend, gcry_mpi_t divisor, int round)
{
_gcry_mpi_div (q, r, dividend, divisor, round);
}
void
gcry_mpi_mod (gcry_mpi_t r, gcry_mpi_t dividend, gcry_mpi_t divisor)
{
_gcry_mpi_mod (r, dividend, divisor);
}
void
gcry_mpi_powm (gcry_mpi_t w, const gcry_mpi_t b, const gcry_mpi_t e,
const gcry_mpi_t m)
{
_gcry_mpi_powm (w, b, e, m);
}
int
gcry_mpi_gcd (gcry_mpi_t g, gcry_mpi_t a, gcry_mpi_t b)
{
return _gcry_mpi_gcd (g, a, b);
}
int
gcry_mpi_invm (gcry_mpi_t x, gcry_mpi_t a, gcry_mpi_t m)
{
return _gcry_mpi_invm (x, a, m);
}
gcry_mpi_point_t
gcry_mpi_point_new (unsigned int nbits)
{
return _gcry_mpi_point_new (nbits);
}
void
gcry_mpi_point_release (gcry_mpi_point_t point)
{
_gcry_mpi_point_release (point);
}
gcry_mpi_point_t
gcry_mpi_point_copy (gcry_mpi_point_t point)
{
return _gcry_mpi_point_copy (point);
}
void
gcry_mpi_point_get (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t z,
gcry_mpi_point_t point)
{
_gcry_mpi_point_get (x, y, z, point);
}
void
gcry_mpi_point_snatch_get (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t z,
gcry_mpi_point_t point)
{
_gcry_mpi_point_snatch_get (x, y, z, point);
}
gcry_mpi_point_t
gcry_mpi_point_set (gcry_mpi_point_t point,
gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t z)
{
return _gcry_mpi_point_set (point, x, y, z);
}
gcry_mpi_point_t
gcry_mpi_point_snatch_set (gcry_mpi_point_t point,
gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t z)
{
return _gcry_mpi_point_snatch_set (point, x, y, z);
}
gpg_error_t
gcry_mpi_ec_new (gcry_ctx_t *r_ctx,
gcry_sexp_t keyparam, const char *curvename)
{
return gpg_error (_gcry_mpi_ec_new (r_ctx, keyparam, curvename));
}
gcry_mpi_t
gcry_mpi_ec_get_mpi (const char *name, gcry_ctx_t ctx, int copy)
{
return _gcry_mpi_ec_get_mpi (name, ctx, copy);
}
gcry_mpi_point_t
gcry_mpi_ec_get_point (const char *name, gcry_ctx_t ctx, int copy)
{
return _gcry_mpi_ec_get_point (name, ctx, copy);
}
gpg_error_t
gcry_mpi_ec_set_mpi (const char *name, gcry_mpi_t newvalue, gcry_ctx_t ctx)
{
return gpg_error (_gcry_mpi_ec_set_mpi (name, newvalue, ctx));
}
gpg_error_t
gcry_mpi_ec_set_point (const char *name, gcry_mpi_point_t newvalue,
gcry_ctx_t ctx)
{
return gpg_error (_gcry_mpi_ec_set_point (name, newvalue, ctx));
}
gpg_error_t
gcry_mpi_ec_decode_point (gcry_mpi_point_t result, gcry_mpi_t value,
gcry_ctx_t ctx)
{
return gpg_error (_gcry_mpi_ec_decode_point
(result, value,
ctx? _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC) : NULL));
}
int
gcry_mpi_ec_get_affine (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_point_t point,
gcry_ctx_t ctx)
{
return _gcry_mpi_ec_get_affine (x, y, point,
_gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC));
}
void
gcry_mpi_ec_dup (gcry_mpi_point_t w, gcry_mpi_point_t u, gcry_ctx_t ctx)
{
mpi_ec_t ec = _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC);
if (ec->model == MPI_EC_EDWARDS || ec->model == MPI_EC_MONTGOMERY)
{
mpi_point_resize (w, ec);
mpi_point_resize (u, ec);
}
_gcry_mpi_ec_dup_point (w, u, ec);
}
void
gcry_mpi_ec_add (gcry_mpi_point_t w,
gcry_mpi_point_t u, gcry_mpi_point_t v, gcry_ctx_t ctx)
{
mpi_ec_t ec = _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC);
if (ec->model == MPI_EC_EDWARDS || ec->model == MPI_EC_MONTGOMERY)
{
mpi_point_resize (w, ec);
mpi_point_resize (u, ec);
mpi_point_resize (v, ec);
}
_gcry_mpi_ec_add_points (w, u, v, ec);
}
void
gcry_mpi_ec_sub (gcry_mpi_point_t w,
gcry_mpi_point_t u, gcry_mpi_point_t v, gcry_ctx_t ctx)
{
mpi_ec_t ec = _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC);
if (ec->model == MPI_EC_EDWARDS || ec->model == MPI_EC_MONTGOMERY)
{
mpi_point_resize (w, ec);
mpi_point_resize (u, ec);
mpi_point_resize (v, ec);
}
_gcry_mpi_ec_sub_points (w, u, v, ec);
}
void
gcry_mpi_ec_mul (gcry_mpi_point_t w, gcry_mpi_t n, gcry_mpi_point_t u,
gcry_ctx_t ctx)
{
_gcry_mpi_ec_mul_point (w, n, u,
_gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC));
}
int
gcry_mpi_ec_curve_point (gcry_mpi_point_t point, gcry_ctx_t ctx)
{
return _gcry_mpi_ec_curve_point
(point, _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC));
}
unsigned int
gcry_mpi_get_nbits (gcry_mpi_t a)
{
return _gcry_mpi_get_nbits (a);
}
int
gcry_mpi_test_bit (gcry_mpi_t a, unsigned int n)
{
return _gcry_mpi_test_bit (a, n);
}
void
gcry_mpi_set_bit (gcry_mpi_t a, unsigned int n)
{
_gcry_mpi_set_bit (a, n);
}
void
gcry_mpi_clear_bit (gcry_mpi_t a, unsigned int n)
{
_gcry_mpi_clear_bit (a, n);
}
void
gcry_mpi_set_highbit (gcry_mpi_t a, unsigned int n)
{
_gcry_mpi_set_highbit (a, n);
}
void
gcry_mpi_clear_highbit (gcry_mpi_t a, unsigned int n)
{
_gcry_mpi_clear_highbit (a, n);
}
void
gcry_mpi_rshift (gcry_mpi_t x, gcry_mpi_t a, unsigned int n)
{
_gcry_mpi_rshift (x, a, n);
}
void
gcry_mpi_lshift (gcry_mpi_t x, gcry_mpi_t a, unsigned int n)
{
_gcry_mpi_lshift (x, a, n);
}
gcry_mpi_t
gcry_mpi_set_opaque (gcry_mpi_t a, void *p, unsigned int nbits)
{
return _gcry_mpi_set_opaque (a, p, nbits);
}
gcry_mpi_t
gcry_mpi_set_opaque_copy (gcry_mpi_t a, const void *p, unsigned int nbits)
{
return _gcry_mpi_set_opaque_copy (a, p, nbits);
}
void *
gcry_mpi_get_opaque (gcry_mpi_t a, unsigned int *nbits)
{
return _gcry_mpi_get_opaque (a, nbits);
}
void
gcry_mpi_set_flag (gcry_mpi_t a, enum gcry_mpi_flag flag)
{
_gcry_mpi_set_flag (a, flag);
}
void
gcry_mpi_clear_flag (gcry_mpi_t a, enum gcry_mpi_flag flag)
{
_gcry_mpi_clear_flag (a, flag);
}
int
gcry_mpi_get_flag (gcry_mpi_t a, enum gcry_mpi_flag flag)
{
return _gcry_mpi_get_flag (a, flag);
}
gcry_mpi_t
_gcry_mpi_get_const (int no)
{
switch (no)
{
case 1: return _gcry_mpi_const (MPI_C_ONE);
case 2: return _gcry_mpi_const (MPI_C_TWO);
case 3: return _gcry_mpi_const (MPI_C_THREE);
case 4: return _gcry_mpi_const (MPI_C_FOUR);
case 8: return _gcry_mpi_const (MPI_C_EIGHT);
default: log_bug("unsupported GCRYMPI_CONST_ macro used\n");
}
}
gcry_error_t
gcry_cipher_open (gcry_cipher_hd_t *handle,
int algo, int mode, unsigned int flags)
{
if (!fips_is_operational ())
{
*handle = NULL;
return gpg_error (fips_not_operational ());
}
fips_service_indicator_init ();
return gpg_error (_gcry_cipher_open (handle, algo, mode, flags));
}
void
gcry_cipher_close (gcry_cipher_hd_t h)
{
_gcry_cipher_close (h);
}
gcry_error_t
gcry_cipher_setkey (gcry_cipher_hd_t hd, const void *key, size_t keylen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
fips_service_indicator_init ();
return gcry_error (_gcry_cipher_setkey (hd, key, keylen));
}
gcry_error_t
gcry_cipher_setiv (gcry_cipher_hd_t hd, const void *iv, size_t ivlen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gcry_error (_gcry_cipher_setiv (hd, iv, ivlen));
}
gpg_error_t
gcry_cipher_setctr (gcry_cipher_hd_t hd, const void *ctr, size_t ctrlen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gcry_error (_gcry_cipher_setctr (hd, ctr, ctrlen));
}
gcry_error_t
gcry_cipher_setup_geniv (gcry_cipher_hd_t hd, int method,
const void *fixed_iv, size_t fixed_iv_len,
const void *dyn_iv, size_t dyn_iv_len)
{
return gcry_error (_gcry_cipher_setup_geniv (hd, method,
fixed_iv, fixed_iv_len,
dyn_iv, dyn_iv_len));
}
gcry_error_t
gcry_cipher_geniv (gcry_cipher_hd_t hd, void *iv, size_t iv_len)
{
return gcry_error (_gcry_cipher_geniv (hd, iv, iv_len));
}
gcry_error_t
gcry_cipher_authenticate (gcry_cipher_hd_t hd, const void *abuf, size_t abuflen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_cipher_authenticate (hd, abuf, abuflen));
}
gcry_error_t
gcry_cipher_gettag (gcry_cipher_hd_t hd, void *outtag, size_t taglen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_cipher_gettag (hd, outtag, taglen));
}
gcry_error_t
gcry_cipher_checktag (gcry_cipher_hd_t hd, const void *intag, size_t taglen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_cipher_checktag (hd, intag, taglen));
}
gcry_error_t
gcry_cipher_ctl (gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_cipher_ctl (h, cmd, buffer, buflen));
}
gcry_error_t
gcry_cipher_info (gcry_cipher_hd_t h, int what, void *buffer, size_t *nbytes)
{
return gpg_error (_gcry_cipher_info (h, what, buffer, nbytes));
}
gcry_error_t
gcry_cipher_algo_info (int algo, int what, void *buffer, size_t *nbytes)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_cipher_algo_info (algo, what, buffer, nbytes));
}
const char *
gcry_cipher_algo_name (int algorithm)
{
return _gcry_cipher_algo_name (algorithm);
}
int
gcry_cipher_map_name (const char *name)
{
return _gcry_cipher_map_name (name);
}
int
gcry_cipher_mode_from_oid (const char *string)
{
return _gcry_cipher_mode_from_oid (string);
}
gcry_error_t
gcry_cipher_encrypt (gcry_cipher_hd_t h,
void *out, size_t outsize,
const void *in, size_t inlen)
{
if (!fips_is_operational ())
{
/* Make sure that the plaintext will never make it to OUT. */
if (out)
memset (out, 0x42, outsize);
return gpg_error (fips_not_operational ());
}
return gpg_error (_gcry_cipher_encrypt (h, out, outsize, in, inlen));
}
gcry_error_t
gcry_cipher_decrypt (gcry_cipher_hd_t h,
void *out, size_t outsize,
const void *in, size_t inlen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_cipher_decrypt (h, out, outsize, in, inlen));
}
size_t
gcry_cipher_get_algo_keylen (int algo)
{
return _gcry_cipher_get_algo_keylen (algo);
}
size_t
gcry_cipher_get_algo_blklen (int algo)
{
return _gcry_cipher_get_algo_blklen (algo);
}
gcry_error_t
gcry_mac_algo_info (int algo, int what, void *buffer, size_t *nbytes)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_mac_algo_info (algo, what, buffer, nbytes));
}
const char *
gcry_mac_algo_name (int algorithm)
{
return _gcry_mac_algo_name (algorithm);
}
int
gcry_mac_map_name (const char *string)
{
return _gcry_mac_map_name (string);
}
int
gcry_mac_get_algo (gcry_mac_hd_t hd)
{
return _gcry_mac_get_algo (hd);
}
unsigned int
gcry_mac_get_algo_maclen (int algo)
{
return _gcry_mac_get_algo_maclen (algo);
}
unsigned int
gcry_mac_get_algo_keylen (int algo)
{
return _gcry_mac_get_algo_keylen (algo);
}
gcry_error_t
gcry_mac_open (gcry_mac_hd_t *handle, int algo, unsigned int flags,
gcry_ctx_t ctx)
{
if (!fips_is_operational ())
{
*handle = NULL;
return gpg_error (fips_not_operational ());
}
fips_service_indicator_init ();
return gpg_error (_gcry_mac_open (handle, algo, flags, ctx));
}
void
gcry_mac_close (gcry_mac_hd_t hd)
{
_gcry_mac_close (hd);
}
gcry_error_t
gcry_mac_setkey (gcry_mac_hd_t hd, const void *key, size_t keylen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
fips_service_indicator_init ();
if (fips_mode () && keylen < 14)
fips_service_indicator_mark_non_compliant ();
return gpg_error (_gcry_mac_setkey (hd, key, keylen));
}
gcry_error_t
gcry_mac_setiv (gcry_mac_hd_t hd, const void *iv, size_t ivlen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_mac_setiv (hd, iv, ivlen));
}
gcry_error_t
gcry_mac_write (gcry_mac_hd_t hd, const void *buf, size_t buflen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_mac_write (hd, buf, buflen));
}
gcry_error_t
gcry_mac_read (gcry_mac_hd_t hd, void *outbuf, size_t *outlen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_mac_read (hd, outbuf, outlen));
}
gcry_error_t
gcry_mac_verify (gcry_mac_hd_t hd, const void *buf, size_t buflen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_mac_verify (hd, buf, buflen));
}
gcry_error_t
gcry_mac_ctl (gcry_mac_hd_t h, int cmd, void *buffer, size_t buflen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_mac_ctl (h, cmd, buffer, buflen));
}
gcry_error_t
gcry_pk_encrypt (gcry_sexp_t *result, gcry_sexp_t data, gcry_sexp_t pkey)
{
if (!fips_is_operational ())
{
*result = NULL;
return gpg_error (fips_not_operational ());
}
+ fips_service_indicator_init ();
return gpg_error (_gcry_pk_encrypt (result, data, pkey));
}
gcry_error_t
gcry_pk_decrypt (gcry_sexp_t *result, gcry_sexp_t data, gcry_sexp_t skey)
{
if (!fips_is_operational ())
{
*result = NULL;
return gpg_error (fips_not_operational ());
}
+ fips_service_indicator_init ();
return gpg_error (_gcry_pk_decrypt (result, data, skey));
}
gcry_error_t
gcry_pk_sign (gcry_sexp_t *result, gcry_sexp_t data, gcry_sexp_t skey)
{
if (!fips_is_operational ())
{
*result = NULL;
return gpg_error (fips_not_operational ());
}
+ fips_service_indicator_init ();
return gpg_error (_gcry_pk_sign (result, data, skey));
}
gcry_error_t
gcry_pk_hash_sign (gcry_sexp_t *result, const char *data_tmpl, gcry_sexp_t skey,
gcry_md_hd_t hd, gcry_ctx_t ctx)
{
if (!fips_is_operational ())
{
*result = NULL;
return gpg_error (fips_not_operational ());
}
fips_service_indicator_init ();
return gpg_error (_gcry_pk_sign_md (result, data_tmpl, hd, skey, ctx));
}
gcry_error_t
gcry_pk_verify (gcry_sexp_t sigval, gcry_sexp_t data, gcry_sexp_t pkey)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
+ fips_service_indicator_init ();
return gpg_error (_gcry_pk_verify (sigval, data, pkey));
}
gcry_error_t
gcry_pk_hash_verify (gcry_sexp_t sigval, const char *data_tmpl, gcry_sexp_t pkey,
gcry_md_hd_t hd, gcry_ctx_t ctx)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
fips_service_indicator_init ();
return gpg_error (_gcry_pk_verify_md (sigval, data_tmpl, hd, pkey, ctx));
}
gcry_error_t
gcry_pk_random_override_new (gcry_ctx_t *r_ctx, const unsigned char *p, size_t len)
{
return gpg_error (_gcry_pk_single_data_push (r_ctx, p, len));
}
gcry_error_t
gcry_pk_testkey (gcry_sexp_t key)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
+ fips_service_indicator_init ();
return gpg_error (_gcry_pk_testkey (key));
}
gcry_error_t
gcry_pk_genkey (gcry_sexp_t *r_key, gcry_sexp_t s_parms)
{
if (!fips_is_operational ())
{
*r_key = NULL;
return gpg_error (fips_not_operational ());
}
+ fips_service_indicator_init ();
return gpg_error (_gcry_pk_genkey (r_key, s_parms));
}
gcry_error_t
gcry_pk_ctl (int cmd, void *buffer, size_t buflen)
{
return gpg_error (_gcry_pk_ctl (cmd, buffer, buflen));
}
gcry_error_t
gcry_pk_algo_info (int algo, int what, void *buffer, size_t *nbytes)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_pk_algo_info (algo, what, buffer, nbytes));
}
const char *
gcry_pk_algo_name (int algorithm)
{
return _gcry_pk_algo_name (algorithm);
}
int
gcry_pk_map_name (const char *name)
{
return _gcry_pk_map_name (name);
}
unsigned int
gcry_pk_get_nbits (gcry_sexp_t key)
{
if (!fips_is_operational ())
{
(void)fips_not_operational ();
return 0;
}
-
+ fips_service_indicator_init ();
return _gcry_pk_get_nbits (key);
}
unsigned char *
gcry_pk_get_keygrip (gcry_sexp_t key, unsigned char *array)
{
if (!fips_is_operational ())
{
(void)fips_not_operational ();
return NULL;
}
return _gcry_pk_get_keygrip (key, array);
}
const char *
gcry_pk_get_curve (gcry_sexp_t key, int iterator, unsigned int *r_nbits)
{
if (!fips_is_operational ())
{
(void)fips_not_operational ();
return NULL;
}
+ fips_service_indicator_init ();
return _gcry_pk_get_curve (key, iterator, r_nbits);
}
gcry_sexp_t
gcry_pk_get_param (int algo, const char *name)
{
if (!fips_is_operational ())
{
(void)fips_not_operational ();
return NULL;
}
return _gcry_pk_get_param (algo, name);
}
gcry_error_t
gcry_pubkey_get_sexp (gcry_sexp_t *r_sexp, int mode, gcry_ctx_t ctx)
{
if (!fips_is_operational ())
{
*r_sexp = NULL;
return gpg_error (fips_not_operational ());
}
return gpg_error (_gcry_pubkey_get_sexp (r_sexp, mode, ctx));
}
unsigned int
gcry_ecc_get_algo_keylen (int curveid)
{
return _gcry_ecc_get_algo_keylen (curveid);
}
gpg_error_t
gcry_ecc_mul_point (int curveid, unsigned char *result,
const unsigned char *scalar, const unsigned char *point)
{
return gpg_error (_gcry_ecc_mul_point (curveid, result, scalar, point));
}
gcry_error_t
gcry_md_open (gcry_md_hd_t *h, int algo, unsigned int flags)
{
if (!fips_is_operational ())
{
*h = NULL;
return gpg_error (fips_not_operational ());
}
fips_service_indicator_init ();
return gpg_error (_gcry_md_open (h, algo, flags));
}
void
gcry_md_close (gcry_md_hd_t hd)
{
_gcry_md_close (hd);
}
gcry_error_t
gcry_md_enable (gcry_md_hd_t hd, int algo)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
fips_service_indicator_init ();
return gpg_error (_gcry_md_enable (hd, algo));
}
gcry_error_t
gcry_md_copy (gcry_md_hd_t *bhd, gcry_md_hd_t ahd)
{
if (!fips_is_operational ())
{
*bhd = NULL;
return gpg_error (fips_not_operational ());
}
fips_service_indicator_init ();
return gpg_error (_gcry_md_copy (bhd, ahd));
}
void
gcry_md_reset (gcry_md_hd_t hd)
{
_gcry_md_reset (hd);
}
gcry_error_t
gcry_md_ctl (gcry_md_hd_t hd, int cmd, void *buffer, size_t buflen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_md_ctl (hd, cmd, buffer, buflen));
}
void
gcry_md_write (gcry_md_hd_t hd, const void *buffer, size_t length)
{
if (!fips_is_operational ())
{
(void)fips_not_operational ();
return;
}
_gcry_md_write (hd, buffer, length);
}
unsigned char *
gcry_md_read (gcry_md_hd_t hd, int algo)
{
return _gcry_md_read (hd, algo);
}
gcry_error_t
gcry_md_extract (gcry_md_hd_t hd, int algo, void *buffer, size_t length)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_md_extract (hd, algo, buffer, length));
}
void
gcry_md_hash_buffer (int algo, void *digest,
const void *buffer, size_t length)
{
if (!fips_is_operational ())
{
(void)fips_not_operational ();
fips_signal_error ("called in non-operational state");
}
fips_service_indicator_init ();
_gcry_md_hash_buffer (algo, digest, buffer, length);
}
gpg_error_t
gcry_md_hash_buffers (int algo, unsigned int flags, void *digest,
const gcry_buffer_t *iov, int iovcnt)
{
if (!fips_is_operational ())
{
(void)fips_not_operational ();
fips_signal_error ("called in non-operational state");
}
fips_service_indicator_init ();
return gpg_error (_gcry_md_hash_buffers (algo, flags, digest, iov, iovcnt));
}
gpg_error_t
gcry_md_hash_buffers_ext (int algo, unsigned int flags, void *digest,
int digestlen, const gcry_buffer_t *iov,
int iovcnt)
{
if (!fips_is_operational ())
{
(void)fips_not_operational ();
fips_signal_error ("called in non-operational state");
}
fips_service_indicator_init ();
return gpg_error (_gcry_md_hash_buffers_extract (algo, flags, digest,
digestlen, iov, iovcnt));
}
int
gcry_md_get_algo (gcry_md_hd_t hd)
{
if (!fips_is_operational ())
{
(void)fips_not_operational ();
fips_signal_error ("used in non-operational state");
return 0;
}
return _gcry_md_get_algo (hd);
}
unsigned int
gcry_md_get_algo_dlen (int algo)
{
return _gcry_md_get_algo_dlen (algo);
}
int
gcry_md_is_enabled (gcry_md_hd_t a, int algo)
{
if (!fips_is_operational ())
{
(void)fips_not_operational ();
return 0;
}
return _gcry_md_is_enabled (a, algo);
}
int
gcry_md_is_secure (gcry_md_hd_t a)
{
return _gcry_md_is_secure (a);
}
gcry_error_t
gcry_md_info (gcry_md_hd_t h, int what, void *buffer, size_t *nbytes)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_md_info (h, what, buffer, nbytes));
}
gcry_error_t
gcry_md_algo_info (int algo, int what, void *buffer, size_t *nbytes)
{
return gpg_error (_gcry_md_algo_info (algo, what, buffer, nbytes));
}
const char *
gcry_md_algo_name (int algo)
{
return _gcry_md_algo_name (algo);
}
int
gcry_md_map_name (const char* name)
{
return _gcry_md_map_name (name);
}
gcry_error_t
gcry_md_setkey (gcry_md_hd_t hd, const void *key, size_t keylen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
fips_service_indicator_init ();
if (fips_mode () && keylen < 14)
fips_service_indicator_mark_non_compliant ();
return gpg_error (_gcry_md_setkey (hd, key, keylen));
}
void
gcry_md_debug (gcry_md_hd_t hd, const char *suffix)
{
_gcry_md_debug (hd, suffix);
}
gpg_error_t
gcry_kdf_derive (const void *passphrase, size_t passphraselen,
int algo, int hashalgo,
const void *salt, size_t saltlen,
unsigned long iterations,
size_t keysize, void *keybuffer)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
fips_service_indicator_init ();
return gpg_error (_gcry_kdf_derive (passphrase, passphraselen, algo, hashalgo,
salt, saltlen, iterations,
keysize, keybuffer));
}
gpg_error_t
gcry_kdf_open (gcry_kdf_hd_t *hd, int algo, int subalgo,
const unsigned long *param, unsigned int paramlen,
const void *passphrase, size_t passphraselen,
const void *salt, size_t saltlen,
const void *key, size_t keylen,
const void *ad, size_t adlen)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_kdf_open (hd, algo, subalgo, param, paramlen,
passphrase, passphraselen, salt, saltlen,
key, keylen, ad, adlen));
}
gcry_error_t
gcry_kdf_compute (gcry_kdf_hd_t h, const struct gcry_kdf_thread_ops *ops)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_kdf_compute (h, ops));
}
gcry_error_t
gcry_kdf_final (gcry_kdf_hd_t h, size_t resultlen, void *result)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_kdf_final (h, resultlen, result));
}
void
gcry_kdf_close (gcry_kdf_hd_t h)
{
_gcry_kdf_close (h);
}
gcry_error_t
gcry_kem_genkey (int algo,
void *pubkey, size_t pubkey_len,
void *seckey, size_t seckey_len,
const void *optional, size_t optional_len)
{
return gpg_error (_gcry_kem_genkey (algo,
pubkey, pubkey_len,
seckey, seckey_len,
optional, optional_len));
}
gcry_error_t
gcry_kem_keypair (int algo,
void *pubkey, size_t pubkey_len,
void *seckey, size_t seckey_len)
{
return gpg_error (_gcry_kem_genkey (algo,
pubkey, pubkey_len,
seckey, seckey_len,
NULL, 0));
}
gcry_error_t
gcry_kem_encap (int algo,
const void *pubkey, size_t pubkey_len,
void *ciphertext, size_t ciphertext_len,
void *shared, size_t shared_len,
const void *optional, size_t optional_len)
{
return gpg_error (_gcry_kem_encap (algo,
pubkey, pubkey_len,
ciphertext, ciphertext_len,
shared, shared_len,
optional, optional_len));
}
gcry_error_t
gcry_kem_decap (int algo,
const void *seckey, size_t seckey_len,
const void *ciphertext, size_t ciphertext_len,
void *shared, size_t shared_len,
const void *optional, size_t optional_len)
{
return gpg_error (_gcry_kem_decap (algo,
seckey, seckey_len,
ciphertext, ciphertext_len,
shared, shared_len,
optional, optional_len));
}
void
gcry_randomize (void *buffer, size_t length, enum gcry_random_level level)
{
if (!fips_is_operational ())
{
(void)fips_not_operational ();
fips_signal_fatal_error ("called in non-operational state");
fips_noreturn ();
}
_gcry_randomize (buffer, length, level);
}
gcry_error_t
gcry_random_add_bytes (const void *buffer, size_t length, int quality)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_random_add_bytes (buffer, length, quality));
}
void *
gcry_random_bytes (size_t nbytes, enum gcry_random_level level)
{
if (!fips_is_operational ())
{
(void)fips_not_operational ();
fips_signal_fatal_error ("called in non-operational state");
fips_noreturn ();
}
return _gcry_random_bytes (nbytes,level);
}
void *
gcry_random_bytes_secure (size_t nbytes, enum gcry_random_level level)
{
if (!fips_is_operational ())
{
(void)fips_not_operational ();
fips_signal_fatal_error ("called in non-operational state");
fips_noreturn ();
}
return _gcry_random_bytes_secure (nbytes, level);
}
void
gcry_mpi_randomize (gcry_mpi_t w,
unsigned int nbits, enum gcry_random_level level)
{
if (!fips_is_operational ())
{
(void)fips_not_operational ();
fips_signal_fatal_error ("called in non-operational state");
fips_noreturn ();
}
_gcry_mpi_randomize (w, nbits, level);
}
void
gcry_create_nonce (void *buffer, size_t length)
{
if (!fips_is_operational ())
{
(void)fips_not_operational ();
fips_signal_fatal_error ("called in non-operational state");
fips_noreturn ();
}
_gcry_create_nonce (buffer, length);
}
gcry_error_t
gcry_prime_generate (gcry_mpi_t *prime,
unsigned int prime_bits,
unsigned int factor_bits,
gcry_mpi_t **factors,
gcry_prime_check_func_t cb_func,
void *cb_arg,
gcry_random_level_t random_level,
unsigned int flags)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_prime_generate (prime, prime_bits, factor_bits,
factors, cb_func, cb_arg,
random_level, flags));
}
gcry_error_t
gcry_prime_group_generator (gcry_mpi_t *r_g,
gcry_mpi_t prime, gcry_mpi_t *factors,
gcry_mpi_t start_g)
{
if (!fips_is_operational ())
return gpg_error (fips_not_operational ());
return gpg_error (_gcry_prime_group_generator (r_g, prime, factors, start_g));
}
void
gcry_prime_release_factors (gcry_mpi_t *factors)
{
_gcry_prime_release_factors (factors);
}
gcry_error_t
gcry_prime_check (gcry_mpi_t x, unsigned int flags)
{
return gpg_error (_gcry_prime_check (x, flags));
}
void
gcry_ctx_release (gcry_ctx_t ctx)
{
_gcry_ctx_release (ctx);
}
void
gcry_log_debug (const char *fmt, ...)
{
va_list arg_ptr ;
va_start( arg_ptr, fmt ) ;
_gcry_logv (GCRY_LOG_DEBUG, fmt, arg_ptr);
va_end (arg_ptr);
}
void
gcry_log_debughex (const char *text, const void *buffer, size_t length)
{
_gcry_log_printhex (text, buffer, length);
}
void
gcry_log_debugmpi (const char *text, gcry_mpi_t mpi)
{
_gcry_log_printmpi (text, mpi);
}
void
gcry_log_debugpnt (const char *text, mpi_point_t point, gcry_ctx_t ctx)
{
mpi_ec_t ec = ctx? _gcry_ctx_get_pointer (ctx, CONTEXT_TYPE_EC) : NULL;
_gcry_mpi_point_log (text, point, ec);
}
void
gcry_log_debugsxp (const char *text, gcry_sexp_t sexp)
{
_gcry_log_printsxp (text, sexp);
}
char *
gcry_get_config (int mode, const char *what)
{
return _gcry_get_config (mode, what);
}
void
gcry_set_progress_handler (gcry_handler_progress_t cb, void *cb_data)
{
_gcry_set_progress_handler (cb, cb_data);
}
void
gcry_set_allocation_handler (gcry_handler_alloc_t func_alloc,
gcry_handler_alloc_t func_alloc_secure,
gcry_handler_secure_check_t func_secure_check,
gcry_handler_realloc_t func_realloc,
gcry_handler_free_t func_free)
{
_gcry_set_allocation_handler (func_alloc, func_alloc_secure,
func_secure_check, func_realloc, func_free);
}
void
gcry_set_outofcore_handler (gcry_handler_no_mem_t h, void *opaque)
{
_gcry_set_outofcore_handler (h, opaque);
}
void
gcry_set_fatalerror_handler (gcry_handler_error_t fnc, void *opaque)
{
_gcry_set_fatalerror_handler (fnc, opaque);
}
void
gcry_set_log_handler (gcry_handler_log_t f, void *opaque)
{
_gcry_set_log_handler (f, opaque);
}
void
gcry_set_gettext_handler (const char *(*f)(const char*))
{
_gcry_set_gettext_handler (f);
}
void *
gcry_malloc (size_t n)
{
return _gcry_malloc (n);
}
void *
gcry_calloc (size_t n, size_t m)
{
return _gcry_calloc (n, m);
}
void *
gcry_malloc_secure (size_t n)
{
return _gcry_malloc_secure (n);
}
void *
gcry_calloc_secure (size_t n, size_t m)
{
return _gcry_calloc_secure (n,m);
}
void *
gcry_realloc (void *a, size_t n)
{
return _gcry_realloc (a, n);
}
char *
gcry_strdup (const char *string)
{
return _gcry_strdup (string);
}
void *
gcry_xmalloc (size_t n)
{
return _gcry_xmalloc (n);
}
void *
gcry_xcalloc (size_t n, size_t m)
{
return _gcry_xcalloc (n, m);
}
void *
gcry_xmalloc_secure (size_t n)
{
return _gcry_xmalloc_secure (n);
}
void *
gcry_xcalloc_secure (size_t n, size_t m)
{
return _gcry_xcalloc_secure (n, m);
}
void *
gcry_xrealloc (void *a, size_t n)
{
return _gcry_xrealloc (a, n);
}
char *
gcry_xstrdup (const char *a)
{
return _gcry_xstrdup (a);
}
void
gcry_free (void *a)
{
_gcry_free (a);
}
int
gcry_is_secure (const void *a)
{
return _gcry_is_secure (a);
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Feb 8, 2:57 PM (13 m, 12 s)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
52/57/e69d2bf263711fd3e38db5c8868b
Attached To
rC libgcrypt
Event Timeline
Log In to Comment