Skip to content

File syn_x25519.c

File List > crypto > syn_x25519.c

Go to the documentation of this file

#if __has_include("syn_config.h")
#include "syn_config.h"
#endif

#if !defined(SYN_USE_X25519) || SYN_USE_X25519

#include "../util/syn_pack.h"
#include "syn_x25519.h"

#include <string.h>

typedef int64_t gf[16];

static const gf gf0 = {0};
static const gf gf1 = {1};
static const gf _121665 = {0xDB41, 1};

/* ── Field Arithmetic (GF(2^255 - 19), 16 × 16-bit limbs) ──────────── */

static void car25519(gf o)
{
    int64_t c;
    int i;
    for (i = 0; i < 16; i++) {
        o[i] += (1LL << 16);
        c = o[i] >> 16;
        o[(i + 1) * (i < 15)] += c - 1 + 37 * (c - 1) * (i == 15);
        o[i] -= (int64_t)((uint64_t)c << 16);
    }
}

static void sel25519(gf p, gf q, int b)
{
    int64_t t, c = ~(int64_t)(b - 1);
    int i;
    for (i = 0; i < 16; i++) {
        t = c & (p[i] ^ q[i]);
        p[i] ^= t;
        q[i] ^= t;
    }
}

static void pack25519(uint8_t o[32], const gf n)
{
    int i, j;
    int64_t b;
    gf m, t;
    memcpy(t, n, sizeof(gf));
    car25519(t);
    car25519(t);
    car25519(t);
    for (j = 0; j < 2; j++) {
        m[0] = t[0] - 0xFFED;
        for (i = 1; i < 15; i++) {
            m[i] = t[i] - 0xFFFF - ((m[i - 1] >> 16) & 1);
            m[i - 1] &= 0xFFFF;
        }
        m[15] = t[15] - 0x7FFF - ((m[14] >> 16) & 1);
        b = (m[15] >> 16) & 1;
        m[14] &= 0xFFFF;
        sel25519(t, m, (int)(1 - b));
    }
    for (i = 0; i < 16; i++) {
        syn_poke_u16_le((uint16_t)t[i], o, 2 * i);
    }
}

static void unpack25519(gf o, const uint8_t n[32])
{
    int i;
    for (i = 0; i < 16; i++) {
        o[i] = (int64_t)syn_peek_u16_le(n, 2 * i);
    }
    o[15] &= 0x7FFF;
}

static void A(gf o, const gf a, const gf b)
{
    int i;
    for (i = 0; i < 16; i++)
        o[i] = a[i] + b[i];
}

static void Z(gf o, const gf a, const gf b)
{
    int i;
    for (i = 0; i < 16; i++)
        o[i] = a[i] - b[i];
}

static void M(gf o, const gf a, const gf b)
{
    int64_t t[31];
    int i, j;
    for (i = 0; i < 31; i++)
        t[i] = 0;
    for (i = 0; i < 16; i++)
        for (j = 0; j < 16; j++)
            t[i + j] += a[i] * b[j];
    for (i = 0; i < 15; i++)
        t[i] += 38 * t[i + 16];
    memcpy(o, t, 16 * sizeof(int64_t));
    car25519(o);
    car25519(o);
}

static void S(gf o, const gf a)
{
    M(o, a, a);
}

static void inv25519(gf o, const gf a)
{
    gf c;
    int i;
    memcpy(c, a, sizeof(gf));
    /* Compute a^(p-2) = a^(2^255 - 21) via repeated squaring */
    for (i = 253; i >= 0; i--) {
        S(c, c);
        if (i != 2 && i != 4)
            M(c, c, a);
    }
    memcpy(o, c, sizeof(gf));
}

/* ── X25519 scalar multiplication (Montgomery ladder) ───────────────── */

void syn_x25519(uint8_t shared_out[32], const uint8_t scalar[32], const uint8_t point[32])
{
    uint8_t z[32];
    gf x, a, b, c, d, e, f;
    int64_t r;
    int i;

    memcpy(z, scalar, 32);
    z[31] = (z[31] & 127) | 64;
    z[0] &= 248;

    unpack25519(x, point);

    memcpy(b, x, sizeof(gf));
    memcpy(a, gf1, sizeof(gf));
    memcpy(d, gf1, sizeof(gf));
    memcpy(c, gf0, sizeof(gf));

    for (i = 254; i >= 0; i--) {
        r = (z[i >> 3] >> (i & 7)) & 1;
        sel25519(a, b, (int)r);
        sel25519(c, d, (int)r);
        A(e, a, c);
        Z(a, a, c);
        A(c, b, d);
        Z(b, b, d);
        S(d, e);
        S(f, a);
        M(a, c, a);
        M(c, b, e);
        A(e, a, c);
        Z(a, a, c);
        S(b, a);
        Z(c, d, f);
        M(a, c, _121665);
        A(a, a, d);
        M(c, c, a);
        M(a, d, f);
        M(d, b, x);
        S(b, e);
        sel25519(a, b, (int)r);
        sel25519(c, d, (int)r);
    }
    inv25519(c, c);
    M(a, a, c);
    pack25519(shared_out, a);
}

void syn_x25519_pubkey(uint8_t public_out[32], const uint8_t private_key[32])
{
    static const uint8_t basepoint[32] = {9};
    syn_x25519(public_out, private_key, basepoint);
}

#endif /* SYN_USE_X25519 */