src/jdk.crypto.ec/share/classes/sun/security/ec/XDHKeyAgreement.java
changeset 50053 9bc1e6487cbb
child 51086 57c152eb3198
child 56589 bafd8be2f970
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/XDHKeyAgreement.java	Tue May 08 09:47:28 2018 -0400
@@ -0,0 +1,216 @@
+/*
+ * 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
+        XECParameters xecParams = XECParameters.get(
+            InvalidAlgorithmParameterException::new, params);
+        if (!xecParams.oidEquals(this.ops.getParameters())) {
+            throw new InvalidKeyException(
+                "Incorrect private key parameters"
+            );
+        }
+    }
+
+    private
+    <T extends Throwable>
+    void checkLockedParams(Function<String, T> 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");
+        }
+
+        return secret.clone();
+    }
+
+    @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);
+        return secret.length;
+    }
+
+    @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);
+        }
+    }
+}