summaryrefslogtreecommitdiffstats
path: root/lib/nettle/int/dsa-keygen-fips186.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/nettle/int/dsa-keygen-fips186.c')
-rw-r--r--lib/nettle/int/dsa-keygen-fips186.c466
1 files changed, 466 insertions, 0 deletions
diff --git a/lib/nettle/int/dsa-keygen-fips186.c b/lib/nettle/int/dsa-keygen-fips186.c
new file mode 100644
index 0000000..69f7f8c
--- /dev/null
+++ b/lib/nettle/int/dsa-keygen-fips186.c
@@ -0,0 +1,466 @@
+/* dsa-keygen.c
+ *
+ * Generation of DSA keypairs
+ */
+
+/* nettle, low-level cryptographics library
+ *
+ * Copyright (C) 2013, 2014 Red Hat
+ *
+ * The nettle 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.
+ *
+ * The nettle 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 the nettle library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02111-1301, USA.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <nettle/dsa.h>
+#include <dsa-fips.h>
+
+#include <nettle/bignum.h>
+
+unsigned _dsa_check_qp_sizes(unsigned q_bits, unsigned p_bits, unsigned generate)
+{
+ switch (q_bits) {
+ case 160:
+ FIPS_RULE(generate != 0, 0, "DSA 160-bit generation\n");
+
+ if (p_bits != 1024)
+ return 0;
+ break;
+ case 224:
+ if (p_bits != 2048)
+ return 0;
+ break;
+ case 256:
+ if (p_bits != 2048 && p_bits != 3072)
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+/* This generates p,q params using the A.1.2.1 algorithm in FIPS 186-4.
+ *
+ * The hash function used is SHA384.
+ */
+int
+_dsa_generate_dss_pq(struct dsa_params *params,
+ struct dss_params_validation_seeds *cert,
+ unsigned seed_length, void *seed,
+ void *progress_ctx, nettle_progress_func * progress,
+ unsigned p_bits /* = L */ , unsigned q_bits /* = N */ )
+{
+ mpz_t r, p0, t, z, s, tmp, dp0;
+ int ret;
+ unsigned iterations, old_counter, i;
+ uint8_t *storage = NULL;
+ unsigned storage_length = 0;
+
+ ret = _dsa_check_qp_sizes(q_bits, p_bits, 1);
+ if (ret == 0) {
+ return 0;
+ }
+
+ if (seed_length < q_bits / 8) {
+ _gnutls_debug_log("Seed length must be larger than %d bytes (it is %d)\n", q_bits/8, seed_length);
+ return 0;
+ }
+
+ mpz_init(p0);
+ mpz_init(dp0);
+ mpz_init(r);
+ mpz_init(t);
+ mpz_init(z);
+ mpz_init(s);
+ mpz_init(tmp);
+
+ /* firstseed < 2^(N-1) */
+ mpz_set_ui(r, 1);
+ mpz_mul_2exp(r, r, q_bits - 1);
+
+ nettle_mpz_set_str_256_u(s, seed_length, seed);
+ if (mpz_cmp(s, r) < 0) {
+ goto fail;
+ }
+
+ cert->qseed_length = sizeof(cert->qseed);
+ cert->pseed_length = sizeof(cert->pseed);
+
+ ret = st_provable_prime(params->q,
+ &cert->qseed_length, cert->qseed,
+ &cert->qgen_counter,
+ q_bits,
+ seed_length, seed, progress_ctx, progress);
+ if (ret == 0) {
+ goto fail;
+ }
+
+ if (progress)
+ progress(progress_ctx, 'q');
+
+ ret = st_provable_prime(p0,
+ &cert->pseed_length, cert->pseed,
+ &cert->pgen_counter,
+ 1 + div_ceil(p_bits, 2),
+ cert->qseed_length, cert->qseed,
+ progress_ctx, progress);
+ if (ret == 0) {
+ goto fail;
+ }
+
+ iterations = div_ceil(p_bits, DIGEST_SIZE*8);
+ old_counter = cert->pgen_counter;
+
+ if (iterations > 0) {
+ storage_length = iterations * DIGEST_SIZE;
+ storage = malloc(storage_length);
+ if (storage == NULL) {
+ goto fail;
+ }
+
+ nettle_mpz_set_str_256_u(s, cert->pseed_length, cert->pseed);
+ for (i = 0; i < iterations; i++) {
+ cert->pseed_length = nettle_mpz_sizeinbase_256_u(s);
+ nettle_mpz_get_str_256(cert->pseed_length, cert->pseed, s);
+
+ hash(&storage[(iterations - i - 1) * DIGEST_SIZE],
+ cert->pseed_length, cert->pseed);
+ mpz_add_ui(s, s, 1);
+ }
+
+ /* x = 2^(p_bits-1) + (x mod 2^(p_bits-1)) */
+ nettle_mpz_set_str_256_u(tmp, storage_length, storage);
+ }
+
+ mpz_set_ui(r, 1);
+ mpz_mul_2exp(r, r, p_bits - 1);
+
+ mpz_fdiv_r_2exp(tmp, tmp, p_bits - 1);
+ mpz_add(tmp, tmp, r);
+
+ /* Generate candidate prime p in [2^(bits-1), 2^bits] */
+
+ /* t = u[x/2c0] */
+ mpz_mul_2exp(dp0, p0, 1); /* dp0 = 2*p0 */
+ mpz_mul(dp0, dp0, params->q); /* dp0 = 2*p0*q */
+
+ mpz_cdiv_q(t, tmp, dp0);
+
+ retry:
+ /* c = 2p0*q*t + 1 */
+ mpz_mul(params->p, dp0, t);
+ mpz_add_ui(params->p, params->p, 1);
+
+ if (mpz_sizeinbase(params->p, 2) > p_bits) {
+ /* t = 2^(bits-1)/2qp0 */
+ mpz_set_ui(tmp, 1);
+ mpz_mul_2exp(tmp, tmp, p_bits - 1);
+ mpz_cdiv_q(t, tmp, dp0);
+
+ /* p = t* 2tq p0 + 1 */
+ mpz_mul(params->p, dp0, t);
+ mpz_add_ui(params->p, params->p, 1);
+ }
+
+ cert->pgen_counter++;
+
+ mpz_set_ui(r, 0);
+
+ if (iterations > 0) {
+ for (i = 0; i < iterations; i++) {
+ cert->pseed_length = nettle_mpz_sizeinbase_256_u(s);
+ nettle_mpz_get_str_256(cert->pseed_length, cert->pseed, s);
+
+ hash(&storage[(iterations - i - 1) * DIGEST_SIZE],
+ cert->pseed_length, cert->pseed);
+ mpz_add_ui(s, s, 1);
+ }
+
+ /* r = a */
+ nettle_mpz_set_str_256_u(r, storage_length, storage);
+ }
+
+ cert->pseed_length = nettle_mpz_sizeinbase_256_u(s);
+ nettle_mpz_get_str_256(cert->pseed_length, cert->pseed, s);
+
+ /* a = 2 + (a mod (p-3)) */
+ mpz_sub_ui(tmp, params->p, 3); /* c is too large to worry about negatives */
+ mpz_mod(r, r, tmp);
+ mpz_add_ui(r, r, 2);
+
+ /* z = a^(2tq) mod p */
+ mpz_mul_2exp(tmp, t, 1); /* tmp = 2t */
+ mpz_mul(tmp, tmp, params->q); /* tmp = 2tq */
+ mpz_powm(z, r, tmp, params->p);
+
+ mpz_sub_ui(tmp, z, 1);
+
+ mpz_gcd(tmp, tmp, params->p);
+ if (mpz_cmp_ui(tmp, 1) == 0) {
+ mpz_powm(tmp, z, p0, params->p);
+ if (mpz_cmp_ui(tmp, 1) == 0) {
+ goto success;
+ }
+ }
+
+ if (progress)
+ progress(progress_ctx, 'x');
+
+ if (cert->pgen_counter >= (4 * p_bits + old_counter))
+ return 0;
+
+ mpz_add_ui(t, t, 1);
+ goto retry;
+
+ success:
+ if (progress)
+ progress(progress_ctx, 'p');
+
+ ret = 1;
+ goto finish;
+
+ fail:
+ ret = 0;
+
+ finish:
+ mpz_clear(dp0);
+ mpz_clear(p0);
+ mpz_clear(tmp);
+ mpz_clear(t);
+ mpz_clear(z);
+ mpz_clear(s);
+ mpz_clear(r);
+ free(storage);
+ return ret;
+}
+
+int
+_dsa_generate_dss_g(struct dsa_params *params,
+ unsigned domain_seed_size, const uint8_t* domain_seed,
+ void *progress_ctx, nettle_progress_func * progress,
+ unsigned index)
+{
+ mpz_t e, w;
+ uint16_t count;
+ uint8_t *dseed = NULL;
+ unsigned dseed_size;
+ unsigned pos;
+ uint8_t digest[DIGEST_SIZE];
+ int ret;
+
+ if (index > 255 || domain_seed_size == 0)
+ return 0;
+
+ dseed_size = domain_seed_size + 4 + 1 + 2;
+ dseed = malloc(dseed_size);
+ if (dseed == NULL)
+ return 0;
+
+ mpz_init(e);
+ mpz_init(w);
+
+ memcpy(dseed, domain_seed, domain_seed_size);
+ pos = domain_seed_size;
+
+ memcpy(dseed + pos, "\x67\x67\x65\x6e", 4);
+ pos += 4;
+
+ *(dseed + pos) = (uint8_t) index;
+ pos += 1;
+
+ mpz_sub_ui(e, params->p, 1);
+ mpz_fdiv_q(e, e, params->q);
+
+ for (count = 1; count < 65535; count++) {
+ *(dseed + pos) = (count >> 8) & 0xff;
+ *(dseed + pos + 1) = count & 0xff;
+
+ hash(digest, dseed_size, dseed);
+
+ nettle_mpz_set_str_256_u(w, DIGEST_SIZE, digest);
+
+ mpz_powm(params->g, w, e, params->p);
+
+ if (mpz_cmp_ui(params->g, 2) >= 0) {
+ /* found */
+ goto success;
+ }
+ if (progress)
+ progress(progress_ctx, 'x');
+ }
+
+ /* if we're here we failed */
+ if (progress)
+ progress(progress_ctx, 'X');
+ ret = 0;
+ goto finish;
+
+ success:
+ if (progress)
+ progress(progress_ctx, 'g');
+
+ ret = 1;
+
+ finish:
+ free(dseed);
+ mpz_clear(e);
+ mpz_clear(w);
+ return ret;
+
+}
+
+/* Generates the public and private DSA (or DH) keys
+ */
+void
+_dsa_generate_dss_xy(struct dsa_params *params,
+ mpz_t y, mpz_t x,
+ void *random_ctx, nettle_random_func * random)
+{
+ mpz_t r;
+
+ mpz_init(r);
+ mpz_set(r, params->q);
+ mpz_sub_ui(r, r, 2);
+ nettle_mpz_random(x, random_ctx, random, r);
+ mpz_add_ui(x, x, 1);
+
+ mpz_powm(y, params->g, x, params->p);
+
+ mpz_clear(r);
+}
+
+/* This generates p, q, g params using the algorithms from FIPS 186-4.
+ * For p, q, the Shawe-Taylor algorithm is used.
+ * For g, the verifiable canonical generation of the generator is used.
+ *
+ * The hash function used is SHA384.
+ *
+ * pub: The output public key
+ * key: The output private key
+ * cert: A certificate that can be used to verify the generated parameters
+ * index: 1 for digital signatures (DSA), 2 for key establishment (DH)
+ * p_bits: The requested size of p
+ * q_bits: The requested size of q
+ *
+ */
+int
+dsa_generate_dss_pqg(struct dsa_params *params,
+ struct dss_params_validation_seeds *cert,
+ unsigned index,
+ void *random_ctx, nettle_random_func * random,
+ void *progress_ctx, nettle_progress_func * progress,
+ unsigned p_bits /* = L */ , unsigned q_bits /* = N */ )
+{
+ int ret;
+ uint8_t domain_seed[MAX_PVP_SEED_SIZE*3];
+ unsigned domain_seed_size = 0;
+
+ ret = _dsa_check_qp_sizes(q_bits, p_bits, 1);
+ if (ret == 0)
+ return 0;
+
+ cert->seed_length = 2 * (q_bits / 8) + 1;
+
+ if (cert->seed_length > sizeof(cert->seed))
+ return 0;
+
+ random(random_ctx, cert->seed_length, cert->seed);
+
+ ret = _dsa_generate_dss_pq(params, cert, cert->seed_length, cert->seed,
+ progress_ctx, progress, p_bits, q_bits);
+ if (ret == 0)
+ return 0;
+
+ domain_seed_size = cert->seed_length + cert->qseed_length + cert->pseed_length;
+ memcpy(domain_seed, cert->seed, cert->seed_length);
+ memcpy(&domain_seed[cert->seed_length], cert->pseed, cert->pseed_length);
+ memcpy(&domain_seed[cert->seed_length+cert->pseed_length], cert->qseed, cert->qseed_length);
+ ret = _dsa_generate_dss_g(params, domain_seed_size, domain_seed,
+ progress_ctx, progress, index);
+ if (ret == 0)
+ return 0;
+
+ return 1;
+}
+
+int
+_dsa_generate_dss_pqg(struct dsa_params *params,
+ struct dss_params_validation_seeds *cert,
+ unsigned index,
+ unsigned seed_size, void *seed,
+ void *progress_ctx, nettle_progress_func * progress,
+ unsigned p_bits /* = L */ , unsigned q_bits /* = N */ )
+{
+ int ret;
+ uint8_t domain_seed[MAX_PVP_SEED_SIZE*3];
+ unsigned domain_seed_size = 0;
+
+ ret = _dsa_check_qp_sizes(q_bits, p_bits, 1);
+ if (ret == 0)
+ return 0;
+
+ if (_gnutls_fips_mode_enabled() != 0) {
+ cert->seed_length = 2 * (q_bits / 8) + 1;
+
+ FIPS_RULE(cert->seed_length != seed_size, 0, "unsupported DSA seed length (is %d, should be %d)\n", seed_size, cert->seed_length);
+ } else {
+ cert->seed_length = seed_size;
+ }
+
+ if (cert->seed_length > sizeof(cert->seed))
+ return 0;
+
+
+ memcpy(cert->seed, seed, cert->seed_length);
+
+ ret = _dsa_generate_dss_pq(params, cert, cert->seed_length, cert->seed,
+ progress_ctx, progress, p_bits, q_bits);
+ if (ret == 0)
+ return 0;
+
+ domain_seed_size = cert->seed_length + cert->qseed_length + cert->pseed_length;
+ memcpy(domain_seed, cert->seed, cert->seed_length);
+ memcpy(&domain_seed[cert->seed_length], cert->pseed, cert->pseed_length);
+ memcpy(&domain_seed[cert->seed_length+cert->pseed_length], cert->qseed, cert->qseed_length);
+ ret = _dsa_generate_dss_g(params, domain_seed_size, domain_seed,
+ progress_ctx, progress, index);
+ if (ret == 0)
+ return 0;
+
+ return 1;
+}
+
+int
+dsa_generate_dss_keypair(struct dsa_params *params,
+ mpz_t y,
+ mpz_t x,
+ void *random_ctx, nettle_random_func * random,
+ void *progress_ctx, nettle_progress_func * progress)
+{
+ _dsa_generate_dss_xy(params, y, x, random_ctx, random);
+
+ if (progress)
+ progress(progress_ctx, '\n');
+
+ return 1;
+
+}