FieldElement x, y, yy, u, v, v3, vxx, check; y = curve.getField().fromByteArray(s); yy = y.square(); u = yy.subtractOne(); v = yy.multiply(curve.getD()).addOne(); v3 = v.square().multiply(v); x = v3.square().multiply(v).multiply(u); x = x.pow22523(); x = v3.multiply(u).multiply(x); vxx = x.square().multiply(v); check = vxx.subtract(u); // vx^2-u if (check.isNonZero()) { check = vxx.add(u); // vx^2+u if (check.isNonZero()) throw new IllegalArgumentException("not a valid GroupElement"); x = x.multiply(curve.getI()); if ((x.isNegative() ? 1 : 0) != Utils.bit(s, curve.getField().getb()-1)) { x = x.negate(); this.Y = y;
/** * Constant-time conditional move. * <p> * Replaces this with u if b == 1.<br> * Replaces this with this if b == 0. * <p> * Method is package private only so that tests run. * * @param u The group element to return if b == 1. * @param b in {0, 1} * @return u if b == 1; this if b == 0; Results undefined if b is not in {0, 1}. */ GroupElement cmov(final GroupElement u, final int b) { return precomp(curve, X.cmov(u.X, b), Y.cmov(u.Y, b), Z.cmov(u.Z, b)); }
/** * Precomputes table for {@link #doubleScalarMultiplyVariableTime(GroupElement, byte[], byte[])}. * @since 0.9.36 split out from precompute() */ private GroupElement[] precomputeDouble() { // Precomputation for double scalar multiplication. // P,3P,5P,7P,9P,11P,13P,15P GroupElement[] dblPrecmp = new GroupElement[8]; GroupElement Bi = this; for (int i = 0; i < 8; i++) { final FieldElement recip = Bi.Z.invert(); final FieldElement x = Bi.X.multiply(recip); final FieldElement y = Bi.Y.multiply(recip); dblPrecmp[i] = precomp(this.curve, y.add(x), y.subtract(x), x.multiply(y).multiply(this.curve.get2D())); // Bi = edwards(B,edwards(B,Bi)) Bi = this.add(this.add(Bi.toCached()).toP3().toCached()).toP3(); } return dblPrecmp; }
/** * GroupElement subtraction using the twisted Edwards addition law with * extended coordinates (Hisil2008). * <p> * r = p - q * <p> * Negating q means negating the value of the coordinate X2 and T2. * The formula is in accordance to {@link #add the above addition}. * * @param q the PRECOMP representation of the GroupElement to subtract. * @return the P1P1 representation of the result. */ public GroupElement sub(GroupElement q) { if (this.repr != Representation.P3) throw new UnsupportedOperationException(); if (q.repr != Representation.CACHED) throw new IllegalArgumentException(); FieldElement YpX, YmX, A, B, C, ZZ, D; YpX = Y.add(X); YmX = Y.subtract(X); A = YpX.multiply(q.Y); // q->Y-X B = YmX.multiply(q.X); // q->Y+X C = q.T.multiply(T); // q->2dT ZZ = Z.multiply(q.Z); D = ZZ.add(ZZ); return p1p1(curve, A.subtract(B), A.add(B), D.subtract(C), D.add(C)); }
/** * Converts the group element to an encoded point on the curve. * * @return The encoded point as byte array. */ public byte[] toByteArray() { switch (this.repr) { case P2: case P3: FieldElement recip = Z.invert(); FieldElement x = X.multiply(recip); FieldElement y = Y.multiply(recip); byte[] s = y.toByteArray(); s[s.length-1] |= (x.isNegative() ? (byte) 0x80 : 0); return s; default: return toP2().toByteArray(); } }
/** * Verify that a point is on the curve. * @param curve The curve to check. * @return true if the point lies on the curve. */ public boolean isOnCurve(Curve curve) { switch (repr) { case P2: case P3: FieldElement recip = Z.invert(); FieldElement x = X.multiply(recip); FieldElement y = Y.multiply(recip); FieldElement xx = x.square(); FieldElement yy = y.square(); FieldElement dxxyy = curve.getD().multiply(xx).multiply(yy); return curve.getField().ONE.add(dxxyy).add(xx).equals(yy); default: return toP2().isOnCurve(curve); } }
case P3: // Ignore T for P3 representation FieldElement XX, YY, B, A, AA, Yn, Zn; XX = this.X.square(); YY = this.Y.square(); B = this.Z.squareAndDouble(); A = this.X.add(this.Y); AA = A.square(); Yn = YY.add(XX); Zn = YY.subtract(XX); return p1p1(this.curve, AA.subtract(Yn), Yn, Zn, B.subtract(Zn)); default: throw new UnsupportedOperationException();
return this.X.equals(ge.X) && this.Y.equals(ge.Y); final FieldElement x1 = this.X.multiply(ge.Z); final FieldElement y1 = this.Y.multiply(ge.Z); final FieldElement x2 = ge.X.multiply(this.Z); final FieldElement y2 = ge.Y.multiply(this.Z); return x1.equals(x2) && y1.equals(y2); case P1P1: return this.X.equals(ge.X) && this.Y.equals(ge.Y) && this.T.equals(ge.T); final FieldElement x3 = this.X.multiply(ge.Z); final FieldElement y3 = this.Y.multiply(ge.Z); final FieldElement t3 = this.T.multiply(ge.Z); final FieldElement x4 = ge.X.multiply(this.Z); final FieldElement y4 = ge.Y.multiply(this.Z); final FieldElement t4 = ge.T.multiply(this.Z); return x3.equals(x4) && y3.equals(y4) && t3.equals(t4); default:
public FieldElement addOne() { return add(f.ONE); }
public Field(int b, byte[] q, Encoding enc) { this.b = b; this.enc = enc; this.enc.setField(this); this.q = fromByteArray(q); // Set up constants ZERO = fromByteArray(Constants.ZERO); ONE = fromByteArray(Constants.ONE); TWO = fromByteArray(Constants.TWO); FOUR = fromByteArray(Constants.FOUR); FIVE = fromByteArray(Constants.FIVE); EIGHT = fromByteArray(Constants.EIGHT); // Precompute values qm2 = this.q.subtract(TWO); qm5d8 = this.q.subtract(FIVE).divide(EIGHT); }
/** * Negates a group element. * * @param g The group element. * @return The negated group element. */ public static GroupElement negateGroupElement(final GroupElement g) { if (g.getRepresentation() != GroupElement.Representation.P3) { throw new IllegalArgumentException("g must have representation P3"); } return GroupElement.p3(g.getCurve(), g.getX().negate(), g.getY(), g.getZ(), g.getT().negate()); }
public FieldElement subtractOne() { return subtract(f.ONE); }
@Test public void isNonZeroReturnsTrueIfFieldElementIsNonZero() { // Act: final FieldElement f = getNonZeroFieldElement(); // Assert: Assert.assertThat(f.isNonZero(), IsEqual.equalTo(true)); }
@Test public void squareAndDoubleReturnsCorrectResult() { for (int i=0; i<1000; i++) { // Arrange: final FieldElement f1 = getRandomFieldElement(); final BigInteger b1 = toBigInteger(f1); // Act: final FieldElement f2 = f1.squareAndDouble(); final BigInteger b2 = toBigInteger(f2).mod(getQ()); // Assert: Assert.assertThat(b2, IsEqual.equalTo(b1.multiply(b1).multiply(new BigInteger("2")).mod(getQ()))); } }
@Test public void squareReturnsCorrectResult() { for (int i=0; i<1000; i++) { // Arrange: final FieldElement f1 = getRandomFieldElement(); final BigInteger b1 = toBigInteger(f1); // Act: final FieldElement f2 = f1.square(); final BigInteger b2 = toBigInteger(f2).mod(getQ()); // Assert: Assert.assertThat(b2, IsEqual.equalTo(b1.multiply(b1).mod(getQ()))); } }
@Test public void pow22523ReturnsCorrectResult() { for (int i=0; i<1000; i++) { // Arrange: final FieldElement f1 = getRandomFieldElement(); final BigInteger b1 = toBigInteger(f1); // Act: final FieldElement f2 = f1.pow22523(); final BigInteger b2 = toBigInteger(f2).mod(getQ()); // Assert: Assert.assertThat(b2, IsEqual.equalTo(b1.modPow(BigInteger.ONE.shiftLeft(252).subtract(new BigInteger("3")), getQ()))); } }
@Test public void invertReturnsCorrectResult() { for (int i=0; i<1000; i++) { // Arrange: final FieldElement f1 = getRandomFieldElement(); final BigInteger b1 = toBigInteger(f1); // Act: final FieldElement f2 = f1.invert(); final BigInteger b2 = toBigInteger(f2).mod(getQ()); // Assert: Assert.assertThat(b2, IsEqual.equalTo(b1.modInverse(getQ()))); } }
/** * GroupElement subtraction using the twisted Edwards addition law with * extended coordinates (Hisil2008). * <p> * this must be in P^3 representation and q in PRECOMP representation. * r = p - q where p = this = (X1 : Y1 : Z1 : T1), q = (q.X, q.Y, q.Z) = (Y2/Z2 + X2/Z2, Y2/Z2 - X2/Z2, 2 * d * X2/Z2 * Y2/Z2) * <p> * Negating q means negating the value of X2 and T2 (the latter is irrelevant here). * The formula is in accordance to {@link #madd the above addition}. * * @param q the PRECOMP representation of the GroupElement to subtract. * @return the P1P1 representation of the result. */ private GroupElement msub(GroupElement q) { if (this.repr != Representation.P3) throw new UnsupportedOperationException(); if (q.repr != Representation.PRECOMP) throw new IllegalArgumentException(); FieldElement YpX, YmX, A, B, C, D; YpX = this.Y.add(this.X); YmX = this.Y.subtract(this.X); A = YpX.multiply(q.Y); // q->y-x B = YmX.multiply(q.X); // q->y+x C = q.Z.multiply(this.T); // q->2dxy D = this.Z.add(this.Z); return p1p1(this.curve, A.subtract(B), A.add(B), D.subtract(C), D.add(C)); }