GordianCoreFactory.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.core.factory;

import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
import io.github.tonywasher.joceanus.gordianknot.api.base.GordianKeySpec;
import io.github.tonywasher.joceanus.gordianknot.api.base.GordianLength;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianCipherFactory;
import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestFactory;
import io.github.tonywasher.joceanus.gordianknot.api.factory.GordianAsyncFactory;
import io.github.tonywasher.joceanus.gordianknot.api.factory.GordianFactory;
import io.github.tonywasher.joceanus.gordianknot.api.factory.GordianFactoryType;
import io.github.tonywasher.joceanus.gordianknot.api.keyset.GordianKeySet;
import io.github.tonywasher.joceanus.gordianknot.api.keyset.GordianKeySetFactory;
import io.github.tonywasher.joceanus.gordianknot.api.lock.GordianLockFactory;
import io.github.tonywasher.joceanus.gordianknot.api.lock.GordianPasswordLockSpec;
import io.github.tonywasher.joceanus.gordianknot.api.mac.GordianMacFactory;
import io.github.tonywasher.joceanus.gordianknot.api.random.GordianRandomFactory;
import io.github.tonywasher.joceanus.gordianknot.api.zip.GordianZipFactory;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseFactory;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianCoreKnuthObfuscater;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianFactoryGenerator;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianIdManager;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianParameters;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianPersonalisation;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianRandomSource;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianSeededRandom;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianValidator;
import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianDataException;
import io.github.tonywasher.joceanus.gordianknot.impl.core.key.GordianKeyAlgId;
import io.github.tonywasher.joceanus.gordianknot.impl.core.keyset.GordianCoreKeySetFactory;
import io.github.tonywasher.joceanus.gordianknot.impl.core.lock.GordianCoreLockFactory;
import io.github.tonywasher.joceanus.gordianknot.impl.core.random.GordianCoreRandomFactory;
import io.github.tonywasher.joceanus.gordianknot.impl.core.zip.GordianCoreZipFactory;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;

import java.security.SecureRandom;

/**
 * Core factory.
 */
