integrity

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 15, 2026 License: MIT Imports: 10 Imported by: 0

README

integrity

Parse Subresource Integrity metadata and verify package streams while they are read. The module supports SHA-256, SHA-384, and SHA-512 using only the Go standard library.

Install

go get github.com/git-pkgs/integrity

Parse and format SRI metadata

metadata, err := integrity.ParseSRI(
	"sha256-LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=",
)
if err != nil {
	return err
}

fmt.Println(metadata[0].Algorithm())
fmt.Println(metadata[0].Hex())
fmt.Println(integrity.FormatSRI(metadata))

ParseSRI accepts surrounding whitespace, metadata lists containing more than one hash, and standard or URL-safe base64 with optional padding. It ignores options because SRI does not define any yet. FormatSRI emits lower-case algorithm names, padded standard base64, and one space between entries. ParseHex constructs a digest from the hexadecimal values commonly stored by package caches and SBOMs.

All constructors check the digest length for its algorithm. Digest.Bytes returns a copy, while Digest.Hex and Digest.SRI return canonical encodings.

Verify a stream

expected, err := integrity.ParseSRI(
	"sha512-19P5Un6URGs8lrxEAotLVa/3dYfU1acO+ddf//wkvY4vz/SLa8BPaKNTTOYu6gjBR5JW8NwZfAdsBMYNWiS41A==",
)
if err != nil {
	return err
}

defer response.Body.Close()

reader, err := integrity.NewReader(response.Body, integrity.SHA256, integrity.SHA512)
if err != nil {
	return err
}

temporary, err := os.CreateTemp(filepath.Dir(destinationPath), ".package-*")
if err != nil {
	return err
}
temporaryPath := temporary.Name()
defer os.Remove(temporaryPath)
defer temporary.Close()

if _, err := io.Copy(temporary, reader); err != nil {
	return err
}

result := reader.Result()
if err := result.Verify(expected); err != nil {
	return err
}
if err := temporary.Close(); err != nil {
	return err
}
if err := os.Rename(temporaryPath, destinationPath); err != nil {
	return err
}
fmt.Println(result.Bytes)

The reader calculates each requested algorithm once and counts every byte returned to the caller. A result becomes complete after the reader observes EOF. Verification before EOF returns ErrIncomplete, including when the source was closed after a partial read.

SRI verification uses the strongest supported algorithm in the metadata list. Any digest using that algorithm can match. A matching weaker digest does not replace a mismatch from a stronger algorithm.

The caller must close the source and keep copied bytes private until verification succeeds. Write cache entries to a temporary file or object, then commit them after Verify returns nil. Reporting failures and cache policy remain with the caller.

Development

Run the tests and race detector:

go test -race ./...

Run the linters and vulnerability scan:

make lint

Run each fuzz target:

go test -fuzz=FuzzParseSRI -fuzztime=30s
go test -fuzz=FuzzSRIRoundTrip -fuzztime=30s

Run the benchmarks:

go test -run '^$' -bench . -benchmem

License

MIT

Documentation

Overview

Package integrity parses Subresource Integrity metadata and verifies streams against cryptographic digests.

It supports SHA-256, SHA-384, and SHA-512 without depending on HTTP, storage, or package-manager types.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func FormatSRI

func FormatSRI(sri SRI) string

FormatSRI formats a metadata list with lower-case algorithm names, padded standard base64, and one space between entries.

Types

type Algorithm

type Algorithm uint8

Algorithm identifies a supported digest algorithm.

const (
	// SHA256 identifies SHA-256 digests.
	SHA256 Algorithm = iota
	// SHA384 identifies SHA-384 digests.
	SHA384
	// SHA512 identifies SHA-512 digests.
	SHA512
)

func (Algorithm) String

func (a Algorithm) String() string

String returns the lower-case SRI name for the algorithm.

type Digest

type Digest struct {
	// contains filtered or unexported fields
}

Digest is an immutable algorithm and digest-byte pair.

Use ParseHex or ParseSRI to construct a Digest. Bytes returns a copy of the stored bytes.

func ParseHex

func ParseHex(algorithm Algorithm, value string) (Digest, error)

ParseHex parses a hexadecimal digest for algorithm.

func (Digest) Algorithm

func (d Digest) Algorithm() Algorithm

Algorithm returns the digest algorithm.

func (Digest) Bytes

func (d Digest) Bytes() []byte

Bytes returns a copy of the raw digest bytes.

func (Digest) Equal

func (d Digest) Equal(other Digest) bool

Equal reports whether two digests use the same algorithm and contain the same raw bytes. The byte comparison takes constant time for equal-length values.

func (Digest) Hex

func (d Digest) Hex() string

Hex returns the canonical lower-case hexadecimal encoding.

func (Digest) SRI

func (d Digest) SRI() string

SRI returns the canonical Subresource Integrity encoding.

type ErrIncomplete

type ErrIncomplete struct{}

ErrIncomplete reports that verification was requested before the reader observed EOF.

func (*ErrIncomplete) Error

func (*ErrIncomplete) Error() string

Error implements error.

type Reader

type Reader struct {
	// contains filtered or unexported fields
}

Reader calculates requested digests as bytes pass through it. Bytes remain unverified until a completed Result passes Verify.

Example
package main

import (
	"fmt"
	"io"
	"strings"

	"github.com/git-pkgs/integrity"
)

func main() {
	expected, err := integrity.ParseSRI("sha256-LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=")
	if err != nil {
		panic(err)
	}
	reader, err := integrity.NewReader(strings.NewReader("hello"), integrity.SHA256)
	if err != nil {
		panic(err)
	}
	if _, err := io.Copy(io.Discard, reader); err != nil {
		panic(err)
	}

	result := reader.Result()
	fmt.Println(result.Bytes, result.Complete)
	fmt.Println(result.Verify(expected) == nil)
}
Output:
5 true
true

func NewReader

func NewReader(source io.Reader, algorithms ...Algorithm) (*Reader, error)

NewReader returns a reader that calculates each requested algorithm once.

func (*Reader) Read

func (r *Reader) Read(p []byte) (int, error)

Read passes bytes from the source through each requested digest calculator.

func (*Reader) Result

func (r *Reader) Result() Result

Result returns a snapshot. Calling Result does not finish or reset the reader.

type Result

type Result struct {
	Digests  []Digest
	Bytes    int64
	Complete bool
}

Result is a snapshot of a Reader's calculated digests and progress.

func (Result) Verify

func (r Result) Verify(expected SRI) error

Verify applies the W3C SRI matching rule to a completed result. Only the strongest algorithm in expected is considered, and any digest using that algorithm may match.

type SRI

type SRI []Digest

SRI is a Subresource Integrity metadata list.

func ParseSRI

func ParseSRI(value string) (SRI, error)

ParseSRI parses a whitespace-separated Subresource Integrity metadata list. It accepts standard and URL-safe base64 with optional padding and ignores options, which SRI currently leaves undefined.

Example
package main

import (
	"fmt"

	"github.com/git-pkgs/integrity"
)

func main() {
	metadata, err := integrity.ParseSRI("sha256-LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=")
	if err != nil {
		panic(err)
	}

	fmt.Println(metadata[0].Algorithm())
	fmt.Println(metadata[0].Hex())
}
Output:
sha256
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL