RFC 9530 Digest Fields: build, parse, and verify Content-Digest,
Repr-Digest, Want-Content-Digest, and Want-Repr-Digest.
Content vs Representation
Content-Digest covers the actual message content bytes, what is on the
wire after any content coding. Repr-Digest covers the selected
representation, which under Content-Encoding: gzip or a Range request
is a different byte string. RFC 3230's single Digest field was obsoleted
precisely because it never said which of the two it meant.
This library never guesses: you pass the bytes, it hashes them. For
Repr-Digest you must pass the representation bytes as you define them.
See HTTPDigest.Stream for incremental hashing, HTTPDigest.Plug for
incoming Plug request verification, and HTTPDigest.Req for outgoing Req
requests.
Summary
Functions
Builds a Want-Content-Digest header value from {algorithm, preference}
pairs.
Builds a Want-Repr-Digest header value. See build_want_content_digest/1.
Builds a Content-Digest header value over body (iodata).
Parses a Content-Digest header value into a map of algorithm to raw digest
bytes.
Parses a Repr-Digest header value. See parse_content_digest/2.
Parses a Want-Content-Digest header into a map of algorithm to preference.
Parses a Want-Repr-Digest header. See parse_want_content_digest/1.
Builds a Repr-Digest header value over the representation bytes.
Selects the response digest algorithm requested by a Want-* header.
Verifies a Content-Digest header against body.
Verifies a digest header against precomputed digests, such as
%{sha256: <<...>>} from a streaming body reader.
Verifies a Repr-Digest header against representation bytes. See verify_content/3.
Functions
@spec build_want_content_digest(keyword(integer())) :: {:ok, String.t()} | {:error, HTTPDigest.Error.t()}
Builds a Want-Content-Digest header value from {algorithm, preference}
pairs.
Preferences are integers from 0 to 10. A preference of 0 means not acceptable.
iex> HTTPDigest.build_want_content_digest(sha512: 10, sha256: 5)
{:ok, "sha-512=10, sha-256=5"}
@spec build_want_repr_digest(keyword(integer())) :: {:ok, String.t()} | {:error, HTTPDigest.Error.t()}
Builds a Want-Repr-Digest header value. See build_want_content_digest/1.
@spec content_digest( iodata(), keyword() ) :: {:ok, String.t()} | {:error, HTTPDigest.Error.t()}
Builds a Content-Digest header value over body (iodata).
iex> HTTPDigest.content_digest(~s({"hello": "world"}))
{:ok, "sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:"}Options:
:algorithms- list of:sha256/:sha512(default[:sha256]):allow_insecure- permit:md5/:sha(defaultfalse)
@spec parse_content_digest( binary(), keyword() ) :: {:ok, map()} | {:error, HTTPDigest.Error.t()}
Parses a Content-Digest header value into a map of algorithm to raw digest
bytes.
Known algorithms get atom keys such as :sha256. Unknown dictionary keys are
preserved under string keys because RFC 9530 receivers must ignore unknown
algorithms unless the caller opts into on_unknown: :error.
@spec parse_repr_digest( binary(), keyword() ) :: {:ok, map()} | {:error, HTTPDigest.Error.t()}
Parses a Repr-Digest header value. See parse_content_digest/2.
@spec parse_want_content_digest(binary()) :: {:ok, map()} | {:error, HTTPDigest.Error.t()}
Parses a Want-Content-Digest header into a map of algorithm to preference.
Known algorithms get atom keys. Unknown algorithms are preserved under string keys.
@spec parse_want_repr_digest(binary()) :: {:ok, map()} | {:error, HTTPDigest.Error.t()}
Parses a Want-Repr-Digest header. See parse_want_content_digest/1.
@spec repr_digest( iodata(), keyword() ) :: {:ok, String.t()} | {:error, HTTPDigest.Error.t()}
Builds a Repr-Digest header value over the representation bytes.
Mechanically identical to content_digest/2; the caller is responsible for
supplying the representation bytes, such as the uncompressed document when
the message content is gzip-coded, or the full document on a range response.
@spec select_from_want( binary(), keyword() ) :: {:ok, atom()} | {:error, HTTPDigest.Error.t()}
Selects the response digest algorithm requested by a Want-* header.
Among supported algorithms with preference greater than 0, the highest preference wins. Ties go to the stronger algorithm.
@spec verify_content(iodata(), binary(), keyword()) :: {:ok, atom()} | {:error, HTTPDigest.Error.t()}
Verifies a Content-Digest header against body.
The default :strongest policy checks only the strongest digest present
whose algorithm is supported, so a tampered strong digest fails even when a
weaker digest still matches. policy: :any accepts the strongest matching
digest and should only be used when compatibility requires it.
@spec verify_digests(%{optional(atom()) => binary()}, binary(), keyword()) :: {:ok, atom()} | {:error, HTTPDigest.Error.t()}
Verifies a digest header against precomputed digests, such as
%{sha256: <<...>>} from a streaming body reader.
@spec verify_repr(iodata(), binary(), keyword()) :: {:ok, atom()} | {:error, HTTPDigest.Error.t()}
Verifies a Repr-Digest header against representation bytes. See verify_content/3.