gchq/CyberChef · error · OperationError
Decryption Error -- Computed Hashes Do Not Match
Error message
Decryption Error -- Computed Hashes Do Not Match
What it means
The integrity check on SM2 decryption at SM2.mjs:161. SM2 encryption embeds a MAC tag (c3) over the plaintext; on decrypt the code recomputes the tag and at this line compares it to the supplied c3. A mismatch means the recovered plaintext is not authentic — almost always the wrong key, corrupted ciphertext, or a malformed SM2 envelope.
Source
Thrown at src/core/lib/SM2.mjs:161
/*
* Compute the p2 (secret) value by taking the C1 point provided in the encrypted package, and multiplying by the private k value
*/
const p2 = c1.multiply(this.privateKey);
/*
* Similar to encryption; compute sufficient length key material and XOR the input data to recover the original message
*/
const key = this.kdf(p2, c2.byteLength);
for (let i = 0; i < c2.byteLength; i++) {
c2[i] ^= Utils.ord(key[i]);
}
const check = this.c3(p2, c2);
if (check === c3) {
return c2.buffer;
} else {
throw new OperationError("Decryption Error -- Computed Hashes Do Not Match");
}
}
/**
* Generates a large random number
*
* @param {*} limit
* @returns
*/
getBigRandom(limit) {
return new r.BigInteger(limit.bitLength(), this.rng)
.mod(limit.subtract(r.BigInteger.ONE))
.add(r.BigInteger.ONE);
}
/**
* Helper function for generating a large random K number; utilized for generating our initial C1 pointView on GitHub (pinned to 4290ea7539)
Solutions
- Verify the private key being used is the partner of the public key the sender encrypted to.
- Confirm the ciphertext fields (c1, c2, c3) are in the layout this implementation expects (C1||C3||C2 per GM/T 0003-2012).
- Re-extract the ciphertext through a clean base64/hex path and check for truncation.
- If interoperating with another SM2 library, normalise the c-field ordering before calling decrypt.
Example fix
// before: ciphertext from a library that orders fields C1||C2||C3 const pt = sm2.decrypt(ct, privateKey); // after: reorder to C1||C3||C2 as this implementation expects const reordered = concat(c1, c3, c2); const pt = sm2.decrypt(reordered, privateKey);
Defensive patterns
Strategy: try-catch
Validate before calling
// Cannot pre-validate the MAC without decrypting, but you can validate structure.
function assertSm2CiphertextShape(ct, expectedPointBytes = 65) {
// Minimum: C1 (point) + C3 (32B digest) + C2 (>=0B)
if (ct.byteLength < expectedPointBytes + 32)
throw new TypeError("SM2 ciphertext too short to contain C1||C3||C2");
} Type guard
function looksLikeSm2CiphertextBlob(ct, minLen = 97) {
return ct instanceof Uint8Array && ct.byteLength >= minLen;
} Try / catch
import OperationError from "../errors/OperationError.mjs";
try {
const pt = sm2.decrypt(ct, privateKey);
} catch (e) {
if (e instanceof OperationError && /Computed Hashes Do Not Match/.test(e.message)) {
// integrity failure: do NOT return partial plaintext. Re-check key/ciphertext/field order.
} else throw e;
} Prevention
- Never treat a MAC mismatch as recoverable — the plaintext is unauthenticated.
- Confirm C1/C2/C3 ordering matches the implementation (GM/T 0003-2012 uses C1||C3||C2).
- Keep the encrypting public key and decrypting private key as a verified pair.
- Run round-trip encrypt+decrypt in tests for every key pair you ship.
When it happens
Trigger: SM2 decrypt called with a private key that does not correspond to the public key used to encrypt; ciphertext (c1,c2,c3) truncated, reordered, or byte-corrupted; the c3 tag was computed/encoded in a different order than this implementation expects (older SM2 spec ordered c1||c3||c2, newer GM/T 0003 orders c1||c2||c3).
Common situations: Mixing SM2 implementations that disagree on c1/c2/c3 ordering; key pair mismatch between encryptor and decryptor; base64/hex transport corruption of the ciphertext blob; decrypting data encrypted under a different SM2 curve or with a non-standard KDF.
Related errors
- Invalid Public Key
- Invalid PKCS#5 padding.
- Invalid BIT padding.
- Invalid ciphertext length: ${originalLength} bytes. Must be
- With ECB or CBC modes, the input must be divisible into 16 b
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/47d017edb51030ad.
Report an issue: GitHub.