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 SecP224K1Point 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 SecP224K1Point(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 SecP224K1Point(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    SecP224K1Point(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs,
56        boolean withCompression)
57    {
58        super(curve, x, y, zs);
59
60        this.withCompression = withCompression;
61    }
62
63    protected ECPoint detach()
64    {
65        return new SecP224K1Point(null, getAffineXCoord(), getAffineYCoord());
66    }
67
68    // B.3 pg 62
69    public ECPoint add(ECPoint b)
70    {
71        if (this.isInfinity())
72        {
73            return b;
74        }
75        if (b.isInfinity())
76        {
77            return this;
78        }
79        if (this == b)
80        {
81            return twice();
82        }
83
84        ECCurve curve = this.getCurve();
85
86        SecP224K1FieldElement X1 = (SecP224K1FieldElement)this.x, Y1 = (SecP224K1FieldElement)this.y;
87        SecP224K1FieldElement X2 = (SecP224K1FieldElement)b.getXCoord(), Y2 = (SecP224K1FieldElement)b.getYCoord();
88
89        SecP224K1FieldElement Z1 = (SecP224K1FieldElement)this.zs[0];
90        SecP224K1FieldElement Z2 = (SecP224K1FieldElement)b.getZCoord(0);
91
92        int c;
93        int[] tt1 = Nat224.createExt();
94        int[] t2 = Nat224.create();
95        int[] t3 = Nat224.create();
96        int[] t4 = Nat224.create();
97
98        boolean Z1IsOne = Z1.isOne();
99        int[] U2, S2;
100        if (Z1IsOne)
101        {
102            U2 = X2.x;
103            S2 = Y2.x;
104        }
105        else
106        {
107            S2 = t3;
108            SecP224K1Field.square(Z1.x, S2);
109
110            U2 = t2;
111            SecP224K1Field.multiply(S2, X2.x, U2);
112
113            SecP224K1Field.multiply(S2, Z1.x, S2);
114            SecP224K1Field.multiply(S2, Y2.x, S2);
115        }
116
117        boolean Z2IsOne = Z2.isOne();
118        int[] U1, S1;
119        if (Z2IsOne)
120        {
121            U1 = X1.x;
122            S1 = Y1.x;
123        }
124        else
125        {
126            S1 = t4;
127            SecP224K1Field.square(Z2.x, S1);
128
129            U1 = tt1;
130            SecP224K1Field.multiply(S1, X1.x, U1);
131
132            SecP224K1Field.multiply(S1, Z2.x, S1);
133            SecP224K1Field.multiply(S1, Y1.x, S1);
134        }
135
136        int[] H = Nat224.create();
137        SecP224K1Field.subtract(U1, U2, H);
138
139        int[] R = t2;
140        SecP224K1Field.subtract(S1, S2, R);
141
142        // Check if b == this or b == -this
143        if (Nat224.isZero(H))
144        {
145            if (Nat224.isZero(R))
146            {
147                // this == b, i.e. this must be doubled
148                return this.twice();
149            }
150
151            // this == -b, i.e. the result is the point at infinity
152            return curve.getInfinity();
153        }
154
155        int[] HSquared = t3;
156        SecP224K1Field.square(H, HSquared);
157
158        int[] G = Nat224.create();
159        SecP224K1Field.multiply(HSquared, H, G);
160
161        int[] V = t3;
162        SecP224K1Field.multiply(HSquared, U1, V);
163
164        SecP224K1Field.negate(G, G);
165        Nat224.mul(S1, G, tt1);
166
167        c = Nat224.addBothTo(V, V, G);
168        SecP224K1Field.reduce32(c, G);
169
170        SecP224K1FieldElement X3 = new SecP224K1FieldElement(t4);
171        SecP224K1Field.square(R, X3.x);
172        SecP224K1Field.subtract(X3.x, G, X3.x);
173
174        SecP224K1FieldElement Y3 = new SecP224K1FieldElement(G);
175        SecP224K1Field.subtract(V, X3.x, Y3.x);
176        SecP224K1Field.multiplyAddToExt(Y3.x, R, tt1);
177        SecP224K1Field.reduce(tt1, Y3.x);
178
179        SecP224K1FieldElement Z3 = new SecP224K1FieldElement(H);
180        if (!Z1IsOne)
181        {
182            SecP224K1Field.multiply(Z3.x, Z1.x, Z3.x);
183        }
184        if (!Z2IsOne)
185        {
186            SecP224K1Field.multiply(Z3.x, Z2.x, Z3.x);
187        }
188
189        ECFieldElement[] zs = new ECFieldElement[] { Z3 };
190
191        return new SecP224K1Point(curve, X3, Y3, zs, this.withCompression);
192    }
193
194    // B.3 pg 62
195    public ECPoint twice()
196    {
197        if (this.isInfinity())
198        {
199            return this;
200        }
201
202        ECCurve curve = this.getCurve();
203
204        SecP224K1FieldElement Y1 = (SecP224K1FieldElement)this.y;
205        if (Y1.isZero())
206        {
207            return curve.getInfinity();
208        }
209
210        SecP224K1FieldElement X1 = (SecP224K1FieldElement)this.x, Z1 = (SecP224K1FieldElement)this.zs[0];
211
212        int c;
213
214        int[] Y1Squared = Nat224.create();
215        SecP224K1Field.square(Y1.x, Y1Squared);
216
217        int[] T = Nat224.create();
218        SecP224K1Field.square(Y1Squared, T);
219
220        int[] M = Nat224.create();
221        SecP224K1Field.square(X1.x, M);
222        c = Nat224.addBothTo(M, M, M);
223        SecP224K1Field.reduce32(c, M);
224
225        int[] S = Y1Squared;
226        SecP224K1Field.multiply(Y1Squared, X1.x, S);
227        c = Nat.shiftUpBits(7, S, 2, 0);
228        SecP224K1Field.reduce32(c, S);
229
230        int[] t1 = Nat224.create();
231        c = Nat.shiftUpBits(7, T, 3, 0, t1);
232        SecP224K1Field.reduce32(c, t1);
233
234        SecP224K1FieldElement X3 = new SecP224K1FieldElement(T);
235        SecP224K1Field.square(M, X3.x);
236        SecP224K1Field.subtract(X3.x, S, X3.x);
237        SecP224K1Field.subtract(X3.x, S, X3.x);
238
239        SecP224K1FieldElement Y3 = new SecP224K1FieldElement(S);
240        SecP224K1Field.subtract(S, X3.x, Y3.x);
241        SecP224K1Field.multiply(Y3.x, M, Y3.x);
242        SecP224K1Field.subtract(Y3.x, t1, Y3.x);
243
244        SecP224K1FieldElement Z3 = new SecP224K1FieldElement(M);
245        SecP224K1Field.twice(Y1.x, Z3.x);
246        if (!Z1.isOne())
247        {
248            SecP224K1Field.multiply(Z3.x, Z1.x, Z3.x);
249        }
250
251        return new SecP224K1Point(curve, X3, Y3, new ECFieldElement[] { Z3 }, this.withCompression);
252    }
253
254    public ECPoint twicePlus(ECPoint b)
255    {
256        if (this == b)
257        {
258            return threeTimes();
259        }
260        if (this.isInfinity())
261        {
262            return b;
263        }
264        if (b.isInfinity())
265        {
266            return twice();
267        }
268
269        ECFieldElement Y1 = this.y;
270        if (Y1.isZero())
271        {
272            return b;
273        }
274
275        return twice().add(b);
276    }
277
278    public ECPoint threeTimes()
279    {
280        if (this.isInfinity() || this.y.isZero())
281        {
282            return this;
283        }
284
285        // NOTE: Be careful about recursions between twicePlus and threeTimes
286        return twice().add(this);
287    }
288
289    public ECPoint negate()
290    {
291        if (this.isInfinity())
292        {
293            return this;
294        }
295
296        return new SecP224K1Point(curve, this.x, this.y.negate(), this.zs, this.withCompression);
297    }
298}
299