/* MyEccSharedSecretCallback.java
 *
 * Copyright (C) 2006-2025 wolfSSL Inc.
 *
 * This file is part of wolfSSL.
 *
 * wolfSSL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * wolfSSL 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
 */

import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import com.wolfssl.*;

import java.security.KeyFactory;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.ECPrivateKey;
import java.security.spec.KeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import javax.crypto.KeyAgreement;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.util.Arrays;

/*
 * Example ECC shared secret callback implementation
 * This example uses the Java JCE KeyAgreement and ECC key classes
 * to generate a shared secret.
 */
class MyEccSharedSecretCallback implements WolfSSLEccSharedSecretCallback
{
    public int eccSharedSecretCallback(WolfSSLSession ssl,
            WolfCryptEccKey otherKey, ByteBuffer pubKeyDer, long[] pubKeyDerSz,
            ByteBuffer out, long[] outSz, int side, Object ctx) {

        int ret = -1;
        ECPublicKey ecPubKey = null;
        ECPrivateKey ecPrivKey = null;
        byte[] secret = null;

        System.out.println("------- Entered MyEccSharedSecretCallback -------");

        if (ssl == null || otherKey == null) {
            System.out.println("Bad arguments, ssl or otherKey object is null");
            return -1;
        }

        MyEccSharedSecretCtx eccSharedSecretCtx = (MyEccSharedSecretCtx)ctx;

        try {

            KeyFactory kf = KeyFactory.getInstance("EC");

            if (side == WolfSSL.WOLFSSL_CLIENT_END) {

                /* otherKey holds server's public key */
                System.out.println("side = WOLFSSL_CLIENT_END");

                KeySpec pubSpec =
                    new X509EncodedKeySpec(otherKey.getPublicKeyDer());
                ecPubKey = (ECPublicKey)kf.generatePublic(pubSpec);
                System.out.println("EC params: " + ecPubKey.getParams());

                /* Create key pair, export public part to pubKeyDer */
                KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
                kpg.initialize(ecPubKey.getParams());

                KeyPair kp = kpg.generateKeyPair();

                ecPrivKey = (ECPrivateKey)kp.getPrivate();
                ECPublicKey ecPubKeyOut = (ECPublicKey)kp.getPublic();

                /* strip off header, wolfSSL needs raw ECC public key */
                byte[] ecPubKeyEncoded = ecPubKeyOut.getEncoded();
                ecPubKeyEncoded = Arrays.copyOfRange(ecPubKeyEncoded, 26,
                                                     ecPubKeyEncoded.length);

                if (ecPubKeyEncoded.length > pubKeyDer.capacity()) {
                    System.out.println("ERROR: ECC public key is too long " +
                                       "for DER buffer");
                    return -1;
                }

                pubKeyDer.clear();
                pubKeyDer.put(ecPubKeyEncoded);
                pubKeyDerSz[0] = ecPubKeyEncoded.length;

            } else {

                System.out.println("... side = WOLFSSL_SERVER_END");

                /* otherKey holds server private key */
                KeySpec privSpec =
                    new PKCS8EncodedKeySpec(otherKey.getPrivateKeyPKCS8());
                ecPrivKey = (ECPrivateKey)kf.generatePrivate(privSpec);

                /* Import public key from pubKeyDer */
                byte[] pubKeyTmp = new byte[pubKeyDer.capacity()];
                pubKeyDer.get(pubKeyTmp);

                X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubKeyTmp);
                ecPubKey = (ECPublicKey)kf.generatePublic(pubSpec);
            }

            /* generate shared secret */
            KeyAgreement keyAgree = KeyAgreement.getInstance("ECDH");
            keyAgree.init(ecPrivKey);
            keyAgree.doPhase(ecPubKey, true);

            secret = keyAgree.generateSecret();

            if (secret.length > out.capacity()) {
                System.out.println("ERROR: ECC shared secret length too long " +
                                   "for output buffer");
                return -1;
            }

            out.clear();
            out.put(secret);
            outSz[0] = secret.length;

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e2) {
            e2.printStackTrace();
        } catch (InvalidAlgorithmParameterException e3) {
            e3.printStackTrace();
        } catch (InvalidKeyException e4) {
            e4.printStackTrace();
        }

        System.out.println("------- Leaving MyEccSharedSecretCallback -------");

        return 0;
    }
}

