Skip to main content

internal/notify/signature.go

internal/notify · 77 lines · 4 declarations · source

Declarations

const SignatureHeader

const SignatureHeader = "Taisce-Signature"

SignatureHeader carries the version, the time and the signature, in one header.

One header rather than three, because three can disagree: a receiver that reads the timestamp from one header and verifies a signature computed over another has verified nothing. Here the material is unambiguous — the version, the timestamp and the body — and the header is the whole claim.

source

const SignatureVersion

const SignatureVersion = "v1"

SignatureVersion is the first field of the header and of the signed material. It exists so that a change to what is signed cannot be read by an old receiver as the same claim.

source

func Sign

func Sign(secret []byte, at time.Time, body []byte) string

Sign returns the header value for a body at a moment.

Why the timestamp is inside the signed material

A signature over the body alone is valid forever. Anybody who captures one delivery can replay it a week later and the receiver cannot tell. Signing the timestamp with the body means a replay is either stale — which the receiver rejects by looking at the clock — or a forgery, which requires the secret.

source

func Verify

func Verify(secret []byte, header string, body []byte, now time.Time, tolerance time.Duration) error

Verify is what a receiver does, written here so the suite can hold the signature to it and so an adapter has one correct implementation to copy rather than three guesses.

Tolerance bounds how old a delivery may be and still be believed. It is the receiver's choice and there is no right answer; what matters is that there is one, because without it the timestamp is decoration.

source