| /* |
| * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.security.util.math.intpoly; |
| |
| import sun.security.util.math.*; |
| |
| import java.math.BigInteger; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.util.Arrays; |
| |
| /** |
| * A large number polynomial representation using sparse limbs of signed |
| * long (64-bit) values. Limb values will always fit within a long, so inputs |
| * to multiplication must be less than 32 bits. All IntegerPolynomial |
| * implementations allow at most one addition before multiplication. Additions |
| * after that will result in an ArithmeticException. |
| * |
| * The following element operations are branch-free for all subclasses: |
| * |
| * fixed |
| * mutable |
| * add |
| * additiveInverse |
| * multiply |
| * square |
| * subtract |
| * conditionalSwapWith |
| * setValue (may branch on high-order byte parameter only) |
| * setSum |
| * setDifference |
| * setProduct |
| * setSquare |
| * addModPowerTwo |
| * asByteArray |
| * |
| * All other operations may branch in some subclasses. |
| * |
| */ |
| |
| public abstract class IntegerPolynomial implements IntegerFieldModuloP { |
| |
| protected static final BigInteger TWO = BigInteger.valueOf(2); |
| |
| protected final int numLimbs; |
| private final BigInteger modulus; |
| protected final int bitsPerLimb; |
| private final long[] posModLimbs; |
| private final int maxAdds; |
| |
| /** |
| * Reduce an IntegerPolynomial representation (a) and store the result |
| * in a. Requires that a.length == numLimbs. |
| */ |
| protected abstract void reduce(long[] a); |
| |
| /** |
| * Multiply an IntegerPolynomial representation (a) with a long (b) and |
| * store the result in an IntegerPolynomial representation in a. Requires |
| * that a.length == numLimbs. |
| */ |
| protected void multByInt(long[] a, long b) { |
| for (int i = 0; i < a.length; i++) { |
| a[i] *= b; |
| } |
| reduce(a); |
| } |
| |
| /** |
| * Multiply two IntegerPolynomial representations (a and b) and store the |
| * result in an IntegerPolynomial representation (r). Requires that |
| * a.length == b.length == r.length == numLimbs. It is allowed for a and r |
| * to be the same array. |
| */ |
| protected abstract void mult(long[] a, long[] b, long[] r); |
| |
| /** |
| * Multiply an IntegerPolynomial representation (a) with itself and store |
| * the result in an IntegerPolynomialRepresentation (r). Requires that |
| * a.length == r.length == numLimbs. It is allowed for a and r |
| * to be the same array. |
| */ |
| protected abstract void square(long[] a, long[] r); |
| |
| IntegerPolynomial(int bitsPerLimb, |
| int numLimbs, |
| int maxAdds, |
| BigInteger modulus) { |
| |
| |
| this.numLimbs = numLimbs; |
| this.modulus = modulus; |
| this.bitsPerLimb = bitsPerLimb; |
| this.maxAdds = maxAdds; |
| |
| posModLimbs = setPosModLimbs(); |
| } |
| |
| private long[] setPosModLimbs() { |
| long[] result = new long[numLimbs]; |
| setLimbsValuePositive(modulus, result); |
| return result; |
| } |
| |
| protected int getNumLimbs() { |
| return numLimbs; |
| } |
| |
| public int getMaxAdds() { |
| return maxAdds; |
| } |
| |
| @Override |
| public BigInteger getSize() { |
| return modulus; |
| } |
| |
| @Override |
| public ImmutableElement get0() { |
| return new ImmutableElement(false); |
| } |
| |
| @Override |
| public ImmutableElement get1() { |
| return new ImmutableElement(true); |
| } |
| |
| @Override |
| public ImmutableElement getElement(BigInteger v) { |
| return new ImmutableElement(v); |
| } |
| |
| @Override |
| public SmallValue getSmallValue(int value) { |
| int maxMag = 1 << (bitsPerLimb - 1); |
| if (Math.abs(value) >= maxMag) { |
| throw new IllegalArgumentException( |
| "max magnitude is " + maxMag); |
| } |
| return new Limb(value); |
| } |
| |
| /** |
| * This version of encode takes a ByteBuffer that is properly ordered, and |
| * may extract larger values (e.g. long) from the ByteBuffer for better |
| * performance. The implementation below only extracts bytes from the |
| * buffer, but this method may be overridden in field-specific |
| * implementations. |
| */ |
| protected void encode(ByteBuffer buf, int length, byte highByte, |
| long[] result) { |
| |
| int numHighBits = 32 - Integer.numberOfLeadingZeros(highByte); |
| int numBits = 8 * length + numHighBits; |
| int requiredLimbs = (numBits + bitsPerLimb - 1) / bitsPerLimb; |
| if (requiredLimbs > numLimbs) { |
| long[] temp = new long[requiredLimbs]; |
| encodeSmall(buf, length, highByte, temp); |
| // encode does a full carry/reduce |
| System.arraycopy(temp, 0, result, 0, result.length); |
| } else { |
| encodeSmall(buf, length, highByte, result); |
| } |
| } |
| |
| protected void encodeSmall(ByteBuffer buf, int length, byte highByte, |
| long[] result) { |
| |
| int limbIndex = 0; |
| long curLimbValue = 0; |
| int bitPos = 0; |
| for (int i = 0; i < length; i++) { |
| long curV = buf.get() & 0xFF; |
| |
| if (bitPos + 8 >= bitsPerLimb) { |
| int bitsThisLimb = bitsPerLimb - bitPos; |
| curLimbValue += (curV & (0xFF >> (8 - bitsThisLimb))) << bitPos; |
| result[limbIndex++] = curLimbValue; |
| curLimbValue = curV >> bitsThisLimb; |
| bitPos = 8 - bitsThisLimb; |
| } |
| else { |
| curLimbValue += curV << bitPos; |
| bitPos += 8; |
| } |
| } |
| |
| // one more for the high byte |
| if (highByte != 0) { |
| long curV = highByte & 0xFF; |
| if (bitPos + 8 >= bitsPerLimb) { |
| int bitsThisLimb = bitsPerLimb - bitPos; |
| curLimbValue += (curV & (0xFF >> (8 - bitsThisLimb))) << bitPos; |
| result[limbIndex++] = curLimbValue; |
| curLimbValue = curV >> bitsThisLimb; |
| } |
| else { |
| curLimbValue += curV << bitPos; |
| } |
| } |
| |
| if (limbIndex < result.length) { |
| result[limbIndex++] = curLimbValue; |
| } |
| Arrays.fill(result, limbIndex, result.length, 0); |
| |
| postEncodeCarry(result); |
| } |
| |
| protected void encode(byte[] v, int offset, int length, byte highByte, |
| long[] result) { |
| |
| ByteBuffer buf = ByteBuffer.wrap(v, offset, length); |
| buf.order(ByteOrder.LITTLE_ENDIAN); |
| encode(buf, length, highByte, result); |
| } |
| |
| // Encode does not produce compressed limbs. A simplified carry/reduce |
| // operation can be used to compress the limbs. |
| protected void postEncodeCarry(long[] v) { |
| reduce(v); |
| } |
| |
| public ImmutableElement getElement(byte[] v, int offset, int length, |
| byte highByte) { |
| |
| long[] result = new long[numLimbs]; |
| |
| encode(v, offset, length, highByte, result); |
| |
| return new ImmutableElement(result, 0); |
| } |
| |
| protected BigInteger evaluate(long[] limbs) { |
| BigInteger result = BigInteger.ZERO; |
| for (int i = limbs.length - 1; i >= 0; i--) { |
| result = result.shiftLeft(bitsPerLimb) |
| .add(BigInteger.valueOf(limbs[i])); |
| } |
| return result.mod(modulus); |
| } |
| |
| protected long carryValue(long x) { |
| // compressing carry operation |
| // if large positive number, carry one more to make it negative |
| // if large negative number (closer to zero), carry one fewer |
| return (x + (1 << (bitsPerLimb - 1))) >> bitsPerLimb; |
| } |
| |
| protected void carry(long[] limbs, int start, int end) { |
| |
| for (int i = start; i < end; i++) { |
| |
| long carry = carryOut(limbs, i); |
| limbs[i + 1] += carry; |
| } |
| } |
| |
| protected void carry(long[] limbs) { |
| |
| carry(limbs, 0, limbs.length - 1); |
| } |
| |
| /** |
| * Carry out of the specified position and return the carry value. |
| */ |
| protected long carryOut(long[] limbs, int index) { |
| long carry = carryValue(limbs[index]); |
| limbs[index] -= (carry << bitsPerLimb); |
| return carry; |
| } |
| |
| private void setLimbsValue(BigInteger v, long[] limbs) { |
| // set all limbs positive, and then carry |
| setLimbsValuePositive(v, limbs); |
| carry(limbs); |
| } |
| |
| protected void setLimbsValuePositive(BigInteger v, long[] limbs) { |
| BigInteger mod = BigInteger.valueOf(1 << bitsPerLimb); |
| for (int i = 0; i < limbs.length; i++) { |
| limbs[i] = v.mod(mod).longValue(); |
| v = v.shiftRight(bitsPerLimb); |
| } |
| } |
| |
| /** |
| * Carry out of the last limb and reduce back in. This method will be |
| * called as part of the "finalReduce" operation that puts the |
| * representation into a fully-reduced form. It is representation- |
| * specific, because representations have different amounts of empty |
| * space in the high-order limb. Requires that limbs.length=numLimbs. |
| */ |
| protected abstract void finalCarryReduceLast(long[] limbs); |
| |
| /** |
| * Convert reduced limbs into a number between 0 and MODULUS-1. |
| * Requires that limbs.length == numLimbs. This method only works if the |
| * modulus has at most three terms. |
| */ |
| protected void finalReduce(long[] limbs) { |
| |
| // This method works by doing several full carry/reduce operations. |
| // Some representations have extra high bits, so the carry/reduce out |
| // of the high position is implementation-specific. The "unsigned" |
| // carry operation always carries some (negative) value out of a |
| // position occupied by a negative value. So after a number of |
| // passes, all negative values are removed. |
| |
| // The first pass may leave a negative value in the high position, but |
| // this only happens if something was carried out of the previous |
| // position. So the previous position must have a "small" value. The |
| // next full carry is guaranteed not to carry out of that position. |
| |
| for (int pass = 0; pass < 2; pass++) { |
| // unsigned carry out of last position and reduce in to |
| // first position |
| finalCarryReduceLast(limbs); |
| |
| // unsigned carry on all positions |
| long carry = 0; |
| for (int i = 0; i < numLimbs - 1; i++) { |
| limbs[i] += carry; |
| carry = limbs[i] >> bitsPerLimb; |
| limbs[i] -= carry << bitsPerLimb; |
| } |
| limbs[numLimbs - 1] += carry; |
| } |
| |
| // Limbs are positive and all less than 2^bitsPerLimb, and the |
| // high-order limb may be even smaller due to the representation- |
| // specific carry/reduce out of the high position. |
| // The value may still be greater than the modulus. |
| // Subtract the max limb values only if all limbs end up non-negative |
| // This only works if there is at most one position where posModLimbs |
| // is less than 2^bitsPerLimb - 1 (not counting the high-order limb, |
| // if it has extra bits that are cleared by finalCarryReduceLast). |
| int smallerNonNegative = 1; |
| long[] smaller = new long[numLimbs]; |
| for (int i = numLimbs - 1; i >= 0; i--) { |
| smaller[i] = limbs[i] - posModLimbs[i]; |
| // expression on right is 1 if smaller[i] is nonnegative, |
| // 0 otherwise |
| smallerNonNegative *= (int) (smaller[i] >> 63) + 1; |
| } |
| conditionalSwap(smallerNonNegative, limbs, smaller); |
| |
| } |
| |
| /** |
| * Decode the value in v and store it in dst. Requires that v is final |
| * reduced. I.e. all limbs in [0, 2^bitsPerLimb) and value in [0, modulus). |
| */ |
| protected void decode(long[] v, byte[] dst, int offset, int length) { |
| |
| int nextLimbIndex = 0; |
| long curLimbValue = v[nextLimbIndex++]; |
| int bitPos = 0; |
| for (int i = 0; i < length; i++) { |
| |
| int dstIndex = i + offset; |
| if (bitPos + 8 >= bitsPerLimb) { |
| dst[dstIndex] = (byte) curLimbValue; |
| curLimbValue = 0; |
| if (nextLimbIndex < v.length) { |
| curLimbValue = v[nextLimbIndex++]; |
| } |
| int bitsAdded = bitsPerLimb - bitPos; |
| int bitsLeft = 8 - bitsAdded; |
| |
| dst[dstIndex] += (curLimbValue & (0xFF >> bitsAdded)) |
| << bitsAdded; |
| curLimbValue >>= bitsLeft; |
| bitPos = bitsLeft; |
| } else { |
| dst[dstIndex] = (byte) curLimbValue; |
| curLimbValue >>= 8; |
| bitPos += 8; |
| } |
| } |
| } |
| |
| /** |
| * Add two IntegerPolynomial representations (a and b) and store the result |
| * in an IntegerPolynomialRepresentation (dst). Requires that |
| * a.length == b.length == dst.length. It is allowed for a and |
| * dst to be the same array. |
| */ |
| protected void addLimbs(long[] a, long[] b, long[] dst) { |
| for (int i = 0; i < dst.length; i++) { |
| dst[i] = a[i] + b[i]; |
| } |
| } |
| |
| /** |
| * Branch-free conditional assignment of b to a. Requires that set is 0 or |
| * 1, and that a.length == b.length. If set==0, then the values of a and b |
| * will be unchanged. If set==1, then the values of b will be assigned to a. |
| * The behavior is undefined if swap has any value other than 0 or 1. |
| */ |
| protected static void conditionalAssign(int set, long[] a, long[] b) { |
| int maskValue = 0 - set; |
| for (int i = 0; i < a.length; i++) { |
| long dummyLimbs = maskValue & (a[i] ^ b[i]); |
| a[i] = dummyLimbs ^ a[i]; |
| } |
| } |
| |
| /** |
| * Branch-free conditional swap of a and b. Requires that swap is 0 or 1, |
| * and that a.length == b.length. If swap==0, then the values of a and b |
| * will be unchanged. If swap==1, then the values of a and b will be |
| * swapped. The behavior is undefined if swap has any value other than |
| * 0 or 1. |
| */ |
| protected static void conditionalSwap(int swap, long[] a, long[] b) { |
| int maskValue = 0 - swap; |
| for (int i = 0; i < a.length; i++) { |
| long dummyLimbs = maskValue & (a[i] ^ b[i]); |
| a[i] = dummyLimbs ^ a[i]; |
| b[i] = dummyLimbs ^ b[i]; |
| } |
| } |
| |
| /** |
| * Stores the reduced, little-endian value of limbs in result. |
| */ |
| protected void limbsToByteArray(long[] limbs, byte[] result) { |
| |
| long[] reducedLimbs = limbs.clone(); |
| finalReduce(reducedLimbs); |
| |
| decode(reducedLimbs, result, 0, result.length); |
| } |
| |
| /** |
| * Add the reduced number corresponding to limbs and other, and store |
| * the low-order bytes of the sum in result. Requires that |
| * limbs.length==other.length. The result array may have any length. |
| */ |
| protected void addLimbsModPowerTwo(long[] limbs, long[] other, |
| byte[] result) { |
| |
| long[] reducedOther = other.clone(); |
| long[] reducedLimbs = limbs.clone(); |
| finalReduce(reducedOther); |
| finalReduce(reducedLimbs); |
| |
| addLimbs(reducedLimbs, reducedOther, reducedLimbs); |
| |
| // may carry out a value which can be ignored |
| long carry = 0; |
| for (int i = 0; i < numLimbs; i++) { |
| reducedLimbs[i] += carry; |
| carry = reducedLimbs[i] >> bitsPerLimb; |
| reducedLimbs[i] -= carry << bitsPerLimb; |
| } |
| |
| decode(reducedLimbs, result, 0, result.length); |
| } |
| |
| private abstract class Element implements IntegerModuloP { |
| |
| protected long[] limbs; |
| protected int numAdds; |
| |
| public Element(BigInteger v) { |
| limbs = new long[numLimbs]; |
| setValue(v); |
| } |
| |
| public Element(boolean v) { |
| this.limbs = new long[numLimbs]; |
| this.limbs[0] = v ? 1l : 0l; |
| this.numAdds = 0; |
| } |
| |
| private Element(long[] limbs, int numAdds) { |
| this.limbs = limbs; |
| this.numAdds = numAdds; |
| } |
| |
| private void setValue(BigInteger v) { |
| setLimbsValue(v, limbs); |
| this.numAdds = 0; |
| } |
| |
| @Override |
| public IntegerFieldModuloP getField() { |
| return IntegerPolynomial.this; |
| } |
| |
| @Override |
| public BigInteger asBigInteger() { |
| return evaluate(limbs); |
| } |
| |
| @Override |
| public MutableElement mutable() { |
| return new MutableElement(limbs.clone(), numAdds); |
| } |
| |
| protected boolean isSummand() { |
| return numAdds < maxAdds; |
| } |
| |
| @Override |
| public ImmutableElement add(IntegerModuloP genB) { |
| |
| Element b = (Element) genB; |
| if (!(isSummand() && b.isSummand())) { |
| throw new ArithmeticException("Not a valid summand"); |
| } |
| |
| long[] newLimbs = new long[limbs.length]; |
| for (int i = 0; i < limbs.length; i++) { |
| newLimbs[i] = limbs[i] + b.limbs[i]; |
| } |
| |
| int newNumAdds = Math.max(numAdds, b.numAdds) + 1; |
| return new ImmutableElement(newLimbs, newNumAdds); |
| } |
| |
| @Override |
| public ImmutableElement additiveInverse() { |
| |
| long[] newLimbs = new long[limbs.length]; |
| for (int i = 0; i < limbs.length; i++) { |
| newLimbs[i] = -limbs[i]; |
| } |
| |
| ImmutableElement result = new ImmutableElement(newLimbs, numAdds); |
| return result; |
| } |
| |
| protected long[] cloneLow(long[] limbs) { |
| long[] newLimbs = new long[numLimbs]; |
| copyLow(limbs, newLimbs); |
| return newLimbs; |
| } |
| protected void copyLow(long[] limbs, long[] out) { |
| System.arraycopy(limbs, 0, out, 0, out.length); |
| } |
| |
| @Override |
| public ImmutableElement multiply(IntegerModuloP genB) { |
| |
| Element b = (Element) genB; |
| |
| long[] newLimbs = new long[limbs.length]; |
| mult(limbs, b.limbs, newLimbs); |
| return new ImmutableElement(newLimbs, 0); |
| } |
| |
| @Override |
| public ImmutableElement square() { |
| long[] newLimbs = new long[limbs.length]; |
| IntegerPolynomial.this.square(limbs, newLimbs); |
| return new ImmutableElement(newLimbs, 0); |
| } |
| |
| public void addModPowerTwo(IntegerModuloP arg, byte[] result) { |
| |
| Element other = (Element) arg; |
| if (!(isSummand() && other.isSummand())) { |
| throw new ArithmeticException("Not a valid summand"); |
| } |
| addLimbsModPowerTwo(limbs, other.limbs, result); |
| } |
| |
| public void asByteArray(byte[] result) { |
| if (!isSummand()) { |
| throw new ArithmeticException("Not a valid summand"); |
| } |
| limbsToByteArray(limbs, result); |
| } |
| } |
| |
| protected class MutableElement extends Element |
| implements MutableIntegerModuloP { |
| |
| protected MutableElement(long[] limbs, int numAdds) { |
| super(limbs, numAdds); |
| } |
| |
| @Override |
| public ImmutableElement fixed() { |
| return new ImmutableElement(limbs.clone(), numAdds); |
| } |
| |
| @Override |
| public void conditionalSet(IntegerModuloP b, int set) { |
| |
| Element other = (Element) b; |
| |
| conditionalAssign(set, limbs, other.limbs); |
| numAdds = other.numAdds; |
| } |
| |
| @Override |
| public void conditionalSwapWith(MutableIntegerModuloP b, int swap) { |
| |
| MutableElement other = (MutableElement) b; |
| |
| conditionalSwap(swap, limbs, other.limbs); |
| int numAddsTemp = numAdds; |
| numAdds = other.numAdds; |
| other.numAdds = numAddsTemp; |
| } |
| |
| |
| @Override |
| public MutableElement setValue(IntegerModuloP v) { |
| Element other = (Element) v; |
| |
| System.arraycopy(other.limbs, 0, limbs, 0, other.limbs.length); |
| numAdds = other.numAdds; |
| return this; |
| } |
| |
| @Override |
| public MutableElement setValue(byte[] arr, int offset, |
| int length, byte highByte) { |
| |
| encode(arr, offset, length, highByte, limbs); |
| this.numAdds = 0; |
| |
| return this; |
| } |
| |
| @Override |
| public MutableElement setValue(ByteBuffer buf, int length, |
| byte highByte) { |
| |
| encode(buf, length, highByte, limbs); |
| numAdds = 0; |
| |
| return this; |
| } |
| |
| @Override |
| public MutableElement setProduct(IntegerModuloP genB) { |
| Element b = (Element) genB; |
| mult(limbs, b.limbs, limbs); |
| numAdds = 0; |
| return this; |
| } |
| |
| @Override |
| public MutableElement setProduct(SmallValue v) { |
| int value = ((Limb) v).value; |
| multByInt(limbs, value); |
| numAdds = 0; |
| return this; |
| } |
| |
| @Override |
| public MutableElement setSum(IntegerModuloP genB) { |
| |
| Element b = (Element) genB; |
| if (!(isSummand() && b.isSummand())) { |
| throw new ArithmeticException("Not a valid summand"); |
| } |
| |
| for (int i = 0; i < limbs.length; i++) { |
| limbs[i] = limbs[i] + b.limbs[i]; |
| } |
| |
| numAdds = Math.max(numAdds, b.numAdds) + 1; |
| return this; |
| } |
| |
| @Override |
| public MutableElement setDifference(IntegerModuloP genB) { |
| |
| Element b = (Element) genB; |
| if (!(isSummand() && b.isSummand())) { |
| throw new ArithmeticException("Not a valid summand"); |
| } |
| |
| for (int i = 0; i < limbs.length; i++) { |
| limbs[i] = limbs[i] - b.limbs[i]; |
| } |
| |
| numAdds = Math.max(numAdds, b.numAdds) + 1; |
| return this; |
| } |
| |
| @Override |
| public MutableElement setSquare() { |
| IntegerPolynomial.this.square(limbs, limbs); |
| numAdds = 0; |
| return this; |
| } |
| |
| @Override |
| public MutableElement setAdditiveInverse() { |
| |
| for (int i = 0; i < limbs.length; i++) { |
| limbs[i] = -limbs[i]; |
| } |
| return this; |
| } |
| |
| @Override |
| public MutableElement setReduced() { |
| |
| reduce(limbs); |
| numAdds = 0; |
| return this; |
| } |
| } |
| |
| class ImmutableElement extends Element implements ImmutableIntegerModuloP { |
| |
| protected ImmutableElement(BigInteger v) { |
| super(v); |
| } |
| |
| protected ImmutableElement(boolean v) { |
| super(v); |
| } |
| |
| protected ImmutableElement(long[] limbs, int numAdds) { |
| super(limbs, numAdds); |
| } |
| |
| @Override |
| public ImmutableElement fixed() { |
| return this; |
| } |
| |
| } |
| |
| class Limb implements SmallValue { |
| int value; |
| |
| Limb(int value) { |
| this.value = value; |
| } |
| } |
| |
| |
| } |