go 37 lines · 5 steps

Signing and verifying payloads with HMAC in Go

A small Signer type that produces and checks HMAC-SHA256 signatures using constant-time comparison.

Explained by highlit
1package security
2 
3import (
4 "crypto/hmac"
5 "crypto/sha256"
6 "encoding/hex"
7 "errors"
8 "fmt"
9)
10 
11type Signer struct {
12 secret []byte
13}
14 
15func NewSigner(secret string) *Signer {
16 return &Signer{secret: []byte(secret)}
17}
18 
19func (s *Signer) Sign(payload []byte) string {
20 mac := hmac.New(sha256.New, s.secret)
21 mac.Write(payload)
22 return hex.EncodeToString(mac.Sum(nil))
23}
24 
25var ErrInvalidSignature = errors.New("security: invalid signature")
26 
27func (s *Signer) Verify(payload []byte, signature string) error {
28 expected := s.Sign(payload)
29 if !hmac.Equal([]byte(expected), []byte(signature)) {
30 return ErrInvalidSignature
31 }
32 return nil
33}
34 
35func (s *Signer) SignHeader(payload []byte) string {
36 return fmt.Sprintf("sha256=%s", s.Sign(payload))
37}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1HMAC binds a message to a secret key so recipients can detect tampering.
  2. 2Signature comparison must be constant-time to avoid leaking bytes through timing.
  3. 3Wrapping a secret in a type keeps key handling and signing logic in one place.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Signing and verifying payloads with HMAC in Go — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code