Path: blob/master/src/jdk.crypto.ec/share/classes/sun/security/ec/ed/EdDSAOperations.java
41162 views
/*1* Copyright (c) 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*/24package sun.security.ec.ed;2526import sun.security.ec.point.AffinePoint;27import sun.security.ec.point.Point;28import sun.security.util.ArrayUtil;29import sun.security.util.math.IntegerFieldModuloP;30import sun.security.util.math.IntegerModuloP;31import sun.security.util.math.MutableIntegerModuloP;3233import java.math.BigInteger;34import java.security.InvalidKeyException;35import java.security.NoSuchAlgorithmException;36import java.security.SecureRandom;37import java.security.SignatureException;38import java.security.spec.EdDSAParameterSpec;39import java.security.spec.EdECPoint;40import java.util.Arrays;41import java.util.function.Function;4243/*44* A class containing the operations of the EdDSA signature scheme. The45* parameters include an object that performs the elliptic curve point46* arithmetic, and EdDSAOperations uses this object to construct the signing47* and verification operations.48*/49public class EdDSAOperations {5051private final EdDSAParameters params;5253public EdDSAOperations(EdDSAParameters params)54throws NoSuchAlgorithmException {5556this.params = params;57}5859public EdDSAParameters getParameters() {60return params;61}6263public byte[] generatePrivate(SecureRandom random) {64byte[] result = new byte[params.getKeyLength()];65random.nextBytes(result);66return result;67}6869public EdECPoint computePublic(byte[] privateKey) {70byte[] privateKeyHash = params.digest(privateKey);71int byteLength = privateKeyHash.length / 2;72byte[] s = Arrays.copyOf(privateKeyHash, byteLength);73prune(s);74IntegerModuloP fieldS = params.getOrderField().getElement(s);75fieldS.asByteArray(s);76Point A = params.getEdOperations().basePointMultiply(s);77return asEdECPoint(A.asAffine());78}7980private static EdECPoint asEdECPoint(AffinePoint p) {81return new EdECPoint(p.getX().asBigInteger().testBit(0),82p.getY().asBigInteger());83}8485public byte[] sign(EdDSAParameterSpec sigParams, byte[] privateKey,86byte[] message) {8788byte[] privateKeyHash = params.digest(privateKey);8990int byteLength = privateKeyHash.length / 2;91byte[] s = Arrays.copyOf(privateKeyHash, byteLength);92prune(s);93IntegerModuloP sElem = params.getOrderField().getElement(s);94sElem.asByteArray(s);95Point A = params.getEdOperations().basePointMultiply(s);96byte[] prefix = Arrays.copyOfRange(privateKeyHash,97privateKeyHash.length / 2, privateKeyHash.length);98byte[] dom = params.dom(sigParams);99byte[] r = params.digest(dom, prefix, message);100101// reduce r modulo the order102IntegerModuloP fieldR = params.getOrderField().getElement(r);103r = new byte[params.getKeyLength()];104fieldR.asByteArray(r);105106Point R = params.getEdOperations().basePointMultiply(r);107108byte[] encodedR = encode(byteLength, R);109byte[] encodedA = encode(byteLength, A);110byte[] k = params.digest(dom, encodedR, encodedA, message);111112// S computation is in group-order field113IntegerFieldModuloP subField = params.getOrderField();114IntegerModuloP kElem = subField.getElement(k);115IntegerModuloP rElem = subField.getElement(r);116MutableIntegerModuloP S = kElem.mutable().setProduct(sElem);117S.setSum(rElem);118// need to be reduced before output conversion119S.setReduced();120byte[] sArr = S.asByteArray(byteLength);121byte[] rArr = encode(byteLength, R);122123byte[] result = new byte[byteLength * 2];124System.arraycopy(rArr, 0, result, 0, byteLength);125System.arraycopy(sArr, 0, result, byteLength, byteLength);126return result;127}128129public boolean verify(EdDSAParameterSpec sigParams, AffinePoint affineA,130byte[] publicKey, byte[] message, byte[] signature)131throws SignatureException {132133if (signature == null) {134throw new SignatureException("signature was null");135}136byte[] encR = Arrays.copyOf(signature, signature.length / 2);137byte[] encS = Arrays.copyOfRange(signature, signature.length / 2,138signature.length);139140// reject s if it is too large141ArrayUtil.reverse(encS);142BigInteger bigS = new BigInteger(1, encS);143if (bigS.compareTo(params.getOrderField().getSize()) >= 0) {144throw new SignatureException("s is too large");145}146ArrayUtil.reverse(encS);147148byte[] dom = params.dom(sigParams);149AffinePoint affineR = decodeAffinePoint(SignatureException::new, encR);150byte[] k = params.digest(dom, encR, publicKey, message);151// reduce k to improve performance of multiply152IntegerFieldModuloP subField = params.getOrderField();153IntegerModuloP kElem = subField.getElement(k);154k = kElem.asByteArray(k.length / 2);155156Point pointR = params.getEdOperations().of(affineR);157Point pointA = params.getEdOperations().of(affineA);158159EdECOperations edOps = params.getEdOperations();160Point lhs = edOps.basePointMultiply(encS);161Point rhs = edOps.setSum(edOps.setProduct(pointA.mutable(), k),162pointR.mutable());163164return lhs.affineEquals(rhs);165}166167public boolean verify(EdDSAParameterSpec sigParams, byte[] publicKey,168byte[] message, byte[] signature)169throws InvalidKeyException, SignatureException {170171AffinePoint affineA = decodeAffinePoint(InvalidKeyException::new,172publicKey);173return verify(sigParams, affineA, publicKey, message, signature);174}175176public177<T extends Throwable>178AffinePoint decodeAffinePoint(Function<String, T> exception, byte[] arr)179throws T {180181if (arr.length != params.getKeyLength()) {182throw exception.apply("incorrect length");183}184185arr = arr.clone();186int xLSB = (0xFF & arr[arr.length - 1]) >>> 7;187arr[arr.length - 1] &= 0x7F;188int yLength = (params.getBits() + 7) >> 3;189IntegerModuloP y =190params.getField().getElement(arr, 0, yLength, (byte) 0);191// reject non-canonical y values192ArrayUtil.reverse(arr);193BigInteger bigY = new BigInteger(1, arr);194if (bigY.compareTo(params.getField().getSize()) >= 0) {195throw exception.apply("y value is too large");196}197return params.getEdOperations().decodeAffinePoint(exception, xLSB, y);198}199200public201<T extends Throwable>202AffinePoint decodeAffinePoint(Function<String, T> exception,203EdECPoint point)204throws T {205206// reject non-canonical y values207if (point.getY().compareTo(params.getField().getSize()) >= 0) {208throw exception.apply("y value is too large");209}210211int xLSB = point.isXOdd() ? 1 : 0;212IntegerModuloP y = params.getField().getElement(point.getY());213return params.getEdOperations().decodeAffinePoint(exception, xLSB, y);214}215216/**217* Mask off the high order bits of an encoded integer in an array. The218* array is modified in place.219*220* @param arr an array containing an encoded integer221* @param bits the number of bits to keep222* @return the number, in range [0,8], of bits kept in the highest byte223*/224private static int maskHighOrder(byte[] arr, int bits) {225226int lastByteIndex = arr.length - 1;227int bitsDiff = arr.length * 8 - bits;228int highBits = 8 - bitsDiff;229byte msbMaskOff = (byte) ((1 << highBits) - 1);230arr[lastByteIndex] &= msbMaskOff;231232return highBits;233}234235/**236* Prune an encoded scalar value by modifying it in place. The extra237* high-order bits are masked off, the highest valid bit it set, and the238* number is rounded down to a multiple of the co-factor.239*240* @param k an encoded scalar value241* @param bits the number of bits in the scalar242* @param logCofactor the base-2 logarithm of the co-factor243*/244private static void prune(byte[] k, int bits, int logCofactor) {245246int lastByteIndex = k.length - 1;247248// mask off unused high-order bits249int highBits = maskHighOrder(k, bits);250251// set the highest bit252if (highBits == 0) {253k[lastByteIndex - 1] |= 0x80;254} else {255byte msbMaskOn = (byte) (1 << (highBits - 1));256k[lastByteIndex] |= msbMaskOn;257}258259// round down to a multiple of the co-factor260byte lsbMaskOff = (byte) (0xFF << logCofactor);261k[0] &= lsbMaskOff;262}263264void prune(byte[] arr) {265prune(arr, params.getBits(), params.getLogCofactor());266}267268private static byte[] encode(int length, Point p) {269return encode(length, p.asAffine());270}271272private static byte[] encode(int length, AffinePoint p) {273byte[] result = p.getY().asByteArray(length);274int xLSB = p.getX().asByteArray(1)[0] & 0x01;275result[result.length - 1] |= (xLSB << 7);276return result;277}278}279280281