• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

famoser / elliptic / 16575888417

28 Jul 2025 05:28PM UTC coverage: 97.719% (+2.1%) from 95.58%
16575888417

Pull #15

github

web-flow
Merge 6503eb405 into d917b1701
Pull Request #15: Improve bernstein math

250 of 252 new or added lines in 31 files covered. (99.21%)

6 existing lines in 1 file now uncovered.

1371 of 1403 relevant lines covered (97.72%)

40.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.46
/src/Curves/BernsteinCurveFactory.php
1
<?php
2

3
namespace Famoser\Elliptic\Curves;
4

5
use Famoser\Elliptic\Math\MathInterface;
6
use Famoser\Elliptic\Math\Primitives\PrimeField;
7
use Famoser\Elliptic\Primitives\BirationalMap;
8
use Famoser\Elliptic\Primitives\Curve;
9
use Famoser\Elliptic\Primitives\CurveType;
10
use Famoser\Elliptic\Primitives\Point;
11

12
/**
13
 * Montgomery curves from https://datatracker.ietf.org/doc/html/rfc7748.
14
 * Popularized for fast elliptic curve cryptography in https://cr.yp.to/ecdh/curve25519-20060209.pdf
15
 *
16
 * Defined using evaluated values for easy and fast consumption.
17
 * See unit tests to check that these variables indeed correspond to their definition (e.g., that p = 2^255 - 19).
18
 */
