|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.util.math.intpoly; |
|
|
|
import java.lang.invoke.MethodHandles; |
|
import java.math.BigInteger; |
|
import java.nio.*; |
|
|
|
/** |
|
* An IntegerFieldModuloP designed for use with the Poly1305 authenticator. |
|
* The representation uses 5 signed long values. |
|
*/ |
|
|
|
public class IntegerPolynomial1305 extends IntegerPolynomial { |
|
|
|
protected static final int SUBTRAHEND = 5; |
|
protected static final int NUM_LIMBS = 5; |
|
private static final int POWER = 130; |
|
private static final int BITS_PER_LIMB = 26; |
|
private static final BigInteger MODULUS |
|
= TWO.pow(POWER).subtract(BigInteger.valueOf(SUBTRAHEND)); |
|
|
|
public IntegerPolynomial1305() { |
|
super(BITS_PER_LIMB, NUM_LIMBS, 1, MODULUS); |
|
} |
|
|
|
protected void mult(long[] a, long[] b, long[] r) { |
|
|
|
// Use grade-school multiplication into primitives to avoid the |
|
// temporary array allocation. This is equivalent to the following |
|
// code: |
|
// long[] c = new long[2 * NUM_LIMBS - 1]; |
|
// for(int i = 0; i < NUM_LIMBS; i++) { |
|
// for(int j - 0; j < NUM_LIMBS; j++) { |
|
// c[i + j] += a[i] * b[j] |
|
// } |
|
// } |
|
|
|
long c0 = (a[0] * b[0]); |
|
long c1 = (a[0] * b[1]) + (a[1] * b[0]); |
|
long c2 = (a[0] * b[2]) + (a[1] * b[1]) + (a[2] * b[0]); |
|
long c3 = (a[0] * b[3]) + (a[1] * b[2]) + (a[2] * b[1]) + (a[3] * b[0]); |
|
long c4 = (a[0] * b[4]) + (a[1] * b[3]) + (a[2] * b[2]) + (a[3] * b[1]) + (a[4] * b[0]); |
|
long c5 = (a[1] * b[4]) + (a[2] * b[3]) + (a[3] * b[2]) + (a[4] * b[1]); |
|
long c6 = (a[2] * b[4]) + (a[3] * b[3]) + (a[4] * b[2]); |
|
long c7 = (a[3] * b[4]) + (a[4] * b[3]); |
|
long c8 = (a[4] * b[4]); |
|
|
|
carryReduce(r, c0, c1, c2, c3, c4, c5, c6, c7, c8); |
|
} |
|
|
|
private void carryReduce(long[] r, long c0, long c1, long c2, long c3, |
|
long c4, long c5, long c6, long c7, long c8) { |
|
|
|
r[2] = c2 + (c7 * SUBTRAHEND); |
|
c3 += (c8 * SUBTRAHEND); |
|
|
|
|
|
long carry3 = carryValue(c3); |
|
r[3] = c3 - (carry3 << BITS_PER_LIMB); |
|
c4 += carry3; |
|
|
|
long carry4 = carryValue(c4); |
|
r[4] = c4 - (carry4 << BITS_PER_LIMB); |
|
c5 += carry4; |
|
|
|
|
|
r[0] = c0 + (c5 * SUBTRAHEND); |
|
r[1] = c1 + (c6 * SUBTRAHEND); |
|
|
|
|
|
carry(r); |
|
} |
|
|
|
@Override |
|
protected void square(long[] a, long[] r) { |
|
// Use grade-school multiplication with a simple squaring optimization. |
|
// Multiply into primitives to avoid the temporary array allocation. |
|
// This is equivalent to the following code: |
|
// long[] c = new long[2 * NUM_LIMBS - 1]; |
|
// for(int i = 0; i < NUM_LIMBS; i++) { |
|
// c[2 * i] = a[i] * a[i]; |
|
// for(int j = i + 1; j < NUM_LIMBS; j++) { |
|
// c[i + j] += 2 * a[i] * a[j] |
|
// } |
|
// } |
|
|
|
long c0 = (a[0] * a[0]); |
|
long c1 = 2 * (a[0] * a[1]); |
|
long c2 = 2 * (a[0] * a[2]) + (a[1] * a[1]); |
|
long c3 = 2 * (a[0] * a[3] + a[1] * a[2]); |
|
long c4 = 2 * (a[0] * a[4] + a[1] * a[3]) + (a[2] * a[2]); |
|
long c5 = 2 * (a[1] * a[4] + a[2] * a[3]); |
|
long c6 = 2 * (a[2] * a[4]) + (a[3] * a[3]); |
|
long c7 = 2 * (a[3] * a[4]); |
|
long c8 = (a[4] * a[4]); |
|
|
|
carryReduce(r, c0, c1, c2, c3, c4, c5, c6, c7, c8); |
|
} |
|
|
|
@Override |
|
protected void encode(ByteBuffer buf, int length, byte highByte, |
|
long[] result) { |
|
if (length == 16) { |
|
long low = buf.getLong(); |
|
long high = buf.getLong(); |
|
encode(high, low, highByte, result); |
|
} else { |
|
super.encode(buf, length, highByte, result); |
|
} |
|
} |
|
|
|
protected void encode(long high, long low, byte highByte, long[] result) { |
|
result[0] = low & 0x3FFFFFFL; |
|
result[1] = (low >>> 26) & 0x3FFFFFFL; |
|
result[2] = (low >>> 52) + ((high & 0x3FFFL) << 12); |
|
result[3] = (high >>> 14) & 0x3FFFFFFL; |
|
result[4] = (high >>> 40) + (highByte << 24L); |
|
} |
|
|
|
protected void encode(byte[] v, int offset, int length, byte highByte, |
|
long[] result) { |
|
if (length == 16) { |
|
ByteBuffer asLongLEBuffer = ByteBuffer.wrap(v, offset, length) |
|
.order(ByteOrder.LITTLE_ENDIAN); |
|
long low = asLongLEBuffer.getLong(); |
|
long high = asLongLEBuffer.getLong(); |
|
encode(high, low, highByte, result); |
|
} else { |
|
super.encode(v, offset, length, highByte, result); |
|
} |
|
} |
|
|
|
private void modReduceIn(long[] limbs, int index, long x) { |
|
|
|
long reducedValue = (x * SUBTRAHEND); |
|
limbs[index - NUM_LIMBS] += reducedValue; |
|
} |
|
|
|
@Override |
|
protected void finalCarryReduceLast(long[] limbs) { |
|
long carry = limbs[numLimbs - 1] >> bitsPerLimb; |
|
limbs[numLimbs - 1] -= carry << bitsPerLimb; |
|
modReduceIn(limbs, numLimbs, carry); |
|
} |
|
|
|
protected final void modReduce(long[] limbs, int start, int end) { |
|
|
|
for (int i = start; i < end; i++) { |
|
modReduceIn(limbs, i, limbs[i]); |
|
limbs[i] = 0; |
|
} |
|
} |
|
|
|
protected void modReduce(long[] limbs) { |
|
|
|
modReduce(limbs, NUM_LIMBS, NUM_LIMBS - 1); |
|
} |
|
|
|
@Override |
|
protected long carryValue(long x) { |
|
// This representation has plenty of extra space, so we can afford to |
|
// do a simplified carry operation that is more time-efficient. |
|
|
|
return x >> BITS_PER_LIMB; |
|
} |
|
|
|
@Override |
|
protected void postEncodeCarry(long[] v) { |
|
// not needed because carry is unsigned |
|
} |
|
|
|
@Override |
|
protected void reduce(long[] limbs) { |
|
long carry3 = carryOut(limbs, 3); |
|
long new4 = carry3 + limbs[4]; |
|
|
|
long carry4 = carryValue(new4); |
|
limbs[4] = new4 - (carry4 << BITS_PER_LIMB); |
|
|
|
modReduceIn(limbs, 5, carry4); |
|
carry(limbs); |
|
} |
|
|
|
} |
|
|