RequestSeal.Digest (RequestSeal v0.2.0)

Copy Markdown View Source

RFC 9530 content/representation digests and preference fields, using OTP hashes.

compute/3 hashes a retained RequestSeal.Body without re-encoding its bytes. Algorithms are an explicit nonempty, unique list drawn from "sha-256" and "sha-512". Its result is a Structured Fields dictionary, serialized with serialize/1. init/3, update/2, and finish/1 support caller-fed binary chunks without retaining content, reading handles, or starting processes. :max_bytes defaults to 16,777,216 and may be lowered to any nonnegative integer. Retained-body compute/3 and check/3 cannot raise this 16 MiB hard limit, bounding work on already retained content. Only caller-fed streams may explicitly opt in to a higher :max_bytes through init/3: incremental hash state uses constant memory and retains no chunks. The caller owns that larger work budget. A chunk exceeding the remaining limit rejects before hashing it. State is caller-owned and immutable; use each returned state and finish only at EOF. Finishing does not prove transport completion; the caller supplies that boundary.

Hash HTTP content after transfer framing removal but before content decoding. Gzip bytes remain gzip bytes. A range's content is only the transmitted range; a HEAD response's content is empty. Representation hashing always uses the complete selected representation, including content encoding (Sections 2–3). The caller selects that representation using HTTP method, status, metadata, and resource semantics; this library never reconstructs it from partial content.

check/3 takes a validated Message and :content or :representation. Content uses the Message's retained body. Representation requires the option :representation, a retained Body containing the entire selected representation, even when it equals content. No unavailable body substitutes for empty bytes. :section selects :headers (default) or :trailers; sections never merge. :max_bytes sets the computation bound. Unknown or duplicate options reject. check_stream/3 checks caller-fed state against the field selected by its kind; its only option is :section. A trailer must be observed before checking it.

Every supported checksum present must match. Before dictionary deduplication, checks reject a supported algorithm appearing more than once across the combined occurrences in the selected section with :conflicting_digest, even if its checksum values agree. Unknown and deprecated algorithms are parsed but never computed; success reports only their count. They cannot establish a match alone. A stream must have computed every supported algorithm in the field. A successful map contains :kind, :bytes, :checked (supported algorithm names), and :unsupported (count); it proves checksum agreement only, with no signature coverage, authentication, identity, or authorization claim. Legacy Digest fields never substitute for these fields.

parse/1 and serialize/1 use the RFC 8941 dictionary/byte-sequence schema mandated by RFC 9530; known checksum lengths are checked. parse_preferences/1, serialize_preferences/1, and preferences/3 use integer weights in 0..10. Preferences are hints, with 0 meaning unacceptable (Section 4); this module selects no algorithm from them. Parameters and duplicate keys follow Structured Fields rules (last value, first position), except supported checksum algorithms in check/3 and check_stream/3, which reject any repeat with :conflicting_digest. Inspection hides hash contexts.

Summary

Functions

Recompute the selected field from retained content or explicit representation.

Check hashes accumulated through caller EOF against a Message digest field.

Compute SHA-256/SHA-512 checksums from retained exact body bytes.

Finalize checksums as an ordered Structured Fields dictionary at caller EOF.

Initialize incremental hashes for an explicit semantic kind and algorithms.

Parse an RFC 9530 integrity dictionary; parameters retain RFC semantics.

Parse an RFC 9530 preference dictionary with integer weights in 0..10.

Read an explicit Want-Content-Digest or Want-Repr-Digest field/section.

Serialize a digest dictionary with known algorithm checksum-size validation.

Serialize an RFC 9530 preference dictionary with weight validation.

Hash one binary content chunk, rejecting overflow before cryptographic work.

Types

failure()

t()

@type t() :: %RequestSeal.Digest{
  bytes: non_neg_integer(),
  hashes: [{binary(), reference()}],
  kind: :content | :representation,
  max_bytes: non_neg_integer()
}

Functions

check(message, kind, opts \\ [])

@spec check(RequestSeal.Message.t(), :content | :representation, keyword()) ::
  {:ok, map()} | failure()

Recompute the selected field from retained content or explicit representation.

check_stream(state, message, opts \\ [])

@spec check_stream(t(), RequestSeal.Message.t(), keyword()) ::
  {:ok, map()} | failure()

Check hashes accumulated through caller EOF against a Message digest field.

compute(body, algorithms, opts \\ [])

Compute SHA-256/SHA-512 checksums from retained exact body bytes.

finish(state)

@spec finish(t()) :: {:ok, RequestSeal.StructuredFields.Value.t()} | failure()

Finalize checksums as an ordered Structured Fields dictionary at caller EOF.

init(kind, algorithms, opts \\ [])

@spec init(:content | :representation, [binary()], keyword()) ::
  {:ok, t()} | failure()

Initialize incremental hashes for an explicit semantic kind and algorithms.

:max_bytes may exceed 16 MiB to explicitly opt in to a larger streaming work budget.

parse(wire)

@spec parse(binary()) :: {:ok, RequestSeal.StructuredFields.Value.t()} | failure()

Parse an RFC 9530 integrity dictionary; parameters retain RFC semantics.

parse_preferences(wire)

@spec parse_preferences(binary()) ::
  {:ok, RequestSeal.StructuredFields.Value.t()} | failure()

Parse an RFC 9530 preference dictionary with integer weights in 0..10.

preferences(message, name, section \\ :headers)

@spec preferences(RequestSeal.Message.t(), binary(), :headers | :trailers) ::
  {:ok, RequestSeal.StructuredFields.Value.t()} | failure()

Read an explicit Want-Content-Digest or Want-Repr-Digest field/section.

serialize(value)

@spec serialize(RequestSeal.StructuredFields.Value.t()) :: {:ok, binary()} | failure()

Serialize a digest dictionary with known algorithm checksum-size validation.

serialize_preferences(value)

@spec serialize_preferences(RequestSeal.StructuredFields.Value.t()) ::
  {:ok, binary()} | failure()

Serialize an RFC 9530 preference dictionary with weight validation.

update(state, chunk)

@spec update(t(), binary()) :: {:ok, t()} | failure()

Hash one binary content chunk, rejecting overflow before cryptographic work.