summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/lib/hcrypto/x25519/fe_51/fe.h
blob: de876264c87c7a3117353c0c11d74890bd263a65 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*
 Ignores top bit of h.
 */

void
fe25519_frombytes(fe25519 h, const unsigned char *s)
{
    const uint64_t mask = 0x7ffffffffffffULL;
    uint64_t h0, h1, h2, h3, h4;

    h0 = (LOAD64_LE(s     )      ) & mask;
    h1 = (LOAD64_LE(s +  6) >>  3) & mask;
    h2 = (LOAD64_LE(s + 12) >>  6) & mask;
    h3 = (LOAD64_LE(s + 19) >>  1) & mask;
    h4 = (LOAD64_LE(s + 24) >> 12) & mask;

    h[0] = h0;
    h[1] = h1;
    h[2] = h2;
    h[3] = h3;
    h[4] = h4;
}

static void
fe25519_reduce(fe25519 h, const fe25519 f)
{
    const uint64_t mask = 0x7ffffffffffffULL;
    uint128_t t[5];

    t[0] = f[0];
    t[1] = f[1];
    t[2] = f[2];
    t[3] = f[3];
    t[4] = f[4];

    t[1] += t[0] >> 51;
    t[0] &= mask;
    t[2] += t[1] >> 51;
    t[1] &= mask;
    t[3] += t[2] >> 51;
    t[2] &= mask;
    t[4] += t[3] >> 51;
    t[3] &= mask;
    t[0] += 19 * (t[4] >> 51);
    t[4] &= mask;

    t[1] += t[0] >> 51;
    t[0] &= mask;
    t[2] += t[1] >> 51;
    t[1] &= mask;
    t[3] += t[2] >> 51;
    t[2] &= mask;
    t[4] += t[3] >> 51;
    t[3] &= mask;
    t[0] += 19 * (t[4] >> 51);
    t[4] &= mask;

    /* now t is between 0 and 2^255-1, properly carried. */
    /* case 1: between 0 and 2^255-20. case 2: between 2^255-19 and 2^255-1. */

    t[0] += 19ULL;

    t[1] += t[0] >> 51;
    t[0] &= mask;
    t[2] += t[1] >> 51;
    t[1] &= mask;
    t[3] += t[2] >> 51;
    t[2] &= mask;
    t[4] += t[3] >> 51;
    t[3] &= mask;
    t[0] += 19ULL * (t[4] >> 51);
    t[4] &= mask;

    /* now between 19 and 2^255-1 in both cases, and offset by 19. */

    t[0] += 0x8000000000000 - 19ULL;
    t[1] += 0x8000000000000 - 1ULL;
    t[2] += 0x8000000000000 - 1ULL;
    t[3] += 0x8000000000000 - 1ULL;
    t[4] += 0x8000000000000 - 1ULL;

    /* now between 2^255 and 2^256-20, and offset by 2^255. */

    t[1] += t[0] >> 51;
    t[0] &= mask;
    t[2] += t[1] >> 51;
    t[1] &= mask;
    t[3] += t[2] >> 51;
    t[2] &= mask;
    t[4] += t[3] >> 51;
    t[3] &= mask;
    t[4] &= mask;

    h[0] = t[0];
    h[1] = t[1];
    h[2] = t[2];
    h[3] = t[3];
    h[4] = t[4];
}

void
fe25519_tobytes(unsigned char *s, const fe25519 h)
{
    fe25519  t;
    uint64_t t0, t1, t2, t3;

    fe25519_reduce(t, h);
    t0 = t[0] | (t[1] << 51);
    t1 = (t[1] >> 13) | (t[2] << 38);
    t2 = (t[2] >> 26) | (t[3] << 25);
    t3 = (t[3] >> 39) | (t[4] << 12);
    STORE64_LE(s +  0, t0);
    STORE64_LE(s +  8, t1);
    STORE64_LE(s + 16, t2);
    STORE64_LE(s + 24, t3);
}