packages feed

crypton-2.1.3: cbits/tests/width/p256_width.c

/* Exercise the public P-256 API and print everything it answers, so that the
   32-bit build and the 64-bit build can be compared byte for byte.  Every
   value crosses the boundary as big-endian bytes, which is the one form the
   two representations agree on by construction. */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "p256/p256.h"

/* The header declares crypton_p256_point_mul and crypton_p256_modinv, and
   nothing defines them.  What exists is this family, which has no header at
   all -- the Haskell side declares it through the FFI. */
void crypton_p256e_point_mul(const crypton_p256_int *n,
    const crypton_p256_int *in_x, const crypton_p256_int *in_y,
    crypton_p256_int *out_x, crypton_p256_int *out_y);
void crypton_p256e_point_add(
    const crypton_p256_int *in_x1, const crypton_p256_int *in_y1,
    const crypton_p256_int *in_x2, const crypton_p256_int *in_y2,
    crypton_p256_int *out_x, crypton_p256_int *out_y);
void crypton_p256e_point_negate(
    const crypton_p256_int *in_x, const crypton_p256_int *in_y,
    crypton_p256_int *out_x, crypton_p256_int *out_y);
void crypton_p256e_modadd(const crypton_p256_int *MOD,
    const crypton_p256_int *a, const crypton_p256_int *b, crypton_p256_int *c);
void crypton_p256e_modsub(const crypton_p256_int *MOD,
    const crypton_p256_int *a, const crypton_p256_int *b, crypton_p256_int *c);
void crypton_p256e_scalar_invert(const crypton_p256_int *a, crypton_p256_int *b);

static uint64_t s0 = 0x243f6a8885a308d3ULL, s1 = 0x13198a2e03707344ULL;
static uint64_t rnd(void) {           /* xoroshiro-ish, deterministic */
    uint64_t x = s0, y = s1;
    s0 = y;
    x ^= x << 23;
    s1 = x ^ y ^ (x >> 17) ^ (y >> 26);
    return s1 + y;
}
static void rnd_bytes(uint8_t *p, int n) {
    for (int i = 0; i < n; i++) p[i] = (uint8_t)(rnd() >> 24);
}
static void show(const char *tag, const crypton_p256_int *v) {
    uint8_t b[P256_NBYTES];
    crypton_p256_to_bin(v, b);
    printf("%s ", tag);
    for (int i = 0; i < P256_NBYTES; i++) printf("%02x", b[i]);
    printf("\n");
}
static void from_hex(const char *h, crypton_p256_int *out) {
    uint8_t b[P256_NBYTES];
    for (int i = 0; i < P256_NBYTES; i++) {
        unsigned v; sscanf(h + 2 * i, "%2x", &v); b[i] = (uint8_t)v;
    }
    crypton_p256_from_bin(b, out);
}

