/*
* 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
*
*/
/* This file contains the functions needed for RSA/DSA public key
* encryption and signatures.
*/
#include "gnutls_int.h"
#include
#include
#include "errors.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if ENABLE_GOST
#if NEED_INT_ECC
#include "ecc/gostdsa.h"
#include "ecc-gost-curve.h"
#else
#include
#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
#include
#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(¶ms->params[DSA_P], ¶ms->params[DSA_Q],
¶ms->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(¶ms->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(¶ms);
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, ¶ms, 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(¶ms);
gnutls_pk_params_release(¶ms);
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(¶ms);
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, ¶ms, 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(¶ms);
gnutls_pk_params_release(¶ms);
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, ¶ms->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(¶ms->params[DSA_Y], ¶ms->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(¶ms->params[DSA_Y], ¶ms->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,
¶ms->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(¶ms->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(¶ms->params[ECC_X], ¶ms->params[ECC_Y],
¶ms->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(¶ms->params[GOST_X], ¶ms->params[GOST_Y],
¶ms->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(¶ms->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, ¶ms->params[RSA_E1], ¶ms->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(¶ms->params[RSA_E1]);
zrelease_mpi_key(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->params[RSA_E1]);
zrelease_mpi_key(¶ms->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
};