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.
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.
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.
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.