GordianZuc256Engine.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.ext.engines;

import org.bouncycastle.util.Memoable;

/**
 * Zuc256Mac implementation.
 * Based on http://www.is.cas.cn/ztzl2016/zouchongzhi/201801/W020180126529970733243.pdf
 */
@SuppressWarnings("checkstyle:MagicNumber")
public class GordianZuc256Engine
        extends GordianZuc128Engine {
    /**
     * the constants D.
     */
    private static final byte[] EK_D = {
            0b0100010, 0b0101111, 0b0100100, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
            0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
    };

    /**
     * the constants D for 32 bit Mac.
     */
    private static final byte[] EK_D32 = {
            0b0100010, 0b0101111, 0b0100101, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
            0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
    };

    /**
     * the constants D for 64 bit Mac.
     */
    private static final byte[] EK_D64 = {
            0b0100011, 0b0101111, 0b0100100, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
            0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
    };

    /**
     * the constants D for 128 bit Mac.
     */
    private static final byte[] EK_D128 = {
            0b0100011, 0b0101111, 0b0100101, 0b0101010, 0b1101101, 0b1000000, 0b1000000, 0b1000000,
            0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1000000, 0b1010010, 0b0010000, 0b0110000
    };

    /**
     * The selected D constants.
     */
    private byte[] theD;

    /**
     * Constructor for streamCipher.
     */
    public GordianZuc256Engine() {
        theD = EK_D;
    }

    /**
     * Constructor for Mac.
     *
     * @param pLength the Mac length
     */
    public GordianZuc256Engine(final int pLength) {
        switch (pLength) {
            case 32:
                theD = EK_D32;
                break;
            case 64:
                theD = EK_D64;
                break;
            case 128:
                theD = EK_D128;
                break;
            default:
                throw new IllegalArgumentException("Unsupported length: " + pLength);
        }
    }

    /**
     * Constructor for Memoable.
     *
     * @param pSource the source engine
     */
    private GordianZuc256Engine(final GordianZuc256Engine pSource) {
        super(pSource);
    }

    @Override
    protected int getMaxIterations() {
        return 625;
    }

    @Override
    public String getAlgorithmName() {
        return "Zuc-256";
    }

    /**
     * Build a 31-bit integer from constituent parts.
     *
     * @param a part A
     * @param b part B
     * @param c part C
     * @param d part D
     * @return the built integer
     */
    private static int makeU31(final byte a, final byte b, final byte c, final byte d) {
        return (((a & 0xFF) << 23) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | (d & 0xFF));
    }

    @Override
    protected void setKeyAndIV(final int[] pLFSR,
                               final byte[] k,
                               final byte[] iv) {
        /* Check lengths */
        if (k == null || k.length != 32) {
            throw new IllegalArgumentException("A key of 32 bytes is needed");
        }
        if (iv == null || iv.length != 25) {
            throw new IllegalArgumentException("An IV of 25 bytes is needed");
        }

        /* expand key and IV */
        pLFSR[0] = makeU31(k[0], theD[0], k[21], k[16]);
        pLFSR[1] = makeU31(k[1], theD[1], k[22], k[17]);
        pLFSR[2] = makeU31(k[2], theD[2], k[23], k[18]);
        pLFSR[3] = makeU31(k[3], theD[3], k[24], k[19]);
        pLFSR[4] = makeU31(k[4], theD[4], k[25], k[20]);
        pLFSR[5] = makeU31(iv[0], (byte) (theD[5] | (iv[17] & 0x3F)), k[5], k[26]);
        pLFSR[6] = makeU31(iv[1], (byte) (theD[6] | (iv[18] & 0x3F)), k[6], k[27]);
        pLFSR[7] = makeU31(iv[10], (byte) (theD[7] | (iv[19] & 0x3F)), k[7], iv[2]);
        pLFSR[8] = makeU31(k[8], (byte) (theD[8] | (iv[20] & 0x3F)), iv[3], iv[11]);
        pLFSR[9] = makeU31(k[9], (byte) (theD[9] | (iv[21] & 0x3F)), iv[12], iv[4]);
        pLFSR[10] = makeU31(iv[5], (byte) (theD[10] | (iv[22] & 0x3F)), k[10], k[28]);
        pLFSR[11] = makeU31(k[11], (byte) (theD[11] | (iv[23] & 0x3F)), iv[6], iv[13]);
        pLFSR[12] = makeU31(k[12], (byte) (theD[12] | (iv[24] & 0x3F)), iv[7], iv[14]);
        pLFSR[13] = makeU31(k[13], theD[13], iv[15], iv[8]);
        pLFSR[14] = makeU31(k[14], (byte) (theD[14] | ((k[31] >>> 4) & 0xF)), iv[16], iv[9]);
        pLFSR[15] = makeU31(k[15], (byte) (theD[15] | (k[31] & 0xF)), k[30], k[29]);
    }

    @Override
    public GordianZuc256Engine copy() {
        return new GordianZuc256Engine(this);
    }

    @Override
    public void reset(final Memoable pState) {
        final GordianZuc256Engine e = (GordianZuc256Engine) pState;
        super.reset(pState);
        theD = e.theD;
    }
}