{"record":{"id":"09ab50424941e126","repo":"OpenNHP/opennhp","slug":"invalid-public-key-length-got-d-want-32","errorCode":null,"errorMessage":"invalid public key length: got %d, want 32","messagePattern":"invalid public key length: got (.+?), want 32","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nhp/core/verifier/csv/csv.go","lineNumber":245,"sourceCode":"\t// x1, y1 = [r]G\n\t// x2, y2 = [s]PubKey\n\tx1, y1 := pub.Curve.ScalarBaseMult(s.Bytes())\n\tx2, y2 := pub.Curve.ScalarMult(pub.X, pub.Y, t.Bytes())\n\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 {","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/nhp/core/verifier/csv/csv.go#L227-L263","documentation":"Attestation.verifySm2SignatureWithId verifies an SM2 signature over the quote/attestation data and requires the public key coordinates qx and qy to each be exactly 32 bytes (big-endian, later byte-reversed for the CSV/Huawei format). It throws \"invalid public key length: got %d, want 32\" when either coordinate is not 32 bytes — typically because the key came from a big.Int.Bytes() that strips leading zero bytes, or the attestation blob was parsed with wrong offsets.","triggerScenarios":"verifyCertChain or Verify extracting the attestation public key from a CSV quote where the coordinate fields were parsed at wrong offsets/lengths, or where a coordinate with leading zero bytes was converted via big.Int.Bytes() (yielding <32 bytes) before being passed in.","commonSituations":"Parsing quote structures from different TEE/CSV firmware versions with changed field layouts; converting pub.X.Bytes()/pub.Y.Bytes() directly into 32-byte slots without left-padding, so small-coordinate keys (probability ~1/256 per coordinate) fail; hand-crafted or corrupted attestation reports; mixing little-endian raw fields with this function's expectations.","solutions":["Left-pad each coordinate to 32 bytes before calling: copy into a fixed [32]byte buffer aligned to the end (big-endian) rather than using big.Int.Bytes() directly.","Verify the offset/length used to slice qx/qy out of the attestation quote matches the CSV report specification for your firmware version.","Check the quote's own reported key length field and reject malformed reports upstream with a clearer message.","Confirm the attestation report was not truncated or corrupted in transit (compare sizes against the expected quote structure)."],"exampleFix":"// before\nqx := pub.X.Bytes() // may be < 32 bytes if leading zeros stripped\nattestation.verifySm2SignatureWithId(qx, qy, r, s, id, msg) // \"invalid public key length\"\n\n// after\nfunc fixed32(n *big.Int) []byte {\n    b := make([]byte, 32)\n    nb := n.Bytes()\n    copy(b[32-len(nb):], nb)\n    return b\n}\nattestation.verifySm2SignatureWithId(fixed32(pub.X), fixed32(pub.Y), fixed32(sigR), fixed32(sigS), id, msg)","handlingStrategy":"validation","validationCode":"func coord32(n []byte) ([]byte, error) {\n    if len(n) > 32 {\n        return nil, fmt.Errorf(\"coordinate too long: %d\", len(n))\n    }\n    out := make([]byte, 32)\n    copy(out[32-len(n):], n) // left-pad, big-endian\n    return out, nil\n}\n// before calling: qx, err := coord32(pub.X.Bytes()); qy, err := coord32(pub.Y.Bytes())","typeGuard":"func has32ByteCoords(qx, qy []byte) bool {\n    return len(qx) == 32 && len(qy) == 32\n}","tryCatchPattern":"if err := attestation.verifySm2SignatureWithId(qx, qy, r, s, id, msg); err != nil {\n    if strings.Contains(err.Error(), \"invalid public key length\") {\n        return fmt.Errorf(\"attestation pubkey not in fixed 32-byte big-endian form: %w\", err)\n    }\n    return err\n}","preventionTips":["Never pass big.Int.Bytes() output directly as a fixed-width coordinate; always left-pad to 32 bytes","Validate quote field offsets against the CSV specification version you target","Check length fields in the attestation report before slicing qx/qy out of the raw blob","Add unit tests with keys whose coordinates have leading zero bytes (small X or Y)"],"tags":["go","crypto","sm2","attestation","csv-tee"],"backgroundTag":"invalid-argument-value","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"}