{"record":{"id":"bc6cbf19b7e738f3","repo":"golang/go","slug":"ecdsa-sign-must-be-called-with-a-hash-not-with-c","errorCode":null,"errorMessage":"ecdsa: Sign must be called with a hash, not with crypto.Hash(0)","messagePattern":"ecdsa: Sign must be called with a hash, not with crypto\\.Hash\\(0\\)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/ecdsa/ecdsa.go","lineNumber":327,"sourceCode":"// with opts.HashFunc()) using the private key, priv. If the hash is longer than\n// the bit-length of the private key's curve order, the hash will be truncated\n// to that length. It returns the ASN.1 encoded signature, like [SignASN1].\n//\n// If random is not nil, the signature is randomized. Most applications should use\n// [crypto/rand.Reader] as random, but unless GODEBUG=cryptocustomrand=1 is set, a\n// secure source of random bytes is always used, and the actual Reader is ignored.\n// The GODEBUG setting will be removed in a future Go release. Instead, use\n// [testing/cryptotest.SetGlobalRandom].\n//\n// If random is nil, Sign will produce a deterministic signature according to RFC\n// 6979. When producing a deterministic signature, opts.HashFunc() must be the\n// function used to produce digest and priv.Curve must be one of\n// [elliptic.P224], [elliptic.P256], [elliptic.P384], or [elliptic.P521].\nfunc (priv *PrivateKey) Sign(random io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {\n\tif opts != nil {\n\t\th := opts.HashFunc()\n\t\tif h == 0 {\n\t\t\treturn nil, errors.New(\"ecdsa: Sign must be called with a hash, not with crypto.Hash(0)\")\n\t\t}\n\t\tif h.Size() != len(digest) {\n\t\t\treturn nil, errors.New(\"ecdsa: hash length does not match hash function\")\n\t\t}\n\t}\n\tif random == nil {\n\t\treturn signRFC6979(priv, digest, opts)\n\t}\n\trandom = rand.CustomReader(random)\n\treturn SignASN1(random, priv, digest)\n}\n\n// GenerateKey generates a new ECDSA private key for the specified curve.\n//\n// Since Go 1.26, a secure source of random bytes is always used, and the Reader is\n// ignored unless GODEBUG=cryptocustomrand=1 is set. This setting will be removed\n// in a future Go release. Instead, use [testing/cryptotest.SetGlobalRandom].\nfunc GenerateKey(c elliptic.Curve, r io.Reader) (*PrivateKey, error) {","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/ecdsa/ecdsa.go#L309-L345","documentation":"Thrown by PrivateKey.Sign() when opts is non-nil but opts.HashFunc() returns crypto.Hash(0), meaning Hash(0) (no hash). ECDSA signing via the crypto.Signer interface requires a hash to be specified — passing a zero-value Hash indicates the caller forgot to set the hash function or incorrectly claims the data is already hashed without specifying which hash.","triggerScenarios":"Calling priv.Sign(rand, digest, opts) where opts is non-nil but opts.HashFunc() == 0. This happens when a custom crypto.SignerOpts implementation returns 0, or when using crypto.Hash(0) explicitly. Note: passing opts == nil skips this check and goes to the RFC 6979 deterministic path (which then requires non-nil opts for the hash).","commonSituations":"Implementing a custom crypto.SignerOpts that doesn't properly set the hash; passing a zero-value opts struct; incorrect integration with libraries that expect SignerOpts to always specify a real hash; confusing the nil-opts path (deterministic) with the zero-hash path (rejected).","solutions":["Ensure opts.HashFunc() returns a valid crypto.Hash value (e.g., crypto.SHA256) that matches the hash used to produce the digest.","If the data is not pre-hashed and you want to sign raw bytes, hash them first with the appropriate hash function, then pass matching opts.","If you truly want deterministic signing, pass opts as a properly configured crypto.SignerOpts (not nil, with a real hash)."],"exampleFix":"// before\nopts := struct{ hash crypto.Hash }{hash: 0}\nsig, err := priv.Sign(rand.Reader, digest, opts)\n\n// after\nsig, err := priv.Sign(rand.Reader, digest, crypto.SHA256) // or a SignerOpts returning crypto.SHA256","handlingStrategy":"validation","validationCode":"func validateSignerOpts(opts crypto.SignerOpts) error {\n    if opts == nil { return nil } // nil opts is handled separately\n    if opts.HashFunc() == 0 {\n        return errors.New(\"SignerOpts must specify a non-zero hash\")\n    }\n    return nil\n}","typeGuard":"func hasValidHash(opts crypto.SignerOpts) bool {\n    return opts == nil || opts.HashFunc() != 0\n}","tryCatchPattern":"sig, err := priv.Sign(rand.Reader, digest, opts)\nif err != nil && strings.Contains(err.Error(), \"crypto.Hash(0)\") {\n    return fmt.Errorf(\"must specify a real hash function in SignerOpts: %w\", err)\n}","preventionTips":["Always pass a concrete crypto.Hash (e.g., crypto.SHA256) as opts when the digest is pre-hashed.","Use crypto.Hash values as the opts parameter directly since crypto.Hash implements SignerOpts."],"tags":["crypto","ecdsa","signing","hash","input-validation","api-misuse"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}