packages feed

crypton-2.1.2: cbits/crypton_ecc_s2n.c

#include <string.h>

#include "crypton_ecc_s2n.h"
#include "crypton_ecc_s2n_curves.h"
#include "crypton_cpu.h"

/* P-384, Montgomery domain, six words a coordinate */
extern void p384_montjscalarmul(uint64_t *res, const uint64_t *s, const uint64_t *p);
extern void p384_montjscalarmul_alt(uint64_t *res, const uint64_t *s, const uint64_t *p);
extern void bignum_tomont_p384(uint64_t *z, const uint64_t *x);
extern void bignum_tomont_p384_alt(uint64_t *z, const uint64_t *x);
extern void bignum_deamont_p384(uint64_t *z, const uint64_t *x);
extern void bignum_deamont_p384_alt(uint64_t *z, const uint64_t *x);
extern void bignum_montmul_p384(uint64_t *z, const uint64_t *x, const uint64_t *y);
extern void bignum_montmul_p384_alt(uint64_t *z, const uint64_t *x, const uint64_t *y);
extern void bignum_montsqr_p384(uint64_t *z, const uint64_t *x);
extern void bignum_montsqr_p384_alt(uint64_t *z, const uint64_t *x);
extern void bignum_montinv_p384(uint64_t *z, const uint64_t *x);

/* P-521, ordinary values, nine words a coordinate */
extern void p521_jscalarmul(uint64_t *res, const uint64_t *s, const uint64_t *p);
extern void p521_jscalarmul_alt(uint64_t *res, const uint64_t *s, const uint64_t *p);
extern void bignum_mul_p521(uint64_t *z, const uint64_t *x, const uint64_t *y);
extern void bignum_mul_p521_alt(uint64_t *z, const uint64_t *x, const uint64_t *y);
extern void bignum_sqr_p521(uint64_t *z, const uint64_t *x);
extern void bignum_sqr_p521_alt(uint64_t *z, const uint64_t *x);
extern void bignum_inv_p521(uint64_t *z, const uint64_t *x);

/* One flavour or the other, all the way through: on x86-64 the plain form
 * wants MULX, ADCX and ADOX and _alt is the fallback, so mixing them would
 * fault on a machine without those. */
struct p384_asm {
	void (*tomont)(uint64_t *, const uint64_t *);
	void (*deamont)(uint64_t *, const uint64_t *);
	void (*montmul)(uint64_t *, const uint64_t *, const uint64_t *);
	void (*montsqr)(uint64_t *, const uint64_t *);
	void (*jscalarmul)(uint64_t *, const uint64_t *, const uint64_t *);
};
struct p521_asm {
	void (*mul)(uint64_t *, const uint64_t *, const uint64_t *);
	void (*sqr)(uint64_t *, const uint64_t *);
	void (*jscalarmul)(uint64_t *, const uint64_t *, const uint64_t *);
};

static const struct p384_asm p384_std = {
	bignum_tomont_p384, bignum_deamont_p384, bignum_montmul_p384,
	bignum_montsqr_p384, p384_montjscalarmul
};
static const struct p384_asm p384_alt = {
	bignum_tomont_p384_alt, bignum_deamont_p384_alt,
	bignum_montmul_p384_alt, bignum_montsqr_p384_alt,
	p384_montjscalarmul_alt
};
static const struct p521_asm p521_std = {
	bignum_mul_p521, bignum_sqr_p521, p521_jscalarmul
};
static const struct p521_asm p521_alt = {
	bignum_mul_p521_alt, bignum_sqr_p521_alt, p521_jscalarmul_alt
};

/* The same question as for P-256, answered the same way; see
 * cbits/p256/p256_s2n.c. */
static int use_alt(void)
{
#if defined(__aarch64__) || defined(__arm64__)
#ifdef __APPLE__
	return 1;
#else
	return 0;
#endif
#else
	return (crypton_x86_simd_features() & CRYPTON_X86_ADX) == 0;
#endif
}

#define MAXWORDS 9

static void be_to_le64(uint64_t *w, const uint8_t *b, uint32_t len)
{
	uint32_t i;

	for (i = 0; i < MAXWORDS; i++)
		w[i] = 0;
	for (i = 0; i < len; i++) {
		uint32_t pos = len - 1 - i;
		w[pos / 8] |= (uint64_t)b[i] << (8 * (pos % 8));
	}
}

