packages feed

crypton-2.2.3: cbits/crypton_mont_sqr.py

#!/usr/bin/env python3
"""Generate cbits/crypton_mont_sqr.h: Montgomery squaring for AArch64 at the
limb counts of RSA's CRT halves, fully unrolled and product-scanning.

Usage: python3 cbits/crypton_mont_sqr.py > cbits/crypton_mont_sqr.h

The result is a column at a time, with the reduction folded into the same
scan (the FIPS method), so the running sum stays in three registers instead
of going through memory a row at a time.  Each product off the diagonal is
summed once into a column of its own, doubled, and added in.
"""

SIZES = (16, 24, 32)

HEADER = """\
/* Generated by cbits/crypton_mont_sqr.py; do not edit.
 *
 * Montgomery squaring for AArch64 at 16, 24 and 32 limbs, the halves of
 * RSA-2048, -3072 and -4096.  Each answers what mont_sqr answers. */
#ifndef CRYPTON_MONT_SQR_H
#define CRYPTON_MONT_SQR_H

#include "crypton_bignum.h"

/* (t2:t1:t0) += x * y */
#define MONT_SQR_MAC(t0, t1, t2, x, y)                                  \\
	do {                                                                \\
		limb_t lo_, hi_;                                                \\
		__asm__("mul\\t%0, %5, %6\\n\\t"                                   \\
		        "umulh\\t%1, %5, %6\\n\\t"                                 \\
		        "adds\\t%2, %2, %0\\n\\t"                                  \\
		        "adcs\\t%3, %3, %1\\n\\t"                                  \\
		        "adc\\t%4, %4, xzr"                                       \\
		        : "=&r"(lo_), "=&r"(hi_), "+r"(t0), "+r"(t1), "+r"(t2)  \\
		        : "r"(x), "r"(y)                                        \\
		        : "cc");                                                \\
	} while (0)

/* (s2:s1:s0) = x * y */
#define MONT_SQR_MUL(s0, s1, s2, x, y)                                  \\
	__asm__("mul\\t%0, %3, %4\\n\\t"                                       \\
	        "umulh\\t%1, %3, %4\\n\\t"                                     \\
	        "mov\\t%2, xzr"                                               \\
	        : "=&r"(s0), "=&r"(s1), "=r"(s2)                            \\
	        : "r"(x), "r"(y))

/* (t2:t1:t0) += 2 * (s2:s1:s0) */
#define MONT_SQR_ADD2(t0, t1, t2, s0, s1, s2)                           \\
	do {                                                                \\
		s2 = (s2 << 1) | (s1 >> 63);                                    \\
		s1 = (s1 << 1) | (s0 >> 63);                                    \\
		s0 <<= 1;                                                       \\
		__asm__("adds\\t%0, %0, %3\\n\\t"                                  \\
		        "adcs\\t%1, %1, %4\\n\\t"                                  \\
		        "adc\\t%2, %2, %5"                                        \\
		        : "+r"(t0), "+r"(t1), "+r"(t2)                          \\
		        : "r"(s0), "r"(s1), "r"(s2)                             \\
		        : "cc");                                                \\
	} while (0)
"""

FOOTER = """\
#endif
"""


def gen(n):
    out = []
    w = out.append
    w(f"/* r = a * a * R^-1 mod m, for a under m; t is 2n limbs of scratch */")
    w(f"static void crypton_mont_sqr{n}(limb_t *r, const limb_t *a, const limb_t *m,")
    w(f"{' ' * len(f'static void crypton_mont_sqr{n}(')}limb_t n0, limb_t *t)")
    w("{")
    w(f"\tlimb_t *u = t, *z = t + {n};")
    w("\tlimb_t t0 = 0, t1 = 0, t2 = 0, s0, s1, s2, borrow, take;")
    w("")
    for i in range(2 * n - 1):
        w(f"\t/* column {i} */")
        pairs = [(j, i - j) for j in range(max(0, i - n + 1), n) if j < i - j]
        if pairs:
            j, k = pairs[0]
            w(f"\tMONT_SQR_MUL(s0, s1, s2, a[{j}], a[{k}]);")
            for (j, k) in pairs[1:]:
                w(f"\tMONT_SQR_MAC(s0, s1, s2, a[{j}], a[{k}]);")
            w("\tMONT_SQR_ADD2(t0, t1, t2, s0, s1, s2);")
        if i % 2 == 0:
            w(f"\tMONT_SQR_MAC(t0, t1, t2, a[{i // 2}], a[{i // 2}]);")
        for j in range(max(0, i - n + 1), min(i, n)):
            w(f"\tMONT_SQR_MAC(t0, t1, t2, u[{j}], m[{i - j}]);")
        if i < n:
            w(f"\tu[{i}] = t0 * n0;")
            w(f"\tMONT_SQR_MAC(t0, t1, t2, u[{i}], m[0]);")
        else:
            w(f"\tz[{i - n}] = t0;")
        w("\tt0 = t1;")
        w("\tt1 = t2;")
        w("\tt2 = 0;")
    w(f"\tz[{n - 1}] = t0;")
    w("")
    w("\t/* under 2m, with t1 the bit above n limbs: at most one subtraction */")
    w(f"\tborrow = sub_n(r, z, m, {n});")
    w("\ttake = t1 | (borrow ^ 1);")
    w(f"\tselect_n(r, r, z, take & 1, {n});")
    w("}")
    w("")
    return "\n".join(out)


def main():
    print(HEADER)
    for n in SIZES:
        print(gen(n))
    print(FOOTER, end="")


if __name__ == "__main__":
    main()