BouncyDigestFactory.java

/*
 * GordianKnot: Security Suite
 * Copyright 2012-2026. 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 io.github.tonywasher.joceanus.gordianknot.impl.bc;

import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
import io.github.tonywasher.joceanus.gordianknot.api.base.GordianLength;
import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestSpec;
import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestSubSpec.GordianDigestState;
import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestType;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseData;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseFactory;
import io.github.tonywasher.joceanus.gordianknot.impl.core.digest.GordianCoreDigestFactory;
import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianDataException;
import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianBlake2Base;
import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianBlake2Xof;
import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianBlake2bDigest;
import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianBlake2sDigest;
import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianBlake3Digest;
import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianCubeHashDigest;
import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianGroestlDigest;
import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianJHDigest;
import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianKangarooDigest.GordianKangarooBase;
import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianKangarooDigest.GordianKangarooTwelve;
import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianKangarooDigest.GordianMarsupilamiFourteen;
import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianSkeinDigest;
import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianSkeinXof;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Xof;
import org.bouncycastle.crypto.digests.AsconHash256;
import org.bouncycastle.crypto.digests.AsconXof128;
import org.bouncycastle.crypto.digests.DSTU7564Digest;
import org.bouncycastle.crypto.digests.GOST3411Digest;
import org.bouncycastle.crypto.digests.GOST3411_2012_256Digest;
import org.bouncycastle.crypto.digests.GOST3411_2012_512Digest;
import org.bouncycastle.crypto.digests.Haraka256Digest;
import org.bouncycastle.crypto.digests.Haraka512Digest;
import org.bouncycastle.crypto.digests.ISAPDigest;
import org.bouncycastle.crypto.digests.MD2Digest;
import org.bouncycastle.crypto.digests.MD4Digest;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.digests.PhotonBeetleDigest;
import org.bouncycastle.crypto.digests.RIPEMD128Digest;
import org.bouncycastle.crypto.digests.RIPEMD160Digest;
import org.bouncycastle.crypto.digests.RIPEMD256Digest;
import org.bouncycastle.crypto.digests.RIPEMD320Digest;
import org.bouncycastle.crypto.digests.RomulusDigest;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.digests.SHA224Digest;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.digests.SHA384Digest;
import org.bouncycastle.crypto.digests.SHA3Digest;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.digests.SHA512tDigest;
import org.bouncycastle.crypto.digests.SHAKEDigest;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.digests.SparkleDigest;
import org.bouncycastle.crypto.digests.SparkleDigest.SparkleParameters;
import org.bouncycastle.crypto.digests.TigerDigest;
import org.bouncycastle.crypto.digests.WhirlpoolDigest;
import org.bouncycastle.crypto.digests.XoodyakDigest;

/**
 * BouncyCastle Digest Factory.
 */