19
class BernsteinCurveFactory
20
{
21
    public static function curve25519(): Curve
21✔
22
    {
23
        /**
24
         * sage:
25
         * F = GF(2^255 - 19)
26
         * e = EllipticCurve(F, [0, 486662, 0, 1, 0])
27
         * u = e(9, 43114425171068552920764898935933967039370386198203806730763910166200978582548)
28
         */
29

30
        // p = 2^255 - 19
31
        $p = gmp_init('7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFED', 16);
21✔
32
        $a = gmp_init(486662);
21✔
33
        $b = gmp_init(1);
21✔
34

35
        $u = gmp_init(9);
21✔
36
        // see https://www.rfc-editor.org/errata/eid4730; the v in the RFC is incorrect
37
        $v = gmp_init('431144251710685529207648989359339670393703861982038067307639101
21✔
38
66200978582548', 10);
21✔
39
        $P = new Point($u, $v);
21✔
40

41
        // order = 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed
42
        $order = gmp_init('10000000 00000000 00000000 00000000 14DEF9DE A2F79CD6 5812631A 5CF5D3ED', 16);
21✔
43
        $cofactor = gmp_init(8);
21✔
44

45
        return new Curve(CurveType::Montgomery, $p, $a, $b, $P, $order, $cofactor);
21✔
46
    }
47

48
    public static function curve25519ToEdwards25519(): BirationalMap
20✔
49
    {
50
        $p = gmp_init('7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFED', 16);
12✔
51
        $field = new PrimeField($p);
12✔
52

53
        // sqrt(-486664)
54
        // - calculated using sage: sqrt(GF(2^255-19)(-486664), all=True)
55
        // - then chosen the second value as it correctly converts the base points
56
        $squareRootOfMinus486664 = gmp_init('F26EDF46 0A006BBD 27B08DC0 3FC4F7EC 5A1D3D14 B7D1A82C C6E04AAF F457E06', 16);
12✔
57

58
        // (x, y) = (sqrt(-486664)*u/v, (u-1)/(u+1))
59
        $map = static function (MathInterface $math, Point $point) use ($field, $squareRootOfMinus486664) {
20✔
60
            $x = $field->mul(
10✔
61
                $field->mul($squareRootOfMinus486664, $point->x),
10✔
62
                /** @phpstan-ignore-next-line */
63
                $field->invert($point->y)
10✔
64
            );
10✔
65

66
            // if x is 0, short-cut to resolve y = 1 (following the normal flow would choose the other y^2 root)
67
            if (gmp_cmp($x, 0) === 0) {
10✔
68
                return $math->getInfinity();
1✔
69
            }
70

71
            $y = $field->mul(
10✔
72
                $field->sub($point->x, gmp_init(1)),
10✔
73
                /** @phpstan-ignore-next-line */
74
                $field->invert(
10✔
75
                    $field->add($point->x, gmp_init(1))
10✔
76
                )
10✔
77
            );
10✔
78

79
            return new Point($x, $y);
10✔
80
        };
20✔
81

82
        // (u, v) = ((1+y)/(1-y), sqrt(-486664)*u/x)
83
        $reverse = static function (MathInterface $math, Point $point) use ($field, $squareRootOfMinus486664) {
20✔
84
            $divisor = $field->sub(gmp_init(1), $point->y);
10✔
85
            if (gmp_cmp($divisor, 0) === 0) {
10✔
86
                return $math->getInfinity();
1✔
87
            }
88
            if (gmp_cmp($point->x, 0) === 0) {
10✔
NEW
89
                return $math->getInfinity();
×
90
            }
91

92
            $u = $field->mul(
10✔
93
                $field->add(gmp_init(1), $point->y),
10✔
94
                /** @phpstan-ignore-next-line */
95
                $field->invert($divisor)
10✔
96
            );
10✔
97

98
            $v = $field->mul(
10✔
99
                $field->mul($squareRootOfMinus486664, $u),
10✔
100
                /** @phpstan-ignore-next-line */
101
                $field->invert($point->x)
10✔
102
            );
10✔
103

104
            return new Point($u, $v);
10✔
105
        };
20✔
106

107
        return new BirationalMap($map, $reverse);
12✔
108
    }
109

110
    public static function edwards25519(): Curve
4✔
111
    {
112
        // p = 2^255 - 19
113
        $p = gmp_init('7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFED', 16);
4✔
114
        // a = -1 mod p
115
        $a = gmp_init('7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFEC', 16);
4✔
116
        $d = gmp_init('370957059346694393431380835087545651895421138798432190163887855330
4✔
117
      85940283555', 10);
4✔
118

119
        $x = gmp_init('151122213495354007725011514095885315114540126930418572060461132
4✔
120
      83949847762202');
4✔
121
        $y = gmp_init('463168356949264781694283940034751631413079938662562256157830336
4✔
122
      03165251855960', 10);
4✔
123
        $P = new Point($x, $y);
4✔
124

125
        // order = 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed
126
        $order = gmp_init('10000000 00000000 00000000 00000000 14DEF9DE A2F79CD6 5812631A 5CF5D3ED', 16);
4✔
127
        $cofactor = gmp_init(8);
4✔
128

129
        return new Curve(CurveType::TwistedEdwards, $p, $a, $d, $P, $order, $cofactor);
4✔
130
    }
131

132
    public static function curve448(): Curve
6✔
133
    {
134
        /**
135
         * sage:
136
         * F = GF(2^448 - 2^224 - 1)
137
         * e = EllipticCurve(F, [0, 156326, 0, 1, 0])
138
         * u = e(5, 355293926785568175264127502063783334808976399387714271831880898435169088786967410002932673765864550910142774147268105838985595290606362)
139
         */
140

141
        // p = 2^448 - 2^224 - 1
142
        $p = gmp_init('FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF', 16);
6✔
143
        $a = gmp_init(156326);
6✔
144
        $b = gmp_init(1);
6✔
145

146
        $u = gmp_init(5);
6✔
147
        $v = gmp_init('355293926785568175264127502063783334808976399387714271831880898
6✔
148
      435169088786967410002932673765864550910142774147268105838985595290
149
      606362', 10);
6✔
150
        $P = new Point($u, $v);
6✔
151

152
        // order = 2^446 - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d
153
        $order = gmp_init('3FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 7CCA23E9 C44EDB49 AED63690 216CC272 8DC58F55 2378C292 AB5844F3', 16);
6✔
154
        $cofactor = gmp_init(4);
6✔
155

156
        return new Curve(CurveType::Montgomery, $p, $a, $b, $P, $order, $cofactor);
6✔
157
    }
158

159
    public static function curve448ToEdwards(): BirationalMap
9✔
160
    {
161
        $p = gmp_init('FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF', 16);
1✔
162
        $field = new PrimeField($p);
1✔
163

164
        // sqrt(156324)
165
        // - calculated using sage: sqrt(GF(2^448-2^224-1)(156324), all=True)
166
        // - then chosen the first value as it correctly converts the base points
167
        $squareRootOf15634 = gmp_init('45B2C5F7 D649EED0 77ED1AE4 5F44D541 43E34F71 4B71AA96 C945AF01 2D182975 0734CDE9 FADDBDA4 C066F7ED 54419CA5 2C85DE1E 8AAE4E6C', 16);
1✔
168

169
        // (x, y) = (sqrt(156324)*u/v, (1+u)/(1-u))
170
        $map = static function (MathInterface $math, Point $point) use ($field, $squareRootOf15634) {
9✔
171
            $x = $field->mul(
9✔
172
                $field->mul($squareRootOf15634, $point->x),
9✔
173
                /** @phpstan-ignore-next-line */
174
                $field->invert($point->y)
9✔
175
            );
9✔
176
            // note: if x === 0, then y === 1 (as opposed to the other map from curve25519 to edwards25519
177
            $y = $field->mul(
9✔
178
                $field->add(gmp_init(1), $point->x),
9✔
179
                /** @phpstan-ignore-next-line */
180
                $field->invert(
9✔
181
                    $field->sub(gmp_init(1), $point->x)
9✔
182
                )
9✔
183
            );
9✔
184

185
            return new Point($x, $y);
9✔
186
        };
9✔
187

188
        // (u, v) = ((y-1)/(y+1), sqrt(156324)*u/x)
189
        $reverse = static function (MathInterface $math, Point $point) use ($field, $squareRootOf15634) {
9✔
190
            $divisor = $field->add($point->y, gmp_init(1));
9✔
191
            if (gmp_cmp($divisor, 0) === 0) {
9✔
NEW
192
                return $math->getInfinity();
×
193
            }
194
            if (gmp_cmp($point->x, 0) === 0) {
9✔
195
                return $math->getInfinity();
2✔
196
            }
197

198
            $u = $field->mul(
9✔
199
                $field->sub($point->y, gmp_init(1)),
9✔
200
                /** @phpstan-ignore-next-line */
201
                $field->invert($divisor)
9✔
202
            );
9✔
203

204
            $v = $field->mul(
9✔
205
                $field->mul($squareRootOf15634, $u),
9✔
206
                /** @phpstan-ignore-next-line */
207
                $field->invert($point->x)
9✔
208
            );
9✔
209

210
            return new Point($u, $v);
9✔
211
        };
9✔
212

213
        return new BirationalMap($map, $reverse);
1✔
214
    }
215

216
    public static function curve448Edwards(): Curve
2✔
217
    {
218
        // p = 2^448 - 2^224 - 1
219
        $p = gmp_init('FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF', 16);
2✔
220
        // a = 1 mod p
221
        $a = gmp_init('1', 10);
2✔
222
        $d = gmp_init('611975850744529176160423220965553317543219696871016626328968936415
2✔
223
      087860042636474891785599283666020414768678979989378147065462815545
224
      017', 10);
2✔
225

226
        $x = gmp_init('345397493039729516374008604150537410266655260075183290216406970
2✔
227
      281645695073672344430481787759340633221708391583424041788924124567
228
      700732', 10);
2✔
229
        $y = gmp_init('363419362147803445274661903944002267176820680343659030140745099
2✔
230
      590306164083365386343198191849338272965044442230921818680526749009
231
      182718', 10);
2✔
232
        $P = new Point($x, $y);
2✔
233

234
        // order = 2^446 - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d
235
        $order = gmp_init('3FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 7CCA23E9 C44EDB49 AED63690 216CC272 8DC58F55 2378C292 AB5844F3', 16);
2✔
236
        $cofactor = gmp_init(4);
2✔
237

238
        return new Curve(CurveType::Edwards, $p, $a, $d, $P, $order, $cofactor);
2✔
239
    }
240

241
    public static function edwards448(): Curve
1✔
242
    {
243
        // p = 2^448 - 2^224 - 1
244
        $p = gmp_init('FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF', 16);
1✔
245
        // a = 1 mod p
246
        $a = gmp_init('1', 10);
1✔
247
        $d = gmp_init('FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFF6756 ', 16);
1✔
248

249
        $x = gmp_init('224580040295924300187604334099896036246789641632564134246125461
1✔
250
      686950415467406032909029192869357953282578032075146446173674602635
251
      247710', 10);
1✔
252
        $y = gmp_init('298819210078481492676017930443930673437544040154080242095928241
1✔
253
      372331506189835876003536878655418784733982303233503462500531545062
254
      832660', 10);
1✔
255
        $P = new Point($x, $y);
1✔
256

257
        // order = 2^446 - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d
258
        $order = gmp_init('3FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 7CCA23E9 C44EDB49 AED63690 216CC272 8DC58F55 2378C292 AB5844F3', 16);
1✔
259
        $cofactor = gmp_init(4);
1✔
260

261
        return new Curve(CurveType::Edwards, $p, $a, $d, $P, $order, $cofactor);
1✔
262
    }
263
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc