Path: blob/master/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java
41161 views
/*1* Copyright (c) 2018, 2020, 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.security.ProviderException;33import java.security.spec.*;34import java.util.Arrays;35import java.util.Optional;3637public class ECDSAOperations {3839public static class Seed {40private final byte[] seedValue;4142public Seed(byte[] seedValue) {43this.seedValue = seedValue;44}4546public byte[] getSeedValue() {47return seedValue;48}49}5051public static class Nonce {52private final byte[] nonceValue;5354public Nonce(byte[] nonceValue) {55this.nonceValue = nonceValue;56}5758public byte[] getNonceValue() {59return nonceValue;60}61}6263private final ECOperations ecOps;64private final AffinePoint basePoint;6566public ECDSAOperations(ECOperations ecOps, ECPoint basePoint) {67this.ecOps = ecOps;68this.basePoint = toAffinePoint(basePoint, ecOps.getField());69}7071public ECOperations getEcOperations() {72return ecOps;73}7475public AffinePoint basePointMultiply(byte[] scalar) {76return ecOps.multiply(basePoint, scalar).asAffine();77}7879public static AffinePoint toAffinePoint(ECPoint point,80IntegerFieldModuloP field) {8182ImmutableIntegerModuloP affineX = field.getElement(point.getAffineX());83ImmutableIntegerModuloP affineY = field.getElement(point.getAffineY());84return new AffinePoint(affineX, affineY);85}8687public static88Optional<ECDSAOperations> forParameters(ECParameterSpec ecParams) {89Optional<ECOperations> curveOps =90ECOperations.forParameters(ecParams);91return curveOps.map(92ops -> new ECDSAOperations(ops, ecParams.getGenerator())93);94}9596/**97*98* Sign a digest using the provided private key and seed.99* IMPORTANT: The private key is a scalar represented using a100* little-endian byte array. This is backwards from the conventional101* representation in ECDSA. The routines that produce and consume this102* value uses little-endian, so this deviation from convention removes103* the requirement to swap the byte order. The returned signature is in104* the conventional byte order.105*106* @param privateKey the private key scalar as a little-endian byte array107* @param digest the digest to be signed108* @param seed the seed that will be used to produce the nonce. This object109* should contain an array that is at least 64 bits longer than110* the number of bits required to represent the group order.111* @return the ECDSA signature value112* @throws IntermediateValueException if the signature cannot be produced113* due to an unacceptable intermediate or final value. If this114* exception is thrown, then the caller should discard the nonnce and115* try again with an entirely new nonce value.116*/117public byte[] signDigest(byte[] privateKey, byte[] digest, Seed seed)118throws IntermediateValueException {119120byte[] nonceArr = ecOps.seedToScalar(seed.getSeedValue());121122Nonce nonce = new Nonce(nonceArr);123return signDigest(privateKey, digest, nonce);124}125126/**127*128* Sign a digest using the provided private key and nonce.129* IMPORTANT: The private key and nonce are scalars represented by a130* little-endian byte array. This is backwards from the conventional131* representation in ECDSA. The routines that produce and consume these132* values use little-endian, so this deviation from convention removes133* the requirement to swap the byte order. The returned signature is in134* the conventional byte order.135*136* @param privateKey the private key scalar as a little-endian byte array137* @param digest the digest to be signed138* @param nonce the nonce object containing a little-endian scalar value.139* @return the ECDSA signature value140* @throws IntermediateValueException if the signature cannot be produced141* due to an unacceptable intermediate or final value. If this142* exception is thrown, then the caller should discard the nonnce and143* try again with an entirely new nonce value.144*/145public byte[] signDigest(byte[] privateKey, byte[] digest, Nonce nonce)146throws IntermediateValueException {147148IntegerFieldModuloP orderField = ecOps.getOrderField();149int orderBits = orderField.getSize().bitLength();150if (orderBits % 8 != 0 && orderBits < digest.length * 8) {151// This implementation does not support truncating digests to152// a length that is not a multiple of 8.153throw new ProviderException("Invalid digest length");154}155156byte[] k = nonce.getNonceValue();157// check nonce length158int length = (orderField.getSize().bitLength() + 7) / 8;159if (k.length != length) {160throw new ProviderException("Incorrect nonce length");161}162163MutablePoint R = ecOps.multiply(basePoint, k);164IntegerModuloP r = R.asAffine().getX();165// put r into the correct field by fully reducing to an array166byte[] temp = new byte[length];167r = b2a(r, orderField, temp);168byte[] result = new byte[2 * length];169ArrayUtil.reverse(temp);170System.arraycopy(temp, 0, result, 0, length);171// compare r to 0172if (ECOperations.allZero(temp)) {173throw new IntermediateValueException();174}175176IntegerModuloP dU = orderField.getElement(privateKey);177int lengthE = Math.min(length, digest.length);178byte[] E = new byte[lengthE];179System.arraycopy(digest, 0, E, 0, lengthE);180ArrayUtil.reverse(E);181IntegerModuloP e = orderField.getElement(E);182IntegerModuloP kElem = orderField.getElement(k);183IntegerModuloP kInv = kElem.multiplicativeInverse();184MutableIntegerModuloP s = r.mutable();185s.setProduct(dU).setSum(e).setProduct(kInv);186// store s in result187s.asByteArray(temp);188ArrayUtil.reverse(temp);189System.arraycopy(temp, 0, result, length, length);190// compare s to 0191if (ECOperations.allZero(temp)) {192throw new IntermediateValueException();193}194195return result;196197}198public boolean verifySignedDigest(byte[] digest, byte[] sig, ECPoint pp) {199200IntegerFieldModuloP field = ecOps.getField();201IntegerFieldModuloP orderField = ecOps.getOrderField();202int length = (orderField.getSize().bitLength() + 7) / 8;203204byte[] r;205byte[] s;206207int encodeLength = sig.length / 2;208if (sig.length %2 != 0 || encodeLength > length) {209return false;210} else if (encodeLength == length) {211r = Arrays.copyOf(sig, length);212s = Arrays.copyOfRange(sig, length, length * 2);213} else {214r = new byte[length];215s = new byte[length];216System.arraycopy(sig, 0, r, length - encodeLength, encodeLength);217System.arraycopy(sig, encodeLength, s, length - encodeLength, encodeLength);218}219220ArrayUtil.reverse(r);221ArrayUtil.reverse(s);222IntegerModuloP ri = orderField.getElement(r);223IntegerModuloP si = orderField.getElement(s);224// z225int lengthE = Math.min(length, digest.length);226byte[] E = new byte[lengthE];227System.arraycopy(digest, 0, E, 0, lengthE);228ArrayUtil.reverse(E);229IntegerModuloP e = orderField.getElement(E);230231IntegerModuloP sInv = si.multiplicativeInverse();232ImmutableIntegerModuloP u1 = e.multiply(sInv);233ImmutableIntegerModuloP u2 = ri.multiply(sInv);234235AffinePoint pub = new AffinePoint(field.getElement(pp.getAffineX()),236field.getElement(pp.getAffineY()));237238byte[] temp1 = new byte[length];239b2a(u1, orderField, temp1);240241byte[] temp2 = new byte[length];242b2a(u2, orderField, temp2);243244MutablePoint p1 = ecOps.multiply(basePoint, temp1);245MutablePoint p2 = ecOps.multiply(pub, temp2);246247ecOps.setSum(p1, p2.asAffine());248IntegerModuloP result = p1.asAffine().getX();249result = result.additiveInverse().add(ri);250251b2a(result, orderField, temp1);252return ECOperations.allZero(temp1);253}254255public static ImmutableIntegerModuloP b2a(IntegerModuloP b,256IntegerFieldModuloP orderField, byte[] temp1) {257b.asByteArray(temp1);258ImmutableIntegerModuloP b2 = orderField.getElement(temp1);259b2.asByteArray(temp1);260return b2;261}262}263264265