0 day streak
go / expert
Snippet

Timing-Safe Token Verification with crypto/subtle

A naive comparison like bytes.Equal or == on two strings returns as soon as it finds a difference. Network attackers can measure that early exit and brute-force a MAC one byte at a time — extending the matching prefix as response latency grows. subtle.ConstantTimeCompare performs the same comparison but always reads every byte and folds the result into a single accumulator, so execution time depends only on length, not on content. It returns 1 for equal and 0 otherwise. Use it for HMAC tags, password hashes, session tokens, and any other secret-bearing byte sequence.

snippet.go
go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main
import (
"crypto/hmac"
"crypto/sha256"
"crypto/subtle"
"fmt"
)
func sign(key, msg []byte) []byte {
m := hmac.New(sha256.New, key)
m.Write(msg)
return m.Sum(nil)
}
func verify(provided, expected []byte) bool {
// bytes.Equal short-circuits on the first mismatch and
// would leak the matching prefix length via wall-clock
// timing. ConstantTimeCompare always touches every byte.
return subtle.ConstantTimeCompare(provided, expected) == 1
}
func main() {
key := []byte("server-secret")
want := sign(key, []byte("user=42"))
got := make([]byte, len(want)) // attacker's guess
fmt.Println("ok:", verify(got, want))
}
Breakdown
1
m := hmac.New(sha256.New, key)
hmac.New binds the secret to a hash construction; the resulting tag is unforgeable without key, which is exactly what the verifier must check.
2
subtle.ConstantTimeCompare(provided, expected)
Iterates over every byte with XOR-and-OR accumulation. Different lengths return 0 immediately, but equal-length inputs always cost the same.
3
== 1
The function returns int (1 or 0) rather than bool — explicit comparison to 1 documents the constant-time contract at the call site.
4
got := make([]byte, len(want))
An all-zero candidate stands in for an attacker's guess; in production this would arrive from the wire as the X-Signature header.