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.spec.GordianDigestSpec;
import io.github.tonywasher.joceanus.gordianknot.api.digest.spec.GordianDigestSubSpec.GordianDigestState;
import io.github.tonywasher.joceanus.gordianknot.api.digest.spec.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.core.spec.digest.GordianCoreDigestSpec;
import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.digest.GordianCoreDigestSubSpec.GordianCoreDigestState;
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);
final GordianCoreDigestSpec mySpec = (GordianCoreDigestSpec) pDigestSpec;
/* Create digest */
final Digest myBCDigest = getBCDigest(mySpec);
return myBCDigest instanceof Xof myXof
? new BouncyDigestXof(mySpec, myXof)
: new BouncyDigest(mySpec, myBCDigest);
}
/**
* Create the BouncyCastle digest.
*
* @param pDigestSpec the digestSpec
* @return the digest
* @throws GordianException on error
*/
private static Digest getBCDigest(final GordianCoreDigestSpec pDigestSpec) throws GordianException {
/* Access digest details */
final GordianDigestType myType = pDigestSpec.getDigestType();
final GordianLength myLen = pDigestSpec.getDigestLength();
/* Switch on digest type */
return switch (myType) {
case SHA2 -> getSHA2Digest(pDigestSpec);
case RIPEMD -> getRIPEMDDigest(myLen);
case SKEIN -> pDigestSpec.isXofMode()
? getSkeinXof(pDigestSpec.getCoreDigestState())
: getSkeinDigest(pDigestSpec.getCoreDigestState(), myLen);
case SHA3 -> getSHA3Digest(myLen);
case SHAKE -> new SHAKEDigest(pDigestSpec.getCoreDigestState().getLength().getLength());
case KANGAROO -> getKangarooDigest(pDigestSpec);
case HARAKA -> getHarakaDigest(pDigestSpec);
case BLAKE2 -> pDigestSpec.isXofMode()
? getBlake2Xof(pDigestSpec)
: getBlake2Digest(pDigestSpec);
case BLAKE3 -> new GordianBlake3Digest(myLen.getByteLength());
case STREEBOG -> getStreebogDigest(myLen);
case KUPYNA -> getKupynaDigest(myLen);
case GROESTL -> new GordianGroestlDigest(myLen.getLength());
case JH -> new GordianJHDigest(myLen.getLength());
case CUBEHASH -> new GordianCubeHashDigest(myLen.getLength());
case GOST -> new GOST3411Digest();
case TIGER -> new TigerDigest();
case WHIRLPOOL -> new WhirlpoolDigest();
case SM3 -> new SM3Digest();
case SHA1 -> new SHA1Digest();
case MD5 -> new MD5Digest();
case MD4 -> new MD4Digest();
case MD2 -> new MD2Digest();
case ASCON -> getAsconDigest(pDigestSpec);
case ISAP -> new ISAPDigest();
case PHOTONBEETLE -> new PhotonBeetleDigest();
case ROMULUS -> new RomulusDigest();
case SPARKLE -> getSparkleDigest(pDigestSpec);
case XOODYAK -> 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) {
return switch (pLength) {
case LEN_128 -> new RIPEMD128Digest();
case LEN_160 -> new RIPEMD160Digest();
case LEN_256 -> new RIPEMD256Digest();
default -> new RIPEMD320Digest();
};
}
/**
* Create the BouncyCastle Blake2 digest.
*
* @param pSpec the digest spec
* @return the digest
*/
static GordianBlake2Base getBlake2Digest(final GordianCoreDigestSpec pSpec) {
final int myLength = pSpec.getDigestLength().getLength();
return pSpec.getCoreDigestState().isBlake2bState()
? new GordianBlake2bDigest(myLength)
: new GordianBlake2sDigest(myLength);
}
/**
* Create the BouncyCastle Blake2Xof digest.
*
* @param pSpec the digest spec
* @return the digest
*/
static GordianBlake2Xof getBlake2Xof(final GordianCoreDigestSpec pSpec) {
final GordianCoreDigestState myState = pSpec.getCoreDigestState();
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 GordianCoreDigestSpec 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 GordianCoreDigestSpec 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 GordianCoreDigestSpec 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 GordianCoreDigestSpec 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 GordianCoreDigestSpec pSpec) {
final GordianLength myLen = pSpec.getDigestLength();
final GordianDigestState myState = pSpec.getDigestState();
return switch (myLen) {
case LEN_224 -> GordianDigestState.STATE256.equals(myState)
? new SHA224Digest()
: new SHA512tDigest(myLen.getLength());
case LEN_256 -> GordianDigestState.STATE256.equals(myState)
? new SHA256Digest()
: new SHA512tDigest(myLen.getLength());
case LEN_384 -> new SHA384Digest();
default -> 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 GordianCoreDigestState 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 GordianCoreDigestState 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();
}
}