auth.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. package tuic
  2. import (
  3. "crypto/subtle"
  4. "crypto/tls"
  5. "errors"
  6. "fmt"
  7. "sync"
  8. "sync/atomic"
  9. "github.com/google/uuid"
  10. )
  11. var (
  12. ErrUserNotFound = errors.New("tuic: user not found")
  13. ErrAuthFailed = errors.New("tuic: authentication failed")
  14. ErrInvalidTLSState = errors.New("tuic: TLS connection state not available")
  15. )
  16. // User represents a configured TUIC client for authentication and billing.
  17. type User struct {
  18. TrafficID int
  19. UUID [16]byte
  20. UUIDStr string
  21. Password string
  22. Email string
  23. Traffic *UserTraffic
  24. sessions atomic.Int64
  25. }
  26. type UserTraffic struct {
  27. BytesUp atomic.Int64
  28. BytesDown atomic.Int64
  29. }
  30. // UserRegistry is a thread-safe registry of TUIC users for an inbound.
  31. type UserRegistry struct {
  32. mu sync.RWMutex
  33. users map[[16]byte]*User
  34. retired map[*User]struct{}
  35. }
  36. // NewUserRegistry creates an empty UserRegistry.
  37. func NewUserRegistry() *UserRegistry {
  38. return &UserRegistry{
  39. users: make(map[[16]byte]*User),
  40. retired: make(map[*User]struct{}),
  41. }
  42. }
  43. // SetUsers updates the user list atomically in memory, preserving counters and
  44. // pointer stability for active sessions. It returns any users removed from the registry.
  45. func (ur *UserRegistry) SetUsers(clients []TuicClientSettings) (revoked []*User) {
  46. ur.mu.Lock()
  47. defer ur.mu.Unlock()
  48. newMap := make(map[[16]byte]*User, len(clients))
  49. for _, c := range clients {
  50. parsed, err := uuid.Parse(c.UUID)
  51. if err != nil {
  52. continue
  53. }
  54. if existing, ok := ur.users[parsed]; ok && existing.Password == c.Password && existing.Email == c.Email && existing.TrafficID == c.TrafficID {
  55. newMap[parsed] = existing
  56. } else if ok {
  57. ur.retired[existing] = struct{}{}
  58. revoked = append(revoked, existing)
  59. newMap[parsed] = newUser(parsed, c)
  60. } else {
  61. newMap[parsed] = newUser(parsed, c)
  62. }
  63. }
  64. for id, oldUser := range ur.users {
  65. if _, ok := newMap[id]; !ok {
  66. revoked = append(revoked, oldUser)
  67. ur.retired[oldUser] = struct{}{}
  68. }
  69. }
  70. ur.users = newMap
  71. return revoked
  72. }
  73. func newUser(id [16]byte, c TuicClientSettings) *User {
  74. return &User{
  75. TrafficID: c.TrafficID, UUID: id, UUIDStr: uuid.UUID(id).String(), Password: c.Password, Email: c.Email,
  76. Traffic: &UserTraffic{},
  77. }
  78. }
  79. // AddTestTraffic adds traffic counters to a user by email for testing purposes.
  80. func (ur *UserRegistry) AddTestTraffic(email string, up, down int64) bool {
  81. ur.mu.RLock()
  82. defer ur.mu.RUnlock()
  83. for _, u := range ur.users {
  84. if u.Email == email {
  85. u.Traffic.BytesUp.Add(up)
  86. u.Traffic.BytesDown.Add(down)
  87. return true
  88. }
  89. }
  90. return false
  91. }
  92. // ClientTrafficDelta represents the traffic delta for a user.
  93. type ClientTrafficDelta struct {
  94. TrafficID int
  95. Email string
  96. UUID string
  97. InboundID int
  98. Up int64
  99. Down int64
  100. }
  101. // CollectTrafficDeltas drains and returns byte deltas for all users since the last call.
  102. func (ur *UserRegistry) CollectTrafficDeltas() []ClientTrafficDelta {
  103. ur.mu.Lock()
  104. defer ur.mu.Unlock()
  105. var deltas []ClientTrafficDelta
  106. collect := func(u *User, retired bool) {
  107. up := u.Traffic.BytesUp.Swap(0)
  108. down := u.Traffic.BytesDown.Swap(0)
  109. if up > 0 || down > 0 {
  110. deltas = append(deltas, ClientTrafficDelta{
  111. TrafficID: u.TrafficID,
  112. Email: u.Email,
  113. UUID: u.UUIDStr,
  114. Up: up,
  115. Down: down,
  116. })
  117. }
  118. if retired && u.sessions.Load() == 0 {
  119. delete(ur.retired, u)
  120. }
  121. }
  122. for _, u := range ur.users {
  123. collect(u, false)
  124. }
  125. for u := range ur.retired {
  126. collect(u, true)
  127. }
  128. return deltas
  129. }
  130. func (ur *UserRegistry) sessionEnded(user *User) {
  131. if user != nil {
  132. user.sessions.Add(-1)
  133. }
  134. }
  135. // Authenticate verifies the client's token using RFC 5705 Keying Material Exporter.
  136. // According to TUIC v5 specification:
  137. // - label: client UUID
  138. // - context: raw password
  139. // - length: 32 bytes
  140. func (ur *UserRegistry) Authenticate(cs *tls.ConnectionState, rawUUID [16]byte, token [32]byte) (*User, error) {
  141. return ur.authenticate(cs, rawUUID, token, nil)
  142. }
  143. // AuthenticateAndRegister holds the registry read lock through connection
  144. // registration, making successful authentication atomic with user revocation.
  145. func (ur *UserRegistry) AuthenticateAndRegister(cs *tls.ConnectionState, rawUUID [16]byte, token [32]byte, register func(*User) bool) (*User, error) {
  146. return ur.authenticate(cs, rawUUID, token, register)
  147. }
  148. func (ur *UserRegistry) authenticate(cs *tls.ConnectionState, rawUUID [16]byte, token [32]byte, register func(*User) bool) (*User, error) {
  149. if cs == nil {
  150. return nil, ErrInvalidTLSState
  151. }
  152. ur.mu.RLock()
  153. defer ur.mu.RUnlock()
  154. user, exists := ur.users[rawUUID]
  155. if !exists {
  156. return nil, ErrUserNotFound
  157. }
  158. if !cs.HandshakeComplete {
  159. return nil, ErrInvalidTLSState
  160. }
  161. // Try with raw 16-byte UUID as label
  162. expectedToken, err := cs.ExportKeyingMaterial(string(rawUUID[:]), []byte(user.Password), 32)
  163. if err == nil && subtle.ConstantTimeCompare(token[:], expectedToken) == 1 {
  164. if register != nil && !register(user) {
  165. return nil, ErrUserNotFound
  166. }
  167. return user, nil
  168. }
  169. // Fallback to formatted 36-char string representation of UUID as label
  170. expectedTokenStr, errStr := cs.ExportKeyingMaterial(user.UUIDStr, []byte(user.Password), 32)
  171. if errStr == nil && subtle.ConstantTimeCompare(token[:], expectedTokenStr) == 1 {
  172. if register != nil && !register(user) {
  173. return nil, ErrUserNotFound
  174. }
  175. return user, nil
  176. }
  177. if err != nil && errStr != nil {
  178. return nil, fmt.Errorf("%w: export keying material: %w", ErrAuthFailed, err)
  179. }
  180. return nil, ErrAuthFailed
  181. }
  182. func ValidateClients(clients []TuicClientSettings) error {
  183. seen := make(map[uuid.UUID]bool, len(clients))
  184. for _, client := range clients {
  185. id, err := uuid.Parse(client.UUID)
  186. if err != nil {
  187. return errors.New("tuic: invalid client UUID")
  188. }
  189. if seen[id] {
  190. return errors.New("tuic: duplicate client UUID")
  191. }
  192. seen[id] = true
  193. }
  194. return nil
  195. }