{"record":{"id":"14341fc2144e2bdb","repo":"kopia/kopia","slug":"error-hashing","errorCode":null,"errorMessage":"error hashing","messagePattern":"error hashing","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/hmac/hmac.go","lineNumber":38,"sourceCode":"\n\tvar hash [sha256.Size]byte\n\n\toutput.Write(h.Sum(hash[:0])) //nolint:errcheck\n}\n\n// VerifyAndStrip verifies that given block of bytes has correct HMAC-SHA256 checksum and strips it.\nfunc VerifyAndStrip(input gather.Bytes, secret []byte, output *gather.WriteBuffer) error {\n\tif input.Length() < sha256.Size {\n\t\treturn errors.New(\"invalid data - too short\")\n\t}\n\n\tp := input.Length() - sha256.Size\n\n\th := hmac.New(sha256.New, secret)\n\tr := input.Reader()\n\n\tif _, err := io.CopyN(io.MultiWriter(h, output), r, int64(p)); err != nil {\n\t\treturn errors.Wrap(err, \"error hashing\")\n\t}\n\n\tvar sigBuf, actualSignature [sha256.Size]byte\n\n\tvalidSignature := h.Sum(sigBuf[:0])\n\n\tn, err := r.Read(actualSignature[:])\n\tif err != nil || n != sha256.Size {\n\t\treturn errors.Wrap(err, \"error reading signature\")\n\t}\n\n\tif hmac.Equal(validSignature, actualSignature[:]) {\n\t\treturn nil\n\t}\n\n\treturn errors.New(\"invalid data - corrupted\")\n}\n","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/kopia/kopia/blob/82495e54b584c1ef6073c9e1be048f57f8aef078/internal/hmac/hmac.go#L20-L56","documentation":"VerifyAndStrip validates an HMAC-SHA256 signature appended to the end of an input byte range and, while doing so, streams the payload into both the HMAC and the output writer via io.CopyN. This error wraps any failure of that copy — typically the input is SHORTER than expected (fewer than p bytes available), the underlying reader fails, or the output writer fails.","triggerScenarios":"Calling Verify (or reading blobs from cache) where input.Length() - sha256.Size exceeds the actual readable bytes, the underlying storage reader errors mid-copy, or the destination output writer fails while draining the payload.","commonSituations":"Truncated or corrupted cache files whose declared length exceeds real content; storage backend read errors during verification; concurrent modification shrinking the data between Length() and read.","solutions":["Check for the wrapped cause: an io.ErrUnexpectedEOF/EOF usually means the input is truncated — delete the corrupted cache entry and re-fetch","Verify input.Length() is accurate and the signature suffix (last sha256.Size bytes) is present before calling VerifyAndStrip","Re-download or re-verify the blob from the primary storage if the cache copy is truncated","If the output writer is the failure, ensure it is open and writable before verification"],"exampleFix":"// before\nif _, err := io.CopyN(io.MultiWriter(h, output), r, int64(p)); err != nil {\n    return errors.Wrap(err, \"error hashing\")\n}\n// after\nif _, err := io.CopyN(io.MultiWriter(h, output), r, int64(p)); err != nil {\n    return errors.Wrapf(err, \"error hashing %d-byte payload (input length %d)\", p, input.Length())\n}","handlingStrategy":"validation","validationCode":"// verify the input plausibly contains payload + signature before verifying\nif input.Length() < sha256.Size {\n    return errors.New(\"input too short to contain HMAC signature\")\n}","typeGuard":null,"tryCatchPattern":"if err := h.VerifyAndStrip(input, output, secret); err != nil {\n    if stderrors.Is(err, io.ErrUnexpectedEOF) || stderrors.Is(err, io.EOF) {\n        // treat as corrupted/truncated cache entry: invalidate and re-fetch\n    }\n    return fmt.Errorf(\"verification failed: %w\", err)\n}","preventionTips":["Validate cached blob lengths match metadata before verification","Delete and re-fetch cache entries on any read/verification error","Avoid mutating byte ranges concurrently with VerifyAndStrip","Detect signature suffix presence (last 32 bytes) before attempting verification"],"tags":["go","hmac","io"],"backgroundTag":"checksum-mismatch","analyzedSha":"82495e54b584c1ef6073c9e1be048f57f8aef078","analyzedAt":"2026-09-07T20:35:21.689Z","contentChangedAt":"2026-09-07T20:35:21.689Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}