Path: blob/master/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java
64507 views
/*1* Copyright (c) 2018, 2021, 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.ArrayUtil;29import sun.security.util.math.*;30import static sun.security.ec.ECOperations.IntermediateValueException;3132import java.math.BigInteger;33import java.security.ProviderException;34import java.security.spec.*;35import java.util.Arrays;36import java.util.Optional;3738public class ECDSAOperations {3940public static class Seed {41private final byte[] seedValue;4243public Seed(byte[] seedValue) {44this.seedValue = seedValue;45}4647public byte[] getSeedValue() {48return seedValue;49}50}5152public static class Nonce {53private final byte[] nonceValue;5455public Nonce(byte[] nonceValue) {56this.nonceValue = nonceValue;57}5859public byte[] getNonceValue() {60return nonceValue;61}62}6364private final ECOperations ecOps;65private final AffinePoint basePoint;6667public ECDSAOperations(ECOperations ecOps, ECPoint basePoint) {68this.ecOps = ecOps;69this.basePoint = toAffinePoint(basePoint, ecOps.getField());70}7172public ECOperations getEcOperations() {73return ecOps;74}7576public AffinePoint basePointMultiply(byte[] scalar) {77return ecOps.multiply(basePoint, scalar).asAffine();78}7980public static AffinePoint toAffinePoint(ECPoint point,81IntegerFieldModuloP field) {8283ImmutableIntegerModuloP affineX = field.getElement(point.getAffineX());84ImmutableIntegerModuloP affineY = field.getElement(point.getAffineY());85return new AffinePoint(affineX, affineY);86}8788public static89Optional<ECDSAOperations> forParameters(ECParameterSpec ecParams) {90Optional<ECOperations> curveOps =91ECOperations.forParameters(ecParams);92return curveOps.map(93ops -> new ECDSAOperations(ops, ecParams.getGenerator())94);95}9697/**98*99* Sign a digest using the provided private key and seed.100* IMPORTANT: The private key is a scalar represented using a101* little-endian byte array. This is backwards from the conventional102* representation in ECDSA. The routines that produce and consume this103* value uses little-endian, so this deviation from convention removes104* the requirement to swap the byte order. The returned signature is in105* the conventional byte order.106*107* @param privateKey the private key scalar as a little-endian byte array108* @param digest the digest to be signed109* @param seed the seed that will be used to produce the nonce. This object110* should contain an array that is at least 64 bits longer than111* the number of bits required to represent the group order.112* @return the ECDSA signature value113* @throws IntermediateValueException if the signature cannot be produced114* due to an unacceptable intermediate or final value. If this115* exception is thrown, then the caller should discard the nonnce and116* try again with an entirely new nonce value.117*/118public byte[] signDigest(byte[] privateKey, byte[] digest, Seed seed)119throws IntermediateValueException {120121byte[] nonceArr = ecOps.seedToScalar(seed.getSeedValue());122123Nonce nonce = new Nonce(nonceArr);124return signDigest(privateKey, digest, nonce);125}126127/**128*129* Sign a digest using the provided private key and nonce.130* IMPORTANT: The private key and nonce are scalars represented by a131* little-endian byte array. This is backwards from the conventional132* representation in ECDSA. The routines that produce and consume these133* values use little-endian, so this deviation from convention removes134* the requirement to swap the byte order. The returned signature is in135* the conventional byte order.136*137* @param privateKey the private key scalar as a little-endian byte array138* @param digest the digest to be signed139* @param nonce the nonce object containing a little-endian scalar value.140* @return the ECDSA signature value141* @throws IntermediateValueException if the signature cannot be produced142* due to an unacceptable intermediate or final value. If this143* exception is thrown, then the caller should discard the nonnce and144* try again with an entirely new nonce value.145*/146public byte[] signDigest(byte[] privateKey, byte[] digest, Nonce nonce)147throws IntermediateValueException {148149IntegerFieldModuloP orderField = ecOps.getOrderField();150int orderBits = orderField.getSize().bitLength();151if (orderBits % 8 != 0 && orderBits < digest.length * 8) {152// This implementation does not support truncating digests to153// a length that is not a multiple of 8.154throw new ProviderException("Invalid digest length");155}156157byte[] k = nonce.getNonceValue();158// check nonce length159int length = (orderField.getSize().bitLength() + 7) / 8;160if (k.length != length) {161throw new ProviderException("Incorrect nonce length");162}163164MutablePoint R = ecOps.multiply(basePoint, k);165IntegerModuloP r = R.asAffine().getX();166// put r into the correct field by fully reducing to an array167byte[] temp = new byte[length];168r = b2a(r, orderField, temp);169byte[] result = new byte[2 * length];170ArrayUtil.reverse(temp);171System.arraycopy(temp, 0, result, 0, length);172// compare r to 0173if (ECOperations.allZero(temp)) {174throw new IntermediateValueException();175}176177IntegerModuloP dU = orderField.getElement(privateKey);178int lengthE = Math.min(length, digest.length);179byte[] E = new byte[lengthE];180System.arraycopy(digest, 0, E, 0, lengthE);181ArrayUtil.reverse(E);182IntegerModuloP e = orderField.getElement(E);183IntegerModuloP kElem = orderField.getElement(k);184IntegerModuloP kInv = kElem.multiplicativeInverse();185MutableIntegerModuloP s = r.mutable();186s.setProduct(dU).setSum(e).setProduct(kInv);187// store s in result188s.asByteArray(temp);189ArrayUtil.reverse(temp);190System.arraycopy(temp, 0, result, length, length);191// compare s to 0192if (ECOperations.allZero(temp)) {193throw new IntermediateValueException();194}195196return result;197198}199public boolean verifySignedDigest(byte[] digest, byte[] sig, ECPoint pp) {200201IntegerFieldModuloP field = ecOps.getField();202IntegerFieldModuloP orderField = ecOps.getOrderField();203BigInteger mod = orderField.getSize();204int length = (mod.bitLength() + 7) / 8;205206byte[] r;207byte[] s;208209int encodeLength = sig.length / 2;210if (sig.length %2 != 0 || encodeLength > length) {211return false;212} else if (encodeLength == length) {213r = Arrays.copyOf(sig, length);214s = Arrays.copyOfRange(sig, length, length * 2);215} else {216r = new byte[length];217s = new byte[length];218System.arraycopy(sig, 0, r, length - encodeLength, encodeLength);219System.arraycopy(sig, encodeLength, s, length - encodeLength, encodeLength);220}221222BigInteger rb = new BigInteger(1, r);223BigInteger sb = new BigInteger(1, s);224if (rb.signum() == 0 || sb.signum() == 0225|| rb.compareTo(mod) >= 0 || sb.compareTo(mod) >= 0) {226return false;227}228229ArrayUtil.reverse(r);230ArrayUtil.reverse(s);231IntegerModuloP ri = orderField.getElement(r);232IntegerModuloP si = orderField.getElement(s);233// z234int lengthE = Math.min(length, digest.length);235byte[] E = new byte[lengthE];236System.arraycopy(digest, 0, E, 0, lengthE);237ArrayUtil.reverse(E);238IntegerModuloP e = orderField.getElement(E);239240IntegerModuloP sInv = si.multiplicativeInverse();241ImmutableIntegerModuloP u1 = e.multiply(sInv);242ImmutableIntegerModuloP u2 = ri.multiply(sInv);243244AffinePoint pub = new AffinePoint(field.getElement(pp.getAffineX()),245field.getElement(pp.getAffineY()));246247byte[] temp1 = new byte[length];248b2a(u1, orderField, temp1);249250byte[] temp2 = new byte[length];251b2a(u2, orderField, temp2);252253MutablePoint p1 = ecOps.multiply(basePoint, temp1);254MutablePoint p2 = ecOps.multiply(pub, temp2);255256ecOps.setSum(p1, p2.asAffine());257IntegerModuloP result = p1.asAffine().getX();258result = result.additiveInverse().add(ri);259260b2a(result, orderField, temp1);261return ECOperations.allZero(temp1);262}263264public static ImmutableIntegerModuloP b2a(IntegerModuloP b,265IntegerFieldModuloP orderField, byte[] temp1) {266b.asByteArray(temp1);267ImmutableIntegerModuloP b2 = orderField.getElement(temp1);268b2.asByteArray(temp1);269return b2;270}271}272273274