summaryrefslogtreecommitdiffstats
path: root/lib/nettle/pk.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/nettle/pk.c')
-rw-r--r--lib/nettle/pk.c3969
1 files changed, 3969 insertions, 0 deletions
diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c
new file mode 100644
index 0000000..f38016b
--- /dev/null
+++ b/lib/nettle/pk.c
@@ -0,0 +1,3969 @@
+/*
+ * Copyright (C) 2010-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2013-2017 Nikos Mavrogiannopoulos
+ * Copyright (C) 2016-2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GNUTLS.
+ *
+ * The GNUTLS library 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.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+/* This file contains the functions needed for RSA/DSA public key
+ * encryption and signatures.
+ */
+
+#include "gnutls_int.h"
+#include <mpi.h>
+#include <pk.h>
+#include "errors.h"
+#include <datum.h>
+#include <global.h>
+#include <tls-sig.h>
+#include <num.h>
+#include <x509/x509_int.h>
+#include <x509/common.h>
+#include <random.h>
+#include <pk.h>
+#include <nettle/dsa.h>
+#include <dsa-fips.h>
+#include <rsa-fips.h>
+#include <nettle/rsa.h>
+#include <gnutls/crypto.h>
+#include <nettle/bignum.h>
+#include <nettle/ecc.h>
+#include <nettle/ecdsa.h>
+#include <nettle/ecc-curve.h>
+#include <nettle/curve25519.h>
+#include <nettle/curve448.h>
+#include <nettle/eddsa.h>
+#include <nettle/version.h>
+#if ENABLE_GOST
+#if NEED_INT_ECC
+#include "ecc/gostdsa.h"
+#include "ecc-gost-curve.h"
+#else
+#include <nettle/gostdsa.h>
+#define gost_point_mul_g ecc_point_mul_g
+#define gost_point_set ecc_point_set
+#endif
+#include "gost/gostdsa2.h"
+#endif
+#include "int/ecdsa-compute-k.h"
+#include "int/dsa-compute-k.h"
+#include <gnettle.h>
+#include <fips.h>
+#include "dh.h"
+
+static inline const struct ecc_curve *get_supported_nist_curve(int curve);
+static inline const struct ecc_curve *get_supported_gost_curve(int curve);
+
+/* When these callbacks are used for a nettle operation, the
+ * caller must check the macro HAVE_LIB_ERROR() after the operation
+ * is complete. If the macro is true, the operation is to be considered
+ * failed (meaning the random generation failed).
+ */
+static void rnd_key_func(void *_ctx, size_t length, uint8_t * data)
+{
+ if (gnutls_rnd(GNUTLS_RND_KEY, data, length) < 0) {
+ _gnutls_switch_lib_state(LIB_STATE_ERROR);
+ }
+}
+
+static void rnd_tmpkey_func(void *_ctx, size_t length, uint8_t * data)
+{
+ if (gnutls_rnd(GNUTLS_RND_RANDOM, data, length) < 0) {
+ _gnutls_switch_lib_state(LIB_STATE_ERROR);
+ }
+}
+
+static void rnd_nonce_func(void *_ctx, size_t length, uint8_t * data)
+{
+ if (gnutls_rnd(GNUTLS_RND_NONCE, data, length) < 0) {
+ _gnutls_switch_lib_state(LIB_STATE_ERROR);
+ }
+}
+
+static void rnd_mpz_func(void *_ctx, size_t length, uint8_t * data)
+{
+ mpz_t *k = _ctx;
+ nettle_mpz_get_str_256 (length, data, *k);
+}
+
+static void rnd_nonce_func_fallback(void *_ctx, size_t length, uint8_t * data)
+{
+ if (unlikely(_gnutls_get_lib_state() != LIB_STATE_SELFTEST)) {
+ _gnutls_switch_lib_state(LIB_STATE_ERROR);
+ }
+
+ memset(data, 0xAA, length);
+}
+
+static void
+ecc_scalar_zclear (struct ecc_scalar *s)
+{
+ zeroize_key(s->p, ecc_size(s->ecc)*sizeof(mp_limb_t));
+ ecc_scalar_clear(s);
+}
+
+static void
+ecc_point_zclear (struct ecc_point *p)
+{
+ zeroize_key(p->p, ecc_size_a(p->ecc)*sizeof(mp_limb_t));
+ ecc_point_clear(p);
+}
+
+static void
+_dsa_params_get(const gnutls_pk_params_st * pk_params,
+ struct dsa_params *pub)
+{
+ memcpy(pub->p, pk_params->params[DSA_P], SIZEOF_MPZT);
+
+ if (pk_params->params[DSA_Q])
+ memcpy(&pub->q, pk_params->params[DSA_Q], SIZEOF_MPZT);
+ memcpy(pub->g, pk_params->params[DSA_G], SIZEOF_MPZT);
+}
+
+static void
+_rsa_params_to_privkey(const gnutls_pk_params_st * pk_params,
+ struct rsa_private_key *priv)
+{
+ memcpy(priv->d, pk_params->params[RSA_PRIV], SIZEOF_MPZT);
+ memcpy(priv->p, pk_params->params[RSA_PRIME1], SIZEOF_MPZT);
+ memcpy(priv->q, pk_params->params[RSA_PRIME2], SIZEOF_MPZT);
+ memcpy(priv->c, pk_params->params[RSA_COEF], SIZEOF_MPZT);
+ memcpy(priv->a, pk_params->params[RSA_E1], SIZEOF_MPZT);
+ memcpy(priv->b, pk_params->params[RSA_E2], SIZEOF_MPZT);
+ /* we do not rsa_private_key_prepare() because it involves a multiplication.
+ * we call it once when we import the parameters */
+ priv->size =
+ nettle_mpz_sizeinbase_256_u(TOMPZ
+ (pk_params->params[RSA_MODULUS]));
+}
+
+/* returns a negative value on invalid pubkey */
+static int
+_rsa_params_to_pubkey(const gnutls_pk_params_st * pk_params,
+ struct rsa_public_key *pub)
+{
+ memcpy(pub->n, pk_params->params[RSA_MODULUS], SIZEOF_MPZT);
+ memcpy(pub->e, pk_params->params[RSA_PUB], SIZEOF_MPZT);
+ if (rsa_public_key_prepare(pub) == 0)
+ return gnutls_assert_val(GNUTLS_E_PK_INVALID_PUBKEY);
+
+ return 0;
+}
+
+static int
+_ecc_params_to_privkey(const gnutls_pk_params_st * pk_params,
+ struct ecc_scalar *priv,
+ const struct ecc_curve *curve)
+{
+ ecc_scalar_init(priv, curve);
+ if (ecc_scalar_set(priv, pk_params->params[ECC_K]) == 0) {
+ ecc_scalar_clear(priv);
+ return gnutls_assert_val(GNUTLS_E_PK_INVALID_PRIVKEY);
+ }
+
+ return 0;
+}
+
+static int
+_ecc_params_to_pubkey(const gnutls_pk_params_st * pk_params,
+ struct ecc_point *pub, const struct ecc_curve *curve)
+{
+ ecc_point_init(pub, curve);
+ if (ecc_point_set
+ (pub, pk_params->params[ECC_X], pk_params->params[ECC_Y]) == 0) {
+ ecc_point_clear(pub);
+ return gnutls_assert_val(GNUTLS_E_PK_INVALID_PUBKEY);
+ }
+
+ return 0;
+}
+
+#if ENABLE_GOST
+static int
+_gost_params_to_privkey(const gnutls_pk_params_st * pk_params,
+ struct ecc_scalar *priv,
+ const struct ecc_curve *curve)
+{
+ ecc_scalar_init(priv, curve);
+ if (ecc_scalar_set(priv, pk_params->params[GOST_K]) == 0) {
+ ecc_scalar_clear(priv);
+ return gnutls_assert_val(GNUTLS_E_PK_INVALID_PRIVKEY);
+ }
+
+ return 0;
+}
+
+static int
+_gost_params_to_pubkey(const gnutls_pk_params_st * pk_params,
+ struct ecc_point *pub, const struct ecc_curve *curve)
+{
+ ecc_point_init(pub, curve);
+ if (gost_point_set
+ (pub, pk_params->params[GOST_X], pk_params->params[GOST_Y]) == 0) {
+ ecc_point_clear(pub);
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ return 0;
+}
+#endif
+
+static int
+ecc_shared_secret(struct ecc_scalar *private_key,
+ struct ecc_point *public_key, void *out, unsigned size)
+{
+ struct ecc_point r;
+ mpz_t x, y;
+ int ret = 0;
+
+ mpz_init(x);
+ mpz_init(y);
+ ecc_point_init(&r, public_key->ecc);
+
+ ecc_point_mul(&r, private_key, public_key);
+
+ ecc_point_get(&r, x, y);
+
+ /* Check if the point is not an identity element. Note that this cannot
+ * happen in nettle implementation, because it cannot represent an
+ * infinity point. */
+ if (mpz_cmp_ui(x, 0) == 0 && mpz_cmp_ui(y, 0) == 0) {
+ ret = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ goto cleanup;
+ }
+
+ nettle_mpz_get_str_256(size, out, x);
+
+ cleanup:
+ mpz_clear(x);
+ mpz_clear(y);
+ ecc_point_clear(&r);
+
+ return ret;
+}
+
+#define MAX_DH_BITS DEFAULT_MAX_VERIFY_BITS
+/* This is used when we have no idea on the structure
+ * of p-1 used by the peer. It is still a conservative
+ * choice, but small than what we've been using before.
+ */
+#define DH_EXPONENT_SIZE(p_size) (2*_gnutls_pk_bits_to_subgroup_bits(p_size))
+
+static inline int
+edwards_curve_mul(gnutls_pk_algorithm_t algo,
+ uint8_t *q, const uint8_t *n, const uint8_t *p)
+{
+ switch (algo) {
+ case GNUTLS_PK_ECDH_X25519:
+ curve25519_mul(q, n, p);
+ return 0;
+ case GNUTLS_PK_ECDH_X448:
+ curve448_mul(q, n, p);
+ return 0;
+ default:
+ return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ }
+}
+
+/* This is used for DH or ECDH key derivation. In DH for example
+ * it is given the peers Y and our x, and calculates Y^x
+ */
+static int _wrap_nettle_pk_derive(gnutls_pk_algorithm_t algo,
+ gnutls_datum_t * out,
+ const gnutls_pk_params_st * priv,
+ const gnutls_pk_params_st * pub,
+ const gnutls_datum_t * nonce,
+ unsigned int flags)
+{
+ int ret;
+ bool not_approved = false;
+
+ switch (algo) {
+ case GNUTLS_PK_DH: {
+ bigint_t f, x, q, prime;
+ bigint_t k = NULL, primesub1 = NULL, r = NULL;
+ unsigned int bits;
+
+ if (nonce != NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ f = pub->params[DH_Y];
+ x = priv->params[DH_X];
+ q = priv->params[DH_Q];
+ prime = priv->params[DH_P];
+
+ ret = _gnutls_mpi_init_multi(&k, &primesub1, &r, NULL);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_mpi_sub_ui(primesub1, prime, 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto dh_cleanup;
+ }
+
+ /* check if f==0,1, or f >= p-1 */
+ if ((_gnutls_mpi_cmp_ui(f, 1) == 0)
+ || (_gnutls_mpi_cmp_ui(f, 0) == 0)
+ || (_gnutls_mpi_cmp(f, primesub1) >= 0)) {
+ gnutls_assert();
+ ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
+ goto dh_cleanup;
+ }
+
+ /* if we have Q check that y ^ q mod p == 1 */
+ if (q != NULL) {
+ ret = _gnutls_mpi_powm(r, f, q, prime);
+ if (ret < 0) {
+ gnutls_assert();
+ goto dh_cleanup;
+ }
+ ret = _gnutls_mpi_cmp_ui(r, 1);
+ if (ret != 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
+ goto dh_cleanup;
+ }
+ } else if ((flags & PK_DERIVE_TLS13) &&
+ _gnutls_fips_mode_enabled()) {
+ /* Mandatory in FIPS mode for TLS 1.3 */
+ ret = gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ goto dh_cleanup;
+ }
+
+ /* prevent denial of service */
+ bits = _gnutls_mpi_get_nbits(prime);
+ if (bits == 0 || bits > MAX_DH_BITS) {
+ gnutls_assert();
+ ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
+ goto dh_cleanup;
+ }
+
+ if (bits < 2048) {
+ not_approved = true;
+ }
+
+ ret = _gnutls_mpi_powm(k, f, x, prime);
+ if (ret < 0) {
+ gnutls_assert();
+ goto dh_cleanup;
+ }
+
+ /* check if k==0,1, or k = p-1 */
+ if ((_gnutls_mpi_cmp_ui(k, 1) == 0)
+ || (_gnutls_mpi_cmp_ui(k, 0) == 0)
+ || (_gnutls_mpi_cmp(k, primesub1) == 0)) {
+ ret = gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ goto dh_cleanup;
+ }
+
+ if (flags & PK_DERIVE_TLS13) {
+ ret =
+ _gnutls_mpi_dprint_size(k, out,
+ (bits+7)/8);
+ } else {
+ ret = _gnutls_mpi_dprint(k, out);
+ }
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto dh_cleanup;
+ }
+
+ ret = 0;
+dh_cleanup:
+ _gnutls_mpi_release(&r);
+ _gnutls_mpi_release(&primesub1);
+ zrelease_temp_mpi_key(&k);
+ if (ret < 0)
+ goto cleanup;
+
+ break;
+ }
+ case GNUTLS_PK_EC:
+ {
+ struct ecc_scalar ecc_priv;
+ struct ecc_point ecc_pub;
+ const struct ecc_curve *curve;
+
+ out->data = NULL;
+
+ if (nonce != NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ curve = get_supported_nist_curve(priv->curve);
+ if (curve == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ goto cleanup;
+ }
+
+ /* P-192 is not supported in FIPS 140-3 */
+ if (priv->curve == GNUTLS_ECC_CURVE_SECP192R1) {
+ not_approved = true;
+ }
+
+ ret = _ecc_params_to_pubkey(pub, &ecc_pub, curve);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _ecc_params_to_privkey(priv, &ecc_priv, curve);
+ if (ret < 0) {
+ ecc_point_clear(&ecc_pub);
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ out->size = gnutls_ecc_curve_get_size(priv->curve);
+ /*ecc_size(curve)*sizeof(mp_limb_t); */
+ out->data = gnutls_malloc(out->size);
+ if (out->data == NULL) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_MEMORY_ERROR);
+ goto ecc_cleanup;
+ }
+
+ ret = ecc_shared_secret(&ecc_priv, &ecc_pub, out->data,
+ out->size);
+ if (ret < 0)
+ gnutls_free(out->data);
+
+ ecc_cleanup:
+ ecc_point_clear(&ecc_pub);
+ ecc_scalar_zclear(&ecc_priv);
+ if (ret < 0)
+ goto cleanup;
+ break;
+ }
+ case GNUTLS_PK_ECDH_X25519:
+ case GNUTLS_PK_ECDH_X448:
+ {
+ unsigned size = gnutls_ecc_curve_get_size(priv->curve);
+
+ /* Edwards curves are not approved */
+ not_approved = true;
+
+ if (nonce != NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ /* The point is in pub, while the private part (scalar) in priv. */
+
+ if (size == 0 || priv->raw_priv.size != size) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ out->data = gnutls_malloc(size);
+ if (out->data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ out->size = size;
+
+ ret = edwards_curve_mul(algo, out->data, priv->raw_priv.data, pub->raw_pub.data);
+ if (ret < 0)
+ goto cleanup;
+
+ if (_gnutls_mem_is_zero(out->data, out->size)) {
+ gnutls_free(out->data);
+ gnutls_assert();
+ ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
+ goto cleanup;
+ }
+ break;
+ }
+#if ENABLE_GOST
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ {
+ struct ecc_scalar ecc_priv;
+ struct ecc_point ecc_pub;
+ const struct ecc_curve *curve;
+
+ /* GOST curves are not approved */
+ not_approved = true;
+
+ out->data = NULL;
+
+ curve = get_supported_gost_curve(priv->curve);
+ if (curve == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE;
+ goto cleanup;
+ }
+
+ if (nonce == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ ret = _gost_params_to_pubkey(pub, &ecc_pub, curve);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gost_params_to_privkey(priv, &ecc_priv, curve);
+ if (ret < 0) {
+ ecc_point_clear(&ecc_pub);
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ out->size = 2 * gnutls_ecc_curve_get_size(priv->curve);
+ out->data = gnutls_malloc(out->size);
+ if (out->data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto gost_cleanup;
+ }
+
+ gostdsa_vko(&ecc_priv, &ecc_pub,
+ nonce->size, nonce->data,
+ out->data);
+
+ gost_cleanup:
+ ecc_point_clear(&ecc_pub);
+ ecc_scalar_zclear(&ecc_priv);
+ if (ret < 0)
+ goto cleanup;
+ break;
+ }
+#endif
+ default:
+ gnutls_assert();
+ ret = GNUTLS_E_INTERNAL_ERROR;
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else if (not_approved) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+
+ return ret;
+}
+
+static int
+_wrap_nettle_pk_encrypt(gnutls_pk_algorithm_t algo,
+ gnutls_datum_t * ciphertext,
+ const gnutls_datum_t * plaintext,
+ const gnutls_pk_params_st * pk_params)
+{
+ int ret;
+ mpz_t p;
+
+ FAIL_IF_LIB_ERROR;
+
+ mpz_init(p);
+
+ switch (algo) {
+ case GNUTLS_PK_RSA:
+ {
+ struct rsa_public_key pub;
+ nettle_random_func *random_func;
+
+ ret = _rsa_params_to_pubkey(pk_params, &pub);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (_gnutls_get_lib_state() == LIB_STATE_SELFTEST)
+ random_func = rnd_nonce_func_fallback;
+ else
+ random_func = rnd_nonce_func;
+ ret =
+ rsa_encrypt(&pub, NULL, random_func,
+ plaintext->size, plaintext->data,
+ p);
+ if (ret == 0 || HAVE_LIB_ERROR()) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_ENCRYPTION_FAILED);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_mpi_dprint_size(p, ciphertext,
+ pub.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ break;
+ }
+ default:
+ gnutls_assert();
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ }
+
+ mpz_clear(p);
+
+ FAIL_IF_LIB_ERROR;
+ return ret;
+}
+
+static int
+_wrap_nettle_pk_decrypt(gnutls_pk_algorithm_t algo,
+ gnutls_datum_t * plaintext,
+ const gnutls_datum_t * ciphertext,
+ const gnutls_pk_params_st * pk_params)
+{
+ int ret;
+
+ FAIL_IF_LIB_ERROR;
+
+ plaintext->data = NULL;
+
+ /* make a sexp from pkey */
+ switch (algo) {
+ case GNUTLS_PK_RSA:
+ {
+ struct rsa_private_key priv;
+ struct rsa_public_key pub;
+ size_t length;
+ bigint_t c;
+ nettle_random_func *random_func;
+
+ _rsa_params_to_privkey(pk_params, &priv);
+ ret = _rsa_params_to_pubkey(pk_params, &pub);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (ciphertext->size != pub.size) {
+ ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ goto cleanup;
+ }
+
+ if (_gnutls_mpi_init_scan_nz
+ (&c, ciphertext->data,
+ ciphertext->size) != 0) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_MPI_SCAN_FAILED);
+ goto cleanup;
+ }
+
+ length = pub.size;
+ plaintext->data = gnutls_malloc(length);
+ if (plaintext->data == NULL) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ if (_gnutls_get_lib_state() == LIB_STATE_SELFTEST)
+ random_func = rnd_nonce_func_fallback;
+ else
+ random_func = rnd_nonce_func;
+ ret =
+ rsa_decrypt_tr(&pub, &priv, NULL, random_func,
+ &length, plaintext->data,
+ TOMPZ(c));
+ _gnutls_mpi_release(&c);
+ plaintext->size = length;
+
+ if (ret == 0 || HAVE_LIB_ERROR()) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_DECRYPTION_FAILED);
+ goto cleanup;
+ }
+
+ break;
+ }
+ default:
+ gnutls_assert();
+ ret = GNUTLS_E_INTERNAL_ERROR;
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (ret < 0) {
+ gnutls_free(plaintext->data);
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ }
+
+ FAIL_IF_LIB_ERROR;
+ return ret;
+}
+
+/* Note: we do not allocate in this function to avoid asymettric
+ * unallocation (which creates a side channel) in case of failure
+ * */
+static int
+_wrap_nettle_pk_decrypt2(gnutls_pk_algorithm_t algo,
+ const gnutls_datum_t * ciphertext,
+ unsigned char * plaintext,
+ size_t plaintext_size,
+ const gnutls_pk_params_st * pk_params)
+{
+ struct rsa_private_key priv;
+ struct rsa_public_key pub;
+ bigint_t c;
+ uint32_t is_err;
+ int ret;
+ nettle_random_func *random_func;
+
+ FAIL_IF_LIB_ERROR;
+
+ if (algo != GNUTLS_PK_RSA || plaintext == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ goto fail;
+ }
+
+ _rsa_params_to_privkey(pk_params, &priv);
+ ret = _rsa_params_to_pubkey(pk_params, &pub);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ if (ciphertext->size != pub.size) {
+ ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ goto fail;
+ }
+
+ if (_gnutls_mpi_init_scan_nz(&c, ciphertext->data,
+ ciphertext->size) != 0) {
+ ret = gnutls_assert_val(GNUTLS_E_MPI_SCAN_FAILED);
+ goto fail;
+ }
+
+ if (_gnutls_get_lib_state() == LIB_STATE_SELFTEST)
+ random_func = rnd_nonce_func_fallback;
+ else
+ random_func = rnd_nonce_func;
+ ret = rsa_sec_decrypt(&pub, &priv, NULL, random_func,
+ plaintext_size, plaintext, TOMPZ(c));
+
+ /* The decrypt operation is infallible; treat the approved
+ * operation as complete at this point, regardless of any
+ * decryption failure detected below.
+ */
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+
+ /* after this point, any conditional on failure that cause differences
+ * in execution may create a timing or cache access pattern side
+ * channel that can be used as an oracle, so thread very carefully */
+ _gnutls_mpi_release(&c);
+ /* Here HAVE_LIB_ERROR() should be fine as it doesn't have
+ * branches in it and returns a bool */
+ is_err = HAVE_LIB_ERROR();
+ /* if is_err != 0 */
+ is_err = CONSTCHECK_NOT_EQUAL(is_err, 0);
+ /* or ret == 0 */
+ is_err |= CONSTCHECK_EQUAL(ret, 0);
+ /* then return GNUTLS_E_DECRYPTION_FAILED */
+ return (int)((is_err * UINT_MAX) & GNUTLS_E_DECRYPTION_FAILED);
+
+ fail:
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+
+ return ret;
+}
+
+#define CHECK_INVALID_RSA_PSS_PARAMS(dig_size, salt_size, pub_size, err) \
+ if (unlikely(dig_size + salt_size + 2 > pub_size)) \
+ return gnutls_assert_val(err)
+
+static int
+_rsa_pss_sign_digest_tr(gnutls_digest_algorithm_t dig,
+ const struct rsa_public_key *pub,
+ const struct rsa_private_key *priv,
+ void *rnd_ctx, nettle_random_func *rnd_func,
+ size_t salt_size,
+ const uint8_t *digest,
+ mpz_t s)
+{
+ int (*sign_func)(const struct rsa_public_key *,
+ const struct rsa_private_key *,
+ void *, nettle_random_func *,
+ size_t, const uint8_t *,
+ const uint8_t *,
+ mpz_t);
+ uint8_t *salt = NULL;
+ size_t hash_size;
+ int ret;
+
+ switch (dig) {
+ case GNUTLS_DIG_SHA256:
+ sign_func = rsa_pss_sha256_sign_digest_tr;
+ hash_size = 32;
+ break;
+ case GNUTLS_DIG_SHA384:
+ sign_func = rsa_pss_sha384_sign_digest_tr;
+ hash_size = 48;
+ break;
+ case GNUTLS_DIG_SHA512:
+ sign_func = rsa_pss_sha512_sign_digest_tr;
+ hash_size = 64;
+ break;
+ default:
+ gnutls_assert();
+ return GNUTLS_E_UNKNOWN_ALGORITHM;
+ }
+
+ /* This is also checked in pss_encode_mgf1, but error out earlier. */
+ CHECK_INVALID_RSA_PSS_PARAMS(hash_size, salt_size, pub->size, GNUTLS_E_PK_INVALID_PUBKEY_PARAMS);
+
+ if (salt_size > 0) {
+ salt = gnutls_malloc(salt_size);
+ if (salt == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ ret = gnutls_rnd(GNUTLS_RND_NONCE, salt, salt_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ ret = sign_func(pub, priv, rnd_ctx, rnd_func, salt_size, salt,
+ digest, s);
+ if (ret == 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PK_SIGN_FAILED;
+ } else
+ ret = 0;
+
+ cleanup:
+ gnutls_free(salt);
+ return ret;
+}
+
+static inline gnutls_ecc_curve_t
+get_eddsa_curve(gnutls_pk_algorithm_t algo)
+{
+ switch (algo) {
+ case GNUTLS_PK_EDDSA_ED25519:
+ return GNUTLS_ECC_CURVE_ED25519;
+ case GNUTLS_PK_EDDSA_ED448:
+ return GNUTLS_ECC_CURVE_ED448;
+ default:
+ return gnutls_assert_val(GNUTLS_ECC_CURVE_INVALID);
+ }
+}
+
+static inline gnutls_ecc_curve_t
+get_ecdh_curve(gnutls_pk_algorithm_t algo)
+{
+ switch (algo) {
+ case GNUTLS_PK_ECDH_X25519:
+ return GNUTLS_ECC_CURVE_X25519;
+ case GNUTLS_PK_ECDH_X448:
+ return GNUTLS_ECC_CURVE_X448;
+ default:
+ return gnutls_assert_val(GNUTLS_ECC_CURVE_INVALID);
+ }
+}
+
+static inline int
+eddsa_sign(gnutls_pk_algorithm_t algo,
+ const uint8_t *pub,
+ const uint8_t *priv,
+ size_t length, const uint8_t *msg,
+ uint8_t *signature)
+{
+ switch (algo) {
+ case GNUTLS_PK_EDDSA_ED25519:
+ ed25519_sha512_sign(pub, priv, length, msg, signature);
+ return 0;
+ case GNUTLS_PK_EDDSA_ED448:
+ ed448_shake256_sign(pub, priv, length, msg, signature);
+ return 0;
+ default:
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM);
+ }
+}
+
+/* This is the lower-level part of privkey_sign_raw_data().
+ *
+ * It accepts data in the appropriate hash form, i.e., DigestInfo
+ * for PK_RSA, hash for PK_ECDSA, PK_DSA, PK_RSA_PSS, and raw data
+ * for Ed25519 and Ed448.
+ *
+ * in case of EC/DSA, signed data are encoded into r,s values
+ */
+static int
+_wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo,
+ gnutls_datum_t * signature,
+ const gnutls_datum_t * vdata,
+ const gnutls_pk_params_st * pk_params,
+ const gnutls_x509_spki_st * sign_params)
+{
+ int ret;
+ unsigned int hash_len;
+ const mac_entry_st *me;
+ bool not_approved = false;
+
+ FAIL_IF_LIB_ERROR;
+
+ /* check if the curve relates to the algorithm used */
+ if (IS_EC(algo) && gnutls_ecc_curve_get_pk(pk_params->curve) != algo) {
+ ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ goto cleanup;
+ }
+
+ /* deterministic ECDSA/DSA is prohibited under FIPS except in
+ * the selftests */
+ if ((algo == GNUTLS_PK_DSA || algo == GNUTLS_PK_ECDSA) &&
+ (sign_params->flags & GNUTLS_PK_FLAG_REPRODUCIBLE) &&
+ _gnutls_fips_mode_enabled() &&
+ _gnutls_get_lib_state() != LIB_STATE_SELFTEST) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ switch (algo) {
+ case GNUTLS_PK_EDDSA_ED25519: /* we do EdDSA */
+ case GNUTLS_PK_EDDSA_ED448:
+ {
+ const gnutls_ecc_curve_entry_st *e;
+
+ /* EdDSA is not approved yet */
+ not_approved = true;
+
+ if (unlikely(get_eddsa_curve(algo) != pk_params->curve)) {
+ ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ goto cleanup;
+ }
+
+ e = _gnutls_ecc_curve_get_params(pk_params->curve);
+ if (e == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ signature->data = gnutls_malloc(e->sig_size);
+ if (signature->data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ signature->size = e->sig_size;
+
+ if (pk_params->raw_pub.size != e->size || pk_params->raw_priv.size != e->size) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_SIGN_FAILED);
+ goto cleanup;
+ }
+
+ ret = eddsa_sign(algo,
+ pk_params->raw_pub.data,
+ pk_params->raw_priv.data,
+ vdata->size, vdata->data,
+ signature->data);
+ if (ret < 0)
+ goto cleanup;
+
+ break;
+ }
+#if ENABLE_GOST
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ {
+ struct ecc_scalar priv;
+ struct dsa_signature sig;
+ const struct ecc_curve *curve;
+
+ /* GOSTDSA is not approved */
+ not_approved = true;
+
+ curve = get_supported_gost_curve(pk_params->curve);
+ if (curve == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ goto cleanup;
+ }
+
+ ret =
+ _ecc_params_to_privkey(pk_params, &priv,
+ curve);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* This call will return a valid MAC entry and
+ * getters will check that is not null anyway. */
+ me = hash_to_entry(_gnutls_gost_digest(pk_params->algo));
+ if (_gnutls_mac_get_algo_len(me) != vdata->size) {
+ _gnutls_debug_log
+ ("Security level of algorithm requires hash %s(%zd)\n",
+ _gnutls_mac_get_name(me),
+ _gnutls_mac_get_algo_len(me));
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ dsa_signature_init(&sig);
+
+ gostdsa_sign(&priv, NULL, rnd_tmpkey_func,
+ vdata->size, vdata->data, &sig);
+
+ ret =
+ _gnutls_encode_gost_rs(signature, &sig.r, &sig.s,
+ (ecc_bit_size(curve) + 7) / 8);
+
+ dsa_signature_clear(&sig);
+ ecc_scalar_zclear(&priv);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ break;
+ }
+#endif
+ case GNUTLS_PK_ECDSA: /* we do ECDSA */
+ {
+ struct ecc_scalar priv;
+ struct dsa_signature sig;
+ int curve_id = pk_params->curve;
+ const struct ecc_curve *curve;
+ mpz_t k;
+ void *random_ctx;
+ nettle_random_func *random_func;
+
+ curve = get_supported_nist_curve(curve_id);
+ if (curve == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ goto cleanup;
+ }
+
+ /* P-192 is not supported in FIPS 140-3 */
+ if (curve_id == GNUTLS_ECC_CURVE_SECP192R1) {
+ not_approved = true;
+ }
+
+ ret =
+ _ecc_params_to_privkey(pk_params, &priv,
+ curve);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ dsa_signature_init(&sig);
+
+ me = _gnutls_dsa_q_to_hash(pk_params,
+ &hash_len);
+
+ /* Only SHA-2 is allowed in FIPS 140-3 */
+ switch (me->id) {
+ case GNUTLS_MAC_SHA256:
+ case GNUTLS_MAC_SHA384:
+ case GNUTLS_MAC_SHA512:
+ case GNUTLS_MAC_SHA224:
+ break;
+ default:
+ not_approved = true;
+ }
+
+ if (hash_len > vdata->size) {
+ gnutls_assert();
+ _gnutls_debug_log
+ ("Security level of algorithm requires hash %s(%d) or better\n",
+ _gnutls_mac_get_name(me), hash_len);
+ hash_len = vdata->size;
+ }
+
+ mpz_init(k);
+ if (_gnutls_get_lib_state() == LIB_STATE_SELFTEST ||
+ (sign_params->flags & GNUTLS_PK_FLAG_REPRODUCIBLE)) {
+ ret = _gnutls_ecdsa_compute_k(k,
+ curve_id,
+ pk_params->params[ECC_K],
+ DIG_TO_MAC(sign_params->dsa_dig),
+ vdata->data,
+ vdata->size);
+ if (ret < 0)
+ goto ecdsa_cleanup;
+ random_ctx = &k;
+ random_func = rnd_mpz_func;
+ } else {
+ random_ctx = NULL;
+ random_func = rnd_nonce_func;
+ }
+ ecdsa_sign(&priv, random_ctx, random_func, hash_len,
+ vdata->data, &sig);
+
+ /* prevent memory leaks */
+ if (HAVE_LIB_ERROR()) {
+ ret = GNUTLS_E_LIB_IN_ERROR_STATE;
+ goto ecdsa_cleanup;
+ }
+
+ ret =
+ _gnutls_encode_ber_rs(signature, &sig.r,
+ &sig.s);
+
+ ecdsa_cleanup:
+ dsa_signature_clear(&sig);
+ ecc_scalar_zclear(&priv);
+ mpz_clear(k);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ break;
+ }
+ case GNUTLS_PK_DSA:
+ {
+ struct dsa_params pub;
+ bigint_t priv;
+ struct dsa_signature sig;
+ mpz_t k;
+ void *random_ctx;
+ nettle_random_func *random_func;
+
+ /* DSA is currently being defined as sunset with the
+ * current draft of FIPS 186-5 */
+ not_approved = true;
+
+ memset(&priv, 0, sizeof(priv));
+ memset(&pub, 0, sizeof(pub));
+ _dsa_params_get(pk_params, &pub);
+
+ priv = pk_params->params[DSA_X];
+
+ dsa_signature_init(&sig);
+
+ me = _gnutls_dsa_q_to_hash(pk_params,
+ &hash_len);
+
+ if (hash_len > vdata->size) {
+ gnutls_assert();
+ _gnutls_debug_log
+ ("Security level of algorithm requires hash %s(%d) or better (have: %d)\n",
+ _gnutls_mac_get_name(me), hash_len, (int)vdata->size);
+ hash_len = vdata->size;
+ }
+
+ mpz_init(k);
+ if (_gnutls_get_lib_state() == LIB_STATE_SELFTEST ||
+ (sign_params->flags & GNUTLS_PK_FLAG_REPRODUCIBLE)) {
+ ret = _gnutls_dsa_compute_k(k,
+ pub.q,
+ TOMPZ(priv),
+ DIG_TO_MAC(sign_params->dsa_dig),
+ vdata->data,
+ vdata->size);
+ if (ret < 0)
+ goto dsa_fail;
+ /* cancel-out dsa_sign's addition of 1 to random data */
+ mpz_sub_ui (k, k, 1);
+ random_ctx = &k;
+ random_func = rnd_mpz_func;
+ } else {
+ random_ctx = NULL;
+ random_func = rnd_nonce_func;
+ }
+ ret =
+ dsa_sign(&pub, TOMPZ(priv), random_ctx, random_func,
+ hash_len, vdata->data, &sig);
+ if (ret == 0 || HAVE_LIB_ERROR()) {
+ gnutls_assert();
+ ret = GNUTLS_E_PK_SIGN_FAILED;
+ goto dsa_fail;
+ }
+
+ ret =
+ _gnutls_encode_ber_rs(signature, &sig.r,
+ &sig.s);
+
+ dsa_fail:
+ dsa_signature_clear(&sig);
+ mpz_clear(k);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ break;
+ }
+ case GNUTLS_PK_RSA:
+ {
+ struct rsa_private_key priv;
+ struct rsa_public_key pub;
+ nettle_random_func *random_func;
+ mpz_t s;
+
+ _rsa_params_to_privkey(pk_params, &priv);
+
+ ret = _rsa_params_to_pubkey(pk_params, &pub);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* RSA modulus size should be 2048-bit or larger in FIPS
+ * 140-3. In addition to this, only SHA-2 is allowed
+ * for SigGen; it is checked in pk_prepare_hash lib/pk.c
+ */
+ if (unlikely(mpz_sizeinbase(pub.n, 2) < 2048)) {
+ not_approved = true;
+ }
+
+ mpz_init(s);
+
+ if (_gnutls_get_lib_state() == LIB_STATE_SELFTEST)
+ random_func = rnd_nonce_func_fallback;
+ else
+ random_func = rnd_nonce_func;
+ ret =
+ rsa_pkcs1_sign_tr(&pub, &priv, NULL, random_func,
+ vdata->size, vdata->data, s);
+ if (ret == 0 || HAVE_LIB_ERROR()) {
+ gnutls_assert();
+ ret = GNUTLS_E_PK_SIGN_FAILED;
+ goto rsa_fail;
+ }
+
+ ret =
+ _gnutls_mpi_dprint_size(s, signature,
+ pub.size);
+
+ rsa_fail:
+ mpz_clear(s);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ break;
+ }
+ case GNUTLS_PK_RSA_PSS:
+ {
+ struct rsa_private_key priv;
+ struct rsa_public_key pub;
+ mpz_t s;
+
+ _rsa_params_to_privkey(pk_params, &priv);
+
+ ret = _rsa_params_to_pubkey(pk_params, &pub);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* RSA modulus size should be 2048-bit or larger in FIPS
+ * 140-3. In addition to this, only SHA-2 is allowed
+ * for SigGen; however, Nettle only support SHA256,
+ * SHA384, and SHA512 for RSA-PSS (see
+ * _rsa_pss_sign_digest_tr in this file for details).
+ */
+ if (unlikely(mpz_sizeinbase(pub.n, 2) < 2048)) {
+ not_approved = true;
+ }
+
+ mpz_init(s);
+
+ ret =
+ _rsa_pss_sign_digest_tr(sign_params->rsa_pss_dig,
+ &pub, &priv,
+ NULL, rnd_nonce_func,
+ sign_params->salt_size,
+ vdata->data, s);
+ if (ret < 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PK_SIGN_FAILED;
+ goto rsa_pss_fail;
+ }
+
+ ret =
+ _gnutls_mpi_dprint_size(s, signature,
+ pub.size);
+
+ rsa_pss_fail:
+ mpz_clear(s);
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ break;
+ }
+ default:
+ gnutls_assert();
+ ret = GNUTLS_E_INTERNAL_ERROR;
+ goto cleanup;
+ }
+
+ ret = 0;
+
+ cleanup:
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else if (not_approved) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+
+ FAIL_IF_LIB_ERROR;
+ return ret;
+}
+
+static int
+_rsa_pss_verify_digest(gnutls_digest_algorithm_t dig,
+ const struct rsa_public_key *pub,
+ size_t salt_size,
+ const uint8_t *digest,
+ size_t digest_size,
+ const mpz_t s)
+{
+ int (*verify_func) (const struct rsa_public_key *,
+ size_t,
+ const uint8_t *,
+ const mpz_t);
+ size_t hash_size;
+
+ switch (dig) {
+ case GNUTLS_DIG_SHA256:
+ verify_func = rsa_pss_sha256_verify_digest;
+ hash_size = 32;
+ break;
+ case GNUTLS_DIG_SHA384:
+ verify_func = rsa_pss_sha384_verify_digest;
+ hash_size = 48;
+ break;
+ case GNUTLS_DIG_SHA512:
+ verify_func = rsa_pss_sha512_verify_digest;
+ hash_size = 64;
+ break;
+ default:
+ gnutls_assert();
+ return 0;
+ }
+
+ if (digest_size != hash_size)
+ return gnutls_assert_val(0);
+
+ CHECK_INVALID_RSA_PSS_PARAMS(hash_size, salt_size, pub->size, 0);
+
+ return verify_func(pub, salt_size, digest, s);
+}
+
+static inline int
+eddsa_verify(gnutls_pk_algorithm_t algo,
+ const uint8_t *pub,
+ size_t length, const uint8_t *msg,
+ const uint8_t *signature)
+{
+ int ret;
+
+ switch (algo) {
+ case GNUTLS_PK_EDDSA_ED25519:
+ ret = ed25519_sha512_verify(pub, length, msg, signature);
+ if (ret == 0)
+ return gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED);
+ return 0;
+ case GNUTLS_PK_EDDSA_ED448:
+ ret = ed448_shake256_verify(pub, length, msg, signature);
+ if (ret == 0)
+ return gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED);
+ return 0;
+ default:
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM);
+ }
+}
+
+static int
+_wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo,
+ const gnutls_datum_t * vdata,
+ const gnutls_datum_t * signature,
+ const gnutls_pk_params_st * pk_params,
+ const gnutls_x509_spki_st * sign_params)
+{
+ int ret;
+ unsigned int hash_len;
+ bigint_t tmp[2] = { NULL, NULL };
+ bool not_approved = false;
+
+ FAIL_IF_LIB_ERROR;
+
+ /* check if the curve relates to the algorithm used */
+ if (IS_EC(algo) && gnutls_ecc_curve_get_pk(pk_params->curve) != algo) {
+ ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ goto cleanup;
+ }
+
+ switch (algo) {
+ case GNUTLS_PK_EDDSA_ED25519: /* we do EdDSA */
+ case GNUTLS_PK_EDDSA_ED448:
+ {
+ const gnutls_ecc_curve_entry_st *e;
+
+ /* EdDSA is not approved yet */
+ not_approved = true;
+
+ if (unlikely(get_eddsa_curve(algo) != pk_params->curve)) {
+ ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ goto cleanup;
+ }
+
+ e = _gnutls_ecc_curve_get_params(pk_params->curve);
+ if (e == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ goto cleanup;
+ }
+
+ if (signature->size != e->sig_size) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED);
+ goto cleanup;
+ }
+
+ if (pk_params->raw_pub.size != e->size) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_SIGN_FAILED);
+ goto cleanup;
+ }
+
+ ret = eddsa_verify(algo,
+ pk_params->raw_pub.data,
+ vdata->size, vdata->data,
+ signature->data);
+ break;
+ }
+#if ENABLE_GOST
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ {
+ struct ecc_point pub;
+ struct dsa_signature sig;
+ const struct ecc_curve *curve;
+ const mac_entry_st *me;
+
+ /* GOSTDSA is not approved */
+ not_approved = true;
+
+ curve = get_supported_gost_curve(pk_params->curve);
+ if (curve == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ goto cleanup;
+ }
+
+ /* This call will return a valid MAC entry and
+ * getters will check that is not null anyway. */
+ me = hash_to_entry(_gnutls_gost_digest(pk_params->algo));
+ if (_gnutls_mac_get_algo_len(me) != vdata->size) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_decode_gost_rs(signature, &tmp[0],
+ &tmp[1]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _gost_params_to_pubkey(pk_params, &pub, curve);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ memcpy(sig.r, tmp[0], SIZEOF_MPZT);
+ memcpy(sig.s, tmp[1], SIZEOF_MPZT);
+
+ ret = gostdsa_verify(&pub, vdata->size, vdata->data, &sig);
+ if (ret == 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PK_SIG_VERIFY_FAILED;
+ } else
+ ret = 0;
+
+ ecc_point_clear(&pub);
+ break;
+ }
+#endif
+ case GNUTLS_PK_ECDSA: /* ECDSA */
+ {
+ struct ecc_point pub;
+ struct dsa_signature sig;
+ int curve_id = pk_params->curve;
+ const struct ecc_curve *curve;
+ const mac_entry_st *me;
+
+ curve = get_supported_nist_curve(curve_id);
+ if (curve == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_decode_ber_rs(signature, &tmp[0],
+ &tmp[1]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ _ecc_params_to_pubkey(pk_params, &pub, curve);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ memcpy(sig.r, tmp[0], SIZEOF_MPZT);
+ memcpy(sig.s, tmp[1], SIZEOF_MPZT);
+
+ me = _gnutls_dsa_q_to_hash(pk_params, &hash_len);
+
+ /* SHA-1 is allowed for SigVer in FIPS 140-3 in legacy
+ * mode */
+ switch (me->id) {
+ case GNUTLS_MAC_SHA1:
+ case GNUTLS_MAC_SHA256:
+ case GNUTLS_MAC_SHA384:
+ case GNUTLS_MAC_SHA512:
+ case GNUTLS_MAC_SHA224:
+ break;
+ default:
+ not_approved = true;
+ }
+
+ if (hash_len > vdata->size)
+ hash_len = vdata->size;
+
+ ret =
+ ecdsa_verify(&pub, hash_len, vdata->data,
+ &sig);
+ if (ret == 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PK_SIG_VERIFY_FAILED;
+ } else
+ ret = 0;
+
+ ecc_point_clear(&pub);
+ break;
+ }
+ case GNUTLS_PK_DSA:
+ {
+ struct dsa_params pub;
+ struct dsa_signature sig;
+ bigint_t y;
+
+ /* DSA is currently being defined as sunset with the
+ * current draft of FIPS 186-5 */
+ not_approved = true;
+
+ ret =
+ _gnutls_decode_ber_rs(signature, &tmp[0],
+ &tmp[1]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ memset(&pub, 0, sizeof(pub));
+ _dsa_params_get(pk_params, &pub);
+ y = pk_params->params[DSA_Y];
+
+ memcpy(sig.r, tmp[0], SIZEOF_MPZT);
+ memcpy(sig.s, tmp[1], SIZEOF_MPZT);
+
+ _gnutls_dsa_q_to_hash(pk_params, &hash_len);
+
+ if (hash_len > vdata->size)
+ hash_len = vdata->size;
+
+ ret =
+ dsa_verify(&pub, TOMPZ(y), hash_len, vdata->data, &sig);
+ if (ret == 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_PK_SIG_VERIFY_FAILED;
+ } else
+ ret = 0;
+
+ break;
+ }
+ case GNUTLS_PK_RSA:
+ {
+ struct rsa_public_key pub;
+ size_t bits;
+
+ ret = _rsa_params_to_pubkey(pk_params, &pub);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ bits = mpz_sizeinbase(pub.n, 2);
+
+ /* In FIPS 140-3, RSA key size should be larger than
+ * 2048-bit or one of the known lengths (1024, 1280,
+ * 1536, 1792; i.e., multiple of 256-bits).
+ *
+ * In addition to this, only SHA-1 and SHA-2 are allowed
+ * for SigVer; it is checked in _pkcs1_rsa_verify_sig in
+ * lib/pubkey.c.
+ */
+ if (unlikely(bits < 2048 &&
+ bits != 1024 && bits != 1280 &&
+ bits != 1536 && bits != 1792)) {
+ not_approved = true;
+ }
+
+ if (signature->size != pub.size) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_mpi_init_scan_nz(&tmp[0], signature->data,
+ signature->size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret =
+ rsa_pkcs1_verify(&pub, vdata->size,
+ vdata->data, TOMPZ(tmp[0]));
+ if (ret == 0)
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_PK_SIG_VERIFY_FAILED);
+ else
+ ret = 0;
+
+ break;
+ }
+ case GNUTLS_PK_RSA_PSS:
+ {
+ struct rsa_public_key pub;
+
+ if ((sign_params->flags &
+ GNUTLS_PK_FLAG_RSA_PSS_FIXED_SALT_LENGTH) &&
+ sign_params->salt_size != vdata->size) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED);
+ goto cleanup;
+ }
+
+ ret = _rsa_params_to_pubkey(pk_params, &pub);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* RSA modulus size should be 2048-bit or larger in FIPS
+ * 140-3. In addition to this, only SHA-1 and SHA-2 are
+ * allowed for SigVer, while Nettle only supports
+ * SHA256, SHA384, and SHA512 for RSA-PSS (see
+ * _rsa_pss_verify_digest in this file for the details).
+ */
+ if (unlikely(mpz_sizeinbase(pub.n, 2) < 2048)) {
+ not_approved = true;
+ }
+
+ if (signature->size != pub.size) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED);
+ goto cleanup;
+ }
+
+ ret =
+ _gnutls_mpi_init_scan_nz(&tmp[0], signature->data,
+ signature->size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _rsa_pss_verify_digest(sign_params->rsa_pss_dig,
+ &pub,
+ sign_params->salt_size,
+ vdata->data, vdata->size,
+ TOMPZ(tmp[0]));
+ if (ret == 0)
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_PK_SIG_VERIFY_FAILED);
+ else
+ ret = 0;
+
+ break;
+ }
+ default:
+ gnutls_assert();
+ ret = GNUTLS_E_INTERNAL_ERROR;
+ goto cleanup;
+ }
+
+ cleanup:
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else if (not_approved) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+
+ _gnutls_mpi_release(&tmp[0]);
+ _gnutls_mpi_release(&tmp[1]);
+ FAIL_IF_LIB_ERROR;
+ return ret;
+}
+
+static inline const struct ecc_curve *get_supported_nist_curve(int curve)
+{
+ switch (curve) {
+#ifdef ENABLE_NON_SUITEB_CURVES
+ case GNUTLS_ECC_CURVE_SECP192R1:
+ return nettle_get_secp_192r1();
+ case GNUTLS_ECC_CURVE_SECP224R1:
+ return nettle_get_secp_224r1();
+#endif
+ case GNUTLS_ECC_CURVE_SECP256R1:
+ return nettle_get_secp_256r1();
+ case GNUTLS_ECC_CURVE_SECP384R1:
+ return nettle_get_secp_384r1();
+ case GNUTLS_ECC_CURVE_SECP521R1:
+ return nettle_get_secp_521r1();
+ default:
+ return NULL;
+ }
+}
+
+static inline const char *get_supported_nist_curve_order(int curve)
+{
+ static const struct {
+ int curve;
+ const char *order;
+ } orders[] = {
+#ifdef ENABLE_NON_SUITEB_CURVES
+ { GNUTLS_ECC_CURVE_SECP192R1,
+ "ffffffffffffffffffffffff99def836"
+ "146bc9b1b4d22831" },
+ { GNUTLS_ECC_CURVE_SECP224R1,
+ "ffffffffffffffffffffffffffff16a2"
+ "e0b8f03e13dd29455c5c2a3d" },
+#endif
+ { GNUTLS_ECC_CURVE_SECP256R1,
+ "ffffffff00000000ffffffffffffffff"
+ "bce6faada7179e84f3b9cac2fc632551" },
+ { GNUTLS_ECC_CURVE_SECP384R1,
+ "ffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffc7634d81f4372ddf"
+ "581a0db248b0a77aecec196accc52973" },
+ { GNUTLS_ECC_CURVE_SECP521R1,
+ "1fffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffff"
+ "ffa51868783bf2f966b7fcc0148f709a"
+ "5d03bb5c9b8899c47aebb6fb71e91386"
+ "409" },
+ };
+ size_t i;
+
+ for (i = 0; i < sizeof(orders)/sizeof(orders[0]); i++) {
+ if (orders[i].curve == curve)
+ return orders[i].order;
+ }
+ return NULL;
+}
+
+static inline const char *get_supported_nist_curve_modulus(int curve)
+{
+ static const struct {
+ int curve;
+ const char *order;
+ } orders[] = {
+#ifdef ENABLE_NON_SUITEB_CURVES
+ { GNUTLS_ECC_CURVE_SECP192R1,
+ "fffffffffffffffffffffffffffffffe"
+ "ffffffffffffffff" },
+ { GNUTLS_ECC_CURVE_SECP224R1,
+ "ffffffffffffffffffffffffffffffff"
+ "000000000000000000000001" },
+#endif
+ { GNUTLS_ECC_CURVE_SECP256R1,
+ "ffffffff000000010000000000000000"
+ "00000000ffffffffffffffffffffffff" },
+ { GNUTLS_ECC_CURVE_SECP384R1,
+ "ffffffffffffffffffffffffffffffff"
+ "fffffffffffffffffffffffffffffffe"
+ "ffffffff0000000000000000ffffffff" },
+ { GNUTLS_ECC_CURVE_SECP521R1,
+ "1ff"
+ "ffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffff"
+ "ffffffffffffffffffffffffffffffff" },
+ };
+ size_t i;
+
+ for (i = 0; i < sizeof(orders)/sizeof(orders[0]); i++) {
+ if (orders[i].curve == curve)
+ return orders[i].order;
+ }
+ return NULL;
+}
+
+static inline const struct ecc_curve *get_supported_gost_curve(int curve)
+{
+ switch (curve) {
+#if ENABLE_GOST
+ case GNUTLS_ECC_CURVE_GOST256CPA:
+ case GNUTLS_ECC_CURVE_GOST256CPXA:
+ case GNUTLS_ECC_CURVE_GOST256B:
+ return nettle_get_gost_gc256b();
+ case GNUTLS_ECC_CURVE_GOST512A:
+ return nettle_get_gost_gc512a();
+#endif
+ default:
+ return NULL;
+ }
+}
+
+static int _wrap_nettle_pk_curve_exists(gnutls_ecc_curve_t curve)
+{
+ switch (curve) {
+ case GNUTLS_ECC_CURVE_ED25519:
+ case GNUTLS_ECC_CURVE_X25519:
+ case GNUTLS_ECC_CURVE_ED448:
+ case GNUTLS_ECC_CURVE_X448:
+ return 1;
+ default:
+ return ((get_supported_nist_curve(curve)!=NULL ||
+ get_supported_gost_curve(curve)!=NULL)?1:0);
+ }
+}
+
+static int _wrap_nettle_pk_exists(gnutls_pk_algorithm_t pk)
+{
+ switch (pk) {
+ case GNUTLS_PK_RSA:
+ case GNUTLS_PK_DSA:
+ case GNUTLS_PK_DH:
+ case GNUTLS_PK_ECDSA:
+ case GNUTLS_PK_ECDH_X25519:
+ case GNUTLS_PK_RSA_PSS:
+ case GNUTLS_PK_EDDSA_ED25519:
+#if ENABLE_GOST
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+#endif
+ case GNUTLS_PK_ECDH_X448:
+ case GNUTLS_PK_EDDSA_ED448:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int _wrap_nettle_pk_sign_exists(gnutls_sign_algorithm_t sign)
+{
+ switch (sign) {
+ case GNUTLS_SIGN_RSA_SHA1:
+ case GNUTLS_SIGN_DSA_SHA1:
+ case GNUTLS_SIGN_RSA_MD5:
+ case GNUTLS_SIGN_RSA_MD2:
+ case GNUTLS_SIGN_RSA_RMD160:
+ case GNUTLS_SIGN_RSA_SHA256:
+ case GNUTLS_SIGN_RSA_SHA384:
+ case GNUTLS_SIGN_RSA_SHA512:
+ case GNUTLS_SIGN_RSA_SHA224:
+ case GNUTLS_SIGN_DSA_SHA224:
+ case GNUTLS_SIGN_DSA_SHA256:
+ case GNUTLS_SIGN_ECDSA_SHA1:
+ case GNUTLS_SIGN_ECDSA_SHA224:
+ case GNUTLS_SIGN_ECDSA_SHA256:
+ case GNUTLS_SIGN_ECDSA_SHA384:
+ case GNUTLS_SIGN_ECDSA_SHA512:
+ case GNUTLS_SIGN_DSA_SHA384:
+ case GNUTLS_SIGN_DSA_SHA512:
+ case GNUTLS_SIGN_ECDSA_SHA3_224:
+ case GNUTLS_SIGN_ECDSA_SHA3_256:
+ case GNUTLS_SIGN_ECDSA_SHA3_384:
+ case GNUTLS_SIGN_ECDSA_SHA3_512:
+
+ case GNUTLS_SIGN_DSA_SHA3_224:
+ case GNUTLS_SIGN_DSA_SHA3_256:
+ case GNUTLS_SIGN_DSA_SHA3_384:
+ case GNUTLS_SIGN_DSA_SHA3_512:
+ case GNUTLS_SIGN_RSA_SHA3_224:
+ case GNUTLS_SIGN_RSA_SHA3_256:
+ case GNUTLS_SIGN_RSA_SHA3_384:
+ case GNUTLS_SIGN_RSA_SHA3_512:
+
+ case GNUTLS_SIGN_RSA_PSS_SHA256:
+ case GNUTLS_SIGN_RSA_PSS_SHA384:
+ case GNUTLS_SIGN_RSA_PSS_SHA512:
+ case GNUTLS_SIGN_EDDSA_ED25519:
+ case GNUTLS_SIGN_RSA_RAW:
+
+ case GNUTLS_SIGN_ECDSA_SECP256R1_SHA256:
+ case GNUTLS_SIGN_ECDSA_SECP384R1_SHA384:
+ case GNUTLS_SIGN_ECDSA_SECP521R1_SHA512:
+
+ case GNUTLS_SIGN_RSA_PSS_RSAE_SHA256:
+ case GNUTLS_SIGN_RSA_PSS_RSAE_SHA384:
+ case GNUTLS_SIGN_RSA_PSS_RSAE_SHA512:
+
+#if ENABLE_GOST
+ case GNUTLS_SIGN_GOST_94:
+ case GNUTLS_SIGN_GOST_256:
+ case GNUTLS_SIGN_GOST_512:
+#endif
+ case GNUTLS_SIGN_EDDSA_ED448:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* Generates algorithm's parameters. That is:
+ * For DSA: p, q, and g are generated.
+ * For RSA: nothing
+ * For ECDSA/EDDSA: nothing
+ */
+static int
+wrap_nettle_pk_generate_params(gnutls_pk_algorithm_t algo,
+ unsigned int level /*bits or curve*/ ,
+ gnutls_pk_params_st * params)
+{
+ int ret;
+ unsigned int i, q_bits;
+
+ FAIL_IF_LIB_ERROR;
+
+ params->algo = algo;
+
+ switch (algo) {
+ case GNUTLS_PK_DSA:
+ case GNUTLS_PK_DH:
+ {
+ struct dsa_params pub;
+ struct dss_params_validation_seeds cert;
+ unsigned index;
+
+ dsa_params_init(&pub);
+
+ if (GNUTLS_BITS_HAVE_SUBGROUP(level)) {
+ q_bits = GNUTLS_BITS_TO_SUBGROUP(level);
+ level = GNUTLS_BITS_TO_GROUP(level);
+ } else {
+ q_bits = _gnutls_pk_bits_to_subgroup_bits(level);
+ }
+
+ if (q_bits == 0)
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+
+ if (_gnutls_fips_mode_enabled() != 0 || params->pkflags & GNUTLS_PK_FLAG_PROVABLE) {
+ if (algo==GNUTLS_PK_DSA)
+ index = 1;
+ else
+ index = 2;
+
+ if (params->palgo != 0 && params->palgo != GNUTLS_DIG_SHA384) {
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto dsa_fail;
+ }
+
+ params->palgo = GNUTLS_DIG_SHA384;
+
+ if (params->seed_size) {
+ ret =
+ _dsa_generate_dss_pqg(&pub, &cert,
+ index, params->seed_size, params->seed,
+ NULL, NULL, level, q_bits);
+ } else {
+ ret =
+ dsa_generate_dss_pqg(&pub, &cert,
+ index, NULL, rnd_tmpkey_func,
+ NULL, NULL, level, q_bits);
+ }
+ if (ret != 1 || HAVE_LIB_ERROR()) {
+ gnutls_assert();
+ ret = GNUTLS_E_PK_GENERATION_ERROR;
+ goto dsa_fail;
+ }
+
+ if (cert.seed_length && cert.seed_length < sizeof(params->seed)) {
+ params->seed_size = cert.seed_length;
+ memcpy(params->seed, cert.seed, cert.seed_length);
+ }
+
+ /* verify the generated parameters */
+ ret = dsa_validate_dss_pqg(&pub, &cert, index);
+ if (ret != 1) {
+ gnutls_assert();
+ ret = GNUTLS_E_PK_GENERATION_ERROR;
+ goto dsa_fail;
+ }
+ } else {
+ if (q_bits < 160)
+ q_bits = 160;
+
+ ret = dsa_generate_params(&pub, NULL, rnd_tmpkey_func,
+ NULL, NULL, level, q_bits);
+ if (ret != 1 || HAVE_LIB_ERROR()) {
+ gnutls_assert();
+ ret = GNUTLS_E_PK_GENERATION_ERROR;
+ goto dsa_fail;
+ }
+ }
+
+ params->params_nr = 0;
+
+ ret = _gnutls_mpi_init_multi(&params->params[DSA_P], &params->params[DSA_Q],
+ &params->params[DSA_G], NULL);
+ if (ret < 0) {
+ gnutls_assert();
+ goto dsa_fail;
+ }
+ params->params_nr = 3;
+
+ mpz_set(TOMPZ(params->params[DSA_P]), pub.p);
+ mpz_set(TOMPZ(params->params[DSA_Q]), pub.q);
+ mpz_set(TOMPZ(params->params[DSA_G]), pub.g);
+
+ ret = 0;
+
+ dsa_fail:
+ dsa_params_clear(&pub);
+
+ if (ret < 0)
+ goto fail;
+
+ break;
+ }
+ case GNUTLS_PK_RSA_PSS:
+ case GNUTLS_PK_RSA:
+ case GNUTLS_PK_ECDSA:
+ case GNUTLS_PK_EDDSA_ED25519:
+ case GNUTLS_PK_EDDSA_ED448:
+ case GNUTLS_PK_ECDH_X25519:
+ case GNUTLS_PK_ECDH_X448:
+#if ENABLE_GOST
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+#endif
+ break;
+ default:
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ FAIL_IF_LIB_ERROR;
+ return 0;
+
+ fail:
+
+ for (i = 0; i < params->params_nr; i++) {
+ _gnutls_mpi_release(&params->params[i]);
+ }
+ params->params_nr = 0;
+
+ FAIL_IF_LIB_ERROR;
+ return ret;
+}
+
+#ifdef ENABLE_FIPS140
+int _gnutls_dh_generate_key(gnutls_dh_params_t dh_params,
+ gnutls_datum_t *priv_key, gnutls_datum_t *pub_key);
+
+int _gnutls_dh_compute_key(gnutls_dh_params_t dh_params,
+ const gnutls_datum_t *priv_key, const gnutls_datum_t *pub_key,
+ const gnutls_datum_t *peer_key, gnutls_datum_t *Z);
+
+int _gnutls_ecdh_compute_key(gnutls_ecc_curve_t curve,
+ const gnutls_datum_t *x, const gnutls_datum_t *y,
+ const gnutls_datum_t *k,
+ const gnutls_datum_t *peer_x, const gnutls_datum_t *peer_y,
+ gnutls_datum_t *Z);
+
+int _gnutls_ecdh_generate_key(gnutls_ecc_curve_t curve,
+ gnutls_datum_t *x, gnutls_datum_t *y,
+ gnutls_datum_t *k);
+
+
+int _gnutls_dh_generate_key(gnutls_dh_params_t dh_params,
+ gnutls_datum_t *priv_key, gnutls_datum_t *pub_key)
+{
+ gnutls_pk_params_st params;
+ int ret;
+
+ gnutls_pk_params_init(&params);
+ params.params[DH_P] = _gnutls_mpi_copy(dh_params->params[0]);
+ params.params[DH_G] = _gnutls_mpi_copy(dh_params->params[1]);
+
+ params.params_nr = 5;
+ params.algo = GNUTLS_PK_DH;
+
+ priv_key->data = NULL;
+ pub_key->data = NULL;
+
+ ret = _gnutls_pk_generate_keys(GNUTLS_PK_DH, dh_params->q_bits, &params, 0);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ ret =
+ _gnutls_mpi_dprint_lz(params.params[DH_X], priv_key);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret =
+ _gnutls_mpi_dprint_lz(params.params[DH_Y], pub_key);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = 0;
+ goto cleanup;
+ fail:
+ gnutls_free(pub_key->data);
+ gnutls_free(priv_key->data);
+ cleanup:
+ gnutls_pk_params_clear(&params);
+ gnutls_pk_params_release(&params);
+ return ret;
+}
+
+/* Note that the value of Z will have the leading bytes stripped if they are zero -
+ * which follows the TLS approach. */
+int _gnutls_dh_compute_key(gnutls_dh_params_t dh_params,
+ const gnutls_datum_t *priv_key, const gnutls_datum_t *pub_key,
+ const gnutls_datum_t *peer_key, gnutls_datum_t *Z)
+{
+ gnutls_pk_params_st pub, priv;
+ int ret;
+
+ gnutls_pk_params_init(&pub);
+ pub.params_nr = 5;
+ pub.algo = GNUTLS_PK_DH;
+
+ gnutls_pk_params_init(&priv);
+ priv.params_nr = 5;
+ priv.algo = GNUTLS_PK_DH;
+
+ if (_gnutls_mpi_init_scan_nz
+ (&pub.params[DH_Y], peer_key->data,
+ peer_key->size) != 0) {
+ ret =
+ gnutls_assert_val(GNUTLS_E_MPI_SCAN_FAILED);
+ goto cleanup;
+ }
+
+ priv.params[DH_P] = _gnutls_mpi_copy(dh_params->params[0]);
+ priv.params[DH_G] = _gnutls_mpi_copy(dh_params->params[1]);
+ if (dh_params->params[2])
+ priv.params[DH_Q] = _gnutls_mpi_copy(dh_params->params[2]);
+
+ if (_gnutls_mpi_init_scan_nz
+ (&priv.params[DH_X], priv_key->data,
+ priv_key->size) != 0) {
+ ret =
+ gnutls_assert_val(GNUTLS_E_MPI_SCAN_FAILED);
+ goto cleanup;
+ }
+
+ Z->data = NULL;
+
+ ret = _gnutls_pk_derive(GNUTLS_PK_DH, Z, &priv, &pub);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_pk_params_clear(&pub);
+ gnutls_pk_params_release(&pub);
+ gnutls_pk_params_clear(&priv);
+ gnutls_pk_params_release(&priv);
+ return ret;
+}
+
+int _gnutls_ecdh_generate_key(gnutls_ecc_curve_t curve,
+ gnutls_datum_t *x, gnutls_datum_t *y,
+ gnutls_datum_t *k)
+{
+ gnutls_pk_params_st params;
+ int ret;
+
+ gnutls_pk_params_init(&params);
+ params.params_nr = 3;
+ params.curve = curve;
+ params.algo = GNUTLS_PK_ECDSA;
+
+ x->data = NULL;
+ y->data = NULL;
+ k->data = NULL;
+
+ ret = _gnutls_pk_generate_keys(GNUTLS_PK_ECDSA, curve, &params, 0);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ ret =
+ _gnutls_mpi_dprint_lz(params.params[ECC_X], x);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret =
+ _gnutls_mpi_dprint_lz(params.params[ECC_Y], y);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret =
+ _gnutls_mpi_dprint_lz(params.params[ECC_K], k);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+ ret = 0;
+ goto cleanup;
+ fail:
+ gnutls_free(y->data);
+ gnutls_free(x->data);
+ gnutls_free(k->data);
+ cleanup:
+ gnutls_pk_params_clear(&params);
+ gnutls_pk_params_release(&params);
+ return ret;
+}
+
+int _gnutls_ecdh_compute_key(gnutls_ecc_curve_t curve,
+ const gnutls_datum_t *x, const gnutls_datum_t *y,
+ const gnutls_datum_t *k,
+ const gnutls_datum_t *peer_x, const gnutls_datum_t *peer_y,
+ gnutls_datum_t *Z)
+{
+ gnutls_pk_params_st pub, priv;
+ int ret;
+
+ gnutls_pk_params_init(&pub);
+ pub.params_nr = 3;
+ pub.algo = GNUTLS_PK_ECDSA;
+ pub.curve = curve;
+
+ gnutls_pk_params_init(&priv);
+ priv.params_nr = 3;
+ priv.algo = GNUTLS_PK_ECDSA;
+ priv.curve = curve;
+
+ if (_gnutls_mpi_init_scan_nz
+ (&pub.params[ECC_Y], peer_y->data,
+ peer_y->size) != 0) {
+ ret =
+ gnutls_assert_val(GNUTLS_E_MPI_SCAN_FAILED);
+ goto cleanup;
+ }
+
+ if (_gnutls_mpi_init_scan_nz
+ (&pub.params[ECC_X], peer_x->data,
+ peer_x->size) != 0) {
+ ret =
+ gnutls_assert_val(GNUTLS_E_MPI_SCAN_FAILED);
+ goto cleanup;
+ }
+
+ if (_gnutls_mpi_init_scan_nz
+ (&priv.params[ECC_Y], y->data,
+ y->size) != 0) {
+ ret =
+ gnutls_assert_val(GNUTLS_E_MPI_SCAN_FAILED);
+ goto cleanup;
+ }
+
+ if (_gnutls_mpi_init_scan_nz
+ (&priv.params[ECC_X], x->data,
+ x->size) != 0) {
+ ret =
+ gnutls_assert_val(GNUTLS_E_MPI_SCAN_FAILED);
+ goto cleanup;
+ }
+
+ if (_gnutls_mpi_init_scan_nz
+ (&priv.params[ECC_K], k->data,
+ k->size) != 0) {
+ ret =
+ gnutls_assert_val(GNUTLS_E_MPI_SCAN_FAILED);
+ goto cleanup;
+ }
+
+ Z->data = NULL;
+
+ ret = _gnutls_pk_derive(GNUTLS_PK_ECDSA, Z, &priv, &pub);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_pk_params_clear(&pub);
+ gnutls_pk_params_release(&pub);
+ gnutls_pk_params_clear(&priv);
+ gnutls_pk_params_release(&priv);
+ return ret;
+}
+
+static int pct_test(gnutls_pk_algorithm_t algo, const gnutls_pk_params_st* params)
+{
+ int ret;
+ gnutls_datum_t sig = {NULL, 0};
+ const char const_data[20] = "onetwothreefourfive";
+ const char const_data_sha256[32] = "onetwothreefourfivesixseveneight";
+ const char const_data_sha384[48] = "onetwothreefourfivesixseveneightnineteneleventwe";
+ const char const_data_sha512[64] = "onetwothreefourfivesixseveneightnineteneleventwelvethirteenfourt";
+ gnutls_datum_t ddata, tmp = {NULL,0};
+ char* gen_data = NULL;
+ gnutls_x509_spki_st spki;
+ gnutls_fips140_context_t context;
+
+ memcpy(&spki, &params->spki, sizeof(spki));
+
+ if (algo == GNUTLS_PK_DSA || algo == GNUTLS_PK_EC) {
+ unsigned hash_len;
+
+ _gnutls_dsa_q_to_hash(params, &hash_len);
+ gen_data = gnutls_malloc(hash_len);
+ gnutls_rnd(GNUTLS_RND_NONCE, gen_data, hash_len);
+
+ ddata.data = (void*)gen_data;
+ ddata.size = hash_len;
+ } else if (algo == GNUTLS_PK_GOST_01 || algo == GNUTLS_PK_GOST_12_256) {
+ ddata.data = (void*)const_data_sha256;
+ ddata.size = sizeof(const_data_sha256);
+ } else if (algo == GNUTLS_PK_GOST_12_512) {
+ ddata.data = (void*)const_data_sha512;
+ ddata.size = sizeof(const_data_sha512);
+ } else if (algo == GNUTLS_PK_RSA_PSS) {
+ if (spki.rsa_pss_dig == GNUTLS_DIG_UNKNOWN)
+ spki.rsa_pss_dig = GNUTLS_DIG_SHA256;
+
+ switch (spki.rsa_pss_dig) {
+ case GNUTLS_DIG_SHA256:
+ ddata.data = (void*)const_data_sha256;
+ ddata.size = sizeof(const_data_sha256);
+ break;
+ case GNUTLS_DIG_SHA384:
+ ddata.data = (void*)const_data_sha384;
+ ddata.size = sizeof(const_data_sha384);
+ break;
+ case GNUTLS_DIG_SHA512:
+ ddata.data = (void*)const_data_sha512;
+ ddata.size = sizeof(const_data_sha512);
+ break;
+ default:
+ ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR);
+ goto cleanup;
+ }
+ } else {
+ ddata.data = (void*)const_data;
+ ddata.size = sizeof(const_data);
+ }
+
+ switch (algo) {
+ case GNUTLS_PK_RSA:
+ /* Push a temporary FIPS context because _gnutls_pk_encrypt and
+ * _gnutls_pk_decrypt below will mark RSAES-PKCS1-v1_5 operation
+ * non-approved */
+ if (gnutls_fips140_context_init(&context) < 0) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR);
+ goto cleanup;
+ }
+ if (gnutls_fips140_push_context(context) < 0) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR);
+ gnutls_fips140_context_deinit(context);
+ goto cleanup;
+ }
+
+ ret = _gnutls_pk_encrypt(algo, &sig, &ddata, params);
+ if (ret < 0) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR);
+ }
+ if (ret == 0 &&
+ ddata.size == sig.size &&
+ memcmp(ddata.data, sig.data, sig.size) == 0) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR);
+ }
+ if (ret == 0 &&
+ _gnutls_pk_decrypt(algo, &tmp, &sig, params) < 0) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR);
+ }
+ if (ret == 0 &&
+ !(tmp.size == ddata.size &&
+ memcmp(tmp.data, ddata.data, tmp.size) == 0)) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR);
+ }
+
+ if (unlikely(gnutls_fips140_pop_context() < 0)) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR);
+ }
+ gnutls_fips140_context_deinit(context);
+
+ if (ret < 0) {
+ goto cleanup;
+ }
+
+ free(sig.data);
+ sig.data = NULL;
+
+ FALLTHROUGH;
+ case GNUTLS_PK_EC: /* we only do keys for ECDSA */
+ case GNUTLS_PK_EDDSA_ED25519:
+ case GNUTLS_PK_EDDSA_ED448:
+ case GNUTLS_PK_DSA:
+ case GNUTLS_PK_RSA_PSS:
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ ret = _gnutls_pk_sign(algo, &sig, &ddata, params, &spki);
+ if (ret < 0) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR);
+ goto cleanup;
+ }
+
+ ret = _gnutls_pk_verify(algo, &ddata, &sig, params, &spki);
+ if (ret < 0) {
+ ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR);
+ gnutls_assert();
+ goto cleanup;
+ }
+ break;
+ case GNUTLS_PK_DH:
+ case GNUTLS_PK_ECDH_X25519:
+ case GNUTLS_PK_ECDH_X448:
+ ret = 0;
+ goto cleanup;
+ default:
+ ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_PK_ALGORITHM);
+ goto cleanup;
+ }
+
+ ret = 0;
+cleanup:
+ if (ret == GNUTLS_E_PK_GENERATION_ERROR) {
+ _gnutls_switch_lib_state(LIB_STATE_ERROR);
+ }
+ gnutls_free(gen_data);
+ gnutls_free(sig.data);
+ gnutls_free(tmp.data);
+ return ret;
+}
+#endif
+
+static inline int
+eddsa_public_key(gnutls_pk_algorithm_t algo,
+ uint8_t *pub, const uint8_t *priv)
+{
+ switch (algo) {
+ case GNUTLS_PK_EDDSA_ED25519:
+ ed25519_sha512_public_key(pub, priv);
+ return 0;
+ case GNUTLS_PK_EDDSA_ED448:
+ ed448_shake256_public_key(pub, priv);
+ return 0;
+ default:
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM);
+ }
+}
+
+static inline int
+edwards_curve_mul_g(gnutls_pk_algorithm_t algo,
+ uint8_t *q, const uint8_t *n)
+{
+ switch (algo) {
+ case GNUTLS_PK_ECDH_X25519:
+ curve25519_mul_g(q, n);
+ return 0;
+ case GNUTLS_PK_ECDH_X448:
+ curve448_mul_g(q, n);
+ return 0;
+ default:
+ return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ }
+}
+
+static inline int
+dh_find_q(const gnutls_pk_params_st *pk_params, mpz_t q)
+{
+ gnutls_datum_t prime = { NULL, 0 };
+ gnutls_datum_t generator = { NULL, 0 };
+ uint8_t *data_q;
+ size_t n_q;
+ bigint_t _q;
+ int ret = 0;
+
+ ret = _gnutls_mpi_dprint(pk_params->params[DSA_P], &prime);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_mpi_dprint(pk_params->params[DSA_G], &generator);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (!_gnutls_dh_prime_match_fips_approved(prime.data,
+ prime.size,
+ generator.data,
+ generator.size,
+ &data_q,
+ &n_q)) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ if (_gnutls_mpi_init_scan_nz(&_q, data_q, n_q) != 0) {
+ ret = gnutls_assert_val(GNUTLS_E_MPI_SCAN_FAILED);
+ goto cleanup;
+ }
+
+ mpz_set(q, TOMPZ(_q));
+ _gnutls_mpi_release(&_q);
+
+ cleanup:
+ gnutls_free(prime.data);
+ gnutls_free(generator.data);
+
+ return ret;
+}
+
+/* To generate a DH key either q must be set in the params or
+ * level should be set to the number of required bits.
+ */
+static int
+wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo,
+ unsigned int level /*bits or curve */ ,
+ gnutls_pk_params_st * params,
+ unsigned ephemeral /*non-zero if they are ephemeral keys */)
+{
+ int ret;
+ unsigned int i;
+ unsigned rnd_level;
+ nettle_random_func *rnd_func;
+ bool not_approved = false;
+
+ FAIL_IF_LIB_ERROR;
+
+ /* check if the curve relates to the algorithm used */
+ if (IS_EC(algo) && gnutls_ecc_curve_get_pk(level) != algo) {
+ ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ goto cleanup;
+ }
+
+ if (ephemeral) {
+ rnd_level = GNUTLS_RND_RANDOM;
+ rnd_func = rnd_tmpkey_func;
+ } else {
+ rnd_func = rnd_key_func;
+ rnd_level = GNUTLS_RND_KEY;
+ }
+
+ switch (algo) {
+ case GNUTLS_PK_DSA:
+#ifdef ENABLE_FIPS140
+ if (_gnutls_fips_mode_enabled() != 0) {
+ struct dsa_params pub;
+ mpz_t x, y;
+
+ /* DSA is currently being defined as sunset with the
+ * current draft of FIPS 186-5 */
+ not_approved = true;
+
+ if (params->params[DSA_Q] == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ _dsa_params_get(params, &pub);
+
+ mpz_init(x);
+ mpz_init(y);
+
+ ret =
+ dsa_generate_dss_keypair(&pub, y, x,
+ NULL, rnd_func,
+ NULL, NULL);
+ if (ret != 1 || HAVE_LIB_ERROR()) {
+ gnutls_assert();
+ ret = GNUTLS_E_PK_GENERATION_ERROR;
+ goto dsa_fail;
+ }
+
+ ret = _gnutls_mpi_init_multi(&params->params[DSA_Y], &params->params[DSA_X], NULL);
+ if (ret < 0) {
+ gnutls_assert();
+ goto dsa_fail;
+ }
+
+ mpz_set(TOMPZ(params->params[DSA_Y]), y);
+ mpz_set(TOMPZ(params->params[DSA_X]), x);
+ params->params_nr += 2;
+
+ dsa_fail:
+ mpz_clear(x);
+ mpz_clear(y);
+
+ if (ret < 0)
+ goto cleanup;
+
+ break;
+ }
+#endif
+ FALLTHROUGH;
+ case GNUTLS_PK_DH:
+ {
+ struct dsa_params pub;
+ mpz_t r;
+ mpz_t x, y;
+ int max_tries;
+ unsigned have_q = 0;
+ mpz_t q;
+ mpz_t primesub1;
+ mpz_t ypowq;
+
+ if (algo != params->algo) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ _dsa_params_get(params, &pub);
+
+ if (params->params[DSA_Q] != NULL)
+ have_q = 1;
+
+ /* This check is for the case !ENABLE_FIPS140 */
+ if (algo == GNUTLS_PK_DSA && have_q == 0) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ mpz_init(r);
+ mpz_init(x);
+ mpz_init(y);
+
+ mpz_init(q);
+ mpz_init(primesub1);
+ mpz_init(ypowq);
+
+ max_tries = 3;
+ do {
+ if (have_q) {
+ mpz_set(r, pub.q);
+ mpz_sub_ui(r, r, 2);
+ nettle_mpz_random(x, NULL, rnd_func, r);
+ mpz_add_ui(x, x, 1);
+ } else {
+ unsigned size = mpz_sizeinbase(pub.p, 2);
+ if (level == 0)
+ level = MIN(size, DH_EXPONENT_SIZE(size));
+ nettle_mpz_random_size(x, NULL, rnd_func, level);
+
+ if (level >= size)
+ mpz_mod(x, x, pub.p);
+ }
+
+ mpz_powm(y, pub.g, x, pub.p);
+
+ max_tries--;
+ if (max_tries <= 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_RANDOM_FAILED;
+ goto dh_fail;
+ }
+
+ if (HAVE_LIB_ERROR()) {
+ gnutls_assert();
+ ret = GNUTLS_E_LIB_IN_ERROR_STATE;
+ goto dh_fail;
+ }
+
+ } while(mpz_cmp_ui(y, 1) == 0);
+
+#ifdef ENABLE_FIPS140
+ if (_gnutls_fips_mode_enabled()) {
+ /* Perform FFC full public key validation checks
+ * according to SP800-56A (revision 3), 5.6.2.3.1.
+ */
+
+ /* Step 1: 2 <= y <= p - 2 */
+ mpz_sub_ui(primesub1, pub.p, 1);
+
+ if (mpz_cmp_ui(y, 2) < 0 || mpz_cmp(y, primesub1) >= 0) {
+ ret = gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
+ goto dh_fail;
+ }
+
+ /* Step 2: 1 = y^q mod p */
+ if (have_q)
+ mpz_set(q, pub.q);
+ else {
+ ret = dh_find_q(params, q);
+ if (ret < 0)
+ goto dh_fail;
+ }
+
+ mpz_powm(ypowq, y, q, pub.p);
+ if (mpz_cmp_ui(ypowq, 1) != 0) {
+ ret = gnutls_assert_val(GNUTLS_E_RANDOM_FAILED);
+ goto dh_fail;
+ }
+ }
+#endif
+
+ ret = _gnutls_mpi_init_multi(&params->params[DSA_Y], &params->params[DSA_X], NULL);
+ if (ret < 0) {
+ gnutls_assert();
+ goto dh_fail;
+ }
+
+ mpz_set(TOMPZ(params->params[DSA_Y]), y);
+ mpz_set(TOMPZ(params->params[DSA_X]), x);
+ params->params_nr += 2;
+
+ ret = 0;
+
+ dh_fail:
+ mpz_clear(r);
+ mpz_clear(x);
+ mpz_clear(y);
+ mpz_clear(q);
+ mpz_clear(primesub1);
+ mpz_clear(ypowq);
+
+ if (ret < 0)
+ goto cleanup;
+
+ break;
+ }
+ case GNUTLS_PK_RSA_PSS:
+ case GNUTLS_PK_RSA:
+ {
+ struct rsa_public_key pub;
+ struct rsa_private_key priv;
+
+ rsa_public_key_init(&pub);
+ rsa_private_key_init(&priv);
+
+ mpz_set_ui(pub.e, 65537);
+
+ if ((params->pkflags & GNUTLS_PK_FLAG_PROVABLE) || _gnutls_fips_mode_enabled() != 0) {
+ params->pkflags |= GNUTLS_PK_FLAG_PROVABLE;
+ if (params->palgo != 0 && params->palgo != GNUTLS_DIG_SHA384) {
+ ret = GNUTLS_E_INVALID_REQUEST;
+ goto rsa_fail;
+ }
+
+ params->palgo = GNUTLS_DIG_SHA384;
+
+ if (params->seed_size) {
+ ret = _rsa_generate_fips186_4_keypair(&pub, &priv,
+ params->seed_size, params->seed,
+ NULL, NULL, level);
+ } else {
+ unsigned retries = 0;
+ /* The provable RSA key generation process is deterministic
+ * but has an internal maximum iteration counter and when
+ * exceed will fail for certain random seeds. This is a very
+ * rare condition, but it nevertheless happens and even CI builds fail
+ * occasionally. When we generate the random seed internally, remediate
+ * by retrying a different seed on failure. */
+ do {
+ params->seed_size = sizeof(params->seed);
+ ret =
+ rsa_generate_fips186_4_keypair(&pub, &priv, NULL,
+ rnd_func, NULL, NULL,
+ &params->seed_size, params->seed,
+ level);
+ } while (ret != 1 && ++retries < 3);
+ }
+ } else {
+ not_approved = true;
+
+ ret =
+ rsa_generate_keypair(&pub, &priv, NULL,
+ rnd_func, NULL, NULL,
+ level, 0);
+ }
+ if (ret != 1 || HAVE_LIB_ERROR()) {
+ gnutls_assert();
+ ret = GNUTLS_E_PK_GENERATION_ERROR;
+ goto rsa_fail;
+ }
+
+ params->params_nr = 0;
+ for (i = 0; i < RSA_PRIVATE_PARAMS; i++) {
+ ret = _gnutls_mpi_init(&params->params[i]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto rsa_fail;
+ }
+ params->params_nr++;
+ }
+
+ /* In FIPS 140-3, pub.n should be 2048-bit or larger; it
+ * is assured in rsa_generate_fips186_4_keypair in
+ * lib/nettle/int/rsa-keygen-fips186.c. */
+
+ mpz_set(TOMPZ(params->params[RSA_MODULUS]), pub.n);
+ mpz_set(TOMPZ(params->params[RSA_PUB]), pub.e);
+ mpz_set(TOMPZ(params->params[RSA_PRIV]), priv.d);
+ mpz_set(TOMPZ(params->params[RSA_PRIME1]), priv.p);
+ mpz_set(TOMPZ(params->params[RSA_PRIME2]), priv.q);
+ mpz_set(TOMPZ(params->params[RSA_COEF]), priv.c);
+ mpz_set(TOMPZ(params->params[RSA_E1]), priv.a);
+ mpz_set(TOMPZ(params->params[RSA_E2]), priv.b);
+
+ ret = 0;
+
+ rsa_fail:
+ rsa_private_key_clear(&priv);
+ rsa_public_key_clear(&pub);
+
+ if (ret < 0)
+ goto cleanup;
+
+ break;
+ }
+ case GNUTLS_PK_EDDSA_ED25519:
+ case GNUTLS_PK_EDDSA_ED448:
+ {
+ unsigned size = gnutls_ecc_curve_get_size(level);
+
+ /* EdDSA is not approved yet */
+ not_approved = true;
+
+ if (params->pkflags & GNUTLS_PK_FLAG_PROVABLE) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ if (unlikely(get_eddsa_curve(algo) != level)) {
+ ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ goto cleanup;
+ }
+
+ if (size == 0) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ params->curve = level;
+
+ params->raw_priv.data = gnutls_malloc(size);
+ if (params->raw_priv.data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ params->raw_pub.data = gnutls_malloc(size);
+ if (params->raw_pub.data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ ret = gnutls_rnd(rnd_level, params->raw_priv.data, size);
+ if (ret < 0) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+ params->raw_pub.size = size;
+ params->raw_priv.size = size;
+
+ ret = eddsa_public_key(algo,
+ params->raw_pub.data,
+ params->raw_priv.data);
+ if (ret < 0)
+ goto cleanup;
+
+ break;
+ }
+ case GNUTLS_PK_ECDSA:
+ if (params->pkflags & GNUTLS_PK_FLAG_PROVABLE)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ {
+ struct ecc_scalar key;
+ struct ecc_point pub;
+ const struct ecc_curve *curve;
+ struct ecc_scalar n;
+ struct ecc_scalar m;
+ struct ecc_point r;
+ mpz_t x, y, xx, yy, nn, mm;
+
+ curve = get_supported_nist_curve(level);
+ if (curve == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ goto cleanup;
+ }
+
+ /* P-192 is not supported in FIPS 140-3 */
+ if (level == GNUTLS_ECC_CURVE_SECP192R1) {
+ not_approved = true;
+ }
+
+ mpz_init(x);
+ mpz_init(y);
+ mpz_init(xx);
+ mpz_init(yy);
+ mpz_init(nn);
+ mpz_init(mm);
+
+ ecc_scalar_init(&key, curve);
+ ecc_point_init(&pub, curve);
+ ecc_scalar_init(&n, curve);
+ ecc_scalar_init(&m, curve);
+ ecc_point_init(&r, curve);
+
+ ecdsa_generate_keypair(&pub, &key, NULL, rnd_func);
+ if (HAVE_LIB_ERROR()) {
+ ret = gnutls_assert_val(GNUTLS_E_LIB_IN_ERROR_STATE);
+ goto ecc_fail;
+ }
+
+ ret = _gnutls_mpi_init_multi(&params->params[ECC_X], &params->params[ECC_Y],
+ &params->params[ECC_K], NULL);
+ if (ret < 0) {
+ gnutls_assert();
+ goto ecc_fail;
+ }
+
+ params->curve = level;
+ params->params_nr = ECC_PRIVATE_PARAMS;
+
+ ecc_point_get(&pub, x, y);
+
+#ifdef ENABLE_FIPS140
+ if (_gnutls_fips_mode_enabled()) {
+ /* Perform ECC full public key validation checks
+ * according to SP800-56A (revision 3), 5.6.2.3.3.
+ */
+
+ const char *order, *modulus;
+
+ /* Step 1: verify that Q is not an identity
+ * element (an infinity point). Note that this
+ * cannot happen in the nettle implementation,
+ * because it cannot represent an infinity point
+ * on curves. */
+ if (mpz_cmp_ui(x, 0) == 0 && mpz_cmp_ui(y, 0) == 0) {
+ ret = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ goto ecc_fail;
+ }
+
+ /* Step 2: verify that both coordinates of Q are
+ * in the range [0, p - 1].
+ *
+ * Step 3: verify that Q lie on the curve
+ *
+ * Both checks are performed in nettle. */
+ if (!ecc_point_set(&r, x, y)) {
+ ret = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ goto ecc_fail;
+ }
+
+ /* Step 4: verify that n * Q, where n is the
+ * curve order, result in an identity element
+ *
+ * Since nettle internally cannot represent an
+ * identity element on curves, we validate this
+ * instead:
+ *
+ * (n - 1) * Q = -Q
+ *
+ * That effectively means: n * Q = -Q + Q = O
+ */
+ order = get_supported_nist_curve_order(level);
+ if (unlikely(order == NULL)) {
+ ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ goto ecc_fail;
+ }
+
+ ret = mpz_set_str(nn, order, 16);
+ if (unlikely(ret < 0)) {
+ ret = gnutls_assert_val(GNUTLS_E_MPI_SCAN_FAILED);
+ goto ecc_fail;
+ }
+
+ modulus = get_supported_nist_curve_modulus(level);
+ if (unlikely(modulus == NULL)) {
+ ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ goto ecc_fail;
+ }
+
+ ret = mpz_set_str(mm, modulus, 16);
+ if (unlikely(ret < 0)) {
+ ret = gnutls_assert_val(GNUTLS_E_MPI_SCAN_FAILED);
+ goto ecc_fail;
+ }
+
+ /* (n - 1) * Q = -Q */
+ mpz_sub_ui (nn, nn, 1);
+ ecc_scalar_set(&n, nn);
+ ecc_point_mul(&r, &n, &r);
+ ecc_point_get(&r, xx, yy);
+ mpz_sub (mm, mm, y);
+
+ if (mpz_cmp(xx, x) != 0 || mpz_cmp(yy, mm) != 0) {
+ ret = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ goto ecc_fail;
+ }
+ } else {
+ not_approved = true;
+ }
+#endif
+
+ mpz_set(TOMPZ(params->params[ECC_X]), x);
+ mpz_set(TOMPZ(params->params[ECC_Y]), y);
+
+ ecc_scalar_get(&key, TOMPZ(params->params[ECC_K]));
+
+ ret = 0;
+
+ ecc_fail:
+ mpz_clear(x);
+ mpz_clear(y);
+ mpz_clear(xx);
+ mpz_clear(yy);
+ mpz_clear(nn);
+ mpz_clear(mm);
+ ecc_point_clear(&pub);
+ ecc_scalar_clear(&key);
+ ecc_point_clear(&r);
+ ecc_scalar_clear(&n);
+ ecc_scalar_clear(&m);
+
+ if (ret < 0)
+ goto cleanup;
+
+ break;
+ }
+#if ENABLE_GOST
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ if (params->pkflags & GNUTLS_PK_FLAG_PROVABLE)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ {
+ struct ecc_scalar key;
+ struct ecc_point pub;
+ const struct ecc_curve *curve;
+ const mac_entry_st *me;
+
+ /* GOST curves are not approved */
+ not_approved = true;
+
+ curve = get_supported_gost_curve(level);
+ if (curve == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+ goto cleanup;
+ }
+
+ me = hash_to_entry(_gnutls_gost_digest(algo));
+ if (!me || me->output_size * 8 != ecc_bit_size(curve)) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ ecc_scalar_init(&key, curve);
+ ecc_point_init(&pub, curve);
+
+ gostdsa_generate_keypair(&pub, &key, NULL, rnd_key_func);
+ if (HAVE_LIB_ERROR()) {
+ ret = gnutls_assert_val(GNUTLS_E_LIB_IN_ERROR_STATE);
+ goto ecc_fail;
+ }
+
+
+ ret = _gnutls_mpi_init_multi(&params->params[GOST_X], &params->params[GOST_Y],
+ &params->params[GOST_K], NULL);
+ if (ret < 0) {
+ gnutls_assert();
+ goto gost_fail;
+ }
+
+ params->curve = level;
+ params->params_nr = GOST_PRIVATE_PARAMS;
+
+ ecc_point_get(&pub, TOMPZ(params->params[GOST_X]),
+ TOMPZ(params->params[GOST_Y]));
+ ecc_scalar_get(&key, TOMPZ(params->params[GOST_K]));
+
+ ret = 0;
+
+ gost_fail:
+ ecc_point_clear(&pub);
+ ecc_scalar_clear(&key);
+
+ if (ret < 0)
+ goto cleanup;
+
+ break;
+ }
+#endif
+ case GNUTLS_PK_ECDH_X25519:
+ case GNUTLS_PK_ECDH_X448:
+ {
+ unsigned size = gnutls_ecc_curve_get_size(level);
+
+ not_approved = true;
+
+ if (size == 0) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ params->curve = level;
+
+ params->raw_priv.data = gnutls_malloc(size);
+ if (params->raw_priv.data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ params->raw_pub.data = gnutls_malloc(size);
+ if (params->raw_pub.data == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ ret = gnutls_rnd(rnd_level, params->raw_priv.data, size);
+ if (ret < 0) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+ params->raw_pub.size = size;
+ params->raw_priv.size = size;
+
+ ret = edwards_curve_mul_g(algo, params->raw_pub.data, params->raw_priv.data);
+ if (ret < 0)
+ goto cleanup;
+ break;
+ }
+ default:
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ params->algo = algo;
+
+#ifdef ENABLE_FIPS140
+ ret = pct_test(algo, params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+#endif
+
+ cleanup:
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ for (i = 0; i < params->params_nr; i++) {
+ _gnutls_mpi_release(&params->params[i]);
+ }
+ params->params_nr = 0;
+ gnutls_free(params->raw_priv.data);
+ gnutls_free(params->raw_pub.data);
+ } else if (not_approved) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+
+ FAIL_IF_LIB_ERROR;
+ return ret;
+}
+
+
+static int
+wrap_nettle_pk_verify_priv_params(gnutls_pk_algorithm_t algo,
+ const gnutls_pk_params_st * params)
+{
+ int ret;
+
+ switch (algo) {
+ case GNUTLS_PK_RSA:
+ case GNUTLS_PK_RSA_PSS:
+ {
+ bigint_t t1 = NULL, t2 = NULL;
+
+ if (params->params_nr != RSA_PRIVATE_PARAMS)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_INVALID_REQUEST);
+
+ ret = _gnutls_mpi_init_multi(&t1, &t2, NULL);
+ if (ret < 0)
+ return
+ gnutls_assert_val(ret);
+
+ _gnutls_mpi_mulm(t1, params->params[RSA_PRIME1],
+ params->params[RSA_PRIME2],
+ params->params[RSA_MODULUS]);
+ if (_gnutls_mpi_cmp_ui(t1, 0) != 0) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_ILLEGAL_PARAMETER);
+ goto rsa_cleanup;
+ }
+
+ mpz_invert(TOMPZ(t1),
+ TOMPZ(params->params[RSA_PRIME2]),
+ TOMPZ(params->params[RSA_PRIME1]));
+ if (_gnutls_mpi_cmp(t1, params->params[RSA_COEF])
+ != 0) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_ILLEGAL_PARAMETER);
+ goto rsa_cleanup;
+ }
+
+ /* [RSA_PRIME1] = d % p-1, [RSA_PRIME2] = d % q-1 */
+ _gnutls_mpi_sub_ui(t1, params->params[RSA_PRIME1],
+ 1);
+ ret = _gnutls_mpi_modm(t2, params->params[RSA_PRIV], t1);
+ if (ret < 0) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_MEMORY_ERROR);
+ goto rsa_cleanup;
+ }
+
+ if (_gnutls_mpi_cmp(params->params[RSA_E1], t2) !=
+ 0) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_ILLEGAL_PARAMETER);
+ goto rsa_cleanup;
+ }
+
+ _gnutls_mpi_sub_ui(t1, params->params[RSA_PRIME2],
+ 1);
+
+ ret = _gnutls_mpi_modm(t2, params->params[RSA_PRIV], t1);
+ if (ret < 0) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_MEMORY_ERROR);
+ goto rsa_cleanup;
+ }
+
+ if (_gnutls_mpi_cmp(params->params[RSA_E2], t2) !=
+ 0) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_ILLEGAL_PARAMETER);
+ goto rsa_cleanup;
+ }
+
+ ret = 0;
+
+ rsa_cleanup:
+ zrelease_mpi_key(&t1);
+ zrelease_mpi_key(&t2);
+ }
+
+ break;
+ case GNUTLS_PK_DSA:
+ {
+ bigint_t t1 = NULL;
+
+ if (params->params_nr != DSA_PRIVATE_PARAMS)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_INVALID_REQUEST);
+
+ ret = _gnutls_mpi_init(&t1);
+ if (ret < 0)
+ return
+ gnutls_assert_val(ret);
+
+ ret = _gnutls_mpi_powm(t1, params->params[DSA_G],
+ params->params[DSA_X],
+ params->params[DSA_P]);
+ if (ret < 0) {
+ gnutls_assert();
+ goto dsa_cleanup;
+ }
+
+ if (_gnutls_mpi_cmp(t1, params->params[DSA_Y]) !=
+ 0) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_ILLEGAL_PARAMETER);
+ goto dsa_cleanup;
+ }
+
+ ret = 0;
+
+ dsa_cleanup:
+ zrelease_mpi_key(&t1);
+ }
+
+ break;
+ case GNUTLS_PK_ECDSA:
+ {
+ struct ecc_point r, pub;
+ struct ecc_scalar priv;
+ mpz_t x1, y1, x2, y2;
+ const struct ecc_curve *curve;
+
+ if (params->params_nr != ECC_PRIVATE_PARAMS)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_INVALID_REQUEST);
+
+ curve = get_supported_nist_curve(params->curve);
+ if (curve == NULL)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+
+ ret = _ecc_params_to_pubkey(params, &pub, curve);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _ecc_params_to_privkey(params, &priv, curve);
+ if (ret < 0) {
+ ecc_point_clear(&pub);
+ return gnutls_assert_val(ret);
+ }
+
+ ecc_point_init(&r, curve);
+ /* verify that x,y lie on the curve */
+ ret =
+ ecc_point_set(&r, TOMPZ(params->params[ECC_X]),
+ TOMPZ(params->params[ECC_Y]));
+ if (ret == 0) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_ILLEGAL_PARAMETER);
+ goto ecc_cleanup;
+ }
+ ecc_point_clear(&r);
+
+ ecc_point_init(&r, curve);
+ ecc_point_mul_g(&r, &priv);
+
+ mpz_init(x1);
+ mpz_init(y1);
+ ecc_point_get(&r, x1, y1);
+ ecc_point_zclear(&r);
+
+ mpz_init(x2);
+ mpz_init(y2);
+ ecc_point_get(&pub, x2, y2);
+
+ /* verify that k*(Gx,Gy)=(x,y) */
+ if (mpz_cmp(x1, x2) != 0 || mpz_cmp(y1, y2) != 0) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_ILLEGAL_PARAMETER);
+ goto ecc_cleanup;
+ }
+
+ ret = 0;
+
+ ecc_cleanup:
+ ecc_scalar_zclear(&priv);
+ ecc_point_clear(&pub);
+
+ mpz_clear(x1);
+ mpz_clear(y1);
+ mpz_clear(x2);
+ mpz_clear(y2);
+ }
+ break;
+ case GNUTLS_PK_EDDSA_ED25519:
+ case GNUTLS_PK_EDDSA_ED448: {
+ gnutls_ecc_curve_t curve;
+ const gnutls_ecc_curve_entry_st *e;
+ uint8_t pub[57]; /* can accommodate both curves */
+
+ curve = get_eddsa_curve(algo);
+ e = _gnutls_ecc_curve_get_params(curve);
+ if (e == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (params->raw_pub.data == NULL) {
+ return 0; /* nothing to verify */
+ }
+
+ if (params->raw_pub.size != e->size)
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+
+ ret = eddsa_public_key(algo, pub, params->raw_priv.data);
+ if (ret < 0)
+ return ret;
+
+ if (memcmp(params->raw_pub.data, pub, e->size) != 0)
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+
+ ret = 0;
+ break;
+ }
+ case GNUTLS_PK_ECDH_X25519:
+ case GNUTLS_PK_ECDH_X448: {
+ gnutls_ecc_curve_t curve;
+ const gnutls_ecc_curve_entry_st *e;
+ uint8_t pub[57]; /* can accommodate both curves */
+
+ curve = get_ecdh_curve(algo);
+ e = _gnutls_ecc_curve_get_params(curve);
+ if (e == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (params->raw_pub.data == NULL) {
+ return 0; /* nothing to verify */
+ }
+
+ if (params->raw_pub.size != e->size)
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+
+ ret = edwards_curve_mul_g(algo, pub, params->raw_priv.data);
+ if (ret < 0)
+ return ret;
+
+ if (memcmp(params->raw_pub.data, pub, e->size) != 0)
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+
+ ret = 0;
+ break;
+ }
+#if ENABLE_GOST
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ {
+ struct ecc_point r, pub;
+ struct ecc_scalar priv;
+ mpz_t x1, y1, x2, y2;
+ const struct ecc_curve *curve;
+
+ if (params->params_nr != GOST_PRIVATE_PARAMS)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_INVALID_REQUEST);
+
+ curve = get_supported_gost_curve(params->curve);
+ if (curve == NULL)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+
+ ret = _gost_params_to_pubkey(params, &pub, curve);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gost_params_to_privkey(params, &priv, curve);
+ if (ret < 0) {
+ ecc_point_clear(&pub);
+ return gnutls_assert_val(ret);
+ }
+
+ ecc_point_init(&r, curve);
+ /* verify that x,y lie on the curve */
+ ret =
+ gost_point_set(&r, TOMPZ(params->params[GOST_X]),
+ TOMPZ(params->params[GOST_Y]));
+ if (ret == 0) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_ILLEGAL_PARAMETER);
+ goto gost_cleanup;
+ }
+ ecc_point_clear(&r);
+
+ ecc_point_init(&r, curve);
+ gost_point_mul_g(&r, &priv);
+
+ mpz_init(x1);
+ mpz_init(y1);
+ ecc_point_get(&r, x1, y1);
+ ecc_point_zclear(&r);
+
+ mpz_init(x2);
+ mpz_init(y2);
+ ecc_point_get(&pub, x2, y2);
+
+ /* verify that k*(Gx,Gy)=(x,y) */
+ if (mpz_cmp(x1, x2) != 0 || mpz_cmp(y1, y2) != 0) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_ILLEGAL_PARAMETER);
+ goto gost_cleanup;
+ }
+
+ ret = 0;
+
+ gost_cleanup:
+ ecc_scalar_zclear(&priv);
+ ecc_point_clear(&pub);
+
+ mpz_clear(x1);
+ mpz_clear(y1);
+ mpz_clear(x2);
+ mpz_clear(y2);
+ }
+ break;
+#endif
+ default:
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ return ret;
+}
+
+static int
+wrap_nettle_pk_verify_pub_params(gnutls_pk_algorithm_t algo,
+ const gnutls_pk_params_st * params)
+{
+ int ret;
+
+ switch (algo) {
+ case GNUTLS_PK_RSA:
+ case GNUTLS_PK_RSA_PSS:
+ case GNUTLS_PK_DSA:
+ case GNUTLS_PK_EDDSA_ED25519:
+ case GNUTLS_PK_EDDSA_ED448:
+ return 0;
+ case GNUTLS_PK_ECDSA:
+ {
+ /* just verify that x and y lie on the curve */
+ struct ecc_point r, pub;
+ const struct ecc_curve *curve;
+
+ if (params->params_nr != ECC_PUBLIC_PARAMS)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_INVALID_REQUEST);
+
+ curve = get_supported_nist_curve(params->curve);
+ if (curve == NULL)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+
+ ret = _ecc_params_to_pubkey(params, &pub, curve);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ecc_point_init(&r, curve);
+ /* verify that x,y lie on the curve */
+ ret =
+ ecc_point_set(&r, TOMPZ(params->params[ECC_X]),
+ TOMPZ(params->params[ECC_Y]));
+ if (ret == 0) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_ILLEGAL_PARAMETER);
+ goto ecc_cleanup;
+ }
+ ecc_point_clear(&r);
+
+ ret = 0;
+
+ ecc_cleanup:
+ ecc_point_clear(&pub);
+ }
+ break;
+#if ENABLE_GOST
+ case GNUTLS_PK_GOST_01:
+ case GNUTLS_PK_GOST_12_256:
+ case GNUTLS_PK_GOST_12_512:
+ {
+ /* just verify that x and y lie on the curve */
+ struct ecc_point r, pub;
+ const struct ecc_curve *curve;
+
+ if (params->params_nr != GOST_PUBLIC_PARAMS)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_INVALID_REQUEST);
+
+ curve = get_supported_gost_curve(params->curve);
+ if (curve == NULL)
+ return
+ gnutls_assert_val
+ (GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+
+ ret = _gost_params_to_pubkey(params, &pub, curve);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ecc_point_init(&r, curve);
+ /* verify that x,y lie on the curve */
+ ret =
+ ecc_point_set(&r, TOMPZ(params->params[GOST_X]),
+ TOMPZ(params->params[GOST_Y]));
+ if (ret == 0) {
+ ret =
+ gnutls_assert_val
+ (GNUTLS_E_ILLEGAL_PARAMETER);
+ goto gost_cleanup;
+ }
+ ecc_point_clear(&r);
+
+ ret = 0;
+
+ gost_cleanup:
+ ecc_point_clear(&pub);
+ }
+ break;
+#endif
+ default:
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ return ret;
+}
+
+static int calc_rsa_exp(gnutls_pk_params_st * params)
+{
+ bigint_t tmp;
+ int ret;
+
+ if (params->params_nr < RSA_PRIVATE_PARAMS - 2) {
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+
+ params->params[RSA_E1] = params->params[RSA_E2] = NULL;
+
+ ret = _gnutls_mpi_init_multi(&tmp, &params->params[RSA_E1], &params->params[RSA_E2], NULL);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* [6] = d % p-1, [7] = d % q-1 */
+ _gnutls_mpi_sub_ui(tmp, params->params[RSA_PRIME1], 1);
+ ret =
+ _gnutls_mpi_modm(params->params[RSA_E1], params->params[RSA_PRIV] /*d */ , tmp);
+ if (ret < 0)
+ goto fail;
+
+ _gnutls_mpi_sub_ui(tmp, params->params[RSA_PRIME2], 1);
+ ret =
+ _gnutls_mpi_modm(params->params[RSA_E2], params->params[RSA_PRIV] /*d */ , tmp);
+ if (ret < 0)
+ goto fail;
+
+ zrelease_mpi_key(&tmp);
+
+ return 0;
+
+fail:
+ zrelease_mpi_key(&tmp);
+ zrelease_mpi_key(&params->params[RSA_E1]);
+ zrelease_mpi_key(&params->params[RSA_E2]);
+
+ return ret;
+}
+
+static int calc_rsa_priv(gnutls_pk_params_st * params)
+{
+ bigint_t lcm, p1, q1;
+ int ret;
+
+ params->params[RSA_PRIV] = NULL;
+
+ ret = _gnutls_mpi_init_multi(&params->params[RSA_PRIV], &lcm, &p1, &q1, NULL);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* lcm(p - 1, q - 1) */
+ mpz_sub_ui(p1, params->params[RSA_PRIME1], 1);
+ mpz_sub_ui(q1, params->params[RSA_PRIME2], 1);
+ mpz_lcm(lcm, p1, q1);
+
+ zrelease_mpi_key(&p1);
+ zrelease_mpi_key(&q1);
+
+ /* d = e^{-1} (mod lcm) */
+ ret = mpz_invert(params->params[RSA_PRIV], params->params[RSA_PUB], lcm);
+
+ zrelease_mpi_key(&lcm);
+
+ if (ret == 0) {
+ zrelease_mpi_key(&params->params[RSA_PRIV]);
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+
+ return 0;
+}
+
+static int calc_dsa_pub(gnutls_pk_params_st * params)
+{
+ int ret;
+
+ params->params[DSA_Y] = NULL;
+
+ ret = _gnutls_mpi_init(&params->params[DSA_Y]);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* y = g^x mod p */
+ ret = _gnutls_mpi_powm(params->params[DSA_Y], params->params[DSA_G],
+ params->params[DSA_X], params->params[DSA_P]);
+ if (ret < 0) {
+ zrelease_mpi_key(&params->params[DSA_Y]);
+ return gnutls_assert_val(ret);
+ }
+
+ return 0;
+}
+
+static int
+wrap_nettle_pk_fixup(gnutls_pk_algorithm_t algo,
+ gnutls_direction_t direction,
+ gnutls_pk_params_st * params)
+{
+ int ret;
+
+ if (direction != GNUTLS_IMPORT)
+ return 0;
+
+ if (algo == GNUTLS_PK_RSA) {
+ struct rsa_private_key priv;
+
+ if (params->params[RSA_PRIV] == NULL) {
+ ret = calc_rsa_priv(params);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ params->params_nr++;
+ }
+
+ /* do not trust the generated values. Some old private keys
+ * generated by us have mess on the values. Those were very
+ * old but it seemed some of the shipped example private
+ * keys were as old.
+ */
+ if (params->params_nr < RSA_PRIVATE_PARAMS - 3)
+ return gnutls_assert_val(GNUTLS_E_PK_INVALID_PRIVKEY);
+
+ if (params->params[RSA_COEF] == NULL) {
+ ret = _gnutls_mpi_init(&params->params[RSA_COEF]);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ if (mpz_cmp_ui(TOMPZ(params->params[RSA_PRIME1]), 0) == 0)
+ return gnutls_assert_val(GNUTLS_E_PK_INVALID_PRIVKEY);
+
+ if (mpz_invert(TOMPZ(params->params[RSA_COEF]),
+ TOMPZ(params->params[RSA_PRIME2]),
+ TOMPZ(params->params[RSA_PRIME1])) == 0)
+ return gnutls_assert_val(GNUTLS_E_PK_INVALID_PRIVKEY);
+
+ /* calculate exp1 [6] and exp2 [7] */
+ zrelease_mpi_key(&params->params[RSA_E1]);
+ zrelease_mpi_key(&params->params[RSA_E2]);
+
+ /* marks RSA_COEF as present */
+ params->params_nr = RSA_PRIVATE_PARAMS - 2;
+ ret = calc_rsa_exp(params);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ params->params_nr = RSA_PRIVATE_PARAMS;
+
+ /* perform nettle's internal checks */
+ _rsa_params_to_privkey(params, &priv);
+ ret = rsa_private_key_prepare(&priv);
+ if (ret == 0) {
+ return gnutls_assert_val(GNUTLS_E_PK_INVALID_PRIVKEY);
+ }
+ } else if (algo == GNUTLS_PK_EDDSA_ED25519 ||
+ algo == GNUTLS_PK_EDDSA_ED448) {
+ if (unlikely(get_eddsa_curve(algo) != params->curve))
+ return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+
+ if (params->raw_priv.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_PK_INVALID_PRIVKEY);
+
+ if (params->raw_pub.data == NULL) {
+ params->raw_pub.data = gnutls_malloc(params->raw_priv.size);
+ }
+
+ if (params->raw_pub.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ ret = eddsa_public_key(algo,
+ params->raw_pub.data,
+ params->raw_priv.data);
+ if (ret < 0) {
+ gnutls_free(params->raw_pub.data);
+ return ret;
+ }
+
+ params->raw_pub.size = params->raw_priv.size;
+ } else if (algo == GNUTLS_PK_ECDH_X25519 ||
+ algo == GNUTLS_PK_ECDH_X448) {
+ if (unlikely(get_ecdh_curve(algo) != params->curve))
+ return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+
+ if (params->raw_priv.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_PK_INVALID_PRIVKEY);
+
+ if (params->raw_pub.data == NULL) {
+ params->raw_pub.data = gnutls_malloc(params->raw_priv.size);
+ }
+
+ if (params->raw_pub.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ ret = edwards_curve_mul_g(algo,
+ params->raw_pub.data,
+ params->raw_priv.data);
+ if (ret < 0) {
+ gnutls_free(params->raw_pub.data);
+ return ret;
+ }
+
+ params->raw_pub.size = params->raw_priv.size;
+ } else if (algo == GNUTLS_PK_RSA_PSS) {
+ if (params->params_nr < RSA_PRIVATE_PARAMS - 3)
+ return gnutls_assert_val(GNUTLS_E_PK_INVALID_PRIVKEY);
+
+ if (params->spki.rsa_pss_dig != 0) {
+ unsigned pub_size = nettle_mpz_sizeinbase_256_u(TOMPZ(params->params[RSA_MODULUS]));
+ /* sanity check for private key */
+ CHECK_INVALID_RSA_PSS_PARAMS(gnutls_hash_get_len(params->spki.rsa_pss_dig),
+ params->spki.salt_size, pub_size,
+ GNUTLS_E_PK_INVALID_PUBKEY_PARAMS);
+ }
+ } else if (algo == GNUTLS_PK_DSA) {
+ if (params->params[DSA_Y] == NULL) {
+ ret = calc_dsa_pub(params);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ params->params_nr++;
+ }
+ }
+#if ENABLE_GOST
+ else if (algo == GNUTLS_PK_GOST_01 ||
+ algo == GNUTLS_PK_GOST_12_256 ||
+ algo == GNUTLS_PK_GOST_12_512) {
+ struct ecc_point r;
+ struct ecc_scalar priv;
+ const struct ecc_curve *curve;
+
+ if (params->params_nr != GOST_PRIVATE_PARAMS)
+ return gnutls_assert_val
+ (GNUTLS_E_INVALID_REQUEST);
+
+ curve = get_supported_gost_curve(params->curve);
+ if (curve == NULL)
+ return gnutls_assert_val
+ (GNUTLS_E_ECC_UNSUPPORTED_CURVE);
+
+ if (ecc_bit_size(curve) < _gnutls_mpi_get_nbits(params->params[GOST_K]))
+ gostdsa_unmask_key(curve, TOMPZ(params->params[GOST_K]));
+
+ ret = _gost_params_to_privkey(params, &priv, curve);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ ecc_point_init(&r, curve);
+ gost_point_mul_g(&r, &priv);
+
+ ecc_point_get(&r, params->params[GOST_X],
+ params->params[GOST_Y]);
+
+ ecc_point_clear(&r);
+ ecc_scalar_clear(&priv);
+ }
+#endif
+
+ return 0;
+}
+
+int crypto_pk_prio = INT_MAX;
+
+gnutls_crypto_pk_st _gnutls_pk_ops = {
+ .encrypt = _wrap_nettle_pk_encrypt,
+ .decrypt = _wrap_nettle_pk_decrypt,
+ .decrypt2 = _wrap_nettle_pk_decrypt2,
+ .sign = _wrap_nettle_pk_sign,
+ .verify = _wrap_nettle_pk_verify,
+ .verify_priv_params = wrap_nettle_pk_verify_priv_params,
+ .verify_pub_params = wrap_nettle_pk_verify_pub_params,
+ .generate_params = wrap_nettle_pk_generate_params,
+ .generate_keys = wrap_nettle_pk_generate_keys,
+ .pk_fixup_private_params = wrap_nettle_pk_fixup,
+ .derive = _wrap_nettle_pk_derive,
+ .curve_exists = _wrap_nettle_pk_curve_exists,
+ .pk_exists = _wrap_nettle_pk_exists,
+ .sign_exists = _wrap_nettle_pk_sign_exists
+};