| // Copyright 2023 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build go1.21 |
| |
| package quic |
| |
| import ( |
| "crypto/hmac" |
| "crypto/rand" |
| "crypto/sha256" |
| "hash" |
| "sync" |
| ) |
| |
| const statelessResetTokenLen = 128 / 8 |
| |
| // A statelessResetToken is a stateless reset token. |
| // https://www.rfc-editor.org/rfc/rfc9000#section-10.3 |
| type statelessResetToken [statelessResetTokenLen]byte |
| |
| type statelessResetTokenGenerator struct { |
| canReset bool |
| |
| // The hash.Hash interface is not concurrency safe, |
| // so we need a mutex here. |
| // |
| // There shouldn't be much contention on stateless reset token generation. |
| // If this proves to be a problem, we could avoid the mutex by using a separate |
| // generator per Conn, or by using a concurrency-safe generator. |
| mu sync.Mutex |
| mac hash.Hash |
| } |
| |
| func (g *statelessResetTokenGenerator) init(secret [32]byte) { |
| zero := true |
| for _, b := range secret { |
| if b != 0 { |
| zero = false |
| break |
| } |
| } |
| if zero { |
| // Generate tokens using a random secret, but don't send stateless resets. |
| rand.Read(secret[:]) |
| g.canReset = false |
| } else { |
| g.canReset = true |
| } |
| g.mac = hmac.New(sha256.New, secret[:]) |
| } |
| |
| func (g *statelessResetTokenGenerator) tokenForConnID(cid []byte) (token statelessResetToken) { |
| g.mu.Lock() |
| defer g.mu.Unlock() |
| defer g.mac.Reset() |
| g.mac.Write(cid) |
| copy(token[:], g.mac.Sum(nil)) |
| return token |
| } |