gchq/CyberChef · error · OperationError

IV mismatch

Error message

IV mismatch

What it means

The last step of RFC 3394 unwrap compares the recovered 8-byte integrity register A against the expected IV; a mismatch proves the data did not unwrap correctly (wrong KEK, wrong IV, or corrupted wrapped blob). This is an authenticated integrity failure — unlike a padding oracle, AES-KW gives a clean yes/no.

Source

Thrown at src/core/operations/AESKeyUnwrap.mjs:116

                const aView = new DataView(aBuffer);
                aView.setUint32(0, aView.getUint32(0) ^ cntUpper);
                aView.setUint32(4, aView.getUint32(4) ^ cntLower);
                A = Utils.arrayBufferToStr(aBuffer, false);
                decipher.start();
                decipher.update(forge.util.createBuffer(A + R[i] + paddingBlock));
                decipher.finish();
                const B = decipher.output.getBytes();
                A = B.substring(0, 8);
                R[i] = B.substring(8, 16);
                cntLower--;
                if (cntLower < 0) {
                    cntUpper--;
                    cntLower = 0xffffffff;
                }
            }
        }
        if (A !== iv) {
            throw new OperationError("IV mismatch");
        }
        const P = R.join("");

        if (outputType === "Hex") {
            return toHexFast(Utils.strToArrayBuffer(P));
        }
        return P;
    }

}

export default AESKeyUnwrap;

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Confirm the KEK is byte-identical to the one used during wrap (and its format option is correct).
  2. If the wrap used a non-standard IV, supply that exact 8-byte IV; otherwise use A6A6A6A6A6A6A6A6.
  3. Verify the wrapped input was not truncated or altered and that its format option matches.
  4. Re-wrap with the intended KEK/IV to produce a known-good blob for comparison.

Example fix

// before: unwrap with wrong KEK → 'IV mismatch'
// after: supply the exact KEK used during AESKeyWrap (same bytes, same format option)
Defensive patterns

Strategy: try-catch

Validate before calling

function preflightUnwrap(kekBytes, ivBytes, wrappedBytes) {
  if (![16,24,32].includes(kekBytes.length)) throw new Error('bad KEK length');
  if (ivBytes.length !== 8) throw new Error('bad IV length');
  if (wrappedBytes.length < 24 || wrappedBytes.length % 8 !== 0) throw new Error('bad wrapped length');
}

Type guard

function unwrapParamsOk(kek, iv, wrapped) { return isAesKek(kek) && isWrapIv(iv) && isUnwrappable(wrapped); }

Try / catch

try {
  return aesKeyUnwrap(wrapped, kek, iv);
} catch (e) {
  if (/IV mismatch/.test(e.message)) {
    // integrity failure: wrong KEK, wrong IV, or corrupted blob — do not retry blindly
    throw new Error('Key-wrap integrity check failed: verify KEK, IV, and wrapped data');
  }
  throw e;
}

Prevention

When it happens

Trigger: After the 6·n rounds complete, the recovered A != iv. Causes: KEK differs from the one used to wrap; the wrap used a non-default IV that was not supplied here; the wrapped bytes were altered/truncated/reordered; format-option mismatch silently changed bytes.

Common situations: Wrong KEK (most common); default IV A6A6A6A6… assumed but the wrap used a custom IV; copy-paste truncation of the wrapped blob; hex/base64 format mismatch on KEK or wrapped data.

Related errors


AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13). Data as JSON: /api/errors/94346198cb439849. Report an issue: GitHub.