public abstract class GordianCoreFactory
        implements GordianBaseFactory {
    /**
     * The number of seed bytes.
     */
    private static final int SEED_SIZE = GordianLength.LEN_256.getByteLength();

    /**
     * Parameters.
     */
    private final GordianParameters theParameters;

    /**
     * Factory generator.
     */
    private final GordianFactoryGenerator theGenerator;

    /**
     * Random Source.
     */
    private final GordianRandomSource theRandom;

    /**
     * Personalisation.
     */
    private final GordianPersonalisation thePersonalisation;

    /**
     * IdManager.
     */
    private final GordianIdManager theIdManager;

    /**
     * Obfuscater.
     */
    private final GordianCoreKnuthObfuscater theObfuscater;

    /**
     * Embedded KeySet.
     */
    private final GordianKeySet theKeySet;

    /**
     * Digest Factory.
     */
    private final GordianDigestFactory theDigestFactory;

    /**
     * Cipher Factory.
     */
    private final GordianCipherFactory theCipherFactory;

    /**
     * Mac Factory.
     */
    private final GordianMacFactory theMacFactory;

    /**
     * Random Factory.
     */
    private final GordianRandomFactory theRandomFactory;

    /**
     * KeySet Factory.
     */
    private final GordianKeySetFactory theKeySetFactory;

    /**
     * Lock Factory.
     */
    private final GordianCoreLockFactory theLockFactory;

    /**
     * Zip Factory.
     */
    private final GordianCoreZipFactory theZipFactory;

    /**
     * The validator.
     */
    private final GordianValidator theValidator;

    /**
     * The Key AlgIds.
     */
    private GordianKeyAlgId theKeyAlgIds;

    /**
     * Async Factory.
     */
    private GordianAsyncFactory theAsyncFactory;

    /**
     * Constructor.
     *
     * @param pGenerator  the factory generator
     * @param pParameters the parameters
     * @throws GordianException on error
     */
    protected GordianCoreFactory(final GordianFactoryGenerator pGenerator,
                                 final GordianParameters pParameters) throws GordianException {
        /* Check parameters */
        if (pParameters == null || !pParameters.validate()) {
            throw new GordianDataException("Invalid Parameters");
        }

        /* Store the parameters */
        theGenerator = pGenerator;
        theParameters = pParameters;

        /* Create the random source */
        theRandom = new GordianRandomSource();

        /* Declare factories */
        theValidator = newValidator();
        theDigestFactory = newDigestFactory(this);
        theCipherFactory = newCipherFactory(this);
        theMacFactory = newMacFactory(this);
        theRandomFactory = new GordianCoreRandomFactory(this);
        theKeySetFactory = new GordianCoreKeySetFactory(this);
        theLockFactory = new GordianCoreLockFactory(this);
        theZipFactory = new GordianCoreZipFactory(this);

        /* Declare personalisation */
        thePersonalisation = new GordianPersonalisation(this);
        theIdManager = new GordianIdManager(this);
        theObfuscater = new GordianCoreKnuthObfuscater(this);
        theKeySet = createEmbeddedKeySet();
    }

    @Override
    public GordianFactory newFactory(final GordianParameters pParameters) throws GordianException {
        return theGenerator.newFactory(pParameters);
    }

    /**
     * Obtain the random source.
     *
     * @return the random source
     */
    public GordianRandomSource getRandomSource() {
        return theRandom;
    }

    /**
     * Obtain the parameters.
     *
     * @return the parameters
     */
    public GordianParameters getParameters() {
        return theParameters;
    }

    @Override
    public GordianFactoryType getFactoryType() {
        return theParameters.getFactoryType();
    }

    /**
     * Obtain the security seed.
     *
     * @return the security seed
     */
    public byte[] getSecuritySeed() {
        return theParameters.getSecuritySeed();
    }

    /**
     * Obtain the keySet seed.
     *
     * @return the keySet seed
     */
    public byte[] getKeySetSeed() {
        return theParameters.getKeySetSeed();
    }

    /**
     * Is this an internal factory?
     *
     * @return true/false
     */
    public boolean isInternal() {
        return theParameters.isInternal();
    }

    /**
     * Is this a random factory?
     *
     * @return true/false
     */
    public boolean isRandom() {
        return getKeySetSeed() != null;
    }

    @Override
    public GordianKeySet getEmbeddedKeySet() {
        return theKeySet;
    }

    /**
     * Create embedded keySet.
     *
     * @return the embedded keySet
     * @throws GordianException on error
     */
    private GordianKeySet createEmbeddedKeySet() throws GordianException {
        /* Obtain the keySet seed */
        final byte[] mySeed = thePersonalisation.getKeySetVector();

        /* Derive the keySet */
        final GordianKeySetGenerate myKeySets = (GordianKeySetGenerate) getKeySetFactory();
        return myKeySets.generateKeySet(mySeed);
    }

    @Override
    public GordianPersonalisation getPersonalisation() {
        return thePersonalisation;
    }

    @Override
    public GordianIdManager getIdManager() {
        return theIdManager;
    }

    @Override
    public GordianCoreKnuthObfuscater getObfuscater() {
        return theObfuscater;
    }

    @Override
    public void reSeedRandom() {
        /* Access the random */
        final SecureRandom myRandom = theRandom.getRandom();
        if (myRandom instanceof GordianSeededRandom mySeeded) {
            /* Generate and apply the new seed */
            final byte[] mySeed = mySeeded.generateSeed(SEED_SIZE);
            mySeeded.setSeed(mySeed);
            mySeeded.reseed(null);
        }
    }

    @Override
    public GordianDigestFactory getDigestFactory() {
        return theDigestFactory;
    }

    /**
     * Create a new digest factory.
     *
     * @param pFactory the factory
     * @return the new digest factory
     */
    public abstract GordianDigestFactory newDigestFactory(GordianBaseFactory pFactory);

    @Override
    public GordianCipherFactory getCipherFactory() {
        return theCipherFactory;
    }

    /**
     * Create a new cipher factory.
     *
     * @param pFactory the factory
     * @return the new cipher factory
     */
    public abstract GordianCipherFactory newCipherFactory(GordianBaseFactory pFactory);

    @Override
    public GordianMacFactory getMacFactory() {
        return theMacFactory;
    }

    /**
     * Create a new MAC factory.
     *
     * @param pFactory the factory
     * @return the new MAC factory
     */
    public abstract GordianMacFactory newMacFactory(GordianBaseFactory pFactory);

    @Override
    public GordianRandomFactory getRandomFactory() {
        return theRandomFactory;
    }

    @Override
    public GordianKeySetFactory getKeySetFactory() {
        return theKeySetFactory;
    }

    @Override
    public GordianLockFactory getLockFactory() {
        return theLockFactory;
    }

    @Override
    public GordianFactoryLock newFactoryLock(final GordianFactory pFactoryToLock,
                                             final GordianPasswordLockSpec pLockSpec,
                                             final char[] pPassword) throws GordianException {
        return theLockFactory.newFactoryLock(pFactoryToLock, pLockSpec, pPassword);
    }

    @Override
    public GordianFactoryLock newFactoryLock(final GordianPasswordLockSpec pLockSpec,
                                             final GordianFactoryType pFactoryType,
                                             final char[] pPassword) throws GordianException {
        return theLockFactory.newFactoryLock(pLockSpec, pFactoryType, pPassword);
    }

    @Override
    public GordianFactoryLock resolveFactoryLock(final byte[] pLockBytes,
                                                 final char[] pPassword) throws GordianException {
        return theLockFactory.resolveFactoryLock(pLockBytes, pPassword);
    }

    /**
     * Obtain the validator.
     *
     * @return the validator
     */
    public GordianValidator getValidator() {
        return theValidator;
    }

    /**
     * Create a new validator.
     *
     * @return the new validator
     */
    public GordianValidator newValidator() {
        return new GordianValidator();
    }

    @Override
    public GordianZipFactory getZipFactory() {
        return theZipFactory;
    }

    @Override
    public GordianAsyncFactory getAsyncFactory() {
        if (theAsyncFactory == null) {
            theAsyncFactory = newAsyncFactory(this);
        }
        return theAsyncFactory;
    }

    /**
     * Create a new Async factory.
     *
     * @param pFactory the factory
     * @return the new Async factory
     */
    public abstract GordianAsyncFactory newAsyncFactory(GordianBaseFactory pFactory);

    /**
     * Obtain Identifier for keySpec.
     *
     * @param pSpec the keySpec.
     * @return the Identifier
     */
    public AlgorithmIdentifier getIdentifierForSpec(final GordianKeySpec pSpec) {
        return getKeyAlgIds().getIdentifierForSpec(pSpec);
    }

    /**
     * Obtain keySpec for Identifier.
     *
     * @param pIdentifier the identifier.
     * @return the keySpec (or null if not found)
     */
    public GordianKeySpec getKeySpecForIdentifier(final AlgorithmIdentifier pIdentifier) {
        return getKeyAlgIds().getKeySpecForIdentifier(pIdentifier);
    }

    /**
     * Obtain the key algorithm Ids.
     *
     * @return the key Algorithm Ids
     */
    private GordianKeyAlgId getKeyAlgIds() {
        if (theKeyAlgIds == null) {
            theKeyAlgIds = new GordianKeyAlgId(this);
        }
        return theKeyAlgIds;
    }

    @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 GordianCoreFactory myThat)) {
            return false;
        }

        /* Check Differences */
        return theParameters.equals(myThat.theParameters);
    }

    @Override
    public int hashCode() {
        return theParameters.hashCode();
    }
}