JcaKeyPair.java

/*******************************************************************************
 * GordianKnot: Security Suite
 * Copyright 2012,2025 Tony Washer
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy
 * of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 ******************************************************************************/
package net.sourceforge.joceanus.gordianknot.impl.jca;

import net.sourceforge.joceanus.gordianknot.api.base.GordianException;
import net.sourceforge.joceanus.gordianknot.api.keypair.GordianKeyPair;
import net.sourceforge.joceanus.gordianknot.api.keypair.GordianKeyPairSpec;
import net.sourceforge.joceanus.gordianknot.api.keypair.GordianStateAwareKeyPair;
import net.sourceforge.joceanus.gordianknot.impl.core.exc.GordianDataException;
import net.sourceforge.joceanus.gordianknot.impl.core.keypair.GordianCoreKeyPair;
import net.sourceforge.joceanus.gordianknot.impl.core.keypair.GordianPrivateKey;
import net.sourceforge.joceanus.gordianknot.impl.core.keypair.GordianPrivateKey.GordianStateAwarePrivateKey;
import net.sourceforge.joceanus.gordianknot.impl.core.keypair.GordianPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.dh.BCDHPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.dh.BCDHPublicKey;
import org.bouncycastle.jcajce.spec.DHDomainParameterSpec;
import org.bouncycastle.pqc.jcajce.interfaces.LMSPrivateKey;
import org.bouncycastle.pqc.jcajce.interfaces.XMSSMTPrivateKey;
import org.bouncycastle.pqc.jcajce.interfaces.XMSSPrivateKey;

import javax.crypto.spec.DHParameterSpec;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Objects;

/**
 * BouncyCastle Asymmetric KeyPair.
 */
