1package org.bouncycastle.math.ec.custom.sec;
2
3import org.bouncycastle.math.ec.ECCurve;
4import org.bouncycastle.math.ec.ECFieldElement;
5import org.bouncycastle.math.ec.ECPoint;
6import org.bouncycastle.math.raw.Nat;
7
8public class SecP521R1Point extends ECPoint.AbstractFp
9{
10    /**
11     * Create a point which encodes with point compression.
12     *
13     * @param curve
14     *            the curve to use
15     * @param x
16     *            affine x co-ordinate
17     * @param y
18     *            affine y co-ordinate
19     *
20     * @deprecated Use ECCurve.createPoint to construct points
21     */
22    public SecP521R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y)
23    {
24        this(curve, x, y, false);
25    }
26
27    /**
28     * Create a point that encodes with or without point compresion.
29     *
30     * @param curve
31     *            the curve to use
32     * @param x
33     *            affine x co-ordinate
34     * @param y
35     *            affine y co-ordinate
36     * @param withCompression
37     *            if true encode with point compression
38     *
39     * @deprecated per-point compression property will be removed, refer
40     *             {@link #getEncoded(boolean)}
41     */
42    public SecP521R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
43    {
44        super(curve, x, y);
45
46        if ((x == null) != (y == null))
47        {
48            throw new IllegalArgumentException("Exactly one of the field elements is null");
49        }
50
51        this.withCompression = withCompression;
52    }
53
54    SecP521R1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
55    {
56        super(curve, x, y, zs);
57
58        this.withCompression = withCompression;
59    }
60
61    protected ECPoint detach()
62    {
63        return new SecP521R1Point(null, getAffineXCoord(), getAffineYCoord());
64    }
65
66    public ECPoint add(ECPoint b)
67    {
68        if (this.isInfinity())
69        {
70            return b;
71        }
72        if (b.isInfinity())
73        {
74            return this;
75        }
76        if (this == b)
77        {
78            return twice();
79        }
80
81        ECCurve curve = this.getCurve();
82
83        SecP521R1FieldElement X1 = (SecP521R1FieldElement)this.x, Y1 = (SecP521R1FieldElement)this.y;
84        SecP521R1FieldElement X2 = (SecP521R1FieldElement)b.getXCoord(), Y2 = (SecP521R1FieldElement)b.getYCoord();
85
86        SecP521R1FieldElement Z1 = (SecP521R1FieldElement)this.zs[0];
87        SecP521R1FieldElement Z2 = (SecP521R1FieldElement)b.getZCoord(0);
88
89        int[] t1 = Nat.create(17);
90        int[] t2 = Nat.create(17);
91        int[] t3 = Nat.create(17);
92        int[] t4 = Nat.create(17);
93
94        boolean Z1IsOne = Z1.isOne();
95        int[] U2, S2;
96        if (Z1IsOne)
97        {
98            U2 = X2.x;
99            S2 = Y2.x;
100        }
101        else
102        {
103            S2 = t3;
104            SecP521R1Field.square(Z1.x, S2);
105
106            U2 = t2;
107            SecP521R1Field.multiply(S2, X2.x, U2);
108
109            SecP521R1Field.multiply(S2, Z1.x, S2);
110            SecP521R1Field.multiply(S2, Y2.x, S2);
111        }
112
113        boolean Z2IsOne = Z2.isOne();
114        int[] U1, S1;
115        if (Z2IsOne)
116        {
117            U1 = X1.x;
118            S1 = Y1.x;
119        }
120        else
121        {
122            S1 = t4;
123            SecP521R1Field.square(Z2.x, S1);
124
125            U1 = t1;
126            SecP521R1Field.multiply(S1, X1.x, U1);
127
128            SecP521R1Field.multiply(S1, Z2.x, S1);
129            SecP521R1Field.multiply(S1, Y1.x, S1);
130        }
131
132        int[] H = Nat.create(17);
133        SecP521R1Field.subtract(U1, U2, H);
134
135        int[] R = t2;
136        SecP521R1Field.subtract(S1, S2, R);
137
138        // Check if b == this or b == -this
139        if (Nat.isZero(17, H))
140        {
141            if (Nat.isZero(17, R))
142            {
143                // this == b, i.e. this must be doubled
144                return this.twice();
145            }
146
147            // this == -b, i.e. the result is the point at infinity
148            return curve.getInfinity();
149        }
150
151        int[] HSquared = t3;
152        SecP521R1Field.square(H, HSquared);
153
154        int[] G = Nat.create(17);
155        SecP521R1Field.multiply(HSquared, H, G);
156
157        int[] V = t3;
158        SecP521R1Field.multiply(HSquared, U1, V);
159
160        SecP521R1Field.multiply(S1, G, t1);
161
162        SecP521R1FieldElement X3 = new SecP521R1FieldElement(t4);
163        SecP521R1Field.square(R, X3.x);
164        SecP521R1Field.add(X3.x, G, X3.x);
165        SecP521R1Field.subtract(X3.x, V, X3.x);
166        SecP521R1Field.subtract(X3.x, V, X3.x);
167
168        SecP521R1FieldElement Y3 = new SecP521R1FieldElement(G);
169        SecP521R1Field.subtract(V, X3.x, Y3.x);
170        SecP521R1Field.multiply(Y3.x, R, t2);
171        SecP521R1Field.subtract(t2, t1, Y3.x);
172
173        SecP521R1FieldElement Z3 = new SecP521R1FieldElement(H);
174        if (!Z1IsOne)
175        {
176            SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x);
177        }
178        if (!Z2IsOne)
179        {
180            SecP521R1Field.multiply(Z3.x, Z2.x, Z3.x);
181        }
182
183        ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
184
185        return new SecP521R1Point(curve, X3, Y3, zs, this.withCompression);
186    }
187
188    public ECPoint twice()
189    {
190        if (this.isInfinity())
191        {
192            return this;
193        }
194
195        ECCurve curve = this.getCurve();
196
197        SecP521R1FieldElement Y1 = (SecP521R1FieldElement)this.y;
198        if (Y1.isZero())
199        {
200            return curve.getInfinity();
201        }
202
203        SecP521R1FieldElement X1 = (SecP521R1FieldElement)this.x, Z1 = (SecP521R1FieldElement)this.zs[0];
204
205        int[] t1 = Nat.create(17);
206        int[] t2 = Nat.create(17);
207
208        int[] Y1Squared = Nat.create(17);
209        SecP521R1Field.square(Y1.x, Y1Squared);
210
211        int[] T = Nat.create(17);
212        SecP521R1Field.square(Y1Squared, T);
213
214        boolean Z1IsOne = Z1.isOne();
215
216        int[] Z1Squared = Z1.x;
217        if (!Z1IsOne)
218        {
219            Z1Squared = t2;
220            SecP521R1Field.square(Z1.x, Z1Squared);
221        }
222
223        SecP521R1Field.subtract(X1.x, Z1Squared, t1);
224
225        int[] M = t2;
226        SecP521R1Field.add(X1.x, Z1Squared, M);
227        SecP521R1Field.multiply(M, t1, M);
228        Nat.addBothTo(17, M, M, M);
229        SecP521R1Field.reduce23(M);
230
231        int[] S = Y1Squared;
232        SecP521R1Field.multiply(Y1Squared, X1.x, S);
233        Nat.shiftUpBits(17, S, 2, 0);
234        SecP521R1Field.reduce23(S);
235
236        Nat.shiftUpBits(17, T, 3, 0, t1);
237        SecP521R1Field.reduce23(t1);
238
239        SecP521R1FieldElement X3 = new SecP521R1FieldElement(T);
240        SecP521R1Field.square(M, X3.x);
241        SecP521R1Field.subtract(X3.x, S, X3.x);
242        SecP521R1Field.subtract(X3.x, S, X3.x);
243
244        SecP521R1FieldElement Y3 = new SecP521R1FieldElement(S);
245        SecP521R1Field.subtract(S, X3.x, Y3.x);
246        SecP521R1Field.multiply(Y3.x, M, Y3.x);
247        SecP521R1Field.subtract(Y3.x, t1, Y3.x);
248
249        SecP521R1FieldElement Z3 = new SecP521R1FieldElement(M);
250        SecP521R1Field.twice(Y1.x, Z3.x);
251        if (!Z1IsOne)
252        {
253            SecP521R1Field.multiply(Z3.x, Z1.x, Z3.x);
254        }
255
256        return new SecP521R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
257    }
258
259    public ECPoint twicePlus(ECPoint b)
260    {
261        if (this == b)
262        {
263            return threeTimes();
264        }
265        if (this.isInfinity())
266        {
267            return b;
268        }
269        if (b.isInfinity())
270        {
271            return twice();
272        }
273
274        ECFieldElement Y1 = this.y;
275        if (Y1.isZero())
276        {
277            return b;
278        }
279
280        return twice().add(b);
281    }
282
283    public ECPoint threeTimes()
284    {
285        if (this.isInfinity() || this.y.isZero())
286        {
287            return this;
288        }
289
290        // NOTE: Be careful about recursions between twicePlus and threeTimes
291        return twice().add(this);
292    }
293
294    protected ECFieldElement two(ECFieldElement x)
295    {
296        return x.add(x);
297    }
298
299    protected ECFieldElement three(ECFieldElement x)
300    {
301        return two(x).add(x);
302    }
303
304    protected ECFieldElement four(ECFieldElement x)
305    {
306        return two(two(x));
307    }
308
309    protected ECFieldElement eight(ECFieldElement x)
310    {
311        return four(two(x));
312    }
313
314    protected ECFieldElement doubleProductFromSquares(ECFieldElement a, ECFieldElement b,
315        ECFieldElement aSquared, ECFieldElement bSquared)
316    {
317        /*
318         * NOTE: If squaring in the field is faster than multiplication, then this is a quicker
319         * way to calculate 2.A.B, if A^2 and B^2 are already known.
320         */
321        return a.add(b).square().subtract(aSquared).subtract(bSquared);
322    }
323
324    public ECPoint negate()
325    {
326        if (this.isInfinity())
327        {
328            return this;
329        }
330
331        return new SecP521R1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression);
332    }
333}
334