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 point

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Verify the private key being used is the partner of the public key the sender encrypted to.
  2. Confirm the ciphertext fields (c1, c2, c3) are in the layout this implementation expects (C1||C3||C2 per GM/T 0003-2012).
  3. Re-extract the ciphertext through a clean base64/hex path and check for truncation.
  4. 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

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


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