OpenNHP/opennhp · error

failed to create SM4 cipher for CBC

Error message

failed to create SM4 cipher for CBC: %w

What it means

CBCEncryption(GCM_SM4, key, ...) wraps sm4.NewCipher(key[:16]) failure as this message. gmsm's sm4.NewCipher fails only for key lengths other than 16 bytes; because OpenNHP slices the first 16 bytes of the fixed 32-byte key array, failure implies malformed/nil key material or a non-standard gmsm dependency. It occurs while initializing the SM4-CBC block cipher (IV from key[16:]).

Solutions

  1. Ensure the SM2 ECDH/KDF step fully populated the 32-byte key before calling CBCEncryption
  2. Log the wrapped error to capture sm4.NewCipher's underlying key-length message
  3. Pin a known-good github.com/emmansun/gmsm version and run go mod tidy
  4. Add a caller-side check that the key is non-zero before encryption
  5. Verify with go test ./nhp/core/... (TestGMSharedKey covers SM4 paths)

Example fix

// before
shared := sm2Ecdh.SharedSecret(peerPub) // empty on failed exchange
ciphertext, _ := core.CBCEncryption(core.GCM_SM4, (*[core.SymmetricKeySize]byte)(shared), plaintext, false)
// after
shared := sm2Ecdh.SharedSecret(peerPub)
if len(shared) == 0 {
    return errors.New("SM2 key exchange failed: empty shared secret")
}
var key [core.SymmetricKeySize]byte
utils.Memcpy(key[:], kdf(shared))
ciphertext, err := core.CBCEncryption(core.GCM_SM4, &key, plaintext, false)
if err != nil {
    return fmt.Errorf("sm4 cbc encrypt: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

if key == nil {
    return errors.New("SM4-CBC key not initialized")
}
if len(sharedSecret) == 0 {
    return errors.New("empty shared secret - SM2 key exchange failed")
}

Try / catch

ciphertext, err := core.CBCEncryption(core.GCM_SM4, &key, plaintext, false)
if err != nil {
    if errors.Is(err, core.ErrNotApplicable) {
        return errors.New("SM4-CBC not applicable for this cipher type")
    }
    return fmt.Errorf("SM4-CBC encrypt failed: %w", err)
}

Prevention

When it happens

Trigger: Calling CBCEncryption with GCM_SM4 and a nil pointer or improperly sized array (e.g. via unsafe casts), or after gmsm library changes to NewCipher semantics; not triggerable with correct 32-byte key arrays.

Common situations: GMSM-scheme bulk encryption paths where the SM2 key exchange silently failed leaving a zeroed key; custom code constructing shorter key buffers; pinning an incompatible emmansun/gmsm version.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07). Data as JSON: /api/errors/79b83ddc61f903fc. Report an issue: GitHub.

Appendix: source

Thrown at nhp/core/crypto.go:202

	}
}

func CBCEncryption(t GcmTypeEnum, key *[SymmetricKeySize]byte, plaintext []byte, inPlace bool) ([]byte, error) {
	var block cipher.Block
	var iv []byte
	var err error
	switch t {
	case GCM_AES256:
		block, err = aes.NewCipher(key[:])
		if err != nil {
			return nil, fmt.Errorf("failed to create AES cipher for CBC: %w", err)
		}
		iv = key[8:24]

	case GCM_SM4:
		block, err = sm4.NewCipher(key[:16])
		if err != nil {
			return nil, fmt.Errorf("failed to create SM4 cipher for CBC: %w", err)
		}
		iv = key[16:]

	case GCM_CHACHA20POLY1305:
		return nil, ErrNotApplicable

	default:
		return nil, fmt.Errorf("unsupported cipher type for CBC: %d", t)
	}

	var paddedPlainText []byte
	if len(plaintext)%block.BlockSize() == 0 {
		// skip padding
		paddedPlainText = plaintext
	} else {
		pkcs7 := padding.NewPKCS7Padding(uint(block.BlockSize()))
		paddedPlainText = pkcs7.Pad(plaintext)
	}

View on GitHub (pinned to 6e04ca5ff0)