peass-ng/PEASS-ng · error · InvalidOperationException

attempt to absorb while squeezing

Error message

attempt to absorb while squeezing

What it means

KeccakDigest.Absorb(byte) throws InvalidOperationException when the absorb phase is invoked after the squeeze phase has started (the `squeezing` flag is true). Keccak/SHA-3 is a two-phase sponge: all input must be absorbed before any output is squeezed; absorbing after switching to squeezing would corrupt the state. This single-byte overload is reached via Update().

Source

Thrown at winPEAS/winPEASexe/winPEAS/3rdParty/BouncyCastle/crypto/digests/KeccakDigest.cs:154

        private void InitSponge(int rate)
        {
            if (rate <= 0 || rate >= 1600 || (rate & 63) != 0)
                throw new InvalidOperationException("invalid rate value");

            this.rate = rate;
            Array.Clear(state, 0, state.Length);
            Arrays.Fill(this.dataQueue, (byte)0);
            this.bitsInQueue = 0;
            this.squeezing = false;
            this.fixedOutputLength = (1600 - rate) >> 1;
        }

        protected void Absorb(byte data)
        {
            if ((bitsInQueue & 7) != 0)
                throw new InvalidOperationException("attempt to absorb with odd length queue");
            if (squeezing)
                throw new InvalidOperationException("attempt to absorb while squeezing");

            dataQueue[bitsInQueue >> 3] = data;
            if ((bitsInQueue += 8) == rate)
            {
                KeccakAbsorb(dataQueue, 0);
                bitsInQueue = 0;
            }
        }

        protected void Absorb(byte[] data, int off, int len)
        {
            if ((bitsInQueue & 7) != 0)
                throw new InvalidOperationException("attempt to absorb with odd length queue");
            if (squeezing)
                throw new InvalidOperationException("attempt to absorb while squeezing");

            int bytesInQueue = bitsInQueue >> 3;
            int rateBytes = rate >> 3;

View on GitHub (pinned to 53fb989abc)

Solutions

  1. Call digest.Reset() before reusing the instance for a new hash computation.
  2. Restructure code so all Update/BlockUpdate calls precede the single DoFinal call.
  3. If a second hash over different data is needed, create a new KeccakDigest instance instead of reusing a finalized one.

Example fix

// before
digest.BlockUpdate(part1, 0, part1.Length);
digest.DoFinal(hash1, 0);
digest.BlockUpdate(part2, 0, part2.Length); // throws
// after
digest.BlockUpdate(part1, 0, part1.Length);
digest.DoFinal(hash1, 0);
digest.Reset();
digest.BlockUpdate(part2, 0, part2.Length);
Defensive patterns

Strategy: validation

Validate before calling

// Track finalize state before each update
bool finalized = false;
void SafeUpdate(KeccakDigest d, byte b) {
    if (finalized) throw new InvalidOperationException("Call Reset() before reusing the digest");
    d.Update(b);
}

Type guard

bool IsUsable(KeccakDigest d, bool wasFinalized) => !wasFinalized;

Try / catch

try { digest.Update(data[i]); } catch (InvalidOperationException ex) when (ex.Message.Contains("squeezing")) { digest.Reset(); digest.Update(data[i]); }

Prevention

When it happens

Trigger: Calling digest.Update(...) (or BlockUpdate) after calling DoFinal has put the digest into squeezing phase — e.g. calling Update between two DoFinal calls without a Reset.

Common situations: Streaming hash of multiple parts where the developer reuses the digest instance after DoFinal without Reset; a helper that computes a final hash then appends more data; incremental hashing loops that over-run the final block.

Related errors


AI-assisted analysis of peass-ng/PEASS-ng@53fb989abc (2026-09-02). Data as JSON: /api/errors/6abdd64b32cca475. Report an issue: GitHub.