| /* | |
| * 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.point.AffinePoint; | |
| import java.io.ByteArrayOutputStream; | |
| import java.security.AlgorithmParameters; | |
| import java.security.InvalidAlgorithmParameterException; | |
| import java.security.InvalidKeyException; | |
| import java.security.InvalidParameterException; | |
| import java.security.NoSuchAlgorithmException; | |
| import java.security.PrivateKey; | |
| import java.security.ProviderException; | |
| import java.security.PublicKey; | |
| import java.security.SecureRandom; | |
| import java.security.SignatureException; | |
| import java.security.SignatureSpi; | |
| import java.security.interfaces.EdECPrivateKey; | |
| import java.security.interfaces.EdECPublicKey; | |
| import java.security.spec.AlgorithmParameterSpec; | |
| import java.security.spec.EdDSAParameterSpec; | |
| import java.security.spec.NamedParameterSpec; | |
| import java.util.function.Function; | |
| public class EdDSASignature extends SignatureSpi { | |
| private interface MessageAccumulator { | |
| void add(byte b); | |
| void add(byte[] data, int off, int len); | |
| byte[] getMessage(); | |
| } | |
| private static class DigestAccumulator implements MessageAccumulator { | |
| private final EdDSAParameters.Digester digester; | |
| DigestAccumulator(EdDSAParameters.Digester digester) { | |
| this.digester = digester; | |
| } | |
| @Override | |
| public void add(byte b) { | |
| digester.update(b); | |
| } | |
| @Override | |
| public void add(byte[] data, int off, int len) { | |
| digester.update(data, off, len); | |
| } | |
| @Override | |
| public byte[] getMessage() { | |
| return digester.digest(); | |
| } | |
| } | |
| private static class MemoryAccumulator implements MessageAccumulator { | |
| ByteArrayOutputStream message = new ByteArrayOutputStream(); | |
| @Override | |
| public void add(byte b) { | |
| message.write(b); | |
| } | |
| @Override | |
| public void add(byte[] data, int off, int len) { | |
| message.write(data, off, len); | |
| } | |
| @Override | |
| public byte[] getMessage() { | |
| return message.toByteArray(); | |
| } | |
| } | |
| private byte[] privateKey; | |
| private AffinePoint publicKeyPoint; | |
| private byte[] publicKeyBytes; | |
| private EdDSAOperations ops; | |
| private EdDSAParameters lockedParams = null; | |
| private MessageAccumulator message = null; | |
| private EdDSAParameterSpec sigParams = new EdDSAParameterSpec(false); | |
| public EdDSASignature() { | |
| // do nothing | |
| } | |
| EdDSASignature(NamedParameterSpec paramSpec) { | |
| lockedParams = EdDSAParameters.get(ProviderException::new, paramSpec); | |
| } | |
| @Override | |
| protected void engineInitVerify(PublicKey publicKey) | |
| throws InvalidKeyException { | |
| if (!(publicKey instanceof EdECPublicKey)) { | |
| throw new InvalidKeyException("Unsupported key type"); | |
| } | |
| EdECPublicKey edKey = (EdECPublicKey) publicKey; | |
| EdDSAParameters params = EdDSAParameters.get( | |
| InvalidKeyException::new, edKey.getParams()); | |
| initImpl(params); | |
| this.privateKey = null; | |
| this.publicKeyPoint = ops.decodeAffinePoint(InvalidKeyException::new, | |
| edKey.getPoint()); | |
| EdDSAPublicKeyImpl pubKeyImpl = new EdDSAPublicKeyImpl(params, | |
| edKey.getPoint()); | |
| this.publicKeyBytes = pubKeyImpl.getEncodedPoint(); | |
| } | |
| @Override | |
| protected void engineInitSign(PrivateKey privateKey) | |
| throws InvalidKeyException { | |
| engineInitSign(privateKey, null); | |
| } | |
| @Override | |
| protected void engineInitSign(PrivateKey privateKey, SecureRandom random) | |
| throws InvalidKeyException { | |
| if (!(privateKey instanceof EdECPrivateKey)) { | |
| throw new InvalidKeyException("Unsupported key type"); | |
| } | |
| EdECPrivateKey edKey = (EdECPrivateKey) privateKey; | |
| initImpl(edKey.getParams()); | |
| this.privateKey = edKey.getBytes().orElseThrow( | |
| () -> new InvalidKeyException("No private key value")); | |
| this.publicKeyPoint = null; | |
| this.publicKeyBytes = null; | |
| } | |
| private | |
| <T extends Throwable> | |
| void checkLockedParams(Function<String, T> exception, | |
| EdDSAParameters params) throws T { | |
| if (lockedParams != null && lockedParams != params) { | |
| throw exception.apply("Parameters must be " + | |
| lockedParams.getName()); | |
| } | |
| } | |
| private void ensureMessageInit() throws SignatureException { | |
| if (message == null) { | |
| initMessage(); | |
| } | |
| } | |
| private void initMessage() throws SignatureException { | |
| if (this.ops == null) { | |
| throw new SignatureException("not initialized"); | |
| } | |
| EdDSAParameters params = ops.getParameters(); | |
| if (sigParams.isPrehash()) { | |
| this.message = new DigestAccumulator(params.createDigester(64)); | |
| } else { | |
| this.message = new MemoryAccumulator(); | |
| } | |
| } | |
| @Override | |
| protected void engineUpdate(byte b) throws SignatureException { | |
| ensureMessageInit(); | |
| this.message.add(b); | |
| } | |
| @Override | |
| protected void engineUpdate(byte[] b, int off, int len) | |
| throws SignatureException { | |
| ensureMessageInit(); | |
| this.message.add(b, off, len); | |
| } | |
| @Override | |
| protected byte[] engineSign() throws SignatureException { | |
| if (privateKey == null) { | |
| throw new SignatureException("Missing private key"); | |
| } | |
| ensureMessageInit(); | |
| byte[] result = ops.sign(this.sigParams, this.privateKey, | |
| message.getMessage()); | |
| message = null; | |
| return result; | |
| } | |
| @Override | |
| protected boolean engineVerify(byte[] sigBytes) throws SignatureException { | |
| if (publicKeyBytes == null) { | |
| throw new SignatureException("Missing publicKey"); | |
| } | |
| ensureMessageInit(); | |
| boolean result = ops.verify(this.sigParams, this.publicKeyPoint, | |
| this.publicKeyBytes, message.getMessage(), sigBytes); | |
| message = null; | |
| return result; | |
| } | |
| private void initImpl(EdDSAParameters params) throws InvalidKeyException { | |
| checkLockedParams(InvalidKeyException::new, params); | |
| try { | |
| this.ops = new EdDSAOperations(params); | |
| } catch (NoSuchAlgorithmException ex) { | |
| throw new ProviderException(ex); | |
| } | |
| // message is (re)set to null | |
| // it will be initialized on first update | |
| this.message = null; | |
| } | |
| private void initImpl(NamedParameterSpec paramSpec) | |
| throws InvalidKeyException { | |
| EdDSAParameters params = EdDSAParameters.get( | |
| InvalidKeyException::new, paramSpec); | |
| initImpl(params); | |
| } | |
| @Deprecated | |
| @Override | |
| protected Object engineGetParameter(String param) | |
| throws InvalidParameterException { | |
| throw new UnsupportedOperationException("getParameter() not supported"); | |
| } | |
| @Deprecated | |
| @Override | |
| protected void engineSetParameter(String param, Object value) | |
| throws InvalidParameterException { | |
| throw new UnsupportedOperationException("setParameter() not supported"); | |
| } | |
| @Override | |
| protected void engineSetParameter(AlgorithmParameterSpec params) | |
| throws InvalidAlgorithmParameterException { | |
| // by convention, ignore null parameters | |
| if (params == null) { | |
| return; | |
| } | |
| if (params instanceof EdDSAParameterSpec) { | |
| if (message != null) { | |
| // sign/verify in progress | |
| throw new InvalidParameterException("Cannot change signature " + | |
| "parameters during operation"); | |
| } | |
| EdDSAParameterSpec edDsaParams = (EdDSAParameterSpec) params; | |
| checkContextLength(edDsaParams); | |
| this.sigParams = edDsaParams; | |
| } else { | |
| throw new InvalidAlgorithmParameterException( | |
| "Only EdDSAParameterSpec supported"); | |
| } | |
| } | |
| private static void checkContextLength(EdDSAParameterSpec edDsaParams) | |
| throws InvalidAlgorithmParameterException { | |
| if (edDsaParams.getContext().isPresent()) { | |
| byte[] context = edDsaParams.getContext().get(); | |
| if (context.length > 255) { | |
| throw new InvalidAlgorithmParameterException( | |
| "Context is longer than 255 bytes"); | |
| } | |
| } | |
| } | |
| // There is no RFC-defined ASN.1 for prehash and context (RFC 8410) | |
| @Override | |
| protected AlgorithmParameters engineGetParameters() { | |
| return null; | |
| } | |
| public static class Ed25519 extends EdDSASignature { | |
| public Ed25519() { | |
| super(NamedParameterSpec.ED25519); | |
| } | |
| } | |
| public static class Ed448 extends EdDSASignature { | |
| public Ed448() { | |
| super(NamedParameterSpec.ED448); | |
| } | |
| } | |
| } |