barrel_sync_sig (barrel_docdb v1.1.1)

View Source

Ed25519 signed-request auth for the barrel sync wire (protocol).

Shared, pure helpers used by both ends of the wire: the client signer (barrel_rep_transport_http, same app) and the server verifier (barrel_server_auth, which depends on barrel_docdb). It lives here, the sync wire's home, so both sides share one canonical string.

A request is authenticated by an Ed25519 signature over:

     ts | keyId | METHOD | path | content_sha256_hex

ts is milliseconds since the epoch (decimal ASCII); content_sha256 is the lowercase hex SHA-256 of the request body, carried in the x-barrel-content-sha256 header so the verifier never reads the body (streamed attachments can be large). The signature travels in an Authorization: Signature keyId="..",ts="..",sig="base64" header.

This mirrors the retired barrel_memory peer-auth scheme, fixing its two weaknesses: the body is bound end to end (the handler re-hashes it against the signed header), and a missing/unknown key fails closed here (never open). Replay protection is a server-side concern (barrel_server_sig_cache); this module is pure.

Summary

Functions

The canonical signing string. Method is the uppercase verb, Path the request path (no scheme/host/query), ContentHashHex the value of x-barrel-content-sha256.

Lowercase hex SHA-256 of a body (the value for x-barrel-content-sha256).

Parse an Authorization header value. Returns not_signature for anything that is not the Signature scheme (e.g. Bearer), so the caller can fall through to other auth. Malformed Signature headers return {error, malformed}.

Build the Authorization: Signature ... header value for a request. PrivKey is a 32-byte raw Ed25519 private key.

Verify a parsed signature against the configured signers and a skew window. Pure: replay is checked separately by the caller. Signers maps keyId to a 32-byte raw Ed25519 public key.

Types

parsed/0

-type parsed() :: #{key_id := binary(), ts := integer(), sig := binary()}.

Functions

canonical(TsBin, KeyId, Method, Path, ContentHashHex)

-spec canonical(binary(), binary(), binary(), binary(), binary()) -> binary().

The canonical signing string. Method is the uppercase verb, Path the request path (no scheme/host/query), ContentHashHex the value of x-barrel-content-sha256.

content_sha256(Body)

-spec content_sha256(iodata()) -> binary().

Lowercase hex SHA-256 of a body (the value for x-barrel-content-sha256).

parse_auth(Other)

-spec parse_auth(binary() | undefined) -> {ok, parsed()} | not_signature | {error, malformed}.

Parse an Authorization header value. Returns not_signature for anything that is not the Signature scheme (e.g. Bearer), so the caller can fall through to other auth. Malformed Signature headers return {error, malformed}.

sign(KeyId, PrivKey, Method, Path, ContentHashHex, TsMs)

-spec sign(binary(), binary(), binary(), binary(), binary(), integer()) -> binary().

Build the Authorization: Signature ... header value for a request. PrivKey is a 32-byte raw Ed25519 private key.

verify(Method, Path, ContentHashHex, _, Signers, SkewMs)

-spec verify(binary(), binary(), binary(), parsed(), map(), integer()) ->
                ok | {error, unknown_key | bad_signature | stale}.

Verify a parsed signature against the configured signers and a skew window. Pure: replay is checked separately by the caller. Signers maps keyId to a 32-byte raw Ed25519 public key.