001package org.apache.commons.ssl.org.bouncycastle.asn1.ua;
002
003import java.math.BigInteger;
004import java.util.Random;
005
006import org.bouncycastle.math.ec.ECConstants;
007import org.bouncycastle.math.ec.ECCurve;
008import org.bouncycastle.math.ec.ECFieldElement;
009import org.bouncycastle.math.ec.ECPoint;
010
011/**
012 * DSTU4145 encodes points somewhat differently than X9.62
013 * It compresses the point to the size of the field element
014 */
015public abstract class DSTU4145PointEncoder
016{
017    private static ECFieldElement trace(ECFieldElement fe)
018    {
019        ECFieldElement t = fe;
020        for (int i = 1; i < fe.getFieldSize(); ++i)
021        {
022            t = t.square().add(fe);
023        }
024        return t;
025    }
026
027    /**
028     * Solves a quadratic equation <code>z<sup>2</sup> + z = beta</code>(X9.62
029     * D.1.6) The other solution is <code>z + 1</code>.
030     *
031     * @param beta The value to solve the quadratic equation for.
032     * @return the solution for <code>z<sup>2</sup> + z = beta</code> or
033     *         <code>null</code> if no solution exists.
034     */
035    private static ECFieldElement solveQuadraticEquation(ECCurve curve, ECFieldElement beta)
036    {
037        if (beta.isZero())
038        {
039            return beta;
040        }
041
042        ECFieldElement zeroElement = curve.fromBigInteger(ECConstants.ZERO);
043
044        ECFieldElement z = null;
045        ECFieldElement gamma = null;
046
047        Random rand = new Random();
048        int m = beta.getFieldSize();
049        do
050        {
051            ECFieldElement t = curve.fromBigInteger(new BigInteger(m, rand));
052            z = zeroElement;
053            ECFieldElement w = beta;
054            for (int i = 1; i <= m - 1; i++)
055            {
056                ECFieldElement w2 = w.square();
057                z = z.square().add(w2.multiply(t));
058                w = w2.add(beta);
059            }
060            if (!w.isZero())
061            {
062                return null;
063            }
064            gamma = z.square().add(z);
065        }
066        while (gamma.isZero());
067
068        return z;
069    }
070
071    public static byte[] encodePoint(ECPoint Q)
072    {
073        /*if (!Q.isCompressed())
074              Q=new ECPoint.F2m(Q.getCurve(),Q.getX(),Q.getY(),true);
075
076          byte[] bytes=Q.getEncoded();
077
078          if (bytes[0]==0x02)
079              bytes[bytes.length-1]&=0xFE;
080          else if (bytes[0]==0x02)
081              bytes[bytes.length-1]|=0x01;
082
083          return Arrays.copyOfRange(bytes, 1, bytes.length);*/
084
085        Q = Q.normalize();
086
087        ECFieldElement x = Q.getAffineXCoord();
088
089        byte[] bytes = x.getEncoded();
090
091        if (!x.isZero())
092        {
093            ECFieldElement z = Q.getAffineYCoord().divide(x);
094            if (trace(z).isOne())
095            {
096                bytes[bytes.length - 1] |= 0x01;
097            }
098            else
099            {
100                bytes[bytes.length - 1] &= 0xFE;
101            }
102        }
103
104        return bytes;
105    }
106
107    public static ECPoint decodePoint(ECCurve curve, byte[] bytes)
108    {
109        /*byte[] bp_enc=new byte[bytes.length+1];
110          if (0==(bytes[bytes.length-1]&0x1))
111              bp_enc[0]=0x02;
112          else
113              bp_enc[0]=0x03;
114          System.arraycopy(bytes, 0, bp_enc, 1, bytes.length);
115          if (!trace(curve.fromBigInteger(new BigInteger(1, bytes))).equals(curve.getA().toBigInteger()))
116              bp_enc[bp_enc.length-1]^=0x01;
117
118          return curve.decodePoint(bp_enc);*/
119
120        ECFieldElement k = curve.fromBigInteger(BigInteger.valueOf(bytes[bytes.length - 1] & 0x1));
121
122        ECFieldElement xp = curve.fromBigInteger(new BigInteger(1, bytes));
123        if (!trace(xp).equals(curve.getA()))
124        {
125            xp = xp.addOne();
126        }
127
128        ECFieldElement yp = null;
129        if (xp.isZero())
130        {
131            yp = curve.getB().sqrt();
132        }
133        else
134        {
135            ECFieldElement beta = xp.square().invert().multiply(curve.getB()).add(curve.getA()).add(xp);
136            ECFieldElement z = solveQuadraticEquation(curve, beta);
137            if (z != null)
138            {
139                if (!trace(z).equals(k))
140                {
141                    z = z.addOne();
142                }
143                yp = xp.multiply(z);
144            }
145        }
146
147        if (yp == null)
148        {
149            throw new IllegalArgumentException("Invalid point compression");
150        }
151
152        return curve.createPoint(xp.toBigInteger(), yp.toBigInteger());
153    }
154}