/* * Copyright (c) 2020, 2023, 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.ec.ed; import sun.security.ec.ParametersMap; import sun.security.provider.SHAKE256; import sun.security.util.ObjectIdentifier; import sun.security.util.KnownOIDs; import sun.security.util.math.*; import sun.security.util.math.intpoly.*; import sun.security.x509.AlgorithmId; import java.io.IOException; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.*; import java.security.spec.*; import java.util.function.Function; /* * The set of parameters that defines an instance of the EdDSA signature * scheme. */ public class EdDSAParameters { public interface DigesterFactory { // Default digest creator Digester createDigester(); // Override this method if multiple key lengths are needed default Digester createDigester(int len) { return createDigester(); } // Return a digest over all the provided byte arrays default byte[] digest(byte[]... data) { Digester d = createDigester(); for (byte[] curData : data) { d.update(curData, 0, curData.length); } return d.digest(); } } // Hash for Ed25519 private static class SHA512DigesterFactory implements DigesterFactory { @Override public Digester createDigester() { try { MessageDigest md = MessageDigest.getInstance("SHA-512"); return new MessageDigester(md); } catch (NoSuchAlgorithmException ex) { throw new ProviderException(ex); } } } // Hash for Ed448 private static class SHAKE256DigesterFactory implements DigesterFactory { @Override // Most usage for Ed448 is 114bytes long public Digester createDigester() { return new SHAKE256Digester(114); } // Ed448 uses 64bytes long hash for the signature message @Override public Digester createDigester(int len) { return new SHAKE256Digester(len); } } public interface Digester { void update(byte data); void update(byte[] data, int off, int len); byte[] digest(); } private static class MessageDigester implements Digester { private final MessageDigest md; private MessageDigester(MessageDigest md) { this.md = md; } @Override public void update(byte data) { md.update(data); } @Override public void update(byte[] data, int off, int len) { md.update(data, off, len); } @Override public byte[] digest() { try { return md.digest(); } finally { md.reset(); } } } private static class SHAKE256Digester implements Digester { SHAKE256 md; SHAKE256Digester(int len) { md = new SHAKE256(len); } @Override public void update(byte data) { md.update(data); } @Override public void update(byte[] data, int off, int len) { md.update(data, off, len); } @Override public byte[] digest() { try { return md.digest(); } finally { md.reset(); } } } private static final ParametersMap namedParams = new ParametersMap<>(); private final String name; private final ObjectIdentifier oid; private final IntegerFieldModuloP field; private final IntegerFieldModuloP orderField; private final ImmutableIntegerModuloP d; private final EdECOperations edOperations; private final DigesterFactory digester; private final int keyLength; private final int bits; private final int logCofactor; private final Function dom; public EdDSAParameters(String name, ObjectIdentifier oid, IntegerFieldModuloP field, IntegerFieldModuloP orderField, ImmutableIntegerModuloP d, EdECOperations edOps, DigesterFactory digester, Function dom, int keyLength, int bits, int logCofactor) { this.oid = oid; this.name = name; this.field = field; this.orderField = orderField; this.d = d; this.edOperations = edOps; this.digester = digester; this.keyLength = keyLength; this.bits = bits; this.logCofactor = logCofactor; this.dom = dom; } public String getName() { return name; } public ObjectIdentifier getOid() { return oid; } public IntegerFieldModuloP getField() { return field; } public IntegerFieldModuloP getOrderField() { return orderField; } public ImmutableIntegerModuloP getD() { return d; } public EdECOperations getEdOperations() { return edOperations; } public int getKeyLength() { return keyLength; } public int getBits() { return bits; } public int getLogCofactor() { return logCofactor; } public Digester createDigester() { return digester.createDigester(); } public Digester createDigester(int len) { return digester.createDigester(len); } public byte[] digest(byte[]... data) { return digester.digest(data); } public byte[] dom(EdDSAParameterSpec sigParams) { return dom.apply(sigParams); } private static final String prefixStr25519 = "SigEd25519 no Ed25519 collisions"; private static final String prefixStr448 = "SigEd448"; // Used for Ed25519 static byte[] dom2(EdDSAParameterSpec sigParams) { if (!sigParams.isPrehash() && !sigParams.getContext().isPresent()) { return new byte[0]; } return domImpl(prefixStr25519, sigParams); } // Used for Ed488 static byte[] dom4(EdDSAParameterSpec sigParams) { return domImpl(prefixStr448, sigParams); } static byte[] domImpl(String prefixStr, EdDSAParameterSpec sigParams) { byte[] prefix = prefixStr.getBytes(StandardCharsets.US_ASCII); byte[] context = sigParams.getContext().orElse(new byte[0]); int length = prefix.length + 2 + context.length; byte[] result = new byte[length]; System.arraycopy(prefix, 0, result, 0, prefix.length); byte x = (byte) (sigParams.isPrehash() ? 1 : 0); result[prefix.length] = x; result[prefix.length + 1] = (byte) context.length; System.arraycopy(context, 0, result, prefix.length + 2, context.length); return result; } static { // set up Ed25519 IntegerFieldModuloP ed25519Field = IntegerPolynomial25519.ONE; IntegerFieldModuloP ed25519OrderField = Curve25519OrderField.ONE; BigInteger biD = new BigInteger("3709570593466943934313808350875" + "4565189542113879843219016388785533085940283555"); ImmutableIntegerModuloP d = ed25519Field.getElement(biD); BigInteger baseX = new BigInteger("15112221349535400772501151409" + "588531511454012693041857206046113283949847762202"); BigInteger baseY = new BigInteger("46316835694926478169428394003" + "475163141307993866256225615783033603165251855960"); EdECOperations edOps = new Ed25519Operations(d, baseX, baseY); String name = NamedParameterSpec.ED25519.getName(); ObjectIdentifier oid = ObjectIdentifier.of(KnownOIDs.Ed25519); int bits = 255; DigesterFactory digester = new SHA512DigesterFactory(); EdDSAParameters params = new EdDSAParameters(name, oid, ed25519Field, ed25519OrderField, d, edOps, digester, EdDSAParameters::dom2, 32, bits, 3); namedParams.put(name, oid, bits, params); // set up Ed448 IntegerFieldModuloP ed448Field = IntegerPolynomial448.ONE; IntegerFieldModuloP ed448OrderField = Curve448OrderField.ONE; biD = ed448Field.getSize().subtract(new BigInteger("39081")); d = ed448Field.getElement(biD); baseX = new BigInteger("224580040295924300187604334" + "099896036246789641632564134246125461686950415467406032909" + "029192869357953282578032075146446173674602635247710"); baseY = new BigInteger("298819210078481492676017930" + "443930673437544040154080242095928241372331506189835876003" + "536878655418784733982303233503462500531545062832660"); edOps = new Ed448Operations(d, baseX, baseY); name = NamedParameterSpec.ED448.getName(); oid = ObjectIdentifier.of(KnownOIDs.Ed448); bits = 448; digester = new SHAKE256DigesterFactory(); params = new EdDSAParameters(name, oid, ed448Field, ed448OrderField, d, edOps, digester, EdDSAParameters::dom4, 57, bits, 2); namedParams.put(name, oid, bits, params); namedParams.fix(); } public static EdDSAParameters getBySize(Function exception, int size) throws T { return namedParams.getBySize(exception, size); } public static EdDSAParameters get(Function exception, AlgorithmId algId) throws T { return namedParams.get(exception, algId); } public static EdDSAParameters get(Function exception, AlgorithmParameterSpec params) throws T { return namedParams.get(exception, params); } }