{"record":{"id":"47d017edb51030ad","repo":"gchq/CyberChef","slug":"decryption-error-computed-hashes-do-not-match","errorCode":null,"errorMessage":"Decryption Error -- Computed Hashes Do Not Match","messagePattern":"Decryption Error -- Computed Hashes Do Not Match","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/lib/SM2.mjs","lineNumber":161,"sourceCode":"        /*\n        * Compute the p2 (secret) value by taking the C1 point provided in the encrypted package, and multiplying by the private k value\n        */\n        const p2 = c1.multiply(this.privateKey);\n\n        /*\n         * Similar to encryption; compute sufficient length key material and XOR the input data to recover the original message\n         */\n        const key = this.kdf(p2, c2.byteLength);\n\n        for (let i = 0; i < c2.byteLength; i++) {\n            c2[i] ^= Utils.ord(key[i]);\n        }\n\n        const check = this.c3(p2, c2);\n        if (check === c3) {\n            return c2.buffer;\n        } else {\n            throw new OperationError(\"Decryption Error -- Computed Hashes Do Not Match\");\n        }\n    }\n\n\n    /**\n     * Generates a large random number\n     *\n     * @param {*} limit\n     * @returns\n     */\n    getBigRandom(limit) {\n        return new r.BigInteger(limit.bitLength(), this.rng)\n\t    .mod(limit.subtract(r.BigInteger.ONE))\n\t    .add(r.BigInteger.ONE);\n    }\n\n    /**\n     * Helper function for generating a large random K number; utilized for generating our initial C1 point","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/lib/SM2.mjs#L143-L179","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before: ciphertext from a library that orders fields C1||C2||C3\nconst pt = sm2.decrypt(ct, privateKey);\n// after: reorder to C1||C3||C2 as this implementation expects\nconst reordered = concat(c1, c3, c2);\nconst pt = sm2.decrypt(reordered, privateKey);","handlingStrategy":"try-catch","validationCode":"// Cannot pre-validate the MAC without decrypting, but you can validate structure.\nfunction assertSm2CiphertextShape(ct, expectedPointBytes = 65) {\n  // Minimum: C1 (point) + C3 (32B digest) + C2 (>=0B)\n  if (ct.byteLength < expectedPointBytes + 32)\n    throw new TypeError(\"SM2 ciphertext too short to contain C1||C3||C2\");\n}","typeGuard":"function looksLikeSm2CiphertextBlob(ct, minLen = 97) {\n  return ct instanceof Uint8Array && ct.byteLength >= minLen;\n}","tryCatchPattern":"import OperationError from \"../errors/OperationError.mjs\";\ntry {\n  const pt = sm2.decrypt(ct, privateKey);\n} catch (e) {\n  if (e instanceof OperationError && /Computed Hashes Do Not Match/.test(e.message)) {\n    // integrity failure: do NOT return partial plaintext. Re-check key/ciphertext/field order.\n  } else throw e;\n}","preventionTips":["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."],"tags":["sm2","ecc","decryption","integrity","mac-failure"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}