public class BouncyDigestFactory
        extends GordianCoreDigestFactory {
    /**
     * Constructor.
     *
     * @param pFactory the factory
     */
    BouncyDigestFactory(final GordianBaseFactory pFactory) {
        /* Initialize underlying class */
        super(pFactory);
    }

    @Override
    public BouncyDigest createDigest(final GordianDigestSpec pDigestSpec) throws GordianException {
        /* Check validity of DigestSpec */
        checkDigestSpec(pDigestSpec);

        /* Create digest */
        final Digest myBCDigest = getBCDigest(pDigestSpec);
        return myBCDigest instanceof Xof myXof
                ? new BouncyDigestXof(pDigestSpec, myXof)
                : new BouncyDigest(pDigestSpec, myBCDigest);
    }

    /**
     * Create the BouncyCastle digest.
     *
     * @param pDigestSpec the digestSpec
     * @return the digest
     * @throws GordianException on error
     */
    private static Digest getBCDigest(final GordianDigestSpec pDigestSpec) throws GordianException {
        /* Access digest details */
        final GordianDigestType myType = pDigestSpec.getDigestType();
        final GordianLength myLen = pDigestSpec.getDigestLength();

        /* Switch on digest type */
        switch (myType) {
            case SHA2:
                return getSHA2Digest(pDigestSpec);
            case RIPEMD:
                return getRIPEMDDigest(myLen);
            case SKEIN:
                return pDigestSpec.isXofMode()
                        ? getSkeinXof(pDigestSpec.getDigestState())
                        : getSkeinDigest(pDigestSpec.getDigestState(), myLen);
            case SHA3:
                return getSHA3Digest(myLen);
            case SHAKE:
                return new SHAKEDigest(pDigestSpec.getDigestState().getLength().getLength());
            case KANGAROO:
                return getKangarooDigest(pDigestSpec);
            case HARAKA:
                return getHarakaDigest(pDigestSpec);
            case BLAKE2:
                return pDigestSpec.isXofMode()
                        ? getBlake2Xof(pDigestSpec)
                        : getBlake2Digest(pDigestSpec);
            case BLAKE3:
                return new GordianBlake3Digest(myLen.getByteLength());
            case STREEBOG:
                return getStreebogDigest(myLen);
            case KUPYNA:
                return getKupynaDigest(myLen);
            case GROESTL:
                return new GordianGroestlDigest(myLen.getLength());
            case JH:
                return new GordianJHDigest(myLen.getLength());
            case CUBEHASH:
                return new GordianCubeHashDigest(myLen.getLength());
            case GOST:
                return new GOST3411Digest();
            case TIGER:
                return new TigerDigest();
            case WHIRLPOOL:
                return new WhirlpoolDigest();
            case SM3:
                return new SM3Digest();
            case SHA1:
                return new SHA1Digest();
            case MD5:
                return new MD5Digest();
            case MD4:
                return new MD4Digest();
            case MD2:
                return new MD2Digest();
            case ASCON:
                return getAsconDigest(pDigestSpec);
            case ISAP:
                return new ISAPDigest();
            case PHOTONBEETLE:
                return new PhotonBeetleDigest();
            case ROMULUS:
                return new RomulusDigest();
            case SPARKLE:
                return getSparkleDigest(pDigestSpec);
            case XOODYAK:
                return new XoodyakDigest();
            default:
                throw new GordianDataException(GordianBaseData.getInvalidText(pDigestSpec.toString()));
        }
    }

    /**
     * Create the BouncyCastle RIPEMD digest.
     *
     * @param pLength the digest length
     * @return the digest
     */
    private static Digest getRIPEMDDigest(final GordianLength pLength) {
        switch (pLength) {
            case LEN_128:
                return new RIPEMD128Digest();
            case LEN_160:
                return new RIPEMD160Digest();
            case LEN_256:
                return new RIPEMD256Digest();
            case LEN_320:
            default:
                return new RIPEMD320Digest();
        }
    }

    /**
     * Create the BouncyCastle Blake2 digest.
     *
     * @param pSpec the digest spec
     * @return the digest
     */
    static GordianBlake2Base getBlake2Digest(final GordianDigestSpec pSpec) {
        final int myLength = pSpec.getDigestLength().getLength();
        return pSpec.getDigestState().isBlake2bState()
                ? new GordianBlake2bDigest(myLength)
                : new GordianBlake2sDigest(myLength);
    }

    /**
     * Create the BouncyCastle Blake2Xof digest.
     *
     * @param pSpec the digest spec
     * @return the digest
     */
    static GordianBlake2Xof getBlake2Xof(final GordianDigestSpec pSpec) {
        final GordianDigestState myState = pSpec.getDigestState();
        final int myLength = pSpec.getDigestLength().getLength();
        return myState.isBlake2bState()
                ? new GordianBlake2Xof(new GordianBlake2bDigest(myLength))
                : new GordianBlake2Xof(new GordianBlake2sDigest(myLength));
    }

    /**
     * Create the BouncyCastle Kangaroo digest.
     *
     * @param pSpec the digest spec
     * @return the digest
     */
    private static GordianKangarooBase getKangarooDigest(final GordianDigestSpec pSpec) {
        final int myLength = pSpec.getDigestLength().getByteLength();
        return GordianDigestState.STATE128.equals(pSpec.getDigestState())
                ? new GordianKangarooTwelve(myLength)
                : new GordianMarsupilamiFourteen(myLength);
    }

    /**
     * Create the BouncyCastle Haraka digest.
     *
     * @param pSpec the digest spec
     * @return the digest
     */
    private static Digest getHarakaDigest(final GordianDigestSpec pSpec) {
        return GordianDigestState.STATE256.equals(pSpec.getDigestState())
                ? new Haraka256Digest()
                : new Haraka512Digest();
    }

    /**
     * Create the BouncyCastle Ascon digest.
     *
     * @param pSpec the digest spec
     * @return the digest
     */
    private static Digest getAsconDigest(final GordianDigestSpec pSpec) {
        return pSpec.isXofMode() ? new AsconXof128()
                : new AsconHash256();
    }

    /**
     * Create the BouncyCastle Sparkle digest.
     *
     * @param pSpec the digest spec
     * @return the digest
     */
    private static Digest getSparkleDigest(final GordianDigestSpec pSpec) {
        return GordianLength.LEN_256 == pSpec.getDigestLength()
                ? new SparkleDigest(SparkleParameters.ESCH256)
                : new SparkleDigest(SparkleParameters.ESCH384);
    }

    /**
     * Create the BouncyCastle SHA2 digest.
     *
     * @param pSpec the digestSpec
     * @return the digest
     */
    private static Digest getSHA2Digest(final GordianDigestSpec pSpec) {
        final GordianLength myLen = pSpec.getDigestLength();
        final GordianDigestState myState = pSpec.getDigestState();
        switch (myLen) {
            case LEN_224:
                return GordianDigestState.STATE256.equals(myState)
                        ? new SHA224Digest()
                        : new SHA512tDigest(myLen.getLength());
            case LEN_256:
                return GordianDigestState.STATE256.equals(myState)
                        ? new SHA256Digest()
                        : new SHA512tDigest(myLen.getLength());
            case LEN_384:
                return new SHA384Digest();
            case LEN_512:
            default:
                return new SHA512Digest();
        }
    }

    /**
     * Create the BouncyCastle SHA3 digest.
     *
     * @param pLength the digest length
     * @return the digest
     */
    private static Digest getSHA3Digest(final GordianLength pLength) {
        return new SHA3Digest(pLength.getLength());
    }

    /**
     * Create the BouncyCastle Kupyna digest.
     *
     * @param pLength the digest length
     * @return the digest
     */
    private static Digest getKupynaDigest(final GordianLength pLength) {
        return new DSTU7564Digest(pLength.getLength());
    }

    /**
     * Create the BouncyCastle skeinDigest.
     *
     * @param pState  the state
     * @param pLength the digest length
     * @return the digest
     */
    private static Digest getSkeinDigest(final GordianDigestState pState,
                                         final GordianLength pLength) {
        return new GordianSkeinDigest(pState.getLength().getLength(), pLength.getLength());
    }

    /**
     * Create the BouncyCastle skeinXof.
     *
     * @param pState the state
     * @return the digest
     */
    private static Digest getSkeinXof(final GordianDigestState pState) {
        final int myLength = pState.getLength().getLength();
        return new GordianSkeinXof(new GordianSkeinDigest(myLength, myLength));
    }

    /**
     * Create the BouncyCastle Streebog digest.
     *
     * @param pLength the digest length
     * @return the digest
     */
    private static Digest getStreebogDigest(final GordianLength pLength) {
        return GordianLength.LEN_256.equals(pLength)
                ? new GOST3411_2012_256Digest()
                : new GOST3411_2012_512Digest();
    }
}