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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1HMAC binds a message to a secret key so recipients can detect tampering.
- 2Signature comparison must be constant-time to avoid leaking bytes through timing.
- 3Wrapping a secret in a type keeps key handling and signing logic in one place.
Related explainers
go
package handlers import ( "context"
Liveness vs readiness health checks in Gin
health-checks
dependency-injection
context-timeout
Intermediate
7 steps
go
package handlers import ( "net/http"
Custom query validation in Gin
validation
query-binding
deduplication
Intermediate
8 steps
go
package storage import ( "context"
A SQLite-backed order store in Go
database/sql
sqlite
error-wrapping
Intermediate
7 steps
typescript
import { CanActivate, ExecutionContext, Injectable,
How guards chain auth in NestJS
guards
authentication
authorization
Intermediate
8 steps
go
package auth import ( "errors"
Hashing and verifying passwords with bcrypt in Go
password-hashing
bcrypt
error-handling
Intermediate
7 steps
go
package fsutil import ( "io/fs"
Walking a directory tree to find large files in Go
filesystem
recursion
closures
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/signing-and-verifying-payloads-with-hmac-in-go-explained-go-1e38/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.