peass-ng/PEASS-ng · error · InvalidOperationException
attempt to absorb with odd length queue
Error message
attempt to absorb with odd length queue
What it means
During absorbing, the Keccak implementation buffers bits and must always append 8 bits at a byte boundary. Absorb(byte) checks bitsInQueue % 8 == 0 before writing; if a partial byte is queued it throws InvalidOperationException. This is an internal invariant — the public Update always feeds whole bytes, so it indicates misuse of an instance mid-update or corrupt state.
Source
Thrown at winPEAS/winPEASexe/winPEAS/3rdParty/BouncyCastle/crypto/digests/KeccakDigest.cs:152
}
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");
View on GitHub (pinned to 53fb989abc)
Solutions
- Create a new digest instance (or call Reset()) instead of reusing an instance whose update failed halfway.
- Use one IDigest instance per thread; synchronize access if sharing is unavoidable.
- Feed data only via BlockUpdate()/Update() with whole bytes; never call Absorb directly after custom bit manipulation.
Example fix
// before var d = new KeccakDigest(256); Parallel.For(0, 10, i => d.BlockUpdate(data, 0, data.Length)); // races corrupt queue // after var d = new KeccakDigest(256); d.BlockUpdate(data, 0, data.Length); // single-threaded, or lock around updates
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure single-threaded, whole-byte updates
lock (digestLock) { digest.BlockUpdate(data, 0, data.Length); } Try / catch
try { digest.BlockUpdate(data, 0, data.Length); }
catch (InvalidOperationException ex) {
digest.Reset(); // discard misaligned state and restart the hash
} Prevention
- Never share an IDigest across threads without locking
- Reset() or recreate a digest after any failed update
- Feed data only via BlockUpdate/Update with whole bytes
- Avoid custom bit-level manipulation of the sponge queue
When it happens
Trigger: Absorb() reached while bitsInQueue is not a multiple of 8 — e.g. after a failed/partial Update left the queue misaligned, concurrent BlockUpdate calls on a shared digest instance, or calling the protected Absorb directly from a subclass after manipulating the queue.
Common situations: Sharing one IDigest across threads without synchronization; resuming a hash from a deserialized half-used instance; custom sponge subclasses that call Absorb byte-wise after a bit-level squeeze.
Related errors
- invalid rate value
- unsupported state size: only 512/1024 are allowed
- must be one of 128, 224, 256, 288, 384, or 512.
- Keys > 32 are not supported
- Invalid digest length (required: 1 - 32)
AI-assisted analysis of peass-ng/PEASS-ng@53fb989abc (2026-09-02).
Data as JSON: /api/errors/a5b2f739e206670c.
Report an issue: GitHub.