int main(void) {
    crypton_p256_int n, x, y, x2, y2, r, a, b, n2tmp;
    uint8_t buf[P256_NBYTES];

    show("order", &crypton_SECP256r1_n);
    show("prime", &crypton_SECP256r1_p);
    show("bparam", &crypton_SECP256r1_b);

    /* the scalars worth naming: zero, one, the order and its neighbours, and
       the all-bits-set shapes the comb recoding has to single out */
    static const char *corners[] = {
        "0000000000000000000000000000000000000000000000000000000000000000",
        "0000000000000000000000000000000000000000000000000000000000000001",
        "0000000000000000000000000000000000000000000000000000000000000002",
        "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", /* n */
        "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", /* n-1 */
        "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", /* n+1 */
        "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
        "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
        "0000000000000000000000000000000000000000000000000000000000000000",
    };
    for (unsigned i = 0; i < sizeof corners / sizeof *corners; i++) {
        from_hex(corners[i], &n);
        crypton_p256_base_point_mul(&n, &x, &y);
        printf("corner%u valid=%d\n", i, crypton_p256_is_valid_point(&x, &y));
        show("  cx", &x); show("  cy", &y);
    }

    for (int it = 0; it < 1024; it++) {
        rnd_bytes(buf, P256_NBYTES);
        crypton_p256_from_bin(buf, &n);
        crypton_p256_mod(&crypton_SECP256r1_n, &n, &n);
        show("n", &n);

        crypton_p256_base_point_mul(&n, &x, &y);
        printf("valid=%d zero=%d odd=%d even=%d\n",
               crypton_p256_is_valid_point(&x, &y),
               crypton_p256_is_zero(&x), crypton_p256_is_odd(&y),
               crypton_p256_is_even(&y));
        show("x", &x); show("y", &y);

        /* n2 * (that point), then n1*G + n2*P through the vartime pair */
        rnd_bytes(buf, P256_NBYTES);
        crypton_p256_from_bin(buf, &a);
        crypton_p256_mod(&crypton_SECP256r1_n, &a, &a);
        crypton_p256e_point_mul(&a, &x, &y, &x2, &y2);
        show("px", &x2); show("py", &y2);
        printf("pvalid=%d\n", crypton_p256_is_valid_point(&x2, &y2));

        rnd_bytes(buf, P256_NBYTES);
        crypton_p256_from_bin(buf, &b);
        crypton_p256_mod(&crypton_SECP256r1_n, &b, &b);
        crypton_p256_points_mul_vartime(&a, &b, &x, &y, &x2, &y2);
        show("vx", &x2); show("vy", &y2);

        /* the integer side: every arithmetic entry point the header offers */
        crypton_p256_modmul(&crypton_SECP256r1_n, &a, 0, &b, &r); show("mul", &r);
        crypton_p256_modmul(&crypton_SECP256r1_p, &a, 1, &b, &r); show("mulc", &r);
        crypton_p256e_scalar_invert(&a, &r); show("inv", &r);
        crypton_p256e_modadd(&crypton_SECP256r1_n, &a, &b, &r); show("madd", &r);
        crypton_p256e_modsub(&crypton_SECP256r1_n, &a, &b, &r); show("msub", &r);
        crypton_p256e_point_add(&x, &y, &x2, &y2, &r, &n2tmp); show("ax", &r); show("ay", &n2tmp);
        crypton_p256e_point_negate(&x, &y, &r, &n2tmp); show("gx", &r); show("gy", &n2tmp);
        crypton_p256_modinv_vartime(&crypton_SECP256r1_n, &a, &r); show("invv", &r);
        printf("cmp=%d add=%d sub=%d addd=%d\n",
               crypton_p256_cmp(&a, &b),
               crypton_p256_add(&a, &b, &r),
               crypton_p256_sub(&a, &b, &r),
               crypton_p256_add_d(&a, 0x9e3779b9u, &r));
        show("sum", &r);
        /* the shifts are defined as n % P256_BITSPERDIGIT, which is 32 on one
           build and 64 on the other, so keep the ask inside both */
        /* a shift of nothing has to be the number itself.  Held here rather
           than left to the random amounts below, because zero is the amount
           that used to shift a digit by its own width. */
        crypton_p256_shl(&a, 0, &r);
        printf("shl0=%d ", crypton_p256_cmp(&a, &r) == 0);
        crypton_p256_shr(&a, 0, &r);
        printf("shr0=%d\n", crypton_p256_cmp(&a, &r) == 0);
        for (int s = 0; s < 3; s++) {
            int k = (int)(rnd() % 32);
            printf("shl%d=%d\n", k, (int)(crypton_p256_shl(&a, k, &r) & 0xff));
            show("shl", &r);
            crypton_p256_shr(&a, k, &r); show("shr", &r);
        }
        for (int bit = 0; bit < 256; bit += 37)
            printf("bit%d=%d ", bit, crypton_p256_get_bit(&a, bit));
        printf("\n");
    }
    return 0;
}