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
- Hash with SM3 first: dgst = SM3().update(msg).finish() (or the module's sm3 helper) yielding 32 bytes
- Pass bytes, not hex strings: bytes.fromhex(hex_digest) if you only have hex
- 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
- Always hash with SM3, not SHA-256, in SM2 workflows
- Keep digests as bytes; convert hex only at display boundaries
- Assert len(digest)==32 in helper functions
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.