public class JcaKeyPair
        extends GordianCoreKeyPair {
    /**
     * Constructor.
     * @param pPublic the public key
     */
    protected JcaKeyPair(final JcaPublicKey pPublic) {
        this(pPublic, null);
    }

    /**
     * Constructor.
     * @param pPublic the public key
     * @param pPrivate the private key
     */
    protected JcaKeyPair(final JcaPublicKey pPublic,
                         final JcaPrivateKey pPrivate) {
        super(pPublic, pPrivate);
    }

    @Override
    public JcaPublicKey getPublicKey() {
        return (JcaPublicKey) super.getPublicKey();
    }

    @Override
    public JcaPrivateKey getPrivateKey() {
        return (JcaPrivateKey) super.getPrivateKey();
    }

    @Override
    public JcaKeyPair getPublicOnly() {
        return new JcaKeyPair(getPublicKey());
    }

    /**
     * Check for jcaKeyPair.
     * @param pKeyPair the keyPair to check
     * @throws GordianException on error
     */
    public static void checkKeyPair(final GordianKeyPair pKeyPair) throws GordianException {
        /* Check that it is a JcaKeyPair */
        if (!(pKeyPair instanceof JcaKeyPair)) {
            /* Reject keyPair */
            throw new GordianDataException("Invalid KeyPair");
        }
    }

    /**
     * Check for jcaKeyPair.
     * @param pKeyPair the keyPair to check
     * @param pSpec the required keySpec
     * @throws GordianException on error
     */
    public static void checkKeyPair(final GordianKeyPair pKeyPair,
                                    final GordianKeyPairSpec pSpec) throws GordianException {
        /* Check the keyPair */
        checkKeyPair(pKeyPair);

        /* Check that it the correct key type */
        if (!pSpec.equals(pKeyPair.getKeyPairSpec())) {
            /* Reject keyPair */
            throw new GordianDataException("Invalid KeyPairType");
        }
    }

    /**
     * Jca PublicKey.
     */
    public static class JcaPublicKey
            extends GordianPublicKey {
        /**
         * Public Key details.
         */
        private final PublicKey theKey;

        /**
         * Constructor.
         * @param pKeySpec the keySpec
         * @param pPublicKey the public key
         */
        protected JcaPublicKey(final GordianKeyPairSpec pKeySpec,
                               final PublicKey pPublicKey) {
            super(pKeySpec);
            theKey = pPublicKey;
        }

        /**
         * Obtain the public key.
         * @return the key
         */
        protected PublicKey getPublicKey() {
            return theKey;
        }

        @Override
        public boolean equals(final Object pThat) {
            /* Handle the trivial cases */
            if (pThat == this) {
                return true;
            }
            if (pThat == null) {
                return false;
            }

            /* Make sure that the object is the same class */
            if (!(pThat instanceof JcaPublicKey)) {
                return false;
            }

            /* Access the target field */
            final JcaPublicKey myThat = (JcaPublicKey) pThat;

            /* Check differences */
            return getKeySpec().equals(myThat.getKeySpec())
                    && theKey.equals(myThat.getPublicKey());
        }

        @Override
        public int hashCode() {
            return Objects.hash(getKeySpec(), theKey);
        }
    }

    /**
     * Jca PrivateKey.
     */
    public static class JcaPrivateKey
            extends GordianPrivateKey {
        /**
         * Private Key details.
         */
        private final PrivateKey theKey;

        /**
         * Constructor.
         * @param pKeySpec the keySpec
         * @param pPrivateKey the private key
         */
        protected JcaPrivateKey(final GordianKeyPairSpec pKeySpec,
                                final PrivateKey pPrivateKey) {
            super(pKeySpec);
            theKey = pPrivateKey;
        }

        /**
         * Obtain the private key.
         * @return the key
         */
        protected PrivateKey getPrivateKey() {
            return theKey;
        }

        @Override
        public boolean equals(final Object pThat) {
            /* Handle the trivial cases */
            if (pThat == this) {
                return true;
            }
            if (pThat == null) {
                return false;
            }

            /* Make sure that the object is the same class */
            if (!(pThat instanceof JcaPrivateKey)) {
                return false;
            }

            /* Access the target field */
            final JcaPrivateKey myThat = (JcaPrivateKey) pThat;

            /* Check differences */
            return getKeySpec().equals(myThat.getKeySpec())
                    && theKey.equals(myThat.getPrivateKey());
        }

        @Override
        public int hashCode() {
            return Objects.hash(getKeySpec(), theKey);
        }
    }

    /**
     * Jca StateAware PrivateKey.
     */
    public static class JcaStateAwarePrivateKey
            extends JcaPrivateKey
            implements GordianStateAwarePrivateKey {
        /**
         * The private key.
         */
        private final PrivateKey thePrivateKey;

        /**
         * Constructor.
         * @param pKeySpec the key spec
         * @param pKey the key
         */
        JcaStateAwarePrivateKey(final GordianKeyPairSpec pKeySpec,
                                final PrivateKey pKey) {
            super(pKeySpec, pKey);
            thePrivateKey = pKey;
        }

        @Override
        public PrivateKey getPrivateKey() {
            return thePrivateKey;
        }

        @Override
        public long getUsagesRemaining() {
            if (thePrivateKey instanceof LMSPrivateKey) {
                return ((LMSPrivateKey) getPrivateKey()).getUsagesRemaining();
            }
            if (thePrivateKey instanceof XMSSMTPrivateKey) {
                return ((XMSSMTPrivateKey) getPrivateKey()).getUsagesRemaining();
            }
            return thePrivateKey instanceof XMSSPrivateKey
                    ? ((XMSSPrivateKey) getPrivateKey()).getUsagesRemaining()
                    : 0;
        }

        @Override
        public JcaStateAwarePrivateKey getKeyShard(final int pNumUsages) {
            if (thePrivateKey instanceof LMSPrivateKey) {
                return new JcaStateAwarePrivateKey(getKeySpec(), ((LMSPrivateKey) getPrivateKey()).extractKeyShard(pNumUsages));
            }
            if (thePrivateKey instanceof XMSSMTPrivateKey) {
                return new JcaStateAwarePrivateKey(getKeySpec(), ((XMSSMTPrivateKey) getPrivateKey()).extractKeyShard(pNumUsages));
            }
            return thePrivateKey instanceof XMSSPrivateKey
                   ? new JcaStateAwarePrivateKey(getKeySpec(), ((XMSSPrivateKey) getPrivateKey()).extractKeyShard(pNumUsages))
                   : null;
        }

        @Override
        public boolean equals(final Object pThat) {
            /* Handle the trivial cases */
            if (pThat == this) {
                return true;
            }
            if (pThat == null) {
                return false;
            }

            /* Make sure that the object is the same class */
            if (!(pThat instanceof JcaStateAwarePrivateKey)) {
                return false;
            }

            /* Access the target field */
            final JcaStateAwarePrivateKey myThat = (JcaStateAwarePrivateKey) pThat;

            /* Check differences */
            return getKeySpec().equals(myThat.getKeySpec())
                    && thePrivateKey.equals(myThat.getPrivateKey());
        }

        @Override
        public int hashCode() {
            return Objects.hash(getKeySpec(), thePrivateKey);
        }
    }

    /**
     * Jca DH PublicKey.
     */
    public static class JcaDHPublicKey
            extends JcaPublicKey {
        /**
         * Public Key details.
         */
        private final BCDHPublicKey theKey;

        /**
         * Constructor.
         * @param pKeySpec the keySpec
         * @param pPublicKey the public key
         */
        protected JcaDHPublicKey(final GordianKeyPairSpec pKeySpec,
                                 final BCDHPublicKey pPublicKey) {
            super(pKeySpec, pPublicKey);
            theKey = pPublicKey;
        }

        @Override
        protected BCDHPublicKey getPublicKey() {
            return theKey;
        }

        @Override
        public boolean equals(final Object pThat) {
            /* Handle the trivial cases */
            if (pThat == this) {
                return true;
            }
            if (pThat == null) {
                return false;
            }

            /* Make sure that the object is the same class */
            if (!(pThat instanceof JcaDHPublicKey)) {
                return false;
            }

            /* Access the target field */
            final JcaDHPublicKey myThat = (JcaDHPublicKey) pThat;

            /* Check differences */
            return getKeySpec().equals(myThat.getKeySpec())
                    && theKey.getY().equals(myThat.getPublicKey().getY())
                    && dhParamsAreEqual(theKey.getParams(), myThat.getPublicKey().getParams());
        }

        @Override
        public int hashCode() {
            return Objects.hash(getKeySpec(), theKey);
        }
    }

    /**
     * check DH Parameters are equal (ignoring L!!).
     * @param pFirst the first parameters
     * @param pSecond the second parameters
     * @return true/false
     */
    private static boolean dhParamsAreEqual(final DHParameterSpec pFirst,
                                            final DHParameterSpec pSecond) {
        final DHDomainParameterSpec myFirst = (DHDomainParameterSpec) pFirst;
        final DHDomainParameterSpec mySecond = (DHDomainParameterSpec) pSecond;
        return myFirst.getP().equals(mySecond.getP())
                && myFirst.getG().equals(mySecond.getG())
                && myFirst.getQ().equals(mySecond.getQ());
    }

    /**
     * Jca DH PrivateKey.
     */
    public static class JcaDHPrivateKey
            extends JcaPrivateKey {
        /**
         * The private key.
         */
        private final BCDHPrivateKey thePrivateKey;

        /**
         * Constructor.
         * @param pKeySpec the key spec
         * @param pKey the key
         */
        JcaDHPrivateKey(final GordianKeyPairSpec pKeySpec,
                        final BCDHPrivateKey pKey) {
            super(pKeySpec, pKey);
            thePrivateKey = pKey;
        }

        @Override
        public BCDHPrivateKey getPrivateKey() {
            return thePrivateKey;
        }

        @Override
        public boolean equals(final Object pThat) {
            /* Handle the trivial cases */
            if (pThat == this) {
                return true;
            }
            if (pThat == null) {
                return false;
            }

            /* Make sure that the object is the same class */
            if (!(pThat instanceof JcaDHPrivateKey)) {
                return false;
            }

            /* Access the target field */
            final JcaDHPrivateKey myThat = (JcaDHPrivateKey) pThat;

            /* Check differences */
            return getKeySpec().equals(myThat.getKeySpec())
                    && thePrivateKey.getX().equals(myThat.getPrivateKey().getX())
                    && dhParamsAreEqual(thePrivateKey.getParams(), myThat.getPrivateKey().getParams());
        }

        @Override
        public int hashCode() {
            return Objects.hash(getKeySpec(), thePrivateKey);
        }
    }

    /**
     * Jca StateAware KeyPair.
     */
    public static class JcaStateAwareKeyPair
            extends JcaKeyPair
            implements GordianStateAwareKeyPair {
        /**
         * Constructor.
         * @param pPublic the public key
         * @param pPrivate the private key
         */
        JcaStateAwareKeyPair(final JcaPublicKey pPublic,
                             final JcaStateAwarePrivateKey pPrivate) {
            super(pPublic, pPrivate);
        }

        @Override
        public JcaStateAwarePrivateKey getPrivateKey() {
            return (JcaStateAwarePrivateKey) super.getPrivateKey();
        }

        @Override
        public long getUsagesRemaining() {
            return getPrivateKey().getUsagesRemaining();
        }

        @Override
        public JcaStateAwareKeyPair getKeyPairShard(final int pNumUsages) {
            return new JcaStateAwareKeyPair(getPublicKey(), getPrivateKey().getKeyShard(pNumUsages));
        }
    }
}