GordianDigestSubSpec.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.api.digest;

import io.github.tonywasher.joceanus.gordianknot.api.base.GordianLength;

/**
 * Digest SubSpec.
 */
public interface GordianDigestSubSpec {
    /**
     * Obtain the possible subSpecTypes for the digestType.
     *
     * @param pType the digestType
     * @return the subSpec types
     */
    static GordianDigestSubSpec[] getPossibleSubSpecsForType(final GordianDigestType pType) {
        switch (pType) {
            case SHA2:
            case BLAKE2:
            case HARAKA:
                return new GordianDigestState[]{GordianDigestState.STATE256, GordianDigestState.STATE512};
            case SKEIN:
                return new GordianDigestState[]{GordianDigestState.STATE256, GordianDigestState.STATE512, GordianDigestState.STATE1024};
            case SHAKE:
            case KANGAROO:
                return new GordianDigestState[]{GordianDigestState.STATE128, GordianDigestState.STATE256};
            default:
                return new GordianDigestState[]{null};
        }
    }

    /**
     * Obtain the subSpec for the type and length.
     *
     * @param pType   the digestType
     * @param pLength the length
     * @return the subSpec
     */
    static GordianDigestSubSpec getDefaultSubSpecForTypeAndLength(final GordianDigestType pType,
                                                                  final GordianLength pLength) {
        switch (pType) {
            case SHA2:
                return pLength == GordianLength.LEN_224 || pLength == GordianLength.LEN_256
                        ? GordianDigestState.STATE256
                        : GordianDigestState.STATE512;
            case SKEIN:
                switch (pLength) {
                    case LEN_1024:
                        return GordianDigestState.STATE1024;
                    case LEN_512:
                    case LEN_384:
                        return GordianDigestState.STATE512;
                    default:
                        return GordianDigestState.STATE256;
                }
            case SHAKE:
            case KANGAROO:
                return pLength == GordianLength.LEN_256
                        ? GordianDigestState.STATE128
                        : GordianDigestState.STATE256;
            case BLAKE2:
                return pLength == GordianLength.LEN_128 || pLength == GordianLength.LEN_224
                        ? GordianDigestState.STATE256
                        : GordianDigestState.STATE512;
            case HARAKA:
                return GordianDigestState.STATE256;
            default:
                return null;
        }
    }

    /**
     * State subSpecification.
     */
    enum GordianDigestState implements GordianDigestSubSpec {
        /**
         * 128.
         */
        STATE128(GordianLength.LEN_128),

        /**
         * 256.
         */
        STATE256(GordianLength.LEN_256),

        /**
         * 512.
         */
        STATE512(GordianLength.LEN_512),

        /**
         * 1024.
         */
        STATE1024(GordianLength.LEN_1024);

        /**
         * The length.
         */
        private final GordianLength theLength;

        /**
         * Constructor.
         *
         * @param pLength the length
         */
        GordianDigestState(final GordianLength pLength) {
            theLength = pLength;
        }

        /**
         * Obtain length for state.
         *
         * @return the length
         */
        public GordianLength getLength() {
            return theLength;
        }

        @Override
        public String toString() {
            return theLength.toString();
        }

        /**
         * Is this state a hybrid for sha2 length?
         *
         * @param pLength the length
         * @return true/false
         */
        public boolean isSha2Hybrid(final GordianLength pLength) {
            return this == STATE512
                    && (GordianLength.LEN_224.equals(pLength)
                    || GordianLength.LEN_256.equals(pLength));
        }

        /**
         * Obtain the length for an explicit Xof variant.
         *
         * @param pType the digestType
         * @return the length
         */
        GordianLength lengthForXofType(final GordianDigestType pType) {
            switch (pType) {
                case SKEIN:
                    switch (this) {
                        case STATE256:
                        case STATE512:
                        case STATE1024:
                            return theLength;
                        default:
                            return null;
                    }
                case BLAKE2:
                    switch (this) {
                        case STATE256:
                        case STATE512:
                            return theLength;
                        default:
                            return null;
                    }
                default:
                    return null;
            }
        }

