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