GordianAsconXof128.java

package org.bouncycastle.crypto.patch.digests;

import org.bouncycastle.crypto.OutputLengthException;
import org.bouncycastle.crypto.Xof;

/**
 * Ascon-XOF128 was introduced in NIST Special Publication (SP) 800-232
 * (Initial Public Draft).
 * <p>
 * Additional details and the specification can be found in:
 * <a href="https://csrc.nist.gov/pubs/sp/800/232/ipd">NIST SP 800-232 (Initial Public Draft)</a>.
 * For reference source code and implementation details, please see:
 * <a href="https://github.com/ascon/ascon-c">Reference, highly optimized, masked C and
 * ASM implementations of Ascon (NIST SP 800-232)</a>.
 * </p>
 */
public class GordianAsconXof128
        extends GordianAsconBaseDigest
        implements Xof
{
    protected boolean m_squeezing = false;

    private final byte[] buffer = new byte[ASCON_HASH_RATE];
    protected int bytesInBuffer;

    public GordianAsconXof128()
    {
        this(true);
    }

    protected GordianAsconXof128(final boolean doReset)
    {
        if (doReset)
        {
            reset();
        }
    }

    protected long pad(int i)
    {
        return 0x01L << (i << 3);
    }

    protected long loadBytes(final byte[] bytes, int inOff)
    {
        return GordianPack.littleEndianToLong(bytes, inOff);
    }

    protected long loadBytes(final byte[] bytes, int inOff, int n)
    {
        return GordianPack.littleEndianToLong(bytes, inOff, n);
    }

    protected void setBytes(long w, byte[] bytes, int inOff)
    {
        GordianPack.longToLittleEndian(w, bytes, inOff);
    }

    protected void setBytes(long w, byte[] bytes, int inOff, int n)
    {
        GordianPack.longToLittleEndian(w, bytes, inOff, n);
    }

    protected void padAndAbsorb()
    {
        if (!m_squeezing)
        {
            m_squeezing = true;
            super.padAndAbsorb();
        }
        else
        {
            p(ASCON_PB_ROUNDS);
        }
    }

    @Override
    public String getAlgorithmName()
    {
        return "Ascon-XOF-128";
    }

    @Override
    public void update(byte in)
    {
        if (m_squeezing)
        {
            throw new IllegalArgumentException("attempt to absorb while squeezing");
        }
        super.update(in);
    }

    @Override
    public void update(byte[] input, int inOff, int len)
    {
        if (m_squeezing)
        {
            throw new IllegalArgumentException("attempt to absorb while squeezing");
        }
        super.update(input, inOff, len);
    }

    @Override
    public int doOutput(byte[] output, int outOff, int outLen)
    {
        if (outLen + outOff > output.length)
        {
            throw new OutputLengthException("output buffer is too short");
        }

        /* Use buffered output first */
        int bytesOutput = 0;
        if (bytesInBuffer != 0)
        {
            int startPos = ASCON_HASH_RATE - bytesInBuffer;
            int bytesToOutput = Math.min(outLen, bytesInBuffer);
            System.arraycopy(buffer, startPos, output, outOff, bytesToOutput);
            bytesInBuffer -= bytesToOutput;
            bytesOutput += bytesToOutput;
        }

        /* If we still need to output data */
        if (outLen - bytesOutput >= ASCON_HASH_RATE)
        {
            /* Output full blocks */
            int bytesToOutput = ASCON_HASH_RATE * ((outLen - bytesOutput) / ASCON_HASH_RATE);
            bytesOutput += hash(output, outOff + bytesOutput, bytesToOutput);
        }

        /* If we need to output a partial buffer */
        if (bytesOutput < outLen)
        {
            /* Access the next buffer's worth of data */
            hash(buffer, 0, ASCON_HASH_RATE);

            /* Copy required length of data */
            int bytesToOutput = outLen - bytesOutput;
            System.arraycopy(buffer, 0, output, outOff + bytesOutput, bytesToOutput);
            bytesInBuffer = buffer.length - bytesToOutput;
            bytesOutput += bytesToOutput;
        }

        /* return the length of data output */
        return bytesOutput;
    }

    @Override
    public int doFinal(byte[] output, int outOff, int outLen)
    {
        int rlt = doOutput(output, outOff, outLen);
        reset();
        return rlt;
    }

    @Override
    public int getByteLength()
    {
        return 8;
    }

    @Override
    public void reset()
    {
        m_squeezing = false;
        bytesInBuffer = 0;
        super.reset();
        /* initialize */
        x0 = -2701369817892108309L;
        x1 = -3711838248891385495L;
        x2 = -1778763697082575311L;
        x3 = 1072114354614917324L;
        x4 = -2282070310009238562L;
    }

    protected void baseReset()
    {
        super.reset();
    }
}