        /**
         * is length available for this type and length?
         *
         * @param pType   the digestType
         * @param pLength the length
         * @return true/false
         */
        boolean validForTypeAndLength(final GordianDigestType pType,
                                      final GordianLength pLength) {
            switch (pType) {
                case SHA2:
                    return validForSha2Length(pLength);
                case SHAKE:
                case KANGAROO:
                    return validForSHAKELength(pLength);
                case SKEIN:
                    return validForSkeinLength(pLength);
                case BLAKE2:
                    return validForBlake2Length(pLength);
                case HARAKA:
                    return validForHarakaLength(pLength);
                default:
                    return false;
            }
        }

        /**
         * Is this state valid for the skeinLength?
         *
         * @param pLength the length
         * @return true/false
         */
        private boolean validForSha2Length(final GordianLength pLength) {
            switch (this) {
                case STATE512:
                    switch (pLength) {
                        case LEN_224:
                        case LEN_256:
                        case LEN_384:
                        case LEN_512:
                            return true;
                        default:
                            return false;
                    }
                case STATE256:
                    switch (pLength) {
                        case LEN_224:
                        case LEN_256:
                            return true;
                        default:
                            return false;
                    }
                default:
                    return false;
            }
        }

        /**
         * Is this state valid for the skeinLength?
         *
         * @param pLength the length
         * @return true/false
         */
        private boolean validForSHAKELength(final GordianLength pLength) {
            switch (this) {
                case STATE256:
                    return pLength == GordianLength.LEN_512;
                case STATE128:
                    return pLength == GordianLength.LEN_256;
                default:
                    return false;
            }
        }

        /**
         * Is this state valid for the skeinLength?
         *
         * @param pLength the length
         * @return true/false
         */
        private boolean validForSkeinLength(final GordianLength pLength) {
            switch (this) {
                case STATE1024:
                    switch (pLength) {
                        case LEN_384:
                        case LEN_512:
                        case LEN_1024:
                            return true;
                        default:
                            return false;
                    }
                case STATE512:
                    switch (pLength) {
                        case LEN_128:
                        case LEN_160:
                        case LEN_224:
                        case LEN_256:
                        case LEN_384:
                        case LEN_512:
                            return true;
                        default:
                            return false;
                    }
                case STATE256:
                    switch (pLength) {
                        case LEN_128:
                        case LEN_160:
                        case LEN_224:
                        case LEN_256:
                            return true;
                        default:
                            return false;
                    }
                default:
                    return false;
            }
        }

        /**
         * Is this state valid for the blake2Length.
         *
         * @param pLength the length
         * @return true/false
         */
        private boolean validForBlake2Length(final GordianLength pLength) {
            switch (this) {
                case STATE512:
                    switch (pLength) {
                        case LEN_160:
                        case LEN_256:
                        case LEN_384:
                        case LEN_512:
                            return true;
                        default:
                            return false;
                    }
                case STATE256:
                    switch (pLength) {
                        case LEN_128:
                        case LEN_160:
                        case LEN_224:
                        case LEN_256:
                            return true;
                        default:
                            return false;
                    }
                default:
                    return false;
            }
        }

        /**
         * Is this state valid for the harakaLength.
         *
         * @param pLength the length
         * @return true/false
         */
        private boolean validForHarakaLength(final GordianLength pLength) {
            switch (this) {
                case STATE512:
                case STATE256:
                    return pLength == GordianLength.LEN_256;
                default:
                    return false;
            }
        }

        /**
         * Is this the Blake2b algorithm?
         *
         * @return true/false
         */
        public boolean isBlake2bState() {
            return GordianDigestState.STATE512.equals(this);
        }

        /**
         * Obtain the blake2Algorithm name for State.
         *
         * @param pXofMode is this a Xof variant
         * @return the algorithm name
         */
        public String getBlake2Algorithm(final boolean pXofMode) {
            return (pXofMode ? "X" : "")
                    + (isBlake2bState() ? "b" : "s");
        }

        /**
         * Obtain the kangarooAlgorithm name for State.
         *
         * @return the algorithmName
         */
        String getKangarooAlgorithm() {
            return this == STATE256
                    ? GordianDigestResource.DIGEST_MARSUPILAMI.getValue()
                    : GordianDigestResource.DIGEST_KANGAROO.getValue();
        }
    }
}