{"record":{"id":"15fda29a90f796eb","repo":"slackhq/nebula","slug":"could-not-calculate-alternate-fingerprint-to-verif","errorCode":null,"errorMessage":"could not calculate alternate fingerprint to verify: %w","messagePattern":"could not calculate alternate fingerprint to verify: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cert/ca_pool.go","lineNumber":177,"sourceCode":"\t\treturn nil, fmt.Errorf(\"no certificate\")\n\t}\n\tfp, err := c.Fingerprint()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not calculate fingerprint to verify: %w\", err)\n\t}\n\n\tsigner, err := ncp.verify(c, now, fp, \"\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Pre nebula v1.10.3 could generate signatures in either high or low s form and validation\n\t// of signatures allowed for either. Nebula v1.10.3 and beyond clamps signature generation to low-s form\n\t// but validation still allows for either. Since a change in the signature bytes affects the fingerprint, we\n\t// need to test both forms until such a time comes that we enforce low-s form on signature validation.\n\tfp2, err := CalculateAlternateFingerprint(c)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not calculate alternate fingerprint to verify: %w\", err)\n\t}\n\tif fp2 != \"\" && ncp.IsBlocklisted(fp2) {\n\t\treturn nil, ErrBlockListed\n\t}\n\n\tcc := CachedCertificate{\n\t\tCertificate:       c,\n\t\tInvertedGroups:    make(map[string]struct{}),\n\t\tFingerprint:       fp,\n\t\tfingerprint2:      fp2,\n\t\tsignerFingerprint: signer.Fingerprint,\n\t}\n\n\tfor _, g := range c.Groups() {\n\t\tcc.InvertedGroups[g] = struct{}{}\n\t}\n\n\treturn &cc, nil","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/cert/ca_pool.go#L159-L195","documentation":"VerifyCertificate computes a second (alternate) fingerprint of the certificate by swapping the ECDSA signature's s value between high-s and low-s form, so certificates signed before nebula v1.10.3 (which allowed high-s signatures) can still be blocklist-checked. This wrapper error means the underlying CalculateAlternateFingerprint call failed while computing that alternate form; the original parse/swap error is wrapped via %w.","triggerScenarios":"Calling CAPool.VerifyCertificate on a P256 certificate whose signature bytes cannot be swapped by p256.Swap (e.g. a malformed or truncated ECDSA signature in the cert). Non-P256 certificates short-circuit and return \"\", so only P256 certs with bad signature encoding reach this failure.","commonSituations":"Verifying a hand-crafted or corrupted certificate; a certificate produced by a third-party CA tool that emitted a non-canonical/malformed ECDSA signature; fuzzed or truncated certificate bytes loaded from a bad config source.","solutions":["Inspect the wrapped error (%w) to see the exact p256.Swap failure","Re-export or re-issue the certificate so it carries a well-formed ECDSA signature","Verify the certificate bytes were not truncated/corrupted in transit or in config","If the cert is not needed for blocklist checks, validate its signature directly with Certificate.CheckSignature instead of relying on VerifyCertificate's fingerprint path"],"exampleFix":"// before\ncc, err := pool.VerifyCertificate(time.Now(), cert) // fails: could not calculate alternate fingerprint\n// after\nif err := cert.CheckSignature(caPubKey); err != nil { /* fix/reissue cert first */ }\ncc, err := pool.VerifyCertificate(time.Now(), cert)","handlingStrategy":"try-catch","validationCode":"if cert.Curve() == cert.Curve_P256 {\n    // ensure signature parses before VerifyCertificate\n    if !cert.CheckSignature(caPubKey) {\n        return fmt.Errorf(\"certificate signature malformed; alternate fingerprint will fail\")\n    }\n}","typeGuard":null,"tryCatchPattern":"cc, err := pool.VerifyCertificate(now, c)\nif err != nil {\n    var altFpErr bool\n    if strings.Contains(err.Error(), \"alternate fingerprint\") {\n        altFpErr = true // malformed P256 signature; treat cert as corrupt\n    }\n    return fmt.Errorf(\"verify failed (altFp=%v): %w\", altFpErr, err)\n}","preventionTips":["Only load certificates from trusted, intact sources","Validate cert signatures with CheckSignature before relying on fingerprint paths","Watch for wrapped errors with errors.Unwrap to find the root cause","Pin to library versions >= v1.10.3 where signatures are canonical low-s"],"tags":["certificate","fingerprint","p256","signature"],"backgroundTag":"certificate-fingerprint-error","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}