packages feed

crypton-2.1.2: cbits/crypton_modinv.c

/*
 * Inversion modulo an odd number through s2n-bignum, whose routine takes a
 * fixed number of division steps rather than an exponentiation: twenty to
 * thirty times less work than Fermat's little theorem at the sizes here.
 * Measured on an Apple M4, inverting modulo a curve order:
 *
 *              Fermat     this
 *     P-256    6.02 us    0.80
 *     P-384    31.3       1.20
 *     P-521    63.2       2.05
 *
 * It uses no instruction beyond the base architecture on either x86-64 or
 * AArch64, so unlike the rest of the vendored assembly there is nothing to
 * ask the processor first.
 */
#include <string.h>

#include "crypton_modinv.h"

#ifdef CRYPTON_S2N_BIGNUM

extern void bignum_modinv(uint64_t k, uint64_t *z, const uint64_t *a,
                          const uint64_t *b, uint64_t *t);

/* 4096 bits and no more, which covers every modulus that reaches here -- the
 * order of a curve, or a prime factor of an RSA modulus -- and keeps the
 * working space on the stack.  Anything larger is handed back. */
#define MODINV_MAXWORDS 64

int crypton_modinv_sec(uint8_t *z, const uint8_t *a, const uint8_t *m,
                       uint32_t len)
{
	uint64_t aw[MODINV_MAXWORDS], mw[MODINV_MAXWORDS];
	uint64_t zw[MODINV_MAXWORDS], t[3 * MODINV_MAXWORDS];
	uint32_t k = (len + 7) / 8;
	uint32_t i;

	/* An even modulus is the one case it answers without saying it
	 * cannot: it returns a number that is not an inverse rather than
	 * failing, so keep it away from here.  Every caller's modulus is odd. */
	if (len == 0 || k > MODINV_MAXWORDS || (m[len - 1] & 1) == 0)
		return 1;

	for (i = 0; i < k; i++) {
		aw[i] = 0;
		mw[i] = 0;
	}
	for (i = 0; i < len; i++) {
		uint32_t pos = len - 1 - i;

		aw[pos / 8] |= (uint64_t)a[i] << (8 * (pos % 8));
		mw[pos / 8] |= (uint64_t)m[i] << (8 * (pos % 8));
	}

	bignum_modinv(k, zw, aw, mw, t);

	for (i = 0; i < len; i++)
		z[len - 1 - i] = (uint8_t)(zw[i / 8] >> (8 * (i % 8)));
	return 0;
}

#else

int crypton_modinv_sec(uint8_t *z, const uint8_t *a, const uint8_t *m,
                       uint32_t len)
{
	(void)z;
	(void)a;
	(void)m;
	(void)len;
	return 1;
}

#endif