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
- Call digest.Reset() before reusing the instance for a new hash computation.
- Restructure code so all Update/BlockUpdate calls precede the single DoFinal call.
- 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
- Treat IDigest instances as single-use: Reset() immediately after DoFinal().
- Wrap digest usage in using/try-finally that resets the instance.
- Never interleave Update and DoFinal across logical computations.
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
- must be in the range 1 to 7
- outputLength not a multiple of 8
- data
- must be in the range 0 to 7
- if 'data' is empty, 'padBits' must be 0
AI-assisted analysis of peass-ng/PEASS-ng@53fb989abc (2026-09-02).
Data as JSON: /api/errors/6abdd64b32cca475.
Report an issue: GitHub.