Path: blob/master/src/jdk.crypto.ec/share/classes/sun/security/ec/ECOperations.java
41161 views
/*1* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.ec;2627import sun.security.ec.point.*;28import sun.security.util.math.*;29import sun.security.util.math.intpoly.*;3031import java.math.BigInteger;32import java.security.ProviderException;33import java.security.spec.ECFieldFp;34import java.security.spec.ECParameterSpec;35import java.security.spec.EllipticCurve;36import java.util.Map;37import java.util.Optional;3839/*40* Elliptic curve point arithmetic for prime-order curves where a=-3.41* Formulas are derived from "Complete addition formulas for prime order42* elliptic curves" by Renes, Costello, and Batina.43*/4445public class ECOperations {4647/*48* An exception indicating a problem with an intermediate value produced49* by some part of the computation. For example, the signing operation50* will throw this exception to indicate that the r or s value is 0, and51* that the signing operation should be tried again with a different nonce.52*/53static class IntermediateValueException extends Exception {54private static final long serialVersionUID = 1;55}5657static final Map<BigInteger, IntegerFieldModuloP> fields = Map.of(58IntegerPolynomialP256.MODULUS, new IntegerPolynomialP256(),59IntegerPolynomialP384.MODULUS, new IntegerPolynomialP384(),60IntegerPolynomialP521.MODULUS, new IntegerPolynomialP521()61);6263static final Map<BigInteger, IntegerFieldModuloP> orderFields = Map.of(64P256OrderField.MODULUS, new P256OrderField(),65P384OrderField.MODULUS, new P384OrderField(),66P521OrderField.MODULUS, new P521OrderField()67);6869public static Optional<ECOperations> forParameters(ECParameterSpec params) {7071EllipticCurve curve = params.getCurve();72if (!(curve.getField() instanceof ECFieldFp)) {73return Optional.empty();74}75ECFieldFp primeField = (ECFieldFp) curve.getField();7677BigInteger three = BigInteger.valueOf(3);78if (!primeField.getP().subtract(curve.getA()).equals(three)) {79return Optional.empty();80}81IntegerFieldModuloP field = fields.get(primeField.getP());82if (field == null) {83return Optional.empty();84}8586IntegerFieldModuloP orderField = orderFields.get(params.getOrder());87if (orderField == null) {88return Optional.empty();89}9091ImmutableIntegerModuloP b = field.getElement(curve.getB());92ECOperations ecOps = new ECOperations(b, orderField);93return Optional.of(ecOps);94}9596final ImmutableIntegerModuloP b;97final SmallValue one;98final SmallValue two;99final SmallValue three;100final SmallValue four;101final ProjectivePoint.Immutable neutral;102private final IntegerFieldModuloP orderField;103104public ECOperations(IntegerModuloP b, IntegerFieldModuloP orderField) {105this.b = b.fixed();106this.orderField = orderField;107108this.one = b.getField().getSmallValue(1);109this.two = b.getField().getSmallValue(2);110this.three = b.getField().getSmallValue(3);111this.four = b.getField().getSmallValue(4);112113IntegerFieldModuloP field = b.getField();114this.neutral = new ProjectivePoint.Immutable(field.get0(),115field.get1(), field.get0());116}117118public IntegerFieldModuloP getField() {119return b.getField();120}121public IntegerFieldModuloP getOrderField() {122return orderField;123}124125protected ProjectivePoint.Immutable getNeutral() {126return neutral;127}128129public boolean isNeutral(Point p) {130ProjectivePoint<?> pp = (ProjectivePoint<?>) p;131132IntegerModuloP z = pp.getZ();133134IntegerFieldModuloP field = z.getField();135int byteLength = (field.getSize().bitLength() + 7) / 8;136byte[] zBytes = z.asByteArray(byteLength);137return allZero(zBytes);138}139140byte[] seedToScalar(byte[] seedBytes)141throws IntermediateValueException {142143// Produce a nonce from the seed using FIPS 186-4,section B.5.1:144// Per-Message Secret Number Generation Using Extra Random Bits145// or146// Produce a scalar from the seed using FIPS 186-4, section B.4.1:147// Key Pair Generation Using Extra Random Bits148149// To keep the implementation simple, sample in the range [0,n)150// and throw IntermediateValueException in the (unlikely) event151// that the result is 0.152153// Get 64 extra bits and reduce in to the nonce154int seedBits = orderField.getSize().bitLength() + 64;155if (seedBytes.length * 8 < seedBits) {156throw new ProviderException("Incorrect seed length: " +157seedBytes.length * 8 + " < " + seedBits);158}159160// input conversion only works on byte boundaries161// clear high-order bits of last byte so they don't influence nonce162int lastByteBits = seedBits % 8;163if (lastByteBits != 0) {164int lastByteIndex = seedBits / 8;165byte mask = (byte) (0xFF >>> (8 - lastByteBits));166seedBytes[lastByteIndex] &= mask;167}168169int seedLength = (seedBits + 7) / 8;170IntegerModuloP scalarElem =171orderField.getElement(seedBytes, 0, seedLength, (byte) 0);172int scalarLength = (orderField.getSize().bitLength() + 7) / 8;173byte[] scalarArr = new byte[scalarLength];174scalarElem.asByteArray(scalarArr);175if (ECOperations.allZero(scalarArr)) {176throw new IntermediateValueException();177}178return scalarArr;179}180181/*182* Compare all values in the array to 0 without branching on any value183*184*/185public static boolean allZero(byte[] arr) {186byte acc = 0;187for (int i = 0; i < arr.length; i++) {188acc |= arr[i];189}190return acc == 0;191}192193/*194* 4-bit branchless array lookup for projective points.195*/196private void lookup4(ProjectivePoint.Immutable[] arr, int index,197ProjectivePoint.Mutable result, IntegerModuloP zero) {198199for (int i = 0; i < 16; i++) {200int xor = index ^ i;201int bit3 = (xor & 0x8) >>> 3;202int bit2 = (xor & 0x4) >>> 2;203int bit1 = (xor & 0x2) >>> 1;204int bit0 = (xor & 0x1);205int inverse = bit0 | bit1 | bit2 | bit3;206int set = 1 - inverse;207208ProjectivePoint.Immutable pi = arr[i];209result.conditionalSet(pi, set);210}211}212213private void double4(ProjectivePoint.Mutable p, MutableIntegerModuloP t0,214MutableIntegerModuloP t1, MutableIntegerModuloP t2,215MutableIntegerModuloP t3, MutableIntegerModuloP t4) {216217for (int i = 0; i < 4; i++) {218setDouble(p, t0, t1, t2, t3, t4);219}220}221222/**223* Multiply an affine point by a scalar and return the result as a mutable224* point.225*226* @param affineP the point227* @param s the scalar as a little-endian array228* @return the product229*/230public MutablePoint multiply(AffinePoint affineP, byte[] s) {231232// 4-bit windowed multiply with branchless lookup.233// The mixed addition is faster, so it is used to construct the array234// at the beginning of the operation.235236IntegerFieldModuloP field = affineP.getX().getField();237ImmutableIntegerModuloP zero = field.get0();238// temporaries239MutableIntegerModuloP t0 = zero.mutable();240MutableIntegerModuloP t1 = zero.mutable();241MutableIntegerModuloP t2 = zero.mutable();242MutableIntegerModuloP t3 = zero.mutable();243MutableIntegerModuloP t4 = zero.mutable();244245ProjectivePoint.Mutable result = new ProjectivePoint.Mutable(field);246result.getY().setValue(field.get1().mutable());247248ProjectivePoint.Immutable[] pointMultiples =249new ProjectivePoint.Immutable[16];250// 0P is neutral---same as initial result value251pointMultiples[0] = result.fixed();252253ProjectivePoint.Mutable ps = new ProjectivePoint.Mutable(field);254ps.setValue(affineP);255// 1P = P256pointMultiples[1] = ps.fixed();257258// the rest are calculated using mixed point addition259for (int i = 2; i < 16; i++) {260setSum(ps, affineP, t0, t1, t2, t3, t4);261pointMultiples[i] = ps.fixed();262}263264ProjectivePoint.Mutable lookupResult = ps.mutable();265266for (int i = s.length - 1; i >= 0; i--) {267268double4(result, t0, t1, t2, t3, t4);269270int high = (0xFF & s[i]) >>> 4;271lookup4(pointMultiples, high, lookupResult, zero);272setSum(result, lookupResult, t0, t1, t2, t3, t4);273274double4(result, t0, t1, t2, t3, t4);275276int low = 0xF & s[i];277lookup4(pointMultiples, low, lookupResult, zero);278setSum(result, lookupResult, t0, t1, t2, t3, t4);279}280281return result;282283}284285/*286* Point double287*/288private void setDouble(ProjectivePoint.Mutable p, MutableIntegerModuloP t0,289MutableIntegerModuloP t1, MutableIntegerModuloP t2,290MutableIntegerModuloP t3, MutableIntegerModuloP t4) {291292t0.setValue(p.getX()).setSquare();293t1.setValue(p.getY()).setSquare();294t2.setValue(p.getZ()).setSquare();295t3.setValue(p.getX()).setProduct(p.getY());296t4.setValue(p.getY()).setProduct(p.getZ());297298t3.setSum(t3);299p.getZ().setProduct(p.getX());300301p.getZ().setProduct(two);302303p.getY().setValue(t2).setProduct(b);304p.getY().setDifference(p.getZ());305306p.getX().setValue(p.getY()).setProduct(two);307p.getY().setSum(p.getX());308p.getY().setReduced();309p.getX().setValue(t1).setDifference(p.getY());310311p.getY().setSum(t1);312p.getY().setProduct(p.getX());313p.getX().setProduct(t3);314315t3.setValue(t2).setProduct(two);316t2.setSum(t3);317p.getZ().setProduct(b);318319t2.setReduced();320p.getZ().setDifference(t2);321p.getZ().setDifference(t0);322t3.setValue(p.getZ()).setProduct(two);323p.getZ().setReduced();324p.getZ().setSum(t3);325t0.setProduct(three);326327t0.setDifference(t2);328t0.setProduct(p.getZ());329p.getY().setSum(t0);330331t4.setSum(t4);332p.getZ().setProduct(t4);333334p.getX().setDifference(p.getZ());335p.getZ().setValue(t4).setProduct(t1);336337p.getZ().setProduct(four);338339}340341/*342* Mixed point addition. This method constructs new temporaries each time343* it is called. For better efficiency, the method that reuses temporaries344* should be used if more than one sum will be computed.345*/346public void setSum(MutablePoint p, AffinePoint p2) {347348IntegerModuloP zero = p.getField().get0();349MutableIntegerModuloP t0 = zero.mutable();350MutableIntegerModuloP t1 = zero.mutable();351MutableIntegerModuloP t2 = zero.mutable();352MutableIntegerModuloP t3 = zero.mutable();353MutableIntegerModuloP t4 = zero.mutable();354setSum((ProjectivePoint.Mutable) p, p2, t0, t1, t2, t3, t4);355356}357358/*359* Mixed point addition360*/361private void setSum(ProjectivePoint.Mutable p, AffinePoint p2,362MutableIntegerModuloP t0, MutableIntegerModuloP t1,363MutableIntegerModuloP t2, MutableIntegerModuloP t3,364MutableIntegerModuloP t4) {365366t0.setValue(p.getX()).setProduct(p2.getX());367t1.setValue(p.getY()).setProduct(p2.getY());368t3.setValue(p2.getX()).setSum(p2.getY());369t4.setValue(p.getX()).setSum(p.getY());370p.getX().setReduced();371t3.setProduct(t4);372t4.setValue(t0).setSum(t1);373374t3.setDifference(t4);375t4.setValue(p2.getY()).setProduct(p.getZ());376t4.setSum(p.getY());377378p.getY().setValue(p2.getX()).setProduct(p.getZ());379p.getY().setSum(p.getX());380t2.setValue(p.getZ());381p.getZ().setProduct(b);382383p.getX().setValue(p.getY()).setDifference(p.getZ());384p.getX().setReduced();385p.getZ().setValue(p.getX()).setProduct(two);386p.getX().setSum(p.getZ());387388p.getZ().setValue(t1).setDifference(p.getX());389p.getX().setSum(t1);390p.getY().setProduct(b);391392t1.setValue(t2).setProduct(two);393t2.setSum(t1);394t2.setReduced();395p.getY().setDifference(t2);396397p.getY().setDifference(t0);398p.getY().setReduced();399t1.setValue(p.getY()).setProduct(two);400p.getY().setSum(t1);401402t1.setValue(t0).setProduct(two);403t0.setSum(t1);404t0.setDifference(t2);405406t1.setValue(t4).setProduct(p.getY());407t2.setValue(t0).setProduct(p.getY());408p.getY().setValue(p.getX()).setProduct(p.getZ());409410p.getY().setSum(t2);411p.getX().setProduct(t3);412p.getX().setDifference(t1);413414p.getZ().setProduct(t4);415t1.setValue(t3).setProduct(t0);416p.getZ().setSum(t1);417418}419420/*421* Projective point addition422*/423private void setSum(ProjectivePoint.Mutable p, ProjectivePoint.Mutable p2,424MutableIntegerModuloP t0, MutableIntegerModuloP t1,425MutableIntegerModuloP t2, MutableIntegerModuloP t3,426MutableIntegerModuloP t4) {427428t0.setValue(p.getX()).setProduct(p2.getX());429t1.setValue(p.getY()).setProduct(p2.getY());430t2.setValue(p.getZ()).setProduct(p2.getZ());431432t3.setValue(p.getX()).setSum(p.getY());433t4.setValue(p2.getX()).setSum(p2.getY());434t3.setProduct(t4);435436t4.setValue(t0).setSum(t1);437t3.setDifference(t4);438t4.setValue(p.getY()).setSum(p.getZ());439440p.getY().setValue(p2.getY()).setSum(p2.getZ());441t4.setProduct(p.getY());442p.getY().setValue(t1).setSum(t2);443444t4.setDifference(p.getY());445p.getX().setSum(p.getZ());446p.getY().setValue(p2.getX()).setSum(p2.getZ());447448p.getX().setProduct(p.getY());449p.getY().setValue(t0).setSum(t2);450p.getY().setAdditiveInverse().setSum(p.getX());451p.getY().setReduced();452453p.getZ().setValue(t2).setProduct(b);454p.getX().setValue(p.getY()).setDifference(p.getZ());455p.getZ().setValue(p.getX()).setProduct(two);456457p.getX().setSum(p.getZ());458p.getX().setReduced();459p.getZ().setValue(t1).setDifference(p.getX());460p.getX().setSum(t1);461462p.getY().setProduct(b);463t1.setValue(t2).setSum(t2);464t2.setSum(t1);465t2.setReduced();466467p.getY().setDifference(t2);468p.getY().setDifference(t0);469p.getY().setReduced();470t1.setValue(p.getY()).setSum(p.getY());471472p.getY().setSum(t1);473t1.setValue(t0).setProduct(two);474t0.setSum(t1);475476t0.setDifference(t2);477t1.setValue(t4).setProduct(p.getY());478t2.setValue(t0).setProduct(p.getY());479480p.getY().setValue(p.getX()).setProduct(p.getZ());481p.getY().setSum(t2);482p.getX().setProduct(t3);483484p.getX().setDifference(t1);485p.getZ().setProduct(t4);486t1.setValue(t3).setProduct(t0);487488p.getZ().setSum(t1);489490}491}492493494495