jumpserver/jumpserver · error · ValueError

Invalid SM3 digest size

Error message

Invalid SM3 digest size

What it means

ValueError raised by sign when the digest argument is not exactly SM3_DIGEST_SIZE (32) bytes. SM2 signs an SM3 digest, so the input must be the fixed 32-byte SM3 output; the wrapper enforces this before invoking the native signer.

Source

Thrown at apps/common/utils/gmssl_python.py:485

		fp = libc.fopen(path.encode('utf-8'), 'wb')
		if gmssl.sm2_public_key_info_to_pem(byref(self), c_void_p(fp)) != 1:
			raise NativeError('libgmssl inner error')
		libc.fclose(c_void_p(fp))

	def import_public_key_info_pem(self, path):
		libc.fopen.restype = c_void_p
		fp = libc.fopen(path.encode('utf-8'), 'rb')
		if gmssl.sm2_public_key_info_from_pem(byref(self), c_void_p(fp)) != 1:
			raise NativeError('libgmssl inner error')
		libc.fclose(c_void_p(fp))
		self._has_public_key = True
		self._has_private_key = False

	def sign(self, dgst):
		if self._has_private_key == False:
			raise TypeError('has no private key')
		if len(dgst) != SM3_DIGEST_SIZE:
			raise ValueError('Invalid SM3 digest size')
		sig = create_string_buffer(SM2_MAX_SIGNATURE_SIZE)
		siglen = c_size_t()
		if gmssl.sm2_sign(byref(self), dgst, sig, byref(siglen)) != 1:
			raise NativeError('libgmssl inner error')
		return sig[:siglen.value]

	def verify(self, dgst, signature):
		if self._has_public_key == False:
			raise TypeError('has no public key')
		if len(dgst) != SM3_DIGEST_SIZE:
			raise ValueError('Invalid SM3 digest size')
		if gmssl.sm2_verify(byref(self), dgst, signature, c_size_t(len(signature))) != 1:
			return False
		return True

	def encrypt(self, data):
		if self._has_public_key == False:
			raise TypeError('has no public key')

View on GitHub (pinned to 6ec464fabd)

Solutions

  1. Hash with SM3 first: dgst = SM3().update(msg).finish() (or the module's sm3 helper) yielding 32 bytes
  2. Pass bytes, not hex strings: bytes.fromhex(hex_digest) if you only have hex
  3. Double-check the digest pipeline length: assert len(dgst) == 32 before signing

Example fix

// before
sig = sm2.sign('a1b2...32-byte-hex-string...')   # 64-char str -> ValueError
// after
from gmssl_python import SM3
dgst = SM3(bytes(msg, 'utf-8')).finish() if hasattr(SM3, '__call__') else sm3_digest(msg)
sig = sm2.sign(dgst)  # exactly 32 bytes
Defensive patterns

Strategy: validation

Validate before calling

from gmssl_python import SM3_DIGEST_SIZE
dgst = bytes.fromhex(dgst_hex) if isinstance(dgst, str) else dgst
assert len(dgst) == SM3_DIGEST_SIZE, f'digest must be {SM3_DIGEST_SIZE} bytes'

Type guard

def is_sm3_digest(d) -> bool:
    return isinstance(d, (bytes, bytearray)) and len(d) == 32

Try / catch

try:
    sig = sm2.sign(dgst)
except ValueError as e:
    if 'digest size' in str(e):
        raise ValueError('hash the message with SM3 before signing') from None
    raise

Prevention

When it happens

Trigger: Calling sign(dgst) with a SHA-256 digest (32 bytes but semantically wrong is still accepted-length-wise; a 16/20/64-byte digest raises), with a hex-encoded digest string (64 chars), or with a raw message instead of its digest.

Common situations: Hashing with SHA-1/SHA-512 instead of SM3; passing hex digests from logs; signing the message itself rather than sm3(msg); using str instead of bytes.

Related errors


AI-assisted analysis of jumpserver/jumpserver@6ec464fabd (2026-08-28). Data as JSON: /api/errors/1704ea84b0355096. Report an issue: GitHub.