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

  1. Create a new digest instance (or call Reset()) instead of reusing an instance whose update failed halfway.
  2. Use one IDigest instance per thread; synchronize access if sharing is unavoidable.
  3. 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

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


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