{"record":{"id":"80ea06696de74909","repo":"OpenNHP/opennhp","slug":"invalid-signature-length-got-d-want-32","errorCode":null,"errorMessage":"invalid signature length: got %d, want 32","messagePattern":"invalid signature length: got (.+?), want 32","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nhp/core/verifier/csv/csv.go","lineNumber":249,"sourceCode":"\n\tif x1.Cmp(x2) == 0 && y1.Cmp(y2) == 0 {\n\t\t// x1, y1 = Double(x1, y1)\n\t\tx1, y1 = pub.Curve.Double(x1, y1)\n\t} else {\n\t\t// x1, y1 = x1 + x2, y1 + y2\n\t\tx1, y1 = pub.Curve.Add(x1, y1, x2, y2)\n\t}\n\n\treturn r.Cmp(new(big.Int).Mod(new(big.Int).Add(x1, e), sm2_N)) == 0\n}\n\nfunc (a *Attestation) verifySm2SignatureWithId(qx, qy, r, s []byte, id []byte, msg []byte) error {\n\tif len(qx) != 32 || len(qy) != 32 {\n\t\treturn fmt.Errorf(\"invalid public key length: got %d, want 32\", len(qx))\n\t}\n\n\tif len(r) != 32 || len(s) != 32 {\n\t\treturn fmt.Errorf(\"invalid signature length: got %d, want 32\", len(r))\n\t}\n\n\tqx = ReverseBytes(qx)\n\tqy = ReverseBytes(qy)\n\tr = ReverseBytes(r)\n\ts = ReverseBytes(s)\n\n\tpubKeyBytes := append(qx, qy...)\n\tpubKeyHex := hex.EncodeToString(pubKeyBytes)\n\n\tid_msg := buildIDMsg(id, len(id), ECKEY, pubKeyHex)\n\n\tza, err := Sm3Digest(id_msg)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tmsgAll := append(za, msg...)","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/nhp/core/verifier/csv/csv.go#L231-L267","documentation":"Attestation.verifySm2SignatureWithId requires the signature components r and s to each be exactly 32 bytes, because they are byte-reversed and assembled into the SM2 verification equation per GB/T 32918. It throws \"invalid signature length: got %d, want 32\" when either r or s is not 32 bytes — typically a DER-encoded or ASN.1-style signature, or a minimal-length big.Int encoding that dropped leading zero bytes.","triggerScenarios":"verifyCertChain or Verify passing signature bytes decoded from a DER/ASN.1 SEQUENCE(r,s) blob (variable length, ~70 bytes) instead of the raw 64-byte r||s form, or splitting a 64-byte raw signature at a wrong offset, or using big.Int.Bytes() on r/s without left-padding to 32 bytes.","commonSituations":"Signatures produced by standard Go crypto/ecdsa SignAsn1 or OpenSSL (DER) fed into the CSV verifier expecting fixed-width 32-byte components; signatures extracted from quotes with off-by-one offsets; small r or s values losing leading zero bytes when serialized via big.Int.Bytes(); corrupted attestation payloads.","solutions":["Ensure the signature is in raw 64-byte r||s form: if you have DER, parse the ASN.1 SEQUENCE and extract r and s individually, then left-pad each to 32 bytes.","Left-pad r and s to exactly 32 bytes (big-endian) before calling, instead of using big.Int.Bytes() output directly.","Verify the offsets used to slice r and s from the attestation quote/structure match the expected 32+32 layout.","Log len(r) and len(s) at the call site to identify whether one component or both are malformed, then fix the producer side accordingly."],"exampleFix":"// before\nrBytes := sigR.Bytes() // 31 bytes when r has a leading zero -> \"invalid signature length\"\nrBytes = bigIntTo32(sigR) // 32-byte left-padded, byte-reversed\nsBytes = bigIntTo32(sigS)\nattestation.verifySm2SignatureWithId(qx, qy, rBytes, sBytes, id, msg)","handlingStrategy":"validation","validationCode":"func sigComponent32(n *big.Int) []byte {\n    b := make([]byte, 32)\n    nb := n.Bytes()\n    copy(b[32-len(nb):], nb) // left-pad to fixed 32 bytes, big-endian\n    return b\n}\n// before calling: r := sigComponent32(sigR); s := sigComponent32(sigS)","typeGuard":"func isRaw64ByteSig(sig []byte) bool {\n    return len(sig) == 64 // raw r||s form; DER signatures (~70-72 bytes) must be converted first\n}","tryCatchPattern":"if err := attestation.verifySm2SignatureWithId(qx, qy, r, s, id, msg); err != nil {\n    if strings.Contains(err.Error(), \"invalid signature length\") {\n        return fmt.Errorf(\"signature must be fixed 32-byte r and s (raw r||s, not DER): %w\", err)\n    }\n    return err\n}","preventionTips":["Convert DER/ASN.1 ECDSA signatures to raw 64-byte r||s before feeding the CSV verifier","Always left-pad r and s to 32 bytes rather than relying on big.Int.Bytes() minimal encoding","Confirm the 32/32 split offsets when extracting r and s from an attestation quote","Test verification with signatures where r or s has leading zero bytes to catch padding bugs early"],"tags":["go","crypto","sm2","signature","attestation"],"backgroundTag":"invalid-argument-format","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}