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.Nat384;
8
9public class SecP384R1Point 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 SecP384R1Point(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 SecP384R1Point(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    SecP384R1Point(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 SecP384R1Point(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        SecP384R1FieldElement X1 = (SecP384R1FieldElement)this.x, Y1 = (SecP384R1FieldElement)this.y;
85        SecP384R1FieldElement X2 = (SecP384R1FieldElement)b.getXCoord(), Y2 = (SecP384R1FieldElement)b.getYCoord();
86
87        SecP384R1FieldElement Z1 = (SecP384R1FieldElement)this.zs[0];
88        SecP384R1FieldElement Z2 = (SecP384R1FieldElement)b.getZCoord(0);
89
90        int c;
91        int[] tt1 = Nat.create(24);
92        int[] tt2 = Nat.create(24);
93        int[] t3 = Nat.create(12);
94        int[] t4 = Nat.create(12);
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            SecP384R1Field.square(Z1.x, S2);
107
108            U2 = tt2;
109            SecP384R1Field.multiply(S2, X2.x, U2);
110
111            SecP384R1Field.multiply(S2, Z1.x, S2);
112            SecP384R1Field.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            SecP384R1Field.square(Z2.x, S1);
126
127            U1 = tt1;
128            SecP384R1Field.multiply(S1, X1.x, U1);
129
130            SecP384R1Field.multiply(S1, Z2.x, S1);
131            SecP384R1Field.multiply(S1, Y1.x, S1);
132        }
133
134        int[] H = Nat.create(12);
135        SecP384R1Field.subtract(U1, U2, H);
136
137        int[] R = Nat.create(12);
138        SecP384R1Field.subtract(S1, S2, R);
139
140        // Check if b == this or b == -this
141        if (Nat.isZero(12, H))
142        {
143            if (Nat.isZero(12, 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        SecP384R1Field.square(H, HSquared);
155
156        int[] G = Nat.create(12);
157        SecP384R1Field.multiply(HSquared, H, G);
158
159        int[] V = t3;
160        SecP384R1Field.multiply(HSquared, U1, V);
161
162        SecP384R1Field.negate(G, G);
163        Nat384.mul(S1, G, tt1);
164
165        c = Nat.addBothTo(12, V, V, G);
166        SecP384R1Field.reduce32(c, G);
167
168        SecP384R1FieldElement X3 = new SecP384R1FieldElement(t4);
169        SecP384R1Field.square(R, X3.x);
170        SecP384R1Field.subtract(X3.x, G, X3.x);
171
172        SecP384R1FieldElement Y3 = new SecP384R1FieldElement(G);
173        SecP384R1Field.subtract(V, X3.x, Y3.x);
174        Nat384.mul(Y3.x, R, tt2);
175        SecP384R1Field.addExt(tt1, tt2, tt1);
176        SecP384R1Field.reduce(tt1, Y3.x);
177
178        SecP384R1FieldElement Z3 = new SecP384R1FieldElement(H);
179        if (!Z1IsOne)
180        {
181            SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x);
182        }
183        if (!Z2IsOne)
184        {
185            SecP384R1Field.multiply(Z3.x, Z2.x, Z3.x);
186        }
187
188        ECFieldElement[] zs = new ECFieldElement[]{ Z3 };
189
190        return new SecP384R1Point(curve, X3, Y3, zs, this.withCompression);
191    }
192
193    public ECPoint twice()
194    {
195        if (this.isInfinity())
196        {
197            return this;
198        }
199
200        ECCurve curve = this.getCurve();
201
202        SecP384R1FieldElement Y1 = (SecP384R1FieldElement)this.y;
203        if (Y1.isZero())
204        {
205            return curve.getInfinity();
206        }
207
208        SecP384R1FieldElement X1 = (SecP384R1FieldElement)this.x, Z1 = (SecP384R1FieldElement)this.zs[0];
209
210        int c;
211        int[] t1 = Nat.create(12);
212        int[] t2 = Nat.create(12);
213
214        int[] Y1Squared = Nat.create(12);
215        SecP384R1Field.square(Y1.x, Y1Squared);
216
217        int[] T = Nat.create(12);
218        SecP384R1Field.square(Y1Squared, T);
219
220        boolean Z1IsOne = Z1.isOne();
221
222        int[] Z1Squared = Z1.x;
223        if (!Z1IsOne)
224        {
225            Z1Squared = t2;
226            SecP384R1Field.square(Z1.x, Z1Squared);
227        }
228
229        SecP384R1Field.subtract(X1.x, Z1Squared, t1);
230
231        int[] M = t2;
232        SecP384R1Field.add(X1.x, Z1Squared, M);
233        SecP384R1Field.multiply(M, t1, M);
234        c = Nat.addBothTo(12, M, M, M);
235        SecP384R1Field.reduce32(c, M);
236
237        int[] S = Y1Squared;
238        SecP384R1Field.multiply(Y1Squared, X1.x, S);
239        c = Nat.shiftUpBits(12, S, 2, 0);
240        SecP384R1Field.reduce32(c, S);
241
242        c = Nat.shiftUpBits(12, T, 3, 0, t1);
243        SecP384R1Field.reduce32(c, t1);
244
245        SecP384R1FieldElement X3 = new SecP384R1FieldElement(T);
246        SecP384R1Field.square(M, X3.x);
247        SecP384R1Field.subtract(X3.x, S, X3.x);
248        SecP384R1Field.subtract(X3.x, S, X3.x);
249
250        SecP384R1FieldElement Y3 = new SecP384R1FieldElement(S);
251        SecP384R1Field.subtract(S, X3.x, Y3.x);
252        SecP384R1Field.multiply(Y3.x, M, Y3.x);
253        SecP384R1Field.subtract(Y3.x, t1, Y3.x);
254
255        SecP384R1FieldElement Z3 = new SecP384R1FieldElement(M);
256        SecP384R1Field.twice(Y1.x, Z3.x);
257        if (!Z1IsOne)
258        {
259            SecP384R1Field.multiply(Z3.x, Z1.x, Z3.x);
260        }
261
262        return new SecP384R1Point(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
263    }
264
265    public ECPoint twicePlus(ECPoint b)
266    {
267        if (this == b)
268        {
269            return threeTimes();
270        }
271        if (this.isInfinity())
272        {
273            return b;
274        }
275        if (b.isInfinity())
276        {
277            return twice();
278        }
279
280        ECFieldElement Y1 = this.y;
281        if (Y1.isZero())
282        {
283            return b;
284        }
285
286        return twice().add(b);
287    }
288
289    public ECPoint threeTimes()
290    {
291        if (this.isInfinity() || this.y.isZero())
292        {
293            return this;
294        }
295
296        // NOTE: Be careful about recursions between twicePlus and threeTimes
297        return twice().add(this);
298    }
299
300    public ECPoint negate()
301    {
302        if (this.isInfinity())
303        {
304            return this;
305        }
306
307        return new SecP384R1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression);
308    }
309}
310