/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifdef FREEBL_NO_DEPEND
#include "stubs.h"
#endif

#include "blapi.h"

#include "mpi.h"
#include "mpprime.h"
#include "secerr.h"
#include "secmpi.h"

mp_err
mpp_random_secure(mp_int *a)
{
    SECStatus rv;
    rv = RNG_GenerateGlobalRandomBytes((unsigned char *)MP_DIGITS(a), MP_USED(a) * sizeof(mp_digit));
    if (rv != SECSuccess) {
        return MP_UNDEF;
    }
    MP_SIGN(a) = MP_ZPOS;
    return MP_OKAY;
}

mp_err
mpp_pprime_secure(mp_int *a, int nt)
{
    return mpp_pprime_ext_random(a, nt, &mpp_random_secure);
}

mp_err
mpp_pprime_or_power_secure(mp_int *a, mp_int *fact, int nt)
{
    return mpp_pprime_or_power_ext_random(a, fact, nt, &mpp_random_secure);
}

mp_err
mpp_make_prime_secure(mp_int *start, mp_size nBits, mp_size strong)
{
    return mpp_make_prime_ext_random(start, nBits, strong, &mpp_random_secure);
}

/*
** Number of times to attempt to generate a prime (p or q) from a random
** seed (the seed changes for each iteration).
*/
#define MAX_PRIME_GEN_ATTEMPTS 10

SECStatus
generate_prime(mp_int *prime, int primeLen)
{
    mp_err err = MP_OKAY;
    SECStatus rv = SECSuccess;
    int piter;
    unsigned char *pb = NULL;
    pb = PORT_Alloc(primeLen);
    if (!pb) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto cleanup;
    }
    for (piter = 0; piter < MAX_PRIME_GEN_ATTEMPTS; piter++) {
        CHECK_SEC_OK(RNG_GenerateGlobalRandomBytes(pb, primeLen));
        pb[0] |= 0xC0;            /* set two high-order bits */
        pb[primeLen - 1] |= 0x01; /* set low-order bit       */
        CHECK_MPI_OK(mp_read_unsigned_octets(prime, pb, primeLen));
        err = mpp_make_prime_secure(prime, primeLen * 8, PR_FALSE);
        if (err != MP_NO)
            goto cleanup;
        /* keep going while err == MP_NO */
    }
cleanup:
    if (pb)
        PORT_ZFree(pb, primeLen);
    if (err) {
        MP_TO_SEC_ERROR(err);
        rv = SECFailure;
    }
    return rv;
}
