/** * Is the FieldElement negative in this encoding? * <p> * Return true if x is in {1,3,5,...,q-2}<br> * Return false if x is in {0,2,4,...,q-1} * <p> * Preconditions: * </p><ul> * <li>|x| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. * </ul> * * @return true if x is in {1,3,5,...,q-2}, false otherwise. */ public boolean isNegative(FieldElement x) { byte[] s = encode(x); return (s[0] & 1) != 0; }
/** * Constant-time conditional move. Well, actually it is a conditional copy. * Logic is inspired by the SUPERCOP implementation at: * https://github.com/floodyberry/supercop/blob/master/crypto_sign/ed25519/ref10/fe_cmov.c * * @param val the other field element. * @param b must be 0 or 1, otherwise results are undefined. * @return a copy of this if b == 0, or a copy of val if b == 1. * @since 0.9.36 */ @Override public FieldElement cmov(FieldElement val, int b) { Ed25519FieldElement that = (Ed25519FieldElement) val; b = -b; int[] result = new int[10]; for (int i = 0; i < 10; i++) { result[i] = this.t[i]; int x = this.t[i] ^ that.t[i]; x &= b; result[i] ^= x; } return new Ed25519FieldElement(this.f, result); }
long h0 = load_4(in, 0); long h1 = load_3(in, 4) << 6; long h2 = load_3(in, 7) << 5; long h3 = load_3(in, 10) << 3; long h4 = load_3(in, 13) << 2; long h5 = load_4(in, 16); long h6 = load_3(in, 20) << 7; long h7 = load_3(in, 23) << 5; long h8 = load_3(in, 26) << 4; long h9 = (load_3(in, 29) & 0x7FFFFF) << 2; long carry0; long carry1; h[8] = (int) h8; h[9] = (int) h9; return new Ed25519FieldElement(f, h);
@Override public boolean equals(Object obj) { if (!(obj instanceof Ed25519FieldElement)) return false; Ed25519FieldElement fe = (Ed25519FieldElement) obj; return 1==Utils.equal(toByteArray(), fe.toByteArray()); }
/** * Test method for {@link net.i2p.crypto.eddsa.math.bigint.BigIntegerScalarOps#reduce(byte[])}. */ @Test public void testReduce() { // Example from test case 1 byte[] r = Utils.hexToBytes("b6b19cd8e0426f5983fa112d89a143aa97dab8bc5deb8d5b6253c928b65272f4044098c2a990039cde5b6a4818df0bfb6e40dc5dee54248032962323e701352d"); assertThat(scalarOps.reduce(r), is(equalTo(Utils.hexToBytes("f38907308c893deaf244787db4af53682249107418afc2edc58f75ac58a07404")))); }
/** * Gets the underlying finite field with q=2^255 - 19 elements. * * @return The finite field. */ public static Field getField() { return new Field( 256, // b Utils.hexToBytes("edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"), // q new Ed25519LittleEndianEncoding()); }
/** * Test method for {@link net.i2p.crypto.eddsa.math.bigint.BigIntegerScalarOps#multiplyAndAdd(byte[], byte[], byte[])}. */ @Test public void testMultiplyAndAdd() { // Example from test case 1 byte[] h = Utils.hexToBytes("86eabc8e4c96193d290504e7c600df6cf8d8256131ec2c138a3e7e162e525404"); byte[] a = Utils.hexToBytes("307c83864f2833cb427a2ef1c00a013cfdff2768d980c0a3a520f006904de94f"); byte[] r = Utils.hexToBytes("f38907308c893deaf244787db4af53682249107418afc2edc58f75ac58a07404"); byte[] S = Utils.hexToBytes("5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"); assertThat(scalarOps.multiplyAndAdd(h, a, r), is(equalTo(S))); }
/** * h = -f * <p> * TODO-CR BR: see above. * <p> * Preconditions: * </p><ul> * <li>|f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. * </ul><p> * Postconditions: * </p><ul> * <li>|h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. * </ul> * * @return The field element (-1) * this. */ public FieldElement negate() { int[] h = new int[10]; for (int i = 0; i < 10; i++) { h[i] = - t[i]; } return new Ed25519FieldElement(f, h); }
@Override public String toString() { return "[Ed25519FieldElement val="+Utils.bytesToHex(toByteArray())+"]"; } }
@Test public void reduceReturnsExpectedResult() { for (int i=0; i<1000; i++) { // Arrange: final byte[] bytes = MathUtils.getRandomByteArray(64); // Act: final byte[] reduced1 = scalarOps.reduce(bytes); final byte[] reduced2 = MathUtils.reduceModGroupOrder(bytes); // Assert: Assert.assertThat(MathUtils.toBigInteger(reduced1).compareTo(MathUtils.getGroupOrder()), IsEqual.equalTo(-1)); Assert.assertThat(MathUtils.toBigInteger(reduced1).compareTo(new BigInteger("-1")), IsEqual.equalTo(1)); Assert.assertThat(reduced1, IsEqual.equalTo(reduced2)); } }
/** * h = f - g * <p> * Can overlap h with f or g. * <p> * TODO-CR BR: See above. * <p> * Preconditions: * </p><ul> * <li>|f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. * <li>|g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. * </ul><p> * Postconditions: * </p><ul> * <li>|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. * </ul> * * @param val The field element to subtract. * @return The field element this - val. **/ public FieldElement subtract(FieldElement val) { int[] g = ((Ed25519FieldElement)val).t; int[] h = new int[10]; for (int i = 0; i < 10; i++) { h[i] = t[i] - g[i]; } return new Ed25519FieldElement(f, h); }
/** * Gets a value indicating whether or not the field element is non-zero. * * @return 1 if it is non-zero, 0 otherwise. */ public boolean isNonZero() { final byte[] s = toByteArray(); return Utils.equal(s, ZERO) == 0; }
/** * h = f + g * <p> * TODO-CR BR: h is allocated via new, probably not a good idea. Do we need the copying into temp variables if we do that? * <p> * Preconditions: * </p><ul> * <li>|f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. * <li>|g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. * </ul><p> * Postconditions: * </p><ul> * <li>|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. * </ul> * * @param val The field element to add. * @return The field element this + val. */ public FieldElement add(FieldElement val) { int[] g = ((Ed25519FieldElement)val).t; int[] h = new int[10]; for (int i = 0; i < 10; i++) { h[i] = t[i] + g[i]; } return new Ed25519FieldElement(f, h); }
h[8] = (int) h8; h[9] = (int) h9; return new Ed25519FieldElement(f, h);
@Test (expected = IllegalArgumentException.class) public void cannotConstructFieldElementWithoutField() { // Assert: new Ed25519FieldElement(null, new int[9]); }
protected FieldElement getNonZeroFieldElement() { final int[] t = new int[10]; t[0] = 5; return new Ed25519FieldElement(MathUtils.getField(), t); }
protected FieldElement getZeroFieldElement() { return new Ed25519FieldElement(MathUtils.getField(), new int[10]); }
/** * Gets a random field element where |t[i]| <= 2^24 for 0 <= i <= 9. * * @return The field element. */ public static FieldElement getRandomFieldElement() { final int[] t = new int[10]; for (int j=0; j<10; j++) { t[j] = random.nextInt(1 << 25) - (1 << 24); } return new Ed25519FieldElement(getField(), t); }
@Test public void canConstructFieldElementFromArrayWithCorrectLength() { // Assert: new Ed25519FieldElement(MathUtils.getField(), new int[10]); }
@Test (expected = IllegalArgumentException.class) public void cannotConstructFieldElementFromArrayWithIncorrectLength() { // Assert: new Ed25519FieldElement(MathUtils.getField(), new int[9]); }