static void le64_to_be(uint8_t *b, uint32_t len, const uint64_t *w)
{
	uint32_t i;

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

static int is_zero(const uint64_t *w, int words)
{
	uint64_t acc = 0;
	int i;

	for (i = 0; i < words; i++)
		acc |= w[i];
	return acc == 0;
}

static int mul_p384(uint8_t *outx, uint8_t *outy, const uint8_t *px,
                    const uint8_t *py, const uint8_t *k, uint32_t klen)
{
	const struct p384_asm *f = use_alt() ? &p384_alt : &p384_std;
	uint64_t pt[18], res[18], sc[MAXWORDS], t[MAXWORDS];
	uint64_t zi[6], zi2[6], zi3[6], num[6];
	static const uint64_t one[6] = {1, 0, 0, 0, 0, 0};

	be_to_le64(t, px, P384_PLEN);
	f->tomont(pt, t);
	be_to_le64(t, py, P384_PLEN);
	f->tomont(pt + 6, t);
	f->tomont(pt + 12, one);
	be_to_le64(sc, k, klen);

	f->jscalarmul(res, sc, pt);
	if (is_zero(res + 12, 6))
		return 1;

	/* affine again: x = X/Z^2, y = Y/Z^3, with the inverse taken in the
	 * Montgomery domain so that it lands where the rest of these are */
	bignum_montinv_p384(zi, res + 12);
	f->montsqr(zi2, zi);
	f->montmul(zi3, zi2, zi);
	f->montmul(num, res, zi2);
	f->deamont(t, num);
	le64_to_be(outx, P384_PLEN, t);
	f->montmul(num, res + 6, zi3);
	f->deamont(t, num);
	le64_to_be(outy, P384_PLEN, t);
	return 0;
}

static int mul_p521(uint8_t *outx, uint8_t *outy, const uint8_t *px,
                    const uint8_t *py, const uint8_t *k, uint32_t klen)
{
	const struct p521_asm *f = use_alt() ? &p521_alt : &p521_std;
	uint64_t pt[27], res[27], sc[MAXWORDS], t[MAXWORDS];
	uint64_t zi[9], zi2[9], zi3[9];
	static const uint64_t one[9] = {1, 0, 0, 0, 0, 0, 0, 0, 0};

	be_to_le64(pt, px, P521_PLEN);
	be_to_le64(pt + 9, py, P521_PLEN);
	memcpy(pt + 18, one, sizeof(one));
	be_to_le64(sc, k, klen);

	f->jscalarmul(res, sc, pt);
	if (is_zero(res + 18, 9))
		return 1;

	bignum_inv_p521(zi, res + 18);
	f->sqr(zi2, zi);
	f->mul(zi3, zi2, zi);
	f->mul(t, res, zi2);
	le64_to_be(outx, P521_PLEN, t);
	f->mul(t, res + 9, zi3);
	le64_to_be(outy, P521_PLEN, t);
	return 0;
}

int crypton_s2n_ecc_mul(int *ret, uint8_t *outx, uint8_t *outy,
                        const uint8_t *px, const uint8_t *py,
                        const uint8_t *k, uint32_t klen,
                        const uint8_t *a, const uint8_t *b,
                        const uint8_t *p, uint32_t plen)
{
	int is384;

	if (plen == P384_PLEN && memcmp(p, P384_P, plen) == 0
	    && memcmp(a, P384_A, plen) == 0 && memcmp(b, P384_B, plen) == 0)
		is384 = 1;
	else if (plen == P521_PLEN && memcmp(p, P521_P, plen) == 0
	         && memcmp(a, P521_A, plen) == 0
	         && memcmp(b, P521_B, plen) == 0)
		is384 = 0;
	else
		return 0; /* some other curve; the C answers it */

	/* A scalar longer than the prime is not something the word arrays
	 * here hold, and it is not what any caller of these two curves
	 * sends, so leave it to the C rather than grow a second path. */
	if (klen == 0 || klen > plen)
		return 0;

	/* The C does not require the coordinates to be reduced -- it takes
	 * whatever fits in its limbs and lets the conversion to Montgomery
	 * form reduce it.  Rather than carry a reduction here to match, hand
	 * that case back: nothing sends one, and this way the two cannot
	 * disagree about it.  (A differential test against the C found this;
	 * the first version of this check returned -1 and was wrong.) */
	if (memcmp(px, p, plen) >= 0 || memcmp(py, p, plen) >= 0)
		return 0;

	*ret = is384 ? mul_p384(outx, outy, px, py, k, klen)
	             : mul_p521(outx, outy, px, py, k, klen);
	return 1;
}