/* * Copyright (c) 2018, 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; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.Key; import java.security.SecureRandom; import java.security.ProviderException; import java.security.interfaces.XECPrivateKey; import java.security.interfaces.XECPublicKey; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.NamedParameterSpec; import javax.crypto.KeyAgreementSpi; import javax.crypto.SecretKey; import javax.crypto.ShortBufferException; import java.util.function.Function; public class XDHKeyAgreement extends KeyAgreementSpi { private byte[] privateKey; private byte[] secret; private XECOperations ops; private XECParameters lockedParams = null; XDHKeyAgreement() { // do nothing } XDHKeyAgreement(AlgorithmParameterSpec paramSpec) { lockedParams = XECParameters.get(ProviderException::new, paramSpec); } @Override protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException { initImpl(key); } @Override protected void engineInit(Key key, final AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { initImpl(key); // the private key parameters must match params, if present if (params != null) { XECParameters xecParams = XECParameters.get( InvalidAlgorithmParameterException::new, params); if (!xecParams.oidEquals(this.ops.getParameters())) { throw new InvalidKeyException( "Incorrect private key parameters" ); } } } private void checkLockedParams(Function exception, XECParameters params) throws T { if (lockedParams != null && lockedParams != params) { throw exception.apply("Parameters must be " + lockedParams.getName()); } } private void initImpl(Key key) throws InvalidKeyException { if (!(key instanceof XECPrivateKey)) { throw new InvalidKeyException ("Unsupported key type"); } XECPrivateKey privateKey = (XECPrivateKey) key; XECParameters xecParams = XECParameters.get( InvalidKeyException::new, privateKey.getParams()); checkLockedParams(InvalidKeyException::new, xecParams); this.ops = new XECOperations(xecParams); this.privateKey = privateKey.getScalar().orElseThrow( () -> new InvalidKeyException("No private key value") ); secret = null; } @Override protected Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException, IllegalStateException { if (this.privateKey == null) { throw new IllegalStateException("Not initialized"); } if (this.secret != null) { throw new IllegalStateException("Phase already executed"); } if (!lastPhase) { throw new IllegalStateException ("Only two party agreement supported, lastPhase must be true"); } if (!(key instanceof XECPublicKey)) { throw new InvalidKeyException ("Unsupported key type"); } XECPublicKey publicKey = (XECPublicKey) key; // Ensure public key parameters are compatible with private key XECParameters xecParams = XECParameters.get(InvalidKeyException::new, publicKey.getParams()); if (!ops.getParameters().oidEquals(xecParams)) { throw new InvalidKeyException( "Public key parameters are not compatible with private key."); } // The privateKey may be modified to a value that is equivalent for // the purposes of this algorithm. byte[] computedSecret = ops.encodedPointMultiply( this.privateKey, publicKey.getU()); // test for contributory behavior if (allZero(computedSecret)) { throw new InvalidKeyException("Point has small order"); } this.secret = computedSecret; return null; } /* * Constant-time check for an all-zero array */ private boolean allZero(byte[] arr) { byte orValue = (byte) 0; for (int i = 0; i < arr.length; i++) { orValue |= arr[i]; } return orValue == (byte) 0; } @Override protected byte[] engineGenerateSecret() throws IllegalStateException { if (secret == null) { throw new IllegalStateException("Not initialized correctly"); } byte[] result = secret; secret = null; return result; } @Override protected int engineGenerateSecret(byte[] sharedSecret, int offset) throws IllegalStateException, ShortBufferException { if (secret == null) { throw new IllegalStateException("Not initialized correctly"); } int secretLen = this.secret.length; if (offset + secretLen > sharedSecret.length) { throw new ShortBufferException("Need " + secretLen + " bytes, only " + (sharedSecret.length - offset) + " available"); } System.arraycopy(this.secret, 0, sharedSecret, offset, secretLen); secret = null; return secretLen; } @Override protected SecretKey engineGenerateSecret(String algorithm) throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException { throw new NoSuchAlgorithmException("Not supported"); } static class X25519 extends XDHKeyAgreement { public X25519() { super(NamedParameterSpec.X25519); } } static class X448 extends XDHKeyAgreement { public X448() { super(NamedParameterSpec.X448); } } }